from odoo.exceptions import ValidationError from odoo.tests.common import TransactionCase class TestGrading(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.year = cls.env["mc.academic.year"].create({ "name": "TEST-GRADE-2026-27", "date_start": "2026-06-01", "date_end": "2027-04-30", }) cls.term = cls.env["mc.academic.term"].create({ "name": "TEST GRADE Term", "year_id": cls.year.id, "date_start": "2026-06-01", "date_end": "2026-09-30", }) cls.scale_a = cls.env["mc.grading.scale"].create({"name": "TEST Scale A"}) cls.env["mc.grading.interval"].create({ "scale_id": cls.scale_a.id, "threshold": 90, "letter": "A1", }) cls.env["mc.grading.interval"].create({ "scale_id": cls.scale_a.id, "threshold": 75, "letter": "A2", }) cls.env["mc.grading.interval"].create({ "scale_id": cls.scale_a.id, "threshold": 0, "letter": "F", }) cls.scale_b = cls.env["mc.grading.scale"].create({"name": "TEST Scale B"}) cls.env["mc.grading.interval"].create({ "scale_id": cls.scale_b.id, "threshold": 50, "letter": "PASS", }) cls.env["mc.grading.interval"].create({ "scale_id": cls.scale_b.id, "threshold": 0, "letter": "FAIL", }) cls.program = cls.env["mc.program"].create({ "name": "TEST GRADE Program", "code": "TEST-GRADE-P1", "sequence_no": 1, "display_label": "Test Grade", "grading_scale_id": cls.scale_a.id, }) cls.batch = cls.env["mc.batch"].create({ "name": "TEST GRADE Batch", "program_id": cls.program.id, "year_id": cls.year.id, }) cls.exam = cls.env["mc.exam"].create({ "name": "TEST GRADE Exam", "term_id": cls.term.id, "batch_id": cls.batch.id, "subject_id": cls.env["mc.subject"].create({"name": "TEST GRADE Subject", "code": "TEST-GRADE-S1"}).id, "max_marks": 100, "pass_marks": 35, "date": "2026-07-01", }) partner = cls.env["res.partner"].create({"name": "Grade Test Student"}) cls.student = cls.env["mc.student"].create({ "partner_id": partner.id, "name": "Grade Test Student", }) def test_scale_picks_highest_matching_threshold(self): self.assertEqual(self.scale_a.get_grade_interval(95).letter, "A1") self.assertEqual(self.scale_a.get_grade_interval(90).letter, "A1") self.assertEqual(self.scale_a.get_grade_interval(89.9).letter, "A2") self.assertEqual(self.scale_a.get_grade_interval(10).letter, "F") def test_mark_grade_computed_from_program_scale(self): mark = self.env["mc.mark"].create({ "exam_id": self.exam.id, "student_id": self.student.id, "marks_obtained": 92, }) self.assertEqual(mark.grade, "A1") def test_changing_scale_recomputes_existing_marks(self): mark = self.env["mc.mark"].create({ "exam_id": self.exam.id, "student_id": self.student.id, "marks_obtained": 60, }) self.assertEqual(mark.grade, "F") # 60 < 75 threshold under Scale A self.program.grading_scale_id = self.scale_b.id self.assertEqual(mark.grade, "PASS") # same 60%, Scale B calls it a pass def test_no_scale_configured_leaves_grade_blank(self): self.program.grading_scale_id = False mark = self.env["mc.mark"].create({ "exam_id": self.exam.id, "student_id": self.student.id, "marks_obtained": 92, }) self.assertFalse(mark.grade) def test_marks_out_of_range_rejected(self): with self.assertRaises(ValidationError): self.env["mc.mark"].create({ "exam_id": self.exam.id, "student_id": self.student.id, "marks_obtained": 150, })