File size: 3,161 Bytes
c6abe34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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