1
0
mirror of https://github.com/krateng/maloja.git synced 2023-08-10 21:12:55 +03:00

Modularizing HTML generation, Part I

This commit is contained in:
Krateng 2018-12-26 17:42:55 +01:00
parent bed50452b9
commit 3cfa8428ff
9 changed files with 112 additions and 77 deletions

View File

@ -525,8 +525,7 @@ def runserver(PORT):
loadAPIkeys() loadAPIkeys()
run(dbserver, host='0.0.0.0', port=PORT, server='waitress') run(dbserver, host='::', port=PORT, server='waitress')
def build_db(): def build_db():
@ -607,36 +606,32 @@ def sync():
def db_query(artists=None,title=None,track=None,since=None,to=None,associated=False): def db_query(artists=None,title=None,track=None,since=None,to=None,associated=False):
(since, to) = getTimestamps(since,to) (since, to) = getTimestamps(since,to)
# 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
# if not, multiple artists are interpreted as requesting all scrobbles they were all involved in (but possibly other too) # if not, duplicate artist arguments are ignored
# eg a track named "Awesome Song" by "TWICE", "AOA" and "f(x)" would count when we specifiy only the artists "AOA" and "f(x)", but not when we add the title (because then we'd be
# looking for that specific track with only those two artists - which could in fact exist)
# artists to numbers
artists = set([(ARTISTS.index(a) if isinstance(a,str) else a) for a in artists]) artists = set([(ARTISTS.index(a) if isinstance(a,str) else a) for a in artists])
#for artist in artists:
# if isinstance(artist, str):
# artist = ARTISTS.index(artist)
#if isinstance(title, str):
# track = (frozenset(artists),title)
# track = TRACKS.index(track)
# if track is specified (only number works), we ignore title string #check if track is requested via title
if title!=None and track==None: if title!=None and track==None:
track = TRACKS.index((frozenset(artists),title)) track = TRACKS.index((frozenset(artists),title))
if artists == []:
artists = None artists = None
# if we're not looking for a track (either directly or per title artist arguments, which is converted to track above)
# we only need one artist
elif track==None and len(artists) != 0:
artist = artists.pop()
else:
artist = None
# right now we always request everything by name, maybe we don't actually need the request by number, but i'll leave it in for now # right now we always request everything by name, maybe we don't actually need the request by number, but i'll leave it in for now
if associated: if associated:
return [getScrobbleObject(s) for s in SCROBBLES if (s[0] == track or track==None) and (artists==None or artists.issubset(coa.getCreditedList(TRACKS[s[0]][0]))) and (since < s[1] < to)] return [getScrobbleObject(s) for s in SCROBBLES if (s[0] == track or track==None) and (artist==None or artist in coa.getCreditedList(TRACKS[s[0]][0])) and (since < s[1] < to)]
else: else:
return [getScrobbleObject(s) for s in SCROBBLES if (s[0] == track or track==None) and (artists==None or artists.issubset(TRACKS[s[0]][0])) and (since < s[1] < to)] return [getScrobbleObject(s) for s in SCROBBLES if (s[0] == track or track==None) and (artist==None or artist in TRACKS[s[0]][0]) and (since < s[1] < to)]
# pointless to check for artist when track is checked because every track has a fixed set of artists, but it's more elegant this way # pointless to check for artist when track is checked because every track has a fixed set of artists, but it's more elegant this way
@ -705,10 +700,10 @@ def db_search(query,type=None):
def getTimestamps(f,t): def getTimestamps(f,t):
#(f,t) = inp #(f,t) = inp
if isinstance(f, str) and f.lower() == "today": if isinstance(f, str) and f.lower() == "today":
tod = datetime.date.today() tod = datetime.datetime.utcnow()
f = [tod.year,tod.month,tod.day] f = [tod.year,tod.month,tod.day]
if isinstance(t, str) and t.lower() == "today": if isinstance(t, str) and t.lower() == "today":
tod = datetime.date.today() tod = datetime.datetime.utcnow()
t = [tod.year,tod.month,tod.day] t = [tod.year,tod.month,tod.day]

64
htmlgenerators.py Normal file
View File

@ -0,0 +1,64 @@
def artistLink(name):
import urllib
return "<a href='/artist?artist=" + urllib.parse.quote(name) + "'>" + name + "</a>"
# necessary because urllib.parse.urlencode doesnt handle multidicts
def keysToUrl(*dicts):
import urllib
st = ""
keys = removeIdentical(*dicts)
for k in keys:
values = keys.getall(k)
st += "&".join([urllib.parse.urlencode({k:v}) for v in values])
st += "&"
return st
def removeIdentical(*dicts):
from bottle import FormsDict
#combine multiple dicts
keys = FormsDict()
for d in dicts:
for k in d:
try: #multidicts
for v in d.getall(k):
keys.append(k,v)
except: #normaldicts
v = d.get(k)
keys.append(k,v)
new = FormsDict()
for k in keys:
values = set(keys.getall(k))
for v in values:
new.append(k,v)
return new
def getTimeDesc(timestamp):
import datetime
tim = datetime.datetime.utcfromtimestamp(timestamp)
return tim.strftime("%d. %b %Y %I:%M %p")
# limit a multidict to only the specified keys
# would be a simple constructor expression, but multidicts apparently don't let me do that
# hardcoding this to only allow multi values for a key in one case: artist when there is also a title specified
def pickKeys(d,*keys):
from bottle import FormsDict
if isinstance(d,dict) or not "title" in d:
return {k:d.get(k) for k in d if k in keys}
else:
# create a normal dictionary of lists
newd = {k:d.getall(k) for k in d if k in keys and k=="artist"}
newd2 = {k:[d.get(k)] for k in d if k in keys and k!="artist"}
# one by one add the list entries to the formsdict
finald = FormsDict()
for k in newd:
for v in newd.get(k):
finald.append(k,v)
return finald

View File

@ -2,7 +2,7 @@
from bottle import Bottle, route, get, post, error, run, template, static_file, request, response, FormsDict from bottle import Bottle, route, get, post, error, run, template, static_file, request, response, FormsDict
from importlib.machinery import SourceFileLoader from importlib.machinery import SourceFileLoader
from utilities import removeIdentical from htmlgenerators import removeIdentical
import _thread import _thread
import waitress import waitress
import urllib.request import urllib.request
@ -120,4 +120,4 @@ except:
## start database server ## start database server
_thread.start_new_thread(SourceFileLoader("database","database.py").load_module().runserver,(DATABASE_PORT,)) _thread.start_new_thread(SourceFileLoader("database","database.py").load_module().runserver,(DATABASE_PORT,))
run(webserver, host='0.0.0.0', port=MAIN_PORT, server='waitress') run(webserver, host='::', port=MAIN_PORT, server='waitress')

View File

@ -228,32 +228,4 @@ def cacheImage(url,path,filename):
target = path + "/" + filename + "." + response.info().get_content_subtype() target = path + "/" + filename + "." + response.info().get_content_subtype()
urllib.request.urlretrieve(url,target) urllib.request.urlretrieve(url,target)
def artistLink(name):
import urllib
return "<a href='/artist?artist=" + urllib.parse.quote(name) + "'>" + name + "</a>"
# necessary because urllib.parse.urlencode doesnt handle multidicts
def keysToUrl(keys):
import urllib
st = ""
for k in removeIdentical(keys):
values = keys.getall(k)
st += "&".join([urllib.parse.urlencode({k:v}) for v in values])
st += "&"
return st
def removeIdentical(keys):
from bottle import FormsDict
new = FormsDict()
for k in keys:
values = set(keys.getall(k))
for v in values:
new.append(k,v)
return new
def getTimeDesc(timestamp):
import datetime
tim = datetime.datetime.utcfromtimestamp(timestamp)
return tim.strftime("%d. %b %Y %I:%M %p")

View File

@ -18,7 +18,7 @@
<span>KEY_ASSOCIATED</span> <span>KEY_ASSOCIATED</span>
<p class="stats"><a href="/scrobbles?artist=KEY_ENC_ARTISTNAME">KEY_SCROBBLES Scrobbles</a></p> <p class="stats"><a href="/scrobbles?artist=KEY_ENC_ARTISTNAME">KEY_SCROBBLES Scrobbles</a></p>
<p>KEY_DESCRIPTION</p> <p class="desc">KEY_DESCRIPTION</p>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -39,6 +39,15 @@ table.top_info td.text h1 {
display:inline; display:inline;
padding-right:5px; padding-right:5px;
} }
p.desc a {
padding-left:20px;
background-repeat:no-repeat;
background-size:contain;
background-position:left;
background-image:url("https://www.last.fm/static/images/lastfm_avatar_twitter.66cd2c48ce03.png");
}
/* /*
table.top_info td.text .stats { table.top_info td.text .stats {
color:grey; color:grey;

View File

@ -3,16 +3,15 @@ import json
def replacedict(keys,dbport): def replacedict(keys,dbport):
from utilities import getArtistInfo, getTimeDesc, artistLink, keysToUrl from utilities import getArtistInfo
from htmlgenerators import getTimeDesc, artistLink, keysToUrl, pickKeys
timekeys = pickKeys(keys,"since","to","in")
#hand down the since and from arguments limitkeys = pickKeys(keys,"artist","title")
#extrakeys = urllib.parse.urlencode(keys,doseq=True)
extrakeys = keysToUrl(keys)
limitstring = "" limitstring = ""
response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/scrobbles?" + extrakeys) response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/scrobbles?" + keysToUrl(limitkeys,timekeys))
db_data = json.loads(response.read()) db_data = json.loads(response.read())
scrobbles = db_data["list"] scrobbles = db_data["list"]

View File

@ -3,12 +3,13 @@ import json
def replacedict(keys,dbport): def replacedict(keys,dbport):
from utilities import getArtistInfo, artistLink from utilities import getArtistInfo
from htmlgenerators import artistLink, keysToUrl, pickKeys
#hand down the since and from arguments timekeys = pickKeys(keys,"since","to","in")
extrakeys = urllib.parse.urlencode(keys,quote_via=urllib.parse.quote,safe="/") limitkeys = pickKeys(keys)
response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/charts/artists?" + extrakeys) response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/charts/artists?" + keysToUrl(timekeys,limitkeys))
db_data = json.loads(response.read()) db_data = json.loads(response.read())
charts = db_data["list"][:50] charts = db_data["list"][:50]
topartist = charts[0]["artist"] topartist = charts[0]["artist"]
@ -17,7 +18,7 @@ def replacedict(keys,dbport):
imgurl = info.get("image") imgurl = info.get("image")
response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/scrobbles?" + extrakeys) response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/scrobbles?" + keysToUrl(timekeys,limitkeys))
db_data = json.loads(response.read()) db_data = json.loads(response.read())
scrobblelist = db_data["list"] scrobblelist = db_data["list"]
scrobbles = len(scrobblelist) scrobbles = len(scrobblelist)
@ -34,8 +35,8 @@ def replacedict(keys,dbport):
html += artistLink(e["artist"]) html += artistLink(e["artist"])
if (e["counting"] != []): if (e["counting"] != []):
html += " <span class='extra'>incl. " + ", ".join([artistLink(a) for a in e["counting"]]) + "</span>" html += " <span class='extra'>incl. " + ", ".join([artistLink(a) for a in e["counting"]]) + "</span>"
html += "</td><td class='amount'><a href='/scrobbles?artist=" + urllib.parse.quote(e["artist"]) + "&associated&" + extrakeys + "'>" + str(e["scrobbles"]) + "</a></td>" html += "</td><td class='amount'><a href='/scrobbles?artist=" + urllib.parse.quote(e["artist"]) + "&associated&" + keysToUrl(timekeys) + "'>" + str(e["scrobbles"]) + "</a></td>"
html += "<td class='bar'><a href='/scrobbles?artist=" + urllib.parse.quote(e["artist"]) + "&associated&" + extrakeys + "'><div style='width:" + str(e["scrobbles"]/maxbar * 100) + "%;'></div></a></td>" html += "<td class='bar'><a href='/scrobbles?artist=" + urllib.parse.quote(e["artist"]) + "&associated&" + keysToUrl(timekeys) + "'><div style='width:" + str(e["scrobbles"]/maxbar * 100) + "%;'></div></a></td>"
html += "</tr>" html += "</tr>"
i += 1 i += 1
html += "</table>" html += "</table>"

View File

@ -3,18 +3,13 @@ import json
def replacedict(keys,dbport): def replacedict(keys,dbport):
from utilities import getArtistInfo, artistLink, keysToUrl from utilities import getArtistInfo
from htmlgenerators import artistLink, keysToUrl, pickKeys
# we don't use the associated key for top tracks so we don't wanna hand it down to functions we're calling timekeys = pickKeys(keys,"since","to","in")
keys.pop("associated",None) limitkeys = pickKeys(keys,"artist")
#hand down the since and from arguments response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/charts/tracks?" + keysToUrl(timekeys,limitkeys))
extrakeys = urllib.parse.urlencode(keys,quote_via=urllib.parse.quote,safe="/")
# I should probably add a separate variable for keys that are passed to db functions and keys that are inherited to links (usually only time)
#extrakeys = keysToUrl(keys)
# top tracks should always be of one artist
response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/charts/tracks?" + extrakeys)
db_data = json.loads(response.read()) db_data = json.loads(response.read())
charts = db_data["list"][:50] charts = db_data["list"][:50]
limitstring = "" limitstring = ""
@ -30,7 +25,7 @@ def replacedict(keys,dbport):
imgurl = info.get("image") imgurl = info.get("image")
response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/scrobbles?" + extrakeys) response = urllib.request.urlopen("http://localhost:" + str(dbport) + "/scrobbles?" + keysToUrl(timekeys,limitkeys))
db_data = json.loads(response.read()) db_data = json.loads(response.read())
scrobblelist = db_data["list"] scrobblelist = db_data["list"]
scrobbles = len(scrobblelist) scrobbles = len(scrobblelist)
@ -45,8 +40,8 @@ def replacedict(keys,dbport):
html += "<td class='rank'>#" + str(i) + "</td><td class='artists'>" html += "<td class='rank'>#" + str(i) + "</td><td class='artists'>"
html += ", ".join([artistLink(a) for a in e["track"]["artists"]]) html += ", ".join([artistLink(a) for a in e["track"]["artists"]])
html += "</td><td class='title'>" + e["track"]["title"] html += "</td><td class='title'>" + e["track"]["title"]
html += "</td><td class='amount'><a href='/scrobbles?" + "&".join(["artist=" + urllib.parse.quote(a) for a in e["track"]["artists"]]) + "&title=" + urllib.parse.quote(e["track"]["title"]) + "&" + extrakeys + "'>" + str(e["scrobbles"]) + "</a></td>" html += "</td><td class='amount'><a href='/scrobbles?" + "&".join(["artist=" + urllib.parse.quote(a) for a in e["track"]["artists"]]) + "&title=" + urllib.parse.quote(e["track"]["title"]) + "&" + keysToUrl(timekeys) + "'>" + str(e["scrobbles"]) + "</a></td>"
html += "<td class='bar'><a href='/scrobbles?" + "&".join(["artist=" + urllib.parse.quote(a) for a in e["track"]["artists"]]) + "&title=" + urllib.parse.quote(e["track"]["title"]) + "&" + extrakeys + "'><div style='width:" + str(e["scrobbles"]/maxbar * 100) + "%;'></div></a>" html += "<td class='bar'><a href='/scrobbles?" + "&".join(["artist=" + urllib.parse.quote(a) for a in e["track"]["artists"]]) + "&title=" + urllib.parse.quote(e["track"]["title"]) + "&" + keysToUrl(timekeys) + "'><div style='width:" + str(e["scrobbles"]/maxbar * 100) + "%;'></div></a>"
html += "</td>" html += "</td>"
html += "</tr>" html += "</tr>"
i += 1 i += 1