"""Utility functions for the Sound Recordings Takedown process.""" from datetime import datetime from email import encoders from email.mime.base import MIMEBase from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import hashlib import os import boto3 import config import pandas as pd from src.utils import constants logger = config.setup_logger(__name__) s3_client = boto3.client('s3', region_name=config.AWS_REGION) ses_client = boto3.client('ses', region_name=config.AWS_REGION) def upload_to_s3(file_path: str, bucket: str, prefix: str) -> None: """Upload a local file to S3. Args: file_path (str): Local path of the file to upload. bucket (str): S3 bucket name. prefix (str): S3 key prefix (directory). """ try: filename = os.path.basename(file_path) key = f'{prefix.rstrip("/")}/{filename}' s3_client.upload_file( Filename=file_path, Bucket=bucket, Key=key, ExtraArgs={ 'ExpectedBucketOwner': config.S3_EXPECTED_OWNER } ) except Exception as e: logger.exception(f'Failed to upload {file_path} to s3://{bucket}/{prefix}: {e}') raise def export_and_upload(takedown_df: pd.DataFrame, isrc_track_df: pd.DataFrame, output_file: str) -> None: """Save DataFrames to an Excel file and upload to S3.""" try: os.makedirs(os.path.dirname(output_file), exist_ok=True) with pd.ExcelWriter(output_file, engine='openpyxl') as writer: takedown_df.to_excel(writer, sheet_name='sheet1', index=False) isrc_track_df.to_excel(writer, sheet_name='sheet2', index=False) upload_to_s3(output_file, config.S3_BUCKET, config.S3_TMP_FILE_DIR) except Exception as e: logger.exception(f'Failed to export and upload {output_file}: {e}') raise def send_success_email(filepath: str, filename: str) -> None: """Send a success email with Excel file attachment.""" try: with open(filepath, 'rb') as f: xlsx_data = f.read() file_size = f'{len(xlsx_data) / float(1 << 10):,.0f} KB' md5 = hashlib.md5(xlsx_data).hexdigest() current_time = datetime.now().astimezone().strftime('%a %b %d %H:%M:%S %Z %Y') subject = ( constants.SUCCESS_EMAIL_SUBJECT if config.ENVIRONMENT == config.PROD_ENVIRONMENT else f'{constants.SUCCESS_EMAIL_SUBJECT} ({config.ENVIRONMENT.upper()})' ) registry_note = '' if config.SEND_UPCS_TO_MASTERS_REGISTRY else constants.REGISTRY_SKIPPED_NOTE message_body = constants.SUCCESS_MESSAGE_BODY.format( registry_note=registry_note, filename=filename, md5=md5, file_size=file_size, current_time=current_time, ) msg = MIMEMultipart('mixed') msg['Subject'] = subject msg['From'] = config.EMAIL_SENDER msg['To'] = config.EMAIL_RECIPIENTS[0] msg.attach(MIMEText(message_body, 'plain', 'utf-8')) xlsx_mime = MIMEBase( 'application', 'vnd.openxmlformats-officedocument.spreadsheetml.sheet' ) xlsx_mime.set_payload(xlsx_data) encoders.encode_base64(xlsx_mime) xlsx_mime.add_header( 'Content-Disposition', f'attachment; filename="{filename}"' ) msg.attach(xlsx_mime) ses_client.send_raw_email( Source=config.EMAIL_SENDER, Destinations=config.EMAIL_RECIPIENTS, RawMessage={'Data': msg.as_string()}, ) logger.info(f'Success email sent with attachment: {filename}') except Exception as e: logger.exception(f'Failed to send success email for file: {filename} due to: {e}') raise