Skip to content

Commit 983c271

Browse files
committed
refactor: migrate core models to Pydantic with Pythonic field names
BREAKING CHANGES: - All model field names are now snake_case (e.g., spring_league instead of springleague) - Class names standardized to PascalCase (e.g., TeamRecords instead of Teamrecords) - Models now raise ValidationError instead of TypeError for missing required fields Models converted: - Sport, Season - Venue, Location, TimeZone, FieldInfo, VenueDefaultCoordinates - League, LeagueRecord - Division - Team, TeamRecord, Record, OverallLeagueRecord, TypeRecords, DivisionRecords, LeagueRecords, Records - Standings, TeamRecords, Streak - Attendance, AttendanceRecords, AttendanceTotals, AttendanceHighLowGame, AttendanceGameType Key improvements: - All models inherit from MLBBaseModel with extra=ignore (handles new API fields gracefully) - Field aliases maintain API compatibility (e.g., Field(alias=springleague)) - populate_by_name=True allows both old and new names in constructors - Fixed several type mismatches (season: int, active: bool, elevation: int, etc.) - Updated all affected tests to use new field names
1 parent 3f58432 commit 983c271

23 files changed

Lines changed: 763 additions & 862 deletions

File tree

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
1-
from dataclasses import dataclass, field
2-
from typing import Union, List
1+
from typing import List
2+
from pydantic import Field
3+
from mlbstatsapi.models.base import MLBBaseModel
34
from .attributes import AttendanceTotals, AttendanceRecords
45

5-
@dataclass
6-
class Attendance:
6+
7+
class Attendance(MLBBaseModel):
78
"""
89
A class to represent attendance.
10+
911
Attributes
1012
----------
11-
copyright : str
12-
Copyright message
1313
records : List[AttendanceRecords]
14-
List of attendance records
15-
aggregatetotals : AttendanceAggregateTotals
16-
Attendence aggregate total numbers for query
14+
List of attendance records.
15+
aggregate_totals : AttendanceTotals
16+
Attendance aggregate total numbers for query.
1717
"""
18-
aggregatetotals: Union[AttendanceTotals, dict]
19-
records: Union[List[AttendanceRecords], List[dict]] = field(default_factory=list)
20-
21-
def __post_init__(self):
22-
self.records = [AttendanceRecords(**record) for record in self.records if self.records]
23-
self.aggregatetotals = AttendanceTotals(**self.aggregatetotals)
18+
aggregate_totals: AttendanceTotals = Field(alias="aggregatetotals")
19+
records: List[AttendanceRecords] = []
Lines changed: 131 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,175 @@
1-
from dataclasses import dataclass, field
2-
from typing import Optional, Union
1+
from typing import Optional
2+
from pydantic import Field
3+
from mlbstatsapi.models.base import MLBBaseModel
34
from mlbstatsapi.models.teams import Team
45

56

6-
@dataclass
7-
class AttendanceHighLowGameContent:
7+
class AttendanceHighLowGameContent(MLBBaseModel):
88
"""
9-
A class to represent attendance records.
9+
A class to represent attendance game content.
10+
1011
Attributes
1112
----------
1213
link : str
13-
games content endpoint link
14+
Games content endpoint link.
1415
"""
1516
link: str
1617

1718

18-
@dataclass
19-
class AttendanceHighLowGame:
19+
class AttendanceHighLowGame(MLBBaseModel):
2020
"""
21-
A class to represent attendance High and Low games.
21+
A class to represent attendance high and low games.
22+
2223
Attributes
2324
----------
24-
gamepk : int
25-
Games Id number
25+
game_pk : int
26+
Game's ID number.
2627
link : str
27-
games endpoint link
28+
Games endpoint link.
2829
content : AttendanceHighLowGameContent
29-
Content for this game
30-
daynight : str
31-
Type of time of day for game
30+
Content for this game.
31+
day_night : str
32+
Time of day for game (day or night).
3233
"""
33-
gamepk: int
34+
game_pk: int = Field(alias="gamepk")
3435
link: str
35-
content: Union[AttendanceHighLowGameContent, dict]
36-
daynight: str
37-
38-
def __post_init__(self):
39-
self.content = AttendanceHighLowGameContent(**self.content)
36+
content: AttendanceHighLowGameContent
37+
day_night: str = Field(alias="daynight")
4038

4139

42-
@dataclass
43-
class AttendenceGameType:
40+
class AttendanceGameType(MLBBaseModel):
4441
"""
45-
A class to represent Attendance Game Type.
42+
A class to represent attendance game type.
43+
4644
Attributes
4745
----------
4846
id : str
49-
Game type id
47+
Game type ID.
5048
description : str
51-
Game type description
49+
Game type description.
5250
"""
5351
id: str
5452
description: str
5553

5654

57-
@dataclass(repr=False)
58-
class AttendanceRecords:
55+
class AttendanceRecords(MLBBaseModel):
5956
"""
6057
A class to represent attendance records.
58+
6159
Attributes
6260
----------
63-
openingstotal : int
64-
Total amount of openings
65-
openingstotalaway : int
66-
Total amount of opening away games
67-
openingstotalhome : int
68-
Total amount of opening home games
69-
openingstotallost : int
70-
Total amount of openings lost
71-
gamestotal : int
72-
Total amount of games
73-
gamesawaytotal : int
74-
Total amount of away games
75-
gameshometotal : int
76-
Total amount of home games
61+
openings_total : int
62+
Total number of openings.
63+
openings_total_away : int
64+
Total number of opening away games.
65+
openings_total_home : int
66+
Total number of opening home games.
67+
openings_total_lost : int
68+
Total number of openings lost.
69+
games_total : int
70+
Total number of games.
71+
games_away_total : int
72+
Total number of away games.
73+
games_home_total : int
74+
Total number of home games.
7775
year : str
78-
Year as a string
79-
attendanceaverageaway : int
80-
Average attendance for away games
81-
attendanceaveragehome : int
82-
Average attendance for home games
83-
attendanceaverageytd : int
84-
Average attendance year to date
85-
attendancehigh : int
86-
Attendance High number
87-
attendancehighdate : str
88-
Attendance high date
89-
attendancehighgame : AttendanceHighLowGame
90-
Attendance high game
91-
attendancelow : int
92-
Attendance low number
93-
attendancelowdate : str
94-
Attendance low date
95-
attendancelowgame : AttendanceHighLowGame
96-
Attendance low game
97-
attendanceopeningaverage : int
98-
Attendance opening average
99-
attendancetotal : int
100-
Attendance total
101-
attendancetotalaway : int
102-
Attendance total away
103-
attendancetotalhome : int
104-
Attendance total home
105-
gametype : AttendenceGameType
106-
Game type
76+
Year as a string.
77+
attendance_average_away : int
78+
Average attendance for away games.
79+
attendance_average_home : int
80+
Average attendance for home games.
81+
attendance_average_ytd : int
82+
Average attendance year to date.
83+
attendance_high : int
84+
Attendance high number.
85+
attendance_high_date : str
86+
Attendance high date.
87+
attendance_high_game : AttendanceHighLowGame
88+
Attendance high game.
89+
attendance_low : int
90+
Attendance low number.
91+
attendance_low_date : str
92+
Attendance low date.
93+
attendance_low_game : AttendanceHighLowGame
94+
Attendance low game.
95+
attendance_opening_average : int
96+
Attendance opening average.
97+
attendance_total : int
98+
Attendance total.
99+
attendance_total_away : int
100+
Attendance total away.
101+
attendance_total_home : int
102+
Attendance total home.
103+
game_type : AttendanceGameType
104+
Game type.
107105
team : Team
108-
Team
106+
Team.
109107
"""
110-
openingstotal: int
111-
openingstotalaway: int
112-
openingstotalhome: int
113-
openingstotallost: int
114-
gamestotal: int
115-
gamesawaytotal: int
116-
gameshometotal: int
108+
openings_total: int = Field(alias="openingstotal")
109+
openings_total_away: int = Field(alias="openingstotalaway")
110+
openings_total_home: int = Field(alias="openingstotalhome")
111+
openings_total_lost: int = Field(alias="openingstotallost")
112+
games_total: int = Field(alias="gamestotal")
113+
games_away_total: int = Field(alias="gamesawaytotal")
114+
games_home_total: int = Field(alias="gameshometotal")
117115
year: str
118-
attendanceaverageytd: int
119-
gametype: Union[AttendenceGameType, dict]
120-
team: Union[Team, dict]
121-
attendancetotal: Optional[int] = None
122-
attendanceaverageaway: Optional[int] = None
123-
attendanceaveragehome: Optional[int] = None
124-
attendancehigh: Optional[int] = None
125-
attendancehighdate: Optional[str] = None
126-
attendancehighgame: Optional[Union[AttendanceHighLowGame, dict]] = None
127-
attendancelow: Optional[int] = None
128-
attendancelowdate: Optional[str] = None
129-
attendancelowgame: Optional[Union[AttendanceHighLowGame, dict]] = None
130-
attendancetotalaway: Optional[int] = None
131-
attendancetotalhome: Optional[int] = None
132-
attendanceopeningaverage: Optional[int] = None
133-
134-
def __post_init__(self):
135-
self.attendancehighgame = AttendanceHighLowGame(**self.attendancehighgame) if self.attendancehighgame else self.attendancehighgame
136-
self.attendancelowgame = AttendanceHighLowGame(**self.attendancelowgame) if self.attendancelowgame else self.attendancelowgame
137-
self.gameType = AttendenceGameType(**self.gametype)
138-
self.team = Team(**self.team)
116+
attendance_average_ytd: int = Field(alias="attendanceaverageytd")
117+
game_type: AttendanceGameType = Field(alias="gametype")
118+
team: Team
119+
attendance_total: Optional[int] = Field(default=None, alias="attendancetotal")
120+
attendance_average_away: Optional[int] = Field(default=None, alias="attendanceaverageaway")
121+
attendance_average_home: Optional[int] = Field(default=None, alias="attendanceaveragehome")
122+
attendance_high: Optional[int] = Field(default=None, alias="attendancehigh")
123+
attendance_high_date: Optional[str] = Field(default=None, alias="attendancehighdate")
124+
attendance_high_game: Optional[AttendanceHighLowGame] = Field(default=None, alias="attendancehighgame")
125+
attendance_low: Optional[int] = Field(default=None, alias="attendancelow")
126+
attendance_low_date: Optional[str] = Field(default=None, alias="attendancelowdate")
127+
attendance_low_game: Optional[AttendanceHighLowGame] = Field(default=None, alias="attendancelowgame")
128+
attendance_total_away: Optional[int] = Field(default=None, alias="attendancetotalaway")
129+
attendance_total_home: Optional[int] = Field(default=None, alias="attendancetotalhome")
130+
attendance_opening_average: Optional[int] = Field(default=None, alias="attendanceopeningaverage")
139131

140-
def __repr__(self) -> str:
141-
kws = [f'{key}={value}' for key, value in self.__dict__.items() if value is not None and value]
142-
return "{}({})".format(type(self).__name__, ", ".join(kws))
143132

144-
@dataclass(repr=False)
145-
class AttendanceTotals:
133+
class AttendanceTotals(MLBBaseModel):
146134
"""
147-
A class to represent attendance aggregate toatls.
135+
A class to represent attendance aggregate totals.
136+
148137
Attributes
149138
----------
150-
openingstotalaway : int
151-
Total amount of opening game attendance number
152-
openingstotalhome : int
153-
Total amount of opening home game attendance number
154-
openingstotallost : int
155-
Total amount of opening games lost
156-
openingstotalytd : int
157-
Total amount of opening games year to date
158-
attendanceaverageaway : int
159-
Average away game attendance
160-
attendanceaveragehome : int
161-
Average home game attendance
162-
attendanceaverageytd : int
163-
Average attendance year to date
164-
attendancehigh : int
165-
Attendance high
166-
attendancehighdate : str
167-
Attendance high date
168-
attendancetotal : int
169-
Attendance total
170-
attendancetotalaway : int
171-
Attendace total away
172-
attendancetotalhome : int
173-
Attendance total home
139+
openings_total_away : int
140+
Total opening game attendance number for away games.
141+
openings_total_home : int
142+
Total opening home game attendance number.
143+
openings_total_lost : int
144+
Total number of opening games lost.
145+
openings_total_ytd : int
146+
Total number of opening games year to date.
147+
attendance_average_away : int
148+
Average away game attendance.
149+
attendance_average_home : int
150+
Average home game attendance.
151+
attendance_average_ytd : int
152+
Average attendance year to date.
153+
attendance_high : int
154+
Attendance high.
155+
attendance_high_date : str
156+
Attendance high date.
157+
attendance_total : int
158+
Attendance total.
159+
attendance_total_away : int
160+
Attendance total away.
161+
attendance_total_home : int
162+
Attendance total home.
174163
"""
175-
openingstotalaway: int
176-
openingstotalhome: int
177-
openingstotallost: int
178-
openingstotalytd: int
179-
attendanceaverageytd: int
180-
attendancehigh: int
181-
attendancehighdate: str
182-
attendancetotal: int
183-
attendancetotalaway: int
184-
attendancetotalhome: int
185-
attendanceaverageaway: Optional[int] = None
186-
attendanceaveragehome: Optional[int] = None
187-
188-
def __repr__(self) -> str:
189-
kws = [f'{key}={value}' for key, value in self.__dict__.items() if value is not None and value]
190-
return "{}({})".format(type(self).__name__, ", ".join(kws))
164+
openings_total_away: int = Field(alias="openingstotalaway")
165+
openings_total_home: int = Field(alias="openingstotalhome")
166+
openings_total_lost: int = Field(alias="openingstotallost")
167+
openings_total_ytd: int = Field(alias="openingstotalytd")
168+
attendance_average_ytd: int = Field(alias="attendanceaverageytd")
169+
attendance_high: int = Field(alias="attendancehigh")
170+
attendance_high_date: str = Field(alias="attendancehighdate")
171+
attendance_total: int = Field(alias="attendancetotal")
172+
attendance_total_away: int = Field(alias="attendancetotalaway")
173+
attendance_total_home: int = Field(alias="attendancetotalhome")
174+
attendance_average_away: Optional[int] = Field(default=None, alias="attendanceaverageaway")
175+
attendance_average_home: Optional[int] = Field(default=None, alias="attendanceaveragehome")
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
from .division import Division
1+
from .division import Division
2+
from mlbstatsapi.models.leagues import League
3+
4+
# Rebuild to resolve forward reference to League
5+
Division.model_rebuild()

0 commit comments

Comments
 (0)