[Web] remove now unused slow episode pagination
[mygpo.git] / mygpo / web / views / podcast.py
bloba2baa8c52aa7c32bf1af89e8560621a22e8ce9ca
1 from functools import wraps, partial
2 from datetime import datetime
4 from django.core.urlresolvers import reverse
5 from django.http import HttpResponseBadRequest, HttpResponseRedirect, Http404
6 from django.shortcuts import render
7 from django.contrib.auth.decorators import login_required
8 from django.contrib.sites.models import RequestSite
9 from django.utils.translation import ugettext as _
10 from django.contrib import messages
11 from django.views.decorators.vary import vary_on_cookie
12 from django.views.decorators.cache import never_cache, cache_control
13 from django.shortcuts import get_object_or_404
14 from django.contrib.contenttypes.models import ContentType
16 from mygpo.podcasts.models import Podcast, PodcastGroup, Episode, Tag
17 from mygpo.users.models import SubscriptionException
18 from mygpo.subscriptions.models import Subscription
19 from mygpo.subscriptions import (
20 subscribe as subscribe_podcast,
21 unsubscribe as unsubscribe_podcast,
22 subscribe_all as subscribe_podcast_all,
23 unsubscribe_all as unsubscribe_podcast_all,
24 get_subscribe_targets
26 from mygpo.history.models import HistoryEntry
27 from mygpo.core.tasks import flattr_thing
28 from mygpo.utils import normalize_feed_url
29 from mygpo.users.settings import PUBLIC_SUB_PODCAST, FLATTR_TOKEN
30 from mygpo.publisher.utils import check_publisher_permission
31 from mygpo.usersettings.models import UserSettings
32 from mygpo.users.models import Client
33 from mygpo.web.forms import SyncForm
34 from mygpo.decorators import allowed_methods
35 from mygpo.web.utils import get_podcast_link_target, get_page_list, \
36 check_restrictions
38 import logging
39 logger = logging.getLogger(__name__)
42 @vary_on_cookie
43 @cache_control(private=True)
44 @allowed_methods(['GET'])
45 def show(request, podcast):
46 """ Shows a podcast detail page """
48 podcast = check_restrictions(podcast)
50 current_site = RequestSite(request)
51 num_episodes = 20
52 episodes = episode_list(podcast, request.user, limit=num_episodes)
53 user = request.user
55 max_listeners = max([e.listeners for e in episodes] + [0])
57 episode = None
59 if episodes:
60 episode = episodes[0]
61 episodes = episodes[1:]
63 if podcast.group:
64 group = podcast.group
65 rel_podcasts = group.podcast_set.exclude(pk=podcast.pk)
66 else:
67 rel_podcasts = []
69 tags = get_tags(podcast, user)
70 has_tagged = any(t['is_own'] for t in tags)
72 if user.is_authenticated():
73 subscribed_devices = Client.objects.filter(
74 subscription__user=user,
75 subscription__podcast=podcast,
78 subscribe_targets = get_subscribe_targets(podcast, user)
80 has_history = HistoryEntry.objects.filter(user=user, podcast=podcast)\
81 .exists()
82 can_flattr = (user.profile.settings.get_wksetting(FLATTR_TOKEN) and
83 podcast.flattr_url)
85 else:
86 has_history = False
87 subscribed_devices = []
88 subscribe_targets = []
89 can_flattr = False
91 is_publisher = check_publisher_permission(user, podcast)
93 episodes_total = podcast.episode_count or 0
94 num_pages = episodes_total / num_episodes
95 page_list = get_page_list(1, num_pages, 1, 15)
97 return render(request, 'podcast.html', {
98 'tags': tags,
99 'has_tagged': has_tagged,
100 'url': current_site,
101 'has_history': has_history,
102 'podcast': podcast,
103 'devices': subscribed_devices,
104 'related_podcasts': rel_podcasts,
105 'can_subscribe': len(subscribe_targets) > 0,
106 'subscribe_targets': subscribe_targets,
107 'episode': episode,
108 'episodes': episodes,
109 'max_listeners': max_listeners,
110 'can_flattr': can_flattr,
111 'is_publisher': is_publisher,
112 'page_list': page_list,
113 'current_page': 1,
117 def get_tags(podcast, user, max_tags=50):
118 """ Returns all tags that user sees for the given podcast
120 The tag list is a list of dicts in the form of {'tag': 'tech', 'is_own':
121 True}. "is_own" indicates if the tag was created by the given user. """
122 tags = {}
124 for tag in podcast.tags.all():
125 t = tag.tag.lower()
126 if not t in tags:
127 tags[t] = {'tag': t, 'is_own': False}
129 if tag.user == user:
130 tags[t]['is_own'] = True
132 return tags.values()
135 def episode_list(podcast, user, offset=0, limit=20):
136 """ Returns a list of episodes """
137 # fast pagination by using Episode.order instead of offset/limit
138 page_start = podcast.max_episode_order - offset
139 page_end = page_start - limit
140 return Episode.objects.filter(podcast=podcast,
141 order__lte=page_start,
142 order__gt=page_end)\
143 .order_by('-order')
146 def all_episodes(request, podcast, page_size=20):
148 # Make sure page request is an int. If not, deliver first page.
149 try:
150 page = int(request.GET.get('page', '1'))
151 except ValueError:
152 page = 1
154 user = request.user
156 episodes = episode_list(podcast, user, (page-1) * page_size, page_size)
157 episodes_total = podcast.episode_count or 0
158 num_pages = episodes_total / page_size
159 page_list = get_page_list(1, num_pages, page, 15)
161 max_listeners = max([e.listeners for e in episodes] + [0])
163 is_publisher = check_publisher_permission(user, podcast)
165 return render(request, 'episodes.html', {
166 'podcast': podcast,
167 'episodes': episodes,
168 'max_listeners': max_listeners,
169 'page_list': page_list,
170 'current_page': page,
171 'is_publisher': is_publisher,
175 @never_cache
176 @login_required
177 def add_tag(request, podcast):
179 tag_str = request.GET.get('tag', '')
180 if not tag_str:
181 return HttpResponseBadRequest()
183 user = request.user
185 tags = tag_str.split(',')
186 tags = map(unicode.strip, tags)
188 ContentType.objects.get_for_model(podcast)
190 for tag in tags:
191 Tag.objects.get_or_create(
192 tag=tag,
193 source=Tag.USER,
194 user=user,
195 content_type=ContentType.objects.get_for_model(podcast),
196 object_id=podcast.id,
199 if request.GET.get('next', '') == 'mytags':
200 return HttpResponseRedirect('/tags/')
202 return HttpResponseRedirect(get_podcast_link_target(podcast))
205 @never_cache
206 @login_required
207 def remove_tag(request, podcast):
209 tag_str = request.GET.get('tag', '')
210 if not tag_str:
211 return HttpResponseBadRequest()
213 user = request.user
215 tags = tag_str.split(',')
216 tags = map(unicode.strip, tags)
218 ContentType.objects.get_for_model(podcast)
220 Tag.objects.filter(
221 tag__in=tags,
222 source=Tag.USER,
223 user=user,
224 content_type=ContentType.objects.get_for_model(podcast),
225 object_id=podcast.id,
226 ).delete()
228 if request.GET.get('next', '') == 'mytags':
229 return HttpResponseRedirect('/tags/')
231 return HttpResponseRedirect(get_podcast_link_target(podcast))
234 @never_cache
235 @login_required
236 @allowed_methods(['GET', 'POST'])
237 def subscribe(request, podcast):
239 if request.method == 'POST':
241 # multiple UIDs from the /podcast/<slug>/subscribe
242 device_uids = [k for (k,v) in request.POST.items() if k==v]
244 # single UID from /podcast/<slug>
245 if 'targets' in request.POST:
246 devices = request.POST.get('targets')
247 devices = devices.split(',')
248 device_uids.extend(devices)
250 for uid in device_uids:
251 try:
252 device = request.user.client_set.get(uid=uid)
253 subscribe_podcast(podcast, request.user, device)
255 except Client.DoesNotExist as e:
256 messages.error(request, str(e))
258 return HttpResponseRedirect(get_podcast_link_target(podcast))
260 targets = get_subscribe_targets(podcast, request.user)
262 return render(request, 'subscribe.html', {
263 'targets': targets,
264 'podcast': podcast,
268 @never_cache
269 @login_required
270 @allowed_methods(['POST'])
271 def subscribe_all(request, podcast):
272 """ subscribe all of the user's devices to the podcast """
273 user = request.user
274 subscribe_podcast_all(podcast, user)
275 return HttpResponseRedirect(get_podcast_link_target(podcast))
278 @never_cache
279 @login_required
280 def unsubscribe(request, podcast, device_uid):
282 return_to = request.GET.get('return_to', None)
284 if not return_to:
285 raise Http404('Wrong URL')
287 user = request.user
288 try:
289 device = user.client_set.get(uid=device_uid)
291 except Client.DoesNotExist as e:
292 messages.error(request, str(e))
293 return HttpResponseRedirect(return_to)
295 try:
296 unsubscribe_podcast(podcast, user, device)
297 except SubscriptionException as e:
298 logger.exception('Web: %(username)s: could not unsubscribe from podcast %(podcast_url)s on device %(device_id)s' %
299 {'username': request.user.username, 'podcast_url': podcast.url, 'device_id': device.id})
301 return HttpResponseRedirect(return_to)
304 @never_cache
305 @login_required
306 @allowed_methods(['POST'])
307 def unsubscribe_all(request, podcast):
308 """ unsubscribe all of the user's devices from the podcast """
309 user = request.user
310 unsubscribe_podcast_all(podcast, user)
311 return HttpResponseRedirect(get_podcast_link_target(podcast))
314 @never_cache
315 @login_required
316 def subscribe_url(request):
317 url = request.GET.get('url', None)
319 if not url:
320 raise Http404('http://my.gpodder.org/subscribe?url=http://www.example.com/podcast.xml')
322 url = normalize_feed_url(url)
324 if not url:
325 raise Http404('Please specify a valid url')
327 podcast = Podcast.objects.get_or_create_for_url(url)
329 return HttpResponseRedirect(get_podcast_link_target(podcast, 'subscribe'))
332 @never_cache
333 @allowed_methods(['POST'])
334 def set_public(request, podcast, public):
335 settings, created = UserSettings.objects.get_or_create(
336 user=request.user,
337 content_type=ContentType.objects.get_for_model(podcast),
338 object_id=podcast.pk,
340 settings.set_wksetting(PUBLIC_SUB_PODCAST, public)
341 settings.save()
342 return HttpResponseRedirect(get_podcast_link_target(podcast))
345 @never_cache
346 @login_required
347 def flattr_podcast(request, podcast):
348 """ Flattrs a podcast, records an event and redirects to the podcast """
350 user = request.user
351 site = RequestSite(request)
352 now = datetime.utcnow()
354 # do flattring via the tasks queue, but wait for the result
355 task = flattr_thing.delay(user, podcast.get_id(), site.domain,
356 request.is_secure(), 'Podcast')
357 success, msg = task.get()
359 if success:
360 messages.success(request, _("Flattr\'d"))
362 else:
363 messages.error(request, msg)
365 return HttpResponseRedirect(get_podcast_link_target(podcast))
368 # To make all view accessible via either IDs or Slugs
369 # a decorator queries the podcast and passes the Id on to the
370 # regular views
372 def slug_decorator(f):
373 @wraps(f)
374 def _decorator(request, slug, *args, **kwargs):
376 try:
377 podcast = Podcast.objects.filter(
378 slugs__slug=slug,
379 slugs__content_type=ContentType.objects.get_for_model(Podcast),
381 podcast = podcast.prefetch_related('slugs', 'urls').get()
382 except Podcast.DoesNotExist:
383 raise Http404
385 # redirect when a non-cannonical slug is used
386 if slug != podcast.slug:
387 return HttpResponseRedirect(get_podcast_link_target(podcast))
389 return f(request, podcast, *args, **kwargs)
391 return _decorator
394 def id_decorator(f):
395 @wraps(f)
396 def _decorator(request, podcast_id, *args, **kwargs):
398 try:
399 podcast = Podcast.objects.filter(id=podcast_id)
400 podcast = podcast.prefetch_related('slugs', 'urls').get()
402 # if the podcast has a slug, redirect to its canonical URL
403 if podcast.slug:
404 return HttpResponseRedirect(get_podcast_link_target(podcast))
406 return f(request, podcast, *args, **kwargs)
408 except Podcast.DoesNotExist:
409 podcast = get_object_or_404(Podcast, merged_uuids__uuid=podcast_id)
410 return HttpResponseRedirect(get_podcast_link_target(podcast))
412 return _decorator
415 show_slug = slug_decorator(show)
416 subscribe_slug = slug_decorator(subscribe)
417 subscribe_all_slug = slug_decorator(subscribe_all)
418 unsubscribe_slug = slug_decorator(unsubscribe)
419 unsubscribe_all_slug = slug_decorator(unsubscribe_all)
420 add_tag_slug = slug_decorator(add_tag)
421 remove_tag_slug = slug_decorator(remove_tag)
422 set_public_slug = slug_decorator(set_public)
423 all_episodes_slug = slug_decorator(all_episodes)
424 flattr_podcast_slug = slug_decorator(flattr_podcast)
427 show_id = id_decorator(show)
428 subscribe_id = id_decorator(subscribe)
429 subscribe_all_id = id_decorator(subscribe_all)
430 unsubscribe_id = id_decorator(unsubscribe)
431 unsubscribe_all_id = id_decorator(unsubscribe_all)
432 add_tag_id = id_decorator(add_tag)
433 remove_tag_id = id_decorator(remove_tag)
434 set_public_id = id_decorator(set_public)
435 all_episodes_id = id_decorator(all_episodes)
436 flattr_podcast_id = id_decorator(flattr_podcast)