paper-dynasty-card-creation/custom_cards/submit_kalin_young.py
Cal Corum 1de8b1db2f Add custom card profiles, S3 upload with timestamp cache-busting, and CLI enhancements
- Add Sippie Swartzel custom batter profile (0.820 OPS, SS/RF, no HR power)
- Update Kalin Young profile (0.891 OPS, All-Star rarity)
- Update Admiral Ball Traits profile with innings field
- Fix S3 cache-busting to include Unix timestamp for same-day updates
- Add pd_cards/core/upload.py and scouting.py modules
- Add custom card submission scripts and documentation
- Add uv.lock for dependency tracking
2026-01-25 21:57:35 -06:00

204 lines
6.2 KiB
Python

"""
Submit Kalin Young updates to Paper Dynasty Database
Updates:
- BattingCardRatings: +1.55 walks/singles, -3.10 strikeouts (both splits)
- CardPositions: RF Range 2/Error 6, LF Range 4/Error 6
- Target OPS: 0.880 (from 0.837)
Does NOT upload to AWS - user will review first.
"""
import asyncio
from db_calls import db_get, db_put, db_patch
from datetime import datetime
# Player info
PLAYER_ID = 13009
BATTINGCARD_ID = 6072
CARDSET_ID = 29
# New ratings (from kalin_young_preview.py)
NEW_RATINGS = {
'L': { # vs LHP
'battingcard_id': BATTINGCARD_ID,
'vs_hand': 'L',
'homerun': 2.05,
'bp_homerun': 2.00,
'triple': 0.00,
'double_three': 0.00,
'double_two': 6.00,
'double_pull': 2.35,
'single_two': 5.05,
'single_one': 5.10,
'single_center': 5.80, # +1.55
'bp_single': 5.00,
'walk': 16.05, # +1.55
'hbp': 1.00,
'strikeout': 15.40, # -3.10
'lineout': 15.00,
'popout': 0.00,
'flyout_a': 0.00,
'flyout_bq': 0.75,
'flyout_lf_b': 3.65,
'flyout_rf_b': 3.95,
'groundout_a': 3.95,
'groundout_b': 10.00,
'groundout_c': 4.90,
'pull_rate': 0.33,
'center_rate': 0.37,
'slap_rate': 0.30,
},
'R': { # vs RHP
'battingcard_id': BATTINGCARD_ID,
'vs_hand': 'R',
'homerun': 2.10,
'bp_homerun': 1.00,
'triple': 1.25,
'double_three': 0.00,
'double_two': 6.35,
'double_pull': 1.80,
'single_two': 5.40,
'single_one': 4.95,
'single_center': 6.05, # +1.55
'bp_single': 5.00,
'walk': 14.55, # +1.55
'hbp': 2.00,
'strikeout': 17.90, # -3.10
'lineout': 12.00,
'popout': 1.00,
'flyout_a': 0.00,
'flyout_bq': 0.50,
'flyout_lf_b': 4.10,
'flyout_rf_b': 4.00,
'groundout_a': 4.00,
'groundout_b': 5.00,
'groundout_c': 9.05,
'pull_rate': 0.25,
'center_rate': 0.40,
'slap_rate': 0.35,
}
}
# New defensive positions
NEW_POSITIONS = [
{
'player_id': PLAYER_ID,
'position': 'RF',
'range': 2, # was 3
'error': 6, # was 7
'arm': 0,
'fielding_pct': None
},
{
'player_id': PLAYER_ID,
'position': 'LF',
'range': 4,
'error': 6, # was 7
'arm': 0,
'fielding_pct': None
}
]
def calc_ops(r):
"""Calculate OPS from ratings dict."""
avg = (r['homerun'] + r['bp_homerun']/2 + r['triple'] + r['double_three'] +
r['double_two'] + r['double_pull'] + r['single_two'] + r['single_one'] +
r['single_center'] + r['bp_single']/2) / 108
obp = avg + (r['hbp'] + r['walk']) / 108
slg = (r['homerun']*4 + r['bp_homerun']*2 + r['triple']*3 +
(r['double_three'] + r['double_two'] + r['double_pull'])*2 +
r['single_two'] + r['single_one'] + r['single_center'] + r['bp_single']/2) / 108
return obp + slg
def verify_total(r):
"""Verify ratings sum to 108."""
return sum([
r['homerun'], r['bp_homerun'], r['triple'],
r['double_three'], r['double_two'], r['double_pull'],
r['single_two'], r['single_one'], r['single_center'], r['bp_single'],
r['walk'], r['hbp'], r['strikeout'],
r['lineout'], r['popout'],
r['flyout_a'], r['flyout_bq'], r['flyout_lf_b'], r['flyout_rf_b'],
r['groundout_a'], r['groundout_b'], r['groundout_c']
])
async def main():
"""Update Kalin Young's batting card ratings and defensive positions."""
print("\n" + "="*70)
print("UPDATING KALIN YOUNG IN DATABASE")
print("="*70)
print(f"\nPlayer ID: {PLAYER_ID}")
print(f"BattingCard ID: {BATTINGCARD_ID}")
# Verify totals
print("\nVerifying ratings totals...")
for hand, ratings in NEW_RATINGS.items():
total = verify_total(ratings)
ops = calc_ops(ratings)
print(f" vs {hand}HP: Total={total:.2f}, OPS={ops:.3f}")
if abs(total - 108.0) > 0.01:
print(f" ⚠ WARNING: Total is not 108!")
return
# Calculate combined OPS
ops_vl = calc_ops(NEW_RATINGS['L'])
ops_vr = calc_ops(NEW_RATINGS['R'])
total_ops = (ops_vl + ops_vr + min(ops_vl, ops_vr)) / 3
print(f" Combined OPS: {total_ops:.3f}")
# Step 1: Update BattingCardRatings
print("\nUpdating BattingCardRatings...")
ratings_list = [NEW_RATINGS['L'], NEW_RATINGS['R']]
ratings_payload = {'ratings': ratings_list}
try:
result = await db_put('battingcardratings', payload=ratings_payload, timeout=10)
print(f" ✓ Updated ratings: {result}")
except Exception as e:
print(f" ✗ Error updating ratings: {e}")
return
# Step 2: Update CardPositions
print("\nUpdating CardPositions...")
positions_payload = {'positions': NEW_POSITIONS}
try:
result = await db_put('cardpositions', payload=positions_payload, timeout=10)
print(f" ✓ Updated positions: {result}")
except Exception as e:
print(f" ✗ Error updating positions: {e}")
return
# Summary
print("\n" + "="*70)
print("✓ KALIN YOUNG UPDATED SUCCESSFULLY!")
print("="*70)
print(f"\nPlayer ID: {PLAYER_ID}")
print(f"Cardset: 2005 Custom (ID: {CARDSET_ID})")
print(f"\nRating Changes:")
print(f" Walk vL: 14.50 → 16.05 (+1.55)")
print(f" Walk vR: 13.00 → 14.55 (+1.55)")
print(f" Single (Center) vL: 4.25 → 5.80 (+1.55)")
print(f" Single (Center) vR: 4.50 → 6.05 (+1.55)")
print(f" Strikeout vL: 18.50 → 15.40 (-3.10)")
print(f" Strikeout vR: 21.00 → 17.90 (-3.10)")
print(f"\nOPS: 0.837 → {total_ops:.3f}")
print(f"\nDefensive Updates:")
print(f" RF: Range 3→2, Error 7→6, Arm 0")
print(f" LF: Range 4, Error 7→6, Arm 0")
print(f"\n⚠️ AWS upload skipped - review card first:")
now = datetime.now()
release_date = f"{now.year}-{now.month}-{now.day}"
print(f" PNG: https://pd.manticorum.com/api/v2/players/{PLAYER_ID}/battingcard?d={release_date}")
print(f" HTML: https://pd.manticorum.com/api/v2/players/{PLAYER_ID}/battingcard?d={release_date}&html=true")
print("")
if __name__ == "__main__":
asyncio.run(main())