Migrate from ESPN api to TheSportsDB #363
Labels
No labels
Kestra
bug
enhancement
someday
subtask
☁️ api
🎛️ infrastructure
🐞 sentry
📆 2025 Season
📝 pages
allpicks
📝 pages
picks
📝 pages
standings
🚀 performance
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
johnsturgeon/tgfp-web#363
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
I have a membership / pay for access so this should be a more 'proper' long term solution
Phase 3 — TheSportsDB cutover checklist
Phase 2 (this issue) is done: schema is provider-neutral and
DataSource.THE_SPORTS_DBexists as an enum value, but nothing reads or writes it yet. Every job still
hardcodes ESPN. This is what's left.
0. Answer first — this gates everything else
Game.spreadis anon-nullable float and today it comes from
ESPNNflOdd.favored_team_spread.Picks, locks and upsets all hang off spread + favorite. If TheSportsDB has no
betting lines, the cutover is not a straight swap — we either keep ESPN as an
odds-only source or add a third provider. Settle this before writing any client.
1. Provider client
app/thesportsdb/mirroringapp/espn_nfl/'s public surface:games(),teams(),standings(),find_game(),find_teams(),find_standing_for_team()._game_from_nfl_game()consumes:id,start_time,week_no,season_type,season,game_status_type,spread,favored_team,home_team,away_team,total_home_points,total_away_points,extra_info.2. Normalize the two ESPN-specific vocabularies
These leak into the database, so they have to be translated at the provider boundary
or existing rows stop matching:
Game.is_final/Game.is_pregamecompare against the literalstrings
"STATUS_FINAL"and"STATUS_SCHEDULED". If TheSportsDB writes its ownvocabulary into
game_status, awards and score updates break silently — noexception, games just never look final.
1=Pre, 2=Regular, 3=Postis ESPN's numbering, and it'sstored on
Game.season_typeand used byWeekInfo. Map to the same ints.3. Dispatch instead of hardcoded ESPN
config/op.env).DataSource.create_picks._espn_team()— filters onDataSource.ESPNand the newGamesets
data_source=DataSource.ESPN. Both become the active provider.update_game._update_one_game()— buildsESPNNfl(...)directly; should pickthe client from
game.data_sourceso historical rows still resolve.sync_team_records()— same, buildsESPNNfl()directly.4. Team row cutover (Option 1, overwrite the 32 rows)
short_name— do not delete and re-insert.game.favorite_team_id,road_team_idandhome_team_idare FKs toteam.id;dropping the rows orphans every historical game. Also
discord_emojiandlogo_urlare TGFP-owned with no provider equivalent and would be lost.external_team_id+data_sourcetogether in one transaction.silently stops updating.
5. Timing
table carry ESPN ids with
data_source='espn', andupdate_gamepolls them bythat id — swapping team ids mid-week strands in-flight games.
data_source='espn'permanently. That's what the columnis for; the composite unique constraint is on
(data_source, external_*_id), soold ESPN rows and new rows coexist without collision.
6. Verify
create_the_picks()against a scratch DB and confirmteams resolve, spreads are sane, and no
.one()raisesNoResultFound.is_finaland awardsrecalculate.