from collections import defaultdict from dataclasses import dataclass, asdict from datetime import date, timedelta from typing import List from faker import Faker from server.constants import DSP, SPOTIFY_PLAYLIST_IMAGE_URL_MASK from server.utils.playlists.v0.common import sum_periods from tests.helpers import BaseFieldFabricator, FieldFabricator fake = Faker() def get_random_list_int_track_ids(length: int = 1): def get_random_track_id(): return str(fake.random_int(1000000000, 9999999999)) response = [] for _ in range(length): response.append(get_random_track_id()) return response @dataclass class Track: id: str isrc: str name: str artists_name: str artwork_url: str class PlaylistsApplePreviousTopResponseFabricator(BaseFieldFabricator, FieldFabricator): def _get_track(self, track_id: str, i: int = 0): return asdict(Track( id=track_id, isrc=self.get_string_field("isrc", i), name=fake.text(20), artists_name=fake.name(), artwork_url=fake.url() )) def get_tracks(self, tracks: List[int]): response = [] for i, track_id in enumerate(tracks): response.append(self._get_track(track_id, i)) return response @staticmethod def _get_fake_date(start_date: date = None, end_date: date = None, days_delta: int = 30): today = end_date or date.today() delta = start_date or (today - timedelta(days=days_delta)) return (fake.date_between_dates(delta, today)).isoformat() def get_tracks_first_stream_dates(self, isrc: List[str]): return {i: self._get_fake_date() for i in isrc} @staticmethod def get_fake_latest_date(dsp: str): return [{"dsp": dsp, "updated_date": date.today() - timedelta(days=1)}] @staticmethod def get_fake_playlists(length: int = 100): def get_fake_playlist(i: int = 0): return f"pl.{fake.random_int(10, 99)}{fake.lexify()}" return [{"playlist_id": get_fake_playlist(i), "name": fake.text(20)} for i in range(length)] def get_fake_previous_top_dates(self, playlist_ids: list[str], isrc: list[str]): len_isrc = len(isrc) - 1 response = [] pl_ids = [] for playlist in playlist_ids: if fake.boolean(15): entry_date = self._get_fake_date() exit_date = self._get_fake_date(date.fromisoformat(entry_date)) response.append( { "entry_date": entry_date, "exit_date": exit_date, "playlist_id": playlist, "isrc": isrc[fake.random_int(max=len_isrc)] } ) pl_ids.append(playlist) return response, pl_ids @staticmethod def get_fake_playlist_streams_weekly(playlist_ids: list[str]): return [{"streams": fake.random_int(min=10000, max=100000), "playlist_id": pl} for pl in playlist_ids] @staticmethod def get_fake_images(playlist_ids): return [ {"id": pl, "image_url": fake.image_url()} for pl in playlist_ids ] @staticmethod def get_spotify_playlist_images(playlist_ids): return [ {"id": p_id, "image_url": SPOTIFY_PLAYLIST_IMAGE_URL_MASK.format(playlist_id=p_id)} for p_id in playlist_ids ] @staticmethod def _get_playlists_previous_top_response( dsp, history_mapping, playlists_mapping, tracks_mapping, playlist_id_to_image_url_map, track_first_dates, limit ): result = [] for playlist_id, playlist_tracks in history_mapping.items(): playlist = { "id": playlist_id, "name": playlists_mapping[playlist_id]["name"], "streams": int(playlists_mapping[playlist_id]["streams_latest"]), "type": dsp.value, "image_url": playlist_id_to_image_url_map.get(playlist_id, ""), "tracks": [], } result.append(playlist) for track_id, history_dates in playlist_tracks.items(): track_data = tracks_mapping[str(track_id)] track = { "id": int(track_id) if dsp == DSP.APPLE else track_id, "name": track_data["name"], "isrc": track_data["isrc"], "first_date": track_first_dates[track_data["isrc"]] if track_data["isrc"] in track_first_dates else False, "periods": sum_periods(history_dates), } playlist["tracks"].append(track) result = sorted(result, key=lambda x: (-x["streams"])) if limit: result = result[:limit] return {"playlists": result} def get_fake_response_to_check( self, dsp, tracks_mapped, track_first_dates, playlists_top_weekly, history_data, streams_data, playlist_images_data, limit=None ): isrc_to_track_id = {track["isrc"]: track["id"] for track in tracks_mapped.values()} playlists_mapping = {p["playlist_id"]: {"name": p["name"], "streams_latest": 0} for p in playlists_top_weekly} history_mapping = defaultdict(lambda: defaultdict(list)) for item in history_data: history_mapping[item["playlist_id"]][isrc_to_track_id[item["isrc"]]].append( [item["entry_date"], item["exit_date"]] ) for item in streams_data: playlists_mapping[item["playlist_id"]]["streams_latest"] = item["streams"] playlist_images_dict = {data["id"]: data.get("image_url") for data in playlist_images_data} result = self._get_playlists_previous_top_response( dsp=DSP(dsp), history_mapping=history_mapping, playlists_mapping=playlists_mapping, tracks_mapping=tracks_mapped, playlist_id_to_image_url_map=playlist_images_dict, track_first_dates=track_first_dates, limit=limit, ) return result