Refine Technique Path Scope Logic and Enhance Test Coverage
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m24s
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m24s
- Updated the `exercise_passes_technique_path_scope` function to clarify the requirements for technique inclusion, ensuring that the primary technique must appear in the exercise text. - Enhanced the logic to allow for relaxed matching based on parts of the primary topic, improving flexibility in exercise validation. - Added new tests to validate the rejection of off-topic exercises, specifically addressing cases where only stage goals mention the primary technique. - Improved the selection logic in `pick_best_path_hit` to ensure proper handling of roadmap stage matches.
This commit is contained in:
parent
713a344d17
commit
f63b09fc9c
|
|
@ -207,7 +207,10 @@ def exercise_passes_technique_path_scope(
|
||||||
relaxed: bool = False,
|
relaxed: bool = False,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Technik-Pfad: keine Geschwister-Technik; Haupttechnik im Übungstext oder Stufen-Lernziel.
|
Technik-Pfad: keine Geschwister-Technik; Haupttechnik muss im Übungstext vorkommen.
|
||||||
|
|
||||||
|
Das Stufen-Lernziel allein reicht nicht — sonst würden themenfremde Übungen (z. B. Kumite)
|
||||||
|
nur wegen „Mawashi Geri“ im Lernziel durch das Gate rutschen.
|
||||||
"""
|
"""
|
||||||
primary = _normalize_phrase(primary_topic)
|
primary = _normalize_phrase(primary_topic)
|
||||||
if not primary:
|
if not primary:
|
||||||
|
|
@ -218,11 +221,14 @@ def exercise_passes_technique_path_scope(
|
||||||
if excludes and _blob_matches_stage_excludes(blob, excludes):
|
if excludes and _blob_matches_stage_excludes(blob, excludes):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
in_exercise = _phrase_in_blob(primary, blob)
|
if _phrase_in_blob(primary, blob):
|
||||||
in_stage = _phrase_in_blob(primary, _blob_from_fields("", "", learning_goal, []))
|
|
||||||
if in_exercise or in_stage:
|
|
||||||
return True
|
return True
|
||||||
return relaxed
|
|
||||||
|
if relaxed:
|
||||||
|
parts = [p for p in primary.split() if len(p) >= 4]
|
||||||
|
if parts and any(_phrase_in_blob(part, blob) for part in parts):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _detect_development_arc(q_lower: str) -> List[str]:
|
def _detect_development_arc(q_lower: str) -> List[str]:
|
||||||
|
|
@ -1245,15 +1251,16 @@ def pick_best_path_hit(
|
||||||
return best
|
return best
|
||||||
|
|
||||||
chosen = _scan(strict=True)
|
chosen = _scan(strict=True)
|
||||||
if chosen:
|
|
||||||
return chosen
|
|
||||||
chosen = _scan(strict=False)
|
|
||||||
if chosen:
|
if chosen:
|
||||||
return chosen
|
return chosen
|
||||||
|
|
||||||
if roadmap_stage_match:
|
if roadmap_stage_match:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
chosen = _scan(strict=False)
|
||||||
|
if chosen:
|
||||||
|
return chosen
|
||||||
|
|
||||||
# Notfall (nur retrieval-first / Brücken): bester verbleibender Treffer
|
# Notfall (nur retrieval-first / Brücken): bester verbleibender Treffer
|
||||||
fallback: Optional[Dict[str, Any]] = None
|
fallback: Optional[Dict[str, Any]] = None
|
||||||
fallback_key: Tuple[float, float] = (-1.0, -1.0)
|
fallback_key: Tuple[float, float] = (-1.0, -1.0)
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,62 @@ def test_pick_best_skips_kumite_for_mawashi_athletic_path():
|
||||||
assert int(chosen["id"]) == 2
|
assert int(chosen["id"]) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_technique_scope_rejects_kumite_when_only_stage_goal_mentions_mawashi():
|
||||||
|
siblings = technique_sibling_excludes("mawashi geri")
|
||||||
|
assert not exercise_passes_technique_path_scope(
|
||||||
|
primary_topic="mawashi geri",
|
||||||
|
title="Kumite Grundstellungen",
|
||||||
|
summary="Partner-Distanz und freier Kampf",
|
||||||
|
goal="Kumite-Technik",
|
||||||
|
learning_goal="Sprungkraft und Koordination für Mawashi Geri",
|
||||||
|
sibling_excludes=siblings,
|
||||||
|
relaxed=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pick_best_roadmap_rejects_kumite_with_path_primary_topic():
|
||||||
|
q = "gesprungener Mawashi Geri Sprungphase"
|
||||||
|
brief = enrich_brief_with_path_constraints(build_semantic_brief(q), q)
|
||||||
|
primary = (brief.primary_topic or "mawashi geri").strip()
|
||||||
|
stage_goal = "Sprungvorbereitung für Mawashi Geri"
|
||||||
|
stage_brief = build_stage_match_brief(
|
||||||
|
learning_goal=stage_goal,
|
||||||
|
path_primary_topic=primary,
|
||||||
|
path_technique_excludes=technique_sibling_excludes(primary),
|
||||||
|
)
|
||||||
|
hits = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Kumite Distanztraining",
|
||||||
|
"summary": "Partnerarbeit",
|
||||||
|
"goal": "Kampfvorbereitung",
|
||||||
|
"score": 0.95,
|
||||||
|
"semantic_score": 0.4,
|
||||||
|
"stage_semantic_score": 0.35,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "Sprungkraft Plyometrie",
|
||||||
|
"summary": "Absprung für Tritttechnik",
|
||||||
|
"goal": "Sprungkraft Mawashi Geri Vorbereitung",
|
||||||
|
"score": 0.7,
|
||||||
|
"semantic_score": 0.45,
|
||||||
|
"stage_semantic_score": 0.42,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
chosen = pick_best_path_hit(
|
||||||
|
hits,
|
||||||
|
set(),
|
||||||
|
stage_learning_goal=stage_goal,
|
||||||
|
roadmap_stage_match=True,
|
||||||
|
stage_match_brief=stage_brief,
|
||||||
|
path_primary_topic=primary,
|
||||||
|
path_technique_excludes=technique_sibling_excludes(primary),
|
||||||
|
)
|
||||||
|
assert chosen is not None
|
||||||
|
assert int(chosen["id"]) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_technique_scope_rejects_sibling_geri_for_mawashi_path():
|
def test_technique_scope_rejects_sibling_geri_for_mawashi_path():
|
||||||
siblings = technique_sibling_excludes("mawashi geri")
|
siblings = technique_sibling_excludes("mawashi geri")
|
||||||
assert any("mae" in s for s in siblings)
|
assert any("mae" in s for s in siblings)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user