def get_episode_link_target(episode, podcast, view_name='episode', add_args=[]): """ Returns the link-target for an Episode, preferring slugs over Ids automatically distringuishes between relational Episode objects and CouchDB-based Episodes """ from mygpo.core.models import Podcast # prefer slugs if episode.slug: args = [podcast.slug or podcast.get_id(), episode.slug] view_name = '%s-slug-id' % view_name # for short URLs, prefer oldids over CouchDB-IDs elif episode.oldid: args = [episode.oldid] # fallback: CouchDB-IDs else: if not podcast: if isinstance(episode.podcast, Podcast): podcast = episode.podcast elif isinstance(episode.podcast, basestring): podcast = podcast_by_id(episode.podcast) args = [podcast.slug or podcast.get_id(), episode._id] view_name = '%s-slug-id' % view_name return strip_tags(reverse(view_name, args=args + add_args))
def update_podcast_subscribers(podcast_id): """ Updates the subscriber count of a podcast """ try: podcast = podcast_by_id(podcast_id) # calculate current number of subscribers subscriber_count = podcast_subscriber_count(podcast) subs_cur = SubscriberData(timestamp=datetime.utcnow(), subscriber_count=subscriber_count) # sort all subscriber data entries subs_all = sorted(podcast.subscribers + [subs_cur], key=lambda e: e.timestamp) # move all but latest two to history subs_history = subscriberdata_for_podcast(podcast_id) subs_history.subscribers = subs_all[:-2] subs_history.save() # move latest two to podcast podcast.subscribers = subs_all[-2:] podcast.save() # TODO: which exceptions? except Exception as ex: raise update_podcast_subscribers.retry(exc=ex)
def flattr_thing(user, thing_id, domain, is_secure, thing_type): """ Task to flattr a thing """ flattr = Flattr(user, domain, is_secure) if thing_type == 'Podcast': thing = podcast_by_id(thing_id) elif thing_type == 'Episode': thing = episode_by_id(thing_id) else: raise NotImplemented(_("Can't flattr a '%s'") % thing_type) if not thing.flattr_url: return False, _('No Payment URL available') try: success, msg = flattr.flattr_url(thing.flattr_url) if settings.FLATTR_MYGPO_THING: flattr.flattr_url(settings.FLATTR_MYGPO_THING) except Exception as ex: raise flattr_thing.retry(exc=ex) return success, msg
def episode(request, episode): site = RequestSite(request) podcast = podcast_by_id(episode.podcast) if not check_publisher_permission(request.user, podcast): return HttpResponseForbidden() if request.method == 'POST': form = None # EpisodeForm(request.POST, instance=e) # if form.is_valid(): # form.save() elif request.method == 'GET': form = None # EpisodeForm(instance=e) timeline_data = list(episode_listener_data(episode)) heatmap = EpisodeHeatmap(episode.podcast, episode._id, duration=episode.duration) return render(request, 'publisher/episode.html', { 'is_secure': request.is_secure(), 'domain': site.domain, 'episode': episode, 'podcast': podcast, 'form': form, 'timeline_data': timeline_data, 'heatmap': heatmap, })
def add_action(request, episode): device = request.user.get_device(request.POST.get('device')) action_str = request.POST.get('action') timestamp = request.POST.get('timestamp', '') if timestamp: try: timestamp = dateutil.parser.parse(timestamp) except (ValueError, AttributeError): timestamp = datetime.utcnow() else: timestamp = datetime.utcnow() action = EpisodeAction() action.timestamp = timestamp action.upload_timestamp = get_timestamp(datetime.utcnow()) action.device = device.id if device else None action.action = action_str state = episode_state_for_user_episode(request.user, episode) add_episode_actions(state, [action]) podcast = podcast_by_id(episode.podcast) return HttpResponseRedirect(get_episode_link_target(episode, podcast))
def update_episode_slug(request, episode): """ sets a new "main" slug, and moves the existing to the merged slugs """ new_slug = request.POST.get('slug') podcast = podcast_by_id(episode.podcast) if new_slug: # remove the new slug from other episodes (of the same podcast) other_episodes = episodes_for_slug(podcast.get_id(), new_slug) for other_episode in other_episodes: if other_episode == episode: continue remove_episode_slug(other_episode, new_slug) messages.warning(request, _(u'Removed slug {slug} from {episode}'.format( slug=new_slug, episode=other_episode.title)) ) set_episode_slug(episode, new_slug) # TODO: we should use better cache invalidation cache.clear() return HttpResponseRedirect( get_episode_link_target(episode, podcast, 'episode-publisher-detail') )
def add_user_recursive(self, user, docs): """ adds a user and all the podcast and episodes it references """ # User docs.add(user._id) # Suggestions suggestions = suggestions_for_user(user) docs.add(suggestions._id) progress(0, len(docs), '', stream=sys.stderr) # Podcast States for p_state in podcast_states_for_user(user): self.add_podcast_state(p_state, docs) progress(0, len(docs), p_state, stream=sys.stderr) # Podcast podcast = podcast_by_id(p_state.podcast) self.add_podcast(podcast, docs) progress(0, len(docs), podcast, stream=sys.stderr) # Episodes for episode in episodes_for_podcast(podcast): self.add_episode(episode, docs) progress(0, len(docs), episode, stream=sys.stderr) e_state = episode_state_for_user_episode(user, episode) self.add_episode_state(e_state, docs) progress(0, len(docs), e_state, stream=sys.stderr)
def test_merge_podcasts(self): podcast1 = podcast_by_id(self.podcast1.get_id()) podcast2 = podcast_by_id(self.podcast2.get_id()) podcast3 = podcast_by_id(self.podcast3.get_id()) # assert that the podcasts are actually grouped self.assertEqual(podcast2._id, podcast3._id) self.assertNotEqual(podcast2.get_id(), podcast2._id) self.assertNotEqual(podcast3.get_id(), podcast3._id) # Create additional data that will be merged state1 = episode_state_for_user_episode(self.user, self.episode1) state2 = episode_state_for_user_episode(self.user, self.episode2) action1 = EpisodeAction(action='play', timestamp=datetime.utcnow(), upload_timestamp=get_timestamp(datetime.utcnow())) action2 = EpisodeAction(action='download', timestamp=datetime.utcnow(), upload_timestamp=get_timestamp(datetime.utcnow())) add_episode_actions(state1, [action1]) add_episode_actions(state2, [action2]) # copy of the object episode2 = episode_by_id(self.episode2._id) # decide which episodes to merge groups = [(0, [self.episode1, self.episode2])] counter = Counter() pm = PodcastMerger([podcast2, podcast1], counter, groups) pm.merge() state1 = episode_state_for_user_episode(self.user, self.episode1) state2 = episode_state_for_user_episode(self.user, episode2) self.assertIn(action1, state1.actions) self.assertIn(action2, state1.actions) self.assertEqual(state2._id, None) episode1 = episode_by_id(self.episode1._id) # episode2 has been merged into episode1, so it must contain its # merged _id self.assertEqual(episode1.merged_ids, [episode2._id])
def _decorator(request, id, *args, **kwargs): episode = episode_for_oldid(id) if episode is None: raise Http404 # redirect to Id or slug URL podcast = podcast_by_id(episode.podcast) return HttpResponseRedirect(get_episode_link_target(episode, podcast))
def toggle_favorite(request, episode): episode_state = episode_state_for_user_episode(request.user, episode) is_fav = episode_state.is_favorite() set_episode_favorite(episode_state, not is_fav) podcast = podcast_by_id(episode.podcast) return HttpResponseRedirect(get_episode_link_target(episode, podcast))
def handle(self, *args, **options): db = get_main_database() status = self.get_cmd_status() since = self.get_since(status, options) objects = self.get_objects(db, since) actions = Counter() # create unfinished command run status run_status = CommandRunStatus() run_status.timestamp_started = datetime.utcnow() run_status.start_seq = since # add it to existing one (if any) status.runs.append(run_status) status.save() total = db.info()['update_seq'] has_slug = lambda x: bool(x.slug) for seq, obj in objects: total = db.info()['update_seq'] if isinstance(obj, PodcastGroup): podcasts = filter(has_slug, obj.podcasts) if isinstance(obj, Podcast): podcasts = filter(has_slug, [obj]) elif isinstance(obj, Episode): if has_slug(obj): continue podcast = podcast_by_id(obj.podcast) if not podcast: continue podcasts = filter(has_slug, [podcast]) updated = self.handle_podcasts(podcasts) actions['updated'] += updated if not options['silent']: status_str = ', '.join('%s: %d' % x for x in actions.items()) progress(seq, total, status_str) # finish command run status run_status.timestamp_finished = datetime.utcnow() run_status.end_seq = total run_status.status_counter = dict(actions) # and overwrite existing one (we could keep a longer log here) status.runs = [run_status] status.save()
def episode(request, episode): podcast = podcast_by_id(episode.podcast) podcast = check_restrictions(podcast) user = request.user if not podcast: raise Http404 if user.is_authenticated(): episode_state = episode_state_for_user_episode(user, episode) is_fav = episode_state.is_favorite() # pre-populate data for fetch_data podcasts_dict = {podcast.get_id(): podcast} episodes_dict = {episode._id: episode} has_history = bool(list(episode_state.get_history_entries())) played_parts = EpisodeHeatmap(podcast.get_id(), episode._id, user._id, duration=episode.duration) devices = dict( (d.id, d.name) for d in user.devices ) can_flattr = user.get_wksetting(FLATTR_TOKEN) and episode.flattr_url else: has_history = False is_fav = False played_parts = None devices = {} can_flattr = False is_publisher = check_publisher_permission(user, podcast) prev = podcast.get_episode_before(episode) next = podcast.get_episode_after(episode) return render(request, 'episode.html', { 'episode': episode, 'podcast': podcast, 'prev': prev, 'next': next, 'has_history': has_history, 'is_favorite': is_fav, 'played_parts': played_parts, 'actions': EPISODE_ACTION_TYPES, 'devices': devices, 'can_flattr': can_flattr, 'is_publisher': is_publisher, })
def _decorator(request, p_slug_id, e_slug_id, *args, **kwargs): episode = episode_for_slug_id(p_slug_id, e_slug_id) if episode is None: raise Http404 # redirect when Id or a merged (non-cannonical) slug is used if episode.slug and episode.slug != e_slug_id: podcast = podcast_by_id(episode.podcast) return HttpResponseRedirect( get_episode_link_target(episode, podcast)) return f(request, episode, *args, **kwargs)
def remove_podcast_from_podcastlist(plist, podcast_id): if podcast_id in plist.podcasts: plist.podcasts.remove(podcast_id) if not podcast_id in plist.podcasts: # the podcast might be there with another id podcast = podcast_by_id(podcast_id) for podcast_id in podcast.get_ids(): if podcast_id in plist.podcasts: plist.podcasts.remove(podcast_id) plist.save()
def mytags(request): tags_podcast = {} tags_tag = defaultdict(list) for podcast_id, taglist in tags_for_user(request.user).items(): podcast = podcast_by_id(podcast_id) tags_podcast[podcast] = taglist for tag in taglist: tags_tag[ tag ].append(podcast) return render(request, 'mytags.html', { 'tags_podcast': tags_podcast, 'tags_tag': dict(tags_tag.items()), })
def episode_data(episode, domain, podcast=None): podcast = podcast or podcast_by_id(episode.podcast) data = { "title": episode.title, "url": episode.url, "podcast_title": podcast.title if podcast else '', "podcast_url": podcast.url if podcast else '', "description": episode.description, "website": episode.link, "mygpo_link": 'http://%(domain)s%(res)s' % dict(domain=domain, res=get_episode_link_target(episode, podcast)) if podcast else '' } if episode.released: data['released'] = episode.released.strftime('%Y-%m-%dT%H:%M:%S') return data
def episode_state_for_user_episode(user, episode): if not user: raise QueryParameterMissing('user') if not episode: raise QueryParameterMissing('episode') key = 'episode-state-userid-%s-episodeid-%s' % (sha1(user._id).hexdigest(), sha1(episode._id).hexdigest()) # Disabled as cache invalidation does not work properly # state = cache.get(key) # if state: # return state udb = get_userdata_database() state = get_single_result(udb, 'episode_states/by_user_episode', key = [user._id, episode._id], include_docs = True, limit = 1, schema = EpisodeUserState, ) if state: cache.set(key, state) return state else: podcast = podcast_by_id(episode.podcast) state = EpisodeUserState() state.episode = episode._id state.podcast = episode.podcast state.user = user._id state.ref_url = episode.url state.podcast_ref_url = podcast.url # don't cache here, because the state is saved by the calling function return state
def history(request, episode): """ shows the history of the episode """ user = request.user podcast = podcast_by_id(episode.podcast) episode_state = episode_state_for_user_episode(user, episode) # pre-populate data for fetch_data podcasts_dict = {podcast.get_id(): podcast} episodes_dict = {episode._id: episode} history = list(episode_state.get_history_entries()) HistoryEntry.fetch_data(user, history, podcasts=podcasts_dict, episodes=episodes_dict) devices = dict( (d.id, d.name) for d in user.devices ) return render(request, 'episode-history.html', { 'episode': episode, 'podcast': podcast, 'history': history, 'actions': EPISODE_ACTION_TYPES, 'devices': devices, })
def flattr_episode(request, episode): """ Flattrs an episode, records an event and redirects to the episode """ user = request.user site = RequestSite(request) # Flattr via the tasks queue, but wait for the result task = flattr_thing.delay(user, episode._id, site.domain, request.is_secure(), 'Episode') success, msg = task.get() if success: action = EpisodeAction() action.action = 'flattr' action.upload_timestamp = get_timestamp(datetime.utcnow()) state = episode_state_for_user_episode(request.user, episode) add_episode_actions(state, [action]) messages.success(request, _("Flattr\'d")) else: messages.error(request, msg) podcast = podcast_by_id(episode.podcast) return HttpResponseRedirect(get_episode_link_target(episode, podcast))
def test_merge(self): p1 = Podcast() p1.urls = ['http://example.com/podcast1.rss'] p1.save() p2 = Podcast() p2.urls = ['http://example.com/podcast2.rss'] p2.save() e1 = Episode() e1.title = 'Episode 1' e1.podcast = p1.get_id() e1.urls = ['http://example.com/podcast1/e1.mp3'] e1.save() e2 = Episode() e2.title = 'Episode 2' e2.podcast = p1.get_id() e2.urls = ['http://example.com/podcast1/e2.mp3'] e2.save() e3 = Episode() e3.title = 'Episode 3' e3.podcast = p2.get_id() e3.urls = ['http://example.com/podcast2/e2.mp3'] e3.save() e4 = Episode() e4.title = 'Episode 4' e4.podcast = p2.get_id() e4.urls = ['http://example.com/podcast2/e3.mp3'] e4.save() user = User() user.username = '******' user.email = '*****@*****.**' user.set_password('secret') device1 = Device() device1.uid = 'dev1' device2 = Device() device2.uid = 'dev2' user.devices.append(device1) user.devices.append(device2) user.save() p1.subscribe(user, device1) time.sleep(1) p1.unsubscribe(user, device1) time.sleep(1) p1.subscribe(user, device1) p2.subscribe(user, device2) s1 = episode_state_for_user_episode(user, e1) add_episode_actions(s1, [EpisodeAction(action='play', upload_timestamp=get_timestamp(datetime.utcnow()))]) s3 = episode_state_for_user_episode(user, e3) add_episode_actions(s3, [EpisodeAction(action='play', upload_timestamp=get_timestamp(datetime.utcnow()))]) # we need that for later e3_id = e3._id actions = Counter() # decide which episodes to merge groups = [(0, [e1]), (1, [e2, e3]), (2, [e4])] # carry out the merge pm = PodcastMerger([p1, p2], actions, groups) pm.merge() e1 = episode_by_id(e1._id) es1 = episode_state_for_user_episode(user, e1) self.assertEqual(len(es1.actions), 1) # check if merged episode's id can still be accessed e3 = episode_by_id(e3_id) es3 = episode_state_for_user_episode(user, e3) self.assertEqual(len(es3.actions), 1) p1 = podcast_by_id(p1.get_id()) ps1 = podcast_state_for_user_podcast(user, p1) self.assertEqual(len(ps1.get_subscribed_device_ids()), 2) self.assertEqual(len(list(episodes_for_podcast(p1))), 3)
def post(self, request, podcast_id): podcast = podcast_by_id(podcast_id) state = podcast_state_for_user_podcast(request.user, podcast) set_podcast_privacy_settings(state, self.public) return HttpResponseRedirect(reverse('privacy'))