[PodcastLists] migration to Django ORM
[mygpo.git] / mygpo / publisher / utils.py
blobf628c231f32befd4f1866af76aabf7f71be8ee42
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 import collections
19 from datetime import timedelta, datetime, time
21 from mygpo.podcasts.models import Episode
22 from mygpo.utils import daterange, flatten
23 from mygpo.publisher.models import PublishedPodcast
24 from mygpo.db.couchdb.episode_state import podcast_listener_count_timespan, \
25 episode_listener_count_timespan
28 def listener_data(podcasts, start_date=datetime(2010, 1, 1), leap=timedelta(days=1)):
29 """ Returns data for the podcast listener timeseries
31 An iterator with data for each day (starting from either the first released
32 episode or the earliest listen-event) is returned, where each day
33 is reresented by a dictionary
35 * date: the day
36 * listeners: the number of listeners on that day
37 * episode: (one of) the episode(s) released on that day
38 """
40 # pre-calculate episode list, make it index-able by release-date
41 episodes = Episode.objects.filter(podcast__in=podcasts, released__gt=start_date)
42 episodes = dict((e.released.date(), e) for e in episodes)
44 listeners = [ podcast_listener_count_timespan(p, start=start_date)
45 for p in podcasts ]
46 listeners = filter(None, listeners)
48 # we start either at the first episode-release or the first listen-event
49 events = []
51 if episodes.keys():
52 events.append(min(episodes.keys()))
54 if listeners:
55 events.append(min([l[0][0] for l in listeners]))
57 if not events:
58 return
60 start = min(events)
62 for d in daterange(start, leap=leap):
64 listener_sum = 0
65 for l in listeners:
66 if not l:
67 continue
69 day, count = l[0]
70 if day == d:
71 listener_sum += count
72 l.pop(0)
74 episode = episodes[d] if d in episodes else None
76 yield dict(date=d, listeners=listener_sum, episode=episode)
80 def episode_listener_data(episode, start_date=datetime(2010, 1, 1), leap=timedelta(days=1)):
81 """ Returns data for the episode listener timeseries
83 An iterator with data for each day (starting from the first listen-event)
84 is returned, where each day is represented by a dictionary
86 * date: the day
87 * listeners: the number of listeners on that day
88 * episode: the episode, if it was released on that day, otherwise None
89 """
91 # TODO
93 return
95 listeners = episode_listener_count_timespan(episode, start=start_date)
97 if not listeners:
98 return
100 # we always start at the first listen-event
101 start = listeners[0][0]
102 start = datetime.combine(start, time())
104 for d in daterange(start, leap=leap):
105 next = d + leap
107 if listeners and listeners[0] and listeners[0][0] == d.date():
108 day, l = listeners.pop(0)
109 else:
110 l = 0
112 released = episode.released and episode.released >= d and episode.released <= next
113 released_episode = episode if released else None
115 yield dict(date=d, listeners=l, episode=released_episode)
118 def subscriber_data(podcasts):
119 coll_data = collections.defaultdict(int)
121 # TODO
123 return []
125 # TODO. rewrite
126 for podcast in podcasts:
127 create_entry = lambda r: (r.timestamp.strftime('%y-%m'), r.subscriber_count)
129 subdata = [podcast.subscribers]
131 data = dict(map(create_entry, subdata))
133 for k in data:
134 coll_data[k] += data[k]
136 # create a list of {'x': label, 'y': value}
137 coll_data = sorted([dict(x=a, y=b) for (a, b) in coll_data.items()], key=lambda x: x['x'])
139 return coll_data
142 def check_publisher_permission(user, podcast):
143 """ Checks if the user has publisher permissions for the given podcast """
145 if not user.is_authenticated():
146 return False
148 if user.is_staff:
149 return True
151 return PublishedPodcast.objects.filter(publisher=user, podcast=podcast).exists()
154 def colour_repr(val, max_val, colours):
156 returns a color representing the given value within a color gradient.
158 The color gradient is given by a list of (r, g, b) tupels. The value
159 is first located within two colors (of the list) and then approximated
160 between these two colors, based on its position within this segment.
162 if len(colours) == 1:
163 return colours[0]
165 if max_val == 0:
166 return colours[0]
168 # calculate position in the gradient; defines the segment
169 pos = float(val) / max_val
170 colour_nr1 = min(len(colours)-1, int(pos * (len(colours)-1)))
171 colour_nr2 = min(len(colours)-1, colour_nr1+1)
172 colour1 = colours[ colour_nr1 ]
173 colour2 = colours[ colour_nr2 ]
175 r1, g1, b1 = colour1
176 r2, g2, b2 = colour2
178 # determine bounds of segment
179 lower_bound = float(max_val) / (len(colours)-1) * colour_nr1
180 upper_bound = min(max_val, lower_bound + float(max_val) / (len(colours)-1))
182 # position within the segment
183 percent = (val - lower_bound) / upper_bound
185 r_step = r2 - r1
186 g_step = g2 - g1
187 b_step = b2 - b1
189 return (r1 + r_step * percent, g1 + g_step * percent, b1 + b_step * percent)