diff --git a/database.py b/database.py index b352967..21c1183 100644 --- a/database.py +++ b/database.py @@ -114,9 +114,20 @@ def getTrackID(artists,title): return i -#### -## HTTP requests -#### + + + + + + +######## +######## +## HTTP requests and their associated functions +######## +######## + + + @dbserver.route("/test") def test_server(): @@ -137,95 +148,174 @@ def test_server(): # 205 Database server is up, but DB is not fully built or is inconsistent # 403 Database server is up, but provided API key is not valid + +## All database functions are separated - the external wrapper only reads the request keys, converts them into lists and renames them where necessary, and puts the end result in a dict if not already so it can be returned as json + @dbserver.route("/scrobbles") -def get_scrobbles(): +def get_scrobbles_external(): keys = FormsDict.decode(request.query) + ckeys = {} + ckeys["artists"], ckeys["title"] = keys.getall("artist"), keys.get("title") + ckeys["since"], ckeys["to"], ckeys["within"] = keys.get("since"), keys.get("to"), keys.get("in") + ckeys["associated"] = (keys.get("associated")!=None) + ckeys["max"] = keys.get("max") - r = db_query(artists=keys.getall("artist"),title=keys.get("title"),since=keys.get("since"),to=keys.get("to"),within=keys.get("in"),associated=(keys.get("associated")!=None)) + result = get_scrobbles(**ckeys) + return {"list":result} + +def get_scrobbles(**keys): + r = db_query(**{k:keys[k] for k in keys if k in ["artists","title","since","to","within","associated"]}) r.reverse() if keys.get("max") is not None: - return {"list":r[:int(keys.get("max"))]} + return r[:int(keys.get("max"))] else: - return {"list":r} ##json can't be a list apparently??? + return r + + + + + +# DEPRECATED +# UNUSED +@dbserver.route("/amounts") +def get_amounts_external(): + return get_amounts() #really now + +def get_amounts(): + return {"scrobbles":len(SCROBBLES),"tracks":len(TRACKS),"artists":len(ARTISTS)} + @dbserver.route("/numscrobbles") -def get_scrobbles(): +def get_scrobbles_num_external(): keys = FormsDict.decode(request.query) + ckeys = {} + ckeys["artists"], ckeys["title"] = keys.getall("artist"), keys.get("title") + ckeys["since"], ckeys["to"], ckeys["within"] = keys.get("since"), keys.get("to"), keys.get("in") + ckeys["associated"] = (keys.get("associated")!=None) - r = db_query(artists=keys.getall("artist"),title=keys.get("title"),since=keys.get("since"),to=keys.get("to"),within=keys.get("in"),associated=(keys.get("associated")!=None)) - r.reverse() + result = get_scrobbles_num(**ckeys) + return {"amount":result} + +def get_scrobbles_num(**keys): + r = db_query(**{k:keys[k] for k in keys if k in ["artists","title","since","to","within","associated"]}) + return len(r) + +# DEPRECATED +# UNUSED +@dbserver.route("/charts") +def get_charts_external(): + keys = FormsDict.decode(request.query) + ckeys = {} + ckeys["since"], ckeys["to"], ckeys["within"] = keys.get("since"), keys.get("to"), keys.get("in") + + result = get_scrobbles_num(**ckeys) + return {"number":result} + +#def get_charts(**keys): +# return db_aggregate(**{k:keys[k] for k in keys if k in ["since","to","within"]}) + + + + - return {"amount":len(r)} @dbserver.route("/tracks") -def get_tracks(): +def get_tracks_external(): keys = FormsDict.decode(request.query) - artist = keys.get("artist") + ckeys = {} + ckeys["artist"] = keys.get("artist") + + result = get_tracks(**ckeys) + return {"list":result} + +def get_tracks(artist=None): + if artist is not None: artistid = ARTISTS.index(artist) - + else: + artistid = None + # Option 1 - ls = [getTrackObject(t) for t in TRACKS if (artistid in t[0]) or (artistid==None)] + return [getTrackObject(t) for t in TRACKS if (artistid in t[0]) or (artistid==None)] # Option 2 is a bit more elegant but much slower #tracklist = [getTrackObject(t) for t in TRACKS] #ls = [t for t in tracklist if (artist in t["artists"]) or (artist==None)] - - return {"list":ls} - + + @dbserver.route("/artists") +def get_artists_external(): + result = get_artists() + return {"list":result} + def get_artists(): + return ARTISTS #well - return {"list":ARTISTS} - -@dbserver.route("/amounts") -def get_amounts(): - return {"scrobbles":len(SCROBBLES),"tracks":len(TRACKS),"artists":len(ARTISTS)} + + + + + @dbserver.route("/charts/artists") -def get_charts_artists(): - since = request.query.get("since") - to = request.query.get("to") - within=request.query.get("in") - - return {"list":db_aggregate(by="ARTIST",since=since,to=to,within=within)} - -@dbserver.route("/charts/tracks") -def get_charts_tracks(): +def get_charts_artists_external(): keys = FormsDict.decode(request.query) - since = keys.get("since") - to = keys.get("to") - within=request.query.get("in") - artist = keys.get("artist") + ckeys = {} + ckeys["since"], ckeys["to"], ckeys["within"] = keys.get("since"), keys.get("to"), keys.get("in") - return {"list":db_aggregate(by="TRACK",since=since,to=to,within=within,artist=artist)} + result = get_charts_artists(**ckeys) + return {"list":result} + +def get_charts_artists(**keys): + return db_aggregate(by="ARTIST",**{k:keys[k] for k in keys if k in ["since","to","within"]}) + + + + + + +@dbserver.route("/charts/tracks") +def get_charts_tracks_external(): + keys = FormsDict.decode(request.query) + ckeys = {} + ckeys["since"], ckeys["to"], ckeys["within"] = keys.get("since"), keys.get("to"), keys.get("in") + ckeys["artist"] = keys.get("artist") -@dbserver.route("/charts") -def get_charts(): - since = request.query.get("since") - to = request.query.get("to") - within=request.query.get("in") + result = get_charts_tracks(**ckeys) + return {"list":result} - return {"number":db_aggregate(since=since,to=to,within=within)} +def get_charts_tracks(**keys): + return db_aggregate(by="TRACK",**{k:keys[k] for k in keys if k in ["since","to","within","artist"]}) + + + + + + + + @dbserver.route("/pulse") -def get_pulse(): - since = request.query.get("since") - to = request.query.get("to") - within=request.query.get("in") - (ts_start,ts_end) = getTimestamps(since,to,within) - step = request.query.get("step","month") - trail = int(request.query.get("trail",3)) - - [step,stepn] = (step.split("-") + [1])[:2] # makes the multiplier 1 if not assigned - stepn = int(stepn) +def get_pulse_external(): + keys = FormsDict.decode(request.query) + ckeys = {} + ckeys["since"], ckeys["to"], ckeys["within"] = keys.get("since"), keys.get("to"), keys.get("in") + ckeys["step"], ckeys["trail"] = keys.get("step"), int_or_none(keys.get("trail")) + if ckeys["step"] is not None: [ckeys["step"],ckeys["stepn"]] = (ckeys["step"].split("-") + [1])[:2] # makes the multiplier 1 if not assigned + if "stepn" in ckeys: ckeys["stepn"] = int(ckeys["stepn"]) + cleandict(ckeys) + results = get_pulse(**ckeys) + return {"list":results} + +def get_pulse(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"]}) 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 d_start = getNext(d_start,step,stepn * trail * -1) # go one range back to begin @@ -234,31 +324,40 @@ def get_pulse(): d_current = d_start while True: d_current_end = getNext(d_current,step,stepn * trail) - #print("Checking from " + str(d_current[0]) + "-" + str(d_current[1]) + "-" + str(d_current[2]) + " to " + str(d_current_end[0]) + "-" + str(d_current_end[1]) + "-" + str(d_current_end[2])) res = db_aggregate(since=d_current,to=d_current_end) 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): break - return {"list":results} + return results + + + + + + @dbserver.route("/top/artists") -def get_top_artists(): - since = request.query.get("since") - to = request.query.get("to") - within=request.query.get("in") - (ts_start,ts_end) = getTimestamps(since,to,within) - step = request.query.get("step","month") - trail = int(request.query.get("trail",3)) +def get_top_artists_external(): + + keys = FormsDict.decode(request.query) + ckeys = {} + ckeys["since"], ckeys["to"], ckeys["within"] = keys.get("since"), keys.get("to"), keys.get("in") + ckeys["step"], ckeys["trail"] = keys.get("step"), int_or_none(keys.get("trail")) + if ckeys["step"] is not None: [ckeys["step"],ckeys["stepn"]] = (ckeys["step"].split("-") + [1])[:2] # makes the multiplier 1 if not assigned + if "stepn" in ckeys: ckeys["stepn"] = int(ckeys["stepn"]) - [step,stepn] = (step.split("-") + [1])[:2] # makes the multiplier 1 if not assigned - stepn = int(stepn) + cleandict(ckeys) + results = get_top_artists(**ckeys) + return {"list":results} +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"]}) 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 * trail * -1) # go one range back to begin @@ -267,7 +366,6 @@ def get_top_artists(): d_current = d_start while True: d_current_end = getNext(d_current,step,stepn * trail) - #print("Checking from " + str(d_current[0]) + "-" + str(d_current[1]) + "-" + str(d_current[2]) + " to " + str(d_current_end[0]) + "-" + str(d_current_end[1]) + "-" + str(d_current_end[2])) try: res = db_aggregate(since=d_current,to=d_current_end,by="ARTIST")[0] results.append({"from":d_current,"to":d_current_end,"artist":res["artist"],"scrobbles":res["scrobbles"]}) @@ -277,32 +375,44 @@ def get_top_artists(): if isPast(d_current_end,d_end): break - return {"list":results} - + return results + + + + + + + + + + @dbserver.route("/top/tracks") -def get_top_tracks(): - since = request.query.get("since") - to = request.query.get("to") - within=request.query.get("in") - (ts_start,ts_end) = getTimestamps(since,to,within) - step = request.query.get("step","month") - trail = int(request.query.get("trail",3)) - - [step,stepn] = (step.split("-") + [1])[:2] # makes the multiplier 1 if not assigned - stepn = int(stepn) +def get_top_tracks_external(): + keys = FormsDict.decode(request.query) + ckeys = {} + ckeys["since"], ckeys["to"], ckeys["within"] = keys.get("since"), keys.get("to"), keys.get("in") + ckeys["step"], ckeys["trail"] = keys.get("step"), int_or_none(keys.get("trail")) + if ckeys["step"] is not None: [ckeys["step"],ckeys["stepn"]] = (ckeys["step"].split("-") + [1])[:2] # makes the multiplier 1 if not assigned + if "stepn" in ckeys: ckeys["stepn"] = int(ckeys["stepn"]) + cleandict(ckeys) + results = get_top_tracks(**ckeys) + return {"list":results} + +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"]}) 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 * trail * -1) # go one range back to begin - + + results = [] d_current = d_start while True: d_current_end = getNext(d_current,step,stepn * trail) - #print("Checking from " + str(d_current[0]) + "-" + str(d_current[1]) + "-" + str(d_current[2]) + " to " + str(d_current_end[0]) + "-" + str(d_current_end[1]) + "-" + str(d_current_end[2])) try: res = db_aggregate(since=d_current,to=d_current_end,by="TRACK")[0] results.append({"from":d_current,"to":d_current_end,"track":res["track"],"scrobbles":res["scrobbles"]}) @@ -312,7 +422,18 @@ def get_top_tracks(): if isPast(d_current_end,d_end): break - return {"list":results} + return results + + + + + + + + + + + def getStartOf(timestamp,unit): @@ -356,10 +477,16 @@ def getNext(time,unit="auto",step=1): elif unit == "week": return getNext(time,"day",step * 7) -@dbserver.route("/artistinfo") -def artistInfo(): +@dbserver.route("/artistinfo") +def artistInfo_external(): keys = FormsDict.decode(request.query) - artist = keys.get("artist") + ckeys = {} + ckeys["artist"] = keys.get("artist") + + results = artistInfo(**ckeys) + return results + +def artistInfo(artist): charts = db_aggregate(by="ARTIST") scrobbles = len(db_query(artists=[artist])) #we cant take the scrobble number from the charts because that includes all countas scrobbles @@ -372,19 +499,28 @@ def artistInfo(): artist = coa.getCredited(artist) c = [e for e in charts if e["artist"] == artist][0] return {"replace":artist,"scrobbles":scrobbles,"position":charts.index(c) + 1} + + + + @dbserver.route("/trackinfo") -def trackInfo(): +def trackInfo_external(): keys = FormsDict.decode(request.query) - artists = keys.getall("artist") - title = keys.get("title") + ckeys = {} + ckeys["artists"],ckeys["title"] = keys.getall("artist"), keys.get("title") + results = trackInfo(**ckeys) + return results + +def trackInfo(artists,title): charts = db_aggregate(by="TRACK") scrobbles = len(db_query(artists=artists,title=title)) #we cant take the scrobble number from the charts because that includes all countas scrobbles - c = [e for e in charts if set(e["track"]["artists"]) == set(artists) and e["track"]["title"] == title][0] return {"scrobbles":scrobbles,"position":charts.index(c) + 1} + + def isPast(date,limit): if not date[0] == limit[0]: @@ -393,6 +529,12 @@ def isPast(date,limit): return date[1] > limit[1] return (date[2] > limit[2]) + + + + + + @dbserver.get("/newscrobble") def pseudo_post_scrobble(): keys = FormsDict.decode(request.query) # The Dal★Shabet handler @@ -766,7 +908,9 @@ 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(f=None,t=None,i=None): +def getTimestamps(since=None,to=None,within=None): + + f,t,i = since,to,within if i is not None: f = i diff --git a/utilities.py b/utilities.py index 2d6371a..613d41d 100644 --- a/utilities.py +++ b/utilities.py @@ -135,6 +135,24 @@ def addEntries(filename,al): for a in al: line = "\t".join(a) f.write(line + "\n") + + + +### Useful functions + +def int_or_none(input_): + try: + return int(input_) + except: + return None + +def cleandict(d): + newdict = {k:d[k] for k in d if d[k] is not None} + d.clear() + d.update(newdict) + + + ### Logging