Compare commits
24 commits
feat/add-n
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b77d7b7fdc | ||
|
|
a57870f880 | ||
|
|
7c21886bfa | ||
|
|
4cc7af16b1 | ||
|
|
8dfe542acd | ||
|
|
a56db8856b | ||
|
|
b6237fcb2f | ||
|
|
2caf2d4734 | ||
|
|
118677d960 | ||
|
|
b3a01a4f16 | ||
|
|
c8032cbf62 | ||
|
|
0696ce409f | ||
|
|
ce18dfb93e | ||
|
|
b082df962f | ||
|
|
396b34d5ca | ||
|
|
bc166cae62 | ||
|
|
576ed74ad4 | ||
|
|
5f256a03d8 | ||
|
|
0eb3ce33d0 | ||
|
|
aabf94941a | ||
|
|
a8f30d7ef2 | ||
|
|
87a820ef03 | ||
|
|
b71ce98ede | ||
|
|
c3fc1e3178 |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
|
|
@ -2,7 +2,7 @@ name: CI Pipeline
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: [ "main", "feat/*" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
|
|
@ -12,10 +12,10 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Code auschecken
|
||||
uses: https://github.com/actions/checkout@v4 # <-- Vollständige URL oder github/ Prefix
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
|
||||
- name: Java 21 einrichten
|
||||
uses: https://github.com/actions/setup-java@v4 # <-- Vollständige URL oder github/ Prefix
|
||||
uses: https://github.com/actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
|
|
@ -24,10 +24,10 @@ jobs:
|
|||
run: chmod +x gradlew
|
||||
|
||||
- name: 1. Code-Style (Linter) prüfen
|
||||
run: ./gradlew ktlintCheck
|
||||
run: ./gradlew ktlintCheck --no-daemon
|
||||
|
||||
- name: 2. Tests ausführen
|
||||
run: ./gradlew test
|
||||
run: ./gradlew test --no-daemon
|
||||
|
||||
- name: 3. Backend bauen
|
||||
run: ./gradlew build -x test --max-workers=2 -Dorg.gradle.jvmargs="-Xmx1024m"
|
||||
run: ./gradlew build -x test --max-workers=2 --no-daemon -Dorg.gradle.jvmargs="-Xmx1024m"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -11,3 +11,4 @@ build/
|
|||
*.iws
|
||||
*.ipr
|
||||
out/
|
||||
build/
|
||||
|
|
|
|||
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,2 +0,0 @@
|
|||
#Wed Sep 02 15:03:29 CEST 2026
|
||||
gradle.version=9.5.1
|
||||
Binary file not shown.
Binary file not shown.
10
.idea/.gitignore
vendored
10
.idea/.gitignore
vendored
|
|
@ -1,10 +0,0 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Ignored default folder with query files
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="kidio_db@82.165.11.162" uuid="c8ad70de-d5fc-407a-9165-4f24328fa311">
|
||||
<driver-ref>postgresql</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:postgresql://82.165.11.162:5432/kidio_db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="externalSystemId" value="Gradle" />
|
||||
<option name="version" value="2.4.0" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="homebrew-26" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
118
README.md
118
README.md
|
|
@ -13,8 +13,9 @@ Kinder können durch das Lösen von Lernaufgaben, beispielsweise Mathematikaufga
|
|||
| Feature | Beschreibung |
|
||||
| :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **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. |
|
||||
| **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. |
|
||||
| **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. |
|
||||
|
|
@ -98,7 +99,8 @@ Prüft, ob der Backend-Server ordnungsgemäß läuft.
|
|||
```json
|
||||
{
|
||||
"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
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsIn...",
|
||||
"familyName": "Familie Schmidt"
|
||||
"userId": "6380776f-88d3-4e9f-a2be-0d9cacae447e",
|
||||
"familyName": "Familie Schmidt",
|
||||
"childAge": 8
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -156,9 +160,9 @@ Authorization: Bearer <Token>
|
|||
|
||||
**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
|
||||
{
|
||||
|
|
@ -181,29 +185,28 @@ Liefert die aktuell verbleibende Bildschirmzeit sowie den aktuellen Sperrstatus.
|
|||
|
||||
**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:**
|
||||
|
||||
```json
|
||||
{
|
||||
"userId": "default_user",
|
||||
"usedSecondsSinceLastSync": 200
|
||||
"usedSecondsSinceLastSync": 30
|
||||
}
|
||||
```
|
||||
|
||||
**Antwort:**
|
||||
**Antwort – `200 OK`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"remainingSeconds": 1600,
|
||||
"remainingSeconds": 1770,
|
||||
"isBlocked": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6. Alle verfügbaren Aufgaben abrufen
|
||||
#### 6. Lernaufgaben abrufen (mit Fach- & Altersfilter)
|
||||
|
||||
**Methode:** `GET`
|
||||
|
||||
|
|
@ -213,24 +216,42 @@ Zieht die seit dem letzten Abgleich verbrauchten Sekunden von der verfügbaren B
|
|||
/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:**
|
||||
|
||||
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
|
||||
[
|
||||
{
|
||||
"id": "t1",
|
||||
"question": "Was ist 12 + 15?",
|
||||
"id": "zum-math-001",
|
||||
"subject": "Mathematik",
|
||||
"minAge": 8,
|
||||
"maxAge": 9,
|
||||
"question": "Was ist das Ergebnis von 6 × 8?",
|
||||
"options": [
|
||||
"25",
|
||||
"27",
|
||||
"30",
|
||||
"22"
|
||||
"42",
|
||||
"48",
|
||||
"54",
|
||||
"56"
|
||||
],
|
||||
"rewardMinutes": 10
|
||||
"rewardMinutes": 5,
|
||||
"license": "CC BY-SA 4.0"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
@ -251,22 +272,22 @@ Liefert eine Liste aller verfügbaren Lernaufgaben.
|
|||
|
||||
```json
|
||||
{
|
||||
"taskId": "t1",
|
||||
"selectedAnswer": "27"
|
||||
"taskId": "zum-math-001",
|
||||
"selectedAnswer": "48"
|
||||
}
|
||||
```
|
||||
|
||||
**Antwort – korrekte Lösung:**
|
||||
**Antwort – korrekte Lösung (`200 OK`):**
|
||||
|
||||
```json
|
||||
{
|
||||
"isCorrect": true,
|
||||
"earnedMinutes": 10,
|
||||
"message": "Super gemacht! Du hast 10 Minuten gewonnen."
|
||||
"earnedMinutes": 5,
|
||||
"message": "Super gemacht! Du hast 5 Minuten gewonnen."
|
||||
}
|
||||
```
|
||||
|
||||
**Antwort – falsche Lösung:**
|
||||
**Antwort – falsche Lösung (`200 OK`):**
|
||||
|
||||
```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
|
||||
|
||||
Die Anwendung wird über folgende Konfigurationsdatei konfiguriert:
|
||||
|
|
@ -517,3 +570,16 @@ MIT License
|
|||
```
|
||||
|
||||
Eine entsprechende `LICENSE`-Datei sollte sich im Root-Verzeichnis des Repositorys befinden.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Kidio Ökosystem
|
||||
Für Kinder (Der spielerische Teil - "Kid"):
|
||||
|
||||
* Kidio Learn: Interaktive Lernspiele (Mathe, Sprachen, Logik), die sich dem Alter und Lernfortschritt des Kindes anpassen.
|
||||
|
||||
* Kidio Create: Tools und Plattformen, um eigene Ideen, Kunstwerke oder kleine Projekte zu entwickeln und zu teilen.
|
||||
|
||||
* Kidio Media: Ein sicherer Bereich mit kindgerechten Videos, Hörspielen und Büchern (ähnlich wie YouTube Kids).
|
||||

|
||||
|
|
@ -11,6 +11,12 @@ version = "1.0.0-SNAPSHOT"
|
|||
application {
|
||||
mainClass = "io.ktor.server.netty.EngineMain"
|
||||
}
|
||||
ktor {
|
||||
openApi {
|
||||
enabled = true // Aktiviert die OpenAPI-Code-Generierung
|
||||
codeInferenceEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
|
|
@ -20,7 +26,7 @@ dependencies {
|
|||
implementation(ktorLibs.server.config.yaml)
|
||||
implementation(ktorLibs.server.core)
|
||||
implementation(ktorLibs.server.netty)
|
||||
|
||||
implementation("io.ktor:ktor-server-openapi")
|
||||
// JWT Authentication
|
||||
implementation("io.ktor:ktor-server-auth")
|
||||
implementation("io.ktor:ktor-server-auth-jwt")
|
||||
|
|
@ -46,3 +52,11 @@ dependencies {
|
|||
// redis
|
||||
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.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue