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