8.6 KiB
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:
{"detail":"Anmeldung erforderlich."}
Status: 401 Unauthorized, Header WWW-Authenticate: Basic realm="boehmitools".
Host-Verbindungstest
GET /api/health
Ohne Authentifizierung erreichbar. Antwortfelder laut Backend:
{
"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_schematraining_contracttracker_schemaresult_datapromptanalysis_schemanormalization
Status: 200, oder hostweit 401.
GET /api/plans
Antwort:
{
"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:
{
"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:
{"expected_revision":7,"tracker":{}}
Antwort:
{
"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:
{
"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:
{
"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:
{
"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:
{"scope":"week","week":1}
Request Gesamtanalyse:
{"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:
{"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:
{"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.