import datetime import json from typing import Any, Dict, Iterable, Optional, Sequence, Tuple from apollo_notifications.constants import APPLE_CATEGORY_ID from apollo_notifications.playlists.utils import get_track_in_playlist_push_key from tests import factories from apollo_main_db.push_notifications.models import TopicEnum, VendorEnum NO_VALUE_KEY = "__NO_VALUE" def get_formatted_today_yesterday(): today = datetime.date.today() yesterday = today - datetime.timedelta(days=1) return today.strftime("%Y-%m-%d"), yesterday.strftime("%Y-%m-%d") def playlist_id(i): return f"playlist_id_{i}" def playlist_name(i): return f"playlist_name_{i}" def playlist_uri(i): return f"playlist_uri_{i}" def playlist_image_url(i): return f"playlist_image_url_{i}" def track_id(i): return i def track_name(i): return f"track_name_{i}" def track_isrc(i): return f"track_isrc_{i}" def buzz_user(i): return i def user_id(i): return f"user_id_{i}" def account_id(i): return f"account_id_{i}" def create_basic_structures( playlists_n: int, # creates batch of playlists from 1 to integer passed tracks_n: int, # creates batch of tracks from 1 to integer passed users_n: int, # creates batch of users from 1 to integer passed market_to_tracks: Dict[str, Iterable[int]], category_id_to_playlists_map: Dict[int, Iterable[int]] = None, # by default apple category is used blacklisted_playlists: Iterable[int] = None, last_top_playlists: Dict[str, Iterable[int]] = None, previous_top_playlists: Dict[str, Iterable[int]] = None, playlist_to_last_and_previous_tracks_map: Dict[ int, Tuple[Optional[Dict[str, Iterable[int]]], Optional[Dict[str, Iterable[int]]]] ] = None, starred_tracks: Iterable[int] = None, active_devices: int = 2, last_date: datetime.date = None, ): last_date = last_date or datetime.date.today() last_date_str = last_date.strftime("%Y-%m-%d") previous_date_str = (last_date - datetime.timedelta(days=1)).strftime("%Y-%m-%d") update_datetime_str = datetime.datetime.combine(last_date, datetime.time(hour=3)).strftime("%Y-%m-%d %H:%M:%S") # create tracks tracks_markets = {} for market, tracks_idx in market_to_tracks.items(): for track_idx in tracks_idx: tracks_markets[track_idx] = market tracks = [ factories.AppleSongFactory.create( id=track_id(i), name=track_name(i), isrc=track_isrc(i), storefront=tracks_markets[i], artist_name="artist" ) for i in range(1, tracks_n + 1) ] for track_idx in starred_tracks or []: for user_idx in range(1, users_n + 1): factories.StarredContentFactory.create( user_id=user_id(user_idx), isrc=tracks[track_idx - 1].isrc, uri=tracks[track_idx - 1].id ) # create categories category_id_to_curator_id_map = {} for i, category_id in enumerate(set((category_id_to_playlists_map or {}).keys()) | {APPLE_CATEGORY_ID}): factories.BuzzUserFactory.create(category_id=category_id, user_name=buzz_user(i)) category_id_to_curator_id_map[category_id] = buzz_user(i) # create playlists playlist_to_category_id_map = {} for category_id, playlist_idxs in (category_id_to_playlists_map or {}).items(): for pl_idx in playlist_idxs: playlist_to_category_id_map[pl_idx] = category_id playlists = [ factories.ApplePlaylistFactory.create( id=playlist_id(i), name=playlist_name(i), artwork=playlist_image_url(i), curator_id=category_id_to_curator_id_map[playlist_to_category_id_map.get(i, APPLE_CATEGORY_ID)], ) for i in range(1, playlists_n + 1) ] for pl_idx in blacklisted_playlists or []: factories.PlaylistBlacklistFactory.create(vendor="apple", playlist_id=playlists[pl_idx - 1].id) for market, pl_idxs in (last_top_playlists or {}).items(): for pl_idx in pl_idxs: factories.AppleWeeklyTopPlaylistFactory.create( playlist_id=playlists[pl_idx - 1].id, country_code=market, date=last_date_str ) for market, pl_idxs in (previous_top_playlists or {}).items(): for pl_idx in pl_idxs: factories.AppleWeeklyTopPlaylistFactory.create( playlist_id=playlists[pl_idx - 1].id, country_code=market, date=previous_date_str ) # create entries for pl_idx, tracks_data in (playlist_to_last_and_previous_tracks_map or {}).items(): last_date_market_to_tracks, previous_date_market_to_tracks = tracks_data for market, tracks_idx in (last_date_market_to_tracks or {}).items(): for track_idx in tracks_idx: factories.ApplePlaylistTracklistHistoryReduced2Factory.create( playlist_id=playlists[pl_idx - 1].id, song_id=tracks[track_idx - 1].id, date=last_date_str, storefront=market, ) for market, tracks_idx in (previous_date_market_to_tracks or {}).items(): for track_idx in tracks_idx: factories.ApplePlaylistTracklistHistoryReduced2Factory.create( playlist_id=playlists[pl_idx - 1].id, song_id=tracks[track_idx - 1].id, date=previous_date_str, storefront=market, ) for market in (last_date_market_to_tracks or {}).keys() | (previous_date_market_to_tracks or {}).keys(): factories.ApplePlaylistStatistics.create( playlist_id=playlists[pl_idx - 1].id, market=market, tracklist_updated_at=update_datetime_str ) # create user data for user_idx in range(1, users_n + 1): [ factories.UserDeviceFactory.create( token=f"inactive_{user_idx}{i}", user_id=user_id(user_idx), is_active=False ) for i in range(1, 3) ] [ factories.UserDeviceFactory.create(token=f"token_{user_idx}{i}", user_id=user_id(user_idx), is_active=True) for i in range(1, active_devices + 1) ] def create_push_message_data( playlist_idx: int, track_idx: int, user_idx: int, topic: str, country_code: str, title: str, date: str, artist: str = "artist", vendor: str = "apple", add_playlist_image: bool = False, ) -> Dict[str, Any]: _playlist_id, _playlist_name = playlist_id(playlist_idx), playlist_name(playlist_idx) _track_id, _track_isrc = track_id(track_idx), track_isrc(track_idx) data = { "id": str( get_track_in_playlist_push_key( date, _playlist_id, user_id(user_idx), topic, country_code, vendor, _track_isrc ) ), "artist_name": artist, "isrc": _track_isrc, "track_id": str(_track_id), "track_name": track_name(track_idx), "topic": topic, "target": _playlist_name, "vendor": vendor, "country_code": "_gl" if country_code == "global" else country_code, "change": None, "playlist_id": _playlist_id, "playlist_name": _playlist_name, "playlist_image_url": playlist_image_url(playlist_idx) if add_playlist_image else None, } return { "id": data["id"], "user_id": user_id(user_idx), "title": title, "track_id": str(_track_id), "topic": TopicEnum.__members__[topic.upper()], "vendor": VendorEnum.__members__[vendor.upper()], "date": datetime.datetime.fromisoformat(date), "data": data, } def verify_push( expected_data, push, json_fields=("data",), push_attr_getter=lambda p, attr: getattr(p, attr, NO_VALUE_KEY) ): for name, expected_value in expected_data.items(): value = push_attr_getter(push, name) assert value != NO_VALUE_KEY if name in json_fields: verify_push(expected_value, json.loads(value), push_attr_getter=lambda p, attr: p.get(attr, NO_VALUE_KEY)) else: assert value == expected_value def verify_push_messages(expected_push_id_to_data_map: Dict[str, Dict[str, Any]], push_messages: Sequence): assert len(expected_push_id_to_data_map) == len(push_messages) for push in push_messages: expected_push = expected_push_id_to_data_map.get(str(push.id)) assert expected_push is not None verify_push(expected_push, push)