chore: initial import

This commit is contained in:
2026-07-24 21:37:03 +02:00
commit 6b4ed9bc36
111 changed files with 95131 additions and 0 deletions
+280
View File
@@ -0,0 +1,280 @@
# 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/<plugin-id>` 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 <base64(username:password)>` 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.