from typing import List, Optional from app.stat_import.schemas import ParsedPlayerRow, TeamTotalsSchema, ValidationIssueSchema from app.stat_import.enums import ReviewIssueType, ReviewSeverity def validate_player_row(row: ParsedPlayerRow, row_index: int) -> List[ValidationIssueSchema]: issues = [] path_prefix = f"players[{row_index}]" # Rebounds consistency if row.off_reb + row.def_reb != row.reb: issues.append(ValidationIssueSchema( issue_type=ReviewIssueType.IMPOSSIBLE_NUMERIC_TOTALS, severity=ReviewSeverity.WARNING, field_path=f"{path_prefix}.reb", raw_value=str(row.reb), suggested_value=str(row.off_reb + row.def_reb) )) # Points consistency expected_pts = (row.three_p_made * 3) + (row.two_p_made * 2) + (row.ft_made * 1) # Give some leniency since 3p might extract wrongly while 2p+3p = fg works normally # Only block if points absolutely defy mathematical possibility if expected_pts != row.points and expected_pts > 0: issues.append(ValidationIssueSchema( issue_type=ReviewIssueType.IMPOSSIBLE_NUMERIC_TOTALS, severity=ReviewSeverity.WARNING, field_path=f"{path_prefix}.points", raw_value=str(row.points), suggested_value=str(expected_pts) )) # Field goals arithmetic if row.fg_made > row.fg_attempted: issues.append(ValidationIssueSchema( issue_type=ReviewIssueType.INVALID_PERCENTAGES, severity=ReviewSeverity.ERROR, field_path=f"{path_prefix}.fg_made", raw_value=f"{row.fg_made}/{row.fg_attempted}", suggested_value=f"{row.fg_attempted}/{row.fg_made}" )) return issues def validate_team_totals(players: List[ParsedPlayerRow], totals: TeamTotalsSchema) -> List[ValidationIssueSchema]: issues = [] sum_pts = sum(p.points for p in players) if sum_pts != totals.points and totals.points > 0: issues.append(ValidationIssueSchema( issue_type=ReviewIssueType.SUMMARY_STAT_INCONSISTENCY, severity=ReviewSeverity.WARNING, field_path="team_totals.points", raw_value=str(totals.points), suggested_value=str(sum_pts) )) return issues