This commit is contained in:
Team3
2026-06-07 12:18:07 +02:00
parent b414d7f464
commit 1649a046d2
4 changed files with 59 additions and 12 deletions

View File

@@ -46,10 +46,31 @@ async def get_topics():
return db_topics + sorted(derived - set(db_topics))
# Lernschulden-Regel: JE Format (MiniGuide/Guide/FullGuide) höchstens 5 erstellte,
# aber nicht absolvierte Guides. Darüber sind nur Neu-Generierungen bereits
# erstellter Guides erlaubt. Themen, Bausteine und OnePager sind unbegrenzt.
MAX_OFFENE_GUIDES = 5
# Lernschulden-Regeln (nur Neu-Erstellungen; Themen, Bausteine, OnePager unbegrenzt):
# - JE Format (MiniGuide/Guide/FullGuide) höchstens 3 erstellte, nicht absolvierte Guides
# - Progression pro Thema: Guide erst nach absolviertem MiniGuide, FullGuide erst nach absolviertem Guide
MAX_OFFENE_GUIDES = 3
VORSTUFE = {"Guide": "MiniGuide", "FullGuide": "Guide"}
async def _ist_absolviert(topic: str, fmt: str) -> bool:
"""Alle Kapitel des neuesten fertigen Guides (Thema+Format) abgehakt?"""
neueste = None
for g in await list_guides():
if g["topic"] == topic and g["format"] == fmt and g["status"] == "done":
if neueste is None or g["created_at"] > neueste["created_at"]:
neueste = g
if neueste is None:
return False
path = guide_content_path(topic, fmt)
if not path.exists():
return False
try:
chapters = json.loads(path.read_text(encoding="utf-8")).get("chapters", [])
except ValueError:
return False
titles = {c.get("title") for c in chapters}
return bool(titles) and titles <= set(await list_progress(neueste["id"]))
async def _formate_stats() -> dict:
@@ -88,6 +109,12 @@ async def get_stats():
return {"themen": len(themen), "formate": await _formate_stats()}
@router.get("/topics/fortschritt")
async def topic_fortschritt(topic: str):
"""Absolviert-Status pro Format — fürs Freischalten der nächsten Ausbaustufe."""
return {fmt: await _ist_absolviert(topic, fmt) for fmt in ("MiniGuide", "Guide", "FullGuide")}
@router.post("/topics")
async def add_topic(req: TopicCreateRequest):
await create_topic(req.name.strip())
@@ -169,10 +196,13 @@ async def create(req: GuideCreateRequest):
for g in await list_guides():
if g["topic"] == req.topic.strip() and g["format"] == req.format and g["status"] in ("queued", "generating"):
raise HTTPException(409, "Generierung läuft bereits")
# Lernschulden-Regel: neue Guides nur, wenn das Format weniger als 5 offene hat (erstellt, nicht absolviert).
# Resume (Schritt-Dateien vorhanden) ist ausgenommen — der Guide wurde bereits angefangen.
# Lernschulden-Regeln — nur für Neu-Erstellungen; Resume (Schritt-Dateien
# vorhanden) und Neu-Generieren bestehender Guides sind ausgenommen.
content = guide_content_path(req.topic.strip(), req.format)
if req.format != "OnePager" and not content.exists() and not guide_slot_dateien(content):
vorstufe = VORSTUFE.get(req.format)
if vorstufe and not await _ist_absolviert(req.topic.strip(), vorstufe):
raise HTTPException(409, f"Erst den {vorstufe} dieses Themas absolvieren")
stat = (await _formate_stats()).get(req.format, {"erstellt": 0, "absolviert": 0})
offen = stat["erstellt"] - stat["absolviert"]
if offen >= MAX_OFFENE_GUIDES: