feat/adding-h5p-tasks #10
105
README.md
105
README.md
|
|
@ -13,8 +13,9 @@ Kinder können durch das Lösen von Lernaufgaben, beispielsweise Mathematikaufga
|
||||||
| Feature | Beschreibung |
|
| Feature | Beschreibung |
|
||||||
| :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **JWT-Authentifizierung** | Sichere API-Endpunkte für authentifizierte Benutzer über JSON Web Tokens (JWT). |
|
| **JWT-Authentifizierung** | Sichere API-Endpunkte für authentifizierte Benutzer über JSON Web Tokens (JWT). |
|
||||||
| **Lernaufgaben (Tasks)** | Abrufen von interaktiven Lernaufgaben mit vordefinierten Antwortmöglichkeiten und Belohnungen in Minuten. |
|
| **Lernaufgaben (Tasks)** | Abrufen von interaktiven Lernaufgaben mit vordefinierten Antwortmöglichkeiten, Filterung nach Fach und Alter sowie Belohnungen in Minuten. |
|
||||||
| **Antwort-Verifizierung** | Überprüfung der vom Kind ausgewählten Antworten. Bei korrekter Antwort wird die Belohnung direkt auf die verbleibende Bildschirmzeit aufgerechnet. |
|
| **Antwort-Verifizierung** | Überprüfung der vom Kind ausgewählten Antworten. Bei korrekter Antwort wird die Belohnung direkt auf die verbleibende Bildschirmzeit aufgerechnet. |
|
||||||
|
| **OER / H5P Ingestion Pipeline** | Automatisierter Live-Crawler und Batch-Import von freien Bildungsinhalten (ZUM.de / H5P) inklusive HTML-Bereinigung und Lizenzattributierung (CC-BY). |
|
||||||
| **Bildschirmzeit-Synchronisation** | Laufender Datenabgleich der verbrauchten Bildschirmzeit. Sinkt die verbleibende Zeit auf oder unter null, wird der Status automatisch auf `isBlocked = true` gesetzt. |
|
| **Bildschirmzeit-Synchronisation** | Laufender Datenabgleich der verbrauchten Bildschirmzeit. Sinkt die verbleibende Zeit auf oder unter null, wird der Status automatisch auf `isBlocked = true` gesetzt. |
|
||||||
| **Zwei-Ebenen-Speicher** | PostgreSQL als persistente Datenbank, kombiniert mit Redis-Caching für schnelle Screentime-Abfragen. |
|
| **Zwei-Ebenen-Speicher** | PostgreSQL als persistente Datenbank, kombiniert mit Redis-Caching für schnelle Screentime-Abfragen. |
|
||||||
| **Fallback-Mechanismus** | Ist Redis nicht erreichbar, fällt das System automatisch und transparent auf PostgreSQL zurück. |
|
| **Fallback-Mechanismus** | Ist Redis nicht erreichbar, fällt das System automatisch und transparent auf PostgreSQL zurück. |
|
||||||
|
|
@ -98,7 +99,8 @@ Prüft, ob der Backend-Server ordnungsgemäß läuft.
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"token": "eyJhbGciOiJIUzI1NiIsIn...",
|
"token": "eyJhbGciOiJIUzI1NiIsIn...",
|
||||||
"familyName": "Familie Schmidt"
|
"userId": "6380776f-88d3-4e9f-a2be-0d9cacae447e",
|
||||||
|
"message": "Registrierung erfolgreich"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -128,7 +130,9 @@ Prüft, ob der Backend-Server ordnungsgemäß läuft.
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"token": "eyJhbGciOiJIUzI1NiIsIn...",
|
"token": "eyJhbGciOiJIUzI1NiIsIn...",
|
||||||
"familyName": "Familie Schmidt"
|
"userId": "6380776f-88d3-4e9f-a2be-0d9cacae447e",
|
||||||
|
"familyName": "Familie Schmidt",
|
||||||
|
"childAge": 8
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -156,9 +160,9 @@ Authorization: Bearer <Token>
|
||||||
|
|
||||||
**Beschreibung:**
|
**Beschreibung:**
|
||||||
|
|
||||||
Liefert die aktuell verbleibende Bildschirmzeit sowie den aktuellen Sperrstatus.
|
Liefert die aktuell verbleibende Bildschirmzeit sowie den aktuellen Sperrstatus des authentifizierten Benutzers.
|
||||||
|
|
||||||
**Antwort:**
|
**Antwort – `200 OK`:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -181,29 +185,28 @@ Liefert die aktuell verbleibende Bildschirmzeit sowie den aktuellen Sperrstatus.
|
||||||
|
|
||||||
**Beschreibung:**
|
**Beschreibung:**
|
||||||
|
|
||||||
Zieht die seit dem letzten Abgleich verbrauchten Sekunden von der verfügbaren Bildschirmzeit ab.
|
Zieht die seit dem letzten Abgleich vom Endgerät verbrauchten Sekunden von der verfügbaren Bildschirmzeit ab.
|
||||||
|
|
||||||
**Request-Body:**
|
**Request-Body:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"userId": "default_user",
|
"usedSecondsSinceLastSync": 30
|
||||||
"usedSecondsSinceLastSync": 200
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Antwort:**
|
**Antwort – `200 OK`:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"remainingSeconds": 1600,
|
"remainingSeconds": 1770,
|
||||||
"isBlocked": false
|
"isBlocked": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 6. Alle verfügbaren Aufgaben abrufen
|
#### 6. Lernaufgaben abrufen (mit Fach- & Altersfilter)
|
||||||
|
|
||||||
**Methode:** `GET`
|
**Methode:** `GET`
|
||||||
|
|
||||||
|
|
@ -213,24 +216,42 @@ Zieht die seit dem letzten Abgleich verbrauchten Sekunden von der verfügbaren B
|
||||||
/api/v1/tasks
|
/api/v1/tasks
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Optionale Query-Parameter:**
|
||||||
|
|
||||||
|
| Parameter | Typ | Standardwert | Beschreibung |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| `subject` | String | `null` | Schulfach (z. B. `Mathematik`, `Deutsch`, `Sachunterricht`, `Englisch`, `Biologie`, `Geographie`, `Geschichte`). |
|
||||||
|
| `age` | Integer | `null` | Zielalter des Kindes (filtert nach `minAge <= age AND maxAge >= age`). |
|
||||||
|
| `limit` | Integer | `10` | Maximale Anzahl an zurückgegebenen Aufgaben. |
|
||||||
|
|
||||||
|
**Beispiel-Anfrage:**
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /api/v1/tasks?subject=Mathematik&age=8&limit=5
|
||||||
|
```
|
||||||
|
|
||||||
**Beschreibung:**
|
**Beschreibung:**
|
||||||
|
|
||||||
Liefert eine Liste aller verfügbaren Lernaufgaben.
|
Liefert kuratierte oder aus OER/H5P importierte Lernaufgaben. Die korrekte Lösung (`correctAnswer`) wird zum Schutz vor Manipulationen serverseitig geheim gehalten und niemals an den Client übertragen.
|
||||||
|
|
||||||
**Antwort:**
|
**Antwort – `200 OK`:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "t1",
|
"id": "zum-math-001",
|
||||||
"question": "Was ist 12 + 15?",
|
"subject": "Mathematik",
|
||||||
|
"minAge": 8,
|
||||||
|
"maxAge": 9,
|
||||||
|
"question": "Was ist das Ergebnis von 6 × 8?",
|
||||||
"options": [
|
"options": [
|
||||||
"25",
|
"42",
|
||||||
"27",
|
"48",
|
||||||
"30",
|
"54",
|
||||||
"22"
|
"56"
|
||||||
],
|
],
|
||||||
"rewardMinutes": 10
|
"rewardMinutes": 5,
|
||||||
|
"license": "CC BY-SA 4.0"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
@ -251,22 +272,22 @@ Liefert eine Liste aller verfügbaren Lernaufgaben.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"taskId": "t1",
|
"taskId": "zum-math-001",
|
||||||
"selectedAnswer": "27"
|
"selectedAnswer": "48"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Antwort – korrekte Lösung:**
|
**Antwort – korrekte Lösung (`200 OK`):**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"isCorrect": true,
|
"isCorrect": true,
|
||||||
"earnedMinutes": 10,
|
"earnedMinutes": 5,
|
||||||
"message": "Super gemacht! Du hast 10 Minuten gewonnen."
|
"message": "Super gemacht! Du hast 5 Minuten gewonnen."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Antwort – falsche Lösung:**
|
**Antwort – falsche Lösung (`200 OK`):**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -278,6 +299,38 @@ Liefert eine Liste aller verfügbaren Lernaufgaben.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## H5P / ZUM.de Aufgaben-Import (Standalone CLI)
|
||||||
|
|
||||||
|
Das Backend verfügt über ein integriertes CLI-Tool zum automatisierten Importieren und Crawlen interaktiver Lernaufgaben von **ZUM.de / OER (H5P)**.
|
||||||
|
|
||||||
|
### Batch-Import ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alle Fächer importieren (z. B. 100 Aufgaben)
|
||||||
|
./gradlew runIngestion --args="--limit=100"
|
||||||
|
|
||||||
|
# Gezielt ein Schulfach importieren (z. B. Mathematik)
|
||||||
|
./gradlew runIngestion --args="--subject=Mathematik --limit=50"
|
||||||
|
|
||||||
|
# Deutsch für Grundschüler importieren
|
||||||
|
./gradlew runIngestion --args="--subject=Deutsch --limit=30"
|
||||||
|
```
|
||||||
|
|
||||||
|
### CLI-Optionen
|
||||||
|
|
||||||
|
| Argument | Standardwert | Beschreibung |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `--subject=<Name>` | *(alle Fächer)* | Filtert Aufgaben nach Fach (z. B. `Mathematik`, `Deutsch`, `Englisch`, `Sachunterricht`). |
|
||||||
|
| `--limit=<Zahl>` | `50` | Maximale Anzahl an Aufgaben, die im Durchlauf importiert/aktualisiert werden. |
|
||||||
|
|
||||||
|
### Funktionsweise der Ingestion-Pipeline
|
||||||
|
1. **Live-Crawler & Discovery:** Durchsucht `apps.zum.de` nach interaktiven Inhalten (`H5P.MultiChoice`, `H5P.TrueFalse`, `H5P.QuestionSet`).
|
||||||
|
2. **Textbereinigung:** Entfernt störende HTML-Tags (`<p>`, `<strong>`, ` `) für native mobile Darstellung.
|
||||||
|
3. **Taxonomie- & Alters-Mapping:** Weist Altersgrenzen (`minAge`, `maxAge`) basierend auf Klassenstufen automatisch zu.
|
||||||
|
4. **Idempotenter Upsert:** Speichert Fragen, Optionen, geheime Lösungen und CC-Lizenzen direkt relational in PostgreSQL (`TasksTable`). Duplikate werden vermieden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Projektkonfiguration und Umgebungsvariablen
|
## Projektkonfiguration und Umgebungsvariablen
|
||||||
|
|
||||||
Die Anwendung wird über folgende Konfigurationsdatei konfiguriert:
|
Die Anwendung wird über folgende Konfigurationsdatei konfiguriert:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ version = "1.0.0-SNAPSHOT"
|
||||||
application {
|
application {
|
||||||
mainClass = "io.ktor.server.netty.EngineMain"
|
mainClass = "io.ktor.server.netty.EngineMain"
|
||||||
}
|
}
|
||||||
|
ktor {
|
||||||
|
openApi {
|
||||||
|
enabled = true // Aktiviert die OpenAPI-Code-Generierung
|
||||||
|
codeInferenceEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(21)
|
jvmToolchain(21)
|
||||||
|
|
@ -20,7 +26,7 @@ dependencies {
|
||||||
implementation(ktorLibs.server.config.yaml)
|
implementation(ktorLibs.server.config.yaml)
|
||||||
implementation(ktorLibs.server.core)
|
implementation(ktorLibs.server.core)
|
||||||
implementation(ktorLibs.server.netty)
|
implementation(ktorLibs.server.netty)
|
||||||
|
implementation("io.ktor:ktor-server-openapi")
|
||||||
// JWT Authentication
|
// JWT Authentication
|
||||||
implementation("io.ktor:ktor-server-auth")
|
implementation("io.ktor:ktor-server-auth")
|
||||||
implementation("io.ktor:ktor-server-auth-jwt")
|
implementation("io.ktor:ktor-server-auth-jwt")
|
||||||
|
|
@ -46,3 +52,11 @@ dependencies {
|
||||||
// redis
|
// redis
|
||||||
implementation("redis.clients:jedis:5.1.2")
|
implementation("redis.clients:jedis:5.1.2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register<JavaExec>("runIngestion") {
|
||||||
|
group = "application"
|
||||||
|
description = "Runs the standalone H5P/ZUM task ingestion CLI tool"
|
||||||
|
mainClass.set("com.oliver.kidio.backend.ingestion.H5PIngestionCli")
|
||||||
|
classpath = sourceSets["main"].runtimeClasspath
|
||||||
|
standardInput = System.`in`
|
||||||
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
ノH<EFBFBD>テG
|
ユ~
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue