import uuid from django.conf import settings from django.db import models class CompletionMode(models.TextChoices): ONLINE = "online", "Online" OFFLINE = "offline", "Offline" BLENDED = "blended", "Blended" class CompletionStatus(models.TextChoices): NOT_STARTED = "not_started", "Not Started" IN_PROGRESS = "in_progress", "In Progress" PENDING_SIGNOFF = "pending_signoff", "Pending Trainer Signoff" COMPLETED = "completed", "Completed" FAILED = "failed", "Failed" class SignoffDecision(models.TextChoices): APPROVED = "approved", "Approved" REJECTED = "rejected", "Rejected" class TrainingRecord(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) enrollment = models.OneToOneField("tracking.Enrollment", on_delete=models.CASCADE, related_name="training_record") completion_mode = models.CharField(max_length=20, choices=CompletionMode.choices, default=CompletionMode.ONLINE) status = models.CharField(max_length=20, choices=CompletionStatus.choices, default=CompletionStatus.NOT_STARTED) online_passed_at = models.DateTimeField(null=True, blank=True) completed_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = "training_record" class TrainerSignoff(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) training_record = models.ForeignKey(TrainingRecord, on_delete=models.CASCADE, related_name="signoffs") trainer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="given_signoffs") decision = models.CharField(max_length=20, choices=SignoffDecision.choices) notes = models.TextField(blank=True) signed_off_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = "training_trainer_signoff" ordering = ["-signed_off_at"] class SignoffEvidence(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) training_record = models.ForeignKey(TrainingRecord, on_delete=models.CASCADE, related_name="evidence") file_name = models.CharField(max_length=255) file_path = models.CharField(max_length=1000) uploaded_at = models.DateTimeField(auto_now_add=True) uploaded_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="uploaded_evidence") class Meta: db_table = "training_signoff_evidence" class AttendanceStatus(models.TextChoices): PRESENT = "present", "Present" ABSENT = "absent", "Absent" LATE = "late", "Late" class Meeting(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) title = models.CharField(max_length=255) starts_at = models.DateTimeField() created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = "training_meeting" ordering = ["starts_at", "created_at"] class MeetingParticipant(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE, related_name="participants") user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="meeting_participations") attendance_status = models.CharField(max_length=20, choices=AttendanceStatus.choices, null=True, blank=True) attendance_recorded_at = models.DateTimeField(null=True, blank=True) attendance_recorded_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="recorded_meeting_attendance") attendance_session = models.ForeignKey( "attendance.AttendanceSession", on_delete=models.SET_NULL, null=True, blank=True, related_name="meeting_attendance_links", ) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = "training_meeting_participant" constraints = [models.UniqueConstraint(fields=["meeting", "user"], name="training_unique_meeting_participant")] class CallSessionStatus(models.TextChoices): ACTIVE = "active", "Active" ENDED = "ended", "Ended" class CallSession(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) meeting = models.OneToOneField(Meeting, on_delete=models.CASCADE, related_name="call_session") status = models.CharField(max_length=20, choices=CallSessionStatus.choices, default=CallSessionStatus.ACTIVE) started_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="started_call_sessions", ) started_at = models.DateTimeField(auto_now_add=True) ended_at = models.DateTimeField(null=True, blank=True) class Meta: db_table = "training_call_session" class CallParticipantStatus(models.TextChoices): JOINED = "joined", "Joined" LEFT = "left", "Left" class CallParticipant(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) session = models.ForeignKey(CallSession, on_delete=models.CASCADE, related_name="call_participants") user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="call_participations", ) status = models.CharField(max_length=20, choices=CallParticipantStatus.choices, default=CallParticipantStatus.JOINED) is_muted = models.BooleanField(default=False) screen_share_active = models.BooleanField(default=False) joined_at = models.DateTimeField(auto_now_add=True) left_at = models.DateTimeField(null=True, blank=True) class Meta: db_table = "training_call_participant" class CallEventType(models.TextChoices): JOIN = "join", "Join" LEAVE = "leave", "Leave" MUTE = "mute", "Mute" UNMUTE = "unmute", "Unmute" KICK = "kick", "Kick" SCREEN_SHARE_START = "screen_share_start", "Screen Share Start" SCREEN_SHARE_STOP = "screen_share_stop", "Screen Share Stop" SESSION_STARTED = "session_started", "Session Started" SESSION_ENDED = "session_ended", "Session Ended" class CallEvent(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) session = models.ForeignKey(CallSession, on_delete=models.CASCADE, related_name="events") event_type = models.CharField(max_length=30, choices=CallEventType.choices) actor = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="call_events_as_actor", ) target_user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="call_events_as_target", ) metadata = models.JSONField(default=dict, blank=True) occurred_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = "training_call_event" ordering = ["occurred_at"] class EmployeeAttendance(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="employee_attendance") attendance_date = models.DateField() status = models.CharField(max_length=20, choices=AttendanceStatus.choices) meeting = models.ForeignKey(Meeting, on_delete=models.SET_NULL, null=True, blank=True, related_name="employee_attendance_entries") source = models.CharField(max_length=64, default="meeting") updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="updated_employee_attendance") updated_at = models.DateTimeField(auto_now=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = "training_employee_attendance" constraints = [ models.UniqueConstraint( fields=["user", "attendance_date", "meeting"], name="training_unique_employee_attendance_for_meeting", ) ]