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 # Disabled as cache invalidation does not work properly
30 # state = cache.get(key)
34 udb
= get_userdata_database()
35 r
= udb
.view('episode_states/by_user_episode',
36 key
= [user
._id
, episode
._id
],
39 schema
= EpisodeUserState
,
48 podcast
= podcast_by_id(episode
.podcast
)
50 state
= EpisodeUserState()
51 state
.episode
= episode
._id
52 state
.podcast
= episode
.podcast
54 state
.ref_url
= episode
.url
55 state
.podcast_ref_url
= podcast
.url
56 # don't cache here, because the state is saved by the calling function
62 def all_episode_states(episode
):
65 raise QueryParameterMissing('episode')
67 udb
= get_userdata_database()
68 r
= udb
.view('episode_states/by_podcast_episode',
69 startkey
= [episode
.podcast
, episode
._id
, None],
70 endkey
= [episode
.podcast
, episode
._id
, {}],
72 schema
= EpisodeUserState
,
78 def all_podcast_episode_states(podcast
):
81 raise QueryParameterMissing('podcast')
83 udb
= get_userdata_database()
84 r
= udb
.view('episode_states/by_podcast_episode',
85 startkey
= [podcast
.get_id(), None, None],
86 endkey
= [podcast
.get_id(), {}, {}],
88 schema
= EpisodeUserState
,
94 @cache_result(timeout
=60*60)
95 def podcast_listener_count(episode
):
96 """ returns the number of users that have listened to this podcast """
99 raise QueryParameterMissing('episode')
101 udb
= get_userdata_database()
102 r
= udb
.view('listeners/by_podcast',
103 startkey
= [episode
.get_id(), None],
104 endkey
= [episode
.get_id(), {}],
108 stale
= 'update_after',
110 return r
.first()['value'] if r
else 0
113 @cache_result(timeout
=60*60)
114 def podcast_listener_count_timespan(podcast
, start
=None, end
={}):
115 """ returns (date, listener-count) tuples for all days w/ listeners """
118 raise QueryParameterMissing('podcast')
120 if isinstance(start
, datetime
):
121 start
= start
.isoformat()
123 if isinstance(end
, datetime
):
124 end
= end
.isoformat()
126 udb
= get_userdata_database()
127 r
= udb
.view('listeners/by_podcast',
128 startkey
= [podcast
.get_id(), start
],
129 endkey
= [podcast
.get_id(), end
],
133 stale
= 'update_after',
136 return map(_wrap_listener_count
, r
)
139 @cache_result(timeout
=60*60)
140 def episode_listener_counts(episode
):
141 """ (Episode-Id, listener-count) tuples for episodes w/ listeners """
144 raise QueryParameterMissing('episode')
146 udb
= get_userdata_database()
147 r
= udb
.view('listeners/by_podcast_episode',
148 startkey
= [episode
.get_id(), None, None],
149 endkey
= [episode
.get_id(), {}, {}],
153 stale
= 'update_after',
156 return map(_wrap_listeners
, r
)
160 def get_podcasts_episode_states(podcast
, user_id
):
161 """ Returns the latest episode actions for the podcast's episodes """
164 raise QueryParameterMissing('podcast')
167 raise QueryParameterMissing('user_id')
169 udb
= get_userdata_database()
170 res
= udb
.view('episode_states/by_user_podcast',
171 startkey
= [user_id
, podcast
.get_id(), None],
172 endkey
= [user_id
, podcast
.get_id(), {}],
175 return map(lambda r
: r
['value'], res
)
179 @cache_result(timeout
=60*60)
180 def episode_listener_count(episode
, start
=None, end
={}):
181 """ returns the number of users that have listened to this episode """
184 raise QueryParameterMissing('episode')
186 udb
= get_userdata_database()
187 r
= udb
.view('listeners/by_episode',
188 startkey
= [episode
._id
, start
],
189 endkey
= [episode
._id
, end
],
193 stale
= 'update_after',
195 return r
.first()['value'] if r
else 0
199 @cache_result(timeout
=60*60)
200 def episode_listener_count_timespan(episode
, start
=None, end
={}):
201 """ returns (date, listener-count) tuples for all days w/ listeners """
204 raise QueryParameterMissing('episode')
207 if isinstance(start
, datetime
):
208 start
= start
.isoformat()
210 if isinstance(end
, datetime
):
211 end
= end
.isoformat()
213 udb
= get_userdata_database()
214 r
= udb
.view('listeners/by_episode',
215 startkey
= [episode
._id
, start
],
216 endkey
= [episode
._id
, end
],
220 stale
= 'update_after',
223 return map(_wrap_listener_count
, r
)
227 def episode_state_for_ref_urls(user
, podcast_url
, episode_url
):
230 raise QueryParameterMissing('user')
233 raise QueryParameterMissing('podcast_url')
236 raise QueryParameterMissing('episode_url')
239 cache_key
= 'episode-state-%s-%s-%s' % (user
._id
,
240 sha1(podcast_url
).hexdigest(),
241 sha1(episode_url
).hexdigest())
243 state
= cache
.get(cache_key
)
247 udb
= get_userdata_database()
248 res
= udb
.view('episode_states/by_ref_urls',
249 key
= [user
._id
, podcast_url
, episode_url
],
252 schema
= EpisodeUserState
,
257 state
.ref_url
= episode_url
258 state
.podcast_ref_url
= podcast_url
259 cache
.set(cache_key
, state
, 60*60)
263 podcast
= podcast_for_url(podcast_url
, create
=True)
264 episode
= episode_for_podcast_id_url(podcast
.get_id(), episode_url
,
266 return episode_state_for_user_episode(user
, episode
)
270 def get_episode_actions(user_id
, since
=None, until
={}, podcast_id
=None,
272 """ Returns Episode Actions for the given criteria"""
275 raise QueryParameterMissing('user_id')
280 if not podcast_id
and not device_id
:
281 view
= 'episode_actions/by_user'
282 startkey
= [user_id
, since
]
283 endkey
= [user_id
, until
]
285 elif podcast_id
and not device_id
:
286 view
= 'episode_actions/by_podcast'
287 startkey
= [user_id
, podcast_id
, since
]
288 endkey
= [user_id
, podcast_id
, until
]
290 elif device_id
and not podcast_id
:
291 view
= 'episode_actions/by_device'
292 startkey
= [user_id
, device_id
, since
]
293 endkey
= [user_id
, device_id
, until
]
296 view
= 'episode_actions/by_podcast_device'
297 startkey
= [user_id
, podcast_id
, device_id
, since
]
298 endkey
= [user_id
, podcast_id
, device_id
, until
]
300 udb
= get_userdata_database()
306 return map(lambda r
: r
['value'], res
)
310 @cache_result(timeout
=60*60)
311 def episode_states_count():
312 udb
= get_userdata_database()
313 r
= udb
.view('episode_states/by_user_episode',
315 stale
= 'update_after',
320 def get_nth_episode_state(n
):
321 udb
= get_userdata_database()
322 first
= udb
.view('episode_states/by_user_episode',
326 schema
= EpisodeUserState
,
328 return first
.one() if first
else None
331 def get_duplicate_episode_states(user
, episode
):
334 raise QueryParameterMissing('user')
337 raise QueryParameterMissing('episode')
339 udb
= get_userdata_database()
340 states
= udb
.view('episode_states/by_user_episode',
341 key
= [user
, episode
],
343 schema
= EpisodeUserState
,
348 def _wrap_listener_count(res
):
349 date
= parser
.parse(res
['key'][1]).date()
350 listeners
= res
['value']
351 return (date
, listeners
)
354 def _wrap_listeners(res
):
355 episode
= res
['key'][1]
356 listeners
= res
['value']
357 return (episode
, listeners
)
360 @cache_result(timeout
=60*60)
361 def get_heatmap(podcast_id
, episode_id
, user_id
):
362 udb
= get_userdata_database()
364 group_level
= len(filter(None, [podcast_id
, episode_id
, user_id
]))
366 r
= udb
.view('heatmap/by_episode',
367 startkey
= [podcast_id
, episode_id
, user_id
],
368 endkey
= [podcast_id
, episode_id
or {}, user_id
or {}],
371 group_level
= group_level
,
372 stale
= 'update_after',
379 res
= r
.first()['value']
380 return res
['heatmap'], res
['borders']
383 @repeat_on_conflict(['state'])
384 def add_episode_actions(state
, actions
):
385 udb
= get_userdata_database()
386 state
.add_actions(actions
)