diff --git a/src/StatGenerator.py b/src/StatGenerator.py index d6ebc41..b6dcaee 100644 --- a/src/StatGenerator.py +++ b/src/StatGenerator.py @@ -3,11 +3,10 @@ from src.Library import * from pathlib import Path import json -out = { - "local_count": 0, - "resolved_count": 0, - "unresolved_tracks": [], -} + +def is_resolved(track: Track): + return track.local_path is not None + def get_mapping(mapping_path) -> dict[str, Any]: data = {} @@ -24,32 +23,192 @@ def get_mapping(mapping_path) -> dict[str, Any]: return out -def find_add_local_paths(mapping_path: Path, local_lib: Library, remote_lib: Library): - mapping = get_mapping(mapping_path) +class AlbumStat: + def __init__(self): + self.album: Album = Album() + self.score: int = 0 + self.resolved_precent: float = 0 - out["resolved_count"] = len(mapping) + def calc_resolved_precent(self): + self.resolved_precent = (sum(1 for track in self.album.tracks if is_resolved(track)) / len( + self.album.tracks)) * 100 - remote_id_map = {track.id: track for track in remote_lib.tracks} - local_id_map = {track.id: track for track in local_lib.tracks} - - for local, remote in mapping.items(): - remote_id_map[remote].local_path = local_id_map[local].local_path + def get_str(self): + return (f"{str(int(self.resolved_precent))} % - " + f"{self.album.title} - " + f"{str(self.album.artists[0].title) if len(self.album.artists) else " "}") -def generate_stats(local_lib: Library, remote_lib: Library, output_path : Path): +class ArtistStat: + def __init__(self): + self.artist: Artist = Artist() + self.score: int = 0 + self.resolved_precent: float = 0 + self.albums_stats: list[AlbumStat] = [] - out["local_count"] = len(local_lib.tracks) + def calc_resolved_precent(self): + if len(self.artist.tracks) == 0: + self.resolved_precent = 0 + return - for track in remote_lib.top_tracks: - if not track.local_path: - out["unresolved_tracks"].append( - f"{track.title} - {track.artists[0].title if len(track.artists) else ""} - {track.album.title}") + self.resolved_precent = (sum(1 for track in self.artist.tracks if is_resolved(track)) / len( + self.artist.tracks)) * 100 + + def get_str(self): + return (f"{str(int(self.resolved_precent))} % - " + f"{self.artist.title}") - with output_path.open("w", encoding="utf-8") as f: - json.dump(out, f, indent=2, ensure_ascii=False) +class StatGenerator: + def __init__(self): + self.out = { + "local_count": 0, + "resolved_count": 0, + + "popular_artists": [], + "popular_albums": [], + + "unresolved_albums": [], + "unresolved_tracks": [], + } + + self.max_popular_albums = 100 + self.max_popular_artists = 100 + + self.max_unresolved_tracks = 100 + self.max_unresolved_albums = 20 + self.resolved_percentage_threshold = 30 + + self.albums: List[AlbumStat] = [] + self.artists: List[ArtistStat] = [] + + self.albums_map: dict[str, AlbumStat] = {} + self.artists_map: dict[str, ArtistStat] = {} + + self.local_lib: Library = Library() + self.remote_lib: Library = Library() + + self.mapping: dict[str, Any] = Any + + self.remote_id_map: dict[str, Track] = {} + self.local_id_map: dict[str, Track] = {} + + def find_add_local_paths(self, mapping_path: Path): + self.mapping = get_mapping(mapping_path) + + self.remote_id_map = {track.id: track for track in self.remote_lib.tracks} + self.local_id_map = {track.id: track for track in self.local_lib.tracks} + + for local, remote in self.mapping.items(): + self.remote_id_map[remote].local_path = self.local_id_map[local].local_path + + def score_artists(self): + for artist in self.remote_lib.artists: + artist_stat = ArtistStat() + artist_stat.artist = artist + artist_stat.score = 0 + self.artists_map[artist_stat.artist.id] = artist_stat + self.artists.append(artist_stat) + + for idx, track in enumerate(self.remote_lib.top_tracks): + score = len(self.remote_lib.top_tracks) - idx + for artist in track.artists: + self.artists_map[artist.id].score += score + + for artist_stat in self.artists: + artist_stat.calc_resolved_precent() + + self.artists = sorted(self.artists, key=lambda item: item.score, reverse=True) + + def score_albums(self): + for album in self.remote_lib.albums: + stat = AlbumStat() + stat.album = album + self.albums_map[album.id] = stat + + for idx, track in enumerate(self.remote_lib.top_tracks): + score = len(self.remote_lib.top_tracks) - idx + self.albums_map[track.album.id].score += score + + for _, album in self.albums_map.items(): + album.calc_resolved_precent() + + self.albums = [item[1] for item in + sorted(self.albums_map.items(), key=lambda item: item[1].score, reverse=True)] + + for album_stat in self.albums: + for artist in album_stat.album.artists: + self.artists_map[artist.id].albums_stats.append(album_stat) + + def stat_generic(self): + self.out["resolved_count"] = len(self.mapping) + + def gen_album_songs(album_stat): + tracks = [] + + for track in album_stat.album.tracks: + tracks.append(f"{"✔" if is_resolved(track) else "𐄂"} {track.title} -> {track.local_path}") + + return { + "album" : album_stat.get_str(), + "tracks" : tracks + } + + for artist_stat in self.artists: + artist_log = { + "artist": artist_stat.get_str(), + "albums": [gen_album_songs(album) for album in artist_stat.albums_stats] + } + + if len(self.out["popular_artists"]) < self.max_popular_artists: + self.out["popular_artists"].append(artist_log) + + for album_data in self.albums: + log = album_data.get_str() + + if len(self.out["popular_albums"]) < self.max_popular_albums: + self.out["popular_albums"].append(log) + + def stat_unresolved_albums(self): + for album_data in self.albums: + if album_data.resolved_precent > self.resolved_percentage_threshold: + continue + + log = album_data.get_str() + + if len(self.out["unresolved_albums"]) < self.max_unresolved_albums: + self.out["unresolved_albums"].append(log) + + def stat_unresolved_tracks(self): + self.out["local_count"] = len(self.local_lib.tracks) + + for track in self.remote_lib.top_tracks: + if not track.local_path: + + log = f"{track.title} - {track.artists[0].title if len(track.artists) else ""} - {track.album.title}" + if len(self.out["unresolved_tracks"]) < self.max_unresolved_tracks: + self.out["unresolved_tracks"].append(log) + + def save_stats(self, output_path: Path): + with output_path.open("w", encoding="utf-8") as f: + json.dump(self.out, f, indent=2, ensure_ascii=False) + + def log_stats(self, mapping_path: Path, output_path: Path, local_lib: Library, remote_lib: Library): + self.remote_lib = remote_lib + self.local_lib = local_lib + + self.find_add_local_paths(mapping_path) + + self.score_artists() + self.score_albums() + + self.stat_generic() + self.stat_unresolved_tracks() + self.stat_unresolved_albums() + + self.save_stats(output_path) -def log_stats(mapping_path : Path, output_path : Path, local_lib: Library, remote_lib: Library): - find_add_local_paths(mapping_path, local_lib, remote_lib) - generate_stats(local_lib, remote_lib, output_path) +def log_stats(mapping_path: Path, output_path: Path, local_lib: Library, remote_lib: Library): + stat_gen = StatGenerator() + stat_gen.log_stats(mapping_path, output_path, local_lib, remote_lib)