"""Lambda populate_hfa_pending_request function module.""" from datetime import datetime from datetime import timedelta from typing import Dict from typing import List from typing import Union from asgiref.sync import async_to_sync from sentry_sdk import capture_exception from lambdacommon.common_config import logger from lambdacommon.aws import ses from lambdacommon import util import config from src.queries.sql_queries import CHECK_HFA_PROCESS_FOR_DATE, INSERT_HFA_ORCHARD_TRACK_LICENSES from src.utils import constants from src.logic import hfa util.init_sentry_for_lambda() def insert_pending_hfa_request_to_publishing(results: List[Dict[str, Union[str, int]]]) -> None: """Insert pending HFA requests into the publishing database. Args: results (List[Dict[str, Union[str, int]]]): A list of dictionaries containing the pending requests to insert. """ try: logger.info(f'Inserting {len(results)} pending HFA requests into publishing DB.') with util.mysql_connection(**config.PB_DB_CREDENTIALS) as conn: with conn.cursor() as cursor: values = [ ( result[constants.FIELD_TRACK_ID], constants.STATE, result[constants.FIELD_HFA_CONF_CODE] ) for result in results ] cursor.executemany( INSERT_HFA_ORCHARD_TRACK_LICENSES, values ) inserted_count = cursor.rowcount conn.commit() logger.info(f'Successfully inserted {inserted_count} records into publishing DB.') except Exception as e: logger.exception('Error inserting HFA requests into publishing DB.') raise e def is_datetime_valid(input_datetime: str) -> bool: """Validate if the input datetime string is in the correct format and within the valid range. Args: input_datetime (str): Datetime string to be validated. Returns: bool: True if datetime is valid, False otherwise. """ try: datetime_obj = datetime.strptime(input_datetime, '%Y-%m-%d %H:%M:%S') today = datetime.now() max_allowed_date = today - timedelta(days=config.DAYS_LOOKBACK) if datetime_obj > today or datetime_obj < max_allowed_date: return False return True except ValueError: logger.error(f'Invalid datetime format for input: {input_datetime}') return False def has_already_processed(processed_datetime: str) -> bool: """Check if the processing has already occurred for the provided datetime. Args: processed_datetime (str): The datetime to check against in the database. Returns: bool: True if already processed, False otherwise. """ try: with util.mysql_connection(**config.PB_DB_CREDENTIALS) as conn: with conn.cursor() as cursor: cursor.execute(CHECK_HFA_PROCESS_FOR_DATE, (processed_datetime,)) row = cursor.fetchone() return row['cnt'] > 0 except Exception as e: logger.exception('Error checking if processing has already occurred.') raise e async def async_handler(event, context): """Handle the Lambda invocation asynchronously.""" try: logger.info('Lambda Started') input_datetime_str = event.get('datetime') if event else None if input_datetime_str: if not is_datetime_valid(input_datetime_str): raise ValueError( f'Provided datetime must be within the last {config.DAYS_LOOKBACK} days and not in the future.' ) current_datetime = datetime.strptime(input_datetime_str, '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d %H:%M:%S') else: current_datetime = datetime.now().strftime('%Y-%m-%d %H:%M:%S') logger.info(f'Running for datetime: {current_datetime}') if has_already_processed(current_datetime): logger.info('Already processed for this datetime. Exiting.') return { 'status': 'skipped', 'message': f'Already processed for {current_datetime}.' } results = await hfa.get_pending_hfa_request() if results: insert_pending_hfa_request_to_publishing(results) logger.info('Lambda Execution completed.') return { 'status': 'success', 'datetime': str(current_datetime), 'records_processed': len(results) if results else 0 } except Exception as e: capture_exception(e) logger.exception(str(e)) subject = ( constants.MAIL_SUBJECT if config.ENVIRONMENT == config.PROD_ENVIRONMENT else f'{constants.MAIL_SUBJECT} ({config.ENVIRONMENT})' ) ses.send_email( recipients=config.EMAIL_RECIPIENTS, sender=config.EMAIL_SENDER, subject=subject, message=str(e) ) raise e def handler(event, context): """Lambda entry point. Args: event (dict): The event data containing the input datetime. context: The Lambda context object. Returns: dict: The result of the Lambda execution. """ return async_to_sync(async_handler)(event, context)