"""MCQ answer grading.""" from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from data.question import TSQuestion from .config import EnvConfig def _normalize(s: str, *, lower: bool) -> str: s = str(s).strip() return s.lower() if lower else s def grade_answer( submitted: str, question: TSQuestion, config: EnvConfig, ) -> tuple[bool, float]: """ Returns (is_fully_correct, partial_score in [0,1]). Binary match to ``question.answer``, or equality to an option whose normalized text equals the normalized ground-truth answer. ``partial_credit_t1u`` is reserved for future multi-part T1U grading. """ lower = config.case_insensitive_match truth = _normalize(question.answer, lower=lower) pred = _normalize(submitted, lower=lower) if pred == truth: return True, 1.0 for opt in question.options: if pred == _normalize(opt, lower=lower) and _normalize(opt, lower=lower) == truth: return True, 1.0 return False, 0.0