Ejemplo n.º 1
0
Archivo: tests.py Proyecto: fk-lx/mygpo
    def test_merge_podcasts(self):

        # 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([self.podcast1, self.podcast2], 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)
Ejemplo n.º 2
0
    def get(self, request, username, device_uid):

        now = datetime.utcnow()
        now_ = get_timestamp(now)

        user = request.user

        try:
            device = user.client_set.get(uid=device_uid)
        except Client.DoesNotExist as e:
            return HttpResponseNotFound(str(e))

        try:
            since = self.get_since(request)
        except ValueError as e:
            return HttpResponseBadRequest(str(e))

        include_actions = parse_bool(request.GET.get("include_actions", False))

        domain = RequestSite(request).domain

        add, rem, subscriptions = self.get_subscription_changes(
            user, device, since, now, domain)
        updates = self.get_episode_changes(user, subscriptions, domain,
                                           include_actions, since)

        return JsonResponse({
            "add": add,
            "rem": rem,
            "updates": updates,
            "timestamp": get_timestamp(now),
        })
Ejemplo n.º 3
0
    def get(self, request, username, device_uid):

        now = datetime.utcnow()
        now_ = get_timestamp(now)

        user = request.user

        try:
            device = user.client_set.get(uid=device_uid)
        except Client.DoesNotExist as e:
            return HttpResponseNotFound(str(e))

        try:
            since = self.get_since(request)
        except ValueError as e:
            return HttpResponseBadRequest(str(e))

        include_actions = parse_bool(request.GET.get('include_actions', False))

        domain = RequestSite(request).domain

        add, rem, subscriptions = self.get_subscription_changes(user, device,
                                                                since, now,
                                                                domain)
        updates = self.get_episode_changes(user, subscriptions, domain,
                                           include_actions, since)

        return JsonResponse({
            'add': add,
            'rem': rem,
            'updates': updates,
            'timestamp': get_timestamp(now),
        })
Ejemplo n.º 4
0
def get_episode_changes(user, podcast, device, since, until, aggregated, version):

    devices = dict( (dev.id, dev.uid) for dev in user.devices )

    args = {}
    if podcast is not None:
        args['podcast_id'] = podcast.get_id()

    if device is not None:
        args['device_id'] = device.id

    actions = EpisodeAction.filter(user._id, since, until, **args)

    if version == 1:
        actions = imap(convert_position, actions)

    clean_data = partial(clean_episode_action_data,
            user=user, devices=devices)

    actions = map(clean_data, actions)
    actions = filter(None, actions)

    if aggregated:
        actions = dict( (a['episode'], a) for a in actions ).values()

    until_ = get_timestamp(until)

    return {'actions': actions, 'timestamp': until_}
Ejemplo n.º 5
0
Archivo: lists.py Proyecto: fk-lx/mygpo
def create(request, username, format):
    """ Creates a new podcast list and links to it in the Location header """

    title = request.GET.get('title', None)

    if not title:
        return HttpResponseBadRequest('Title missing')

    slug = slugify(title)

    if not slug:
        return HttpResponseBadRequest('Invalid title')

    plist = podcastlist_for_user_slug(request.user._id, slug)

    if plist:
        return HttpResponse('List already exists', status=409)

    urls = parse_subscription(request.body, format)
    podcasts = [podcast_for_url(url, create=True) for url in urls]
    podcast_ids = map(Podcast.get_id, podcasts)

    plist = PodcastList()
    plist.created_timestamp = get_timestamp(datetime.utcnow())
    plist.title = title
    plist.slug = slug
    plist.user = request.user._id
    plist.podcasts = podcast_ids
    plist.save()

    response = HttpResponse(status=201)
    list_url = reverse('api-get-list', args=[request.user.username, slug, format])
    response['Location'] = list_url

    return response
Ejemplo n.º 6
0
    def post(self, request, version, username, device_uid):
        """ Client sends subscription updates """
        now = get_timestamp(datetime.utcnow())
        logger.info('Subscription Upload @{username}/{device_uid}'.format(
            username=request.user.username, device_uid=device_uid))

        d = get_device(request.user, device_uid,
                       request.META.get('HTTP_USER_AGENT', ''))

        actions = self.parsed_body(request)

        add = list(filter(None, actions.get('add', [])))
        rem = list(filter(None, actions.get('remove', [])))
        logger.info('Subscription Upload @{username}/{device_uid}: add '
                    '{num_add}, remove {num_remove}'.format(
                        username=request.user.username,
                        device_uid=device_uid,
                        num_add=len(add),
                        num_remove=len(rem)))

        update_urls = self.update_subscriptions(request.user, d, add, rem)

        return JsonResponse({
            'timestamp': now,
            'update_urls': update_urls,
        })
Ejemplo n.º 7
0
Archivo: views.py Proyecto: fk-lx/mygpo
def create_list(request):
    title = request.POST.get('title', None)

    if not title:
        messages.error(request, _('You have to specify a title.'))
        return HttpResponseRedirect(reverse('lists-overview'))

    slug = slugify(title)

    if not slug:
        messages.error(request, _('"{title}" is not a valid title').format(
                    title=title))
        return HttpResponseRedirect(reverse('lists-overview'))

    plist = podcastlist_for_user_slug(request.user._id, slug)

    if plist is None:
        plist = PodcastList()
        plist.created_timestamp = get_timestamp(datetime.utcnow())
        plist.title = title
        plist.slug = slug
        plist.user = request.user._id
        plist.save()

    list_url = reverse('list-show', args=[request.user.username, slug])
    return HttpResponseRedirect(list_url)
Ejemplo n.º 8
0
    def post(self, request, version, username, device_uid):
        """ Client sends subscription updates """
        now = get_timestamp(datetime.utcnow())
        logger.info(
            "Subscription Upload @{username}/{device_uid}".format(
                username=request.user.username, device_uid=device_uid
            )
        )

        d = get_device(
            request.user, device_uid, request.META.get("HTTP_USER_AGENT", "")
        )

        actions = self.parsed_body(request)

        add = list(filter(None, actions.get("add", [])))
        rem = list(filter(None, actions.get("remove", [])))
        logger.info(
            "Subscription Upload @{username}/{device_uid}: add "
            "{num_add}, remove {num_remove}".format(
                username=request.user.username,
                device_uid=device_uid,
                num_add=len(add),
                num_remove=len(rem),
            )
        )

        update_urls = self.update_subscriptions(request.user, d, add, rem)

        return JsonResponse({"timestamp": now, "update_urls": update_urls})
Ejemplo n.º 9
0
    def post(self, request, version, username, device_uid):
        """ Client sends subscription updates """
        now = get_timestamp(datetime.utcnow())
        logger.info(
            'Subscription Upload @{username}/{device_uid}'.format(
                username=request.user.username, device_uid=device_uid
            )
        )

        d = get_device(
            request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')
        )

        actions = self.parsed_body(request)

        add = list(filter(None, actions.get('add', [])))
        rem = list(filter(None, actions.get('remove', [])))
        logger.info(
            'Subscription Upload @{username}/{device_uid}: add '
            '{num_add}, remove {num_remove}'.format(
                username=request.user.username,
                device_uid=device_uid,
                num_add=len(add),
                num_remove=len(rem),
            )
        )

        update_urls = self.update_subscriptions(request.user, d, add, rem)

        return JsonResponse({'timestamp': now, 'update_urls': update_urls})
Ejemplo n.º 10
0
def parse_episode_action(action, user, update_urls, now, ua_string):
    action_str  = action.get('action', None)
    if not valid_episodeaction(action_str):
        raise Exception('invalid action %s' % action_str)

    new_action = EpisodeAction()

    new_action.action = action['action']

    if action.get('device', False):
        device = get_device(user, action['device'], ua_string)
        new_action.device = device.id

    if action.get('timestamp', False):
        new_action.timestamp = dateutil.parser.parse(action['timestamp'])
    else:
        new_action.timestamp = now
    new_action.timestamp = new_action.timestamp.replace(microsecond=0)

    new_action.upload_timestamp = get_timestamp(now)

    new_action.started = action.get('started', None)
    new_action.playmark = action.get('position', None)
    new_action.total = action.get('total', None)

    return new_action
Ejemplo n.º 11
0
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))
Ejemplo n.º 12
0
    def test_limit_actions(self):
        """ Test that max MAX_EPISODE_ACTIONS episodes are returned """

        timestamps = []
        t = datetime.utcnow()
        for n in range(15):
            timestamp = t - timedelta(seconds=n)
            EpisodeHistoryEntry.objects.create(
                timestamp=timestamp,
                episode=self.episode,
                user=self.user,
                action=EpisodeHistoryEntry.DOWNLOAD,
            )
            timestamps.append(timestamp)

        url = reverse(episodes, kwargs={'version': '2', 'username': self.user.username})
        response = self.client.get(url, {'since': '0'}, **self.extra)
        self.assertEqual(response.status_code, 200, response.content)
        response_obj = json.loads(response.content.decode('utf-8'))
        actions = response_obj['actions']

        # 10 actions should be returned
        self.assertEqual(len(actions), 10)

        timestamps = sorted(timestamps)

        # the first 10 actions, according to their timestamp should be returned
        for action, timestamp in zip(actions, timestamps):
            self.assertEqual(timestamp.isoformat(), action['timestamp'])

        # the `timestamp` field in the response should be the timestamp of the
        # last returned action
        self.assertEqual(get_timestamp(timestamps[9]), response_obj['timestamp'])
Ejemplo n.º 13
0
Archivo: tests.py Proyecto: fk-lx/mygpo
    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])
Ejemplo n.º 14
0
def subscriptions(request, username, device_uid):

    now = datetime.now()
    now_ = get_timestamp(now)

    if request.method == 'GET':

        try:
            device = request.user.get_device_by_uid(device_uid)
        except DeviceDoesNotExist as e:
            return HttpResponseNotFound(str(e))

        since_ = request.GET.get('since', None)
        if since_ is None:
            return HttpResponseBadRequest('parameter since missing')
        try:
            since = datetime.fromtimestamp(float(since_))
        except ValueError:
            return HttpResponseBadRequest('since-value is not a valid timestamp')

        changes = get_subscription_changes(request.user, device, since, now)

        return JsonResponse(changes)

    elif request.method == 'POST':
        d = get_device(request.user, device_uid,
                request.META.get('HTTP_USER_AGENT', ''))

        if not request.body:
            return HttpResponseBadRequest('POST data must not be empty')

        try:
            actions = parse_request_body(request)
        except (JSONDecodeError, UnicodeDecodeError, ValueError) as e:
            msg = (u'Could not decode subscription update POST data for ' +
                   'user %s: %s') % (username,
                   request.body.decode('ascii', errors='replace'))
            logger.warn(msg, exc_info=True)
            return HttpResponseBadRequest(msg)

        add = actions['add'] if 'add' in actions else []
        rem = actions['remove'] if 'remove' in actions else []

        add = filter(None, add)
        rem = filter(None, rem)

        try:
            update_urls = update_subscriptions(request.user, d, add, rem)
        except ValueError, e:
            return HttpResponseBadRequest(e)

        return JsonResponse({
            'timestamp': now_,
            'update_urls': update_urls,
            })
Ejemplo n.º 15
0
def episodes(request, username, version=1):

    version = int(version)
    now = datetime.now()
    now_ = get_timestamp(now)
    ua_string = request.META.get('HTTP_USER_AGENT', '')

    if request.method == 'POST':
        try:
            actions = json.loads(request.raw_post_data)
        except (JSONDecodeError, UnicodeDecodeError) as e:
            log('Advanced API: could not decode episode update POST data for user %s: %s' % (username, e))
            return HttpResponseBadRequest()

        try:
            update_urls = update_episodes(request.user, actions, now, ua_string)
        except DeviceUIDException as e:
            import traceback
            log('could not update episodes for user %s: %s %s: %s' % (username, e, traceback.format_exc(), actions))
            return HttpResponseBadRequest(str(e))

        return JsonResponse({'timestamp': now_, 'update_urls': update_urls})

    elif request.method == 'GET':
        podcast_url= request.GET.get('podcast', None)
        device_uid = request.GET.get('device', None)
        since_     = request.GET.get('since', None)
        aggregated = parse_bool(request.GET.get('aggregated', False))

        try:
            since = datetime.fromtimestamp(float(since_)) if since_ else None
        except ValueError:
            return HttpResponseBadRequest('since-value is not a valid timestamp')

        if podcast_url:
            podcast = Podcast.for_url(podcast_url)
            if not podcast:
                raise Http404
        else:
            podcast = None

        if device_uid:

            try:
                device = request.user.get_device_by_uid(device_uid)
            except DeviceDoesNotExist as e:
                return HttpResponseNotFound(str(e))

        else:
            device = None

        changes = get_episode_changes(request.user, podcast, device, since,
                now, aggregated, version)

        return JsonResponse(changes)
Ejemplo n.º 16
0
    def test_no_actions(self):
        """ Test when there are no actions to return """

        t1 = get_timestamp(datetime.utcnow())

        url = reverse(episodes, kwargs={'version': '2', 'username': self.user.username})
        response = self.client.get(url, {'since': '0'}, **self.extra)
        self.assertEqual(response.status_code, 200, response.content)
        response_obj = json.loads(response.content.decode('utf-8'))
        actions = response_obj['actions']

        # 10 actions should be returned
        self.assertEqual(len(actions), 0)

        returned = response_obj['timestamp']
        t2 = get_timestamp(datetime.utcnow())
        # the `timestamp` field in the response should be the timestamp of the
        # last returned action
        self.assertGreaterEqual(returned, t1)
        self.assertGreaterEqual(t2, returned)
Ejemplo n.º 17
0
def get_episode_changes(user, podcast, device, since, until, aggregated,
                        version):

    history = EpisodeHistoryEntry.objects.filter(user=user,
                                                 timestamp__lt=until)

    # return the earlier entries first
    history = history.order_by("timestamp")

    if since:
        history = history.filter(timestamp__gte=since)

    if podcast is not None:
        history = history.filter(episode__podcast=podcast)

    if device is not None:
        history = history.filter(client=device)

    if version == 1:
        history = map(convert_position, history)

    # Limit number of returned episode actions
    max_actions = dsettings.MAX_EPISODE_ACTIONS
    history = history[:max_actions]

    # evaluate query and turn into list, for negative indexing
    history = list(history)

    actions = [episode_action_json(a, user) for a in history]

    if aggregated:
        actions = list(dict((a["episode"], a) for a in actions).values())

    if history:
        ts = get_timestamp(history[-1].timestamp)
    else:
        ts = get_timestamp(until)

    return {"actions": actions, "timestamp": ts}
Ejemplo n.º 18
0
def get_episode_changes(user, podcast, device, since, until, aggregated, version):

    history = EpisodeHistoryEntry.objects.filter(user=user, timestamp__lt=until)

    # return the earlier entries first
    history = history.order_by('timestamp')

    if since:
        history = history.filter(timestamp__gte=since)

    if podcast is not None:
        history = history.filter(episode__podcast=podcast)

    if device is not None:
        history = history.filter(client=device)

    if version == 1:
        history = map(convert_position, history)

    # Limit number of returned episode actions
    max_actions = dsettings.MAX_EPISODE_ACTIONS
    history = history[:max_actions]

    # evaluate query and turn into list, for negative indexing
    history = list(history)

    actions = [episode_action_json(a, user) for a in history]

    if aggregated:
        actions = list(dict((a['episode'], a) for a in actions).values())

    if history:
        ts = get_timestamp(history[-1].timestamp)
    else:
        ts = get_timestamp(until)

    return {'actions': actions, 'timestamp': ts}
Ejemplo n.º 19
0
    def get_changes(self, user, device, since, until):
        """ Returns subscription changes for the given device """
        history = get_subscription_history(user, device, since, until)
        logger.info('Subscription History: {num}'.format(num=len(history)))

        add, rem = subscription_diff(history)
        logger.info('Subscription Diff: +{num_add}/-{num_remove}'.format(
            num_add=len(add), num_remove=len(rem)))

        until_ = get_timestamp(until)

        # TODO: we'd need to get the ref_urls here somehow
        add_urls = [p.url for p in add]
        rem_urls = [p.url for p in rem]
        return (add_urls, rem_urls, until_)
Ejemplo n.º 20
0
    def get_changes(self, user, device, since, until):
        """ Returns subscription changes for the given device """
        history = get_subscription_history(user, device, since, until)
        logger.info('Subscription History: {num}'.format(num=len(history)))

        add, rem = subscription_diff(history)
        logger.info('Subscription Diff: +{num_add}/-{num_remove}'.format(
            num_add=len(add), num_remove=len(rem)))

        until_ = get_timestamp(until)

        # TODO: we'd need to get the ref_urls here somehow
        add_urls = [p.url for p in add]
        rem_urls = [p.url for p in rem]
        return (add_urls, rem_urls, until_)
Ejemplo n.º 21
0
    def post(self, request, username):
        """ Add / remove Chapters to/from an episode """
        user = request.user
        now_ = get_timestamp(datetime.utcnow())

        body = self.parsed_body(request)

        podcast_url, episode_url, update_urls = self.get_urls(body)
        body['podcast'] = podcast_url
        body['episode'] = episode_url

        if not podcast_url or not episode_url:
            raise RequestException('Invalid Podcast or Episode URL')

        self.update_chapters(body, user)
        return JsonResponse({'update_url': update_urls, 'timestamp': now_})
Ejemplo n.º 22
0
    def post(self, request, username):
        """ Add / remove Chapters to/from an episode """
        user = request.user
        now_ = get_timestamp(datetime.utcnow())

        body = self.parsed_body(request)

        podcast_url, episode_url, update_urls = self.get_urls(body)
        body['podcast'] = podcast_url
        body['episode'] = episode_url

        if not podcast_url or not episode_url:
            raise RequestException('Invalid Podcast or Episode URL')

        self.update_chapters(body, user)
        return JsonResponse({'update_url': update_urls, 'timestamp': now_})
Ejemplo n.º 23
0
def subscriptions(request, username, device_uid):

    now = datetime.now()
    now_ = get_timestamp(now)

    if request.method == 'GET':

        try:
            device = request.user.get_device_by_uid(device_uid)
        except DeviceDoesNotExist as e:
            return HttpResponseNotFound(str(e))

        since_ = request.GET.get('since', None)
        if since_ == None:
            return HttpResponseBadRequest('parameter since missing')
        try:
            since = datetime.fromtimestamp(float(since_))
        except ValueError:
            return HttpResponseBadRequest('since-value is not a valid timestamp')

        changes = get_subscription_changes(request.user, device, since, now)

        return JsonResponse(changes)

    elif request.method == 'POST':
        d = get_device(request.user, device_uid,
                request.META.get('HTTP_USER_AGENT', ''))

        if not request.raw_post_data:
            return HttpResponseBadRequest('POST data must not be empty')

        actions = json.loads(request.raw_post_data)
        add = actions['add'] if 'add' in actions else []
        rem = actions['remove'] if 'remove' in actions else []

        add = filter(None, add)
        rem = filter(None, rem)

        try:
            update_urls = update_subscriptions(request.user, d, add, rem)
        except IntegrityError, e:
            return HttpResponseBadRequest(e)

        return JsonResponse({
            'timestamp': now_,
            'update_urls': update_urls,
            })
Ejemplo n.º 24
0
    def post(self, request, version, username, device_uid):
        """ Client sends subscription updates """
        now = get_timestamp(datetime.utcnow())

        d = get_device(request.user, device_uid,
                       request.META.get('HTTP_USER_AGENT', ''))

        actions = self.parsed_body(request)

        add = filter(None, actions.get('add', []))
        rem = filter(None, actions.get('remove', []))

        update_urls = self.update_subscriptions(request.user, d, add, rem)

        return JsonResponse({
            'timestamp': now,
            'update_urls': update_urls,
        })
Ejemplo n.º 25
0
def episode_for_podcast_id_url(podcast_id, episode_url, create=False):

    if not podcast_id:
        raise QueryParameterMissing('podcast_id')

    if not episode_url:
        raise QueryParameterMissing('episode_url')


    key = u'episode-podcastid-%s-url-%s' % (
            sha1(podcast_id.encode('utf-8')).hexdigest(),
            sha1(episode_url.encode('utf-8')).hexdigest())

#   Disabled as cache invalidation is not working properly
#   episode = cache.get(key)
#   if episode:
#       return episode

    db = get_main_database()
    episode = get_single_result(db, 'episodes/by_podcast_url',
            key          = [podcast_id, episode_url],
            include_docs = True,
            reduce       = False,
            schema       = Episode,
        )

    if episode:
        if episode.needs_update:
            incomplete_obj.send_robust(sender=episode)
        else:
            cache.set(key, episode)
        return episode

    if create:
        episode = Episode()
        episode.created_timestamp = get_timestamp(datetime.utcnow())
        episode.podcast = podcast_id
        episode.urls = [episode_url]
        episode.save()
        incomplete_obj.send_robust(sender=episode)
        return episode

    return None
Ejemplo n.º 26
0
Archivo: tasks.py Proyecto: fk-lx/mygpo
def auto_flattr_episode(user, episode_id):
    """ Task to auto-flattr an episode

    In addition to the flattring itself, it also records the event """

    success, msg = flattr_thing(user, episode_id, None, False, 'Episode')

    if not success:
        return False

    episode = episode_by_id(episode_id)
    state = episode_state_for_user_episode(user, episode)

    action = EpisodeAction()
    action.action = 'flattr'
    action.upload_timestamp = get_timestamp(datetime.utcnow())
    add_episode_actions(state, [action])

    return True
Ejemplo n.º 27
0
    def get(self, request, username):
        """ Get chapters for an episode """
        user = request.user
        now_ = get_timestamp(datetime.utcnow())

        podcast_url, episode_url, _update_urls = self.get_urls(request)

        episode = Episode.objects.filter(podcast__urls__url=podcast_url,
                                         urls__url=episode_url).get()

        chapters = Chapter.objects.filter(user=user, episode=episode)

        since = self.get_since(request)
        if since:
            chapters = chapters.filter(created__gte=since)

        chapters_json = map(self.chapter_to_json, chapters)

        return JsonResponse({'chapters': chapters_json, 'timestamp': now_})
Ejemplo n.º 28
0
def updates(request, username, device_uid):
    now = datetime.now()
    now_ = get_timestamp(now)

    try:
        device = request.user.get_device_by_uid(device_uid)
    except DeviceDoesNotExist as e:
        return HttpResponseNotFound(str(e))

    since_ = request.GET.get('since', None)
    if since_ == None:
        return HttpResponseBadRequest('parameter since missing')
    try:
        since = datetime.fromtimestamp(float(since_))
    except ValueError:
        return HttpResponseBadRequest('since-value is not a valid timestamp')

    include_actions = parse_bool(request.GET.get('include_actions', False))

    ret = get_subscription_changes(request.user, device, since, now)
    domain = RequestSite(request).domain

    subscriptions = list(device.get_subscribed_podcasts())

    podcasts = dict( (p.url, p) for p in subscriptions )
    prepare_podcast_data = partial(get_podcast_data, podcasts, domain)

    ret['add'] = map(prepare_podcast_data, ret['add'])

    devices = dict( (dev.id, dev.uid) for dev in request.user.devices )
    clean_action_data = partial(clean_episode_action_data,
            user=request.user, devices=devices)

    # index subscribed podcasts by their Id for fast access
    podcasts = dict( (p.get_id(), p) for p in subscriptions )
    prepare_episode_data = partial(get_episode_data, podcasts, domain,
            clean_action_data, include_actions)

    episode_updates = get_episode_updates(request.user, subscriptions, since)
    ret['updates'] = map(prepare_episode_data, episode_updates)

    return JsonResponse(ret)
Ejemplo n.º 29
0
    def get(self, request, username):
        """ Get chapters for an episode """
        user = request.user
        now_ = get_timestamp(datetime.utcnow())

        podcast_url, episode_url, _update_urls = self.get_urls(request)

        episode = Episode.objects.filter(
            podcast__urls__url=podcast_url, urls__url=episode_url
        ).get()

        chapters = Chapter.objects.filter(user=user, episode=episode)

        since = self.get_since(request)
        if since:
            chapters = chapters.filter(created__gte=since)

        chapters_json = map(self.chapter_to_json, chapters)

        return JsonResponse({'chapters': chapters_json, 'timestamp': now_})
Ejemplo n.º 30
0
def get_episode_changes(user, podcast, device, since, until, aggregated, version):

    history = EpisodeHistoryEntry.objects.filter(user=user,
                                                 timestamp__lt=until)

    if since:
        history = history.filter(timestamp__gte=since)

    if podcast is not None:
        history = history.filter(episode__podcast=podcast)

    if device is not None:
        history = history.filter(client=device)

    if version == 1:
        history = map(convert_position, history)

    actions = [episode_action_json(a, user) for a in history]

    if aggregated:
        actions = list(dict( (a['episode'], a) for a in actions ).values())

    return {'actions': actions, 'timestamp': get_timestamp(until)}
Ejemplo n.º 31
0
def podcast_for_url(url, create=False):

    if not url:
        raise QueryParameterMissing('url')

    key = 'podcast-by-url-%s' % sha1(url.encode('utf-8')).hexdigest()

    podcast = cache.get(key)
    if podcast:
        return podcast

    db = get_main_database()
    podcast_group = get_single_result(db, 'podcasts/by_url',
            key          = url,
            include_docs = True,
            wrapper      = _wrap_pg,
        )

    if podcast_group:
        podcast = podcast_group.get_podcast_by_url(url)

        if podcast.needs_update:
            incomplete_obj.send_robust(sender=podcast)
        else:
            cache.set(key, podcast)

        return podcast

    if create:
        podcast = Podcast()
        podcast.created_timestamp = get_timestamp(datetime.utcnow())
        podcast.urls = [url]
        podcast.save()
        incomplete_obj.send_robust(sender=podcast)
        return podcast

    return None
Ejemplo n.º 32
0
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))
Ejemplo n.º 33
0
def episodes(request, username, version=1):

    version = int(version)
    now = datetime.utcnow()
    now_ = get_timestamp(now)
    ua_string = request.META.get("HTTP_USER_AGENT", "")

    if request.method == "POST":
        try:
            actions = parse_request_body(request)
        except (UnicodeDecodeError, ValueError) as e:
            msg = ("Could not decode episode update POST data for " +
                   "user %s: %s") % (
                       username,
                       request.body.decode("ascii", errors="replace"),
                   )
            logger.warning(msg, exc_info=True)
            return HttpResponseBadRequest(msg)

        logger.info("start: user %s: %d actions from %s" %
                    (request.user, len(actions), ua_string))

        # handle in background
        if (dsettings.API_ACTIONS_MAX_NONBG is not None
                and len(actions) > dsettings.API_ACTIONS_MAX_NONBG):
            bg_handler = dsettings.API_ACTIONS_BG_HANDLER
            if bg_handler is not None:

                modname, funname = bg_handler.rsplit(".", 1)
                mod = import_module(modname)
                fun = getattr(mod, funname)

                fun(request.user, actions, now, ua_string)

                # TODO: return 202 Accepted
                return JsonResponse({"timestamp": now_, "update_urls": []})

        try:
            update_urls = update_episodes(request.user, actions, now,
                                          ua_string)
        except ValidationError as e:
            logger.warning(
                "Validation Error while uploading episode actions "
                "for user %s: %s",
                username,
                str(e),
            )
            return HttpResponseBadRequest(str(e))

        except InvalidEpisodeActionAttributes as e:
            msg = (
                "invalid episode action attributes while uploading episode actions for user %s"
                % (username, ))
            logger.warning(msg, exc_info=True)
            return HttpResponseBadRequest(str(e))

        logger.info("done:  user %s: %d actions from %s" %
                    (request.user, len(actions), ua_string))
        return JsonResponse({"timestamp": now_, "update_urls": update_urls})

    elif request.method == "GET":
        podcast_url = request.GET.get("podcast", None)
        device_uid = request.GET.get("device", None)
        since_ = request.GET.get("since", None)
        aggregated = parse_bool(request.GET.get("aggregated", False))

        try:
            since = int(since_) if since_ else None
            if since is not None:
                since = datetime.utcfromtimestamp(since)
        except ValueError:
            return HttpResponseBadRequest(
                "since-value is not a valid timestamp")

        if podcast_url:
            podcast = get_object_or_404(Podcast, urls__url=podcast_url)
        else:
            podcast = None

        if device_uid:

            try:
                user = request.user
                device = user.client_set.get(uid=device_uid)
            except Client.DoesNotExist as e:
                return HttpResponseNotFound(str(e))

        else:
            device = None

        changes = get_episode_changes(request.user, podcast, device, since,
                                      now, aggregated, version)

        return JsonResponse(changes)
Ejemplo n.º 34
0
 def get_changes(self, device, since, until):
     """ Returns subscription changes for the given device """
     add_urls, rem_urls = device.get_subscription_changes(since, until)
     until_ = get_timestamp(until)
     return (add_urls, rem_urls, until_)
Ejemplo n.º 35
0
def episodes(request, username, version=1):

    version = int(version)
    now = datetime.now()
    now_ = get_timestamp(now)
    ua_string = request.META.get('HTTP_USER_AGENT', '')

    if request.method == 'POST':
        try:
            actions = parse_request_body(request)
        except (JSONDecodeError, UnicodeDecodeError, ValueError) as e:
            msg = ('Could not decode episode update POST data for ' +
                   'user %s: %s') % (username,
                   request.body.decode('ascii', errors='replace'))
            logger.warn(msg, exc_info=True)
            return HttpResponseBadRequest(msg)

        logger.info('start: user %s: %d actions from %s' % (request.user._id, len(actions), ua_string))

        # handle in background
        if len(actions) > dsettings.API_ACTIONS_MAX_NONBG:
            bg_handler = dsettings.API_ACTIONS_BG_HANDLER
            if bg_handler is not None:

                modname, funname = bg_handler.rsplit('.', 1)
                mod = import_module(modname)
                fun = getattr(mod, funname)

                fun(request.user, actions, now, ua_string)

                # TODO: return 202 Accepted
                return JsonResponse({'timestamp': now_, 'update_urls': []})


        try:
            update_urls = update_episodes(request.user, actions, now, ua_string)
        except DeviceUIDException as e:
            logger.warn('invalid device UID while uploading episode actions for user %s', username)
            return HttpResponseBadRequest(str(e))

        except InvalidEpisodeActionAttributes as e:
            msg = 'invalid episode action attributes while uploading episode actions for user %s' % (username,)
            logger.warn(msg, exc_info=True)
            return HttpResponseBadRequest(str(e))

        logger.info('done:  user %s: %d actions from %s' % (request.user._id, len(actions), ua_string))
        return JsonResponse({'timestamp': now_, 'update_urls': update_urls})

    elif request.method == 'GET':
        podcast_url= request.GET.get('podcast', None)
        device_uid = request.GET.get('device', None)
        since_     = request.GET.get('since', None)
        aggregated = parse_bool(request.GET.get('aggregated', False))

        try:
            since = int(since_) if since_ else None
        except ValueError:
            return HttpResponseBadRequest('since-value is not a valid timestamp')

        if podcast_url:
            podcast = podcast_for_url(podcast_url)
            if not podcast:
                raise Http404
        else:
            podcast = None

        if device_uid:

            try:
                device = request.user.get_device_by_uid(device_uid)
            except DeviceDoesNotExist as e:
                return HttpResponseNotFound(str(e))

        else:
            device = None

        changes = get_episode_changes(request.user, podcast, device, since,
                now_, aggregated, version)

        return JsonResponse(changes)
Ejemplo n.º 36
0
Archivo: tests.py Proyecto: fk-lx/mygpo
    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)
Ejemplo n.º 37
0
def episodes(request, username, version=1):

    version = int(version)
    now = datetime.utcnow()
    now_ = get_timestamp(now)
    ua_string = request.META.get('HTTP_USER_AGENT', '')

    if request.method == 'POST':
        try:
            actions = parse_request_body(request)
        except (UnicodeDecodeError, ValueError) as e:
            msg = ('Could not decode episode update POST data for ' +
                   'user %s: %s') % (username,
                                     request.body.decode('ascii',
                                                         errors='replace'))
            logger.warn(msg, exc_info=True)
            return HttpResponseBadRequest(msg)

        logger.info('start: user %s: %d actions from %s' %
                    (request.user, len(actions), ua_string))

        # handle in background
        if len(actions) > dsettings.API_ACTIONS_MAX_NONBG:
            bg_handler = dsettings.API_ACTIONS_BG_HANDLER
            if bg_handler is not None:

                modname, funname = bg_handler.rsplit('.', 1)
                mod = import_module(modname)
                fun = getattr(mod, funname)

                fun(request.user, actions, now, ua_string)

                # TODO: return 202 Accepted
                return JsonResponse({'timestamp': now_, 'update_urls': []})

        try:
            update_urls = update_episodes(request.user, actions, now,
                                          ua_string)
        except ValidationError as e:
            logger.warning(
                'Validation Error while uploading episode actions '
                'for user %s: %s', username, str(e))
            return HttpResponseBadRequest(str(e))

        except InvalidEpisodeActionAttributes as e:
            msg = 'invalid episode action attributes while uploading episode actions for user %s' % (
                username, )
            logger.warn(msg, exc_info=True)
            return HttpResponseBadRequest(str(e))

        logger.info('done:  user %s: %d actions from %s' %
                    (request.user, len(actions), ua_string))
        return JsonResponse({'timestamp': now_, 'update_urls': update_urls})

    elif request.method == 'GET':
        podcast_url = request.GET.get('podcast', None)
        device_uid = request.GET.get('device', None)
        since_ = request.GET.get('since', None)
        aggregated = parse_bool(request.GET.get('aggregated', False))

        try:
            since = int(since_) if since_ else None
            if since is not None:
                since = datetime.utcfromtimestamp(since)
        except ValueError:
            return HttpResponseBadRequest(
                'since-value is not a valid timestamp')

        if podcast_url:
            podcast = get_object_or_404(Podcast, urls__url=podcast_url)
        else:
            podcast = None

        if device_uid:

            try:
                user = request.user
                device = user.client_set.get(uid=device_uid)
            except Client.DoesNotExist as e:
                return HttpResponseNotFound(str(e))

        else:
            device = None

        changes = get_episode_changes(request.user, podcast, device, since,
                                      now, aggregated, version)

        return JsonResponse(changes)
Ejemplo n.º 38
0
def get_subscription_changes(user, device, since, until):
    add_urls, rem_urls = device.get_subscription_changes(since, until)
    until_ = get_timestamp(until)
    return {'add': add_urls, 'remove': rem_urls, 'timestamp': until_}