update
This commit is contained in:
@@ -13,9 +13,12 @@ from database import (
|
||||
create_topic, list_topics as db_list_topics, delete_topic,
|
||||
list_progress, set_progress, delete_progress,
|
||||
create_element, list_elements, get_element, update_element, delete_element,
|
||||
get_vertiefung, set_vertiefung, list_vertiefungen,
|
||||
list_baustein_progress, add_gute_antwort, set_baustein_absolviert, delete_baustein_daten,
|
||||
)
|
||||
from bausteine import generate_bausteine, cancel_bausteine, bausteine_status, active_bausteine, reset_bausteine
|
||||
from elements import generate_element, chat_with_guide, chat_with_element, check_element, style_element, refine_suggestion
|
||||
from lernen import NOETIG, baustein_chat, baustein_element_anlegen, baustein_pruefung, vertiefung_generieren
|
||||
from guide import generate_guide, guide_slot_dateien
|
||||
from pipeline import cancel_guide
|
||||
from regeln import FORMATE, formate_stats, guide_lock, ist_absolviert, lade_lernstand
|
||||
@@ -28,6 +31,9 @@ from models import (
|
||||
ElementUpdateRequest, ElementCheckRequest, ElementCheckResponse, ElementStyleResponse,
|
||||
ElementRefineRequest, ElementRefineResponse,
|
||||
ProgressUpdate, ProgressResponse, ProjectResponse, ProviderInfo,
|
||||
VertiefungRequest, VertiefungResponse,
|
||||
BausteinChatRequest, BausteinChatResponse,
|
||||
BausteinPruefungRequest, BausteinPruefungResponse, BausteinLernstandResponse,
|
||||
)
|
||||
from paths import bausteine_topics, guide_content_path, project_dir, topic_dir
|
||||
|
||||
@@ -53,18 +59,18 @@ async def get_topics():
|
||||
@router.get("/stats")
|
||||
async def get_stats():
|
||||
"""Tracker: Themen-Anzahl + pro Format erstellt/absolviert."""
|
||||
guides, progress = await lade_lernstand()
|
||||
guides, progress, bausteine_done = await lade_lernstand()
|
||||
themen = set(await db_list_topics()) | {g["topic"] for g in guides} | set(bausteine_topics())
|
||||
if PROJECTS_DIR.is_dir():
|
||||
themen |= {e.name for e in PROJECTS_DIR.iterdir() if e.is_dir()}
|
||||
return {"themen": len(themen), "formate": formate_stats(guides, progress)}
|
||||
return {"themen": len(themen), "formate": formate_stats(guides, progress, bausteine_done)}
|
||||
|
||||
|
||||
@router.get("/topics/fortschritt")
|
||||
async def topic_fortschritt(topic: str):
|
||||
"""Absolviert-Status pro Format — fürs Freischalten der nächsten Ausbaustufe."""
|
||||
guides, progress = await lade_lernstand()
|
||||
return {fmt: ist_absolviert(topic, fmt, guides, progress) for fmt in FORMATE}
|
||||
guides, progress, bausteine_done = await lade_lernstand()
|
||||
return {fmt: ist_absolviert(topic, fmt, guides, progress, bausteine_done) for fmt in FORMATE}
|
||||
|
||||
|
||||
@router.post("/topics")
|
||||
@@ -76,6 +82,7 @@ async def add_topic(req: TopicCreateRequest):
|
||||
@router.delete("/topics")
|
||||
async def remove_topic(topic: str):
|
||||
await delete_topic(topic)
|
||||
await delete_baustein_daten(topic)
|
||||
shutil.rmtree(topic_dir(topic), ignore_errors=True)
|
||||
return {"ok": True}
|
||||
|
||||
@@ -138,12 +145,85 @@ async def remove_bausteine(topic: str):
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
# --- Baustein-Lernen: Vertiefung, Chat, Prüfung ---
|
||||
|
||||
@router.get("/bausteine/lernstand", response_model=BausteinLernstandResponse)
|
||||
async def baustein_lernstand(topic: str):
|
||||
"""Prüfungs-Stand + Vertiefungs-Existenz pro Baustein (roher Titel als Key)."""
|
||||
progress = await list_baustein_progress(topic)
|
||||
mit_vertiefung = await list_vertiefungen(topic)
|
||||
bausteine = {
|
||||
p["baustein"]: {
|
||||
"gute_antworten": p["gute_antworten"],
|
||||
"absolviert": p["absolviert"] is not None,
|
||||
"vertiefung": p["baustein"] in mit_vertiefung,
|
||||
}
|
||||
for p in progress
|
||||
}
|
||||
for b in mit_vertiefung - set(bausteine):
|
||||
bausteine[b] = {"gute_antworten": 0, "absolviert": False, "vertiefung": True}
|
||||
return {"bausteine": bausteine}
|
||||
|
||||
|
||||
@router.get("/bausteine/vertiefung", response_model=VertiefungResponse)
|
||||
async def get_baustein_vertiefung(topic: str, baustein: str):
|
||||
md = await get_vertiefung(topic, baustein)
|
||||
if md is None:
|
||||
raise HTTPException(404, "Keine Vertiefung vorhanden")
|
||||
return {"md": md}
|
||||
|
||||
|
||||
@router.post("/bausteine/vertiefung", response_model=VertiefungResponse)
|
||||
async def create_baustein_vertiefung(req: VertiefungRequest):
|
||||
md = await vertiefung_generieren(req.topic, req.baustein, req.section, provider=req.provider)
|
||||
if md is None:
|
||||
raise HTTPException(502, "Vertiefung fehlgeschlagen — bitte erneut versuchen")
|
||||
await set_vertiefung(req.topic, req.baustein, md)
|
||||
return {"md": md}
|
||||
|
||||
|
||||
@router.post("/bausteine/chat", response_model=BausteinChatResponse)
|
||||
async def baustein_chat_route(req: BausteinChatRequest):
|
||||
vertiefung = await get_vertiefung(req.topic, req.baustein)
|
||||
reply = await baustein_chat(
|
||||
req.topic, req.baustein, req.section, vertiefung,
|
||||
[m.model_dump() for m in req.messages], provider=req.provider,
|
||||
)
|
||||
return {"reply": reply}
|
||||
|
||||
|
||||
@router.post("/bausteine/pruefung", response_model=BausteinPruefungResponse)
|
||||
async def baustein_pruefung_route(req: BausteinPruefungRequest):
|
||||
stand = next(
|
||||
(p for p in await list_baustein_progress(req.topic) if p["baustein"] == req.baustein),
|
||||
{"gute_antworten": 0, "absolviert": None},
|
||||
)
|
||||
vertiefung = await get_vertiefung(req.topic, req.baustein)
|
||||
data = await baustein_pruefung(
|
||||
req.topic, req.baustein, req.section, vertiefung,
|
||||
[m.model_dump() for m in req.messages], stand["gute_antworten"], provider=req.provider,
|
||||
)
|
||||
if data is None:
|
||||
raise HTTPException(502, "Prüfung fehlgeschlagen — bitte erneut versuchen")
|
||||
|
||||
gute = stand["gute_antworten"]
|
||||
if data["bewertung"] == "gut":
|
||||
gute = await add_gute_antwort(req.topic, req.baustein)
|
||||
absolviert = stand["absolviert"] is not None
|
||||
if gute >= NOETIG or data["bestanden"]:
|
||||
frisch = await set_baustein_absolviert(req.topic, req.baustein)
|
||||
absolviert = True
|
||||
if frisch:
|
||||
asyncio.create_task(baustein_element_anlegen(req.topic, req.baustein, req.section, req.provider))
|
||||
return {"reply": data["reply"], "bewertung": data["bewertung"], "gute_antworten": gute, "absolviert": absolviert}
|
||||
|
||||
|
||||
# --- Guides ---
|
||||
|
||||
@router.post("/guides", response_model=GuideResponse)
|
||||
async def create(req: GuideCreateRequest):
|
||||
guides, progress = await lade_lernstand()
|
||||
grund = guide_lock(req.topic.strip(), req.format, guides, progress)
|
||||
guides, progress, bausteine_done = await lade_lernstand()
|
||||
grund = guide_lock(req.topic.strip(), req.format, guides, progress, bausteine_done)
|
||||
if grund:
|
||||
raise HTTPException(400 if grund == "Erst Bausteine erstellen" else 409, grund)
|
||||
await create_topic(req.topic.strip())
|
||||
@@ -171,8 +251,8 @@ async def list_all():
|
||||
@router.get("/guides/locks")
|
||||
async def guide_locks(topic: str):
|
||||
"""Sperr-Gründe pro Format für den ▶-Button — None = erstellbar."""
|
||||
guides, progress = await lade_lernstand()
|
||||
return {fmt: guide_lock(topic, fmt, guides, progress) for fmt in ("OnePager", *FORMATE)}
|
||||
guides, progress, bausteine_done = await lade_lernstand()
|
||||
return {fmt: guide_lock(topic, fmt, guides, progress, bausteine_done) for fmt in ("OnePager", *FORMATE)}
|
||||
|
||||
|
||||
@router.get("/guides/{guide_id}", response_model=GuideResponse)
|
||||
|
||||
Reference in New Issue
Block a user