Coverage for models/post_match.py: 100.00%

31 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2026-04-15 17:36 +0000

1""" 

2Post-Match Stats Pydantic models. 

3 

4Models for recording goals, substitutions, and player stats 

5after a match has been completed. 

6""" 

7 

8from pydantic import BaseModel, Field 

9 

10 

11class PostMatchGoal(BaseModel): 

12 """Model for recording a post-match goal event.""" 

13 

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

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

16 player_name: str | None = Field(None, max_length=200, description="Name of goal scorer (when no roster)") 

17 match_minute: int = Field(..., ge=1, le=130, description="Minute the goal was scored") 

18 extra_time: int | None = Field(None, ge=1, description="Stoppage time minutes (e.g., 3 for 45+3)") 

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

20 

21 

22class PostMatchSubstitution(BaseModel): 

23 """Model for recording a post-match substitution event.""" 

24 

25 team_id: int = Field(..., description="ID of the team making the substitution") 

26 player_in_id: int = Field(..., description="ID of the player coming on") 

27 player_out_id: int = Field(..., description="ID of the player coming off") 

28 match_minute: int = Field(..., ge=1, le=130, description="Minute the substitution occurred") 

29 extra_time: int | None = Field(None, ge=1, description="Stoppage time minutes") 

30 

31 

32class PostMatchCard(BaseModel): 

33 """Model for recording a post-match card event (yellow or red).""" 

34 

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

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

37 player_name: str | None = Field(None, max_length=200, description="Name of player (when no roster)") 

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

39 match_minute: int = Field(..., ge=1, le=130, description="Minute the card was given") 

40 extra_time: int | None = Field(None, ge=1, description="Stoppage time minutes") 

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

42 

43 

44class PlayerStatEntry(BaseModel): 

45 """Model for a single player's match stats update.""" 

46 

47 player_id: int = Field(..., description="Player ID from roster") 

48 started: bool = Field(..., description="Whether the player started the match") 

49 played: bool = Field(..., description="Whether the player participated in the match") 

50 minutes_played: int = Field(..., ge=0, le=130, description="Total minutes played") 

51 yellow_cards: int = Field(0, ge=0, le=2, description="Yellow cards received (0-2)") 

52 red_cards: int = Field(0, ge=0, le=1, description="Red cards received (0-1)") 

53 

54 

55class BatchPlayerStatsUpdate(BaseModel): 

56 """Model for batch-updating player stats for a team in a match.""" 

57 

58 players: list[PlayerStatEntry] = Field(..., min_length=1, description="List of player stat entries")