73f23680022af996d4287b6fa0f1dc575fe2f1d7
[mygpo.git] / mygpo / api / backend.py
blob73f23680022af996d4287b6fa0f1dc575fe2f1d7
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 django.db.models import Max
21 from datetime import datetime, timedelta
22 import re
24 try:
25 import json
27 # Python 2.5 seems to have a different json module
28 if not 'dumps' in dir(json):
29 raise ImportError
31 except ImportError:
32 import json
35 def get_podcasts_for_languages(languages=None, podcast_query=Podcast.objects.all()):
36 if not languages:
37 return Podcast.objects.all()
39 regex = '^(' + '|'.join(languages) + ')'
40 return podcast_query.filter(language__regex=regex)
43 def get_toplist(count, languages=None, types=None):
44 """
45 Returns count podcasts with the most subscribers (individual users)
46 If languages is given as an array of 2-character languages codes, only
47 podcasts with the given languages are considered
49 For language-specific lists the entries' oldplace attribute is calculated
50 based on the same language list
51 """
53 # if we include all "known" types, ignore this criteria
54 # this will then include all types, also unknown ones
55 if types and len(types) == len(CONTENT_TYPES):
56 types = None
58 if not languages and not types:
59 return ToplistEntry.objects.all()[:count]
60 else:
61 podcast_entries_base = ToplistEntry.objects.all()
62 group_entries_base = ToplistEntry.objects.all()
64 if languages:
65 lang_regex = '^(' + '|'.join(languages) + ')'
66 podcast_entries_base = podcast_entries_base.filter(podcast__language__regex=lang_regex)
67 group_entries_base = group_entries_base.filter(podcast_group__podcast__language__regex=lang_regex).distinct()
69 if types:
70 type_regex = '.*(' + '|'.join(types) + ').*'
71 podcast_entries_base = podcast_entries_base.filter(podcast__content_types__regex=type_regex)
72 group_entries_base = group_entries_base.filter(podcast_group__podcast__content_types__regex=type_regex).distinct()
75 old_podcast_entries = list(podcast_entries_base.exclude(oldplace=0).order_by('oldplace')[:count])
76 old_group_entries = list(group_entries_base.exclude(oldplace=0).order_by('oldplace')[:count])
77 old_list = merge_toplists(old_podcast_entries, old_group_entries, lambda x: x.oldplace, reverse=False)
78 old_items = [e.get_item() for e in old_list]
80 podcast_entries = podcast_entries_base.order_by('-subscriptions')[:count]
81 group_entries = group_entries_base.order_by('-subscriptions')[:count]
82 cur_list = merge_toplists(podcast_entries, group_entries, lambda x: x.subscriptions, reverse=True, count=count)
84 for x in cur_list:
85 x.oldplace = old_items.index(x.get_item())+1 if x.get_item() in old_items else 0
87 return cur_list
89 def get_episode_toplist(count, languages=None, types=None):
90 """Returns the first num entries of the episode toplist with the given search criteria"""
92 # if we include all "known" types, ignore this criteria
93 # this will then include all types, also unknown ones
94 if types and len(types) == len(CONTENT_TYPES):
95 types = None
97 entries = EpisodeToplistEntry.objects.all()
99 if languages:
100 regex = '^(' + '|'.join(languages) + ')'
101 entries = entries.filter(episode__podcast__language__regex=regex)
103 if types:
104 # we can just use a regex here, because searching for the right "type" of an
105 # episode is more complex; we first look for all podcasts with the right type
106 # and then look through their episodes
107 type_regex = '.*(' + '|'.join(types) + ').*'
108 entries = entries.filter(episode__podcast__content_types__regex=type_regex)
109 entry_list = []
110 for e in entries:
111 if e.episode.mimetype and get_type(e.episode.mimetype) in types:
112 entry_list.append(e)
114 if len(entry_list) >= count:
115 break
117 return entries[:count]
120 def merge_toplists(podcast_entries, group_entries, sortkey, reverse, count=None):
122 merges a podcast- and a group toplist based on the given sortkey
124 entries = list(podcast_entries)
125 entries.extend(group_entries)
126 entries.sort(key=sortkey, reverse=reverse)
127 if count:
128 entries = entries[:count]
129 return entries
132 def get_random_picks(languages=None, recent_days=timedelta(days=7)):
133 # threshold = datetime.today() - recent_days
135 all_podcasts = Podcast.objects.all().exclude(title='').order_by('?')
136 lang_podcasts = get_podcasts_for_languages(languages, all_podcasts)
137 # recent_podcasts = lang_podcasts.annotate(latest_release=Max('episode__timestamp')).filter(latest_release__gt=threshold)
139 # if recent_podcasts.count() > 0:
140 # return recent_podcasts
142 if lang_podcasts.count() > 0:
143 return lang_podcasts
145 else:
146 return all_podcasts
148 def get_all_subscriptions(user):
149 return set([s.podcast for s in Subscription.objects.filter(user=user)])
151 def get_public_subscriptions(user):
152 subscriptions = [s for s in Subscription.objects.filter(user=user)]
153 public_subscriptions = set([s.podcast for s in subscriptions if s.get_meta().public])
154 return public_subscriptions