| import re |
| from typing import Tuple, Dict, List, Any |
| from app.models.stats import PlayerStatRow, TeamTotalsRow, TeamStatistics |
|
|
| def parse_made_attempted(value: str) -> Tuple[int, int]: |
| """Parse a field like '5-10' or '5/10' into (made, attempted). Returns (0, 0) if invalid.""" |
| if not value or not isinstance(value, str): |
| return 0, 0 |
| match = re.search(r'(\d+)\s*[\-/\|]\s*(\d+)', value) |
| if match: |
| return int(match.group(1)), int(match.group(2)) |
| return 0, 0 |
|
|
| def validate_and_compute_totals(players: List[PlayerStatRow]) -> TeamTotalsRow: |
| """Computes all FIBA-style totals from player rows.""" |
| totals = TeamTotalsRow() |
| |
| for p in players: |
| |
| if p.fg and p.fg_m == 0 and p.fg_a == 0: |
| p.fg_m, p.fg_a = parse_made_attempted(p.fg) |
| if p.tp and p.tp_m == 0 and p.tp_a == 0: |
| p.tp_m, p.tp_a = parse_made_attempted(p.tp) |
| if p.thp and p.thp_m == 0 and p.thp_a == 0: |
| p.thp_m, p.thp_a = parse_made_attempted(p.thp) |
| if p.ft and p.ft_m == 0 and p.ft_a == 0: |
| p.ft_m, p.ft_a = parse_made_attempted(p.ft) |
| |
| |
| p.fg_pct = round((p.fg_m / p.fg_a * 100), 1) if p.fg_a > 0 else 0.0 |
| p.tp_pct = round((p.tp_m / p.tp_a * 100), 1) if p.tp_a > 0 else 0.0 |
| p.thp_pct = round((p.thp_m / p.thp_a * 100), 1) if p.thp_a > 0 else 0.0 |
| p.ft_pct = round((p.ft_m / p.ft_a * 100), 1) if p.ft_a > 0 else 0.0 |
| |
| |
| totals.pts += p.pts |
| totals.fg_m += p.fg_m |
| totals.fg_a += p.fg_a |
| totals.tp_m += p.tp_m |
| totals.tp_a += p.tp_a |
| totals.thp_m += p.thp_m |
| totals.thp_a += p.thp_a |
| totals.ft_m += p.ft_m |
| totals.ft_a += p.ft_a |
| totals.off += p.off |
| totals.def_reb += p.def_reb |
| totals.reb += p.reb |
| totals.ast += p.ast |
| totals.to_cnt += p.to_cnt |
| totals.stl += p.stl |
| totals.blk += p.blk |
| totals.pf += p.pf |
| totals.fls_on += p.fls_on |
| totals.index += (p.index or 0) |
|
|
| |
| totals.fg_pct = round((totals.fg_m / totals.fg_a * 100), 1) if totals.fg_a > 0 else 0.0 |
| totals.tp_pct = round((totals.tp_m / totals.tp_a * 100), 1) if totals.tp_a > 0 else 0.0 |
| totals.thp_pct = round((totals.thp_m / totals.thp_a * 100), 1) if totals.thp_a > 0 else 0.0 |
| totals.ft_pct = round((totals.ft_m / totals.ft_a * 100), 1) if totals.ft_a > 0 else 0.0 |
| |
| return totals |
|
|