Feature #78
Von Julius Haman vor 6 Tagen aktualisiert
Ziel: In der Trainingsübersicht die Spalten **Belastung** und **Schwerpunkt** anbieten (Standard ausgeblendet, wie andere optionale Spalten über `TrainingStatDefinitions` / `DefaultVisibleInGrid = false`). Aufbauend auf #77, dort sind Berechnung (Edwards-TRIMP) und Regeln für den Schwerpunkt beschrieben. ### Warum speichern Belastung und Schwerpunkt brauchen die Zeit je Pulszone und damit alle `TrainingPoints` eines Trainings. Das Grid müsste sonst für jedes Training alle Trainingspunkte laden, was bei vielen Trainings zu langsam ist. Deshalb wird der Wert beim Aufzeichnen einmal berechnet und am Training gespeichert (wie `AveragePulse`, `MaxPulse` usw.). ### Umsetzung * **Neue Spalten in `Training`** (z. B. `TrainingLoad` als Zahl, `TrainingFocus` als Kennung des Schwerpunkts, nullable) in `Training.Persistant.cs`. * **Migration** (nächste Version) mit den neuen Spalten. * **Neue Trainings**: Berechnung mit dem `TrainingLoadCalculator` aus #77 beim Speichern des Trainings, dort wo bereits `MaxPulseAtRecording` gesetzt wird (`FreiesTrainingWindowViewModel`), damit die Werte mit den zum Aufzeichnungszeitpunkt gültigen Zonengrenzen entstehen. * **Einmaliger Backfill** für Bestandstrainings, wie bei `MaxPulseAtRecording` in der Migration 3.1.1. Dort war es ein SQL-UPDATE; für die Belastung ist die Zeit je Zone aus den Zeitabständen der Punkte nötig, daher entweder SQL mit Fensterfunktion (`LAG`) oder ein einmaliger Lauf in C# beim ersten Start nach dem Update. Trainings ohne `MaxPulseAtRecording` (Zonengrenzen nicht bestimmbar) bleiben leer bzw. bekommen den Wert des Benutzers wie beim bisherigen Nachfüllen in `UsersViewModel.SaveAsync`. * **Grid**: Spalten "Belastung" und "Schwerpunkt" in `TrainingStatDefinitions` (Standard ausgeblendet), Beschriftungen in der Resx unter `TrainingSessionsViewModel.*`. Leere Werte (kein Maximalpuls, zu kurzes Training) als leere Zelle. * **Detailseite (#77)** liest bei vorhandenem gespeicherten Wert diesen, sonst berechnet sie ihn weiterhin selbst, damit beides dasselbe Ergebnis zeigt. ### Umgesetzt * **Gespeicherte Werte**: `Training.TrainingLoad` (Zahl, eine Nachkommastelle) und `Training.TrainingFocus` (Enum `TrainingFocus`, in der Datenbank als Enum-Name gespeichert, `NULL` = kein Schwerpunkt). Das Enum liegt jetzt in ErControl.Model. Migration `Version3120`, Datenbankversion 3.1.2.0 (3.1.1 ist veröffentlicht, deshalb keine Faltung in die Migration 3.1.1) * **Neue Trainings**: `TrainingLoadCalculator.Update` berechnet beim Speichern (FreiesTrainingWindowViewModel) mit dem historisierten Maximalpuls, Fallback auf den aktuellen Benutzer * **Bestandstrainings**: einmaliger Nachlauf `TrainingLoadBackfillService` beim nächsten Programmstart (C#, damit die Regeln aus #77 nicht in SQL gedoppelt werden; Prefs-Merker `TrainingLoadBackfillDone`, läuft wie der Lat/Lon-Backfill auf dem Threadpool im Splash). Liest ohne Change Tracking und schreibt per `ExecuteUpdate`, weil der Context mit der angemeldeten Sitzung geteilt wird. Fallback für Trainings ohne `MaxPulseAtRecording`: Maximalpuls des Benutzers, sonst der berechnete Wert (wie die Detailseite). Trainings ohne Pulsdaten bleiben leer * **Trainingsübersicht**: Spalten "Belastung" und "Schwerpunkt" (Standard ausgeblendet, über die Spaltenauswahl wählbar, vor der Bemerkung). Die Bezeichnung des Schwerpunkts wird erst bei der Anzeige in der aktuellen Sprache aufgelöst (`TrainingFocusConverter`) * **Detailseite (#77)** zeigt bei vorhandenem gespeicherten Wert diesen, sonst berechnet sie ihn selbst * Tests: Migration von Grund auf und Backfill gegen eine echte SQLite-Datenbank (`TrainingLoadBackfillTests`) sowie `TrainingLoadCalculator.Update` ### Entscheidungen * Backfill per C#-Lauf beim Start statt per SQL in der Migration * Schwerpunkt als Kennung (Enum-Name) gespeichert * Nur Belastung und Schwerpunkt gespeichert, nicht die Sekunden je Zone. Ändern sich später die Schwellenwerte der Schwerpunkt-Regeln, müssen die gespeicherten Werte neu berechnet werden (dafür den Merker zurücksetzen und `TrainingLoad` leeren) ### Zu beachten * Ändern sich später die Schwellenwerte für den Schwerpunkt, müssen die gespeicherten Werte neu berechnet werden (erneuter Backfill oder Neuberechnung beim Start). Alternativ nur die Belastung speichern und den Schwerpunkt aus gespeicherten Zonenzeiten ableiten. * Wird der Maximalpuls eines Benutzers nachträglich geändert, ändern sich die gespeicherten Werte der Trainings nicht (Historisierung wie bei den Pulszonen). ### Offene Entscheidungen * Backfill per SQL in der Migration oder per C#-Lauf beim Start? * Schwerpunkt als Text oder als Kennung/Enum speichern (Übersetzung erst bei der Anzeige)? Empfehlung: Kennung, damit Sprache und Bezeichnungen frei änderbar bleiben. * Alternativ Sekunden je Zone (5 Spalten) speichern und Belastung und Schwerpunkt daraus ableiten: flexibler bei späteren Änderungen der Regeln, aber mehr Spalten.