Package lastfm :: Module weeklychart
[hide private]
[frames] | no frames]

Source Code for Module lastfm.weeklychart

  1  #!/usr/bin/env python 
  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 
9 10 -class WeeklyChart(LastfmBase, Cacheable):
11 """A class for representing the weekly charts""" 12
13 - def init(self, subject, start, end, 14 stats = None):
15 self._subject = subject 16 self._start = start 17 self._end = end 18 self._stats = stats
19 20 @property
21 - def subject(self):
22 return self._subject
23 24 @property
25 - def start(self):
26 return self._start
27 28 @property
29 - def end(self):
30 return self._end
31 32 @property
33 - def stats(self):
34 return self._stats
35 36 @staticmethod
37 - def create_from_data(api, subject, data):
38 return WeeklyChart( 39 subject = subject, 40 start = datetime.utcfromtimestamp(int(data.attrib['from'])), 41 end = datetime.utcfromtimestamp(int(data.attrib['to'])) 42 )
43 44 @staticmethod
45 - def _check_weekly_chart_params(params, start = None, end = None):
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
60 - def _hash_func(*args, **kwds):
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
71 - def __hash__(self):
72 return self.__class__._hash_func( 73 subject = self.subject, 74 start = self.start, 75 end = self.end 76 )
77
78 - def __eq__(self, other):
79 return self.subject == other.subject and \ 80 self.start == other.start and \ 81 self.end == other.end
82
83 - def __lt__(self, other):
84 if self.subject == other.subject: 85 if self.start == other.start: 86 return self.end < other.end 87 else: 88 return self.start < other.start 89 else: 90 return self.subject < other.subject
91
92 - def __repr__(self):
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
102 -class WeeklyAlbumChart(WeeklyChart):
103 """A class for representing the weekly album charts"""
104 - def init(self, subject, start, end, stats, albums):
105 super(WeeklyAlbumChart, self).init(subject, start, end, stats) 106 self._albums = albums
107 108 @property
109 - def albums(self):
110 return self._albums
111 112 @staticmethod
113 - def create_from_data(api, subject, data):
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
156 -class WeeklyArtistChart(WeeklyChart):
157 """A class for representing the weekly artist charts"""
158 - def init(self, subject, start, end, stats, artists):
159 super(WeeklyArtistChart, self).init(subject, start, end, stats) 160 self._artists = artists
161 162 @property
163 - def artists(self):
164 return self._artists
165 166 @staticmethod
167 - def create_from_data(api, subject, data):
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
204 -class WeeklyTrackChart(WeeklyChart):
205 """A class for representing the weekly track charts"""
206 - def init(self, subject, start, end, tracks, stats):
207 super(WeeklyTrackChart, self).init(subject, start, end, stats) 208 self._tracks = tracks
209 210 @property
211 - def tracks(self):
212 return self._tracks
213 214 @staticmethod
215 - def create_from_data(api, subject, data):
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
257 -class WeeklyTagChart(WeeklyChart):
258 """A class for representing the weekly tag charts"""
259 - def init(self, subject, start, end, tags, stats):
260 super(WeeklyTagChart, self).init(subject, start, end, stats) 261 self._tags = tags
262 263 @property
264 - def tags(self):
265 return self._tags
266 267 @staticmethod
268 - def create_from_data(api, subject, start, end):
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