Add Catch-all Update-Retrieval to Advanced API
[mygpo.git] / mygpo / web / utils.py
blobeea6a3045e8a0d19f1a8682136deff5c9b657e13
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
147 ps = []
148 if (cur - start) > show_max / 2:
149 ps.extend(range(start, show_max / 4))
150 ps.append('...')
151 ps.extend(range(cur - show_max / 4, cur))
153 else:
154 ps.extend(range(start, cur))
156 ps.append(cur)
158 if (total - cur) > show_max / 2:
159 add = show_max / 2 - len(ps) # for the first pages, show more pages at the beginning
160 ps.extend(range(cur + 1, cur + show_max / 4 + add))
161 ps.append('...')
162 ps.extend(range(total - show_max / 4, total + 1))
164 else:
165 ps.extend(range(cur + 1, total + 1))
167 return ps