creative-x-ferrero/scripts/check_status.py
DJP 78e19ca82d Add error status handling and detailed error logging
Changes:
- Detect 'error' status from API
- Mark upload as failed when error status received
- Display full error response for debugging
- Log error details to help diagnose issues
2026-01-09 14:50:36 -05:00

299 lines
10 KiB
Python

#!/usr/bin/env python3
"""
Check status of CreativeX preflight requests
Usage:
python check_status.py
python check_status.py --file file.mp4
python check_status.py --poll --interval 30
python check_status.py --summary
"""
import argparse
import sys
import time
from pathlib import Path
from datetime import datetime
# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import load_config
from core.api_client import CreativeXAPIClient, APIError
from utils.state_manager import StateManager
from utils.logger import setup_logger
class StatusChecker:
"""Monitor preflight status and retrieve results"""
def __init__(self, config):
"""Initialize status checker"""
self.config = config
# Setup logger
self.logger = setup_logger(
__name__,
log_dir=config.logs_dir,
log_level=config.log_level
)
# Initialize components
self.api_client = CreativeXAPIClient(
config.api_base_url,
config.access_token,
config.api_max_retries,
config.api_timeout
)
self.state_manager = StateManager(config.state_file)
def check_status(self, filename: str) -> dict:
"""
Check status of single upload
Args:
filename: Filename to check
Returns:
dict: Current status data
"""
upload = self.state_manager.get_upload(filename)
if not upload:
raise ValueError(f"Upload not found: {filename}")
request_id = upload.get('request_id')
if not request_id:
raise ValueError(f"No request_id for {filename}")
self.logger.info(f"Checking status: {filename}")
try:
response = self.api_client.get_preflight_status(request_id)
# Update state
status = response.get('status', 'unknown')
if status == 'completed':
self.state_manager.update_status(
filename,
StateManager.STATUS_COMPLETED,
results=response,
completed_at=datetime.now().isoformat()
)
self.logger.info(f" ✓ Completed")
elif status in ['processing', 'pending']:
self.state_manager.update_status(
filename,
StateManager.STATUS_PROCESSING,
results=response
)
self.logger.info(f" ⟳ Processing")
elif status == 'error':
# Extract error details
error_msg = response.get('error', response.get('errors', 'Unknown error'))
self.logger.error(f" ✗ Error: {error_msg}")
self.state_manager.mark_failed(filename, str(error_msg))
# Print full response for debugging
print(f"\nFull error response:")
import json
print(json.dumps(response, indent=2))
else:
self.logger.info(f" Status: {status}")
return response
except APIError as e:
self.logger.error(f" ✗ Error: {e}")
return {'error': str(e)}
def check_all_processing(self) -> dict:
"""
Check status of all uploads with status: processing or preflight_created
Returns:
dict: Summary of status changes
"""
processing = self.state_manager.get_uploads_by_status(StateManager.STATUS_PROCESSING)
preflight_created = self.state_manager.get_uploads_by_status(StateManager.STATUS_PREFLIGHT_CREATED)
all_uploads = processing + preflight_created
if not all_uploads:
self.logger.info("No uploads in processing state")
return {'checked': 0, 'completed': 0, 'processing': 0}
self.logger.info(f"\nChecking {len(all_uploads)} uploads...")
self.logger.info("-" * 60)
summary = {'checked': 0, 'completed': 0, 'processing': 0, 'errors': 0}
for upload in all_uploads:
filename = upload['filename']
summary['checked'] += 1
try:
result = self.check_status(filename)
if result.get('status') == 'completed':
summary['completed'] += 1
elif result.get('status') in ['processing', 'pending']:
summary['processing'] += 1
elif 'error' in result:
summary['errors'] += 1
except Exception as e:
self.logger.error(f"Error checking {filename}: {e}")
summary['errors'] += 1
return summary
def print_summary(self) -> None:
"""
Print formatted summary of all uploads
"""
summary = self.state_manager.get_summary()
print("\n" + "=" * 60)
print("PREFLIGHT STATUS SUMMARY")
print("=" * 60)
print(f"Total Uploads: {summary['total']}")
print(f" ✓ Completed: {summary['completed']}")
print(f" ⟳ Processing: {summary['processing']}")
print(f" ⊙ Preflight Created: {summary['preflight_created']}")
print(f" ⏳ Uploading: {summary['uploading']}")
print(f" ⊙ Pending: {summary['pending']}")
print(f" ✗ Failed: {summary['failed']}")
print("=" * 60)
# Show processing uploads
processing = self.state_manager.get_uploads_by_status(StateManager.STATUS_PROCESSING)
if processing:
print("\nProcessing:")
for upload in processing:
filename = upload['filename']
created_at = upload.get('preflight_created_at', '')
print(f"{filename}")
if created_at:
print(f" Created: {created_at}")
# Show completed uploads
completed = self.state_manager.get_uploads_by_status(StateManager.STATUS_COMPLETED)
if completed:
print("\nRecently Completed:")
for upload in completed[:5]: # Show last 5
filename = upload['filename']
results = upload.get('results', {})
score = results.get('score', 'N/A')
print(f"{filename} - Score: {score}")
# Show failed uploads
failed = self.state_manager.get_uploads_by_status(StateManager.STATUS_FAILED)
if failed:
print("\nFailed:")
for upload in failed:
filename = upload['filename']
errors = upload.get('errors', [])
error_msg = errors[-1] if errors else 'Unknown error'
print(f"{filename}")
print(f" Error: {error_msg}")
print("=" * 60 + "\n")
def poll_until_complete(self, interval_minutes: int = 30, max_hours: int = 48) -> None:
"""
Continuously poll for status updates
Args:
interval_minutes: Time between checks
max_hours: Maximum time to wait before giving up
"""
start_time = datetime.now()
max_seconds = max_hours * 3600
interval_seconds = interval_minutes * 60
self.logger.info(f"Starting polling (interval: {interval_minutes}min, max: {max_hours}h)")
try:
while True:
# Check elapsed time
elapsed = (datetime.now() - start_time).total_seconds()
if elapsed > max_seconds:
self.logger.info(f"Max wait time ({max_hours}h) exceeded")
break
# Check all processing uploads
summary = self.check_all_processing()
# Print update
self.logger.info("\n" + "-" * 60)
self.logger.info(f"Poll summary: {summary['completed']} completed, "
f"{summary['processing']} still processing")
self.logger.info(f"Elapsed: {elapsed/3600:.1f}h / {max_hours}h")
self.logger.info("-" * 60)
# If nothing processing, we're done
if summary['processing'] == 0:
self.logger.info("All uploads complete!")
break
# Wait for next check
self.logger.info(f"Next check in {interval_minutes} minutes...")
time.sleep(interval_seconds)
except KeyboardInterrupt:
self.logger.info("\nPolling interrupted by user")
self.logger.info("State has been saved. You can resume polling later.")
def main():
"""CLI entry point"""
parser = argparse.ArgumentParser(
description='Check preflight status and retrieve results'
)
parser.add_argument('--file', help='Check specific file')
parser.add_argument('--poll', action='store_true',
help='Continuously poll until complete')
parser.add_argument('--interval', type=int, default=30,
help='Polling interval in minutes (default: 30)')
parser.add_argument('--summary', action='store_true',
help='Show summary only')
args = parser.parse_args()
# Load configuration
try:
config = load_config()
except Exception as e:
print(f"Error loading configuration: {e}")
sys.exit(1)
# Initialize checker
checker = StatusChecker(config)
# Execute based on arguments
if args.summary:
checker.print_summary()
elif args.file:
try:
result = checker.check_status(args.file)
print(f"\nStatus: {result.get('status', 'unknown')}")
if result.get('status') == 'completed':
print(f"Score: {result.get('score', 'N/A')}")
print(f"Scorecard URL: {result.get('scorecard_url', 'N/A')}")
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
elif args.poll:
checker.poll_until_complete(interval_minutes=args.interval)
else:
# Default: check all and show summary
checker.check_all_processing()
checker.print_summary()
if __name__ == '__main__':
main()