Run 2to3-3.4
[mygpo.git] / mygpo / publisher / utils.py
blobefd27bcdae060970aa561ee42cab09a5263694d6
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 collections import namedtuple, defaultdict
19 from datetime import timedelta, datetime, time
21 from mygpo.podcasts.models import Episode
22 from mygpo.utils import daterange, flatten
23 from mygpo.history.models import EpisodeHistoryEntry
24 from mygpo.history.stats import playcounts_timerange
25 from mygpo.publisher.models import PublishedPodcast
28 ListenerData = namedtuple('ListenerData', 'date playcount episode')
30 def listener_data(podcasts, start_date=datetime(2010, 1, 1),
31 leap=timedelta(days=1)):
32 """ Returns data for the podcast listener timeseries
34 An iterator with data for each day (starting from either the first released
35 episode or the earliest play-event) is returned, where each day is
36 reresented by a ListenerData tuple. """
37 # index episodes by releaes-date
38 episodes = Episode.objects.filter(podcast__in=podcasts,
39 released__gt=start_date)
40 episodes = {e.released.date(): e for e in episodes}
42 history = EpisodeHistoryEntry.objects\
43 .filter(episode__podcast__in=podcasts,
44 timestamp__gte=start_date)\
45 # contains play-counts, indexed by date {date: play-count}
46 play_counts = playcounts_timerange(history)
48 # we start either at the first episode-release or the first listen-event
49 events = list(episodes.keys()) + list(play_counts.keys())
51 if not events:
52 # if we don't have any events, stop
53 return
55 start = min(events)
56 for date in daterange(start, leap=leap):
57 playcount = play_counts.get(date, 0)
58 episode = episodes.get(date, None)
59 yield ListenerData(date, playcount, episode)
62 def episode_listener_data(episode, start_date=datetime(2010, 1, 1),
63 leap=timedelta(days=1)):
64 """ Returns data for the episode listener timeseries
66 An iterator with data for each day (starting from the first event
67 is returned, where each day is represented by a ListenerData tuple """
68 history = EpisodeHistoryEntry.objects\
69 .filter(episode=episode,
70 timestamp__gte=start_date)\
71 # contains play-counts, indexed by date {date: play-count}
72 play_counts = playcounts_timerange(history)
74 # we start either at the episode-release or the first listen-event
75 events = list(play_counts.keys()) + \
76 [episode.released.date()] if episode.released else []
78 if not events:
79 return
81 # we always start at the first listen-event
82 start = min(events)
83 for date in daterange(start, leap=leap):
84 playcount = play_counts.get(date, 0)
85 e = episode if (episode.released.date() == date) else None
86 yield ListenerData(date, playcount, e)
89 def subscriber_data(podcasts):
90 coll_data = defaultdict(int)
92 # TODO
94 return []
96 # TODO. rewrite
97 for podcast in podcasts:
98 create_entry = lambda r: (r.timestamp.strftime('%y-%m'), r.subscriber_count)
100 subdata = [podcast.subscribers]
102 data = dict(list(map(create_entry, subdata)))
104 for k in data:
105 coll_data[k] += data[k]
107 # create a list of {'x': label, 'y': value}
108 coll_data = sorted([dict(x=a, y=b) for (a, b) in list(coll_data.items())], key=lambda x: x['x'])
110 return coll_data
113 def check_publisher_permission(user, podcast):
114 """ Checks if the user has publisher permissions for the given podcast """
116 if not user.is_authenticated():
117 return False
119 if user.is_staff:
120 return True
122 return PublishedPodcast.objects.filter(publisher=user, podcast=podcast).exists()
125 def colour_repr(val, max_val, colours):
127 returns a color representing the given value within a color gradient.
129 The color gradient is given by a list of (r, g, b) tupels. The value
130 is first located within two colors (of the list) and then approximated
131 between these two colors, based on its position within this segment.
133 if len(colours) == 1:
134 return colours[0]
136 if max_val == 0:
137 return colours[0]
139 # calculate position in the gradient; defines the segment
140 pos = float(val) / max_val
141 colour_nr1 = min(len(colours)-1, int(pos * (len(colours)-1)))
142 colour_nr2 = min(len(colours)-1, colour_nr1+1)
143 colour1 = colours[ colour_nr1 ]
144 colour2 = colours[ colour_nr2 ]
146 r1, g1, b1 = colour1
147 r2, g2, b2 = colour2
149 # determine bounds of segment
150 lower_bound = float(max_val) / (len(colours)-1) * colour_nr1
151 upper_bound = min(max_val, lower_bound + float(max_val) / (len(colours)-1))
153 # position within the segment
154 percent = (val - lower_bound) / upper_bound
156 r_step = r2 - r1
157 g_step = g2 - g1
158 b_step = b2 - b1
160 return (r1 + r_step * percent, g1 + g_step * percent, b1 + b_step * percent)