|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +Generate conference story images for speakers and sessions. |
| 4 | +
|
| 5 | +This script uses the refactored modules to generate story images for the conference, |
| 6 | +following SOLID principles for better maintainability and testability. |
| 7 | +""" |
| 8 | +import argparse |
| 9 | +import logging |
| 10 | +from concurrent.futures import ThreadPoolExecutor |
| 11 | +from typing import Dict, Any, List, Tuple |
| 12 | + |
| 13 | +from config import Config |
| 14 | +from data_fetcher import DataFetcher |
| 15 | +from story_generator import StoryGenerator |
| 16 | + |
| 17 | +# Set up logging |
| 18 | +logging.basicConfig( |
| 19 | + level=logging.INFO, |
| 20 | + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' |
| 21 | +) |
| 22 | +logger = logging.getLogger(__name__) |
| 23 | + |
| 24 | + |
| 25 | +def parse_arguments(): |
| 26 | + """Parse command line arguments""" |
| 27 | + parser = argparse.ArgumentParser( |
| 28 | + description='Generate story images for conference speakers and sessions') |
| 29 | + parser.add_argument('--max-workers', type=int, default=10, |
| 30 | + help='Maximum number of worker threads') |
| 31 | + parser.add_argument('--timeout', type=int, default=10, |
| 32 | + help='Timeout for HTTP requests in seconds') |
| 33 | + parser.add_argument('--output-dir', type=str, default='output', |
| 34 | + help='Directory to save generated images') |
| 35 | + return parser.parse_args() |
| 36 | + |
| 37 | + |
| 38 | +def process_session(generator: StoryGenerator, session: Dict[str, Any], |
| 39 | + speaker: Dict[str, Any], track_name: str) -> str: |
| 40 | + """Process a single session and generate its story image |
| 41 | + |
| 42 | + Args: |
| 43 | + generator: StoryGenerator instance |
| 44 | + session: Session data |
| 45 | + speaker: Speaker data |
| 46 | + track_name: Name of the track |
| 47 | + |
| 48 | + Returns: |
| 49 | + Path to the generated image file |
| 50 | + """ |
| 51 | + try: |
| 52 | + return generator.generate_story(speaker, session, track_name) |
| 53 | + except Exception as e: |
| 54 | + logger.error( |
| 55 | + f"Failed to process session {session.get('title', 'Unknown')}: {e}") |
| 56 | + raise |
| 57 | + |
| 58 | + |
| 59 | +def main(): |
| 60 | + """Main entry point""" |
| 61 | + args = parse_arguments() |
| 62 | + |
| 63 | + # Initialize configuration |
| 64 | + config = Config() |
| 65 | + # Override output directory from command line if provided |
| 66 | + if args.output_dir: |
| 67 | + config.OUTPUT_DIR = args.output_dir |
| 68 | + |
| 69 | + # Ensure output directory exists |
| 70 | + config.ensure_output_dir() |
| 71 | + |
| 72 | + # Initialize components |
| 73 | + data_fetcher = DataFetcher(request_timeout=args.timeout) |
| 74 | + story_generator = StoryGenerator(config, data_fetcher) |
| 75 | + |
| 76 | + logger.info("Fetching speakers and sessions...") |
| 77 | + |
| 78 | + try: |
| 79 | + # Fetch data |
| 80 | + speakers_lookup = data_fetcher.fetch_speakers(config.SPEAKERS_API) |
| 81 | + tracks = data_fetcher.fetch_sessions_by_track(config.SESSIONS_API) |
| 82 | + |
| 83 | + # Process all sessions |
| 84 | + with ThreadPoolExecutor(max_workers=args.max_workers) as executor: |
| 85 | + futures = [] |
| 86 | + |
| 87 | + for track_name, sessions in tracks.items(): |
| 88 | + logger.info( |
| 89 | + f"Processing track: {track_name} with {len(sessions)} sessions") |
| 90 | + |
| 91 | + for session in sessions: |
| 92 | + speaker_ids = session.get('speakers', []) |
| 93 | + speaker_id = speaker_ids[0]['id'] if speaker_ids else None |
| 94 | + |
| 95 | + if speaker_id and speaker_id in speakers_lookup: |
| 96 | + speaker = speakers_lookup[speaker_id] |
| 97 | + futures.append( |
| 98 | + executor.submit( |
| 99 | + process_session, |
| 100 | + story_generator, |
| 101 | + session, |
| 102 | + speaker, |
| 103 | + track_name |
| 104 | + ) |
| 105 | + ) |
| 106 | + |
| 107 | + # Wait for all tasks to complete |
| 108 | + completed = 0 |
| 109 | + for future in futures: |
| 110 | + future.result() |
| 111 | + completed += 1 |
| 112 | + if completed % 5 == 0: |
| 113 | + logger.info( |
| 114 | + f"Processed {completed}/{len(futures)} sessions") |
| 115 | + |
| 116 | + logger.info("✅ All stories generated!") |
| 117 | + |
| 118 | + except Exception as e: |
| 119 | + logger.error(f"Error processing stories: {e}") |
| 120 | + return 1 |
| 121 | + |
| 122 | + return 0 |
| 123 | + |
| 124 | + |
| 125 | +if __name__ == "__main__": |
| 126 | + exit(main()) |
0 commit comments