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>
216 lines
6.9 KiB
Python
216 lines
6.9 KiB
Python
"""
|
|
Scouting report generation commands.
|
|
|
|
Commands for generating scouting reports and ratings comparisons.
|
|
"""
|
|
|
|
import asyncio
|
|
from pathlib import Path
|
|
from typing import Optional, List
|
|
|
|
import typer
|
|
from rich.console import Console
|
|
|
|
app = typer.Typer(no_args_is_help=True)
|
|
console = Console()
|
|
|
|
|
|
@app.command()
|
|
def batters(
|
|
cardset_ids: Optional[List[int]] = typer.Option(None, "--cardset-id", "-c", help="Cardset ID(s) to include (can specify multiple)"),
|
|
output_dir: Path = typer.Option(Path("scouting"), "--output", "-o", help="Output directory"),
|
|
):
|
|
"""
|
|
Generate batting scouting reports.
|
|
|
|
Creates CSV files with batting ratings and comparisons.
|
|
|
|
Example:
|
|
pd-cards scouting batters --cardset-id 27 --cardset-id 29
|
|
"""
|
|
console.print()
|
|
console.print("=" * 70)
|
|
console.print("[bold]BATTING SCOUTING REPORT[/bold]")
|
|
console.print("=" * 70)
|
|
|
|
if cardset_ids:
|
|
console.print(f"Cardset IDs: {cardset_ids}")
|
|
else:
|
|
console.print("Cardset IDs: All")
|
|
|
|
console.print(f"Output: {output_dir}")
|
|
console.print()
|
|
|
|
try:
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
|
|
import scouting_batters as sb
|
|
import copy
|
|
|
|
async def run_scouting():
|
|
# Ensure output directory exists
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
console.print("Pulling scouting data...")
|
|
batting_dfs = await sb.get_scouting_dfs(cardset_ids or [])
|
|
console.print(f" Received {len(batting_dfs)} rows")
|
|
|
|
console.print("Generating basic scouting report...")
|
|
await sb.post_calc_basic(copy.deepcopy(batting_dfs))
|
|
console.print(f" [green]✓ Saved to {output_dir}/batting-basic.csv[/green]")
|
|
|
|
console.print("Generating ratings guide...")
|
|
await sb.post_calc_ratings(copy.deepcopy(batting_dfs))
|
|
console.print(f" [green]✓ Saved to {output_dir}/batting-ratings.csv[/green]")
|
|
|
|
console.print()
|
|
console.print("[green]✓ Batting scouting complete[/green]")
|
|
|
|
asyncio.run(run_scouting())
|
|
|
|
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 pitchers(
|
|
cardset_ids: Optional[List[int]] = typer.Option(None, "--cardset-id", "-c", help="Cardset ID(s) to include (can specify multiple)"),
|
|
output_dir: Path = typer.Option(Path("scouting"), "--output", "-o", help="Output directory"),
|
|
):
|
|
"""
|
|
Generate pitching scouting reports.
|
|
|
|
Creates CSV files with pitching ratings and comparisons.
|
|
|
|
Example:
|
|
pd-cards scouting pitchers --cardset-id 27 --cardset-id 29
|
|
"""
|
|
console.print()
|
|
console.print("=" * 70)
|
|
console.print("[bold]PITCHING SCOUTING REPORT[/bold]")
|
|
console.print("=" * 70)
|
|
|
|
if cardset_ids:
|
|
console.print(f"Cardset IDs: {cardset_ids}")
|
|
else:
|
|
console.print("Cardset IDs: All")
|
|
|
|
console.print(f"Output: {output_dir}")
|
|
console.print()
|
|
|
|
try:
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
|
|
import scouting_pitchers as sp
|
|
import copy
|
|
|
|
async def run_scouting():
|
|
# Ensure output directory exists
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
console.print("Pulling scouting data...")
|
|
pitching_dfs = await sp.get_scouting_dfs(cardset_ids or [])
|
|
console.print(f" Received {len(pitching_dfs)} rows")
|
|
|
|
console.print("Generating basic scouting report...")
|
|
await sp.post_calc_basic(copy.deepcopy(pitching_dfs))
|
|
console.print(f" [green]✓ Saved to {output_dir}/pitching-basic.csv[/green]")
|
|
|
|
console.print("Generating ratings guide...")
|
|
await sp.post_calc_ratings(copy.deepcopy(pitching_dfs))
|
|
console.print(f" [green]✓ Saved to {output_dir}/pitching-ratings.csv[/green]")
|
|
|
|
console.print()
|
|
console.print("[green]✓ Pitching scouting complete[/green]")
|
|
|
|
asyncio.run(run_scouting())
|
|
|
|
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 all(
|
|
cardset_ids: Optional[List[int]] = typer.Option(None, "--cardset-id", "-c", help="Cardset ID(s) to include"),
|
|
output_dir: Path = typer.Option(Path("scouting"), "--output", "-o", help="Output directory"),
|
|
):
|
|
"""
|
|
Generate all scouting reports (batters and pitchers).
|
|
|
|
Example:
|
|
pd-cards scouting all --cardset-id 27
|
|
"""
|
|
console.print()
|
|
console.print("=" * 70)
|
|
console.print("[bold]FULL SCOUTING REPORT[/bold]")
|
|
console.print("=" * 70)
|
|
console.print()
|
|
|
|
# Run batters
|
|
console.print("[bold]Phase 1: Batters[/bold]")
|
|
try:
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
|
|
import scouting_batters as sb
|
|
import scouting_pitchers as sp
|
|
import copy
|
|
|
|
async def run_all_scouting():
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Batters
|
|
console.print("Pulling batting data...")
|
|
batting_dfs = await sb.get_scouting_dfs(cardset_ids or [])
|
|
console.print(f" Received {len(batting_dfs)} batter rows")
|
|
|
|
await sb.post_calc_basic(copy.deepcopy(batting_dfs))
|
|
console.print(f" [green]✓ batting-basic.csv[/green]")
|
|
|
|
await sb.post_calc_ratings(copy.deepcopy(batting_dfs))
|
|
console.print(f" [green]✓ batting-ratings.csv[/green]")
|
|
|
|
console.print()
|
|
console.print("[bold]Phase 2: Pitchers[/bold]")
|
|
|
|
# Pitchers
|
|
console.print("Pulling pitching data...")
|
|
pitching_dfs = await sp.get_scouting_dfs(cardset_ids or [])
|
|
console.print(f" Received {len(pitching_dfs)} pitcher rows")
|
|
|
|
await sp.post_calc_basic(copy.deepcopy(pitching_dfs))
|
|
console.print(f" [green]✓ pitching-basic.csv[/green]")
|
|
|
|
await sp.post_calc_ratings(copy.deepcopy(pitching_dfs))
|
|
console.print(f" [green]✓ pitching-ratings.csv[/green]")
|
|
|
|
console.print()
|
|
console.print("=" * 70)
|
|
console.print("[bold green]✓ ALL SCOUTING REPORTS COMPLETE[/bold green]")
|
|
console.print("=" * 70)
|
|
|
|
asyncio.run(run_all_scouting())
|
|
|
|
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)
|