paper-dynasty-card-creation/custom_cards/submit_luan_arroto.py
Cal Corum 0a17745389 Run black and ruff across entire codebase
Standardize formatting with black and apply ruff auto-fixes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:24:33 -05:00

315 lines
10 KiB
Python

"""
Submit Luan Arroto to Paper Dynasty Database
FINAL VERSION with:
- Platoon splits (better vs RHP)
- No triples
- Custom groundball ratios (3:2:1 vs L, 3:3:1 vs R)
- Spray chart variation (10% pull vs L, 33% pull vs R)
"""
import asyncio
from custom_cards.archetype_definitions import BatterArchetype
from custom_cards.archetype_calculator import (
BatterRatingCalculator,
calculate_total_ops,
)
from db_calls import db_get, db_post, db_patch, db_put
from creation_helpers import mlbteam_and_franchise
import boto3
from datetime import datetime
import aiohttp
# AWS Configuration
AWS_BUCKET_NAME = "paper-dynasty"
AWS_REGION = "us-east-1"
S3_BASE_URL = f"https://{AWS_BUCKET_NAME}.s3.{AWS_REGION}.amazonaws.com"
# Configuration
CARDSET_NAME = "2005 Custom"
CARDSET_ID = 29
SEASON = 2005
TEAM_ABBREV = "FA" # Free Agent
PLAYER_DESCRIPTION = "2005 Custom"
async def main():
"""Create Luan Arroto with final specifications."""
print("\n" + "=" * 70)
print("SUBMITTING LUAN ARROTO TO DATABASE")
print("=" * 70)
# Step 1: Design archetype
print("\nCalculating ratings...")
archetype = BatterArchetype(
name="Luan Arroto Final",
description="Contact hitter with platoon advantage and spray approach",
avg_vs_r=0.310,
obp_vs_r=0.450,
slg_vs_r=0.385,
bb_pct_vs_r=0.14,
k_pct_vs_r=0.10,
avg_vs_l=0.285,
obp_vs_l=0.455,
slg_vs_l=0.355,
bb_pct_vs_l=0.17,
k_pct_vs_l=0.09,
hr_per_hit=0.025,
triple_per_hit=0.00,
double_per_hit=0.22,
gb_pct=0.48,
fb_pct=0.27,
ld_pct=0.25,
hard_pct=0.28,
med_pct=0.52,
soft_pct=0.20,
pull_pct=0.215,
center_pct=0.35,
oppo_pct=0.435,
ifh_pct=0.09,
hr_fb_pct=0.06,
speed_rating=8,
steal_jump=3,
xbt_pct=0.58,
hit_run_skill=9,
primary_positions=["1B", "RF"],
defensive_rating=6,
)
# Step 2: Calculate ratings
calc = BatterRatingCalculator(archetype)
ratings = calc.calculate_ratings(battingcard_id=0)
baserunning = calc.calculate_baserunning()
# Step 3: Apply customizations
ratings[1]["pull_rate"] = 0.33 # vs R
ratings[0]["pull_rate"] = 0.10 # vs L
for rating in ratings:
rating["slap_rate"] = 1.0 - rating["pull_rate"] - rating["center_rate"]
baserunning.update(
{
"steal_low": 3,
"steal_high": 20,
"steal_auto": 0,
"steal_jump": 0.0277777,
"running": 10,
}
)
# Step 4: Custom groundball ratios
vl = ratings[0]
vr = ratings[1]
total_gb_vl = vl["groundout_a"] + vl["groundout_b"] + vl["groundout_c"]
total_gb_vr = vr["groundout_a"] + vr["groundout_b"] + vr["groundout_c"]
# vs L: 3:2:1 ratio
vl["groundout_a"] = (3 / 6) * total_gb_vl
vl["groundout_b"] = (2 / 6) * total_gb_vl
vl["groundout_c"] = (1 / 6) * total_gb_vl
# vs R: 3:3:1 ratio
vr["groundout_a"] = (3 / 7) * total_gb_vr
vr["groundout_b"] = (3 / 7) * total_gb_vr
vr["groundout_c"] = (1 / 7) * total_gb_vr
# Verify
total_ops = calculate_total_ops(vl, vr, is_pitcher=False)
print(f" ✓ Combined OPS: {total_ops:.3f}")
print(f" ✓ ISO vs L: {vl['slg']-vl['avg']:.3f}")
print(f" ✓ ISO vs R: {vr['slg']-vr['avg']:.3f}")
print(" ✓ Groundball ratios: vs L (3:2:1), vs R (3:3:1)")
# Step 5: Use existing cardset
print(f"\nUsing cardset '{CARDSET_NAME}' (ID: {CARDSET_ID})...")
cardset = {"id": CARDSET_ID, "name": CARDSET_NAME, "season": SEASON}
print(f" ✓ Cardset ID: {cardset['id']}")
# Step 6: Get team info
print(f"\nGetting team info for '{TEAM_ABBREV}'...")
mlb_team_id, franchise_id = mlbteam_and_franchise(TEAM_ABBREV)
print(f" ✓ MLB Team ID: {mlb_team_id}, Franchise ID: {franchise_id}")
# Step 7: Skip MLBPlayer (API constraint issues)
print("\nSkipping MLBPlayer creation...")
bbref_id = "custom_arrotl01"
mlbplayer_id = None # Leave null per user request
print(" ✓ Skipped MLBPlayer")
# Step 8: Create Player
print("\nCreating Player record...")
now = datetime.now()
release_date = f"{now.year}-{now.month}-{now.day}"
player_payload = {
"p_name": "Luan Arroto",
"cost": "88",
"image": "change-me",
"mlbclub": mlb_team_id,
"franchise": franchise_id,
"cardset_id": cardset["id"],
"set_num": 99999,
"rarity_id": 3, # Starter
"pos_1": "1B",
"description": PLAYER_DESCRIPTION,
"bbref_id": bbref_id,
"fangr_id": 0,
"mlbplayer_id": mlbplayer_id,
}
player = await db_post("players", payload=player_payload)
player_id = player["player_id"]
print(f" ✓ Created Player ID: {player_id}")
# Step 9: Create BattingCard
print("\nCreating BattingCard...")
batting_card_payload = {
"cards": [
{
"player_id": player_id,
"key_bbref": bbref_id,
"key_fangraphs": 0,
"key_mlbam": 0,
"key_retro": "",
"name_first": "Luan",
"name_last": "Arroto",
"steal_low": baserunning["steal_low"],
"steal_high": baserunning["steal_high"],
"steal_auto": baserunning["steal_auto"],
"steal_jump": baserunning["steal_jump"],
"hit_and_run": baserunning["hit_and_run"],
"running": baserunning["running"],
"hand": "L",
}
]
}
await db_put("battingcards", payload=batting_card_payload, timeout=10)
bc_query = await db_get("battingcards", params=[("player_id", player_id)])
battingcard_id = bc_query["cards"][0]["id"]
print(f" ✓ Created BattingCard ID: {battingcard_id}")
# Step 10: Create BattingCardRatings
print("\nCreating BattingCardRatings...")
for rating in ratings:
rating["battingcard_id"] = battingcard_id
rating["bat_hand"] = "L"
ratings_payload = {"ratings": ratings}
await db_put("battingcardratings", payload=ratings_payload, timeout=10)
print(" ✓ Created ratings for vs L and vs R")
# Step 11: Create CardPositions
print("\nCreating CardPositions...")
positions_payload = {
"positions": [
{
"player_id": player_id,
"position": "1B",
"range": 4,
"error": 10,
"arm": None,
"fielding_pct": None,
},
{
"player_id": player_id,
"position": "RF",
"range": 4,
"error": 6,
"arm": 2,
"fielding_pct": None,
},
]
}
await db_put("cardpositions", payload=positions_payload, timeout=10)
print(
" ✓ Created positions: 1B (range 4, error 10), RF (range 4, arm +2, error 6)"
)
# Step 12: Generate and upload card image
print("\nGenerating card image...")
api_image_url = f"https://pd.manticorum.com/api/v2/players/{player_id}/battingcard?d={release_date}"
try:
async with aiohttp.ClientSession() as session:
async with session.get(
api_image_url, timeout=aiohttp.ClientTimeout(total=15)
) as resp:
if resp.status == 200:
image_bytes = await resp.read()
image_size_kb = len(image_bytes) / 1024
print(f" ✓ Fetched card image ({image_size_kb:.1f} KB)")
# Upload to S3
print("\nUploading to S3...")
s3_client = boto3.client("s3", region_name=AWS_REGION)
cardset_str = f'{cardset["id"]:03d}'
s3_key = f"cards/cardset-{cardset_str}/player-{player_id}/battingcard.png"
s3_client.put_object(
Bucket=AWS_BUCKET_NAME,
Key=s3_key,
Body=image_bytes,
ContentType="image/png",
CacheControl="public, max-age=300",
Metadata={
"player-id": str(player_id),
"card-type": "batting",
"upload-date": datetime.now().isoformat(),
},
)
s3_url = f"{S3_BASE_URL}/{s3_key}?d={release_date}"
print(f" ✓ Uploaded to S3: {s3_key}")
# Update player with S3 URL
await db_patch(
"players", object_id=player_id, params=[("image", s3_url)]
)
print(" ✓ Updated player record with S3 URL")
else:
error_text = await resp.text()
print(
f" ⚠ Card generation failed (HTTP {resp.status}): {error_text}"
)
except Exception as e:
print(f" ⚠ Error during card generation/upload: {e}")
print(" Player created but image not uploaded")
# Summary
print("\n" + "=" * 70)
print("✓ LUAN ARROTO CREATED SUCCESSFULLY!")
print("=" * 70)
print(f"\nPlayer ID: {player_id}")
print(f"Cardset: {cardset['name']} (ID: {cardset['id']})")
print("Hand: L")
print("Positions: 1B (range 4, error 10), RF (range 4, arm +2, error 6)")
print("\nOffensive Profile:")
print(" vs LHP: .285/.455/.355 (.810 OPS)")
print(" vs RHP: .310/.450/.385 (.835 OPS)")
print(f" Combined OPS: {total_ops:.3f}")
print(" Pull rates: 10% vs L, 33% vs R")
print("\nGroundball Distribution:")
print(" vs LHP: 3:2:1 (A:9.85, B:6.57, C:3.28)")
print(" vs RHP: 3:3:1 (A:8.36, B:8.36, C:2.79)")
print("\nBaserunning:")
print(f" Running: {baserunning['running']}/17")
print(f" Steal: {baserunning['steal_low']}-{baserunning['steal_high']}")
print(f" Jump: {baserunning['steal_jump']:.7f}")
print("\nView card:")
print(f" PNG: {api_image_url}")
print(f" HTML: {api_image_url}&html=true")
if "s3_url" in locals():
print(f" S3: {s3_url}")
print("")
if __name__ == "__main__":
asyncio.run(main())