study-partner / test_weak_topic.py
nz-nz's picture
Deploy Recall study-partner app (stub-mode demo)
7563305 verified
Raw
History Blame Contribute Delete
2.97 kB
"""
NAH-13 — weak-topic targeting: next_card biases toward low-average-grade topics.
No model needed (pure scheduling logic). Run: python3 test_weak_topic.py
"""
import learning_engine as le
from schema import new_card
def _deck():
return [
new_card("strong q1", "a", topic="Algebra", card_id="alg1"),
new_card("strong q2", "a", topic="Algebra", card_id="alg2"),
new_card("weak q1", "a", topic="Geometry", card_id="geo1"),
new_card("weak q2", "a", topic="Geometry", card_id="geo2"),
]
def test_no_history_keeps_order():
s = le.init_session(_deck())
# No answers yet -> serve the queue head untouched.
assert le.next_card(s)["id"] == "alg1"
assert s["queue"][0] == "alg1"
print("ok no history -> normal queue order")
def test_weak_topic_surfaces_first():
s = le.init_session(_deck())
# Simulate: aced Algebra, bombed Geometry.
s["history"] = [
{"card_id": "alg1", "topic": "Algebra", "grade": 5, "user_answer": ""},
{"card_id": "geo1", "topic": "Geometry", "grade": 1, "user_answer": ""},
]
# Queue still leads with an Algebra card, but a weak Geometry card is in reach.
s["queue"] = ["alg2", "geo2", "alg1"]
card = le.next_card(s)
assert card["topic"] == "Geometry", f"expected weak topic first, got {card}"
assert s["queue"][0] == card["id"], "chosen card must be rotated to the front"
print("ok weak topic (Geometry) surfaced ahead of strong topic")
def test_lookahead_window_respected():
s = le.init_session(_deck())
s["history"] = [{"card_id": "geo1", "topic": "Geometry", "grade": 0, "user_answer": ""}]
# Weak Geometry card sits beyond the lookahead window -> not pulled forward.
s["queue"] = ["alg1", "alg2", "alg2", "alg1", "geo2"]
le.WEAK_LOOKAHEAD # window is 4; geo2 is at index 4
card = le.next_card(s)
assert card["topic"] == "Algebra", "weak card outside window should not jump"
print("ok weak card beyond lookahead window left in place")
def test_apply_result_contract_holds():
# next_card rotates the served card to front; apply_result pops the front.
s = le.init_session(_deck())
s["history"] = [{"card_id": "geo1", "topic": "Geometry", "grade": 1, "user_answer": ""}]
s["queue"] = ["alg1", "geo2"]
card = le.next_card(s)
assert card["id"] == "geo2"
before = len(s["queue"])
le.apply_result(s, card, {"score": 4, "correct": True,
"explanation": "", "missed_concept": ""})
assert "geo2" not in s["queue"][:1], "served card should be popped from front"
assert len(s["queue"]) == before, "popped one, re-inserted one"
print("ok apply_result still pops the served card from the front")
if __name__ == "__main__":
test_no_history_keeps_order()
test_weak_topic_surfaces_first()
test_lookahead_window_respected()
test_apply_result_contract_holds()
print("\nAll NAH-13 weak-topic tests passed.")