""" 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 from exceptions import logger # 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(f" ✓ 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(f" ✓ 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(f" ✓ 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(f" ✓ 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(f" ✓ 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(f" 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(f"Hand: L") print(f"Positions: 1B (range 4, error 10), RF (range 4, arm +2, error 6)") print(f"\nOffensive Profile:") print(f" vs LHP: .285/.455/.355 (.810 OPS)") print(f" vs RHP: .310/.450/.385 (.835 OPS)") print(f" Combined OPS: {total_ops:.3f}") print(f" Pull rates: 10% vs L, 33% vs R") print(f"\nGroundball Distribution:") print(f" vs LHP: 3:2:1 (A:9.85, B:6.57, C:3.28)") print(f" vs RHP: 3:3:1 (A:8.36, B:8.36, C:2.79)") print(f"\nBaserunning:") print(f" Running: {baserunning['running']}/17") print(f" Steal: {baserunning['steal_low']}-{baserunning['steal_high']}") print(f" Jump: {baserunning['steal_jump']:.7f}") print(f"\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())