1 from datetime
import datetime
4 from django
.db
import transaction
6 from mygpo
.users
.models
import Client
7 from mygpo
.subscriptions
.models
import (Subscription
, SubscribedPodcast
,
9 from mygpo
.subscriptions
.signals
import subscription_changed
10 from mygpo
.history
.models
import HistoryEntry
11 from mygpo
.utils
import to_maxlength
14 logger
= logging
.getLogger(__name__
)
17 SUBSCRIPTION_ACTIONS
= (
18 HistoryEntry
.SUBSCRIBE
,
19 HistoryEntry
.UNSUBSCRIBE
,
23 def subscribe(podcast
, user
, client
, ref_url
=None):
24 """ subscribes user to the current podcast on one client """
26 ref_url
= ref_url
or podcast
.url
27 now
= datetime
.utcnow()
29 subscription
, created
= Subscription
.objects
.get_or_create(
30 user
=user
, client
=client
, podcast
=podcast
, defaults
={
31 'ref_url': to_maxlength(Subscription
, 'ref_url', ref_url
),
40 logger
.info('{user} subscribed to {podcast} on {client}'.format(
41 user
=user
, podcast
=podcast
, client
=client
))
43 HistoryEntry
.objects
.create(
48 action
=HistoryEntry
.SUBSCRIBE
,
51 subscription_changed
.send(sender
=podcast
, user
=user
,
52 client
=client
, subscribed
=True)
56 def unsubscribe(podcast
, user
, client
):
57 """ unsubscribes user from the current podcast on one client """
58 now
= datetime
.utcnow()
61 subscription
= Subscription
.objects
.get(
66 except Subscription
.DoesNotExist
:
71 logger
.info('{user} unsubscribed from {podcast} on {client}'.format(
72 user
=user
, podcast
=podcast
, client
=client
))
74 HistoryEntry
.objects
.create(
79 action
=HistoryEntry
.UNSUBSCRIBE
,
82 subscription_changed
.send(sender
=podcast
, user
=user
,
83 client
=client
, subscribed
=False)
87 def subscribe_all(podcast
, user
, ref_url
=None):
88 """ subscribes user to the current podcast on all clients """
89 clients
= user
.client_set
.all()
90 for client
in clients
:
91 subscribe(podcast
, user
, client
, ref_url
)
95 def unsubscribe_all(podcast
, user
):
96 """ unsubscribes user from the current podcast on all clients """
97 now
= datetime
.utcnow()
99 clients
= user
.client_set
.filter(subscription__podcast
=podcast
)
100 for client
in clients
:
101 unsubscribe(podcast
, user
, client
)
104 def get_subscribe_targets(podcast
, user
):
105 """ Clients / SyncGroup on which the podcast can be subscribed
107 This excludes all devices/syncgroups on which the podcast is already
110 clients
= Client
.objects
.filter(user
=user
)\
111 .exclude(subscription__podcast
=podcast
)\
112 .select_related('sync_group')
115 for client
in clients
:
116 if client
.sync_group
:
117 targets
.add(client
.sync_group
)
124 def get_subscribed_podcasts(user
, only_public
=False):
125 """ Returns all subscribed podcasts for the user
127 The attribute "url" contains the URL that was used when subscribing to
130 subscriptions
= Subscription
.objects
.filter(user
=user
)\
131 .order_by('podcast')\
132 .distinct('podcast')\
133 .select_related('podcast')
134 private
= PodcastConfig
.objects
.get_private_podcasts(user
)
137 for subscription
in subscriptions
:
138 podcast
= subscription
.podcast
139 public
= subscription
.podcast
not in private
141 # check if we want to include this podcast
142 if only_public
and not public
:
145 subpodcast
= SubscribedPodcast(podcast
, public
, subscription
.ref_url
)
146 podcasts
.append(subpodcast
)
151 def get_subscription_history(user
, client
=None, since
=None, until
=None,
153 """ Returns chronologically ordered subscription history entries
155 Setting device_id restricts the actions to a certain device
158 logger
.info('Subscription History for {user}'.format(user
=user
.username
))
159 history
= HistoryEntry
.objects
.filter(user
=user
)\
160 .filter(action__in
=SUBSCRIPTION_ACTIONS
)\
161 .order_by('timestamp')
164 logger
.info('... client {client_uid}'.format(client_uid
=client
.uid
))
165 history
= history
.filter(client
=client
)
168 logger
.info('... since {since}'.format(since
=since
))
169 history
= history
.filter(timestamp__gt
=since
)
172 logger
.info('... until {until}'.format(until
=until
))
173 history
= history
.filter(timestamp__lte
=until
)
176 logger
.info('... only public')
177 private
= PodcastConfig
.objects
.get_private_podcasts(user
)
178 history
= history
.exclude(podcast__in
=private
)
183 def get_subscription_change_history(history
):
184 """ Actions that added/removed podcasts from the subscription list
186 Returns an iterator of all subscription actions that either
187 * added subscribed a podcast that hasn't been subscribed directly
188 before the action (but could have been subscribed) earlier
189 * removed a subscription of the podcast is not longer subscribed
192 This method assumes, that no subscriptions exist at the beginning of
196 subscriptions
= collections
.defaultdict(int)
198 for entry
in history
:
199 if entry
.action
== HistoryEntry
.SUBSCRIBE
:
200 subscriptions
[entry
.podcast
] += 1
202 # a new subscription has been added
203 if subscriptions
[entry
.podcast
] == 1:
206 elif entry
.action
== HistoryEntry
.UNSUBSCRIBE
:
207 subscriptions
[entry
.podcast
] -= 1
209 # the last subscription has been removed
210 if subscriptions
[entry
.podcast
] == 0:
214 def subscription_diff(history
):
215 """ Calculates a diff of subscriptions based on a history (sub/unsub) """
217 subscriptions
= collections
.defaultdict(int)
219 for entry
in history
:
220 if entry
.action
== HistoryEntry
.SUBSCRIBE
:
221 subscriptions
[entry
.podcast
] += 1
223 elif entry
.action
== HistoryEntry
.UNSUBSCRIBE
:
224 subscriptions
[entry
.podcast
] -= 1
226 subscribe
= [podcast
for (podcast
, value
) in
227 subscriptions
.items() if value
> 0]
228 unsubscribe
= [podcast
for (podcast
, value
) in
229 subscriptions
.items() if value
< 0]
231 return subscribe
, unsubscribe