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
)
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()
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
)
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
)
56 'update_urls': update_urls
,
59 def update_subscriptions(self
, user
, device
, add
, remove
):
61 conflicts
= intersect(add
, remove
)
63 msg
= "can not add and remove '{}' at the same time".format(
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
)
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
)
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_
)