Query "userdata" views in correct database
[mygpo.git] / mygpo / db / couchdb / episode_state.py
blob49ee22933117addff9ab86e09e8e8a110dccd3df
1 from hashlib import sha1
2 from datetime import datetime
3 from dateutil import parser
5 from django.core.cache import cache
7 from mygpo.users.models import EpisodeUserState
8 from mygpo.db import QueryParameterMissing
9 from mygpo.db.couchdb.podcast import podcast_by_id, podcast_for_url
10 from mygpo.db.couchdb.episode import episode_for_podcast_id_url
11 from mygpo.db.couchdb import get_main_database, get_userdata_database
12 from mygpo.cache import cache_result
13 from mygpo.decorators import repeat_on_conflict
17 def episode_state_for_user_episode(user, episode):
19 if not user:
20 raise QueryParameterMissing('user')
22 if not episode:
23 raise QueryParameterMissing('episode')
26 key = 'episode-state-userid-%s-episodeid-%s' % (sha1(user._id).hexdigest(),
27 sha1(episode._id).hexdigest())
29 state = cache.get(key)
30 if state:
31 return state
33 udb = get_userdata_database()
34 r = udb.view('episode_states/by_user_episode',
35 key = [user._id, episode._id],
36 include_docs = True,
37 limit = 1,
38 wrapper = EpisodeUserState,
41 if r:
42 state = r.one()
43 cache.set(key, state)
44 return state
46 else:
47 podcast = podcast_by_id(episode.podcast)
49 state = EpisodeUserState()
50 state.episode = episode._id
51 state.podcast = episode.podcast
52 state.user = user._id
53 state.ref_url = episode.url
54 state.podcast_ref_url = podcast.url
55 # don't cache here, because the state is saved by the calling function
57 return state
61 def all_episode_states(episode):
63 if not episode:
64 raise QueryParameterMissing('episode')
66 udb = get_userdata_database()
67 r = udb.view('episode_states/by_podcast_episode',
68 startkey = [episode.podcast, episode._id, None],
69 endkey = [episode.podcast, episode._id, {}],
70 include_docs = True,
71 wrapper = EpisodeUserState,
73 return list(r)
77 def all_podcast_episode_states(podcast):
79 if not podcast:
80 raise QueryParameterMissing('podcast')
82 udb = get_userdata_database()
83 r = udb.view('episode_states/by_podcast_episode',
84 startkey = [podcast.get_id(), None, None],
85 endkey = [podcast.get_id(), {}, {}],
86 include_docs = True,
87 wrapper = EpisodeUserState,
89 return list(r)
93 @cache_result(timeout=60*60)
94 def podcast_listener_count(episode):
95 """ returns the number of users that have listened to this podcast """
97 if not episode:
98 raise QueryParameterMissing('episode')
100 udb = get_userdata_database()
101 r = udb.view('listeners/by_podcast',
102 startkey = [episode.get_id(), None],
103 endkey = [episode.get_id(), {}],
104 group = True,
105 group_level = 1,
106 reduce = True,
107 stale = 'update_after',
109 return r.first()['value'] if r else 0
112 @cache_result(timeout=60*60)
113 def podcast_listener_count_timespan(podcast, start=None, end={}):
114 """ returns (date, listener-count) tuples for all days w/ listeners """
116 if not podcast:
117 raise QueryParameterMissing('podcast')
119 if isinstance(start, datetime):
120 start = start.isoformat()
122 if isinstance(end, datetime):
123 end = end.isoformat()
125 udb = get_userdata_database()
126 r = udb.view('listeners/by_podcast',
127 startkey = [podcast.get_id(), start],
128 endkey = [podcast.get_id(), end],
129 group = True,
130 group_level = 2,
131 reduce = True,
132 stale = 'update_after',
135 return map(_wrap_listener_count, r)
138 @cache_result(timeout=60*60)
139 def episode_listener_counts(episode):
140 """ (Episode-Id, listener-count) tuples for episodes w/ listeners """
142 if not episode:
143 raise QueryParameterMissing('episode')
145 udb = get_userdata_database()
146 r = udb.view('listeners/by_podcast_episode',
147 startkey = [episode.get_id(), None, None],
148 endkey = [episode.get_id(), {}, {}],
149 group = True,
150 group_level = 2,
151 reduce = True,
152 stale = 'update_after',
155 return map(_wrap_listeners, r)
159 def get_podcasts_episode_states(podcast, user_id):
160 """ Returns the latest episode actions for the podcast's episodes """
162 if not podcast:
163 raise QueryParameterMissing('podcast')
165 if not user_id:
166 raise QueryParameterMissing('user_id')
168 udb = get_userdata_database()
169 res = udb.view('episode_states/by_user_podcast',
170 startkey = [user_id, podcast.get_id(), None],
171 endkey = [user_id, podcast.get_id(), {}],
174 return map(lambda r: r['value'], res)
178 @cache_result(timeout=60*60)
179 def episode_listener_count(episode, start=None, end={}):
180 """ returns the number of users that have listened to this episode """
182 if not episode:
183 raise QueryParameterMissing('episode')
185 udb = get_userdata_database()
186 r = udb.view('listeners/by_episode',
187 startkey = [episode._id, start],
188 endkey = [episode._id, end],
189 group = True,
190 group_level = 2,
191 reduce = True,
192 stale = 'update_after',
194 return r.first()['value'] if r else 0
198 @cache_result(timeout=60*60)
199 def episode_listener_count_timespan(episode, start=None, end={}):
200 """ returns (date, listener-count) tuples for all days w/ listeners """
202 if not episode:
203 raise QueryParameterMissing('episode')
206 if isinstance(start, datetime):
207 start = start.isoformat()
209 if isinstance(end, datetime):
210 end = end.isoformat()
212 udb = get_userdata_database()
213 r = udb.view('listeners/by_episode',
214 startkey = [episode._id, start],
215 endkey = [episode._id, end],
216 group = True,
217 group_level = 3,
218 reduce = True,
219 stale = 'update_after',
222 return map(_wrap_listener_count, r)
226 def episode_state_for_ref_urls(user, podcast_url, episode_url):
228 if not user:
229 raise QueryParameterMissing('user')
231 if not podcast_url:
232 raise QueryParameterMissing('podcast_url')
234 if not episode_url:
235 raise QueryParameterMissing('episode_url')
238 cache_key = 'episode-state-%s-%s-%s' % (user._id,
239 sha1(podcast_url).hexdigest(),
240 sha1(episode_url).hexdigest())
242 state = cache.get(cache_key)
243 if state:
244 return state
246 udb = get_userdata_database()
247 res = udb.view('episode_states/by_ref_urls',
248 key = [user._id, podcast_url, episode_url],
249 limit = 1,
250 include_docs=True,
251 wrapper = EpisodeUserState,
254 if res:
255 state = res.first()
256 state.ref_url = episode_url
257 state.podcast_ref_url = podcast_url
258 cache.set(cache_key, state, 60*60)
259 return state
261 else:
262 podcast = podcast_for_url(podcast_url, create=True)
263 episode = episode_for_podcast_id_url(podcast.get_id(), episode_url,
264 create=True)
265 return episode_state_for_user_episode(user, episode)
269 def get_episode_actions(user_id, since=None, until={}, podcast_id=None,
270 device_id=None):
271 """ Returns Episode Actions for the given criteria"""
273 if not user_id:
274 raise QueryParameterMissing('user_id')
276 if since >= until:
277 return []
279 if not podcast_id and not device_id:
280 view = 'episode_actions/by_user'
281 startkey = [user_id, since]
282 endkey = [user_id, until]
284 elif podcast_id and not device_id:
285 view = 'episode_actions/by_podcast'
286 startkey = [user_id, podcast_id, since]
287 endkey = [user_id, podcast_id, until]
289 elif device_id and not podcast_id:
290 view = 'episode_actions/by_device'
291 startkey = [user_id, device_id, since]
292 endkey = [user_id, device_id, until]
294 else:
295 view = 'episode_actions/by_podcast_device'
296 startkey = [user_id, podcast_id, device_id, since]
297 endkey = [user_id, podcast_id, device_id, until]
299 udb = get_userdata_database()
300 res = udb.view(view,
301 startkey = startkey,
302 endkey = endkey
305 return map(lambda r: r['value'], res)
309 @cache_result(timeout=60*60)
310 def episode_states_count():
311 udb = get_userdata_database()
312 r = udb.view('episode_states/by_user_episode',
313 limit = 0,
314 stale = 'update_after',
316 return r.total_rows
319 def get_nth_episode_state(n):
320 udb = get_userdata_database()
321 first = udb.view('episode_states/by_user_episode',
322 skip = n,
323 include_docs = True,
324 limit = 1,
325 wrapper = EpisodeUserState,
327 return first.one() if first else None
330 def get_duplicate_episode_states(user, episode):
332 if not user:
333 raise QueryParameterMissing('user')
335 if not episode:
336 raise QueryParameterMissing('episode')
338 udb = get_userdata_database()
339 states = udb.view('episode_states/by_user_episode',
340 key = [user, episode],
341 include_docs = True,
342 wrapper = EpisodeUserState,
344 return list(states)
347 def _wrap_listener_count(res):
348 date = parser.parse(res['key'][1]).date()
349 listeners = res['value']
350 return (date, listeners)
353 def _wrap_listeners(res):
354 episode = res['key'][1]
355 listeners = res['value']
356 return (episode, listeners)
359 @cache_result(timeout=60*60)
360 def get_heatmap(podcast_id, episode_id, user_id):
361 udb = get_userdata_database()
363 group_level = len(filter(None, [podcast_id, episode_id, user_id]))
365 r = udb.view('heatmap/by_episode',
366 startkey = [podcast_id, episode_id, user_id],
367 endkey = [podcast_id, episode_id or {}, user_id or {}],
368 reduce = True,
369 group = True,
370 group_level = group_level,
371 stale = 'update_after',
374 if not r:
375 return [], []
377 else:
378 res = r.first()['value']
379 return res['heatmap'], res['borders']
382 @repeat_on_conflict(['state'])
383 def add_episode_actions(state, actions):
384 state.add_actions(actions)
385 state.save()