first commit

This commit is contained in:
Tudor Sitaru
2025-10-07 14:52:04 +01:00
commit ddde67ca62
73 changed files with 14025 additions and 0 deletions

View 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())