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