List lifecycle polish and cycle batches, from live walkthrough feedback
Generating a call list is non-destructive by intent: the button reads Regenerate when an unused list exists and replaces it freely, with a confirmation only when the day already has recorded outcomes (which are always kept). The print view shows resolved calls' outcomes, offers generate/regenerate in place, and the home page links to the existing list. The day editor now supports adding calls after the fact (any roster student, with status/assessment/note), completing full editability: add, edit, delete. Cycle mode is now a true rolling rotation driven by non-skipped call counts: batches of n take the next students of the current pass, remainder batches come up short rather than wrapping, a new pass starts when everyone has been called, and the cycle continues across lists and class days. Live mode uses the same rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,8 +63,16 @@ def test_generate_and_print(instructor):
|
||||
assert page.count("<tr>") == 13 # header + 12 rows
|
||||
|
||||
|
||||
def test_regenerate_replaces_pending(instructor):
|
||||
def test_regenerate_replaces_pending_and_labels_change(instructor):
|
||||
page = instructor.get("/instructor/").get_data(as_text=True)
|
||||
assert "Generate list for" in page
|
||||
|
||||
instructor.post(f"/instructor/day/{TODAY}/generate", data={"n": "12"})
|
||||
page = instructor.get("/instructor/").get_data(as_text=True)
|
||||
assert "View today's list" in page
|
||||
assert "Regenerate list" in page
|
||||
|
||||
# Regenerating replaces the unused list outright.
|
||||
resp = instructor.post(
|
||||
f"/instructor/day/{TODAY}/generate",
|
||||
data={"n": "5"},
|
||||
@@ -72,6 +80,47 @@ def test_regenerate_replaces_pending(instructor):
|
||||
)
|
||||
assert resp.get_data(as_text=True).count("<tr>") == 6
|
||||
|
||||
# No recorded outcomes yet: no confirmation dialog wired up.
|
||||
page = instructor.get(f"/instructor/day/{TODAY}/print").get_data(as_text=True)
|
||||
assert "onsubmit" not in page
|
||||
|
||||
# Record one outcome; the regenerate form now carries a warning.
|
||||
edit = instructor.get(f"/instructor/day/{TODAY}").get_data(as_text=True)
|
||||
call_id = re.findall(r'name="status-(\d+)"', edit)[0]
|
||||
instructor.post(
|
||||
f"/instructor/day/{TODAY}",
|
||||
data={f"status-{call_id}": "answered"},
|
||||
)
|
||||
page = instructor.get(f"/instructor/day/{TODAY}/print").get_data(as_text=True)
|
||||
assert "already have recorded outcomes" in page
|
||||
|
||||
|
||||
def test_add_call_after_the_fact(instructor):
|
||||
page = instructor.get(f"/instructor/day/{TODAY}").get_data(as_text=True)
|
||||
student_id = re.search(r'<option value="(\d+)">', page).group(1)
|
||||
good_id = re.search(r'value="(\d+)">GOOD</option>', page).group(1)
|
||||
|
||||
resp = instructor.post(
|
||||
f"/instructor/day/{TODAY}/add",
|
||||
data={
|
||||
"student_id": student_id,
|
||||
"status": "answered",
|
||||
"assessment": good_id,
|
||||
"note": "volunteered",
|
||||
},
|
||||
follow_redirects=True,
|
||||
)
|
||||
page = resp.get_data(as_text=True)
|
||||
assert len(re.findall(r'name="status-(\d+)"', page)) == 1
|
||||
assert 'value="volunteered"' in page
|
||||
|
||||
# Junk student ids are rejected.
|
||||
resp = instructor.post(
|
||||
f"/instructor/day/{TODAY}/add",
|
||||
data={"student_id": "99999", "status": "answered"},
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_day_edit_saves_outcomes(instructor):
|
||||
instructor.post(f"/instructor/day/{TODAY}/generate", data={"n": "2"})
|
||||
|
||||
Reference in New Issue
Block a user