import logging import uuid from apollo_push_client import Push, PushClient, PushData from botocore.client import BaseClient from sqlalchemy.orm import Session from redis import StrictRedis from apollo_main_db.push_notifications.models import TopicEnum, VendorEnum from spotify_charts_notifications import constants, utils from spotify_charts_notifications.config import Config from spotify_charts_notifications.main_db import session_scope from spotify_charts_notifications.redis_db import get_redis_lock, is_new_track def process_major_moves_of_tracks( market: str, today: str, yesterday: str, push_client: PushClient, redis_client: StrictRedis, session: Session) -> None: """Fetch major moves of tracks and send push messages to SQS Args: market (str): Date with %Y-%m-%d format. today (str): Playlist ID. yesterday (str): Song ID. push_client (PushClient): Client for sending push messages. redis_client: Redis client. session (Session): DB session. """ major_moves_query = utils.get_major_moves(session, market, start_date=today, end_date=yesterday).subquery() _market = constants.APOLLO_GLOBAL_MARKET \ if market == constants.SPOTIFY_GLOBAL_MARKET else market starred_tracks = utils.get_only_starred_tracks_with_device_tokens(session, major_moves_query, market=_market) starred_tracks = utils.process_moves_isrc_duplicates(starred_tracks) label = 'major_moves' push_messages = [] for t in starred_tracks: track_id = t.track_url.replace(constants.SPOTIFY_URL_PREFIX, '') if not is_new_track( date=today, label=label, market=market, user_id=t.user_id, track_id=track_id, redis_client=redis_client ): continue moved_up = t.moves < 0 message_template = constants.MAJOR_MOVES_TEMPLATE \ if moved_up else constants.SIGNIFICANT_DROP_TEMPLATE message_id = str(uuid.uuid4()) push_message = Push( id=message_id, title='', message=message_template.format( artist_name=t.artist_name, track_name=t.track_name, position=t.position, moves=abs(t.moves), ), tokens=t.device_tokens.split(','), user_id=t.user_id, data=PushData( id=message_id, isrc=t.isrc, artist_name=t.artist_name, track_id=track_id, track_name=t.track_name, topic=TopicEnum.CHART_MAJOR_MOVES.name.lower(), position=t.position, change=t.moves, target=constants.PUSH_TARGET, vendor=VendorEnum.SPOTIFY.name.lower(), ) ) push_messages.append(push_message) for sent_messages in push_client.send_messages(push_messages): push_client.save_messages_to_db(sent_messages) def process_added_tracks( market: str, today: str, yesterday: str, push_client: PushClient, redis_client: StrictRedis, session: Session) -> None: """Fetch added tracks and send push messages to SQS Args: market (str): Date with %Y-%m-%d format. today (str): Playlist ID. yesterday (str): Song ID. push_client (PushClient): Client for sending push messages. redis_client: Redis client. session (Session): DB session. """ added_tracks_query = utils.get_diff_tracks_by_dates( session, market, start_date=today, end_date=yesterday).subquery() _market = constants.APOLLO_GLOBAL_MARKET \ if market == constants.SPOTIFY_GLOBAL_MARKET else market starred_tracks = utils.get_only_starred_tracks_with_device_tokens(session, added_tracks_query, market=_market) label = 'added_tracks' push_messages = [] for t in starred_tracks: track_id = t.track_url.replace(constants.SPOTIFY_URL_PREFIX, '') if not is_new_track( date=today, label=label, market=market, user_id=t.user_id, track_id=track_id, redis_client=redis_client ): continue message_id = str(uuid.uuid4()) push_message = Push( id=message_id, title='', message=constants.ADDED_TRACK_TEMPLATE.format( artist_name=t.artist_name, track_name=t.track_name, position=t.position, ), tokens=t.device_tokens.split(','), user_id=t.user_id, data=PushData( id=message_id, isrc=t.isrc, artist_name=t.artist_name, track_id=track_id, track_name=t.track_name, topic=TopicEnum.CHART_ADDITIONS.name.lower(), position=t.position, target=constants.PUSH_TARGET, vendor=VendorEnum.SPOTIFY.name.lower(), ) ) push_messages.append(push_message) for sent_messages in push_client.send_messages(push_messages): push_client.save_messages_to_db(sent_messages) def process_dropped_tracks( market: str, today: str, yesterday: str, push_client: PushClient, redis_client: StrictRedis, session: Session) -> None: """Fetch dropped tracks and send push messages to SQS Args: market (str): Date with %Y-%m-%d format. today (str): Playlist ID. yesterday (str): Song ID. push_client (PushClient): Client for sending push messages. redis_client: Redis client session (Session): DB session. """ dropped_tracks_query = utils.get_diff_tracks_by_dates( session, market, start_date=yesterday, end_date=today).subquery() _market = constants.APOLLO_GLOBAL_MARKET \ if market == constants.SPOTIFY_GLOBAL_MARKET else market starred_tracks = utils.get_only_starred_tracks_with_device_tokens(session, dropped_tracks_query, market=_market) label = 'dropped_tracks' push_messages = [] for t in starred_tracks: track_id = t.track_url.replace(constants.SPOTIFY_URL_PREFIX, '') if not is_new_track( date=today, label=label, market=market, user_id=t.user_id, track_id=track_id, redis_client=redis_client ): continue message_id = str(uuid.uuid4()) push_message = Push( id=message_id, title='', message=constants.DROPPED_TRACK_TEMPLATE.format( artist_name=t.artist_name, track_name=t.track_name, position=t.position, ), tokens=t.device_tokens.split(','), user_id=t.user_id, data=PushData( id=message_id, isrc=t.isrc, artist_name=t.artist_name, track_id=track_id, track_name=t.track_name, topic=TopicEnum.CHART_REMOVALS.name.lower(), position=t.position, target=constants.PUSH_TARGET, vendor=VendorEnum.SPOTIFY.name.lower(), ) ) push_messages.append(push_message) for sent_messages in push_client.send_messages(push_messages): push_client.save_messages_to_db(sent_messages) def handler( logger: logging.Logger, sqs_client: BaseClient, redis_client: StrictRedis, app_config: Config ) -> dict: """Lambda job. Args: logger: Logger instance. sqs_client: Client for SQS AWS. app_config: Config object. redis_client: Redis client Returns: JSON serializable response """ # set lock for 15 m to avoid possible race condition between lambdas lock = get_redis_lock(redis_client) if not lock.acquire(blocking=False): logger.info('Locked') return {} for market, in utils.get_markets(): with session_scope() as session: dates = utils.get_chart_last_dates(market=market) if len(dates) != 2: continue push_client = PushClient( logger_client=logger, sqs_client=sqs_client, db_session=session, config=app_config ) today, yesterday = dates process_major_moves_of_tracks(market, today, yesterday, push_client, redis_client, session) process_added_tracks(market, today, yesterday, push_client, redis_client, session) process_dropped_tracks(market, today, yesterday, push_client, redis_client, session) lock.release() return {}