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 functools
import wraps
20 from mygpo
.history
.models
import EpisodeHistoryEntry
23 class EpisodeHeatmap(object):
24 """ Information about how often certain parts of Episodes are played """
26 def __init__(self
, podcast
, episode
=None, user
=None, duration
=None):
27 """ Initialize a new Episode heatmap """
28 self
.duration
= duration
32 history
= EpisodeHistoryEntry
.objects
.filter(episode__podcast
=podcast
)
35 history
= history
.filter(episode
=episode
)
38 history
= history
.filter(user
=user
)
40 self
.history
= history
43 def _raw_heatmap(events
):
44 """ Returns the detailled (exact) heatmap
46 >>> _raw_heatmap([(70, 200), (0, 100), (0, 50)])
47 ([2, 1, 2, 1], [0, 50, 70, 100, 200])
50 # get a list of all borders that occur in events
52 for start
, end
in events
:
55 borders
= sorted(borders
)
57 # this contains the value for the spaces within the borders
58 # therefore we need one field less then we have borders
59 counts
= [0] * (len(borders
)-1)
61 for start
, end
in events
:
62 # for each event we calculate its range
63 start_idx
= borders
.index(start
)
64 end_idx
= borders
.index(end
)
66 # and increase the play-count within the range by 1
67 for inc
in range(start_idx
, end_idx
):
68 counts
[inc
] = counts
[inc
] + 1
70 return counts
, borders
72 # we return the heatmap as (start, stop, playcount) tuples
73 # for i in range(len(counts)):
74 # yield (borders[i], borders[i+1], counts[i])
77 self
.heatmap
, self
.borders
= self
._raw
_heatmap
(
78 self
.history
.values_list('started', 'stopped'))
80 def query_if_required():
81 """ If required, queries the database before calling the function """
84 def tmp(self
, *args
, **kwargs
):
85 if None in (self
.heatmap
, self
.borders
):
88 return f(self
, *args
, **kwargs
)
95 """ Returns the highest number of plays of all sections """
97 return max(self
.heatmap
, default
=0)
102 """ Returns an iterator that emits (from, to, play-counts) tuples
104 Each tuple represents one part in the heatmap with a distinct
105 play-count. from and to indicate the range of section in seconds."""
107 # this could be written as "yield from"
108 for x
in self
._sections
(self
.heatmap
, self
.borders
):
112 def _sections(heatmap
, borders
):
113 """ Merges heatmap-counts and borders into a list of 3-tuples
115 Each tuple contains (start-border, end-border, play-count)
117 >>> list(_sections([2, 1, 2, 1], [0, 50, 70, 100, 200]))
118 [(0, 50, 2), (50, 70, 1), (70, 100, 2), (100, 200, 1)]
120 for i
in range(len(heatmap
)):
121 yield (borders
[i
], borders
[i
+1], heatmap
[i
])
124 def __nonzero__(self
):
125 return any(self
.heatmap
)