# Provider Normalization Contract HoopScout ingestion consumes provider data through a normalized, provider-agnostic contract defined in: - `apps/providers/contracts.py` - `apps/providers/interfaces.py` ## Contract scope Adapters must return only normalized entities used by ingestion: - `players` - `competitions` - `teams` - `seasons` - `player_stats` - `player_careers` - optional `cursor` Raw provider response structures must remain inside `apps/providers` (client/adapter/mapping code). `ExternalMapping.raw_payload` is used only for diagnostics and troubleshooting. ## Current balldontlie assumptions (MVP) - Source scope is NBA-centric. - Competition is normalized as a single NBA competition (`competition-nba`). - Team country is not reliably available in source payloads and is normalized to `null`. - Player nationality/birth/physical details are not available in player list payloads and are normalized to `null` (except fields explicitly present). - Configured seasons are normalized from `PROVIDER_BALLDONTLIE_SEASONS`; the highest configured season is marked `is_current=true`. - Advanced metrics (`usage_rate`, `true_shooting_pct`, `player_efficiency_rating`) are currently unavailable from this source path and normalized to `null`. ## Domain rules vs provider assumptions - Domain rules live in ingestion/domain services and models. - Provider assumptions live only in adapter/mapping modules. - New providers must map to the same normalized contract and should not require ingestion logic changes.