Realistische Demo-Daten (12 Wochen, 49 Sessions) + Roadmap + README-Verweis
Image bauen und in die Registry schieben / build (push) Successful in 8s
Image bauen und in die Registry schieben / build (push) Successful in 8s
- demo-data.json neu generiert: 12 Wochen Push/Pull/Legs mit Plateau- Phasen, Deload-Woche, ausgelassenen Trainingstagen, Reps-Streuung, nicht abgehakten Saetzen und Notizen; dazu 12 Laeufe und 5 Fahrten - ROADMAP.md: interne Roadmap in 4 Phasen (Inhalt in der App bearbeiten, herstellerunabhaengige Info-Texte mit Bildern/Schritten, Plugin-System mit Ollama/Cloud-Coach, Wearable-Import, CLI-Harness, Volumen/1RM, PWA, a11y) - README: Roadmap-Sektion - test_auth: erwartete Demo-Zaehler dynamisch aus der Datei berechnet - Tests: 158 Checks gruen (39+18+43+32+26)
This commit is contained in:
1 parent
25ed34e80e
commit
e91cd30479
4 files changed
+4145
-1398
No files matched your search
@@ -118,6 +118,13 @@ Fürs lokale Entwickeln bleibt `docker-compose.yml` mit `build: .` bestehen.
|
|||||||
- Datenverzeichnis frei wählbar (`TRAINING_STATE_DIR`)
|
- Datenverzeichnis frei wählbar (`TRAINING_STATE_DIR`)
|
||||||
- Keine externen Dienste, keine Telemetrie, keine Analyse deiner Daten
|
- Keine externen Dienste, keine Telemetrie, keine Analyse deiner Daten
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
Die geplante Weiterentwicklung (Übungskatalog in der App bearbeiten,
|
||||||
|
herstellerunabhängige Info-Texte mit Bildern/Schritten, Plugin-System mit
|
||||||
|
Ollama/Cloud-Coach, Wearable-Import, CLI-Harness) steht in
|
||||||
|
[ROADMAP.md](ROADMAP.md).
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|
||||||
AGPL-3.0. Siehe `LICENSE`.
|
AGPL-3.0. Siehe `LICENSE`.
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
# REKORD - Roadmap
|
||||||
|
|
||||||
|
Interne Roadmap. Prioritaeten: P1 = naechste Iterationen, P2 = bald, P3 =
|
||||||
|
spaeter. Status: Idee / Geplant / In Arbeit / Fertig.
|
||||||
|
|
||||||
|
Zielrichtung: breiteres Feature-Set, einfachere Erweiterung des
|
||||||
|
Uebungs-/Maschinen-Katalogs, herstellerunabhaengige Inhalte, sanfte
|
||||||
|
Integration (Plugins, lokale Modelle) statt Monolithen. Architektur-Leitplanken
|
||||||
|
bleiben: Config-getrieben, leichtgewichtig, Daten lokal, keine Telemetrie.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1 - Inhalte & Bedienbarkeit
|
||||||
|
|
||||||
|
| Prioritaet | Thema | Status | Notiz |
|
||||||
|
|---|---|---|---|
|
||||||
|
| P1 | **Uebungen/Maschinen in der App bearbeiten** | Idee | Heute aendert man `config.json` von Hand. Ziel: Uebungen, Tage, Maschinen und Info-Texte direkt in der App anlegen/bearbeiten (persistiert neben der Config), damit ein Nicht-Programmierer den Plan ohne Dateieditor pflegt. |
|
||||||
|
| P1 | **Info-Texte erweitern** | Idee | Neben dem Freitext: optionale Bilder (URL oder Upload), Schritte als Checkliste, ein "wertvoller Link" (z.B. zu einer neutralen Anatomie-/Form-Quelle). Wichtig: **nicht hersteller-spezifisch** - die Technogym-Referenzen aus der Config sind nur Platzhalter; der Katalog soll generisch sein. |
|
||||||
|
| P1 | **Uebungsvarianten & Maschinen-Tags** | Idee | Eine Uebung mit mehreren Varianten (KH, Maschine, Kabel) und deren Verfuegbarkeit; beim Training schnell zwischen den Geräten wechseln, die im Studio stehen. |
|
||||||
|
| P1 | **Set-Vorlagen pro Uebung** | Idee | Im Plan definierte Satz-Vorlagen (Anzahl, Ziel-Reps, Pause) als Startpunkt - schon teilweise da ("Plan-Gewichte vorbefuellen"), aber noch nicht pro Satz steuerbar. |
|
||||||
|
| P2 | **Gewichtseinheiten pro Uebung** | Idee | z.B. Bodyweight (kein kg), Bandwiderstand, Maschinen-Gewicht inkl. Hebel - neben dem globalen kg/lb. |
|
||||||
|
|
||||||
|
## Phase 2 - Integration & Automation
|
||||||
|
|
||||||
|
| Prioritaet | Thema | Status | Notiz |
|
||||||
|
|---|---|---|---|
|
||||||
|
| P1 | **Plugin-System** | Idee | Kleine Plugins (JS im Browser und/oder Server-Skripte) mit klaren Hooks: nach Session-Speichern, beim Export, beim Anzeigen einer Uebung, beim Abschluss. Registrierung in der Config. So bleiben Erweiterungen ausserhalb des Kerns. |
|
||||||
|
| P1 | **Ollama/Cloud-Coach** | Idee | Plugin, das Form-Tipps/Zusammenfassungen per API generiert - **local-first** (Ollama), Cloud nur mit expliziter Zustimmung und eigener Wahl des Anbieters/Keys. Keine Daten gehen ohne Erlaubnis nach aussen. |
|
||||||
|
| P2 | **Webhooks bei Events** | Idee | Nach "Session abgeschlossen", "Benutzer angelegt" etc. - fuer eigene Automation (Backup, Benachrichtigungen, Hausautomation). |
|
||||||
|
| P2 | **API-Keys pro Benutzer** | Idee | Neben den Login-Tokens: eigene read/write Keys pro Benutzer fuer Scripts, ohne das Konto freizugeben. |
|
||||||
|
| P2 | **Effizienter Harness (CLI)** | Idee | Kleines Kommandozeilen-/Script-Interface (`rekord`-CLI oder Python-Client) gegen die API: Workouts eintragen/abrufen, Statistiken ziehen - fuer Cron-Jobs, TUI, Automation. |
|
||||||
|
| P3 | **Wearable-Import** | Idee | Garmin/Apple/Fitbit/Samsung-Health-Exporte (CSV/JSON) als Plugin importieren; Puls etc. an Cardio-Sessions anhaengen. Kein Live-Sync in Phase 1. |
|
||||||
|
|
||||||
|
## Phase 3 - Analyse & UX
|
||||||
|
|
||||||
|
| Prioritaet | Thema | Status | Notiz |
|
||||||
|
|---|---|---|---|
|
||||||
|
| P1 | **Volumen & 1RM-Schaetzung** | Idee | Trainingsvolumen (kg × Reps) je Uebung/Tag, 1RM-Schaetzung (z.B. Epley) als zusaetzliche Statistik-Kennzahl. |
|
||||||
|
| P2 | **Wochenvergleich & Trends** | Idee | Statistik ueber Wochen hinweg (nicht nur "Differenz zur ersten Messung"): gleitende Durchschnitte, PR-Markierungen, Verlaufs-Graph (ohne externe Chart-Lib, wenn moeglich). |
|
||||||
|
| P2 | **PWA / Offline** | Idee | Als installierbare App (Service Worker, App-Icon) - nicht kritisch, da serverseitig, aber schoener auf dem Handy. |
|
||||||
|
| P2 | **Sessions am selben Tag bessern** | Idee | Mehrere Sessions pro Tag sind drin; UX fuer "morgens laufen, abends Gym" vereinfachen (schneller Aktivitaets-/Tag-Wechsel). |
|
||||||
|
|
||||||
|
## Phase 4 - Qualitaet & Betrieb
|
||||||
|
|
||||||
|
| Prioritaet | Thema | Status | Notiz |
|
||||||
|
|---|---|---|---|
|
||||||
|
| P2 | **Barrierefreiheit (a11y)** | Idee | Tastatur-Navigation, Kontraste, Screenreader-Labels systematisch pruefen. |
|
||||||
|
| P2 | **Backup/Retention** | Idee | Automatische Export-Erinnerung oder in-App-Backup-Timer; Dokumentation fuer Rotation. |
|
||||||
|
| P3 | **Mehrsprachigkeit ausbauen** | Idee | i18n-Architektur ist da (de/en); weitere Sprachen als Community-Beitrag. |
|
||||||
|
| P3 | **Mehr-Server / Replikation** | Idee | Datenhaltung bleibt SQLite; falls noetig: Import/Export als Synchronisationsweg dokumentieren statt Live-Replikation. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Konzepte in einem Satz
|
||||||
|
|
||||||
|
- **Content in der App statt in der Datei** - der Uebungskatalog wird Teil des
|
||||||
|
Produkts, nicht des Repos.
|
||||||
|
- **Herstellerunabhaengig** - Maschinen-Referenzen sind Hinweise, keine
|
||||||
|
Abhaengigkeit; Links/Medien sind optional und austauschbar.
|
||||||
|
- **Plugins statt Kern-Zerfaserung** - alles, was nicht jeder braucht
|
||||||
|
(Coach, Sync, Import), wird Plugin.
|
||||||
|
- **Local-first** - lokale Modelle (Ollama) sind der Default, Cloud nur auf
|
||||||
|
ausdruecklichen Wunsch.
|
||||||
+4060
-1391
File diff suppressed because it is too large.
Load diff
+15
-7
@@ -147,7 +147,14 @@ def main():
|
|||||||
st, imp = req("POST", "/api/import",
|
st, imp = req("POST", "/api/import",
|
||||||
{"format": "trainingsplan-export-v2",
|
{"format": "trainingsplan-export-v2",
|
||||||
"data": json.dumps(demo)}, token=token_b)
|
"data": json.dumps(demo)}, token=token_b)
|
||||||
check("Demo-Import in Berta", st == 200 and imp["created"] == 22, imp)
|
demo_sessions = len(demo["sessions"])
|
||||||
|
demo_gym = sum(1 for s in demo["sessions"] if s["activity"] == "gym")
|
||||||
|
demo_ex_dates = len({s["date"] for s in demo["sessions"]
|
||||||
|
for e in s.get("entries", [])
|
||||||
|
if e.get("exercise") == "Push/Bankdrücken KH (je Hand)"
|
||||||
|
and e.get("weight")})
|
||||||
|
check("Demo-Import in Berta", st == 200
|
||||||
|
and imp["created"] == demo_sessions, imp)
|
||||||
|
|
||||||
# --- Berta trainiert selbst -------------------------------------------
|
# --- Berta trainiert selbst -------------------------------------------
|
||||||
st, sess_b = req("POST", "/api/sessions",
|
st, sess_b = req("POST", "/api/sessions",
|
||||||
@@ -161,8 +168,8 @@ def main():
|
|||||||
|
|
||||||
st, done_b = req("GET", "/api/sessions?status=done", token=token_b)
|
st, done_b = req("GET", "/api/sessions?status=done", token=token_b)
|
||||||
st, done_a = req("GET", "/api/sessions?status=done", token=token_a)
|
st, done_a = req("GET", "/api/sessions?status=done", token=token_a)
|
||||||
check("Isolation: B sieht 23 eigene Sessions (22 Demo + 1)",
|
check("Isolation: B sieht alle Demo-Sessions + 1 eigene",
|
||||||
len(done_b) == 23, len(done_b))
|
len(done_b) == demo_sessions + 1, len(done_b))
|
||||||
check("Isolation: A sieht seine 1 Session (nicht B)",
|
check("Isolation: A sieht seine 1 Session (nicht B)",
|
||||||
len(done_a) == 1, len(done_a))
|
len(done_a) == 1, len(done_a))
|
||||||
|
|
||||||
@@ -191,22 +198,23 @@ def main():
|
|||||||
|
|
||||||
# --- Workouts-API -----------------------------------------------------
|
# --- Workouts-API -----------------------------------------------------
|
||||||
st, wo_b = req("GET", "/api/workouts", token=token_b)
|
st, wo_b = req("GET", "/api/workouts", token=token_b)
|
||||||
check("Workouts-API: 23 Workouts", st == 200 and len(wo_b) == 23, len(wo_b))
|
check("Workouts-API: alle Demo-Workouts + eigene",
|
||||||
|
st == 200 and len(wo_b) == demo_sessions + 1, len(wo_b))
|
||||||
w = wo_b[0] # neueste Session = Berta heute
|
w = wo_b[0] # neueste Session = Berta heute
|
||||||
check("Workouts-API: Shape", w["activity_name"] == "Gym (Kraft)"
|
check("Workouts-API: Shape", w["activity_name"] == "Gym (Kraft)"
|
||||||
and w["day"] == "Push" and w["duration_min"] is not None
|
and w["day"] == "Push" and w["duration_min"] is not None
|
||||||
and w["exercises"][0]["name"] == "Bankdrücken KH (je Hand)"
|
and w["exercises"][0]["name"] == "Bankdrücken KH (je Hand)"
|
||||||
and w["exercises"][0]["sets"][0]["weight"] == "40", w)
|
and w["exercises"][0]["sets"][0]["weight"] == "40", w)
|
||||||
st, wo_b = req("GET", "/api/workouts?activity=gym", token=token_b)
|
st, wo_b = req("GET", "/api/workouts?activity=gym", token=token_b)
|
||||||
check("Workouts nach Import (gym): 15 Demo + 1 eigene", len(wo_b) == 16,
|
check("Workouts nach Import (gym): Demo + 1 eigene",
|
||||||
len(wo_b))
|
len(wo_b) == demo_gym + 1, len(wo_b))
|
||||||
st, wo_a = req("GET", "/api/workouts", token=token_a)
|
st, wo_a = req("GET", "/api/workouts", token=token_a)
|
||||||
check("A bleibt unberuehrt vom Import", len(wo_a) == 1, len(wo_a))
|
check("A bleibt unberuehrt vom Import", len(wo_a) == 1, len(wo_a))
|
||||||
st, stats = req("GET", "/api/stats?exercise="
|
st, stats = req("GET", "/api/stats?exercise="
|
||||||
+ urllib.parse.quote("Push/Bankdrücken KH (je Hand)"),
|
+ urllib.parse.quote("Push/Bankdrücken KH (je Hand)"),
|
||||||
token=token_b)
|
token=token_b)
|
||||||
check("Statistik aus Demo-Daten + eigene Session",
|
check("Statistik aus Demo-Daten + eigene Session",
|
||||||
st == 200 and len(stats) == 6
|
st == 200 and len(stats) == demo_ex_dates + 1
|
||||||
and stats[0]["weight"] < stats[-1]["weight"]
|
and stats[0]["weight"] < stats[-1]["weight"]
|
||||||
and stats[-1]["weight"] == 40, stats)
|
and stats[-1]["weight"] == 40, stats)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user