Add per-part true/false breakdown to exam review summary for enhanced clarity

This commit is contained in:
Ross
2025-11-10 13:05:04 +00:00
parent 66899b54bd
commit f3a0a9fd8a
3 changed files with 136 additions and 4 deletions
+63 -2
View File
@@ -518,6 +518,11 @@ def exam_review_question_summary(request, pk: int, q_index: int):
for k in counts.keys():
pcts[k] = 0.0
# Provide an explicit iterable of (key, count) pairs for templates to consume.
# This avoids relying on attribute-resolution like `exam_response_counts.items`
# in templates which can be ambiguous in Django's template variable lookup.
exam_response_items = sorted(counts.items(), key=lambda x: x[1], reverse=True)
# Compute percent correct using per-answer scoring
correct_count = None
correct_pct = None
@@ -555,6 +560,60 @@ def exam_review_question_summary(request, pk: int, q_index: int):
correct_count = None
correct_pct = None
# Per-part (a-e) true/false aggregation. Each physics question has 5 parts
# stored as boolean fields on UserAnswer (a,b,c,d,e). Build counts and
# percentages for each part so templates can render them as separate TF
# sub-questions.
per_part_stats = []
try:
parts = ["a", "b", "c", "d", "e"]
# initialize counters
for idx, part in enumerate(parts):
per_part_stats.append({
"part": part,
"label": getattr(question, part).strip() if getattr(question, part, None) else f"Part {idx+1}",
"true_count": 0,
"false_count": 0,
"total": 0,
"correct_count": 0,
"correct_pct": 0.0,
"pct_true": 0.0,
})
for ua in ua_qs:
for idx, part in enumerate(parts):
try:
val = getattr(ua, part)
except Exception:
continue
if val is True:
per_part_stats[idx]["true_count"] += 1
elif val is False:
per_part_stats[idx]["false_count"] += 1
# count only explicit booleans as total
if isinstance(val, bool):
per_part_stats[idx]["total"] += 1
# correct counting
try:
correct_val = getattr(question, f"{part}_answer")
except Exception:
correct_val = None
if isinstance(val, bool) and isinstance(correct_val, bool):
if val == correct_val:
per_part_stats[idx]["correct_count"] += 1
# compute percentages
for stats in per_part_stats:
if stats["total"]:
stats["pct_true"] = round(100.0 * stats["true_count"] / stats["total"], 1)
stats["correct_pct"] = round(100.0 * stats["correct_count"] / stats["total"], 1)
else:
stats["pct_true"] = 0.0
stats["correct_pct"] = 0.0
except Exception:
per_part_stats = []
# Provide a best-effort correct answer display
correct_answer = None
try:
@@ -571,13 +630,15 @@ def exam_review_question_summary(request, pk: int, q_index: int):
"question": question,
"q_index": q_index,
"app_name": "physics",
"exam_response_counts": counts,
"exam_response_pcts": pcts,
"exam_response_counts": counts,
"exam_response_items": exam_response_items,
"exam_response_pcts": pcts,
"exam_response_total": total_responses,
"exam_response_correct_count": correct_count,
"exam_response_correct_pct": correct_pct,
"exam_response_texts": {},
"correct_answer": correct_answer,
"per_part_stats": per_part_stats,
}
return render(request, "physics/partials/exam_review_question_summary_fragment.html", context)