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(ls
):
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'])
45 >>> sanitize_language_codes(['de-at', 'en', 'en-gb', '(asdf', 'Deutsch'])
49 ls
= [sanitize_language_code(l
) for l
in ls
if l
and RE_LANG
.match(l
)]
53 def get_language_names(lang
):
55 Takes a list of language codes and returns a list of tuples
62 except UnknownLocaleError
:
65 if locale
.display_name
:
66 res
[l
] = locale
.display_name
71 def get_page_list(start
, total
, cur
, show_max
):
73 returns a list of pages to be linked for navigation in a paginated view
75 >>> get_page_list(1, 100, 1, 10)
76 [1, 2, 3, 4, 5, 6, '...', 98, 99, 100]
78 >>> get_page_list(1, 100, 50, 10)
79 [1, '...', 48, 49, 50, 51, '...', 98, 99, 100]
81 >>> get_page_list(1, 100, 99, 10)
82 [1, '...', 97, 98, 99, 100]
84 >>> get_page_list(1, 3, 2, 10)
88 if show_max
>= (total
- start
):
89 return range(start
, total
+1)
92 if (cur
- start
) > show_max
/ 2:
93 ps
.extend(range(start
, show_max
/ 4))
95 ps
.extend(range(cur
- show_max
/ 4, cur
))
98 ps
.extend(range(start
, cur
))
102 if (total
- cur
) > show_max
/ 2:
103 # for the first pages, show more pages at the beginning
104 add
= show_max
/ 2 - len(ps
)
105 ps
.extend(range(cur
+ 1, cur
+ show_max
/ 4 + add
))
107 ps
.extend(range(total
- show_max
/ 4, total
+ 1))
110 ps
.extend(range(cur
+ 1, total
+ 1))
115 def process_lang_params(request
):
117 lang
= request
.GET
.get('lang', 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 '')
133 def maintenance(request
, *args
, **kwargs
):
134 resp
= render(request
, 'maintenance.html', {})
135 resp
.status_code
= 503
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
149 args
= [podcast
.slug
]
150 view_name
= '%s-slug-id' % view_name
152 # as a fallback we use CouchDB-IDs
154 args
= [podcast
.get_id()]
155 view_name
= '%s-slug-id' % view_name
157 return reverse(view_name
, args
=args
+ add_args
)
160 def get_podcast_group_link_target(group
, view_name
, add_args
=[]):
161 """ Returns the link-target for a Podcast group, preferring slugs over Ids
163 automatically distringuishes between relational Podcast objects and
164 CouchDB-based Podcasts """
166 from mygpo
.core
.models
import PodcastGroup
171 view_name
= '%s-slug-id' % view_name
173 # to keep URLs short, we use use oldids
177 # as a fallback we use CouchDB-IDs
180 view_name
= '%s-slug-id' % view_name
182 return reverse(view_name
, args
=args
+ add_args
)
185 def get_episode_link_target(episode
, podcast
, view_name
='episode',
187 """ Returns the link-target for an Episode, preferring slugs over Ids
189 automatically distringuishes between relational Episode objects and
190 CouchDB-based Episodes """
192 from mygpo
.core
.models
import Podcast
196 args
= [podcast
.slug
or podcast
.get_id(), episode
.slug
]
197 view_name
= '%s-slug-id' % view_name
199 # for short URLs, prefer oldids over CouchDB-IDs
201 args
= [episode
.oldid
]
203 # fallback: CouchDB-IDs
206 if isinstance(episode
.podcast
, Podcast
):
207 podcast
= episode
.podcast
208 elif isinstance(episode
.podcast
, basestring
):
209 podcast
= podcast_by_id(episode
.podcast
)
211 args
= [podcast
.slug
or podcast
.get_id(), episode
._id
]
212 view_name
= '%s-slug-id' % view_name
214 return strip_tags(reverse(view_name
, args
=args
+ add_args
))
217 def fetch_episode_data(episodes
, podcasts
={}):
220 podcast_ids
= [episode
.podcast
for episode
in episodes
]
221 podcasts
= podcasts_to_dict(podcast_ids
)
223 def set_podcast(episode
):
224 episode
= proxy_object(episode
)
225 episode
.podcast
= podcasts
.get(episode
.podcast
, None)
228 return map(set_podcast
, episodes
)
231 # doesn't include the '@' because it's not stored as part of a twitter handle
232 TWITTER_CHARS
= string
.ascii_letters
+ string
.digits
+ '_'
235 def normalize_twitter(s
):
236 """ normalize user input that is supposed to be a Twitter handle """
237 return "".join(i
for i
in s
if i
in TWITTER_CHARS
)