Projekt

Allgemein

Profil

Feature #77

Von Julius Haman vor 6 Tagen aktualisiert

Ziel: Auf der Trainingsdetailseite aus der Verteilung der Pulszonen zwei einfache Angaben ableiten - **Belastung** (wie anstrengend war das Training) und **Schwerpunkt** (wofür war es hauptsächlich gut). Bewusst nicht "Nutzen des Trainings": Die Zonenverteilung beschreibt die Dosis, nicht die Wirkung, denn die hängt von Ziel, Trainingszustand und Erholung ab. 

 ### Ausgangslage 

 * `TrainingDetailViewModel.BuildZonePie` berechnet die Zeit je Pulszone (`zoneSeconds[5]`, zeitgewichtet über die Zeitabstände der Trainingspunkte) bereits, nutzt sie aber nur für das Kreisdiagramm. 
 * Zonenmodell (`User.PulseZoneIndex`): 5 gleich breite Zonen mit 50/60/70/80/90/100 % von HFmax (Erholung, Leicht, Mittel, Hart, Maximum). HFmax historisiert über `Training.MaxPulseAtRecording`, Fallback auf den aktuellen Benutzer. 
 * `PulseZoneIndex` klemmt Werte unter 50 % auf Zone 1 - für die Belastung muss Puls unter 50 % HFmax separat behandelt werden (Gewicht 0). 
 * Im Benutzermodell gibt es keine FTP/Schwellenleistung (`StartingPower` ist nur der Startwert der pulsgesteuerten Regelung). Ein leistungsbasierter Wert ist daher nicht Teil dieses Tickets. 
 * `FitnessNote` ist bereits durch die Erholungspuls-Note belegt und wird nicht wiederverwendet. 

 ### Belastung (Edwards-TRIMP) 

 Belastung = Summe über alle Zonen von (Minuten in der Zone x Zonennummer). Erholung = 1, Leicht = 2, Mittel = 3, Hart = 4, Maximum = 5 Punkte pro Minute. Puls unter 50 % HFmax zählt 0. 

 * Ergebnis ist eine Zahl ohne Einheit, nur zum Vergleichen (240 ist etwa doppelt so belastend wie 120). 
 * Beispiel: 10 min Erholung + 30 min Leicht + 15 min Mittel + 5 min Hart = 10 + 60 + 45 + 20 = 135 Punkte. 
 * Lange lockere Trainings bekommen dadurch automatisch viele Punkte (2 h Leicht = 240 = 1 h Hart). Das ist gewollt. 
 * Optional später: Einordnung relativ zur eigenen Historie ("leichter / üblich / härter als die letzten 10 Trainings"), statt absoluter Schwellen. 

 ### Schwerpunkt (regelbasiertes Label) 

 Wird ausschließlich aus der prozentualen Zeitverteilung berechnet, unabhängig von der Belastung. Regeln der Reihe nach geprüft: 

 | Bedingung (Zeitanteil) | Label | 
 |---|---| 
 | Dauer unter 10 min | kein Label | 
 | Zone 1 >= 60 % | Erholung | 
 | Zone 5 >= 10 % oder (Zone 4 + Zone 5) >= 30 % mit deutlichem Wechsel zwischen hoch und niedrig | Intervall / hochintensiv | 
 | Zone 4 >= 40 % | Schwelle / Tempo | 
 | Zone 2 + Zone 3 >= 70 % | Grundlagenausdauer | 
 | sonst | gemischt | 

 * Schwellenwerte als benannte Konstanten, damit sie sich leicht nachjustieren lassen. Es sind Heuristiken. 
 * Grenze: Der Puls hinkt bei kurzen Intervallen hinterher, "Intervall" wird dort eher zu selten erkannt. 
 * Die Zonen laufen nach % von HFmax; Zone 3 entspricht damit etwa dem klassischen GA1, Zone 4 dem Schwellenbereich. Die 3-Zonen-Aufteilung nach Seiler passt nicht 1:1. 

 ### Umsetzung 

 1. **Rechenklasse** in ErControl.Core (z. B. `TrainingLoadCalculator`): liefert Sekunden je Zone, Belastung und Schwerpunkt. `BuildZonePie` nutzt sie ebenfalls (Logik nur an einer Stelle), gut testbar. 
 2. **Trainingsdetailseite**: Kachel "Belastung" und Zeile "Schwerpunkt" beim Kreisdiagramm. Texte in die Resx unter `TrainingSessionsViewModel.*`. Wird beim Öffnen berechnet, keine Migration nötig. 

 ### Umgesetzt 

 * **Rechenklasse `TrainingLoadCalculator`** (ErControl.Core/Services): liefert Sekunden je Pulszone, Belastung (Edwards-TRIMP) und Schwerpunkt. Die Schwellenwerte der Schwerpunkt-Regeln stehen als Konstanten dort. Der "deutliche Wechsel zwischen hoch und niedrig" ist als Zone 4+5 >= 30 % **und** Zone 1+2 >= 20 % umgesetzt. Mit Unit-Tests (`TrainingLoadCalculatorTests`) 
 * **Trainingsdetailseite**: Belastung und Schwerpunkt stehen unterhalb der Legende in der Pulszonen-Karte (keine Kachel). Die Zeile Schwerpunkt entfällt, wenn das Training keinen bekommt (unter 10 min). Texte in der Resx (Deutsch/Englisch) unter `TrainingSessionsViewModel.*`; berechnet beim Öffnen der Seite, keine Migration 
 * **Kreisdiagramm** nutzt dieselbe Auswertung. Änderung: Trainingspunkte ohne Pulsmessung (Puls 0) zählen nicht mehr als Zone Erholung - ein Training ohne Brustgurt zeigt kein Pulszonen-Diagramm mehr statt 100 % Erholung 

 ### Später (nicht Teil dieses Tickets) 

 * Spalten "Belastung" und "Schwerpunkt" in der Trainingsübersicht (Standard ausgeblendet). Dafür Wert speichern (neue Spalte, Migration, einmaliger Backfill wie bei `MaxPulseAtRecording`), sonst müsste das Grid für jedes Training alle `TrainingPoints` laden. 
 * Wochensumme der Belastung. 
 * Leistungsbasierte Belastung (TSS-artig), sobald ein FTP-Feld im Benutzer existiert. 
 * Banister-TRIMP mit Ruhepuls (`RestingPulseAtRecording` ist bereits historisiert) als Alternative zu Edwards. 

 ### Offene Entscheidungen 

 * Grid-Spalten: umgesetzt wird das in #78. Umsetzung zunächst nur Detailseite (Schritt 1 und 2) oder gleich mit Grid-Spalten? 
 * Schwellenwerte und Bezeichnungen der Labels bestätigen.

Zurück