paper-dynasty-card-creation/pd_cards/commands/upload.py
Cal Corum 5b75a3d38f Implement CLI wrappers for live-series, retrosheet, scouting, upload
Migrated all major card creation workflows to pd-cards CLI:

live-series:
- update: Full FanGraphs/BBRef card generation with CLI options
- status: Show cardset status from database

retrosheet:
- process: Historical Retrosheet data processing
- arms: Generate outfield arm ratings from play-by-play
- validate: Check for position anomalies in cardsets
- defense: Fetch defensive stats from Baseball Reference

scouting:
- batters: Generate batting scouting reports
- pitchers: Generate pitching scouting reports
- all: Generate all reports at once

upload:
- s3: Upload card images to AWS S3
- check: Validate cards without uploading
- refresh: Re-generate and re-upload card images

Updated CLAUDE.md with comprehensive CLI documentation.
Legacy scripts remain available but CLI is now the primary interface.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 16:39:38 -06:00

223 lines
6.9 KiB
Python

"""
Card image upload commands.
Commands for uploading card images to AWS S3.
"""
import asyncio
from pathlib import Path
from typing import Optional
import typer
from rich.console import Console
app = typer.Typer(no_args_is_help=True)
console = Console()
@app.command()
def s3(
cardset: str = typer.Option(..., "--cardset", "-c", help="Cardset name to upload"),
start_id: Optional[int] = typer.Option(None, "--start-id", help="Player ID to start from (for resuming)"),
limit: Optional[int] = typer.Option(None, "--limit", "-l", help="Limit number of cards to process"),
html: bool = typer.Option(False, "--html", help="Upload HTML preview cards instead of PNG"),
skip_batters: bool = typer.Option(False, "--skip-batters", help="Skip batting cards"),
skip_pitchers: bool = typer.Option(False, "--skip-pitchers", help="Skip pitching cards"),
upload: bool = typer.Option(True, "--upload/--no-upload", help="Upload to S3"),
update_urls: bool = typer.Option(True, "--update-urls/--no-update-urls", help="Update player URLs in database"),
dry_run: bool = typer.Option(False, "--dry-run", "-n", help="Preview without uploading"),
):
"""
Upload card images to AWS S3.
Fetches card images from Paper Dynasty API and uploads to S3 bucket.
Example:
pd-cards upload s3 --cardset "2005 Live" --limit 10
"""
console.print()
console.print("=" * 70)
console.print(f"[bold]S3 UPLOAD - {cardset}[/bold]")
console.print("=" * 70)
console.print(f"Cardset: {cardset}")
if start_id:
console.print(f"Starting from player ID: {start_id}")
if limit:
console.print(f"Limit: {limit} cards")
if html:
console.print("Mode: HTML preview cards")
if skip_batters:
console.print("Skipping: Batting cards")
if skip_pitchers:
console.print("Skipping: Pitching cards")
console.print(f"Upload to S3: {upload and not dry_run}")
console.print(f"Update URLs: {update_urls and not dry_run}")
console.print()
if dry_run:
console.print("[yellow]DRY RUN - no uploads will be made[/yellow]")
console.print()
console.print("[green]Validation passed - ready to run[/green]")
raise typer.Exit(0)
try:
import sys
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
import check_cards_and_upload as ccu
# Configure the module's globals
ccu.CARDSET_NAME = cardset
ccu.START_ID = start_id
ccu.TEST_COUNT = limit if limit else 9999
ccu.HTML_CARDS = html
ccu.SKIP_BATS = skip_batters
ccu.SKIP_ARMS = skip_pitchers
ccu.UPLOAD_TO_S3 = upload
ccu.UPDATE_PLAYER_URLS = update_urls
# Re-initialize S3 client if uploading
if upload:
import boto3
ccu.s3_client = boto3.client('s3', region_name=ccu.AWS_REGION)
else:
ccu.s3_client = None
console.print("[bold]Starting S3 upload...[/bold]")
console.print()
asyncio.run(ccu.main([]))
console.print()
console.print("=" * 70)
console.print(f"[bold green]✓ S3 UPLOAD COMPLETE[/bold green]")
console.print("=" * 70)
except ImportError as e:
console.print(f"[red]Error importing modules: {e}[/red]")
raise typer.Exit(1)
except Exception as e:
console.print(f"[red]Error: {e}[/red]")
import traceback
traceback.print_exc()
raise typer.Exit(1)
@app.command()
def refresh(
cardset: str = typer.Option(..., "--cardset", "-c", help="Cardset name"),
limit: Optional[int] = typer.Option(None, "--limit", "-l", help="Limit number of cards"),
dry_run: bool = typer.Option(False, "--dry-run", "-n", help="Preview without refreshing"),
):
"""
Refresh card images for a cardset.
Re-generates and re-uploads card images.
Example:
pd-cards upload refresh --cardset "2005 Live" --limit 10
"""
console.print()
console.print("=" * 70)
console.print(f"[bold]CARD REFRESH - {cardset}[/bold]")
console.print("=" * 70)
console.print(f"Cardset: {cardset}")
if limit:
console.print(f"Limit: {limit} cards")
if dry_run:
console.print("[yellow]DRY RUN - no changes will be made[/yellow]")
console.print()
console.print("[green]Validation passed - ready to run[/green]")
raise typer.Exit(0)
try:
import sys
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
import refresh_cards as rc
# Configure the module
rc.CARDSET_NAME = cardset
rc.TEST_COUNT = limit if limit else 9999
console.print("[bold]Starting card refresh...[/bold]")
console.print()
asyncio.run(rc.main([]))
console.print()
console.print("=" * 70)
console.print(f"[bold green]✓ CARD REFRESH COMPLETE[/bold green]")
console.print("=" * 70)
except ImportError as e:
console.print(f"[red]Error importing modules: {e}[/red]")
console.print("Try: python refresh_cards.py")
raise typer.Exit(1)
except Exception as e:
console.print(f"[red]Error: {e}[/red]")
import traceback
traceback.print_exc()
raise typer.Exit(1)
@app.command()
def check(
cardset: str = typer.Option(..., "--cardset", "-c", help="Cardset name"),
limit: Optional[int] = typer.Option(None, "--limit", "-l", help="Limit number of cards to check"),
output_dir: Path = typer.Option(Path("data-output"), "--output", "-o", help="Output directory"),
):
"""
Check and validate card images without uploading.
Downloads card images and saves locally for review.
Example:
pd-cards upload check --cardset "2005 Live" --limit 10
"""
console.print()
console.print("=" * 70)
console.print(f"[bold]CARD CHECK - {cardset}[/bold]")
console.print("=" * 70)
console.print(f"Cardset: {cardset}")
if limit:
console.print(f"Limit: {limit} cards")
console.print(f"Output: {output_dir}")
console.print()
try:
import sys
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
import check_cards_and_upload as ccu
# Configure for check-only mode
ccu.CARDSET_NAME = cardset
ccu.START_ID = None
ccu.TEST_COUNT = limit if limit else 9999
ccu.HTML_CARDS = False
ccu.UPLOAD_TO_S3 = False
ccu.UPDATE_PLAYER_URLS = False
ccu.s3_client = None
console.print("[bold]Starting card check...[/bold]")
console.print()
asyncio.run(ccu.main([]))
console.print()
console.print("[green]✓ Card check complete[/green]")
except ImportError as e:
console.print(f"[red]Error importing modules: {e}[/red]")
raise typer.Exit(1)
except Exception as e:
console.print(f"[red]Error: {e}[/red]")
import traceback
traceback.print_exc()
raise typer.Exit(1)