paper-dynasty-card-creation/creation_helpers.py
2024-11-10 14:42:12 -06:00

1099 lines
33 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import csv
import datetime
import math
from decimal import ROUND_HALF_EVEN, Decimal
from exceptions import logger
import pandas as pd
import pybaseball as pb
import random
import requests
import time
from db_calls import db_get
from db_calls_card_creation import *
from bs4 import BeautifulSoup
D20_CHANCES = {
'2': {
'chances': 1,
'inc': .05
},
'3': {
'chances': 2,
'inc': .1
},
'4': {
'chances': 3,
'inc': .15
},
'5': {
'chances': 4,
'inc': .2
},
'6': {
'chances': 5,
'inc': .25
},
'7': {
'chances': 6,
'inc': .3
},
'8': {
'chances': 5,
'inc': .25
},
'9': {
'chances': 4,
'inc': .2
},
'10': {
'chances': 3,
'inc': .15
},
'11': {
'chances': 2,
'inc': .1
},
'12': {
'chances': 1,
'inc': .05
}
}
BLANK_RESULTS = {
'vL': {
'1': {
'2': {
'result': None,
'splits': None,
'2d6': None
},
'3': {
'result': None,
'splits': None,
'2d6': None
},
'4': {
'result': None,
'splits': None,
'2d6': None
},
'5': {
'result': None,
'splits': None,
'2d6': None
},
'6': {
'result': None,
'splits': None,
'2d6': None
},
'7': {
'result': None,
'splits': None,
'2d6': None
},
'8': {
'result': None,
'splits': None,
'2d6': None
},
'9': {
'result': None,
'splits': None,
'2d6': None
},
'10': {
'result': None,
'splits': None,
'2d6': None
},
'11': {
'result': None,
'splits': None,
'2d6': None
},
'12': {
'result': None,
'splits': None,
'2d6': None
},
'splits': 0
},
'2': {
'2': {
'result': None,
'splits': None,
'2d6': None
},
'3': {
'result': None,
'splits': None,
'2d6': None
},
'4': {
'result': None,
'splits': None,
'2d6': None
},
'5': {
'result': None,
'splits': None,
'2d6': None
},
'6': {
'result': None,
'splits': None,
'2d6': None
},
'7': {
'result': None,
'splits': None,
'2d6': None
},
'8': {
'result': None,
'splits': None,
'2d6': None
},
'9': {
'result': None,
'splits': None,
'2d6': None
},
'10': {
'result': None,
'splits': None,
'2d6': None
},
'11': {
'result': None,
'splits': None,
'2d6': None
},
'12': {
'result': None,
'splits': None,
'2d6': None
},
'splits': 0
},
'3': {
'2': {
'result': None,
'splits': None,
'2d6': None
},
'3': {
'result': None,
'splits': None,
'2d6': None
},
'4': {
'result': None,
'splits': None,
'2d6': None
},
'5': {
'result': None,
'splits': None,
'2d6': None
},
'6': {
'result': None,
'splits': None,
'2d6': None
},
'7': {
'result': None,
'splits': None,
'2d6': None
},
'8': {
'result': None,
'splits': None,
'2d6': None
},
'9': {
'result': None,
'splits': None,
'2d6': None
},
'10': {
'result': None,
'splits': None,
'2d6': None
},
'11': {
'result': None,
'splits': None,
'2d6': None
},
'12': {
'result': None,
'splits': None,
'2d6': None
},
'splits': 0
}
},
'vR': {
'1': {
'2': {
'result': None,
'splits': None,
'2d6': None
},
'3': {
'result': None,
'splits': None,
'2d6': None
},
'4': {
'result': None,
'splits': None,
'2d6': None
},
'5': {
'result': None,
'splits': None,
'2d6': None
},
'6': {
'result': None,
'splits': None,
'2d6': None
},
'7': {
'result': None,
'splits': None,
'2d6': None
},
'8': {
'result': None,
'splits': None,
'2d6': None
},
'9': {
'result': None,
'splits': None,
'2d6': None
},
'10': {
'result': None,
'splits': None,
'2d6': None
},
'11': {
'result': None,
'splits': None,
'2d6': None
},
'12': {
'result': None,
'splits': None,
'2d6': None
},
'splits': 0
},
'2': {
'2': {
'result': None,
'splits': None,
'2d6': None
},
'3': {
'result': None,
'splits': None,
'2d6': None
},
'4': {
'result': None,
'splits': None,
'2d6': None
},
'5': {
'result': None,
'splits': None,
'2d6': None
},
'6': {
'result': None,
'splits': None,
'2d6': None
},
'7': {
'result': None,
'splits': None,
'2d6': None
},
'8': {
'result': None,
'splits': None,
'2d6': None
},
'9': {
'result': None,
'splits': None,
'2d6': None
},
'10': {
'result': None,
'splits': None,
'2d6': None
},
'11': {
'result': None,
'splits': None,
'2d6': None
},
'12': {
'result': None,
'splits': None,
'2d6': None
},
'splits': 0
},
'3': {
'2': {
'result': None,
'splits': None,
'2d6': None
},
'3': {
'result': None,
'splits': None,
'2d6': None
},
'4': {
'result': None,
'splits': None,
'2d6': None
},
'5': {
'result': None,
'splits': None,
'2d6': None
},
'6': {
'result': None,
'splits': None,
'2d6': None
},
'7': {
'result': None,
'splits': None,
'2d6': None
},
'8': {
'result': None,
'splits': None,
'2d6': None
},
'9': {
'result': None,
'splits': None,
'2d6': None
},
'10': {
'result': None,
'splits': None,
'2d6': None
},
'11': {
'result': None,
'splits': None,
'2d6': None
},
'12': {
'result': None,
'splits': None,
'2d6': None
},
'splits': 0
}
}
}
TESTING = False
YES = ['y', 'yes', 'yeet', 'please', 'yeah']
CLUB_LIST = {
'ANA': 'Anaheim Angels',
'ARI': 'Arizona Diamondbacks',
'ATL': 'Atlanta Braves',
'BAL': 'Baltimore Orioles',
'BOS': 'Boston Red Sox',
'CHC': 'Chicago Cubs',
'CHW': 'Chicago White Sox',
'CIN': 'Cincinnati Reds',
'CLE': 'Cleveland Guardians',
'COL': 'Colorado Rockies',
'DET': 'Detroit Tigers',
'HOU': 'Houston Astros',
'KCR': 'Kansas City Royals',
'LAA': 'Los Angeles Angels',
'LAD': 'Los Angeles Dodgers',
'FLA': 'Florida Marlins',
'MIA': 'Miami Marlins',
'MIL': 'Milwaukee Brewers',
'MIN': 'Minnesota Twins',
'MON': 'Montreal Expos',
'NYM': 'New York Mets',
'NYY': 'New York Yankees',
'OAK': 'Oakland Athletics',
'PHI': 'Philadelphia Phillies',
'PIT': 'Pittsburgh Pirates',
'SDP': 'San Diego Padres',
'SEA': 'Seattle Mariners',
'SFG': 'San Francisco Giants',
'STL': 'St Louis Cardinals',
'TBD': 'Tampa Bay Devil Rays',
'TBR': 'Tampa Bay Rays',
'TEX': 'Texas Rangers',
'TOR': 'Toronto Blue Jays',
'WSN': 'Washington Nationals',
'TOT': 'None',
'2 Tms': 'None',
'2TM': 'None',
'3 Tms': 'None',
'3TM': 'None',
'4 Tms': 'None',
'4TM': 'None'
}
FRANCHISE_LIST = {
'ANA': 'Los Angeles Angels',
'ARI': 'Arizona Diamondbacks',
'ATL': 'Atlanta Braves',
'BAL': 'Baltimore Orioles',
'BOS': 'Boston Red Sox',
'CHC': 'Chicago Cubs',
'CHW': 'Chicago White Sox',
'CIN': 'Cincinnati Reds',
'CLE': 'Cleveland Guardians',
'COL': 'Colorado Rockies',
'DET': 'Detroit Tigers',
'FLA': 'Miami Marlins',
'HOU': 'Houston Astros',
'KCR': 'Kansas City Royals',
'LAA': 'Los Angeles Angels',
'LAD': 'Los Angeles Dodgers',
'MIA': 'Miami Marlins',
'MIL': 'Milwaukee Brewers',
'MIN': 'Minnesota Twins',
'MON': 'Washington Nationals',
'NYM': 'New York Mets',
'NYY': 'New York Yankees',
'OAK': 'Oakland Athletics',
'PHI': 'Philadelphia Phillies',
'PIT': 'Pittsburgh Pirates',
'SDP': 'San Diego Padres',
'SEA': 'Seattle Mariners',
'SFG': 'San Francisco Giants',
'STL': 'St Louis Cardinals',
'TBD': 'Tampa Bay Rays',
'TBR': 'Tampa Bay Rays',
'TEX': 'Texas Rangers',
'TOR': 'Toronto Blue Jays',
'WSN': 'Washington Nationals',
'TOT': 'None',
'2 Tms': 'None',
'2TM': 'None',
'3 Tms': 'None',
'3TM': 'None',
'4 Tms': 'None',
'4TM': 'None'
}
# PLAYER_DB_BACKUP = {
# 684007: {'key_mlbam': 684007, 'key_retro': 'imans001', 'key_fangraphs': 33829, 'key_bbref': 'imanash01'},
#
# }
def get_args(args):
logger.info(f'Process arguments: {args}')
final_args = {}
for x in args:
if "=" not in x:
raise TypeError(f'Invalid <key>=<value> argument: {x}')
key, value = x.split("=")
logger.info(f'key: {key} / value: {value}')
if key in final_args:
raise ValueError(f'Duplicate argument: {key}')
final_args[key] = value
return final_args
async def pd_players_df(cardset_id: int):
p_query = await db_get(
'players',
params=[('inc_dex', False), ('cardset_id', cardset_id), ('short_output', True)]
)
if p_query['count'] == 0:
return pd.DataFrame({
'player_id': [], 'p_name': [], 'cost': [], 'image': [], 'image2': [], 'mlbclub': [], 'franchise': [],
'cardset': [], 'set_num': [], 'rarity': [], 'pos_1': [], 'pos_2': [], 'pos_3': [], 'pos_4': [], 'pos_5': [],
'pos_6': [], 'pos_7': [], 'pos_8': [], 'headshot': [], 'vanity_card': [], 'strat_code': [], 'bbref_id': [],
'fangr_id': [], 'description': [], 'quantity': [], 'mlbplayer': []
})
return pd.DataFrame(p_query['players'])
async def pd_positions_df(cardset_id: int):
pos_query = await db_get(
'cardpositions', params=[('cardset_id', cardset_id), ('short_output', True), ('sort', 'innings-desc')])
if pos_query['count'] == 0:
raise ValueError('No position ratings returned from Paper Dynasty API')
all_pos = pd.DataFrame(pos_query['positions']).rename(columns={'player': 'player_id'})
return all_pos
def get_pitching_peripherals(season: int):
url = f'https://www.baseball-reference.com/leagues/majors/{season}-standard-pitching.shtml'
soup = BeautifulSoup(requests.get(url).text, 'html.parser')
time.sleep(3)
table = soup.find('table', {'id': 'players_standard_pitching'})
headers = []
data = []
indeces = []
for row in table.find_all('tr'):
row_data = []
col_names = []
for cell in row.find_all('td'):
try:
player_id = cell['data-append-csv']
row_data.append(player_id)
if len(headers) == 0:
col_names.append('key_bbref')
except Exception as e:
pass
row_data.append(cell.text)
if len(headers) == 0:
col_names.append(cell['data-stat'])
if len(row_data) > 0:
data.append(row_data)
indeces.append(row_data[0])
if len(headers) == 0:
headers.extend(col_names)
pit_frame = pd.DataFrame(data, index=indeces, columns=headers).query('key_bbref == key_bbref')
return pit_frame.drop_duplicates(subset=['key_bbref'], keep='first')
def mround(x, prec=2, base=.05):
num, to = Decimal(str(x)), Decimal(str(base))
return float(round(num / to) * to)
# return float(round(Decimal(str(x)) / Decimal(str(base)) * Decimal(str(base)), prec))
# return round(base * round(float(x) / base), prec)
def chances_from_row(row_num):
if row_num == '2' or row_num == '12':
return 1
if row_num == '3' or row_num == '11':
return 2
if row_num == '4' or row_num == '10':
return 3
if row_num == '5' or row_num == '9':
return 4
if row_num == '6' or row_num == '8':
return 5
if row_num == '7':
return 6
raise ValueError(f'No chance count found for row_num {row_num}')
def legal_splits(tot_chances):
legal_2d6 = []
for x in D20_CHANCES:
num_incs = mround(tot_chances) / D20_CHANCES[x]['inc']
if num_incs - int(num_incs) == 0 and int(20 - num_incs) > 0:
legal_2d6.append({
'2d6': int(x),
'incs': int(num_incs),
'bad_chances': mround(D20_CHANCES[x]['chances'] * (int(20 - num_incs) / 20)),
'bad_incs': int(20 - num_incs)
})
random.shuffle(legal_2d6)
# if TESTING: print(f'tot_chances: {myround(tot_chances)}')
# if TESTING: print(f'legal_2d6: {legal_2d6}')
return legal_2d6
def result_string(tba_data, row_num, split_min=None, split_max=None):
bold1 = f'{"<b>" if tba_data["bold"] else ""}'
bold2 = f'{"</b>" if tba_data["bold"] else ""}'
row_string = f'{"<b> </b>" if int(row_num) < 10 else ""}{row_num}'
if TESTING: print(f'adding {tba_data["string"]} to row {row_num} / '
f'split_min: {split_min} / split_max: {split_max}')
# No splits; standard result
if not split_min:
return f'{bold1}{row_string}-{tba_data["string"]}{bold2}'
# With splits
split_nums = f'{split_min if split_min != 20 else ""}{"-" if split_min != 20 else ""}{split_max}'
data_string = tba_data["sm-string"] if "sm-string" in tba_data.keys() else tba_data["string"]
spaces = 18 - len(data_string) - len(split_nums)
if 'WALK' in data_string:
spaces -= 3
elif 'SI**' in data_string:
spaces += 1
elif 'DO*' in data_string:
spaces -= 1
elif 'DO*' in data_string:
spaces -= 2
elif 'so' in data_string:
spaces += 3
elif 'gb' in data_string:
spaces -= 3
if TESTING: print(f'len(tba_data["string"]): {len(data_string)} / len(split_nums): {len(split_nums)} '
f'spaces: {spaces}')
if split_min == 1 or split_min is None:
row_output = f'{row_string}-'
else:
row_output = '<b> </b>'
if TESTING: print(f'row_output: {row_output}')
return f'{bold1}{row_output}{data_string}{" " * spaces}{split_nums}{bold2}'
def result_data(tba_data, row_num, tba_data_bottom=None, top_split_max=None, fatigue=False):
ret_data = {}
top_bold1 = f'{"<b>" if tba_data["bold"] else ""}'
top_bold2 = f'{"</b>" if tba_data["bold"] else ""}'
bot_bold1 = None
bot_bold2 = None
if tba_data_bottom:
bot_bold1 = f'{"<b>" if tba_data_bottom["bold"] else ""}'
bot_bold2 = f'{"</b>" if tba_data_bottom["bold"] else ""}'
if tba_data_bottom is None:
ret_data['2d6'] = f'{top_bold1}{int(row_num)}-{top_bold2}'
ret_data['splits'] = f'{top_bold1}{top_bold2}'
ret_data['result'] = f'{top_bold1}' \
f'{tba_data["string"]}{"" if fatigue else ""}' \
f'{top_bold2}'
else:
ret_data['2d6'] = f'{top_bold1}{int(row_num)}-{top_bold2}\n'
ret_data['splits'] = f'{top_bold1}1{"-" if top_split_max != 1 else ""}' \
f'{top_split_max if top_split_max != 1 else ""}{top_bold2}\n' \
f'{bot_bold1}{top_split_max+1}{"-20" if top_split_max != 19 else ""}{bot_bold2}'
ret_data['result'] = \
f'{top_bold1}{tba_data["sm-string"] if "sm-string" in tba_data.keys() else tba_data["string"]}' \
f'{top_bold2}\n' \
f'{bot_bold1}' \
f'{tba_data_bottom["sm-string"] if "sm-string" in tba_data_bottom.keys() else tba_data_bottom["string"]}' \
f'{bot_bold2}'
return ret_data
def get_of(batter_hand, pitcher_hand, pull_side=True):
if batter_hand == 'R':
return 'lf' if pull_side else 'rf'
if batter_hand == 'L':
return 'rf' if pull_side else 'lf'
if batter_hand == 'S':
if pitcher_hand == 'L':
return 'rf' if pull_side else 'rf'
else:
return 'lf' if pull_side else 'lf'
def get_col(col_num):
if col_num == '1':
return 'one'
if col_num == '2':
return 'two'
if col_num == '3':
return 'three'
def write_to_csv(output_path, file_name: str, row_data: list):
# Build the csv output
fpath = (output_path / f'{file_name}').with_suffix('.csv')
# logger.info(f'Printing following data to {file_name}:\n\n{row_data}')
with fpath.open(mode='w+', newline='', encoding='utf-8') as csv_File:
writer = csv.writer(csv_File)
writer.writerows(row_data)
def get_position_string(all_pos: list, inc_p: bool):
if len(all_pos) == 0:
return 'dh'
of_arm = None
of_error = None
of_innings = None
lf_range = None
lf_innings = 0
cf_range = None
cf_innings = 0
rf_range = None
rf_innings = 0
all_def = []
for x in all_pos:
if x.position == 'OF':
of_arm = f'{"+" if "-" not in x.arm else ""}{x.arm}'
of_error = x.error
of_innings = x.innings
elif x.position == 'CF':
cf_range = x.range
cf_innings = x.innings
elif x.position == 'LF':
lf_range = x.range
lf_innings = x.innings
elif x.position == 'RF':
rf_range = x.range
rf_innings = x.innings
elif x.position == 'C':
all_def.append(
(f'c-{x.range}({"+" if int(x.arm) >= 0 else ""}{x.arm}) e{x.error} T-{x.overthrow}(pb-{x.pb})', x.innings)
)
elif 'P' in x.position and not inc_p:
pass
else:
all_def.append((f'{x.position.lower()}-{x.range}e{x.error}', x.innings))
if of_arm is not None:
logger.info(
f'\n\nProcessing OF player ID {all_pos[0].player_id}\nlf-{lf_range} / cf-{cf_range} / rf-{rf_range}'
)
all_of = []
if lf_innings > 0:
all_of.append((lf_range, lf_innings, 'lf'))
if cf_innings > 0:
all_of.append((cf_range, cf_innings, 'cf'))
if rf_innings > 0:
all_of.append((rf_range, rf_innings, 'rf'))
logger.info(f'all_of: {all_of}')
if len(all_of) > 0:
all_of.sort(key=lambda y: y[1], reverse=True)
logger.info(f'sorted of: {all_of}')
out_string = f'{all_of[0][2]}-{all_of[0][0]}({of_arm})e{of_error}'
if len(all_of) >= 2:
out_string += f', {all_of[1][2]}-{all_of[1][0]}e{of_error}'
if len(all_of) >= 3:
out_string += f', {all_of[2][2]}-{all_of[2][0]}e{of_error}'
logger.info(f'of string: {out_string}')
all_def.append((out_string, of_innings))
all_def.sort(key=lambda z: z[1], reverse=True)
final_defense = ''
for x in all_def:
if len(final_defense) > 0:
final_defense += f', '
final_defense += f'{x[0]}'
return final_defense
def ordered_positions(all_pos: list) -> list:
if len(all_pos) == 0:
return ['DH']
all_def = []
for x in all_pos:
if x.position not in ['OF', 'P', 'SP', 'RP', 'CP']:
all_def.append((x.innings, x.position))
all_def.sort(key=lambda y: y[0], reverse=True)
return [x[1] for x in all_def]
def ordered_pitching_positions(all_pos: list) -> list:
all_def = []
for x in all_pos:
if x.position in ['SP', 'RP', 'CP']:
all_def.append((x.innings, x.position))
all_def.sort(key=lambda y: y[0], reverse=True)
return [x[1] for x in all_def]
def defense_rg(all_pos: list) -> list:
rg_data = [
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None
]
all_pitcher = True
for line in all_pos:
if 'P' not in line.position:
all_pitcher = False
break
for line in all_pos:
if line.position == 'P' and all_pitcher:
this_pit = PitcherData.get_or_none(PitcherData.player == line.player, PitcherData.cardset == line.cardset)
if this_pit:
rg_data[0] = line.range
rg_data[9] = line.error
rg_data[22] = this_pit.wild_pitch
rg_data[23] = this_pit.balk
elif line.position == 'C':
rg_data[1] = line.range
rg_data[10] = line.error
rg_data[19] = line.arm
rg_data[20] = line.overthrow
rg_data[21] = line.pb
elif line.position == '1B':
rg_data[2] = line.range
rg_data[11] = line.error
elif line.position == '2B':
rg_data[3] = line.range
rg_data[12] = line.error
elif line.position == '3B':
rg_data[4] = line.range
rg_data[13] = line.error
elif line.position == 'SS':
rg_data[5] = line.range
rg_data[14] = line.error
elif line.position == 'LF':
rg_data[6] = line.range
elif line.position == 'CF':
rg_data[7] = line.range
elif line.position == 'RF':
rg_data[8] = line.range
elif line.position == 'OF':
rg_data[15] = line.error
rg_data[16] = line.error
rg_data[17] = line.error
rg_data[18] = line.arm
return rg_data
def sanitize_chance_output(total_chances, min_chances=1.0, rounding=0.05):
if total_chances < min_chances:
logger.debug(f'sanitize: {total_chances} is less than min_chances ({min_chances}); returning 0')
return 0
rounded_decimal = mround(total_chances, base=rounding)
if rounding == 1.0:
return rounded_decimal
exact_chances = [
Decimal('1.05'), Decimal('1.1'), Decimal('1.2'), Decimal('1.25'), Decimal('1.3'), Decimal('1.35'),
Decimal('1.4'), Decimal('1.5'), Decimal('1.6'), Decimal('1.65'), Decimal('1.7'), Decimal('1.75'),
Decimal('1.8'), Decimal('1.9'), Decimal('1.95'), Decimal('2.1'), Decimal('2.2'), Decimal('2.25'),
Decimal('2.4'), Decimal('2.5'), Decimal('2.55'), Decimal('2.6'), Decimal('2.7'), Decimal('2.75'),
Decimal('2.8'), Decimal('2.85'), Decimal('3.2'), Decimal('3.25'), Decimal('3.3'), Decimal('3.4'),
Decimal('3.5'), Decimal('3.6'), Decimal('3.75'), Decimal('3.8'), Decimal('3.9'), Decimal('4.2'),
Decimal('4.25'), Decimal('4.5'), Decimal('4.75'), Decimal('4.8'), Decimal('5.1'), Decimal('5.4'),
Decimal('5.7')
]
if rounded_decimal > exact_chances[-1]:
return rounded_decimal
for x in exact_chances:
if rounded_decimal <= x:
return float(x)
raise ArithmeticError(f'Attempt to sanitize {total_chances} rounded to {rounded_decimal} and could not be matched to an exact result')
def legacy_sanitize_chance_output(total_chances: float, min_chances: float = 1.0, rounding: float = 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)
logger.debug(f'r_val: {r_val}')
rounded_val = Decimal(float(math.floor(r_val / Decimal(rounding)) * Decimal(rounding))).quantize(Decimal("0.05"), ROUND_HALF_EVEN)
if math.floor(rounded_val) == rounded_val:
return rounded_val
exact_chances = [
Decimal('1.05'), Decimal('1.1'), Decimal('1.2'), Decimal('1.25'), Decimal('1.3'), Decimal('1.35'),
Decimal('1.4'), Decimal('1.5'), Decimal('1.6'), Decimal('1.65'), Decimal('1.7'), Decimal('1.75'),
Decimal('1.8'), Decimal('1.9'), Decimal('1.95'), Decimal('2.1'), Decimal('2.2'), Decimal('2.25'),
Decimal('2.4'), Decimal('2.5'), Decimal('2.55'), Decimal('2.6'), Decimal('2.7'), Decimal('2.75'),
Decimal('2.8'), Decimal('2.85'), Decimal('3.2'), Decimal('3.25'), Decimal('3.3'), Decimal('3.4'),
Decimal('3.5'), Decimal('3.6'), Decimal('3.75'), Decimal('3.8'), Decimal('3.9'), Decimal('4.2'),
Decimal('4.25'), Decimal('4.5'), Decimal('4.75'), Decimal('4.8'), Decimal('5.1'), Decimal('5.4'),
Decimal('5.7')
]
if rounded_val > exact_chances[-1]:
return rounded_val
for x in exact_chances:
if rounded_val <= x:
return x
def mlbteam_and_franchise(mlbam_playerid):
api_url = f'https://statsapi.mlb.com/api/v1/people/{mlbam_playerid}?hydrate=currentTeam'
logger.info(f'Calling {api_url}')
p_data = {'mlbclub': None, 'franchise': None}
# club_list = [
# 'Arizona Diamondbacks',
# 'Atlanta Braves',
# 'Baltimore Orioles',
# 'Boston Red Sox',
# 'Chicago Cubs',
# 'Chicago White Sox',
# 'Cincinnati Reds',
# 'Cleveland Guardians',
# 'Colorado Rockies',
# 'Detroit Tigers',
# 'Houston Astros',
# 'Kansas City Royals',
# 'Los Angeles Angels',
# 'Los Angeles Dodgers',
# 'Miami Marlins',
# 'Milwaukee Brewers',
# 'Minnesota Twins',
# 'New York Mets',
# 'New York Yankees',
# 'Oakland Athletics',
# 'Philadelphia Phillies',
# 'Pittsburgh Pirates',
# 'San Diego Padres',
# 'Seattle Mariners',
# 'San Francisco Giants',
# 'St Louis Cardinals',
# 'Tampa Bay Rays',
# 'Texas Rangers',
# 'Toronto Blue Jays',
# 'Washington Nationals'
# ]
try:
resp = requests.get(api_url, timeout=2)
except requests.ReadTimeout as e:
logger.error(f'mlbteam_and_franchise - ReadTimeout pull MLB team for MLB AM player ID {mlbam_playerid}')
return p_data
if resp.status_code == 200:
data = resp.json()
data = data['people'][0]
logger.debug(f'data: {data}')
if data['currentTeam']['name'] in CLUB_LIST.values():
p_data['mlbclub'] = data['currentTeam']['name']
p_data['franchise'] = data['currentTeam']['name']
else:
logger.error(f'Could not set team for {mlbam_playerid}; received {data["currentTeam"]["name"]}')
else:
logger.error(f'mlbteam_and_franchise - Bad response from mlbstatsapi: {resp.status_code}')
return p_data
def get_all_pybaseball_ids(player_id: list, key_type: str, is_custom: bool = False, full_name: str = None):
if is_custom:
try:
long_player_id = int(player_id[0])
if long_player_id >= 999942001:
backyard_players = [
'akhan',
'amkhan',
'adelvecchio',
'afrazier',
'awebber',
'bblackwood',
'drobinson',
'dpetrovich',
'esteele',
'ghasselhoff',
'jsmith',
'jgarcia',
'kkawaguchi',
'kphillips',
'keckman',
'lcrocket',
'llui',
'mluna',
'mdubois',
'mthomas',
'psanchez',
'pwheeler',
'rworthington',
'rjohnson',
'rdobbs',
'sdobbs',
'swebber',
'smorgan',
'tdelvecchio',
'vkawaguchi'
]
return pd.Series(
{'key_bbref': backyard_players[long_player_id - 999942001],
'key_fangraphs': player_id[0],
'key_mlbam': player_id[0],
'bat_hand': 'L' if long_player_id in [
999942004, 999942007, 999942010, 999942018, 999942019, 999942020, 999942022
] else 'R'
},
)
except Exception as e:
logger.warning(e)
q = pb.playerid_reverse_lookup(player_id, key_type=key_type)
if len(q.values) > 0:
return_val = q.loc[0]
# Check manual players
elif full_name is not None:
names = full_name.split(' ')
q = pb.playerid_lookup(last=names[-1], first=' '.join(names[:-1]), fuzzy=True)
if len(q.values) == 0:
logger.error(f'get_all_pybaseball_ids - Could not find id {player_id} / {key_type} or '
f'{full_name} / full name in pybaseball')
return None
elif len(q.values) > 1:
# q = q.drop(q[q['mlb_played_last'].isnull()])
# q.astype({'mlb_played_last': 'int32'})
# q = q.dropna()
q = q.drop(q[q['mlb_played_last'] == ''].index)
q = q.sort_values(by=['mlb_played_last'], ascending=False)
return_val = q.loc[0]
return_val['key_fangraphs'] = player_id[0]
else:
logger.error(f'get_all_pybaseball_ids - Could not find id {player_id} / {key_type} in pybaseball')
return_val = None
# p_query = await db_get('mlbplayers', params=[(f'key_{key_type}', player_id)])
# if p_query['count'] > 0:
# return_val = pd.DataFrame(p_query['players'])
# else:
# logger.error(f'get_all_pybaseball_ids - Could not find id {player_id} / {key_type} in PD mlbplayers table')
# return_val = None
return return_val
def sanitize_name(start_name: str) -> str:
return (start_name
.replace("é", "e")
.replace("á", "a")
.replace(".", "")
.replace("Á", "A")
.replace("ñ", "n")
.replace("ó", "o")
.replace("í", "i")
.replace("ú", "u")
.replace("'", "")
.replace('-', ' '))
def get_hand(df_data):
try:
if df_data['Name'][-1] == '*':
return 'L'
elif df_data['Name'][-1] == '#':
return 'S'
else:
return 'R'
except Exception as e:
logger.error(f'Error in get_hand for {df_data["Name"]}')
return 'R'