From 0c42531218424da2f559c7720bc79186f5195aca Mon Sep 17 00:00:00 2001 From: Krateng Date: Sat, 2 Mar 2019 22:55:22 +0100 Subject: [PATCH] Implemented proper range descriptions --- database.py | 150 +++++++------ htmlgenerators.py | 104 ++++----- htmlmodules.py | 14 +- malojatime.py | 477 ++++++++++++++++++++++++++++------------ website/artist.py | 2 +- website/pulse.html | 4 +- website/pulse.py | 11 +- website/scrobbles.py | 4 +- website/start.py | 2 +- website/topartists.html | 4 +- website/topartists.py | 7 +- website/toptracks.py | 5 +- 12 files changed, 505 insertions(+), 279 deletions(-) diff --git a/database.py b/database.py index 1738ab1..062ad7d 100644 --- a/database.py +++ b/database.py @@ -6,6 +6,7 @@ import os import datetime from cleanup import * from utilities import * +from malojatime import * import sys dbserver = Bottle() @@ -67,6 +68,7 @@ def createScrobble(artists,title,time): i = getTrackID(artists,title) obj = (i,time,False) SCROBBLES.append(obj) + register_scrobbletime(time) def readScrobble(artists,title,time): @@ -76,6 +78,7 @@ def readScrobble(artists,title,time): i = getTrackID(artists,title) obj = (i,time,True) SCROBBLES.append(obj) + register_scrobbletime(time) def getArtistID(name): @@ -314,7 +317,7 @@ def get_pulse_external(): def get_pulse(step="month",stepn=1,trail=1,**keys): - (ts_start,ts_end) = getTimestamps(**{k:keys[k] for k in keys if k in ["since","to","within"]}) + (ts_start,ts_end) = time_stamps(**{k:keys[k] for k in keys if k in ["since","to","within"]}) d_start = getStartOf(ts_start,step) d_end = getStartOf(ts_end,step) d_start = getNext(d_start,step,stepn) # first range should end right after the first active scrobbling week / month / whatever relevant step @@ -330,7 +333,7 @@ def get_pulse(step="month",stepn=1,trail=1,**keys): res = len(db_query(since=d_current,to=d_current_end,**{k:keys[k] for k in keys if k in ["artists","artist","track","title","associated"]})) results.append({"from":d_current,"to":d_current_end,"scrobbles":res}) d_current = getNext(d_current,step,stepn) - if isPast(d_current_end,d_end): + if isPast(d_current,d_end): break return results @@ -358,7 +361,7 @@ def get_top_artists_external(): def get_top_artists(step="month",stepn=1,trail=3,**keys): - (ts_start,ts_end) = getTimestamps(**{k:keys[k] for k in keys if k in ["since","to","within"]}) + (ts_start,ts_end) = time_stamps(**{k:keys[k] for k in keys if k in ["since","to","within"]}) d_start = getStartOf(ts_start,step) d_end = getStartOf(ts_end,step) d_start = getNext(d_start,step,stepn) # first range should end right after the first active scrobbling week / month / whatever relevant step @@ -375,7 +378,7 @@ def get_top_artists(step="month",stepn=1,trail=3,**keys): except: results.append({"from":d_current,"to":d_current_end,"artist":None,"scrobbles":0}) d_current = getNext(d_current,step,stepn) - if isPast(d_current_end,d_end): + if isPast(d_current,d_end): break return results @@ -404,7 +407,7 @@ def get_top_tracks_external(): def get_top_tracks(step="month",stepn=1,trail=3,**keys): - (ts_start,ts_end) = getTimestamps(**{k:keys[k] for k in keys if k in ["since","to","within"]}) + (ts_start,ts_end) = time_stamps(**{k:keys[k] for k in keys if k in ["since","to","within"]}) d_start = getStartOf(ts_start,step) d_end = getStartOf(ts_end,step) d_start = getNext(d_start,step,stepn) # first range should end right after the first active scrobbling week / month / whatever relevant step @@ -422,7 +425,7 @@ def get_top_tracks(step="month",stepn=1,trail=3,**keys): except: results.append({"from":d_current,"to":d_current_end,"track":None,"scrobbles":0}) d_current = getNext(d_current,step,stepn) - if isPast(d_current_end,d_end): + if isPast(d_current,d_end): break return results @@ -854,7 +857,7 @@ def db_query(artist=None,artists=None,title=None,track=None,since=None,to=None,w # print(within) # print(associated) - (since, to) = getTimestamps(since,to,within) + (since, to) = time_stamps(since,to,within) # this is not meant as a search function. we *can* query the db with a string, but it only works if it matches exactly # if a title is specified, we assume that a specific track (with the exact artist combination) is requested @@ -898,7 +901,7 @@ def db_query(artist=None,artists=None,title=None,track=None,since=None,to=None,w # Queries that... well... aggregate def db_aggregate(by=None,since=None,to=None,within=None,artist=None): - (since, to) = getTimestamps(since,to,within) + (since, to) = time_stamps(since,to,within) if isinstance(artist, str): artist = ARTISTS.index(artist) @@ -959,71 +962,72 @@ def db_search(query,type=None): # Takes user inputs like YYYY/MM and returns the timestamps. Returns timestamp if timestamp was already given. # to dates are interpreted differently (from 2010 and to 2010 both include all of 2010) -def getTimestamps(since=None,to=None,within=None): - - f,t,i = since,to,within - - if i is not None: - f = i - t = i - - if isinstance(f, str) and f.lower() == "today": - tod = datetime.datetime.utcnow() - f = [tod.year,tod.month,tod.day] - if isinstance(t, str) and t.lower() == "today": - tod = datetime.datetime.utcnow() - t = [tod.year,tod.month,tod.day] - - - if isinstance(f, str) and f.lower() == "month": - tod = datetime.datetime.utcnow() - f = [tod.year,tod.month] - if isinstance(t, str) and t.lower() == "month": - tod = datetime.datetime.utcnow() - t = [tod.year,tod.month] - - - if isinstance(f, str) and f.lower() == "year": - tod = datetime.datetime.utcnow() - f = [tod.year] - if isinstance(t, str) and t.lower() == "year": - tod = datetime.datetime.utcnow() - t = [tod.year] - - - if isinstance(f, str): - f = [int(x) for x in f.split("/")] - - if isinstance(t, str): - t = [int(x) for x in t.split("/")] - - - # this step is done if either the input is a list or the first step was done (which creates a list) - if isinstance(f, list): - date = [1970,1,1] - date[:len(f)] = f - #while len(f) < 3: f.append(1) # padding month and day - f = date - #f = int(datetime.datetime(date[0],date[1],date[2],date[3],date[4],tzinfo=datetime.timezone.utc).timestamp()) - f = int(datetime.datetime(f[0],f[1],f[2],tzinfo=datetime.timezone.utc).timestamp()) - - if isinstance(t, list): - t = getNext(t) - #while len(t) < 3: t.append(1) - date = [1970,1,1] - date[:len(t)] = t - t = date - #t = int(datetime.datetime(date[0],date[1],date[2],date[3],date[4],tzinfo=datetime.timezone.utc).timestamp()) - t = int(datetime.datetime(t[0],t[1],t[2],tzinfo=datetime.timezone.utc).timestamp()) - - if (f==None): - f = min(timestamps) - if (t==None): - t = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).timestamp() - - return (f,t) - - +# NOW DONE IN THE MALOJATIME MODULE +#def getTimestamps(since=None,to=None,within=None): +# +# f,t,i = since,to,within +# +# if i is not None: +# f = i +# t = i +# +# if isinstance(f, str) and f.lower() == "today": +# tod = datetime.datetime.utcnow() +# f = [tod.year,tod.month,tod.day] +# if isinstance(t, str) and t.lower() == "today": +# tod = datetime.datetime.utcnow() +# t = [tod.year,tod.month,tod.day] +# +# +# if isinstance(f, str) and f.lower() == "month": +# tod = datetime.datetime.utcnow() +# f = [tod.year,tod.month] +# if isinstance(t, str) and t.lower() == "month": +# tod = datetime.datetime.utcnow() +# t = [tod.year,tod.month] +# +# +# if isinstance(f, str) and f.lower() == "year": +# tod = datetime.datetime.utcnow() +# f = [tod.year] +# if isinstance(t, str) and t.lower() == "year": +# tod = datetime.datetime.utcnow() +# t = [tod.year] +# +# +# if isinstance(f, str): +# f = [int(x) for x in f.split("/")] +# +# if isinstance(t, str): +# t = [int(x) for x in t.split("/")] +# +# +# # this step is done if either the input is a list or the first step was done (which creates a list) +# if isinstance(f, list): +# date = [1970,1,1] +# date[:len(f)] = f +# #while len(f) < 3: f.append(1) # padding month and day +# f = date +# #f = int(datetime.datetime(date[0],date[1],date[2],date[3],date[4],tzinfo=datetime.timezone.utc).timestamp()) +# f = int(datetime.datetime(f[0],f[1],f[2],tzinfo=datetime.timezone.utc).timestamp()) +# +# if isinstance(t, list): +# t = getNext(t) +# #while len(t) < 3: t.append(1) +# date = [1970,1,1] +# date[:len(t)] = t +# t = date +# #t = int(datetime.datetime(date[0],date[1],date[2],date[3],date[4],tzinfo=datetime.timezone.utc).timestamp()) +# t = int(datetime.datetime(t[0],t[1],t[2],tzinfo=datetime.timezone.utc).timestamp()) +# +# if (f==None): +# f = min(timestamps) +# if (t==None): +# t = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).timestamp() +# +# return (f,t) +# +# diff --git a/htmlgenerators.py b/htmlgenerators.py index 314957e..97d492c 100644 --- a/htmlgenerators.py +++ b/htmlgenerators.py @@ -61,52 +61,52 @@ def removeIdentical(*dicts): return new -def getTimeDesc(timestamp,short=False): - tim = datetime.datetime.utcfromtimestamp(timestamp) - if short: - now = datetime.datetime.now(tz=datetime.timezone.utc) - difference = int(now.timestamp() - timestamp) +#def getTimeDesc(timestamp,short=False): +# tim = datetime.datetime.utcfromtimestamp(timestamp) +# if short: +# now = datetime.datetime.now(tz=datetime.timezone.utc) +# difference = int(now.timestamp() - timestamp) +# +# if difference < 10: return "just now" +# if difference < 60: return str(difference) + " seconds ago" +# difference = int(difference/60) +# if difference < 60: return str(difference) + " minutes ago" if difference>1 else str(difference) + " minute ago" +# difference = int(difference/60) +# if difference < 24: return str(difference) + " hours ago" if difference>1 else str(difference) + " hour ago" +# difference = int(difference/24) +# if difference < 5: return tim.strftime("%A") +# if difference < 31: return str(difference) + " days ago" if difference>1 else str(difference) + " day ago" +# #if difference < 300 and tim.year == now.year: return tim.strftime("%B") +# #if difference < 300: return tim.strftime("%B %Y") +# +# return tim.strftime("%d. %B %Y") +# else: +# return tim.strftime("%d. %b %Y %I:%M %p") - if difference < 10: return "just now" - if difference < 60: return str(difference) + " seconds ago" - difference = int(difference/60) - if difference < 60: return str(difference) + " minutes ago" if difference>1 else str(difference) + " minute ago" - difference = int(difference/60) - if difference < 24: return str(difference) + " hours ago" if difference>1 else str(difference) + " hour ago" - difference = int(difference/24) - if difference < 5: return tim.strftime("%A") - if difference < 31: return str(difference) + " days ago" if difference>1 else str(difference) + " day ago" - #if difference < 300 and tim.year == now.year: return tim.strftime("%B") - #if difference < 300: return tim.strftime("%B %Y") - - return tim.strftime("%d. %B %Y") - else: - return tim.strftime("%d. %b %Y %I:%M %p") - -def getRangeDesc(timeA,timeB,inclusiveB=True): - # string to list - if isinstance(timeA,str): timeA = timeA.split("/") - if isinstance(timeB,str): timeB = timeB.split("/") - - # if lists, we have it potentially much easier: - if isinstance(timeA,list) and isinstance(timeB,list): - if timeA == timeB: - date = [1970,1,1] - date[:len(timeA)] = timeA - dto = datetime.datetime(date[0],date[1],date[2],tzinfo=datetime.timezone.utc) - if len(timeA) == 3: - return dto.strftime("%d. %b %Y") - if len(timeA) == 2: - return dto.strftime("%B %Y") - if len(timeA) == 1: - return dto.strftime("%Y") - - from database import getTimestamps - - (timeA, timeB) = getTimestamps(since=timeA, to=timeB) - - - return getTimeDesc(timeA) + " to " + getTimeDesc(timeB) +#def getRangeDesc(since=None,to=None,inclusiveB=True): +# # string to list +# if isinstance(timeA,str): timeA = timeA.split("/") +# if isinstance(timeB,str): timeB = timeB.split("/") +# +# # if lists, we have it potentially much easier: +# if isinstance(timeA,list) and isinstance(timeB,list): +# if timeA == timeB: +# date = [1970,1,1] +# date[:len(timeA)] = timeA +# dto = datetime.datetime(date[0],date[1],date[2],tzinfo=datetime.timezone.utc) +# if len(timeA) == 3: +# return dto.strftime("%d. %b %Y") +# if len(timeA) == 2: +# return dto.strftime("%B %Y") +# if len(timeA) == 1: +# return dto.strftime("%Y") +# +# +# +# (timeA, timeB) = getTimestamps(since=timeA, to=timeB) +# +# +# return getTimeDesc(timeA) + " to " + getTimeDesc(timeB) @@ -191,10 +191,10 @@ def pickKeys(d,*keys): return finald # removes all duplicate keys, except artists when a title is specified -def clean(d): - if isinstance(d,dict): - return - else: - for k in d: - if (k != "artist") or "title" not in d: - d[k] = d.pop(k) +#def clean(d): +# if isinstance(d,dict): +# return +# else: +# for k in d: +# if (k != "artist") or "title" not in d: +# d[k] = d.pop(k) diff --git a/htmlmodules.py b/htmlmodules.py index 0fc4517..e62a75a 100644 --- a/htmlmodules.py +++ b/htmlmodules.py @@ -1,6 +1,7 @@ from htmlgenerators import * import database from utilities import getArtistsInfo, getTracksInfo +from malojatime import * import urllib @@ -26,7 +27,7 @@ def module_scrobblelist(max_=None,pictures=False,shortTimeDesc=False,**kwargs): #scrobbleimages = [e.get("image") for e in getTracksInfo(scrobbleswithpictures)] #will still work with scrobble objects as they are a technically a subset of track objects scrobbleimages = ["/image?title=" + urllib.parse.quote(t["title"]) + "&" + "&".join(["artist=" + urllib.parse.quote(a) for a in t["artists"]]) for t in scrobbleswithpictures] - representative = scrobbles[0] if scrobbles is not [] else None + representative = scrobbles[0] if len(scrobbles) is not 0 else None # build list i = 0 @@ -34,7 +35,7 @@ def module_scrobblelist(max_=None,pictures=False,shortTimeDesc=False,**kwargs): for s in scrobbles: html += "" - html += "" + getTimeDesc(s["time"],short=shortTimeDesc) + "" + html += "" + time_desc(s["time"],short=shortTimeDesc) + "" if pictures: html += """
""" html += "" + artistLinks(s["artists"]) + "" @@ -58,6 +59,13 @@ def module_pulse(max_=None,**kwargs): ranges = database.get_pulse(**kwargs_time,**kwargs_filter) + if max_ is not None: ranges = ranges[:max_] + + # if time range not explicitly specified, only show from first appearance +# if "since" not in kwargs: +# while ranges[0]["scrobbles"] == 0: +# del ranges[0] + maxbar = max([t["scrobbles"] for t in ranges]) maxbar = max(maxbar,1) @@ -67,7 +75,7 @@ def module_pulse(max_=None,**kwargs): fromstr = "/".join([str(e) for e in t["from"]]) tostr = "/".join([str(e) for e in t["to"]]) html += "" - html += "" + getRangeDesc(t["from"],t["to"]) + "" + html += "" + range_desc(t["from"],t["to"],short=True) + "" html += "" + scrobblesLink({"since":fromstr,"to":tostr},amount=t["scrobbles"],**kwargs_filter) + "" html += "" + scrobblesLink({"since":fromstr,"to":tostr},percent=t["scrobbles"]*100/maxbar,**kwargs_filter) + "" html += "" diff --git a/malojatime.py b/malojatime.py index 8a0be29..55aba5a 100644 --- a/malojatime.py +++ b/malojatime.py @@ -1,7 +1,16 @@ import datetime from calendar import monthrange +from os.path import commonprefix +FIRST_SCROBBLE = int(datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).timestamp()) + +def register_scrobbletime(timestamp): + global FIRST_SCROBBLE + if timestamp < FIRST_SCROBBLE: + FIRST_SCROBBLE = int(timestamp) + + # This is meant to be a time object that is aware of its own precision # now I know what you're saying # "This is total overengineering, Jimmy!" @@ -10,161 +19,351 @@ from calendar import monthrange # and you are absolutely correct # but my name isn't Jimmy # so we're doing objects -class Time(): - - precision = 0 - # 0 unused, no information at all, embrace eternity - # 1 year - # 2 month - # 3 day - # 4 specified by exact timestamp - - def __init__(self,*time): - # time can be a int (timestamp), list or string (/-delimited list) - - if len(time) == 1: - time = time[0] #otherwise we will already have a tuple and we can deal with that - if isinstance(time,int) and time < 10000: time = [time] # if we have a low number, it's not a timestamp, but a year - - - if isinstance(time,str): - time = time.split("/") - if isinstance(time,list) or isinstance(time,tuple): - time = [int(x) for x in time][:3] - self.precision = len(time) - if len(time) > 0: self.YEAR = time[0] - if len(time) > 1: self.MONTH = time[1] - if len(time) > 2: self.DAY = time[2] - elif isinstance(time,int): - self.precision = 4 - self.TIMESTAMP = time - dt = datetime.datetime.utcfromtimestamp(time) - self.YEAR, self.MONTH, self.DAY = dt.year, dt.month, dt.day +#class Time(): +# +# precision = 0 +# # 0 unused, no information at all, embrace eternity +# # 1 year +# # 2 month +# # 3 day +# # 4 specified by exact timestamp +# +# def __init__(self,*time): +# # time can be a int (timestamp), list or string (/-delimited list) +# +# if len(time) == 1: +# time = time[0] #otherwise we will already have a tuple and we can deal with that +# if isinstance(time,int) and time < 10000: time = [time] # if we have a low number, it's not a timestamp, but a year +# +# +# if isinstance(time,str): +# time = time.split("/") +# if isinstance(time,list) or isinstance(time,tuple): +# time = [int(x) for x in time][:3] +# self.precision = len(time) +# if len(time) > 0: self.YEAR = time[0] +# if len(time) > 1: self.MONTH = time[1] +# if len(time) > 2: self.DAY = time[2] +# elif isinstance(time,int): +# self.precision = 4 +# self.TIMESTAMP = time +# dt = datetime.datetime.utcfromtimestamp(time) +# self.YEAR, self.MONTH, self.DAY = dt.year, dt.month, dt.day +# +# +# def _array(self): +# if self.precision == 4: +# timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) +# return [timeobject.year,timeobject.month,timeobject.day] +# if self.precision == 3: return [self.YEAR,self.MONTH,self.DAY] +# if self.precision == 2: return [self.YEAR,self.MONTH] +# if self.precision == 1: return [self.YEAR] +# +# +# def get(self): +# if self.precision == 4: return self.TIMESTAMP +# if self.precision == 3: return [self.YEAR,self.MONTH,self.DAY] +# if self.precision == 2: return [self.YEAR,self.MONTH] +# if self.precision == 1: return [self.YEAR] +# +# def getStartTimestamp(self): +# if self.precision == 4: return self.TIMESTAMP +# else: +# YEAR = self.YEAR if self.precision > 0 else 1970 +# MONTH = self.MONTH if self.precision > 1 else 1 +# DAY = self.DAY if self.precision > 2 else 1 +# return int(datetime.datetime(YEAR,MONTH,DAY,tzinfo=datetime.timezone.utc).timestamp()) +# +# def getEndTimestamp(self): +# if self.precision == 4: return self.TIMESTAMP +# else: return self.getNext().getStartTimestamp()-1 +# +# # get next time of the same precision, e.g. next month if month of this time was defined (even if it's 1 or 12) +# def getNext(self,obj=True): +# if self.precision == 4: result = self.TIMESTAMP + 1 +# else: result = _getNext(self._array()) +# +# if obj: return Time(result) +# else: return result +# +# +# def pad(self,precision=3): +# arrayA, arrayB = self._array(), self._array() +# if self.precision < min(2,precision): +# arrayA.append(1) +# arrayB.append(12) +# if self.precision+1 < min(3,precision): +# arrayA.append(1) +# arrayB.append(monthrange(*arrayB)[1]) +# +# return (arrayA,arrayB) +# +# def describe(self,short=False): +# if self.precision == 4: +# if short: +# now = datetime.datetime.now(tz=datetime.timezone.utc) +# difference = int(now.timestamp() - self.TIMESTAMP) +# +# if difference < 10: return "just now" +# if difference < 60: return str(difference) + " seconds ago" +# difference = int(difference/60) +# if difference < 60: return str(difference) + " minutes ago" if difference>1 else str(difference) + " minute ago" +# difference = int(difference/60) +# if difference < 24: return str(difference) + " hours ago" if difference>1 else str(difference) + " hour ago" +# difference = int(difference/24) +# timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) +# if difference < 5: return timeobject.strftime("%A") +# if difference < 31: return str(difference) + " days ago" if difference>1 else str(difference) + " day ago" +# #if difference < 300 and tim.year == now.year: return tim.strftime("%B") +# #if difference < 300: return tim.strftime("%B %Y") +# +# return timeobject.strftime("%d. %B %Y") +# else: +# timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) +# return tim.strftime("%d. %b %Y %I:%M %p") +# +# else: +# YEAR = self.YEAR if self.precision > 0 else 2022 +# MONTH = self.MONTH if self.precision > 1 else 5 #else numbers dont matter, just needed to create the datetime object +# DAY = self.DAY if self.precision > 2 else 4 +# timeobject = datetime.datetime(YEAR,MONTH,DAY,tzinfo=datetime.timezone.utc) +# if self.precision == 3: return timeobject.strftime("%d. %B %Y") +# if self.precision == 2: return timeobject.strftime("%B %Y") +# if self.precision == 1: return timeobject.strftime("%Y") +# if self.precision == 0: return "Embrace Eternity" - def _array(self): - if self.precision == 4: - timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) - return [timeobject.year,timeobject.month,timeobject.day] - if self.precision == 3: return [self.YEAR,self.MONTH,self.DAY] - if self.precision == 2: return [self.YEAR,self.MONTH] - if self.precision == 1: return [self.YEAR] +#def getRange(timeA,timeB): +# return (timeA.getStartTimestamp(),timeB.getEndTimestamp()) +# +#def getRangeDesc(timeA,timeB): +# aA, aB = timeA.get(), timeB.get() +# if len(aA) != len(aB): +# prec = max(len(aA),len(aB)) +# aA, aB = timeA.pad(prec)[0], timeB.pad(prec)[1] +# if aA == aB: +# return Time(aA).describe() +# if aA[:-1] == aB[:-1]: +# return " ".join(Time(aA).describe().split(" ")[:-1]) + " to " + Time(aB).describe() #what +# + + +# alright forget everything I've just told you +# so how bout this: +# we completely ignore times +# all singular times (only used for scrobbles) are only ever expressed in timestamps anyway and remain simple ints +# ranges specified in any kind of list are completely separated from them +# even if you specify the pulse +# holy feck this is so much better + + +# converts strings and stuff to lists +def time_fix(t): + + + if isinstance(t, str) and t.lower() == "today": + tod = datetime.datetime.utcnow() + t = [tod.year,tod.month,tod.day] + if isinstance(t, str) and t.lower() == "month": + tod = datetime.datetime.utcnow() + t = [tod.year,tod.month] + if isinstance(t, str) and t.lower() == "year": + tod = datetime.datetime.utcnow() + t = [tod.year] + + + if isinstance(t,str): t = t.split("/") + #if isinstance(t,tuple): t = list(t) + + t = [int(p) for p in t] + + return t[:3] + +# makes times the same precision level +def time_pad(f,t): + f,t = time_fix(f), time_fix(t) + while len(f) < len(t): + if len(f) == 1: f.append(1) + elif len(f) == 2: f.append(1) + while len(f) > len(t): + if len(t) == 1: t.append(12) + elif len(t) == 2: t.append(monthrange(*t)[1]) + + return (f,t) - def get(self): - if self.precision == 4: return self.TIMESTAMP - if self.precision == 3: return [self.YEAR,self.MONTH,self.DAY] - if self.precision == 2: return [self.YEAR,self.MONTH] - if self.precision == 1: return [self.YEAR] - - def getStartTimestamp(self): - if self.precision == 4: return self.TIMESTAMP +def time_desc(t,short=False): + if isinstance(t,int): + if short: + now = datetime.datetime.now(tz=datetime.timezone.utc) + difference = int(now.timestamp() - t) + + if difference < 10: return "just now" + if difference < 60: return str(difference) + " seconds ago" + difference = int(difference/60) + if difference < 60: return str(difference) + " minutes ago" if difference>1 else str(difference) + " minute ago" + difference = int(difference/60) + if difference < 24: return str(difference) + " hours ago" if difference>1 else str(difference) + " hour ago" + difference = int(difference/24) + timeobject = datetime.datetime.utcfromtimestamp(t) + if difference < 5: return timeobject.strftime("%A") + if difference < 31: return str(difference) + " days ago" if difference>1 else str(difference) + " day ago" + #if difference < 300 and tim.year == now.year: return tim.strftime("%B") + #if difference < 300: return tim.strftime("%B %Y") + + return timeobject.strftime("%d. %B %Y") else: - YEAR = self.YEAR if self.precision > 0 else 1970 - MONTH = self.MONTH if self.precision > 1 else 1 - DAY = self.DAY if self.precision > 2 else 1 - return int(datetime.datetime(YEAR,MONTH,DAY,tzinfo=datetime.timezone.utc).timestamp()) + timeobject = datetime.datetime.utcfromtimestamp(t) + return timeobject.strftime("%d. %b %Y %I:%M %p") + + else: + t = time_fix(t) + date = [1970,1,1] + date[:len(t)] = t + timeobject = datetime.datetime(date[0],date[1],date[2],tzinfo=datetime.timezone.utc) + if len(t) == 3: return timeobject.strftime("%d. %B %Y") + if len(t) == 2: return timeobject.strftime("%B %Y") + if len(t) == 1: return timeobject.strftime("%Y") - def getEndTimestamp(self): - if self.precision == 4: return self.TIMESTAMP - else: return self.getNext().getStartTimestamp()-1 - - # get next time of the same precision, e.g. next month if month of this time was defined (even if it's 1 or 12) - def getNext(self,obj=True): - if self.precision == 4: result = self.TIMESTAMP + 1 - else: result = _getNext(self._array()) +def range_desc(since=None,to=None,within=None,short=False): - if obj: return Time(result) - else: return result + if within is not None: + since = within + to = within + if since is None: + sincestr = "" + if to is None: + tostr = "" - - def pad(self,precision=3): - arrayA, arrayB = self._array(), self._array() - if self.precision < min(2,precision): - arrayA.append(1) - arrayB.append(12) - if self.precision+1 < min(3,precision): - arrayA.append(1) - arrayB.append(monthrange(*arrayB)[1]) - - return (arrayA,arrayB) - - def describe(self,short=False): - if self.precision == 4: - if short: - now = datetime.datetime.now(tz=datetime.timezone.utc) - difference = int(now.timestamp() - self.TIMESTAMP) - - if difference < 10: return "just now" - if difference < 60: return str(difference) + " seconds ago" - difference = int(difference/60) - if difference < 60: return str(difference) + " minutes ago" if difference>1 else str(difference) + " minute ago" - difference = int(difference/60) - if difference < 24: return str(difference) + " hours ago" if difference>1 else str(difference) + " hour ago" - difference = int(difference/24) - timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) - if difference < 5: return timeobject.strftime("%A") - if difference < 31: return str(difference) + " days ago" if difference>1 else str(difference) + " day ago" - #if difference < 300 and tim.year == now.year: return tim.strftime("%B") - #if difference < 300: return tim.strftime("%B %Y") - - return timeobject.strftime("%d. %B %Y") + if isinstance(since,int) and to is None: + sincestr = "since " + time_desc(since) + shortsincestr = sincestr + elif isinstance(to,int) and since is None: + tostr = "up until " + time_desc(to) + elif isinstance(since,int) and not isinstance(to,int): + sincestr = "from " + time_desc(since) + shortsincestr = time_desc(since) + tostr = "to the end of " + time_desc(to) + elif isinstance(to,int) and not isinstance(since,int): + sincestr = "from the start of " + time_desc(since) + shortsincestr = time_desc(since) + tostr = "to " + time_desc(to) + +# if isinstance(since,int) and isinstance(to,int): result = "from " + time_desc(since) + " to " + time_desc(to) +# elif isinstance(since,int): result = "from " + time_desc(since) + " to the end of " + time_desc(to) +# elif isinstance(to,int): result = "from the start of " + time_desc(since) + " to " + time_desc(to) + else: + if since is not None and to is not None: + since,to = time_pad(since,to) + if since == to: + if len(since) == 3: + sincestr = "on " + time_desc(since) + else: + sincestr = "in " + time_desc(since) + shortsincestr = time_desc(since) + tostr = "" else: - timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) - return tim.strftime("%d. %b %Y %I:%M %p") + fparts = time_desc(since).split(" ") + tparts = time_desc(to).split(" ") + fparts.reverse() + tparts.reverse() + + fparts = fparts[len(commonprefix([fparts,tparts])):] + + fparts.reverse() + tparts.reverse() + + sincestr = "from " + " ".join(fparts) + shortsincestr = " ".join(fparts) + tostr = "to " + " ".join(tparts) + else: - YEAR = self.YEAR if self.precision > 0 else 2022 - MONTH = self.MONTH if self.precision > 1 else 5 #else numbers dont matter, just needed to create the datetime object - DAY = self.DAY if self.precision > 2 else 4 - timeobject = datetime.datetime(YEAR,MONTH,DAY,tzinfo=datetime.timezone.utc) - if self.precision == 3: return timeobject.strftime("%d. %B %Y") - if self.precision == 2: return timeobject.strftime("%B %Y") - if self.precision == 1: return timeobject.strftime("%Y") - if self.precision == 0: return "Embrace Eternity" + if since is not None: + sincestr = "since " + time_desc(since) + shortsincestr = sincestr + if to is not None: + tostr = "up until " + time_desc(to) - -def getRange(timeA,timeB): - return (timeA.getStartTimestamp(),timeB.getEndTimestamp()) - -def getRangeDesc(timeA,timeB): - aA, aB = timeA.get(), timeB.get() - if len(aA) != len(aB): - prec = max(len(aA),len(aB)) - aA, aB = timeA.pad(prec)[0], timeB.pad(prec)[1] - if aA == aB: - return Time(aA).describe() - if aA[:-1] == aB[:-1]: - return " ".join(Time(aA).describe().split(" ")[:-1]) + " to " + Time(aB).describe() #what + if short: return shortsincestr + " " + tostr + else: return sincestr + " " + tostr -def _getNext(time,unit="auto",step=1): - result = time[:] - if unit == "auto": - # see how long the list is, increment by the last specified unit - unit = [None,"year","month","day"][len(time)] +def time_stamps(since=None,to=None,within=None): - if unit == "year": - result[0] += step - return result - elif unit == "month": - result[1] += step - while result[1] > 12: - result[1] -= 12 - result[0] += 1 - while result[1] < 1: - result[1] += 12 - result[0] -= 1 - return result - elif unit == "day": - dt = datetime.datetime(time[0],time[1],time[2]) - d = datetime.timedelta(days=step) - newdate = dt + d - return [newdate.year,newdate.month,newdate.day] - #eugh - elif unit == "week": - return getNext(time,"day",step * 7) + from database import getNext + + if within is not None: + since = within + to = within + + + + if (since==None): stamp1 = FIRST_SCROBBLE + else: + since = time_fix(since) + date = [1970,1,1] + date[:len(since)] = since + stamp1 = int(datetime.datetime(date[0],date[1],date[2],tzinfo=datetime.timezone.utc).timestamp()) + + if (to==None): stamp2 = int(datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).timestamp()) + else: + to = time_fix(to) + to = getNext(to) + date = [1970,1,1] + date[:len(to)] = to + stamp2 = int(datetime.datetime(date[0],date[1],date[2],tzinfo=datetime.timezone.utc).timestamp()) + + + return (stamp1,stamp2) + + + + +def delimit_desc(step,stepn,trail): + txt = "" + if stepn is not 1: txt += _num(stepn) + "-" + txt += {"year":"Yearly","month":"Monthly","day":"Daily"}[step.lower()] + #if trail is not 1: txt += " " + _num(trail) + "-Trailing" + if trail is not 1: txt += " Trailing" #we don't need all the info in the title + + return txt + + +def _num(i): + names = ["Zero","One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Eleven","Twelve"] + if i < len(names): return names[i] + else: return str(i) + + +#def _getNext(time,unit="auto",step=1): +# result = time[:] +# if unit == "auto": +# # see how long the list is, increment by the last specified unit +# unit = [None,"year","month","day"][len(time)] +# +# if unit == "year": +# result[0] += step +# return result +# elif unit == "month": +# result[1] += step +# while result[1] > 12: +# result[1] -= 12 +# result[0] += 1 +# while result[1] < 1: +# result[1] += 12 +# result[0] -= 1 +# return result +# elif unit == "day": +# dt = datetime.datetime(time[0],time[1],time[2]) +# d = datetime.timedelta(days=step) +# newdate = dt + d +# return [newdate.year,newdate.month,newdate.day] +# #eugh +# elif unit == "week": +# return getNext(time,"day",step * 7) +# diff --git a/website/artist.py b/website/artist.py index a7b8618..d48d325 100644 --- a/website/artist.py +++ b/website/artist.py @@ -4,7 +4,7 @@ import database def instructions(keys): from utilities import getArtistInfo - from htmlgenerators import clean, artistLink, artistLinks, KeySplit + from htmlgenerators import artistLink, artistLinks, KeySplit from htmlmodules import module_pulse, module_trackcharts filterkeys, _, _, _ = KeySplit(keys,forceArtist=True) diff --git a/website/pulse.html b/website/pulse.html index 71745bf..2ea5f54 100644 --- a/website/pulse.html +++ b/website/pulse.html @@ -3,7 +3,7 @@ - Maloja - Pulse + Maloja - KEY_PULSEDETAILS Pulse @@ -14,7 +14,7 @@
-

Pulse


+

KEY_PULSEDETAILS Pulse


KEY_LIMITS diff --git a/website/pulse.py b/website/pulse.py index e25d97e..8137175 100644 --- a/website/pulse.py +++ b/website/pulse.py @@ -4,8 +4,9 @@ import database def instructions(keys): from utilities import getArtistInfo, getTrackInfo - from htmlgenerators import getTimeDesc, artistLink, artistLinks, trackLink, scrobblesLink, keysToUrl, getRangeDesc, KeySplit - from htmlmodules import module_pulse + from htmlgenerators import artistLink, artistLinks, trackLink, scrobblesLink, keysToUrl, KeySplit + from htmlmodules import module_pulse + from malojatime import range_desc, delimit_desc filterkeys, timekeys, delimitkeys, _ = KeySplit(keys) @@ -26,6 +27,10 @@ def instructions(keys): moreartists = data["associated"] if moreartists != []: limitstring += " including " + artistLinks(moreartists) + "" + + limitstring += " " + range_desc(**timekeys) + + delimitstring = delimit_desc(**delimitkeys) # get image @@ -42,7 +47,7 @@ def instructions(keys): html_pulse = module_pulse(**filterkeys,**timekeys,**delimitkeys) - replace = {"KEY_PULSE_TABLE":html_pulse,"KEY_IMAGEURL":imgurl,"KEY_LIMITS":limitstring} + replace = {"KEY_PULSE_TABLE":html_pulse,"KEY_IMAGEURL":imgurl,"KEY_LIMITS":limitstring,"KEY_PULSEDETAILS":delimitstring} return (replace,pushresources) diff --git a/website/scrobbles.py b/website/scrobbles.py index 68cee8e..81b2003 100644 --- a/website/scrobbles.py +++ b/website/scrobbles.py @@ -5,7 +5,8 @@ import database def instructions(keys): from utilities import getArtistInfo, getTrackInfo from htmlgenerators import artistLink, artistLinks, trackLink, KeySplit - from htmlmodules import module_scrobblelist + from htmlmodules import module_scrobblelist + from malojatime import range_desc filterkeys, timekeys, _, amountkeys = KeySplit(keys) @@ -24,6 +25,7 @@ def instructions(keys): if moreartists != []: limitstring += " including " + artistLinks(moreartists) + "" + limitstring += " " + range_desc(**timekeys) html, amount, rep = module_scrobblelist(**filterkeys,**timekeys,**amountkeys) diff --git a/website/start.py b/website/start.py index 436b94c..b96e6a0 100644 --- a/website/start.py +++ b/website/start.py @@ -7,7 +7,7 @@ from htmlmodules import module_scrobblelist, module_pulse def instructions(keys): from utilities import getArtistsInfo, getTracksInfo - from htmlgenerators import artistLink, artistLinks, trackLink, scrobblesArtistLink, scrobblesLink, keysToUrl, pickKeys, clean, getTimeDesc, getRangeDesc + from htmlgenerators import artistLink, trackLink max_show = 14 posrange = ["#" + str(i) for i in range(1,max_show+1)] diff --git a/website/topartists.html b/website/topartists.html index 4205426..ed5d21e 100644 --- a/website/topartists.html +++ b/website/topartists.html @@ -3,7 +3,7 @@ - Maloja - Top Artists in KEY_RANGE + Maloja - Top Artists KEY_RANGE @@ -15,7 +15,7 @@

Top Artists


- in KEY_RANGE + KEY_RANGE diff --git a/website/topartists.py b/website/topartists.py index a7c3552..5907878 100644 --- a/website/topartists.py +++ b/website/topartists.py @@ -5,10 +5,15 @@ def instructions(keys): from utilities import getArtistInfo from htmlgenerators import KeySplit from htmlmodules import module_artistcharts + from malojatime import range_desc _, timekeys, _, amountkeys = KeySplit(keys) + limitstring = range_desc(**timekeys) + + + html_charts, rep = module_artistcharts(**amountkeys,**timekeys) if rep is not None: @@ -19,6 +24,6 @@ def instructions(keys): pushresources = [{"file":imgurl,"type":"image"}] if imgurl.startswith("/") else [] - replace = {"KEY_TOPARTIST_IMAGEURL":imgurl,"KEY_ARTISTLIST":html_charts} + replace = {"KEY_TOPARTIST_IMAGEURL":imgurl,"KEY_ARTISTLIST":html_charts,"KEY_RANGE":limitstring} return (replace,pushresources) diff --git a/website/toptracks.py b/website/toptracks.py index 8611fc5..36bc6a5 100644 --- a/website/toptracks.py +++ b/website/toptracks.py @@ -4,7 +4,8 @@ import urllib def instructions(keys): from utilities import getArtistInfo, getTrackInfo from htmlgenerators import artistLink, KeySplit - from htmlmodules import module_trackcharts + from htmlmodules import module_trackcharts + from malojatime import range_desc filterkeys, timekeys, _, amountkeys = KeySplit(keys) @@ -22,6 +23,8 @@ def instructions(keys): imgurl = getTrackInfo(rep["artists"],rep["title"]).get("image") else: imgurl = "" + + limitstring += " " + range_desc(**timekeys) pushresources = [{"file":imgurl,"type":"image"}] if imgurl.startswith("/") else []