2008-07-14 15:50:18 +05:30
|
|
|
#!/usr/bin/env python
|
|
|
|
|
2008-08-25 17:14:48 +05:30
|
|
|
__author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
|
2008-09-02 22:06:16 +05:30
|
|
|
__version__ = "0.2"
|
2008-08-27 19:03:00 +05:30
|
|
|
__license__ = "GNU Lesser General Public License"
|
2009-03-17 20:51:40 +05:30
|
|
|
__package__ = "lastfm"
|
2008-08-27 19:03:00 +05:30
|
|
|
|
2009-04-07 10:27:51 +05:30
|
|
|
from functools import reduce
|
2008-12-31 11:00:23 +05:30
|
|
|
from lastfm.base import LastfmBase
|
2009-04-18 10:41:22 +05:30
|
|
|
from lastfm.mixin import mixin
|
2009-04-12 10:37:41 +05:30
|
|
|
from lastfm.util import logging
|
2009-03-17 20:51:40 +05:30
|
|
|
from operator import xor
|
2008-08-27 19:03:00 +05:30
|
|
|
|
2009-04-18 10:41:22 +05:30
|
|
|
@mixin("cacheable", "property_adder")
|
2009-03-31 09:09:23 +05:30
|
|
|
class Chart(LastfmBase):
|
2009-03-28 12:54:24 +05:30
|
|
|
"""The base class for all the chart classes"""
|
2009-04-18 10:41:22 +05:30
|
|
|
class Meta(object):
|
|
|
|
properties = ["subject", "start", "end", "stats"]
|
2008-08-27 19:03:00 +05:30
|
|
|
|
2009-03-27 11:24:12 +05:30
|
|
|
def init(self, subject, start, end, stats = None):
|
2008-12-30 19:21:04 +05:30
|
|
|
self._subject = subject
|
|
|
|
self._start = start
|
|
|
|
self._end = end
|
|
|
|
self._stats = stats
|
2008-08-27 19:03:00 +05:30
|
|
|
|
|
|
|
@staticmethod
|
2009-03-28 12:54:24 +05:30
|
|
|
def _check_chart_params(params, subject, start = None, end = None):
|
2009-03-17 20:51:40 +05:30
|
|
|
if xor(start is None, end is None):
|
2008-12-30 15:57:13 +05:30
|
|
|
raise InvalidParametersError("both start and end have to be provided.")
|
2008-08-27 22:33:40 +05:30
|
|
|
if start is not None and end is not None:
|
2009-03-28 12:54:24 +05:30
|
|
|
if not (isinstance(start, datetime) and isinstance(end, datetime)):
|
2008-12-30 15:57:13 +05:30
|
|
|
raise InvalidParametersError("start and end must be datetime.datetime instances")
|
2009-03-28 12:54:24 +05:30
|
|
|
params.update({
|
|
|
|
'from': int(calendar.timegm(start.timetuple())),
|
|
|
|
'to': int(calendar.timegm(end.timetuple()))
|
|
|
|
})
|
2008-08-27 22:33:40 +05:30
|
|
|
return params
|
|
|
|
|
2008-08-27 19:03:00 +05:30
|
|
|
@staticmethod
|
2008-12-30 19:21:04 +05:30
|
|
|
def _hash_func(*args, **kwds):
|
2008-08-27 19:03:00 +05:30
|
|
|
try:
|
2008-08-28 19:55:59 +05:30
|
|
|
return hash("%s%s%s%s" % (
|
|
|
|
kwds['subject'].__class__.__name__,
|
|
|
|
kwds['subject'].name,
|
|
|
|
kwds['start'],
|
|
|
|
kwds['end']
|
|
|
|
))
|
2008-08-27 19:03:00 +05:30
|
|
|
except KeyError:
|
2008-12-30 15:57:13 +05:30
|
|
|
raise InvalidParametersError("subject, start and end have to be provided for hashing")
|
2008-08-27 19:03:00 +05:30
|
|
|
|
|
|
|
def __hash__(self):
|
2008-12-30 19:21:04 +05:30
|
|
|
return self.__class__._hash_func(
|
2009-03-28 12:54:24 +05:30
|
|
|
subject = self.subject,
|
|
|
|
start = self.start,
|
|
|
|
end = self.end
|
|
|
|
)
|
2008-08-27 19:03:00 +05:30
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
return self.subject == other.subject and \
|
|
|
|
self.start == other.start and \
|
|
|
|
self.end == other.end
|
|
|
|
|
|
|
|
def __lt__(self, other):
|
|
|
|
if self.subject == other.subject:
|
|
|
|
if self.start == other.start:
|
|
|
|
return self.end < other.end
|
|
|
|
else:
|
|
|
|
return self.start < other.start
|
|
|
|
else:
|
|
|
|
return self.subject < other.subject
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "<lastfm.%s: for %s:%s from %s to %s>" % \
|
|
|
|
(
|
|
|
|
self.__class__.__name__,
|
|
|
|
self.subject.__class__.__name__,
|
|
|
|
self.subject.name,
|
|
|
|
self.start.strftime("%x"),
|
|
|
|
self.end.strftime("%x"),
|
|
|
|
)
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-04-18 10:41:22 +05:30
|
|
|
@mixin("property_adder")
|
2009-03-28 12:54:24 +05:30
|
|
|
class AlbumChart(Chart):
|
2009-04-18 10:41:22 +05:30
|
|
|
class Meta(object):
|
|
|
|
properties = ["albums"]
|
|
|
|
|
2008-08-28 19:55:59 +05:30
|
|
|
def init(self, subject, start, end, stats, albums):
|
2009-03-28 12:54:24 +05:30
|
|
|
super(AlbumChart, self).init(subject, start, end, stats)
|
2008-12-30 19:21:04 +05:30
|
|
|
self._albums = albums
|
2008-08-27 19:03:00 +05:30
|
|
|
|
2009-04-18 10:41:22 +05:30
|
|
|
@mixin("property_adder")
|
2009-03-28 12:54:24 +05:30
|
|
|
class ArtistChart(Chart):
|
2009-04-18 10:41:22 +05:30
|
|
|
class Meta(object):
|
|
|
|
properties = ["artists"]
|
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
def init(self, subject, start, end, stats, artists):
|
|
|
|
super(ArtistChart, self).init(subject, start, end, stats)
|
|
|
|
self._artists = artists
|
|
|
|
|
2009-04-18 10:41:22 +05:30
|
|
|
@mixin("property_adder")
|
2009-03-28 12:54:24 +05:30
|
|
|
class TrackChart(Chart):
|
2009-04-18 10:41:22 +05:30
|
|
|
class Meta(object):
|
|
|
|
properties = ["tracks"]
|
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
def init(self, subject, start, end, tracks, stats):
|
|
|
|
super(TrackChart, self).init(subject, start, end, stats)
|
|
|
|
self._tracks = tracks
|
|
|
|
|
2009-04-18 10:41:22 +05:30
|
|
|
@mixin("property_adder")
|
2009-03-28 12:54:24 +05:30
|
|
|
class TagChart(Chart):
|
2009-04-18 10:41:22 +05:30
|
|
|
class Meta(object):
|
|
|
|
properties = ["tags"]
|
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
def init(self, subject, start, end, tags, stats):
|
|
|
|
super(TagChart, self).init(subject, start, end, stats)
|
|
|
|
self._tags = tags
|
2009-04-18 10:41:22 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class WeeklyChart(Chart):
|
|
|
|
"""A class for representing the weekly charts"""
|
|
|
|
@staticmethod
|
|
|
|
def create_from_data(api, subject, data):
|
|
|
|
return WeeklyChart(
|
|
|
|
subject = subject,
|
|
|
|
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
|
|
|
end = datetime.utcfromtimestamp(int(data.attrib['to']))
|
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _check_chart_params(params, subject, start = None, end = None):
|
|
|
|
params = Chart._check_chart_params(params, subject, start, end)
|
|
|
|
if start is not None and end is not None:
|
|
|
|
wcl = subject.weekly_chart_list
|
|
|
|
is_valid = False
|
|
|
|
for wc in wcl:
|
|
|
|
if wc.start == start and wc.end == end:
|
|
|
|
is_valid = True
|
|
|
|
if not is_valid:
|
|
|
|
raise InvalidParametersError("%s - %s chart dates are invalid" % (start, end))
|
|
|
|
return params
|
|
|
|
|
|
|
|
class WeeklyAlbumChart(AlbumChart, WeeklyChart):
|
|
|
|
"""A class for representing the weekly album charts"""
|
2008-08-27 19:03:00 +05:30
|
|
|
@staticmethod
|
2008-12-30 15:57:13 +05:30
|
|
|
def create_from_data(api, subject, data):
|
2008-08-28 19:55:59 +05:30
|
|
|
w = WeeklyChart(
|
|
|
|
subject = subject,
|
|
|
|
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
|
|
|
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
|
|
|
)
|
2008-08-27 19:03:00 +05:30
|
|
|
return WeeklyAlbumChart(
|
2009-03-21 12:23:24 +05:30
|
|
|
subject = subject,
|
|
|
|
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
|
|
|
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
|
|
|
stats = Stats(
|
|
|
|
subject = subject,
|
|
|
|
playcount = reduce(
|
|
|
|
lambda x,y:(
|
|
|
|
x + int(y.findtext('playcount'))
|
|
|
|
),
|
|
|
|
data.findall('album'),
|
|
|
|
0
|
|
|
|
)
|
|
|
|
),
|
|
|
|
albums = [
|
|
|
|
Album(
|
|
|
|
api,
|
|
|
|
subject = w,
|
|
|
|
name = a.findtext('name'),
|
|
|
|
mbid = a.findtext('mbid'),
|
|
|
|
artist = Artist(
|
|
|
|
api,
|
|
|
|
subject = w,
|
|
|
|
name = a.findtext('artist'),
|
|
|
|
mbid = a.find('artist').attrib['mbid'],
|
|
|
|
),
|
|
|
|
stats = Stats(
|
|
|
|
subject = a.findtext('name'),
|
|
|
|
rank = int(a.attrib['rank']),
|
|
|
|
playcount = int(a.findtext('playcount')),
|
|
|
|
),
|
|
|
|
url = a.findtext('url'),
|
|
|
|
)
|
|
|
|
for a in data.findall('album')
|
|
|
|
]
|
|
|
|
)
|
2008-08-27 19:03:00 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class WeeklyArtistChart(ArtistChart, WeeklyChart):
|
2008-08-27 19:03:00 +05:30
|
|
|
"""A class for representing the weekly artist charts"""
|
|
|
|
@staticmethod
|
2008-12-30 15:57:13 +05:30
|
|
|
def create_from_data(api, subject, data):
|
2008-08-28 19:55:59 +05:30
|
|
|
w = WeeklyChart(
|
|
|
|
subject = subject,
|
|
|
|
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
|
|
|
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
|
|
|
)
|
2009-01-01 01:39:06 +05:30
|
|
|
count_attribute = data.find('artist').findtext('playcount') and 'playcount' or 'weight'
|
|
|
|
def get_count_attribute(artist):
|
|
|
|
return {count_attribute: int(eval(artist.findtext(count_attribute)))}
|
|
|
|
def get_count_attribute_sum(artists):
|
2009-03-21 12:23:24 +05:30
|
|
|
return {count_attribute: reduce(
|
|
|
|
lambda x, y:(x + int(eval(y.findtext(count_attribute)))), artists, 0
|
|
|
|
)}
|
2009-01-01 01:39:06 +05:30
|
|
|
|
2008-08-27 19:03:00 +05:30
|
|
|
return WeeklyArtistChart(
|
2009-03-21 12:23:24 +05:30
|
|
|
subject = subject,
|
|
|
|
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
|
|
|
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
|
|
|
stats = Stats(
|
|
|
|
subject = subject,
|
|
|
|
**get_count_attribute_sum(data.findall('artist'))
|
|
|
|
),
|
|
|
|
artists = [
|
|
|
|
Artist(
|
|
|
|
api,
|
|
|
|
subject = w,
|
|
|
|
name = a.findtext('name'),
|
|
|
|
mbid = a.findtext('mbid'),
|
|
|
|
stats = Stats(
|
|
|
|
subject = a.findtext('name'),
|
|
|
|
rank = int(a.attrib['rank']),
|
|
|
|
**get_count_attribute(a)
|
|
|
|
),
|
|
|
|
url = a.findtext('url'),
|
|
|
|
)
|
|
|
|
for a in data.findall('artist')
|
|
|
|
]
|
|
|
|
)
|
2008-08-27 19:03:00 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class WeeklyTrackChart(TrackChart, WeeklyChart):
|
2008-08-27 19:03:00 +05:30
|
|
|
"""A class for representing the weekly track charts"""
|
|
|
|
@staticmethod
|
2008-12-30 15:57:13 +05:30
|
|
|
def create_from_data(api, subject, data):
|
2008-08-28 19:55:59 +05:30
|
|
|
w = WeeklyChart(
|
2009-03-21 12:23:24 +05:30
|
|
|
subject = subject,
|
|
|
|
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
|
|
|
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
|
|
|
)
|
2008-08-27 19:03:00 +05:30
|
|
|
return WeeklyTrackChart(
|
2009-03-21 12:23:24 +05:30
|
|
|
subject = subject,
|
|
|
|
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
|
|
|
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
|
|
|
stats = Stats(
|
|
|
|
subject = subject,
|
|
|
|
playcount = reduce(
|
|
|
|
lambda x,y:(
|
|
|
|
x + int(y.findtext('playcount'))
|
|
|
|
),
|
|
|
|
data.findall('track'),
|
|
|
|
0
|
|
|
|
)
|
|
|
|
),
|
|
|
|
tracks = [
|
|
|
|
Track(
|
|
|
|
api,
|
|
|
|
subject = w,
|
|
|
|
name = t.findtext('name'),
|
|
|
|
mbid = t.findtext('mbid'),
|
|
|
|
artist = Artist(
|
|
|
|
api,
|
|
|
|
name = t.findtext('artist'),
|
|
|
|
mbid = t.find('artist').attrib['mbid'],
|
|
|
|
),
|
|
|
|
stats = Stats(
|
|
|
|
subject = t.findtext('name'),
|
|
|
|
rank = int(t.attrib['rank']),
|
|
|
|
playcount = int(t.findtext('playcount')),
|
|
|
|
),
|
|
|
|
url = t.findtext('url'),
|
|
|
|
)
|
|
|
|
for t in data.findall('track')
|
|
|
|
]
|
|
|
|
)
|
2009-01-02 19:02:46 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class WeeklyTagChart(TagChart, WeeklyChart):
|
2009-01-02 19:02:46 +05:30
|
|
|
"""A class for representing the weekly tag charts"""
|
|
|
|
@staticmethod
|
2009-01-02 19:08:24 +05:30
|
|
|
def create_from_data(api, subject, start, end):
|
2009-01-02 19:02:46 +05:30
|
|
|
w = WeeklyChart(
|
|
|
|
subject = subject,
|
|
|
|
start = start,
|
|
|
|
end = end,
|
|
|
|
)
|
|
|
|
max_tag_count = 3
|
|
|
|
global_top_tags = api.get_global_top_tags()
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
wac = subject.get_weekly_artist_chart(start, end)
|
|
|
|
all_tags = defaultdict(lambda:0)
|
|
|
|
tag_weights = defaultdict(lambda:0)
|
|
|
|
total_playcount = 0
|
|
|
|
artist_count = 0
|
|
|
|
for artist in wac.artists:
|
|
|
|
artist_count += 1
|
|
|
|
total_playcount += artist.stats.playcount
|
|
|
|
tag_count = 0
|
|
|
|
for tag in artist.top_tags:
|
|
|
|
if tag not in global_top_tags: continue
|
|
|
|
if tag_count >= max_tag_count: break
|
|
|
|
all_tags[tag] += 1
|
|
|
|
tag_count += 1
|
|
|
|
|
|
|
|
artist_pp = artist.stats.playcount/float(wac.stats.playcount)
|
|
|
|
cumulative_pp = total_playcount/float(wac.stats.playcount)
|
|
|
|
if (cumulative_pp > 0.75 or artist_pp < 0.01) and artist_count > 10:
|
|
|
|
break
|
|
|
|
|
|
|
|
for artist in wac.artists[:artist_count]:
|
|
|
|
artist_pp = artist.stats.playcount/float(wac.stats.playcount)
|
|
|
|
tf = 1/float(max_tag_count)
|
|
|
|
tag_count = 0
|
|
|
|
weighted_tfidfs = {}
|
|
|
|
for tag in artist.top_tags:
|
|
|
|
if tag not in global_top_tags: continue
|
|
|
|
if tag_count >= max_tag_count: break
|
|
|
|
|
|
|
|
df = all_tags[tag]/float(artist_count)
|
|
|
|
tfidf = tf/df
|
|
|
|
weighted_tfidf = float(max_tag_count - tag_count)*tfidf
|
|
|
|
weighted_tfidfs[tag.name] = weighted_tfidf
|
|
|
|
tag_count += 1
|
|
|
|
|
|
|
|
sum_weighted_tfidfs = sum(weighted_tfidfs.values())
|
|
|
|
for tag in weighted_tfidfs:
|
|
|
|
tag_weights[tag] += weighted_tfidfs[tag]/sum_weighted_tfidfs*artist_pp
|
|
|
|
|
|
|
|
artist_pp = artist.stats.playcount/float(wac.stats.playcount)
|
|
|
|
|
|
|
|
tag_weights_sum = sum(tag_weights.values())
|
|
|
|
tag_weights = tag_weights.items()
|
|
|
|
tag_weights.sort(key=lambda x:x[1], reverse=True)
|
|
|
|
for i in xrange(len(tag_weights)):
|
|
|
|
tag, weight = tag_weights[i]
|
|
|
|
tag_weights[i] = (tag, weight, i+1)
|
|
|
|
|
2009-01-03 14:54:29 +05:30
|
|
|
wtc = WeeklyTagChart(
|
2009-01-02 19:02:46 +05:30
|
|
|
subject = subject,
|
|
|
|
start = wac.start,
|
|
|
|
end = wac.end,
|
|
|
|
stats = Stats(
|
|
|
|
subject = subject,
|
|
|
|
playcount = 1000
|
|
|
|
),
|
|
|
|
tags = [
|
|
|
|
Tag(
|
|
|
|
api,
|
|
|
|
subject = w,
|
|
|
|
name = tag,
|
|
|
|
stats = Stats(
|
|
|
|
subject = tag,
|
|
|
|
rank = rank,
|
|
|
|
count = int(round(1000*weight/tag_weights_sum)),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
for (tag, weight, rank) in tag_weights
|
|
|
|
]
|
|
|
|
)
|
2009-01-03 14:54:29 +05:30
|
|
|
wtc._artist_spectrum_analyzed = 100*total_playcount/float(wac.stats.playcount)
|
|
|
|
return wtc
|
2009-03-27 11:24:12 +05:30
|
|
|
|
|
|
|
class RollingChart(Chart):
|
2009-03-28 12:54:24 +05:30
|
|
|
"""Base class for the rolling charts classes"""
|
|
|
|
@classmethod
|
|
|
|
def _check_chart_params(cls, params, subject, start = None, end = None):
|
|
|
|
duration = cls._period['duration']
|
|
|
|
params = Chart._check_chart_params(params, subject, start, end)
|
|
|
|
if start is not None and end is not None:
|
|
|
|
mcl = MonthlyChart.get_chart_list(subject)
|
|
|
|
is_valid = False
|
|
|
|
for i in xrange(len(mcl)-(duration-1)):
|
|
|
|
if mcl[i].start == start and mcl[i+(duration-1)].end == end:
|
|
|
|
is_valid = True
|
|
|
|
if not is_valid:
|
|
|
|
raise InvalidParametersError("%s - %s chart dates are invalid" % (start, end))
|
|
|
|
return params
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
@classmethod
|
|
|
|
def create_from_data(cls, subject, key_func,
|
|
|
|
start = None, end = None):
|
|
|
|
chart_type = cls.mro()[0]._chart_type
|
|
|
|
period = cls.mro()[3]._period
|
|
|
|
globals()["%slyChart" % period['name'].title().replace(' ','')]._check_chart_params({}, subject, start, end)
|
|
|
|
mcl = MonthlyChart.get_chart_list(subject)
|
|
|
|
if start is None and end is None:
|
|
|
|
start = mcl[-period['duration']].start
|
|
|
|
end = mcl[-1].end
|
|
|
|
wcl = subject.weekly_chart_list
|
|
|
|
period_wcl = [wc for wc in wcl
|
|
|
|
if start < wc.start < end or start < wc.end < end]
|
|
|
|
period_wacl = []
|
|
|
|
for wc in period_wcl:
|
|
|
|
try:
|
|
|
|
period_wacl.append(
|
|
|
|
getattr(subject, "get_weekly_%s_chart" % chart_type)(wc.start, wc.end))
|
2009-04-12 10:37:41 +05:30
|
|
|
except LastfmError as ex:
|
|
|
|
logging.log_silenced_exceptions(ex)
|
2009-03-28 12:54:24 +05:30
|
|
|
stats_dict = period_wacl[0].__dict__["_%ss" % chart_type][0].stats.__dict__
|
|
|
|
count_attribute = [k for k in stats_dict.keys()
|
|
|
|
if stats_dict[k] is not None and k not in ['_rank', '_subject']][0]
|
|
|
|
items = {}
|
|
|
|
for wac in period_wacl:
|
|
|
|
for item in wac.__dict__["_%ss" % chart_type]:
|
|
|
|
key = key_func(item)
|
|
|
|
mw_start = max(wac.start, start)
|
|
|
|
mw_end = min(wac.end, end)
|
|
|
|
count = item.stats.__dict__[count_attribute] * (mw_end - mw_start).days / 7.0
|
|
|
|
if key in items:
|
|
|
|
items[key].stats.__dict__[count_attribute] += count
|
|
|
|
else:
|
|
|
|
items[key] = item
|
|
|
|
items[key].stats.__dict__[count_attribute] = count
|
|
|
|
items = items.values()
|
|
|
|
items = [a for a in items if a.stats.__dict__[count_attribute] >= 1]
|
|
|
|
items.sort(key = lambda a: a.stats.__dict__[count_attribute], reverse=True)
|
|
|
|
for i,item in enumerate(items):
|
|
|
|
item.stats._rank = i + 1
|
|
|
|
item.stats.__dict__[count_attribute] = int(item.stats.__dict__[count_attribute])
|
|
|
|
return globals()[
|
|
|
|
"%sly%sChart" % (
|
|
|
|
period['name'].title().replace(' ',''),
|
|
|
|
chart_type.capitalize()
|
|
|
|
)](
|
|
|
|
subject = subject,
|
|
|
|
start = start,
|
|
|
|
end = end,
|
|
|
|
stats = Stats(
|
|
|
|
subject = subject,
|
2009-03-31 09:09:23 +05:30
|
|
|
**{count_attribute[1:]: sum(a.stats.__dict__[count_attribute] for a in items)}
|
2009-03-28 12:54:24 +05:30
|
|
|
),
|
|
|
|
**{"%ss" % chart_type: items}
|
|
|
|
)
|
|
|
|
|
|
|
|
class RollingAlbumChart(AlbumChart):
|
|
|
|
@classmethod
|
|
|
|
def create_from_data(cls, subject, start = None, end = None):
|
|
|
|
key_func = lambda album: "::".join((album.name, album.artist.name))
|
|
|
|
return super(cls.mro()[3], cls).create_from_data(
|
|
|
|
subject, key_func, start, end)
|
|
|
|
|
|
|
|
class RollingArtistChart(ArtistChart):
|
|
|
|
@classmethod
|
|
|
|
def create_from_data(cls, subject, start = None, end = None):
|
|
|
|
key_func = lambda artist: artist.name
|
|
|
|
return super(cls.mro()[3], cls).create_from_data(
|
|
|
|
subject, key_func, start, end)
|
|
|
|
|
|
|
|
class RollingTrackChart(TrackChart):
|
|
|
|
@classmethod
|
|
|
|
def create_from_data(cls, subject, start = None, end = None):
|
|
|
|
key_func = lambda track: "::".join((track.name, track.artist.name))
|
|
|
|
return super(cls.mro()[3], cls).create_from_data(
|
|
|
|
subject, key_func, start, end)
|
|
|
|
|
|
|
|
class RollingTagChart(TagChart):
|
|
|
|
@classmethod
|
|
|
|
def create_from_data(cls, subject, start = None, end = None):
|
|
|
|
key_func = lambda tag: tag.name
|
|
|
|
chart = super(cls.mro()[3], cls).create_from_data(
|
|
|
|
subject, key_func, start, end)
|
2009-03-31 09:09:23 +05:30
|
|
|
count_sum = sum(t.stats.count for t in chart.tags)
|
2009-03-28 12:54:24 +05:30
|
|
|
for t in chart.tags:
|
|
|
|
t.stats.__dict__['_count'] /= count_sum
|
|
|
|
return chart
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class MonthlyChart(RollingChart):
|
|
|
|
"""A class for representing the monthly charts"""
|
|
|
|
_period = {'name': 'month', 'duration': 1}
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_chart_list(subject):
|
|
|
|
wcl = subject.weekly_chart_list
|
|
|
|
months = set()
|
|
|
|
for l in wcl:
|
|
|
|
months.add(l.start.replace(day=1, hour=12, minute=0, second=0))
|
|
|
|
months = list(months)
|
|
|
|
months.sort()
|
|
|
|
months[0] = wcl[0].start.replace(hour=12, minute=0, second=0)
|
|
|
|
months.append(wcl[-1].end.replace(hour=12, minute=0, second=0))
|
|
|
|
|
|
|
|
return [MonthlyChart(
|
|
|
|
subject=subject,
|
|
|
|
start=months[i],
|
|
|
|
end=months[i+1]
|
|
|
|
)
|
|
|
|
for i in xrange(len(months)-1)]
|
|
|
|
|
|
|
|
class MonthlyAlbumChart(RollingAlbumChart, MonthlyChart):
|
|
|
|
"""A class for representing the monthly album charts"""
|
|
|
|
_chart_type = "album"
|
|
|
|
|
|
|
|
class MonthlyArtistChart(RollingArtistChart, MonthlyChart):
|
|
|
|
"""A class for representing the monthly artist charts"""
|
|
|
|
_chart_type = "artist"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class MonthlyTrackChart(RollingTrackChart, MonthlyChart):
|
|
|
|
"""A class for representing the monthly track charts"""
|
|
|
|
_chart_type = "track"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class MonthlyTagChart(RollingTagChart, MonthlyChart):
|
|
|
|
"""A class for representing the monthly tag charts"""
|
|
|
|
_chart_type = "tag"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class QuaterlyChart(RollingChart):
|
|
|
|
"""A class for representing the three monthly charts"""
|
|
|
|
_period = {'name': 'quater', 'duration': 3}
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class QuaterlyAlbumChart(RollingAlbumChart, QuaterlyChart):
|
|
|
|
"""A class for representing the three monthly album charts"""
|
|
|
|
_chart_type = "album"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class QuaterlyArtistChart(RollingArtistChart, QuaterlyChart):
|
|
|
|
"""A class for representing the three monthly artist charts"""
|
|
|
|
_chart_type = "artist"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class QuaterlyTrackChart(RollingTrackChart, QuaterlyChart):
|
|
|
|
"""A class for representing the three monthly track charts"""
|
|
|
|
_chart_type = "track"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class QuaterlyTagChart(RollingTagChart, QuaterlyChart):
|
|
|
|
"""A class for representing the three monthly tag charts"""
|
|
|
|
_chart_type = "tag"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class HalfYearlyChart(RollingChart):
|
|
|
|
"""A class for representing the six monthly charts"""
|
|
|
|
_period = {'name': 'half year', 'duration': 6}
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class HalfYearlyAlbumChart(RollingAlbumChart, HalfYearlyChart):
|
|
|
|
"""A class for representing the six monthly album charts"""
|
|
|
|
_chart_type = "album"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class HalfYearlyArtistChart(RollingArtistChart, HalfYearlyChart):
|
|
|
|
"""A class for representing the six monthly artist charts"""
|
|
|
|
_chart_type = "artist"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class HalfYearlyTrackChart(RollingTrackChart, HalfYearlyChart):
|
|
|
|
"""A class for representing the six monthly track charts"""
|
|
|
|
_chart_type = "track"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class HalfYearlyTagChart(RollingTagChart, HalfYearlyChart):
|
|
|
|
"""A class for representing the six monthly tag charts"""
|
|
|
|
_chart_type = "tag"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
|
|
|
class YearlyChart(RollingChart):
|
2009-03-28 12:54:24 +05:30
|
|
|
"""A class for representing the yearly charts"""
|
|
|
|
_period = {'name': 'year', 'duration': 12}
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class YearlyAlbumChart(RollingAlbumChart, YearlyChart):
|
|
|
|
"""A class for representing the yearly album charts"""
|
|
|
|
_chart_type = "album"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class YearlyArtistChart(RollingArtistChart, YearlyChart):
|
|
|
|
"""A class for representing the yearly artist charts"""
|
|
|
|
_chart_type = "artist"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class YearlyTrackChart(RollingTrackChart, YearlyChart):
|
|
|
|
"""A class for representing the yearly track charts"""
|
|
|
|
_chart_type = "track"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
2009-03-28 12:54:24 +05:30
|
|
|
class YearlyTagChart(RollingTagChart, YearlyChart):
|
|
|
|
"""A class for representing the yearly tag charts"""
|
|
|
|
_chart_type = "tag"
|
2009-03-27 11:24:12 +05:30
|
|
|
|
|
|
|
__all__ = [
|
|
|
|
'WeeklyChart',
|
|
|
|
'WeeklyAlbumChart', 'WeeklyArtistChart', 'WeeklyTrackChart', 'WeeklyTagChart',
|
|
|
|
'MonthlyChart',
|
|
|
|
'MonthlyAlbumChart', 'MonthlyArtistChart', 'MonthlyTrackChart', 'MonthlyTagChart',
|
2009-03-28 12:54:24 +05:30
|
|
|
'QuaterlyChart',
|
|
|
|
'QuaterlyAlbumChart', 'QuaterlyArtistChart', 'QuaterlyTrackChart', 'QuaterlyTagChart',
|
|
|
|
'HalfYearlyChart',
|
|
|
|
'HalfYearlyAlbumChart', 'HalfYearlyArtistChart', 'HalfYearlyTrackChart', 'HalfYearlyTagChart',
|
2009-03-27 11:24:12 +05:30
|
|
|
'YearlyChart',
|
|
|
|
'YearlyAlbumChart', 'YearlyArtistChart', 'YearlyTrackChart', 'YearlyTagChart'
|
|
|
|
]
|
2008-08-27 19:03:00 +05:30
|
|
|
from datetime import datetime
|
2008-08-27 22:33:40 +05:30
|
|
|
import calendar
|
2008-08-27 19:03:00 +05:30
|
|
|
|
2008-12-31 11:00:23 +05:30
|
|
|
from lastfm.album import Album
|
|
|
|
from lastfm.artist import Artist
|
2009-03-28 12:54:24 +05:30
|
|
|
from lastfm.error import InvalidParametersError, LastfmError
|
2008-12-31 11:00:23 +05:30
|
|
|
from lastfm.stats import Stats
|
2009-01-02 19:02:46 +05:30
|
|
|
from lastfm.track import Track
|
|
|
|
from lastfm.tag import Tag
|