Merge branch 'master' into django-1.10
[mygpo.git] / mygpo / publisher / utils.py
blob79fe1521f332ddcc397d05990625a61d68502eb1
1 from collections import namedtuple, defaultdict
2 from datetime import timedelta, datetime, time
4 from mygpo.podcasts.models import Episode
5 from mygpo.utils import daterange, flatten
6 from mygpo.history.models import EpisodeHistoryEntry
7 from mygpo.history.stats import playcounts_timerange
8 from mygpo.publisher.models import PublishedPodcast
11 ListenerData = namedtuple('ListenerData', 'date playcount episode')
13 def listener_data(podcasts, start_date=datetime(2010, 1, 1),
14 leap=timedelta(days=1)):
15 """ Returns data for the podcast listener timeseries
17 An iterator with data for each day (starting from either the first released
18 episode or the earliest play-event) is returned, where each day is
19 reresented by a ListenerData tuple. """
20 # index episodes by releaes-date
21 episodes = Episode.objects.filter(podcast__in=podcasts,
22 released__gt=start_date)
23 episodes = {e.released.date(): e for e in episodes}
25 history = EpisodeHistoryEntry.objects\
26 .filter(episode__podcast__in=podcasts,
27 timestamp__gte=start_date)\
28 # contains play-counts, indexed by date {date: play-count}
29 play_counts = playcounts_timerange(history)
31 # we start either at the first episode-release or the first listen-event
32 events = list(episodes.keys()) + list(play_counts.keys())
34 if not events:
35 # if we don't have any events, stop
36 return
38 start = min(events)
39 for date in daterange(start, leap=leap):
40 playcount = play_counts.get(date, 0)
41 episode = episodes.get(date, None)
42 yield ListenerData(date, playcount, episode)
45 def episode_listener_data(episode, start_date=datetime(2010, 1, 1),
46 leap=timedelta(days=1)):
47 """ Returns data for the episode listener timeseries
49 An iterator with data for each day (starting from the first event
50 is returned, where each day is represented by a ListenerData tuple """
51 history = EpisodeHistoryEntry.objects\
52 .filter(episode=episode,
53 timestamp__gte=start_date)\
54 # contains play-counts, indexed by date {date: play-count}
55 play_counts = playcounts_timerange(history)
57 # we start either at the episode-release or the first listen-event
58 events = list(play_counts.keys()) + \
59 [episode.released.date()] if episode.released else []
61 if not events:
62 return
64 # we always start at the first listen-event
65 start = min(events)
66 for date in daterange(start, leap=leap):
67 playcount = play_counts.get(date, 0)
68 e = episode if (episode.released.date() == date) else None
69 yield ListenerData(date, playcount, e)
72 def subscriber_data(podcasts):
73 coll_data = defaultdict(int)
75 # TODO
77 return []
79 # TODO. rewrite
80 for podcast in podcasts:
81 create_entry = lambda r: (r.timestamp.strftime('%y-%m'), r.subscriber_count)
83 subdata = [podcast.subscribers]
85 data = dict(map(create_entry, subdata))
87 for k in data:
88 coll_data[k] += data[k]
90 # create a list of {'x': label, 'y': value}
91 coll_data = sorted([dict(x=a, y=b) for (a, b) in coll_data.items()], key=lambda x: x['x'])
93 return coll_data
96 def check_publisher_permission(user, podcast):
97 """ Checks if the user has publisher permissions for the given podcast """
99 if not user.is_authenticated:
100 return False
102 if user.is_staff:
103 return True
105 return PublishedPodcast.objects.filter(publisher=user, podcast=podcast).exists()
108 def colour_repr(val, max_val, colours):
110 returns a color representing the given value within a color gradient.
112 The color gradient is given by a list of (r, g, b) tupels. The value
113 is first located within two colors (of the list) and then approximated
114 between these two colors, based on its position within this segment.
116 if len(colours) == 1:
117 return colours[0]
119 if max_val == 0:
120 return colours[0]
122 # calculate position in the gradient; defines the segment
123 pos = float(val) / max_val
124 colour_nr1 = min(len(colours)-1, int(pos * (len(colours)-1)))
125 colour_nr2 = min(len(colours)-1, colour_nr1+1)
126 colour1 = colours[ colour_nr1 ]
127 colour2 = colours[ colour_nr2 ]
129 r1, g1, b1 = colour1
130 r2, g2, b2 = colour2
132 # determine bounds of segment
133 lower_bound = float(max_val) / (len(colours)-1) * colour_nr1
134 upper_bound = min(max_val, lower_bound + float(max_val) / (len(colours)-1))
136 # position within the segment
137 percent = (val - lower_bound) / upper_bound
139 r_step = r2 - r1
140 g_step = g2 - g1
141 b_step = b2 - b1
143 return (r1 + r_step * percent, g1 + g_step * percent, b1 + b_step * percent)