fix subscription list for podcasts w/o last_episode_timestamp
[mygpo.git] / mygpo / web / views / subscriptions.py
bloba62a6666773629fb2e0c16052aab6fe401c727a8
1 from datetime import datetime
3 from django.core.urlresolvers import reverse
4 from django.contrib.auth.decorators import login_required
5 from django.contrib.sites.models import RequestSite
6 from django.shortcuts import render
7 from django.contrib.syndication.views import Feed
8 from django.utils.translation import ugettext as _
9 from django.http import HttpResponse, Http404
10 from django.views.decorators.vary import vary_on_cookie
11 from django.views.decorators.cache import cache_control
13 from mygpo.utils import parse_bool, unzip, skip_pairs
14 from mygpo.decorators import requires_token
15 from mygpo.api import simple
16 from mygpo.users.models import HistoryEntry, User
17 from mygpo.web.utils import symbian_opml_changes, get_podcast_link_target
18 from mygpo.db.couchdb.podcast import podcasts_to_dict
19 from mygpo.db.couchdb.podcast_state import subscriptions_by_user
22 @vary_on_cookie
23 @cache_control(private=True)
24 @login_required
25 def show_list(request):
26 current_site = RequestSite(request)
27 subscriptionlist = create_subscriptionlist(request)
28 return render(request, 'subscriptions.html', {
29 'subscriptionlist': subscriptionlist,
30 'url': current_site
34 @vary_on_cookie
35 @cache_control(private=True)
36 @login_required
37 def download_all(request):
38 podcasts = request.user.get_subscribed_podcasts()
39 response = simple.format_podcast_list(podcasts, 'opml', request.user.username)
40 response['Content-Disposition'] = 'attachment; filename=all-subscriptions.opml'
41 return response
44 @requires_token(token_name='subscriptions_token', denied_template='user_subscriptions_denied.html')
45 def for_user(request, username):
46 user = User.get_user(username)
47 if not user:
48 raise Http404
50 subscriptions = user.get_subscribed_podcasts(public=True)
51 token = user.get_token('subscriptions_token')
53 return render(request, 'user_subscriptions.html', {
54 'subscriptions': subscriptions,
55 'other_user': user,
56 'token': token,
59 @requires_token(token_name='subscriptions_token')
60 def for_user_opml(request, username):
61 user = User.get_user(username)
62 if not user:
63 raise Http404
65 subscriptions = user.get_subscribed_podcasts(public=True)
67 if parse_bool(request.GET.get('symbian', False)):
68 subscriptions = map(symbian_opml_changes, subscriptions)
70 response = render(request, 'user_subscriptions.opml', {
71 'subscriptions': subscriptions,
72 'other_user': user
74 response['Content-Disposition'] = 'attachment; filename=%s-subscriptions.opml' % username
75 return response
78 def create_subscriptionlist(request):
79 user = request.user
80 subscriptions = subscriptions_by_user(user)
82 if not subscriptions:
83 return []
85 # Load all Podcasts and Devices first to ensure that they are
86 # only loaded once, not for each occurance in a subscription
87 public, podcast_ids, device_ids = unzip(subscriptions)
88 podcast_ids= list(set(podcast_ids))
89 device_ids = list(set(device_ids))
91 podcasts = podcasts_to_dict(podcast_ids)
92 devices = dict([ (id, user.get_device(id)) for id in device_ids])
94 subscription_list = {}
95 for public, podcast_id, device_id in subscriptions:
96 device = devices[device_id]
97 if not podcast_id in subscription_list:
98 podcast = podcasts.get(podcast_id, None)
99 if podcast is None:
100 continue
102 subscription_list[podcast_id] = {
103 'podcast': podcasts[podcast_id],
104 'devices': [device] if device else [],
105 'episodes': podcast.episode_count,
107 else:
108 if device:
109 subscription_list[podcast_id]['devices'].append(device)
111 subscriptions = subscription_list.values()
112 sort_key = lambda s: s['podcast'].latest_episode_timestamp or datetime.utcnow()
113 subscriptions = sorted(subscriptions, key=sort_key, reverse=True)
114 return subscriptions
117 @requires_token(token_name='subscriptions_token')
118 def subscriptions_feed(request, username):
119 # Create to feed manually so we can wrap the token-authentication around it
120 f = SubscriptionsFeed(username)
121 obj = f.get_object(request, username)
122 feedgen = f.get_feed(obj, request)
123 response = HttpResponse(mimetype=feedgen.mime_type)
124 feedgen.write(response, 'utf-8')
125 return response
128 class SubscriptionsFeed(Feed):
129 """ A feed showing subscription changes for a certain user """
131 def __init__(self, username):
132 self.username = username
134 def get_object(self, request, username):
135 self.site = RequestSite(request)
136 return User.get_user(username)
138 def title(self, user):
139 return _('%(username)s\'s Podcast Subscriptions on %(site)s') % \
140 dict(username=user.username, site=self.site)
142 def description(self, user):
143 return _('Recent changes to %(username)s\'s podcast subscriptions on %(site)s') % \
144 dict(username=user.username, site=self.site)
146 def link(self, user):
147 return reverse('shared-subscriptions', args=[user.username])
149 def items(self, user):
150 NUM_ITEMS = 20
151 history = user.get_global_subscription_history(public=True)
152 history = skip_pairs(history)
153 history = list(history)[-NUM_ITEMS:]
154 history = HistoryEntry.fetch_data(user, history)
155 history = filter(lambda e:e.podcast, history)
156 return history
158 def author_name(self, user):
159 return user.username
161 def author_link(self, user):
162 return reverse('shared-subscriptions', args=[user.username])
164 # entry-specific data below
166 description_template = "subscription-feed-description.html"
168 def item_title(self, entry):
169 if entry.action == 'subscribe':
170 s = _('%(username)s subscribed to %(podcast)s (%(site)s)')
171 else:
172 s = _('%(username)s unsubscribed from %(podcast)s (%(site)s)')
174 return s % dict(username=self.username,
175 podcast=entry.podcast.display_title,
176 site=self.site)
178 def item_link(self, item):
179 return get_podcast_link_target(item.podcast)
181 def item_pubdate(self, item):
182 return item.timestamp