#!/usr/bin/env python3 """ Utility for interacting with AWS S3 storage. """ import json import os import boto3 from botocore.exceptions import ClientError from datetime import datetime import uuid class AWSS3Storage: """AWS S3 Storage utility for saving and retrieving data.""" def __init__(self, bucket_name=None, region_name=None): """ Initialize the AWS S3 Storage utility. Args: bucket_name (str, optional): S3 bucket name. Defaults to env var AWS_S3_BUCKET_NAME. region_name (str, optional): AWS region. Defaults to env var AWS_REGION or 'us-east-1'. """ self.bucket_name = bucket_name or os.getenv('AWS_S3_BUCKET_NAME') self.region_name = region_name or os.getenv('AWS_REGION', 'us-east-1') # Initialize S3 client self.s3 = boto3.client( 's3', aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'), region_name=self.region_name ) def save_pitch_data(self, script_id, elevator_pitch, full_pitch, competitors_data=None, market_insights=None): """ Save pitch data to S3 bucket. Args: script_id (str): Unique identifier for the script elevator_pitch (str): Short elevator pitch full_pitch (str): Full pitch content competitors_data (str, optional): Competitor analysis markdown market_insights (str, optional): Market insights markdown Returns: dict: Response data including success status and file path """ if not script_id: script_id = f"pitch_{datetime.now().strftime('%Y%m%d')}_{uuid.uuid4().hex[:8]}" # Prepare data structure pitch_data = { "script_id": script_id, "created_at": datetime.now().isoformat(), "elevator_pitch": elevator_pitch, "full_pitch": full_pitch, "competitors_data": competitors_data, "market_insights": market_insights } # Convert to JSON json_data = json.dumps(pitch_data, indent=2) # Define the object key (file path in S3) object_key = f"pitches/{script_id}.json" try: # Upload to S3 self.s3.put_object( Bucket=self.bucket_name, Key=object_key, Body=json_data, ContentType='application/json' ) # Generate a presigned URL for temporary access (expires in 1 hour) presigned_url = self.s3.generate_presigned_url( 'get_object', Params={'Bucket': self.bucket_name, 'Key': object_key}, ExpiresIn=3600 # URL expires in 1 hour ) return { "success": True, "message": "Pitch data saved successfully", "script_id": script_id, "s3_path": f"s3://{self.bucket_name}/{object_key}", "presigned_url": presigned_url } except ClientError as e: error_message = e.response.get('Error', {}).get('Message', str(e)) return { "success": False, "message": f"Failed to save pitch data: {error_message}", "script_id": script_id } def get_pitch_data(self, script_id): """ Retrieve pitch data from S3 bucket. Args: script_id (str): Unique identifier for the script Returns: dict: Pitch data or error information """ object_key = f"pitches/{script_id}.json" try: # Get object from S3 response = self.s3.get_object( Bucket=self.bucket_name, Key=object_key ) # Read and parse JSON data json_data = response['Body'].read().decode('utf-8') pitch_data = json.loads(json_data) return { "success": True, "data": pitch_data } except ClientError as e: error_code = e.response.get('Error', {}).get('Code') error_message = e.response.get('Error', {}).get('Message', str(e)) return { "success": False, "message": f"Failed to retrieve pitch data: {error_message}", "error_code": error_code, "script_id": script_id } def list_pitches(self, limit=50): """ List available pitches in the S3 bucket. Args: limit (int, optional): Maximum number of results. Defaults to 50. Returns: dict: List of pitch metadata or error information """ try: # List objects with prefix response = self.s3.list_objects_v2( Bucket=self.bucket_name, Prefix="pitches/", MaxKeys=limit ) pitches = [] if 'Contents' in response: for item in response['Contents']: # Extract script_id from the key key = item['Key'] if key.endswith('.json'): script_id = key.split('/')[-1].replace('.json', '') # Add basic metadata pitches.append({ "script_id": script_id, "last_modified": item['LastModified'].isoformat(), "size_bytes": item['Size'] }) return { "success": True, "count": len(pitches), "pitches": pitches } except ClientError as e: error_message = e.response.get('Error', {}).get('Message', str(e)) return { "success": False, "message": f"Failed to list pitches: {error_message}" }