import pandas as pd import pydantic from pydantic import root_validator, validator from typing import Literal, Optional class DataMismatchError(Exception): pass class BattingCardModel(pydantic.BaseModel): player_id: Optional[int] = None variant: int = 0 steal_low: int = 3 steal_high: int = 20 steal_auto: bool = False steal_jump: float = 0 bunting: str = 'C' hit_and_run: str = 'C' running: int = 10 offense_col: int = None hand: Literal['R', 'L', 'S'] = 'R' class CardPositionModel(pydantic.BaseModel): player_id: int variant: int = 0 position: Literal['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'DH'] innings: int = 1 range: int = 5 error: int = 0 arm: Optional[int] = None pb: Optional[int] = None overthrow: Optional[int] = None @root_validator def position_validator(cls, values): if values['position'] in ['C', 'LF', 'CF', 'RF'] and values['arm'] is None: raise ValueError(f'{values["position"]} must have an arm rating') if values['position'] == 'C' and (values['pb'] is None or values['overthrow'] is None): raise ValueError('Catchers must have a pb and overthrow rating') return values class BattingCardRatingsModel(pydantic.BaseModel): battingcard_id: int vs_hand: Literal['R', 'L', 'vR', 'vL'] homerun: float = 0.0 bp_homerun: float = 0.0 triple: float = 0.0 double_three: float = 0.0 double_two: float = 0.0 double_pull: float = 0.0 single_two: float = 0.0 single_one: float = 0.0 single_center: float = 0.0 bp_single: float = 0.0 hbp: float = 0.0 walk: float = 0.0 strikeout: float = 0.0 lineout: float = 0.0 popout: float = 0.0 flyout_a: float = 0.0 flyout_bq: float = 0.0 flyout_lf_b: float = 0.0 flyout_rf_b: float = 0.0 groundout_a: float = 0.0 groundout_b: float = 0.0 groundout_c: float = 0.0 avg: float = 0.0 obp: float = 0.0 slg: float = 0.0 pull_rate: float = 0.0 center_rate: float = 0.0 slap_rate: float = 0.0 @validator("avg", always=True) def avg_validator(cls, v, values, **kwargs): return (values['homerun'] + values['bp_homerun'] / 2 + values['triple'] + values['double_three'] + values['double_two'] + values['double_pull'] + values['single_two'] + values['single_one'] + values['single_center'] + values['bp_single'] / 2) / 108 @validator("obp", always=True) def obp_validator(cls, v, values, **kwargs): return ((values['hbp'] + values['walk']) / 108) + values['avg'] @validator("slg", always=True) def slg_validator(cls, v, values, **kwargs): return (values['homerun'] * 4 + values['bp_homerun'] * 2 + values['triple'] * 3 + values['double_three'] * 2 + values['double_two'] * 2 + values['double_pull'] * 2 + values['single_two'] + values['single_one'] + values['single_center'] + values['bp_single'] / 2) / 108 @root_validator def validate_chance_total(cls, values): total_chances = ( values['homerun'] + values['bp_homerun'] + values['triple'] + values['double_three'] + values['double_two'] + values['double_pull'] + values['single_two'] + values['single_one'] + values['single_center'] + values['bp_single'] + values['hbp'] + values['walk'] + values['strikeout'] + values['lineout'] + values['popout'] + values['flyout_a'] + values['flyout_bq'] + values['flyout_lf_b'] + values['flyout_rf_b'] + values['groundout_a'] + values['groundout_b'] + values['groundout_c']) if round(total_chances) != 108: raise ValueError(f'BC {values["battingcard_id"]} must have exactly 108 chances on the card ' f'{values["vs_hand"]}; {round(total_chances)} listed') return values