[UserSettings] refactor, add tests
[mygpo.git] / mygpo / web / views / episode.py
blob404996b1bcd19e9368feed34eec87648ee14223a
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 from datetime import datetime
19 from functools import wraps
21 import dateutil.parser
23 from django.shortcuts import render
24 from django.http import HttpResponseRedirect, Http404
25 from django.contrib.auth.decorators import login_required
26 from django.contrib.sites.models import RequestSite
27 from django.contrib.contenttypes.models import ContentType
28 from django.views.decorators.vary import vary_on_cookie
29 from django.views.decorators.cache import never_cache, cache_control
30 from django.contrib import messages
31 from django.utils.translation import ugettext as _
33 from mygpo.podcasts.models import Podcast, Episode
34 from mygpo.api.constants import EPISODE_ACTION_TYPES
35 from mygpo.core.tasks import flattr_thing
36 from mygpo.utils import parse_time, get_timestamp
37 from mygpo.users.settings import FLATTR_TOKEN
38 from mygpo.web.heatmap import EpisodeHeatmap
39 from mygpo.history.stats import last_played_episodes
40 from mygpo.publisher.utils import check_publisher_permission
41 from mygpo.web.utils import get_episode_link_target, check_restrictions
42 from mygpo.history.models import EpisodeHistoryEntry
43 from mygpo.favorites.models import FavoriteEpisode
44 from mygpo.userfeeds.feeds import FavoriteFeed
47 @vary_on_cookie
48 @cache_control(private=True)
49 def episode(request, episode):
51 podcast = episode.podcast
53 podcast = check_restrictions(podcast)
55 user = request.user
57 if not podcast:
58 raise Http404
60 if user.is_authenticated():
62 is_fav = FavoriteEpisode.objects.filter(user=user, episode=episode)\
63 .exists()
65 # pre-populate data for fetch_data
66 podcasts_dict = {podcast.get_id(): podcast}
67 episodes_dict = {episode.id.hex: episode}
69 has_history = EpisodeHistoryEntry.objects.filter(user=user,
70 episode=episode)\
71 .exists()
73 played_parts = EpisodeHeatmap(podcast, episode, user, episode.duration)
75 devices = {c.id.hex: c for c in user.client_set.all()}
76 can_flattr = user.profile.settings.get_wksetting(FLATTR_TOKEN) and episode.flattr_url
78 else:
79 has_history = False
80 is_fav = False
81 played_parts = None
82 devices = {}
83 can_flattr = False
85 is_publisher = check_publisher_permission(user, podcast)
87 prev = None #podcast.get_episode_before(episode)
88 next = None #podcast.get_episode_after(episode)
90 return render(request, 'episode.html', {
91 'episode': episode,
92 'podcast': podcast,
93 'prev': prev,
94 'next': next,
95 'has_history': has_history,
96 'is_favorite': is_fav,
97 'played_parts': played_parts,
98 'actions': EPISODE_ACTION_TYPES,
99 'devices': devices,
100 'can_flattr': can_flattr,
101 'is_publisher': is_publisher,
105 @never_cache
106 @login_required
107 @vary_on_cookie
108 @cache_control(private=True)
109 def history(request, episode):
110 """ shows the history of the episode """
112 user = request.user
113 podcast = episode.podcast
115 history = EpisodeHistoryEntry.objects.filter(user=user,
116 episode=episode,)\
117 .order_by('-timestamp')\
118 .prefetch_related('episode',
119 'episode__slugs',
120 'episode__podcast',
121 'episode__podcast__slugs',
122 'client')
124 clients = user.client_set.all()
126 return render(request, 'episode-history.html', {
127 'episode': episode,
128 'podcast': podcast,
129 'history': history,
130 'actions': EPISODE_ACTION_TYPES,
131 'clients': clients,
135 @never_cache
136 @login_required
137 def toggle_favorite(request, episode):
138 user = request.user
140 fav, created = FavoriteEpisode.objects.get_or_create(
141 user=user,
142 episode=episode,
145 # if the episode was already a favorite, remove it
146 if not created:
147 fav.delete()
149 podcast = episode.podcast
150 return HttpResponseRedirect(get_episode_link_target(episode, podcast))
154 @vary_on_cookie
155 @cache_control(private=True)
156 @login_required
157 def list_favorites(request):
158 user = request.user
159 site = RequestSite(request)
161 favorites = FavoriteEpisode.episodes_for_user(user)
163 recently_listened = last_played_episodes(user)
165 favfeed = FavoriteFeed(user)
166 feed_url = favfeed.get_public_url(site.domain)
168 podcast = Podcast.objects.filter(urls__url=feed_url).first()
170 token = request.user.profile.favorite_feeds_token
172 return render(request, 'favorites.html', {
173 'episodes': favorites,
174 'feed_token': token,
175 'site': site,
176 'podcast': podcast,
177 'recently_listened': recently_listened,
181 @never_cache
182 def add_action(request, episode):
184 user = request.user
185 client = user.client_set.get(id=request.POST.get('device'))
187 action_str = request.POST.get('action')
188 timestamp = request.POST.get('timestamp', '')
190 if timestamp:
191 try:
192 timestamp = dateutil.parser.parse(timestamp)
193 except (ValueError, AttributeError):
194 timestamp = datetime.utcnow()
195 else:
196 timestamp = datetime.utcnow()
198 EpisodeHistoryEntry.create_entry(user, episode, action_str, client,
199 timestamp)
200 podcast = episode.podcast
201 return HttpResponseRedirect(get_episode_link_target(episode, podcast))
204 @never_cache
205 @login_required
206 def flattr_episode(request, episode):
207 """ Flattrs an episode, records an event and redirects to the episode """
209 user = request.user
210 site = RequestSite(request)
212 # Flattr via the tasks queue, but wait for the result
213 task = flattr_thing.delay(user, episode._id, site.domain,
214 request.is_secure(), 'Episode')
215 success, msg = task.get()
217 if success:
218 messages.success(request, _("Flattr\'d"))
220 else:
221 messages.error(request, msg)
223 podcast = episode.podcast
224 return HttpResponseRedirect(get_episode_link_target(episode, podcast))
227 # To make all view accessible via either IDs or Slugs
228 # a decorator queries the episode and passes the Id on to the
229 # regular views
231 def slug_decorator(f):
232 @wraps(f)
233 def _decorator(request, p_slug, e_slug, *args, **kwargs):
235 pquery = Podcast.objects.filter(
236 slugs__slug=p_slug,
237 slugs__scope='',
240 try:
241 podcast = pquery.prefetch_related('slugs').get()
242 except Podcast.DoesNotExist:
243 raise Http404
245 equery = Episode.objects.filter(
246 podcast = podcast,
247 slugs__slug=e_slug,
248 slugs__scope=podcast.id.hex,
251 try:
252 episode = equery.prefetch_related('urls', 'slugs').get()
254 # set previously fetched podcast, to avoid additional query
255 episode.podcast = podcast
257 except Episode.DoesNotExist:
258 raise Http404
260 # redirect when Id or a merged (non-cannonical) slug is used
261 if episode.slug and episode.slug != e_slug:
262 return HttpResponseRedirect(
263 get_episode_link_target(episode, podcast))
265 return f(request, episode, *args, **kwargs)
267 return _decorator
270 def id_decorator(f):
271 @wraps(f)
272 def _decorator(request, p_id, e_id, *args, **kwargs):
274 try:
275 query = Episode.objects.filter(id=e_id,
276 podcast_id=p_id)
277 episode = query.select_related('podcast').get()
279 except Episode.DoesNotExist:
280 raise Http404
282 # redirect when Id or a merged (non-cannonical) slug is used
283 if episode.slug and episode.slug != e_id:
284 podcast = episode.podcast
285 return HttpResponseRedirect(
286 get_episode_link_target(episode, podcast))
288 return f(request, episode, *args, **kwargs)
290 return _decorator
294 show_slug = slug_decorator(episode)
295 toggle_favorite_slug = slug_decorator(toggle_favorite)
296 add_action_slug = slug_decorator(add_action)
297 flattr_episode_slug = slug_decorator(flattr_episode)
298 episode_history_slug = slug_decorator(history)
300 show_id = id_decorator(episode)
301 toggle_favorite_id = id_decorator(toggle_favorite)
302 add_action_id = id_decorator(add_action)
303 flattr_episode_id = id_decorator(flattr_episode)
304 episode_history_id = id_decorator(history)