File size: 3,378 Bytes
27cf88f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | """
Quiz scoring, progress tracking, and performance feedback.
"""
from collections import defaultdict
def calculate_score(answers: dict, questions: list[dict]) -> dict:
"""
answers: {question_index: user_answer}
For MCQ: int (index of chosen option)
For SATA: list[int] (indices of chosen options)
Returns comprehensive score report.
"""
total = len(questions)
correct = 0
by_category = defaultdict(lambda: {"correct": 0, "total": 0})
by_difficulty = defaultdict(lambda: {"correct": 0, "total": 0})
wrong_questions = []
for i, q in enumerate(questions):
cat = q.get("category", "Unknown")
diff = q.get("difficulty", "unknown")
by_category[cat]["total"] += 1
by_difficulty[diff]["total"] += 1
user_ans = answers.get(i)
if user_ans is None:
wrong_questions.append({"question": q, "user_answer": None, "correct": False})
continue
is_correct = _check_answer(q, user_ans)
if is_correct:
correct += 1
by_category[cat]["correct"] += 1
by_difficulty[diff]["correct"] += 1
else:
wrong_questions.append({
"question": q,
"user_answer": user_ans,
"correct": False,
})
pct = round((correct / total) * 100, 1) if total > 0 else 0
return {
"total": total,
"correct": correct,
"incorrect": total - correct,
"percentage": pct,
"pass": pct >= 60,
"by_category": dict(by_category),
"by_difficulty": dict(by_difficulty),
"wrong_questions": wrong_questions,
"performance_band": _band(pct),
"feedback": _feedback(pct),
}
def _check_answer(question: dict, user_answer) -> bool:
correct = question.get("correct")
if question["type"] == "sata":
if not isinstance(user_answer, (list, set)):
return False
return set(user_answer) == set(correct)
else:
return user_answer == correct
def _band(pct: float) -> str:
if pct >= 85: return "Excellent"
if pct >= 75: return "Proficient"
if pct >= 60: return "Borderline — keep practising"
if pct >= 45: return "Developing — review weak areas"
return "Needs significant review"
def _feedback(pct: float) -> str:
if pct >= 85:
return "Outstanding performance! You demonstrate strong clinical knowledge across NCLEX domains."
if pct >= 75:
return "Good work! You're on track for NCLEX success. Focus on your weaker categories."
if pct >= 60:
return "Borderline pass. Review your missed questions carefully — focus on rationales, not just answers."
if pct >= 45:
return "Keep studying! Focus on understanding WHY each answer is correct using the rationale."
return "Significant review needed. Consider revisiting core nursing content for each category you missed."
def category_percentage(score_report: dict) -> dict[str, float]:
"""Return {category: percentage} for charting."""
out = {}
for cat, data in score_report["by_category"].items():
t = data["total"]
out[cat] = round((data["correct"] / t) * 100, 1) if t > 0 else 0
return out
|