2026-09-15 10:47:19 +05:30

128 lines
6.0 KiB
Python

from odoo import models, fields, api
from odoo.exceptions import ValidationError
class EducationExamResult(models.Model):
_name = 'education.exam.result'
_description = 'Exam Result'
_rec_name = 'student_id'
student_id = fields.Many2one('education.student', string='Student', required=True)
exam_id = fields.Many2one('education.exam', string='Exam', required=True, ondelete='cascade')
total_max_marks = fields.Float(string='Total Maximum Marks', compute='_compute_totals', store=True)
total_obtained_marks = fields.Float(string='Total Obtained Marks', compute='_compute_totals', store=True)
percentage = fields.Float(string='Percentage', compute='_compute_totals', store=True)
grade_id = fields.Many2one('education.grade', string='Grade', compute='_compute_totals', store=True)
grade_point = fields.Float(string='Grade Point', compute='_compute_totals', store=True)
result_status = fields.Selection([
('pending', 'Pending / Not Evaluated'),
('pass', 'Pass'),
('fail', 'Fail')
], string='Result Status', compute='_compute_totals', store=True, default='pending')
published = fields.Boolean(string='Published', default=False, tracking=True)
published_date = fields.Date(string='Published Date')
line_ids = fields.One2many('education.exam.result.line', 'result_id', string='Subject Marks')
_sql_constraints = [
('unique_student_exam', 'unique(student_id, exam_id)', 'A result already exists for this student for this exam!')
]
@api.depends('line_ids.max_marks', 'line_ids.obtained_marks', 'line_ids.status', 'exam_id.grading_system_id')
def _compute_totals(self):
for rec in self:
max_marks = sum(rec.line_ids.mapped('max_marks'))
obtained_marks = sum(rec.line_ids.mapped('obtained_marks'))
rec.total_max_marks = max_marks
rec.total_obtained_marks = obtained_marks
if max_marks > 0:
rec.percentage = (obtained_marks / max_marks) * 100.0
else:
rec.percentage = 0.0
# Determine overall grade
if rec.exam_id and rec.exam_id.grading_system_id:
grade = rec.exam_id.grading_system_id.get_grade(rec.percentage)
if grade:
rec.grade_id = grade.id
rec.grade_point = grade.grade_point
else:
rec.grade_id = False
rec.grade_point = 0.0
else:
rec.grade_id = False
rec.grade_point = 0.0
# Overall status is pending if any subject is pending, Fail if any subject is failed, otherwise Pass
if not rec.line_ids or any(line.status == 'pending' for line in rec.line_ids):
rec.result_status = 'pending'
elif any(line.status == 'fail' for line in rec.line_ids):
rec.result_status = 'fail'
else:
rec.result_status = 'pass'
class EducationExamResultLine(models.Model):
_name = 'education.exam.result.line'
_description = 'Exam Result Line'
result_id = fields.Many2one('education.exam.result', string='Result', required=True, ondelete='cascade')
subject_id = fields.Many2one('education.subject', string='Subject', required=True)
exam_type_id = fields.Many2one('education.exam.type', string='Exam Type')
max_marks = fields.Float(string='Maximum Marks', required=True)
pass_marks = fields.Float(string='Pass Marks', required=True)
obtained_marks = fields.Float(string='Obtained Marks', default=-1.0) # -1 indicates not entered
percentage = fields.Float(string='Percentage', compute='_compute_grade_details', store=True)
grade_id = fields.Many2one('education.grade', string='Grade', compute='_compute_grade_details', store=True)
grade_point = fields.Float(string='Grade Point', compute='_compute_grade_details', store=True)
status = fields.Selection([
('pending', 'Pending / Not Evaluated'),
('pass', 'Pass'),
('fail', 'Fail')
], string='Status', compute='_compute_grade_details', store=True, default='pending')
remarks = fields.Char(string='Remarks')
@api.constrains('obtained_marks', 'max_marks')
def _check_marks(self):
for record in self:
if record.obtained_marks > record.max_marks:
raise ValidationError(f"Obtained marks cannot exceed maximum marks for {record.subject_id.name}.")
# Using -1 to indicate not entered yet. So < -1 is invalid.
if record.obtained_marks < -1.0:
raise ValidationError("Obtained marks cannot be negative.")
@api.depends('obtained_marks', 'max_marks', 'pass_marks', 'result_id.exam_id.grading_system_id')
def _compute_grade_details(self):
for rec in self:
if rec.obtained_marks >= 0 and rec.max_marks > 0:
rec.percentage = (rec.obtained_marks / rec.max_marks) * 100.0
# Check pass/fail based on marks first
if rec.obtained_marks >= rec.pass_marks:
rec.status = 'pass'
else:
rec.status = 'fail'
# Apply grading system
if rec.result_id.exam_id.grading_system_id:
grade = rec.result_id.exam_id.grading_system_id.get_grade(rec.percentage)
if grade:
rec.grade_id = grade.id
rec.grade_point = grade.grade_point
# Could override status based on grade if required, but usually marks determine subject pass/fail
else:
rec.grade_id = False
rec.grade_point = 0.0
else:
rec.grade_id = False
rec.grade_point = 0.0
else:
rec.percentage = 0.0
rec.grade_id = False
rec.grade_point = 0.0
rec.status = 'pending' # default to pending until marks are entered