An end-to-end application that analyzes football match data and delivers structured betting recommendations powered by a local AI model via Ollama. Built with FastAPI, Next.js 14, and SQLite for offline-first fixture data.
| Feature | Description |
|---|---|
| π§ AI Recommendations | Submits match context to a local Ollama model (kimi-k2.5:cloud) and returns a structured betting recommendation with confidence score and reasoning |
| π Match Schedule Viewer | Displays the full 2026 World Cup fixture list (group stage β Final) with team logos and dates |
| πΎ Offline-First Data | Fixtures are stored in a local SQLite database β zero API calls needed after the first seed |
| π Smart API Caching | When the external football API is available, it updates the DB automatically; otherwise it falls back to the built-in static schedule |
| π¨ Clay-Inspired UI | Clean white/blue design system with generous border radii, oat borders, and playful hover animations |
| π CORS-Enabled | FastAPI backend is pre-configured to accept requests from the Next.js frontend |
Football_gupa/
β
βββ main.py # FastAPI app β routes & CORS setup
βββ requirements.txt # Python dependencies (pinned)
βββ seed_db.py # Seed DB from API-Sports (1 API call, then cached)
βββ seed_static.py # Seed DB from built-in schedule (zero API calls)
βββ world_cup.db # SQLite fixture database (auto-created)
βββ DESIGN.md # UI design system reference (Clay-inspired)
β
βββ models/
β βββ schemas.py # Pydantic models: MatchInput, BettingRecommendation, MatchOverview
β
βββ services/
β βββ ai_agent.py # Calls local Ollama model, returns structured JSON
β βββ football_api.py # Fixture fetcher β DB β JSON cache β live API
β βββ database.py # SQLite layer: init, upsert, query helpers
β
βββ frontend/ # Next.js 14 application
βββ app/
βββ page.tsx # Main page β two-tab layout (Predictor / Matches)
βββ globals.css # Design tokens + all component styles
βββ lib/
β βββ api.ts # Service layer β all fetch calls (never in components)
βββ components/
βββ MatchForm.tsx # Match input form with form picker + odds
βββ ResultCard.tsx # AI result display with animated confidence bar
βββ MatchesViewer.tsx # World Cup match cards grid
| Tool | Version |
|---|---|
| Python | 3.11+ |
| Node.js | 18+ |
| Ollama | Latest |
| API-Sports Account | Optional (for live fixture data) |
git clone https://github.com/twoeme/Football_gupa.git
cd Football_gupa/Football_gupa# Create and activate a virtual environment
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS / Linux
# Install dependencies
pip install -r requirements.txtCreate a .env file in the Football_gupa/ root:
# Required for AI recommendations
OLLAMA_URL=http://localhost:11434/api/generate
OLLAMA_MODEL=kimi-k2.5:cloud
# Optional β only needed if you want live fixture updates from API-Sports
API_SPORTS_KEY=your_api_sports_key_here
β οΈ Never commit your.envfile. It is listed in.gitignore.
Run the seeder once. It will try the live API first, and automatically fall back to the built-in 2026 World Cup schedule if the API is unavailable:
python seed_db.pyOr, to skip the API entirely and use the built-in static schedule (zero API cost):
python seed_static.pyYou should see output like:
Static seed complete β 62 fixture(s) written. DB now holds 62 match(es).
Make sure Ollama is running and the model is available:
ollama run kimi-k2.5:cloudIn the Football_gupa/ directory:
uvicorn main:app --reloadThe API is now live at http://127.0.0.1:8000.
Interactive docs available at http://127.0.0.1:8000/docs.
In a new terminal, navigate into the frontend folder:
cd frontend
npm install
npm run devOpen http://localhost:3000 in your browser. π
Returns a welcome message and a link to the Swagger docs.
Returns all World Cup 2026 fixtures from the local SQLite database.
Response:
{
"matches": [
{
"fixture_id": 900013,
"date": "2026-06-13T01:00:00+00:00",
"status": "NS",
"home_team": "USA",
"home_logo": "https://media.api-sports.io/football/teams/2.png",
"away_team": "Panama",
"away_logo": "https://media.api-sports.io/football/teams/70.png",
"home_goals": null,
"away_goals": null
}
]
}Submits a match for AI analysis and returns a betting recommendation.
Request body:
{
"team_a": "Argentina",
"team_b": "France",
"team_a_recent_form": "W W W W W",
"team_b_recent_form": "W W W D W",
"odds_team_a": 2.80,
"odds_draw": 3.10,
"odds_team_b": 2.75,
"additional_context": "MbappΓ© is in top form. Messi recovering from minor fatigue."
}Response:
{
"recommended_bet": "Argentina to Win",
"confidence_score": 72.5,
"reasoning": "Argentina's perfect recent form combined with their slight odds advantage..."
}python seed_db.py
βββ API key present?
β βββ YES β Call API-Sports (1 request) β Write to world_cup.db
β β βββ API returns 0 results? β fallback β
β βββ NO β fallback β
βββ seed_static.py β 62 announced WC2026 fixtures β world_cup.db
GET /matches (frontend request)
βββ world_cup.db has rows? β Serve from SQLite (free, instant)
βββ No DB rows but fresh JSON cache? β Load cache β seed DB β serve
βββ Nothing cached β call API live β seed DB β serve
After the first seed, every frontend request is served from the local SQLite file β no API quota consumed.
| Library | Version | Purpose |
|---|---|---|
| FastAPI | 0.115.4 | REST API framework |
| Uvicorn | 0.32.0 | ASGI server |
| Pydantic | 2.9.2 | Request/response validation |
| Requests | 2.32.3 | HTTP client for Ollama + API-Sports |
| python-dotenv | 1.0.1 | Environment variable loading |
| SQLite3 | stdlib | Local fixture database |
| Library | Version | Purpose |
|---|---|---|
| Next.js | 14.2.3 | React framework with App Router |
| React | 18.3.1 | UI library |
| TypeScript | 5.4.5 | Type safety |
| Lucide React | 0.378.0 | Icons |
| Tool | Purpose |
|---|---|
| Ollama | Runs local LLMs without cloud API keys |
| kimi-k2.5:cloud | Default model for match analysis |
| Variable | Default | Description |
|---|---|---|
OLLAMA_URL |
http://localhost:11434/api/generate |
Ollama API endpoint |
OLLAMA_MODEL |
kimi-k2.5:cloud |
Model name to use for analysis |
API_SPORTS_KEY |
β | Your API-Sports key (optional) |
NEXT_PUBLIC_API_URL |
http://localhost:8000 |
Backend URL for the Next.js app |
- Betting disclaimer: This tool is for educational and entertainment purposes only. It does not guarantee any betting outcome. Always gamble responsibly.
- AI model: The quality of recommendations depends on the local Ollama model you have installed. Larger models will generally produce better reasoning.
- API-Sports free tier: The free plan allows 100 requests per day. The SQLite caching system is designed to minimise this usage to as few as 1 request total.
- Fixture data accuracy: The static schedule (
seed_static.py) is based on officially announced 2026 World Cup groupings and dates. Knockout rounds use placeholder bracket notation until results are determined.
This project is part of a Data Analytics portfolio. Feel free to fork and adapt for personal use.
Built with β½ FastAPI + Next.js + Ollama