Enhance exam review responses to include sensible display names for respondents, improving clarity and context in the review process.

This commit is contained in:
Ross
2025-11-10 10:52:43 +00:00
parent 72b7940a8c
commit 1010f17e3d
2 changed files with 62 additions and 27 deletions
@@ -24,17 +24,18 @@
</div> </div>
</div> </div>
<ul class="list-group"> <ul class="list-group">
{% for ua in exam_user_answers %} {% for item in exam_user_answers %}
{% with ua=item.ua %}
<li class="list-group-item d-flex justify-content-between align-items-start"> <li class="list-group-item d-flex justify-content-between align-items-start">
<div> <div>
{% if ua.user %}
{% if reveal_respondents %} {% if reveal_respondents %}
<strong>{{ ua.user.get_full_name|default:ua.user.username }}</strong> {% if item.display_name_raw %}
<strong>{{ item.display_name_raw }}</strong>
{% else %} {% else %}
<strong>Respondent {{ forloop.counter }}</strong> <strong>{{ ua.get_candidate_masked }}</strong>
{% endif %} {% endif %}
{% else %} {% else %}
<strong>Anonymous</strong> <strong>Respondent {{ forloop.counter }}</strong>
{% endif %} {% endif %}
<div>Answer: <strong>{{ ua.answer }}</strong></div> <div>Answer: <strong>{{ ua.answer }}</strong></div>
{% if ua.created_date %} {% if ua.created_date %}
@@ -53,6 +54,7 @@
{% endif %} {% endif %}
</div> </div>
</li> </li>
{% endwith %}
{% empty %} {% empty %}
<li class="list-group-item">No responses recorded for this exam.</li> <li class="list-group-item">No responses recorded for this exam.</li>
{% endfor %} {% endfor %}
+35 -2
View File
@@ -274,14 +274,47 @@ def exam_review_question_responses(request, pk: int, q_index: int):
raise Http404("Question not found in exam") raise Http404("Question not found in exam")
try: try:
exam_user_answers = question.cid_user_answers.filter(exam=exam).select_related("user") exam_user_answers_qs = question.cid_user_answers.filter(exam=exam).select_related("user")
except Exception: except Exception:
exam_user_answers = question.cid_user_answers.all() exam_user_answers_qs = question.cid_user_answers.all()
# Allow an HTMX-driven reveal of respondent names. Default: anonymised. # Allow an HTMX-driven reveal of respondent names. Default: anonymised.
reveal = request.GET.get("reveal", "0") reveal = request.GET.get("reveal", "0")
reveal_respondents = str(reveal).lower() in ("1", "true", "yes", "on") reveal_respondents = str(reveal).lower() in ("1", "true", "yes", "on")
# Build a small list that includes a sensible display name for each respondent.
# If the answer is from a regular user use their full name/username. If it's a CID
# candidate try to resolve the CidUser and show CID + name if available. If neither
# is present fall back to empty/anonymous.
exam_user_answers = []
for ua in exam_user_answers_qs:
# default display
display = None
is_cid = False
try:
if getattr(ua, "user", None):
user = ua.user
# prefer full name where available
full = getattr(user, "get_full_name", None)
if full:
name = user.get_full_name() or user.username
else:
name = getattr(user, "username", str(user))
display = name
elif getattr(ua, "cid", None):
cid_val = ua.cid
cu = CidUser.objects.filter(cid=cid_val).first()
if cu is not None and cu.name:
display = f"CID{cid_val} ({cu.name})"
else:
display = f"CID{cid_val}"
is_cid = True
except Exception:
display = None
exam_user_answers.append({"ua": ua, "display_name_raw": display, "is_cid": is_cid})
context = { context = {
"exam": exam, "exam": exam,
"question": question, "question": question,