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
):
20 raise QueryParameterMissing('user')
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
)
33 udb
= get_userdata_database()
34 r
= udb
.view('episode_states/by_user_episode',
35 key
= [user
._id
, episode
._id
],
38 wrapper
= EpisodeUserState
,
47 podcast
= podcast_by_id(episode
.podcast
)
49 state
= EpisodeUserState()
50 state
.episode
= episode
._id
51 state
.podcast
= episode
.podcast
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
61 def all_episode_states(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
, {}],
71 wrapper
= EpisodeUserState
,
77 def all_podcast_episode_states(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(), {}, {}],
87 wrapper
= EpisodeUserState
,
93 @cache_result(timeout
=60*60)
94 def podcast_listener_count(episode
):
95 """ returns the number of users that have listened to this podcast """
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(), {}],
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 """
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
],
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 """
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(), {}, {}],
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 """
163 raise QueryParameterMissing('podcast')
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 """
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
],
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 """
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
],
219 stale
= 'update_after',
222 return map(_wrap_listener_count
, r
)
226 def episode_state_for_ref_urls(user
, podcast_url
, episode_url
):
229 raise QueryParameterMissing('user')
232 raise QueryParameterMissing('podcast_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
)
246 udb
= get_userdata_database()
247 res
= udb
.view('episode_states/by_ref_urls',
248 key
= [user
._id
, podcast_url
, episode_url
],
251 wrapper
= EpisodeUserState
,
256 state
.ref_url
= episode_url
257 state
.podcast_ref_url
= podcast_url
258 cache
.set(cache_key
, state
, 60*60)
262 podcast
= podcast_for_url(podcast_url
, create
=True)
263 episode
= episode_for_podcast_id_url(podcast
.get_id(), episode_url
,
265 return episode_state_for_user_episode(user
, episode
)
269 def get_episode_actions(user_id
, since
=None, until
={}, podcast_id
=None,
271 """ Returns Episode Actions for the given criteria"""
274 raise QueryParameterMissing('user_id')
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
]
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()
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',
314 stale
= 'update_after',
319 def get_nth_episode_state(n
):
320 udb
= get_userdata_database()
321 first
= udb
.view('episode_states/by_user_episode',
325 wrapper
= EpisodeUserState
,
327 return first
.one() if first
else None
330 def get_duplicate_episode_states(user
, episode
):
333 raise QueryParameterMissing('user')
336 raise QueryParameterMissing('episode')
338 udb
= get_userdata_database()
339 states
= udb
.view('episode_states/by_user_episode',
340 key
= [user
, episode
],
342 wrapper
= EpisodeUserState
,
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 {}],
370 group_level
= group_level
,
371 stale
= 'update_after',
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
)