Boost Azure Demo
This commit is contained in:
@@ -603,6 +603,156 @@ def gen_answer(student: dict, assignment: dict, aq: dict, q: dict, week_idx: int
|
||||
}
|
||||
|
||||
|
||||
def derive_working_steps(q: dict, ans: dict):
|
||||
solve_mode = ans["solve_mode"]
|
||||
correct = bool(ans["is_correct"])
|
||||
topic = q["topic"]
|
||||
sub_topic = q.get("sub_topic") or topic
|
||||
expected = q["correct_answer"]
|
||||
given = ans["answer_text"]
|
||||
misconception_tag = ans.get("misconception_tag")
|
||||
|
||||
if solve_mode == "just_answer":
|
||||
return "I solved it mentally and wrote the final answer only."
|
||||
|
||||
if solve_mode == "solve_together":
|
||||
if correct:
|
||||
return f"I followed guided steps for {topic.lower()} and reached {given}."
|
||||
return f"I needed guided help on {sub_topic.lower()}, but I still ended with {given} instead of {expected}."
|
||||
|
||||
if solve_mode == "handwritten":
|
||||
if correct:
|
||||
return f"I worked it out on paper using {sub_topic.lower()} and checked that the final answer was {given}."
|
||||
return f"I worked it out on paper for {sub_topic.lower()}, but my final answer was {given} instead of {expected}."
|
||||
|
||||
if correct:
|
||||
return f"I used a step-by-step method for {sub_topic.lower()} and got the correct answer {given}."
|
||||
|
||||
misconception_explanations = {
|
||||
"add_tops_add_bottoms": "I added the numerators and denominators directly instead of finding a common denominator.",
|
||||
"fraction_op_confusion": "I mixed up the fraction rule and used the wrong operation method.",
|
||||
"fraction_general_uncertainty": "I was unsure which fraction method to use, so my working was inconsistent.",
|
||||
"place_value_misalignment": "I lined the digits up incorrectly and misread the place value.",
|
||||
"arithmetic_slip": "My method was close, but I made a careless arithmetic slip in the calculation.",
|
||||
"scaffolding_dependence": "I could start the method, but I was not secure enough to finish it independently.",
|
||||
"word_problem_interpretation": "I misunderstood what the word problem was asking me to calculate.",
|
||||
}
|
||||
|
||||
explanation = misconception_explanations.get(
|
||||
misconception_tag,
|
||||
f"My method did not lead to the expected answer {expected}.",
|
||||
)
|
||||
return f"I tried a step-by-step method for {sub_topic.lower()}, but I got {given}. {explanation}"
|
||||
|
||||
|
||||
def derive_review_fields(q: dict, ans: dict):
|
||||
correct = bool(ans["is_correct"])
|
||||
solve_mode = ans["solve_mode"]
|
||||
misconception_tag = ans["misconception_tag"]
|
||||
expected = q["correct_answer"]
|
||||
|
||||
if correct:
|
||||
understanding_score = {
|
||||
"step_by_step": 0.95,
|
||||
"handwritten": 0.85,
|
||||
"just_answer": 0.75,
|
||||
"solve_together": 0.65,
|
||||
}.get(solve_mode, 0.75)
|
||||
confidence = {
|
||||
"step_by_step": 0.82,
|
||||
"handwritten": 0.78,
|
||||
"just_answer": 0.9,
|
||||
"solve_together": 0.62,
|
||||
}.get(solve_mode, 0.78)
|
||||
needs_attention = understanding_score < 0.72
|
||||
issue_reason = (
|
||||
"Correct answer, but there is limited evidence that the method is secure."
|
||||
if needs_attention else None
|
||||
)
|
||||
ai_feedback_review = (
|
||||
"Correct answer with clear method evidence."
|
||||
if solve_mode == "step_by_step"
|
||||
else "Correct answer, but understanding evidence is lighter because the response is brief."
|
||||
if needs_attention
|
||||
else "Correct answer with secure understanding shown."
|
||||
)
|
||||
else:
|
||||
understanding_score = {
|
||||
"step_by_step": 0.4,
|
||||
"handwritten": 0.32,
|
||||
"just_answer": 0.2,
|
||||
"solve_together": 0.28,
|
||||
}.get(solve_mode, 0.25)
|
||||
confidence = {
|
||||
"step_by_step": 0.55,
|
||||
"handwritten": 0.6,
|
||||
"just_answer": 0.72,
|
||||
"solve_together": 0.5,
|
||||
}.get(solve_mode, 0.6)
|
||||
needs_attention = True
|
||||
issue_reason = {
|
||||
"add_tops_add_bottoms": "The student added the numerator and denominator directly instead of finding a common denominator.",
|
||||
"fraction_op_confusion": "The student confused the fraction operation and did not apply the correct method.",
|
||||
"fraction_general_uncertainty": "The student shows insecure understanding of equivalent or comparable fractions.",
|
||||
"place_value_misalignment": "The student misread place value, causing digits to be aligned incorrectly.",
|
||||
"arithmetic_slip": "The final answer is wrong, suggesting a careless arithmetic slip rather than a secure method.",
|
||||
"scaffolding_dependence": "The student appears dependent on scaffolding and does not show secure independent understanding.",
|
||||
"word_problem_interpretation": "The student did not translate the word problem into the correct calculation.",
|
||||
}.get(
|
||||
misconception_tag,
|
||||
f"The answer does not match the correct answer ({expected}), showing an incomplete understanding of the method.",
|
||||
)
|
||||
ai_feedback_review = f"Incorrect answer compared with the expected answer {expected}. {issue_reason}"
|
||||
|
||||
return {
|
||||
"review_correctness_score": 1.0,
|
||||
"review_question_score": 1.0,
|
||||
"review_understanding_score": round(understanding_score, 3),
|
||||
"review_confidence": round(confidence, 3),
|
||||
"review_needs_attention": needs_attention,
|
||||
"review_issue_reason": issue_reason,
|
||||
"review_tags": [misconception_tag] if misconception_tag else [],
|
||||
"ai_feedback_review": ai_feedback_review,
|
||||
}
|
||||
|
||||
|
||||
def build_assignment_review_summary(student_name: str, assignment_name: str, reviews: list[dict]):
|
||||
if not reviews:
|
||||
return None, "No submitted work is available for this assignment yet.", None
|
||||
|
||||
per_question_scores = []
|
||||
weak_topics: dict[str, list[float]] = {}
|
||||
attention_count = 0
|
||||
correct_count = 0
|
||||
|
||||
for review in reviews:
|
||||
correctness_value = 1.0 if review["is_correct"] else 0.0
|
||||
understanding_value = review["review_understanding_score"]
|
||||
question_score = (correctness_value + understanding_value) / 2
|
||||
per_question_scores.append(question_score)
|
||||
weak_topics.setdefault(review["topic"], []).append(question_score)
|
||||
if review["review_needs_attention"]:
|
||||
attention_count += 1
|
||||
if review["is_correct"]:
|
||||
correct_count += 1
|
||||
|
||||
overall_score = round(sum(per_question_scores) / len(per_question_scores) * 10, 2)
|
||||
weakest_topics = sorted(
|
||||
((topic, sum(values) / len(values)) for topic, values in weak_topics.items()),
|
||||
key=lambda item: (item[1], item[0]),
|
||||
)[:2]
|
||||
weakest_topic_text = ", ".join(topic for topic, _ in weakest_topics) if weakest_topics else "general fluency"
|
||||
next_step_outcome = "accept" if overall_score >= 6.0 else "support" if overall_score >= 4.5 else "redo"
|
||||
ai_feedback = (
|
||||
f"{student_name} completed {assignment_name} with {correct_count}/{len(reviews)} correct responses. "
|
||||
f"Overall score is {overall_score}/10. "
|
||||
f"The weakest areas were {weakest_topic_text}. "
|
||||
f"{attention_count} question(s) need extra attention."
|
||||
)
|
||||
|
||||
return overall_score, ai_feedback, next_step_outcome
|
||||
|
||||
|
||||
# Order assignments oldest -> newest for week_idx threading.
|
||||
ASSIGNMENT_DEFS_SORTED = sorted(ASSIGNMENT_DEFS, key=lambda a: a[3]) # by due_offset
|
||||
weeks_total = len(ASSIGNMENT_DEFS_SORTED)
|
||||
@@ -710,9 +860,12 @@ for week_idx, assignment_def in enumerate(ASSIGNMENT_DEFS_SORTED):
|
||||
questions_to_answer = a_questions[:n_done]
|
||||
|
||||
running_time_offset = 0
|
||||
answer_reviews = []
|
||||
for aq in questions_to_answer:
|
||||
q = QB_BY_ID[aq["question_bank_id"]]
|
||||
ans = gen_answer(student, assignment_def, aq, q, week_idx, weeks_total)
|
||||
working_steps = derive_working_steps(q, ans)
|
||||
review = derive_review_fields(q, ans)
|
||||
answered_at = started_at + timedelta(seconds=running_time_offset + ans["time_on_task_seconds"])
|
||||
running_time_offset += ans["time_on_task_seconds"] + RNG.randint(5, 30)
|
||||
|
||||
@@ -723,12 +876,23 @@ for week_idx, assignment_def in enumerate(ASSIGNMENT_DEFS_SORTED):
|
||||
"answer_type": "LATEX",
|
||||
"answer_latex": ans["answer_text"],
|
||||
"extracted_answer": ans["answer_text"],
|
||||
"solve_mode": ans["solve_mode"],
|
||||
"working_steps": working_steps,
|
||||
"graded_marks": 1 if ans["is_correct"] else 0,
|
||||
"marks_awarded": 1 if ans["is_correct"] else 0,
|
||||
"ai_reasoning": (
|
||||
"Answer matches expected solution." if ans["is_correct"]
|
||||
else f"Incorrect; expected {q['correct_answer']}."
|
||||
),
|
||||
"is_correct": ans["is_correct"],
|
||||
"ai_feedback": review["ai_feedback_review"],
|
||||
"review_needs_attention": review["review_needs_attention"],
|
||||
"review_issue_reason": review["review_issue_reason"],
|
||||
"review_correctness_score": review["review_correctness_score"],
|
||||
"review_understanding_score": review["review_understanding_score"],
|
||||
"review_question_score": review["review_question_score"],
|
||||
"review_confidence": review["review_confidence"],
|
||||
"review_tags": review["review_tags"],
|
||||
"grading_status": "GRADED",
|
||||
"grading_attempts": 1,
|
||||
"is_active": True,
|
||||
@@ -743,6 +907,12 @@ for week_idx, assignment_def in enumerate(ASSIGNMENT_DEFS_SORTED):
|
||||
"_question_difficulty": q["difficulty"],
|
||||
"_answered_at": ms(answered_at),
|
||||
})
|
||||
answer_reviews.append({
|
||||
"topic": q["topic"],
|
||||
"is_correct": ans["is_correct"],
|
||||
"review_understanding_score": review["review_understanding_score"],
|
||||
"review_needs_attention": review["review_needs_attention"],
|
||||
})
|
||||
answer_id_seq += 1
|
||||
total_score += (1 if ans["is_correct"] else 0)
|
||||
|
||||
@@ -761,6 +931,11 @@ for week_idx, assignment_def in enumerate(ASSIGNMENT_DEFS_SORTED):
|
||||
log_id_seq += 1
|
||||
|
||||
submitted_at = started_at + timedelta(seconds=running_time_offset) if submitted else None
|
||||
overall_score, assignee_ai_feedback, next_step_outcome = build_assignment_review_summary(
|
||||
student["fullname"],
|
||||
name,
|
||||
answer_reviews,
|
||||
)
|
||||
|
||||
assignee = {
|
||||
"id": assignee_id,
|
||||
@@ -771,6 +946,9 @@ for week_idx, assignment_def in enumerate(ASSIGNMENT_DEFS_SORTED):
|
||||
"started_at": ms(started_at),
|
||||
"submitted_at": ms(submitted_at) if submitted_at else None,
|
||||
"total_marks": total_score if submitted else None,
|
||||
"overall_score": overall_score if submitted else None,
|
||||
"ai_feedback": assignee_ai_feedback if submitted else None,
|
||||
"next_step_outcome": next_step_outcome if submitted else None,
|
||||
"is_active": True,
|
||||
"deactivated_at": None,
|
||||
"created_at": ms(days_ago(-(offset - 7))),
|
||||
|
||||
Reference in New Issue
Block a user