claude-configs/skills/paper-dynasty/scripts/generate_summary.py
Cal Corum 8a1d15911f Initial commit: Claude Code configuration backup
Version control Claude Code configuration including:
- Global instructions (CLAUDE.md)
- User settings (settings.json)
- Custom agents (architect, designer, engineer, etc.)
- Custom skills (create-skill templates and workflows)

Excludes session data, secrets, cache, and temporary files per .gitignore.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 16:34:21 -06:00

281 lines
8.4 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Generate summary report for Paper Dynasty card update
Collects statistics and notable changes for release notes.
Usage:
python generate_summary.py <database_path> [--previous-db <path>]
"""
import sqlite3
import sys
import json
from pathlib import Path
from datetime import datetime
from typing import List, Dict, Optional, Tuple
RARITY_TIERS = {
"Reserve": 1,
"Replacement": 2,
"Starter": 3,
"All-Star": 4,
"MVP": 5,
"Hall of Fame": 6
}
def get_card_counts(cursor: sqlite3.Cursor) -> Dict[str, int]:
"""Get total card counts"""
batting = cursor.execute("SELECT COUNT(*) FROM batting_cards").fetchone()[0]
pitching = cursor.execute("SELECT COUNT(*) FROM pitching_cards").fetchone()[0]
return {"batting": batting, "pitching": pitching, "total": batting + pitching}
def get_new_players(cursor: sqlite3.Cursor, since_date: Optional[str] = None) -> int:
"""Count new players added since date"""
if not since_date:
# Default to last 7 days
query = """
SELECT COUNT(DISTINCT player_name)
FROM (
SELECT player_name, created_date FROM batting_cards
WHERE created_date >= date('now', '-7 days')
UNION
SELECT player_name, created_date FROM pitching_cards
WHERE created_date >= date('now', '-7 days')
)
"""
else:
query = f"""
SELECT COUNT(DISTINCT player_name)
FROM (
SELECT player_name FROM batting_cards
WHERE created_date >= '{since_date}'
UNION
SELECT player_name FROM pitching_cards
WHERE created_date >= '{since_date}'
)
"""
try:
return cursor.execute(query).fetchone()[0]
except sqlite3.OperationalError:
# created_date column might not exist
return 0
def get_rarity_changes(
current_cursor: sqlite3.Cursor,
previous_db_path: Optional[Path] = None,
threshold: int = 2
) -> Tuple[List[Dict], List[Dict]]:
"""
Compare rarity changes between current and previous database.
Returns (upgrades, downgrades) where each is a list of dicts with:
- player_name
- card_id
- old_rarity
- new_rarity
- change (tier difference)
"""
if not previous_db_path or not previous_db_path.exists():
return [], []
prev_conn = sqlite3.connect(previous_db_path)
prev_cursor = prev_conn.cursor()
upgrades = []
downgrades = []
# Compare batting cards
query = """
SELECT
c.player_name,
c.card_id,
p.rarity as old_rarity,
c.rarity as new_rarity
FROM batting_cards c
JOIN prev.batting_cards p ON c.card_id = p.card_id
WHERE c.rarity != p.rarity
"""
try:
current_cursor.execute("ATTACH DATABASE ? AS prev", (str(previous_db_path),))
changes = current_cursor.execute(query).fetchall()
for name, card_id, old_rarity, new_rarity in changes:
old_tier = RARITY_TIERS.get(old_rarity, 0)
new_tier = RARITY_TIERS.get(new_rarity, 0)
change = new_tier - old_tier
if abs(change) >= threshold:
record = {
"player_name": name,
"card_id": card_id,
"old_rarity": old_rarity,
"new_rarity": new_rarity,
"change": change
}
if change > 0:
upgrades.append(record)
else:
downgrades.append(record)
current_cursor.execute("DETACH DATABASE prev")
except sqlite3.OperationalError as e:
print(f"Warning: Could not compare rarity changes: {e}", file=sys.stderr)
prev_conn.close()
# Sort by magnitude of change
upgrades.sort(key=lambda x: x['change'], reverse=True)
downgrades.sort(key=lambda x: x['change'])
return upgrades, downgrades
def get_date_range(card_creation_dir: Path) -> Tuple[str, str]:
"""Extract date range from retrosheet_data.py"""
retrosheet_file = card_creation_dir / "retrosheet_data.py"
if not retrosheet_file.exists():
return "Unknown", "Unknown"
content = retrosheet_file.read_text()
start_date = "Unknown"
end_date = "Unknown"
for line in content.split('\n'):
if 'START_DATE' in line and '=' in line:
start_date = line.split('=')[1].strip().strip('"\'')
elif 'END_DATE' in line and '=' in line:
end_date = line.split('=')[1].strip().strip('"\'')
return start_date, end_date
def generate_markdown_summary(
counts: Dict[str, int],
new_players: int,
upgrades: List[Dict],
downgrades: List[Dict],
date_range: Tuple[str, str],
csv_files: List[str]
) -> str:
"""Generate markdown summary report"""
today = datetime.now().strftime('%Y-%m-%d')
start_date, end_date = date_range
lines = [
f"# Paper Dynasty Card Update - {today}",
"",
"## Overview",
f"- **Total Cards**: {counts['batting']} batting, {counts['pitching']} pitching",
f"- **New Players**: {new_players}",
f"- **Data Range**: {start_date} to {end_date}",
"",
]
if upgrades or downgrades:
lines.append("## Notable Rarity Changes")
lines.append("")
if upgrades:
lines.append("### Upgrades")
for player in upgrades[:10]: # Max 10
tier_change = f"+{player['change']}" if player['change'] > 0 else str(player['change'])
lines.append(
f"- **{player['player_name']}** (ID: {player['card_id']}): "
f"{player['old_rarity']}{player['new_rarity']} ({tier_change} tiers)"
)
if len(upgrades) > 10:
lines.append(f"- *...and {len(upgrades) - 10} more*")
lines.append("")
if downgrades:
lines.append("### Downgrades")
for player in downgrades[:10]: # Max 10
tier_change = str(player['change']) # Already negative
lines.append(
f"- **{player['player_name']}** (ID: {player['card_id']}): "
f"{player['old_rarity']}{player['new_rarity']} ({tier_change} tiers)"
)
if len(downgrades) > 10:
lines.append(f"- *...and {len(downgrades) - 10} more*")
lines.append("")
lines.extend([
"## Files Generated",
"- ✅ Card images uploaded to S3",
"- ✅ Scouting CSVs transferred to database server",
])
for csv_file in csv_files:
lines.append(f" - {csv_file}")
lines.extend([
"",
"## Validation",
"- ✅ No negative groundball_b values",
"- ✅ All required fields populated",
"- ✅ Database integrity checks passed",
"",
"---",
"Generated by Claude Code - Paper Dynasty Cards Skill",
])
return "\n".join(lines)
def main():
if len(sys.argv) < 2:
print("Usage: python generate_summary.py <database_path> [--previous-db <path>]")
sys.exit(1)
db_path = Path(sys.argv[1])
previous_db = Path(sys.argv[3]) if len(sys.argv) > 3 and sys.argv[2] == '--previous-db' else None
if not db_path.exists():
print(f"❌ Database not found: {db_path}")
sys.exit(1)
# Connect to database
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Collect data
counts = get_card_counts(cursor)
new_players = get_new_players(cursor)
upgrades, downgrades = get_rarity_changes(cursor, previous_db, threshold=2)
# Get date range from retrosheet_data.py
card_creation_dir = db_path.parent
date_range = get_date_range(card_creation_dir)
# Get CSV files from scouting directory
scouting_dir = card_creation_dir / "scouting"
csv_files = sorted([f.name for f in scouting_dir.glob("*.csv")]) if scouting_dir.exists() else []
# Generate summary
summary = generate_markdown_summary(counts, new_players, upgrades, downgrades, date_range, csv_files)
# Print to stdout
print(summary)
# Save to file
output_file = Path.home() / ".claude" / "scratchpad" / f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_card_update_summary.md"
output_file.parent.mkdir(parents=True, exist_ok=True)
output_file.write_text(summary)
print(f"\n✅ Summary saved to: {output_file}", file=sys.stderr)
conn.close()
if __name__ == "__main__":
main()