From 1aee2feb16819c4527d9c46f838bf39411f8eb5d Mon Sep 17 00:00:00 2001 From: aj Date: Mon, 3 Feb 2020 09:44:33 +0000 Subject: [PATCH] added tags and tag update --- main.py | 21 +++++++ music/db/database.py | 96 ++++++++++++++++++++++++++++ music/model/tag.py | 128 ++++++++++++++++++++++++++++++++++++++ music/tasks/update_tag.py | 72 +++++++++++++++++++++ 4 files changed, 317 insertions(+) create mode 100644 music/model/tag.py create mode 100644 music/tasks/update_tag.py diff --git a/main.py b/main.py index b282268..9b349e8 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,27 @@ from music import app +from music.tasks.update_tag import update_tag as do_update_tag app = app + +def update_tag(event, context): + import base64 + import logging + import json + + logger = logging.getLogger('music') + + if 'data' in event: + body = json.loads(base64.b64decode(event['data']).decode('utf-8')) + + if 'username' not in body or 'tag_id' not in body: + logger.error('malformed body') + return + + do_update_tag(username=body["username"], tag_id=body["tag_id"]) + else: + logger.error('no data in event') + + if __name__ == '__main__': app.run(host='127.0.0.1', port=8080, debug=True) diff --git a/music/db/database.py b/music/db/database.py index 9338553..cf6dc93 100644 --- a/music/db/database.py +++ b/music/db/database.py @@ -9,6 +9,7 @@ from fmframework.net.network import Network as FmNetwork from music.db.user import DatabaseUser from music.model.user import User from music.model.playlist import Playlist, RecentsPlaylist, LastFMChartPlaylist, Sort +from music.model.tag import Tag db = firestore.Client() @@ -324,3 +325,98 @@ def delete_playlist(username: str, name: str) -> None: playlist.db_ref.delete() else: logger.error(f'playlist {name} not found for {username}') + + +def get_user_tags(username: str) -> List[Tag]: + logger.info(f'getting tags for {username}') + + user = get_user(username) + + if user: + tag_refs = [i for i in user.db_ref.collection(u'tags').stream()] + + return [parse_tag_reference(username=username, tag_snapshot=i) for i in tag_refs] + else: + logger.error(f'user {username} not found') + + +def get_tag(username: str = None, tag_id: str = None) -> Optional[Tag]: + logger.info(f'retrieving {tag_id} for {username}') + + user = get_user(username) + + if user: + + tags = [i for i in user.db_ref.collection(u'tags').where(u'tag_id', u'==', tag_id).stream()] + + if len(tags) == 0: + logger.error(f'tag {tag_id} for {user} not found') + return None + if len(tags) > 1: + logger.critical(f"multiple {tag_id}'s for {user} found") + return None + + return parse_tag_reference(username=username, tag_snapshot=tags[0]) + else: + logger.error(f'user {username} not found') + + +def parse_tag_reference(username, tag_ref=None, tag_snapshot=None) -> Tag: + if tag_ref is None and tag_snapshot is None: + raise ValueError('no tag object supplied') + + if tag_ref is None: + tag_ref = tag_snapshot.reference + + if tag_snapshot is None: + tag_snapshot = tag_ref.get() + + tag_dict = tag_snapshot.to_dict() + + return Tag(tag_id=tag_dict['tag_id'], + name=tag_dict.get('name', 'n/a'), + username=username, + + db_ref=tag_ref, + + tracks=tag_dict.get('tracks', []), + albums=tag_dict.get('albums', []), + artists=tag_dict.get('artists', []), + + count=tag_dict.get('count', 0), + proportion=tag_dict.get('proportion', 0.0), + total_user_scrobbles=tag_dict.get('total_user_scrobbles', 0), + + last_updated=tag_dict.get('last_updated')) + + +def update_tag(username: str, tag_id: str, updates: dict) -> None: + if len(updates) > 0: + logger.debug(f'updating {tag_id} for {username}') + + user = get_user(username) + + tags = [i for i in user.db_ref.collection(u'tags').where(u'tag_id', u'==', tag_id).stream()] + + if len(tags) == 0: + logger.error(f'tag {tag_id} for {username} not found') + return None + if len(tags) > 1: + logger.critical(f"multiple {tag_id}'s for {username} found") + return None + + tag = tags[0].reference + tag.update(updates) + else: + logger.debug(f'nothing to update for {tag_id} for {username}') + + +def delete_tag(username: str, tag_id: str) -> None: + logger.info(f'deleting {tag_id} for {username}') + + tag = get_tag(username=username, tag_id=tag_id) + + if tag: + tag.db_ref.delete() + else: + logger.error(f'playlist {tag_id} not found for {username}') diff --git a/music/model/tag.py b/music/model/tag.py new file mode 100644 index 0000000..2f24211 --- /dev/null +++ b/music/model/tag.py @@ -0,0 +1,128 @@ +from datetime import datetime +import music.db.database as db + + +class Tag: + + def __init__(self, + tag_id: str, + name: str, + username: str, + + db_ref, + + tracks, + albums, + artists, + + count: int, + proportion: float, + total_user_scrobbles: int, + + last_updated: datetime): + self.tag_id = tag_id + self._name = name + self.username = username + + self.db_ref = db_ref + + self._tracks = tracks + self._albums = albums + self._artists = artists + + self._count = count + self._proportion = proportion + self._total_user_scrobbles = total_user_scrobbles + + self._last_updated = last_updated + + def to_dict(self): + return { + 'tag_id': self.tag_id, + 'name': self.name, + 'username': self.username, + + 'tracks': self.tracks, + 'albums': self.albums, + 'artists': self.artists, + + 'count': self.count, + 'proportion': self.proportion, + + 'last_updated': self.last_updated + } + + def update_database(self, updates): + db.update_tag(username=self.username, tag_id=self.tag_id, updates=updates) + + @property + def name(self): + return self._name + + @name.setter + def name(self, value): + db.update_tag(username=self.username, tag_id=self.tag_id, updates={'name': value}) + self._name = value + + @property + def tracks(self): + return self._tracks + + @tracks.setter + def tracks(self, value): + db.update_tag(username=self.username, tag_id=self.tag_id, updates={'tracks': value}) + self._tracks = value + + @property + def albums(self): + return self._albums + + @albums.setter + def albums(self, value): + db.update_tag(username=self.username, tag_id=self.tag_id, updates={'albums': value}) + self._albums = value + + @property + def artists(self): + return self._artists + + @artists.setter + def artists(self, value): + db.update_tag(username=self.username, tag_id=self.tag_id, updates={'artists': value}) + self._artists = value + + @property + def count(self): + return self._count + + @count.setter + def count(self, value): + db.update_tag(username=self.username, tag_id=self.tag_id, updates={'count': value}) + self._count = value + + @property + def proportion(self): + return self._proportion + + @proportion.setter + def proportion(self, value): + db.update_tag(username=self.username, tag_id=self.tag_id, updates={'proportion': value}) + self._proportion = value + + @property + def total_user_scrobbles(self): + return self._total_user_scrobbles + + @total_user_scrobbles.setter + def total_user_scrobbles(self, value): + db.update_tag(username=self.username, tag_id=self.tag_id, updates={'total_user_scrobbles': value}) + self._total_user_scrobbles = value + + @property + def last_updated(self): + return self._last_updated + + @last_updated.setter + def last_updated(self, value): + db.update_tag(username=self.username, tag_id=self.tag_id, updates={'last_updated': value}) + self._last_updated = value diff --git a/music/tasks/update_tag.py b/music/tasks/update_tag.py new file mode 100644 index 0000000..d1e5fe1 --- /dev/null +++ b/music/tasks/update_tag.py @@ -0,0 +1,72 @@ +import logging +from datetime import datetime + +import music.db.database as database + +logger = logging.getLogger(__name__) + + +def update_tag(username, tag_id): + logger.info(f'updating {username} / {tag_id}') + + tag = database.get_tag(username=username, tag_id=tag_id) + + if tag is None: + logger.error(f'{tag_id} for {username} not found') + return + + user = database.get_user(username) + + if user.lastfm_username is None or len(user.lastfm_username) == 0: + logger.error(f'{username} has no last.fm username') + return + + net = database.get_authed_lastfm_network(username=username) + + tag_count = 0 + user_scrobbles = net.get_user_scrobble_count() + + artists = [] + for artist in tag.artists: + net_artist = net.get_artist(name=artist['name']) + + if net_artist is not None: + artist['count'] = net_artist.user_scrobbles + tag_count += net_artist.user_scrobbles + + artists.append(artist) + + albums = [] + for album in tag.albums: + net_album = net.get_album(name=album['name'], artist=album['artist']) + + if net_album is not None: + album['count'] = net_album.user_scrobbles + + if album['artist'].lower() not in [i.lower() for i in [j['name'] for j in artists]]: + tag_count += net_album.user_scrobbles + + albums.append(album) + + tracks = [] + for track in tag.tracks: + net_track = net.get_track(name=track['name'], artist=track['artist']) + + if net_track is not None: + track['count'] = net_track.user_scrobbles + + if track['artist'].lower() not in [i.lower() for i in [j['name'] for j in artists]]: + tag_count += net_track.user_scrobbles + + tracks.append(track) + + tag.update_database({ + 'tracks': tracks, + 'albums': albums, + 'artists': artists, + + 'total_user_scrobbles': user_scrobbles, + 'count': tag_count, + 'proportion': (tag_count / user_scrobbles) * 100, + 'last_updated': datetime.utcnow() + })