<filename>exam/models/exam.py
# See LICENSE file for full copyright and licensing details.

from datetime import date, datetime
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError

import calendar

class StudentStudent(models.Model):
    _inherit = 'student.student'
    _description = 'Student Information'

    exam_results_ids = fields.One2many('exam.result', 'student_id',
                                       'Exam History', readonly=True)

    @api.multi
    def set_alumni(self):
        '''Override method to make exam results of student active false
        when student is alumni'''
        for rec in self:
            addexam_result = self.env['additional.exam.result'].\
                search([('student_id', '=', rec.id)])
            regular_examresult = self.env['exam.result'].\
                search([('student_id', '=', rec.id)])
            if addexam_result:
                addexam_result.write({'active': False})
            if regular_examresult:
                regular_examresult.write({'active': False})
        return super(StudentStudent, self).set_alumni()

    @api.model
    def _search(self, args, offset=0, limit=None, order=None, count=False,
                access_rights_uid=None):
        '''Override method to get exam of student selected'''
        if self._context.get('exam'):
            exam_obj = self.env['exam.exam']
            exam_data = exam_obj.browse(self._context['exam'])
            std_ids = [std_id.id for std_id in exam_data.standard_id]
            args.append(('standard_id', 'in', std_ids))
        return super(StudentStudent, self)._search(
            args=args, offset=offset, limit=limit, order=order, count=count,
            access_rights_uid=access_rights_uid)


class ExtendedTimeTable(models.Model):
    _inherit = 'time.table'

    @api.multi
    def unlink(self):
        exam = self.env['exam.exam']
        schedule_line = self.env['exam.schedule.line']
        for rec in self:
            exam_search = exam.search([('state', '=', 'running')])
            for data in exam_search:
                schedule_line_search = schedule_line.search([('exam_id', '=',
                                                              data.id),
                                                             ('timetable_id',
                                                              '=', rec.id)])
                if schedule_line_search:
                    raise ValidationError(_('''You cannot delete schedule of
                    exam which is in running!'''))
        return super(ExtendedTimeTable, self).unlink()

    timetable_type = fields.Selection(selection_add=[('exam', 'Exam')],
                                      string='Time Table Type', required=True,
                                      inivisible=False)
    exam_timetable_line_ids = fields.One2many('time.table.line', 'table_id',
                                              'TimeTable Lines')
    exam_id = fields.Many2one('exam.exam', 'Exam')

    @api.constrains('exam_timetable_line_ids')
    def _check_exam(self):
        '''Method to check same exam is not assigned on same day'''
        if self.timetable_type == 'exam':
            if not self.exam_timetable_line_ids:
                raise ValidationError(_(''' Please Enter Exam Timetable!'''))
            domain = [('table_id', 'in', self.ids)]
            line_ids = self.env['time.table.line'].search(domain)
            for rec in line_ids:
                records = [rec_check.id for rec_check in line_ids
                           if (rec.day_of_week == rec_check.day_of_week and
                               rec.start_time == rec_check.start_time and
                               rec.end_time == rec_check.end_time and
                               rec.teacher_id.id == rec.teacher_id.id and
                               rec.exm_date == rec.exm_date)]
                if len(records) > 1:
                    raise ValidationError(_('''You cannot set exam at same
                                            time %s  at same day %s for
                                            teacher %s!''') %
                                           (rec.start_time, rec.day_of_week,
                                            rec.teacher_id.name))


class ExtendedTimeTableLine(models.Model):
    _inherit = 'time.table.line'

    exm_date = fields.Date('Exam Date')
    day_of_week = fields.Char('Week Day')
    class_room_id = fields.Many2one('class.room', 'Classroom')

    @api.onchange('exm_date')
    def onchange_date_day(self):
        '''Method to get weekday from date'''
        for rec in self:
            rec.day_of_week = False
            if rec.exm_date:
                rec.day_of_week=calendar.day_name[rec.exm_date.weekday()]  #'Wednesday'
                #week_day = datetime.strptime(rec.exm_date, "%Y-%m-%d")
                #rec.day_of_week = week_day.strftime("%A").title()
                

    @api.multi
    def _check_date(self):
        '''Method to check constraint of start date and end date'''
        for line in self:
            if line.exm_date:
                dt = datetime.strptime(line.exm_date, "%Y-%m-%d")
                if line.week_day != dt.strftime("%A").lower():
                    return False
                elif dt.__str__() < datetime.strptime(date.today().__str__(),
                                                      "%Y-%m-%d").__str__():
                    raise ValidationError(_('''Invalid Date Error !
                        Either you have selected wrong day
                                       for the date or you have selected\
                                       invalid date!'''))

    @api.constrains('teacher_id')
    def check_supervisior_exam(self):
            for rec in self:
                if (rec.table_id.timetable_type == 'exam' and
                        not rec.teacher_id):
                        raise ValidationError(_('''PLease Enter Supervisior!
                        '''))

    @api.constrains('start_time', 'end_time')
    def check_time(self):
        for rec in self:
            if rec.start_time >= rec.end_time:
                raise ValidationError(_('''Start time should be less than end
                time!'''))

    @api.constrains('teacher_id', 'class_room_id')
    def check_teacher_room(self):
        timetable_rec = self.env['time.table'].search([('id', '!=',
                                                        self.table_id.id)])
        if timetable_rec:
            for data in timetable_rec:
                for record in data.timetable_ids:
                    if (data.timetable_type == 'exam' and
                            self.table_id.timetable_type == 'exam' and
                            self.class_room_id == record.class_room_id and
                            self.start_time == record.start_time):
                            raise ValidationError(_("The room is occupied!"))

    @api.constrains('subject_id', 'class_room_id')
    def check_exam_date(self):
        for rec in self.table_id.exam_timetable_line_ids:
            record = self.table_id
            if rec.id not in self.ids:
                if (record.timetable_type == 'exam' and
                        self.exm_date == rec.exm_date and
                        self.start_time == rec.start_time):
                    raise ValidationError(_('''There is already Exam at
                        same Date and Time!'''))
                if (record.timetable_type == 'exam' and
                        self.table_id.timetable_type == 'exam' and
                        self.subject_id == rec.subject_id):
                        raise ValidationError(_('''%s Subject Exam Already
                        Taken''') % (self.subject_id.name))
                if (record.timetable_type == 'exam' and
                        self.table_id.timetable_type == 'exam' and
                        self.exm_date == rec.exm_date and
                        self.class_room_id == rec.class_room_id and
                        self.start_time == rec.start_time):
                    raise ValidationError(_('''%s is occupied by '%s' for %s
                    class!''') % (self.class_room_id.name, record.name,
                                  record.standard_id.standard_id.name))


class ExamExam(models.Model):
    _name = 'exam.exam'
    _description = 'Exam Information'

    @api.constrains('start_date', 'end_date')
    def check_date_exam(self):
        '''Method to check constraint of exam start date and end date'''
        for rec in self:
            if rec.end_date < rec.start_date:
                raise ValidationError(_('''Exam end date should be
                                  greater than start date!'''))
            for line in rec.exam_schedule_ids:
                if line.timetable_id:
                    for tt in line.timetable_id.exam_timetable_line_ids:
                        if not rec.start_date <= tt.exm_date <= rec.end_date:
                            raise ValidationError(_('Invalid Exam Schedule\
                            \n\nExam Dates must be in between Start\
                            date and End date !'))

    @api.constrains('active')
    def check_active(self):
        '''if exam results is not in done state then raise an
        validation Warning'''
        result_obj = self.env['exam.result']
        if not self.active:
            for result in result_obj.search([('s_exam_ids', '=', self.id)]):
                if result.state != 'done':
                    raise ValidationError(_('Kindly,mark as done %s\
                    examination results!') % (self.name))

    active = fields.Boolean('Active', default="True")
    name = fields.Char("Exam Name", required=True,
                       help="Name of Exam")
    exam_code = fields.Char('Exam Code', required=True, readonly=True,
                            help="Code of exam",
                            default=lambda obj:
                            obj.env['ir.sequence'].next_by_code('exam.exam'))
    standard_id = fields.Many2many('standard.standard',
                                   'standard_standard_exam_rel', 'standard_id',
                                   'event_id', 'Participant Standards',
                                   help="Select Standard")
    start_date = fields.Datetime("Exam Start Date",
                             help="Exam will start from this date")
    end_date = fields.Datetime("Exam End date", help="Exam will end at this date")
    duration = fields.Float('Exam Duration', help="Exam duration in minutes")
    state = fields.Selection([('draft', 'Draft'),
                              ('running', 'Running'),
                              ('finished', 'Finished'),
                              ('cancelled', 'Cancelled')], 'State',
                             readonly=True, default='draft')
    grade_system = fields.Many2one('grade.master', "Grade System",
                                   help="Select Grade System")
    academic_year = fields.Many2one('academic.year', 'Academic Year',
                                    help="Select Academic Year")
    exam_schedule_ids = fields.One2many('exam.schedule.line', 'exam_id',
                                        'Exam Schedule')

    @api.multi
    def set_to_draft(self):
        '''Method to set state to draft'''
        self.state = 'draft'

    @api.multi
    def set_running(self):
        '''Method to set state to running'''
        for rec in self:
            if not rec.standard_id:
                raise ValidationError(_('Please Select Standard!'))
            if rec.exam_schedule_ids:
                rec.state = 'running'
            else:
                raise ValidationError(_('You must add one Exam Schedule!'))

    @api.multi
    def set_finish(self):
        '''Method to set state to finish'''
        self.state = 'finished'

    @api.multi
    def set_cancel(self):
        '''Method to set state to cancel'''
        self.state = 'cancelled'

    @api.multi
    def generate_result(self):
        '''Method to generate result'''
        result_obj = self.env['exam.result']
        student_obj = self.env['student.student']
        result_list = []
        for rec in self:
            for exam_schedule in rec.exam_schedule_ids:
                domain = [('standard_id', '=', exam_schedule.standard_id.id),
                          ('year', '=', rec.academic_year.id),
                          ('state', '=', 'done'),
                          ('school_id', '=',
                           exam_schedule.standard_id.school_id.id)]
                students = student_obj.search(domain)
                for student in students:
                    domain = [('standard_id', '=',
                               student.standard_id.id),
                              ('student_id', '=', student.id),
                              ('s_exam_ids', '=', rec.id)]
                    result_exists = result_obj.search(domain)
                    if result_exists:
                        [result_list.append(res.id) for res in result_exists]
                    else:
                        rs_dict = {'s_exam_ids': rec.id,
                                   'student_id': student.id,
                                   'standard_id': student.standard_id.id,
                                   'roll_no_id': student.roll_no,
                                   'grade_system': rec.grade_system.id,
                                   }
                        exam_line = []
                        timetable = exam_schedule.sudo().timetable_id
                        for line in timetable.sudo().timetable_ids:
                            min_mrks = line.subject_id.minimum_marks
                            max_mrks = line.subject_id.maximum_marks
                            sub_vals = {'subject_id': line.subject_id.id,
                                        'minimum_marks': min_mrks,
                                        'maximum_marks': max_mrks}
                            exam_line.append((0, 0, sub_vals))
                        rs_dict.update({'result_ids': exam_line})
                        result = result_obj.create(rs_dict)
                        result_list.append(result.id)
        return {'name': _('Result Info'),
                'view_type': 'form',
                'view_mode': 'tree,form',
                'res_model': 'exam.result',
                'type': 'ir.actions.act_window',
                'domain': [('id', 'in', result_list)]}


class ExamScheduleLine(models.Model):
    _name = 'exam.schedule.line'
    _description = "Exam Schedule Line Details"

    @api.onchange('standard_ids')
    def onchange_standard(self):
        '''Method to get standard according to the standard selected'''
        standard_ids = []
        for rec in self:
            standard_ids = [std.id for std in rec.standard_ids]
        return {'domain': {'standard_id': [('standard_id', 'in',
                                            standard_ids)]}}

    standard_id = fields.Many2one('school.standard', 'Standard',
                                  help="Select Standard")
    timetable_id = fields.Many2one('time.table', 'Exam Schedule')
    exam_id = fields.Many2one('exam.exam', 'Exam')
    standard_ids = fields.Many2many('standard.standard',
                                    string='Participant Standards')


class AdditionalExam(models.Model):
    _name = 'additional.exam'
    _description = 'additional Exam Information'

    @api.multi
    def _compute_color_name(self):
        for rec in self:
            rec.color_name = rec.subject_id.id

    name = fields.Char("Additional Exam Name", required=True,
                       help="Name of Exam")
    addtional_exam_code = fields.Char('Exam Code', required=True,
                                      help="Exam Code",
                                      readonly=True,
                                      default=lambda obj:
                                      obj.env['ir.sequence'].
                                      next_by_code('additional.exam'))
    standard_id = fields.Many2one("school.standard", "Standard")
    subject_id = fields.Many2one("subject.subject", "Subject Name")
    exam_date = fields.Datetime("Exam Date")
    maximum_marks = fields.Integer("Maximum Mark",
                                   help="Minimum Marks of exam")
    minimum_marks = fields.Integer("Minimum Mark",
                                   help="Maximum Marks of Exam")
    weightage = fields.Char("WEIGHTAGE")
    create_date = fields.Date("Created Date", help="Exam Created Date")
    write_date = fields.Date("Updated date", help="Exam Updated Date")
    color_name = fields.Integer("Color index of creator",
                                compute='_compute_color_name', store=False)

    @api.constrains('maximum_marks', 'minimum_marks')
    def check_marks(self):
        if self.minimum_marks > self.maximum_marks:
            raise ValidationError(_('''Configure Maximum marks greater than
            minimum marks!'''))

    @api.model
    def create(self, vals):
        curr_dt = datetime.now()
        new_dt = datetime.strftime(curr_dt, '%m/%d/%Y')
        vals.update({'create_date': new_dt})
        return super(AdditionalExam, self).create(vals)

    @api.multi
    def write(self, vals):
        curr_dt = datetime.now()
        new_dt = datetime.strftime(curr_dt, '%m/%d/%Y')
        vals.update({'write_date': new_dt})
        return super(AdditionalExam, self).write(vals)


class ExamResult(models.Model):
    _name = 'exam.result'
    _inherit = ["mail.thread", "resource.mixin"]
    _rec_name = 'roll_no_id'
    _description = 'exam result Information'

    @api.depends('result_ids', 'result_ids.obtain_marks',
                 'result_ids.marks_reeval')
    def _compute_total(self):
        '''Method to compute total'''
        for rec in self:
            total = 0.0
            if rec.result_ids:
                for line in rec.result_ids:
                    obtain_marks = line.obtain_marks
                    if line.state == "re-evaluation":
                        obtain_marks = line.marks_reeval
                    total += obtain_marks
            rec.total = total

    @api.depends('total')
    def _compute_per(self):
        '''Method to compute percentage'''
        total = 0.0
        obtained_total = 0.0
        obtain_marks = 0.0
        per = 0.0
        for result in self:
            for sub_line in result.result_ids:
                if sub_line.state == "re-evaluation":
                    obtain_marks = sub_line.marks_reeval
                else:
                    obtain_marks = sub_line.obtain_marks
                total += sub_line.maximum_marks or 0
                obtained_total += obtain_marks
            if total > 1.0:
                per = (obtained_total / total) * 100
                if result.grade_system:
                    for grade_id in result.grade_system.grade_ids:
                        if per >= grade_id.from_mark and\
                                per <= grade_id.to_mark:
                            result.grade = grade_id.grade or ''
            result.percentage = per

    @api.depends('percentage')
    def _compute_result(self):
        '''Method to compute result'''
        for rec in self:
            flag = False
            if rec.result_ids:
                for grade in rec.result_ids:
                    if not grade.grade_line_id.fail:
                            rec.result = 'Pass'
                    else:
                        flag = True
            if flag:
                rec.result = 'Fail'

    @api.model
    def create(self, vals):
        if vals.get('student_id'):
            student = self.env['student.student'].browse(vals.get('student_id'
                                                                  ))
            vals.update({'roll_no_id': student.roll_no,
                         'standard_id': student.standard_id.id
                         })
        return super(ExamResult, self).create(vals)

    @api.multi
    def write(self, vals):
        if vals.get('student_id'):
            student = self.env['student.student'].browse(vals.get('student_id'
                                                                  ))
            vals.update({'roll_no_id': student.roll_no,
                         'standard_id': student.standard_id.id
                         })
        return super(ExamResult, self).write(vals)

    @api.multi
    def unlink(self):
        for rec in self:
            if rec.state != 'draft':
                raise ValidationError(_('''You can delete record in unconfirm
                state only!'''))
        return super(ExamResult, self).unlink()

    @api.onchange('student_id')
    def onchange_student(self):
        '''Method to get standard and roll no of student selected'''
        self.standard_id = False
        self.roll_no_id = False
        if self.student_id:
            self.standard_id = self.student_id.standard_id.id
            self.roll_no_id = self.student_id.roll_no

    s_exam_ids = fields.Many2one("exam.exam", "Examination", required=True,
                                 help="Select Exam")
    student_id = fields.Many2one("student.student", "<NAME>",
                                 required=True,
                                 help="Select Student")
    roll_no_id = fields.Integer(string="Roll No",
                                readonly=True)
    pid = fields.Char(related='student_id.pid', string="Student ID",
                      readonly=True)
    standard_id = fields.Many2one("school.standard", "Standard",
                                  help="Select Standard")
    result_ids = fields.One2many("exam.subject", "exam_id", "Exam Subjects")
    total = fields.Float(compute='_compute_total', string='Obtain Total',
                         store=True, help="Total of marks")
    percentage = fields.Float("Percentage", compute="_compute_per",
                              store=True,
                              help="Percentage Obtained")
    result = fields.Char(compute='_compute_result', string='Result',
                         store=True, help="Result Obtained")
    grade = fields.Char("Grade", compute="_compute_per",
                        store=True, help="Grade Obtained")
    state = fields.Selection([('draft', 'Draft'),
                              ('confirm', 'Confirm'),
                              ('re-evaluation', 'Re-Evaluation'),
                              ('re-evaluation_confirm',
                               'Re-Evaluation Confirm'),
                              ('done', 'Done')],
                             'State', readonly=True,
                             track_visibility='onchange',
                             default='draft')
    color = fields.Integer('Color')
    active = fields.Boolean('Active', default=True)
    grade_system = fields.Many2one('grade.master', "Grade System",
                                   help="Grade System selected")
    message_ids = fields.One2many('mail.message', 'res_id', 'Messages',
                                  domain=lambda self: [('model', '=',
                                                        self._name)],
                                  auto_join=True)
    message_follower_ids = fields.One2many('mail.followers', 'res_id',
                                           'Followers',
                                           domain=lambda self: [('res_model',
                                                                 '=',
                                                                 self._name
                                                                 )])

    @api.multi
    def result_confirm(self):
        '''Method to confirm result'''
        for rec in self:
            for line in rec.result_ids:
                if line.maximum_marks == 0:
                    # Check subject marks not greater than maximum marks
                    raise ValidationError(_('Kindly add maximum\
                            marks of subject "%s".') % (line.subject_id.name))
                elif line.minimum_marks == 0:
                    raise ValidationError(_('Kindly add minimum\
                        marks of subject "%s".') % (line.subject_id.name))
                elif ((line.maximum_marks == 0 or line.minimum_marks == 0) and
                      line.obtain_marks):
                    raise ValidationError(_('Kindly add marks\
                        details of subject "%s"!') % (line.subject_id.name))
            vals = {'grade': rec.grade,
                    'percentage': rec.percentage,
                    'state': 'confirm'
                    }
            rec.write(vals)

    @api.multi
    def re_evaluation_confirm(self):
        '''Method to change state to re_evaluation_confirm'''
        self.state = 're-evaluation_confirm'

    @api.multi
    def result_re_evaluation(self):
        '''Method to set state to re-evaluation'''
        for rec in self:
            for line in rec.result_ids:
                line.marks_reeval = line.obtain_marks
            rec.state = 're-evaluation'

    @api.multi
    def set_done(self):
        '''Method to obtain history of student'''
        history_obj = self.env['student.history']
        for rec in self:
            vals = {'student_id': rec.student_id.id,
                    'academice_year_id': rec.student_id.year.id,
                    'standard_id': rec.standard_id.id,
                    'percentage': rec.percentage,
                    'result': rec.result}
            history = history_obj.search([('student_id', '=',
                                           rec.student_id.id),
                                          ('academice_year_id', '=',
                                           rec.student_id.year.id),
                                          ('standard_id', '=',
                                           rec.standard_id.id)
                                          ])
            if history:
                history_obj.write(vals)
            elif not history:
                history_obj.create(vals)
            rec.state = 'done'


class ExamGradeLine(models.Model):
    _name = "exam.grade.line"
    _description = 'Exam Subject Information'
    _rec_name = 'standard_id'

    standard_id = fields.Many2one('standard.standard', 'Standard')
    exam_id = fields.Many2one('exam.result', 'Result')
    grade = fields.Char('Grade')


class ExamSubject(models.Model):
    _name = "exam.subject"
    _description = 'Exam Subject Information'
    _rec_name = 'subject_id'

    @api.constrains('obtain_marks', 'minimum_marks', 'maximum_marks',
                    'marks_reeval')
    def _validate_marks(self):
        '''Method to validate marks'''
        if self.obtain_marks > self.maximum_marks:
            raise ValidationError(_('''The obtained marks
            should not extend maximum marks!'''))
        if self.minimum_marks > self.maximum_marks:
            raise ValidationError(_('''The minimum marks
            should not extend maximum marks!'''))
        if(self.marks_reeval > self.maximum_marks):
            raise ValidationError(_('''The revaluation marks
            should not extend maximum marks!'''))

    @api.depends('exam_id', 'obtain_marks', 'marks_reeval')
    def _compute_grade(self):
        '''Method to compute grade after re-evaluation'''
        for rec in self:
            grade_lines = rec.exam_id.grade_system.grade_ids
            if (rec.exam_id and rec.exam_id.student_id and grade_lines):
                for grade_id in grade_lines:
                    if rec.obtain_marks and rec.marks_reeval <= 0.0:
                        b_id = rec.obtain_marks <= grade_id.to_mark
                        if (rec.obtain_marks >= grade_id.from_mark and b_id):
                            rec.grade_line_id = grade_id
                    if rec.marks_reeval and rec.obtain_marks >= 0.0:
                        r_id = rec.marks_reeval <= grade_id.to_mark
                        if (rec.marks_reeval >= grade_id.from_mark and r_id):
                            rec.grade_line_id = grade_id

    exam_id = fields.Many2one('exam.result', 'Result')
    state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirm'),
                              ('re-evaluation', 'Re-Evaluation'),
                              ('re-evaluation_confirm',
                               'Re-Evaluation Confirm')],
                             related='exam_id.state', string="State")
    subject_id = fields.Many2one("subject.subject", "Subject Name")
    obtain_marks = fields.Float("Obtain Marks", group_operator="avg")
    minimum_marks = fields.Float("Minimum Marks",
                                 help="Minimum Marks of subject")
    maximum_marks = fields.Float("Maximum Marks",
                                 help="Maximum Marks of subject")
    marks_reeval = fields.Float("Marks After Re-evaluation",
                                help="Marks Obtain after Re-evaluation")
    grade_line_id = fields.Many2one('grade.line', "Grade",
                                    compute='_compute_grade')


class AdditionalExamResult(models.Model):
    _name = 'additional.exam.result'
    _description = 'subject result Information'
    _rec_name = 'roll_no_id'

    @api.depends('a_exam_id', 'obtain_marks')
    def _compute_student_result(self):
        '''Method to compute result of student'''
        for rec in self:
            if rec.a_exam_id and rec.a_exam_id:
                if rec.a_exam_id.minimum_marks < \
                        rec.obtain_marks:
                    rec.result = 'Pass'
                else:
                    rec.result = 'Fail'

    @api.model
    def create(self, vals):
        '''Override create method to get roll no and standard'''
        student = self.env['student.student'].browse(vals.get('student_id'))
        vals.update({'roll_no_id': student.roll_no,
                     'standard_id': student.standard_id.id
                     })
        return super(AdditionalExamResult, self).create(vals)

    @api.multi
    def write(self, vals):
        '''Override write method to get roll no and standard'''
        student = self.env['student.student'].browse(vals.get('student_id'))
        vals.update({'roll_no_id': student.roll_no,
                     'standard_id': student.standard_id.id
                     })
        return super(AdditionalExamResult, self).write(vals)

    @api.onchange('student_id')
    def onchange_student(self):
        ''' Method to get student roll no and standard by selecting student'''
        self.standard_id = self.student_id.standard_id.id
        self.roll_no_id = self.student_id.roll_no

    @api.constrains('obtain_marks')
    def _validate_obtain_marks(self):
        if self.obtain_marks > self.a_exam_id.subject_id.maximum_marks:
            raise ValidationError(_('''The obtained marks should not extend
                                    maximum marks!'''))

    a_exam_id = fields.Many2one('additional.exam', 'Additional Examination',
                                required=True,
                                help="Select Additional Exam")
    student_id = fields.Many2one('student.student', '<NAME>',
                                 required=True,
                                 help="Select Student")
    roll_no_id = fields.Integer("Roll No",
                                readonly=True)
    standard_id = fields.Many2one('school.standard',
                                  "Standard", readonly=True)
    obtain_marks = fields.Float('Obtain Marks', help="Marks obtain in exam")
    result = fields.Char(compute='_compute_student_result', string='Result',
                         help="Result Obtained", store=True)
    active = fields.Boolean('Active', default=True)

<filename>exam/models/exam.py
# See LICENSE file for full copyright and licensing details.

from datetime import date, datetime
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError

import calendar

class StudentStudent(models.Model):
    _inherit = 'student.student'
    _description = 'Student Information'

    exam_results_ids = fields.One2many('exam.result', 'student_id',
                                       'Exam History', readonly=True)

    @api.multi
    def set_alumni(self):
        '''Override method to make exam results of student active false
        when student is alumni'''
        for rec in self:
            addexam_result = self.env['additional.exam.result'].\
                search([('student_id', '=', rec.id)])
            regular_examresult = self.env['exam.result'].\
                search([('student_id', '=', rec.id)])
            if addexam_result:
                addexam_result.write({'active': False})
            if regular_examresult:
                regular_examresult.write({'active': False})
        return super(StudentStudent, self).set_alumni()

    @api.model
    def _search(self, args, offset=0, limit=None, order=None, count=False,
                access_rights_uid=None):
        '''Override method to get exam of student selected'''
        if self._context.get('exam'):
            exam_obj = self.env['exam.exam']
            exam_data = exam_obj.browse(self._context['exam'])
            std_ids = [std_id.id for std_id in exam_data.standard_id]
            args.append(('standard_id', 'in', std_ids))
        return super(StudentStudent, self)._search(
            args=args, offset=offset, limit=limit, order=order, count=count,
            access_rights_uid=access_rights_uid)


class ExtendedTimeTable(models.Model):
    _inherit = 'time.table'

    @api.multi
    def unlink(self):
        exam = self.env['exam.exam']
        schedule_line = self.env['exam.schedule.line']
        for rec in self:
            exam_search = exam.search([('state', '=', 'running')])
            for data in exam_search:
                schedule_line_search = schedule_line.search([('exam_id', '=',
                                                              data.id),
                                                             ('timetable_id',
                                                              '=', rec.id)])
                if schedule_line_search:
                    raise ValidationError(_('''You cannot delete schedule of
                    exam which is in running!'''))
        return super(ExtendedTimeTable, self).unlink()

    timetable_type = fields.Selection(selection_add=[('exam', 'Exam')],
                                      string='Time Table Type', required=True,
                                      inivisible=False)
    exam_timetable_line_ids = fields.One2many('time.table.line', 'table_id',
                                              'TimeTable Lines')
    exam_id = fields.Many2one('exam.exam', 'Exam')

    @api.constrains('exam_timetable_line_ids')
    def _check_exam(self):
        '''Method to check same exam is not assigned on same day'''
        if self.timetable_type == 'exam':
            if not self.exam_timetable_line_ids:
                raise ValidationError(_(''' Please Enter Exam Timetable!'''))
            domain = [('table_id', 'in', self.ids)]
            line_ids = self.env['time.table.line'].search(domain)
            for rec in line_ids:
                records = [rec_check.id for rec_check in line_ids
                           if (rec.day_of_week == rec_check.day_of_week and
                               rec.start_time == rec_check.start_time and
                               rec.end_time == rec_check.end_time and
                               rec.teacher_id.id == rec.teacher_id.id and
                               rec.exm_date == rec.exm_date)]
                if len(records) > 1:
                    raise ValidationError(_('''You cannot set exam at same
                                            time %s  at same day %s for
                                            teacher %s!''') %
                                           (rec.start_time, rec.day_of_week,
                                            rec.teacher_id.name))


class ExtendedTimeTableLine(models.Model):
    _inherit = 'time.table.line'

    exm_date = fields.Date('Exam Date')
    day_of_week = fields.Char('Week Day')
    class_room_id = fields.Many2one('class.room', 'Classroom')

    @api.onchange('exm_date')
    def onchange_date_day(self):
        '''Method to get weekday from date'''
        for rec in self:
            rec.day_of_week = False
            if rec.exm_date:
                rec.day_of_week=calendar.day_name[rec.exm_date.weekday()]  #'Wednesday'
                #week_day = datetime.strptime(rec.exm_date, "%Y-%m-%d")
                #rec.day_of_week = week_day.strftime("%A").title()
                

    @api.multi
    def _check_date(self):
        '''Method to check constraint of start date and end date'''
        for line in self:
            if line.exm_date:
                dt = datetime.strptime(line.exm_date, "%Y-%m-%d")
                if line.week_day != dt.strftime("%A").lower():
                    return False
                elif dt.__str__() < datetime.strptime(date.today().__str__(),
                                                      "%Y-%m-%d").__str__():
                    raise ValidationError(_('''Invalid Date Error !
                        Either you have selected wrong day
                                       for the date or you have selected\
                                       invalid date!'''))

    @api.constrains('teacher_id')
    def check_supervisior_exam(self):
            for rec in self:
                if (rec.table_id.timetable_type == 'exam' and
                        not rec.teacher_id):
                        raise ValidationError(_('''PLease Enter Supervisior!
                        '''))

    @api.constrains('start_time', 'end_time')
    def check_time(self):
        for rec in self:
            if rec.start_time >= rec.end_time:
                raise ValidationError(_('''Start time should be less than end
                time!'''))

    @api.constrains('teacher_id', 'class_room_id')
    def check_teacher_room(self):
        timetable_rec = self.env['time.table'].search([('id', '!=',
                                                        self.table_id.id)])
        if timetable_rec:
            for data in timetable_rec:
                for record in data.timetable_ids:
                    if (data.timetable_type == 'exam' and
                            self.table_id.timetable_type == 'exam' and
                            self.class_room_id == record.class_room_id and
                            self.start_time == record.start_time):
                            raise ValidationError(_("The room is occupied!"))

    @api.constrains('subject_id', 'class_room_id')
    def check_exam_date(self):
        for rec in self.table_id.exam_timetable_line_ids:
            record = self.table_id
            if rec.id not in self.ids:
                if (record.timetable_type == 'exam' and
                        self.exm_date == rec.exm_date and
                        self.start_time == rec.start_time):
                    raise ValidationError(_('''There is already Exam at
                        same Date and Time!'''))
                if (record.timetable_type == 'exam' and
                        self.table_id.timetable_type == 'exam' and
                        self.subject_id == rec.subject_id):
                        raise ValidationError(_('''%s Subject Exam Already
                        Taken''') % (self.subject_id.name))
                if (record.timetable_type == 'exam' and
                        self.table_id.timetable_type == 'exam' and
                        self.exm_date == rec.exm_date and
                        self.class_room_id == rec.class_room_id and
                        self.start_time == rec.start_time):
                    raise ValidationError(_('''%s is occupied by '%s' for %s
                    class!''') % (self.class_room_id.name, record.name,
                                  record.standard_id.standard_id.name))


class ExamExam(models.Model):
    _name = 'exam.exam'
    _description = 'Exam Information'

    @api.constrains('start_date', 'end_date')
    def check_date_exam(self):
        '''Method to check constraint of exam start date and end date'''
        for rec in self:
            if rec.end_date < rec.start_date:
                raise ValidationError(_('''Exam end date should be
                                  greater than start date!'''))
            for line in rec.exam_schedule_ids:
                if line.timetable_id:
                    for tt in line.timetable_id.exam_timetable_line_ids:
                        if not rec.start_date <= tt.exm_date <= rec.end_date:
                            raise ValidationError(_('Invalid Exam Schedule\
                            \n\nExam Dates must be in between Start\
                            date and End date !'))

    @api.constrains('active')
    def check_active(self):
        '''if exam results is not in done state then raise an
        validation Warning'''
        result_obj = self.env['exam.result']
        if not self.active:
            for result in result_obj.search([('s_exam_ids', '=', self.id)]):
                if result.state != 'done':
                    raise ValidationError(_('Kindly,mark as done %s\
                    examination results!') % (self.name))

    active = fields.Boolean('Active', default="True")
    name = fields.Char("Exam Name", required=True,
                       help="Name of Exam")
    exam_code = fields.Char('Exam Code', required=True, readonly=True,
                            help="Code of exam",
                            default=lambda obj:
                            obj.env['ir.sequence'].next_by_code('exam.exam'))
    standard_id = fields.Many2many('standard.standard',
                                   'standard_standard_exam_rel', 'standard_id',
                                   'event_id', 'Participant Standards',
                                   help="Select Standard")
    start_date = fields.Datetime("Exam Start Date",
                             help="Exam will start from this date")
    end_date = fields.Datetime("Exam End date", help="Exam will end at this date")
    duration = fields.Float('Exam Duration', help="Exam duration in minutes")
    state = fields.Selection([('draft', 'Draft'),
                              ('running', 'Running'),
                              ('finished', 'Finished'),
                              ('cancelled', 'Cancelled')], 'State',
                             readonly=True, default='draft')
    grade_system = fields.Many2one('grade.master', "Grade System",
                                   help="Select Grade System")
    academic_year = fields.Many2one('academic.year', 'Academic Year',
                                    help="Select Academic Year")
    exam_schedule_ids = fields.One2many('exam.schedule.line', 'exam_id',
                                        'Exam Schedule')

    @api.multi
    def set_to_draft(self):
        '''Method to set state to draft'''
        self.state = 'draft'

    @api.multi
    def set_running(self):
        '''Method to set state to running'''
        for rec in self:
            if not rec.standard_id:
                raise ValidationError(_('Please Select Standard!'))
            if rec.exam_schedule_ids:
                rec.state = 'running'
            else:
                raise ValidationError(_('You must add one Exam Schedule!'))

    @api.multi
    def set_finish(self):
        '''Method to set state to finish'''
        self.state = 'finished'

    @api.multi
    def set_cancel(self):
        '''Method to set state to cancel'''
        self.state = 'cancelled'

    @api.multi
    def generate_result(self):
        '''Method to generate result'''
        result_obj = self.env['exam.result']
        student_obj = self.env['student.student']
        result_list = []
        for rec in self:
            for exam_schedule in rec.exam_schedule_ids:
                domain = [('standard_id', '=', exam_schedule.standard_id.id),
                          ('year', '=', rec.academic_year.id),
                          ('state', '=', 'done'),
                          ('school_id', '=',
                           exam_schedule.standard_id.school_id.id)]
                students = student_obj.search(domain)
                for student in students:
                    domain = [('standard_id', '=',
                               student.standard_id.id),
                              ('student_id', '=', student.id),
                              ('s_exam_ids', '=', rec.id)]
                    result_exists = result_obj.search(domain)
                    if result_exists:
                        [result_list.append(res.id) for res in result_exists]
                    else:
                        rs_dict = {'s_exam_ids': rec.id,
                                   'student_id': student.id,
                                   'standard_id': student.standard_id.id,
                                   'roll_no_id': student.roll_no,
                                   'grade_system': rec.grade_system.id,
                                   }
                        exam_line = []
                        timetable = exam_schedule.sudo().timetable_id
                        for line in timetable.sudo().timetable_ids:
                            min_mrks = line.subject_id.minimum_marks
                            max_mrks = line.subject_id.maximum_marks
                            sub_vals = {'subject_id': line.subject_id.id,
                                        'minimum_marks': min_mrks,
                                        'maximum_marks': max_mrks}
                            exam_line.append((0, 0, sub_vals))
                        rs_dict.update({'result_ids': exam_line})
                        result = result_obj.create(rs_dict)
                        result_list.append(result.id)
        return {'name': _('Result Info'),
                'view_type': 'form',
                'view_mode': 'tree,form',
                'res_model': 'exam.result',
                'type': 'ir.actions.act_window',
                'domain': [('id', 'in', result_list)]}


class ExamScheduleLine(models.Model):
    _name = 'exam.schedule.line'
    _description = "Exam Schedule Line Details"

    @api.onchange('standard_ids')
    def onchange_standard(self):
        '''Method to get standard according to the standard selected'''
        standard_ids = []
        for rec in self:
            standard_ids = [std.id for std in rec.standard_ids]
        return {'domain': {'standard_id': [('standard_id', 'in',
                                            standard_ids)]}}

    standard_id = fields.Many2one('school.standard', 'Standard',
                                  help="Select Standard")
    timetable_id = fields.Many2one('time.table', 'Exam Schedule')
    exam_id = fields.Many2one('exam.exam', 'Exam')
    standard_ids = fields.Many2many('standard.standard',
                                    string='Participant Standards')


class AdditionalExam(models.Model):
    _name = 'additional.exam'
    _description = 'additional Exam Information'

    @api.multi
    def _compute_color_name(self):
        for rec in self:
            rec.color_name = rec.subject_id.id

    name = fields.Char("Additional Exam Name", required=True,
                       help="Name of Exam")
    addtional_exam_code = fields.Char('Exam Code', required=True,
                                      help="Exam Code",
                                      readonly=True,
                                      default=lambda obj:
                                      obj.env['ir.sequence'].
                                      next_by_code('additional.exam'))
    standard_id = fields.Many2one("school.standard", "Standard")
    subject_id = fields.Many2one("subject.subject", "Subject Name")
    exam_date = fields.Datetime("Exam Date")
    maximum_marks = fields.Integer("Maximum Mark",
                                   help="Minimum Marks of exam")
    minimum_marks = fields.Integer("Minimum Mark",
                                   help="Maximum Marks of Exam")
    weightage = fields.Char("WEIGHTAGE")
    create_date = fields.Date("Created Date", help="Exam Created Date")
    write_date = fields.Date("Updated date", help="Exam Updated Date")
    color_name = fields.Integer("Color index of creator",
                                compute='_compute_color_name', store=False)

    @api.constrains('maximum_marks', 'minimum_marks')
    def check_marks(self):
        if self.minimum_marks > self.maximum_marks:
            raise ValidationError(_('''Configure Maximum marks greater than
            minimum marks!'''))

    @api.model
    def create(self, vals):
        curr_dt = datetime.now()
        new_dt = datetime.strftime(curr_dt, '%m/%d/%Y')
        vals.update({'create_date': new_dt})
        return super(AdditionalExam, self).create(vals)

    @api.multi
    def write(self, vals):
        curr_dt = datetime.now()
        new_dt = datetime.strftime(curr_dt, '%m/%d/%Y')
        vals.update({'write_date': new_dt})
        return super(AdditionalExam, self).write(vals)


class ExamResult(models.Model):
    _name = 'exam.result'
    _inherit = ["mail.thread", "resource.mixin"]
    _rec_name = 'roll_no_id'
    _description = 'exam result Information'

    @api.depends('result_ids', 'result_ids.obtain_marks',
                 'result_ids.marks_reeval')
    def _compute_total(self):
        '''Method to compute total'''
        for rec in self:
            total = 0.0
            if rec.result_ids:
                for line in rec.result_ids:
                    obtain_marks = line.obtain_marks
                    if line.state == "re-evaluation":
                        obtain_marks = line.marks_reeval
                    total += obtain_marks
            rec.total = total

    @api.depends('total')
    def _compute_per(self):
        '''Method to compute percentage'''
        total = 0.0
        obtained_total = 0.0
        obtain_marks = 0.0
        per = 0.0
        for result in self:
            for sub_line in result.result_ids:
                if sub_line.state == "re-evaluation":
                    obtain_marks = sub_line.marks_reeval
                else:
                    obtain_marks = sub_line.obtain_marks
                total += sub_line.maximum_marks or 0
                obtained_total += obtain_marks
            if total > 1.0:
                per = (obtained_total / total) * 100
                if result.grade_system:
                    for grade_id in result.grade_system.grade_ids:
                        if per >= grade_id.from_mark and\
                                per <= grade_id.to_mark:
                            result.grade = grade_id.grade or ''
            result.percentage = per

    @api.depends('percentage')
    def _compute_result(self):
        '''Method to compute result'''
        for rec in self:
            flag = False
            if rec.result_ids:
                for grade in rec.result_ids:
                    if not grade.grade_line_id.fail:
                            rec.result = 'Pass'
                    else:
                        flag = True
            if flag:
                rec.result = 'Fail'

    @api.model
    def create(self, vals):
        if vals.get('student_id'):
            student = self.env['student.student'].browse(vals.get('student_id'
                                                                  ))
            vals.update({'roll_no_id': student.roll_no,
                         'standard_id': student.standard_id.id
                         })
        return super(ExamResult, self).create(vals)

    @api.multi
    def write(self, vals):
        if vals.get('student_id'):
            student = self.env['student.student'].browse(vals.get('student_id'
                                                                  ))
            vals.update({'roll_no_id': student.roll_no,
                         'standard_id': student.standard_id.id
                         })
        return super(ExamResult, self).write(vals)

    @api.multi
    def unlink(self):
        for rec in self:
            if rec.state != 'draft':
                raise ValidationError(_('''You can delete record in unconfirm
                state only!'''))
        return super(ExamResult, self).unlink()

    @api.onchange('student_id')
    def onchange_student(self):
        '''Method to get standard and roll no of student selected'''
        self.standard_id = False
        self.roll_no_id = False
        if self.student_id:
            self.standard_id = self.student_id.standard_id.id
            self.roll_no_id = self.student_id.roll_no

    s_exam_ids = fields.Many2one("exam.exam", "Examination", required=True,
                                 help="Select Exam")
    student_id = fields.Many2one("student.student", "<NAME>",
                                 required=True,
                                 help="Select Student")
    roll_no_id = fields.Integer(string="Roll No",
                                readonly=True)
    pid = fields.Char(related='student_id.pid', string="Student ID",
                      readonly=True)
    standard_id = fields.Many2one("school.standard", "Standard",
                                  help="Select Standard")
    result_ids = fields.One2many("exam.subject", "exam_id", "Exam Subjects")
    total = fields.Float(compute='_compute_total', string='Obtain Total',
                         store=True, help="Total of marks")
    percentage = fields.Float("Percentage", compute="_compute_per",
                              store=True,
                              help="Percentage Obtained")
    result = fields.Char(compute='_compute_result', string='Result',
                         store=True, help="Result Obtained")
    grade = fields.Char("Grade", compute="_compute_per",
                        store=True, help="Grade Obtained")
    state = fields.Selection([('draft', 'Draft'),
                              ('confirm', 'Confirm'),
                              ('re-evaluation', 'Re-Evaluation'),
                              ('re-evaluation_confirm',
                               'Re-Evaluation Confirm'),
                              ('done', 'Done')],
                             'State', readonly=True,
                             track_visibility='onchange',
                             default='draft')
    color = fields.Integer('Color')
    active = fields.Boolean('Active', default=True)
    grade_system = fields.Many2one('grade.master', "Grade System",
                                   help="Grade System selected")
    message_ids = fields.One2many('mail.message', 'res_id', 'Messages',
                                  domain=lambda self: [('model', '=',
                                                        self._name)],
                                  auto_join=True)
    message_follower_ids = fields.One2many('mail.followers', 'res_id',
                                           'Followers',
                                           domain=lambda self: [('res_model',
                                                                 '=',
                                                                 self._name
                                                                 )])

    @api.multi
    def result_confirm(self):
        '''Method to confirm result'''
        for rec in self:
            for line in rec.result_ids:
                if line.maximum_marks == 0:
                    # Check subject marks not greater than maximum marks
                    raise ValidationError(_('Kindly add maximum\
                            marks of subject "%s".') % (line.subject_id.name))
                elif line.minimum_marks == 0:
                    raise ValidationError(_('Kindly add minimum\
                        marks of subject "%s".') % (line.subject_id.name))
                elif ((line.maximum_marks == 0 or line.minimum_marks == 0) and
                      line.obtain_marks):
                    raise ValidationError(_('Kindly add marks\
                        details of subject "%s"!') % (line.subject_id.name))
            vals = {'grade': rec.grade,
                    'percentage': rec.percentage,
                    'state': 'confirm'
                    }
            rec.write(vals)

    @api.multi
    def re_evaluation_confirm(self):
        '''Method to change state to re_evaluation_confirm'''
        self.state = 're-evaluation_confirm'

    @api.multi
    def result_re_evaluation(self):
        '''Method to set state to re-evaluation'''
        for rec in self:
            for line in rec.result_ids:
                line.marks_reeval = line.obtain_marks
            rec.state = 're-evaluation'

    @api.multi
    def set_done(self):
        '''Method to obtain history of student'''
        history_obj = self.env['student.history']
        for rec in self:
            vals = {'student_id': rec.student_id.id,
                    'academice_year_id': rec.student_id.year.id,
                    'standard_id': rec.standard_id.id,
                    'percentage': rec.percentage,
                    'result': rec.result}
            history = history_obj.search([('student_id', '=',
                                           rec.student_id.id),
                                          ('academice_year_id', '=',
                                           rec.student_id.year.id),
                                          ('standard_id', '=',
                                           rec.standard_id.id)
                                          ])
            if history:
                history_obj.write(vals)
            elif not history:
                history_obj.create(vals)
            rec.state = 'done'


class ExamGradeLine(models.Model):
    _name = "exam.grade.line"
    _description = 'Exam Subject Information'
    _rec_name = 'standard_id'

    standard_id = fields.Many2one('standard.standard', 'Standard')
    exam_id = fields.Many2one('exam.result', 'Result')
    grade = fields.Char('Grade')


class ExamSubject(models.Model):
    _name = "exam.subject"
    _description = 'Exam Subject Information'
    _rec_name = 'subject_id'

    @api.constrains('obtain_marks', 'minimum_marks', 'maximum_marks',
                    'marks_reeval')
    def _validate_marks(self):
        '''Method to validate marks'''
        if self.obtain_marks > self.maximum_marks:
            raise ValidationError(_('''The obtained marks
            should not extend maximum marks!'''))
        if self.minimum_marks > self.maximum_marks:
            raise ValidationError(_('''The minimum marks
            should not extend maximum marks!'''))
        if(self.marks_reeval > self.maximum_marks):
            raise ValidationError(_('''The revaluation marks
            should not extend maximum marks!'''))

    @api.depends('exam_id', 'obtain_marks', 'marks_reeval')
    def _compute_grade(self):
        '''Method to compute grade after re-evaluation'''
        for rec in self:
            grade_lines = rec.exam_id.grade_system.grade_ids
            if (rec.exam_id and rec.exam_id.student_id and grade_lines):
                for grade_id in grade_lines:
                    if rec.obtain_marks and rec.marks_reeval <= 0.0:
                        b_id = rec.obtain_marks <= grade_id.to_mark
                        if (rec.obtain_marks >= grade_id.from_mark and b_id):
                            rec.grade_line_id = grade_id
                    if rec.marks_reeval and rec.obtain_marks >= 0.0:
                        r_id = rec.marks_reeval <= grade_id.to_mark
                        if (rec.marks_reeval >= grade_id.from_mark and r_id):
                            rec.grade_line_id = grade_id

    exam_id = fields.Many2one('exam.result', 'Result')
    state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirm'),
                              ('re-evaluation', 'Re-Evaluation'),
                              ('re-evaluation_confirm',
                               'Re-Evaluation Confirm')],
                             related='exam_id.state', string="State")
    subject_id = fields.Many2one("subject.subject", "Subject Name")
    obtain_marks = fields.Float("Obtain Marks", group_operator="avg")
    minimum_marks = fields.Float("Minimum Marks",
                                 help="Minimum Marks of subject")
    maximum_marks = fields.Float("Maximum Marks",
                                 help="Maximum Marks of subject")
    marks_reeval = fields.Float("Marks After Re-evaluation",
                                help="Marks Obtain after Re-evaluation")
    grade_line_id = fields.Many2one('grade.line', "Grade",
                                    compute='_compute_grade')


class AdditionalExamResult(models.Model):
    _name = 'additional.exam.result'
    _description = 'subject result Information'
    _rec_name = 'roll_no_id'

    @api.depends('a_exam_id', 'obtain_marks')
    def _compute_student_result(self):
        '''Method to compute result of student'''
        for rec in self:
            if rec.a_exam_id and rec.a_exam_id:
                if rec.a_exam_id.minimum_marks < \
                        rec.obtain_marks:
                    rec.result = 'Pass'
                else:
                    rec.result = 'Fail'

    @api.model
    def create(self, vals):
        '''Override create method to get roll no and standard'''
        student = self.env['student.student'].browse(vals.get('student_id'))
        vals.update({'roll_no_id': student.roll_no,
                     'standard_id': student.standard_id.id
                     })
        return super(AdditionalExamResult, self).create(vals)

    @api.multi
    def write(self, vals):
        '''Override write method to get roll no and standard'''
        student = self.env['student.student'].browse(vals.get('student_id'))
        vals.update({'roll_no_id': student.roll_no,
                     'standard_id': student.standard_id.id
                     })
        return super(AdditionalExamResult, self).write(vals)

    @api.onchange('student_id')
    def onchange_student(self):
        ''' Method to get student roll no and standard by selecting student'''
        self.standard_id = self.student_id.standard_id.id
        self.roll_no_id = self.student_id.roll_no

    @api.constrains('obtain_marks')
    def _validate_obtain_marks(self):
        if self.obtain_marks > self.a_exam_id.subject_id.maximum_marks:
            raise ValidationError(_('''The obtained marks should not extend
                                    maximum marks!'''))

    a_exam_id = fields.Many2one('additional.exam', 'Additional Examination',
                                required=True,
                                help="Select Additional Exam")
    student_id = fields.Many2one('student.student', '<NAME>',
                                 required=True,
                                 help="Select Student")
    roll_no_id = fields.Integer("Roll No",
                                readonly=True)
    standard_id = fields.Many2one('school.standard',
                                  "Standard", readonly=True)
    obtain_marks = fields.Float('Obtain Marks', help="Marks obtain in exam")
    result = fields.Char(compute='_compute_student_result', string='Result',
                         help="Result Obtained", store=True)
    active = fields.Boolean('Active', default=True)

# See LICENSE file for full copyright and licensing details. Override method to make exam results of student active false         when student is alumni Override method to get exam of student selected You cannot delete schedule of                     exam which is in running! Method to check same exam is not assigned on same day Please Enter Exam Timetable! You cannot set exam at same                                             time %s  at same day %s for                                             teacher %s! Method to get weekday from date #'Wednesday' #week_day = datetime.strptime(rec.exm_date, "%Y-%m-%d") #rec.day_of_week = week_day.strftime("%A").title() Method to check constraint of start date and end date Invalid Date Error !                         Either you have selected wrong day                                        for the date or you have selected\                                        invalid date! PLease Enter Supervisior! Start time should be less than end                 time! There is already Exam at                         same Date and Time! %s Subject Exam Already                         Taken %s is occupied by '%s' for %s                     class! Method to check constraint of exam start date and end date Exam end date should be                                   greater than start date! if exam results is not in done state then raise an         validation Warning Method to set state to draft Method to set state to running Method to set state to finish Method to set state to cancel Method to generate result Method to get standard according to the standard selected Configure Maximum marks greater than             minimum marks! Method to compute total Method to compute percentage Method to compute result You can delete record in unconfirm                 state only! Method to get standard and roll no of student selected Method to confirm result # Check subject marks not greater than maximum marks Method to change state to re_evaluation_confirm Method to set state to re-evaluation Method to obtain history of student Method to validate marks The obtained marks             should not extend maximum marks! The minimum marks             should not extend maximum marks! The revaluation marks             should not extend maximum marks! Method to compute grade after re-evaluation Method to compute result of student Override create method to get roll no and standard Override write method to get roll no and standard Method to get student roll no and standard by selecting student The obtained marks should not extend                                     maximum marks!