Implemented proper range descriptions

This commit is contained in:
Krateng 2019-03-02 22:55:22 +01:00
parent 9d0fc8e073
commit 0c42531218
12 changed files with 505 additions and 279 deletions

View File

@ -6,6 +6,7 @@ import os
import datetime import datetime
from cleanup import * from cleanup import *
from utilities import * from utilities import *
from malojatime import *
import sys import sys
dbserver = Bottle() dbserver = Bottle()
@ -67,6 +68,7 @@ def createScrobble(artists,title,time):
i = getTrackID(artists,title) i = getTrackID(artists,title)
obj = (i,time,False) obj = (i,time,False)
SCROBBLES.append(obj) SCROBBLES.append(obj)
register_scrobbletime(time)
def readScrobble(artists,title,time): def readScrobble(artists,title,time):
@ -76,6 +78,7 @@ def readScrobble(artists,title,time):
i = getTrackID(artists,title) i = getTrackID(artists,title)
obj = (i,time,True) obj = (i,time,True)
SCROBBLES.append(obj) SCROBBLES.append(obj)
register_scrobbletime(time)
def getArtistID(name): def getArtistID(name):
@ -314,7 +317,7 @@ def get_pulse_external():
def get_pulse(step="month",stepn=1,trail=1,**keys): 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_start = getStartOf(ts_start,step)
d_end = getStartOf(ts_end,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 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"]})) 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}) results.append({"from":d_current,"to":d_current_end,"scrobbles":res})
d_current = getNext(d_current,step,stepn) d_current = getNext(d_current,step,stepn)
if isPast(d_current_end,d_end): if isPast(d_current,d_end):
break break
return results return results
@ -358,7 +361,7 @@ def get_top_artists_external():
def get_top_artists(step="month",stepn=1,trail=3,**keys): 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_start = getStartOf(ts_start,step)
d_end = getStartOf(ts_end,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 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: except:
results.append({"from":d_current,"to":d_current_end,"artist":None,"scrobbles":0}) results.append({"from":d_current,"to":d_current_end,"artist":None,"scrobbles":0})
d_current = getNext(d_current,step,stepn) d_current = getNext(d_current,step,stepn)
if isPast(d_current_end,d_end): if isPast(d_current,d_end):
break break
return results return results
@ -404,7 +407,7 @@ def get_top_tracks_external():
def get_top_tracks(step="month",stepn=1,trail=3,**keys): 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_start = getStartOf(ts_start,step)
d_end = getStartOf(ts_end,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 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: except:
results.append({"from":d_current,"to":d_current_end,"track":None,"scrobbles":0}) results.append({"from":d_current,"to":d_current_end,"track":None,"scrobbles":0})
d_current = getNext(d_current,step,stepn) d_current = getNext(d_current,step,stepn)
if isPast(d_current_end,d_end): if isPast(d_current,d_end):
break break
return results 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(within)
# print(associated) # 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 # 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 # 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 # Queries that... well... aggregate
def db_aggregate(by=None,since=None,to=None,within=None,artist=None): 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): if isinstance(artist, str):
artist = ARTISTS.index(artist) 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. # 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) # to dates are interpreted differently (from 2010 and to 2010 both include all of 2010)
def getTimestamps(since=None,to=None,within=None): # NOW DONE IN THE MALOJATIME MODULE
#def getTimestamps(since=None,to=None,within=None):
f,t,i = since,to,within #
# f,t,i = since,to,within
if i is not None: #
f = i # if i is not None:
t = i # f = i
# t = i
if isinstance(f, str) and f.lower() == "today": #
tod = datetime.datetime.utcnow() # if isinstance(f, str) and f.lower() == "today":
f = [tod.year,tod.month,tod.day] # tod = datetime.datetime.utcnow()
if isinstance(t, str) and t.lower() == "today": # f = [tod.year,tod.month,tod.day]
tod = datetime.datetime.utcnow() # if isinstance(t, str) and t.lower() == "today":
t = [tod.year,tod.month,tod.day] # tod = datetime.datetime.utcnow()
# t = [tod.year,tod.month,tod.day]
#
if isinstance(f, str) and f.lower() == "month": #
tod = datetime.datetime.utcnow() # if isinstance(f, str) and f.lower() == "month":
f = [tod.year,tod.month] # tod = datetime.datetime.utcnow()
if isinstance(t, str) and t.lower() == "month": # f = [tod.year,tod.month]
tod = datetime.datetime.utcnow() # if isinstance(t, str) and t.lower() == "month":
t = [tod.year,tod.month] # tod = datetime.datetime.utcnow()
# t = [tod.year,tod.month]
#
if isinstance(f, str) and f.lower() == "year": #
tod = datetime.datetime.utcnow() # if isinstance(f, str) and f.lower() == "year":
f = [tod.year] # tod = datetime.datetime.utcnow()
if isinstance(t, str) and t.lower() == "year": # f = [tod.year]
tod = datetime.datetime.utcnow() # if isinstance(t, str) and t.lower() == "year":
t = [tod.year] # tod = datetime.datetime.utcnow()
# t = [tod.year]
#
if isinstance(f, str): #
f = [int(x) for x in f.split("/")] # if isinstance(f, str):
# f = [int(x) for x in f.split("/")]
if isinstance(t, str): #
t = [int(x) for x in t.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): # # this step is done if either the input is a list or the first step was done (which creates a list)
date = [1970,1,1] # if isinstance(f, list):
date[:len(f)] = f # date = [1970,1,1]
#while len(f) < 3: f.append(1) # padding month and day # date[:len(f)] = f
f = date # #while len(f) < 3: f.append(1) # padding month and day
#f = int(datetime.datetime(date[0],date[1],date[2],date[3],date[4],tzinfo=datetime.timezone.utc).timestamp()) # f = date
f = int(datetime.datetime(f[0],f[1],f[2],tzinfo=datetime.timezone.utc).timestamp()) # #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) # if isinstance(t, list):
#while len(t) < 3: t.append(1) # t = getNext(t)
date = [1970,1,1] # #while len(t) < 3: t.append(1)
date[:len(t)] = t # date = [1970,1,1]
t = date # date[:len(t)] = t
#t = int(datetime.datetime(date[0],date[1],date[2],date[3],date[4],tzinfo=datetime.timezone.utc).timestamp()) # t = date
t = int(datetime.datetime(t[0],t[1],t[2],tzinfo=datetime.timezone.utc).timestamp()) # #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 (f==None):
if (t==None): # f = min(timestamps)
t = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).timestamp() # if (t==None):
# t = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).timestamp()
return (f,t) #
# return (f,t)
#
#

View File

@ -61,52 +61,52 @@ def removeIdentical(*dicts):
return new return new
def getTimeDesc(timestamp,short=False): #def getTimeDesc(timestamp,short=False):
tim = datetime.datetime.utcfromtimestamp(timestamp) # tim = datetime.datetime.utcfromtimestamp(timestamp)
if short: # if short:
now = datetime.datetime.now(tz=datetime.timezone.utc) # now = datetime.datetime.now(tz=datetime.timezone.utc)
difference = int(now.timestamp() - timestamp) # 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" #def getRangeDesc(since=None,to=None,inclusiveB=True):
if difference < 60: return str(difference) + " seconds ago" # # string to list
difference = int(difference/60) # if isinstance(timeA,str): timeA = timeA.split("/")
if difference < 60: return str(difference) + " minutes ago" if difference>1 else str(difference) + " minute ago" # if isinstance(timeB,str): timeB = timeB.split("/")
difference = int(difference/60) #
if difference < 24: return str(difference) + " hours ago" if difference>1 else str(difference) + " hour ago" # # if lists, we have it potentially much easier:
difference = int(difference/24) # if isinstance(timeA,list) and isinstance(timeB,list):
if difference < 5: return tim.strftime("%A") # if timeA == timeB:
if difference < 31: return str(difference) + " days ago" if difference>1 else str(difference) + " day ago" # date = [1970,1,1]
#if difference < 300 and tim.year == now.year: return tim.strftime("%B") # date[:len(timeA)] = timeA
#if difference < 300: return tim.strftime("%B %Y") # dto = datetime.datetime(date[0],date[1],date[2],tzinfo=datetime.timezone.utc)
# if len(timeA) == 3:
return tim.strftime("%d. %B %Y") # return dto.strftime("%d. %b %Y")
else: # if len(timeA) == 2:
return tim.strftime("%d. %b %Y %I:%M %p") # return dto.strftime("%B %Y")
# if len(timeA) == 1:
def getRangeDesc(timeA,timeB,inclusiveB=True): # return dto.strftime("%Y")
# string to list #
if isinstance(timeA,str): timeA = timeA.split("/") #
if isinstance(timeB,str): timeB = timeB.split("/") #
# (timeA, timeB) = getTimestamps(since=timeA, to=timeB)
# if lists, we have it potentially much easier: #
if isinstance(timeA,list) and isinstance(timeB,list): #
if timeA == timeB: # return getTimeDesc(timeA) + " to " + getTimeDesc(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)
@ -191,10 +191,10 @@ def pickKeys(d,*keys):
return finald return finald
# removes all duplicate keys, except artists when a title is specified # removes all duplicate keys, except artists when a title is specified
def clean(d): #def clean(d):
if isinstance(d,dict): # if isinstance(d,dict):
return # return
else: # else:
for k in d: # for k in d:
if (k != "artist") or "title" not in d: # if (k != "artist") or "title" not in d:
d[k] = d.pop(k) # d[k] = d.pop(k)

View File

@ -1,6 +1,7 @@
from htmlgenerators import * from htmlgenerators import *
import database import database
from utilities import getArtistsInfo, getTracksInfo from utilities import getArtistsInfo, getTracksInfo
from malojatime import *
import urllib 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 = [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] 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 # build list
i = 0 i = 0
@ -34,7 +35,7 @@ def module_scrobblelist(max_=None,pictures=False,shortTimeDesc=False,**kwargs):
for s in scrobbles: for s in scrobbles:
html += "<tr>" html += "<tr>"
html += "<td class='time'>" + getTimeDesc(s["time"],short=shortTimeDesc) + "</td>" html += "<td class='time'>" + time_desc(s["time"],short=shortTimeDesc) + "</td>"
if pictures: if pictures:
html += """<td class='icon'><div style="background-image:url('""" + scrobbleimages[i] + """')"></div></td>""" html += """<td class='icon'><div style="background-image:url('""" + scrobbleimages[i] + """')"></div></td>"""
html += "<td class='artists'>" + artistLinks(s["artists"]) + "</td>" html += "<td class='artists'>" + artistLinks(s["artists"]) + "</td>"
@ -58,6 +59,13 @@ def module_pulse(max_=None,**kwargs):
ranges = database.get_pulse(**kwargs_time,**kwargs_filter) 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([t["scrobbles"] for t in ranges])
maxbar = max(maxbar,1) maxbar = max(maxbar,1)
@ -67,7 +75,7 @@ def module_pulse(max_=None,**kwargs):
fromstr = "/".join([str(e) for e in t["from"]]) fromstr = "/".join([str(e) for e in t["from"]])
tostr = "/".join([str(e) for e in t["to"]]) tostr = "/".join([str(e) for e in t["to"]])
html += "<tr>" html += "<tr>"
html += "<td>" + getRangeDesc(t["from"],t["to"]) + "</td>" html += "<td>" + range_desc(t["from"],t["to"],short=True) + "</td>"
html += "<td class='amount'>" + scrobblesLink({"since":fromstr,"to":tostr},amount=t["scrobbles"],**kwargs_filter) + "</td>" html += "<td class='amount'>" + scrobblesLink({"since":fromstr,"to":tostr},amount=t["scrobbles"],**kwargs_filter) + "</td>"
html += "<td class='bar'>" + scrobblesLink({"since":fromstr,"to":tostr},percent=t["scrobbles"]*100/maxbar,**kwargs_filter) + "</td>" html += "<td class='bar'>" + scrobblesLink({"since":fromstr,"to":tostr},percent=t["scrobbles"]*100/maxbar,**kwargs_filter) + "</td>"
html += "</tr>" html += "</tr>"

View File

@ -1,5 +1,14 @@
import datetime import datetime
from calendar import monthrange 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 # This is meant to be a time object that is aware of its own precision
@ -10,90 +19,184 @@ from calendar import monthrange
# and you are absolutely correct # and you are absolutely correct
# but my name isn't Jimmy # but my name isn't Jimmy
# so we're doing objects # so we're doing objects
class Time(): #class Time():
#
precision = 0 # precision = 0
# 0 unused, no information at all, embrace eternity # # 0 unused, no information at all, embrace eternity
# 1 year # # 1 year
# 2 month # # 2 month
# 3 day # # 3 day
# 4 specified by exact timestamp # # 4 specified by exact timestamp
#
def __init__(self,*time): # def __init__(self,*time):
# time can be a int (timestamp), list or string (/-delimited list) # # time can be a int (timestamp), list or string (/-delimited list)
#
if len(time) == 1: # if len(time) == 1:
time = time[0] #otherwise we will already have a tuple and we can deal with that # 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,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"
if isinstance(time,str): #def getRange(timeA,timeB):
time = time.split("/") # return (timeA.getStartTimestamp(),timeB.getEndTimestamp())
if isinstance(time,list) or isinstance(time,tuple): #
time = [int(x) for x in time][:3] #def getRangeDesc(timeA,timeB):
self.precision = len(time) # aA, aB = timeA.get(), timeB.get()
if len(time) > 0: self.YEAR = time[0] # if len(aA) != len(aB):
if len(time) > 1: self.MONTH = time[1] # prec = max(len(aA),len(aB))
if len(time) > 2: self.DAY = time[2] # aA, aB = timeA.pad(prec)[0], timeB.pad(prec)[1]
elif isinstance(time,int): # if aA == aB:
self.precision = 4 # return Time(aA).describe()
self.TIMESTAMP = time # if aA[:-1] == aB[:-1]:
dt = datetime.datetime.utcfromtimestamp(time) # return " ".join(Time(aA).describe().split(" ")[:-1]) + " to " + Time(aB).describe() #what
self.YEAR, self.MONTH, self.DAY = dt.year, dt.month, dt.day #
def _array(self): # alright forget everything I've just told you
if self.precision == 4: # so how bout this:
timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) # we completely ignore times
return [timeobject.year,timeobject.month,timeobject.day] # all singular times (only used for scrobbles) are only ever expressed in timestamps anyway and remain simple ints
if self.precision == 3: return [self.YEAR,self.MONTH,self.DAY] # ranges specified in any kind of list are completely separated from them
if self.precision == 2: return [self.YEAR,self.MONTH] # even if you specify the pulse
if self.precision == 1: return [self.YEAR] # holy feck this is so much better
def get(self): # converts strings and stuff to lists
if self.precision == 4: return self.TIMESTAMP def time_fix(t):
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): if isinstance(t, str) and t.lower() == "today":
arrayA, arrayB = self._array(), self._array() tod = datetime.datetime.utcnow()
if self.precision < min(2,precision): t = [tod.year,tod.month,tod.day]
arrayA.append(1) if isinstance(t, str) and t.lower() == "month":
arrayB.append(12) tod = datetime.datetime.utcnow()
if self.precision+1 < min(3,precision): t = [tod.year,tod.month]
arrayA.append(1) if isinstance(t, str) and t.lower() == "year":
arrayB.append(monthrange(*arrayB)[1]) tod = datetime.datetime.utcnow()
t = [tod.year]
return (arrayA,arrayB)
def describe(self,short=False): if isinstance(t,str): t = t.split("/")
if self.precision == 4: #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 time_desc(t,short=False):
if isinstance(t,int):
if short: if short:
now = datetime.datetime.now(tz=datetime.timezone.utc) now = datetime.datetime.now(tz=datetime.timezone.utc)
difference = int(now.timestamp() - self.TIMESTAMP) difference = int(now.timestamp() - t)
if difference < 10: return "just now" if difference < 10: return "just now"
if difference < 60: return str(difference) + " seconds ago" if difference < 60: return str(difference) + " seconds ago"
@ -102,7 +205,7 @@ class Time():
difference = int(difference/60) difference = int(difference/60)
if difference < 24: return str(difference) + " hours ago" if difference>1 else str(difference) + " hour ago" if difference < 24: return str(difference) + " hours ago" if difference>1 else str(difference) + " hour ago"
difference = int(difference/24) difference = int(difference/24)
timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) timeobject = datetime.datetime.utcfromtimestamp(t)
if difference < 5: return timeobject.strftime("%A") 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 < 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 and tim.year == now.year: return tim.strftime("%B")
@ -110,61 +213,157 @@ class Time():
return timeobject.strftime("%d. %B %Y") return timeobject.strftime("%d. %B %Y")
else: else:
timeobject = datetime.datetime.utcfromtimestamp(self.TIMESTAMP) timeobject = datetime.datetime.utcfromtimestamp(t)
return tim.strftime("%d. %b %Y %I:%M %p") return timeobject.strftime("%d. %b %Y %I:%M %p")
else: else:
YEAR = self.YEAR if self.precision > 0 else 2022 t = time_fix(t)
MONTH = self.MONTH if self.precision > 1 else 5 #else numbers dont matter, just needed to create the datetime object date = [1970,1,1]
DAY = self.DAY if self.precision > 2 else 4 date[:len(t)] = t
timeobject = datetime.datetime(YEAR,MONTH,DAY,tzinfo=datetime.timezone.utc) timeobject = datetime.datetime(date[0],date[1],date[2],tzinfo=datetime.timezone.utc)
if self.precision == 3: return timeobject.strftime("%d. %B %Y") if len(t) == 3: return timeobject.strftime("%d. %B %Y")
if self.precision == 2: return timeobject.strftime("%B %Y") if len(t) == 2: return timeobject.strftime("%B %Y")
if self.precision == 1: return timeobject.strftime("%Y") if len(t) == 1: return timeobject.strftime("%Y")
if self.precision == 0: return "Embrace Eternity"
def range_desc(since=None,to=None,within=None,short=False):
def getRange(timeA,timeB): if within is not None:
return (timeA.getStartTimestamp(),timeB.getEndTimestamp()) since = within
to = within
if since is None:
sincestr = ""
if to is None:
tostr = ""
def getRangeDesc(timeA,timeB): if isinstance(since,int) and to is None:
aA, aB = timeA.get(), timeB.get() sincestr = "since " + time_desc(since)
if len(aA) != len(aB): shortsincestr = sincestr
prec = max(len(aA),len(aB)) elif isinstance(to,int) and since is None:
aA, aB = timeA.pad(prec)[0], timeB.pad(prec)[1] tostr = "up until " + time_desc(to)
if aA == aB: elif isinstance(since,int) and not isinstance(to,int):
return Time(aA).describe() sincestr = "from " + time_desc(since)
if aA[:-1] == aB[:-1]: shortsincestr = time_desc(since)
return " ".join(Time(aA).describe().split(" ")[:-1]) + " to " + Time(aB).describe() #what 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:
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:
if since is not None:
sincestr = "since " + time_desc(since)
shortsincestr = sincestr
if to is not None:
tostr = "up until " + time_desc(to)
if short: return shortsincestr + " " + tostr
else: return sincestr + " " + tostr
def _getNext(time,unit="auto",step=1): def time_stamps(since=None,to=None,within=None):
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": from database import getNext
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)
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)
#

View File

@ -4,7 +4,7 @@ import database
def instructions(keys): def instructions(keys):
from utilities import getArtistInfo from utilities import getArtistInfo
from htmlgenerators import clean, artistLink, artistLinks, KeySplit from htmlgenerators import artistLink, artistLinks, KeySplit
from htmlmodules import module_pulse, module_trackcharts from htmlmodules import module_pulse, module_trackcharts
filterkeys, _, _, _ = KeySplit(keys,forceArtist=True) filterkeys, _, _, _ = KeySplit(keys,forceArtist=True)

View File

@ -3,7 +3,7 @@
<html> <html>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Maloja - Pulse</title> <title>Maloja - KEY_PULSEDETAILS Pulse</title>
<link rel="stylesheet" href="maloja.css" /> <link rel="stylesheet" href="maloja.css" />
</head> </head>
@ -14,7 +14,7 @@
<div style="background-image:url('KEY_IMAGEURL')"></div> <div style="background-image:url('KEY_IMAGEURL')"></div>
</td> </td>
<td class="text"> <td class="text">
<h1>Pulse</h1><br/> <h1>KEY_PULSEDETAILS Pulse</h1><br/>
<span>KEY_LIMITS</span> <span>KEY_LIMITS</span>
<!--<p class="stats">KEY_SCROBBLES Scrobbles</p>--> <!--<p class="stats">KEY_SCROBBLES Scrobbles</p>-->

View File

@ -4,8 +4,9 @@ import database
def instructions(keys): def instructions(keys):
from utilities import getArtistInfo, getTrackInfo from utilities import getArtistInfo, getTrackInfo
from htmlgenerators import getTimeDesc, artistLink, artistLinks, trackLink, scrobblesLink, keysToUrl, getRangeDesc, KeySplit from htmlgenerators import artistLink, artistLinks, trackLink, scrobblesLink, keysToUrl, KeySplit
from htmlmodules import module_pulse from htmlmodules import module_pulse
from malojatime import range_desc, delimit_desc
filterkeys, timekeys, delimitkeys, _ = KeySplit(keys) filterkeys, timekeys, delimitkeys, _ = KeySplit(keys)
@ -27,6 +28,10 @@ def instructions(keys):
if moreartists != []: if moreartists != []:
limitstring += " <span class='extra'>including " + artistLinks(moreartists) + "</span>" limitstring += " <span class='extra'>including " + artistLinks(moreartists) + "</span>"
limitstring += " " + range_desc(**timekeys)
delimitstring = delimit_desc(**delimitkeys)
# get image # get image
if filterkeys.get("track") is not None: if filterkeys.get("track") is not None:
@ -42,7 +47,7 @@ def instructions(keys):
html_pulse = module_pulse(**filterkeys,**timekeys,**delimitkeys) 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) return (replace,pushresources)

View File

@ -6,6 +6,7 @@ def instructions(keys):
from utilities import getArtistInfo, getTrackInfo from utilities import getArtistInfo, getTrackInfo
from htmlgenerators import artistLink, artistLinks, trackLink, KeySplit 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) filterkeys, timekeys, _, amountkeys = KeySplit(keys)
@ -24,6 +25,7 @@ def instructions(keys):
if moreartists != []: if moreartists != []:
limitstring += " <span class='extra'>including " + artistLinks(moreartists) + "</span>" limitstring += " <span class='extra'>including " + artistLinks(moreartists) + "</span>"
limitstring += " " + range_desc(**timekeys)
html, amount, rep = module_scrobblelist(**filterkeys,**timekeys,**amountkeys) html, amount, rep = module_scrobblelist(**filterkeys,**timekeys,**amountkeys)

View File

@ -7,7 +7,7 @@ from htmlmodules import module_scrobblelist, module_pulse
def instructions(keys): def instructions(keys):
from utilities import getArtistsInfo, getTracksInfo 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 max_show = 14
posrange = ["#" + str(i) for i in range(1,max_show+1)] posrange = ["#" + str(i) for i in range(1,max_show+1)]

View File

@ -3,7 +3,7 @@
<html> <html>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Maloja - Top Artists in KEY_RANGE</title> <title>Maloja - Top Artists KEY_RANGE</title>
<link rel="stylesheet" href="maloja.css" /> <link rel="stylesheet" href="maloja.css" />
</head> </head>
@ -15,7 +15,7 @@
</td> </td>
<td class="text"> <td class="text">
<h1>Top Artists</h1><br/> <h1>Top Artists</h1><br/>
<span>in KEY_RANGE</span> <span>KEY_RANGE</span>
<!--<p class="stats">KEY_SCROBBLES Scrobbles</p>--> <!--<p class="stats">KEY_SCROBBLES Scrobbles</p>-->
</td> </td>

View File

@ -5,10 +5,15 @@ def instructions(keys):
from utilities import getArtistInfo from utilities import getArtistInfo
from htmlgenerators import KeySplit from htmlgenerators import KeySplit
from htmlmodules import module_artistcharts from htmlmodules import module_artistcharts
from malojatime import range_desc
_, timekeys, _, amountkeys = KeySplit(keys) _, timekeys, _, amountkeys = KeySplit(keys)
limitstring = range_desc(**timekeys)
html_charts, rep = module_artistcharts(**amountkeys,**timekeys) html_charts, rep = module_artistcharts(**amountkeys,**timekeys)
if rep is not None: if rep is not None:
@ -19,6 +24,6 @@ def instructions(keys):
pushresources = [{"file":imgurl,"type":"image"}] if imgurl.startswith("/") else [] 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) return (replace,pushresources)

View File

@ -5,6 +5,7 @@ def instructions(keys):
from utilities import getArtistInfo, getTrackInfo from utilities import getArtistInfo, getTrackInfo
from htmlgenerators import artistLink, KeySplit 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) filterkeys, timekeys, _, amountkeys = KeySplit(keys)
@ -23,6 +24,8 @@ def instructions(keys):
else: else:
imgurl = "" imgurl = ""
limitstring += " " + range_desc(**timekeys)
pushresources = [{"file":imgurl,"type":"image"}] if imgurl.startswith("/") else [] pushresources = [{"file":imgurl,"type":"image"}] if imgurl.startswith("/") else []