fix(exam): blank total only for absent AND unmarked; flip status on mark
A roster student starts 'absent' and a direct mark would otherwise still show a blank total. Now total is blank only when absent with no marks; recording a mark advances the submission out of absent/unmatched to 'marking'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a1d297ac30
commit
62234dbbcb
@ -187,8 +187,14 @@ def _assemble_results(ctx: ExamContext, batch: Dict[str, Any]) -> Dict[str, Any]
|
||||
results = []
|
||||
for s in submissions: # every submission incl. absent → A7
|
||||
sub_marks = by_sub.get(s["id"], {})
|
||||
is_absent = s.get("status") == "absent"
|
||||
total = None if is_absent else sum(v or 0 for v in sub_marks.values())
|
||||
# Blank total ONLY for a genuine no-show (absent AND nothing marked). A student with any
|
||||
# mark gets a real total regardless of status; a present-but-unmarked student totals 0.
|
||||
if sub_marks:
|
||||
total = sum(v or 0 for v in sub_marks.values())
|
||||
elif s.get("status") == "absent":
|
||||
total = None
|
||||
else:
|
||||
total = 0
|
||||
results.append({
|
||||
"submission_id": s["id"],
|
||||
"student_id": s.get("student_id"),
|
||||
@ -248,7 +254,7 @@ async def upsert_mark(
|
||||
# Derive batch_id from the submission (as-user read → also enforces the caller owns the batch
|
||||
# the submission belongs to). The client never supplies the RLS scoping key directly.
|
||||
submission = _first(
|
||||
ctx.supabase.table("student_submissions").select("id, batch_id").eq("id", body.submission_id).limit(1).execute()
|
||||
ctx.supabase.table("student_submissions").select("id, batch_id, status").eq("id", body.submission_id).limit(1).execute()
|
||||
)
|
||||
if not submission:
|
||||
raise HTTPException(status_code=404, detail="Submission not found")
|
||||
@ -273,6 +279,12 @@ async def upsert_mark(
|
||||
upserted = _first(ctx.supabase.table("mark_entries").upsert(row).execute())
|
||||
if not upserted:
|
||||
raise HTTPException(status_code=500, detail="Failed to upsert mark")
|
||||
|
||||
# A marked student is, by definition, not absent — advance the submission out of the
|
||||
# no-submission states so results/queue reflect that marking has started.
|
||||
if submission.get("status") in ("absent", "unmatched"):
|
||||
ctx.supabase.table("student_submissions").update({"status": "marking"}).eq("id", body.submission_id).execute()
|
||||
|
||||
return upserted
|
||||
|
||||
|
||||
|
||||
@ -233,6 +233,17 @@ def test_upsert_mark_derives_batch_and_roundtrips():
|
||||
assert sum(1 for m in store["mark_entries"] if m["id"] == "mk-1") == 1
|
||||
|
||||
|
||||
def test_upsert_mark_flips_absent_submission_to_marking():
|
||||
store = _batch_with_cohort() # sub2 starts 'absent'
|
||||
c = make_client(store)
|
||||
c.put("/api/exam/marks/mk-2", json={"submission_id": "sub2", "question_id": "q1", "awarded_marks": 2})
|
||||
sub2 = next(s for s in store["student_submissions"] if s["id"] == "sub2")
|
||||
assert sub2["status"] == "marking"
|
||||
# results now show a real total for the (formerly absent) marked student
|
||||
res = {r["submission_id"]: r for r in c.get("/api/exam/batches/b1/results").json()["results"]}
|
||||
assert res["sub2"]["total"] == 2
|
||||
|
||||
|
||||
def test_upsert_mark_submission_404():
|
||||
c = make_client(_batch_with_cohort())
|
||||
assert c.put("/api/exam/marks/mk-x", json={"submission_id": "nope", "question_id": "q1", "awarded_marks": 1}).status_code == 404
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user