diff --git a/physics/templates/physics/partials/exam_review_question_summary_fragment.html b/physics/templates/physics/partials/exam_review_question_summary_fragment.html
index c1b5d854..1740f70c 100644
--- a/physics/templates/physics/partials/exam_review_question_summary_fragment.html
+++ b/physics/templates/physics/partials/exam_review_question_summary_fragment.html
@@ -35,14 +35,30 @@
{# Render top-level answer breakdown from exam_response_counts and pcts #}
- {% for key, count in exam_response_items %}
+ {% for item in exam_response_items %}
-
{{ key }}
-
{{ count }} {{ exam_response_pcts|get_item:key|default:0 }}%
+
+ {% if item.parts %}
+
+ {% for part in item.parts %}
+ {% if part.is_correct == True %}
+ {{ part.value|yesno:"T,F" }}
+ {% elif part.is_correct == False %}
+ {{ part.value|yesno:"T,F" }}
+ {% else %}
+ {{ part.value|default:"-" }}
+ {% endif %}
+ {% endfor %}
+
+ {% else %}
+
{{ item.key }}
+ {% endif %}
+
+
{{ item.count }} {{ item.pct }}%
{% endfor %}
diff --git a/physics/views.py b/physics/views.py
index 65b3679f..7e4c3085 100644
--- a/physics/views.py
+++ b/physics/views.py
@@ -518,10 +518,47 @@ 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)
+ # Provide an explicit iterable of structured items for templates to consume.
+ # Each item includes the original key, count, pct and optional per-part
+ # breakdown (list of {value, is_correct}) for physics multi-part answers.
+ exam_response_items = []
+ parts_names = ["a", "b", "c", "d", "e"]
+ for k, v in sorted(counts.items(), key=lambda x: x[1], reverse=True):
+ item = {"key": k, "count": v, "pct": pcts.get(k, 0.0), "parts": None}
+ # Attempt to parse comma-separated boolean-like keys into parts
+ try:
+ if isinstance(k, str) and "," in k:
+ parts_raw = [p.strip() for p in k.split(",")]
+ if len(parts_raw) == 5:
+ parts_details = []
+ for idx, sval in enumerate(parts_raw):
+ sval_l = sval.lower()
+ if sval_l in ("true", "t", "1", "yes"):
+ val = True
+ elif sval_l in ("false", "f", "0", "no"):
+ val = False
+ elif sval_l in ("none", "", "not answered", "not_answered"):
+ val = None
+ else:
+ # leave as string fallback
+ val = sval
+
+ # determine correctness where possible
+ is_correct = None
+ try:
+ correct_val = getattr(question, f"{parts_names[idx]}_answer")
+ if isinstance(val, bool) and isinstance(correct_val, bool):
+ is_correct = val == correct_val
+ except Exception:
+ is_correct = None
+
+ parts_details.append({"value": val, "is_correct": is_correct})
+
+ item["parts"] = parts_details
+ except Exception:
+ item["parts"] = None
+
+ exam_response_items.append(item)
# Compute percent correct using per-answer scoring
correct_count = None