Merge branch 'master' into develop

This commit is contained in:
Fergal Moran
2015-08-13 20:10:25 +01:00
38 changed files with 1032 additions and 225 deletions

25
Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
FROM python:2.7
ENV PYTHONBUFFERED 1
RUN mkdir /code
RUN mkdir /srv/logs
RUN mkdir /files
RUN mkdir /files/static
RUN mkdir /files/media
RUN mkdir /files/cache
RUN mkdir /files/cache/mixes
RUN mkdir /files/cache/waveforms
RUN mkdir /files/tmp
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
RUN apt-get update && apt-get install -y sox lame vim \
libboost-program-options-dev libsox-fmt-mp3 postgresql-client
ADD . /code/
RUN adduser --disabled-password --gecos '' djworker
RUN chown djworker /files -R
RUN chown djworker /srv/logs -R
RUN export PATH=$PATH:/mnt/bin/

View File

@@ -1,5 +1,6 @@
from calendar import timegm
import datetime
import logging
from rest_framework import permissions
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.response import Response
@@ -15,11 +16,9 @@ from rest_framework import parsers
from social.apps.django_app.utils import psa
from dss import settings
logger = logging.getLogger('spa')
@psa()
def auth_by_token(request, backend):
token = request.data.get('access_token')
user = request.user
user = request.backend.do_auth(
access_token=request.data.get('access_token')
)
@@ -27,7 +26,7 @@ def auth_by_token(request, backend):
return user if user else None
class FacebookView(APIView):
class SocialLoginHandler(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
@@ -38,9 +37,10 @@ class FacebookView(APIView):
try:
user = auth_by_token(request, backend)
except Exception, e:
logger.exception(e)
return Response({
'status': 'Bad request',
'message': 'Could not authenticate with the provided token' if settings.DEBUG else e.message
'message': e.message
}, status=status.HTTP_400_BAD_REQUEST)
if user:
@@ -57,7 +57,8 @@ class FacebookView(APIView):
)
response_data = {
'token': jwt_encode_handler(payload)
'token': jwt_encode_handler(payload),
'session': user.userprofile.get_session_id()
}
return Response(response_data)

View File

@@ -22,9 +22,16 @@ class ChatHelper(ActivityHelper):
# do some persistence stuff with the chat
from core.realtime import chat
user = self.get_session(request)
#user = self.get_session(request)
u = request.user
if not u.is_anonymous():
image = u.userprofile.get_sized_avatar_image(32, 32)
user = u.userprofile.get_nice_name()
else:
image = settings.DEFAULT_USER_IMAGE
user = settings.DEFAULT_USER_NAME
chat.post_chat(request.data['user'], request.data['message'])
chat.post_chat(request.data['user'], image, user, request.data['message'])
return Response(request.data['message'], HTTP_201_CREATED)

View File

@@ -449,9 +449,11 @@ class ActivitySerializer(serializers.HyperlinkedModelSerializer):
class NotificationSerializer(serializers.HyperlinkedModelSerializer):
from_user = UserProfileSerializer(many=False, required=False)
display_name = serializers.SerializerMethodField(method_name='get_display_name')
avatar_image = serializers.SerializerMethodField(method_name='get_avatar_image')
from_user = InlineUserProfileSerializer(source='get_from_user', read_only=True)
notification_url = serializers.ReadOnlyField()
verb = serializers.ReadOnlyField()
target = serializers.ReadOnlyField()
date = serializers.ReadOnlyField()
class Meta:
model = Notification
@@ -459,10 +461,12 @@ class NotificationSerializer(serializers.HyperlinkedModelSerializer):
'id',
'notification_url',
'from_user',
'display_name',
'avatar_image',
'verb',
'target',
'target_desc',
'type',
'date',
'accepted_date',
)
def get_display_name(self, obj):

View File

@@ -1,21 +1,44 @@
from django.conf.urls import patterns, url, include
from rest_framework import permissions
from rest_framework.permissions import IsAuthenticated
from rest_framework.routers import DefaultRouter
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api import views, auth, helpers
from api.auth import FacebookView
from api.auth import SocialLoginHandler
from rest_framework.views import status
from rest_framework.response import Response
from core.realtime import activity
router = DefaultRouter() # trailing_slash=True)
router.register(r'user', views.UserProfileViewSet)
router.register(r'mix', views.MixViewSet)
router.register(r'notification', views.NotificationViewSet)
router.register(r'hitlist', views.HitlistViewSet)
router.register(r'comments', views.CommentViewSet)
router.register(r'activity', views.ActivityViewSet, base_name='activity')
router.register(r'genre', views.GenreViewSet, base_name='genre')
class DebugView(APIView):
permission_classes = (IsAuthenticated, )
authentication_classes = (JSONWebTokenAuthentication, )
def post(self, request, format=None):
try:
activity.post_activity('user:message', request.user.userprofile.get_session_id(), 'Hello Sailor')
except Exception, ex:
print ex.message
return Response({
'status': request.user.first_name,
'message': 'Sailor'
}, status=status.HTTP_200_OK)
urlpatterns = patterns(
'',
url(r'^', include(router.urls)),
@@ -26,17 +49,18 @@ urlpatterns = patterns(
url(r'_search/$', views.SearchResultsView.as_view()),
url(r'^', include(router.urls)),
#url(r'^login/', auth.ObtainAuthToken.as_view()),
#url(r'^logout/', auth.ObtainLogout.as_view()),
url(r'^_login/', SocialLoginHandler.as_view()),
url(r'^token-refresh/', 'rest_framework_jwt.views.refresh_jwt_token'),
# url(r'^_tr/', RefreshToken.as_view()),
url(r'^__u/checkslug', helpers.UserSlugCheckHelper.as_view()),
url(r'^__u/', auth.ObtainUser.as_view()),
url(r'^_act/play', helpers.ActivityPlayHelper.as_view()),
url(r'^_chat/', helpers.ChatHelper.as_view()),
url(r'^_login/', FacebookView.as_view()),
url(r'^__debug/', DebugView.as_view()),
url('', include('social.apps.django_app.urls', namespace='social')),
)

View File

@@ -17,9 +17,8 @@ from rest_framework.status import HTTP_202_ACCEPTED, HTTP_401_UNAUTHORIZED, HTTP
HTTP_200_OK, HTTP_204_NO_CONTENT
from api import serializers
from core.utils.cdn import upload_to_azure
from dss import settings
from core.tasks import create_waveform_task, archive_mix_task
from spa import tasks
from spa.models.genre import Genre
from spa.models.activity import ActivityPlay
from spa.models.mix import Mix
@@ -27,7 +26,7 @@ from spa.models.comment import Comment
from spa.models.notification import Notification
from spa.models.userprofile import UserProfile
logger = logging.getLogger(__name__)
logger = logging.getLogger('spa')
class AnonymousWriteUserDelete(BasePermission):
@@ -121,10 +120,10 @@ class AttachedImageUploadView(views.APIView):
parser_classes = (FileUploadParser,)
def post(self, request):
if request.FILES['file'] is None or request.data.get('data') is None:
if request.data['file'] is None or request.data.get('data') is None:
return Response(status=HTTP_400_BAD_REQUEST)
file_obj = request.FILES['file']
file_obj = request.data['file']
file_hash = request.data.get('data')
try:
mix = Mix.objects.get(uid=file_hash)
@@ -134,6 +133,8 @@ class AttachedImageUploadView(views.APIView):
return Response(HTTP_202_ACCEPTED)
except ObjectDoesNotExist:
return Response(status=HTTP_404_NOT_FOUND)
except Exception, ex:
logger.exception(ex)
return Response(status=HTTP_401_UNAUTHORIZED)
@@ -161,26 +162,43 @@ class PartialMixUploadView(views.APIView):
# noinspection PyBroadException
def post(self, request):
try:
logger.info("Received post file")
uid = request.META.get('HTTP_UPLOAD_HASH')
in_file = request.FILES['file'] if request.FILES else None
in_file = request.data['file'] if request.data else None
file_name, extension = os.path.splitext(in_file.name)
logger.info("Constructing storage")
file_storage = FileSystemStorage(location=os.path.join(settings.CACHE_ROOT, "mixes"))
cache_file = file_storage.save("%s%s" % (uid, extension), ContentFile(in_file.read()))
response = 'File creation in progress'
logger.info("Storage constructed")
try:
logger.debug("Received input file")
logger.debug("Storage is {0}".format(file_storage.base_location))
input_file = os.path.join(file_storage.base_location, cache_file)
# Chain the waveform & archive tasks together
# Probably not the best place for them but will do for now
# First argument to archive_mix_task is not specified as it is piped from create_waveform_task
(create_waveform_task.s(input_file, uid) |
archive_mix_task.s(filetype='mp3', uid=uid)).delay()
# First argument to upload_to_cdn_task is not specified as it is piped from create_waveform_task
except Exception:
logger.debug("Processing input_file: {0}".format(input_file))
logger.debug("Connecting to broker: {0}".format(settings.BROKER_URL))
from celery import group, chain
(
tasks.create_waveform_task.s(input_file, uid) |
tasks.upload_to_cdn_task.subtask(('mp3', uid, 'mixes'), immutable=True) |
tasks.upload_to_cdn_task.subtask(('png', uid, 'waveforms'), immutable=True) |
tasks.notify_subscriber.subtask((request.user.userprofile.get_session_id(), uid), immutable=True)
).delay()
logger.debug("Waveform task started")
except Exception, ex:
logger.exception(ex)
response = \
'Unable to connect to waveform generation task, there may be a delay in getting your mix online'
'Unable to connect to rabbitmq, there may be a delay in getting your mix online'
file_dict = {
'response': response,
@@ -209,12 +227,13 @@ class ActivityViewSet(viewsets.ModelViewSet):
ret = ActivityPlay.objects.filter(mix__user=user).order_by("-id")
if len(ret) >0:
logger.debug("Activity returned: %s".format(ret[0].get_object_slug()))
if len(ret) > 0:
logger.debug("Activity returned: {0}".format(ret[0].get_object_slug()))
return ret
else:
return []
class DownloadItemView(views.APIView):
def get(self, request, *args, **kwargs):
try:
@@ -234,7 +253,10 @@ class NotificationViewSet(viewsets.ModelViewSet):
if not user.is_authenticated():
raise PermissionDenied("Not allowed")
return Notification.objects.filter(to_user=user).order_by('-date')[0:5]
return Notification.objects.filter(to_user=user).order_by('-date')
def perform_update(self, serializer):
return super(NotificationViewSet, self).perform_update(serializer)
class GenreViewSet(viewsets.ModelViewSet):

BIN
bin/wav2png Executable file

Binary file not shown.

View File

@@ -1,9 +1,10 @@
import redis
import json
from dss import settings
def post_activity(channel, session, message):
r = redis.StrictRedis(host='localhost', port=6379, db=0)
r = redis.StrictRedis(host=settings.REDIS_HOST, port=6379, db=0)
response = r.publish(channel, json.dumps({'session': session, 'message': message}))
print "Message sent: {0}".format(response)

View File

@@ -1,8 +1,17 @@
import json
import datetime
import redis
from dss import settings
def post_chat(session, message):
r = redis.StrictRedis(host='localhost', port=6379, db=0)
response = r.publish('chat', json.dumps({'session': session, 'message': message}))
def post_chat(session, image, user, message):
r = redis.StrictRedis(host=settings.REDIS_HOST, port=6379, db=0)
payload = json.dumps({
'session': session,
'message': message,
'user': user,
'image': image,
'date': datetime.datetime.now().isoformat()
})
response = r.publish('chat', payload)
print "Message sent: {0}".format(response)

View File

@@ -1,53 +0,0 @@
import shutil
from celery.task import task
import os
from core.utils.cdn import upload_to_azure
from spa.signals import waveform_generated_signal
try:
from django.contrib.gis.geoip import GeoIP
except ImportError:
pass
from core.utils.waveform import generate_waveform
from dss import settings
@task(time_limit=3600)
def create_waveform_task(in_file, uid):
out_file = os.path.join(settings.MEDIA_ROOT, 'waveforms/%s.png' % uid)
print "Creating waveform \n\tIn: %s\n\tOut: %s" % (in_file, out_file)
generate_waveform(in_file, out_file)
if os.path.isfile(out_file):
print "Waveform generated successfully"
out_file, extension = os.path.splitext(in_file)
new_file = os.path.join(settings.MEDIA_ROOT, "mixes", "%s%s" % (uid, extension))
print "Moving cache audio clip from %s to %s" % (in_file, new_file)
shutil.move(in_file, new_file)
print "Uid: %s" % uid
waveform_generated_signal.send(sender=None, uid=uid)
return new_file
else:
print "Outfile is missing"
@task(time_limit=3600)
def archive_mix_task(in_file, filetype, uid):
print "Sending {0} to azure".format(uid)
try:
upload_to_azure(in_file, filetype, uid)
except Exception, ex:
print "Unable to upload: %s".format(ex.message)
@task
def update_geo_info_task(ip_address, profile_id):
try:
ip = '188.141.70.110' if ip_address == '127.0.0.1' else ip_address
if ip:
g = GeoIP()
city = g.city(ip)
country = g.country(ip)
print "Updated user location"
except Exception, e:
print e.message
pass

View File

@@ -1,41 +1,43 @@
import os
from azure import WindowsAzureMissingResourceError
from azure.storage import BlobService
from core.utils.url import url_path_join
from dss import settings
from dss.storagesettings import AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY, AZURE_CONTAINER
from libcloud.storage.types import Provider
from libcloud.storage.providers import get_driver
from dss import settings
from dss.storagesettings import AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY, AZURE_CONTAINER
def upload_to_azure(in_file, filetype, uid):
def upload_file_to_azure(in_file, file_name, container_name=settings.AZURE_CONTAINER):
if os.path.isfile(in_file):
print "Uploading file for: %s" % in_file
file_name = "%s.%s" % (uid, filetype)
cls = get_driver(Provider.AZURE_BLOBS)
driver = cls(settings.AZURE_ACCOUNT_NAME, settings.AZURE_ACCOUNT_KEY)
container = driver.get_container(container_name=settings.AZURE_CONTAINER)
with open(in_file, 'rb') as iterator:
obj = driver.upload_object_via_stream(
iterator=iterator,
container=container,
object_name=file_name
)
print "Uploaded"
return obj
return upload_stream_to_azure(iterator, file_name, container_name=container_name)
else:
print "infile not found"
return None
def set_azure_details(blob_name, download_name):
def upload_stream_to_azure(iterator, file_name, container_name=settings.AZURE_CONTAINER):
cls = get_driver(Provider.AZURE_BLOBS)
driver = cls(settings.AZURE_ACCOUNT_NAME, settings.AZURE_ACCOUNT_KEY)
container = driver.get_container(container_name)
obj = driver.upload_object_via_stream(
iterator=iterator,
container=container,
object_name=file_name
)
return obj
def set_azure_details(blob_name, download_name, container_name=AZURE_CONTAINER):
try:
blob_service = BlobService(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY)
blob = blob_service.get_blob(AZURE_CONTAINER, blob_name)
blob = blob_service.get_blob(container_name, blob_name)
if blob:
blob_service.set_blob_properties(
AZURE_CONTAINER,
container_name,
blob_name,
x_ms_blob_content_type='application/octet-stream',
x_ms_blob_content_disposition='attachment;filename="{0}"'.format(download_name)
@@ -47,3 +49,29 @@ def set_azure_details(blob_name, download_name):
print "No blob found for: %s" % download_name
except Exception, ex:
print "Error processing blob %s: %s" % (download_name, ex.message)
def file_exists(url):
import httplib
from urlparse import urlparse
p = urlparse(url)
c = httplib.HTTPConnection(p.netloc)
c.request("HEAD", p.path)
r = c.getresponse()
return r.status == 200
def enumerate_objects(container):
blob_service = BlobService(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY)
blobs = blob_service.list_blobs(container)
items = []
for blob in blobs:
items.append(blob.name)
return items
def delete_object(container, name):
blob_service = BlobService(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY)
blob_service.delete_blob(container, name)

View File

@@ -5,19 +5,21 @@ from django.template.defaultfilters import slugify
__author__ = 'fergalm'
def url_path_join(*parts):
"""Join and normalize url path parts with a slash."""
schemes, netlocs, paths, queries, fragments = zip(*(urlparse.urlsplit(part) for part in parts))
# Use the first value for everything but path. Join the path on '/'
scheme = next((x for x in schemes if x), '')
netloc = next((x for x in netlocs if x), '')
path = '/'.join(x.strip('/') for x in paths if x)
query = next((x for x in queries if x), '')
scheme = next((x for x in schemes if x), '')
netloc = next((x for x in netlocs if x), '')
path = '/'.join(x.strip('/') for x in paths if x)
query = next((x for x in queries if x), '')
fragment = next((x for x in fragments if x), '')
return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
def urlclean(url):
#remove double slashes
# remove double slashes
ret = urlparse.urljoin(url, urlparse.urlparse(url).path.replace('//', '/'))
return ret
@@ -90,11 +92,13 @@ def _slug_strip(value, separator='-'):
value = re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value)
return value
def is_absolute(url):
return bool(urlparse.urlparse(url).scheme)
def wrap_full(url):
if not is_absolute(url):
url = "http://%s%s" % (Site.objects.get_current().domain, url)
return url
return url

View File

@@ -0,0 +1,2 @@
from __future__ import absolute_import
from .celeryconf import app as celery_app

19
dss/celeryconf.py Normal file
View File

@@ -0,0 +1,19 @@
from __future__ import absolute_import
import os
import logging
from celery import Celery
logger = logging.getLogger('dss')
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dss.settings')
from django.conf import settings
app = Celery('dss')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

View File

@@ -1,4 +1,5 @@
import os
import sys
from dss import localsettings
if os.name == 'posix':
@@ -20,11 +21,14 @@ LOGGING = {
},
'handlers': {
'file': {
'level': 'DEBUG',
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': LOG_FILE,
'formatter': 'verbose'
},
}, 'console': {
'class': 'logging.StreamHandler',
'stream': sys.stdout,
}
},
'loggers': {
'django': {
@@ -33,7 +37,7 @@ LOGGING = {
'level': 'DEBUG',
},
'spa': {
'handlers': ['file'],
'handlers': ['file', 'console'],
'level': 'DEBUG',
},
}

View File

@@ -1,8 +1,8 @@
# e Django settings for dss project.
import os
import mimetypes
from datetime import timedelta
from django.core.urlresolvers import reverse_lazy
import djcelery
from django.conf import global_settings
from utils import here
@@ -29,7 +29,6 @@ DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': DATABASE_NAME,
'ADMINUSER': 'postgres',
'USER': DATABASE_USER,
'PASSWORD': DATABASE_PASSWORD,
'HOST': DATABASE_HOST,
@@ -38,7 +37,6 @@ DATABASES = {
import sys
if 'test' in sys.argv or 'test_coverage' in sys.argv:
print "Testing"
DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'
ROOT_URLCONF = 'dss.urls'
@@ -70,19 +68,13 @@ TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'django.core.context_processors.media',
'django.core.context_processors.static',
'django.contrib.auth.context_processors.auth',
# TODO: remove..
# `allauth` specific context processors
"allauth.account.context_processors.account",
"allauth.socialaccount.context_processors.socialaccount",
)
MIDDLEWARE_CLASSES = (
'django.middleware.gzip.GZipMiddleware',
'django.middleware.common.CommonMiddleware',
'user_sessions.middleware.SessionMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
@@ -104,7 +96,7 @@ INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'user_sessions',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
@@ -112,17 +104,16 @@ INSTALLED_APPS = (
#'django_facebook',
'django_extensions',
'django_gravatar',
'djcelery',
'corsheaders',
'sorl.thumbnail',
'djcelery',
'spa',
'tinymce',
'gunicorn',
'spa.signals',
'core',
#'schedule',
'django_user_agents',
'storages',
'social.apps.django_app.default',
# TODO: remove
@@ -138,7 +129,6 @@ INSTALLED_APPS = (
'djrill',
'rest_framework',
'rest_framework.authtoken',
'rest_framework_swagger',
)
# where to redirect users to after logging in
@@ -147,7 +137,6 @@ LOGOUT_URL = reverse_lazy('home')
FACEBOOK_APP_ID = '154504534677009'
djcelery.setup_loader()
AVATAR_STORAGE_DIR = MEDIA_ROOT + '/avatars/'
ACCOUNT_LOGOUT_REDIRECT_URL = '/'
@@ -160,7 +149,7 @@ TASTYPIE_ALLOW_MISSING_SLASH = True
SENDFILE_ROOT = os.path.join(MEDIA_ROOT, 'mixes')
SENDFILE_URL = '/media/mixes'
SESSION_ENGINE = 'user_sessions.backends.db'
#SESSION_ENGINE = 'user_sessions.backends.db'
mimetypes.add_type("text/xml", ".plist", False)
@@ -216,4 +205,12 @@ DEFAULT_USER_NAME = 'Anonymouse'
DEFAULT_USER_TITLE = 'Just another DSS lover'
SITE_NAME = 'Deep South Sounds'
THUMBNAIL_PREFIX = 'cache/_tn/'
THUMBNAIL_PREFIX = '_tn/'
# THUMBNAIL_STORAGE = 'storages.backends.azure_storage.AzureStorage'
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': timedelta(seconds=900),
'JWT_ALLOW_REFRESH': True,
'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=30),
}

View File

@@ -9,13 +9,13 @@ admin.autodiscover()
urlpatterns = patterns(
'',
url(r'^admin/', include(admin.site.urls)),
url(r'^api/docs/', include('rest_framework_swagger.urls')),
url(r'^api/v2/', include('api.urls')),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
(r'^grappelli/', include('grappelli.urls')),
(r'^social/', include('spa.social.urls')),
(r'^arges/', include('spa.social.urls')),
url(r'', include('user_sessions.urls', 'user_sessions')),
url(r'^', include('api.urls')),
)
handler500 = 'spa.views.debug_500'
if settings.DEBUG:
from django.views.static import serve

View File

@@ -1,28 +1,6 @@
"""
WSGI config for dss project.
This module contains the WSGI application used by Django's development server
and any production WSGI deployments. It should expose a module-level variable
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
this application via the ``WSGI_APPLICATION`` setting.
Usually you will have the standard Django WSGI application here, but it also
might make sense to replace the whole Django WSGI application with a custom one
that later delegates to the Django one. For example, you could introduce WSGI
middleware here, or combine a Django application with an application of another
framework.
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dss.settings")
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
# Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application)

View File

@@ -1,4 +1,4 @@
Django>=1.6,<1.7
Django==1.6.11
django-extensions
django-sendfile
Werkzeug
@@ -9,14 +9,15 @@ django-dirtyfields
django-storages
django-user-sessions
django-cors-headers
django-rest-swagger
six==1.6.0
django-filter
django-grappelli
django-grappelli==2.5.7
django-model_utils
django-dbbackup
django-user-agents
south
redis
django-celery
sorl-thumbnail
@@ -26,15 +27,15 @@ git+git://github.com/tschellenbach/Django-facebook.git#django-facebook
git+git://github.com/llazzaro/django-scheduler.git#django-scheduler
git+git://github.com/omab/python-social-auth.git#egg=python-social-auth
django-allauth
django-tinymce
apache-libcloud
mandrill
djrill
djangorestframework
djangorestframework-jwt
celery
djangorestframework==3.1.3
djangorestframework-jwt==1.6.0
drf-nested-routers
django-celery
pillow
django-gravatar2
@@ -43,4 +44,4 @@ mutagen
django-enumfield
ipython
ipdb
ipdb

2
run_celery.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
su -m djworker -c "python manage.py celeryd"

2
run_web.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
su -m djworker -c "python manage.py runserver_plus 0.0.0.0:8000"

View File

@@ -1,21 +1,19 @@
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db.models import Count, Q, F
from django.db.models import Count, Q
from django.conf.urls import url
from tastypie import fields
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from django.conf.urls import url
from tastypie.constants import ALL, ALL_WITH_RELATIONS
from tastypie.http import HttpGone, HttpMultipleChoices
from tastypie.utils import trailing_slash
from tastypie_msgpack import Serializer
from dss import settings
from spa.api.v1.BaseResource import BaseResource
from spa.api.v1.PlaylistResource import PlaylistResource
from spa.models.basemodel import BaseModel
from spa.models.userprofile import UserProfile
from spa.models.mix import Mix
from core.tasks import update_geo_info_task
class UserResource(BaseResource):

View File

@@ -1,9 +1,33 @@
from django.core.management.base import NoArgsCommand, BaseCommand
import os
from django.core.management.base import BaseCommand
from core.utils.cdn import upload_to_azure
from core.utils import cdn
from spa.models import Mix
def _update_azure_headers():
ms = Mix.objects.all()
for m in ms:
cdn.set_azure_details('{0}.mp3'.format(m.uid), 'Deep South Sounds - {0}'.format(m.title), 'mixes')
def _check_missing_mixes():
ms = Mix.objects.all()
found = 0
for m in ms:
url = m.get_download_url()
if not cdn.file_exists(url):
file = '/mnt/dev/working/Dropbox/Development/deepsouthsounds.com/media/mixes/{0}.mp3'.format(m.uid)
if os.path.isfile(file):
print '* {0}'.format(file)
#cdn.upload_file_to_azure(file, '{0}.mp3'.format(m.uid), 'mixes')
found += 1
else:
found += 1
print '({0}){1} - {2}'.format(found, m.slug, m.uid)
print '{0} of {1} missing'.format(found, Mix.objects.count())
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
@@ -18,7 +42,7 @@ class Command(BaseCommand):
mixes = Mix.objects.filter(archive_updated=False)
for mix in mixes:
blob_name, download_name = mix.get_cdn_details()
upload_to_azure(blob_name, "mp3", download_name)
cdn.upload_file_to_azure(blob_name, "mp3", download_name)
mix.archive_updated = True
mix.save()
@@ -26,4 +50,11 @@ class Command(BaseCommand):
print "Fatal error, bailing. {0}".format(ex.message)
def handle(self, *args, **options):
pass
if len(args) == 0:
print "Commands are \n\t_check_missing_mixes"
elif args[0] == 'check_missing_mix':
_check_missing_mixes()
elif args[0] == 'update_azure_headers':
_update_azure_headers()
else:
print "Commands are \n\tcheck_missing_mix"

View File

@@ -0,0 +1,15 @@
from django.core.management.base import NoArgsCommand
from spa import models
class Command(NoArgsCommand):
def handle_noargs(self, **options):
try:
models.Notification.objects.all().delete()
act = models.Activity.objects.all().order_by('-id').select_subclasses()
for a in act:
print "Creating for: {0}".format(a)
a.create_notification(accept=True)
except Exception, ex:
print "Debug exception: %s" % ex.message

View File

@@ -1,21 +1,42 @@
import urllib2
from allauth.socialaccount.models import SocialAccount
from django.core.files.base import ContentFile
from azure.storage import BlobService
from django.core.files.base import File
from django.core.files.temp import NamedTemporaryFile
from django.core.management.base import NoArgsCommand
from requests import request, ConnectionError
from dss import storagesettings
from spa.models.userprofile import UserProfile
def save_image(profile, url):
img = NamedTemporaryFile(delete=True)
img.write(urllib2.urlopen(url).read())
img.flush()
profile.avatar_image.save(str(profile.id), File(img))
def save_image_to_azure(profile, url):
try:
response = request('GET', url)
response.raise_for_status()
except ConnectionError:
pass
else:
profile.avatar_image.save(u'',
ContentFile(response.content),
save=False)
profile.save()
service = BlobService(
account_name=storagesettings.AZURE_ACCOUNT_NAME,
account_key=storagesettings.AZURE_ACCOUNT_KEY)
service.put_block_blob_from_bytes(
'avatars',
profile.id,
response.content,
x_ms_blob_content_type=response.headers['content-type']
)
class Command(NoArgsCommand):
@@ -31,6 +52,7 @@ class Command(NoArgsCommand):
if provider_account:
avatar_url = provider_account.get_avatar_url()
save_image(user, avatar_url)
user.save()
except Exception, ex:
print ex.message
else:

View File

@@ -0,0 +1,22 @@
import os
from django.core.management.base import NoArgsCommand
from core.utils import cdn
from spa.models import Mix
class Command(NoArgsCommand):
def handle_noargs(self, **options):
try:
print 'Enumerating items'
items = cdn.enumerate_objects('mixes')
for item in items:
# Check if we have a corresponding mix
uid, type = os.path.splitext(item)
try:
Mix.objects.get(uid=uid)
except Mix.DoesNotExist:
# no mix found - delete the blob
cdn.delete_object('mixes', item)
print "Deleting blob: {0}".format(uid)
except Exception, ex:
print "Debug exception: %s" % ex.message

View File

@@ -1,10 +1,11 @@
from optparse import make_option
import os
from django.core.management.base import NoArgsCommand, BaseCommand
from spa.management.commands import helpers
from django.core.management.base import BaseCommand
from spa.management.commands import helpers
from spa.models.mix import Mix
from core.tasks import create_waveform_task
from spa.tasks import create_waveform_task
class Command(BaseCommand):
@@ -40,7 +41,7 @@ class Command(BaseCommand):
return processed_file
except Exception, ex:
print "Error generating waveform: %s" % ex.message
print "Error generating waveform: {0}".format(ex.message)
return ""

View File

@@ -0,0 +1,305 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'Notification.notification_text'
db.delete_column(u'spa_notification', 'notification_text')
# Deleting field 'Notification.notification_html'
db.delete_column(u'spa_notification', 'notification_html')
# Deleting field 'Notification.notification_url'
db.delete_column(u'spa_notification', 'notification_url')
# Adding field 'Notification.type'
db.add_column(u'spa_notification', 'type',
self.gf('django.db.models.fields.CharField')(max_length=200, null=True),
keep_default=False)
def backwards(self, orm):
# User chose to not deal with backwards NULL issues for 'Notification.notification_text'
raise RuntimeError("Cannot reverse this migration. 'Notification.notification_text' and its values cannot be restored.")
# The following code is provided here to aid in writing a correct migration # Adding field 'Notification.notification_text'
db.add_column(u'spa_notification', 'notification_text',
self.gf('django.db.models.fields.CharField')(max_length=1024),
keep_default=False)
# User chose to not deal with backwards NULL issues for 'Notification.notification_html'
raise RuntimeError("Cannot reverse this migration. 'Notification.notification_html' and its values cannot be restored.")
# The following code is provided here to aid in writing a correct migration # Adding field 'Notification.notification_html'
db.add_column(u'spa_notification', 'notification_html',
self.gf('django.db.models.fields.CharField')(max_length=1024),
keep_default=False)
# Adding field 'Notification.notification_url'
db.add_column(u'spa_notification', 'notification_url',
self.gf('django.db.models.fields.URLField')(max_length=200, null=True),
keep_default=False)
# Deleting field 'Notification.type'
db.delete_column(u'spa_notification', 'type')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'spa._lookup': {
'Meta': {'object_name': '_Lookup'},
'description': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'})
},
'spa.activity': {
'Meta': {'object_name': 'Activity'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.UserProfile']", 'null': 'True', 'blank': 'True'})
},
'spa.activitycomment': {
'Meta': {'object_name': 'ActivityComment', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_comments'", 'to': "orm['spa.Mix']"})
},
'spa.activitydownload': {
'Meta': {'object_name': 'ActivityDownload', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_downloads'", 'to': "orm['spa.Mix']"})
},
'spa.activityfavourite': {
'Meta': {'object_name': 'ActivityFavourite', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_favourites'", 'to': "orm['spa.Mix']"})
},
'spa.activityfollow': {
'Meta': {'object_name': 'ActivityFollow', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'to_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_follow'", 'to': "orm['spa.UserProfile']"})
},
'spa.activitylike': {
'Meta': {'object_name': 'ActivityLike', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_likes'", 'to': "orm['spa.Mix']"})
},
'spa.activityplay': {
'Meta': {'object_name': 'ActivityPlay', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_plays'", 'to': "orm['spa.Mix']"})
},
'spa.chatmessage': {
'Meta': {'object_name': 'ChatMessage'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.TextField', [], {}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'chat_messages'", 'null': 'True', 'to': "orm['spa.UserProfile']"})
},
'spa.comment': {
'Meta': {'object_name': 'Comment'},
'comment': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'likes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'liked_comments'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['spa.Mix']"}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'time_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
'spa.genre': {
'Meta': {'object_name': 'Genre'},
'description': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
},
'spa.label': {
'Meta': {'object_name': 'Label'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'})
},
'spa.mix': {
'Meta': {'ordering': "('-id',)", 'object_name': 'Mix'},
'archive_path': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
'archive_updated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'description': ('django.db.models.fields.TextField', [], {}),
'download_allowed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'duration': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'favourites': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'favourites'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
'filetype': ('django.db.models.fields.CharField', [], {'default': "'mp3'", 'max_length': '10'}),
'genres': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['spa.Genre']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_featured': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'likes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'likes'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
'mix_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '1024', 'blank': 'True'}),
'mp3tags_updated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
'uid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '38', 'blank': 'True'}),
'upload_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mixes'", 'to': "orm['spa.UserProfile']"}),
'waveform_generated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'waveform_version': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'spa.notification': {
'Meta': {'ordering': "('-id',)", 'object_name': 'Notification'},
'accepted_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'from_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notifications'", 'null': 'True', 'to': "orm['spa.UserProfile']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'target': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
'to_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'to_notications'", 'to': "orm['spa.UserProfile']"}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'})
},
'spa.playlist': {
'Meta': {'object_name': 'Playlist'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mixes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['spa.Mix']", 'symmetrical': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'playlists'", 'to': "orm['spa.UserProfile']"})
},
'spa.purchaselink': {
'Meta': {'object_name': 'PurchaseLink'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'track': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'purchase_link'", 'to': "orm['spa.Tracklist']"}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
},
'spa.recurrence': {
'Meta': {'object_name': 'Recurrence', '_ormbases': ['spa._Lookup']},
u'_lookup_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa._Lookup']", 'unique': 'True', 'primary_key': 'True'})
},
'spa.release': {
'Meta': {'object_name': 'Release'},
'embed_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'release_artist': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'release_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)'}),
'release_description': ('django.db.models.fields.TextField', [], {}),
'release_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'release_label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.Label']"}),
'release_title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.UserProfile']"})
},
'spa.releaseaudio': {
'Meta': {'object_name': 'ReleaseAudio'},
'description': ('django.db.models.fields.TextField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'release': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'release_audio'", 'null': 'True', 'to': "orm['spa.Release']"})
},
'spa.tracklist': {
'Meta': {'object_name': 'Tracklist'},
'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'index': ('django.db.models.fields.SmallIntegerField', [], {}),
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tracklist'", 'to': "orm['spa.Mix']"}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'remixer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'timeindex': ('django.db.models.fields.TimeField', [], {'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'spa.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'activity_sharing_facebook': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
'activity_sharing_networks': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'activity_sharing_twitter': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
'avatar_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '1024', 'blank': 'True'}),
'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'social'", 'max_length': '15'}),
'city': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'country': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '35', 'blank': 'True'}),
'email_notifications': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
'following': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'followers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_known_session': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'default': 'None', 'max_length': '50', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': u"orm['auth.User']"})
},
'spa.venue': {
'Meta': {'object_name': 'Venue'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'venue_address': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'venue_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '250'})
}
}
complete_apps = ['spa']

View File

@@ -0,0 +1,274 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Notification.target_desc'
db.add_column(u'spa_notification', 'target_desc',
self.gf('django.db.models.fields.CharField')(max_length=200, null=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Notification.target_desc'
db.delete_column(u'spa_notification', 'target_desc')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'spa._lookup': {
'Meta': {'object_name': '_Lookup'},
'description': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'})
},
'spa.activity': {
'Meta': {'object_name': 'Activity'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.UserProfile']", 'null': 'True', 'blank': 'True'})
},
'spa.activitycomment': {
'Meta': {'object_name': 'ActivityComment', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_comments'", 'to': "orm['spa.Mix']"})
},
'spa.activitydownload': {
'Meta': {'object_name': 'ActivityDownload', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_downloads'", 'to': "orm['spa.Mix']"})
},
'spa.activityfavourite': {
'Meta': {'object_name': 'ActivityFavourite', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_favourites'", 'to': "orm['spa.Mix']"})
},
'spa.activityfollow': {
'Meta': {'object_name': 'ActivityFollow', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'to_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_follow'", 'to': "orm['spa.UserProfile']"})
},
'spa.activitylike': {
'Meta': {'object_name': 'ActivityLike', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_likes'", 'to': "orm['spa.Mix']"})
},
'spa.activityplay': {
'Meta': {'object_name': 'ActivityPlay', '_ormbases': ['spa.Activity']},
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_plays'", 'to': "orm['spa.Mix']"})
},
'spa.chatmessage': {
'Meta': {'object_name': 'ChatMessage'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.TextField', [], {}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'chat_messages'", 'null': 'True', 'to': "orm['spa.UserProfile']"})
},
'spa.comment': {
'Meta': {'object_name': 'Comment'},
'comment': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'likes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'liked_comments'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['spa.Mix']"}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'time_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
'spa.genre': {
'Meta': {'object_name': 'Genre'},
'description': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
},
'spa.label': {
'Meta': {'object_name': 'Label'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'})
},
'spa.mix': {
'Meta': {'ordering': "('-id',)", 'object_name': 'Mix'},
'archive_path': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
'archive_updated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'description': ('django.db.models.fields.TextField', [], {}),
'download_allowed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'duration': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'favourites': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'favourites'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
'filetype': ('django.db.models.fields.CharField', [], {'default': "'mp3'", 'max_length': '10'}),
'genres': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['spa.Genre']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_featured': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'likes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'likes'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
'mix_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '1024', 'blank': 'True'}),
'mp3tags_updated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
'uid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '38', 'blank': 'True'}),
'upload_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mixes'", 'to': "orm['spa.UserProfile']"}),
'waveform_generated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'waveform_version': ('django.db.models.fields.IntegerField', [], {'default': '1'})
},
'spa.notification': {
'Meta': {'ordering': "('-id',)", 'object_name': 'Notification'},
'accepted_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'from_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notifications'", 'null': 'True', 'to': "orm['spa.UserProfile']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'target': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
'target_desc': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
'to_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'to_notications'", 'to': "orm['spa.UserProfile']"}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
'verb': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'})
},
'spa.playlist': {
'Meta': {'object_name': 'Playlist'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mixes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['spa.Mix']", 'symmetrical': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'playlists'", 'to': "orm['spa.UserProfile']"})
},
'spa.purchaselink': {
'Meta': {'object_name': 'PurchaseLink'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'track': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'purchase_link'", 'to': "orm['spa.Tracklist']"}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
},
'spa.recurrence': {
'Meta': {'object_name': 'Recurrence', '_ormbases': ['spa._Lookup']},
u'_lookup_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa._Lookup']", 'unique': 'True', 'primary_key': 'True'})
},
'spa.release': {
'Meta': {'object_name': 'Release'},
'embed_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'release_artist': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'release_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)'}),
'release_description': ('django.db.models.fields.TextField', [], {}),
'release_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'release_label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.Label']"}),
'release_title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.UserProfile']"})
},
'spa.releaseaudio': {
'Meta': {'object_name': 'ReleaseAudio'},
'description': ('django.db.models.fields.TextField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'release': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'release_audio'", 'null': 'True', 'to': "orm['spa.Release']"})
},
'spa.tracklist': {
'Meta': {'object_name': 'Tracklist'},
'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'index': ('django.db.models.fields.SmallIntegerField', [], {}),
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tracklist'", 'to': "orm['spa.Mix']"}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'remixer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'timeindex': ('django.db.models.fields.TimeField', [], {'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'spa.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'activity_sharing_facebook': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
'activity_sharing_networks': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'activity_sharing_twitter': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
'avatar_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '1024', 'blank': 'True'}),
'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'social'", 'max_length': '15'}),
'city': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'country': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '35', 'blank': 'True'}),
'email_notifications': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
'following': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'followers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_known_session': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'default': 'None', 'max_length': '50', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': u"orm['auth.User']"})
},
'spa.venue': {
'Meta': {'object_name': 'Venue'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'object_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 8, 12, 0, 0)', 'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'venue_address': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'venue_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '250'})
}
}
complete_apps = ['spa']

View File

@@ -1,11 +1,9 @@
import abc
from django.contrib.auth.models import AnonymousUser, User
from datetime import datetime
from allauth.socialaccount.models import SocialToken
from datetime import datetime
from django.db import models
from model_utils.managers import InheritanceManager
from open_facebook import OpenFacebook
from core.utils.url import wrap_full
@@ -62,27 +60,20 @@ class Activity(BaseModel):
print ex.message
pass
def create_notification(self):
def create_notification(self, accept=False):
try:
notification = Notification()
notification.from_user = self.user
notification.to_user = self.get_target_user()
notification.notification_text = "%s %s %s" % (
self.user.get_nice_name() if self.user is not None else "Anonymouse",
self.get_verb_past(),
self.get_object_name_for_notification())
notification.notification_html = "<a href='%s'>%s</a> %s <a href='%s'>%s</a>" % (
wrap_full(self.user.get_profile_url() if self.user is not None else ""),
self.user.get_nice_name() if self.user is not None else "Anonymouse",
self.get_verb_past(),
wrap_full(self.get_object_url()),
self.get_object_name_for_notification()
)
notification.notification_url = self.get_object_url()
notification.verb = self.get_verb_past()
notification.target = self.get_object_name()
notification.type = self.get_object_type()
notification.target = self.get_object_slug()
notification.target_desc = self.get_object_name()
if accept:
notification.accepted_date = datetime.now()
notification.save()
except Exception, ex:
print "Error creating activity notification: %s" % ex.message

View File

@@ -10,7 +10,7 @@ from dss import localsettings, settings
class BaseModel(models.Model):
logger = logging.getLogger(__name__)
logger = logging.getLogger('dss')
object_created = models.DateTimeField(auto_now_add=True, default=datetime.now())
object_updated = models.DateTimeField(auto_now=True, default=datetime.now(), db_index=True)
@@ -33,11 +33,8 @@ class BaseModel(models.Model):
def get_image_url(self, image, default):
try:
if os.path.isfile(image.path):
images_root = localsettings.IMAGE_URL if hasattr(localsettings,
'IMAGE_URL') else "%s" % settings.MEDIA_URL
ret = "%s/%s/%s" % (settings.STATIC_URL, images_root, image)
ret = "{0}/{1}".format(settings.MEDIA_URL, image)
return url.urlclean(ret)
except Exception, ex:
pass

View File

@@ -102,4 +102,6 @@ try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ['^spa\.models.fields\.MultiSelectField'])
except ImportError:
pass
pass

View File

@@ -1,8 +1,7 @@
import os
import rfc822
import urlparse
from django.utils.encoding import smart_str
import os
from django.utils.encoding import smart_str
from sorl.thumbnail import get_thumbnail
from django.contrib.sites.models import Site
from django.db import models
@@ -19,8 +18,7 @@ from dss import settings, localsettings
from spa.models.userprofile import UserProfile
from spa.models.basemodel import BaseModel
from core.utils.file import generate_save_file_name
from PIL import Image
import glob
from core.utils import cdn
class Engine(Engine):
@@ -104,7 +102,7 @@ class Mix(BaseModel):
self.clean_image('mix_image', Mix)
# Check for the unlikely event that the waveform has been generated
if os.path.isfile(self.get_waveform_path()):
if cdn.file_exists('{0}{1}.png'.format(localsettings.WAVEFORM_URL, self.uid)):
self.waveform_generated = True
try:
self.duration = mp3_length(self.get_absolute_path())
@@ -170,7 +168,7 @@ class Mix(BaseModel):
try:
if self.mix_image.name and self.mix_image.storage.exists(self.mix_image.name):
ret = get_thumbnail(self.mix_image, size, crop='center')
return "%s/%s" % (settings.MEDIA_URL, ret.name)
return url.urlclean("%s/%s" % (settings.MEDIA_URL, ret.name))
else:
return self.user.get_sized_avatar_image(170, 170)
except Exception, ex:

View File

@@ -13,12 +13,10 @@ class Notification(BaseModel):
from_user = models.ForeignKey('spa.UserProfile', related_name='notifications', null=True, blank=True)
date = models.DateTimeField(auto_now_add=True)
notification_text = models.CharField(max_length=1024)
notification_html = models.CharField(max_length=1024)
notification_url = models.URLField(null=True)
verb = models.CharField(max_length=200, null=True)
type = models.CharField(max_length=200, null=True)
target = models.CharField(max_length=200, null=True)
target_desc = models.CharField(max_length=200, null=True)
accepted_date = models.DateTimeField(null=True)
@@ -29,8 +27,7 @@ class Notification(BaseModel):
def get_notification_url(self):
return '/api/v1/notification/%s' % self.id
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
def __save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if self._activity.should_send_email():
self.send_notification_email()
@@ -81,3 +78,6 @@ class Notification(BaseModel):
except mandrill.Error, e: # Mandrill errors are thrown as exceptions
print 'A mandrill error occurred: %s - %s' % (e.__class__, e)
def get_from_user(self):
return UserProfile.get_user(self.from_user)

7
spa/models/session.py Normal file
View File

@@ -0,0 +1,7 @@
from django.db import models
from spa.models import BaseModel, UserProfile
class Session(BaseModel):
jwt_token = models.CharField(max_length=2048)
user = models.ForeignKey(UserProfile)

View File

@@ -139,6 +139,9 @@ class UserProfile(BaseModel):
except Exception, e:
self.logger.error("Unable to create profile slug: %s", e.message)
def get_session_id(self):
return str(self.id)
def toggle_favourite(self, mix, value):
try:
if value:

View File

@@ -4,6 +4,7 @@ from django.db.models.signals import post_save, pre_save, m2m_changed
from django.dispatch import Signal, receiver
from django.contrib.auth.models import User
from core.realtime import activity
from core.utils.audio.mp3 import mp3_length
from spa.models.activity import ActivityFollow
@@ -18,11 +19,12 @@ def _waveform_generated_callback(sender, **kwargs):
print "Updating model with waveform"
try:
uid = kwargs['uid']
path = kwargs['path']
if uid is not None:
mix = Mix.objects.get(uid=uid)
if mix is not None:
mix.waveform_generated = True
mix.duration = mp3_length(mix.get_absolute_path())
mix.duration = mp3_length(path)
mix.save(update_fields=["waveform_generated", "duration"])
except ObjectDoesNotExist:

62
spa/tasks.py Executable file
View File

@@ -0,0 +1,62 @@
from celery.task import task
import os
import logging
from core.realtime import activity
from core.utils import cdn
from spa.signals import waveform_generated_signal
try:
from django.contrib.gis.geoip import GeoIP
except ImportError:
pass
from core.utils.waveform import generate_waveform
from dss import settings
logger = logging.getLogger('dss')
@task(time_limit=3600)
def create_waveform_task(in_file, uid):
out_file = os.path.join(settings.CACHE_ROOT, 'waveforms/%s.png' % uid)
logger.info("Creating waveform \n\tIn: %s\n\tOut: %s" % (in_file, out_file))
generate_waveform(in_file, out_file)
if os.path.isfile(out_file):
logger.info("Waveform generated successfully")
waveform_generated_signal.send(sender=None, uid=uid, path=in_file)
return out_file
else:
logger.error("Outfile is missing")
@task(timse_limit=3600)
def upload_to_cdn_task(filetype, uid, container_name):
source_file = os.path.join(settings.CACHE_ROOT, '{0}/{1}.{2}'.format(container_name, uid, filetype))
logger.info("Sending {0} to azure".format(uid))
try:
file_name = "{0}.{1}".format(uid, filetype)
cdn.upload_file_to_azure(source_file, file_name, container_name)
return source_file
except Exception, ex:
logger.error("Unable to upload: {0}".format(ex.message))
@task
def update_geo_info_task(ip_address, profile_id):
try:
ip = '188.141.70.110' if ip_address == '127.0.0.1' else ip_address
if ip:
g = GeoIP()
city = g.city(ip)
country = g.country(ip)
print "Updated user location"
except Exception, e:
logger.exception(e)
pass
@task
def notify_subscriber(session_id, uid):
if session_id is not None:
activity.post_activity('user:message', session_id, {'type': 'waveform', 'target': uid})