Refactor physics exam review scoring to count true/false sub-questions individually and improve accuracy of correct answer calculations

This commit is contained in:
Ross
2025-11-10 12:54:29 +00:00
parent bece7f6ce8
commit 24c35148b0
2 changed files with 64 additions and 23 deletions
+45 -9
View File
@@ -1283,17 +1283,53 @@ class ExamViews(View, LoginRequiredMixin):
# If we have responses, iterate and count fully-correct answers using # If we have responses, iterate and count fully-correct answers using
# the model's get_answer_score() which encapsulates per-app logic. # the model's get_answer_score() which encapsulates per-app logic.
if total_responses: if total_responses:
cnt = 0 # Special-case physics: count each true/false sub-question individually.
for ua in ua_qs: if self.app_name == "physics":
# Determine number of sub-items per question
try: try:
score = ua.get_answer_score() n_sub = len(q.get_questions())
except Exception: except Exception:
# If get_answer_score fails for any answer, skip it n_sub = None
continue
if score == per_question_max: total_subitems = 0
cnt += 1 correct_subitems = 0
correct_count = cnt for ua in ua_qs:
correct_pct = round(100.0 * correct_count / total_responses, 1) if total_responses else 0.0 try:
score = ua.get_answer_score()
except Exception:
continue
# score is expected to be iterable (tuple/list) for physics
if isinstance(score, (list, tuple)) and score:
# count numeric-typed positive/1 values as correct
for s in score:
if isinstance(s, (int, float)) and s > 0:
correct_subitems += 1
total_subitems += len(score)
else:
# fallback: treat scalar score as single subitem
if isinstance(score, (int, float)):
if score > 0:
correct_subitems += 1
total_subitems += 1
# Compute percent over all subitems seen
correct_count = correct_subitems
if total_subitems:
correct_pct = round(100.0 * correct_subitems / total_subitems, 1)
else:
correct_pct = 0.0
else:
cnt = 0
for ua in ua_qs:
try:
score = ua.get_answer_score()
except Exception:
# If get_answer_score fails for any answer, skip it
continue
if score == per_question_max:
cnt += 1
correct_count = cnt
correct_pct = round(100.0 * correct_count / total_responses, 1) if total_responses else 0.0
else: else:
correct_count = 0 correct_count = 0
correct_pct = 0.0 correct_pct = 0.0
+19 -14
View File
@@ -522,27 +522,32 @@ def exam_review_question_summary(request, pk: int, q_index: int):
correct_count = None correct_count = None
correct_pct = None correct_pct = None
try: try:
# per-question max for physics is 5 (see generic.get_max_score mapping) # For physics, count true/false sub-questions individually rather than
per_question_max = 5 # requiring a full-match across all parts.
if total_responses: if total_responses:
cnt = 0 correct_subitems = 0
total_subitems = 0
for ua in ua_qs: for ua in ua_qs:
try: try:
score = ua.get_answer_score() score = ua.get_answer_score()
except Exception: except Exception:
continue continue
if isinstance(score, (list, tuple)): if isinstance(score, (list, tuple)) and score:
try: for s in score:
ssum = sum([s for s in score if isinstance(s, (int, float))]) if isinstance(s, (int, float)) and s > 0:
except Exception: correct_subitems += 1
continue total_subitems += len(score)
if ssum == per_question_max:
cnt += 1
else: else:
if isinstance(score, (int, float)) and score == per_question_max: if isinstance(score, (int, float)):
cnt += 1 if score > 0:
correct_count = cnt correct_subitems += 1
correct_pct = round(100.0 * correct_count / total_responses, 1) if total_responses else 0.0 total_subitems += 1
correct_count = correct_subitems
if total_subitems:
correct_pct = round(100.0 * correct_subitems / total_subitems, 1)
else:
correct_pct = 0.0
else: else:
correct_count = 0 correct_count = 0
correct_pct = 0.0 correct_pct = 0.0