from odoo import api, fields, models from odoo.exceptions import ValidationError class DanceRehearsal(models.Model): _name = "dance.rehearsal" _description = "Dance Event Rehearsal" _order = "date desc, start_time desc, id desc" name = fields.Char(compute="_compute_name", store=True) event_id = fields.Many2one("dance.event", required=True, ondelete="cascade") date = fields.Date(required=True, default=fields.Date.context_today) start_time = fields.Float(string="Start Time", required=True) end_time = fields.Float(string="End Time") display_time = fields.Char(compute="_compute_display_time") room = fields.Char() status = fields.Selection( [ ("scheduled", "Scheduled"), ("completed", "Completed"), ("cancelled", "Cancelled"), ], default="scheduled", required=True, ) attendance_ids = fields.One2many( "dance.rehearsal.attendance", "rehearsal_id", string="Attendance", ) attendance_count = fields.Integer(compute="_compute_attendance_counts") present_count = fields.Integer(compute="_compute_attendance_counts") absent_count = fields.Integer(compute="_compute_attendance_counts") late_count = fields.Integer(compute="_compute_attendance_counts") notes = fields.Text() @api.depends("event_id", "date", "start_time") def _compute_name(self): for rehearsal in self: if rehearsal.event_id and rehearsal.date: rehearsal.name = "%s rehearsal - %s" % ( rehearsal.event_id.name, rehearsal.date, ) else: rehearsal.name = "New Rehearsal" @api.depends("start_time", "end_time") def _compute_display_time(self): for rehearsal in self: rehearsal.display_time = rehearsal._float_to_time(rehearsal.start_time) if rehearsal.end_time: rehearsal.display_time += " - %s" % rehearsal._float_to_time(rehearsal.end_time) @api.depends("attendance_ids.status") def _compute_attendance_counts(self): for rehearsal in self: rehearsal.attendance_count = len(rehearsal.attendance_ids) rehearsal.present_count = len( rehearsal.attendance_ids.filtered(lambda line: line.status == "present") ) rehearsal.absent_count = len( rehearsal.attendance_ids.filtered(lambda line: line.status == "absent") ) rehearsal.late_count = len( rehearsal.attendance_ids.filtered(lambda line: line.status == "late") ) @api.constrains("start_time", "end_time") def _check_times(self): for rehearsal in self: if rehearsal.end_time and rehearsal.start_time and rehearsal.end_time <= rehearsal.start_time: raise ValidationError("End time must be after start time.") def action_load_students(self): for rehearsal in self: students = rehearsal.event_id.performance_ids.mapped("student_id") or rehearsal.event_id.student_ids existing_student_ids = set(rehearsal.attendance_ids.mapped("student_id").ids) vals_list = [] for student in students: if student.id in existing_student_ids: continue vals_list.append( { "rehearsal_id": rehearsal.id, "student_id": student.id, "status": "present", } ) if vals_list: self.env["dance.rehearsal.attendance"].create(vals_list) return True def action_mark_all_present(self): for rehearsal in self: if not rehearsal.attendance_ids: rehearsal.action_load_students() rehearsal.attendance_ids.write({"status": "present"}) return True def action_complete(self): self.write({"status": "completed"}) return True def action_cancel(self): self.write({"status": "cancelled"}) return True def action_send_rehearsal_reminder(self): for rehearsal in self: students = rehearsal.attendance_ids.mapped("student_id") or rehearsal.event_id.performance_ids.mapped("student_id") or rehearsal.event_id.student_ids rehearsal.event_id._create_whatsapp_logs( "rehearsal_reminder", students, rehearsal=rehearsal, ) return True def _float_to_time(self, value): hours = int(value or 0) minutes = int(round(((value or 0) - hours) * 60)) suffix = "AM" if hours < 12 else "PM" display_hours = hours % 12 or 12 return "%d:%02d %s" % (display_hours, minutes, suffix) class DanceRehearsalAttendance(models.Model): _name = "dance.rehearsal.attendance" _description = "Dance Rehearsal Attendance" _order = "rehearsal_id desc, student_id" rehearsal_id = fields.Many2one("dance.rehearsal", required=True, ondelete="cascade") event_id = fields.Many2one(related="rehearsal_id.event_id", store=True, readonly=True) student_id = fields.Many2one( "res.partner", required=True, domain="[('is_student', '=', True)]", ondelete="restrict", ) date = fields.Date(related="rehearsal_id.date", store=True, readonly=True) status = fields.Selection( [ ("present", "Present"), ("absent", "Absent"), ("late", "Late"), ], default="present", required=True, ) remarks = fields.Text() _sql_constraints = [ ( "unique_rehearsal_attendance", "unique(student_id, rehearsal_id)", "Attendance is already marked for this student in this rehearsal.", ), ] @api.constrains("student_id", "rehearsal_id") def _check_student_in_event(self): for attendance in self: if attendance.student_id and attendance.rehearsal_id: event = attendance.rehearsal_id.event_id event_students = event.performance_ids.mapped("student_id") or event.student_ids if attendance.student_id not in event_students: raise ValidationError( "Attendance can only be marked for students participating in this event." )