# API-Vertrag boehmitools Training Stand: aus dem vollständigen boehmitools-Quellstand und den Plugins `trainingsplan`/`trainingstracker` 2.0 abgeleitet. Die Plugins werden durch die Host-App unter `/plugins/` gemountet. Alle Pfade unten sind relativ zur konfigurierbaren Serverbasis, standardmäßig `https://tools.d-razz.de/`. ## Authentifizierung Die Host-App schützt alle Pfade außer exakt `/api/health` optional mit HTTP Basic Auth. Ist `APP_PASSWORD` gesetzt, muss der Client `Authorization: Basic ` senden. Der Standard-Benutzername des Servers ist `admin`, sofern `APP_USERNAME` nicht gesetzt wurde. Fehlerantwort bei fehlender oder falscher Anmeldung: ```json {"detail":"Anmeldung erforderlich."} ``` Status: `401 Unauthorized`, Header `WWW-Authenticate: Basic realm="boehmitools"`. ## Host-Verbindungstest ### `GET /api/health` Ohne Authentifizierung erreichbar. Antwortfelder laut Backend: ```json { "status": "ok", "version": "1.0.0", "plugins": {"trainingsplan":"ok","trainingstracker":"ok"}, "tandoor_configured": false, "openai_configured": true, "authentication_enabled": true, "jobs_running": 0 } ``` Status: `200`. Die App prüft danach zusätzlich den authentifizierten Tracker-Health-Endpunkt. ## Trainingstracker Basis: `/plugins/trainingstracker` ### `GET /api/health` Liefert `ok`, Server-Datenpfade, Verfügbarkeit von OpenAI und die unterstützten Vertragsversionen: - `plan_schema` - `training_contract` - `tracker_schema` - `result_data` - `prompt` - `analysis_schema` - `normalization` Status: `200`, oder hostweit `401`. ### `GET /api/plans` Antwort: ```json { "plans": [{ "id": "phase-1.json", "filename": "phase-1.json", "name": "Phase 1", "title": "…", "subtitle": "…", "weeks": 8, "days": 6, "tracked": true, "start_date": "", "completed_sessions": 5, "source_changed": false, "plan_id": "phase-1", "published_revision": 1, "tracker_revision": 7 }], "selected": "phase-1.json" } ``` Status: `200`. ### `POST /api/plans/{plan_id}/select` Kein Request-Body erforderlich. Antwort: `{"ok":true,"selected":"phase-1.json"}`. Status: `200`; `404` mit `{"error":"Trainingsplan nicht gefunden"}`. ### `GET /api/plans/{plan_id}` Antwort: ```json { "plan": { /* normalisierte veröffentlichte Planfassung */ }, "tracker": { /* Tracker-Schema 7 */ }, "analysis_catalog": { /* Wochen- und Gesamtstatus */ }, "equivalence_guide": { /* Variantencluster und Fallback-FAQ */ }, "capabilities": {"openai": true} } ``` `plan` enthält ausschließlich vom Backend erzeugte Felder wie `source_file`, `source_hash`, `plan_id`, `schema_version`, `contract_version`, `compatibility`, `published_revision`, `published_at`, `name`, `title`, `subtitle`, `weeks`, `days`, `phases`, `phase_by_week`, `stages`, `exercise_catalog`, `front`, `training_format`. `tracker` enthält mindestens `version`, `revision`, `source_file`, `source_hash`, `profile`, `progressions`, `sessions`, `week_statuses`, `created_at`, `updated_at`; für die GET-Antwort werden zusätzlich `source_changed`, `analysis_cache` und `analysis_state` zusammengesetzt. Status: `200`; `404`; `400` bei nicht lesbarem Plan. ### `PUT /api/plans/{plan_id}/tracker` Vollständiges Speichern, primär für Kompatibilität. Request entweder direkt Trackerobjekt oder: ```json {"expected_revision":7,"tracker":{}} ``` Antwort: ```json { "ok": true, "updated_at": "…", "revision": 8, "analysis_catalog": {}, "analysis_state": {} } ``` Status: `200`; `400` für ungültige Nutzdaten; `409` bei Revisionskonflikt; `413` über 5 MiB. ### `PATCH /api/plans/{plan_id}/tracker/session` Der von der App verwendete Autosave-Vertrag: ```json { "expected_revision": 7, "profile": {"start_date":"","display_name":"","plan_notes":""}, "week_statuses": {"1":{"status":"open"}}, "session_key": "w01-d01", "session": { "status": "in_progress", "plan_id": "phase-1", "plan_revision": 1, "items": {} } } ``` `profile`, `week_statuses` und `session` sind jeweils optional. Ein Patch ersetzt die betreffende Session vollständig, nicht einzelne Felder innerhalb der Session. Erlaubte Sessionstatus: `planned`, `in_progress`, `stopped`, `completed`. Erlaubte Übungsstatus: `planned`, `completed`, `partial`, `skipped`. Bei `skipped` kann `skip_reason` gespeichert werden. Neue strukturierte Ergebnisse nutzen `result_data` Version 2: ```json { "version": 2, "mode": "reps|seconds|minutes", "laterality": "bilateral|unilateral", "sides_mode": "same|separate", "sets": 3, "weight_kg": 8.0, "values": [8,7,6], "left_values": [8,7,6], "right_values": [8,7,6] } ``` Das Backend akzeptiert maximal 20 Sätze, Werte von 0 bis 100000 und genau ein gemeinsames Gewicht. Alte Freitextfelder `result`, `note`, `progression` bleiben erhalten. Erfolg wie beim PUT. Konfliktantwort: ```json { "error":"Die Session wurde in einem anderen Tab oder Gerät geändert.", "code":"revision_conflict", "current_revision":8 } ``` Status: `409`. Die App darf dann nicht automatisch gegen Revision 8 überschreiben. ### `POST /api/plans/{plan_id}/analysis` Request Woche: ```json {"scope":"week","week":1} ``` Request Gesamtanalyse: ```json {"scope":"overall"} ``` Unveränderte, bereits aktuelle Analyse: `200` mit `cached:true`, `selection`, `analysis_state`, `analysis_cache`, `analysis_catalog`. Neuer Hintergrundjob: `202 Accepted` mit `cached:false`, `job`, `analysis_state`, `analysis_catalog`. Fehler: `400` bei fehlender OpenAI-Konfiguration, ungültiger Woche oder fehlenden Daten. `409` wenn bereits eine Analyse läuft. `404` bei unbekanntem Plan. ### `GET /api/plans/{plan_id}/analysis/status` Antwort enthält `analysis_state`, `analysis_cache`, `analysis_catalog`, `updated_at`. Die App pollt nur bei `analysis_state.status == "running"`. Status: `200`; `400`; `404`. ### `GET /api/plans/{plan_id}/tracker/export` Liefert die Sessiondatei als `application/json`; erzeugt bei Bedarf einen leeren Tracker. Status `200` oder `404`. ## Trainingsplan-Editor Basis: `/plugins/trainingsplan` ### `GET /api/plans` Antwort: `plans` mit `id`, `name`, `active`, `revision`, `published_revision`, `has_unpublished_changes`, plus `active`. ### `POST /api/plans` Request: ```json {"name":"Neue Phase","from":"optionale-plan-id","kind":"training"} ``` `kind` kann technisch auch `recipe` sein; die Android-App erstellt ausschließlich Trainingspläne. Antwort ist der öffentliche Wrapper mit `id`, `plan_id`, `name`, `revision`, `published_revision`, `updated_at`, `published_at`, `has_unpublished_changes`, `tracked_sessions`, `config`. ### `GET /api/plans/{pid}` Öffentlicher Wrapper des Entwurfs. Status `200` oder `404`. ### `POST /api/plans/{pid}/select` Setzt den aktiven Plan. Status `200` oder `404`. ### `POST /api/plans/{pid}` Speichert den Entwurf: ```json {"config":{},"expected_revision":4} ``` Antwort: `ok`, öffentlicher Wrapper, `validation` mit `errors` und `warnings`. Konflikt: `409` mit `code:"revision_conflict"` und `current_revision`. ### `POST /api/plans/{pid}/validate` Request `{"config":{}}`. Antwort: `errors`, `warnings`, normalisierte `config`, `diff`. `diff` enthält Zähler und stabile-ID-basierte Änderungen für `days`, `rotations`, `exercises`, `progression_steps`, außerdem `changes`, `total_changes`, `training_format_changed`, `plan_meta_changed`, `tracked_sessions`. ### `POST /api/plans/{pid}/publish` Request `{"expected_revision":4}`. Erfolgsantwort: `ok`, Wrapper, `diff`, `validation`. Status `400` bei Validierungsfehlern; `409` bei Revisionskonflikt. ### `POST /api/plans/{pid}/rename` Request `{"name":"…"}`. Antwort `id`, `name`, `revision`. Status `400` bei leerem Namen, `404` bei unbekanntem Plan. ### `DELETE /api/plans/{pid}` Antwort `{"ok":true,"active":"…"}`. Status `400`, wenn der letzte Plan gelöscht werden soll; `404` bei unbekanntem Plan. ### `GET /api/plans/{pid}/proposals` Liefert die Vorschlagsdatei unverändert oder `{}`. Vorschläge enthalten unter anderem `id`, `status`, `scope_key`, `analysis_type`, `week`, `target`, `target_label`, `target_exercise_id`, `target_progression_id`, `target_step_id`, `suggested_change`, `reason`, `condition`, `manual_step`, `analysis_created_at`. ### `POST /api/plans/{pid}/proposals/{proposal_id}` Request `{"status":"open|accepted|rejected|reviewed"}`. Antwort `ok`, `proposals`. Ungültiger Status: `400`. ### Weitere vorhandene Endpunkte `GET /api/config`, `POST /api/config`, `GET /api/default`, `POST /api/reset`, `GET /api/plans/{pid}/export`, `POST /api/plans/import`, `POST /api/pdf` existieren. Die native App verwendet für den Kernbetrieb die planbezogenen Endpunkte; sie bearbeitet niemals Serverdateien direkt.