Feature #163
Von Julius Haman vor 19 Tagen aktualisiert
## Idee
Trainings sollen aus ErControl direkt zu Strava hochgeladen werden können, optional automatisch nach Trainingsende.
## Recherche-Ergebnis
- Upload über `POST /api/v3/uploads` (multipart, Scope `activity:write`), Formate FIT/TCX/GPX. Verarbeitung ist asynchron: danach `GET /uploads/{id}` pollen, bis `activity_id` gesetzt ist.
- Die vorhandenen Exporte `TrainingExportService.ExportFit` und `ExportTcx` liefern passende Dateien.
- OAuth: Browser-Login, Redirect auf `http://localhost:<port>/…` mit lokalem Listener, Access-Token läuft nach 6 h ab, Refresh-Token wird gespeichert.
- Strava-Apps starten im Single Player Mode (nur das eigene Konto), per Dashboard bis 10 Athleten, darüber nur nach Review ohne Garantie.
- Eine Strava-API-App kann nur mit Strava-Abo angelegt werden (am 2026-10-13 im Dashboard bestätigt: "nur für Mitglieder").
## Entscheidung
Variante A: Jeder Nutzer legt seine eigene Strava-API-App an und trägt Client-ID und Client-Secret in ErControl ein. Damit gibt es keine zentrale App, keine Athleten-Grenze und kein Review. Voraussetzung ist ein Strava-Abo des Nutzers, das in Doku und Dialog klar stehen muss.
## Umsetzung (Skizze)
- `StravaUploadService`: OAuth mit lokalem HttpListener, Token-Ablage verschlüsselt (DPAPI), Token-Refresh, Upload, Polling, Fehlerbehandlung (Duplikat, ungültige Datei, Netzfehler).
- Einstellungen: Client-ID/Secret, Verbinden/Trennen, Schalter für automatischen Upload.
- Trainingsdetail: Button "Zu Strava hochladen" (zuerst manuell, Automatik danach), Status/Fehler anzeigen, Strava-Link zur Aktivität speichern, um Doppel-Uploads zu vermeiden (`external_id`).
- Tests gegen einen gefälschten `HttpMessageHandler`, ein echter Test erst mit Abo.
- Dokumentation (ercontrolDocs, deutsch und englisch): Schritt-für-Schritt, wie man die Strava-App anlegt.
## Offen
- Der Ende-zu-Ende-Test braucht ein Strava-Abo (ein Monat reicht), erst abschließen, wenn der Code fertig ist.
- Datenschutz-Hinweis in der Doku: Trainingsdaten gehen an Strava.
## Konzept: Integration in die Oberfläche
| Ort | Heute | Neu |
|---|---|---|
| Export-Menü der Trainingsliste (`TrainingSessionsView`) | TCX, FIT, HRM, CSV | Eintrag "Zu Strava hochladen" für die markierten Trainings |
| Trainingsdetail (`TrainingDetailView`) | gleiche Export-Einträge für ein Training | derselbe Eintrag, plus Link "Auf Strava ansehen" |
| Ende eines Trainings (`FreiesTrainingWindowViewModel`) | Speichern | optional: nach dem Speichern automatisch hochladen |
| Einstellungen | – | Abschnitt "Strava": Client-ID/Secret, Verbinden/Trennen, Schalter Auto-Upload |
- `StravaUploadService` in `ErControl.Core/Services` neben `TrainingExportService`: exportiert das Training als FIT in eine Temp-Datei, lädt hoch, pollt `GET /uploads/{id}`.
- Auto-Upload läuft als Hintergrundaufgabe und blockiert das Beenden des Trainings nicht. Bei Fehlern (offline, Token abgelaufen) bleibt das Training als "nicht hochgeladen" markiert und kann manuell nachgeholt werden. Meldung als Hinweis, nicht als Fehlerdialog.
- Mehrfachauswahl in der Liste: nacheinander hochladen, mit Fortschritt und Abschlussmeldung ("3 von 4 erfolgreich").
## Konzept: Upload-Status
Zwei neue Felder am Training (EF-Migration, nullable, Bestandstrainings bleiben leer):
- `StravaActivityId` (long?): gesetzt, sobald Strava die Aktivität angelegt hat
- `StravaUploadedAt` (DateTime?): Zeitpunkt des Uploads
Anzeige:
- Trainingsliste: schmale Spalte mit Symbol (gefüllt = hochgeladen, Tooltip mit Datum). Filter "noch nicht hochgeladen" erleichtert das Nachholen.
- Trainingsdetail: Link auf `https://www.strava.com/activities/<id>`.
- Erneuter Upload eines bereits hochgeladenen Trainings: Rückfrage ("bereits am … hochgeladen, erneut?"). Der Upload gibt `external_id = training.Id` mit, damit Strava Duplikate selbst ablehnt.
Grenze: ErControl weiß nur, was es selbst hochgeladen hat. Wurde die Aktivität in Strava danach gelöscht, steht hier weiter "hochgeladen". Eine Prüfung per Abruf wäre den zusätzlichen API-Verkehr nicht wert.