Feed working

This commit is contained in:
Fergal Moran
2016-07-06 00:11:39 +01:00
parent ef67cba94f
commit 064b19affa
15 changed files with 262 additions and 6 deletions

View File

@@ -98,6 +98,17 @@ class LikeSerializer(serializers.ModelSerializer):
display_name = serializers.ReadOnlyField(source='get_nice_name') display_name = serializers.ReadOnlyField(source='get_nice_name')
class FavouriteSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = (
'display_name',
'slug',
)
display_name = serializers.ReadOnlyField(source='get_nice_name')
class GenreSerializer(serializers.ModelSerializer): class GenreSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Genre model = Genre
@@ -162,6 +173,7 @@ class MixSerializer(serializers.ModelSerializer):
'plays', 'plays',
'downloads', 'downloads',
'is_liked', 'is_liked',
'is_favourited',
] ]
@@ -174,10 +186,11 @@ class MixSerializer(serializers.ModelSerializer):
genres = GenreSerializer(many=True, required=False, read_only=True) genres = GenreSerializer(many=True, required=False, read_only=True)
likes = LikeSerializer(many=True, required=False, read_only=True) # slug_field='slug', many=True, read_only=True) likes = LikeSerializer(many=True, required=False, read_only=True) # slug_field='slug', many=True, read_only=True)
favourites = serializers.SlugRelatedField(slug_field='slug', many=True, read_only=True) favourites = FavouriteSerializer(many=True, required=False, read_only=True) # slug_field='slug', many=True, read_only=True)
plays = InlineActivityPlaySerializer(many=True, read_only=True, source='activity_plays') plays = InlineActivityPlaySerializer(many=True, read_only=True, source='activity_plays')
downloads = InlineActivityDownloadSerializer(read_only=True, source='activity_downloads') downloads = InlineActivityDownloadSerializer(read_only=True, source='activity_downloads')
is_liked = serializers.SerializerMethodField(read_only=True) is_liked = serializers.SerializerMethodField(read_only=True)
is_favourited = serializers.SerializerMethodField(read_only=True)
def update(self, instance, validated_data): def update(self, instance, validated_data):
# all nested representations need to be serialized separately here # all nested representations need to be serialized separately here
@@ -202,6 +215,25 @@ class MixSerializer(serializers.ModelSerializer):
except UserProfile.DoesNotExist: except UserProfile.DoesNotExist:
pass pass
favourites = self.initial_data['favourites']
unfavourited = instance.favourites.exclude(user__userprofile__slug__in=[f['slug'] for f in favourites])
for uf in unfavourited:
# check that the user removing the like is an instance of the current user
# for now, only the current user can like stuff
if uf == self.context['request'].user.userprofile:
instance.update_favourite(uf, False)
for favourite in favourites:
# check that the user adding the like is an instance of the current user
# for now, only the current user can like stuff
try:
user = UserProfile.objects.get(slug=favourite['slug'])
if user is not None and user == self.context['request'].user.userprofile:
instance.update_favourite(user, True)
except UserProfile.DoesNotExist:
pass
genres = self.initial_data['genres'] genres = self.initial_data['genres']
instance.genres.clear() instance.genres.clear()
for genre in genres: for genre in genres:
@@ -224,6 +256,8 @@ class MixSerializer(serializers.ModelSerializer):
return super(MixSerializer, self).update(instance, validated_data) return super(MixSerializer, self).update(instance, validated_data)
except MixUpdateException as ex: except MixUpdateException as ex:
raise ex raise ex
except Exception as ex:
raise ex
def is_valid(self, raise_exception=False): def is_valid(self, raise_exception=False):
return super(MixSerializer, self).is_valid(raise_exception) return super(MixSerializer, self).is_valid(raise_exception)

View File

@@ -123,13 +123,12 @@ INSTALLED_APPS = (
'allauth.socialaccount.providers.twitter', 'allauth.socialaccount.providers.twitter',
'pipeline', 'pipeline',
'dbbackup', 'gunicorn',
'corsheaders', 'corsheaders',
'sorl.thumbnail', 'sorl.thumbnail',
'djcelery', 'djcelery',
'spa', 'spa',
'gunicorn',
'spa.signals', 'spa.signals',
'core', 'core',
'storages', 'storages',
@@ -254,7 +253,9 @@ SOCIAL_AUTH_AUTHENTICATION_BACKENDS = (
'social.backends.yahoo.YahooOpenId' 'social.backends.yahoo.YahooOpenId'
) )
"""
DBBACKUP_STORAGE = 'dbbackup.storage.dropbox_storage' DBBACKUP_STORAGE = 'dbbackup.storage.dropbox_storage'
DBBACKUP_TOKENS_FILEPATH = '._dss_tokens' DBBACKUP_TOKENS_FILEPATH = '._dss_tokens'
DBBACKUP_DROPBOX_APP_KEY = localsettings.DSS_DB_BACKUP_KEY DBBACKUP_DROPBOX_APP_KEY = localsettings.DSS_DB_BACKUP_KEY
DBBACKUP_DROPBOX_APP_SECRET = localsettings.DSS_DB_BACKUP_SECRET DBBACKUP_DROPBOX_APP_SECRET = localsettings.DSS_DB_BACKUP_SECRET
"""

View File

@@ -14,6 +14,7 @@ urlpatterns = patterns(
(r'^_embed/', include('spa.embedding.urls')), (r'^_embed/', include('spa.embedding.urls')),
(r'^__redir/blog/', include('spa.blog.urls')), (r'^__redir/blog/', include('spa.blog.urls')),
(r'^__redir/social/', include('spa.social.urls')), (r'^__redir/social/', include('spa.social.urls')),
(r'^podcast/', include('spa.podcast.urls')),
url(r'', include('user_sessions.urls', 'user_sessions')), url(r'', include('user_sessions.urls', 'user_sessions')),
url(r'^', include('api.urls')), url(r'^', include('api.urls')),
) )

View File

@@ -22,7 +22,6 @@ google-api-python-client
django-celery django-celery
django-scheduler django-scheduler
django-recurrence django-recurrence
# django-dbbackup
azure azure

View File

@@ -0,0 +1,16 @@
import uuid
from django.core.management.base import NoArgsCommand
from spa.models import UserProfile
class Command(NoArgsCommand):
def handle_noargs(self, **options):
try:
users = UserProfile.objects.exclude(uid__isnull=False)
for user in users:
user.uid = uuid.uuid4()
user.save()
except Exception as ex:
print("Debug exception: %s" % ex)

View File

@@ -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='userprofile',
name='uid',
field=models.UUIDField(editable=False, null=True),
),
]

View File

@@ -238,7 +238,7 @@ class Mix(BaseModel):
if user.user.is_authenticated(): if user.user.is_authenticated():
if value: if value:
if self.favourites.filter(user=user.user).count() == 0: if self.favourites.filter(user=user.user).count() == 0:
fav = ActivityFavourite(user=user) # , mix=self) fav = ActivityFavourite(user=user, mix=self)
fav.save() fav.save()
self.favourites.add(user) self.favourites.add(user)
self.save() self.save()

View File

@@ -7,6 +7,7 @@ from django.contrib.auth.models import User
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
from django.db import models from django.db import models
from django.db.models import Count from django.db.models import Count
import uuid
from django_gravatar.helpers import has_gravatar, get_gravatar_url from django_gravatar.helpers import has_gravatar, get_gravatar_url
from sorl import thumbnail from sorl import thumbnail
@@ -41,6 +42,8 @@ class UserProfile(BaseModel):
ACTIVITY_SHARE_NETWORK_TWITTER = 2 ACTIVITY_SHARE_NETWORK_TWITTER = 2
user = models.OneToOneField(User, unique=True, related_name='userprofile') user = models.OneToOneField(User, unique=True, related_name='userprofile')
uid = models.UUIDField(primary_key=False, editable=False, null=True)
avatar_type = models.CharField(max_length=15, default='social') avatar_type = models.CharField(max_length=15, default='social')
avatar_image = models.ImageField(max_length=1024, blank=True, upload_to=avatar_name) avatar_image = models.ImageField(max_length=1024, blank=True, upload_to=avatar_name)
display_name = models.CharField(blank=True, max_length=35) display_name = models.CharField(blank=True, max_length=35)

6
spa/podcast/urls.py Normal file
View File

@@ -0,0 +1,6 @@
from django.conf.urls import patterns, url
urlpatterns = patterns(
'',
url(r'^(?P<uid>[\w\d_.-]+)/favourites/?$', 'spa.podcast.views.favourites', name='podast_favourites_slug'),
)

35
spa/podcast/views.py Normal file
View File

@@ -0,0 +1,35 @@
from django.http import Http404
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template import RequestContext
from spa.models import UserProfile
def favourites(request, uid):
try:
user = UserProfile.objects.order_by('-id').get(uid=uid)
except UserProfile.DoesNotExist:
raise Http404("User does not exist")
fav_list = user.favourites.all()
return _render_podcast(request, user, fav_list)
def _render_podcast(request, user, list):
context = {
'title': 'DSS Favourites',
'description': 'All your favourites on Deep South Sounds',
'link': 'https://deepsouthsounds.com/',
'user': user.first_name,
'summary': 'Deep South Sounds is a collective of like minded house heads from Ireland&quot;s Deep South',
'last_build_date': list[0].upload_date,
'objects': list,
}
response = render_to_response(
'podcast/feed.xml',
context=context,
context_instance=RequestContext(request),
content_type='application/rss+xml'
)
return response

View File

View File

@@ -0,0 +1,33 @@
from django import template
import datetime
import time
from email import utils
register = template.Library()
@register.filter
def get_mix_url(obj):
return obj.get_full_url()
@register.filter
def get_mix_audio_url(obj):
return obj.get_download_url()
@register.filter
def seconds_to_hms(seconds):
try:
m, s = divmod(seconds, 60)
h, m = divmod(m, 60)
return "%d:%02d:%02d" % (h, m, s)
except Exception as ex:
return "00:00:09"
@register.filter
def date_to_rfc2822(date):
nowtuple = date.timetuple()
nowtimestamp = time.mktime(nowtuple)
return utils.formatdate(nowtimestamp)

BIN
static/img/podcast_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<channel>
{% load dss_extras %}
<title>{{ title }}</title>
<description>{{ description }}</description>
<link>{{ link }}</link>
<language>en-ie</language>
<copyright>Copyright 2016</copyright>
<lastBuildDate>{{ last_build_date|date_to_rfc2822 }}</lastBuildDate>
<pubDate>{{ last_build_date|date_to_rfc2822 }}</pubDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<webMaster>webmaster@deepsouthsounds.com</webMaster>
<itunes:author>{{ user }} @ deepsouthsounds</itunes:author>
<itunes:subtitle>{{ title }}</itunes:subtitle>
<itunes:summary>{{ summary }}</itunes:summary>
<itunes:owner>
<itunes:name>Fergal Moran</itunes:name>
<itunes:email>fergal@deepsouthsounds.com</itunes:email>
</itunes:owner>
<itunes:explicit>No</itunes:explicit>
<itunes:image href="https://dsscdn2.blob.core.windows.net/static/podcast_logo.png"/>
<itunes:category text="Technology">
<itunes:category text="Podcasting"/>
</itunes:category>
{% for item in objects %}
<item>
<title>{{ item.title }}</title>
<link>{{ item|get_mix_url }}</link>
<guid>{{ item|get_mix_url }}</guid>
<description>{{ item.description }}</description>
<enclosure url="{{ item|get_mix_audio_url }}" length="{{ item.duration }}" type="audio/mpeg"/>
<category>Podcasts</category>
<pubDate>{{ item.upload_date|date_to_rfc2822 }}</pubDate>
<itunes:author>{{ item.user.display_name }}</itunes:author>
<itunes:explicit>No</itunes:explicit>
<itunes:subtitle>{{ item.description }}</itunes:subtitle>
<itunes:summary>{{ item.description }}</itunes:summary>
<itunes:duration>{{ item.duration|seconds_to_hms }}</itunes:duration>
<itunes:keywords>deep south sounds, deep house, Cork, Fergal Moran, Ed Dunlea, {{ item.title }}, {{ item.user.display_name }}
</itunes:keywords>
</item>
{% endfor %}
</channel>
</rss>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<channel>
<title>{{ object.title }}</title>
<link>{{ object.link }}</link>
<description>{{ object.description|striptags }}</description>
{% if object.language %}<language>{{ object.language }}</language>{% endif %}
<copyright>&#x2117; &amp; &#xA9; {% now "Y" %} {{ object.organization }}. {{ object.copyright }}.</copyright>
<managingEditor>{% for author in object.author.all %}{% if forloop.first %}{% else %}{% if forloop.last %} and {% else %}, {% endif %}{% endif %}{{ author.email }}{% endfor %}</managingEditor>
{% if object.author.email or object.webmaster.email %}<webMaster>{% if object.webmaster.email %}{{ object.webmaster.email }}{% else %}{% endif %}</webMaster>{% endif %}
<lastBuildDate>{{ object.list.0.date|date:"r" }}</lastBuildDate>
{% if object.category_show %}<category{% if object.domain %} domain="{{ object.domain }}"{% endif %}>{{ object.category_show }}</category>{% endif %}
<generator>Django Web Framework</generator>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
{% if object.ttl %}<ttl>{{ object.ttl }}</ttl>{% endif %}
{% if object.image %}<image>
<url>{{ object.image.url }}</url>
<title>{{ object.title }}</title>
<link>{{ object.link }}</link>
</image>{% endif %}
<itunes:author>{{ object.organization }}</itunes:author>
<itunes:owner>
<itunes:name>{% for author in object.author.all %}{% if forloop.first %}{% else %}{% if forloop.last %} and {% else %}, {% endif %}{% endif %}{% if author.first_name or author.last_name %}{% if author.first_name and author.last_name %}{{ author.first_name }} {{ author.last_name }}{% endif %}{% if author.first_name and not author.last_name %}{{ author.first_name }}{% endif %}{% if author.last_name and not author.first_name %}{{ author.last_name }}{% endif %}{% else %}{{ author.username }}{% endif %}{% endfor %}</itunes:name>
<itunes:email>{% for author in object.author.all %}{{ author.email }}{% if forloop.last %}{% else %}, {% endif %}{% endfor %}</itunes:email>
</itunes:owner>
{% if object.subtitle %}<itunes:subtitle>{{ object.subtitle }}</itunes:subtitle>{% endif %}
<itunes:summary>{% if object.summary %}{{ object.summary|striptags }}{% else %}{{ object.description|striptags }}{% endif %}</itunes:summary>
{% if object.image %}<itunes:image href="{{ object.image.url }}" />{% endif %}
{% if object.category.all %}{% for category in object.category.all %}{% if category.name %}<itunes:category text="{{ category.parent.name }}">
<itunes:category text="{{ category.name }}" />
</itunes:category>
{% else %}<itunes:category text="{{ category.parent.name }}" />
{% endif %}{% endfor %}{% endif %}
{% if object.explicit %}<itunes:explicit>{{ object.explicit|lower }}</itunes:explicit>{% endif %}
{% if object.block %}<itunes:block>yes</itunes:block>{% endif %}
{% if object.redirect %}<itunes:new-feed-url>{{ object.redirect }}</itunes:new-feed-url>{% endif %}
{% for episode in object.episode_set.published %}<item>
<title>{{ episode.title }}</title>
<link>{{ episode.enclosure_set.all.0.file.url }}</link>
<description>{{ episode.description|striptags }}</description>
<author>{% for author in object.author.all %}{{ author.email }}{% if forloop.last %}{% else %}, {% endif %}{% endfor %}</author>
{% if episode.category %}<category{% if episode.domain %} url="{{ episode.domain }}"{% endif %}>{{ episode.category }}</category>{% endif %}
<enclosure url="{{ episode.enclosure_set.all.0.file.url }}" length="{{ episode.enclosure_set.all.0.file.size }}" type="{{ episode.enclosure_set.all.0.mime }}" />
<guid isPermalink="true">{{ episode.enclosure_set.all.0.file.url }}</guid>
<pubDate>{{ episode.date|date:"r" }} GMT</pubDate>
<itunes:author>{% for author in episode.author.all %}{% if forloop.first %}{% else %}{% if forloop.last %} and {% else %}, {% endif %}{% endif %}{% if author.first_name or author.last_name %}{% if author.first_name and author.last_name %}{{ author.first_name }} {{ author.last_name }}{% endif %}{% if author.first_name and not author.last_name %}{{ author.first_name }}{% endif %}{% if author.last_name and not author.first_name %}{{ author.last_name }}{% endif %}{% else %}{{ author.username }}{% endif %}{% endfor %}</itunes:author>
{% if episode.subtitle %}<itunes:subtitle>{{ episode.subtitle }}</itunes:subtitle>{% endif %}
<itunes:summary>{% if episode.summary %}{{ episode.summary|striptags }}{% else %}{{ episode.description|striptags }}{% endif %}</itunes:summary>
{% if episode.minutes and episode.seconds %}<itunes:duration>{{ episode.minutes }}:{{ episode.seconds }}</itunes:duration>{% endif %}
{% if episode.keywords %}<itunes:keywords>{{ episode.keywords }}</itunes:keywords>{% endif %}
{% if episode.explicit %}<itunes:explicit>{{ episode.explicit|lower }}</itunes:explicit>{% endif %}
{% if episode.block %}<itunes:block>yes</itunes:block>{% endif %}
</item>
{% endfor %}
</channel>
</rss>