podcast parsing as a celery task
[mygpo.git] / mygpo / web / utils.py
blob6b77c6ee71cfcf400721abbff7eb1f533a43005c
1 import re
2 import string
3 from datetime import datetime
5 from django.views.decorators.cache import never_cache
6 from django.utils.html import strip_tags
7 from django.core.urlresolvers import reverse
8 from django.shortcuts import render
10 from babel import Locale, UnknownLocaleError
12 from mygpo.cache import cache_result
13 from mygpo.core.models import Podcast
14 from mygpo.core.proxy import proxy_object
15 from mygpo.db.couchdb.podcast import podcast_by_id, podcasts_to_dict
18 def get_accepted_lang(request):
19 """ returns a list of language codes accepted by the HTTP request """
21 lang_str = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
22 lang_str = filter(lambda c: c in string.letters+',', lang_str)
23 langs = lang_str.split(',')
24 langs = [s[:2] for s in langs]
25 langs = map(str.strip, langs)
26 langs = filter(None, langs)
27 return list(set(langs))
30 RE_LANG = re.compile('^[a-zA-Z]{2}[-_]?.*$')
33 def sanitize_language_code(lang):
34 return lang[:2].lower()
37 def sanitize_language_codes(langs):
38 """
39 expects a list of language codes and returns a unique lost of the first
40 part of all items. obviously invalid entries are skipped
42 >>> sanitize_language_codes(['de-at', 'de-ch'])
43 ['de']
45 >>> sanitize_language_codes(['de-at', 'en', 'en-gb', '(asdf', 'Deutsch'])
46 ['de', 'en']
47 """
49 return list(set([sanitize_language_code(l) for l in langs if l and RE_LANG.match(l)]))
52 def get_language_names(lang):
53 """
54 Takes a list of language codes and returns a list of tuples
55 with (code, name)
56 """
57 res = {}
58 for l in lang:
59 try:
60 locale = Locale(l)
61 except UnknownLocaleError:
62 continue
64 if locale.display_name:
65 res[l] = locale.display_name
67 return res
72 def get_page_list(start, total, cur, show_max):
73 """
74 returns a list of pages to be linked for navigation in a paginated view
76 >>> get_page_list(1, 100, 1, 10)
77 [1, 2, 3, 4, 5, 6, '...', 98, 99, 100]
79 >>> get_page_list(1, 100, 50, 10)
80 [1, '...', 48, 49, 50, 51, '...', 98, 99, 100]
82 >>> get_page_list(1, 100, 99, 10)
83 [1, '...', 97, 98, 99, 100]
85 >>> get_page_list(1, 3, 2, 10)
86 [1, 2, 3]
87 """
89 if show_max >= (total - start):
90 return range(start, total+1)
92 ps = []
93 if (cur - start) > show_max / 2:
94 ps.extend(range(start, show_max / 4))
95 ps.append('...')
96 ps.extend(range(cur - show_max / 4, cur))
98 else:
99 ps.extend(range(start, cur))
101 ps.append(cur)
103 if (total - cur) > show_max / 2:
104 add = show_max / 2 - len(ps) # for the first pages, show more pages at the beginning
105 ps.extend(range(cur + 1, cur + show_max / 4 + add))
106 ps.append('...')
107 ps.extend(range(total - show_max / 4, total + 1))
109 else:
110 ps.extend(range(cur + 1, total + 1))
112 return ps
115 def process_lang_params(request):
117 lang = request.GET.get('lang', None)
119 if lang is None:
120 langs = get_accepted_lang(request)
121 lang = next(iter(langs), '')
123 return sanitize_language_code(lang)
126 def symbian_opml_changes(podcast):
127 podcast.description = (podcast.title or '') + '\n' \
128 + (podcast.description or '')
129 return podcast
132 @never_cache
133 def maintenance(request, *args, **kwargs):
134 resp = render(request, 'maintenance.html', {})
135 resp.status_code = 503
136 return resp
139 def get_podcast_link_target(podcast, view_name='podcast', add_args=[]):
140 """ Returns the link-target for a Podcast, preferring slugs over Ids
142 automatically distringuishes between relational Podcast objects and
143 CouchDB-based Podcasts """
145 from mygpo.core.models import Podcast
147 # we prefer slugs
148 if podcast.slug:
149 args = [podcast.slug]
150 view_name = '%s-slug-id' % view_name
152 # to keep URLs short, we use use oldids
153 elif podcast.oldid:
154 args = [podcast.oldid]
156 # as a fallback we use CouchDB-IDs
157 else:
158 args = [podcast.get_id()]
159 view_name = '%s-slug-id' % view_name
161 return reverse(view_name, args=args + add_args)
164 def get_podcast_group_link_target(group, view_name, add_args=[]):
165 """ Returns the link-target for a Podcast group, preferring slugs over Ids
167 automatically distringuishes between relational Podcast objects and
168 CouchDB-based Podcasts """
170 from mygpo.core.models import PodcastGroup
172 # we prefer slugs
173 if group.slug:
174 args = [group.slug]
175 view_name = '%s-slug-id' % view_name
177 # to keep URLs short, we use use oldids
178 elif group.oldid:
179 args = [group.oldid]
181 # as a fallback we use CouchDB-IDs
182 else:
183 args = [group._id]
184 view_name = '%s-slug-id' % view_name
186 return reverse(view_name, args=args + add_args)
189 def get_episode_link_target(episode, podcast, view_name='episode', add_args=[]):
190 """ Returns the link-target for an Episode, preferring slugs over Ids
192 automatically distringuishes between relational Episode objects and
193 CouchDB-based Episodes """
195 from mygpo.core.models import Podcast
197 # prefer slugs
198 if episode.slug:
199 args = [podcast.slug or podcast.get_id(), episode.slug]
200 view_name = '%s-slug-id' % view_name
202 # for short URLs, prefer oldids over CouchDB-IDs
203 elif episode.oldid:
204 args = [episode.oldid]
206 # fallback: CouchDB-IDs
207 else:
208 if not podcast:
209 if isinstance(episode.podcast, Podcast):
210 podcast = episode.podcast
211 elif isinstance(episode.podcast, basestring):
212 podcast = podcast_by_id(episode.podcast)
214 args = [podcast.slug or podcast.get_id(), episode._id]
215 view_name = '%s-slug-id' % view_name
217 return strip_tags(reverse(view_name, args=args + add_args))
220 def fetch_episode_data(episodes, podcasts={}):
222 if not podcasts:
223 podcast_ids = [episode.podcast for episode in episodes]
224 podcasts = podcasts_to_dict(podcast_ids)
226 def set_podcast(episode):
227 episode = proxy_object(episode)
228 episode.podcast = podcasts.get(episode.podcast, None)
229 return episode
231 return map(set_podcast, episodes)
234 # doesn't include the '@' because it's not stored as part of a twitter handle
235 TWITTER_CHARS = string.ascii_letters + string.digits + '_'
237 def normalize_twitter(s):
238 """ normalize user input that is supposed to be a Twitter handle """
239 return "".join(i for i in s if i in TWITTER_CHARS)