Add HTMX-based response summary and user responses display for exam questions

This commit is contained in:
Ross
2025-11-09 22:12:18 +00:00
parent 3e9a96d99f
commit d482b77430
5 changed files with 266 additions and 0 deletions
+77
View File
@@ -80,6 +80,7 @@ from helpers.images import image_as_base64
from django.template.defaulttags import register
from generic.mixins import CheckCanEditMixin, SuperuserRequiredMixin
from django.db.models import Count
class AuthorOrCheckerRequiredMixin(object):
@@ -259,6 +260,82 @@ def mark(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False):
n = sk
@login_required
def exam_review_question_responses(request, pk: int, q_index: int):
"""Return the responses partial for a given anatomy exam question (HTMX-friendly)."""
exam = get_object_or_404(Exam, pk=pk)
questions = exam.get_questions()
try:
question = questions[q_index]
except Exception:
raise Http404("Question not found in exam")
try:
exam_user_answers = question.cid_user_answers.filter(exam=exam).select_related("user")
except Exception:
exam_user_answers = question.cid_user_answers.all()
context = {
"exam": exam,
"question": question,
"q_index": q_index,
"app_name": "anatomy",
"exam_user_answers": exam_user_answers,
}
return render(request, "anatomy/partials/exam_review_question_responses_fragment.html", context)
@login_required
def exam_review_question_summary(request, pk: int, q_index: int):
"""Return aggregated response summary partial for anatomy question (HTMX-friendly)."""
exam = get_object_or_404(Exam, pk=pk)
questions = exam.get_questions()
try:
question = questions[q_index]
except Exception:
raise Http404("Question not found in exam")
ua_qs = question.cid_user_answers.filter(exam=exam)
total_responses = ua_qs.count()
# counts by score values ('2','1','0','')
score_counts = {}
for s, c in ua_qs.values_list("score").annotate(count=Count("id")) if False else []:
pass
# simpler: compute via values
score_counts = {row["score"] if row["score"] is not None else "": row["count"] for row in ua_qs.values("score").annotate(count=Count("id"))}
# top submitted answers (by answer_compare)
top_answers = list(ua_qs.values("answer_compare").annotate(count=Count("id")).order_by("-count")[:10])
correct_count = None
correct_pct = None
# compute correct based on matching marked answers if available
if total_responses:
# count scores equal to '2' as correct
correct_count = score_counts.get("2", 0)
correct_pct = round(100.0 * correct_count / total_responses, 1) if total_responses else None
context = {
"exam": exam,
"question": question,
"q_index": q_index,
"app_name": "anatomy",
"exam_response_total": total_responses,
"exam_response_score_counts": score_counts,
"exam_response_correct_count": correct_count,
"exam_response_correct_pct": correct_pct,
"top_answers": top_answers,
}
return render(request, "anatomy/partials/exam_review_question_summary_fragment.html", context)
question_details = {
"total": len(questions),
"current": n + 1,