Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Taktik

Taktik is a football tactical-network prototype. It has a static match-room interface, a small FastAPI backend, and an optional Neo4j importer for StatsBomb event data.

Run the app

These steps are enough to run Taktik locally. Run every command from the project folder.

1. Create the Python environment

Do this once:

py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

The PowerShell prompt should now start with (.venv). Python 3.14 is supported by the dependency versions in requirements.txt.

Configure sign-in

Taktik uses self-managed accounts. Copy the environment template if you have not already done so, then generate a persistent signing secret:

Copy-Item .env.example .env
.\.venv\Scripts\python.exe -c "import secrets; print(secrets.token_urlsafe(48))"

Put the generated value in .env:

TAKTIK_SECRET_KEY=paste-the-generated-value-here
TAKTIK_AUTH_DB=data/taktik-auth.sqlite3

On first launch, open the frontend and select Create account. Passwords require at least 12 characters and are stored only as Argon2 hashes. The first account gets an administrator role and a personal workspace. Keep the SQLite file backed up; it contains accounts and saved queries. For a deployed multi-user installation, move this store to PostgreSQL.

2. Start the API

Keep this terminal open:

$env:PYTHONPATH = "$PWD\backend"
uvicorn app.main:app --reload --port 8000

Check that it started by opening http://127.0.0.1:8000/api/health. API documentation is available at http://127.0.0.1:8000/docs.

3. Start the frontend

Open a second PowerShell terminal in this folder and run:

python -m http.server 8080

Open http://127.0.0.1:8080 in your browser.

The frontend sends API requests to port 8000. When Neo4j is connected, it draws the nodes and weighted relationships returned from your database. If the API is not running, the page still works with its built-in demo data.

The match header, score, pass totals, player lens, event stream, tactical readout, and network visualisation are populated from Neo4j. Try one of the suggested queries, such as High pressure network, Progressive sequences, or Ball recoveries. The API selects the loaded passing team with the most records when the old demo team (Arsenal) is not present in your data.

Workspace features

  • Network explorer: moves directly to the current graph; the focus control opens it in fullscreen when the browser supports it.
  • Analyst filters: open the ... control on the network to constrain the graph by team, player, origin zone, possession phase, score state, period, or match minute. The timeline scrubber applies an end-minute filter during playback.
  • Pitch annotations: choose Add pitch annotation in the same menu, then click the pitch. Annotations are kept locally per match and can be removed by clicking them.
  • Saved queries: persists named queries, tags, notes, shares, and comments to the self-managed workspace database.
  • Export view: exports the active result as JSON or CSV, or opens a print-ready report that can be saved as PDF.
  • Event stream: cycles between all key events, defensive actions, and shots; Load earlier events reveals more imported events.
  • Import match: select a StatsBomb event JSON file from the sidebar. The API imports it into Neo4j and refreshes the dashboard.
  • Match picker: click the workspace name in the sidebar to switch between loaded matches. All dashboard and query results are filtered to the selected match.
  • Graph schema: opens the node and relationship model used by the importer and query API.

The import control requires the API to be running with TAKTIK_ENV=neo4j and a healthy Neo4j connection.

Connect Neo4j (optional)

Only follow this section if you want to import a real StatsBomb match into Neo4j. If you have already loaded a match successfully, skip to Start the API above.

1. Configure the database

Start your Neo4j instance, then create a local .env file:

Copy-Item .env.example .env
notepad .env

Enter your own Neo4j password:

TAKTIK_ENV=neo4j
NEO4J_URI=bolt://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=your-neo4j-password

Never commit .env. It is already ignored by Git.

2. Get one match file

Download StatsBomb Open Data once:

New-Item -ItemType Directory -Force external, data
git clone https://github.com/hudl/open-data.git external/statsbomb-open-data

Copy an event file to the location the loader expects:

$eventFile = Get-ChildItem .\external\statsbomb-open-data\data\events\*.json | Select-Object -First 1
Copy-Item $eventFile.FullName .\data\match.json

3. Load the match

Activate the virtual environment if necessary, then run:

.\.venv\Scripts\Activate.ps1
$env:PYTHONPATH = "$PWD\backend"
$env:TAKTIK_MATCH_FILE = "$PWD\data\match.json"
python -m ingestion.load_to_neo4j

No output and no traceback means the import finished successfully. Restart the API after an import, then confirm http://127.0.0.1:8000/api/health reports "neo4j": "connected".

If you imported a match before updating this project, run the loader once more. It is idempotent: it updates the same event and pass records with the pressure, progressive-pass, recovery, and zone data used by the interface.

Useful commands

# Activate the environment in a new terminal
.\.venv\Scripts\Activate.ps1

# Check backend syntax
python -m compileall -q backend

# Test the query endpoint
Invoke-RestMethod http://127.0.0.1:8000/api/query -Method Post -ContentType 'application/json' -Body '{"query":"Show Arsenal passing under high pressure"}'

Current project status

  • The demo interface and API work without Neo4j.
  • The importer creates players, events, zones, passes, recoveries, and event-zone relationships in Neo4j.
  • With TAKTIK_ENV=neo4j and a connected instance, the four suggested query modes return and render real graph data.
  • The natural-language input currently classifies questions into four supported modes rather than generating arbitrary Cypher.
  • Saved queries are browser-local; they are not yet shared between users or devices.
  • Each import is stored as a Match and all events, passes, and recoveries are tagged with its match ID. The match picker filters the dashboard and query results accordingly. The import name is derived from the uploaded JSON filename; add richer competition, season, and fixture metadata before using it as a production catalogue.

Project files

index.html, style.css, app.js    Frontend
backend/app/                     FastAPI API and demo data
backend/ingestion/               StatsBomb parser and Neo4j loader
requirements.txt                 Python packages
.env.example                     Neo4j configuration template

Data attribution

The optional dataset comes from StatsBomb Open Data. Credit StatsBomb in any published analysis that uses its data.

About

See the pattern. Explain the game.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Contributors

Languages