[Migration] remove subscriberdata_for_podcast
[mygpo.git] / mygpo / publisher / utils.py
blob3f34511860e44d6d7dbae3325cf4c2d5031afc97
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.utils import daterange, flatten
22 from mygpo.db.couchdb.episode_state import podcast_listener_count_timespan, \
23 episode_listener_count_timespan
26 def listener_data(podcasts, start_date=datetime(2010, 1, 1), leap=timedelta(days=1)):
27 """ Returns data for the podcast listener timeseries
29 An iterator with data for each day (starting from either the first released
30 episode or the earliest listen-event) is returned, where each day
31 is reresented by a dictionary
33 * date: the day
34 * listeners: the number of listeners on that day
35 * episode: (one of) the episode(s) released on that day
36 """
38 # pre-calculate episode list, make it index-able by release-date
39 episodes = Episode.objects.filter(podcast__in=podcasts, release__gt=start_date)
40 episodes = dict((e.released.date(), e) for e in episodes)
42 listeners = [ podcast_listener_count_timespan(p, start=start_date)
43 for p in podcasts ]
44 listeners = filter(None, listeners)
46 # we start either at the first episode-release or the first listen-event
47 events = []
49 if episodes.keys():
50 events.append(min(episodes.keys()))
52 if listeners:
53 events.append(min([l[0][0] for l in listeners]))
55 if not events:
56 return
58 start = min(events)
60 for d in daterange(start, leap=leap):
62 listener_sum = 0
63 for l in listeners:
64 if not l:
65 continue
67 day, count = l[0]
68 if day == d:
69 listener_sum += count
70 l.pop(0)
72 episode = episodes[d] if d in episodes else None
74 yield dict(date=d, listeners=listener_sum, episode=episode)
78 def episode_listener_data(episode, start_date=datetime(2010, 1, 1), leap=timedelta(days=1)):
79 """ Returns data for the episode listener timeseries
81 An iterator with data for each day (starting from the first listen-event)
82 is returned, where each day is represented by a dictionary
84 * date: the day
85 * listeners: the number of listeners on that day
86 * episode: the episode, if it was released on that day, otherwise None
87 """
89 listeners = episode_listener_count_timespan(episode, start=start_date)
91 if not listeners:
92 return
94 # we always start at the first listen-event
95 start = listeners[0][0]
96 start = datetime.combine(start, time())
98 for d in daterange(start, leap=leap):
99 next = d + leap
101 if listeners and listeners[0] and listeners[0][0] == d.date():
102 day, l = listeners.pop(0)
103 else:
104 l = 0
106 released = episode.released and episode.released >= d and episode.released <= next
107 released_episode = episode if released else None
109 yield dict(date=d, listeners=l, episode=released_episode)
112 def subscriber_data(podcasts):
113 coll_data = collections.defaultdict(int)
115 # TODO. rewrite
116 for podcast in podcasts:
117 create_entry = lambda r: (r.timestamp.strftime('%y-%m'), r.subscriber_count)
119 subdata = [podcast.subscribers]
121 data = dict(map(create_entry, subdata))
123 for k in data:
124 coll_data[k] += data[k]
126 # create a list of {'x': label, 'y': value}
127 coll_data = sorted([dict(x=a, y=b) for (a, b) in coll_data.items()], key=lambda x: x['x'])
129 return coll_data
132 def check_publisher_permission(user, podcast):
133 """ Checks if the user has publisher permissions for the given podcast """
135 if not user.is_authenticated():
136 return False
138 if user.is_staff:
139 return True
141 return (podcast.get_id() in user.published_objects)
144 def colour_repr(val, max_val, colours):
146 returns a color representing the given value within a color gradient.
148 The color gradient is given by a list of (r, g, b) tupels. The value
149 is first located within two colors (of the list) and then approximated
150 between these two colors, based on its position within this segment.
152 if len(colours) == 1:
153 return colours[0]
155 if max_val == 0:
156 return colours[0]
158 # calculate position in the gradient; defines the segment
159 pos = float(val) / max_val
160 colour_nr1 = min(len(colours)-1, int(pos * (len(colours)-1)))
161 colour_nr2 = min(len(colours)-1, colour_nr1+1)
162 colour1 = colours[ colour_nr1 ]
163 colour2 = colours[ colour_nr2 ]
165 r1, g1, b1 = colour1
166 r2, g2, b2 = colour2
168 # determine bounds of segment
169 lower_bound = float(max_val) / (len(colours)-1) * colour_nr1
170 upper_bound = min(max_val, lower_bound + float(max_val) / (len(colours)-1))
172 # position within the segment
173 percent = (val - lower_bound) / upper_bound
175 r_step = r2 - r1
176 g_step = g2 - g1
177 b_step = b2 - b1
179 return (r1 + r_step * percent, g1 + g_step * percent, b1 + b_step * percent)