Refactored API functions, added missing DB calls

This commit is contained in:
krateng 2023-10-28 15:03:20 +02:00
parent f9ccb10edf
commit 251c0b4020
4 changed files with 100 additions and 59 deletions

View File

@ -4,6 +4,8 @@ import traceback
from bottle import response, static_file, FormsDict
from inspect import signature
from doreah.logging import log
from doreah.auth import authenticated_function
@ -99,6 +101,8 @@ errors = {
})
}
# decorator to catch exceptions and return proper json responses
def catch_exceptions(func):
def protector(*args,**kwargs):
try:
@ -113,9 +117,11 @@ def catch_exceptions(func):
protector.__doc__ = func.__doc__
protector.__annotations__ = func.__annotations__
protector.__name__ = f"EXCPR_{func.__name__}"
return protector
# decorator to expand the docstring with common arguments for the API explorer. DOESNT WRAP
def add_common_args_to_docstring(filterkeys=False,limitkeys=False,delimitkeys=False,amountkeys=False):
def decorator(func):
timeformats = "Possible formats include '2022', '2022/08', '2022/08/01', '2022/W42', 'today', 'thismonth', 'monday', 'august'"
@ -149,14 +155,39 @@ def add_common_args_to_docstring(filterkeys=False,limitkeys=False,delimitkeys=Fa
return decorator
# decorator to take the URI keys and convert them into internal keys
def convert_kwargs(func):
#params = tuple(p for p in signature(func).parameters)
def wrapper(*args,albumartist:Multi[str]=[],trackartist:Multi[str]=[],**kwargs):
kwargs = FormsDict(kwargs)
for a in albumartist:
kwargs.append("albumartist",a)
for a in trackartist:
kwargs.append("trackartist",a)
k_filter, k_limit, k_delimit, k_amount, k_special = uri_to_internal(kwargs,api=True)
try:
return func(*args,k_filter=k_filter, k_limit=k_limit, k_delimit=k_delimit, k_amount=k_amount)
except TypeError:
return func(*args,k_filter=k_filter, k_limit=k_limit, k_delimit=k_delimit, k_amount=k_amount,k_special=k_special)
# TODO: ....really?
wrapper.__doc__ = func.__doc__
wrapper.__name__ = f"CVKWA_{func.__name__}"
return wrapper
# decorator to add pagination info to endpoints (like links to other pages)
# this expects already converted uri args!!!
def add_pagination(endpoint,filterkeys=False,limitkeys=False,delimitkeys=False):
def decorator(func):
def wrapper(*args,**kwargs):
def wrapper(*args,k_filter, k_limit, k_delimit, k_amount):
k_filter, k_limit, k_delimit, k_amount, _ = uri_to_internal(kwargs,api=True)
keydicts = []
if filterkeys: keydicts.append(k_filter)
if limitkeys: keydicts.append(k_limit)
@ -164,7 +195,7 @@ def add_pagination(endpoint,filterkeys=False,limitkeys=False,delimitkeys=False):
keydicts.append(k_amount)
result = func(*args,**kwargs)
result = func(*args,k_filter=k_filter, k_limit=k_limit, k_delimit=k_delimit, k_amount=k_amount)
result['pagination'] = {
'page': k_amount['page'],
@ -177,10 +208,12 @@ def add_pagination(endpoint,filterkeys=False,limitkeys=False,delimitkeys=False):
wrapper.__doc__ = func.__doc__
wrapper.__annotations__ = func.__annotations__
wrapper.__name__ = f"PGNAT_{func.__name__}"
return wrapper
return decorator
@api.get("test")
@catch_exceptions
def test_server(key=None):
@ -233,16 +266,16 @@ def server_info():
@api.get("scrobbles")
@catch_exceptions
@add_common_args_to_docstring(filterkeys=True,limitkeys=True,amountkeys=True)
@convert_kwargs
@add_pagination("scrobbles",filterkeys=True,limitkeys=True)
def get_scrobbles_external(**keys):
def get_scrobbles_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns a list of scrobbles.
:return: list (List)
:rtype: Dictionary
"""
k_filter, k_time, _, k_amount, _ = uri_to_internal(keys,api=True)
ckeys = {**k_filter, **k_time, **k_amount}
ckeys = {**k_filter, **k_limit, **k_amount}
result = database.get_scrobbles(**ckeys)
# this should now all be served by the inner function
@ -259,15 +292,15 @@ def get_scrobbles_external(**keys):
@api.get("numscrobbles")
@catch_exceptions
@add_common_args_to_docstring(filterkeys=True,limitkeys=True,amountkeys=True)
def get_scrobbles_num_external(**keys):
@convert_kwargs
def get_scrobbles_num_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns amount of scrobbles.
:return: amount (Integer)
:rtype: Dictionary
"""
k_filter, k_time, _, k_amount, _ = uri_to_internal(keys)
ckeys = {**k_filter, **k_time, **k_amount}
ckeys = {**k_filter, **k_limit, **k_amount}
result = database.get_scrobbles_num(**ckeys)
return {
@ -280,15 +313,15 @@ def get_scrobbles_num_external(**keys):
@api.get("tracks")
@catch_exceptions
@add_common_args_to_docstring(filterkeys=True)
def get_tracks_external(**keys):
"""Returns all tracks (optionally of an artist).
@convert_kwargs
def get_tracks_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns all tracks (optionally of an artist or on an album).
:return: list (List)
:rtype: Dictionary
"""
k_filter, _, _, _, _ = uri_to_internal(keys,forceArtist=True)
ckeys = {**k_filter}
ckeys = {**k_filter}
result = database.get_tracks(**ckeys)
return {
@ -301,11 +334,13 @@ def get_tracks_external(**keys):
@api.get("artists")
@catch_exceptions
@add_common_args_to_docstring()
def get_artists_external():
@convert_kwargs
def get_artists_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns all artists.
:return: list (List)
:rtype: Dictionary"""
result = database.get_artists()
return {
@ -320,14 +355,14 @@ def get_artists_external():
@api.get("charts/artists")
@catch_exceptions
@add_common_args_to_docstring(limitkeys=True)
def get_charts_artists_external(**keys):
@convert_kwargs
def get_charts_artists_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns artist charts
:return: list (List)
:rtype: Dictionary"""
_, k_time, _, _, _ = uri_to_internal(keys)
ckeys = {**k_time}
ckeys = {**k_limit}
result = database.get_charts_artists(**ckeys)
return {
@ -336,19 +371,17 @@ def get_charts_artists_external(**keys):
}
@api.get("charts/tracks")
@catch_exceptions
@add_common_args_to_docstring(filterkeys=True,limitkeys=True)
def get_charts_tracks_external(**keys):
@convert_kwargs
def get_charts_tracks_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns track charts
:return: list (List)
:rtype: Dictionary"""
k_filter, k_time, _, _, _ = uri_to_internal(keys,forceArtist=True)
# force artist because track charts can never be of a track or album, only of an artist or global
ckeys = {**k_filter, **k_time}
ckeys = {**k_filter, **k_limit}
result = database.get_charts_tracks(**ckeys)
return {
@ -362,14 +395,14 @@ def get_charts_tracks_external(**keys):
@api.get("pulse")
@catch_exceptions
@add_common_args_to_docstring(filterkeys=True,limitkeys=True,delimitkeys=True,amountkeys=True)
def get_pulse_external(**keys):
@convert_kwargs
def get_pulse_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns amounts of scrobbles in specified time frames
:return: list (List)
:rtype: Dictionary"""
k_filter, k_time, k_internal, k_amount, _ = uri_to_internal(keys)
ckeys = {**k_filter, **k_time, **k_internal, **k_amount}
ckeys = {**k_filter, **k_limit, **k_delimit, **k_amount}
results = database.get_pulse(**ckeys)
return {
@ -378,19 +411,17 @@ def get_pulse_external(**keys):
}
@api.get("performance")
@catch_exceptions
@add_common_args_to_docstring(filterkeys=True,limitkeys=True,delimitkeys=True,amountkeys=True)
def get_performance_external(**keys):
@convert_kwargs
def get_performance_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns artist's or track's rank in specified time frames
:return: list (List)
:rtype: Dictionary"""
k_filter, k_time, k_internal, k_amount, _ = uri_to_internal(keys)
ckeys = {**k_filter, **k_time, **k_internal, **k_amount}
ckeys = {**k_filter, **k_limit, **k_delimit, **k_amount}
results = database.get_performance(**ckeys)
return {
@ -404,14 +435,14 @@ def get_performance_external(**keys):
@api.get("top/artists")
@catch_exceptions
@add_common_args_to_docstring(limitkeys=True,delimitkeys=True)
def get_top_artists_external(**keys):
@convert_kwargs
def get_top_artists_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns respective number 1 artists in specified time frames
:return: list (List)
:rtype: Dictionary"""
_, k_time, k_internal, _, _ = uri_to_internal(keys)
ckeys = {**k_time, **k_internal}
ckeys = {**k_limit, **k_delimit}
results = database.get_top_artists(**ckeys)
return {
@ -425,17 +456,16 @@ def get_top_artists_external(**keys):
@api.get("top/tracks")
@catch_exceptions
@add_common_args_to_docstring(limitkeys=True,delimitkeys=True)
def get_top_tracks_external(**keys):
@convert_kwargs
def get_top_tracks_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns respective number 1 tracks in specified time frames
:return: list (List)
:rtype: Dictionary"""
_, k_time, k_internal, _, _ = uri_to_internal(keys)
ckeys = {**k_time, **k_internal}
# IMPLEMENT THIS FOR TOP TRACKS OF ARTIST AS WELL?
ckeys = {**k_limit, **k_delimit}
results = database.get_top_tracks(**ckeys)
# IMPLEMENT THIS FOR TOP TRACKS OF ARTIST AS WELL?
return {
"status":"ok",
@ -448,12 +478,13 @@ def get_top_tracks_external(**keys):
@api.get("artistinfo")
@catch_exceptions
@add_common_args_to_docstring(filterkeys=True)
def artist_info_external(**keys):
@convert_kwargs
def artist_info_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns information about an artist
:return: artist (String), scrobbles (Integer), position (Integer), associated (List), medals (Mapping), topweeks (Integer)
:rtype: Dictionary"""
k_filter, _, _, _, _ = uri_to_internal(keys,forceArtist=True)
ckeys = {**k_filter}
return database.artist_info(**ckeys)
@ -463,18 +494,14 @@ def artist_info_external(**keys):
@api.get("trackinfo")
@catch_exceptions
@add_common_args_to_docstring(filterkeys=True)
def track_info_external(artist:Multi[str]=[],**keys):
@convert_kwargs
def track_info_external(k_filter, k_limit, k_delimit, k_amount):
"""Returns information about a track
:return: track (Mapping), scrobbles (Integer), position (Integer), medals (Mapping), certification (String), topweeks (Integer)
:rtype: Dictionary"""
# transform into a multidict so we can use our nomral uri_to_internal function
keys = FormsDict(keys)
for a in artist:
keys.append("artist",a)
k_filter, _, _, _, _ = uri_to_internal(keys,forceTrack=True)
ckeys = {**k_filter}
ckeys = {**k_filter}
return database.track_info(**ckeys)
@ -561,24 +588,18 @@ def post_scrobble(
@api.post("addpicture")
@authenticated_function(alternate=api_key_correct,api=True)
@catch_exceptions
def add_picture(b64,artist:Multi=[],title=None,albumtitle=None):
"""Uploads a new image for an artist or track.
@convert_kwargs
def add_picture(k_filter, k_limit, k_delimit, k_amount, k_special):
"""Uploads a new image for an artist, album or track.
param string b64: Base 64 representation of the image
param string artist: Artist name. Can be supplied multiple times for tracks with multiple artists.
param string title: Title of the track. Optional.
"""
keys = FormsDict()
for a in artist:
keys.append("artist",a)
if title is not None: keys.append("title",title)
elif albumtitle is not None: keys.append("albumtitle",albumtitle)
k_filter, _, _, _, _ = uri_to_internal(keys)
if "associated" in k_filter: del k_filter["associated"]
if "track" in k_filter: k_filter = k_filter["track"]
elif "album" in k_filter: k_filter = k_filter["album"]
url = images.set_image(b64,**k_filter)
url = images.set_image(k_special['b64'],**k_filter)
return {
'status': 'success',

View File

@ -372,6 +372,14 @@ def get_tracks(dbconn=None,**keys):
result = sqldb.get_tracks_of_artist(keys.get('artist'),dbconn=dbconn)
return result
@waitfordb
def get_albums(dbconn=None,**keys):
if keys.get('artist') is None:
result = sqldb.get_albums(dbconn=dbconn)
else:
result = sqldb.get_albums_of_artists([sqldb.get_artist_id(keys.get('artist'),create_new=False)],dbconn=dbconn)
return result
@waitfordb
def get_artists(dbconn=None):
return sqldb.get_artists(dbconn=dbconn)

View File

@ -1018,6 +1018,15 @@ def get_tracks(dbconn=None):
return tracks_db_to_dict(result,dbconn=dbconn)
@cached_wrapper
@connection_provider
def get_albums(dbconn=None):
op = DB['albums'].select()
result = dbconn.execute(op).all()
return albums_db_to_dict(result,dbconn=dbconn)
### functions that count rows for parameters
@cached_wrapper

View File

@ -78,8 +78,11 @@ def uri_to_internal(keys,accepted_entities=('artist','track','album'),forceTrack
#5
specialkeys = {}
if "remote" in keys: specialkeys["remote"] = keys["remote"]
#if "remote" in keys: specialkeys["remote"] = keys["remote"]
specialkeys["separate"] = (keys.get('separate','no').lower() == 'yes')
for k in keys:
if k not in ['separate']:
specialkeys[k] = keys[k]
return filterkeys, limitkeys, delimitkeys, amountkeys, specialkeys