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
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Expose typed Info endpoints through the primary client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal, overload
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, TypeAdapter
|
|
8
|
+
|
|
9
|
+
from cfb_data._executor import _EndpointExecutor
|
|
10
|
+
from cfb_data._requests import _resolve_request
|
|
11
|
+
from cfb_data.enums import UserUsageApi
|
|
12
|
+
from cfb_data.info.models.pydantic.requests import InfoUsageRequest
|
|
13
|
+
from cfb_data.info.models.pydantic.responses import UserInfo, UserUsage
|
|
14
|
+
|
|
15
|
+
type _UserUsageApiArgument = UserUsageApi | Literal["all", "cfb", "cbb"]
|
|
16
|
+
|
|
17
|
+
_USER_INFO = TypeAdapter(UserInfo)
|
|
18
|
+
_USER_USAGE = TypeAdapter(UserUsage)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _EmptyRequest(BaseModel):
|
|
22
|
+
"""Represent the empty filter set accepted by ``GET /info``."""
|
|
23
|
+
|
|
24
|
+
model_config = ConfigDict(extra="forbid")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_EMPTY_REQUEST = _EmptyRequest()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class InfoResource:
|
|
31
|
+
"""Provide validated operational account and usage metadata."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, executor: _EndpointExecutor) -> None:
|
|
34
|
+
"""Bind the namespace to shared endpoint execution."""
|
|
35
|
+
self._executor = executor
|
|
36
|
+
|
|
37
|
+
async def account(self) -> UserInfo:
|
|
38
|
+
"""Return metadata for the authenticated API account.
|
|
39
|
+
|
|
40
|
+
:return: Validated account tier, quota, products, and feature access.
|
|
41
|
+
:raises CFBDError: If transport or response validation fails.
|
|
42
|
+
"""
|
|
43
|
+
return await self._executor.fetch_one(
|
|
44
|
+
endpoint="/info",
|
|
45
|
+
request=_EMPTY_REQUEST,
|
|
46
|
+
response_adapter=_USER_INFO,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
@overload
|
|
50
|
+
async def usage(self, request: InfoUsageRequest, /) -> UserUsage: ...
|
|
51
|
+
|
|
52
|
+
@overload
|
|
53
|
+
async def usage(
|
|
54
|
+
self,
|
|
55
|
+
request: None = None,
|
|
56
|
+
/,
|
|
57
|
+
*,
|
|
58
|
+
days: int | None = None,
|
|
59
|
+
limit: int | None = None,
|
|
60
|
+
api: _UserUsageApiArgument | None = None,
|
|
61
|
+
) -> UserUsage: ...
|
|
62
|
+
|
|
63
|
+
async def usage(
|
|
64
|
+
self, request: InfoUsageRequest | None = None, /, **filters: object
|
|
65
|
+
) -> UserUsage:
|
|
66
|
+
"""Return recent shared-pool usage for the authenticated account.
|
|
67
|
+
|
|
68
|
+
:param request: Validated request model, mutually exclusive with filters.
|
|
69
|
+
:param filters: Explicit snake-case endpoint filters.
|
|
70
|
+
:return: Validated request totals, endpoint aggregates, and recent calls.
|
|
71
|
+
:raises TypeError: If request styles are mixed or the model type is wrong.
|
|
72
|
+
:raises CFBDError: If request, transport, or response validation fails.
|
|
73
|
+
"""
|
|
74
|
+
endpoint = "/info/usage"
|
|
75
|
+
validated = _resolve_request(
|
|
76
|
+
endpoint=endpoint,
|
|
77
|
+
request_type=InfoUsageRequest,
|
|
78
|
+
request=request,
|
|
79
|
+
filters=filters,
|
|
80
|
+
)
|
|
81
|
+
return await self._executor.fetch_one(
|
|
82
|
+
endpoint=endpoint,
|
|
83
|
+
request=validated,
|
|
84
|
+
response_adapter=_USER_USAGE,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
__all__ = ["InfoResource"]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Export the supported Metrics namespace and public contracts."""
|
|
2
|
+
|
|
3
|
+
from cfb_data.enums import Classification, SeasonType
|
|
4
|
+
|
|
5
|
+
from .models.pydantic import (
|
|
6
|
+
FieldGoalExpectedPoints,
|
|
7
|
+
PlayerGamePPAAverage,
|
|
8
|
+
PlayerGamePPARequest,
|
|
9
|
+
PlayerGamePredictedPointsAdded,
|
|
10
|
+
PlayerSeasonPPARequest,
|
|
11
|
+
PlayerSeasonPPASplit,
|
|
12
|
+
PlayerSeasonPredictedPointsAdded,
|
|
13
|
+
PlayWinProbability,
|
|
14
|
+
PredictedPointsRequest,
|
|
15
|
+
PredictedPointsValue,
|
|
16
|
+
PregameWinProbability,
|
|
17
|
+
PregameWinProbabilityRequest,
|
|
18
|
+
TeamGamePPARequest,
|
|
19
|
+
TeamGamePPAUnit,
|
|
20
|
+
TeamGamePredictedPointsAdded,
|
|
21
|
+
TeamPPACumulative,
|
|
22
|
+
TeamSeasonPPARequest,
|
|
23
|
+
TeamSeasonPPAUnit,
|
|
24
|
+
TeamSeasonPredictedPointsAdded,
|
|
25
|
+
WinProbabilityRequest,
|
|
26
|
+
)
|
|
27
|
+
from .resource import MetricsResource
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"Classification",
|
|
31
|
+
"FieldGoalExpectedPoints",
|
|
32
|
+
"MetricsResource",
|
|
33
|
+
"PlayerGamePPAAverage",
|
|
34
|
+
"PlayerGamePPARequest",
|
|
35
|
+
"PlayerGamePredictedPointsAdded",
|
|
36
|
+
"PlayerSeasonPPARequest",
|
|
37
|
+
"PlayerSeasonPPASplit",
|
|
38
|
+
"PlayerSeasonPredictedPointsAdded",
|
|
39
|
+
"PlayWinProbability",
|
|
40
|
+
"PredictedPointsRequest",
|
|
41
|
+
"PredictedPointsValue",
|
|
42
|
+
"PregameWinProbability",
|
|
43
|
+
"PregameWinProbabilityRequest",
|
|
44
|
+
"SeasonType",
|
|
45
|
+
"TeamGamePPARequest",
|
|
46
|
+
"TeamGamePPAUnit",
|
|
47
|
+
"TeamGamePredictedPointsAdded",
|
|
48
|
+
"TeamPPACumulative",
|
|
49
|
+
"TeamSeasonPPARequest",
|
|
50
|
+
"TeamSeasonPPAUnit",
|
|
51
|
+
"TeamSeasonPredictedPointsAdded",
|
|
52
|
+
"WinProbabilityRequest",
|
|
53
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Export Metrics model families."""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Export validated Metrics request and response models."""
|
|
2
|
+
|
|
3
|
+
from .requests import (
|
|
4
|
+
PlayerGamePPARequest,
|
|
5
|
+
PlayerSeasonPPARequest,
|
|
6
|
+
PredictedPointsRequest,
|
|
7
|
+
PregameWinProbabilityRequest,
|
|
8
|
+
TeamGamePPARequest,
|
|
9
|
+
TeamSeasonPPARequest,
|
|
10
|
+
WinProbabilityRequest,
|
|
11
|
+
)
|
|
12
|
+
from .responses import (
|
|
13
|
+
FieldGoalExpectedPoints,
|
|
14
|
+
PlayerGamePPAAverage,
|
|
15
|
+
PlayerGamePredictedPointsAdded,
|
|
16
|
+
PlayerSeasonPPASplit,
|
|
17
|
+
PlayerSeasonPredictedPointsAdded,
|
|
18
|
+
PlayWinProbability,
|
|
19
|
+
PredictedPointsValue,
|
|
20
|
+
PregameWinProbability,
|
|
21
|
+
TeamGamePPAUnit,
|
|
22
|
+
TeamGamePredictedPointsAdded,
|
|
23
|
+
TeamPPACumulative,
|
|
24
|
+
TeamSeasonPPAUnit,
|
|
25
|
+
TeamSeasonPredictedPointsAdded,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"FieldGoalExpectedPoints",
|
|
30
|
+
"PlayerGamePPAAverage",
|
|
31
|
+
"PlayerGamePPARequest",
|
|
32
|
+
"PlayerGamePredictedPointsAdded",
|
|
33
|
+
"PlayerSeasonPPARequest",
|
|
34
|
+
"PlayerSeasonPPASplit",
|
|
35
|
+
"PlayerSeasonPredictedPointsAdded",
|
|
36
|
+
"PlayWinProbability",
|
|
37
|
+
"PredictedPointsRequest",
|
|
38
|
+
"PredictedPointsValue",
|
|
39
|
+
"PregameWinProbability",
|
|
40
|
+
"PregameWinProbabilityRequest",
|
|
41
|
+
"TeamGamePPARequest",
|
|
42
|
+
"TeamGamePPAUnit",
|
|
43
|
+
"TeamGamePredictedPointsAdded",
|
|
44
|
+
"TeamPPACumulative",
|
|
45
|
+
"TeamSeasonPPARequest",
|
|
46
|
+
"TeamSeasonPPAUnit",
|
|
47
|
+
"TeamSeasonPredictedPointsAdded",
|
|
48
|
+
"WinProbabilityRequest",
|
|
49
|
+
]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Validate request parameters for implemented Metrics 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
|
+
from cfb_data.enums import Classification, SeasonType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _MetricsRequest(BaseModel):
|
|
13
|
+
"""Apply the closed-object contract shared by Metrics requests."""
|
|
14
|
+
|
|
15
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _require_year_or_team(year: int | None, team: str | None) -> None:
|
|
19
|
+
"""Require a season or team selector."""
|
|
20
|
+
if year is None and team is None:
|
|
21
|
+
raise ValueError("year is required when team is not specified")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PredictedPointsRequest(_MetricsRequest):
|
|
25
|
+
"""Validate filters accepted by ``GET /ppa/predicted``."""
|
|
26
|
+
|
|
27
|
+
down: int = Field(ge=1, le=4)
|
|
28
|
+
distance: int = Field(ge=1, le=99)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TeamSeasonPPARequest(_MetricsRequest):
|
|
32
|
+
"""Validate filters accepted by ``GET /ppa/teams``."""
|
|
33
|
+
|
|
34
|
+
year: int | None = Field(default=None, ge=1869)
|
|
35
|
+
team: str | None = Field(default=None, min_length=1)
|
|
36
|
+
conference: str | None = Field(default=None, min_length=1)
|
|
37
|
+
exclude_garbage_time: bool | None = Field(default=None, alias="excludeGarbageTime")
|
|
38
|
+
classification: Classification | None = None
|
|
39
|
+
|
|
40
|
+
@model_validator(mode="after")
|
|
41
|
+
def validate_selectors(self) -> Self:
|
|
42
|
+
"""Require a season or team selector."""
|
|
43
|
+
_require_year_or_team(self.year, self.team)
|
|
44
|
+
return self
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TeamGamePPARequest(_MetricsRequest):
|
|
48
|
+
"""Validate filters accepted by ``GET /ppa/games``."""
|
|
49
|
+
|
|
50
|
+
year: int = Field(ge=1869)
|
|
51
|
+
week: int | None = Field(default=None, ge=0)
|
|
52
|
+
season_type: SeasonType | None = Field(default=None, alias="seasonType")
|
|
53
|
+
team: str | None = Field(default=None, min_length=1)
|
|
54
|
+
conference: str | None = Field(default=None, min_length=1)
|
|
55
|
+
exclude_garbage_time: bool | None = Field(default=None, alias="excludeGarbageTime")
|
|
56
|
+
classification: Classification | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class PlayerGamePPARequest(_MetricsRequest):
|
|
60
|
+
"""Validate filters accepted by ``GET /ppa/players/games``."""
|
|
61
|
+
|
|
62
|
+
year: int = Field(ge=1869)
|
|
63
|
+
week: int | None = Field(default=None, ge=0)
|
|
64
|
+
season_type: SeasonType | None = Field(default=None, alias="seasonType")
|
|
65
|
+
team: str | None = Field(default=None, min_length=1)
|
|
66
|
+
position: str | None = Field(default=None, min_length=1)
|
|
67
|
+
player_id: int | None = Field(default=None, alias="playerId", gt=0)
|
|
68
|
+
threshold: int | None = Field(default=None, ge=0)
|
|
69
|
+
exclude_garbage_time: bool | None = Field(default=None, alias="excludeGarbageTime")
|
|
70
|
+
|
|
71
|
+
@model_validator(mode="after")
|
|
72
|
+
def validate_selectors(self) -> Self:
|
|
73
|
+
"""Require a week or team selector."""
|
|
74
|
+
if self.week is None and self.team is None:
|
|
75
|
+
raise ValueError("week is required when team is not specified")
|
|
76
|
+
return self
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class PlayerSeasonPPARequest(_MetricsRequest):
|
|
80
|
+
"""Validate filters accepted by ``GET /ppa/players/season``."""
|
|
81
|
+
|
|
82
|
+
year: int | None = Field(default=None, ge=1869)
|
|
83
|
+
conference: str | None = Field(default=None, min_length=1)
|
|
84
|
+
team: str | None = Field(default=None, min_length=1)
|
|
85
|
+
position: str | None = Field(default=None, min_length=1)
|
|
86
|
+
player_id: int | None = Field(default=None, alias="playerId", gt=0)
|
|
87
|
+
threshold: int | None = Field(default=None, ge=0)
|
|
88
|
+
exclude_garbage_time: bool | None = Field(default=None, alias="excludeGarbageTime")
|
|
89
|
+
|
|
90
|
+
@model_validator(mode="after")
|
|
91
|
+
def validate_selectors(self) -> Self:
|
|
92
|
+
"""Require a season or player selector."""
|
|
93
|
+
if self.year is None and self.player_id is None:
|
|
94
|
+
raise ValueError("year is required when player_id is not specified")
|
|
95
|
+
return self
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class WinProbabilityRequest(_MetricsRequest):
|
|
99
|
+
"""Validate filters accepted by ``GET /metrics/wp``."""
|
|
100
|
+
|
|
101
|
+
game_id: int = Field(alias="gameId", gt=0)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class PregameWinProbabilityRequest(_MetricsRequest):
|
|
105
|
+
"""Validate filters accepted by ``GET /metrics/wp/pregame``."""
|
|
106
|
+
|
|
107
|
+
year: int | None = Field(default=None, ge=1869)
|
|
108
|
+
week: int | None = Field(default=None, ge=0)
|
|
109
|
+
season_type: SeasonType | None = Field(default=None, alias="seasonType")
|
|
110
|
+
team: str | None = Field(default=None, min_length=1)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
__all__ = [
|
|
114
|
+
"PlayerGamePPARequest",
|
|
115
|
+
"PlayerSeasonPPARequest",
|
|
116
|
+
"PredictedPointsRequest",
|
|
117
|
+
"PregameWinProbabilityRequest",
|
|
118
|
+
"TeamGamePPARequest",
|
|
119
|
+
"TeamSeasonPPARequest",
|
|
120
|
+
"WinProbabilityRequest",
|
|
121
|
+
]
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Validate responses from implemented CFBD Metrics endpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
from cfb_data.enums import SeasonType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _ResponseModel(BaseModel):
|
|
11
|
+
"""Apply the upstream closed-object contract to Metrics responses."""
|
|
12
|
+
|
|
13
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PredictedPointsValue(_ResponseModel):
|
|
17
|
+
"""Represent expected points at one field position."""
|
|
18
|
+
|
|
19
|
+
yard_line: int = Field(alias="yardLine", ge=0, le=100)
|
|
20
|
+
predicted_points: float = Field(alias="predictedPoints")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TeamPPACumulative(_ResponseModel):
|
|
24
|
+
"""Represent cumulative team predicted-points-added values."""
|
|
25
|
+
|
|
26
|
+
total: float
|
|
27
|
+
passing: float
|
|
28
|
+
rushing: float
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TeamSeasonPPAUnit(_ResponseModel):
|
|
32
|
+
"""Represent one side of a team's season PPA metrics."""
|
|
33
|
+
|
|
34
|
+
overall: float
|
|
35
|
+
passing: float
|
|
36
|
+
rushing: float
|
|
37
|
+
first_down: float = Field(alias="firstDown")
|
|
38
|
+
second_down: float = Field(alias="secondDown")
|
|
39
|
+
third_down: float = Field(alias="thirdDown")
|
|
40
|
+
cumulative: TeamPPACumulative
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TeamSeasonPredictedPointsAdded(_ResponseModel):
|
|
44
|
+
"""Represent team predicted-points-added metrics for a season."""
|
|
45
|
+
|
|
46
|
+
season: int = Field(ge=1869)
|
|
47
|
+
conference: str
|
|
48
|
+
team: str
|
|
49
|
+
offense: TeamSeasonPPAUnit
|
|
50
|
+
defense: TeamSeasonPPAUnit
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TeamGamePPAUnit(_ResponseModel):
|
|
54
|
+
"""Represent one side of a team's game PPA metrics."""
|
|
55
|
+
|
|
56
|
+
overall: float
|
|
57
|
+
passing: float
|
|
58
|
+
rushing: float
|
|
59
|
+
first_down: float = Field(alias="firstDown")
|
|
60
|
+
second_down: float = Field(alias="secondDown")
|
|
61
|
+
third_down: float = Field(alias="thirdDown")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TeamGamePredictedPointsAdded(_ResponseModel):
|
|
65
|
+
"""Represent team predicted-points-added metrics for one game."""
|
|
66
|
+
|
|
67
|
+
game_id: int = Field(alias="gameId", gt=0)
|
|
68
|
+
season: int = Field(ge=1869)
|
|
69
|
+
week: int = Field(ge=0)
|
|
70
|
+
season_type: SeasonType = Field(alias="seasonType")
|
|
71
|
+
team: str
|
|
72
|
+
conference: str
|
|
73
|
+
opponent: str
|
|
74
|
+
offense: TeamGamePPAUnit
|
|
75
|
+
defense: TeamGamePPAUnit
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class PlayerGamePPAAverage(_ResponseModel):
|
|
79
|
+
"""Represent a player's per-play PPA averages for one game."""
|
|
80
|
+
|
|
81
|
+
all: float
|
|
82
|
+
passing: float | None = Field(default=None, alias="pass")
|
|
83
|
+
rush: float | None = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class PlayerGamePredictedPointsAdded(_ResponseModel):
|
|
87
|
+
"""Represent player predicted-points-added metrics for one game."""
|
|
88
|
+
|
|
89
|
+
season: int = Field(ge=1869)
|
|
90
|
+
week: int = Field(ge=0)
|
|
91
|
+
season_type: SeasonType = Field(alias="seasonType")
|
|
92
|
+
id: str
|
|
93
|
+
name: str
|
|
94
|
+
position: str
|
|
95
|
+
team: str
|
|
96
|
+
opponent: str
|
|
97
|
+
average_ppa: PlayerGamePPAAverage = Field(alias="averagePPA")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class PlayerSeasonPPASplit(_ResponseModel):
|
|
101
|
+
"""Represent average or total player PPA split by play context."""
|
|
102
|
+
|
|
103
|
+
all: float
|
|
104
|
+
passing: float | None = Field(default=None, alias="pass")
|
|
105
|
+
rush: float | None = None
|
|
106
|
+
first_down: float | None = Field(default=None, alias="firstDown")
|
|
107
|
+
second_down: float | None = Field(default=None, alias="secondDown")
|
|
108
|
+
third_down: float | None = Field(default=None, alias="thirdDown")
|
|
109
|
+
standard_downs: float | None = Field(default=None, alias="standardDowns")
|
|
110
|
+
passing_downs: float | None = Field(default=None, alias="passingDowns")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class PlayerSeasonPredictedPointsAdded(_ResponseModel):
|
|
114
|
+
"""Represent player predicted-points-added metrics for a season."""
|
|
115
|
+
|
|
116
|
+
season: int = Field(ge=1869)
|
|
117
|
+
id: str
|
|
118
|
+
name: str
|
|
119
|
+
position: str
|
|
120
|
+
team: str
|
|
121
|
+
conference: str
|
|
122
|
+
average_ppa: PlayerSeasonPPASplit = Field(alias="averagePPA")
|
|
123
|
+
total_ppa: PlayerSeasonPPASplit = Field(alias="totalPPA")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class PlayWinProbability(_ResponseModel):
|
|
127
|
+
"""Represent the modeled home win probability after one play."""
|
|
128
|
+
|
|
129
|
+
game_id: int = Field(alias="gameId", gt=0)
|
|
130
|
+
play_id: str = Field(alias="playId")
|
|
131
|
+
play_text: str = Field(alias="playText")
|
|
132
|
+
home_id: int = Field(alias="homeId", gt=0)
|
|
133
|
+
home: str
|
|
134
|
+
away_id: int = Field(alias="awayId", gt=0)
|
|
135
|
+
away: str
|
|
136
|
+
spread: float
|
|
137
|
+
home_ball: bool = Field(alias="homeBall")
|
|
138
|
+
home_score: int = Field(alias="homeScore", ge=0)
|
|
139
|
+
away_score: int = Field(alias="awayScore", ge=0)
|
|
140
|
+
yard_line: int = Field(alias="yardLine", ge=0, le=100)
|
|
141
|
+
down: int = Field(ge=0, le=4)
|
|
142
|
+
distance: int = Field(ge=0)
|
|
143
|
+
home_win_probability: float = Field(alias="homeWinProbability", ge=0, le=1)
|
|
144
|
+
play_number: int = Field(alias="playNumber", ge=0)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class PregameWinProbability(_ResponseModel):
|
|
148
|
+
"""Represent one game's pregame home win probability."""
|
|
149
|
+
|
|
150
|
+
season: int = Field(ge=1869)
|
|
151
|
+
season_type: SeasonType = Field(alias="seasonType")
|
|
152
|
+
week: int = Field(ge=0)
|
|
153
|
+
game_id: int = Field(alias="gameId", gt=0)
|
|
154
|
+
home_team: str = Field(alias="homeTeam")
|
|
155
|
+
away_team: str = Field(alias="awayTeam")
|
|
156
|
+
spread: float
|
|
157
|
+
home_win_probability: float = Field(alias="homeWinProbability", ge=0, le=1)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class FieldGoalExpectedPoints(_ResponseModel):
|
|
161
|
+
"""Represent expected points for a field-goal distance."""
|
|
162
|
+
|
|
163
|
+
yards_to_goal: int = Field(alias="yardsToGoal", ge=0, le=100)
|
|
164
|
+
distance: int = Field(ge=0)
|
|
165
|
+
expected_points: float = Field(alias="expectedPoints")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
__all__ = [
|
|
169
|
+
"FieldGoalExpectedPoints",
|
|
170
|
+
"PlayerGamePPAAverage",
|
|
171
|
+
"PlayerGamePredictedPointsAdded",
|
|
172
|
+
"PlayerSeasonPPASplit",
|
|
173
|
+
"PlayerSeasonPredictedPointsAdded",
|
|
174
|
+
"PlayWinProbability",
|
|
175
|
+
"PredictedPointsValue",
|
|
176
|
+
"PregameWinProbability",
|
|
177
|
+
"TeamGamePPAUnit",
|
|
178
|
+
"TeamGamePredictedPointsAdded",
|
|
179
|
+
"TeamPPACumulative",
|
|
180
|
+
"TeamSeasonPPAUnit",
|
|
181
|
+
"TeamSeasonPredictedPointsAdded",
|
|
182
|
+
]
|