1
2
3 __author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
4 __version__ = "0.2"
5 __license__ = "GNU Lesser General Public License"
6
7 from lastfm.base import LastfmBase
8 from lastfm.mixins import Cacheable
11 """A class for representing the weekly charts"""
12
13 - def init(self, subject, start, end,
14 stats = None):
19
20 @property
23
24 @property
27
28 @property
31
32 @property
35
36 @staticmethod
43
44 @staticmethod
46 if (start is not None and end is None) or (start is None and end is not None):
47 raise InvalidParametersError("both start and end have to be provided.")
48 if start is not None and end is not None:
49 if isinstance(start, datetime) and isinstance(end, datetime):
50 params.update({
51 'from': int(calendar.timegm(start.timetuple())),
52 'to': int(calendar.timegm(end.timetuple()))
53 })
54 else:
55 raise InvalidParametersError("start and end must be datetime.datetime instances")
56
57 return params
58
59 @staticmethod
61 try:
62 return hash("%s%s%s%s" % (
63 kwds['subject'].__class__.__name__,
64 kwds['subject'].name,
65 kwds['start'],
66 kwds['end']
67 ))
68 except KeyError:
69 raise InvalidParametersError("subject, start and end have to be provided for hashing")
70
77
82
91
93 return "<lastfm.%s: for %s:%s from %s to %s>" % \
94 (
95 self.__class__.__name__,
96 self.subject.__class__.__name__,
97 self.subject.name,
98 self.start.strftime("%x"),
99 self.end.strftime("%x"),
100 )
101
103 """A class for representing the weekly album charts"""
104 - def init(self, subject, start, end, stats, albums):
107
108 @property
111
112 @staticmethod
114 w = WeeklyChart(
115 subject = subject,
116 start = datetime.utcfromtimestamp(int(data.attrib['from'])),
117 end = datetime.utcfromtimestamp(int(data.attrib['to'])),
118 )
119 return WeeklyAlbumChart(
120 subject = subject,
121 start = datetime.utcfromtimestamp(int(data.attrib['from'])),
122 end = datetime.utcfromtimestamp(int(data.attrib['to'])),
123 stats = Stats(
124 subject = subject,
125 playcount = reduce(
126 lambda x,y:(
127 x + int(y.findtext('playcount'))
128 ),
129 data.findall('album'),
130 0
131 )
132 ),
133 albums = [
134 Album(
135 api,
136 subject = w,
137 name = a.findtext('name'),
138 mbid = a.findtext('mbid'),
139 artist = Artist(
140 api,
141 subject = w,
142 name = a.findtext('artist'),
143 mbid = a.find('artist').attrib['mbid'],
144 ),
145 stats = Stats(
146 subject = a.findtext('name'),
147 rank = int(a.attrib['rank']),
148 playcount = int(a.findtext('playcount')),
149 ),
150 url = a.findtext('url'),
151 )
152 for a in data.findall('album')
153 ]
154 )
155
157 """A class for representing the weekly artist charts"""
158 - def init(self, subject, start, end, stats, artists):
161
162 @property
165
166 @staticmethod
168 w = WeeklyChart(
169 subject = subject,
170 start = datetime.utcfromtimestamp(int(data.attrib['from'])),
171 end = datetime.utcfromtimestamp(int(data.attrib['to'])),
172 )
173 count_attribute = data.find('artist').findtext('playcount') and 'playcount' or 'weight'
174 def get_count_attribute(artist):
175 return {count_attribute: int(eval(artist.findtext(count_attribute)))}
176 def get_count_attribute_sum(artists):
177 return {count_attribute: reduce(lambda x,y:(x + int(eval(y.findtext(count_attribute)))), artists, 0)}
178
179 return WeeklyArtistChart(
180 subject = subject,
181 start = datetime.utcfromtimestamp(int(data.attrib['from'])),
182 end = datetime.utcfromtimestamp(int(data.attrib['to'])),
183 stats = Stats(
184 subject = subject,
185 **get_count_attribute_sum(data.findall('artist'))
186 ),
187 artists = [
188 Artist(
189 api,
190 subject = w,
191 name = a.findtext('name'),
192 mbid = a.findtext('mbid'),
193 stats = Stats(
194 subject = a.findtext('name'),
195 rank = int(a.attrib['rank']),
196 **get_count_attribute(a)
197 ),
198 url = a.findtext('url'),
199 )
200 for a in data.findall('artist')
201 ]
202 )
203
205 """A class for representing the weekly track charts"""
206 - def init(self, subject, start, end, tracks, stats):
209
210 @property
213
214 @staticmethod
216 w = WeeklyChart(
217 subject = subject,
218 start = datetime.utcfromtimestamp(int(data.attrib['from'])),
219 end = datetime.utcfromtimestamp(int(data.attrib['to'])),
220 )
221 return WeeklyTrackChart(
222 subject = subject,
223 start = datetime.utcfromtimestamp(int(data.attrib['from'])),
224 end = datetime.utcfromtimestamp(int(data.attrib['to'])),
225 stats = Stats(
226 subject = subject,
227 playcount = reduce(
228 lambda x,y:(
229 x + int(y.findtext('playcount'))
230 ),
231 data.findall('track'),
232 0
233 )
234 ),
235 tracks = [
236 Track(
237 api,
238 subject = w,
239 name = t.findtext('name'),
240 mbid = t.findtext('mbid'),
241 artist = Artist(
242 api,
243 name = t.findtext('artist'),
244 mbid = t.find('artist').attrib['mbid'],
245 ),
246 stats = Stats(
247 subject = t.findtext('name'),
248 rank = int(t.attrib['rank']),
249 playcount = int(t.findtext('playcount')),
250 ),
251 url = t.findtext('url'),
252 )
253 for t in data.findall('track')
254 ]
255 )
256
258 """A class for representing the weekly tag charts"""
259 - def init(self, subject, start, end, tags, stats):
262
263 @property
266
267 @staticmethod
269 w = WeeklyChart(
270 subject = subject,
271 start = start,
272 end = end,
273 )
274 max_tag_count = 3
275 global_top_tags = api.get_global_top_tags()
276 from collections import defaultdict
277
278 wac = subject.get_weekly_artist_chart(start, end)
279 all_tags = defaultdict(lambda:0)
280 tag_weights = defaultdict(lambda:0)
281 total_playcount = 0
282 artist_count = 0
283 for artist in wac.artists:
284 artist_count += 1
285 total_playcount += artist.stats.playcount
286 tag_count = 0
287 for tag in artist.top_tags:
288 if tag not in global_top_tags: continue
289 if tag_count >= max_tag_count: break
290 all_tags[tag] += 1
291 tag_count += 1
292
293 artist_pp = artist.stats.playcount/float(wac.stats.playcount)
294 cumulative_pp = total_playcount/float(wac.stats.playcount)
295 if (cumulative_pp > 0.75 or artist_pp < 0.01) and artist_count > 10:
296 break
297
298 for artist in wac.artists[:artist_count]:
299 artist_pp = artist.stats.playcount/float(wac.stats.playcount)
300 tf = 1/float(max_tag_count)
301 tag_count = 0
302 weighted_tfidfs = {}
303 for tag in artist.top_tags:
304 if tag not in global_top_tags: continue
305 if tag_count >= max_tag_count: break
306
307 df = all_tags[tag]/float(artist_count)
308 tfidf = tf/df
309 weighted_tfidf = float(max_tag_count - tag_count)*tfidf
310 weighted_tfidfs[tag.name] = weighted_tfidf
311 tag_count += 1
312
313 sum_weighted_tfidfs = sum(weighted_tfidfs.values())
314 for tag in weighted_tfidfs:
315 tag_weights[tag] += weighted_tfidfs[tag]/sum_weighted_tfidfs*artist_pp
316
317 artist_pp = artist.stats.playcount/float(wac.stats.playcount)
318
319 tag_weights_sum = sum(tag_weights.values())
320 tag_weights = tag_weights.items()
321 tag_weights.sort(key=lambda x:x[1], reverse=True)
322 for i in xrange(len(tag_weights)):
323 tag, weight = tag_weights[i]
324 tag_weights[i] = (tag, weight, i+1)
325
326 wtc = WeeklyTagChart(
327 subject = subject,
328 start = wac.start,
329 end = wac.end,
330 stats = Stats(
331 subject = subject,
332 playcount = 1000
333 ),
334 tags = [
335 Tag(
336 api,
337 subject = w,
338 name = tag,
339 stats = Stats(
340 subject = tag,
341 rank = rank,
342 count = int(round(1000*weight/tag_weights_sum)),
343 )
344 )
345 for (tag, weight, rank) in tag_weights
346 ]
347 )
348 wtc._artist_spectrum_analyzed = 100*total_playcount/float(wac.stats.playcount)
349 return wtc
350
351 from datetime import datetime
352 import calendar
353
354 from lastfm.album import Album
355 from lastfm.artist import Artist
356 from lastfm.error import InvalidParametersError
357 from lastfm.stats import Stats
358 from lastfm.track import Track
359 from lastfm.tag import Tag
360