cfb-data 0.4.1__tar.gz → 0.5.0__tar.gz
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-0.4.1 → cfb_data-0.5.0}/PKG-INFO +77 -9
- {cfb_data-0.4.1 → cfb_data-0.5.0}/README.md +72 -8
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/__init__.py +41 -0
- cfb_data-0.5.0/cfb_data/cfb_data/_catalog/__init__.py +1 -0
- cfb_data-0.5.0/cfb_data/cfb_data/_catalog/merge.py +104 -0
- cfb_data-0.5.0/cfb_data/cfb_data/_catalog/models.py +304 -0
- cfb_data-0.5.0/cfb_data/cfb_data/_catalog/projection.py +723 -0
- cfb_data-0.5.0/cfb_data/cfb_data/_catalog/sources.py +300 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/_executor.py +70 -32
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/_transport.py +137 -5
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/adjusted_metrics/models/pydantic/responses.py +39 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/betting/models/pydantic/responses.py +23 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/__init__.py +21 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_backend.py +159 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_catalog.py +94 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_catalog_codecs.py +343 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_coordinator.py +1094 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_identity_codecs.py +106 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_key.py +94 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_models.py +24 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_null.py +405 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_redis.py +1474 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_sqlite.py +1000 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/_sqlite_sql.py +68 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/config.py +174 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/policy.py +174 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/acquire_refresh_lease.sql +7 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/begin_immediate.sql +1 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/catalog_counts.sql +16 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/cleanup_responses.sql +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/delete_coverage_failure.sql +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/delete_response.sql +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/delete_team_aliases.sql +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/enable_foreign_keys.sql +1 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/enable_wal.sql +1 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_athletes.sql +7 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_conference_by_id.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_conference_by_name.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_fresh_coverage.sql +6 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_game_by_id.sql +12 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_games.sql +28 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_team_by_id.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_team_by_name.sql +6 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_venue_by_id.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/find_venue_by_name.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/get_response.sql +13 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/get_response_size.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/get_schema_version.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/initialize_schema_version.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/release_refresh_lease.sql +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/renew_refresh_lease.sql +3 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/schema.sql +235 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/select_catalog_observations.sql +6 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/set_busy_timeout.sql +1 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/set_synchronous_normal.sql +1 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_athlete.sql +7 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_athlete_team_season.sql +5 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_catalog_observation.sql +4 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_coach.sql +7 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_coach_team_season.sql +6 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_conference.sql +9 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_conference_affiliation.sql +5 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_coverage.sql +20 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_coverage_failure.sql +5 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_drive.sql +9 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_game.sql +12 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_play.sql +8 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_playoff_matchup.sql +6 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_recruit.sql +7 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_response.sql +12 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_team.sql +9 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_team_alias.sql +4 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_team_season.sql +6 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_venue.sql +8 -0
- cfb_data-0.5.0/cfb_data/cfb_data/cache/sql/upsert_vocabulary.sql +6 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/client.py +92 -4
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/coaches/models/pydantic/responses.py +91 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/conferences/__init__.py +2 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/conferences/models/pydantic/__init__.py +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/conferences/models/pydantic/identity.py +19 -0
- cfb_data-0.5.0/cfb_data/cfb_data/conferences/models/pydantic/responses.py +165 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/draft/models/pydantic/responses.py +54 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/drives/models/pydantic/responses.py +17 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/errors.py +44 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/games/__init__.py +2 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/games/models/pydantic/__init__.py +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/games/models/pydantic/identity.py +27 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/games/models/pydantic/responses.py +159 -0
- cfb_data-0.5.0/cfb_data/cfb_data/identities/__init__.py +5 -0
- cfb_data-0.5.0/cfb_data/cfb_data/identities/_normalization.py +28 -0
- cfb_data-0.5.0/cfb_data/cfb_data/identities/contracts.py +35 -0
- cfb_data-0.5.0/cfb_data/cfb_data/identities/resource.py +986 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/metrics/models/pydantic/responses.py +68 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/players/__init__.py +2 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/players/models/pydantic/__init__.py +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/players/models/pydantic/identity.py +16 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/players/models/pydantic/responses.py +69 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/playoffs/models/pydantic/responses.py +67 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/plays/models/pydantic/responses.py +122 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/rankings/models/pydantic/responses.py +10 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/recruiting/models/pydantic/responses.py +25 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/stats/models/pydantic/responses.py +90 -2
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/stats/resource.py +3 -2
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/teams/__init__.py +2 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/teams/models/pydantic/__init__.py +2 -0
- cfb_data-0.5.0/cfb_data/cfb_data/teams/models/pydantic/identity.py +17 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/teams/models/pydantic/responses.py +102 -5
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/venues/__init__.py +2 -2
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/venues/models/pydantic/__init__.py +2 -1
- cfb_data-0.5.0/cfb_data/cfb_data/venues/models/pydantic/identity.py +17 -0
- cfb_data-0.5.0/cfb_data/cfb_data/venues/models/pydantic/responses.py +64 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data.egg-info/PKG-INFO +77 -9
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data.egg-info/SOURCES.txt +77 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data.egg-info/requires.txt +5 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/pyproject.toml +11 -2
- cfb_data-0.4.1/cfb_data/cfb_data/conferences/models/pydantic/responses.py +0 -64
- cfb_data-0.4.1/cfb_data/cfb_data/venues/models/pydantic/responses.py +0 -24
- {cfb_data-0.4.1 → cfb_data-0.5.0}/LICENSE +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/_dataframes.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/_parquet.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/_request_rules.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/_requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/_tabular.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/adjusted_metrics/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/adjusted_metrics/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/adjusted_metrics/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/adjusted_metrics/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/adjusted_metrics/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/base/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/base/types.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/betting/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/betting/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/betting/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/betting/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/betting/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/coaches/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/coaches/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/coaches/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/coaches/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/coaches/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/conferences/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/conferences/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/conferences/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/draft/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/draft/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/draft/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/draft/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/draft/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/drives/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/drives/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/drives/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/drives/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/drives/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/enums.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/games/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/games/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/games/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/info/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/info/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/info/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/info/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/info/models/pydantic/responses.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/info/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/metrics/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/metrics/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/metrics/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/metrics/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/metrics/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/players/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/players/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/players/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/playoffs/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/playoffs/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/playoffs/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/playoffs/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/playoffs/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/plays/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/plays/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/plays/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/plays/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/plays/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/py.typed +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/rankings/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/rankings/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/rankings/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/rankings/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/rankings/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/ratings/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/ratings/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/ratings/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/ratings/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/ratings/models/pydantic/responses.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/ratings/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/recruiting/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/recruiting/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/recruiting/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/recruiting/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/recruiting/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/retry.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/stats/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/stats/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/stats/models/pydantic/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/stats/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/teams/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/teams/models/pydantic/requests.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/teams/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/venues/models/__init__.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data/venues/resource.py +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data.egg-info/dependency_links.txt +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/cfb_data/cfb_data.egg-info/top_level.txt +0 -0
- {cfb_data-0.4.1 → cfb_data-0.5.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cfb-data
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Async validated CollegeFootballData access for pandas and Polars
|
|
5
5
|
Author: Ryan Anderson
|
|
6
6
|
License-Expression: MIT
|
|
@@ -16,11 +16,15 @@ Requires-Python: >=3.12
|
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: aiohttp<4,>=3.13
|
|
19
|
+
Requires-Dist: aiosqlite<1,>=0.22
|
|
20
|
+
Requires-Dist: jinja2<4,>=3.1
|
|
19
21
|
Requires-Dist: pandas<4,>=3.0
|
|
20
22
|
Requires-Dist: pydantic<3,>=2.12
|
|
21
23
|
Requires-Dist: pyarrow<26,>=25
|
|
22
24
|
Provides-Extra: polars
|
|
23
25
|
Requires-Dist: polars<2,>=1.43.2; extra == "polars"
|
|
26
|
+
Provides-Extra: redis
|
|
27
|
+
Requires-Dist: redis<8,>=7.1; extra == "redis"
|
|
24
28
|
Provides-Extra: dev
|
|
25
29
|
Requires-Dist: build<2,>=1.5; extra == "dev"
|
|
26
30
|
Requires-Dist: furo>=2025.12.19; extra == "dev"
|
|
@@ -37,7 +41,7 @@ Dynamic: license-file
|
|
|
37
41
|
|
|
38
42
|
# College Football Data Python Toolkit
|
|
39
43
|
|
|
40
|
-
`cfb-data` 0.
|
|
44
|
+
`cfb-data` 0.5.0 is an asynchronous, validated client for the public
|
|
41
45
|
[CollegeFootballData API](https://collegefootballdata.com/) REST endpoint
|
|
42
46
|
groups. It returns eager pandas DataFrames by default and can return the same
|
|
43
47
|
logical tables as Polars DataFrames. Irreducibly nested analytical responses
|
|
@@ -72,8 +76,15 @@ Install the optional Polars backend with:
|
|
|
72
76
|
python -m pip install "cfb-data[polars]"
|
|
73
77
|
```
|
|
74
78
|
|
|
79
|
+
SQLite caching and identity lookup are available in the base installation.
|
|
80
|
+
Install the optional shared Redis backend with:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
python -m pip install "cfb-data[redis]"
|
|
84
|
+
```
|
|
85
|
+
|
|
75
86
|
Python 3.12 and 3.13 are supported. DataFrames are eager; Polars
|
|
76
|
-
`LazyFrame` results are not part of the 0.
|
|
87
|
+
`LazyFrame` results are not part of the 0.5.0 contract.
|
|
77
88
|
|
|
78
89
|
## Authentication and lifecycle
|
|
79
90
|
|
|
@@ -143,6 +154,56 @@ async with CFBDClient(dataframe_backend="polars") as client:
|
|
|
143
154
|
Type checkers infer `pandas.DataFrame` for the default client and
|
|
144
155
|
`polars.DataFrame` when the literal backend is `"polars"`.
|
|
145
156
|
|
|
157
|
+
## Response caching and identities
|
|
158
|
+
|
|
159
|
+
Caching is opt-in. `SQLiteCacheConfig()` provides a private per-user local
|
|
160
|
+
response cache and durable identity catalog without another service:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from cfb_data import CFBDClient, FreshnessMode, SQLiteCacheConfig
|
|
164
|
+
|
|
165
|
+
async with CFBDClient(cache=SQLiteCacheConfig()) as client:
|
|
166
|
+
games = await client.games.list(year=2025)
|
|
167
|
+
repeated = await client.games.list(year=2025) # validated exact cache hit
|
|
168
|
+
|
|
169
|
+
team = await client.identities.teams.resolve("MICH")
|
|
170
|
+
game = await client.identities.games.resolve(game_id=401628347)
|
|
171
|
+
|
|
172
|
+
with client.cache_mode("local_only"):
|
|
173
|
+
offline = await client.games.list(year=2025)
|
|
174
|
+
|
|
175
|
+
retained = await client.identities.teams.resolve(
|
|
176
|
+
"Michigan",
|
|
177
|
+
freshness=FreshnessMode.allow_stale,
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Use `RedisCacheConfig(url=...)` for multiple workers or hosts. Validated
|
|
182
|
+
response records expire according to college-football-specific TTL profiles;
|
|
183
|
+
normalized identity facts and their coverage ledger remain until explicitly
|
|
184
|
+
pruned or rebuilt. Operational account and usage routes are never cached.
|
|
185
|
+
Cache hits are decoded and revalidated through the current Pydantic response
|
|
186
|
+
contract, and cache/backend failures fail open for ordinary API calls without
|
|
187
|
+
silently changing backend types.
|
|
188
|
+
|
|
189
|
+
Source-domain Pydantic models own the upstream fields and typed declarations
|
|
190
|
+
that produce catalog facts. The catalog owns only normalized merge, coverage,
|
|
191
|
+
provenance, persistence, and query semantics. The SQLite schema and queries
|
|
192
|
+
live in packaged `.sql` resources rendered through a strict Jinja handler;
|
|
193
|
+
runtime data continues to use bound SQLite parameters. With persistence
|
|
194
|
+
disabled, the same projection path writes a client-local transient catalog;
|
|
195
|
+
there is no separate response-to-identity fallback. Compact identity result
|
|
196
|
+
types live in their team, conference, venue, game, and player domains, while
|
|
197
|
+
`client.identities` remains the query and hydration facade.
|
|
198
|
+
|
|
199
|
+
Per-operation modes are `default`, `refresh`, `bypass`, and `local_only`.
|
|
200
|
+
`client.identities.hydrate(...)` supports dry-run, bounded-concurrency,
|
|
201
|
+
resumable canonical hydration at `4 + 2S` calls for `S` seasons, or `7 + 2S`
|
|
202
|
+
with vocabularies, with an optional division-classification scope. See the complete
|
|
203
|
+
[response caching and identity guide](docs/guides/cache-and-identities.md) for
|
|
204
|
+
TTL defaults, ambiguity behavior, maintenance, Redis Docker/Compose setup, and
|
|
205
|
+
hosted-service security requirements.
|
|
206
|
+
|
|
146
207
|
## Endpoints
|
|
147
208
|
|
|
148
209
|
Each method accepts either one positional request model or explicit snake-case
|
|
@@ -298,10 +359,12 @@ original string, integer, or float value for DataFrames.
|
|
|
298
359
|
Direct pandas and Polars Parquet methods are not the cfb-data persistence
|
|
299
360
|
compatibility contract: pandas object inference and Polars `Object` columns do
|
|
300
361
|
not provide identical empty- and mixed-value behavior. Version 0.2.0 keeps the
|
|
301
|
-
codec internal so future
|
|
302
|
-
a public save/load API. See
|
|
362
|
+
codec internal so future workflow checkpoints and tabular artifacts can use it
|
|
363
|
+
without prematurely committing to a public save/load API. See
|
|
303
364
|
[`ADR 0003`](docs/architecture/0003-canonical-arrow-parquet.md) for the format,
|
|
304
|
-
validation, and compatibility decisions.
|
|
365
|
+
validation, and compatibility decisions. API response caching and its durable
|
|
366
|
+
identity catalog are a separate accepted architecture in
|
|
367
|
+
[`ADR 0004`](docs/architecture/0004-api-cache-identity-catalog.md).
|
|
305
368
|
|
|
306
369
|
## Retries and errors
|
|
307
370
|
|
|
@@ -371,7 +434,7 @@ Use the typed namespace method and either its request model or keyword filters.
|
|
|
371
434
|
|
|
372
435
|
## Datasets and workflows
|
|
373
436
|
|
|
374
|
-
Version 0.
|
|
437
|
+
Version 0.5.0 does not expose `client.datasets` or `client.workflows`. The
|
|
375
438
|
accepted architecture reserves two higher layers:
|
|
376
439
|
|
|
377
440
|
- datasets compose validated endpoint results and validated subdatasets
|
|
@@ -396,8 +459,13 @@ make docs
|
|
|
396
459
|
make check
|
|
397
460
|
```
|
|
398
461
|
|
|
399
|
-
`make install` creates `.venv` and installs `.[dev,polars]`, giving
|
|
400
|
-
contributors the complete Arrow/Parquet and
|
|
462
|
+
`make install` creates `.venv` and installs `.[dev,polars,redis]`, giving
|
|
463
|
+
contributors the complete Arrow/Parquet, DataFrame, and Redis-client test
|
|
464
|
+
contract. `make redis-up` and `make test-redis` exercise the local shared
|
|
465
|
+
backend; `make test-live` is an explicit credentialed check using untracked
|
|
466
|
+
`.env` configuration. `make test-live-all` is the separately opted-in,
|
|
467
|
+
quota-ledgered 74-route SQLite/Redis matrix and keeps the transport's normal
|
|
468
|
+
bounded retries enabled.
|
|
401
469
|
`make check` runs Ruff, strict mypy, a warning-free Sphinx build, and pytest
|
|
402
470
|
under the same contract as CI. `make docs` writes the local HTML site to
|
|
403
471
|
`docs/_build/html`. Package metadata and all dependency groups live only in
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# College Football Data Python Toolkit
|
|
2
2
|
|
|
3
|
-
`cfb-data` 0.
|
|
3
|
+
`cfb-data` 0.5.0 is an asynchronous, validated client for the public
|
|
4
4
|
[CollegeFootballData API](https://collegefootballdata.com/) REST endpoint
|
|
5
5
|
groups. It returns eager pandas DataFrames by default and can return the same
|
|
6
6
|
logical tables as Polars DataFrames. Irreducibly nested analytical responses
|
|
@@ -35,8 +35,15 @@ Install the optional Polars backend with:
|
|
|
35
35
|
python -m pip install "cfb-data[polars]"
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
SQLite caching and identity lookup are available in the base installation.
|
|
39
|
+
Install the optional shared Redis backend with:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
python -m pip install "cfb-data[redis]"
|
|
43
|
+
```
|
|
44
|
+
|
|
38
45
|
Python 3.12 and 3.13 are supported. DataFrames are eager; Polars
|
|
39
|
-
`LazyFrame` results are not part of the 0.
|
|
46
|
+
`LazyFrame` results are not part of the 0.5.0 contract.
|
|
40
47
|
|
|
41
48
|
## Authentication and lifecycle
|
|
42
49
|
|
|
@@ -106,6 +113,56 @@ async with CFBDClient(dataframe_backend="polars") as client:
|
|
|
106
113
|
Type checkers infer `pandas.DataFrame` for the default client and
|
|
107
114
|
`polars.DataFrame` when the literal backend is `"polars"`.
|
|
108
115
|
|
|
116
|
+
## Response caching and identities
|
|
117
|
+
|
|
118
|
+
Caching is opt-in. `SQLiteCacheConfig()` provides a private per-user local
|
|
119
|
+
response cache and durable identity catalog without another service:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from cfb_data import CFBDClient, FreshnessMode, SQLiteCacheConfig
|
|
123
|
+
|
|
124
|
+
async with CFBDClient(cache=SQLiteCacheConfig()) as client:
|
|
125
|
+
games = await client.games.list(year=2025)
|
|
126
|
+
repeated = await client.games.list(year=2025) # validated exact cache hit
|
|
127
|
+
|
|
128
|
+
team = await client.identities.teams.resolve("MICH")
|
|
129
|
+
game = await client.identities.games.resolve(game_id=401628347)
|
|
130
|
+
|
|
131
|
+
with client.cache_mode("local_only"):
|
|
132
|
+
offline = await client.games.list(year=2025)
|
|
133
|
+
|
|
134
|
+
retained = await client.identities.teams.resolve(
|
|
135
|
+
"Michigan",
|
|
136
|
+
freshness=FreshnessMode.allow_stale,
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use `RedisCacheConfig(url=...)` for multiple workers or hosts. Validated
|
|
141
|
+
response records expire according to college-football-specific TTL profiles;
|
|
142
|
+
normalized identity facts and their coverage ledger remain until explicitly
|
|
143
|
+
pruned or rebuilt. Operational account and usage routes are never cached.
|
|
144
|
+
Cache hits are decoded and revalidated through the current Pydantic response
|
|
145
|
+
contract, and cache/backend failures fail open for ordinary API calls without
|
|
146
|
+
silently changing backend types.
|
|
147
|
+
|
|
148
|
+
Source-domain Pydantic models own the upstream fields and typed declarations
|
|
149
|
+
that produce catalog facts. The catalog owns only normalized merge, coverage,
|
|
150
|
+
provenance, persistence, and query semantics. The SQLite schema and queries
|
|
151
|
+
live in packaged `.sql` resources rendered through a strict Jinja handler;
|
|
152
|
+
runtime data continues to use bound SQLite parameters. With persistence
|
|
153
|
+
disabled, the same projection path writes a client-local transient catalog;
|
|
154
|
+
there is no separate response-to-identity fallback. Compact identity result
|
|
155
|
+
types live in their team, conference, venue, game, and player domains, while
|
|
156
|
+
`client.identities` remains the query and hydration facade.
|
|
157
|
+
|
|
158
|
+
Per-operation modes are `default`, `refresh`, `bypass`, and `local_only`.
|
|
159
|
+
`client.identities.hydrate(...)` supports dry-run, bounded-concurrency,
|
|
160
|
+
resumable canonical hydration at `4 + 2S` calls for `S` seasons, or `7 + 2S`
|
|
161
|
+
with vocabularies, with an optional division-classification scope. See the complete
|
|
162
|
+
[response caching and identity guide](docs/guides/cache-and-identities.md) for
|
|
163
|
+
TTL defaults, ambiguity behavior, maintenance, Redis Docker/Compose setup, and
|
|
164
|
+
hosted-service security requirements.
|
|
165
|
+
|
|
109
166
|
## Endpoints
|
|
110
167
|
|
|
111
168
|
Each method accepts either one positional request model or explicit snake-case
|
|
@@ -261,10 +318,12 @@ original string, integer, or float value for DataFrames.
|
|
|
261
318
|
Direct pandas and Polars Parquet methods are not the cfb-data persistence
|
|
262
319
|
compatibility contract: pandas object inference and Polars `Object` columns do
|
|
263
320
|
not provide identical empty- and mixed-value behavior. Version 0.2.0 keeps the
|
|
264
|
-
codec internal so future
|
|
265
|
-
a public save/load API. See
|
|
321
|
+
codec internal so future workflow checkpoints and tabular artifacts can use it
|
|
322
|
+
without prematurely committing to a public save/load API. See
|
|
266
323
|
[`ADR 0003`](docs/architecture/0003-canonical-arrow-parquet.md) for the format,
|
|
267
|
-
validation, and compatibility decisions.
|
|
324
|
+
validation, and compatibility decisions. API response caching and its durable
|
|
325
|
+
identity catalog are a separate accepted architecture in
|
|
326
|
+
[`ADR 0004`](docs/architecture/0004-api-cache-identity-catalog.md).
|
|
268
327
|
|
|
269
328
|
## Retries and errors
|
|
270
329
|
|
|
@@ -334,7 +393,7 @@ Use the typed namespace method and either its request model or keyword filters.
|
|
|
334
393
|
|
|
335
394
|
## Datasets and workflows
|
|
336
395
|
|
|
337
|
-
Version 0.
|
|
396
|
+
Version 0.5.0 does not expose `client.datasets` or `client.workflows`. The
|
|
338
397
|
accepted architecture reserves two higher layers:
|
|
339
398
|
|
|
340
399
|
- datasets compose validated endpoint results and validated subdatasets
|
|
@@ -359,8 +418,13 @@ make docs
|
|
|
359
418
|
make check
|
|
360
419
|
```
|
|
361
420
|
|
|
362
|
-
`make install` creates `.venv` and installs `.[dev,polars]`, giving
|
|
363
|
-
contributors the complete Arrow/Parquet and
|
|
421
|
+
`make install` creates `.venv` and installs `.[dev,polars,redis]`, giving
|
|
422
|
+
contributors the complete Arrow/Parquet, DataFrame, and Redis-client test
|
|
423
|
+
contract. `make redis-up` and `make test-redis` exercise the local shared
|
|
424
|
+
backend; `make test-live` is an explicit credentialed check using untracked
|
|
425
|
+
`.env` configuration. `make test-live-all` is the separately opted-in,
|
|
426
|
+
quota-ledgered 74-route SQLite/Redis matrix and keeps the transport's normal
|
|
427
|
+
bounded retries enabled.
|
|
364
428
|
`make check` runs Ruff, strict mypy, a warning-free Sphinx build, and pytest
|
|
365
429
|
under the same contract as CI. `make docs` writes the local HTML site to
|
|
366
430
|
`docs/_build/html`. Package metadata and all dependency groups live only in
|
|
@@ -7,6 +7,15 @@ from .adjusted_metrics.models.pydantic.requests import (
|
|
|
7
7
|
KickerPAARRequest,
|
|
8
8
|
)
|
|
9
9
|
from .betting.models.pydantic.requests import BettingLinesRequest
|
|
10
|
+
from .cache import (
|
|
11
|
+
CacheConfig,
|
|
12
|
+
CacheMode,
|
|
13
|
+
CachePolicyConfig,
|
|
14
|
+
CacheProfile,
|
|
15
|
+
CacheTTL,
|
|
16
|
+
RedisCacheConfig,
|
|
17
|
+
SQLiteCacheConfig,
|
|
18
|
+
)
|
|
10
19
|
from .client import CFBDClient, DataFrameBackend
|
|
11
20
|
from .coaches.models.pydantic.requests import (
|
|
12
21
|
CoachesRequest,
|
|
@@ -14,6 +23,7 @@ from .coaches.models.pydantic.requests import (
|
|
|
14
23
|
CoachSeasonsRequest,
|
|
15
24
|
CoachTenuresRequest,
|
|
16
25
|
)
|
|
26
|
+
from .conferences.models.pydantic.identity import ConferenceIdentity
|
|
17
27
|
from .conferences.models.pydantic.requests import (
|
|
18
28
|
ConferenceAffiliationsRequest,
|
|
19
29
|
ConferenceChangesRequest,
|
|
@@ -36,11 +46,17 @@ from .enums import (
|
|
|
36
46
|
from .errors import (
|
|
37
47
|
CFBDAuthenticationError,
|
|
38
48
|
CFBDAuthorizationError,
|
|
49
|
+
CFBDCacheBackendError,
|
|
50
|
+
CFBDCacheError,
|
|
51
|
+
CFBDCacheMissError,
|
|
39
52
|
CFBDClientStateError,
|
|
40
53
|
CFBDConfigurationError,
|
|
41
54
|
CFBDDataFrameConversionError,
|
|
42
55
|
CFBDError,
|
|
43
56
|
CFBDHTTPError,
|
|
57
|
+
CFBDIdentityAmbiguityError,
|
|
58
|
+
CFBDIdentityNotFoundError,
|
|
59
|
+
CFBDNoContentError,
|
|
44
60
|
CFBDOptionalDependencyError,
|
|
45
61
|
CFBDRateLimitError,
|
|
46
62
|
CFBDRequestValidationError,
|
|
@@ -51,6 +67,7 @@ from .errors import (
|
|
|
51
67
|
CFBDTLSError,
|
|
52
68
|
CFBDTransportError,
|
|
53
69
|
)
|
|
70
|
+
from .games.models.pydantic.identity import GameIdentity
|
|
54
71
|
from .games.models.pydantic.requests import (
|
|
55
72
|
AdvancedBoxScoreRequest,
|
|
56
73
|
CalendarRequest,
|
|
@@ -63,6 +80,7 @@ from .games.models.pydantic.requests import (
|
|
|
63
80
|
TeamGameStatsRequest,
|
|
64
81
|
)
|
|
65
82
|
from .games.models.pydantic.responses import AdvancedBoxScore
|
|
83
|
+
from .identities import FreshnessMode, HydrationPlan
|
|
66
84
|
from .info.models.pydantic.requests import InfoUsageRequest
|
|
67
85
|
from .info.models.pydantic.responses import UserInfo, UserUsage
|
|
68
86
|
from .metrics.models.pydantic.requests import (
|
|
@@ -74,6 +92,7 @@ from .metrics.models.pydantic.requests import (
|
|
|
74
92
|
TeamSeasonPPARequest,
|
|
75
93
|
WinProbabilityRequest,
|
|
76
94
|
)
|
|
95
|
+
from .players.models.pydantic.identity import AthleteIdentity
|
|
77
96
|
from .players.models.pydantic.requests import (
|
|
78
97
|
PlayerSearchRequest,
|
|
79
98
|
PlayerSeasonOverviewRequest,
|
|
@@ -118,6 +137,7 @@ from .stats.models.pydantic.requests import (
|
|
|
118
137
|
PlayerSeasonSuccessRequest,
|
|
119
138
|
TeamSeasonStatsRequest,
|
|
120
139
|
)
|
|
140
|
+
from .teams.models.pydantic.identity import TeamIdentity
|
|
121
141
|
from .teams.models.pydantic.requests import (
|
|
122
142
|
FBSTeamsRequest,
|
|
123
143
|
RosterRequest,
|
|
@@ -126,29 +146,42 @@ from .teams.models.pydantic.requests import (
|
|
|
126
146
|
TeamMatchupRequest,
|
|
127
147
|
TeamsRequest,
|
|
128
148
|
)
|
|
149
|
+
from .venues.models.pydantic.identity import VenueIdentity
|
|
129
150
|
|
|
130
151
|
__all__ = [
|
|
131
152
|
"AdjustedPlayerPassingRequest",
|
|
132
153
|
"AdjustedPlayerRushingRequest",
|
|
133
154
|
"AdjustedTeamMetricsRequest",
|
|
155
|
+
"AthleteIdentity",
|
|
134
156
|
"AdvancedBoxScoreRequest",
|
|
135
157
|
"AdvancedBoxScore",
|
|
136
158
|
"AdvancedGameStatsRequest",
|
|
137
159
|
"AdvancedSeasonStatsRequest",
|
|
138
160
|
"BettingLinesRequest",
|
|
139
161
|
"CalendarRequest",
|
|
162
|
+
"CacheConfig",
|
|
163
|
+
"CacheMode",
|
|
164
|
+
"CachePolicyConfig",
|
|
165
|
+
"CacheProfile",
|
|
166
|
+
"CacheTTL",
|
|
140
167
|
"CfpGamesRequest",
|
|
141
168
|
"CfpParticipantsRequest",
|
|
142
169
|
"CfpPlayoff",
|
|
143
170
|
"CfpPlayoffRequest",
|
|
144
171
|
"CFBDAuthenticationError",
|
|
145
172
|
"CFBDAuthorizationError",
|
|
173
|
+
"CFBDCacheBackendError",
|
|
174
|
+
"CFBDCacheError",
|
|
175
|
+
"CFBDCacheMissError",
|
|
146
176
|
"CFBDClient",
|
|
147
177
|
"CFBDClientStateError",
|
|
148
178
|
"CFBDConfigurationError",
|
|
149
179
|
"CFBDDataFrameConversionError",
|
|
150
180
|
"CFBDError",
|
|
151
181
|
"CFBDHTTPError",
|
|
182
|
+
"CFBDIdentityAmbiguityError",
|
|
183
|
+
"CFBDIdentityNotFoundError",
|
|
184
|
+
"CFBDNoContentError",
|
|
152
185
|
"CFBDOptionalDependencyError",
|
|
153
186
|
"CFBDRateLimitError",
|
|
154
187
|
"CFBDRequestValidationError",
|
|
@@ -166,6 +199,7 @@ __all__ = [
|
|
|
166
199
|
"ConferenceAffiliationsRequest",
|
|
167
200
|
"ConferenceChangesRequest",
|
|
168
201
|
"ConferenceClassification",
|
|
202
|
+
"ConferenceIdentity",
|
|
169
203
|
"ConferencesRequest",
|
|
170
204
|
"ConferenceSPRatingsRequest",
|
|
171
205
|
"CoreRatingsRequest",
|
|
@@ -175,12 +209,15 @@ __all__ = [
|
|
|
175
209
|
"EloRatingsRequest",
|
|
176
210
|
"ExpandedSRSRatingsRequest",
|
|
177
211
|
"FPIRatingsRequest",
|
|
212
|
+
"FreshnessMode",
|
|
178
213
|
"GameMediaRequest",
|
|
214
|
+
"GameIdentity",
|
|
179
215
|
"GamesRequest",
|
|
180
216
|
"GameWeatherRequest",
|
|
181
217
|
"GameHavocRequest",
|
|
182
218
|
"FBSTeamsRequest",
|
|
183
219
|
"HomeAway",
|
|
220
|
+
"HydrationPlan",
|
|
184
221
|
"InfoUsageRequest",
|
|
185
222
|
"KickerPAARRequest",
|
|
186
223
|
"LiveGame",
|
|
@@ -208,6 +245,7 @@ __all__ = [
|
|
|
208
245
|
"RecruitingGroupsRequest",
|
|
209
246
|
"RecruitingPlayersRequest",
|
|
210
247
|
"RecruitingTeamsRequest",
|
|
248
|
+
"RedisCacheConfig",
|
|
211
249
|
"RetryPolicy",
|
|
212
250
|
"RosterRequest",
|
|
213
251
|
"ReturningProductionRequest",
|
|
@@ -216,7 +254,9 @@ __all__ = [
|
|
|
216
254
|
"SeasonType",
|
|
217
255
|
"SPRatingsRequest",
|
|
218
256
|
"SRSRatingsRequest",
|
|
257
|
+
"SQLiteCacheConfig",
|
|
219
258
|
"TeamGamePPARequest",
|
|
259
|
+
"TeamIdentity",
|
|
220
260
|
"TeamGameStatsRequest",
|
|
221
261
|
"TeamSeasonStatsRequest",
|
|
222
262
|
"TeamSeasonPPARequest",
|
|
@@ -229,6 +269,7 @@ __all__ = [
|
|
|
229
269
|
"UserInfo",
|
|
230
270
|
"UserUsage",
|
|
231
271
|
"UserUsageApi",
|
|
272
|
+
"VenueIdentity",
|
|
232
273
|
"WinProbabilityRequest",
|
|
233
274
|
"DownType",
|
|
234
275
|
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Provide internal normalized catalog contracts."""
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Merge canonical observations independently of storage backends."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import fields, replace
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Protocol, cast
|
|
8
|
+
|
|
9
|
+
from cfb_data._catalog.models import (
|
|
10
|
+
CatalogFact,
|
|
11
|
+
CatalogObservation,
|
|
12
|
+
FieldObservation,
|
|
13
|
+
ObservationState,
|
|
14
|
+
)
|
|
15
|
+
from cfb_data._catalog.projection import catalog_fact_key
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class _FactConstructor(Protocol):
|
|
19
|
+
"""Construct a canonical fact from merge-validated field values."""
|
|
20
|
+
|
|
21
|
+
def __call__(self, **values: object) -> CatalogFact:
|
|
22
|
+
"""Return one canonical fact."""
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def merge_catalog_observations(
|
|
27
|
+
current: CatalogObservation | None,
|
|
28
|
+
candidate: CatalogObservation,
|
|
29
|
+
) -> CatalogObservation:
|
|
30
|
+
"""Merge observations using authority, time, and stable source precedence.
|
|
31
|
+
|
|
32
|
+
:param current: Previously selected field observations, if any.
|
|
33
|
+
:param candidate: Newly encountered field observations for the same grain.
|
|
34
|
+
:return: A deterministic merged observation.
|
|
35
|
+
:raises ValueError: If the observations describe different fact grains.
|
|
36
|
+
"""
|
|
37
|
+
if current is None:
|
|
38
|
+
return (
|
|
39
|
+
candidate
|
|
40
|
+
if candidate.first_observed_at is not None
|
|
41
|
+
else replace(candidate, first_observed_at=_earliest_evidence(candidate))
|
|
42
|
+
)
|
|
43
|
+
if type(current.fact) is not type(candidate.fact) or catalog_fact_key(
|
|
44
|
+
current.fact
|
|
45
|
+
) != catalog_fact_key(candidate.fact):
|
|
46
|
+
raise ValueError("Catalog observations must share a type and grain")
|
|
47
|
+
|
|
48
|
+
current_fields = {field.field: field for field in current.fields}
|
|
49
|
+
candidate_fields = {field.field: field for field in candidate.fields}
|
|
50
|
+
merged_fields: list[FieldObservation] = []
|
|
51
|
+
values: dict[str, object] = {}
|
|
52
|
+
for fact_field in fields(current.fact):
|
|
53
|
+
existing = current_fields[fact_field.name]
|
|
54
|
+
incoming = candidate_fields[fact_field.name]
|
|
55
|
+
selected = _select_field(existing, incoming)
|
|
56
|
+
merged_fields.append(selected)
|
|
57
|
+
if selected.value.state is ObservationState.value:
|
|
58
|
+
values[fact_field.name] = selected.value.value
|
|
59
|
+
elif selected.value.state is ObservationState.null:
|
|
60
|
+
values[fact_field.name] = None
|
|
61
|
+
else:
|
|
62
|
+
values[fact_field.name] = getattr(current.fact, fact_field.name)
|
|
63
|
+
constructor = cast(_FactConstructor, type(current.fact))
|
|
64
|
+
return CatalogObservation(
|
|
65
|
+
constructor(**values),
|
|
66
|
+
tuple(merged_fields),
|
|
67
|
+
min(_first_observed_at(current), _first_observed_at(candidate)),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _first_observed_at(observation: CatalogObservation) -> datetime:
|
|
72
|
+
"""Return explicit or field-derived first observation time."""
|
|
73
|
+
return observation.first_observed_at or _earliest_evidence(observation)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _earliest_evidence(observation: CatalogObservation) -> datetime:
|
|
77
|
+
"""Return the earliest timestamp carried by one observation's fields."""
|
|
78
|
+
observed = [
|
|
79
|
+
field.observed_at for field in observation.fields if field.authority > 0
|
|
80
|
+
]
|
|
81
|
+
if not observed:
|
|
82
|
+
observed = [field.observed_at for field in observation.fields]
|
|
83
|
+
return min(observed)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _select_field(
|
|
87
|
+
current: FieldObservation, candidate: FieldObservation
|
|
88
|
+
) -> FieldObservation:
|
|
89
|
+
"""Return the field observation that wins deterministic precedence."""
|
|
90
|
+
if candidate.value.state is ObservationState.unobserved:
|
|
91
|
+
return current
|
|
92
|
+
if current.value.state is ObservationState.unobserved:
|
|
93
|
+
return candidate
|
|
94
|
+
current_precedence = (
|
|
95
|
+
current.authority,
|
|
96
|
+
current.observed_at,
|
|
97
|
+
current.source,
|
|
98
|
+
)
|
|
99
|
+
candidate_precedence = (
|
|
100
|
+
candidate.authority,
|
|
101
|
+
candidate.observed_at,
|
|
102
|
+
candidate.source,
|
|
103
|
+
)
|
|
104
|
+
return candidate if candidate_precedence >= current_precedence else current
|