import pydantic from typing import Literal, Optional from pydantic import model_validator 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