243 lines
8.9 KiB
Python
243 lines
8.9 KiB
Python
from odoo import models, fields, api
|
|
from odoo.exceptions import ValidationError, UserError
|
|
|
|
class SchoolGradesheet(models.Model):
|
|
_name = 'school.gradesheet'
|
|
_description = 'Student Gradesheet Report'
|
|
_order = 'session, class_name, course_id'
|
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
|
|
|
# Filter Fields
|
|
session = fields.Char(string="Session", required=True, help="Academic Session (e.g., 2024-2025)")
|
|
# application_id = fields.Many2one('school.application', string="Application", required=True)
|
|
# student_id = fields.Many2one('res.partner', string='Student', required=True)
|
|
# roll_number = fields.Char(string="Roll Number")
|
|
obtained_marks = fields.Float(
|
|
string="Obtained Marks",
|
|
default=0.0
|
|
)
|
|
total_marks = fields.Float(
|
|
string="Total Marks",
|
|
default=100.0
|
|
)
|
|
percentage = fields.Float(
|
|
string="Percentage (%)",
|
|
compute="_compute_percentage",
|
|
store=True
|
|
)
|
|
final_grade = fields.Selection([
|
|
('A+', 'A+ (90-100%)'),
|
|
('A', 'A (80-89%)'),
|
|
('B+', 'B+ (70-79%)'),
|
|
('B', 'B (60-69%)'),
|
|
('C', 'C (50-59%)'),
|
|
('D', 'D (40-49%)'),
|
|
('F', 'F (Below 40%)')
|
|
], string="Grade", default='F')
|
|
|
|
status = fields.Selection([
|
|
('pass', 'Pass'),
|
|
('fail', 'Fail')
|
|
], string="Status", default='fail')
|
|
|
|
remarks = fields.Text(string="Remarks")
|
|
class_name = fields.Selection([
|
|
('1', 'Class 1'), ('2', 'Class 2'), ('3', 'Class 3'),
|
|
('4', 'Class 4'), ('5', 'Class 5'), ('6', 'Class 6'),
|
|
('7', 'Class 7'), ('8', 'Class 8'), ('9', 'Class 9'),
|
|
('10', 'Class 10'), ('11', 'Class 11'), ('12', 'Class 12'),
|
|
], string="Class", required=True)
|
|
|
|
course_id = fields.Many2one('school.course', string="Course", required=True)
|
|
subject_id = fields.Many2one('school.subject', string="Subject")
|
|
|
|
# Report Generation Status
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('generated', 'Generated'),
|
|
('finalized', 'Finalized')
|
|
], string="Status", default='draft', tracking=True)
|
|
|
|
# Generated Data
|
|
student_grade_ids = fields.One2many(
|
|
'school.gradesheet.line', 'gradesheet_id', string="Student Grades"
|
|
)
|
|
|
|
# Report Info
|
|
generated_date = fields.Datetime(string="Generated Date", readonly=True)
|
|
generated_by = fields.Many2one('res.users', string="Generated By", readonly=True)
|
|
total_students = fields.Integer(string="Total Students", compute="_compute_student_stats", store=True)
|
|
passed_students = fields.Integer(string="Passed Students", compute="_compute_student_stats", store=True)
|
|
failed_students = fields.Integer(string="Failed Students", compute="_compute_student_stats", store=True)
|
|
pass_percentage = fields.Float(string="Pass Percentage", compute="_compute_student_stats", store=True)
|
|
|
|
@api.depends('student_grade_ids', 'student_grade_ids.final_grade', 'student_grade_ids.status')
|
|
def _compute_student_stats(self):
|
|
for record in self:
|
|
total = len(record.student_grade_ids)
|
|
passed = len(record.student_grade_ids.filtered(lambda x: x.status == 'pass'))
|
|
failed = total - passed
|
|
|
|
record.total_students = total
|
|
record.passed_students = passed
|
|
record.failed_students = failed
|
|
record.pass_percentage = (passed / total * 100) if total > 0 else 0.0
|
|
|
|
@api.onchange('class_name')
|
|
def _onchange_class_name(self):
|
|
if self.class_name:
|
|
return {
|
|
'domain': {
|
|
'course_id': [('class_id.name', '=', f'Class {self.class_name}')],
|
|
'subject_id': [('class_name', '=', f'Class {self.class_name}')], #Add this
|
|
}
|
|
}
|
|
return {
|
|
'domain': {
|
|
'course_id': [],
|
|
'subject_id': [],
|
|
}
|
|
}
|
|
|
|
def action_fetch_student_marks(self):
|
|
"""Fetch student marks and generate gradesheet"""
|
|
self.ensure_one()
|
|
|
|
if not all([self.session, self.class_name, self.course_id]):
|
|
raise UserError("Please fill all required fields: Session, Class, and Course.")
|
|
|
|
# Clear existing lines
|
|
self.student_grade_ids.unlink()
|
|
|
|
# Get enrolled students for the course
|
|
enrollments = self.env['school.enrollment'].search([
|
|
('course_id', '=', self.course_id.id),
|
|
('class_name', '=', self.class_name),
|
|
('session', '=', self.session),
|
|
('status', '=', 'confirmed')
|
|
])
|
|
|
|
if not enrollments:
|
|
raise UserError(f"No enrolled students found for Class {self.class_name}, Course {self.course_id.name}, Session {self.session}")
|
|
|
|
grade_lines = []
|
|
for enrollment in enrollments:
|
|
# Get student marks (you may need to adapt this based on your marks/exam model)
|
|
student_marks = self._get_student_marks(enrollment)
|
|
|
|
grade_lines.append({
|
|
'gradesheet_id': self.id,
|
|
'student_id': enrollment.student_id.id,
|
|
'enrollment_id': enrollment.id,
|
|
'student_name': enrollment.student_id.name,
|
|
'roll_number': enrollment.application_id.roll_no if enrollment.application_id else '',
|
|
'obtained_marks': student_marks.get('obtained_marks', 0),
|
|
'total_marks': student_marks.get('total_marks', 100),
|
|
'percentage': student_marks.get('percentage', 0),
|
|
'final_grade': student_marks.get('grade', 'F'),
|
|
'status': student_marks.get('status', 'fail'),
|
|
'remarks': student_marks.get('remarks', ''),
|
|
'subject_id': self.subject_id.id,
|
|
})
|
|
|
|
# Create grade lines
|
|
self.env['school.gradesheet.line'].create(grade_lines)
|
|
|
|
# Update gradesheet status
|
|
self.write({
|
|
'state': 'generated',
|
|
'generated_date': fields.Datetime.now(),
|
|
'generated_by': self.env.user.id,
|
|
})
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'message': f'Gradesheet generated successfully! Found {len(grade_lines)} students.',
|
|
'type': 'success',
|
|
'sticky': False,
|
|
}
|
|
}
|
|
|
|
def _get_student_marks(self, enrollment):
|
|
"""
|
|
Get student marks from your existing marks/exam system
|
|
This is a placeholder - adapt according to your actual marks model
|
|
"""
|
|
# Example: If you have a student.marks model or exam.result model
|
|
# marks_record = self.env['student.marks'].search([
|
|
# ('student_id', '=', enrollment.student_id.id),
|
|
# ('course_id', '=', self.course_id.id),
|
|
# ('session', '=', self.session)
|
|
# ], limit=1)
|
|
|
|
# For demo purposes, generating sample marks
|
|
# Replace this with actual marks fetching logic
|
|
import random
|
|
obtained = random.randint(30, 95)
|
|
total = 100
|
|
percentage = (obtained / total) * 100
|
|
|
|
# Grade calculation
|
|
if percentage >= 90:
|
|
grade = 'A+'
|
|
status = 'pass'
|
|
elif percentage >= 80:
|
|
grade = 'A'
|
|
status = 'pass'
|
|
elif percentage >= 70:
|
|
grade = 'B+'
|
|
status = 'pass'
|
|
elif percentage >= 60:
|
|
grade = 'B'
|
|
status = 'pass'
|
|
elif percentage >= 50:
|
|
grade = 'C'
|
|
status = 'pass'
|
|
elif percentage >= 40:
|
|
grade = 'D'
|
|
status = 'pass'
|
|
else:
|
|
grade = 'F'
|
|
status = 'fail'
|
|
|
|
return {
|
|
'obtained_marks': obtained,
|
|
'total_marks': total,
|
|
'percentage': percentage,
|
|
'grade': grade,
|
|
'status': status,
|
|
'remarks': 'Good' if status == 'pass' else 'Needs Improvement'
|
|
}
|
|
|
|
def action_finalize_gradesheet(self):
|
|
"""Finalize the gradesheet"""
|
|
self.ensure_one()
|
|
if self.state != 'generated':
|
|
raise UserError("Please generate the gradesheet first.")
|
|
|
|
self.state = 'finalized'
|
|
return True
|
|
|
|
def action_reset_to_draft(self):
|
|
"""Reset gradesheet to draft"""
|
|
self.ensure_one()
|
|
self.student_grade_ids.unlink()
|
|
self.write({
|
|
'state': 'draft',
|
|
'generated_date': False,
|
|
'generated_by': False,
|
|
})
|
|
return True
|
|
|
|
def action_print_gradesheet(self):
|
|
"""Print gradesheet report"""
|
|
self.ensure_one()
|
|
if not self.student_grade_ids:
|
|
raise UserError("No student data found. Please fetch student marks first.")
|
|
|
|
return self.env.ref('school_management.report_school_gradesheet').report_action(self)
|
|
|
|
|