""" 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