1 from itertools
import product
3 from datetime
import date
, timedelta
5 from django
.core
.cache
import cache
7 from mygpo
.core
.models
import Episode
, Podcast
, PodcastGroup
8 from mygpo
.data
.mimetype
import get_type
, CONTENT_TYPES
9 from mygpo
.utils
import daterange
14 class Toplist(object):
15 """ Base class for Episode and Podcast toplists """
17 def __init__(self
, cls
, view
, languages
=[], types
=[], view_args
={}):
19 self
.languages
= languages
21 self
.view_args
= view_args
23 if len(types
) == len(CONTENT_TYPES
):
29 def _get_query_keys(self
):
30 """ Returns an iterator of query keys that are passed to the view """
32 if not self
.languages
and not self
.types
:
35 elif self
.languages
and not self
.types
:
36 for lang
in self
.languages
:
37 yield ["language", lang
]
39 elif not self
.languages
and self
.types
:
40 for type in self
.types
:
44 for typ
, lang
in product(self
.types
, self
.languages
):
45 yield ["type-language", typ
, lang
]
48 def _query(self
, skip
, limit
):
49 """ Queries the database and returns the sorted results """
52 for key
in self
._get
_query
_keys
():
53 r
= self
._cache
_or
_query
(skip
, limit
, key
)
56 results
= list(set(results
))
57 results
= self
._sort
(results
)
58 return results
[skip
:skip
+limit
]
61 def _cache_or_query(self
, skip
, limit
, key
):
62 cache_str
= '{cls}-{skip}-{limit}-{key}'.format(
65 cls
=self
.__class
__.__name
__,
69 res
= cache
.get(cache_str
)
71 r
= self
.cls
.view(self
.view
,
72 startkey
= key
+ [{}],
73 endkey
= key
+ [None],
77 stale
= 'update_after',
81 cache
.set(cache_str
, res
, CACHE_SECONDS
)
86 def _sort(self
, results
):
90 def __getitem__(self
, key
):
91 if isinstance(key
, slice):
92 start
= key
.start
or 0
93 length
= key
.stop
- start
98 return self
._query
(start
, length
)
102 class EpisodeToplist(Toplist
):
103 """ Retrieves the episode toplist for a certain date """
105 def __init__(self
, languages
=[], types
=[], startdate
=None):
106 super(EpisodeToplist
, self
).__init
__(Episode
,
107 'toplist/episodes', languages
, types
)
108 self
.date
= startdate
or date
.today()
111 def _sort(self
, results
):
112 results
.sort(key
=lambda episode
: episode
.listeners
, reverse
=True)
116 def _get_query_keys(self
):
117 """ Returns the query keys based on instance variables """
119 date_str
= self
.date
.strftime('%Y-%m-%d')
121 for criteria
in super(EpisodeToplist
, self
)._get
_query
_keys
():
122 yield [date_str
] + criteria
126 class PodcastToplist(Toplist
):
127 """ Podcast toplist based on number of subscribers """
129 # FIXME: podcast and episode toplist are separated now, so we could
130 # get rid of the type field
133 def __init__(self
, languages
=[], types
=[]):
134 super(PodcastToplist
, self
).__init
__(Podcast
, 'toplist/podcasts',
136 view_args
=dict(classes
=[Podcast
, PodcastGroup
]))
139 def _get_query_keys(self
):
140 for criteria
in super(PodcastToplist
, self
)._get
_query
_keys
():
141 yield [self
.TYPE
] + criteria
144 def _sort(self
, results
):
145 # sort by subscriber_count and id to ensure same order when subscriber_count is equal
146 cur
= sorted(results
, key
=lambda p
: (p
.subscriber_count(), p
.get_id()), reverse
=True)
147 prev
= sorted(results
, key
=lambda p
: (p
.prev_subscriber_count(), p
.get_id()), reverse
=True)
149 res
= dict( (p
, n
) for n
, p
in enumerate(cur
))
151 for old
, p
in enumerate(prev
):
155 return [(old
+1, p
) for p
, (new
, old
) in sorted(res
.items(), key
=lambda i
: i
[1][0])]