#!/usr/bin/env python3 """ Configuration-based Snapshot Downloader for ParentZone This script reads configuration from a JSON file and downloads snapshots (daily events) from the ParentZone API with pagination support, generating a comprehensive HTML report. """ import argparse import asyncio import json import logging import os from datetime import datetime, timedelta from pathlib import Path # Import the snapshot downloader try: from snapshot_downloader import SnapshotDownloader except ImportError: print("Error: snapshot_downloader.py not found. Please ensure it's in the same directory.") exit(1) class ConfigSnapshotDownloader: def __init__(self, config_file: str): """ Initialize the downloader with configuration from a JSON file. Args: config_file: Path to the JSON configuration file """ self.config = self.load_config(config_file) self.setup_logging() # Create the underlying snapshot downloader self.downloader = SnapshotDownloader( api_url=self.config.get('api_url', 'https://api.parentzone.me'), output_dir=self.config.get('output_dir', 'snapshots'), api_key=self.config.get('api_key'), email=self.config.get('email'), password=self.config.get('password') ) def load_config(self, config_file: str) -> dict: """Load configuration from JSON file.""" try: with open(config_file, 'r') as f: config = json.load(f) # Validate required authentication has_api_key = 'api_key' in config and config['api_key'] has_credentials = 'email' in config and 'password' in config and config['email'] and config['password'] if not has_api_key and not has_credentials: raise ValueError("Either 'api_key' or both 'email' and 'password' must be provided in config") # Set defaults for optional fields config.setdefault('api_url', 'https://api.parentzone.me') config.setdefault('output_dir', 'snapshots') config.setdefault('type_ids', [15]) config.setdefault('max_pages', None) # Set default date range (last year) if not specified if 'date_from' not in config or not config['date_from']: config['date_from'] = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") if 'date_to' not in config or not config['date_to']: config['date_to'] = datetime.now().strftime("%Y-%m-%d") return config except FileNotFoundError: raise FileNotFoundError(f"Configuration file not found: {config_file}") except json.JSONDecodeError as e: raise ValueError(f"Invalid JSON in configuration file: {e}") def setup_logging(self): """Setup logging configuration.""" output_dir = Path(self.config['output_dir']) output_dir.mkdir(exist_ok=True) log_file = output_dir / 'snapshots.log' logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(log_file), logging.StreamHandler() ] ) self.logger = logging.getLogger(__name__) async def download_snapshots(self) -> Path: """ Download snapshots using the configuration settings. Returns: Path to the generated HTML file """ self.logger.info("Starting snapshot download with configuration") self.logger.info(f"Date range: {self.config['date_from']} to {self.config['date_to']}") self.logger.info(f"Type IDs: {self.config['type_ids']}") self.logger.info(f"Output directory: {self.config['output_dir']}") if self.config.get('max_pages'): self.logger.info(f"Max pages limit: {self.config['max_pages']}") try: html_file = await self.downloader.download_snapshots( type_ids=self.config['type_ids'], date_from=self.config['date_from'], date_to=self.config['date_to'], max_pages=self.config.get('max_pages') ) return html_file except Exception as e: self.logger.error(f"Error during snapshot download: {e}") raise def print_config_summary(self): """Print a summary of the current configuration.""" print("=" * 60) print("SNAPSHOT DOWNLOADER CONFIGURATION") print("=" * 60) print(f"API URL: {self.config['api_url']}") print(f"Output Directory: {self.config['output_dir']}") print(f"Date From: {self.config['date_from']}") print(f"Date To: {self.config['date_to']}") print(f"Type IDs: {self.config['type_ids']}") auth_method = "API Key" if self.config.get('api_key') else "Email/Password" print(f"Authentication: {auth_method}") if self.config.get('max_pages'): print(f"Max Pages: {self.config['max_pages']}") print("=" * 60) def create_example_config(): """Create an example configuration file.""" example_config = { "api_url": "https://api.parentzone.me", "output_dir": "./snapshots", "type_ids": [15], "date_from": "2024-01-01", "date_to": "2024-12-31", "max_pages": null, "api_key": "your-api-key-here", "email": "your-email@example.com", "password": "your-password-here" } config_file = Path("snapshot_config_example.json") with open(config_file, 'w') as f: json.dump(example_config, f, indent=2) print(f"āœ… Example configuration created: {config_file}") print("šŸ“ Edit the file with your credentials and settings") return config_file def main(): parser = argparse.ArgumentParser( description="Download ParentZone snapshots using configuration file", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Use existing config file python3 config_snapshot_downloader.py --config snapshot_config.json # Create example config file python3 config_snapshot_downloader.py --create-example # Show config summary before downloading python3 config_snapshot_downloader.py --config snapshot_config.json --show-config Configuration file format: { "api_url": "https://api.parentzone.me", "output_dir": "./snapshots", "type_ids": [15], "date_from": "2024-01-01", "date_to": "2024-12-31", "max_pages": null, "api_key": "your-api-key-here", "email": "your-email@example.com", "password": "your-password-here" } Notes: - Either 'api_key' OR both 'email' and 'password' are required - 'date_from' and 'date_to' default to last year if not specified - 'type_ids' defaults to [15] (snapshot type) - 'max_pages' limits pages fetched (useful for testing) """ ) parser.add_argument( '--config', help='Path to the JSON configuration file' ) parser.add_argument( '--create-example', action='store_true', help='Create an example configuration file and exit' ) parser.add_argument( '--show-config', action='store_true', help='Show configuration summary before downloading' ) parser.add_argument( '--debug', action='store_true', help='Enable debug mode with detailed server response logging' ) args = parser.parse_args() # Handle create example if args.create_example: create_example_config() return 0 # Validate config argument if not args.config: print("Error: --config argument is required (or use --create-example)") print("Run with --help for more information") return 1 try: # Create downloader downloader = ConfigSnapshotDownloader(args.config) # Show configuration if requested if args.show_config: downloader.print_config_summary() print() # Enable debug mode if requested if args.debug: print("šŸ” DEBUG MODE ENABLED - Detailed server responses will be printed") # Set debug flag on the underlying downloader downloader.downloader.debug_mode = True # Download snapshots html_file = asyncio.run(downloader.download_snapshots()) if html_file: print("\n" + "=" * 60) print("āœ… SUCCESS!") print("=" * 60) print(f"šŸ“„ HTML Report: {html_file}") print(f"šŸ“ Open the file in your browser to view the snapshots") print("šŸŽÆ The report includes:") print(" • All snapshots with descriptions and metadata") print(" • Images and attachments (if any)") print(" • Search and filtering capabilities") print(" • Interactive collapsible sections") print("=" * 60) else: print("āš ļø No snapshots were found for the specified period") print("šŸ’” Try adjusting the date range in your configuration") except KeyboardInterrupt: print("\nāš ļø Download interrupted by user") return 1 except FileNotFoundError as e: print(f"āŒ Configuration file error: {e}") print("šŸ’” Use --create-example to generate a template") return 1 except ValueError as e: print(f"āŒ Configuration error: {e}") return 1 except Exception as e: print(f"āŒ Download failed: {e}") return 1 return 0 if __name__ == "__main__": exit(main())