diff --git a/calcs_pitcher.py b/calcs_pitcher.py index 03fee16..fccb14b 100644 --- a/calcs_pitcher.py +++ b/calcs_pitcher.py @@ -21,7 +21,7 @@ def get_pitcher_ratings(df_data) -> List[dict]: vr = PitchingCardRatingsModel( pitchingcard_id=df_data.pitchingcard_id, pit_hand=df_data.hand, - vs_hand='L', + vs_hand='R', all_hits=sanitize_chance_output((df_data['AVG_vR'] - 0.05) * 108), # Subtracting chances from BP results all_other_ob=sanitize_chance_output(108 * (df_data['BB_vR'] + df_data['HBP_vR']) / df_data['TBF_vR']), hard_rate=df_data['Hard%_vR'], @@ -48,6 +48,38 @@ def get_pitcher_ratings(df_data) -> List[dict]: logging.info(f'vR: All Hits: {vr.all_hits} / BP Singles: {vr.bp_single} / Single 2: {vr.single_two} / ' f'Single 1: {vr.single_one} / Single CF: {vr.single_center}') + vl.calculate_xbh(df_data['2B_vL'], df_data['3B_vL'], df_data['HR_vL'], df_data['HR/FB_vL']) + vr.calculate_xbh(df_data['2B_vR'], df_data['3B_vR'], df_data['HR_vR'], df_data['HR/FB_vR']) + + logging.debug(f'vL: All XBH: {vl.all_hits - vl.single_one - vl.single_two - vl.single_center - vl.bp_single} / ' + f'Double**: {vl.double_two} / Double(cf): {vl.double_cf} / Triple: {vl.triple} / ' + f'BP HR: {vl.bp_homerun} / ND HR: {vl.homerun}') + logging.debug(f'vR: All XBH: {vr.all_hits - vr.single_one - vr.single_two - vr.single_center - vr.bp_single} / ' + f'Double**: {vr.double_two} / Double(cf): {vr.double_cf} / Triple: {vr.triple} / ' + f'BP HR: {vr.bp_homerun} / ND HR: {vr.homerun}') + + vl.calculate_other_ob(df_data['BB_vL'], df_data['HBP_vL']) + vr.calculate_other_ob(df_data['BB_vR'], df_data['HBP_vR']) + + logging.info(f'vL: All other OB: {vl.all_other_ob} / HBP: {vl.hbp} / BB: {vl.walk} / ' + f'Total Chances: {vl.total_chances()}') + logging.info(f'vR: All other OB: {vr.all_other_ob} / HBP: {vr.hbp} / BB: {vr.walk} / ' + f'Total Chances: {vr.total_chances()}') + + vl.calculate_strikouts( + df_data['SO_vL'], df_data['TBF_vL'] - df_data['BB_vL'] - df_data['IBB_vL'] - df_data['HBP_vL'], df_data['H_vL']) + vr.calculate_strikouts( + df_data['SO_vR'], df_data['TBF_vR'] - df_data['BB_vR'] - df_data['IBB_vR'] - df_data['HBP_vR'], df_data['H_vR']) + + logging.info(f'vL: All Outs: {vl.all_outs} / Ks: {vl.strikeout} / Current Outs: {vl.total_outs()}') + logging.info(f'vR: All Outs: {vr.all_outs} / Ks: {vr.strikeout} / Current Outs: {vr.total_outs()}') + + vl.calculate_other_outs(df_data['FB%_vL'], df_data['GB%_vL'], df_data['Oppo%_vL']) + vr.calculate_other_outs(df_data['FB%_vR'], df_data['GB%_vR'], df_data['Oppo%_vR']) + + logging.info(f'vL: Total chances: {vl.total_chances()}') + logging.info(f'vR: Total chances: {vr.total_chances()}') + return [vl.custom_to_dict(), vr.custom_to_dict()] @@ -183,7 +215,142 @@ class PitchingCardRatingsModel(pydantic.BaseModel): self.rem_singles -= self.single_center self.rem_xbh = self.all_hits - self.single_center - self.single_one - self.single_two - self.bp_single - logging.info(f'remaining singles: {self.rem_singles}') + logging.info(f'remaining singles: {self.rem_singles} / total xbh: {self.rem_xbh}') + + def calculate_xbh(self, szn_doubles, szn_triples, szn_homeruns, hr_per_fb_rate): + szn_xbh = szn_doubles + szn_triples + szn_homeruns + if szn_xbh == 0: + return + + hr_rate = Decimal(szn_homeruns / szn_xbh) + tr_rate = Decimal(szn_triples / szn_xbh) + do_rate = Decimal(szn_doubles / szn_xbh) + logging.info(f'hr%: {hr_rate:.2f} / tr%: {tr_rate:.2f} / do%: {do_rate:.2f}') + + raw_do_chances = sanitize_chance_output(Decimal(self.rem_xbh * do_rate)) + logging.info(f'raw do chances: {raw_do_chances}') + self.double_two = raw_do_chances if self.soft_rate > .2 else Decimal(0) + self.double_cf = raw_do_chances - self.double_two + self.rem_xbh -= (self.double_two + self.double_cf + self.double_three) + logging.info(f'Double**: {self.double_two} / Double(cf): {self.double_cf} / rem xbh: {self.rem_xbh}') + + self.triple = sanitize_chance_output(Decimal(self.rem_xbh * tr_rate)) + self.rem_xbh -= self.triple + logging.info(f'Triple: {self.triple} / rem xbh: {self.rem_xbh}') + + raw_hr_chances = self.rem_xbh + logging.info(f'raw hr chances: {raw_hr_chances}') + + if hr_per_fb_rate < .08: + self.bp_homerun = sanitize_chance_output(raw_hr_chances, rounding=1.0) + elif hr_per_fb_rate > .28: + self.homerun = raw_hr_chances + elif hr_per_fb_rate > .18: + self.bp_homerun = sanitize_chance_output(raw_hr_chances * Decimal(0.4), rounding=1.0) + self.homerun = self.rem_xbh - self.bp_homerun + else: + self.bp_homerun = sanitize_chance_output(raw_hr_chances * Decimal(.75), rounding=1.0) + self.homerun = self.rem_xbh - self.bp_homerun + logging.info(f'BP HR: {self.bp_homerun} / ND HR: {self.homerun}') + + self.rem_xbh -= (self.bp_homerun + self.homerun) + logging.info(f'excess xbh: {self.rem_xbh}') + + if self.rem_xbh > 0: + if self.triple > 1: + logging.info(f'Passing {self.rem_xbh} xbh to triple') + self.triple += self.rem_xbh + self.rem_xbh = Decimal(0) + elif self.double_cf > 1: + logging.info(f'Passing {self.rem_xbh} xbh to double(cf)') + self.double_cf += self.rem_xbh + self.rem_xbh = Decimal(0) + elif self.double_two > 1: + logging.info(f'Passing {self.rem_xbh} xbh to double**') + self.double_two += self.rem_xbh + self.rem_xbh = Decimal(0) + elif self.single_two > 1: + logging.info(f'Passing {self.rem_xbh} xbh to single**') + self.single_two += self.rem_xbh + self.rem_xbh = Decimal(0) + elif self.single_center > 1: + logging.info(f'Passing {self.rem_xbh} xbh to single(cf)') + self.single_center += self.rem_xbh + self.rem_xbh = Decimal(0) + elif self.single_one > 1: + logging.info(f'Passing {self.rem_xbh} xbh to single*') + self.single_one += self.rem_xbh + self.rem_xbh = Decimal(0) + else: + logging.info(f'Passing {self.rem_xbh} xbh to other_ob') + self.all_other_ob += self.rem_xbh + + def calculate_other_ob(self, szn_walks, szn_hbp): + if szn_walks + szn_hbp == 0: + return + + self.hbp = sanitize_chance_output(self.all_other_ob * Decimal(szn_hbp / (szn_walks + szn_hbp)), rounding=1.0) + self.walk = self.all_other_ob - self.hbp + + def calculate_strikouts(self, szn_strikeouts, szn_ab, szn_hits): + self.strikeout = sanitize_chance_output(self.all_outs * Decimal((szn_strikeouts * 1.2) / (szn_ab - szn_hits))) + + def calculate_other_outs(self, fb_pct, gb_pct, oppo_pct): + rem_outs = Decimal(108) - self.total_chances() + + all_fo = sanitize_chance_output(rem_outs * Decimal(fb_pct)) + if self.pit_hand == 'L': + self.flyout_lf_b = sanitize_chance_output(all_fo * Decimal(oppo_pct)) + else: + self.flyout_rf_b = sanitize_chance_output(all_fo * Decimal(oppo_pct)) + self.flyout_cf_b = all_fo - self.flyout_lf_b - self.flyout_rf_b + rem_outs -= (self.flyout_lf_b + self.flyout_cf_b + self.flyout_rf_b) + + all_gb = rem_outs + self.groundout_a = sanitize_chance_output(all_gb * self.soft_rate) + self.groundout_b = sanitize_chance_output(all_gb - self.groundout_a) + + rem_chances = Decimal(108) - self.total_chances() + logging.info(f'Remaining outs: {rem_chances}') + + if self.strikeout > 1: + logging.info(f'Passing {rem_chances} outs to strikeouts') + self.strikeout += rem_chances + elif self.flyout_cf_b > 1: + logging.info(f'Passing {rem_chances} outs to fly(cf)') + self.flyout_cf_b += rem_chances + elif self.flyout_rf_b > 1: + logging.info(f'Passing {rem_chances} outs to fly(rf)') + self.flyout_rf_b += rem_chances + elif self.flyout_lf_b > 1: + logging.info(f'Passing {rem_chances} outs to fly(lf)') + self.flyout_lf_b += rem_chances + elif self.groundout_a > 1: + logging.info(f'Passing {rem_chances} outs to gbA') + self.groundout_a += rem_chances + elif self.single_one > 1: + logging.info(f'Passing {rem_chances} outs to single*') + self.single_one += rem_chances + elif self.single_center > 1: + logging.info(f'Passing {rem_chances} outs to single(cf)') + self.single_center += rem_chances + elif self.single_two > 1: + logging.info(f'Passing {rem_chances} outs to single**') + self.single_two += rem_chances + elif self.double_two > 1: + logging.info(f'Passing {rem_chances} outs to double**') + self.double_two += rem_chances + elif self.double_cf > 1: + logging.info(f'Passing {rem_chances} outs to double(cf)') + self.double_cf += rem_chances + elif self.triple > 1: + logging.info(f'Passing {rem_chances} outs to triple') + self.triple += rem_chances + elif self.homerun > 1: + logging.info(f'Passing {rem_chances} outs to homerun') + self.homerun += rem_chances + else: + raise ValueError(f'Could not complete card') def total_chances(chance_data): diff --git a/creation_helpers.py b/creation_helpers.py index d0692ce..e16071d 100644 --- a/creation_helpers.py +++ b/creation_helpers.py @@ -1,6 +1,7 @@ import csv import datetime import logging +import math from decimal import Decimal import pandas as pd @@ -478,6 +479,42 @@ async def pd_battingcardratings_df(cardset_id: int): # return pd.DataFrame(bcr_query['ratings']).rename(columns={'battingcard': 'battingcard_id'}) +async def pd_pitchingcardratings_df(cardset_id: int): + vl_query = await db_get( + 'pitchingcardratings', params=[('cardset_id', cardset_id), ('vs_hand', 'L'), ('short_output', True)]) + vr_query = await db_get( + 'pitchingcardratings', params=[('cardset_id', cardset_id), ('vs_hand', 'R'), ('short_output', True)]) + if 0 in [vl_query['count'], vr_query['count']]: + raise ValueError(f'No pitching card ratings returned from Paper Dynasty API') + vl = pd.DataFrame(vl_query['ratings']) + vr = pd.DataFrame(vr_query['ratings']) + ratings = (pd.merge(vl, vr, on='pitchingcard', suffixes=('_vL', '_vR')) + .rename(columns={'pitchingcard': 'pitchingcard_id'})) + + def get_total_ops(df_data): + ops_vl = df_data['obp_vL'] + df_data['slg_vL'] + ops_vr = df_data['obp_vR'] + df_data['slg_vR'] + return (ops_vr + ops_vl + min(ops_vl, ops_vr)) / 3 + ratings['total_OPS'] = ratings.apply(get_total_ops, axis=1) + + return ratings + + def new_rarity_id(df_data): + if df_data['total_OPS'] >= 1.2: + return 99 + elif df_data['total_OPS'] >= 1: + return 1 + elif df_data['total_OPS'] >= .9: + return 2 + elif df_data['total_OPS'] >= .8: + return 3 + elif df_data['total_OPS'] >= .7: + return 4 + else: + return 5 + ratings['new_rarity_id'] = ratings.apply(new_rarity_id, axis=1) + + def get_batting_stats(file_path: str = None, start_date: datetime.datetime = None, end_date: datetime.datetime = None): if file_path is not None: vl_basic = pd.read_csv(f'{file_path}vlhp-basic.csv').query('PA >= 20') @@ -863,5 +900,5 @@ def sanitize_chance_output(total_chances, min_chances=1.0, rounding=0.05): # r_val = mround(total_chances) if total_chances >= min_chances else 0 r_val = Decimal(total_chances) if total_chances >= min_chances else Decimal(0) logging.debug(f'r_val: {r_val}') - return Decimal(float(round(r_val / Decimal(rounding)) * Decimal(rounding))).quantize(Decimal("0.05")) + return Decimal(float(math.floor(r_val / Decimal(rounding)) * Decimal(rounding))).quantize(Decimal("0.05")) # return r_val.quantize(Decimal(rounding)) \ No newline at end of file diff --git a/live_series_update.py b/live_series_update.py index d821198..47e456f 100644 --- a/live_series_update.py +++ b/live_series_update.py @@ -6,6 +6,7 @@ import html5lib import logging import random import requests +import urllib.parse import calcs_batter as cba import calcs_defense as cde @@ -16,7 +17,7 @@ import pydantic import sys from creation_helpers import pd_players_df, get_batting_stats, pd_battingcards_df, pd_battingcardratings_df, \ - get_pitching_stats, get_pitching_peripherals, pd_pitchingcards_df + get_pitching_stats, get_pitching_peripherals, pd_pitchingcards_df, pd_pitchingcardratings_df from db_calls import db_get, db_put, db_post, db_patch from typing import Literal from bs4 import BeautifulSoup @@ -28,7 +29,7 @@ logging.basicConfig( format='%(asctime)s - card-creation - %(levelname)s - %(message)s', level=log_level ) -CARD_BASE_URL = 'https://pd.manticorum.com/api/players' +CARD_BASE_URL = 'https://pddev.manticorum.com/api/players' def sanitize_name(start_name: str) -> str: @@ -164,7 +165,8 @@ async def main(args): new_players.append({ 'p_name': f'{f_name} {l_name}', 'cost': 99999, - 'image': f'{CARD_BASE_URL}/{df_data["player_id"]}/battingcard?d={release_directory}', + 'image': f'{CARD_BASE_URL}/{df_data["player_id"]}/battingcard' + f'{urllib.parse.quote("?d=")}{release_directory}', 'mlbclub': 'None', 'franchise': 'None', 'cardset_id': cardset['id'], @@ -254,7 +256,7 @@ async def main(args): print(f'Calculating batting cards...') offense_stats.apply(create_batting_card, axis=1) print(f'Cards are complete.\n\nPosting cards now...') - if 'post_updates' not in arg_data or arg_data['post_updates'].lower() == 'true': + if 'post_batters' not in arg_data or arg_data['post_batters'].lower() == 'true': resp = await db_put('battingcards', payload={'cards': batting_cards}, timeout=30) print(f'Response: {resp}\n\nMatching batting card database IDs to player stats...') offense_stats = pd.merge( @@ -264,22 +266,22 @@ async def main(args): def create_positions(df_data): for pos_data in [(df_1b, '1b'), (df_2b, '2b'), (df_3b, '3b'), (df_ss, 'ss')]: - if df_data.name in pos_data[0].index: - logging.debug(f'Running {pos_data[1]} stats for {player_data.at[df_data.name, "p_name"]}') + if df_data['key_bbref'] in pos_data[0].index: + logging.debug(f'Running {pos_data[1]} stats for {player_data.at[df_data["key_bbref"], "p_name"]}') position_payload.append({ - "player_id": int(player_data.at[df_data.name, 'player_id']), + "player_id": int(player_data.at[df_data["key_bbref"], 'player_id']), "position": pos_data[1].upper(), - "innings": float(pos_data[0].at[df_data.name, 'Inn_def']), + "innings": float(pos_data[0].at[df_data["key_bbref"], 'Inn_def']), "range": cde.get_if_range( pos_code=pos_data[1], - tz_runs=int(pos_data[0].at[df_data.name, 'tz_runs_total']), + tz_runs=int(pos_data[0].at[df_data["key_bbref"], 'tz_runs_total']), r_dp=0, season_pct=season_pct ), "error": cde.get_any_error( pos_code=pos_data[1], - errors=int(pos_data[0].at[df_data.name, 'E_def']), - chances=int(pos_data[0].at[df_data.name, 'chances']), + errors=int(pos_data[0].at[df_data["key_bbref"], 'E_def']), + chances=int(pos_data[0].at[df_data["key_bbref"], 'chances']), season_pct=season_pct ) }) @@ -287,24 +289,24 @@ async def main(args): of_arms = [] of_payloads = [] for pos_data in [(df_lf, 'lf'), (df_cf, 'cf'), (df_rf, 'rf')]: - if df_data.name in pos_data[0].index: + if df_data["key_bbref"] in pos_data[0].index: of_payloads.append({ - "player_id": int(player_data.at[df_data.name, 'player_id']), + "player_id": int(player_data.at[df_data["key_bbref"], 'player_id']), "position": pos_data[1].upper(), - "innings": float(pos_data[0].at[df_data.name, 'Inn_def']), + "innings": float(pos_data[0].at[df_data["key_bbref"], 'Inn_def']), "range": cde.get_of_range( pos_code=pos_data[1], - tz_runs=int(pos_data[0].at[df_data.name, 'tz_runs_total']), + tz_runs=int(pos_data[0].at[df_data["key_bbref"], 'tz_runs_total']), season_pct=season_pct ) }) - of_arms.append(int(pos_data[0].at[df_data.name, 'bis_runs_outfield'])) + of_arms.append(int(pos_data[0].at[df_data["key_bbref"], 'bis_runs_outfield'])) - if df_data.name in df_of.index and len(of_arms) > 0 and len(of_payloads) > 0: + if df_data["key_bbref"] in df_of.index and len(of_arms) > 0 and len(of_payloads) > 0: error_rating = cde.get_any_error( pos_code=pos_data[1], - errors=int(df_of.at[df_data.name, 'E_def']), - chances=int(df_of.at[df_data.name, 'chances']), + errors=int(df_of.at[df_data["key_bbref"], 'E_def']), + chances=int(df_of.at[df_data["key_bbref"], 'chances']), season_pct=season_pct ) arm_rating = cde.arm_outfield(of_arms) @@ -313,38 +315,38 @@ async def main(args): f['arm'] = arm_rating position_payload.append(f) - if df_data.name in df_c.index: - if df_c.at[df_data.name, 'SB'] + df_c.at[df_data.name, 'CS'] == 0: + if df_data["key_bbref"] in df_c.index: + if df_c.at[df_data["key_bbref"], 'SB'] + df_c.at[df_data["key_bbref"], 'CS'] == 0: arm_rating = 3 else: arm_rating = cde.arm_catcher( - cs_pct=df_c.at[df_data.name, 'caught_stealing_perc'], - raa=int(df_c.at[df_data.name, 'bis_runs_catcher_sb']), + cs_pct=df_c.at[df_data["key_bbref"], 'caught_stealing_perc'], + raa=int(df_c.at[df_data["key_bbref"], 'bis_runs_catcher_sb']), season_pct=season_pct ) position_payload.append({ - "player_id": int(player_data.at[df_data.name, 'player_id']), + "player_id": int(player_data.at[df_data["key_bbref"], 'player_id']), "position": 'C', - "innings": float(df_c.at[df_data.name, 'Inn_def']), + "innings": float(df_c.at[df_data["key_bbref"], 'Inn_def']), "range": cde.range_catcher( - rs_value=int(df_c.at[df_data.name, 'tz_runs_catcher']), + rs_value=int(df_c.at[df_data["key_bbref"], 'tz_runs_catcher']), season_pct=season_pct ), "error": cde.get_any_error( pos_code='c', - errors=int(df_c.at[df_data.name, 'E_def']), - chances=int(df_c.at[df_data.name, 'chances']), + errors=int(df_c.at[df_data["key_bbref"], 'E_def']), + chances=int(df_c.at[df_data["key_bbref"], 'chances']), season_pct=season_pct ), "arm": arm_rating, "pb": cde.pb_catcher( - pb=int(df_c.at[df_data.name, 'PB']), - innings=int(float(df_c.at[df_data.name, 'Inn_def'])), + pb=int(df_c.at[df_data["key_bbref"], 'PB']), + innings=int(float(df_c.at[df_data["key_bbref"], 'Inn_def'])), season_pct=season_pct ), "overthrow": cde.ot_catcher( - errors=int(df_c.at[df_data.name, 'E_def']), - chances=int(df_c.at[df_data.name, 'chances']), + errors=int(df_c.at[df_data["key_bbref"], 'E_def']), + chances=int(df_c.at[df_data["key_bbref"], 'chances']), season_pct=season_pct ) }) @@ -366,7 +368,7 @@ async def main(args): print(f'Calculating card ratings...') offense_stats.apply(create_batting_card_ratings, axis=1) print(f'Ratings are complete\n\nPosting ratings now...') - if 'post_updates' not in arg_data or arg_data['post_updates'].lower() == 'true': + if 'post_batters' not in arg_data or arg_data['post_batters'].lower() == 'true': resp = await db_put('battingcardratings', payload={'ratings': batting_ratings}, timeout=30) print(f'Response: {resp}\n\nPulling fresh PD player data...') @@ -410,10 +412,11 @@ async def main(args): 5: 10, 99: 2400 } - params = [] + params = [('description', f'{player_desc_prefix} {df_data["p_name"]}')] if release_directory not in df_data['image']: - params.extend([('image', f'{CARD_BASE_URL}/{df_data["player_id"]}/card?d={release_directory}')]) + params.extend([('image', f'{CARD_BASE_URL}/{df_data["player_id"]}/battingcard' + f'{urllib.parse.quote("?d=")}{release_directory}')]) if df_data['cost'] == 99999: params.extend([ @@ -505,7 +508,7 @@ async def main(args): player_data.apply(get_player_updates, axis=1) print(f'Sending {len(player_updates)} player updates to PD database...') - if 'post_updates' not in arg_data or arg_data['post_updates'].lower() == 'true': + if 'post_batters' not in arg_data or arg_data['post_batters'].lower() == 'true': for x in player_updates: await db_patch('players', object_id=x, params=player_updates[x]) @@ -535,7 +538,8 @@ async def main(args): new_players.append({ 'p_name': f'{f_name} {l_name}', 'cost': 99999, - 'image': f'{CARD_BASE_URL}/{df_data["player_id"]}/pitchingcard?d={release_directory}', + 'image': f'{CARD_BASE_URL}/{df_data["player_id"]}/' + f'pitchingcard{urllib.parse.quote("?d=")}{release_directory}', 'mlbclub': 'None', 'franchise': 'None', 'cardset_id': cardset['id'], @@ -559,7 +563,7 @@ async def main(args): ).set_index('key_bbref', drop=False) final_pitching = step_pitching.join(df_p, rsuffix='_r') del ids_and_names, all_pitching, p_data, step_pitching - print(f'Player IDs linked to batting stats.\n{len(final_pitching.values)} players remain\n') + print(f'Player IDs linked to pitching stats.\n{len(final_pitching.values)} players remain\n') print(f'Reading pitching peripheral stats...') pit_data = (pd.read_csv(f'{input_path}pitching.csv') @@ -588,13 +592,14 @@ async def main(args): "starter_rating": pow_data[0], "relief_rating": pow_data[1], "closer_rating": cpi.closer_rating(int(df_data['GF']), int(df_data['SV']), int(df_data['G'])), - "hand": df_data['pitch_hand'] + "hand": df_data['pitch_hand'], + "batting": f'#1W{df_data["pitch_hand"]}-C' }) print(f'Calculating pitching cards...') pitching_stats.apply(create_pitching_card, axis=1) print(f'Cards are complete.\n\nPosting cards now...') - if 'post_updates' not in arg_data or arg_data['post_updates'].lower() == 'true': + if 'post_pitchers' not in arg_data or arg_data['post_pitchers'].lower() == 'true': resp = await db_put('pitchingcards', payload={'cards': pitching_cards}, timeout=30) print(f'Response: {resp}\n\nMatching batting card database IDs to player stats...') # final_pitching_stats = pd.merge( @@ -607,26 +612,26 @@ async def main(args): pit_positions = [] def create_pit_position(df_data): - if df_data.name in df_p.index: + if df_data["key_bbref"] in df_p.index: logging.debug(f'Running P stats for {df_data["p_name"]}') pit_positions.append({ - "player_id": int(player_data.at[df_data.name, 'player_id']), + "player_id": int(player_data.at[df_data["key_bbref"], 'player_id']), "position": 'P', - "innings": float(df_p.at[df_data.name, 'Inn_def']), + "innings": float(df_p.at[df_data["key_bbref"], 'Inn_def']), "range": cde.range_pitcher( - rs_value=int(df_p.at[df_data.name, 'bis_runs_total']), + rs_value=int(df_p.at[df_data["key_bbref"], 'bis_runs_total']), season_pct=season_pct ), "error": cde.get_any_error( pos_code='p', - errors=int(df_p.at[df_data.name, 'E_def']), - chances=int(df_p.at[df_data.name, 'chances']), + errors=int(df_p.at[df_data["key_bbref"], 'E_def']), + chances=int(df_p.at[df_data["key_bbref"], 'chances']), season_pct=season_pct ) }) else: pit_positions.append({ - "player_id": int(player_data.at[df_data.name, 'player_id']), + "player_id": int(player_data.at[df_data["key_bbref"], 'player_id']), "position": 'P', "innings": 1, "range": 5, @@ -636,7 +641,7 @@ async def main(args): print(f'Calculating pitcher fielding lines now...') pitching_stats.apply(create_pit_position, axis=1) print(f'Fielding is complete.\n\nPosting positions now...') - if 'post_updates' not in arg_data or arg_data['post_updates'].lower() == 'true': + if 'post_pitchers' not in arg_data or arg_data['post_pitchers'].lower() == 'true': resp = await db_put('cardpositions', payload={'positions': position_payload}, timeout=30) print(f'Response: {resp}\n') @@ -647,12 +652,189 @@ async def main(args): pitching_ratings.extend(cpi.get_pitcher_ratings(df_data)) print(f'Calculating card ratings...') - pitching_stats.apply(create_pitching_card_ratings, axis=1) # LOOK AT SINGLES + pitching_stats.apply(create_pitching_card_ratings, axis=1) print(f'Ratings are complete\n\nPosting ratings now...') - if 'post_updates' not in arg_data or arg_data['post_updates'].lower() == 'true': + if 'post_pitchers' not in arg_data or arg_data['post_pitchers'].lower() == 'true': resp = await db_put('pitchingcardratings', payload={'ratings': pitching_ratings}, timeout=30) print(f'Response: {resp}\n\nPulling all positions to set player positions...') + """ + Pull fresh pd_players and set_index to player_id + Pull fresh battingcards and set_index to player + Pull fresh battingcardratings one hand at a time and join on battingcard (suffixes _vl and vR) + + Join battingcards (left) with battingcardratings (right) as total_ratings on id (left) and battingcard (right) + Join pd_players (left) with total_ratings (right) on indeces + Output: PD player list with batting card, ratings vL, and ratings vR + + Calculate Total OPS as OPSvL + OPSvR + min(OPSvL, OPSvR) / 3 and assign rarity_id + For players with cost of 99999, set cost to * Total OPS / + """ + + def new_rarity_id(df_data): + if df_data['starter_rating'] > 3: + if df_data['total_OPS'] <= 0.4: + return 99 + elif df_data['total_OPS'] <= 0.475: + return 1 + elif df_data['total_OPS'] <= 0.53: + return 2 + elif df_data['total_OPS'] <= 0.6: + return 3 + elif df_data['total_OPS'] <= 0.675: + return 4 + else: + return 5 + else: + if df_data['total_OPS'] <= 0.325: + return 99 + elif df_data['total_OPS'] <= 0.4: + return 1 + elif df_data['total_OPS'] <= 0.475: + return 2 + elif df_data['total_OPS'] <= 0.55: + return 3 + elif df_data['total_OPS'] <= 0.625: + return 4 + else: + return 5 + p_data = await pd_players_df(cardset['id']) + p_data.set_index('player_id', drop=False) + total_ratings = pd.merge( + await pd_pitchingcards_df(cardset['id']), + await pd_pitchingcardratings_df(cardset['id']), + on='pitchingcard_id' + ) + total_ratings['new_rarity_id'] = total_ratings.apply(new_rarity_id, axis=1) + + player_data = pd.merge( + p_data, + total_ratings, + on='player_id' + ).set_index('player_id', drop=False) + # del total_ratings, pitching_stats + + player_updates = {} # { : [ (param pairs) ] } + rarity_group = player_data.query('rarity == new_rarity_id').groupby('rarity') + average_ops = rarity_group['total_OPS'].mean().to_dict() + # cost_groups = rarity_group['cost'].mean() + + def get_player_updates(df_data): + base_costs = { + 1: 810, + 2: 270, + 3: 90, + 4: 30, + 5: 10, + 99: 2400 + } + params = [('description', f'{player_desc_prefix} {df_data["p_name"]}')] + + if release_directory not in df_data['image']: + params.extend([('image', f'{CARD_BASE_URL}/{df_data["player_id"]}/pitchingcard' + f'{urllib.parse.quote("?d=")}{release_directory}')]) + + if df_data['cost'] == 99999: + params.extend([ + ('cost', + round(base_costs[df_data['new_rarity_id']] * df_data['total_OPS'] / + average_ops[df_data['new_rarity_id']])), + ('rarity_id', df_data['new_rarity_id']) + ]) + + elif df_data['rarity'] != df_data['new_rarity_id']: + old_rarity = df_data['rarity'] + new_rarity = df_data['new_rarity_id'] + old_cost = df_data['cost'] + new_cost = 0 + + if old_rarity == 1: + if new_rarity == 2: + new_cost = max(old_cost - 540, 100) + elif new_rarity == 3: + new_cost = max(old_cost - 720, 50) + elif new_rarity == 4: + new_cost = max(old_cost - 780, 15) + elif new_rarity == 5: + new_cost = max(old_cost - 800, 5) + elif new_rarity == 99: + new_cost = old_cost + 1600 + elif old_rarity == 2: + if new_rarity == 1: + new_cost = old_cost + 540 + elif new_rarity == 3: + new_cost = max(old_cost - 180, 50) + elif new_rarity == 4: + new_cost = max(old_cost - 240, 15) + elif new_rarity == 5: + new_cost = max(old_cost - 260, 5) + elif new_rarity == 99: + new_cost = old_cost + 2140 + elif old_rarity == 3: + if new_rarity == 1: + new_cost = old_cost + 720 + elif new_rarity == 2: + new_cost = old_cost + 180 + elif new_rarity == 4: + new_cost = max(old_cost - 60, 15) + elif new_rarity == 5: + new_cost = max(old_cost - 80, 5) + elif new_rarity == 99: + new_cost = old_cost + 2320 + elif old_rarity == 4: + if new_rarity == 1: + new_cost = old_cost + 780 + elif new_rarity == 2: + new_cost = old_cost + 240 + elif new_rarity == 3: + new_cost = old_cost + 60 + elif new_rarity == 5: + new_cost = max(old_cost - 20, 5) + elif new_rarity == 99: + new_cost = old_cost + 2380 + elif old_rarity == 5: + if new_rarity == 1: + new_cost = old_cost + 800 + elif new_rarity == 2: + new_cost = old_cost + 260 + elif new_rarity == 3: + new_cost = old_cost + 80 + elif new_rarity == 4: + new_cost = old_cost + 20 + elif new_rarity == 99: + new_cost = old_cost + 2400 + elif old_rarity == 99: + if new_rarity == 1: + new_cost = max(old_cost - 1600, 800) + elif new_rarity == 2: + new_cost = max(old_cost - 2140, 100) + elif new_rarity == 3: + new_cost = max(old_cost - 2320, 50) + elif new_rarity == 4: + new_cost = max(old_cost - 2380, 15) + elif new_rarity == 5: + new_cost = max(old_cost - 2400, 5) + + if new_cost != 0: + params.extend([('cost', new_cost), ('rarity_id', new_rarity)]) + + if len(params) > 0: + player_updates[df_data.name] = params + + player_data.apply(get_player_updates, axis=1) + + print(f'Sending {len(player_updates)} player updates to PD database...') + if 'post_pitchers' not in arg_data or arg_data['post_pitchers'].lower() == 'true': + for x in player_updates: + await db_patch('players', object_id=x, params=player_updates[x]) + + print(f'Pitcher updates are complete') + p_run_time = datetime.datetime.now() - start_time_two + t_run_time = datetime.datetime.now() - start_time + print(f'Total pitching cards: {len(pitching_cards)}\nNew cardset pitchers: {len(new_players)}\n' + f'Pitcher runtime: {round(p_run_time.total_seconds())} seconds\n') + print(f'Total runtime: {round(t_run_time.total_seconds())} seconds') + if __name__ == '__main__': asyncio.run(main(sys.argv[1:]))