1
0
Files
coldcall_lti/tests/test_selection.py
Benjamin Mako Hill 8d8a463f81 Phase 1: scaffold, data models, selection logic, tests
Flask application skeleton with SQLAlchemy models for courses (including
per-course settings), students, enrollments, opt-outs, and calls; the
weighted and cycle selection logic ported from the manual coldcall
scripts; alembic migrations; and a pytest suite covering selection
behavior and the model query helpers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 16:27:09 -07:00

46 lines
1.4 KiB
Python

import random
from collections import Counter
from coldcall_lti import selection
STUDENTS = ["a", "b", "c", "d"]
def test_weights_halve_per_answered_call():
weights = selection.compute_weights(STUDENTS, {"a": 0, "b": 1, "c": 3}, 2.0)
assert weights == {"a": 1.0, "b": 0.5, "c": 0.125, "d": 1.0}
def test_weight_factor_one_is_uniform():
weights = selection.compute_weights(STUDENTS, {"a": 5, "b": 2}, 1.0)
assert set(weights.values()) == {1.0}
def test_heavily_called_student_selected_less():
rng = random.Random(42)
counts = Counter(
selection.select_student(STUDENTS, {"a": 4}, 2.0, rng)
for _ in range(4000)
)
# "a" has weight 1/16 against 1 for the others, so it should get
# roughly 1/49th of the picks; the others roughly a third each.
assert counts["a"] < 250
for s in ("b", "c", "d"):
assert 1000 < counts[s] < 1700
def test_generate_call_list_downweights_within_list():
rng = random.Random(7)
picks = selection.generate_call_list(STUDENTS, {}, 200, 2.0, rng)
assert len(picks) == 200
counts = Counter(picks)
# Sequential downweighting keeps the distribution close to even.
assert all(35 < counts[s] < 65 for s in STUDENTS)
def test_generate_cycle_list_covers_everyone_once():
rng = random.Random(1)
picks = selection.generate_cycle_list(STUDENTS, rng)
assert sorted(picks) == sorted(STUDENTS)