Files
Adminator 1e51ef7def Nachtrag: alle bisher untracked Ordner + hängende Änderungen mit-committen
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/
- 12 code-workspace-Files
- STATUS-*.md
- viele M/D/R-Änderungen an bereits getrackten Files
- .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 02:27:02 +02:00

5.9 KiB
Raw Permalink Blame History

Vertrag 3: Output-API

Was ein Modul am Ende einer Sitzung an die Plattform liefert. Plus Endpoint zum Zwischenspeichern des Resume-States.

Endgültiges Ergebnis bei Modul-Abschluss

Wenn Schüler*in das Modul abschließt (egal ob bestanden oder abgebrochen), pusht das Modul genau einen Result-Call:

POST /v2beta/api/result
Authorization: Bearer <session_token>
Content-Type: application/json

{
  "moduleSlug": "logistik",
  "moduleVersion": "2.0.0",
  "specVersion": "1.0",
  "completed": true,
  "abandoned": false,
  "score": 78,
  "scoreMax": 100,
  "durationS": 1452,
  "difficulty": "L1",
  "mode": "teacher_started",
  "milestonesReached": [
    "first-order-accepted",
    "level-1-cleared"
  ],
  "lehrplanCoverage": {
    "AT-GW-1.4.2": "full",
    "AT-GW-2.1.1": "partial",
    "AT-GW-3.5.4": "none"
  },
  "freeTextAnswers": [
    {
      "promptId": "reflexion-end",
      "promptText": "Warum hast du dich für die Bahnverbindung entschieden?",
      "answerText": "Weil sie billiger war und nicht im Stau steht."
    }
  ],
  "artifacts": [
    {
      "type": "image/png",
      "url": "/v2beta/sims/logistik/uploads/student-4711/route-final.png",
      "label": "Letzte gewählte Route",
      "thumbnail": "/v2beta/sims/logistik/uploads/student-4711/route-final-thumb.png"
    }
  ],
  "moduleSpecificData": {
    "ordersCompleted": 6,
    "ordersFailed": 2,
    "totalCO2Kg": 142,
    "totalKm": 1845
  },
  "ts": "2026-05-15T14:48:00Z"
}

Pflicht-Felder

Feld Typ Beschreibung
moduleSlug String Aus Manifest
moduleVersion String Aus Manifest
specVersion String Welche Spec-Version genutzt wurde
completed Boolean Modul ehrlich abgeschlossen (nicht abgebrochen)
abandoned Boolean Schüler*in hat Tab geschlossen / Stunde zu Ende ohne Abschluss
score Integer 0N Punktzahl
scoreMax Integer Maximal mögliche Punktzahl
durationS Integer Reine Bearbeitungszeit (ohne Pausen >5min)
difficulty String Aus Input-Param
mode String Aus Input-Param
lehrplanCoverage Object Pro Anker aus Manifest: full / partial / none
ts ISO-Timestamp Zeitpunkt des Abschlusses

Optionale Felder

  • milestonesReached — alle erreichten Milestone-IDs (Subset von manifest.telemetryMilestones)
  • freeTextAnswers — Reflexionsfragen-Antworten der Schüler*in (für Lehrperson-Auswertung)
  • artifacts — Modul-Output (Bilder, Karten, Audio) als URLs
  • moduleSpecificData — Frei strukturiertes JSON mit Modul-eigenen Kennzahlen für die Auswertung

Lehrplan-Coverage

Modul muss pro deklariertem Lehrplan-Anker entscheiden:

  • full — Schüler*in hat zu diesem Anker gearbeitet UND erfolgreich abgeschlossen
  • partial — hat zu diesem Anker gearbeitet, aber nicht voll geschafft
  • none — hat diesen Anker in dieser Sitzung nicht berührt (z.B. weil bestimmtes Level nicht erreicht)

Resume-State zwischenspeichern

Während der Sitzung kann (und soll) das Modul regelmäßig den State speichern, damit Schüler*in nach Crash / Logout fortsetzen kann:

PUT /v2beta/api/module/state
Authorization: Bearer <session_token>
Content-Type: application/json

{
  "moduleSlug": "logistik",
  "state": {
    "<beliebiges Modul-JSON>": "..."
  },
  "summary": "Tag 3, Level 2, 4 von 8 Aufträgen erledigt"
}

Response 200:

{ "ok": true, "savedAt": "2026-05-15T14:23:00Z" }

Empfohlene Frequenz: jede 30s wenn State sich verändert hat, oder bei jedem Milestone. Nicht öfter (Server-Last).

Achtung: state ist ein opakes Modul-internes Format. Plattform speichert es als JSON-Blob in module_state_v2-Tabelle, ohne es zu parsen. Das Feld summary ist für die Lehrperson-Anzeige.

Artifacts hochladen

Wenn das Modul Bilder/Audio/Karten produziert, die in der Lehrer-Auswertung sichtbar sein sollen:

POST /v2beta/api/artifact
Authorization: Bearer <session_token>
Content-Type: multipart/form-data

[file]
moduleSlug=logistik
label=Letzte gewählte Route

Response 201:

{
  "id": 8842,
  "url": "/v2beta/sims/logistik/uploads/student-4711/route-final.png",
  "thumbnail": "/v2beta/sims/logistik/uploads/student-4711/route-final-thumb.png"
}

URL und Thumbnail dann in artifacts-Array des Result-Calls referenzieren.

Was die Plattform mit dem Result macht

  1. Speichert in assessments_v2 (mit created_by_version='v2')
  2. Aktualisiert student_progress_v2 (Modul-Fortschritt für Lerngeschichte-Anzeige)
  3. Aktualisiert lehrplan_progress_v2 (welche Anker hat Schüler*in abgedeckt)
  4. Pushed an Lehrer-Dashboard (Live-Update via Server-Sent-Events)
  5. Macht Result für die Klassen-Auswertung sichtbar

Level-Unlock-Update

Beim Result-Empfang aktualisiert die Plattform automatisch den Level-Unlock-Status der Schüler*in (in student_level_unlock_v2):

  • attempts_per_level[difficulty] += 1
  • best_score_per_level[difficulty] = max von alt und neu
  • Wenn completed = true UND score / scoreMax >= 0.6 UND Schüler*in war auf highest_unlocked_level:
    • highest_completed_level = difficulty
    • highest_unlocked_level = nächster Level aus manifest.difficultyLevels (falls vorhanden)

Modul muss sich darum nicht kümmern — passiert serverseitig.

Geklärt mit Thomas (2026-05-17)

  • freeTextAnswers: mit Klarnamen an die Lehrperson (wie V1). Pädagogisch nötig — Lehrperson kann gezielt auf einzelne Schülerin eingehen. Andere Schülerinnen sehen die Antworten nicht.
  • Artifacts löschen (DSGVO): per Lehrperson-Cockpit „Sitzung komplett löschen" — eine Aktion löscht Assessment + zugeordnete Artifacts. Separater DELETE-Endpoint kommt mit dem Cockpit.
  • moduleSpecificData: bleibt opakes JSON. Cross-Modul-Vergleiche laufen über score, scoreMax, lehrplanCoverage — Modul-eigene Kennzahlen sind Modul-Sache, kein Standardisierungs-Druck.