from collections import defaultdict from typing import Any, Dict, List from server.client.clients.delphi_client import DelphiClient from server.constants.delphi.streams.group_by import StreamsGroupBy from server.constants.delphi.streams.include import StreamsInclude from server.constants.delphi.streams.subset import StreamsSubset from server.legacy.consumer_analytics.vendors import AppleMusicVendor, BaseVendor, SpotifyVendor from server.legacy.core import constants def get_track_playlists_streams(data: dict, market: str) -> dict: """Retrieve tracks playlists streams number for specific market and vendor. Arguments: data: Track streams count in playlists data. market: Market code value. Returns: Tracks streams number data for specific vendor and market. """ result = defaultdict(int) streams_field_name = "globalStreams" if market in constants.ALL_GLOBAL_MARKETS else "localStreams" for item in data.get("items", []): playlist_id = item.get("playlistId") or item.get("containerId") if not playlist_id: continue result[playlist_id] = item.get(streams_field_name, 0) return result async def get_track_first_stream_date( client: DelphiClient, isrc_list: List[str], vendor: str, wrap_response: bool = False ) -> dict: """Get first stream date for sony track by ISRC and vendor Args: client: Delphi client. isrc_list: a list of song ISRC. vendor: Vendor name. wrap_response: Wrap in dict with firstStreamDate node. Returns: firstStreamDate for each ISRC in JSON format. """ response = await client.get_first_stream_dates(isrc_list=isrc_list, dsp_list=[vendor]) result = {i["isrc"]: i["date"] for i in response} return {"firstStreamDate": result} if wrap_response else result def get_vendor(vendor: str) -> BaseVendor: """Get vendor utils. Args: vendor: Vendor name. Returns: Vendor utils object. """ if vendor == constants.SPOTIFY: return SpotifyVendor() elif vendor == constants.APPLE: return AppleMusicVendor() else: raise NotImplementedError() async def get_streams_per_country( client: DelphiClient, isrc: str, vendor: str, start_date: str, end_date: str, streams_only: bool = False, exclude_global: bool = True, include_owner_categories: bool = False, ) -> List[Dict[str, Any]]: """Get streams per country for each date from start to end date. Outdated version, use get_streams_per_country_bulk instead. Args: client: Delphi client. isrc: song ISRC. start_date: Start date in %Y-%m-%d format. end_date: End date in %Y-%m-%d format. vendor: Vendor one of [apple, spotify]. streams_only: Flag to load streams data only. exclude_global: Flag to exclude global (_gl) data from output. include_owner_categories: Include owner categories in date item. """ data = await client.get_streams( isrc_list=[isrc], dsp_list=[vendor], start_date=start_date, end_date=end_date, group_by=[StreamsGroupBy.DATE], include=None if streams_only else [StreamsInclude.SOURCES], ) if not data: return [] vendor = get_vendor(vendor) result = vendor.parse_isrc_map( [isrc], data, None, streams_only=streams_only, exclude_global=exclude_global, item_parser=vendor.parse_isrc_item_v0, include_owner_categories=include_owner_categories, ) if result: result = result[0].get("data") return result or [] async def get_streams_per_country_v1( client: DelphiClient, isrc_list: List[str], vendor: str, start_date: str, end_date: str, market_list: List[str] = None, streams_only: bool = False, combine_isrc: bool = False, exclude_global: bool = True, ) -> List[Dict[str, Any]]: """Get streams for market from start to end date for each isrc in list. Args: client: Delphi client. isrc_list: Song ISRC list. start_date: Start date in %Y-%m-%d format. end_date: End date in %Y-%m-%d format. market_list: Specific markets to get data for, use None or [] to get data for all markets. vendor: Vendor one of [apple, spotify]. streams_only: Flag to load streams data only. combine_isrc: Flag to combine data by market and date for all given isrc. exclude_global: Flag to exclude global (_gl) data from output. """ data = await client.get_streams( isrc_list=isrc_list, dsp_list=[vendor], country_code_list=market_list if market_list else None, start_date=start_date, end_date=end_date, group_by=[StreamsGroupBy.DATE], include=None if streams_only else [StreamsInclude.SOURCES], ) if not data: return [] vendor = get_vendor(vendor) return vendor.parse_isrc_map( isrc_list, data, market_list[0] if market_list and len(market_list) == 1 else None, streams_only=streams_only, exclude_global=exclude_global, combine_isrc=combine_isrc, item_parser=vendor.parse_isrc_item_v0, ) def get_default_playlist_market(vendor: str, market: str, global_market: str = constants.GLOBAL_MARKET) -> str: """Return default playlist market value if chosen one is Global. Arguments: vendor: Vendor - Apple or Spotify. market: Market str value. global_market: Global market code. Returns: str - market value. """ if market != global_market: return market return constants.US_MARKET if vendor == constants.APPLE else "" async def get_tracks_playlists_summary( client: DelphiClient, isrc: str or List[str], vendor: str, start_date: str, end_date: str, market: str, playlist_id: str or None, include_shuffle: bool = False, per_day: bool = False, include_summary: bool = False, all_markets: bool = False, add_isrc: bool = False, ) -> dict or list: """Get tracks playlists summary info in a specified date range. Arguments: client: Delphi client. isrc: song ISRC. start_date: Start date in %Y-%m-%d format. end_date: End date in %Y-%m-%d format. vendor: Vendor one of [apple, spotify]. market: Market code. playlist_id: Streams for this specific playlist only. include_shuffle: Include shuffle streams. per_day: Streams per day or summary for whole interval. include_summary: Include summary block for per day streams. all_markets: Flag to get data for all markets, considering passed selected market as local. add_isrc: Flag to add 'isrc' for each response item. """ country_code_list = None market = get_default_playlist_market(vendor, market, constants.DELPHI_GLOBAL_MARKET) if not all_markets: country_code_list = [constants.DELPHI_GLOBAL_MARKET] + ([market] if market else []) multiple_isrc = isinstance(isrc, list) data = await client.get_streams( isrc_list=isrc if multiple_isrc else [isrc], dsp_list=[vendor], country_code_list=country_code_list, start_date=start_date, end_date=end_date, group_by=[StreamsGroupBy.DATE], subset=StreamsSubset.PLAYLISTS, include=[StreamsInclude.SOURCES] if include_shuffle else None, playlist_id_list=[playlist_id] if playlist_id else None, ) vendor = get_vendor(vendor) if multiple_isrc: data_by_isrc = defaultdict(list) for item in data: data_by_isrc[item["isrc"]].append(item) results = [] for isrc, items in data_by_isrc.items(): results_item = vendor.parse_tracks_playlists_streams( items, market, include_shuffle, per_day, include_summary, all_markets=all_markets, add_isrc=add_isrc ) if include_summary: results_item["isrc"] = isrc results.append(results_item) return results results = vendor.parse_tracks_playlists_streams( data, market, include_shuffle, per_day, include_summary, all_markets=all_markets, add_isrc=add_isrc ) if include_summary: results["isrc"] = isrc results = [results] return results def get_apple_track_demographics_item(item: dict) -> dict: """Get apple track demographics CA like response item. Args: item: Delphi response item. Returns: CA response item. """ age_bands = item["apple_age_bands"] return { "date": item["date"], "countryCode": item["country_code"], "isrc": item["isrc"], "listeners_gender_male_age_18_24": age_bands["male_18_24"] or 0, "listeners_gender_female_age_25_34": age_bands["female_25_34"] or 0, "listeners_gender_male_age_25_34": age_bands["male_25_34"] or 0, "listeners_gender_female_age_35_44": age_bands["female_35_44"] or 0, "listeners_gender_male_age_35_44": age_bands["male_35_44"] or 0, "listeners_gender_male_age_45_54": age_bands["male_45_54"] or 0, "streams_gender_male_age_18_24": age_bands["male_18_24"] or 0, "streams_gender_female_age_25_34": age_bands["female_25_34"] or 0, "streams_gender_male_age_25_34": age_bands["male_25_34"] or 0, "streams_gender_female_age_35_44": age_bands["female_35_44"] or 0, "streams_gender_male_age_35_44": age_bands["male_35_44"] or 0, "streams_gender_male_age_45_54": age_bands["male_45_54"] or 0, "listeners": item["apple_streams_info"].get("listeners", 0), "streams": item.get("streams", 0), }