Coverage for models/live_match.py: 98.51%

67 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2026-04-13 00:07 +0000

1""" 

2Live Match Pydantic models. 

3 

4Models for managing live match state, clock, and events. 

5""" 

6 

7from datetime import datetime 

8 

9from pydantic import BaseModel, Field 

10 

11 

12class LiveMatchClock(BaseModel): 

13 """Model for clock management actions during a live match.""" 

14 

15 action: str = Field( 

16 ..., 

17 description="Clock action: start_first_half, start_halftime, etc.", 

18 ) 

19 half_duration: int | None = Field( 

20 None, 

21 ge=20, 

22 le=60, 

23 description="Duration of each half in minutes (only for start_first_half)", 

24 ) 

25 

26 @property 

27 def valid_actions(self) -> list[str]: 

28 return ["start_first_half", "start_halftime", "start_second_half", "end_match"] 

29 

30 

31class GoalEvent(BaseModel): 

32 """Model for recording a goal event.""" 

33 

34 team_id: int = Field(..., description="ID of the team that scored") 

35 player_name: str | None = Field(None, max_length=200, description="Name of the goal scorer (legacy)") 

36 player_id: int | None = Field(None, description="ID of the goal scorer from roster (preferred)") 

37 message: str | None = Field(None, max_length=500, description="Optional description") 

38 

39 

40class LiveCardEvent(BaseModel): 

41 """Model for recording a card event during a live match.""" 

42 

43 team_id: int = Field(..., description="ID of the team the player belongs to") 

44 player_id: int = Field(..., description="ID of the carded player from roster") 

45 card_type: str = Field(..., pattern="^(yellow_card|red_card)$", description="Type of card: yellow_card or red_card") 

46 message: str | None = Field(None, max_length=500, description="Optional description (e.g., reason for card)") 

47 

48 

49class MessageEvent(BaseModel): 

50 """Model for posting a chat message.""" 

51 

52 message: str = Field(..., min_length=1, max_length=500, description="Message content") 

53 

54 

55class GoalEventUpdate(BaseModel): 

56 """Model for updating a goal event (admin corrections).""" 

57 

58 match_minute: int | None = Field(None, ge=0, le=120, description="Match minute when goal was scored") 

59 extra_time: int | None = Field(None, ge=0, le=30, description="Extra/stoppage time minutes") 

60 player_name: str | None = Field(None, max_length=200, description="Name of the goal scorer") 

61 player_id: int | None = Field(None, description="ID of the goal scorer from roster") 

62 

63 

64class MatchEventResponse(BaseModel): 

65 """Response model for a match event.""" 

66 

67 id: int 

68 match_id: int 

69 event_type: str 

70 team_id: int | None = None 

71 player_name: str | None = None 

72 player_id: int | None = None 

73 match_minute: int | None = None 

74 extra_time: int | None = None 

75 message: str 

76 created_by: str | None = None 

77 created_by_username: str | None = None 

78 created_at: datetime 

79 is_deleted: bool = False 

80 

81 

82class LiveMatchState(BaseModel): 

83 """Full live match state returned to clients.""" 

84 

85 match_id: int 

86 match_status: str 

87 home_score: int | None = None 

88 away_score: int | None = None 

89 

90 # Clock timestamps 

91 kickoff_time: datetime | None = None 

92 halftime_start: datetime | None = None 

93 second_half_start: datetime | None = None 

94 match_end_time: datetime | None = None 

95 half_duration: int = 45 # Minutes per half (default 45 for 90-min games) 

96 

97 # Team info 

98 home_team_id: int 

99 home_team_name: str 

100 away_team_id: int 

101 away_team_name: str 

102 

103 # Match metadata 

104 match_date: str 

105 age_group_name: str | None = None 

106 match_type_name: str | None = None 

107 division_name: str | None = None 

108 

109 # Recent events (last N) 

110 recent_events: list[MatchEventResponse] = [] 

111 

112 

113class LiveMatchSummary(BaseModel): 

114 """Summary info for the live matches list (for tab polling).""" 

115 

116 match_id: int 

117 match_status: str 

118 home_team_name: str 

119 away_team_name: str 

120 home_score: int | None = None 

121 away_score: int | None = None 

122 kickoff_time: datetime | None = None 

123 match_date: str