first commit
This commit is contained in:
290
config_snapshot_downloader.py
Normal file
290
config_snapshot_downloader.py
Normal file
@@ -0,0 +1,290 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user