simplify, refactor APIs
[mygpo.git] / mygpo / api / backend.py
blob737c8b99d577b39a818493f90d352fb4d677ae0d
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 from mygpo.api.models import ToplistEntry, Podcast, Subscription, EpisodeToplistEntry
19 from mygpo.data.mimetype import get_type, CONTENT_TYPES
20 from datetime import timedelta
22 try:
23 import json
25 # Python 2.5 seems to have a different json module
26 if not 'dumps' in dir(json):
27 raise ImportError
29 except ImportError:
30 import json
33 def get_podcasts_for_languages(languages=None, podcast_query=Podcast.objects.all()):
34 if not languages:
35 return Podcast.objects.all()
37 regex = '^(' + '|'.join(languages) + ')'
38 return podcast_query.filter(language__regex=regex)
41 def get_toplist(count, languages=None, types=None):
42 """
43 Returns count podcasts with the most subscribers (individual users)
44 If languages is given as an array of 2-character languages codes, only
45 podcasts with the given languages are considered
47 For language-specific lists the entries' oldplace attribute is calculated
48 based on the same language list
49 """
51 # if we include all "known" types, ignore this criteria
52 # this will then include all types, also unknown ones
53 if types and len(types) == len(CONTENT_TYPES):
54 types = None
56 if not languages and not types:
57 return ToplistEntry.objects.all()[:count]
58 else:
59 podcast_entries_base = ToplistEntry.objects.all()
60 group_entries_base = ToplistEntry.objects.all()
62 if languages:
63 lang_regex = '^(' + '|'.join(languages) + ')'
64 podcast_entries_base = podcast_entries_base.filter(podcast__language__regex=lang_regex)
65 group_entries_base = group_entries_base.filter(podcast_group__podcast__language__regex=lang_regex).distinct()
67 if types:
68 type_regex = '.*(' + '|'.join(types) + ').*'
69 podcast_entries_base = podcast_entries_base.filter(podcast__content_types__regex=type_regex)
70 group_entries_base = group_entries_base.filter(podcast_group__podcast__content_types__regex=type_regex).distinct()
73 old_podcast_entries = list(podcast_entries_base.exclude(oldplace=0).order_by('oldplace')[:count])
74 old_group_entries = list(group_entries_base.exclude(oldplace=0).order_by('oldplace')[:count])
75 old_list = merge_toplists(old_podcast_entries, old_group_entries, lambda x: x.oldplace, reverse=False)
76 old_items = [e.get_item() for e in old_list]
78 podcast_entries = podcast_entries_base.order_by('-subscriptions')[:count]
79 group_entries = group_entries_base.order_by('-subscriptions')[:count]
80 cur_list = merge_toplists(podcast_entries, group_entries, lambda x: x.subscriptions, reverse=True, count=count)
82 for x in cur_list:
83 x.oldplace = old_items.index(x.get_item())+1 if x.get_item() in old_items else 0
85 return cur_list
87 def get_episode_toplist(count, languages=None, types=None):
88 """Returns the first num entries of the episode toplist with the given search criteria"""
90 # if we include all "known" types, ignore this criteria
91 # this will then include all types, also unknown ones
92 if types and len(types) == len(CONTENT_TYPES):
93 types = None
95 entries = EpisodeToplistEntry.objects.all()
97 if languages:
98 regex = '^(' + '|'.join(languages) + ')'
99 entries = entries.filter(episode__podcast__language__regex=regex)
101 if types:
102 # we can just use a regex here, because searching for the right "type" of an
103 # episode is more complex; we first look for all podcasts with the right type
104 # and then look through their episodes
105 type_regex = '.*(' + '|'.join(types) + ').*'
106 entries = entries.filter(episode__podcast__content_types__regex=type_regex)
107 entry_list = []
108 for e in entries:
109 if e.episode.mimetype and get_type(e.episode.mimetype) in types:
110 entry_list.append(e)
112 if len(entry_list) >= count:
113 break
115 return entries[:count]
118 def merge_toplists(podcast_entries, group_entries, sortkey, reverse, count=None):
120 merges a podcast- and a group toplist based on the given sortkey
122 entries = list(podcast_entries)
123 entries.extend(group_entries)
124 entries.sort(key=sortkey, reverse=reverse)
125 if count:
126 entries = entries[:count]
127 return entries
130 def get_random_picks(languages=None, recent_days=timedelta(days=7)):
131 all_podcasts = Podcast.objects.all().exclude(title='').order_by('?')
132 lang_podcasts = get_podcasts_for_languages(languages, all_podcasts)
134 if lang_podcasts.count() > 0:
135 return lang_podcasts
136 else:
137 return all_podcasts
140 def get_all_subscriptions(user):
141 return set([s.podcast for s in Subscription.objects.filter(user=user)])
144 def get_device(user, uid, undelete=True):
146 Loads or creates the device indicated by user, uid.
148 If the device has been deleted and undelete=True, it is undeleted.
150 device, created = Device.objects.get_or_create(user=user, uid=uid)
152 if device.deleted and undelete:
153 device.deleted = False
154 device.save()
156 return device