fix variable reference in episode_state db query
[mygpo.git] / mygpo / db / couchdb / episode_state.py
bloba2d3ed5745a5f38d3144bcb4e7199224969cb161
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.couch import get_main_database
12 from mygpo.cache import cache_result
16 def episode_state_for_user_episode(user, episode):
18 if not user:
19 raise QueryParameterMissing('user')
21 if not episode:
22 raise QueryParameterMissing('episode')
25 key = 'episode-state-userid-%s-episodeid-%s' % (sha1(user._id).hexdigest(),
26 sha1(episode._id).hexdigest())
28 state = cache.get(key)
29 if state:
30 return state
32 r = EpisodeUserState.view('episode_states/by_user_episode',
33 key = [user._id, episode._id],
34 include_docs = True,
35 limit = 1,
38 if r:
39 state = r.one()
40 cache.set(key, state)
41 return state
43 else:
44 podcast = podcast_by_id(episode.podcast)
46 state = EpisodeUserState()
47 state.episode = episode._id
48 state.podcast = episode.podcast
49 state.user = user._id
50 state.ref_url = episode.url
51 state.podcast_ref_url = podcast.url
52 # don't cache here, because the state is saved by the calling function
54 return state
58 def all_episode_states(episode):
60 if not episode:
61 raise QueryParameterMissing('episode')
63 r = EpisodeUserState.view('episode_states/by_podcast_episode',
64 startkey = [episode.podcast, episode._id, None],
65 endkey = [episode.podcast, episode._id, {}],
66 include_docs = True,
68 return list(r)
72 def all_podcast_episode_states(podcast):
74 if not podcast:
75 raise QueryParameterMissing('podcast')
77 r = EpisodeUserState.view('episode_states/by_podcast_episode',
78 startkey = [podcast.get_id(), None, None],
79 endkey = [podcast.get_id(), {}, {}],
80 include_docs = True
82 return list(r)
86 @cache_result(timeout=60*60)
87 def podcast_listener_count(episode):
88 """ returns the number of users that have listened to this podcast """
90 if not episode:
91 raise QueryParameterMissing('episode')
93 r = EpisodeUserState.view('listeners/by_podcast',
94 startkey = [episode.get_id(), None],
95 endkey = [episode.get_id(), {}],
96 group = True,
97 group_level = 1,
98 reduce = True,
100 return r.first()['value'] if r else 0
103 @cache_result(timeout=60*60)
104 def podcast_listener_count_timespan(podcast, start=None, end={}):
105 """ returns (date, listener-count) tuples for all days w/ listeners """
107 if not podcast:
108 raise QueryParameterMissing('podcast')
110 if isinstance(start, datetime):
111 start = start.isoformat()
113 if isinstance(end, datetime):
114 end = end.isoformat()
116 r = EpisodeUserState.view('listeners/by_podcast',
117 startkey = [podcast.get_id(), start],
118 endkey = [podcast.get_id(), end],
119 group = True,
120 group_level = 2,
121 reduce = True,
124 return map(_wrap_listener_count, r)
127 @cache_result(timeout=60*60)
128 def episode_listener_counts(episode):
129 """ (Episode-Id, listener-count) tuples for episodes w/ listeners """
131 if not episode:
132 raise QueryParameterMissing('episode')
135 r = EpisodeUserState.view('listeners/by_podcast_episode',
136 startkey = [episode.get_id(), None, None],
137 endkey = [episode.get_id(), {}, {}],
138 group = True,
139 group_level = 2,
140 reduce = True,
143 return map(_wrap_listeners, r)
147 def get_podcasts_episode_states(podcast, user_id):
148 """ Returns the latest episode actions for the podcast's episodes """
150 if not podcast:
151 raise QueryParameterMissing('podcast')
153 if not user_id:
154 raise QueryParameterMissing('user_id')
157 db = get_main_database()
158 res = db.view('episode_states/by_user_podcast',
159 startkey = [user_id, podcast.get_id(), None],
160 endkey = [user_id, podcast.get_id(), {}],
163 return map(lambda r: r['value'], res)
167 @cache_result(timeout=60*60)
168 def episode_listener_count(episode, start=None, end={}):
169 """ returns the number of users that have listened to this episode """
171 if not episode:
172 raise QueryParameterMissing('episode')
175 r = EpisodeUserState.view('listeners/by_episode',
176 startkey = [episode._id, start],
177 endkey = [episode._id, end],
178 group = True,
179 group_level = 2,
180 reduce = True,
182 return r.first()['value'] if r else 0
186 @cache_result(timeout=60*60)
187 def episode_listener_count_timespan(episode, start=None, end={}):
188 """ returns (date, listener-count) tuples for all days w/ listeners """
190 if not episode:
191 raise QueryParameterMissing('episode')
194 if isinstance(start, datetime):
195 start = start.isoformat()
197 if isinstance(end, datetime):
198 end = end.isoformat()
200 r = EpisodeUserState.view('listeners/by_episode',
201 startkey = [episode._id, start],
202 endkey = [episode._id, end],
203 group = True,
204 group_level = 3,
205 reduce = True,
208 return map(_wrap_listener_count, r)
212 def episode_state_for_ref_urls(user, podcast_url, episode_url):
214 if not user:
215 raise QueryParameterMissing('user')
217 if not podcast_url:
218 raise QueryParameterMissing('podcast_url')
220 if not episode_url:
221 raise QueryParameterMissing('episode_url')
224 cache_key = 'episode-state-%s-%s-%s' % (user._id,
225 sha1(podcast_url).hexdigest(),
226 sha1(episode_url).hexdigest())
228 state = cache.get(cache_key)
229 if state:
230 return state
232 res = EpisodeUserState.view('episode_states/by_ref_urls',
233 key = [user._id, podcast_url, episode_url],
234 limit = 1,
235 include_docs=True,
238 if res:
239 state = res.first()
240 state.ref_url = episode_url
241 state.podcast_ref_url = podcast_url
242 cache.set(cache_key, state, 60*60)
243 return state
245 else:
246 podcast = podcast_for_url(podcast_url, create=True)
247 episode = episode_for_podcast_id_url(podcast.get_id(), episode_url,
248 create=True)
249 return episode_state_for_user_episode(user, episode)
253 def get_episode_actions(user_id, since=None, until={}, podcast_id=None,
254 device_id=None):
255 """ Returns Episode Actions for the given criteria"""
257 if not user_id:
258 raise QueryParameterMissing('user_id')
261 since_str = since.strftime('%Y-%m-%dT%H:%M:%S') if since else None
262 until_str = until.strftime('%Y-%m-%dT%H:%M:%S') if until else {}
264 if since_str >= until_str:
265 return []
267 if not podcast_id and not device_id:
268 view = 'episode_actions/by_user'
269 startkey = [user_id, since_str]
270 endkey = [user_id, until_str]
272 elif podcast_id and not device_id:
273 view = 'episode_actions/by_podcast'
274 startkey = [user_id, podcast_id, since_str]
275 endkey = [user_id, podcast_id, until_str]
277 elif device_id and not podcast_id:
278 view = 'episode_actions/by_device'
279 startkey = [user_id, device_id, since_str]
280 endkey = [user_id, device_id, until_str]
282 else:
283 view = 'episode_actions/by_podcast_device'
284 startkey = [user_id, podcast_id, device_id, since_str]
285 endkey = [user_id, podcast_id, device_id, until_str]
287 db = get_main_database()
288 res = db.view(view,
289 startkey = startkey,
290 endkey = endkey
293 return map(lambda r: r['value'], res)
297 @cache_result(timeout=60*60)
298 def episode_states_count():
299 r = EpisodeUserState.view('episode_states/by_user_episode',
300 limit = 0,
301 stale = 'update_after',
303 return r.total_rows
306 def get_nth_episode_state(n):
307 first = EpisodeUserState.view('episode_states/by_user_episode',
308 skip = n,
309 include_docs = True,
310 limit = 1,
312 return first.one() if first else None
315 def get_duplicate_episode_states(user, episode):
317 if not user:
318 raise QueryParameterMissing('user')
320 if not episode:
321 raise QueryParameterMissing('episode')
323 states = EpisodeUserState.view('episode_states/by_user_episode',
324 key = [user, episode],
325 include_docs = True,
327 return list(states)
330 def _wrap_listener_count(res):
331 date = parser.parse(res['key'][1]).date()
332 listeners = res['value']
333 return (date, listeners)
336 def _wrap_listeners(res):
337 episode = res['key'][1]
338 listeners = res['value']
339 return (episode, listeners)
342 @cache_result(timeout=60*60)
343 def get_heatmap(podcast_id, episode_id, user_id):
344 db = get_main_database()
346 group_level = len(filter(None, [podcast_id, episode_id, user_id]))
348 r = db.view('heatmap/by_episode',
349 startkey = [podcast_id, episode_id, user_id],
350 endkey = [podcast_id, episode_id or {}, user_id or {}],
351 reduce = True,
352 group = True,
353 group_level = group_level,
354 stale = 'update_after',
357 if not r:
358 return [], []
360 else:
361 res = r.first()['value']
362 return res['heatmap'], res['borders']