from odoo import api, fields, models from odoo.exceptions import ValidationError class McMark(models.Model): _name = "mc.mark" _description = "Mark" _order = "exam_id, student_id" _rec_name = "display_name" exam_id = fields.Many2one("mc.exam", string="Exam", required=True, ondelete="cascade") student_id = fields.Many2one("mc.student", string="Student", required=True, ondelete="restrict") marks_obtained = fields.Float(string="Marks Obtained", required=True) grade = fields.Char(string="Grade", compute="_compute_grade", store=True) _exam_student_uniq = models.Constraint( "unique(exam_id, student_id)", "This student already has marks recorded for this exam.", ) @api.depends("student_id.name", "exam_id.display_name") def _compute_display_name(self): for mark in self: mark.display_name = "%s - %s" % (mark.student_id.name or "?", mark.exam_id.display_name or "?") @api.depends("marks_obtained", "exam_id.max_marks", "exam_id.batch_id.program_id.grading_scale_id.interval_ids.threshold") def _compute_grade(self): for mark in self: scale = mark.exam_id.batch_id.program_id.grading_scale_id if not scale or not mark.exam_id.max_marks: mark.grade = False continue percentage = mark.marks_obtained / mark.exam_id.max_marks * 100 interval = scale.get_grade_interval(percentage) mark.grade = interval.letter if interval else False @api.constrains("marks_obtained", "exam_id") def _check_marks_within_range(self): for mark in self: if mark.marks_obtained < 0 or mark.marks_obtained > mark.exam_id.max_marks: raise ValidationError( "Marks obtained (%.2f) must be between 0 and the exam's max marks (%.2f)." % ( mark.marks_obtained, mark.exam_id.max_marks, ) )