Enhance exam review UI with detailed response summaries and progress bars for answer choices
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
<h6>Marked answers</h6>
|
||||
<ul class="list-group">
|
||||
{% for answer in question.answers.all|dictsortreversed:"status" %}
|
||||
<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{% if answer.status == '2' %} list-group-item-success{% elif answer.status == '1' %} list-group-item-warning{% elif answer.status == '0' %} list-group-item-secondary{% endif %}">
|
||||
<div>
|
||||
{% if answer.proposed %}
|
||||
<small class="text-muted me-2">(proposed)</small>
|
||||
|
||||
@@ -17,21 +17,48 @@
|
||||
</div>
|
||||
|
||||
<div class="list-group mb-2">
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>Correct</div>
|
||||
<div><strong>{{ exam_response_score_counts.2|default:0 }}</strong></div>
|
||||
{# Correct (2) #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>Correct</div>
|
||||
<div class="text-end"><strong>{{ exam_response_score_counts.2|default:0 }}</strong> <small class="text-muted">{{ exam_response_score_pcts.2|default:0 }}%</small></div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ exam_response_score_pcts.2|default:0 }}%;" aria-valuenow="{{ exam_response_score_pcts.2|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>Half mark</div>
|
||||
<div><strong>{{ exam_response_score_counts.1|default:0 }}</strong></div>
|
||||
|
||||
{# Half mark (1) #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>Half mark</div>
|
||||
<div class="text-end"><strong>{{ exam_response_score_counts.1|default:0 }}</strong> <small class="text-muted">{{ exam_response_score_pcts.1|default:0 }}%</small></div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar bg-warning" role="progressbar" style="width: {{ exam_response_score_pcts.1|default:0 }}%;" aria-valuenow="{{ exam_response_score_pcts.1|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>Incorrect</div>
|
||||
<div><strong>{{ exam_response_score_counts.0|default:0 }}</strong></div>
|
||||
|
||||
{# Incorrect (0) #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>Incorrect</div>
|
||||
<div class="text-end"><strong>{{ exam_response_score_counts.0|default:0 }}</strong> <small class="text-muted">{{ exam_response_score_pcts.0|default:0 }}%</small></div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: {{ exam_response_score_pcts.0|default:0 }}%;" aria-valuenow="{{ exam_response_score_pcts.0|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>Unmarked</div>
|
||||
<div><strong>{{ exam_response_score_counts.empty|default:0 }}</strong></div>
|
||||
|
||||
{# Unmarked (empty) #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>Unmarked</div>
|
||||
<div class="text-end"><strong>{{ exam_response_score_counts.empty|default:0 }}</strong> <small class="text-muted">{{ exam_response_score_pcts.empty|default:0 }}%</small></div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar bg-secondary" role="progressbar" style="width: {{ exam_response_score_pcts.empty|default:0 }}%;" aria-valuenow="{{ exam_response_score_pcts.empty|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+13
-2
@@ -308,8 +308,8 @@ def exam_review_question_summary(request, pk: int, q_index: int):
|
||||
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"))}
|
||||
# simpler: compute via values and normalise unmarked -> 'empty'
|
||||
score_counts = {str(row["score"]) if row["score"] is not None else "empty": 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])
|
||||
@@ -322,6 +322,16 @@ def exam_review_question_summary(request, pk: int, q_index: int):
|
||||
correct_count = score_counts.get("2", 0)
|
||||
correct_pct = round(100.0 * correct_count / total_responses, 1) if total_responses else None
|
||||
|
||||
# compute percentage distribution for score buckets for the summary bar chart
|
||||
score_keys = ["2", "1", "0", "empty"]
|
||||
score_pcts = {}
|
||||
if total_responses:
|
||||
for k in score_keys:
|
||||
score_pcts[k] = round(100.0 * score_counts.get(k, 0) / total_responses, 1)
|
||||
else:
|
||||
for k in score_keys:
|
||||
score_pcts[k] = 0.0
|
||||
|
||||
context = {
|
||||
"exam": exam,
|
||||
"question": question,
|
||||
@@ -329,6 +339,7 @@ def exam_review_question_summary(request, pk: int, q_index: int):
|
||||
"app_name": "anatomy",
|
||||
"exam_response_total": total_responses,
|
||||
"exam_response_score_counts": score_counts,
|
||||
"exam_response_score_pcts": score_pcts,
|
||||
"exam_response_correct_count": correct_count,
|
||||
"exam_response_correct_pct": correct_pct,
|
||||
"top_answers": top_answers,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<h6>Answers</h6>
|
||||
<ul class="list-group">
|
||||
{% if question.a_answer %}
|
||||
<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{% if question.best_answer == 'a' %} list-group-item-success{% endif %}">
|
||||
<div>
|
||||
<strong class="me-2">A.</strong>
|
||||
<span>{{ question.a_answer }}</span>
|
||||
@@ -28,7 +28,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if question.b_answer %}
|
||||
<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{% if question.best_answer == 'b' %} list-group-item-success{% endif %}">
|
||||
<div>
|
||||
<strong class="me-2">B.</strong>
|
||||
<span>{{ question.b_answer }}</span>
|
||||
@@ -47,7 +47,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if question.c_answer %}
|
||||
<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{% if question.best_answer == 'c' %} list-group-item-success{% endif %}">
|
||||
<div>
|
||||
<strong class="me-2">C.</strong>
|
||||
<span>{{ question.c_answer }}</span>
|
||||
@@ -66,7 +66,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if question.d_answer %}
|
||||
<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{% if question.best_answer == 'd' %} list-group-item-success{% endif %}">
|
||||
<div>
|
||||
<strong class="me-2">D.</strong>
|
||||
<span>{{ question.d_answer }}</span>
|
||||
@@ -85,7 +85,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if question.e_answer %}
|
||||
<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{% if question.best_answer == 'e' %} list-group-item-success{% endif %}">
|
||||
<div>
|
||||
<strong class="me-2">E.</strong>
|
||||
<span>{{ question.e_answer }}</span>
|
||||
|
||||
@@ -16,25 +16,84 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>A</div>
|
||||
<div><span class="me-2">{{ exam_response_counts.a|default:0 }}</span><small class="text-muted">{{ exam_response_pcts.a|default:0 }}%</small></div>
|
||||
{# Choice A #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>A</div>
|
||||
<div class="text-end">
|
||||
<span class="me-2">{{ exam_response_counts.a|default:0 }}</span>
|
||||
<small class="text-muted">{{ exam_response_pcts.a|default:0 }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar {% if question.best_answer == 'a' %}bg-success{% else %}bg-primary{% endif %}" role="progressbar"
|
||||
style="width: {{ exam_response_pcts.a|default:0 }}%;"
|
||||
aria-valuenow="{{ exam_response_pcts.a|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>B</div>
|
||||
<div><span class="me-2">{{ exam_response_counts.b|default:0 }}</span><small class="text-muted">{{ exam_response_pcts.b|default:0 }}%</small></div>
|
||||
|
||||
{# Choice B #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>B</div>
|
||||
<div class="text-end">
|
||||
<span class="me-2">{{ exam_response_counts.b|default:0 }}</span>
|
||||
<small class="text-muted">{{ exam_response_pcts.b|default:0 }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar {% if question.best_answer == 'b' %}bg-success{% else %}bg-info{% endif %}" role="progressbar"
|
||||
style="width: {{ exam_response_pcts.b|default:0 }}%;"
|
||||
aria-valuenow="{{ exam_response_pcts.b|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>C</div>
|
||||
<div><span class="me-2">{{ exam_response_counts.c|default:0 }}</span><small class="text-muted">{{ exam_response_pcts.c|default:0 }}%</small></div>
|
||||
|
||||
{# Choice C #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>C</div>
|
||||
<div class="text-end">
|
||||
<span class="me-2">{{ exam_response_counts.c|default:0 }}</span>
|
||||
<small class="text-muted">{{ exam_response_pcts.c|default:0 }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar {% if question.best_answer == 'c' %}bg-success{% else %}bg-warning{% endif %}" role="progressbar"
|
||||
style="width: {{ exam_response_pcts.c|default:0 }}%;"
|
||||
aria-valuenow="{{ exam_response_pcts.c|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>D</div>
|
||||
<div><span class="me-2">{{ exam_response_counts.d|default:0 }}</span><small class="text-muted">{{ exam_response_pcts.d|default:0 }}%</small></div>
|
||||
|
||||
{# Choice D #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>D</div>
|
||||
<div class="text-end">
|
||||
<span class="me-2">{{ exam_response_counts.d|default:0 }}</span>
|
||||
<small class="text-muted">{{ exam_response_pcts.d|default:0 }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar {% if question.best_answer == 'd' %}bg-success{% else %}bg-danger{% endif %}" role="progressbar"
|
||||
style="width: {{ exam_response_pcts.d|default:0 }}%;"
|
||||
aria-valuenow="{{ exam_response_pcts.d|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>E</div>
|
||||
<div><span class="me-2">{{ exam_response_counts.e|default:0 }}</span><small class="text-muted">{{ exam_response_pcts.e|default:0 }}%</small></div>
|
||||
|
||||
{# Choice E #}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<div>E</div>
|
||||
<div class="text-end">
|
||||
<span class="me-2">{{ exam_response_counts.e|default:0 }}</span>
|
||||
<small class="text-muted">{{ exam_response_pcts.e|default:0 }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height:12px;">
|
||||
<div class="progress-bar {% if question.best_answer == 'e' %}bg-success{% else %}bg-secondary{% endif %}" role="progressbar"
|
||||
style="width: {{ exam_response_pcts.e|default:0 }}%;"
|
||||
aria-valuenow="{{ exam_response_pcts.e|default:0 }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
Reference in New Issue
Block a user