""" Employee Evaluation Models Models for tracking employee performance metrics and reports for the PAD Department Weekly Dashboard evaluation system. """ from django.db import models from django.utils.translation import gettext_lazy as _ from apps.core.models import UUIDModel, TimeStampedModel class EvaluationNote(UUIDModel, TimeStampedModel): """ Notes for employee evaluation tracking. Tracks notes categorized by type and sub-category for each staff member. """ CATEGORY_CHOICES = [ ("non_medical", "Non-Medical"), ("medical", "Medical"), ("er", "ER"), ("hospital", "Hospital"), ] SUBCATEGORY_CHOICES = [ ("it_app", "IT - App"), ("lab", "LAB"), ("doctors_managers_reception", "Doctors/Managers/Reception"), ("hospital_general", "Hospital"), ("medical_reports", "Medical Reports"), ("doctors", "Doctors"), ("other", "Other"), ] staff = models.ForeignKey( "accounts.User", on_delete=models.CASCADE, related_name="evaluation_notes", help_text="Staff member this note is for", ) category = models.CharField(max_length=50, choices=CATEGORY_CHOICES, help_text="Note category") sub_category = models.CharField(max_length=50, choices=SUBCATEGORY_CHOICES, help_text="Note sub-category") count = models.IntegerField(default=1, help_text="Number of notes in this category") note_date = models.DateField(help_text="Date the note was recorded") description = models.TextField(blank=True, help_text="Optional description") created_by = models.ForeignKey( "accounts.User", on_delete=models.SET_NULL, null=True, blank=True, related_name="notes_created", help_text="User who created this note entry", ) class Meta: ordering = ["-note_date", "category", "sub_category"] verbose_name = "Evaluation Note" verbose_name_plural = "Evaluation Notes" indexes = [ models.Index(fields=["staff", "note_date"]), models.Index(fields=["category", "sub_category"]), models.Index(fields=["note_date"]), ] def __str__(self): return f"{self.staff} - {self.get_category_display()} - {self.note_date}" class ComplaintRequest(UUIDModel, TimeStampedModel): """ Tracks complaint request filling and status. Monitors whether complaint requests were filled, on hold, or came from barcode scanning. """ FILLING_TIME_CHOICES = [ ("same_time", "Same Time"), ("within_6h", "Within 6 Hours"), ("6_to_24h", "6 to 24 Hours"), ("after_1_day", "After 1 Day"), ("not_mentioned", "Time Not Mentioned"), ] NON_ACTIVATION_REASON_CHOICES = [ ("converted_to_note", "تم تحويلها ملاحظة (Converted to observation)"), ("issue_resolved_immediately", "تم حل الاشكالية (Issue resolved immediately)"), ("not_meeting_conditions", "غير مستوفية للشروط (Does not meet conditions)"), ("raised_via_cchi", "تم رفع الشكوى عن طريق مجلس الضمان الصحي (Raised via CCHI)"), ("request_not_activated", "لم يتم تفعيل طلب الشكوى (Request not activated)"), ("complainant_withdrew", "بناء على طلب المشتكي (Per complainant request)"), ("complainant_retracted", "المشتكي تنازل عن الشكوى (Complainant retracted)"), ("duplicate", "مكررة (Duplicate)"), ("other", "Other"), ] staff = models.ForeignKey( "accounts.User", on_delete=models.CASCADE, related_name="complaint_requests_sent", help_text="Staff member who sent/filled the request", ) complaint = models.ForeignKey( "complaints.Complaint", on_delete=models.SET_NULL, null=True, blank=True, related_name="complaint_request_records", help_text="Related complaint (if any)", ) hospital = models.ForeignKey( "organizations.Hospital", on_delete=models.SET_NULL, null=True, blank=True, related_name="complaint_requests", help_text="Hospital where the request was made", ) patient_name = models.CharField(max_length=200, blank=True, help_text="Patient name") file_number = models.CharField(max_length=100, blank=True, help_text="Patient file number") complained_department = models.ForeignKey( "organizations.Department", on_delete=models.SET_NULL, null=True, blank=True, related_name="complaint_requests", help_text="Department being complained about", ) incident_date = models.DateField(null=True, blank=True, help_text="Date of the incident") phone_number = models.CharField(max_length=20, blank=True, help_text="Patient phone number") filled = models.BooleanField(default=False, help_text="Whether the request was filled") on_hold = models.BooleanField(default=False, help_text="Whether the request is on hold") not_filled = models.BooleanField(default=False, help_text="Whether the request was not filled") from_barcode = models.BooleanField(default=False, help_text="Whether the request came from barcode scanning") filling_time_category = models.CharField( max_length=20, choices=FILLING_TIME_CHOICES, default="not_mentioned", help_text="When the request was filled" ) request_date = models.DateField(help_text="Date of the request") request_time = models.TimeField(null=True, blank=True, help_text="Time of the request") form_sent_at = models.DateTimeField(null=True, blank=True, help_text="When complaint form was sent to patient") form_sent_time = models.TimeField(null=True, blank=True, help_text="Time when form was sent") filled_at = models.DateTimeField(null=True, blank=True, help_text="When the request was filled") filled_time = models.TimeField(null=True, blank=True, help_text="Time when request was filled") reason_non_activation = models.CharField( max_length=50, choices=NON_ACTIVATION_REASON_CHOICES, blank=True, help_text="Reason complaint was not activated" ) reason_non_activation_other = models.CharField(max_length=200, blank=True, help_text="Other reason details") pr_observations = models.TextField(blank=True, help_text="PR team observations about this request") notes = models.TextField(blank=True, help_text="Additional notes about the request") class Meta: ordering = ["-request_date", "-created_at"] verbose_name = "Complaint Request" verbose_name_plural = "Complaint Requests" indexes = [ models.Index(fields=["staff", "request_date"]), models.Index(fields=["filled", "on_hold"]), models.Index(fields=["request_date"]), models.Index(fields=["hospital", "request_date"]), models.Index(fields=["reason_non_activation"]), ] def __str__(self): status = "Filled" if self.filled else "Not Filled" if self.on_hold: status = "On Hold" return f"{self.staff} - {status} - {self.request_date}" def mark_as_filled(self): """Mark the request as filled.""" from django.utils import timezone self.filled = True self.not_filled = False self.filled_at = timezone.now() self.save(update_fields=["filled", "not_filled", "filled_at"]) def mark_as_not_filled(self): """Mark the request as not filled.""" self.filled = False self.not_filled = True self.save(update_fields=["filled", "not_filled"]) class ReportCompletion(UUIDModel, TimeStampedModel): """ Tracks weekly report completion for employees. Monitors which reports were completed by staff members. """ REPORT_TYPE_CHOICES = [ ("complaint_report", "Complaint Report"), ("complaint_request_report", "Complaint Request Report"), ("observation_report", "Observation Report"), ("incoming_inquiries_report", "Incoming Inquiries Report"), ("outgoing_inquiries_report", "Outgoing Inquiries Report"), ("extension_report", "Extension Report"), ("escalated_complaints_report", "Escalated Complaints Report"), ] staff = models.ForeignKey( "accounts.User", on_delete=models.CASCADE, related_name="report_completions", help_text="Staff member who should complete the report", ) report_type = models.CharField(max_length=50, choices=REPORT_TYPE_CHOICES, help_text="Type of report") is_completed = models.BooleanField(default=False, help_text="Whether the report is completed") completed_at = models.DateTimeField(null=True, blank=True, help_text="When the report was completed") week_start_date = models.DateField(help_text="Start date of the week this report is for") notes = models.TextField(blank=True, help_text="Notes about the report completion") class Meta: ordering = ["-week_start_date", "report_type"] verbose_name = "Report Completion" verbose_name_plural = "Report Completions" unique_together = [["staff", "report_type", "week_start_date"]] indexes = [ models.Index(fields=["staff", "week_start_date"]), models.Index(fields=["report_type", "is_completed"]), models.Index(fields=["week_start_date"]), ] def __str__(self): status = "✓" if self.is_completed else "✗" return f"{self.staff} - {self.get_report_type_display()} - Week of {self.week_start_date} {status}" def mark_completed(self): """Mark this report as completed.""" from django.utils import timezone self.is_completed = True self.completed_at = timezone.now() self.save(update_fields=["is_completed", "completed_at"]) def mark_incomplete(self): """Mark this report as not completed.""" self.is_completed = False self.completed_at = None self.save(update_fields=["is_completed", "completed_at"]) @classmethod def get_completion_percentage(cls, staff, week_start_date): """ Get completion percentage for a staff member for a given week. Returns: float: Percentage of completed reports (0-100) """ total_reports = len(cls.REPORT_TYPE_CHOICES) completed_count = cls.objects.filter(staff=staff, week_start_date=week_start_date, is_completed=True).count() return (completed_count / total_reports) * 100 if total_reports > 0 else 0 class EscalatedComplaintLog(UUIDModel, TimeStampedModel): """ Logs escalated complaints with timing information. Tracks when complaints were escalated relative to the 72-hour SLA. """ ESCALATION_TIMING_CHOICES = [ ("before_72h", "Before 72 Hours"), ("exactly_72h", "72 Hours Exactly"), ("after_72h", "After 72 Hours"), ] complaint = models.ForeignKey( "complaints.Complaint", on_delete=models.CASCADE, related_name="escalation_logs", help_text="The escalated complaint", ) staff = models.ForeignKey( "accounts.User", on_delete=models.CASCADE, related_name="escalated_complaints", help_text="Staff member who the complaint is assigned to", ) escalation_timing = models.CharField( max_length=20, choices=ESCALATION_TIMING_CHOICES, help_text="When the complaint was escalated relative to 72h SLA", ) escalated_at = models.DateTimeField(help_text="When the complaint was escalated") is_resolved = models.BooleanField(default=False, help_text="Whether the escalated complaint was resolved") resolved_at = models.DateTimeField(null=True, blank=True, help_text="When the escalated complaint was resolved") resolution_notes = models.TextField(blank=True, help_text="Notes about the resolution") week_start_date = models.DateField(help_text="Start date of the week this escalation is recorded for") class Meta: ordering = ["-escalated_at", "-created_at"] verbose_name = "Escalated Complaint Log" verbose_name_plural = "Escalated Complaint Logs" indexes = [ models.Index(fields=["staff", "week_start_date"]), models.Index(fields=["escalation_timing", "is_resolved"]), models.Index(fields=["week_start_date"]), ] def __str__(self): status = "Resolved" if self.is_resolved else "Unresolved" return f"{self.staff} - {self.get_escalation_timing_display()} - {status}" def mark_resolved(self, notes=""): """Mark this escalated complaint as resolved.""" from django.utils import timezone self.is_resolved = True self.resolved_at = timezone.now() if notes: self.resolution_notes = notes self.save(update_fields=["is_resolved", "resolved_at", "resolution_notes"]) class InquiryDetail(UUIDModel, TimeStampedModel): """ Detailed tracking of inquiries with types and statuses. Extends the base Inquiry model with evaluation-specific tracking. """ INQUIRY_TYPE_CHOICES = [ ("contact_doctor", "Contact the doctor"), ("sick_leave_reports", "Sick-Leave - Medical Reports"), ("blood_test", "Blood test result"), ("raise_complaint", "Raise a Complaint"), ("app_problem", "Problem with the app"), ("medication", "Ask about medication"), ("insurance_status", "Insurance request status"), ("general_question", "General question"), ("other", "Other"), ] STATUS_CHOICES = [ ("in_progress", "تحت الإجراء (In Progress)"), ("contacted", "تم التواصل (Contacted)"), ("contacted_no_response", "تم التواصل ولم يتم الرد (Contacted No Response)"), ] RESPONSE_TIME_CHOICES = [ ("24h", "24 Hours"), ("48h", "48 Hours"), ("72h", "72 Hours"), ("more_than_72h", "More than 72 Hours"), ] inquiry = models.OneToOneField( "complaints.Inquiry", on_delete=models.CASCADE, related_name="evaluation_detail", help_text="Related inquiry" ) staff = models.ForeignKey( "accounts.User", on_delete=models.CASCADE, related_name="inquiry_details", help_text="Staff member handling the inquiry", ) inquiry_type = models.CharField(max_length=50, choices=INQUIRY_TYPE_CHOICES, help_text="Type of inquiry") is_outgoing = models.BooleanField(default=False, help_text="Whether this is an outgoing inquiry") response_time_category = models.CharField( max_length=20, choices=RESPONSE_TIME_CHOICES, blank=True, help_text="Response time category" ) inquiry_status = models.CharField( max_length=50, choices=STATUS_CHOICES, blank=True, help_text="Current status of the inquiry" ) inquiry_date = models.DateField(help_text="Date of the inquiry") notes = models.TextField(blank=True, help_text="Additional notes") class Meta: ordering = ["-inquiry_date", "-created_at"] verbose_name = "Inquiry Detail" verbose_name_plural = "Inquiry Details" indexes = [ models.Index(fields=["staff", "inquiry_date"]), models.Index(fields=["inquiry_type", "is_outgoing"]), models.Index(fields=["inquiry_date"]), ] def __str__(self): direction = "Outgoing" if self.is_outgoing else "Incoming" return f"{self.staff} - {direction} - {self.get_inquiry_type_display()} - {self.inquiry_date}"