refactoring in publisher and Podcast model code
[mygpo.git] / mygpo / web / utils.py
blobda66cc08509ae5e04a343b5578382e57b5d05770
1 from mygpo.api.models import Podcast, EpisodeAction
2 from mygpo.web.models import Advertisement
3 from babel import Locale, UnknownLocaleError
4 from datetime import datetime
5 import re
7 def get_accepted_lang(request):
8 return list(set([s[:2] for s in request.META.get('HTTP_ACCEPT_LANGUAGE', '').split(',')]))
10 def get_podcast_languages():
11 """
12 Returns all 2-letter language codes that are used by podcasts.
14 It filters obviously invalid strings, but does not check if any
15 of these codes is contained in ISO 639.
16 """
18 langs = [x['language'] for x in Podcast.objects.values('language').distinct()]
19 sane_lang = sanitize_language_codes(langs)
21 sane_lang.sort()
23 return sane_lang
26 def sanitize_language_codes(langs):
27 """
28 expects a list of language codes and returns a unique lost of the first
29 part of all items. obviously invalid entries are skipped
31 >>> sanitize_language_codes(['de-at', 'de-ch'])
32 ['de']
34 >>> sanitize_language_codes(['de-at', 'en', 'en-gb', '(asdf', 'Deutsch'])
35 ['de', 'en]
36 """
38 r = '^[a-zA-Z]{2}[-_]?.*$'
39 return list(set([l[:2] for l in langs if l and re.match(r, l)]))
42 def get_language_names(lang):
43 """
44 Takes a list of language codes and returns a list of tuples
45 with (code, name)
46 """
47 res = {}
48 for l in lang:
49 try:
50 locale = Locale(l)
51 except UnknownLocaleError:
52 continue
54 if locale.display_name:
55 res[l] = locale.display_name
57 return res
60 class UpdatedException(Exception):
61 """Base exception with additional payload"""
62 def __init__(self, data):
63 Exception.__init__(self)
64 self.data = data
67 def get_played_parts(user, episode):
68 """
69 return a list of length of alternating unplayed, played parts of the given
70 episode for the given user and the resulting duration of the episode
72 If no information is available, None and the stored duration of the episode
73 are returned
74 """
75 actions = EpisodeAction.objects.filter(episode=episode, user=user, action='play', playmark__isnull=False, started__isnull=False)
77 if actions.count() == 0:
78 return None, episode.duration
80 lengths = [x.total for x in actions]
81 median_length = lengths[len(lengths)/2]
83 # flatten (merge) all play-parts
84 played_parts = flatten_intervals(actions)
86 # if end of last played part exceeds median length, extend episode
87 if played_parts:
88 length = max(median_length, played_parts[len(played_parts)-1]['end'])
89 else:
90 return None, episode.duration
92 #split up the played parts in alternating 'unplayed' and 'played'
93 #sections, starting with an unplayed
94 sections = []
96 lastpos = 0
97 for played_part in played_parts:
98 sections.append(played_part['start'] - lastpos)
99 sections.append(played_part['end'] - played_part['start'])
100 lastpos = played_part['end']
102 intsections = [int(s) for s in sections]
104 return intsections, length
107 def flatten_intervals(actions):
109 takes a list of EpisodeActions and returns a sorted
110 list of hashtables with start end elements of the flattened
111 play intervals.
113 actions = filter(lambda x: x.started != None and x.playmark != None, actions)
114 actions.sort(key=lambda x: x.started)
115 played_parts = []
116 if len(actions) == 0:
117 return []
119 first = actions[0]
120 flat_date = {'start': first.started, 'end': first.playmark}
121 for action in actions:
122 if action.started <= flat_date['end'] and action.playmark >= flat_date['end']:
123 flat_date['end'] = action.playmark
124 elif action.started >= flat_date['start'] and action.playmark <= flat_date['end']:
125 # part already contained
126 continue
127 else:
128 played_parts.append(flat_date)
129 flat_date = {'start': action.started, 'end': action.playmark}
130 played_parts.append(flat_date)
131 return played_parts
134 def get_sponsored_podcast(when=datetime.now):
135 adv = Advertisement.objects.filter(start__lte=when, end__gte=when)
137 if not adv.exists():
138 return None
139 else:
140 return adv[0]
143 def get_page_list(start, total, cur, show_max):
145 returns a list of pages to be linked for navigation in a paginated view
148 if show_max >= (total - start):
149 return range(start, total+1)
151 ps = []
152 if (cur - start) > show_max / 2:
153 ps.extend(range(start, show_max / 4))
154 ps.append('...')
155 ps.extend(range(cur - show_max / 4, cur))
157 else:
158 ps.extend(range(start, cur))
160 ps.append(cur)
162 if (total - cur) > show_max / 2:
163 add = show_max / 2 - len(ps) # for the first pages, show more pages at the beginning
164 ps.extend(range(cur + 1, cur + show_max / 4 + add))
165 ps.append('...')
166 ps.extend(range(total - show_max / 4, total + 1))
168 else:
169 ps.extend(range(cur + 1, total + 1))
171 return ps
174 def process_lang_params(request, url):
175 if 'lang' in request.GET:
176 lang = list(set([x for x in request.GET.get('lang').split(',') if x]))
178 if request.method == 'POST':
179 if request.POST.get('lang'):
180 lang = list(set(lang + [request.POST.get('lang')]))
181 raise UpdatedException(lang)
183 if not 'lang' in request.GET:
184 lang = get_accepted_lang(request)
186 return sanitize_language_codes(lang)