- 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
319 lines
11 KiB
Python
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())
|