128 lines
6.0 KiB
Python
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
|