From b9e3cd76245dd8f4e4fb85af7140984bfd3d9340 Mon Sep 17 00:00:00 2001 From: krateng Date: Wed, 18 Oct 2023 10:58:46 +0200 Subject: [PATCH] Added UI selector for including associated artists --- dev/releases/3.2.yml | 2 + maloja/database/__init__.py | 22 ++++--- maloja/database/sqldb.py | 58 +++++++++++++------ maloja/jinjaenv/context.py | 4 ++ maloja/malojauri.py | 9 ++- .../jinja/snippets/filterdescription.jinja | 2 +- maloja/web/jinja/snippets/timeselection.jinja | 14 +++++ 7 files changed, 82 insertions(+), 29 deletions(-) diff --git a/dev/releases/3.2.yml b/dev/releases/3.2.yml index 46926d8..f416adc 100644 --- a/dev/releases/3.2.yml +++ b/dev/releases/3.2.yml @@ -6,6 +6,8 @@ minor_release_name: "Nicole" - "[Feature] Added basic support for albums" - "[Feature] New start page" - "[Feature] Added UI for track-artist, track-album and album-artist association" + - "[Feature] Added inline UI for association and merging in chart lists" + - "[Feature] Added UI selector for including associated artists" - "[Performance] Improved image rendering" - "[Bugfix] Fixed configuration of time format" - "[Bugfix] Fixed search on manual scrobble page" diff --git a/maloja/database/__init__.py b/maloja/database/__init__.py index b2cd5c0..f878373 100644 --- a/maloja/database/__init__.py +++ b/maloja/database/__init__.py @@ -309,8 +309,9 @@ def associate_tracks_to_album(target_id,source_ids): @waitfordb def get_scrobbles(dbconn=None,**keys): (since,to) = keys.get('timerange').timestamps() + associated = keys.get('associated',False) if 'artist' in keys: - result = sqldb.get_scrobbles_of_artist(artist=keys['artist'],since=since,to=to,dbconn=dbconn) + result = sqldb.get_scrobbles_of_artist(artist=keys['artist'],since=since,to=to,associated=associated,dbconn=dbconn) elif 'track' in keys: result = sqldb.get_scrobbles_of_track(track=keys['track'],since=since,to=to,dbconn=dbconn) elif 'album' in keys: @@ -324,8 +325,9 @@ def get_scrobbles(dbconn=None,**keys): @waitfordb def get_scrobbles_num(dbconn=None,**keys): (since,to) = keys.get('timerange').timestamps() + associated = keys.get('associated',False) if 'artist' in keys: - result = len(sqldb.get_scrobbles_of_artist(artist=keys['artist'],since=since,to=to,resolve_references=False,dbconn=dbconn)) + result = len(sqldb.get_scrobbles_of_artist(artist=keys['artist'],since=since,to=to,associated=associated,resolve_references=False,dbconn=dbconn)) elif 'track' in keys: result = len(sqldb.get_scrobbles_of_track(track=keys['track'],since=since,to=to,resolve_references=False,dbconn=dbconn)) elif 'album' in keys: @@ -370,14 +372,15 @@ def get_tracks_without_album(dbconn=None,resolve_ids=True): @waitfordb def get_charts_artists(dbconn=None,resolve_ids=True,**keys): (since,to) = keys.get('timerange').timestamps() - result = sqldb.count_scrobbles_by_artist(since=since,to=to,resolve_ids=resolve_ids,dbconn=dbconn) + associated = keys.get('associated',True) + result = sqldb.count_scrobbles_by_artist(since=since,to=to,resolve_ids=resolve_ids,associated=associated,dbconn=dbconn) return result @waitfordb def get_charts_tracks(dbconn=None,resolve_ids=True,**keys): (since,to) = keys.get('timerange').timestamps() if 'artist' in keys: - result = sqldb.count_scrobbles_by_track_of_artist(since=since,to=to,artist=keys['artist'],resolve_ids=resolve_ids,dbconn=dbconn) + result = sqldb.count_scrobbles_by_track_of_artist(since=since,to=to,artist=keys['artist'],associated=keys.get('associated',False),resolve_ids=resolve_ids,dbconn=dbconn) elif 'album' in keys: result = sqldb.count_scrobbles_by_track_of_album(since=since,to=to,album=keys['album'],resolve_ids=resolve_ids,dbconn=dbconn) else: @@ -388,7 +391,7 @@ def get_charts_tracks(dbconn=None,resolve_ids=True,**keys): def get_charts_albums(dbconn=None,resolve_ids=True,**keys): (since,to) = keys.get('timerange').timestamps() if 'artist' in keys: - result = sqldb.count_scrobbles_by_album_of_artist(since=since,to=to,artist=keys['artist'],resolve_ids=resolve_ids,dbconn=dbconn) + result = sqldb.count_scrobbles_by_album_of_artist(since=since,to=to,artist=keys['artist'],associated=keys.get('associated',False),resolve_ids=resolve_ids,dbconn=dbconn) else: result = sqldb.count_scrobbles_by_album(since=since,to=to,resolve_ids=resolve_ids,dbconn=dbconn) return result @@ -629,15 +632,16 @@ def get_featured(dbconn=None): alltime() ] funcs = { - "artist": get_charts_artists, - "album": get_charts_albums, - "track": get_charts_tracks + "artist": (get_charts_artists,{'associated':False}), + "album": (get_charts_albums,{}), + "track": (get_charts_tracks,{}) } result = {t:None for t in funcs} for entity_type in funcs: for r in ranges: - chart = funcs[entity_type](timerange=r) + func,kwargs = funcs[entity_type] + chart = func(timerange=r,**kwargs) if chart: result[entity_type] = chart[0][entity_type] break diff --git a/maloja/database/sqldb.py b/maloja/database/sqldb.py index 508fc4c..c22ca39 100644 --- a/maloja/database/sqldb.py +++ b/maloja/database/sqldb.py @@ -763,19 +763,23 @@ def merge_albums(target_id,source_ids,dbconn=None): @cached_wrapper @connection_provider -def get_scrobbles_of_artist(artist,since=None,to=None,resolve_references=True,dbconn=None): +def get_scrobbles_of_artist(artist,since=None,to=None,resolve_references=True,associated=False,dbconn=None): if since is None: since=0 if to is None: to=now() - artist_id = get_artist_id(artist,dbconn=dbconn) + if associated: + artist_ids = get_associated_artists(artist,resolve_ids=False,dbconn=dbconn) + [get_artist_id(artist,dbconn=dbconn)] + else: + artist_ids = [get_artist_id(artist,dbconn=dbconn)] + jointable = sql.join(DB['scrobbles'],DB['trackartists'],DB['scrobbles'].c.track_id == DB['trackartists'].c.track_id) op = jointable.select().where( DB['scrobbles'].c.timestamp<=to, DB['scrobbles'].c.timestamp>=since, - DB['trackartists'].c.artist_id==artist_id + DB['trackartists'].c.artist_id.in_(artist_ids) ).order_by(sql.asc('timestamp')) result = dbconn.execute(op).all() @@ -911,7 +915,7 @@ def get_tracks(dbconn=None): @cached_wrapper @connection_provider -def count_scrobbles_by_artist(since,to,resolve_ids=True,dbconn=None): +def count_scrobbles_by_artist(since,to,associated=True,resolve_ids=True,dbconn=None): jointable = sql.join( DB['scrobbles'], DB['trackartists'], @@ -924,18 +928,24 @@ def count_scrobbles_by_artist(since,to,resolve_ids=True,dbconn=None): DB['trackartists'].c.artist_id == DB['associated_artists'].c.source_artist, isouter=True ) + + if associated: + artistselect = sql.func.coalesce(DB['associated_artists'].c.target_artist,DB['trackartists'].c.artist_id) + else: + artistselect = DB['trackartists'].c.artist_id + op = sql.select( sql.func.count(sql.func.distinct(DB['scrobbles'].c.timestamp)).label('count'), # only count distinct scrobbles - because of artist replacement, we could end up # with two artists of the same scrobble counting it twice for the same artist # e.g. Irene and Seulgi adding two scrobbles to Red Velvet for one real scrobble - sql.func.coalesce(DB['associated_artists'].c.target_artist,DB['trackartists'].c.artist_id).label('artist_id') + artistselect.label('artist_id') # use the replaced artist as artist to count if it exists, otherwise original one ).select_from(jointable2).where( DB['scrobbles'].c.timestamp<=to, DB['scrobbles'].c.timestamp>=since ).group_by( - sql.func.coalesce(DB['associated_artists'].c.target_artist,DB['trackartists'].c.artist_id) + artistselect ).order_by(sql.desc('count')) result = dbconn.execute(op).all() @@ -1001,9 +1011,12 @@ def count_scrobbles_by_album(since,to,resolve_ids=True,dbconn=None): @connection_provider # this ranks the albums of that artist, not albums the artist appears on - even scrobbles # of tracks the artist is not part of! -def count_scrobbles_by_album_of_artist(since,to,artist,resolve_ids=True,dbconn=None): +def count_scrobbles_by_album_of_artist(since,to,artist,associated=False,resolve_ids=True,dbconn=None): - artist_id = get_artist_id(artist,dbconn=dbconn) + if associated: + artist_ids = get_associated_artists(artist,resolve_ids=False,dbconn=dbconn) + [get_artist_id(artist,dbconn=dbconn)] + else: + artist_ids = [get_artist_id(artist,dbconn=dbconn)] jointable = sql.join( DB['scrobbles'], @@ -1022,7 +1035,7 @@ def count_scrobbles_by_album_of_artist(since,to,artist,resolve_ids=True,dbconn=N ).select_from(jointable2).where( DB['scrobbles'].c.timestamp<=to, DB['scrobbles'].c.timestamp>=since, - DB['albumartists'].c.artist_id == artist_id + DB['albumartists'].c.artist_id.in_(artist_ids) ).group_by(DB['tracks'].c.album_id).order_by(sql.desc('count')) result = dbconn.execute(op).all() @@ -1038,9 +1051,12 @@ def count_scrobbles_by_album_of_artist(since,to,artist,resolve_ids=True,dbconn=N @connection_provider # this ranks the tracks of that artist by the album they appear on - even when the album # is not the artist's -def count_scrobbles_of_artist_by_album(since,to,artist,resolve_ids=True,dbconn=None): +def count_scrobbles_of_artist_by_album(since,to,artist,associated=False,resolve_ids=True,dbconn=None): - artist_id = get_artist_id(artist,dbconn=dbconn) + if associated: + artist_ids = get_associated_artists(artist,resolve_ids=False,dbconn=dbconn) + [get_artist_id(artist,dbconn=dbconn)] + else: + artist_ids = [get_artist_id(artist,dbconn=dbconn)] jointable = sql.join( DB['scrobbles'], @@ -1059,7 +1075,7 @@ def count_scrobbles_of_artist_by_album(since,to,artist,resolve_ids=True,dbconn=N ).select_from(jointable2).where( DB['scrobbles'].c.timestamp<=to, DB['scrobbles'].c.timestamp>=since, - DB['trackartists'].c.artist_id == artist_id + DB['trackartists'].c.artist_id.in_(artist_ids) ).group_by(DB['tracks'].c.album_id).order_by(sql.desc('count')) result = dbconn.execute(op).all() @@ -1074,9 +1090,12 @@ def count_scrobbles_of_artist_by_album(since,to,artist,resolve_ids=True,dbconn=N @cached_wrapper @connection_provider -def count_scrobbles_by_track_of_artist(since,to,artist,resolve_ids=True,dbconn=None): +def count_scrobbles_by_track_of_artist(since,to,artist,associated=False,resolve_ids=True,dbconn=None): - artist_id = get_artist_id(artist,dbconn=dbconn) + if associated: + artist_ids = get_associated_artists(artist,resolve_ids=False,dbconn=dbconn) + [get_artist_id(artist,dbconn=dbconn)] + else: + artist_ids = [get_artist_id(artist,dbconn=dbconn)] jointable = sql.join( DB['scrobbles'], @@ -1090,7 +1109,7 @@ def count_scrobbles_by_track_of_artist(since,to,artist,resolve_ids=True,dbconn=N ).select_from(jointable).filter( DB['scrobbles'].c.timestamp<=to, DB['scrobbles'].c.timestamp>=since, - DB['trackartists'].c.artist_id==artist_id + DB['trackartists'].c.artist_id.in_(artist_ids) ).group_by(DB['scrobbles'].c.track_id).order_by(sql.desc('count')) result = dbconn.execute(op).all() @@ -1301,7 +1320,7 @@ def get_albums_map(album_ids,dbconn=None): @cached_wrapper @connection_provider -def get_associated_artists(*artists,dbconn=None): +def get_associated_artists(*artists,resolve_ids=True,dbconn=None): artist_ids = [get_artist_id(a,dbconn=dbconn) for a in artists] jointable = sql.join( @@ -1319,8 +1338,11 @@ def get_associated_artists(*artists,dbconn=None): ) result = dbconn.execute(op).all() - artists = artists_db_to_dict(result,dbconn=dbconn) - return artists + if resolve_ids: + artists = artists_db_to_dict(result,dbconn=dbconn) + return artists + else: + return [a.id for a in result] @cached_wrapper @connection_provider diff --git a/maloja/jinjaenv/context.py b/maloja/jinjaenv/context.py index 908634a..25aa039 100644 --- a/maloja/jinjaenv/context.py +++ b/maloja/jinjaenv/context.py @@ -72,6 +72,10 @@ def update_jinja_environment(): {"identifier":"longtrailing","replacekeys":{"trail":3},"localisation":"Long Trailing"}, {"identifier":"inert","replacekeys":{"trail":10},"localisation":"Inert","heavy":True}, {"identifier":"cumulative","replacekeys":{"trail":math.inf},"localisation":"Cumulative","heavy":True} + ], + "xassociated": [ + {"identifier":"include_associated","replacekeys":{"associated":True},"localisation":"Associated"}, + {"identifier":"exclude_associated","replacekeys":{"associated":False},"localisation":"Exclusive"} ] } diff --git a/maloja/malojauri.py b/maloja/malojauri.py index 1ae4364..d996f36 100644 --- a/maloja/malojauri.py +++ b/maloja/malojauri.py @@ -31,12 +31,18 @@ def uri_to_internal(keys,forceTrack=False,forceArtist=False,forceAlbum=False,api filterkeys = {"track":{"artists":keys.getall("artist"),"title":keys.get("title")}} elif type == "artist": filterkeys = {"artist":keys.get("artist")} - if "associated" in keys: filterkeys["associated"] = True + filterkeys["associated"] = (keys.get('associated','no').lower() == 'yes') + # associated is only used for filtering by artist, to indicate that we include associated artists + # for actual artist charts, to show that we want to count them, use 'unified' elif type == "album": filterkeys = {"album":{"artists":keys.getall("artist"),"albumtitle":keys.get("albumtitle") or keys.get("title")}} else: filterkeys = {} + # this can be the case regardless of actual entity filter + # e.g if i get scrobbles of an artist associated tells me i want all scrobbles by associated artists + # but seeing the artist charts (wich have no filterkeys) also is affected by this + # 2 limitkeys = {} since,to,within = None,None,None @@ -105,6 +111,7 @@ def internal_to_uri(keys): urikeys.append("artist",a) urikeys.append("albumtitle",keys["album"]["albumtitle"]) + #time if "timerange" in keys: keydict = keys["timerange"].urikeys() diff --git a/maloja/web/jinja/snippets/filterdescription.jinja b/maloja/web/jinja/snippets/filterdescription.jinja index 5652970..444f9cf 100644 --- a/maloja/web/jinja/snippets/filterdescription.jinja +++ b/maloja/web/jinja/snippets/filterdescription.jinja @@ -3,7 +3,7 @@ {% macro desc(filterkeys,limitkeys,prefix="by") %} {% if filterkeys.get('artist') is not none %} - {{ prefix }} {{ links.link(filterkeys.get('artist')) }} + {{ prefix }} {{ links.link(filterkeys.get('artist')) }}{% if filterkeys.get('associated') %} (and associated artists){% endif %} {% elif filterkeys.get('track') is not none %} of {{ links.link(filterkeys.get('track')) }} by {{ links.links(filterkeys["track"]["artists"]) }} diff --git a/maloja/web/jinja/snippets/timeselection.jinja b/maloja/web/jinja/snippets/timeselection.jinja index 99f1ef5..fe8b1d6 100644 --- a/maloja/web/jinja/snippets/timeselection.jinja +++ b/maloja/web/jinja/snippets/timeselection.jinja @@ -61,3 +61,17 @@ {% endif %} + + {% if 'artist' in filterkeys %} +
+ {% for o in xassociated %} + {% if o.replacekeys | map('compare_key_in_dicts',o.replacekeys,allkeys) | alltrue %} + {{ o.localisation }} + {% else %} + {{ o.localisation }} + {% endif %} + {{ "|" if not loop.last }} + {% endfor %} + +
+ {% endif %}