cfb-data 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cfb_data/__init__.py +234 -0
- cfb_data/_dataframes.py +279 -0
- cfb_data/_executor.py +129 -0
- cfb_data/_parquet.py +197 -0
- cfb_data/_request_rules.py +41 -0
- cfb_data/_requests.py +36 -0
- cfb_data/_tabular.py +676 -0
- cfb_data/_transport.py +452 -0
- cfb_data/adjusted_metrics/__init__.py +29 -0
- cfb_data/adjusted_metrics/models/__init__.py +1 -0
- cfb_data/adjusted_metrics/models/pydantic/__init__.py +29 -0
- cfb_data/adjusted_metrics/models/pydantic/requests.py +45 -0
- cfb_data/adjusted_metrics/models/pydantic/responses.py +88 -0
- cfb_data/adjusted_metrics/resource.py +220 -0
- cfb_data/base/__init__.py +6 -0
- cfb_data/base/types.py +112 -0
- cfb_data/betting/__init__.py +15 -0
- cfb_data/betting/models/__init__.py +1 -0
- cfb_data/betting/models/pydantic/__init__.py +6 -0
- cfb_data/betting/models/pydantic/requests.py +35 -0
- cfb_data/betting/models/pydantic/responses.py +62 -0
- cfb_data/betting/resource.py +91 -0
- cfb_data/client.py +305 -0
- cfb_data/coaches/__init__.py +57 -0
- cfb_data/coaches/models/__init__.py +1 -0
- cfb_data/coaches/models/pydantic/__init__.py +57 -0
- cfb_data/coaches/models/pydantic/requests.py +75 -0
- cfb_data/coaches/models/pydantic/responses.py +261 -0
- cfb_data/coaches/resource.py +216 -0
- cfb_data/conferences/__init__.py +23 -0
- cfb_data/conferences/models/__init__.py +1 -0
- cfb_data/conferences/models/pydantic/__init__.py +23 -0
- cfb_data/conferences/models/pydantic/requests.py +69 -0
- cfb_data/conferences/models/pydantic/responses.py +64 -0
- cfb_data/conferences/resource.py +175 -0
- cfb_data/draft/__init__.py +19 -0
- cfb_data/draft/models/__init__.py +1 -0
- cfb_data/draft/models/pydantic/__init__.py +12 -0
- cfb_data/draft/models/pydantic/requests.py +18 -0
- cfb_data/draft/models/pydantic/responses.py +65 -0
- cfb_data/draft/resource.py +132 -0
- cfb_data/drives/__init__.py +13 -0
- cfb_data/drives/models/__init__.py +1 -0
- cfb_data/drives/models/pydantic/__init__.py +17 -0
- cfb_data/drives/models/pydantic/requests.py +42 -0
- cfb_data/drives/models/pydantic/responses.py +46 -0
- cfb_data/drives/resource.py +94 -0
- cfb_data/enums.py +93 -0
- cfb_data/errors.py +234 -0
- cfb_data/games/__init__.py +42 -0
- cfb_data/games/models/__init__.py +1 -0
- cfb_data/games/models/pydantic/__init__.py +106 -0
- cfb_data/games/models/pydantic/requests.py +266 -0
- cfb_data/games/models/pydantic/responses.py +495 -0
- cfb_data/games/resource.py +486 -0
- cfb_data/info/__init__.py +25 -0
- cfb_data/info/models/__init__.py +1 -0
- cfb_data/info/models/pydantic/__init__.py +23 -0
- cfb_data/info/models/pydantic/requests.py +18 -0
- cfb_data/info/models/pydantic/responses.py +103 -0
- cfb_data/info/resource.py +88 -0
- cfb_data/metrics/__init__.py +53 -0
- cfb_data/metrics/models/__init__.py +1 -0
- cfb_data/metrics/models/pydantic/__init__.py +49 -0
- cfb_data/metrics/models/pydantic/requests.py +121 -0
- cfb_data/metrics/models/pydantic/responses.py +182 -0
- cfb_data/metrics/resource.py +371 -0
- cfb_data/players/__init__.py +44 -0
- cfb_data/players/models/__init__.py +1 -0
- cfb_data/players/models/pydantic/__init__.py +41 -0
- cfb_data/players/models/pydantic/requests.py +70 -0
- cfb_data/players/models/pydantic/responses.py +171 -0
- cfb_data/players/resource.py +258 -0
- cfb_data/playoffs/__init__.py +41 -0
- cfb_data/playoffs/models/__init__.py +1 -0
- cfb_data/playoffs/models/pydantic/__init__.py +37 -0
- cfb_data/playoffs/models/pydantic/requests.py +30 -0
- cfb_data/playoffs/models/pydantic/responses.py +173 -0
- cfb_data/playoffs/resource.py +149 -0
- cfb_data/plays/__init__.py +43 -0
- cfb_data/plays/models/__init__.py +1 -0
- cfb_data/plays/models/pydantic/__init__.py +35 -0
- cfb_data/plays/models/pydantic/requests.py +85 -0
- cfb_data/plays/models/pydantic/responses.py +231 -0
- cfb_data/plays/resource.py +249 -0
- cfb_data/py.typed +0 -0
- cfb_data/rankings/__init__.py +16 -0
- cfb_data/rankings/models/__init__.py +1 -0
- cfb_data/rankings/models/pydantic/__init__.py +6 -0
- cfb_data/rankings/models/pydantic/requests.py +36 -0
- cfb_data/rankings/models/pydantic/responses.py +42 -0
- cfb_data/rankings/resource.py +89 -0
- cfb_data/ratings/__init__.py +59 -0
- cfb_data/ratings/models/__init__.py +1 -0
- cfb_data/ratings/models/pydantic/__init__.py +55 -0
- cfb_data/ratings/models/pydantic/requests.py +87 -0
- cfb_data/ratings/models/pydantic/responses.py +215 -0
- cfb_data/ratings/resource.py +342 -0
- cfb_data/recruiting/__init__.py +26 -0
- cfb_data/recruiting/models/__init__.py +1 -0
- cfb_data/recruiting/models/pydantic/__init__.py +23 -0
- cfb_data/recruiting/models/pydantic/requests.py +69 -0
- cfb_data/recruiting/models/pydantic/responses.py +70 -0
- cfb_data/recruiting/resource.py +180 -0
- cfb_data/retry.py +49 -0
- cfb_data/stats/__init__.py +69 -0
- cfb_data/stats/models/__init__.py +1 -0
- cfb_data/stats/models/pydantic/__init__.py +65 -0
- cfb_data/stats/models/pydantic/requests.py +167 -0
- cfb_data/stats/models/pydantic/responses.py +291 -0
- cfb_data/stats/resource.py +400 -0
- cfb_data/teams/__init__.py +40 -0
- cfb_data/teams/models/__init__.py +1 -0
- cfb_data/teams/models/pydantic/__init__.py +26 -0
- cfb_data/teams/models/pydantic/requests.py +96 -0
- cfb_data/teams/models/pydantic/responses.py +116 -0
- cfb_data/teams/resource.py +270 -0
- cfb_data/venues/__init__.py +6 -0
- cfb_data/venues/models/__init__.py +1 -0
- cfb_data/venues/models/pydantic/__init__.py +5 -0
- cfb_data/venues/models/pydantic/responses.py +24 -0
- cfb_data/venues/resource.py +51 -0
- cfb_data-0.4.1.dist-info/METADATA +414 -0
- cfb_data-0.4.1.dist-info/RECORD +127 -0
- cfb_data-0.4.1.dist-info/WHEEL +5 -0
- cfb_data-0.4.1.dist-info/licenses/LICENSE +21 -0
- cfb_data-0.4.1.dist-info/top_level.txt +1 -0
cfb_data/client.py
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""Provide the primary context-managed CollegeFootballData client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from types import TracebackType
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
Literal,
|
|
9
|
+
Self,
|
|
10
|
+
cast,
|
|
11
|
+
overload,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
import pandas as pd
|
|
15
|
+
|
|
16
|
+
from cfb_data._dataframes import _DataFrameAdapter, _PandasAdapter, _PolarsAdapter
|
|
17
|
+
from cfb_data._executor import _EndpointExecutor
|
|
18
|
+
from cfb_data._transport import (
|
|
19
|
+
_HTTPTransport,
|
|
20
|
+
_resolve_api_key,
|
|
21
|
+
_validate_base_url,
|
|
22
|
+
_validate_timeout,
|
|
23
|
+
)
|
|
24
|
+
from cfb_data.adjusted_metrics.resource import AdjustedMetricsResource
|
|
25
|
+
from cfb_data.betting.resource import BettingResource
|
|
26
|
+
from cfb_data.coaches.resource import CoachesResource
|
|
27
|
+
from cfb_data.conferences.resource import ConferencesResource
|
|
28
|
+
from cfb_data.draft.resource import DraftResource
|
|
29
|
+
from cfb_data.drives.resource import DrivesResource
|
|
30
|
+
from cfb_data.errors import CFBDConfigurationError
|
|
31
|
+
from cfb_data.games.resource import GamesResource
|
|
32
|
+
from cfb_data.info.resource import InfoResource
|
|
33
|
+
from cfb_data.metrics.resource import MetricsResource
|
|
34
|
+
from cfb_data.players.resource import PlayersResource
|
|
35
|
+
from cfb_data.playoffs.resource import PlayoffsResource
|
|
36
|
+
from cfb_data.plays.resource import PlaysResource
|
|
37
|
+
from cfb_data.rankings.resource import RankingsResource
|
|
38
|
+
from cfb_data.ratings.resource import RatingsResource
|
|
39
|
+
from cfb_data.recruiting.resource import RecruitingResource
|
|
40
|
+
from cfb_data.retry import RetryPolicy
|
|
41
|
+
from cfb_data.stats.resource import StatsResource
|
|
42
|
+
from cfb_data.teams.resource import TeamsResource
|
|
43
|
+
from cfb_data.venues.resource import VenuesResource
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
import polars as pl
|
|
47
|
+
|
|
48
|
+
type DataFrameBackend = Literal["pandas", "polars"]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class CFBDClient[FrameT]:
|
|
52
|
+
"""Access validated CFBD endpoint namespaces through one pooled session.
|
|
53
|
+
|
|
54
|
+
The client is one-shot and must be used with ``async with``. Endpoint calls
|
|
55
|
+
return eager pandas DataFrames by default or eager Polars DataFrames when
|
|
56
|
+
``dataframe_backend="polars"`` is selected.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
@overload
|
|
60
|
+
def __init__(
|
|
61
|
+
self: CFBDClient[pd.DataFrame],
|
|
62
|
+
api_key: str | None = None,
|
|
63
|
+
*,
|
|
64
|
+
dataframe_backend: Literal["pandas"] = "pandas",
|
|
65
|
+
base_url: str = "https://api.collegefootballdata.com",
|
|
66
|
+
timeout_seconds: float = 30.0,
|
|
67
|
+
retry_policy: RetryPolicy | None = None,
|
|
68
|
+
) -> None: ...
|
|
69
|
+
|
|
70
|
+
@overload
|
|
71
|
+
def __init__(
|
|
72
|
+
self: CFBDClient[pl.DataFrame],
|
|
73
|
+
api_key: str | None = None,
|
|
74
|
+
*,
|
|
75
|
+
dataframe_backend: Literal["polars"],
|
|
76
|
+
base_url: str = "https://api.collegefootballdata.com",
|
|
77
|
+
timeout_seconds: float = 30.0,
|
|
78
|
+
retry_policy: RetryPolicy | None = None,
|
|
79
|
+
) -> None: ...
|
|
80
|
+
|
|
81
|
+
@overload
|
|
82
|
+
def __init__(
|
|
83
|
+
self: CFBDClient[pd.DataFrame | pl.DataFrame],
|
|
84
|
+
api_key: str | None = None,
|
|
85
|
+
*,
|
|
86
|
+
dataframe_backend: DataFrameBackend,
|
|
87
|
+
base_url: str = "https://api.collegefootballdata.com",
|
|
88
|
+
timeout_seconds: float = 30.0,
|
|
89
|
+
retry_policy: RetryPolicy | None = None,
|
|
90
|
+
) -> None: ...
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
api_key: str | None = None,
|
|
95
|
+
*,
|
|
96
|
+
dataframe_backend: DataFrameBackend = "pandas",
|
|
97
|
+
base_url: str = "https://api.collegefootballdata.com",
|
|
98
|
+
timeout_seconds: float = 30.0,
|
|
99
|
+
retry_policy: RetryPolicy | None = None,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Initialize a one-shot client without opening its HTTP session.
|
|
102
|
+
|
|
103
|
+
:param api_key: Explicit API key, or ``None`` to use ``CFBD_API_KEY``.
|
|
104
|
+
:param dataframe_backend: Eager DataFrame implementation to return.
|
|
105
|
+
:param base_url: API origin and optional base path.
|
|
106
|
+
:param timeout_seconds: Finite total timeout for each HTTP attempt.
|
|
107
|
+
:param retry_policy: Custom retry behavior, or defaults when omitted.
|
|
108
|
+
:raises CFBDConfigurationError: If client configuration is invalid.
|
|
109
|
+
"""
|
|
110
|
+
if dataframe_backend not in {"pandas", "polars"}:
|
|
111
|
+
raise CFBDConfigurationError(
|
|
112
|
+
"dataframe_backend must be either 'pandas' or 'polars'"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
transport = _HTTPTransport(
|
|
116
|
+
api_key=_resolve_api_key(api_key),
|
|
117
|
+
base_url=_validate_base_url(base_url),
|
|
118
|
+
timeout_seconds=_validate_timeout(timeout_seconds),
|
|
119
|
+
retry_policy=retry_policy or RetryPolicy(),
|
|
120
|
+
)
|
|
121
|
+
executor = _EndpointExecutor(transport)
|
|
122
|
+
concrete_adapter = (
|
|
123
|
+
_PandasAdapter() if dataframe_backend == "pandas" else _PolarsAdapter()
|
|
124
|
+
)
|
|
125
|
+
# The constructor overload ties this runtime literal branch to FrameT.
|
|
126
|
+
adapter = cast(_DataFrameAdapter[FrameT], concrete_adapter)
|
|
127
|
+
|
|
128
|
+
self._transport = transport
|
|
129
|
+
self._games = GamesResource(executor, adapter)
|
|
130
|
+
self._drives = DrivesResource(executor, adapter)
|
|
131
|
+
self._plays = PlaysResource(executor, adapter)
|
|
132
|
+
self._venues = VenuesResource(executor, adapter)
|
|
133
|
+
self._conferences = ConferencesResource(executor, adapter)
|
|
134
|
+
self._teams = TeamsResource(executor, adapter)
|
|
135
|
+
self._stats = StatsResource(executor, adapter)
|
|
136
|
+
self._metrics = MetricsResource(executor, adapter)
|
|
137
|
+
self._ratings = RatingsResource(executor, adapter)
|
|
138
|
+
self._players = PlayersResource(executor, adapter)
|
|
139
|
+
self._rankings = RankingsResource(executor, adapter)
|
|
140
|
+
self._betting = BettingResource(executor, adapter)
|
|
141
|
+
self._recruiting = RecruitingResource(executor, adapter)
|
|
142
|
+
self._coaches = CoachesResource(executor, adapter)
|
|
143
|
+
self._draft = DraftResource(executor, adapter)
|
|
144
|
+
self._playoffs = PlayoffsResource(executor, adapter)
|
|
145
|
+
self._adjusted_metrics = AdjustedMetricsResource(executor, adapter)
|
|
146
|
+
self._info = InfoResource(executor)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def games(self) -> GamesResource[FrameT]:
|
|
150
|
+
"""Return the typed Games endpoint namespace.
|
|
151
|
+
|
|
152
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
153
|
+
"""
|
|
154
|
+
return self._games
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def drives(self) -> DrivesResource[FrameT]:
|
|
158
|
+
"""Return the typed Drives endpoint namespace.
|
|
159
|
+
|
|
160
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
161
|
+
"""
|
|
162
|
+
return self._drives
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def plays(self) -> PlaysResource[FrameT]:
|
|
166
|
+
"""Return the typed Plays endpoint namespace.
|
|
167
|
+
|
|
168
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
169
|
+
"""
|
|
170
|
+
return self._plays
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def venues(self) -> VenuesResource[FrameT]:
|
|
174
|
+
"""Return the typed Venues endpoint namespace.
|
|
175
|
+
|
|
176
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
177
|
+
"""
|
|
178
|
+
return self._venues
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def conferences(self) -> ConferencesResource[FrameT]:
|
|
182
|
+
"""Return the typed Conferences endpoint namespace.
|
|
183
|
+
|
|
184
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
185
|
+
"""
|
|
186
|
+
return self._conferences
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def teams(self) -> TeamsResource[FrameT]:
|
|
190
|
+
"""Return the typed Teams endpoint namespace.
|
|
191
|
+
|
|
192
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
193
|
+
"""
|
|
194
|
+
return self._teams
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def stats(self) -> StatsResource[FrameT]:
|
|
198
|
+
"""Return the typed Stats endpoint namespace.
|
|
199
|
+
|
|
200
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
201
|
+
"""
|
|
202
|
+
return self._stats
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def metrics(self) -> MetricsResource[FrameT]:
|
|
206
|
+
"""Return the typed Metrics endpoint namespace.
|
|
207
|
+
|
|
208
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
209
|
+
"""
|
|
210
|
+
return self._metrics
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def ratings(self) -> RatingsResource[FrameT]:
|
|
214
|
+
"""Return the typed Ratings endpoint namespace.
|
|
215
|
+
|
|
216
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
217
|
+
"""
|
|
218
|
+
return self._ratings
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def players(self) -> PlayersResource[FrameT]:
|
|
222
|
+
"""Return the typed Players endpoint namespace.
|
|
223
|
+
|
|
224
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
225
|
+
"""
|
|
226
|
+
return self._players
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def rankings(self) -> RankingsResource[FrameT]:
|
|
230
|
+
"""Return the typed Rankings endpoint namespace.
|
|
231
|
+
|
|
232
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
233
|
+
"""
|
|
234
|
+
return self._rankings
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def betting(self) -> BettingResource[FrameT]:
|
|
238
|
+
"""Return the typed Betting endpoint namespace.
|
|
239
|
+
|
|
240
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
241
|
+
"""
|
|
242
|
+
return self._betting
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def recruiting(self) -> RecruitingResource[FrameT]:
|
|
246
|
+
"""Return the typed Recruiting endpoint namespace.
|
|
247
|
+
|
|
248
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
249
|
+
"""
|
|
250
|
+
return self._recruiting
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def coaches(self) -> CoachesResource[FrameT]:
|
|
254
|
+
"""Return the typed Coaches endpoint namespace.
|
|
255
|
+
|
|
256
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
257
|
+
"""
|
|
258
|
+
return self._coaches
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def draft(self) -> DraftResource[FrameT]:
|
|
262
|
+
"""Return the typed Draft endpoint namespace.
|
|
263
|
+
|
|
264
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
265
|
+
"""
|
|
266
|
+
return self._draft
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def playoffs(self) -> PlayoffsResource[FrameT]:
|
|
270
|
+
"""Return the typed Playoffs endpoint namespace.
|
|
271
|
+
|
|
272
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
273
|
+
"""
|
|
274
|
+
return self._playoffs
|
|
275
|
+
|
|
276
|
+
@property
|
|
277
|
+
def adjusted_metrics(self) -> AdjustedMetricsResource[FrameT]:
|
|
278
|
+
"""Return the typed Adjusted Metrics endpoint namespace.
|
|
279
|
+
|
|
280
|
+
:return: Resource bound to this client's session and DataFrame backend.
|
|
281
|
+
"""
|
|
282
|
+
return self._adjusted_metrics
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def info(self) -> InfoResource:
|
|
286
|
+
"""Return the typed operational Info endpoint namespace.
|
|
287
|
+
|
|
288
|
+
:return: Resource bound to this client's session.
|
|
289
|
+
"""
|
|
290
|
+
return self._info
|
|
291
|
+
|
|
292
|
+
async def __aenter__(self) -> Self:
|
|
293
|
+
await self._transport.open()
|
|
294
|
+
return self
|
|
295
|
+
|
|
296
|
+
async def __aexit__(
|
|
297
|
+
self,
|
|
298
|
+
exc_type: type[BaseException] | None,
|
|
299
|
+
exc_value: BaseException | None,
|
|
300
|
+
traceback: TracebackType | None,
|
|
301
|
+
) -> None:
|
|
302
|
+
await self._transport.close()
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
__all__ = ["CFBDClient", "DataFrameBackend"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Export the supported Coaches namespace and public contracts."""
|
|
2
|
+
|
|
3
|
+
from .models.pydantic import (
|
|
4
|
+
Coach,
|
|
5
|
+
CoachAlmaMater,
|
|
6
|
+
CoachCareer,
|
|
7
|
+
CoachCfpContext,
|
|
8
|
+
CoachCfpOutcome,
|
|
9
|
+
CoachDraftContext,
|
|
10
|
+
CoachesRequest,
|
|
11
|
+
CoachPollResume,
|
|
12
|
+
CoachProfile,
|
|
13
|
+
CoachProfileRequest,
|
|
14
|
+
CoachRatingContext,
|
|
15
|
+
CoachRecord,
|
|
16
|
+
CoachRecordSplits,
|
|
17
|
+
CoachRecruitingContext,
|
|
18
|
+
CoachReference,
|
|
19
|
+
CoachScoring,
|
|
20
|
+
CoachSeason,
|
|
21
|
+
CoachSeasonsRequest,
|
|
22
|
+
CoachSeasonTeamReference,
|
|
23
|
+
CoachTeamReference,
|
|
24
|
+
CoachTenure,
|
|
25
|
+
CoachTenuresRequest,
|
|
26
|
+
CoachYearOverYear,
|
|
27
|
+
DetailedCoachSeason,
|
|
28
|
+
)
|
|
29
|
+
from .resource import CoachesResource
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"Coach",
|
|
33
|
+
"CoachAlmaMater",
|
|
34
|
+
"CoachCareer",
|
|
35
|
+
"CoachCfpContext",
|
|
36
|
+
"CoachCfpOutcome",
|
|
37
|
+
"CoachDraftContext",
|
|
38
|
+
"CoachesRequest",
|
|
39
|
+
"CoachesResource",
|
|
40
|
+
"CoachPollResume",
|
|
41
|
+
"CoachProfile",
|
|
42
|
+
"CoachProfileRequest",
|
|
43
|
+
"CoachRatingContext",
|
|
44
|
+
"CoachRecord",
|
|
45
|
+
"CoachRecordSplits",
|
|
46
|
+
"CoachRecruitingContext",
|
|
47
|
+
"CoachReference",
|
|
48
|
+
"CoachScoring",
|
|
49
|
+
"CoachSeason",
|
|
50
|
+
"CoachSeasonsRequest",
|
|
51
|
+
"CoachSeasonTeamReference",
|
|
52
|
+
"CoachTeamReference",
|
|
53
|
+
"CoachTenure",
|
|
54
|
+
"CoachTenuresRequest",
|
|
55
|
+
"CoachYearOverYear",
|
|
56
|
+
"DetailedCoachSeason",
|
|
57
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Export Coaches model families."""
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Export validated Coaches request and response models."""
|
|
2
|
+
|
|
3
|
+
from .requests import (
|
|
4
|
+
CoachesRequest,
|
|
5
|
+
CoachProfileRequest,
|
|
6
|
+
CoachSeasonsRequest,
|
|
7
|
+
CoachTenuresRequest,
|
|
8
|
+
)
|
|
9
|
+
from .responses import (
|
|
10
|
+
Coach,
|
|
11
|
+
CoachAlmaMater,
|
|
12
|
+
CoachCareer,
|
|
13
|
+
CoachCfpContext,
|
|
14
|
+
CoachCfpOutcome,
|
|
15
|
+
CoachDraftContext,
|
|
16
|
+
CoachPollResume,
|
|
17
|
+
CoachProfile,
|
|
18
|
+
CoachRatingContext,
|
|
19
|
+
CoachRecord,
|
|
20
|
+
CoachRecordSplits,
|
|
21
|
+
CoachRecruitingContext,
|
|
22
|
+
CoachReference,
|
|
23
|
+
CoachScoring,
|
|
24
|
+
CoachSeason,
|
|
25
|
+
CoachSeasonTeamReference,
|
|
26
|
+
CoachTeamReference,
|
|
27
|
+
CoachTenure,
|
|
28
|
+
CoachYearOverYear,
|
|
29
|
+
DetailedCoachSeason,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"Coach",
|
|
34
|
+
"CoachAlmaMater",
|
|
35
|
+
"CoachCareer",
|
|
36
|
+
"CoachCfpContext",
|
|
37
|
+
"CoachCfpOutcome",
|
|
38
|
+
"CoachDraftContext",
|
|
39
|
+
"CoachesRequest",
|
|
40
|
+
"CoachPollResume",
|
|
41
|
+
"CoachProfile",
|
|
42
|
+
"CoachProfileRequest",
|
|
43
|
+
"CoachRatingContext",
|
|
44
|
+
"CoachRecord",
|
|
45
|
+
"CoachRecordSplits",
|
|
46
|
+
"CoachRecruitingContext",
|
|
47
|
+
"CoachReference",
|
|
48
|
+
"CoachScoring",
|
|
49
|
+
"CoachSeason",
|
|
50
|
+
"CoachSeasonsRequest",
|
|
51
|
+
"CoachSeasonTeamReference",
|
|
52
|
+
"CoachTeamReference",
|
|
53
|
+
"CoachTenure",
|
|
54
|
+
"CoachTenuresRequest",
|
|
55
|
+
"CoachYearOverYear",
|
|
56
|
+
"DetailedCoachSeason",
|
|
57
|
+
]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Validate request parameters for implemented Coaches endpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Self
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _require_ascending_years(min_year: int | None, max_year: int | None) -> None:
|
|
11
|
+
"""Require an optional year range to be ascending."""
|
|
12
|
+
if min_year is not None and max_year is not None and min_year > max_year:
|
|
13
|
+
raise ValueError("min_year must be less than or equal to max_year")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _CoachesRequest(BaseModel):
|
|
17
|
+
"""Apply the closed-object contract shared by Coaches requests."""
|
|
18
|
+
|
|
19
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CoachesRequest(_CoachesRequest):
|
|
23
|
+
"""Validate filters accepted by ``GET /coaches``."""
|
|
24
|
+
|
|
25
|
+
first_name: str | None = Field(default=None, alias="firstName", min_length=1)
|
|
26
|
+
last_name: str | None = Field(default=None, alias="lastName", min_length=1)
|
|
27
|
+
team: str | None = Field(default=None, min_length=1)
|
|
28
|
+
year: int | None = Field(default=None, ge=1869)
|
|
29
|
+
min_year: int | None = Field(default=None, alias="minYear", ge=1869)
|
|
30
|
+
max_year: int | None = Field(default=None, alias="maxYear", ge=1869)
|
|
31
|
+
|
|
32
|
+
@model_validator(mode="after")
|
|
33
|
+
def validate_year_range(self) -> Self:
|
|
34
|
+
"""Require the season range to be ascending."""
|
|
35
|
+
_require_ascending_years(self.min_year, self.max_year)
|
|
36
|
+
return self
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class CoachProfileRequest(_CoachesRequest):
|
|
40
|
+
"""Validate filters accepted by ``GET /coaches/profile``."""
|
|
41
|
+
|
|
42
|
+
coach_id: int = Field(alias="coachId", gt=0)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class CoachSeasonsRequest(_CoachesRequest):
|
|
46
|
+
"""Validate filters accepted by ``GET /coaches/seasons``."""
|
|
47
|
+
|
|
48
|
+
coach_id: int | None = Field(default=None, alias="coachId", gt=0)
|
|
49
|
+
team: str | None = Field(default=None, min_length=1)
|
|
50
|
+
year: int | None = Field(default=None, ge=1869)
|
|
51
|
+
min_year: int | None = Field(default=None, alias="minYear", ge=1869)
|
|
52
|
+
max_year: int | None = Field(default=None, alias="maxYear", ge=1869)
|
|
53
|
+
|
|
54
|
+
@model_validator(mode="after")
|
|
55
|
+
def validate_year_range(self) -> Self:
|
|
56
|
+
"""Require the season range to be ascending."""
|
|
57
|
+
_require_ascending_years(self.min_year, self.max_year)
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class CoachTenuresRequest(_CoachesRequest):
|
|
62
|
+
"""Validate filters accepted by ``GET /coaches/tenures``."""
|
|
63
|
+
|
|
64
|
+
coach_id: int | None = Field(default=None, alias="coachId", gt=0)
|
|
65
|
+
team: str | None = Field(default=None, min_length=1)
|
|
66
|
+
year: int | None = Field(default=None, ge=1869)
|
|
67
|
+
active: bool | None = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
__all__ = [
|
|
71
|
+
"CoachesRequest",
|
|
72
|
+
"CoachProfileRequest",
|
|
73
|
+
"CoachSeasonsRequest",
|
|
74
|
+
"CoachTenuresRequest",
|
|
75
|
+
]
|