"""Lambda phf-send-notifications function module.""" from datetime import datetime import os from constants import email from constants import errors from constants import general from warmer_util import catch_warmer_event import common_config import lambda_exceptions import ses import util def prepare_message(file_name, received_code=None, received_errors=None): """Prepare e-mail message body. Args: file_name (str): name of file. received_code (str): error or success code which received in event. received_errors (dict): errors received if the file was partially processed. Returns: str, str: E-mail subject, E-mail message body """ notification_params = email.ERROR_CODE_TEXT_MAPPING.get(received_code) message = email.NOTIFICATION_TEXT_PATTERNS[ notification_params.failure_type] notification_subject = notification_params.subject if notification_subject: subject = notification_subject.format(file_name=file_name) reason_text = notification_params.reason fix_text = notification_params.fix_text received_errors = received_errors or {} partial_processing_errors = compose_partial_processing_errors_text( received_errors) if partial_processing_errors: reason_text = '\n\n'.join(partial_processing_errors) if received_code == 'processing_partially_succeeded': fix_text = compose_partial_processing_fix_text( notification_params.fix_text, file_name) message_body = message.format( file_name=file_name, reason_text=reason_text, fix_text=fix_text) return subject, message_body def compose_partial_processing_errors_text(received_errors): """Prepare partial processing error text. Args: received_errors (dict): errors received if the file was partially processed. Returns: list: partial processing errors text. """ partial_processing_errors = [] for key in received_errors: notification = email.ERROR_CODE_TEXT_MAPPING.get(key) multiple_rows_err = [] if ( key == errors.CONTENT_DOES_NOT_MATCH_HEADERS_ERROR_CODE and received_errors[key]['line_numbers']): multiple_rows_err = [notification.reason] row_numbers = '\n'.join( str(num) for num in received_errors[key]['line_numbers']) many_rows_err_msg = ('Skipped rows:\n{}'.format(row_numbers)) multiple_rows_err.append(many_rows_err_msg) if ( key == errors.MISSING_MANDATORY_FIELDS_ERROR_CODE and received_errors[key]): multiple_rows_err = [notification.reason, 'Skipped rows:'] for err in received_errors[key]: missing_values = ', '.join(received_errors[key][err]) one_row_err_msg = 'Row {}: {}.'.format(err, missing_values) multiple_rows_err.append(one_row_err_msg) if ( key == errors.DATA_VALIDATION_ERROR_CODE and received_errors[key]): multiple_rows_err = [notification.reason] for err in received_errors[key]: missing_values = ', '.join(received_errors[key][err]) one_row_err_msg = 'Row {}: {}.'.format(err, missing_values) multiple_rows_err.append(one_row_err_msg) if key == errors.PARTIAL_PROCESSING_ERRORS and received_errors[key]: multiple_row_numbers = [] multiple_rows_err = [notification.reason] for row_numbers in received_errors[key].values(): rows = '\n'.join(str(row_num) for row_num in row_numbers) multiple_row_numbers.append(rows) if multiple_row_numbers: row_numbers = '\n'.join(multiple_row_numbers) many_rows_err_msg = ('Skipped rows:\n{}'.format(row_numbers)) multiple_rows_err.append(many_rows_err_msg) if multiple_rows_err: multiple_rows_err_msg = '\n'.join(multiple_rows_err) partial_processing_errors.append(multiple_rows_err_msg) return partial_processing_errors def compose_partial_processing_fix_text(fix_text, file_name): """Compose partial processing fix text. Args: fix_text (str): fix text to be formatted. file_name (str): name of file. Returns: str: formatted fix text. """ timestamp = datetime.utcnow().strftime('%Y%m%d') file_name_without_ext, file_extension = os.path.splitext(file_name) file_name_timestamp = '{}_{}{}'.format( file_name_without_ext, timestamp, file_extension) file_name_1 = '{}_1{}'.format(file_name_without_ext, file_extension) result_text = fix_text.format( file_name=file_name, file_name_timestamp=file_name_timestamp, file_name_1=file_name_1) return result_text @catch_warmer_event() @util.handle_lambda_result( lambda_name=general.LAMBDA_NAME, default_error_code=errors.EMAIL_SENDING_ERROR_CODE) def handler(event, context): """Lambda entry point.""" status = 200 error_code = file_name = received_code = None error_description = [] key = bucket = received_errors = None s3_object = event.get('s3_object') received_status = event.get('status') received_lambda_internal_error = event.get('error') try: if s3_object: file_name = s3_object.get('key') received_code = event.get('error_code') else: error_code = errors.MISSING_FILE_PATH_CODE lambda_exceptions.notify_and_raise( general.LAMBDA_NAME, errors.INVALID_DATA_ERROR_STATUS, error_code, key, bucket ) lambda_name = event.get('function') if lambda_name == 'phf-sales-data-process' and not ( event.get('error_code')): received_code = 'processing_succeeded' elif ( lambda_name == 'phf-sales-data-process' and received_status in [200, 201]): received_code = 'processing_partially_succeeded' received_errors = event.get('error_description') elif received_lambda_internal_error: received_code = 'file_processing_error' received_errors = [] subject, body = prepare_message( file_name, received_code, received_errors) recipients = [ email for email in common_config.EMAIL_RECIPIENTS.split(' ') if email] ses.send_email( recipients=recipients, sender=common_config.EMAIL_SENDER, subject=subject, message=body) return { 'status': status, 's3_object': s3_object, 'error_code': error_code, 'error_description': error_description or None} except lambda_exceptions.LoggedException as e: error_description = [str(e)] status = 400 return { 'status': status, 's3_object': s3_object, 'error_code': error_code, 'error_description': error_description or None}