FX-7: marking completion status + max_marks validation
batches.py upsert_mark previously never advanced a submission or batch to 'complete', and never validated an award against the question's max. - Reject (422) an awarded_marks that exceeds the question's max_marks — only when a max is actually set (0/None = not-yet-scored AI/unmapped question, unvalidatable). - _advance_completion: a submission with a mark for every markable (leaf) question → 'complete'; a batch whose every non-absent submission is complete → 'complete'. Container questions and absent students are excluded; the helper only promotes, never regresses, so it is safe on every upsert. Adds test_upsert_mark_rejects_over_max and test_upsert_mark_completes_submission_and_batch. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6c73174829
commit
931e254b93
@@ -249,6 +249,30 @@ def test_upsert_mark_submission_404():
|
||||
assert c.put("/api/exam/marks/mk-x", json={"submission_id": "nope", "question_id": "q1", "awarded_marks": 1}).status_code == 404
|
||||
|
||||
|
||||
def test_upsert_mark_rejects_over_max():
|
||||
c = make_client(_batch_with_cohort()) # q1 max_marks = 3
|
||||
r = c.put("/api/exam/marks/mk-over", json={"submission_id": "sub2", "question_id": "q1", "awarded_marks": 4})
|
||||
assert r.status_code == 422
|
||||
|
||||
|
||||
def test_upsert_mark_completes_submission_and_batch():
|
||||
store = base_store(
|
||||
marking_batches=[{"id": "b1", "template_id": TPL, "institute_id": INST_A, "teacher_id": TEACHER, "status": "marking"}],
|
||||
exam_questions=[
|
||||
{"id": "q0", "template_id": TPL, "label": "Q1", "max_marks": 0, "order": 0, "is_container": True},
|
||||
{"id": "q1", "template_id": TPL, "label": "01", "max_marks": 3, "order": 1, "is_container": False},
|
||||
{"id": "q2", "template_id": TPL, "label": "02", "max_marks": 5, "order": 2, "is_container": False},
|
||||
],
|
||||
student_submissions=[{"id": "sub1", "batch_id": "b1", "student_id": "s1", "status": "marking"}],
|
||||
mark_entries=[{"id": "m1", "batch_id": "b1", "submission_id": "sub1", "question_id": "q1", "awarded_marks": 2}],
|
||||
)
|
||||
c = make_client(store)
|
||||
# marking the last leaf question completes the submission (container q0 doesn't block) and the batch
|
||||
assert c.put("/api/exam/marks/m2", json={"submission_id": "sub1", "question_id": "q2", "awarded_marks": 4}).status_code == 200
|
||||
assert next(s for s in store["student_submissions"] if s["id"] == "sub1")["status"] == "complete"
|
||||
assert next(b for b in store["marking_batches"] if b["id"] == "b1")["status"] == "complete"
|
||||
|
||||
|
||||
# ─── scans (E3 guards) ───────────────────────────────────────────────────────
|
||||
|
||||
def _batch_store():
|
||||
|
||||
Reference in New Issue
Block a user