From 34299cb2deacd80fb2c2cc5304d0535cb2b10550 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Tue, 23 Apr 2013 09:46:04 +0100 Subject: [PATCH] Slugified mix urls --- spa/api/v1/MixResource.py | 7 ++- spa/api/v1/UserResource.py | 61 +++++++++++++++---------- spa/management/commands/processaudio.py | 54 ++++++++++++---------- spa/models/Mix.py | 8 +++- spa/models/UserProfile.py | 2 +- static/js/app/app.js | 23 ++++++---- static/js/app/views/mix.js | 2 +- static/js/app/views/sidebaruser.js | 20 ++++++++ templates/base.html | 1 + templates/views/SidebarViewUser.html | 4 ++ 10 files changed, 120 insertions(+), 62 deletions(-) create mode 100644 static/js/app/views/sidebaruser.js create mode 100644 templates/views/SidebarViewUser.html diff --git a/spa/api/v1/MixResource.py b/spa/api/v1/MixResource.py index 2c68080..64dc11e 100644 --- a/spa/api/v1/MixResource.py +++ b/spa/api/v1/MixResource.py @@ -28,6 +28,11 @@ class MixResource(BackboneCompatibleResource): } authorization = Authorization() + def prepend_urls(self): + return [ + url(r"^(?P%s)/(?P[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), + ] + def _parseGenreList(self, genres): #for magic.. ret = [] @@ -113,7 +118,7 @@ class MixResource(BackboneCompatibleResource): bundle.data['user_name'] = bundle.obj.user.get_nice_name() bundle.data['user_profile_url'] = bundle.obj.user.get_absolute_url() bundle.data['user_profile_image'] = bundle.obj.user.get_small_profile_image() - bundle.data['item_url'] = 'mix/%s' % bundle.obj.id + bundle.data['item_url'] = 'mix/%s' % bundle.obj.slug bundle.data['play_count'] = bundle.obj.plays.count() bundle.data['download_count'] = bundle.obj.downloads.count() diff --git a/spa/api/v1/UserResource.py b/spa/api/v1/UserResource.py index 65636f9..6e7c9d7 100644 --- a/spa/api/v1/UserResource.py +++ b/spa/api/v1/UserResource.py @@ -1,3 +1,4 @@ +from django.conf.urls import url from tastypie.authentication import Authentication from tastypie.authorization import Authorization from tastypie.constants import ALL, ALL_WITH_RELATIONS @@ -18,38 +19,48 @@ class UserResource(BackboneCompatibleResource): 'id': ALL, } - def full_dehydrate(self, bundle, for_list=False): - return super(UserResource, self).full_dehydrate(bundle, for_list) + def prepend_urls(self): + return [ + url(r"^(?P%s)/(?P[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), + ] def authorized_read_list(self, object_list, bundle): return object_list.filter(user_id=bundle.request.user.id) + def authorized_read_detail(self, object_list, bundle): + return object_list.only('description') + def dehydrate(self, bundle): - bundle.data['display_name'] = bundle.obj.display_name + + #set the "me" user only properties + if bundle.obj.id == bundle.request.user.id: + bundle.data['email'] = bundle.obj.email + if bundle.obj.activity_sharing is not None: + bundle.data['activity_share_likes'] = \ + (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_LIKES) != 0 + bundle.data['activity_share_favourites'] = \ + (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_FAVOURITES) != 0 + bundle.data['activity_share_comments'] = \ + (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_COMMENTS) != 0 + else: + bundle.data['activity_share_likes'] = 0 + bundle.data['activity_share_favourites'] = 0 + bundle.data['activity_share_comments'] = 0 + + if bundle.obj.activity_sharing_networks is not None: + bundle.data['activity_share_networks_facebook'] = \ + (bundle.obj.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK) != 0 + bundle.data['activity_share_networks_twitter'] = \ + (bundle.obj.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER) != 0 + else: + bundle.data['activity_share_networks_facebook'] = 0 + bundle.data['activity_share_networks_facebook'] = 0 + else: + del bundle.data['activity_sharing'] + del bundle.data['activity_sharing_networks'] + bundle.data['first_name'] = bundle.obj.first_name bundle.data['last_name'] = bundle.obj.last_name - bundle.data['email'] = bundle.obj.email - - if bundle.obj.activity_sharing is not None: - bundle.data['activity_share_likes'] = \ - (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_LIKES) != 0 - bundle.data['activity_share_favourites'] = \ - (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_FAVOURITES) != 0 - bundle.data['activity_share_comments'] = \ - (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_COMMENTS) != 0 - else: - bundle.data['activity_share_likes'] = 0 - bundle.data['activity_share_favourites'] = 0 - bundle.data['activity_share_comments'] = 0 - - if bundle.obj.activity_sharing_networks is not None: - bundle.data['activity_share_networks_facebook'] = \ - (bundle.obj.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK) != 0 - bundle.data['activity_share_networks_twitter'] = \ - (bundle.obj.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER) != 0 - else: - bundle.data['activity_share_networks_facebook'] = 0 - bundle.data['activity_share_networks_facebook'] = 0 return bundle diff --git a/spa/management/commands/processaudio.py b/spa/management/commands/processaudio.py index 23d8497..e3ed368 100644 --- a/spa/management/commands/processaudio.py +++ b/spa/management/commands/processaudio.py @@ -1,26 +1,30 @@ -import os -from django.core.management.base import NoArgsCommand, CommandError -from core.utils.audio import Mp3FileNotFoundException -from core.utils.audio.mp3 import mp3_length -from dss import settings -from spa.models import Mix - - -class Command(NoArgsCommand): - help = "Updates audio files with their durations" - - def handle(self, *args, **options): - try: - candidates = Mix.objects.filter(duration__isnull=True) - for mix in candidates: - try: - print "Finding duration for: %s" % mix.title - length = mp3_length(mix.get_absolute_path()) - print "\tLength: %d" % length - mix.duration = length - mix.save() - except Mp3FileNotFoundException, me: - mix.delete() - print me.message - except Exception, ex: +from django.core.management.base import NoArgsCommand, CommandError +from django.template.defaultfilters import slugify +from core.utils.audio import Mp3FileNotFoundException +from core.utils.audio.mp3 import mp3_length +from spa.models import Mix + + +class Command(NoArgsCommand): + help = "Updates audio files with their durations" + + def handle(self, *args, **options): + try: + candidates = Mix.objects.all() + for mix in candidates: + try: + if mix.duration is None: + print "Finding duration for: %s" % mix.title + length = mp3_length(mix.get_absolute_path()) + print "\tLength: %d" % length + mix.duration = length + if mix.slug == 'Invalid': + print "Slugifying mix: %s" % mix.title + mix.slug = slugify(mix.title) + print "\tNew title: %s" % mix.slug + mix.save() + except Mp3FileNotFoundException, me: + mix.delete() + print me.message + except Exception, ex: raise CommandError(ex.message) \ No newline at end of file diff --git a/spa/models/Mix.py b/spa/models/Mix.py index bd26b75..9070372 100644 --- a/spa/models/Mix.py +++ b/spa/models/Mix.py @@ -2,6 +2,7 @@ import os import rfc822 from datetime import datetime import urlparse +from django.template.defaultfilters import slugify from sorl.thumbnail import get_thumbnail from django.contrib.sites.models import Site @@ -45,6 +46,7 @@ class Mix(_BaseModel): uid = models.CharField(max_length=38, blank=True, unique=True) download_allowed = models.BooleanField(default=False) duration = models.IntegerField(null=True, blank=True) + slug = models.SlugField() genres = models.ManyToManyField(Genre) @@ -52,6 +54,10 @@ class Mix(_BaseModel): return self.title def save(self, force_insert=False, force_update=False, using=None): + if not self.id: + self.slug = slugify(self.title) + + #TODO #turn away now - horrid hack to strip media root url #from image - will sort when I've figured backbone out better if self.mix_image.name.startswith(settings.MEDIA_URL): @@ -71,7 +77,7 @@ class Mix(_BaseModel): return '%s/mixes/%s%s%s' % (settings.MEDIA_ROOT, prefix, self.uid, extension) def get_absolute_url(self): - return '/mix/%i' % self.id + return '/mix/%s' % self.slug def get_full_url(self): return 'http://%s%s' % (Site.objects.get_current().domain, self.get_absolute_url()) diff --git a/spa/models/UserProfile.py b/spa/models/UserProfile.py index bb22454..e58ef53 100644 --- a/spa/models/UserProfile.py +++ b/spa/models/UserProfile.py @@ -37,7 +37,7 @@ class UserProfile(_BaseModel): display_name = models.CharField(blank=True, max_length=35) description = models.CharField(blank=True, max_length=2048) - slug = models.CharField(max_length=35, blank=True, null=True, default=None) + slug = models.SlugField(max_length=50, blank=True, null=True, default=None) activity_sharing = models.IntegerField(default=0) activity_sharing_networks = models.IntegerField(default=0) diff --git a/static/js/app/app.js b/static/js/app/app.js index d0e8c45..d18153a 100644 --- a/static/js/app/app.js +++ b/static/js/app/app.js @@ -54,9 +54,23 @@ var AppRouter = Backbone.Router.extend({ }, user: function (user) { this._renderMixList('latest', { "user": user }); + var model = new User({ + id: user + }); + this.sidebarView = new SidebarViewUser({ + model: model + }); + $('#sidebar').html(this.sidebarView.el); }, mixList: function (type) { this._renderMixList(type); + this.sidebarView = new SidebarView(); + $('#sidebar').html(this.sidebarView.el); + startChat( + $('#chat-messages-body', this.sidebarView.el), + $('#input', this.sidebarView.el), + $('#status', this.sidebarView.el), + $('#header-profile-edit').text()); }, _renderMixList: function (type, data) { var mixList = new MixCollection(); @@ -77,13 +91,6 @@ var AppRouter = Backbone.Router.extend({ } } }); - this.sidebarView = new SidebarView(); - $('#sidebar').html(this.sidebarView.el); - startChat( - $('#chat-messages-body', this.sidebarView.el), - $('#input', this.sidebarView.el), - $('#status', this.sidebarView.el), - $('#header-profile-edit').text()); }, mixDetails: function (id) { var mix = new Mix({ @@ -227,7 +234,7 @@ var AppRouter = Backbone.Router.extend({ } }); -com.podnoms.utils.loadTemplate(['HeaderView', 'SidebarView', 'UserView', 'MixListView', 'MixListItemView', 'MixView', 'MixCreateView', 'CommentListView', 'CommentListItemView', 'ActivityListView', 'ActivityListItemView', 'ReleaseListView', 'ReleaseListItemView', 'ReleaseItemView', 'ReleaseView', 'ReleaseCreateView', 'ReleaseAudioListView', 'ReleaseAudioItemView', 'EventCreateView', 'EventListView', 'EventListItemView', 'EventView', 'EventItemView'], function () { +com.podnoms.utils.loadTemplate(['HeaderView', 'SidebarView', 'SidebarViewUser', 'UserView', 'MixListView', 'MixListItemView', 'MixView', 'MixCreateView', 'CommentListView', 'CommentListItemView', 'ActivityListView', 'ActivityListItemView', 'ReleaseListView', 'ReleaseListItemView', 'ReleaseItemView', 'ReleaseView', 'ReleaseCreateView', 'ReleaseAudioListView', 'ReleaseAudioItemView', 'EventCreateView', 'EventListView', 'EventListItemView', 'EventView', 'EventItemView'], function () { window.app = new AppRouter(); // Trigger the initial route and enable HTML5 History API support, set the // root folder to '/' by default. Change in app.js. diff --git a/static/js/app/views/mix.js b/static/js/app/views/mix.js index cd61b75..1ed36ba 100644 --- a/static/js/app/views/mix.js +++ b/static/js/app/views/mix.js @@ -213,7 +213,7 @@ window.MixView = Backbone.View.extend({ */ var comments = new CommentCollection(); - comments.url = com.podnoms.settings.urlRoot + this.model.get("item_url") + "/comments/"; + comments.url = this.model.get("resource_uri") + "comments/"; comments.mix_id = this.model.id; comments.mix = this.model.get("resource_uri"); comments.fetch({success: function (data) { diff --git a/static/js/app/views/sidebaruser.js b/static/js/app/views/sidebaruser.js new file mode 100644 index 0000000..9f98b8e --- /dev/null +++ b/static/js/app/views/sidebaruser.js @@ -0,0 +1,20 @@ +/** @license + + ---------------------------------------------- + + Copyright (c) 2012, Fergal Moran. All rights reserved. + Code provided under the BSD License: + + */ + +window.SidebarViewUser = Backbone.View.extend({ + events: { + }, + initialize: function () { + this.render(); + }, + render: function () { + $(this.el).html(this.template({"item":this.model.toJSON()})); + return this; + } +}); \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 253574a..1bdcb9d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -90,6 +90,7 @@ + diff --git a/templates/views/SidebarViewUser.html b/templates/views/SidebarViewUser.html new file mode 100644 index 0000000..8d9df14 --- /dev/null +++ b/templates/views/SidebarViewUser.html @@ -0,0 +1,4 @@ +
+

Hello Sailor

+ <%= item.display_name %> +
\ No newline at end of file