diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..b5db2af
--- /dev/null
+++ b/Dockerfile
@@ -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/
\ No newline at end of file
diff --git a/api/auth.py b/api/auth.py
index 201ce3a..df3d5b7 100644
--- a/api/auth.py
+++ b/api/auth.py
@@ -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)
diff --git a/api/helpers.py b/api/helpers.py
index 32b3e32..e3e50d5 100644
--- a/api/helpers.py
+++ b/api/helpers.py
@@ -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)
diff --git a/api/serializers.py b/api/serializers.py
index e94b610..b51fc02 100755
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -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):
diff --git a/api/urls.py b/api/urls.py
index 043c535..75701bf 100755
--- a/api/urls.py
+++ b/api/urls.py
@@ -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')),
)
diff --git a/api/views.py b/api/views.py
index 12948a5..01b8038 100755
--- a/api/views.py
+++ b/api/views.py
@@ -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):
diff --git a/bin/wav2png b/bin/wav2png
new file mode 100755
index 0000000..3caced9
Binary files /dev/null and b/bin/wav2png differ
diff --git a/core/realtime/activity.py b/core/realtime/activity.py
index 1ed2ef0..3af0813 100755
--- a/core/realtime/activity.py
+++ b/core/realtime/activity.py
@@ -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)
diff --git a/core/realtime/chat.py b/core/realtime/chat.py
index e981a5e..fffefe1 100644
--- a/core/realtime/chat.py
+++ b/core/realtime/chat.py
@@ -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)
diff --git a/core/tasks.py b/core/tasks.py
deleted file mode 100755
index 89a37e2..0000000
--- a/core/tasks.py
+++ /dev/null
@@ -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
diff --git a/core/utils/cdn.py b/core/utils/cdn.py
index 09f5a80..5e51b1b 100755
--- a/core/utils/cdn.py
+++ b/core/utils/cdn.py
@@ -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)
\ No newline at end of file
diff --git a/core/utils/url.py b/core/utils/url.py
index 3583a9f..4c59ab5 100755
--- a/core/utils/url.py
+++ b/core/utils/url.py
@@ -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
\ No newline at end of file
+ return url
diff --git a/dss/__init__.py b/dss/__init__.py
index e69de29..d1956f9 100755
--- a/dss/__init__.py
+++ b/dss/__init__.py
@@ -0,0 +1,2 @@
+from __future__ import absolute_import
+from .celeryconf import app as celery_app
diff --git a/dss/celeryconf.py b/dss/celeryconf.py
new file mode 100644
index 0000000..198334e
--- /dev/null
+++ b/dss/celeryconf.py
@@ -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)
diff --git a/dss/logsettings.py b/dss/logsettings.py
index e7d88c2..a98888a 100755
--- a/dss/logsettings.py
+++ b/dss/logsettings.py
@@ -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',
},
}
diff --git a/dss/settings.py b/dss/settings.py
index 605277d..3caa6ea 100755
--- a/dss/settings.py
+++ b/dss/settings.py
@@ -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),
+}
diff --git a/dss/urls.py b/dss/urls.py
index 61c8f58..78fdc2f 100755
--- a/dss/urls.py
+++ b/dss/urls.py
@@ -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
diff --git a/dss/wsgi.py b/dss/wsgi.py
index 8765705..c998099 100755
--- a/dss/wsgi.py
+++ b/dss/wsgi.py
@@ -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)
diff --git a/requirements.txt b/requirements.txt
index c8d12d2..68682e5 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -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
\ No newline at end of file
+ipdb
diff --git a/run_celery.sh b/run_celery.sh
new file mode 100755
index 0000000..fe41a9e
--- /dev/null
+++ b/run_celery.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+su -m djworker -c "python manage.py celeryd"
\ No newline at end of file
diff --git a/run_web.sh b/run_web.sh
new file mode 100755
index 0000000..45e0d8f
--- /dev/null
+++ b/run_web.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+su -m djworker -c "python manage.py runserver_plus 0.0.0.0:8000"
diff --git a/spa/api/v1/UserResource.py b/spa/api/v1/UserResource.py
index 16757f2..3e8f7ea 100755
--- a/spa/api/v1/UserResource.py
+++ b/spa/api/v1/UserResource.py
@@ -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):
diff --git a/spa/management/commands/azure_util.py b/spa/management/commands/azure_util.py
index d86f2b2..c310264 100755
--- a/spa/management/commands/azure_util.py
+++ b/spa/management/commands/azure_util.py
@@ -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"
diff --git a/spa/management/commands/create_notifications.py b/spa/management/commands/create_notifications.py
new file mode 100755
index 0000000..9584e90
--- /dev/null
+++ b/spa/management/commands/create_notifications.py
@@ -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
\ No newline at end of file
diff --git a/spa/management/commands/get_avatars.py b/spa/management/commands/get_avatars.py
index 2ff750d..66ed4e9 100755
--- a/spa/management/commands/get_avatars.py
+++ b/spa/management/commands/get_avatars.py
@@ -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:
diff --git a/spa/management/commands/tidy_cdn.py b/spa/management/commands/tidy_cdn.py
new file mode 100755
index 0000000..0e54b5f
--- /dev/null
+++ b/spa/management/commands/tidy_cdn.py
@@ -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
diff --git a/spa/management/commands/waveforms.py b/spa/management/commands/waveforms.py
index 8824b7c..3d90b59 100755
--- a/spa/management/commands/waveforms.py
+++ b/spa/management/commands/waveforms.py
@@ -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 ""
diff --git a/spa/migrations/0071_auto__del_field_notification_notification_text__del_field_notification.py b/spa/migrations/0071_auto__del_field_notification_notification_text__del_field_notification.py
new file mode 100644
index 0000000..7ecbaa3
--- /dev/null
+++ b/spa/migrations/0071_auto__del_field_notification_notification_text__del_field_notification.py
@@ -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']
\ No newline at end of file
diff --git a/spa/migrations/0072_auto__add_field_notification_target_desc.py b/spa/migrations/0072_auto__add_field_notification_target_desc.py
new file mode 100644
index 0000000..6dbd40d
--- /dev/null
+++ b/spa/migrations/0072_auto__add_field_notification_target_desc.py
@@ -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']
\ No newline at end of file
diff --git a/spa/models/activity.py b/spa/models/activity.py
index 0790fe2..c82e607 100755
--- a/spa/models/activity.py
+++ b/spa/models/activity.py
@@ -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 = "%s %s %s" % (
- 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
diff --git a/spa/models/basemodel.py b/spa/models/basemodel.py
index 7f15512..779b744 100755
--- a/spa/models/basemodel.py
+++ b/spa/models/basemodel.py
@@ -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
diff --git a/spa/models/fields.py b/spa/models/fields.py
index f44c3b2..27896e6 100755
--- a/spa/models/fields.py
+++ b/spa/models/fields.py
@@ -102,4 +102,6 @@ try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ['^spa\.models.fields\.MultiSelectField'])
except ImportError:
- pass
\ No newline at end of file
+ pass
+
+
diff --git a/spa/models/mix.py b/spa/models/mix.py
index 1ed76d6..f5a6201 100755
--- a/spa/models/mix.py
+++ b/spa/models/mix.py
@@ -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:
diff --git a/spa/models/notification.py b/spa/models/notification.py
index cb7c71d..2968c71 100755
--- a/spa/models/notification.py
+++ b/spa/models/notification.py
@@ -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)
diff --git a/spa/models/session.py b/spa/models/session.py
new file mode 100644
index 0000000..2d78ba5
--- /dev/null
+++ b/spa/models/session.py
@@ -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)
diff --git a/spa/models/userprofile.py b/spa/models/userprofile.py
index f3f2aa5..79b9d14 100755
--- a/spa/models/userprofile.py
+++ b/spa/models/userprofile.py
@@ -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:
diff --git a/spa/signals.py b/spa/signals.py
index 5c65bba..1390a55 100755
--- a/spa/signals.py
+++ b/spa/signals.py
@@ -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:
diff --git a/spa/tasks.py b/spa/tasks.py
new file mode 100755
index 0000000..10dbd05
--- /dev/null
+++ b/spa/tasks.py
@@ -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})