Remove unused license preamble
[mygpo.git] / mygpo / api / subscriptions.py
blob5f38984f57d5bd2076ba64a7b306c085985e72b3
1 from datetime import datetime
3 from django.shortcuts import get_object_or_404
5 from mygpo.podcasts.models import Podcast
6 from mygpo.api import APIView, RequestException
7 from mygpo.api.httpresponse import JsonResponse
8 from mygpo.api.backend import get_device
9 from mygpo.utils import get_timestamp, normalize_feed_url, intersect
10 from mygpo.users.models import Client
11 from mygpo.subscriptions import (subscribe, unsubscribe,
12 get_subscription_history, subscription_diff)
14 import logging
15 logger = logging.getLogger(__name__)
18 class SubscriptionsAPI(APIView):
19 """ API for sending and retrieving podcast subscription updates """
21 def get(self, request, version, username, device_uid):
22 """ Client retrieves subscription updates """
23 now = datetime.utcnow()
24 user = request.user
25 device = get_object_or_404(Client, user=user, uid=device_uid)
26 since = self.get_since(request)
27 add, rem, until = self.get_changes(user, device, since, now)
28 return JsonResponse({
29 'add': add,
30 'remove': rem,
31 'timestamp': until
34 def post(self, request, version, username, device_uid):
35 """ Client sends subscription updates """
36 now = get_timestamp(datetime.utcnow())
37 logger.info('Subscription Upload @{username}/{device_uid}'.format(
38 username=request.user.username, device_uid=device_uid))
40 d = get_device(request.user, device_uid,
41 request.META.get('HTTP_USER_AGENT', ''))
43 actions = self.parsed_body(request)
45 add = list(filter(None, actions.get('add', [])))
46 rem = list(filter(None, actions.get('remove', [])))
47 logger.info('Subscription Upload @{username}/{device_uid}: add '
48 '{num_add}, remove {num_remove}'.format(
49 username=request.user.username, device_uid=device_uid,
50 num_add=len(add), num_remove=len(rem)))
52 update_urls = self.update_subscriptions(request.user, d, add, rem)
54 return JsonResponse({
55 'timestamp': now,
56 'update_urls': update_urls,
59 def update_subscriptions(self, user, device, add, remove):
61 conflicts = intersect(add, remove)
62 if conflicts:
63 msg = "can not add and remove '{}' at the same time".format(
64 str(conflicts))
65 logger.warn(msg)
66 raise RequestException(msg)
68 add_s = list(map(normalize_feed_url, add))
69 rem_s = list(map(normalize_feed_url, remove))
71 assert len(add) == len(add_s) and len(remove) == len(rem_s)
73 pairs = zip(add + remove, add_s + rem_s)
74 updated_urls = list(filter(lambda pair: pair[0] != pair[1], pairs))
76 add_s = filter(None, add_s)
77 rem_s = filter(None, rem_s)
79 # If two different URLs (in add and remove) have
80 # been sanitized to the same, we ignore the removal
81 rem_s = filter(lambda x: x not in add_s, rem_s)
83 for add_url in add_s:
84 podcast = Podcast.objects.get_or_create_for_url(add_url)
85 subscribe(podcast, user, device, add_url)
87 remove_podcasts = Podcast.objects.filter(urls__url__in=rem_s)
88 for podcast in remove_podcasts:
89 unsubscribe(podcast, user, device)
91 return updated_urls
93 def get_changes(self, user, device, since, until):
94 """ Returns subscription changes for the given device """
95 history = get_subscription_history(user, device, since, until)
96 logger.info('Subscription History: {num}'.format(num=len(history)))
98 add, rem = subscription_diff(history)
99 logger.info('Subscription Diff: +{num_add}/-{num_remove}'.format(
100 num_add=len(add), num_remove=len(rem)))
102 until_ = get_timestamp(until)
104 # TODO: we'd need to get the ref_urls here somehow
105 add_urls = [p.url for p in add]
106 rem_urls = [p.url for p in rem]
107 return (add_urls, rem_urls, until_)