paper-dynasty-card-creation/custom_cards/submit_tony_smehrik.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

319 lines
11 KiB
Python

"""
Submit Tony Smehrik to Paper Dynasty Database
Custom pitcher with:
- Hand: Left
- Position: SP/RP (starter_rating: 5, relief_rating: 5)
- Combined OPS: 0.585
- OPS vs L: 0.465 (dominant same-side)
- OPS vs R: 0.645 (weaker opposite-side)
- K-rate vs L: ~20% (average)
- K-rate vs R: ~8% (very low)
- FB% vs L: ~44% (average)
- FB% vs R: ~56% (high)
- Team: Custom Ballplayers
- Cardset ID: 29
NO AWS UPLOAD - preview via PD API first
"""
import asyncio
from datetime import datetime
from db_calls import db_get, db_post, db_patch, db_put
from custom_cards.tony_smehrik_preview import calculate_pitcher_rating, DEFAULT_XCHECKS
# Configuration
CARDSET_ID = 29
CARDSET_NAME = "2005 Custom"
SEASON = 2005
PLAYER_DESCRIPTION = "2005 Custom"
# Player info
PLAYER_NAME_FIRST = "Tony"
PLAYER_NAME_LAST = "Smehrik"
HAND = "L"
STARTER_RATING = 5
RELIEF_RATING = 5
CLOSER_RATING = None
async def main():
"""Create Tony Smehrik pitcher card in database."""
print("\n" + "="*70)
print("SUBMITTING TONY SMEHRIK TO DATABASE")
print("="*70)
# Step 1: Calculate ratings (same as preview)
print("\nCalculating ratings...")
pit_hand = 'L'
vl = calculate_pitcher_rating(
vs_hand='L',
pit_hand=pit_hand,
avg=0.185,
obp=0.240,
slg=0.225,
bb_pct=0.055,
k_pct=0.20,
hr_per_hit=0.04,
triple_per_hit=0.02,
double_per_hit=0.22,
fb_pct=0.35,
gb_pct=0.45,
hard_pct=0.30,
med_pct=0.50,
soft_pct=0.20,
oppo_pct=0.26,
hr_fb_pct=0.06,
)
vr = calculate_pitcher_rating(
vs_hand='R',
pit_hand=pit_hand,
avg=0.245,
obp=0.305,
slg=0.340,
bb_pct=0.075,
k_pct=0.08,
hr_per_hit=0.07,
triple_per_hit=0.02,
double_per_hit=0.24,
fb_pct=0.45,
gb_pct=0.35,
hard_pct=0.34,
med_pct=0.46,
soft_pct=0.20,
oppo_pct=0.26,
hr_fb_pct=0.09,
)
# Apply same manual adjustments as preview
# HR adjustments: half of all OB chances go to HR
vl_total_hits = (vl['homerun'] + vl['bp_homerun'] + vl['triple'] +
vl['double_three'] + vl['double_two'] + vl['double_cf'] +
vl['single_two'] + vl['single_one'] + vl['single_center'] + vl['bp_single'])
vl_total_ob = vl_total_hits + vl['walk'] + vl['hbp']
vl_hr_total = vl_total_ob / 2
vl['bp_homerun'] = round(vl_hr_total / 2)
vl['homerun'] = round((vl_hr_total - vl['bp_homerun']) * 20) / 20
vl['hbp'] = 1.0
vr_total_hits = (vr['homerun'] + vr['bp_homerun'] + vr['triple'] +
vr['double_three'] + vr['double_two'] + vr['double_cf'] +
vr['single_two'] + vr['single_one'] + vr['single_center'] + vr['bp_single'])
vr_total_ob = vr_total_hits + vr['walk'] + vr['hbp']
vr_hr_total = vr_total_ob / 2
vr['bp_homerun'] = round(vr_hr_total / 2)
vr['homerun'] = round((vr_hr_total - vr['bp_homerun']) * 20) / 20
vr['hbp'] = 1.0
# Reduce singles to compensate for HR increase
vl_new_hr_total = vl['homerun'] + vl['bp_homerun']
vl_hr_increase = vl_new_hr_total
vl_singles_to_reduce = vl_hr_increase
reduce_from_one = min(vl['single_one'], vl_singles_to_reduce)
vl['single_one'] -= reduce_from_one
vl_singles_to_reduce -= reduce_from_one
reduce_from_two = min(vl['single_two'], vl_singles_to_reduce)
vl['single_two'] -= reduce_from_two
vl_singles_to_reduce -= reduce_from_two
reduce_from_bp = min(vl['bp_single'], vl_singles_to_reduce)
vl['bp_single'] -= reduce_from_bp
vl_singles_to_reduce -= reduce_from_bp
if vl_singles_to_reduce > 0:
vl['double_cf'] = max(0, vl['double_cf'] - vl_singles_to_reduce)
vr_new_hr_total = vr['homerun'] + vr['bp_homerun']
vr_old_hr_total = 0.80 + 1.00
vr_hr_increase = vr_new_hr_total - vr_old_hr_total
vr_singles_to_reduce = vr_hr_increase
reduce_from_one = min(vr['single_one'], vr_singles_to_reduce)
vr['single_one'] -= reduce_from_one
vr_singles_to_reduce -= reduce_from_one
reduce_from_two = min(vr['single_two'], vr_singles_to_reduce)
vr['single_two'] -= reduce_from_two
vr_singles_to_reduce -= reduce_from_two
reduce_from_bp = min(vr['bp_single'], vr_singles_to_reduce)
vr['bp_single'] -= reduce_from_bp
vr_singles_to_reduce -= reduce_from_bp
if vr_singles_to_reduce > 0:
vr['double_cf'] = max(0, vr['double_cf'] - vr_singles_to_reduce)
# Force BP-SI values
vl['bp_single'] = 5.0
vr['bp_single'] = 3.0
# Adjust strikeouts to hit 108
for rating in [vl, vr]:
total = sum([
rating['homerun'], rating['bp_homerun'], rating['triple'],
rating['double_three'], rating['double_two'], rating['double_cf'],
rating['single_two'], rating['single_one'], rating['single_center'], rating['bp_single'],
rating['hbp'], rating['walk'], rating['strikeout'],
rating['flyout_lf_b'], rating['flyout_cf_b'], rating['flyout_rf_b'],
rating['groundout_a'], rating['groundout_b'],
rating['xcheck_p'], rating['xcheck_c'], rating['xcheck_1b'], rating['xcheck_2b'],
rating['xcheck_3b'], rating['xcheck_ss'], rating['xcheck_lf'], rating['xcheck_cf'], rating['xcheck_rf']
])
diff = 108 - total
rating['strikeout'] = round((rating['strikeout'] + diff) * 20) / 20
# Calculate OPS for verification
ops_vl = vl['obp'] + vl['slg']
ops_vr = vr['obp'] + vr['slg']
combined_ops = (ops_vr + ops_vl + max(ops_vl, ops_vr)) / 3
print(f" OPS vs L: {ops_vl:.3f}")
print(f" OPS vs R: {ops_vr:.3f}")
print(f" Combined OPS: {combined_ops:.3f}")
# Step 2: Verify cardset exists
print(f"\nVerifying cardset '{CARDSET_NAME}' (ID: {CARDSET_ID})...")
c_query = await db_get('cardsets', params=[('id', CARDSET_ID)])
if c_query['count'] == 0:
print(f" ERROR: Cardset ID {CARDSET_ID} not found!")
return
print(f" ✓ Cardset verified")
# Step 3: Create Player record
print("\nCreating Player record...")
now = datetime.now()
release_date = f"{now.year}-{now.month}-{now.day}"
bbref_id = f"custom_{PLAYER_NAME_LAST.lower()}{PLAYER_NAME_FIRST[0].lower()}01"
player_payload = {
'p_name': f'{PLAYER_NAME_FIRST} {PLAYER_NAME_LAST}',
'cost': '100', # Default cost
'image': 'change-me',
'mlbclub': 'Custom Ballplayers',
'franchise': 'Custom Ballplayers',
'cardset_id': CARDSET_ID,
'set_num': 99999,
'rarity_id': 3, # Starter rarity
'pos_1': 'SP',
'pos_2': 'RP',
'description': PLAYER_DESCRIPTION,
'bbref_id': bbref_id,
'fangr_id': 0,
'mlbplayer_id': None,
'is_custom': True,
}
player = await db_post('players', payload=player_payload)
player_id = player['player_id']
print(f" ✓ Created Player ID: {player_id}")
# Update player with API image URL for preview
api_image_url = f"https://pd.manticorum.com/api/v2/players/{player_id}/pitchingcard?d={release_date}"
await db_patch('players', object_id=player_id, params=[('image', api_image_url)])
print(f" ✓ Updated Player with API image URL")
# Step 4: Create PitchingCard
print("\nCreating PitchingCard...")
pitching_card_payload = {
'cards': [{
'player_id': player_id,
'key_bbref': bbref_id,
'key_fangraphs': 0,
'key_mlbam': 0,
'key_retro': '',
'name_first': PLAYER_NAME_FIRST,
'name_last': PLAYER_NAME_LAST,
'hand': HAND,
'starter_rating': STARTER_RATING,
'relief_rating': RELIEF_RATING,
'closer_rating': CLOSER_RATING,
}]
}
await db_put('pitchingcards', payload=pitching_card_payload, timeout=10)
# Get the created card ID
pc_query = await db_get('pitchingcards', params=[('player_id', player_id)])
pitchingcard_id = pc_query['cards'][0]['id']
print(f" ✓ Created PitchingCard ID: {pitchingcard_id}")
# Step 5: Create PitchingCardRatings
print("\nCreating PitchingCardRatings...")
# Build ratings payload (remove display-only fields)
def build_rating_payload(rating, pitchingcard_id):
return {
'pitchingcard_id': pitchingcard_id,
'vs_hand': rating['vs_hand'],
'homerun': rating['homerun'],
'bp_homerun': rating['bp_homerun'],
'triple': rating['triple'],
'double_three': rating['double_three'],
'double_two': rating['double_two'],
'double_cf': rating['double_cf'],
'single_two': rating['single_two'],
'single_one': rating['single_one'],
'single_center': rating['single_center'],
'bp_single': rating['bp_single'],
'hbp': rating['hbp'],
'walk': rating['walk'],
'strikeout': rating['strikeout'],
'flyout_lf_b': rating['flyout_lf_b'],
'flyout_cf_b': rating['flyout_cf_b'],
'flyout_rf_b': rating['flyout_rf_b'],
'groundout_a': rating['groundout_a'],
'groundout_b': rating['groundout_b'],
'xcheck_p': rating['xcheck_p'],
'xcheck_c': rating['xcheck_c'],
'xcheck_1b': rating['xcheck_1b'],
'xcheck_2b': rating['xcheck_2b'],
'xcheck_3b': rating['xcheck_3b'],
'xcheck_ss': rating['xcheck_ss'],
'xcheck_lf': rating['xcheck_lf'],
'xcheck_cf': rating['xcheck_cf'],
'xcheck_rf': rating['xcheck_rf'],
}
ratings_payload = {
'ratings': [
build_rating_payload(vl, pitchingcard_id),
build_rating_payload(vr, pitchingcard_id),
]
}
await db_put('pitchingcardratings', payload=ratings_payload, timeout=10)
print(f" ✓ Created ratings for vs L and vs R")
# Step 6: Create CardPositions
print("\nCreating CardPositions...")
positions_payload = {
'positions': [
{
'player_id': player_id,
'position': 'P',
}
]
}
await db_put('cardpositions', payload=positions_payload, timeout=10)
print(f" ✓ Created position record: P")
# Summary
print("\n" + "="*70)
print("✓ TONY SMEHRIK CREATED SUCCESSFULLY!")
print("="*70)
print(f"\nPlayer ID: {player_id}")
print(f"PitchingCard ID: {pitchingcard_id}")
print(f"Cardset: {CARDSET_NAME} (ID: {CARDSET_ID})")
print(f"Hand: {HAND}")
print(f"Starter Rating: {STARTER_RATING} | Relief Rating: {RELIEF_RATING}")
print(f"\nOffensive Profile (OPS Against):")
print(f" vs LHB: .185/.240/.225 ({ops_vl:.3f} OPS)")
print(f" vs RHB: .245/.305/.340 ({ops_vr:.3f} OPS)")
print(f" Combined: {combined_ops:.3f}")
print(f"\nPreview card (NO S3 upload yet):")
print(f" PNG: {api_image_url}")
print(f" HTML: {api_image_url}&html=true")
print("")
if __name__ == "__main__":
asyncio.run(main())