| """ |
| Pydantic models for advanced analytics data structures. |
| """ |
| from datetime import datetime |
| from typing import Optional, List, Dict, Any |
| from uuid import UUID |
| from pydantic import BaseModel, Field |
|
|
|
|
| |
| |
| |
|
|
| class SpacingMetric(BaseModel): |
| """Single spacing quality measurement.""" |
| frame: int |
| timestamp: float |
| spacing_quality: str = Field(..., description="'good', 'average', or 'poor'") |
| avg_distance_m: float |
| paint_players: int |
| overlap_count: int |
| offense_team: int |
|
|
|
|
| class SpacingSummary(BaseModel): |
| """Aggregated spacing statistics.""" |
| total_frames_analyzed: int |
| good_spacing_pct: float |
| average_spacing_pct: float |
| poor_spacing_pct: float |
| avg_distance_overall: float |
|
|
|
|
| |
| |
| |
|
|
| class DefensiveReaction(BaseModel): |
| """Single defensive reaction measurement.""" |
| event_id: str |
| event_type: str |
| event_frame: int |
| defender_track_id: int |
| offensive_player_track_id: int |
| distance_at_event: float |
| reaction_start_frame: Optional[int] = None |
| reaction_delay_ms: Optional[float] = None |
| closeout_speed_mps: Optional[float] = None |
| late_closeout: bool |
|
|
|
|
| class DefensiveReactionSummary(BaseModel): |
| """Aggregated defensive reaction statistics.""" |
| total_defensive_events: int |
| late_closeouts: int |
| late_closeout_rate: float |
| avg_reaction_delay_ms: Optional[float] = None |
| avg_closeout_speed_mps: Optional[float] = None |
|
|
|
|
| |
| |
| |
|
|
| class TransitionEffort(BaseModel): |
| """Transition effort measurement for a player.""" |
| possession_change_frame: int |
| player_track_id: int |
| team_id: int |
| transition_type: str = Field(..., description="'offense_to_defense' or 'defense_to_offense'") |
| effort_type: str = Field(..., description="'sprint', 'jog', or 'walk'") |
| max_speed_mps: float |
| avg_speed_mps: float |
| effort_score: float |
| duration_seconds: float |
|
|
|
|
| class TransitionEffortSummary(BaseModel): |
| """Aggregated transition effort statistics.""" |
| total_transition_events: int |
| sprint_count: int |
| jog_count: int |
| walk_count: int |
| sprint_rate: float |
| avg_effort_score: float |
| avg_max_speed_mps: float |
|
|
|
|
| |
| |
| |
|
|
| class DecisionAnalysis(BaseModel): |
| """Shot decision quality analysis.""" |
| event_id: str |
| shot_frame: int |
| shooter_track_id: int |
| shooter_contested_distance: float |
| open_teammates: int |
| best_teammate_openness: Optional[float] = None |
| decision_quality: str = Field(..., description="'high_expected_value', 'acceptable', or 'low_expected_value'") |
| shot_outcome: str |
|
|
|
|
| class DecisionQualitySummary(BaseModel): |
| """Aggregated decision quality statistics.""" |
| total_shots_analyzed: int |
| high_ev_shots: int |
| acceptable_shots: int |
| low_ev_shots: int |
| low_ev_rate: float |
| avg_shooter_contested_distance: float |
|
|
|
|
| |
| |
| |
|
|
| class LineupMetric(BaseModel): |
| """Performance metrics for a specific lineup.""" |
| team_id: int |
| lineup_hash: str |
| player_track_ids: List[int] |
| possessions_count: int |
| points_scored: int |
| points_allowed: int |
| offensive_rating: float |
| defensive_rating: float |
| net_rating: float |
| avg_spacing_score: float |
| turnovers: int |
| defensive_error_rate: float |
| total_minutes: float |
|
|
|
|
| class LineupImpactSummary(BaseModel): |
| """Aggregated lineup statistics.""" |
| total_lineups: int |
| best_lineup_hash: Optional[str] = None |
| best_lineup_net_rating: float |
| worst_lineup_hash: Optional[str] = None |
| worst_lineup_net_rating: float |
| avg_net_rating: float |
|
|
|
|
| |
| |
| |
|
|
| class FatigueIndex(BaseModel): |
| """Fatigue measurement for a player in a time window.""" |
| player_track_id: int |
| time_window_start: float |
| time_window_end: float |
| minute: int |
| baseline_speed_mps: float |
| current_speed_mps: float |
| speed_drop_percentage: float |
| baseline_reaction_ms: Optional[float] = None |
| current_reaction_ms: Optional[float] = None |
| reaction_delay_increase_percentage: Optional[float] = None |
| fatigue_level: str = Field(..., description="'low', 'medium', or 'high'") |
|
|
|
|
| class FatigueSummary(BaseModel): |
| """Aggregated fatigue statistics.""" |
| total_measurements: int |
| high_fatigue_instances: int |
| medium_fatigue_instances: int |
| avg_speed_drop_pct: float |
| max_speed_drop_pct: float |
|
|
|
|
| |
| |
| |
|
|
| class AutoClip(BaseModel): |
| """Metadata for an automatically generated clip.""" |
| clip_type: str |
| timestamp_start: float |
| timestamp_end: float |
| frame_start: int |
| frame_end: int |
| players_involved: List[int] |
| file_path: str |
| description: str |
| metadata: Dict[str, Any] |
|
|
|
|
| class ClipSummary(BaseModel): |
| """Aggregated clip generation statistics.""" |
| total_clips_generated: int |
| clips_by_type: Dict[str, int] |
| output_directory: str |
|
|
|
|
| |
| |
| |
|
|
| class TeamAdvancedSummary(BaseModel): |
| """Complete advanced analytics summary for a team/game.""" |
| video_id: UUID |
| spacing_summary: Optional[SpacingSummary] = None |
| defensive_summary: Optional[DefensiveReactionSummary] = None |
| transition_summary: Optional[TransitionEffortSummary] = None |
| decision_summary: Optional[DecisionQualitySummary] = None |
| lineup_summary: Optional[LineupImpactSummary] = None |
| fatigue_summary: Optional[FatigueSummary] = None |
| clip_summary: Optional[ClipSummary] = None |
| modules_executed: List[str] |
| modules_failed: List[str] |
|
|
|
|
| class PlayerAdvancedAnalysis(BaseModel): |
| """Advanced analytics for a specific player.""" |
| player_track_id: int |
| video_id: UUID |
| spacing_involvement: int = Field(..., description="Number of frames player was in spacing analysis") |
| defensive_reactions: List[DefensiveReaction] |
| transition_efforts: List[TransitionEffort] |
| decision_analyses: List[DecisionAnalysis] |
| fatigue_indices: List[FatigueIndex] |
| avg_effort_score: float |
| avg_reaction_delay_ms: Optional[float] = None |
| fatigue_level: str |
|
|
|
|
| class LineupComparison(BaseModel): |
| """Comparison of all lineups in a game.""" |
| video_id: UUID |
| team_1_lineups: List[LineupMetric] |
| team_2_lineups: List[LineupMetric] |
| best_overall_lineup: Optional[LineupMetric] = None |
| worst_overall_lineup: Optional[LineupMetric] = None |
|
|
|
|
| class ClipCatalog(BaseModel): |
| """Catalog of all generated clips.""" |
| video_id: UUID |
| clips: List[AutoClip] |
| summary: ClipSummary |
|
|