update
This commit is contained in:
@@ -22,7 +22,7 @@ from config import (
|
||||
)
|
||||
import lesbarkeit
|
||||
from database import list_guides, update_guide
|
||||
from fsutil import atomic_write_json
|
||||
from fsutil import atomic_write_json, atomic_write_text
|
||||
from jsonio import read_json_file as _json_datei
|
||||
from paths import bausteine_path, guide_content_path, project_dir, subbausteine_path
|
||||
from pipeline import (
|
||||
@@ -101,6 +101,35 @@ def guide_slot_dateien(content_path: Path) -> list[Path]:
|
||||
return [p for p in content_path.parent.glob(f"{content_path.stem}.*") if p != content_path]
|
||||
|
||||
|
||||
def _fertig_path(content_path: Path) -> Path:
|
||||
return content_path.parent / f"{content_path.stem}.fertig"
|
||||
|
||||
|
||||
def guide_fertig_step(content_path: Path) -> int:
|
||||
"""Höchster VOLL abgeschlossener Schritt-Index (Marker je Thema+Format). -1 = keiner.
|
||||
Existiert die Content-Datei, sind alle Schritte fertig."""
|
||||
if content_path.exists():
|
||||
return len(GUIDE_STEPS) - 1
|
||||
try:
|
||||
return int(_fertig_path(content_path).read_text(encoding="utf-8").strip())
|
||||
except (OSError, ValueError):
|
||||
return -1
|
||||
|
||||
|
||||
def _set_fertig(content_path: Path, step: int) -> None:
|
||||
"""Marker auf `step` setzen — monoton (nur erhöhen), außer beim Re-Run-Reset (force)."""
|
||||
if step > guide_fertig_step(content_path):
|
||||
atomic_write_text(_fertig_path(content_path), str(step))
|
||||
|
||||
|
||||
def _reset_fertig(content_path: Path, step: int) -> None:
|
||||
"""Marker hart auf `step` setzen (für Re-Run ab Schritt; step kann sinken)."""
|
||||
if step < 0:
|
||||
_fertig_path(content_path).unlink(missing_ok=True)
|
||||
else:
|
||||
atomic_write_text(_fertig_path(content_path), str(step))
|
||||
|
||||
|
||||
# Slot-Datei-Globs je Schritt (Index = GUIDE_STEPS). Stem-verankert, kollisionsfrei.
|
||||
_STEP_GLOBS = (
|
||||
("auswahl-*", "auswahl-mapping-*"), # 0 Auswahl (deterministisch → meist leer)
|
||||
@@ -121,6 +150,7 @@ def _reset_guide_ab_step(content_path: Path, step: int) -> None:
|
||||
for pat in globs:
|
||||
for p in d.glob(f"{stem}.{pat}"):
|
||||
p.unlink(missing_ok=True)
|
||||
_reset_fertig(content_path, step - 1) # Schritte < step gelten als fertig
|
||||
|
||||
|
||||
def _resolve_auswahl(data, entries: dict[int, str], k_min: int, k_max: int) -> list[int] | None:
|
||||
@@ -552,6 +582,7 @@ async def _generate_sections(
|
||||
|
||||
# Garantie: jeder gewählte Baustein steht im Plan (gegen weglassende Agenten/Judges).
|
||||
plan = _mit_resten(plan, sel_entries)
|
||||
_set_fertig(content_path, 1) # Gliederung steht
|
||||
|
||||
# Chunks festlegen: 1 Chunk = 1 Baustein. So läuft JEDER Section-Schritt (Inhalte,
|
||||
# Inhalts-Prüfung, Writer, Lese-Check) mit genau einem Agent je Baustein. Der Writer
|
||||
@@ -605,6 +636,8 @@ async def _generate_sections(
|
||||
if not inhalt_by_num:
|
||||
await _fail(guide_id, "Keine Inhalte identifiziert")
|
||||
return None
|
||||
if all(p.exists() for p in inhalt_paths):
|
||||
_set_fertig(content_path, 2) # Inhalte vollständig
|
||||
|
||||
inhalt_chunk_nums = [[num for ch in chunk for num in ch["nums"] if num in inhalt_by_num] for chunk in chunks]
|
||||
|
||||
@@ -670,6 +703,8 @@ async def _generate_sections(
|
||||
if num in probleme_by_num and sec["md"].strip():
|
||||
inhalt_by_num[num] = sec["md"]
|
||||
|
||||
_set_fertig(content_path, 3) # Inhalts-Check durch
|
||||
|
||||
# Schritt 4: Schreiben — Writer formuliert die geprüften Inhalte aus (Resume).
|
||||
def inhalte_text(chunk) -> str:
|
||||
nums = [num for ch in chunk for num in ch["nums"] if num in inhalt_by_num]
|
||||
@@ -718,6 +753,8 @@ async def _generate_sections(
|
||||
if not by_num:
|
||||
await _fail(guide_id, "Keine Sections in der Writer-Ausgabe gefunden")
|
||||
return None
|
||||
if all(p.exists() for p in paths):
|
||||
_set_fertig(content_path, 4) # Schreiben vollständig
|
||||
|
||||
# Schritt 3: Lese-Prüfungs-Loop — Check pro Writer-Paket, Fix nur für
|
||||
# beanstandete Sections; Folgerunden prüfen NUR die ersetzten Sections.
|
||||
@@ -821,6 +858,7 @@ async def _generate_sections(
|
||||
_log(topic, f"Lese-Prüfung: 1 Runde — Überarbeitung bleibt ungeprüft")
|
||||
break
|
||||
scope = [[num for num in nums if num in ersetzt] for nums in chunk_nums]
|
||||
_set_fertig(content_path, 5) # Lese-Prüfung durch
|
||||
|
||||
# Prüfbar = Format hat Prüfung UND Baustein hat ≥1 relevanten Subbaustein.
|
||||
# Guide ist immer prüfbar (auch ohne Relevanz-Daten, Fallback = alles).
|
||||
|
||||
@@ -19,7 +19,7 @@ from database import (
|
||||
from bausteine import generate_bausteine, cancel_bausteine, bausteine_status, active_bausteine, reset_bausteine, lade_quelle, lade_uebersicht, subbausteine_titel, lade_frage_muster
|
||||
from elements import generate_element, chat_with_guide, chat_with_element, check_element, style_element, refine_suggestion
|
||||
from lernen import NOETIG, MASTERY, MEISTERN, NIVEAUS, baustein_chat, baustein_diskussion, baustein_element_anlegen, pruefung_bewertung, pruefung_bewertung_schnell, pruefung_frage, pruefung_frage_variante, quiz_generieren, lueckwahl_generieren, lueckentext_generieren, lueckentext_pruefen, score_berechnen, floor_aus_meilensteinen, _zufall_malus
|
||||
from guide import generate_guide, guide_slot_dateien
|
||||
from guide import generate_guide, guide_slot_dateien, guide_fertig_step
|
||||
from pipeline import cancel_guide
|
||||
from regeln import FORMATE, formate_stats, guide_lock, ist_absolviert, lade_lernstand, thema_abgeschlossen
|
||||
from models import (
|
||||
@@ -474,6 +474,13 @@ async def guide_locks(topic: str):
|
||||
return {fmt: guide_lock(topic, fmt, guides, progress, levels) for fmt in ("FullGuide", "Rest", *FORMATE)}
|
||||
|
||||
|
||||
@router.get("/guides/steps")
|
||||
async def guide_steps(topic: str):
|
||||
"""Höchster voll abgeschlossener Schritt-Index je Format (artefakt-basiert, -1 = keiner).
|
||||
Treibt die klickbaren Schritt-Kugeln (wie die Bausteine-Phasen)."""
|
||||
return {fmt: guide_fertig_step(guide_content_path(topic, fmt)) for fmt in ("Guide", "FullGuide", "Rest")}
|
||||
|
||||
|
||||
@router.get("/guides/{guide_id}", response_model=GuideResponse)
|
||||
async def get_one(guide_id: str):
|
||||
guide = await get_guide(guide_id)
|
||||
|
||||
Reference in New Issue
Block a user