"""Deterministic answer grading + coarse error classification for Mastery Path.""" from __future__ import annotations from difflib import SequenceMatcher import re from typing import TYPE_CHECKING if TYPE_CHECKING: from deeptutor.learning.models import ErrorType def grade_answer(user_answer: str, expected_answer: str, question_type: str = "short") -> bool: """Grade user answer against expected answer. Args: user_answer: The user's submitted answer. expected_answer: The stored expected answer. question_type: One of "choice", "short", "open". Returns: True if answer is correct. """ user = user_answer.strip().lower() expected = expected_answer.strip().lower() if not expected: return False if question_type == "choice": user_norm = user.replace(" ", "") expected_norm = expected.replace(" ", "") return user_norm == expected_norm if question_type == "short": if user == expected: return True if len(expected) <= 30: return SequenceMatcher(None, user, expected).ratio() >= 0.85 return False if question_type == "open": keywords = [k.strip() for k in re.split(r"[,;,;。\n]+", expected) if k.strip()] if not keywords: return False matched = sum(1 for kw in keywords if kw in user) return matched / len(keywords) >= 0.6 return False def classify_error(user_answer: str) -> ErrorType: """Coarse error classification for a wrong answer. A blank answer signals the student did not know (metacognitive); anything else is treated as a wrong application. The richer four-type taxonomy is assigned later by the LLM in the error-diagnosis stage. """ from deeptutor.learning.models import ErrorType return ErrorType.METACOGNITIVE if not user_answer.strip() else ErrorType.APPLICATION_ERROR __all__ = ["grade_answer", "classify_error"]