- Python 83.3%
- HTML 9%
- CSS 5.5%
- Dockerfile 2.2%
| app | ||
| quadlet | ||
| tests | ||
| tools | ||
| .containerignore | ||
| .env.example | ||
| .gitignore | ||
| _probe2.py | ||
| Containerfile | ||
| README.md | ||
| requirements.txt | ||
Brandflyg statistik
Web app that shows brandflyg statistics per pilot/spanare for a selected year (defaults to the current year), based on data from the myWebLog API v4.
Per person it shows number of missions (total / as pilot / as spanare) and flight time (total / as pilot / as spanare).
Run locally
Put your token in a .env file in the repo root (it is git-ignored, see .env.example):
MWL_API_TOKEN=<your token>
Then:
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe -m uvicorn app.main:app --reload --port 8080
The app reads .env on startup, so no environment variables need to be set manually. A real
environment variable always wins over the value in .env.
Open http://localhost:8080/ . JSON is available at /api/stats?year=2026, health at /healthz.
Configuration
All settings are environment variables, see .env.example. Only MWL_API_TOKEN
is required.
| Variable | Default | Purpose |
|---|---|---|
MWL_API_TOKEN |
– | Bearer token for the myWebLog API |
MWL_API_BASE_URL |
https://api.myweblog.se/main/v4 |
API base URL |
BRANDFLYG_FLIGHT_TYPE_IDS |
– | type_of_flight.id values counted as brandflyg (takes precedence) |
BRANDFLYG_KEYWORDS |
brand |
Fallback keyword match on type_of_flight.name |
PILOT_ROLES |
pilot,pic |
users[].role values counted as pilot |
SPANARE_ROLES |
scout,spanare,observer |
users[].role values counted as spanare |
FLIGHT_TIME_SOURCES |
airborne,block,tach,hobbs |
summary.total_{source}_minutes used as flight time, first match wins |
YEARS_BACK |
10 |
Number of years offered in the year selector |
CACHE_TTL_SECONDS |
300 |
In-memory cache lifetime for API responses |
ROOT_PATH |
– | Sub-path the app is served under, e.g. /subfolder/app |
The mapping is verified against live data: a flight log entry carries type_of_flight,
departure.takeoff_datetime, summary.total_airborne_minutes and a users array where each
member has a role (Pilot, Scout, Instructor). Entries whose flight type name contains
"brand" (BRANDFLYG O1, FFK-BRANDBEVAKNING) are counted; instructors are ignored. To inspect
the live data yourself (also uses .env):
.\.venv\Scripts\python.exe -m tools.probe_api 2026
Build the container
podman build -t brandflyg-statistik:latest -f Containerfile .
Run with Podman Quadlet (rootless systemd user unit)
mkdir -p ~/.config/containers/systemd ~/.config/brandflyg-statistik
cp quadlet/brandflyg-statistik.container ~/.config/containers/systemd/
cp .env.example ~/.config/brandflyg-statistik/app.env # then fill in MWL_API_TOKEN
chmod 600 ~/.config/brandflyg-statistik/app.env
systemctl --user daemon-reload
systemctl --user start brandflyg-statistik.service
systemctl --user status brandflyg-statistik.service
journalctl --user -u brandflyg-statistik.service -f
For a system-wide unit put the file in /etc/containers/systemd/, point EnvironmentFile at an
absolute path such as /etc/brandflyg-statistik/app.env and use systemctl without --user.
To keep the service running after logout: loginctl enable-linger $USER.
The app listens on port 8080 in the container; change the host port via PublishPort in the
Quadlet file. Put a TLS-terminating reverse proxy in front of it if exposed outside the host.
Hosting under a sub-path
Set ROOT_PATH to serve the app from e.g. https://example.com/subfolder/app/:
ROOT_PATH=/subfolder/app
All generated links (year form, static files, JSON link) then include the prefix. Both proxy
styles work: one that strips the prefix before forwarding, and one that forwards the full path —
requests are accepted with or without the prefix. /healthz therefore also stays reachable
without the prefix, so the container health check keeps working.
nginx example:
location /subfolder/app/ {
proxy_pass http://127.0.0.1:8080/subfolder/app/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
Tests
.\.venv\Scripts\python.exe -m pytest -q