A complete Kafka streaming analytics pipeline for completed FIFA World Cup 2026 matches.
This project applies the full streaming analytics workflow to international football match data.
The original example processed sales transactions. This custom version replaces the sales dataset with completed FIFA World Cup 2026 match results.
The Kafka producer reads one completed match at a time from a CSV file, validates it, and sends it to a Kafka topic. The consumer validates each message, calculates match and team statistics, updates group standings, generates a chart, writes CSV output, and stores the results in DuckDB.
World Cup CSV
↓
Kafka Producer
↓
Kafka Topic
↓
Kafka Consumer
↓
Validation and Analytics
↓
CSV + DuckDB + Visualization
The project includes:
- Kafka message production and consumption
- match-record validation
- duplicate match detection
- derived match fields
- wins, draws, and losses
- goals scored and conceded
- goal difference
- points and win percentage
- live group standings
- CSV output
- DuckDB storage
- standings visualization
The producer reads:
data/world_cup_matches.csv
The dataset contains 29 completed FIFA World Cup 2026 matches available at the dataset cutoff time.
Each row represents one completed match and includes:
- match ID and date
- tournament stage and matchday
- group
- home and away teams
- team codes
- goals
- winner and result type
- points awarded
- stadium
- city and host country
- source URL
- dataset cutoff information
The dataset is a fixed snapshot so the project can be reproduced consistently.
The custom producer is:
src/streaming/kafka_producer_sabri.py
The producer:
- reads completed matches from the CSV file
- validates required fields
- validates dates, goals, results, winners, and points
- checks for duplicate match IDs
- sends valid matches to Kafka
- writes invalid matches to a rejected-records CSV
The Kafka topic is:
streaming-06-scenarios-case
The Kafka message key is the World Cup group letter.
Examples:
A
B
J
Using the group as the key logically associates matches from the same group.
Run the producer with:
uv run python -m streaming.kafka_producer_sabriSuccessful producer result:
Sent 29 completed match message(s).
Rejected 0 match message(s).
World Cup producer executed successfully!
The custom consumer is:
src/streaming/kafka_consumer_sabri.py
For every valid match message, the consumer:
- validates the match
- converts numeric fields
- creates a readable scoreline
- calculates the winning margin
- identifies draws
- updates both teams' statistics
- recalculates group standings
- updates the visualization
- writes the match to CSV
- writes matches and standings to DuckDB
The consumer calculates:
- matches played
- wins
- draws
- losses
- goals for
- goals against
- goal difference
- points
- win percentage
- group position
Run the consumer with:
uv run python -m streaming.kafka_consumer_sabriThe completed pipeline processed:
| Metric | Result |
|---|---|
| Matches produced | 29 |
| Producer rejections | 0 |
| Matches consumed | 29 |
| Consumer skips | 0 |
| National teams | 48 |
| Groups | 12 |
Selected findings:
| Finding | Result |
|---|---|
| Mexico points | 6 |
| United States points | 6 |
| Canada goals scored | 7 |
| Germany goals scored | 7 |
| Canada goal difference | +6 |
| Germany goal difference | +6 |
| Mexico win percentage | 100% |
| United States win percentage | 100% |
Mexico led Group A after winning its first two matches. The United States led Group D with six points. Canada and Switzerland both earned four points in Group B, with Canada ranked first because of its stronger goal difference.
The consumer generates a chart showing the current leading teams.
The project generates:
data/output/consumed_world_cup_matches.csv
data/output/world_cup_team_standings.csv
data/output/world_cup.duckdb
data/output/world_cup_team_standings.png
The DuckDB database contains:
matches
team_standings
streaming-06-scenarios/
├── data/
│ ├── world_cup_matches.csv
│ └── output/
│ ├── consumed_world_cup_matches.csv
│ ├── world_cup_team_standings.csv
│ ├── world_cup_team_standings.png
│ └── world_cup.duckdb
├── docs/
│ ├── images/
│ │ └── world_cup_team_standings.png
│ └── index.md
├── src/
│ └── streaming/
│ ├── kafka_producer_sabri.py
│ └── kafka_consumer_sabri.py
├── tests/
├── pyproject.toml
├── zensical.toml
└── README.md
Clone the repository:
git clone https://github.com/sabrouch36/streaming-06-scenarios.git
cd streaming-06-scenarios
code .Install the project dependencies:
uv self update
uv python pin 3.14
uv sync --extra dev --extra docs --upgradeCopy the environment example if needed:
Copy-Item .env.example .envImportant .env values include:
KAFKA_TOPIC=streaming-06-scenarios-case
PRODUCER_MESSAGE_COUNT=29
PRODUCER_MESSAGE_INTERVAL_SECONDS=2
KAFKA_AUTO_OFFSET_RESET=earliest
Use a WSL terminal.
Verify Java:
echo "$JAVA_HOME"
"$JAVA_HOME/bin/java" --versionRebuild the Kafka cluster metadata when starting fresh:
cd ~/kafka
rm -rf /tmp/kraft-combined-logs
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
bin/kafka-storage.sh format --standalone \
-t "$KAFKA_CLUSTER_ID" \
-c config/server.propertiesStart Kafka and leave the terminal running:
cd ~/kafka
bin/kafka-server-start.sh config/server.propertiesOpen another WSL terminal:
cd ~/kafka
bin/kafka-topics.sh --create \
--bootstrap-server localhost:9092 \
--partitions 1 \
--replication-factor 1 \
--topic streaming-06-scenarios-caseVerify the topic:
bin/kafka-topics.sh --list \
--bootstrap-server localhost:9092Use a PowerShell terminal from the project root.
Run the producer:
uv run python -m streaming.kafka_producer_sabriRun the consumer:
uv run python -m streaming.kafka_consumer_sabriRun formatting and validation:
uv run ruff format .
uv run ruff check . --fix
uv run python -m pyright
uv run python -m pytest
uv run python -m zensical buildRun pre-commit checks:
git add -A
uvx pre-commit run --all-filesRepeat the pre-commit command if it modifies files.
Build the Zensical documentation site:
uv run python -m zensical buildThe generated site is written to:
site/
Hosted documentation:
git add -A
git commit -m "Complete World Cup streaming analytics project"
git push -u origin mainThe original sales example calculated transaction totals and detected high-value orders.
My custom modification replaces that logic with football-match validation and live tournament analytics.
The producer validates the relationship between:
- goals
- winner
- result type
- total goals
- points
The consumer updates the standings after every match and ranks teams using:
- points
- goal difference
- goals scored
- team name as a deterministic final tie breaker
This modification demonstrates how Kafka can transform individual sporting events into continuously updated tournament intelligence.
Additional project details, experiments, results, and interpretation are available in the hosted documentation:
