Migration 19: serve.fn_vehicle_trips(vehicle_id, date) — PL/pgSQL state
machine that walks state.position_history for one vehicle on one EAT day
and emits the trip breakdown. Rules:
- reporting_time = first ACC_ON of the day
- trip starts at ACC_ON (or first fix if already ACC_ON / moving)
- trip ends:
* ACC_OFF + stationary (<5 km/h) for >=5 min → end_reason 'work_stop'
* fix gap >30 min → end_reason 'long_gap'
* end of day's data → end_reason 'day_end'
- within a trip, ACC_ON + stationary >=5 min is logged as an idling
segment (no trip split — engine still on)
- distance only accumulates when speed >= 5 km/h (excludes GPS jitter)
- falls back to movement-only segmentation when acc_state is null
across the day (has_acc_data=false in the response)
Returns one jsonb document: vehicle, date, reporting_time, day totals
(distance, driving/idling/stopped/unknown minutes), data_quality flags,
trips[] with start/end/duration/distance/idling/end_reason/stops/path
where path is a GeoJSON LineString ready for the map.
New endpoints (read:fleet, rate-limited):
GET /api/views/vehicle/{id}/trips?date=YYYY-MM-DD JSON
GET /api/views/vehicle/{id}/trips.csv?date=YYYY-MM-DD one row per trip
Defaults date to today in EAT (UTC+3) regardless of host TZ.
|
||
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| db/migrations | ||
| scripts | ||
| tests | ||
| web | ||
| .env.example | ||
| .gitignore | ||
| 260522_fleet_platform_architecture_final.md | ||
| 260522_fleet_platform_prd_final.md | ||
| 20260427_FSG_Vehicles_mitieng.csv | ||
| docker-compose.dev.yml | ||
| Dockerfile | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
fleet-platform
Greenfield rebuild of the Rahamafresh fleet telematics platform — CI is live on Forgejo Actions.
One FastAPI codebase, one Docker image, three container roles (gateway / worker / cron) from the same image. Event-sourced ingest into TimescaleDB + PostGIS. JWT-mandatory reads. Image-tag deploys via Coolify from a self-hosted Forgejo registry.
See:
260522_fleet_platform_prd_final.md— product spec (P1–P4)260522_fleet_platform_architecture_final.md— engineering design (phases A–G)~/.claude/plans/you-are-an-experienced-majestic-planet.md— approved Phase 1 plan
Quick start (dev)
cp .env.example .env
# edit .env: at minimum set POSTGRES_PASSWORD, JWT_SECRET, TRACKSOLID_PUSH_TOKEN
docker compose -f docker-compose.dev.yml up
Health checks:
curl http://localhost:8001/health/gateway
curl http://localhost:8001/health/worker # via worker container's exposed port
curl http://localhost:8001/health/cron
Layout
app/ one FastAPI codebase
entrypoints/ three role entrypoints from the same image
gateway.py /push/jimi/*, /api/views/*, /api/auth/token
worker.py LISTEN parser + projectors
cron.py polling + SLO + contract checker
models/ Pydantic models (Jimi contracts, view shapes)
parsers/ one function per msg_type / poll endpoint
projectors/ single-writer projectors per state table
db/migrations/ dbmate forward-only SQL
scripts/ operational utilities (parity_check, entrypoint.sh)
tests/ pytest
web/ fleet-core.js + index-live.html + login.html
Container roles
The same image runs in three roles, selected by APP_ROLE:
| Role | Workload |
|---|---|
gateway |
HTTP: Tracksolid push receivers + dashboard read API + JWT issuance |
worker |
LISTEN events_raw_new / events_parsed_new → parser → projectors |
cron |
APScheduler: polled ingest (60s/10m), SLO measurement, contract checker |
Failure isolation is the point: a heavy report in worker does not stall gateway.
Deploy
CI (Forgejo Actions) builds on push to main, tags <registry>/fleet-platform:<sha> and :latest. Coolify deploys by tag. Rollback is coolify deploy :<prev-sha>.
dbmate up runs on platform-worker startup before the worker serves traffic; gateway and cron wait on a startup probe that confirms migration completion.