1e51ef7def
- 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>
118 lines
4.9 KiB
Markdown
118 lines
4.9 KiB
Markdown
# V2 Plattform-Bibliotheken
|
||
|
||
Helfer-Libs für V2-Module, sparen Boilerplate. ESM, keine Build-Step,
|
||
keine Dependencies.
|
||
|
||
## Verfügbar
|
||
|
||
| Lib | Zweck |
|
||
|---|---|
|
||
| [telemetry-client.js](telemetry-client.js) | Telemetry, Profil, runtime-config, State, Result. Spec-Vollständig. |
|
||
|
||
## TelemetryClient — Quickstart
|
||
|
||
```js
|
||
import { TelemetryClient } from '../../../v2-platform/lib/telemetry-client.js';
|
||
|
||
// 1. Instanz
|
||
const tc = new TelemetryClient({
|
||
moduleSlug: 'hallo-welt',
|
||
moduleVersion: '1.0.0',
|
||
specVersion: '1.0',
|
||
});
|
||
|
||
// 2. Init (holt runtime-config + profile, startet Heartbeat + Stuck-Detection)
|
||
await tc.init();
|
||
|
||
// 3. Profil + Session sind jetzt verfügbar
|
||
console.log(tc.profile); // { displayName, avatarUrl, easyLanguage, nachteilsausgleich, levelUnlocks, ... }
|
||
console.log(tc.session); // { mode, difficulty, lang, classId, studentId, isMock, ... }
|
||
|
||
// 4. Phase wechseln (Lehrperson-Dashboard sieht das live)
|
||
tc.setPhase('welcome', 'Begrüßung');
|
||
|
||
// 5. Milestone emittieren
|
||
tc.milestone('welcome-shown', 'Begrüßung gesehen', /* scoreDelta */ 1);
|
||
|
||
// 6. Score aktualisieren (fließt in nächste Heartbeats)
|
||
tc.updateScore(42, 100);
|
||
|
||
// 7. Resume-State speichern (regelmäßig oder bei Milestones)
|
||
await tc.saveState({ ...modulInternerState }, 'Tag 3, Level 2');
|
||
|
||
// 8. Modul abschließen (sendet Result-POST + cleanup)
|
||
await tc.complete({
|
||
completed: true,
|
||
lehrplanCoverage: { 'AT-GW-1.4.2': 'full' },
|
||
freeTextAnswers: [],
|
||
moduleSpecificData: { /* modul-eigene Kennzahlen */ },
|
||
});
|
||
|
||
// 9. Bei manuellem Abbruch (z.B. ✕-Klick)
|
||
tc.exit('user-cancelled');
|
||
```
|
||
|
||
## Was die Lib für dich erledigt
|
||
|
||
- **URL-Param-Parsing** — `session_token`, `mode`, `difficulty`, `lang`, `class_id`, `student_id`, `mock=1`
|
||
- **Runtime-Config** — Heartbeat-Frequenz wird auf Plattform-Grenzen geclampt (Default 10–30s)
|
||
- **Heartbeat-Loop** — alle X Sekunden automatisch, pausiert bei AFK (>60s ohne Eingabe)
|
||
- **Stuck-Detection** — bei >90s ohne Eingabe wird Stuck-Event gefeuert (Schwellwert aus runtime-config)
|
||
- **Tab-Close-Beacon** — navigator.sendBeacon-Final-Heartbeat
|
||
- **Fehlertoleranz** — Telemetry-Fetches die scheitern blockieren das Modul nicht (silent fail)
|
||
- **Token-Expired-Handling** — bei 401 default Redirect zu `/v2beta/`, im iframe wird nur Warnung geloggt
|
||
- **Mock-Erkennung** — wenn `?mock=1`, geht alles automatisch gegen die Mock-Plattform
|
||
|
||
## Was du selbst tun musst
|
||
|
||
- **Activity-Tracking** für `click/touchstart/keydown` macht die Lib automatisch (an `document` registriert)
|
||
- **A11y-Profil auswerten** — `tc.profile.preferences.highContrast` etc. → Modul setzt `body[data-high-contrast="…"]`
|
||
- **Easy-Sprache laden** — `tc.session.lang` zeigt was geladen werden soll
|
||
- **Level-Unlock-Sicht** — `tc.profile.levelUnlocks[moduleSlug]` für freien Modus
|
||
- **Nachteilsausgleich** — `tc.profile.nachteilsausgleich === true` → Level-Wähler statt Auftrags-Vorgabe
|
||
|
||
## API-Referenz
|
||
|
||
### Konstruktor
|
||
|
||
```js
|
||
new TelemetryClient({
|
||
moduleSlug: string, // PFLICHT
|
||
moduleVersion: string, // PFLICHT
|
||
specVersion: string, // PFLICHT
|
||
heartbeatPreferenceS?: number, // default 15
|
||
onProfileLoaded?: (profile) => void,
|
||
onError?: (err) => void,
|
||
onTokenExpired?: (reason) => void,
|
||
})
|
||
```
|
||
|
||
### Methoden
|
||
|
||
| Methode | Zweck |
|
||
|---|---|
|
||
| `init()` | Async. Holt runtime-config + profile, startet Heartbeat + Stuck-Loop. |
|
||
| `setPhase(phase, label?, step?)` | Phase wechseln; löst sofort einen Heartbeat aus. |
|
||
| `updateScore(score, scoreMax?)` | Score aktualisieren. Wird in nächsten Heartbeats mitgesendet. |
|
||
| `milestone(id, label, scoreDelta?)` | Milestone-Event posten (id muss im Manifest deklariert sein). |
|
||
| `accessibilityBlocker({blockerType, hint})` | A11y-Blocker melden. |
|
||
| `saveState(state, summary?)` | Resume-State speichern (PUT `/api/module/state`). |
|
||
| `complete(result)` | Result-POST + Cleanup. Ruft destroy() automatisch. |
|
||
| `exit(reason?)` | Abbruch melden + Cleanup. |
|
||
| `destroy()` | Manueller Cleanup (Timer + Listener entfernen). |
|
||
| `elapsedTotalS()`, `elapsedPhaseS()` | Hilfs-Getter für Sekunden seit Start / Phase-Start. |
|
||
|
||
### Properties (nach `await init()`)
|
||
|
||
| Property | Inhalt |
|
||
|---|---|
|
||
| `tc.profile` | Schüler*innen-Profil (`/api/student/me`-Response inkl. Nachteilsausgleich + Level-Unlocks) |
|
||
| `tc.config` | Runtime-Config-Werte (`heartbeat`, `stuck`, `telemetry`, `auth`) |
|
||
| `tc.session` | URL-Param-Werte (`sessionToken`, `studentId`, `classId`, `mode`, `difficulty`, `lang`, `resume`, `isMock`) |
|
||
|
||
## Bekannte Limitierungen
|
||
|
||
- **Token-Auto-Refresh** ist Stub. Echte Refresh-Logik kommt mit V2-Login. Aktuell: bei Token-Ablauf (401) → Redirect zu `/v2beta/`.
|
||
- **`onTokenExpired` Callback** kann das überschreiben, falls Modul eigene Handhabung will (z.B. iframe-Eltern benachrichtigen).
|
||
- **Mock-Erkennung** über `?mock=1`-URL-Param. Wenn dein Modul-Wrapper-Pfad nicht `v2-modules/<slug>/public/` ist, musst du `apiBase` selbst überschreiben (nicht in der API vorgesehen — am besten Pfad anpassen).
|