import pandas as pd import pydantic from pydantic import root_validator, validator from typing import Literal, Optional from pydantic import model_validator from pydantic import computed_field from pydantic_core import PydanticCustomError 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 @model_validator(mode='after') def position_validator(self): if self.position in ['C', 'LF', 'CF', 'RF'] and self.arm is None: raise ValueError(f'player_id: {self.player_id} - {self.position} must have an arm rating') if self.position == 'C' and (self.pb is None or self.overthrow is None): raise ValueError(f'player_id: {self.player_id} - Catchers must have a pb and overthrow rating') return self 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 @model_validator(mode='after') def avg(self): self.avg = (self.homerun + self.bp_homerun / 2 + self.triple + self.double_three + self.double_two + self.double_pull + self.single_two + self.single_one + self.single_center + self.bp_single / 2) / 108 self.obp = ((self.hbp + self.walk) / 108) + self.avg self.slg = (self.homerun * 4 + self.bp_homerun * 2 + self.triple * 3 + self.double_three * 2 + self.double_two * 2 + self.double_pull * 2 + self.single_two + self.single_one + self.single_center + self.bp_single / 2) / 108 total_chances = (self.homerun + self.bp_homerun + self.triple + self.double_three + self.double_two + self.double_pull + self.single_two + self.single_one + self.single_center + self.bp_single + self.hbp + self.walk + self.strikeout + self.lineout + self.popout + self.flyout_a + self.flyout_bq + self.flyout_lf_b + self.flyout_rf_b + self.groundout_a + self.groundout_b + self.groundout_c) if total_chances != 108: raise ValueError(f'BC {self.battingcard_id} has {total_chances} chances listed; must be 108') return self