{"id":51152817,"url":"https://github.com/stiles/world-cup","last_synced_at":"2026-06-26T07:30:49.163Z","repository":{"id":364887520,"uuid":"1269536270","full_name":"stiles/world-cup","owner":"stiles","description":"Collects teams, players, schedule, results and stats for the FIFA World Cup from public FIFA API endpoints, with a CLI for play-by-play of live matches in the terminal. ","archived":false,"fork":false,"pushed_at":"2026-06-15T00:29:47.000Z","size":852,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-15T01:13:59.866Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stiles.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-14T20:45:29.000Z","updated_at":"2026-06-15T01:00:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stiles/world-cup","commit_stats":null,"previous_names":["stiles/world-cup"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/stiles/world-cup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Fworld-cup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Fworld-cup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Fworld-cup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Fworld-cup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stiles","download_url":"https://codeload.github.com/stiles/world-cup/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Fworld-cup/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34808043,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-26T07:30:49.091Z","updated_at":"2026-06-26T07:30:49.156Z","avatar_url":"https://github.com/stiles.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# World Cup data\n\nCollects teams, players, schedule, results and stats for the FIFA World Cup from public FIFA API endpoints, with a CLI for play-by-play of live matches in the terminal. \n\n## Setup\n\n```bash\npython -m venv .venv \u0026\u0026 source .venv/bin/activate\npip install -e .          # or: pip install -r requirements.txt\n```\n\nInstalling the package (`pip install -e .`) also adds the `nutmeg` and\n`worldcup-collect` console scripts.\n\n## Usage\n\nRun every collector:\n\n```bash\npython -m worldcup\n```\n\nRun specific collectors (any subset, space separated):\n\n```bash\npython -m worldcup teams players\npython -m worldcup schedule results\n```\n\nEach module is also runnable on its own:\n\n```bash\npython -m worldcup.teams\npython -m worldcup.player_stats\n```\n\nOutputs land in `data/processed/` as both CSV and JSON. Raw API payloads (where saved) go to `data/raw/`. See [Data layout](#data-layout) for how the current tournament, per-edition and combined files are organized.\n\n## Data layout\n\n`data/processed/` holds three tiers of data:\n\n| Location | What | Produced by |\n|---|---|---|\n| `data/processed/*.csv` (root) | The **current tournament** (2026). The only tier with stats (`team_stats`, `player_stats`). | `python -m worldcup` |\n| `data/processed/\u003cyear\u003e/` | One folder **per edition** (1930-2026) with the core datasets: `teams`, `players`, `schedule`, `results`. No stats. | `worldcup-backfill` |\n| `data/processed/all/` | All editions **combined** into one table per dataset, with a `year` column, plus `coverage.csv`. | `worldcup-backfill` |\n\nThe root files and the `2026/` folder overlap for the core datasets, but the root is the live working set (and carries the stats), while `2026/` is just 2026's slice of the historical archive.\n\nRaw API payloads (where saved) go to `data/raw/`.\n\n## nutmeg: follow a match (play-by-play)\n\n`nutmeg` browses the schedule, picks a match and either recaps a finished game or\nstreams a live one in the terminal, with goals, cards and VAR highlighted.\n\n```bash\nnutmeg                 # today's matches, pick one\nnutmeg USA             # USA's live / last / next match\nnutmeg --match 1       # follow a specific match number\nnutmeg USA --recap     # full timeline of the last USA match\nnutmeg --schedule          # print the schedule and exit\n```\n\nThe `nutmeg` command is available after `pip install -e .`; without installing,\nrun it as `python -m worldcup.nutmeg ...`.\n\nKey flags:\n\n- `--recap` print the full timeline and exit (default for finished matches)\n- `--scoring-only` goals, cards, VAR and key moments only\n- `--recent` when live, show only the last few events instead of the full match so far\n- `--schedule` print the schedule (optionally filtered by team) and exit\n- `--interval N` live poll seconds (default 8)\n- `--tz ZONE` IANA timezone for kickoff times (default: local)\n- `--no-color` disable ANSI color\n\nData comes from the FIFA `timelines/{competition}/{season}/{stage}/{match}` endpoint,\nkeyed by the same IDs the collectors use, so no per-match configuration is needed.\n\n## Examples\n\nAnalysis scripts that build on the collected data live in `examples/`:\n\n```bash\npython examples/team_profiles.py   # per-team average age, height, weight, squad size\n```\n\n## Collect every edition (backfill)\n\n`worldcup-backfill` collects the core datasets (teams, players, schedule, results)\nfor all World Cups, 1930-2026, discovered live from the FIFA seasons endpoint.\n\n```bash\npython -m worldcup.backfill              # all editions\npython -m worldcup.backfill --from 1990  # 1990 onward\npython -m worldcup.backfill 2018 2022     # specific years\npython -m worldcup.backfill --refresh     # re-pull editions already on disk\n```\n\nOutput:\n\n- Per edition: `data/processed/\u003cyear\u003e/{teams,players,schedule,results}.csv` (+ JSON)\n- Combined, with a `year` column: `data/processed/all/{teams,players,schedule,results}.csv`\n- `data/processed/all/coverage.csv` — row counts per dataset per edition\n\nPast tournaments are static, so collected years are skipped unless `--refresh` is\npassed; the run is resumable if a connection drops. Editions without a given\ndataset are skipped gracefully (FIFA actually has squads back to 1930).\n\n## Targeting a different tournament\n\nEdit `worldcup/config.py`:\n\n- `SEASON_ID` - tournament season (2026 = `285023`, 2022 = `255711`)\n- `COMPETITION_ID` - `17` for the men's World Cup\n\n## Data sources\n\n| Output | Endpoint |\n|---|---|\n| `teams` | `api.fifa.com/api/v3/competitions/teams/{season}` |\n| `players` | `api.fifa.com/api/v3/teams/{idTeam}/squad` |\n| `schedule`, `results` | `api.fifa.com/api/v3/calendar/matches` |\n| `team_stats`, `team_stats_wide` | `fdh-api.fifa.com/v1/stats/season/{season}/team/{idTeam}.json` |\n| `player_stats` | `fdh-api.fifa.com/v1/stats/season/{season}/players.json` |\n\n## Outputs\n\n- `teams` - one row per qualified team (id, confederation, names, flag URL)\n- `players` - full squads with position, age, height, weight, jersey number\n- `schedule` - fixtures with stage, group, kickoff times (UTC and venue-local)\n- `results` - scores, winner, possession, tactics, attendance, weather, venue\n- `team_stats` (long) and `team_stats_wide` (pivoted) - aggregate team metrics\n- `player_stats` (long) - per-player metrics joined to player names and teams\n\nStats endpoints only return values once matches have been played, so those files grow as the tournament progresses.\n\n## Layout\n\n```\nworldcup/\n  config.py        tournament IDs, API bases, output paths\n  fetch.py         HTTP with retry + calendar fetcher\n  storage.py       CSV/JSON writers\n  teams.py players.py schedule.py results.py team_stats.py player_stats.py\n  seasons.py       discover all World Cup editions (year -\u003e idSeason)\n  backfill.py      collect every edition (the `worldcup-backfill` command)\n  nutmeg.py        play-by-play CLI (the `nutmeg` command)\n  __main__.py      collector orchestrator (python -m worldcup)\nexamples/          analysis scripts built on the collected data\ndata/processed/    CSV + JSON outputs\n  *.csv            current tournament (2026), incl. stats — python -m worldcup\n  \u003cyear\u003e/          one folder per edition (1930-2026) — worldcup-backfill\n  all/             every edition combined, with a year column + coverage.csv\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstiles%2Fworld-cup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstiles%2Fworld-cup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstiles%2Fworld-cup/lists"}