File size: 2,968 Bytes
7563305
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
"""
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.")