update
This commit is contained in:
@@ -11,7 +11,7 @@ query loops per guide.
|
||||
|
||||
import json
|
||||
|
||||
from database import list_block_scores_all, subs_per_level_all, list_guides, list_progress_all
|
||||
from database import list_block_scores_all, subs_per_level_all, list_guides
|
||||
from guide import guide_slot_files
|
||||
from learning import cap_final, LEVELS, _threshold
|
||||
from paths import blocks_path, guide_content_path
|
||||
@@ -33,8 +33,8 @@ _LEVEL_WORT = {
|
||||
}
|
||||
|
||||
|
||||
async def load_learnstate() -> tuple[list[dict], dict[str, set[str]], dict[str, dict[str, set[str]]]]:
|
||||
"""Guides + chapter progress + blocks per level.
|
||||
async def load_learnstate() -> tuple[list[dict], dict[str, dict[str, set[str]]]]:
|
||||
"""Guides + blocks per level.
|
||||
|
||||
levels: {"beginner"/"advanced"/"expert"/"master": {topic → normalized title}}.
|
||||
The level per block is derived from score + cap (4×relevant subs).
|
||||
@@ -47,7 +47,7 @@ async def load_learnstate() -> tuple[list[dict], dict[str, set[str]], dict[str,
|
||||
for key, p in LEVELS:
|
||||
if cf and score >= _threshold(p, cf):
|
||||
levels[key].setdefault(topic, set()).add(_norm_title(block))
|
||||
return await list_guides(), await list_progress_all(), levels
|
||||
return await list_guides(), levels
|
||||
|
||||
|
||||
def _content_json(topic: str, fmt: str) -> dict | None:
|
||||
@@ -84,39 +84,39 @@ def _latest_done(guides: list[dict], fmt: str) -> dict[str, dict]:
|
||||
return latest
|
||||
|
||||
|
||||
def _guide_all(g: dict, progress: dict[str, set[str]], levelset: dict[str, set[str]]) -> bool:
|
||||
def _guide_all(g: dict, levelset: dict[str, set[str]]) -> bool:
|
||||
"""Are ALL blocks of the guide at the required level?"""
|
||||
sections = _section_title(g["topic"], g["format"])
|
||||
return bool(sections) and sections <= levelset.get(g["topic"], set())
|
||||
|
||||
|
||||
def is_level(topic: str, fmt: str, guides: list[dict], progress: dict[str, set[str]], levelset: dict[str, set[str]]) -> bool:
|
||||
def is_level(topic: str, fmt: str, guides: list[dict], levelset: dict[str, set[str]]) -> bool:
|
||||
"""Latest finished guide (topic+format): all blocks at the level of levelset?"""
|
||||
g = _latest_done(guides, fmt).get(topic)
|
||||
return g is not None and _guide_all(g, progress, levelset)
|
||||
return g is not None and _guide_all(g, levelset)
|
||||
|
||||
|
||||
def ist_completed(topic: str, fmt: str, guides: list[dict], progress: dict[str, set[str]], levels: dict[str, dict[str, set[str]]]) -> bool:
|
||||
def ist_completed(topic: str, fmt: str, guides: list[dict], levels: dict[str, dict[str, set[str]]]) -> bool:
|
||||
"""All blocks of the latest finished guide at least beginner (≥20%)?"""
|
||||
return is_level(topic, fmt, guides, progress, levels["beginner"])
|
||||
return is_level(topic, fmt, guides, levels["beginner"])
|
||||
|
||||
|
||||
def topic_completed(topic: str, guides: list[dict], progress: dict[str, set[str]], levels: dict[str, dict[str, set[str]]]) -> bool:
|
||||
def topic_completed(topic: str, guides: list[dict], levels: dict[str, dict[str, set[str]]]) -> bool:
|
||||
"""Topic done: latest finished guide, all blocks at master (100%)?"""
|
||||
return is_level(topic, "Guide", guides, progress, levels["master"])
|
||||
return is_level(topic, "Guide", guides, levels["master"])
|
||||
|
||||
|
||||
def formats_stats(guides: list[dict], progress: dict[str, set[str]], levels: dict[str, dict[str, set[str]]]) -> dict:
|
||||
def formats_stats(guides: list[dict], levels: dict[str, dict[str, set[str]]]) -> dict:
|
||||
"""Per format created/completed — per topic only the latest finished guide counts."""
|
||||
formats = {}
|
||||
for fmt in FORMATE:
|
||||
latest = _latest_done(guides, fmt)
|
||||
completed = sum(1 for g in latest.values() if _guide_all(g, progress, levels["beginner"]))
|
||||
completed = sum(1 for g in latest.values() if _guide_all(g, levels["beginner"]))
|
||||
formats[fmt] = {"created": len(latest), "completed": completed}
|
||||
return formats
|
||||
|
||||
|
||||
def guide_lock(topic: str, fmt: str, guides: list[dict], progress: dict[str, set[str]], levels: dict[str, dict[str, set[str]]]) -> str | None:
|
||||
def guide_lock(topic: str, fmt: str, guides: list[dict], levels: dict[str, dict[str, set[str]]]) -> str | None:
|
||||
"""Reason why a fresh start for topic+format is locked — None = allowed.
|
||||
|
||||
Exactly the rules from POST /guides: blocks required, no duplicate start,
|
||||
@@ -132,9 +132,9 @@ def guide_lock(topic: str, fmt: str, guides: list[dict], progress: dict[str, set
|
||||
prereq = PRESTAGE.get(fmt)
|
||||
if prereq:
|
||||
level = FREISCHALT_LEVEL[fmt] # completed=10 · understood=20 · mastered=30
|
||||
if not is_level(topic, prereq, guides, progress, levels[level]):
|
||||
if not is_level(topic, prereq, guides, levels[level]):
|
||||
return f"First take the {prereq} of this topic {_LEVEL_WORT[level]}"
|
||||
stat = formats_stats(guides, progress, levels).get(fmt, {"created": 0, "completed": 0})
|
||||
stat = formats_stats(guides, levels).get(fmt, {"created": 0, "completed": 0})
|
||||
open_count = stat["created"] - stat["completed"]
|
||||
if open_count >= MAX_OFFENE_GUIDES:
|
||||
return f"Complete {fmt}s first — at most {MAX_OFFENE_GUIDES} open allowed ({open_count} open)"
|
||||
|
||||
Reference in New Issue
Block a user