import datetime import random from typing import List, Any, Tuple from constants.apple_album import APPLE_ALBUMS_LIST from constants.apple_market import APPLE_MARKETS_LIST from constants.apple_playlist import APPLE_PLAYLIST_LIST from constants.apple_search import APPLE_SEARCH_ITEM_TYPES_LIST from constants.apple_track import APPLE_TRACK_LIST from constants.buzzcategories import APPLE_CATEGORIES, SPOTIFY_CATEGORIES from constants.charts import CHARTS_FIELDS, CHARTS_ORDER_WITH_MOVES, CHARTS_ORDER_NO_MOVES, STARRED_FIELD, \ STARRED_ORDER, SONY_FIELD from constants.isrc import ISRC_LIST from constants.list_type import LIST_TYPE from constants.market import MARKET_LIST from constants.period import PERIOD_LIST from constants.search import SEARCH_LIST, SEARCH_QUERY_LIST from constants.spotify_album import SPOTIFY_ALBUM_LIST from constants.spotify_album_field import SPOTIFY_ALBUM_FIELDS from constants.spotify_artist import SPOTIFY_ARTISTS_LIST from constants.spotify_market import SPOTIFY_MARKETS_LIST from constants.spotify_playlist import SPOTIFY_PLAYLIST_LIST, SPOTIFY_POPULAR_PLAYLIST_LIST from constants.spotify_playlist_fields import SPOTIFY_PLAYLIST_FIELDS_LIST from constants.spotify_search import SPOTIFY_SEARCH_ITEM_TYPES_LIST from constants.spotify_track import SPOTIFY_TRACK_LIST from constants.upc import UPC_LIST from constants.user_data import APP_SLUG, USER_ID from constants.vendor import ALL_VENDORS_LIST, VENDOR_LIST from constants.youtube import VIDEO_ID_LIST class Helper: memo_storage = {} @staticmethod def is_friday(date: str) -> bool: return ( date and datetime.datetime.strptime(date, "%Y-%m-%d").strftime("%A") == "Friday" ) @staticmethod def get_optional(value, default_value=None) -> Any: return random.choice([default_value, value]) @staticmethod def get_params(params: dict) -> dict: return {k: v for k, v in params.items() if v is not None} @staticmethod def get_random_value(obj_list: List[Any], limit: int = 10000) -> str or tuple: return random.choice(obj_list[:limit]) if obj_list else None @classmethod def get_random_list(cls, obj_list: List[Any], list_len: int = 50) -> List[Any]: return list(set([cls.get_random_value(obj_list) for _ in range(list_len)])) @classmethod def get_random_list_ext(cls, obj_list: List[Any], length: int = 5, limit: int = 10000) -> List[Any]: return cls.get_random_list(obj_list[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_date(cls, time_delta: int = 30) -> str: if time_delta < 1: time_delta = 1 today = datetime.date.today() date = today - datetime.timedelta(days=cls.get_int_value(1, time_delta)) return date.strftime("%Y-%m-%d") @classmethod def get_specific_date(cls, day_of_week: int, weeks_delta: int = 10) -> str: today = datetime.date.today() if today.weekday() < day_of_week: today -= datetime.timedelta(weeks=1) today -= datetime.timedelta(days=(today.weekday() - day_of_week)) date = today - datetime.timedelta(weeks=cls.get_int_value(1, weeks_delta)) return date.strftime("%Y-%m-%d") @staticmethod def get_shifted_date(date: str, time_delta: int) -> str: date = datetime.datetime.strptime(date, "%Y-%m-%d") - datetime.timedelta( days=time_delta ) return date.strftime("%Y-%m-%d") @classmethod def get_date_range(cls, range_delta: int = 30, end_delta: int = 30) -> Tuple[str, str]: end = datetime.date.today() - datetime.timedelta(days=cls.get_int_value(1, end_delta)) start = end - datetime.timedelta(days=range_delta) return start.strftime("%Y-%m-%d"), end.strftime("%Y-%m-%d") @staticmethod def get_probable_value(values_list, values_probabilities): return random.choices(values_list, weights=values_probabilities, k=1)[0] @staticmethod def get_int_value(a: int, b: int, step: int = 1) -> int: if step < 1 or step > 1000: step = 1 return round(random.randint(a, b) / step) * step @classmethod def get_bool_value(cls): return cls.get_random_value(["true", "false"]) @staticmethod def get_offset(limit: int = 10): return random.randint(0, limit) * 10 @classmethod def get_isrc(cls, limit: int = 1000) -> str: return cls.get_random_value(ISRC_LIST[:limit]) @classmethod def get_isrc_list(cls, length=5, limit: int = 10000) -> List[Any]: return cls.get_random_list(ISRC_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_vendor(cls, all_vendors: bool = False) -> str: vendors_list = ALL_VENDORS_LIST if all_vendors else VENDOR_LIST return cls.get_random_value(vendors_list) @classmethod def get_vendor_list(cls, all_vendors: bool = True, length: int = 3, limit: int = 10000) -> List[Any]: vendors_list = ALL_VENDORS_LIST if all_vendors else VENDOR_LIST return cls.get_random_list(vendors_list[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_market(cls, market_list: List[str] = MARKET_LIST) -> str: return cls.get_random_value(market_list) @classmethod def get_market_list(cls, length: int = 3, limit: int = 10000) -> List[Any]: return cls.get_random_list(MARKET_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_video_id(cls, limit: int = 1000) -> str: return cls.get_random_value(VIDEO_ID_LIST[:limit]) @classmethod def get_video_list(cls, length: int = 3, limit: int = 10000) -> List[Any]: return cls.get_random_list(VIDEO_ID_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_sort_order(cls) -> str: return Helper.get_random_value(["asc", "desc"]) @classmethod def get_apple_market(cls, limit: int = 10000) -> str: if not limit: limit = 1 return cls.get_random_value(APPLE_MARKETS_LIST[:limit]) @classmethod def get_apple_markets_str(cls, limit: int = 6) -> str: markets = cls.get_random_list(APPLE_MARKETS_LIST) return ",".join(markets[:limit]) @classmethod def get_search_query(cls, limit: int = 10000): return cls.get_random_value(SEARCH_QUERY_LIST[:limit]) @classmethod def get_apple_search_item_types(cls, length=None, limit: int = 10000): return cls.get_random_list( APPLE_SEARCH_ITEM_TYPES_LIST[:limit], cls.get_int_value(1, length or min(len(APPLE_SEARCH_ITEM_TYPES_LIST), limit), 1), ) @classmethod def get_spotify_market(cls, limit: int = 10000) -> str: return cls.get_random_value(SPOTIFY_MARKETS_LIST[:limit]) @classmethod def get_spotify_search_item_types(cls, length=None, limit: int = 100): length = length or len(SPOTIFY_SEARCH_ITEM_TYPES_LIST) types = cls.get_random_list(SPOTIFY_SEARCH_ITEM_TYPES_LIST[:limit], cls.get_int_value(1, length, 1)) return ",".join(types) @classmethod def get_charts_fields(cls, length=None, length_min=2, starred=False, sony=False): int_starred = int(starred) int_sony = int(sony) length = (length or cls.get_int_value(length_min, len(CHARTS_FIELDS))) - int_starred - int_sony return cls.get_random_list(CHARTS_FIELDS, length) + [STARRED_FIELD] * int_starred + [SONY_FIELD] * int_sony @classmethod def get_charts_order_by(cls, length=None, moves=False, starred=False, sony=False): int_starred = int(starred) int_sony = int(sony) options = CHARTS_ORDER_WITH_MOVES if moves else CHARTS_ORDER_NO_MOVES length = (length or cls.get_int_value(0, len(options))) - int_starred - int_sony order_list = cls.get_random_list(options, length) + [STARRED_ORDER] * int_starred + [SONY_FIELD] * int_sony return [('-' * cls.get_int_value(0, 1) + i) for i in order_list] @classmethod def get_type_period(cls) -> str: return cls.get_random_value(PERIOD_LIST) @classmethod def get_list_type(cls) -> str: return cls.get_random_value(LIST_TYPE) @classmethod def get_search(cls) -> str: return cls.get_random_value(SEARCH_LIST) @classmethod def get_timestamp(cls, time_delta: int = 0) -> int: date = datetime.datetime.now() if time_delta: date = date - datetime.timedelta(days=cls.get_int_value(1, time_delta)) return round(date.timestamp()) @classmethod def get_random_playlists(cls, length=50) -> List[Any]: return cls.get_random_list(APPLE_PLAYLIST_LIST + SPOTIFY_PLAYLIST_LIST, cls.get_int_value(1, length, 1)) @classmethod def get_spotify_playlist_id(cls) -> str: return cls.get_random_value(SPOTIFY_PLAYLIST_LIST) @classmethod def get_spotify_playlists(cls, length=1, limit: int = 1) -> List[Any]: return cls.get_random_list(SPOTIFY_PLAYLIST_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_spotify_popular_playlists(cls, length=None) -> List[Any]: length = length or len(SPOTIFY_POPULAR_PLAYLIST_LIST) return cls.get_random_list(SPOTIFY_POPULAR_PLAYLIST_LIST, cls.get_int_value(1, length, 1)) @classmethod def get_upcs(cls) -> List[Any]: return cls.get_random_list(UPC_LIST, cls.get_int_value(1, 50, 1)) @classmethod def get_spotify_track_ids(cls, length=5, limit: int = 1000) -> List[Any]: return cls.get_random_list(SPOTIFY_TRACK_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_spotify_playlist_fields(cls, length=None, limit: int = 1000): return cls.get_random_list( SPOTIFY_PLAYLIST_FIELDS_LIST[:limit], cls.get_int_value(1, min(limit, length or len(SPOTIFY_PLAYLIST_FIELDS_LIST)), 1) ) @classmethod def get_apple_track_ids(cls, length=5, limit: int = 10000) -> List[Any]: return cls.get_random_list(APPLE_TRACK_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_random_track_ids(cls, length=5) -> List[Any]: return cls.get_random_list(SPOTIFY_TRACK_LIST + APPLE_TRACK_LIST, cls.get_int_value(1, length, 1)) @classmethod def get_apple_album_ids(cls, length=5, limit: int = 10000): return cls.get_random_list(APPLE_ALBUMS_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_apple_playlists(cls, length=50, limit=10000) -> List[Any]: return cls.get_random_list(APPLE_PLAYLIST_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_apple_playlist_id(cls) -> str: return cls.get_random_value(APPLE_PLAYLIST_LIST) @staticmethod def get_random_int(_from: int = 0, to: int = 100) -> int: return random.randint(_from, to) @classmethod def get_random_int_list(cls, length: int = 3, limit: int = 7) -> List[int]: return cls.get_random_list(list(range(1, limit + 1)), cls.get_int_value(1, length, 1)) @classmethod def get_limit(cls, to=100) -> int: return cls.get_int_value(10, to, 10) @classmethod def get_spotify_album(cls, limit: int = 1000) -> str: return cls.get_random_value(SPOTIFY_ALBUM_LIST[:limit]) @classmethod def get_spotify_album_ids(cls, length=5, limit: int = 1000): return cls.get_random_list(SPOTIFY_ALBUM_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_spotify_album_fields(cls, limit: int = 100): return cls.get_random_list( SPOTIFY_ALBUM_FIELDS[:limit], cls.get_int_value(1, min(len(SPOTIFY_ALBUM_FIELDS), limit), 1)) @classmethod def get_artist_ids(cls, length=5, limit: int = 1000): return cls.get_random_list(SPOTIFY_ARTISTS_LIST[:limit], cls.get_int_value(1, length, 1)) @classmethod def get_apple_track_id(cls): return cls.get_random_value(APPLE_TRACK_LIST) @classmethod def get_ext_offset(cls, limit: int = 2): return cls.get_optional(cls.get_int_value(0, 10 * limit, 10)) @classmethod def get_ext_limit(cls, limit: int = 3): return cls.get_optional(cls.get_int_value(10, limit, 10)) @classmethod def get_spotify_limit(cls): return cls.get_ext_limit(50) @classmethod def get_apple_limit(cls): return cls.get_ext_limit(25) @classmethod def get_limit_range(cls, _from: int = 1, to: int = 28): return f"days:{Helper.get_random_int(_from, to)}" @classmethod def get_cursor(cls, _date: str): return f"date:{_date}" @classmethod def get_app_user_headers(cls, with_user: bool = True): return {"X-App-Slug": APP_SLUG, **({"X-User-Id": USER_ID} if with_user else {})} @classmethod def get_offset_limit(cls, max_offset: int = 10, max_limit: int = 100): return { "offset": cls.get_optional(cls.get_offset(max_offset)), "limit": cls.get_optional(cls.get_limit(max_limit)), } @classmethod def get_start_end_dates( cls, shift_date: int = 14, is_optional: bool = False, start_name: str = "start_date", end_name: str = "end_date" ): end_date = cls.get_optional(cls.get_date()) if is_optional else cls.get_date() start_date = cls.get_shifted_date(end_date, cls.get_random_int(shift_date)) if end_date else None return {start_name: start_date, end_name: end_date} @classmethod def get_spotify_buzzcategory(cls) -> str: return cls.get_random_value(SPOTIFY_CATEGORIES) @classmethod def get_apple_buzzcategory(cls) -> str: return cls.get_random_value(APPLE_CATEGORIES) @staticmethod def get_random_choice_from_values(*args): return random.choice(args) @classmethod def get_random_value_memoize_times(cls, obj_list: List[Any], key, times: int = 0, limit: int = 10000): """Method to pick random value and memoize it for several calls with a key.""" if key not in cls.memo_storage: val = cls.get_random_value(obj_list, limit) cls.memo_storage[key] = [*[val] * max(times - 1, 0)] else: try: val = cls.memo_storage[key].pop(0) if not cls.memo_storage[key]: cls.memo_storage.pop(key, None) except IndexError: cls.memo_storage.pop(key, None) val = cls.get_random_value(obj_list, limit) return val