Database schema¶
The canonical schema lives in src/gotime/db/models.py. All timestamps are
stored in UTC with tzinfo.
Entity-relationship diagram¶
erDiagram
users ||--o{ locations : "owns"
users ||--o{ trips : "requested"
providers ||--o{ trips : "measured"
providers ||--o{ trip_api_logs : "responded"
locations ||--o{ trips : "start"
locations ||--o{ trips : "end"
locations ||--o{ trip_api_logs : "start"
locations ||--o{ trip_api_logs : "end"
trips ||--o| trip_api_logs : "raw response"
users {
int id PK
varchar name
varchar tz "IANA timezone"
}
providers {
int id PK
varchar name UK "matches gotime registry keys"
}
locations {
int id PK
varchar address
float latitude "WGS-84"
float longitude "WGS-84"
varchar nickname "unique per user"
int user_id FK
}
trips {
int id PK
timestamptz timestamp "UTC"
int start_location_id FK
int end_location_id FK
float duration "minutes"
float duration_in_traffic "minutes"
float distance "miles"
int steps
float fuel_used "reserved"
int provider_id FK
int user_id FK
}
trip_api_logs {
int id PK
timestamptz timestamp
int provider_id FK
int start_location_id FK
int end_location_id FK
json raw_json "unmodified provider body"
}
Tables¶
users¶
Column |
Type |
Null |
Notes |
|---|---|---|---|
|
|
no |
Auto-increment. |
|
|
yes |
Display name. |
|
|
yes |
IANA tz, e.g. |
providers¶
Column |
Type |
Null |
Notes |
|---|---|---|---|
|
|
no |
|
|
|
no |
Matches |
locations¶
Column |
Type |
Null |
Notes |
|---|---|---|---|
|
|
no |
|
|
|
no |
Full postal address. |
|
|
yes |
WGS-84. |
|
|
yes |
WGS-84. |
|
|
yes |
Display label. Unique per-user. |
|
|
yes |
trips¶
Column |
Type |
Units |
Notes |
|---|---|---|---|
|
|
— |
|
|
|
UTC |
Time of the API call. |
|
|
— |
|
|
|
— |
|
|
|
minutes |
Free-flow duration, when available. |
|
|
minutes |
Real-time traffic estimate. |
|
|
miles |
Conversion from meters at persist time. |
|
|
count |
Maneuver steps (when provider returns them). |
|
|
gallons |
Reserved; unused by v1. |
|
|
— |
|
|
|
— |
Indexes and constraints:
Name |
Kind |
Columns |
Purpose |
|---|---|---|---|
|
|
|
Lets the legacy-data merge importers in |
|
|
|
Covers dashboard time-window and date-range queries. |
|
|
|
Covers per-provider aggregates in the stats API. |
trip_api_logs¶
Column |
Type |
Notes |
|---|---|---|
|
|
|
|
|
Matches parent trip. Same |
|
|
|
|
|
|
|
|
|
|
|
Unmodified provider response. Only populated when |
Dialect notes¶
raw_jsonis declared as the generic SQLAlchemyJSONtype. On PostgreSQL this is rendered asJSONB(matchingscripts/merge/seed_canonical.sql); on SQLite it degrades toTEXTwith JSON serialization, which keeps the local dev loop simple.timestampcolumns carry both a Python-side default (_utcnow) and a SQL-sideserver_default=CURRENT_TIMESTAMP. The Python default means SQLite inserts without a server-default still work; the server default keeps the Alembic-managed schema in lock-step with the merge importers’ raw SQL, which depend on the DB-side behaviour.
The alignment between src/gotime/db/models.py,
alembic/versions/0002_trip_indexes_and_defaults.py, and
scripts/merge/seed_canonical.sql is enforced by
tests/test_schema_drift.py, which parses the canonical SQL when
present and fails if any of the three shifts out from under the others.
Provider capability matrix¶
Provider |
Duration |
Duration-in-traffic |
Distance |
Steps |
|---|---|---|---|---|
✅ |
✅ |
✅ |
✅ |
|
Bing |
✅ |
✅ |
✅ |
— |
TomTom |
✅ (derived) |
✅ |
✅ |
— |
HERE |
✅ |
✅ |
✅ |
— |
MapQuest |
✅ |
✅ |
✅ |
— |
Mapbox (driving-traffic) |
✅ |
✅ (same) |
✅ |
— |
Azure |
✅ (derived) |
✅ |
✅ |
— |
Fields marked — are left NULL; downstream analytics must tolerate that.