1 from hashlib
import sha1
2 from random
import random
4 from restkit
import RequestFailed
6 from django
.core
.cache
import cache
8 from mygpo
.core
.models
import Podcast
, PodcastGroup
, PodcastSubscriberData
9 from mygpo
.decorators
import repeat_on_conflict
10 from mygpo
.cache
import cache_result
11 from mygpo
.couch
import get_main_database
12 from mygpo
.db
import QueryParameterMissing
13 from mygpo
.db
.couchdb
.utils
import multi_request_view
, is_couchdb_id
16 def podcast_slugs(base_slug
):
17 res
= Podcast
.view('podcasts/by_slug',
18 startkey
= [base_slug
, None],
19 endkey
= [base_slug
+ 'ZZZZZ', None],
22 return [r
['key'][0] for r
in res
]
25 @cache_result(timeout
=60*60)
27 return Podcast
.view('podcasts/by_id',
29 stale
= 'update_after',
33 @cache_result(timeout
=60*60)
34 def podcasts_for_tag(tag
):
35 """ Returns the podcasts with the current tag.
37 Some podcasts might be returned twice """
40 raise QueryParameterMissing('tag')
42 res
= multi_request_view(Podcast
, 'podcasts/by_tag',
44 startkey
= [tag
, None],
52 yield (r
['key'][1], r
['value'])
54 res
= multi_request_view(Podcast
, 'usertags/podcasts',
56 startkey
= [tag
, None],
64 yield (r
['key'][1], r
['value'])
67 @cache_result(timeout
=60*60)
68 def get_podcast_languages():
69 """ Returns all 2-letter language codes that are used by podcasts.
71 It filters obviously invalid strings, but does not check if any
72 of these codes is contained in ISO 639. """
74 from mygpo
.web
.utils
import sanitize_language_codes
76 res
= Podcast
.view('podcasts/by_language',
81 langs
= [r
['key'][0] for r
in res
]
82 sane_lang
= sanitize_language_codes(langs
)
87 @cache_result(timeout
=60*60)
88 def podcast_by_id(podcast_id
, current_id
=False):
91 raise QueryParameterMissing('podcast_id')
93 r
= Podcast
.view('podcasts/by_id',
95 classes
= [Podcast
, PodcastGroup
],
102 podcast_group
= r
.first()
103 return podcast_group
.get_podcast_by_id(podcast_id
, current_id
)
107 @cache_result(timeout
=60*60)
108 def podcastgroup_by_id(group_id
):
111 raise QueryParameterMissing('group_id')
113 return PodcastGroup
.get(group_id
)
117 @cache_result(timeout
=60*60)
118 def podcast_for_slug(slug
):
121 raise QueryParameterMissing('slug')
123 r
= Podcast
.view('podcasts/by_slug',
124 startkey
= [slug
, None],
135 if doc
['doc_type'] == 'Podcast':
136 return Podcast
.wrap(doc
)
139 pg
= PodcastGroup
.wrap(doc
)
140 return pg
.get_podcast_by_id(pid
)
143 @cache_result(timeout
=60*60)
144 def podcast_for_slug_id(slug_id
):
145 """ Returns the Podcast for either an CouchDB-ID for a Slug """
147 if is_couchdb_id(slug_id
):
148 return podcast_by_id(slug_id
)
150 return podcast_for_slug(slug_id
)
153 @cache_result(timeout
=60*60)
154 def podcastgroup_for_slug_id(slug_id
):
155 """ Returns the Podcast for either an CouchDB-ID for a Slug """
158 raise QueryParameterMissing('slug_id')
160 if is_couchdb_id(slug_id
):
161 return PodcastGroup
.get(slug_id
)
165 return PodcastGroup
.for_slug(slug_id
)
169 def podcasts_by_id(ids
):
172 raise QueryParameterMissing('ids')
177 r
= Podcast
.view('podcasts/by_id',
183 return map(_wrap_podcast_group
, r
)
187 @cache_result(timeout
=60*60)
188 def podcast_for_oldid(oldid
):
191 raise QueryParameterMissing('oldid')
193 r
= Podcast
.view('podcasts/by_oldid',
195 classes
= [Podcast
, PodcastGroup
],
202 podcast_group
= r
.first()
203 return podcast_group
.get_podcast_by_oldid(oldid
)
206 @cache_result(timeout
=60*60)
207 def podcastgroup_for_oldid(oldid
):
210 raise QueryParameterMissing('oldid')
212 r
= PodcastGroup
.view('podcasts/groups_by_oldid',
217 return r
.one() if r
else None
221 def podcast_for_url(url
, create
=False):
224 raise QueryParameterMissing('url')
226 key
= 'podcast-by-url-%s' % sha1(url
).hexdigest()
228 podcast
= cache
.get(key
)
232 r
= Podcast
.view('podcasts/by_url',
234 classes
=[Podcast
, PodcastGroup
],
239 podcast_group
= r
.first()
240 podcast
= podcast_group
.get_podcast_by_url(url
)
241 cache
.set(key
, podcast
)
248 cache
.set(key
, podcast
)
256 def random_podcasts(language
='', chunk_size
=5):
257 """ Returns an iterator of random podcasts
259 optionaly a language code can be specified. If given the podcasts will
260 be restricted to this language. chunk_size determines how many podcasts
261 will be fetched at once """
265 res
= Podcast
.view('podcasts/random',
266 startkey
= [language
, rnd
],
278 if obj
['doc_type'] == 'Podcast':
279 yield Podcast
.wrap(obj
)
281 elif obj
['doc_type'] == 'PodcastGroup':
282 yield PodcastGroup
.wrap(obj
)
286 def podcasts_by_last_update():
287 res
= Podcast
.view('podcasts/by_last_update',
289 stale
= 'update_after',
293 return map(_wrap_podcast_group_key1
, res
)
299 res
= utils
.multi_request_view(cls
, 'podcasts/by_id',
302 stale
= 'update_after',
307 if obj
['doc_type'] == 'Podcast':
308 yield Podcast
.wrap(obj
)
311 pg
= PodcastGroup
.wrap(obj
)
312 podcast
= pg
.get_podcast_by_id(pid
)
316 def all_podcasts_groups(cls
):
317 return cls
.view('podcasts/podcasts_groups', include_docs
=True,
318 classes
=[Podcast
, PodcastGroup
]).iterator()
322 def podcasts_to_dict(ids
, use_cache
=False):
325 raise QueryParameterMissing('ids')
336 res
= cache
.get_many(ids
)
337 cache_objs
.extend(res
.values())
338 ids
= [x
for x
in ids
if x
not in res
.keys()]
340 db_objs
= podcasts_by_id(ids
)
342 for obj
in (cache_objs
+ db_objs
):
344 # get_multi returns dict {'key': _id, 'error': 'not found'}
345 # for non-existing objects
346 if isinstance(obj
, dict) and 'error' in obj
:
351 for i
in obj
.get_ids():
355 cache
.set_many(dict( (obj
.get_id(), obj
) for obj
in db_objs
))
361 def podcasts_need_update():
362 db
= get_main_database()
363 res
= db
.view('episodes/need_update',
369 podcast_id
= r
['key']
370 podcast
= podcast_by_id(podcast_id
)
375 def subscriberdata_for_podcast(podcast_id
):
378 raise QueryParameterMissing('podcast_id')
380 r
= PodcastSubscriberData
.view('podcasts/subscriber_data',
388 data
= PodcastSubscriberData()
389 data
.podcast
= podcast_id
394 def _wrap_podcast_group(res
):
395 if res
['doc']['doc_type'] == 'Podcast':
396 return Podcast
.wrap(res
['doc'])
398 pg
= PodcastGroup
.wrap(res
['doc'])
400 return pg
.get_podcast_by_id(id)
403 def _wrap_podcast_group_key1(res
):
405 if obj
['doc_type'] == 'Podcast':
406 return Podcast
.wrap(obj
)
410 pg
= PodcastGroup
.wrap(obj
)
411 podcast
= pg
.get_podcast_by_id(pid
)
416 def search_wrapper(result
):
418 if doc
['doc_type'] == 'Podcast':
419 p
= Podcast
.wrap(doc
)
420 elif doc
['doc_type'] == 'PodcastGroup':
421 p
= PodcastGroup
.wrap(doc
)
426 @cache_result(timeout
=60*60)
427 def search(q
, offset
=0, num_results
=20):
432 db
= get_main_database()
434 #FIXME current couchdbkit can't parse responses for multi-query searches
435 q
= q
.replace(',', '')
438 res
= db
.search('podcasts/search',
439 wrapper
= search_wrapper
,
444 sort
='\\subscribers<int>')
446 return list(res
), res
.total_rows
448 except RequestFailed
:
452 @repeat_on_conflict(['podcast'])
453 def update_additional_data(podcast
, twitter
):
454 podcast
.twitter
= twitter
457 # clear the whole cache until we have a better invalidation mechanism