No message

This commit is contained in:
Fergal Moran
2013-07-22 09:41:22 +01:00
parent 7d2f000e00
commit 4a49264d15
10 changed files with 172 additions and 62 deletions

View File

@@ -218,7 +218,6 @@ AVATAR_STORAGE_DIR = MEDIA_ROOT + '/avatars/'
ACCOUNT_LOGOUT_REDIRECT_URL = '/'
LOGIN_REDIRECT_URL = '/'
DSS_TEMP_PATH = localsettings.DSS_TEMP_PATH
DSS_LAME_PATH = localsettings.DSS_LAME_PATH
DSS_WAVE_PATH = localsettings.DSS_WAVE_PATH
@@ -264,6 +263,7 @@ REQUIRE_DEBUG = False #DEBUG
if DEBUG:
import mimetypes
mimetypes.add_type("image/png", ".png", True)
mimetypes.add_type("font/woff", ".woff", True)

View File

@@ -1,6 +1,7 @@
import humanize
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from tastypie.constants import ALL, ALL_WITH_RELATIONS
from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource
from spa.models import UserProfile
from spa.models.activity import Activity
@@ -13,6 +14,9 @@ class ActivityResource(BackboneCompatibleResource):
authorization = Authorization()
authentication = Authentication()
always_return_data = True
filtering = {
'user': ALL_WITH_RELATIONS
}
def dehydrate(self, bundle):
try:
@@ -41,3 +45,4 @@ class ActivityResource(BackboneCompatibleResource):
except Exception, ee:
self.logger.debug("Exception: Error dehydrating activity, %s" % ee.message)
return None

View File

@@ -1,7 +1,7 @@
from django.conf.urls import url
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator, InvalidPage
from django.db.models import Count
from django.db.models import Count, Q
from django.http import Http404
from django.template.loader import render_to_string
from tastypie import fields
@@ -22,6 +22,7 @@ from spa.models.mix import Mix
class MixResource(BackboneCompatibleResource):
comments = fields.ToManyField('spa.api.v1.CommentResource.CommentResource', 'comments', null=True)
#downloads = fields.ToManyField('spa.api.v1.ActivityResource.ActivityResource', 'downloads')
favourites = fields.ToManyField('spa.api.v1.UserResource.UserResource', 'favourites', related_name='favourites', full=True)
class Meta:
queryset = Mix.objects.filter(is_active=True)
@@ -30,7 +31,9 @@ class MixResource(BackboneCompatibleResource):
detail_uri_name = 'slug'
excludes = ['download_url', 'is_active', 'local_file', 'upload_date', 'waveform-generated']
filtering = {
'comments': ALL_WITH_RELATIONS
'comments': ALL_WITH_RELATIONS,
'favourites': ALL_WITH_RELATIONS,
'activity_likes': ALL_WITH_RELATIONS
}
authorization = Authorization()
@@ -107,10 +110,11 @@ class MixResource(BackboneCompatibleResource):
def obj_update(self, bundle, **kwargs):
#don't sync the mix_image, this has to be handled separately
del bundle.data['mix_image']
ret = super(MixResource, self).obj_update(bundle, bundle.request)
bundle.obj.update_favourite(bundle.request.user, bundle.data['favourited'])
bundle.obj.update_liked(bundle.request.user, bundle.data['liked'])
ret = super(MixResource, self).obj_update(bundle, bundle.request)
self._unpackGenreList(ret, bundle.data['genre-list'])
return ret
@@ -119,28 +123,57 @@ class MixResource(BackboneCompatibleResource):
if orderby == 'latest':
obj_list = obj_list.order_by('-id')
elif orderby == 'toprated':
obj_list = obj_list.annotate(karma=Count('likes')).order_by('-karma')
obj_list = obj_list.annotate(karma=Count('activity_likes')).order_by('-karma')
elif orderby == 'mostplayed':
obj_list = obj_list.annotate(karma=Count('plays')).order_by('-karma')
obj_list = obj_list.annotate(karma=Count('activity_plays')).order_by('-karma')
elif orderby == 'mostactive':
obj_list = obj_list.annotate(karma=Count('comments')).order_by('-karma')
elif orderby == 'recommended':
obj_list = obj_list.annotate(karma=Count('likes')).order_by('-karma')
obj_list = obj_list.annotate(karma=Count('activity_likes')).order_by('-karma')
return obj_list
"""
def build_filters(self, filters=None):
if filters is None:
filters = {}
# TODO(Ferg@@lMoran.me): Yet another code smell
# I'm doing this shit everywhere in tastypie - here is my canonical rant about it
# Either I'm completely missing something or tastypie was the wrong horse to back here
# There has to be a more prescriptive way to do this shit!!!
# I'm seriously considering swapping out tastpie for a more sane REST framework
# Simple stuff like deciding on
# your own url pattern
# ORM level rather than Resource level filtering
# are extremely difficult/undocumented
orm_filters = super(MixResource, self).build_filters(filters)
#find the non-resource filters that were stripped but the super's build_filters
#and re add them, will probably need to perform some checks here against Meta.filters
#so we can't be filtered willy-nilly
#also, have no idea how filters became a QueryDict??
for f in filters:
if f not in ['format', 'order_by', 'sort']:
orm_filters.update({'custom': Q(f=filters.get(f))})
return orm_filters
"""
def apply_filters(self, request, applicable_filters):
semi_filtered = super(MixResource, self)\
.apply_filters(request, applicable_filters)\
semi_filtered = super(MixResource, self) \
.apply_filters(request, applicable_filters) \
.filter(waveform_generated=True)
f_type = request.GET.get('type', None)
f_user = request.GET.get('user', None)
"""
if f_type == 'favourites':
semi_filtered = semi_filtered.filter(favourites__user=request.user.get_profile())
elif f_type == 'likes':
semi_filtered = semi_filtered.filter(likes__user=request.user.get_profile())
"""
if f_user is not None:
semi_filtered = semi_filtered.filter(user__slug=f_user)
@@ -159,16 +192,20 @@ class MixResource(BackboneCompatibleResource):
bundle.data['user_profile_image'] = bundle.obj.user.get_small_profile_image()
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()
bundle.data['like_count'] = bundle.obj.likes.count()
bundle.data['favourite_count'] = bundle.obj.favourites.count()
bundle.data['play_count'] = bundle.obj.activity_plays.count()
bundle.data['download_count'] = bundle.obj.activity_downloads.count()
bundle.data['like_count'] = bundle.obj.activity_likes.count()
bundle.data['tooltip'] = render_to_string('inc/player_tooltip.html', {'item': bundle.obj})
bundle.data['comment_count'] = bundle.obj.comments.count()
bundle.data['genre-list'] = json.to_ajax(bundle.obj.genres.all(), 'description', 'slug')
bundle.data['liked'] = bundle.obj.is_liked(bundle.request.user)
bundle.data['favourited'] = bundle.obj.is_favourited(bundle.request.user)
bundle.data['favourited'] = bundle.obj.favourites.filter(user=bundle.request.user).count() != 0
return bundle

View File

@@ -1,14 +1,12 @@
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db.models import Count, Q
from tastypie import fields
from tastypie.authentication import Authentication
from tastypie.authorization import DjangoAuthorization
from django.conf.urls import url
from tastypie.http import HttpMultipleChoices, HttpGone
from tastypie.constants import ALL
from tastypie.utils import trailing_slash
from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource
from spa.api.v1.MixResource import MixResource
from spa.models.activity import ActivityFollow
from spa.models.userprofile import UserProfile
from spa.models.mix import Mix
@@ -16,13 +14,18 @@ from spa.models.mix import Mix
class UserResource(BackboneCompatibleResource):
class Meta:
queryset = UserProfile.objects.all().annotate(mix_count=Count('mixes')).order_by('-mix_count')
queryset = UserProfile.objects.all().annotate(mix_count=Count('mixes'))\
.extra(select={'u':'user'}).order_by('-mix_count')
favourites = fields.ToManyField('spa.api.v1.MixResource.MixResource', 'favourites', null=True)
resource_name = 'user'
excludes = ['is_active', 'is_staff', 'is_superuser', 'password']
ordering = ['mix_count']
filtering = {
'slug': ALL,
}
authorization = DjangoAuthorization()
authentication = Authentication()
favourites = fields.ToManyField(MixResource, 'favourites')
favourites = fields.ToManyField('spa.api.v1.MixResource', 'favourites')
def _hydrateBitmapOption(self, source, comparator):
return True if (source & comparator) != 0 else False
@@ -32,9 +35,6 @@ class UserResource(BackboneCompatibleResource):
url(r"^(?P<resource_name>%s)/(?P<slug>[\w\d_.-]+)/favourites%s$" % (
self._meta.resource_name, trailing_slash()),
self.wrap_view('get_user_favourites'), name="api_get_user_favourites"),
url(r"^(?P<resource_name>%s)/(?P<slug>[\w\d_.-]+)/activity%s$" % (
self._meta.resource_name, trailing_slash()),
self.wrap_view('get_user_activity'), name="api_get_user_activity"),
url(r"^(?P<resource_name>%s)/(?P<pk>\d+)/$" % self._meta.resource_name,
self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
url(r"^(?P<resource_name>%s)/(?P<slug>[\w\d_.-]+)/$" % self._meta.resource_name,
@@ -49,6 +49,10 @@ class UserResource(BackboneCompatibleResource):
"""
def apply_filters(self, request, applicable_filters):
semi_filtered = super(UserResource, self).apply_filters(request, applicable_filters)
return semi_filtered
def __apply_filters(self, request, applicable_filters):
semi_filtered = super(UserResource, self).apply_filters(request, applicable_filters)
q = request.GET.get('q', None)
if q is not None:
@@ -60,18 +64,6 @@ class UserResource(BackboneCompatibleResource):
return semi_filtered
def get_user_favourites(self, request, **kwargs):
try:
obj = self.cached_obj_get(bundle=self.build_bundle(request=request),
**self.remove_api_resource_names(kwargs))
except ObjectDoesNotExist:
return HttpGone()
except MultipleObjectsReturned:
return HttpMultipleChoices("More than one resource is found at this URI.")
mixes = MixResource()
return mixes.get_list(request, favourites__user=obj.get_profile())
def _patch_resource(self, bundle):
#Handle the patched items from backbone
if 'following' in bundle.data:
@@ -131,7 +123,7 @@ class UserResource(BackboneCompatibleResource):
self._hydrateBitmapOption(bundle.obj.activity_sharing_networks,
UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER)
bundle.data['like_count'] = Mix.objects.filter(likes__user=bundle.obj).count()
bundle.data['like_count'] = Mix.objects.filter(activity_likes__user=bundle.obj).count()
bundle.data['favourite_count'] = Mix.objects.filter(favourites__user=bundle.obj).count()
bundle.data['follower_count'] = bundle.obj.followers.count()
bundle.data['following_count'] = bundle.obj.following.count()

View File

@@ -0,0 +1,11 @@
from django.core.management.base import NoArgsCommand
from spa.models import Mix
class Command(NoArgsCommand):
def handle_noargs(self, **options):
try:
list = Mix.objects.filter(slug='dss-on-deepvibes-radio-17th-july-jamie-o-sullivan')[0]
pass
except Exception, ex:
print "Debug exception: %s" % ex.message

View File

@@ -71,7 +71,7 @@ class Activity(_BaseModel):
class ActivityFollow(Activity):
to_user = models.ForeignKey('spa.UserProfile', related_name='follower_activity')
to_user = models.ForeignKey('spa.UserProfile', related_name='activity_follow')
def get_target_user(self):
return self.to_user
@@ -93,7 +93,7 @@ class ActivityFollow(Activity):
class ActivityFavourite(Activity):
mix = models.ForeignKey('spa.Mix', related_name='favourites')
mix = models.ForeignKey('spa.Mix', related_name='activity_favourites')
def get_target_user(self):
return self.mix.user
@@ -112,7 +112,7 @@ class ActivityFavourite(Activity):
class ActivityPlay(Activity):
mix = models.ForeignKey('spa.Mix', related_name='plays')
mix = models.ForeignKey('spa.Mix', related_name='activity_plays')
def get_target_user(self):
return self.mix.user
@@ -131,7 +131,7 @@ class ActivityPlay(Activity):
class ActivityLike(Activity):
mix = models.ForeignKey('spa.Mix', related_name='likes')
mix = models.ForeignKey('spa.Mix', related_name='activity_likes')
def get_target_user(self):
return self.mix.user
@@ -150,7 +150,7 @@ class ActivityLike(Activity):
class ActivityDownload(Activity):
mix = models.ForeignKey('spa.Mix', related_name='downloads')
mix = models.ForeignKey('spa.Mix', related_name='activity_downloads')
def get_target_user(self):
return self.mix.user

View File

@@ -51,6 +51,9 @@ class Mix(_BaseModel):
genres = models.ManyToManyField(Genre)
#activity based stuff
favourites = models.ManyToManyField(UserProfile)
def __unicode__(self):
return self.title
@@ -171,7 +174,7 @@ class Mix(_BaseModel):
if user is None:
return False
if user.is_authenticated():
return self.likes.filter(user=user).count() != 0
return self.activity_likes.filter(user=user).count() != 0
return False
@@ -182,9 +185,12 @@ class Mix(_BaseModel):
if user.is_authenticated():
if value:
if self.favourites.filter(user=user).count() == 0:
ActivityFavourite(user=user.get_profile(), mix=self).save()
self.favourites.add(user.get_profile())
self.save()
else:
self.favourites.filter(user=user).delete()
self.favourites.remove(user.get_profile())
self.save()
except Exception, ex:
self.logger.error("Exception updating favourite: %s" % ex.message)
@@ -194,10 +200,10 @@ class Mix(_BaseModel):
return
if user.is_authenticated():
if value:
if self.likes.filter(user=user).count() == 0:
if self.activity_likes.filter(user=user).count() == 0:
ActivityLike(user=user.get_profile(), mix=self).save()
else:
self.likes.filter(user=user).delete()
self.activity_likes.filter(user=user).delete()
except Exception, ex:
self.logger.error("Exception updating like: %s" % ex.message)

View File

@@ -4,6 +4,67 @@ define(['backbone'], function (Backbone) {
this.meta = response.meta || {};
this.page_count = Math.ceil(this.meta.total_count / this.meta.limit);
return response.objects || response;
},
/*
sync: function (method, model, options) {
var headers = {};
if (Backbone.Tastypie.apiKey && Backbone.Tastypie.apiKey.username) {
headers[ 'Authorization' ] = 'ApiKey ' + Backbone.Tastypie.apiKey.username + ':' + Backbone.Tastypie.apiKey.key;
}
if (Backbone.Tastypie.csrfToken) {
headers[ 'X-CSRFToken' ] = Backbone.Tastypie.csrfToken;
}
// Keep `headers` for a potential second request
headers = _.extend(headers, options.headers);
options.headers = headers;
if (( method === 'create' && Backbone.Tastypie.doGetOnEmptyPostResponse ) ||
( method === 'update' && Backbone.Tastypie.doGetOnEmptyPutResponse )) {
var dfd = new $.Deferred();
// Set up 'success' handling
var success = options.success;
dfd.done(function (resp, textStatus, xhr) {
_.isFunction(success) && success(resp);
});
options.success = function (resp, textStatus, xhr) {
// If create is successful but doesn't return a response, fire an extra GET.
// Otherwise, resolve the deferred (which triggers the original 'success' callbacks).
if (!resp && ( xhr.status === 201 || xhr.status === 202 || xhr.status === 204 )) { // 201 CREATED, 202 ACCEPTED or 204 NO CONTENT; response null or empty.
var location = xhr.getResponseHeader('Location') || model.id;
return Backbone.ajax({
url: location,
headers: headers,
success: dfd.resolve,
error: dfd.reject
});
}
else {
return dfd.resolveWith(options.context || options, [ resp, textStatus, xhr ]);
}
};
// Set up 'error' handling
var error = options.error;
dfd.fail(function (xhr, textStatus, errorThrown) {
_.isFunction(error) && error(xhr.responseText);
});
options.error = function (xhr, textStatus, errorText) {
dfd.rejectWith(options.context || options, [ xhr, textStatus, xhr.responseText ]);
};
// Create the request, and make it accessibly by assigning it to the 'request' property on the deferred
dfd.request = Backbone.oldSync(method, model, options);
return dfd;
}
return Backbone.oldSync(method, model, options);
}
});
*/
})
});

View File

@@ -1,22 +1,26 @@
// Generated by CoffeeScript 1.6.2
// Generated by CoffeeScript 1.3.3
(function() {
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
define(['moment', 'app', 'vent', 'marionette', 'utils', 'models/comment/commentCollection', 'views/comment/commentListView', 'text!/tpl/MixListItemView'], function(moment, App, vent, Marionette, utils, CommentsCollection, CommentsListView, Template) {
var MixItemView, _ref;
var MixItemView;
MixItemView = (function(_super) {
__extends(MixItemView, _super);
function MixItemView() {
this.doStart = __bind(this.doStart, this);
this.renderComments = __bind(this.renderComments, this);
this.renderGenres = __bind(this.renderGenres, this);
this.onRender = __bind(this.onRender, this);
this.initialize = __bind(this.initialize, this); _ref = MixItemView.__super__.constructor.apply(this, arguments);
return _ref;
this.initialize = __bind(this.initialize, this);
return MixItemView.__super__.constructor.apply(this, arguments);
}
MixItemView.prototype.template = _.template(Template);
@@ -50,7 +54,6 @@
MixItemView.prototype.onRender = function() {
var id, totalDuration, totalDurationText;
id = this.model.get('id');
if (this.model.get('duration')) {
totalDuration = moment.duration(this.model.get('duration'), "seconds");
@@ -70,7 +73,6 @@
MixItemView.prototype.renderGenres = function() {
var el;
el = this.el;
$.each(this.model.get("genre-list"), function(data) {
$("#genre-list", el).append('<a href="/mixes/' + this.slug + '" class="label label-info">' + this.text + '</a>');
@@ -81,7 +83,6 @@
MixItemView.prototype.renderComments = function() {
var comments;
comments = new CommentsCollection();
comments.url = this.model.get("resource_uri") + "comments/";
comments.mix_id = this.model.id;
@@ -89,7 +90,6 @@
comments.fetch({
success: function(data) {
var content;
console.log(data);
content = new CommentsListView({
collection: comments
@@ -139,7 +139,6 @@
MixItemView.prototype.mixFavourite = function() {
var app;
console.log("MixItemView: favouriteMix");
app = require('app');
vent.trigger("mix:favourite", this.model);
@@ -154,7 +153,6 @@
MixItemView.prototype.mixShare = function(e) {
var mode;
console.log("MixItemView: shareMix");
mode = $(e.currentTarget).data("mode");
console.log("MixItemView: " + mode);

View File

@@ -1,18 +1,18 @@
// Generated by CoffeeScript 1.6.2
// Generated by CoffeeScript 1.3.3
(function() {
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
define(['app', 'moment', 'marionette', 'vent', 'text!/tpl/UserListItemView'], function(App, moment, Marionette, vent, Template) {
var UserItemView, _ref;
var UserItemView;
UserItemView = (function(_super) {
__extends(UserItemView, _super);
function UserItemView() {
this.initialize = __bind(this.initialize, this); _ref = UserItemView.__super__.constructor.apply(this, arguments);
return _ref;
this.initialize = __bind(this.initialize, this);
return UserItemView.__super__.constructor.apply(this, arguments);
}
UserItemView.prototype.template = _.template(Template);