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 from mygpo
.db
.couchdb
.utils
import multi_request_view
300 res
= multi_request_view(Podcast
,'podcasts/by_id',
303 stale
= 'update_after',
308 if obj
['doc_type'] == 'Podcast':
309 yield Podcast
.wrap(obj
)
312 pg
= PodcastGroup
.wrap(obj
)
313 podcast
= pg
.get_podcast_by_id(pid
)
317 def all_podcasts_groups(cls
):
318 return cls
.view('podcasts/podcasts_groups', include_docs
=True,
319 classes
=[Podcast
, PodcastGroup
]).iterator()
323 def podcasts_to_dict(ids
, use_cache
=False):
326 raise QueryParameterMissing('ids')
337 res
= cache
.get_many(ids
)
338 cache_objs
.extend(res
.values())
339 ids
= [x
for x
in ids
if x
not in res
.keys()]
341 db_objs
= podcasts_by_id(ids
)
343 for obj
in (cache_objs
+ db_objs
):
345 # get_multi returns dict {'key': _id, 'error': 'not found'}
346 # for non-existing objects
347 if isinstance(obj
, dict) and 'error' in obj
:
352 for i
in obj
.get_ids():
356 cache
.set_many(dict( (obj
.get_id(), obj
) for obj
in db_objs
))
362 def podcasts_need_update():
363 db
= get_main_database()
364 res
= db
.view('episodes/need_update',
370 podcast_id
= r
['key']
371 podcast
= podcast_by_id(podcast_id
)
376 def subscriberdata_for_podcast(podcast_id
):
379 raise QueryParameterMissing('podcast_id')
381 r
= PodcastSubscriberData
.view('podcasts/subscriber_data',
389 data
= PodcastSubscriberData()
390 data
.podcast
= podcast_id
395 def _wrap_podcast_group(res
):
396 if res
['doc']['doc_type'] == 'Podcast':
397 return Podcast
.wrap(res
['doc'])
399 pg
= PodcastGroup
.wrap(res
['doc'])
401 return pg
.get_podcast_by_id(id)
404 def _wrap_podcast_group_key1(res
):
406 if obj
['doc_type'] == 'Podcast':
407 return Podcast
.wrap(obj
)
411 pg
= PodcastGroup
.wrap(obj
)
412 podcast
= pg
.get_podcast_by_id(pid
)
417 def search_wrapper(result
):
419 if doc
['doc_type'] == 'Podcast':
420 p
= Podcast
.wrap(doc
)
421 elif doc
['doc_type'] == 'PodcastGroup':
422 p
= PodcastGroup
.wrap(doc
)
427 @cache_result(timeout
=60*60)
428 def search(q
, offset
=0, num_results
=20):
433 db
= get_main_database()
435 #FIXME current couchdbkit can't parse responses for multi-query searches
436 q
= q
.replace(',', '')
439 res
= db
.search('podcasts/search',
440 wrapper
= search_wrapper
,
443 stale
= 'update_after',
446 sort
='\\subscribers<int>')
448 return list(res
), res
.total_rows
450 except RequestFailed
:
454 @repeat_on_conflict(['podcast'])
455 def update_additional_data(podcast
, twitter
):
456 podcast
.twitter
= twitter
459 # clear the whole cache until we have a better invalidation mechanism