migrate episode heatmap to CouchDB
[mygpo.git] / mygpo / web / heatmap.py
blobdf62cf4d58a5672f7058dba89c864e5a75324e38
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.users.models import EpisodeUserState
21 class EpisodeHeatmap(object):
22 """ Information about how often certain parts of Episodes are played """
24 def __init__(self, podcast_id, episode_id=None, user_oldid=None):
25 """ Initialize a new Episode heatmap
27 EpisodeHeatmap(podcast_id, [episode_id, [user_oldid]]) """
29 self.podcast_id = podcast_id
31 if episode_id is not None and podcast_id is None:
32 raise ValueError('episode_id can only be used '
33 'if podcast_id is not None')
35 self.episode_id = episode_id
37 if user_oldid is not None and episode_id is None:
38 raise ValueError('user_oldid can only be used '
39 'if episode_id is not None')
41 self.user_oldid = user_oldid
42 self.heatmap = None
43 self.borders = None
46 def _query(self):
47 """ Queries the database and stores the heatmap and its borders """
49 db = EpisodeUserState.get_db()
51 group_level = len(filter(None, [self.podcast_id,
52 self.episode_id, self.user_oldid]))
54 r = db.view('users/episode_heatmap',
55 startkey = [self.podcast_id, self.episode_id,
56 self.user_oldid],
57 endkey = [self.podcast_id, self.episode_id or {},
58 self.user_oldid or {}],
59 reduce = True,
60 group = True,
61 group_level = group_level,
64 if not r:
65 self.heatmap = []
66 self.borders = []
67 else:
68 res = r.first()['value']
69 self.heatmap = res['heatmap']
70 self.borders = res['borders']
73 def query_if_required():
74 """ If required, queries the database before calling the function """
76 def decorator(f):
77 def tmp(self, *args, **kwargs):
78 if None in (self.heatmap, self.borders):
79 self._query()
81 return f(self, *args, **kwargs)
82 return tmp
83 return decorator
86 @property
87 @query_if_required()
88 def max_plays(self):
89 """ Returns the highest number of plays of all sections """
91 return max(self.heatmap)
94 @property
95 @query_if_required()
96 def sections(self):
97 """ Returns an iterator that emits (from, to, play-counts) tuples
99 Each tuple represents one part in the heatmap with a distinct
100 play-count. from and to indicate the range of section in seconds."""
102 for i in range(len(self.heatmap)):
103 yield (self.borders[i], self.borders[i+1], self.heatmap[i])
106 @query_if_required()
107 def __nonzero__(self):
108 return any(self.heatmap)