This commit is contained in:
team3
2026-07-13 05:18:55 +02:00
parent 7ee425f7fa
commit 8926f87185
19 changed files with 703 additions and 41 deletions

View File

@@ -18,7 +18,8 @@ import db
import guide
import ledger
import pipeline
from config import FRONTEND_DIST, PROJECT_ROOT, topic_name_ok
from config import (FRONTEND_DIST, PROJECT_ROOT, PRUEFUNG_PANEL, PRUEFUNG_SCHWELLE,
topic_name_ok)
from ws import hub
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(levelname)s %(message)s")
@@ -52,6 +53,11 @@ class UebenAntwort(BaseModel):
richtig: bool
class PruefungAntwort(BaseModel):
ids: list[int] # die 4 Aussagen des Panels
angekreuzt: list[int] # die als wahr markierten
class AutoFlag(BaseModel):
ebene: str
an: bool
@@ -252,6 +258,19 @@ async def ebene_entfernen(topic: str, ebene: str):
return {"ok": True}
@app.post("/api/topics/{topic}/ebene/{ebene}/aktualisieren")
async def ebene_aktualisieren(topic: str, ebene: str, tief: bool = False):
if not db.one("SELECT name FROM topics WHERE name=?", (topic,)):
raise HTTPException(404, "unbekanntes Topic")
try:
run_id = pipeline.aktualisierung_starten(topic, ebene, tief=tief)
except ValueError as e:
raise HTTPException(400, str(e))
except RuntimeError as e:
raise HTTPException(409, str(e))
return {"run_id": run_id}
@app.delete("/api/topics/{topic}")
async def topic_loeschen(topic: str):
if not db.one("SELECT name FROM topics WHERE name=?", (topic,)):
@@ -315,6 +334,54 @@ def baustein_fertig(topic: str, baustein_id: int):
return {"ok": True}
@app.get("/api/topics/{topic}/baustein/{baustein_id}/pruefung")
def pruefung_holen(topic: str, baustein_id: int):
"""4er-Panels aus verifizierten Aussagen der Baustein-Atome. OHNE Wahrheitswert
(Anti-Cheat) und ohne Paar-Partner im selben Panel (verraten sich gegenseitig)."""
import random
rows = db.query(
"SELECT ar.id, ar.inhalt FROM artefakte ar JOIN atome a ON a.id=ar.atom_id"
" WHERE a.topic=? AND a.baustein_id=? AND ar.typ='aussage'"
" AND ar.status='verifiziert'", (topic, baustein_id))
aussagen = [{"id": r["id"], "text": db.uj(r["inhalt"], {}).get("text", ""),
"paar": db.uj(r["inhalt"], {}).get("paar", "")} for r in rows]
random.shuffle(aussagen)
panels: list[list[dict]] = []
for a in aussagen: # greedy: erstes Panel ohne Paar-Konflikt mit Platz
for p in panels:
if len(p) < PRUEFUNG_PANEL and all(x["paar"] != a["paar"] for x in p):
p.append(a)
break
else:
panels.append([a])
return [{"panel": [{"id": x["id"], "text": x["text"]} for x in p]}
for p in panels if len(p) == PRUEFUNG_PANEL]
@app.post("/api/topics/{topic}/pruefung/antwort")
def pruefung_antwort(topic: str, a: PruefungAntwort):
rows = db.query(f"SELECT ar.id, ar.inhalt, at.baustein_id FROM artefakte ar"
f" JOIN atome at ON at.id=ar.atom_id"
f" WHERE ar.id IN ({','.join('?' * len(a.ids))})", tuple(a.ids))
if len(rows) != len(a.ids):
raise HTTPException(404, "unbekannte Aussage")
wahre = {r["id"] for r in rows if db.uj(r["inhalt"], {}).get("wahr")}
richtig = set(a.angekreuzt) == wahre # exakte Teilmenge, sonst falsch
baustein_id = rows[0]["baustein_id"]
row = db.one("SELECT xp FROM lernstand WHERE topic=? AND baustein_id=?",
(topic, baustein_id))
xp = max(0, (row["xp"] if row else 0) + (1 if richtig else -1))
status = "fertig" if xp >= PRUEFUNG_SCHWELLE else "aktiv"
db.execute("INSERT INTO lernstand(topic, baustein_id, status, xp) VALUES(?,?,?,?)"
" ON CONFLICT(topic, baustein_id) DO UPDATE SET xp=excluded.xp,"
" status=excluded.status", (topic, baustein_id, status, xp))
aufloesung = [{"id": r["id"], "wahr": db.uj(r["inhalt"], {}).get("wahr", False),
"erklaerung": db.uj(r["inhalt"], {}).get("erklaerung", "")}
for r in rows]
return {"richtig": richtig, "aufloesung": aufloesung, "xp": xp,
"fertig": xp >= PRUEFUNG_SCHWELLE}
@app.get("/api/runs/{run_id}/kennzahlen")
def kennzahlen(run_id: int):
return {"zeilen": ledger.kennzahlen(run_id), "verbraucht": ledger.verbraucht(run_id)}