refactoring well-known settings
[mygpo.git] / mygpo / api / backend.py
blobe6333cbbceb6db0582287bdc33bd166fbdb018eb
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 from collections import defaultdict
19 from functools import partial
21 from mygpo.core.models import Podcast, Episode
22 from mygpo.users.models import EpisodeUserState, Device, DeviceDoesNotExist, \
23 PodcastUserState
24 from mygpo.decorators import repeat_on_conflict
25 from mygpo.couch import bulk_save_retry
26 from mygpo.json import json
27 from mygpo.users.settings import STORE_UA
28 from mygpo.db.couchdb.podcast import podcast_for_url, random_podcasts
29 from mygpo.db.couchdb.podcast_state import podcast_state_for_user_podcast
32 def get_random_picks(languages=None):
33 """ Returns random podcasts for the given language """
35 languages = languages or ['']
37 # get one iterator for each language
38 rand_iters = [random_podcasts(lang) for lang in languages]
40 # cycle through them, removing those that don't yield any more results
41 while rand_iters:
42 rand_iter = rand_iters.pop(0)
44 try:
45 podcast = next(rand_iter)
46 rand_iters.append(rand_iter)
47 yield podcast
49 except StopIteration:
50 # don't re-add rand_iter
51 pass
55 def get_device(user, uid, user_agent, undelete=True):
56 """
57 Loads or creates the device indicated by user, uid.
59 If the device has been deleted and undelete=True, it is undeleted.
60 """
62 store_ua = user.get_wksetting(STORE_UA)
64 @repeat_on_conflict(['user'])
65 def _get(user, uid, undelete):
67 save = False
69 try:
70 device = user.get_device_by_uid(uid, only_active=False)
72 except DeviceDoesNotExist:
73 device = Device(uid=uid)
74 user.devices.append(device)
75 save = True
77 if device.deleted and undelete:
78 device.deleted = False
79 user.set_device(device)
80 save = True
82 if store_ua and user_agent and \
83 getattr(device, 'user_agent', None) != user_agent:
84 device.user_agent = user_agent
85 user.set_device(device)
86 save = True
88 if save:
89 user.save()
91 return device
93 return _get(user=user, uid=uid, undelete=undelete)
96 class BulkSubscribe(object):
97 """ Performs bulk subscribe/unsubscribe operations """
99 def __init__(self, user, device, podcasts = {}, actions=None):
100 self.user = user
101 self.device = device
102 self.podcasts = podcasts
103 self.actions = actions or []
105 self.operations = {
106 'subscribe': partial(self._subscribe, device=device),
107 'unsubscribe': partial(self._unsubscribe, device=device),
111 def execute(self):
112 """ Executes all added actions in bulk """
113 obj_funs = map(self._get_obj_fun, self.actions)
114 bulk_save_retry(obj_funs)
116 # prepare for another run
117 self.actions = []
120 def add_action(self, url, op):
121 """ Adds a new (un)subscribe action
123 url is the podcast url to subscribe to / unsubscribe from
124 op is either "subscribe" or "unsubscribe" """
125 self.actions.append( (url, op) )
128 def _get_obj_fun(self, action):
129 url, op = action
131 podcast = self.podcasts.get(url,
132 podcast_for_url(url, create=True))
134 state = podcast_state_for_user_podcast(self.user, podcast)
136 fun = self.operations[op]
137 return (state, fun)
141 def _subscribe(self, state, device):
142 state.subscribe(device)
143 return state
145 def _unsubscribe(self, state, device):
146 state.unsubscribe(device)
147 return state