Files
AOS/skills/quiz-mastery/src/quiz_mastery/evaluator.py
T
Z User e45b68bb8b feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)
Real IMAP connection via imapflow library:
- Connects to IMAP server (IMAPFlow, TLS on port 993)
- Fetches recent messages (configurable limit, default 20)
- Extracts: from, fromEmail, subject, date, snippet (200 chars)
- Thread-to-contact matching (PRD §6.1):
  1. Match sender email against persons table
  2. If no person match, extract domain and match against company.website
  3. Returns matchedCompany + matchedPerson for each email
- Audit-logged (action: crm.mailsync:imap)
- Zod validated (host, port, user, password, folder, limit)
- 502 on IMAP connection failure with detail
- 400 on validation failure

Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
2026-10-27 22:50:50 +00:00

79 lines
2.6 KiB
Python
Executable File

from __future__ import annotations
from .models import Question, AnswerResult
class Evaluator:
"""Evaluates user answers against correct answers.
- single_choice / true_false: exact match
- fill_blank: case-insensitive, strip whitespace
- short_answer: no auto-scoring, returns needs_review=True
"""
def evaluate_answers(
self,
questions: list[Question],
user_answers: dict[str, str],
) -> list[AnswerResult]:
results: list[AnswerResult] = []
for q in questions:
user_answer = user_answers.get(q.id)
if q.type == "short_answer":
# Short answer: cannot auto-evaluate, needs human/LLM review
results.append(
AnswerResult(
question_id=q.id,
user_answer=user_answer,
is_correct=None,
score=None,
feedback="需要人工/LLM评判",
error_type="pending_review",
needs_review=True,
)
)
continue
if user_answer is None:
results.append(
AnswerResult(
question_id=q.id,
user_answer=None,
is_correct=False,
score=0.0,
feedback=f"未作答。正确答案:{q.answer}。{q.explanation}",
error_type="no_answer",
needs_review=False,
)
)
continue
if q.type == "fill_blank":
# Fill-in-the-blank: case-insensitive, strip whitespace
is_correct = (
user_answer.strip().lower() == str(q.answer).strip().lower()
)
else:
# single_choice / true_false: exact match
is_correct = user_answer.strip() == str(q.answer).strip()
results.append(
AnswerResult(
question_id=q.id,
user_answer=user_answer,
is_correct=is_correct,
score=1.0 if is_correct else 0.0,
feedback=(
q.explanation
if is_correct
else f"回答错误。正确答案:{q.answer}。{q.explanation}"
),
error_type="none" if is_correct else "concept_confusion",
needs_review=False,
)
)
return results