From 51de2fb91ca4739c1db33c4afae0c96c1b8494a2 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Wed, 2 Dec 2015 22:56:22 +0000 Subject: [PATCH 01/26] Fixed image sizing and display name --- api/serializers.py | 26 ++++++++++++++++++++------ spa/models/mix.py | 2 +- spa/models/userprofile.py | 1 + 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index dd51523..4c0f359 100755 --- a/api/serializers.py +++ b/api/serializers.py @@ -50,7 +50,7 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): first_name = serializers.ReadOnlyField(source='get_first_name') last_name = serializers.ReadOnlyField(source='get_last_name') - display_name = serializers.ReadOnlyField(source='get_nice_name') + display_name = serializers.ReadOnlyField(source='get_display_name') def get_avatar_image(self, obj): return obj.get_sized_avatar_image(64, 64) @@ -75,11 +75,17 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): return obj.get_sized_avatar_image(64, 64) def get_profile_image_medium(self, obj): - return obj.get_sized_avatar_image(170, 170) + return obj.get_sized_avatar_image(253, 157) def get_profile_image_header(self, obj): return obj.get_sized_avatar_image(1200, 150) + def get_display_name(self, obj): + n = obj.get_nice_name() + if not n or n == "": + n = "%s %s".format(obj.first_name, obj.last_name) + + return n class LikeSerializer(serializers.ModelSerializer): class Meta: @@ -254,6 +260,7 @@ class UserProfileSerializer(serializers.ModelSerializer): profile_image_small = serializers.SerializerMethodField() profile_image_medium = serializers.SerializerMethodField() profile_image_header = serializers.SerializerMethodField() + display_name = serializers.SerializerMethodField(source='get_display_name') top_tags = serializers.SerializerMethodField() @@ -304,6 +311,13 @@ class UserProfileSerializer(serializers.ModelSerializer): pass return super(UserProfileSerializer, self).update(instance, validated_data) + def get_display_name(self, obj): + n = obj.get_nice_name() + if not n or n == "": + n = "%s %s".format(obj.first_name, obj.last_name) + + return n + def get_title(self, obj): try: if obj.description: @@ -346,10 +360,10 @@ class UserProfileSerializer(serializers.ModelSerializer): values('total', 'description', 'slug')[0:3]) def get_profile_image_small(self, obj): - return obj.get_sized_avatar_image(64, 64) + return obj.get_sized_avatar_image(32, 32) def get_profile_image_medium(self, obj): - return obj.get_sized_avatar_image(170, 170) + return obj.get_sized_avatar_image(253, 157) def get_profile_image_header(self, obj): return obj.get_sized_avatar_image(1200, 150) @@ -425,7 +439,7 @@ class HitlistSerializer(serializers.ModelSerializer): return obj.get_nice_name() def get_avatar_image(self, obj): - return obj.get_sized_avatar_image(170, 170) + return obj.get_sized_avatar_image(253, 157) class ActivitySerializer(serializers.HyperlinkedModelSerializer): @@ -475,7 +489,7 @@ class NotificationSerializer(serializers.HyperlinkedModelSerializer): return settings.DEFAULT_USER_NAME if obj.from_user is None else obj.from_user.get_nice_name() def get_avatar_image(self, obj): - return settings.DEFAULT_USER_IMAGE if obj.from_user is None else obj.from_user.get_sized_avatar_image(170, 170) + return settings.DEFAULT_USER_IMAGE if obj.from_user is None else obj.get_sized_avatar_image(253, 157) class MessageSerializer(serializers.ModelSerializer): diff --git a/spa/models/mix.py b/spa/models/mix.py index a220e81..8f4598a 100755 --- a/spa/models/mix.py +++ b/spa/models/mix.py @@ -176,7 +176,7 @@ class Mix(BaseModel): ret = get_thumbnail(self.mix_image, size, crop='center') return url.urlclean("%s/%s" % (settings.MEDIA_URL, ret.name)) else: - return self.user.get_sized_avatar_image(170, 170) + return self.user.get_sized_avatar_image(253, 157) except Exception as ex: pass diff --git a/spa/models/userprofile.py b/spa/models/userprofile.py index 8977fb3..f0e8590 100755 --- a/spa/models/userprofile.py +++ b/spa/models/userprofile.py @@ -174,6 +174,7 @@ class UserProfile(BaseModel): def get_sized_avatar_image(self, width, height): try: + #import ipdb; ipdb.set_trace() image = self.get_avatar_image() logger.debug("get_sized_avatar_image: %s".format(image)) sized = thumbnail.get_thumbnail(image, "%sx%s" % (width, height), crop="center") From ff4f36bb2e69ee0b9087caa723a6e9d50e86f542 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Wed, 2 Dec 2015 23:46:30 +0000 Subject: [PATCH 02/26] 32 --- api/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/serializers.py b/api/serializers.py index 4c0f359..8cef13f 100755 --- a/api/serializers.py +++ b/api/serializers.py @@ -72,7 +72,7 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): return obj.is_follower(self.context['request'].user) def get_profile_image_small(self, obj): - return obj.get_sized_avatar_image(64, 64) + return obj.get_sized_avatar_image(32, 32) def get_profile_image_medium(self, obj): return obj.get_sized_avatar_image(253, 157) From acefac646bd590fd62ffbc82fb397dcfecc2f7ce Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Wed, 2 Dec 2015 23:47:21 +0000 Subject: [PATCH 03/26] 32 --- api/serializers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 8cef13f..f57601a 100755 --- a/api/serializers.py +++ b/api/serializers.py @@ -53,10 +53,10 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): display_name = serializers.ReadOnlyField(source='get_display_name') def get_avatar_image(self, obj): - return obj.get_sized_avatar_image(64, 64) + return obj.get_sized_avatar_image(32, 32) def get_avatar_image_tiny(self, obj): - return obj.get_sized_avatar_image(64, 64) + return obj.get_sized_avatar_image(32, 32) def to_representation(self, instance): if instance.user.is_anonymous(): @@ -223,7 +223,7 @@ class MixSerializer(serializers.ModelSerializer): return super(MixSerializer, self).is_valid(raise_exception) def get_avatar_image(self, obj): - return obj.user.get_sized_avatar_image(64, 64) + return obj.user.get_sized_avatar_image(32, 32) def get_can_edit(self, obj): user = self.context['request'].user From 45be6bbd20c2759c7166885e00785e400f81916b Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Fri, 4 Dec 2015 20:22:50 +0000 Subject: [PATCH 04/26] Fixed headers --- core/utils/cdn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/utils/cdn.py b/core/utils/cdn.py index 9e62ff4..da6946f 100755 --- a/core/utils/cdn.py +++ b/core/utils/cdn.py @@ -26,7 +26,7 @@ def set_azure_details(blob_name, download_name, container_name=AZURE_CONTAINER): blob_service.set_blob_properties( container_name, blob_name, - x_ms_blob_content_type='application/octet-stream', + x_ms_blob_content_type='audio/mpeg', x_ms_blob_content_disposition='attachment;filename="{0}"'.format(download_name) ) print("Processed: %s" % download_name) From 04e4c34ad183d82ae7863f86f028d037ca1ddfc1 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Sun, 6 Dec 2015 17:09:38 +0000 Subject: [PATCH 05/26] Fixed mix tag --- Dockerfile | 2 +- core/utils/cdn.py | 2 +- dss/storagesettings.py | 2 +- spa/management/commands/azure_util.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 52f10e5..5b3dd9a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ WORKDIR /code ADD requirements.txt /code/ ADD . /code/ -RUN apt-get update && apt-get install -y sox lame vim ccze node npm \ +RUN apt-get update --fix-missing && apt-get install -y sox lame vim ccze node npm \ libboost-program-options-dev libsox-fmt-mp3 postgresql-client rsync openssh-client RUN npm install -g yuglify diff --git a/core/utils/cdn.py b/core/utils/cdn.py index da6946f..0553d62 100755 --- a/core/utils/cdn.py +++ b/core/utils/cdn.py @@ -61,4 +61,4 @@ def enumerate_objects(container): 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 + blob_service.delete_blob(container, name) diff --git a/dss/storagesettings.py b/dss/storagesettings.py index c5e283e..6d59c4c 100644 --- a/dss/storagesettings.py +++ b/dss/storagesettings.py @@ -3,7 +3,7 @@ import os print("Importing storage settings") -AZURE_ACCOUNT_NAME = os.environ.get('CDN_NAME', 'dsscdn') +AZURE_ACCOUNT_NAME = os.environ.get('CDN_NAME', 'dsscdn2') AZURE_CONTAINER = 'media' AZURE_ACCOUNT_KEY = localsettings.AZURE_ACCOUNT_KEY AZURE_ITEM_BASE_URL = 'https://{}.blob.core.windows.net/'.format(AZURE_ACCOUNT_NAME) diff --git a/spa/management/commands/azure_util.py b/spa/management/commands/azure_util.py index 71f466a..25a0f42 100755 --- a/spa/management/commands/azure_util.py +++ b/spa/management/commands/azure_util.py @@ -7,7 +7,7 @@ from spa.models.mix import Mix def _update_azure_headers(): - ms = Mix.objects.all() + ms = Mix.objects.filter(slug='september-2015') for m in ms: print("Update headers for {0}".format(m.title)) cdn.set_azure_details( From f8356d4a6c7b8c3df6495f6fcc26cfa1775f4e3c Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Sun, 6 Dec 2015 18:55:56 +0000 Subject: [PATCH 06/26] Create tmp file in Docker --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 5b3dd9a..cd4a560 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,8 @@ RUN mkdir /files/cache RUN mkdir /files/cache/mixes RUN mkdir /files/cache/waveforms RUN mkdir /files/tmp +RUN touch /files/tmp/dss.log +RUN chown djworker /files/tmp/dss.log WORKDIR /code From 2702e8f80d65293bc6b390c241f987d54f929153 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Sun, 6 Dec 2015 19:07:56 +0000 Subject: [PATCH 07/26] Create tmp file in Docker --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cd4a560..a79a870 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,6 @@ RUN mkdir /files/cache/mixes RUN mkdir /files/cache/waveforms RUN mkdir /files/tmp RUN touch /files/tmp/dss.log -RUN chown djworker /files/tmp/dss.log WORKDIR /code From 8239e777e36050a1477602f98432b1ecc57bee4f Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Sun, 6 Dec 2015 19:42:20 +0000 Subject: [PATCH 08/26] Fixed up logging --- dss/logsettings.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dss/logsettings.py b/dss/logsettings.py index f591327..2f550fd 100755 --- a/dss/logsettings.py +++ b/dss/logsettings.py @@ -4,6 +4,12 @@ from dss import localsettings if os.name == 'posix': LOG_FILE = localsettings.DSS_TEMP_PATH + '/dss.log' + if not os.path.exists(LOG_FILE): + print("Creating {}".format(LOG_FILE)) + open(LOG_FILE, 'a').close() + else: + print("{} already exists.".format(LOG_FILE)) + else: LOG_FILE = 'c:\\temp\\dss.log' From 1e8daaf9ea26dfc011f8fdda409cce36a643aaf1 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Thu, 10 Dec 2015 23:29:31 +0000 Subject: [PATCH 09/26] Various tings! --- api/serializers.py | 6 ++--- core/realtime/activity.py | 11 +++++--- dss/localsettings.py | 3 +++ dss/logsettings.py | 2 +- spa/management/commands/backup.py | 42 ++---------------------------- spa/management/commands/helpers.py | 1 + spa/management/commands/restore.py | 41 ++++++++++++++++++++++++++++- 7 files changed, 57 insertions(+), 49 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index f57601a..96fa59c 100755 --- a/api/serializers.py +++ b/api/serializers.py @@ -34,6 +34,9 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): profile_image_small = serializers.SerializerMethodField() profile_image_medium = serializers.SerializerMethodField() profile_image_header = serializers.SerializerMethodField() + first_name = serializers.ReadOnlyField(source='get_first_name') + last_name = serializers.ReadOnlyField(source='get_last_name') + display_name = serializers.SerializerMethodField() class Meta: model = UserProfile @@ -48,9 +51,6 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): 'profile_image_header', ) - first_name = serializers.ReadOnlyField(source='get_first_name') - last_name = serializers.ReadOnlyField(source='get_last_name') - display_name = serializers.ReadOnlyField(source='get_display_name') def get_avatar_image(self, obj): return obj.get_sized_avatar_image(32, 32) diff --git a/core/realtime/activity.py b/core/realtime/activity.py index d202b62..ea44e06 100755 --- a/core/realtime/activity.py +++ b/core/realtime/activity.py @@ -7,10 +7,13 @@ logger = logging.getLogger('dss') def post_activity(channel, message, session=''): - r = redis.StrictRedis(host=settings.REDIS_HOST, port=6379, db=0) - payload = json.dumps({'session': session, 'message': message}) - response = r.publish(channel, payload) - logger.debug("Message sent: {0}".format(payload)) + try: + r = redis.StrictRedis(host=settings.REDIS_HOST, port=6379, db=0) + payload = json.dumps({'session': session, 'message': message}) + response = r.publish(channel, payload) + logger.debug("Message sent: {0}".format(payload)) + except Exception as ex: + logger.error(ex) if __name__ == '__main__': post_activity('site:broadcast', 'bargle', '3a596ca6c97065a67aca3dc4a3ba230d688cf413') diff --git a/dss/localsettings.py b/dss/localsettings.py index d16b2e3..f52a26b 100644 --- a/dss/localsettings.py +++ b/dss/localsettings.py @@ -57,3 +57,6 @@ DSS_DB_BACKUP_SECRET = os.environ.get('DSS_DB_BACKUP_SECRET', '') DSS_DB_BACKUP_TOKEN = os.environ.get('DSS_DB_BACKUP_TOKEN', '') AZURE_ACCOUNT_KEY = os.environ.get('AZURE_ACCOUNT_KEY', '') + +if DEBUG: + from dss.devsettings import * diff --git a/dss/logsettings.py b/dss/logsettings.py index 2f550fd..57b375a 100755 --- a/dss/logsettings.py +++ b/dss/logsettings.py @@ -3,7 +3,7 @@ import sys from dss import localsettings if os.name == 'posix': - LOG_FILE = localsettings.DSS_TEMP_PATH + '/dss.log' + LOG_FILE = localsettings.DSS_TEMP_PATH + 'dss.log' if not os.path.exists(LOG_FILE): print("Creating {}".format(LOG_FILE)) open(LOG_FILE, 'a').close() diff --git a/spa/management/commands/backup.py b/spa/management/commands/backup.py index 1fd999b..8a917ed 100644 --- a/spa/management/commands/backup.py +++ b/spa/management/commands/backup.py @@ -10,47 +10,8 @@ from dropbox.rest import ErrorResponse from dss import settings -""" Monkey patch dropbox upload chunked """ - - -def __upload_chunked(self, chunk_size = 4 * 1024 * 1024): - """Uploads data from this ChunkedUploader's file_obj in chunks, until - an error occurs. Throws an exception when an error occurs, and can - be called again to resume the upload. - - Parameters - chunk_size - The number of bytes to put in each chunk. (Default 4 MB.) - """ - - while self.offset < self.target_length: - next_chunk_size = min(chunk_size, self.target_length - self.offset) - if self.last_block == None: - self.last_block = self.file_obj.read(next_chunk_size) - - try: - (self.offset, self.upload_id) = self.client.upload_chunk( - self.last_block, next_chunk_size, self.offset, self.upload_id) - self.last_block = None - except ErrorResponse as e: - # Handle the case where the server tells us our offset is wrong. - must_reraise = True - if e.status == 400: - reply = e.body - if "offset" in reply and reply['offset'] != 0 and reply['offset'] > self.offset: - self.last_block = None - self.offset = reply['offset'] - must_reraise = False - if must_reraise: - raise - -ChunkedUploader.upload_chunked = __upload_chunked - -from dropbox.client import ChunkedUploader """ Monkey patch dropbox upload chunked """ - - def __upload_chunked(self, chunk_size = 4 * 1024 * 1024): """Uploads data from this ChunkedUploader's file_obj in chunks, until an error occurs. Throws an exception when an error occurs, and can @@ -122,7 +83,8 @@ def _create_backup_bundle(remote_file, location): backup_file = "{0}/{1}".format(settings.DSS_TEMP_PATH, remote_file) tar = tarfile.open(backup_file, "w:gz") - tar.add(location) + print("Remote file: {}".format(remote_file.replace(".tar.gz", ""))) + tar.add(tarfile.TarInfo(remote_file.replace(".tar.gz", "")), location) tar.close() return backup_file diff --git a/spa/management/commands/helpers.py b/spa/management/commands/helpers.py index d3dc6ff..de5c5e4 100644 --- a/spa/management/commands/helpers.py +++ b/spa/management/commands/helpers.py @@ -21,3 +21,4 @@ def download_file( url, file_name): print(status, end=' ') f.close() + diff --git a/spa/management/commands/restore.py b/spa/management/commands/restore.py index f9c7bc9..1bc5804 100644 --- a/spa/management/commands/restore.py +++ b/spa/management/commands/restore.py @@ -1,11 +1,50 @@ +import os +import tarfile + from django.core.management.base import LabelCommand import dropbox from dss import settings +import sys + +from utils import query_yes_no + def _restore_database(): """ find latest database backup """ - client = dropbox.client.DropboxClient(settings.DSS_DB_BACKUP_TOKEN) + client = dropbox.Dropbox(settings.DSS_DB_BACKUP_TOKEN) + files = client.files_list_folder('/media') + latest = None + for f in files.entries: + print(f.server_modified) + if latest is None or f.server_modified > latest.server_modified: + latest = f + + if latest is not None: + #if query_yes_no("Restoring backing from: {}\nProceed (y/n?)".format(latest)): + print("Restoring database") + backup_file = '/tmp/{}'.format(latest.name) + result = client.files_download_to_file(backup_file, latest.path_lower) + print("Downloaded {}".format(result)) + + if os.path.exists(backup_file): + print("Download completed") + o = tarfile.open(backup_file) + o.extract() + else: + print("Unable to download file") + + +""" +import requests +... +headers = ... # set up auth +... +params = { 'list' : 'true' } +response = requests.get('https://api.dropbox.com/1/metadata/dropbox/', params=params, headers=headers) +subdirs = [d['path'] for d in response.json()['contents'] if d['is_dir'] == True] +print(subdirs) +""" class Command(LabelCommand): From beae2eb50e6329533ec82fc8ba3dfc10a38aec80 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Sat, 12 Dec 2015 19:44:07 +0000 Subject: [PATCH 10/26] Changed payload for search result --- api/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/views.py b/api/views.py index a1ff69c..af107e6 100755 --- a/api/views.py +++ b/api/views.py @@ -179,6 +179,7 @@ class SearchResultsView(views.APIView): 'title': mix.title, 'image': mix.get_image_url(), 'slug': mix.slug, + 'user': mix.user.slug, 'url': mix.get_absolute_url(), 'description': mix.description } for mix in Mix.objects.filter(title__icontains=q)[0:10]] From a251926ec450652ba105db483566b0dbc42e09f9 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Sat, 19 Dec 2015 21:45:26 +0000 Subject: [PATCH 11/26] Fixes to serializers --- api/serializers.py | 18 ++++++++++++------ spa/models/mix.py | 18 +++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 96fa59c..b2866c6 100755 --- a/api/serializers.py +++ b/api/serializers.py @@ -202,6 +202,18 @@ class MixSerializer(serializers.ModelSerializer): except UserProfile.DoesNotExist: pass + genres = self.initial_data['genres'] + instance.genres.clear() + for genre in genres: + try: + g = Genre.objects.get(slug=genre.get('slug')) + instance.genres.add(g) + except Genre.DoesNotExist: + """ Possibly allow adding genres here """ + pass + + validated_data.pop('genres', None) + # get any likes that aren't in passed bundle if 'downloads' in validated_data: plays = validated_data['downloads'] or [] @@ -209,12 +221,6 @@ class MixSerializer(serializers.ModelSerializer): instance.add_play(play) validated_data.pop('downloads', None) - if 'genres' in validated_data: - genres = validated_data['genres'] or [] - for genre in genres: - instance.add_genre(genre) - validated_data.pop('genres', None) - return super(MixSerializer, self).update(instance, validated_data) except MixUpdateException as ex: raise ex diff --git a/spa/models/mix.py b/spa/models/mix.py index 8f4598a..43f3f75 100755 --- a/spa/models/mix.py +++ b/spa/models/mix.py @@ -96,7 +96,7 @@ class Mix(BaseModel): return self.__unicode__() def __unicode__(self): - return "{} - {}".format(self.user.get_nice_name(), self.title) + return "{} - {}".format(self.user.get_nice_name(), self.title) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if not self.id: @@ -117,14 +117,14 @@ class Mix(BaseModel): def create_mp3_tags(self, prefix=""): try: tag_mp3( - self.get_absolute_path(), - artist=self.user.get_nice_name(), - title=self.title, - url=self.get_full_url(), - album="Deep South Sounds Mixes", - year=self.upload_date.year, - comment=self.description, - genres=self.get_nice_genres()) + self.get_absolute_path(), + artist=self.user.get_nice_name(), + title=self.title, + url=self.get_full_url(), + album="Deep South Sounds Mixes", + year=self.upload_date.year, + comment=self.description, + genres=self.get_nice_genres()) except Exception as ex: self.logger.exception("Mix: error creating tags: %s" % ex) pass From eff257e582eedc3ff2176bb1cba4254edc56cb83 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Wed, 23 Dec 2015 21:43:36 +0000 Subject: [PATCH 12/26] Added genres to view filter --- api/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/views.py b/api/views.py index af107e6..63816e8 100755 --- a/api/views.py +++ b/api/views.py @@ -101,6 +101,7 @@ class MixViewSet(viewsets.ModelViewSet): 'slug', 'user__slug', 'is_featured', + 'genres__slug', ) ordering_fields = ( @@ -121,7 +122,7 @@ class MixViewSet(viewsets.ModelViewSet): raise PermissionDenied("Not allowed") if 'random' in self.request.query_params: return self.queryset.order_by('?').all() - if 'slug' or 'user__slug' in self.kwargs: + if 'slug' in self.kwargs or 'user__slug' in self.kwargs: """ could be private mix so don't filter """ return self.queryset else: From 896ee8724f8c31a6959151eeb3efa0b8060913aa Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Wed, 13 Jan 2016 10:37:33 +0000 Subject: [PATCH 13/26] Fixed process management command --- api/urls.py | 9 ++++----- dss/settings.py | 8 ++++++++ spa/management/commands/azure_util.py | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/api/urls.py b/api/urls.py index cb0bbb6..2b8eeba 100755 --- a/api/urls.py +++ b/api/urls.py @@ -1,20 +1,17 @@ from django.conf.urls import patterns, url, include -from rest_framework import permissions from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response from rest_framework.routers import DefaultRouter from rest_framework.views import APIView +from rest_framework.views import status from rest_framework_jwt.authentication import JSONWebTokenAuthentication from api import views, auth, helpers 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) @@ -26,6 +23,8 @@ router.register(r'shows', views.ShowViewSet, base_name='shows') router.register(r'blog', views.BlogViewSet, base_name='shows') router.register(r'playlist', views.PlaylistViewSet, base_name='playlists') +router.register(r'user', views.UserProfileViewSet) +router.register(r'mix', views.MixViewSet) class DebugView(APIView): permission_classes = (IsAuthenticated,) diff --git a/dss/settings.py b/dss/settings.py index 28f8fd4..41cf3cc 100755 --- a/dss/settings.py +++ b/dss/settings.py @@ -240,3 +240,11 @@ CORS_ALLOW_HEADERS = ( ) """ End static settings """ +SOCIAL_AUTH_AUTHENTICATION_BACKENDS = ( + 'social.backends.open_id.OpenIdAuth', + 'social.backends.google.GoogleOpenId', + 'social.backends.google.GoogleOAuth2', + 'social.backends.google.GoogleOAuth', + 'social.backends.twitter.TwitterOAuth', + 'social.backends.yahoo.YahooOpenId' +) diff --git a/spa/management/commands/azure_util.py b/spa/management/commands/azure_util.py index 25a0f42..71f466a 100755 --- a/spa/management/commands/azure_util.py +++ b/spa/management/commands/azure_util.py @@ -7,7 +7,7 @@ from spa.models.mix import Mix def _update_azure_headers(): - ms = Mix.objects.filter(slug='september-2015') + ms = Mix.objects.all() for m in ms: print("Update headers for {0}".format(m.title)) cdn.set_azure_details( From a6344007cc77e4a85f2016f1f909d3f07dc67cff Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Thu, 14 Jan 2016 22:28:44 +0000 Subject: [PATCH 14/26] Pre branch --- api/auth.py | 53 ++++++++++++++++++++++++++++++++++++++++--------- dss/settings.py | 1 + 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/api/auth.py b/api/auth.py index 0bf5ec4..124273f 100644 --- a/api/auth.py +++ b/api/auth.py @@ -1,6 +1,8 @@ from calendar import timegm import datetime import logging + +from django.http import HttpResponseBadRequest from rest_framework import permissions from rest_framework.response import Response from rest_framework import renderers @@ -11,8 +13,8 @@ from rest_framework.views import status from rest_framework_jwt.settings import api_settings from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler from rest_framework import parsers - from social.apps.django_app.utils import psa +from social.backends.oauth import BaseOAuth1, BaseOAuth2 logger = logging.getLogger('dss') @@ -25,8 +27,23 @@ BACKENDS = { @psa() def auth_by_token(request, backend): + backend = request.backend + if isinstance(backend, BaseOAuth1): + token = { + 'oauth_token': request.data.get('access_token'), + 'oauth_token_secret': request.data.get('access_token_secret'), + } + """ + token = "oauth_token=" + request.data.get('access_token') + "&oauth_token_secret=" + request.data.get( + 'access_token_secret') + """ + elif isinstance(backend, BaseOAuth2): + token = request.REQUEST.get('access_token') + else: + raise HttpResponseBadRequest('Wrong backend type') + user = request.backend.do_auth( - access_token=request.data.get('access_token') + token, ajax=True ) return user if user else None @@ -59,7 +76,7 @@ class SocialLoginHandler(APIView): payload = jwt_payload_handler(user) if api_settings.JWT_ALLOW_REFRESH: payload['orig_iat'] = timegm( - datetime.datetime.utcnow().utctimetuple() + datetime.datetime.utcnow().utctimetuple() ) response_data = { @@ -90,11 +107,29 @@ class ObtainUser(APIView): def get(self, request): if request.user.is_authenticated(): return Response( - status=status.HTTP_200_OK, data={ - 'id': request.user.id, - 'name': request.user.username, - 'slug': request.user.userprofile.slug, - 'userRole': 'user' - }) + status=status.HTTP_200_OK, data={ + 'id': request.user.id, + 'name': request.user.username, + 'slug': request.user.userprofile.slug, + 'userRole': 'user' + }) else: return Response(status=status.HTTP_401_UNAUTHORIZED) + + +""" +class DjangoRESTFrameworkStrategy(DjangoStrategy): + def request_data(self, merge=True): + if not self.request: + return {} + if merge: + data = self.request.REQUEST + elif self.request.method == 'POST': + data = self.request.POST + else: + data = self.request.GET + if data.get('_content'): + data = data.copy() + data.update(data.pop('_content')) + return data +""" diff --git a/dss/settings.py b/dss/settings.py index 41cf3cc..0c7b3ed 100755 --- a/dss/settings.py +++ b/dss/settings.py @@ -248,3 +248,4 @@ SOCIAL_AUTH_AUTHENTICATION_BACKENDS = ( 'social.backends.twitter.TwitterOAuth', 'social.backends.yahoo.YahooOpenId' ) + From aedc18c1e055417bb8e1a1dc9c09156cffac5843 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Sun, 17 Jan 2016 21:13:13 +0000 Subject: [PATCH 15/26] Getting nowhere --- api/auth.py | 152 ++++++++++++++++++++++++++-------------------------- api/urls.py | 3 +- 2 files changed, 79 insertions(+), 76 deletions(-) diff --git a/api/auth.py b/api/auth.py index 124273f..b834b6d 100644 --- a/api/auth.py +++ b/api/auth.py @@ -1,96 +1,116 @@ -from calendar import timegm -import datetime -import logging +from __future__ import unicode_literals -from django.http import HttpResponseBadRequest -from rest_framework import permissions -from rest_framework.response import Response -from rest_framework import renderers +import datetime +from calendar import timegm +from urllib.parse import parse_qsl + +import requests +from rest_framework import status from rest_framework.authtoken.models import Token from rest_framework.authtoken.serializers import AuthTokenSerializer +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework.views import status +from rest_framework import parsers, renderers from rest_framework_jwt.settings import api_settings from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler -from rest_framework import parsers from social.apps.django_app.utils import psa -from social.backends.oauth import BaseOAuth1, BaseOAuth2 -logger = logging.getLogger('dss') - -BACKENDS = { - 'google': 'google-oauth2', - 'facebook': 'facebook', - 'twitter': 'twitter' -} +from dss import settings @psa() -def auth_by_token(request, backend): - backend = request.backend - if isinstance(backend, BaseOAuth1): - token = { - 'oauth_token': request.data.get('access_token'), - 'oauth_token_secret': request.data.get('access_token_secret'), - } - """ - token = "oauth_token=" + request.data.get('access_token') + "&oauth_token_secret=" + request.data.get( - 'access_token_secret') - """ - elif isinstance(backend, BaseOAuth2): - token = request.REQUEST.get('access_token') - else: - raise HttpResponseBadRequest('Wrong backend type') +def auth_by_token(request, backend, auth_token): + """Decorator that creates/authenticates a user with an access_token""" user = request.backend.do_auth( - token, ajax=True + access_token=auth_token ) + if user: + return user + else: + return None - return user if user else None + +def get_access_token(request, backend): + """ + Tries to get the access token from an OAuth Provider + :param request: + :param backend: + :return: + """ + access_token_url = '' + secret = '' + + if backend == 'facebook': + access_token_url = 'https://graph.facebook.com/oauth/access_token' + secret = settings.SOCIAL_AUTH_FACEBOOK_SECRET + if backend == 'twitter': + access_token_url = 'https://api.twitter.com/oauth/request_token' + secret = settings.SOCIAL_AUTH_TWITTER_SECRET + + params = { + 'client_id': request.data.get('clientId'), + 'redirect_uri': request.data.get('redirectUri'), + 'client_secret': secret, + 'code': request.data.get('code') + } + + # Step 1. Exchange authorization code for access token. + r = requests.get(access_token_url, params=params) + try: + access_token = dict(parse_qsl(r.text))['access_token'] + except KeyError: + access_token = 'FAILED' + return access_token class SocialLoginHandler(APIView): - permission_classes = (permissions.AllowAny,) + """View to authenticate users through social media.""" + permission_classes = (AllowAny,) + + def get(self, request, format=None): + pass def post(self, request, format=None): - auth_token = request.data.get('access_token', None) - backend = BACKENDS.get(request.data.get('backend', None), 'facebook') - + backend = request.query_params.get(u'backend', None) + auth_token = get_access_token(request, backend) if auth_token and backend: try: - user = auth_by_token(request, backend) - except Exception as e: - logger.exception(e) - return Response({ - 'status': 'Bad request', - 'message': e - }, status=status.HTTP_400_BAD_REQUEST) - + # Try to authenticate the user using python-social-auth + user = auth_by_token(request, backend, auth_token) + except Exception: + return Response({'status': 'Bad request', + 'message': 'Could not authenticate with the provided token.'}, + status=status.HTTP_400_BAD_REQUEST) if user: if not user.is_active: - return Response({ - 'status': 'Unauthorized', - 'message': 'User account disabled' - }, status=status.HTTP_401_UNAUTHORIZED) + return Response({'status': 'Unauthorized', + 'message': 'The user account is disabled.'}, status=status.HTTP_401_UNAUTHORIZED) + # This is the part that differs from the normal python-social-auth implementation. + # Return the JWT instead. + + # Get the JWT payload for the user. payload = jwt_payload_handler(user) + + # Include original issued at time for a brand new token, + # to allow token refresh if api_settings.JWT_ALLOW_REFRESH: payload['orig_iat'] = timegm( datetime.datetime.utcnow().utctimetuple() ) + # Create the response object with the JWT payload. response_data = { - 'token': jwt_encode_handler(payload), - 'session': user.userprofile.get_session_id() + 'token': jwt_encode_handler(payload) } return Response(response_data) - else: - return Response({ - 'status': 'Bad request', - 'message': 'Authentication could not be performed with received data.' - }, status=status.HTTP_400_BAD_REQUEST) + return Response({'status': 'Bad request', + 'message': 'Authentication could not be performed with received data.'}, + status=status.HTTP_400_BAD_REQUEST) class ObtainUser(APIView): @@ -115,21 +135,3 @@ class ObtainUser(APIView): }) else: return Response(status=status.HTTP_401_UNAUTHORIZED) - - -""" -class DjangoRESTFrameworkStrategy(DjangoStrategy): - def request_data(self, merge=True): - if not self.request: - return {} - if merge: - data = self.request.REQUEST - elif self.request.method == 'POST': - data = self.request.POST - else: - data = self.request.GET - if data.get('_content'): - data = data.copy() - data.update(data.pop('_content')) - return data -""" diff --git a/api/urls.py b/api/urls.py index 2b8eeba..11a8a69 100755 --- a/api/urls.py +++ b/api/urls.py @@ -59,7 +59,8 @@ urlpatterns = patterns( url(r'_search/$', views.SearchResultsView.as_view()), url(r'^', include(router.urls)), - url(r'^_login/', SocialLoginHandler.as_view()), + url(r'^_login', SocialLoginHandler.as_view()), + url(r'^_a', SocialLoginHandler.as_view()), url(r'^token-refresh/', 'rest_framework_jwt.views.refresh_jwt_token'), url(r'^__u/checkslug', helpers.UserSlugCheckHelper.as_view()), From a0b10245e2bcbe3c77f74a0fe15d33c4a30fbc6f Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Tue, 26 Jan 2016 21:19:59 +0000 Subject: [PATCH 16/26] Version bump --- dss/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dss/settings.py b/dss/settings.py index 176b100..239afc3 100755 --- a/dss/settings.py +++ b/dss/settings.py @@ -20,7 +20,7 @@ DEVELOPMENT = DEBUG # AUTH_USER_MODEL = 'spa.UserProfile' TEMPLATE_DEBUG = DEBUG -VERSION = '2.13.04' +VERSION = '3.0.1' ADMINS = ( ('Fergal Moran', 'fergal.moran@gmail.com'), From 943507e04af480d22fe9e819e441a5dda76c7f58 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Fri, 29 Jan 2016 20:13:42 +0000 Subject: [PATCH 17/26] Fixed headers signal --- spa/models/mix.py | 15 ++++++--------- spa/signals.py | 12 +++--------- spa/tasks.py | 8 ++++++++ 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/spa/models/mix.py b/spa/models/mix.py index 43f3f75..c5ca565 100755 --- a/spa/models/mix.py +++ b/spa/models/mix.py @@ -103,17 +103,14 @@ class Mix(BaseModel): self.slug = unique_slugify(self, self.title) self.clean_image('mix_image', Mix) - # Check for the unlikely event that the waveform has been generated - if cdn.file_exists('{0}{1}.png'.format(settings.WAVEFORM_URL, self.uid)): - self.waveform_generated = True - try: - self.duration = mp3_length(self.get_absolute_path()) - except Mp3FileNotFoundException: - # Not really bothered about this in save as it can be called before we have an mp3 - pass - super(Mix, self).save(force_insert, force_update, using, update_fields) + def set_cdn_details(self, path): + self.waveform_generated = True + self.duration = mp3_length(path) + self.save(update_fields=["waveform_generated", "duration"]) + self.update_file_http_headers(self.uid, self.title) + def create_mp3_tags(self, prefix=""): try: tag_mp3( diff --git a/spa/signals.py b/spa/signals.py index 2756d6c..a570e18 100755 --- a/spa/signals.py +++ b/spa/signals.py @@ -1,16 +1,13 @@ +from django.contrib.auth.models import User from django.contrib.sessions.models import Session from django.core.exceptions import ObjectDoesNotExist 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 import SocialAccountLink from spa.models.activity import ActivityFollow -from spa.models.userprofile import UserProfile from spa.models.mix import Mix +from spa.models.userprofile import UserProfile waveform_generated_signal = Signal() @@ -23,10 +20,7 @@ def _waveform_generated_callback(sender, **kwargs): if uid is not None: mix = Mix.objects.get(uid=uid) if mix is not None: - mix.waveform_generated = True - mix.duration = mp3_length(path) - mix.save(update_fields=["waveform_generated", "duration"]) - + mix.set_cdn_details(path) except ObjectDoesNotExist: print("Mix has still not been uploaded") pass diff --git a/spa/tasks.py b/spa/tasks.py index d98688a..1d87cd9 100755 --- a/spa/tasks.py +++ b/spa/tasks.py @@ -36,6 +36,14 @@ def create_waveform_task(in_file, uid): logger.error("Outfile is missing") +@task(time_limit=3600) +def update_file_http_headers(uid, title): + cdn.set_azure_details( + blob_name='{0}.mp3'.format(uid), + download_name='Deep South Sounds - {0}.mp3'.format(title), + container_name='mixes') + + @task(time_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)) From 1fd3df54889be17c0c6f4ee7f7a1ada545db1368 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Fri, 29 Jan 2016 21:49:54 +0000 Subject: [PATCH 18/26] Lengthened token expiry --- api/auth.py | 1 + dss/settings.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/auth.py b/api/auth.py index 743d117..eb46791 100644 --- a/api/auth.py +++ b/api/auth.py @@ -13,6 +13,7 @@ from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework_jwt.settings import api_settings from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler diff --git a/dss/settings.py b/dss/settings.py index 2e41ca4..33ba9ab 100755 --- a/dss/settings.py +++ b/dss/settings.py @@ -212,7 +212,7 @@ THUMBNAIL_PREFIX = '_tn/' # THUMBNAIL_STORAGE = 'storages.backends.azure_storage.AzureStorage' JWT_AUTH = { - 'JWT_EXPIRATION_DELTA': timedelta(seconds=900), + 'JWT_EXPIRATION_DELTA': timedelta(seconds=(60 * 60 * 24) * 14), # 'JWT_EXPIRATION_DELTA': timedelta(seconds=5), 'JWT_ALLOW_REFRESH': True, 'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=30), From d9cf7023452ed8098551daac32b8663e5bf76850 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Mon, 1 Feb 2016 20:27:29 +0000 Subject: [PATCH 19/26] Dockerfile --- Dockerfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index a79a870..1191b68 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,4 @@ -FROM python:latest -ENV PYTHONBUFFERED 1 +FROM fergalmoran/django RUN mkdir /code RUN mkdir /srv/logs @@ -17,8 +16,6 @@ WORKDIR /code ADD requirements.txt /code/ ADD . /code/ -RUN apt-get update --fix-missing && apt-get install -y sox lame vim ccze node npm \ - libboost-program-options-dev libsox-fmt-mp3 postgresql-client rsync openssh-client RUN npm install -g yuglify RUN pip install -r requirements.txt From 83abc1feadb23ad3937867b0ca9ab0cf779c8cb7 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Fri, 3 Jun 2016 20:51:27 +0100 Subject: [PATCH 20/26] Waveform signalling fixes --- Dockerfile | 24 ++++++------------------ api/auth.py | 3 ++- core/utils/url.py | 3 ++- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1191b68..fd64e60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,12 @@ FROM fergalmoran/django -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 -RUN touch /files/tmp/dss.log - -WORKDIR /code - -ADD requirements.txt /code/ ADD . /code/ -RUN npm install -g yuglify -RUN pip install -r requirements.txt +RUN mkdir /files/static +RUN mkdir /files/media +RUN mkdir /files/cache/mixes +RUN mkdir /files/cache/waveforms +RUN touch /files/tmp/dss.log -RUN adduser --disabled-password --gecos '' djworker -RUN chown djworker /files -R +WORKDIR /code diff --git a/api/auth.py b/api/auth.py index eb46791..fa9a7c8 100644 --- a/api/auth.py +++ b/api/auth.py @@ -181,7 +181,8 @@ class ObtainUser(APIView): 'id': request.user.id, 'name': request.user.username, 'slug': request.user.userprofile.slug, - 'userRole': 'user' + 'session': request.user.userprofile.get_session_id(), + 'userRole': 'user', }) else: return Response(status=status.HTTP_401_UNAUTHORIZED) diff --git a/core/utils/url.py b/core/utils/url.py index 03dd048..6400655 100755 --- a/core/utils/url.py +++ b/core/utils/url.py @@ -1,5 +1,6 @@ -import urllib.parse import re +import urllib + from django.contrib.sites.models import Site from django.template.defaultfilters import slugify From be49f9c9c4a312e1f58b4e908c541f2bd409e2c2 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Fri, 3 Jun 2016 21:08:51 +0100 Subject: [PATCH 21/26] Fixed dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index fd64e60..40a9728 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ FROM fergalmoran/django ADD . /code/ +RUN mkdir /files/ RUN mkdir /files/static RUN mkdir /files/media RUN mkdir /files/cache/mixes From 634469576df04d39f5a9940761d0f2d657bdf021 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Fri, 3 Jun 2016 21:13:36 +0100 Subject: [PATCH 22/26] Fixed dockerfile --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 40a9728..1fc4fea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,8 +6,10 @@ ADD . /code/ 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 RUN touch /files/tmp/dss.log WORKDIR /code From c0137c448cefcdaac32c4f6cd976d08deecd266e Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Tue, 7 Jun 2016 20:43:06 +0100 Subject: [PATCH 23/26] Notifications fixups --- .gitignore | 3 ++- Dockerfile | 3 --- .../0026_notification_notification_text.py | 19 +++++++++++++++++++ spa/migrations/0027_auto_20160607_2042.py | 19 +++++++++++++++++++ spa/models/notification.py | 1 + 5 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 spa/migrations/0026_notification_notification_text.py create mode 100644 spa/migrations/0027_auto_20160607_2042.py diff --git a/.gitignore b/.gitignore index 0cc78b3..36538b7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ dss/celery_settings.py dss/devsettings.py dss.conf dss/debugsettings.py +logs/ mysql test.py mysql2pgsql.yml @@ -34,4 +35,4 @@ reload reset __krud/ celerybeat-schedule -private/ \ No newline at end of file +private/ diff --git a/Dockerfile b/Dockerfile index 1fc4fea..fd64e60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,13 +3,10 @@ FROM fergalmoran/django ADD . /code/ -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 RUN touch /files/tmp/dss.log WORKDIR /code diff --git a/spa/migrations/0026_notification_notification_text.py b/spa/migrations/0026_notification_notification_text.py new file mode 100644 index 0000000..efdb6d8 --- /dev/null +++ b/spa/migrations/0026_notification_notification_text.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('spa', '0025_socialaccountlink_provider_data'), + ] + + operations = [ + migrations.AddField( + model_name='notification', + name='notification_text', + field=models.CharField(null=True, max_length=2048), + ), + ] diff --git a/spa/migrations/0027_auto_20160607_2042.py b/spa/migrations/0027_auto_20160607_2042.py new file mode 100644 index 0000000..6f27ab5 --- /dev/null +++ b/spa/migrations/0027_auto_20160607_2042.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('spa', '0026_notification_notification_text'), + ] + + operations = [ + migrations.AlterField( + model_name='notification', + name='notification_text', + field=models.CharField(max_length=2048), + ), + ] diff --git a/spa/models/notification.py b/spa/models/notification.py index 7ea116f..29aeaff 100755 --- a/spa/models/notification.py +++ b/spa/models/notification.py @@ -17,6 +17,7 @@ class Notification(BaseModel): 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) + notification_text = models.CharField(max_length=2048, null=False) accepted_date = models.DateTimeField(null=True) From 29f6f4c31be8458f7367e653c49ac1de232fc673 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Wed, 8 Jun 2016 22:41:17 +0100 Subject: [PATCH 24/26] Fixed notifications --- api/serializers.py | 5 ++++- .../0026_notification_notification_text.py | 19 ------------------- spa/migrations/0027_auto_20160607_2042.py | 19 ------------------- spa/models/notification.py | 1 - 4 files changed, 4 insertions(+), 40 deletions(-) delete mode 100644 spa/migrations/0026_notification_notification_text.py delete mode 100644 spa/migrations/0027_auto_20160607_2042.py diff --git a/api/serializers.py b/api/serializers.py index b2866c6..1748915 100755 --- a/api/serializers.py +++ b/api/serializers.py @@ -474,7 +474,7 @@ class NotificationSerializer(serializers.HyperlinkedModelSerializer): from_user = InlineUserProfileSerializer(source='get_from_user', read_only=True) notification_url = serializers.ReadOnlyField() verb = serializers.ReadOnlyField() - target = serializers.ReadOnlyField() + target = serializers.SerializerMethodField() date = serializers.ReadOnlyField() class Meta: @@ -497,6 +497,9 @@ class NotificationSerializer(serializers.HyperlinkedModelSerializer): def get_avatar_image(self, obj): return settings.DEFAULT_USER_IMAGE if obj.from_user is None else obj.get_sized_avatar_image(253, 157) + def get_target(self, obj): + return "/{}/{}".format(obj.to_user.slug, obj.target) + class MessageSerializer(serializers.ModelSerializer): from_user = InlineUserProfileSerializer(read_only=True) diff --git a/spa/migrations/0026_notification_notification_text.py b/spa/migrations/0026_notification_notification_text.py deleted file mode 100644 index efdb6d8..0000000 --- a/spa/migrations/0026_notification_notification_text.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('spa', '0025_socialaccountlink_provider_data'), - ] - - operations = [ - migrations.AddField( - model_name='notification', - name='notification_text', - field=models.CharField(null=True, max_length=2048), - ), - ] diff --git a/spa/migrations/0027_auto_20160607_2042.py b/spa/migrations/0027_auto_20160607_2042.py deleted file mode 100644 index 6f27ab5..0000000 --- a/spa/migrations/0027_auto_20160607_2042.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('spa', '0026_notification_notification_text'), - ] - - operations = [ - migrations.AlterField( - model_name='notification', - name='notification_text', - field=models.CharField(max_length=2048), - ), - ] diff --git a/spa/models/notification.py b/spa/models/notification.py index 29aeaff..7ea116f 100755 --- a/spa/models/notification.py +++ b/spa/models/notification.py @@ -17,7 +17,6 @@ class Notification(BaseModel): 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) - notification_text = models.CharField(max_length=2048, null=False) accepted_date = models.DateTimeField(null=True) From 11aa58c288266ce4e0cc66cfbcfb2efb6e19a624 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Mon, 20 Jun 2016 20:42:11 +0100 Subject: [PATCH 25/26] Fixed storage --- dss/storagesettings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dss/storagesettings.py b/dss/storagesettings.py index f236282..f96f011 100644 --- a/dss/storagesettings.py +++ b/dss/storagesettings.py @@ -1,8 +1,8 @@ from dss import localsettings import os -AZURE_ACCOUNT_NAME = os.environ.get('CDN_NAME', 'dsscdn') -DEBUG_AZURE_ACCOUNT_NAME = os.environ.get('CDN_NAME', 'dsscdn2') +DEBUG_AZURE_ACCOUNT_NAME = os.environ.get('CDN_NAME', 'dsscdn') +AZURE_ACCOUNT_NAME = os.environ.get('CDN_NAME', 'dsscdn2') AZURE_CONTAINER = 'media' AZURE_ACCOUNT_KEY = localsettings.AZURE_ACCOUNT_KEY AZURE_ITEM_BASE_URL = 'https://{}.blob.core.windows.net/'.format(AZURE_ACCOUNT_NAME) From 6d09f463bad211d6bf953ddba7f77798169b58fd Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Wed, 29 Jun 2016 18:48:13 +0100 Subject: [PATCH 26/26] Staff edit commnets --- api/serializers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 1748915..e377265 100755 --- a/api/serializers.py +++ b/api/serializers.py @@ -422,8 +422,11 @@ class CommentSerializer(serializers.HyperlinkedModelSerializer): def get_can_edit(self, obj): user = self.context['request'].user - if user is not None and obj.user is not None and user.is_authenticated(): - return user.is_staff or obj.user.id == user.userprofile.id + if user is not None: + if user.is_staff: + return True + if obj.user is not None and user.is_authenticated(): + return obj.user.id == user.userprofile.id return False