Repository navigation
datenmodell
Erklärt, wie Daten im Netzwerk modelliert und lokal gespeichert sind.
app/src/main/kotlin/de/traewelling/app/data/model/Models.ktapp/src/main/kotlin/de/traewelling/app/data/model/SevModels.ktapp/src/main/kotlin/de/traewelling/app/data/model/RoadRouteModels.ktapp/src/main/kotlin/de/traewelling/app/data/model/TransitRouteModels.ktapp/src/main/kotlin/de/traewelling/app/data/local/StatusEntity.ktapp/src/main/kotlin/de/traewelling/app/service/GpsJourneyTimeEstimator.ktapp/src/main/kotlin/de/traewelling/app/service/JourneyTimeResolver.kt
Die Modelle verwenden Gson. @SerializedName legt abweichende JSON-Feldnamen fest; gleich benannte Felder werden direkt zugeordnet.
| Modell | Beschreibung |
|---|---|
OAuthTokenResponse |
Token-Antwort mit accessToken, refreshToken, tokenType, expiresIn |
AuthSession in util/AuthSession.kt ist dagegen ein lokaler atomarer Snapshot aus Server-URL, optionalem nicht leerem Access-Token und Revisionskennung. Er ist kein Retrofit-/OAuth-Antwortmodell. Die Revision trennt auch Abmeldung und erneute Anmeldung mit denselben Zugangsdaten; vergleichende Persistenzoperationen und der Feature-ViewModel-Store verwenden diesen Zustand. Auth beschreibt seinen Lebenszyklus.
| Modell | Beschreibung |
|---|---|
UserResponse |
Wrapper für User-Daten |
User |
Nutzerdaten: id, uuid, username, displayName, profilePicture, bio, totalDistance, totalDuration, points, following, muted, blocked |
| Modell | Beschreibung |
|---|---|
StatusListResponse |
Wrapper für Liste von Statusen mit PaginationLinks |
SingleStatusResponse |
Wrapper für einzelnen Status |
Status |
Check-in-Status: id, body, createdAt, likes, liked, visibility, business, user, checkin, event, tags |
StatusUser |
User-Kurzform für Status |
CheckinInfo |
Check-in-Details mit origin, destination, operator, numerischem trip, tripUuid, Plan-/Echtzeitdaten und manuellen Zeitkorrekturen |
StopOperator |
Betreiber: id als String (Legacy-Zahl oder UUID), bevorzugte uuid, name, identifiers |
| Modell | Beschreibung |
|---|---|
StationIdentifier |
Kennung mit type, identifier sowie optional name und origin |
TrainStation |
Bahnhof: interne id, uuid, name, latitude, longitude, identifiers; ibnr und rilIdentifier werden aus identifiers berechnet |
StopStation |
Konkreter Halt: id, uuid, verschachtelte station, arrivalPlanned/arrivalReal, departurePlanned/departureReal, Gleis- und Ausfallinformationen |
StationSearchResponse |
Liste von TrainStation |
DepartureResponse |
Liste von DepartureTrip |
| Modell | Beschreibung |
|---|---|
DepartureTrip |
Abfahrt: tripId, line, direction, station, plannedWhen, realWhen (JSON when), platform, cancelled; berechnete delayMinutes |
HafasLine |
Linieninfo: name, fahrtNr, product, mode; kein veraltetes line.operator |
TripResponse |
Wrapper für TripDetails |
TripDetails |
Trip: numerische id, uuid, Provider-tripId, lineName, category, operator, stopovers |
StopoversResponse |
Map von tripId zu StopStation-Liste |
| Modell | Beschreibung |
|---|---|
TravelReason |
Enum für den Reisegrund beim Check-in: PRIVATE=0, BUSINESS=1, COMMUTE=2 |
CheckInRequest |
Request für Check-in: tripId, lineName, startStationId, destinationStationId, departure, arrival, body, business, visibility |
CheckInResponse |
Antwort mit Status und Points |
CheckInResult |
Enthält Status und CheckInPoints |
CheckInPoints |
Gesamtpunkte und calculation mit base, reason, distance, factor |
CheckInConflictResponse / CheckInConflictData
|
HTTP-409-Envelope mit data.conflicts als Status-Liste |
CheckInConflictException |
Konfliktliste und verständliche Meldung mit Linie, Ziel und Status-ID |
UpdateStatusRequest |
PUT-Request für Status-Updates; Zielwechsel mit destinationId und destinationArrivalPlanned gemeinsam |
| Modell | Beschreibung |
|---|---|
StatisticsResponse |
Wrapper für StatisticsData |
StatisticsData |
Statistiken: categories, operators, time, purpose |
StatEntry |
Kategorie/Betreiber-Stat: name, count, duration |
StatDay |
Tagesstatistik: date, count, duration |
| Modell | Beschreibung |
|---|---|
NotificationListResponse |
Liste mit Pagination |
Notification |
notification: id, type, lead, notice, link, readAt, createdAt, createdAtForHumans |
| Modell | Beschreibung |
|---|---|
PaginationLinks |
first, last, prev, next URLs |
PaginationMeta |
currentPage, lastPage, total, perPage |
StopStation.stationId liest ausschließlich station.id. Stopover-id wird nie für Stationsanfragen oder Check-ins verwendet: Träwelling widmet dieses Feld nach dem 30.11.2026 zur Halt-ID um.
stationName liest station.name; nur für die Anzeige alter gecachter Statusdaten bleibt ein privater Rückfall auf das frühere JSON-Feld name. stationIdentifier(type) liest ausschließlich station.identifiers. TrainStation.ibnr nutzt de_db_ibnr, TrainStation.rilIdentifier nutzt de_db_ril100.
matchesStopover(other) vergleicht bevorzugt die Stopover-UUID. Falls eine UUID fehlt, müssen interne Station-ID und eine geplante Ankunft oder Abfahrt übereinstimmen. ISO-Zeitstempel werden als Zeitpunkte verglichen. Wiederholte Besuche derselben Station auf einer Rundfahrt bleiben dadurch unterscheidbar.
effectiveArrival ist arrivalReal ?: arrivalPlanned, effectiveDeparture ist departureReal ?: departurePlanned. Diese Modellhelfer wählen vorhandene Felder, prüfen aber deren Parsebarkeit nicht. Die Legacy-Felder arrival und departure werden nicht gelesen. Der Tracking-Service berücksichtigt manuelle Einstieg-/Zielzeiten in seiner internen Route. Das Fahrtdetail behält API-Halte und manuelle Check-in-Zeiten getrennt; die Live-Anzeige verwendet JourneyTimeResolver statt GPS-Prognosen in Echtzeitfelder zu schreiben.
DepartureTrip.delayMinutes berechnet die Differenz zwischen when und plannedWhen in Minuten. Bei fehlender oder ungültiger Zeit ist der Wert null; das Legacy-Feld delay wird nicht verwendet.
| Modell/Feld | Bedeutung und Lebensdauer |
|---|---|
TrackingStop.plannedDepartureMillis |
Geplante Abfahrt des konkreten Besuchs, getrennt von effectiveDepartureMillis; optional für alte Cache-Routen. |
TrackingLiveState.gpsTimes |
Optionales prozesslokales Ergebnis der GPS-Zeitauswertung; nur passende eigene aktive Fahrt, nicht serialisiert. |
TrackingLiveState.sessionRevision |
Bindet den RAM-Livezustand an die Zugangsgeneration; gleicher numerischer Status eines anderen Kontos darf ihn nicht übernehmen. Kein Fahrtcache-Feld und keine Zugangsdaten. |
TrackingLiveState.gpsGeometrySource |
Optional TRIP_POLYLINE beziehungsweise ROAD_MODEL aus der aktuellen Zeitauswertung, sonst frischer aktiver Railprojektion der Stationsengine. Kein Prefetchmarker oder ETA-Beleg; nicht persistiert. |
RideRecognitionState.authSessionRevision |
Bindet prozesslokale Fahrtvorschläge an dieselbe aktuelle Zugangsgeneration; das Check-in-ViewModel verwirft fremde oder frühere Revisionen. Nicht persistiert. |
GpsStopTime |
Besuchsschlüssel, Station-ID, Plan-Ankunft/-Abfahrt und optionale lokale Ankunft/-Abfahrt; Flags unterscheiden beobachtet und geschätzt. |
GpsJourneyTimes |
Liste besuchsbezogener GPS-Zeiten, Unterstützungs-Fixzeitpunkt und Gültigkeitsende; spätestens nach 30 Sekunden unbrauchbar. Geeignete Folgefixes ohne neue Prognose dürfen das Ende nicht verlängern. |
JourneyTime |
Aufgelöster Anzeigezeitpunkt mit JourneyTimeSource, Planzeit, Quellenlabel und optionaler positiver/negativer Abweichung in Minuten. |
JourneyTimeResolver prüft je Ereignis gültige GPS-Zeit, manuelle Zeit, parsebare API-Echtzeit und Planzeit in dieser Reihenfolge. GPS-Matching bevorzugt UUID und verlangt in beiden Zuordnungswegen das exakt gleiche nullable Planpaar einschließlich Markeranwesenheit. Ohne UUID sind zusätzlich gleiche vorhandene Station-ID, mindestens ein parsebarer Planmarker und ein eindeutiger Treffer erforderlich. Diese strengere GPS-Zuordnung ersetzt nicht den allgemeinen API-Helfer matchesStopover. Die GPS-Erweiterung führt keine neuen Retrofit-, Room- oder Status-PUT-Felder ein. Prognosen und Standortbeobachtungen bleiben RAM-Zustand; Details stehen unter GPS-Zeiten.
Die Modelle in SevModels.kt ergänzen die öffentliche Bahnhofskarte, ohne den Träwelling-Vertrag zu verändern:
| Modell | Bedeutung |
|---|---|
SevMap |
Stations-Slug, öffentliche Quellen-URL, Bahnhofkoordinate zur Plausibilitätsprüfung, SEV-Punkte, Weg-/Maßnahmenhinweise und Abrufzeit. |
SevPoint |
Öffentliche sev.*-Kennung, physische Koordinate, optionale Richtungsbezeichnung und Quellen-Bearbeitungsstand. |
SevStopInfo |
Quellenlink, Bezeichnung, Wegbeschreibung und optionales eindeutig zugeordnetes Koordinatenpaar für einen Haltbesuch; bei Rückfall zusätzlich ein Grund. |
SevStopInfo.hasCoordinates unterscheidet einen nutzbaren Punkt von einem reinen Hinweis. GeoJSON liefert Längengrad vor Breitengrad; das App-Modell hält beide benannt. Der Bearbeitungsstand eines Punkts ersetzt kein Gültigkeitsintervall. Ein allgemeiner Kartenmittelpunkt wird niemals als SEV-Punkt ausgegeben.
Die Zuordnung bleibt lokal. API-Stations-ID, Stopover-UUID, Plan-/Echtzeit und Check-in-Werte behalten ihre Identität und Bedeutung; es werden keine SEV-Koordinaten per Status-PUT übertragen. Die Auflösungsregeln und Lebensdauer stehen unter SEV-Ersatzhaltestellen.
RoadRouteModels.kt definiert gemeinsame Geometrieformen; TransitRouteModels.kt ergänzt die native Fahrtbindung. Quellen bleiben ausdrücklich getrennt von Gerätefixes:
| Modell | Bedeutung und Lebensdauer |
|---|---|
RoutePoint |
Benannter Breitengrad/Längengrad eines öffentlichen Haltpunkts oder Geometrieknotens; kein Gerätefix für Routinganfragen. |
RouteGeometry / Alias RoadRouteGeometry
|
Geordnete Endpunkte from/to, Kandidatenlinienzüge in alternatives und fetchedAtMillis; keine Provider-ETA. Road erlaubt bis zu drei, native Abschnitte genau einen Kandidaten. |
GpsGeometrySource |
ROAD_MODEL für approximative SEV-Straßenwege beziehungsweise TRIP_POLYLINE für einen nativen Träwelling-Streckenverlauf. |
GpsSegmentGeometry |
Bindet geometry über fromKey, toKey und source an zwei konkrete geordnete Haltbesuche, auch bei wiederholten Stationsbesuchen. Bestehende Road-Aufrufer behalten ROAD_MODEL als Standard. |
TransitRouteVisit |
Besuchsschlüssel, Stations-ID/-UUID, nullable Planpaar, öffentliche Stationskoordinate und Ausfallstatus; keine Echtzeit, Gleise oder Geräteposition. |
TransitRouteRequest |
Status-ID, Tripidentität und vollständige geordnete Check-in-Besuchsfolge inklusive gestrichener Zwischenbesuche. |
TransitRouteGeometry |
Derselbe Request, eindeutig gebundene verwendbare Abschnitte und Abrufzeit; native Form höchstens 15 Minuten verwendbar. |
Die lokale GPS-Zeitauswertung projiziert auf geeignete Formen. Native Abschnitte können zusätzlich gerichtete Trackinghilfen stützen; physische Ankunft und Zielabschluss bleiben Stationsbelege. Das Pkw-Modell ist keine offizielle SEV-Busroute; der native Backend-Linienzug ist ebenfalls keine garantierte aktuelle Gleisführung. Weder eine Routingdauer noch ein Netzabschluss erzeugt eine GPS-Istzeit. Gerätepositionen werden nicht an diese Quellen gesendet. Geometrien bleiben in Service-/Repository-RAM und erweitern weder Room noch trip_tracking_state; ein Neustart übernimmt keine persistierte Geometrie. Ein tatsächlicher Formwechsel verwirft Zukunftsprognose und Bewegungsfenster, erhält aber bestätigte tatsächliche Ereignisse bei unveränderter Besuchs-/Haltbasis.
fun List<StopStation>.deduplicate(): List<StopStation>Dedupliziert aufeinanderfolgende Halte derselben Station mit gleichen geplanten Zeiten. Unterschiedliche Stationen oder unterschiedliche vorhandene Stopover-UUIDs bleiben auch bei identischen Zeiten erhalten. Bei doppelten Einträgen wird eine vorhandene Gleisinformation beziehungsweise der kürzere Stationsname bevorzugt.
fun StopoversResponse.allStopovers(): List<StopStation>Flacht die Map von tripId zu StopStation-Liste in eine flache Liste.
Die API unterstützt manuelle Korrekturen von Abfahrts-/Ankunftszeiten:
-
manualDeparture: Manuell korrigierte Abfahrtszeit -
manualArrival: Manuell korrigierte Ankunftszeit
Diese werden im CheckInInfo-Modell gespeichert und beim Auflösen der Anzeige berücksichtigt. Eine frische eindeutige GPS-Zeit hat in der eigenen aktiven Begleitung Vorrang; außerhalb dieser Beobachtung bleiben manuelle Zeiten vor API-/Planzeit. Das Bearbeitungsformular wird ohne GPS-Werte initialisiert.
Bei einem Zielwechsel über UpdateStatusRequest wird zusätzlich zur neuen internen Station-ID deren geplante Ankunft als destinationArrivalPlanned gesendet. Der Upstream-Request verlangt beide Felder gemeinsam. Für reine Text-, Sichtbarkeits- oder Zeitänderungen werden sie weggelassen.
Der Reisegrund wird beim Check-in im Feld business übertragen. Das Android-Modell verwendet dafür TravelReason mit den API-Werten 0 (Privat), 1 (Geschäftlich) und 2 (Arbeitsweg). Standard ist TravelReason.PRIVATE.