from typing import Optional, List, Dict, Any from pydantic import BaseModel, Field from uuid import UUID from datetime import datetime class PlayerStatRow(BaseModel): no: Optional[int] = Field(None, alias="jersey_number") player: str = Field(..., alias="name_raw") pos: Optional[str] = Field(None, alias="position") mins: Optional[str] = None pts: int = 0 fg_m: int = 0 fg_a: int = 0 fg_pct: float = 0.0 fg: Optional[str] = "0-0" tp_m: int = 0 tp_a: int = 0 tp_pct: float = 0.0 tp: Optional[str] = "0-0" # 2-pointers thp_m: int = 0 thp_a: int = 0 thp_pct: float = 0.0 thp: Optional[str] = "0-0" # 3-pointers ft_m: int = 0 ft_a: int = 0 ft_pct: float = 0.0 ft: Optional[str] = "0-0" off: int = 0 def_reb: int = Field(0, alias="def") reb: int = 0 ast: int = 0 to_cnt: int = Field(0, alias="to") stl: int = 0 blk: int = 0 blkr: int = 0 # Blocks received pf: int = 0 fls_on: int = 0 # Fouls on (received) plus_minus: Optional[int] = None index: Optional[int] = None row_confidence: Optional[float] = None # UI helper is_starter: bool = False # Auto-linking linked_player_profile_id: Optional[UUID] = None link_confidence: Optional[float] = None link_reason: Optional[str] = None model_config = { "populate_by_name": True } class TeamTotalsRow(BaseModel): pts: int = 0 fg_m: int = 0 fg_a: int = 0 fg_pct: float = 0.0 tp_m: int = 0 tp_a: int = 0 tp_pct: float = 0.0 thp_m: int = 0 thp_a: int = 0 thp_pct: float = 0.0 ft_m: int = 0 ft_a: int = 0 ft_pct: float = 0.0 off: int = 0 def_reb: int = 0 reb: int = 0 ast: int = 0 to_cnt: int = 0 stl: int = 0 blk: int = 0 pf: int = 0 fls_on: int = 0 index: int = 0 class TeamStatistics(BaseModel): points_from_turnovers: int = 0 points_in_paint: int = 0 second_chance_points: int = 0 fast_break_points: int = 0 bench_points: int = 0 biggest_lead: int = 0 biggest_scoring_run: int = 0 class ExtractedMatchStatsPreview(BaseModel): team_name: Optional[str] = None coach: Optional[str] = None assistant: Optional[str] = None opponent_name: Optional[str] = None match_date: Optional[str] = None competition: Optional[str] = None final_score_for: Optional[int] = None final_score_against: Optional[int] = None players: List[PlayerStatRow] = [] starters: List[PlayerStatRow] = [] bench: List[PlayerStatRow] = [] team_totals: Optional[TeamTotalsRow] = None team_statistics: Optional[TeamStatistics] = None overall_confidence: float = 0.0 class MatchStatUploadResponse(BaseModel): id: UUID match_id: UUID organization_id: UUID uploaded_by: UUID storage_path: str file_type: str extract_status: str extracted_json: Optional[Dict[str, Any]] = None confidence: Optional[float] = None created_at: datetime updated_at: datetime class StatsConfirmRequest(BaseModel): extracted_json: ExtractedMatchStatsPreview