- Add `pd-cards scouting upload` command to upload scouting CSVs to database server via SCP - Update CLAUDE.md with critical warning: scouting must always run for ALL cardsets - Document full workflow: `pd-cards scouting all && pd-cards scouting upload` Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
317 lines
10 KiB
Python
317 lines
10 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)
|
|
|
|
|
|
@app.command()
|
|
def upload(
|
|
input_dir: Path = typer.Option(Path("scouting"), "--input", "-i", help="Input directory with scouting CSVs"),
|
|
remote_host: str = typer.Option("sba-db", "--host", "-h", help="Remote host to upload to"),
|
|
remote_dir: str = typer.Option("/home/cal/container-data/pd-database/storage", "--remote-dir", "-r", help="Remote directory path"),
|
|
dry_run: bool = typer.Option(False, "--dry-run", "-n", help="Show what would be uploaded without uploading"),
|
|
):
|
|
"""
|
|
Upload scouting reports to remote server via SCP.
|
|
|
|
Uploads the generated CSV files to the Paper Dynasty database server.
|
|
|
|
Example:
|
|
pd-cards scouting upload
|
|
pd-cards scouting upload --dry-run
|
|
pd-cards scouting upload --host sba-db --remote-dir /path/to/storage
|
|
"""
|
|
import subprocess
|
|
|
|
console.print()
|
|
console.print("=" * 70)
|
|
console.print("[bold]SCOUTING REPORT UPLOAD[/bold]")
|
|
console.print("=" * 70)
|
|
console.print()
|
|
|
|
FILES_TO_UPLOAD = [
|
|
'batting-basic.csv',
|
|
'batting-ratings.csv',
|
|
'pitching-basic.csv',
|
|
'pitching-ratings.csv'
|
|
]
|
|
|
|
# Verify files exist
|
|
console.print("[bold]Verifying files...[/bold]")
|
|
missing_files = []
|
|
for filename in FILES_TO_UPLOAD:
|
|
filepath = input_dir / filename
|
|
if not filepath.exists():
|
|
missing_files.append(str(filepath))
|
|
else:
|
|
console.print(f" [green]✓[/green] {filepath}")
|
|
|
|
if missing_files:
|
|
console.print()
|
|
console.print(f"[red]Missing files:[/red]")
|
|
for f in missing_files:
|
|
console.print(f" [red]✗[/red] {f}")
|
|
console.print()
|
|
console.print("[yellow]Run 'pd-cards scouting all' first to generate reports.[/yellow]")
|
|
raise typer.Exit(1)
|
|
|
|
console.print()
|
|
console.print(f"[bold]Upload destination:[/bold] {remote_host}:{remote_dir}")
|
|
console.print()
|
|
|
|
if dry_run:
|
|
console.print("[yellow]DRY RUN - No files will be uploaded[/yellow]")
|
|
console.print()
|
|
for filename in FILES_TO_UPLOAD:
|
|
console.print(f" Would upload: {input_dir / filename}")
|
|
console.print()
|
|
console.print("=" * 70)
|
|
console.print("[bold yellow]✓ DRY RUN COMPLETE[/bold yellow]")
|
|
console.print("=" * 70)
|
|
return
|
|
|
|
# Upload files via SCP
|
|
console.print("[bold]Uploading files...[/bold]")
|
|
try:
|
|
local_files = [str(input_dir / f) for f in FILES_TO_UPLOAD]
|
|
scp_command = [
|
|
'scp',
|
|
*local_files,
|
|
f"{remote_host}:{remote_dir}/"
|
|
]
|
|
|
|
result = subprocess.run(
|
|
scp_command,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
|
|
for filename in FILES_TO_UPLOAD:
|
|
console.print(f" [green]✓[/green] {filename}")
|
|
|
|
console.print()
|
|
console.print("=" * 70)
|
|
console.print("[bold green]✓ SCOUTING UPLOAD COMPLETE[/bold green]")
|
|
console.print("=" * 70)
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
console.print(f"[red]Upload failed: {e}[/red]")
|
|
if e.stderr:
|
|
console.print(f"[red]{e.stderr}[/red]")
|
|
raise typer.Exit(1)
|
|
except FileNotFoundError:
|
|
console.print("[red]Error: 'scp' command not found. Ensure SSH is installed.[/red]")
|
|
raise typer.Exit(1)
|