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

186 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```http
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:
```http
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:**
```json
{ "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:
```http
POST /v2beta/api/artifact
Authorization: Bearer <session_token>
Content-Type: multipart/form-data
[file]
moduleSlug=logistik
label=Letzte gewählte Route
```
**Response 201:**
```json
{
"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üler*in
eingehen. Andere Schüler*innen 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.