import json from apollo_utils.service.exceptions import ServiceUnavailable from botocore.exceptions import ClientError from datetime import datetime from sentry_sdk import capture_exception from typing import List from server import config from server.client import services from server.constants import DSP from server.constants.distributors import DISTRIBUTORS from server.constants.users.visits import VISIT_DSP_MAPPING, VisitType from server.scenarios.distributors import get_tracks_distributors_map from server.scenarios.tracks.get_tracks import get_tracks from server.utils.boto import get_session from server.utils.tracks import get_dsp_tracks_response def send_user_visits(user_id: str, dsp: str, visit_type: str, instance_id: str) -> bool: """Add log entry of track page visit to SQS. Args: user_id: User ID. dsp: DSP name (Spotify or Apple). visit_type: Visit type (track/playlist/etc). instance_id: Object (track/playlist/etc) ID. Returns: Data processed correctly or not """ if not config.USERS_VISITS_MESSAGE_QUEUE_URL: raise ServiceUnavailable("Message queue URL is not set.") new_entry = { "user_id": user_id, "dsp_id": VISIT_DSP_MAPPING[dsp], "type_id": VisitType.ID_MAPPING[visit_type], "instance_id": instance_id, "created_at": datetime.utcnow(), } sqs_client = get_session().client("sqs") try: sqs_client.send_message( QueueUrl=config.USERS_VISITS_MESSAGE_QUEUE_URL, MessageBody=json.dumps(new_entry, default=str) ) except ClientError as ex: capture_exception(ex) return False return True async def set_tracks_data( dsp: str, country_code: str, track_list: List[dict], id_list: List[str], distributors: List[DISTRIBUTORS] = None ): """Set tracks data field. Args: dsp: DSP (Spotify/Apple). country_code: Country code. track_list: Visited track list. id_list: Track ID list. distributors: List of distributors to get labels for """ dsp = DSP(dsp) dsp_track_list = await get_tracks(track_ids=id_list, dsp=dsp, market=country_code, raw_results=True) track_id_to_distributor = {} if distributors and id_list: track_id_to_distributor = await get_tracks_distributors_map( country_code=country_code, track_id=id_list, distributors=distributors, dsp=dsp, track_list=dsp_track_list, remove_result_dsp_prefix=True, ) dsp_track_list = get_dsp_tracks_response(dsp=dsp, tracklist=dsp_track_list) dsp_track_mapping = {i["id"]: i for i in dsp_track_list} for item in track_list: instance_id = item["instance_id"] track_data = dsp_track_mapping.get(instance_id, {}) distributor = track_id_to_distributor.get(instance_id) artist_list = track_data.get("artists", []) item["data"] = { "song_id": instance_id, "is_sony": distributor == DISTRIBUTORS.SME.value, "distributed_by": distributor, "name": track_data.get("name"), "artists": artist_list, "artist_name": ", ".join(i["name"] for i in artist_list), "image_url": track_data.get("image"), } async def set_playlists_data(playlist_list: List[dict], id_list: List[str]): """Set playlists data field. Args: playlist_list: Visited playlist list. id_list: Playlist ID list. """ data_mapping = {i["playlist_id"]: i for i in (await services.apollo.get_playlists_spotify(id=id_list))} for item in playlist_list: instance_id = item["instance_id"] if instance_id in data_mapping: item["data"] = data_mapping[instance_id] async def get_users_visits( visit_type: str, dsp: str, country_code: str, limit: int, distributors: List[DISTRIBUTORS] = None ) -> list: """Get most visited objects of the user. Args: visit_type: Type (track/playlist/etc). dsp: DSP (Spotify/Apple). country_code: Country code to display data for. limit: Top count. distributors: List of distributors to get labels for Returns: Top visited objects. """ result = await services.apollo.get_users_visits(type=visit_type, dsp=dsp, limit=limit) if not result: return [] id_list = [i["instance_id"] for i in result] if visit_type == VisitType.TRACK: await set_tracks_data(dsp, country_code, result, id_list, distributors=distributors) elif visit_type == VisitType.PLAYLIST: await set_playlists_data(result, id_list) return result