from typing import Callable, Dict, Iterable, List, Optional, Set, Type from apollo_main_db.apollo.models import PlaylistBlacklist from marshmallow import Schema from apollo_notifications.client import NotificationsBaseClient from apollo_notifications.constants import PUSH_SEARCH_MODE_TO_MODEL, PushSearchMode from apollo_notifications.playlists.config import PlaylistsConfig from apollo_notifications.playlists.utils import filter_track_in_playlist_event class NotificationsPlaylistClient(NotificationsBaseClient): """Base class to get playlist vendor-oriented data.""" vendor: str def __init__(self, config: Type[PlaylistsConfig]): super().__init__() self.push_search_mode = PushSearchMode(config.PUSH_SEARCH_MODE) def get_blacklisted_ids(self) -> List[str]: """Returns set of blacklisted playlists ids. """ query = self.session.query( PlaylistBlacklist.playlist_id ).filter( PlaylistBlacklist.vendor == self.vendor ) return [str(q.playlist_id) for q in query] def get_existing_push_messages( self, topics: List[str], target_push_date: Optional[str] = None, min_push_date: Optional[str] = None, max_push_date: Optional[str] = None ) -> Set[str]: """Return set of existing push messages ids for specific date, topic and vendor. Args: target_push_date: str datetime (Y-m-d format) to generate push-messages for (strict value). min_push_date: str datetime to generate push-messages after. max_push_date: str datetime to generate push-messages before (including). topics: list of topics to get push messages for. """ model = PUSH_SEARCH_MODE_TO_MODEL[self.push_search_mode] filters = [ model.topic.in_(topics), model.vendor == self.vendor ] if target_push_date: filters.append(model.date == target_push_date) if min_push_date: filters.append(model.date > min_push_date) if max_push_date: filters.append(model.date <= max_push_date) q = self.session.query( model.id ).filter(*filters) return set([str(pm.id) for pm in q]) def is_new_message( self, filter_function: Callable, item: object, push_date: Optional[str], topics: Iterable[str], markets: Iterable[str], existing_messages: Set[str], *args, **kwargs ) -> bool: """Check if message was already sent or not. 'existing_messages' includes ids of messages sent for the same params ranges, every 'id' is a hash which can be calculated from message params, so by calculating the hash from the passed 'item' parameters (being done 'by filter_function') and checking its presence in 'existing_messages' we can conclude the corresponding message was already sent or not. """ for topic in topics: for market in markets: if not filter_function( item=item, date=push_date, topic=topic, market=market, vendor=self.vendor, existing_messages=existing_messages, *args, **kwargs): return False return True def get_push_messages( self, query: Iterable, topics: Iterable[str], markets: Iterable[str], push_schema: Schema, existing_messages: Set[str], *args, push_date: Optional[str] = None, filter_function: Callable = filter_track_in_playlist_event, include_playlist_id_to_users_map: Optional[Dict[str, Set[str]]] = None, exclude_playlist_id_to_users_map: Optional[Dict[str, Set[str]]] = None, **kwargs ): """Returns dumped filtered push messages.""" messages = [] for item in query: if include_playlist_id_to_users_map and item.user_id \ not in include_playlist_id_to_users_map.get(item.playlist_id, []): continue if exclude_playlist_id_to_users_map and item.user_id \ in exclude_playlist_id_to_users_map.get(item.playlist_id, []): continue if self.is_new_message( filter_function=filter_function, item=item, push_date=push_date, topics=topics, markets=markets, existing_messages=existing_messages, *args, **kwargs ): messages.append(push_schema.dump(item)) return messages