# SLA System Configuration Analysis ## Overview This document explains how the Service Level Agreement (SLA) system is configured and works within the complaint management system. --- ## 1. SLA Configuration Models ### ComplaintSLAConfig Model **File:** `apps/complaints/models.py` The SLA configuration is stored in the `ComplaintSLAConfig` model, which allows flexible, hospital-specific SLA settings. **Key Fields:** - `hospital`: ForeignKey to Hospital (each hospital can have its own SLA rules) - `severity`: Severity level (low, medium, high, critical) - `priority`: Priority level (low, medium, high) - `sla_hours`: Number of hours until SLA deadline (e.g., 24, 48, 72) - `reminder_hours_before`: Hours before deadline to send first reminder (default: 24) - `second_reminder_enabled`: Boolean to enable second reminder (default: false) - `second_reminder_hours_before`: Hours before deadline for second reminder (default: 6) - `thank_you_email_enabled`: Boolean to send thank you email on closure (default: false) - `is_active`: Boolean to enable/disable the config **Unique Constraint:** - `unique_together`: hospital + severity + priority - This means each hospital can have only one SLA config per severity/priority combination **Example Configuration:** ```python # Al-Hammadi Hospital - Medium Severity, Medium Priority hospital_id: "uuid-123" severity: "medium" priority: "medium" sla_hours: 48 reminder_hours_before: 24 second_reminder_enabled: true second_reminder_hours_before: 6 ``` --- ## 2. SLA Calculation in Complaint Model ### How Due Date is Calculated **File:** `apps/complaints/models.py` - `Complaint.calculate_sla_due_date()` The SLA due date is calculated when a complaint is created: ```python def calculate_sla_due_date(self): """ Calculate SLA due date based on severity and hospital configuration. First tries to use ComplaintSLAConfig from database. Falls back to settings.SLA_DEFAULTS if no config exists. """ # Try to get SLA config from database try: sla_config = ComplaintSLAConfig.objects.get( hospital=self.hospital, severity=self.severity, priority=self.priority, is_active=True ) sla_hours = sla_config.sla_hours except ComplaintSLAConfig.DoesNotExist: # Fall back to settings sla_hours = settings.SLA_DEFAULTS["complaint"].get( self.severity, settings.SLA_DEFAULTS["complaint"]["medium"] ) return timezone.now() + timedelta(hours=sla_hours) ``` **Fallback Hierarchy:** 1. **Database Config** (ComplaintSLAConfig) - Checked first 2. **Settings Defaults** (settings.SLA_DEFAULTS) - Used if no database config exists --- ## 3. SLA Tracking Fields in Complaint Model ### Core SLA Fields **File:** `apps/complaints/models.py` ```python class Complaint(UUIDModel, TimeStampedModel): # ... other fields ... # SLA tracking due_at = models.DateTimeField(db_index=True, help_text="SLA deadline") is_overdue = models.BooleanField(default=False, db_index=True) reminder_sent_at = models.DateTimeField(null=True, blank=True, help_text="First SLA reminder timestamp") second_reminder_sent_at = models.DateTimeField(null=True, blank=True, help_text="Second SLA reminder timestamp") escalated_at = models.DateTimeField(null=True, blank=True) ``` **Field Descriptions:** - `due_at`: The calculated SLA deadline (e.g., 48 hours from creation) - `is_overdue`: Boolean flag, automatically updated by periodic tasks - `reminder_sent_at`: Timestamp when first reminder was sent - `second_reminder_sent_at`: Timestamp when second reminder was sent (optional) - `escalated_at`: Timestamp when complaint was escalated due to SLA breach --- ## 4. SLA Workflow ### Step-by-Step Process #### 1. Complaint Creation ``` User submits complaint ↓ Complaint object created with default due_at ↓ Celery task triggered: analyze_complaint_with_ai ↓ AI analyzes severity and priority ↓ Complaint saved with new severity/priority ↓ SLA due_at recalculated based on new severity/priority ``` #### 2. SLA Reminder System **Task:** `send_sla_reminders` (runs hourly via Celery Beat) ``` Every hour: ↓ Check active complaints (OPEN, IN_PROGRESS) ↓ For each complaint: 1. Get SLA config (severity + priority) 2. Calculate reminder_time = due_at - reminder_hours_before 3. If now >= reminder_time AND reminder_sent_at is NULL: - Send reminder email to assignee/manager - Set reminder_sent_at = now - Create timeline entry - Check reminder_escalation rules 4. If second_reminder_enabled AND now >= second_reminder_time: - Send URGENT second reminder - Set second_reminder_sent_at = now - Trigger escalation check ``` #### 3. Overdue Detection **Task:** `check_overdue_complaints` (runs every 15 minutes via Celery Beat) ``` Every 15 minutes: ↓ Check active complaints (not CLOSED, CANCELLED) ↓ For each complaint: 1. If now > due_at AND is_overdue is False: - Set is_overdue = True - Trigger automatic escalation - Log warning ``` #### 4. Escalation System **Task:** `escalate_complaint_auto` (triggered on overdue or reminder escalation) ``` Escalation triggered: ↓ Get current escalation level from metadata (default: 0) ↓ Find matching EscalationRule: - hospital = complaint.hospital - escalation_level = current_level + 1 - trigger_on_overdue = True - severity_filter matches (or empty) - priority_filter matches (or empty) - hours_overdue >= trigger_hours_overdue ↓ Determine escalation target: - department_manager - hospital_admin - px_admin - ceo - specific_user ↓ Reassign complaint to escalation target ↓ Update metadata: escalation_level + 1 ↓ Set escalated_at = now ↓ Create timeline entry ↓ Send notifications ``` #### 5. Reminder-Based Escalation **Task:** `escalate_after_reminder` (triggered after reminder) ``` After reminder sent: ↓ Check EscalationRule: - reminder_escalation_enabled = True - escalation_level = current_level + 1 - hours_since_reminder >= reminder_escalation_hours ↓ Trigger regular escalation task ↓ Add metadata: reminder_escalation = {...} ``` --- ## 5. Escalation Configuration ### EscalationRule Model **File:** `apps/complaints/models.py` ```python class EscalationRule(UUIDModel, TimeStampedModel): """ Configurable escalation rules for complaints. Supports multi-level escalation with configurable hierarchy. """ hospital = models.ForeignKey("organizations.Hospital", ...) name = models.CharField(max_length=200) description = models.TextField(blank=True) # Escalation level (supports multi-level escalation) escalation_level = models.IntegerField(default=1) max_escalation_level = models.IntegerField(default=3) # Trigger conditions trigger_on_overdue = models.BooleanField(default=True) trigger_hours_overdue = models.IntegerField(default=0) # Reminder-based escalation reminder_escalation_enabled = models.BooleanField(default=False) reminder_escalation_hours = models.IntegerField(default=24) # Escalation target escalate_to_role = models.CharField(..., choices=[ ("department_manager", "Department Manager"), ("hospital_admin", "Hospital Admin"), ("px_admin", "PX Admin"), ("ceo", "CEO"), ("specific_user", "Specific User"), ]) escalate_to_user = models.ForeignKey("accounts.User", ...) # For specific_user # Conditions severity_filter = models.CharField(...) # Optional: only escalate specific severity priority_filter = models.CharField(...) # Optional: only escalate specific priority order = models.IntegerField(default=0) is_active = models.BooleanField(default=True) ``` **Multi-Level Escalation Example:** ```python # Level 1: Escalate to Department Manager after 24 hours overdue EscalationRule( hospital=hospital, name="Level 1: Department Manager", escalation_level=1, trigger_on_overdue=True, trigger_hours_overdue=24, escalate_to_role="department_manager" ) # Level 2: Escalate to Hospital Admin after 48 hours overdue EscalationRule( hospital=hospital, name="Level 2: Hospital Admin", escalation_level=2, trigger_on_overdue=True, trigger_hours_overdue=48, escalate_to_role="hospital_admin" ) # Level 3: Escalate to PX Admin after 72 hours overdue EscalationRule( hospital=hospital, name="Level 3: PX Admin", escalation_level=3, trigger_on_overdue=True, trigger_hours_overdue=72, escalate_to_role="px_admin" ) ``` --- ## 6. Celery Beat Configuration ### Scheduled Tasks **File:** `config/celery.py` ```python app.conf.beat_schedule = { # Check overdue complaints every 15 minutes 'check-overdue-complaints': { 'task': 'apps.complaints.tasks.check_overdue_complaints', 'schedule': 900.0, # 15 minutes in seconds }, # Send SLA reminders every hour 'send-sla-reminders': { 'task': 'apps.complaints.tasks.send_sla_reminders', 'schedule': 3600.0, # 1 hour in seconds }, # Check overdue explanation requests every 15 minutes 'check-overdue-explanation-requests': { 'task': 'apps.complaints.tasks.check_overdue_explanation_requests', 'schedule': 900.0, }, # Send explanation reminders every hour 'send-explanation-reminders': { 'task': 'apps.complaints.tasks.send_explanation_reminders', 'schedule': 3600.0, }, } ``` --- ## 7. AI Integration with SLA ### Complaint Type Detection **File:** `apps/core/ai_service.py` - `AIService._detect_complaint_type()` ```python @classmethod def _detect_complaint_type(cls, text: str) -> str: """ Detect if text is a complaint or appreciation using sentiment and keywords. Returns: 'complaint' or 'appreciation' """ # Keywords for appreciation (English and Arabic) appreciation_keywords_en = [ 'thank', 'thanks', 'excellent', 'great', 'wonderful', ... ] appreciation_keywords_ar = [ 'شكرا', 'ممتاز', 'رائع', 'بارك', 'مدهش', ... ] # Keywords for complaints complaint_keywords_en = [ 'problem', 'issue', 'complaint', 'bad', 'terrible', ... ] complaint_keywords_ar = [ 'مشكلة', 'مشاكل', 'سيء', 'مخيب', ... ] # Count keyword matches # Get sentiment analysis # Determine complaint_type return 'complaint' or 'appreciation' ``` ### Impact on SLA **For Complaints:** - SLA tracking is enabled - `due_at` is calculated based on severity/priority - Reminders are sent before deadline - Escalation triggered if overdue - PX Action is automatically created **For Appreciations:** - SLA tracking is **SKIPPED** - `due_at` can be None - No reminders sent - No escalation - No PX Action created - Timeline note: "Appreciation detected - No PX Action or SLA tracking required" ### AI Analysis Task **File:** `apps/complaints/tasks.py` - `analyze_complaint_with_ai()` ```python @shared_task def analyze_complaint_with_ai(complaint_id): """ Analyze a complaint using AI to determine: - Type (complaint vs appreciation) - Severity - Priority - Category - Department - Staff mentions - Emotion """ # Get AI analysis analysis = AIService.analyze_complaint(...) # Extract complaint_type complaint_type = analysis.get('complaint_type', 'complaint') # Update complaint complaint.severity = analysis['severity'] complaint.priority = analysis['priority'] complaint.complaint_type = complaint_type # Skip SLA for appreciations is_appreciation = complaint_type == 'appreciation' if not is_appreciation: # Recalculate SLA due date based on new severity/priority complaint.due_at = complaint.calculate_sla_due_date() complaint.save(update_fields=['due_at']) # Create PX Action (mandatory for complaints) create_px_action(...) else: # Create timeline entry for appreciation ComplaintUpdate.objects.create( complaint=complaint, update_type='note', message="Appreciation detected - No PX Action or SLA tracking required" ) ``` --- ## 8. Explanation SLA System ### ExplanationSLAConfig Model **File:** `apps/complaints/models.py` ```python class ExplanationSLAConfig(UUIDModel, TimeStampedModel): """ SLA configuration for staff explanation requests. Defines time limits and escalation rules for staff to submit explanations. """ hospital = models.ForeignKey("organizations.Hospital", ...) # Time limits response_hours = models.IntegerField(default=48) reminder_hours_before = models.IntegerField(default=12) # Escalation settings auto_escalate_enabled = models.BooleanField(default=True) escalation_hours_overdue = models.IntegerField(default=0) max_escalation_levels = models.IntegerField(default=3) is_active = models.BooleanField(default=True) ``` ### Explanation Request Workflow ``` Complaint filed against staff ↓ PX Admin requests explanation ↓ ComplaintExplanation created with token ↓ Send explanation request email to staff ↓ Set sla_due_at = now + response_hours (default: 48 hours) ↓ Staff receives email with unique link ↓ Staff submits explanation via link ↓ is_used = True, responded_at = now ``` ### Explanation Overdue Workflow ``` Task: check_overdue_explanation_requests (every 15 minutes) ↓ Find unsubmitted explanations past due_at ↓ If auto_escalate_enabled: - Check escalation level - Find staff manager (report_to field) - Create new explanation request for manager - Link to original explanation (escalated_to_manager) - Send email to manager - Increment escalation_level ↓ Repeat until max_escalation_levels reached ``` --- ## 9. Threshold-Based Actions ### ComplaintThreshold Model **File:** `apps/complaints/models.py` ```python class ComplaintThreshold(UUIDModel, TimeStampedModel): """ Configurable thresholds for complaint-related triggers. Defines when to trigger actions based on metrics (e.g., survey scores). """ hospital = models.ForeignKey("organizations.Hospital", ...) threshold_type = models.CharField(..., choices=[ ("resolution_survey_score", "Resolution Survey Score"), ("response_time", "Response Time"), ("resolution_time", "Resolution Time"), ]) threshold_value = models.FloatField() # e.g., 50 for 50% comparison_operator = models.CharField(..., choices=[ ("lt", "Less Than"), ("lte", "Less Than or Equal"), ("gt", "Greater Than"), ("gte", "Greater Than or Equal"), ("eq", "Equal"), ]) action_type = models.CharField(..., choices=[ ("create_px_action", "Create PX Action"), ("send_notification", "Send Notification"), ("escalate", "Escalate"), ]) is_active = models.BooleanField(default=True) ``` ### Resolution Survey Threshold Check ``` Complaint closed ↓ Send resolution survey to patient ↓ Patient completes survey ↓ Task: check_resolution_survey_threshold ↓ Get threshold for hospital ↓ Check if survey.score breaches threshold ↓ If breached: - Create PX Action - Send notification - Or escalate (based on action_type) ``` --- ## 10. Notification System ### SLA Reminder Emails **Templates:** - `templates/complaints/emails/sla_reminder_en.txt` - `templates/complaints/emails/sla_reminder_ar.txt` - `templates/complaints/emails/sla_second_reminder_en.txt` - `templates/complaints/emails/sla_second_reminder_ar.txt` **Email Content:** - Complaint reference number - Complaint title - Hours remaining until deadline - Direct link to complaint - Urgency level (normal vs URGENT for second reminder) ### NotificationService Integration **File:** `apps/notifications/services.py` ```python @shared_task def send_complaint_notification(complaint_id, event_type): """ Send notification for complaint events. event_types: - created: Notify assignee/department manager - assigned: Notify assignee - overdue: Notify assignee and manager - escalated: Notify assignee and manager - resolved: Notify patient - closed: Notify patient """ ``` --- ## 11. Configuration Hierarchy ### Priority Order for SLA Settings 1. **Database Configuration** (Highest Priority) - ComplaintSLAConfig (per hospital, severity, priority) - ExplanationSLAConfig (per hospital) - EscalationRule (per hospital, with conditions) 2. **Settings Defaults** (Fallback) - settings.SLA_DEFAULTS["complaint"][severity] 3. **Hardcoded Defaults** (Lowest Priority) - Default hours: 48 - Default reminder: 24 hours before - Default second reminder: 6 hours before --- ## 12. Key Features ### ✅ Configurable SLA - Hospital-specific SLA settings - Per-severity and per-priority configurations - Multiple reminder levels - Flexible escalation rules ### ✅ Automatic Escalation - Multi-level escalation (up to 3+ levels) - Role-based targets (manager, admin, CEO, etc.) - Conditional escalation (severity, priority filters) - Reminder-based escalation (no action after reminder) ### ✅ AI Integration - Automatic severity/priority classification - Complaint type detection (complaint vs appreciation) - SLA skipping for appreciations - Emotion detection for context ### ✅ Real-time Tracking - Overdue detection every 15 minutes - Reminder checks every hour - Timeline updates for all SLA events - Audit logging ### ✅ Staff Explanation System - Token-based access - SLA deadlines for responses - Automatic escalation to managers - Multi-level escalation up hierarchy --- ## 13. Example Scenarios ### Scenario 1: Standard Complaint with SLA Breach ``` 1. Complaint created: "Wait time too long" - AI analysis: severity=medium, priority=medium, complaint_type=complaint - SLA config: 48 hours - due_at = now + 48 hours 2. 24 hours before deadline: - Reminder sent to assigned staff - reminder_sent_at = now 3. 6 hours before deadline (if second_reminder_enabled): - URGENT second reminder sent - second_reminder_sent_at = now 4. Deadline passes (48 hours after creation): - check_overdue_complaints runs - is_overdue = True - Trigger escalation 5. Escalation (Level 1): - Reassign to department manager - escalation_level = 1 - escalated_at = now - Send notification 6. 24 hours after escalation (if still unresolved): - Trigger Level 2 escalation to hospital admin 7. 48 hours after escalation (if still unresolved): - Trigger Level 3 escalation to PX Admin 8. Maximum level reached: - Stop escalation - Log warning ``` ### Scenario 2: Appreciation (No SLA) ``` 1. Feedback received: "Dr. Ahmed was excellent! Thank you!" - AI analysis: sentiment=positive, complaint_type=appreciation 2. SLA handling: - Skip SLA calculation - due_at = None - No reminders sent - No escalation - No PX Action created 3. Timeline entry: - "Appreciation detected - No PX Action or SLA tracking required" 4. Thank you email (if thank_you_email_enabled): - Send to patient - Express gratitude for feedback ``` ### Scenario 3: Staff Explanation Request with Escalation ``` 1. Complaint filed against Dr. Ahmed - PX Admin requests explanation - Explanation created with token - Email sent to Dr. Ahmed - sla_due_at = now + 48 hours 2. 12 hours before deadline: - Reminder sent to Dr. Ahmed 3. 48 hours deadline passes (no response): - Explanation marked as overdue - auto_escalate_enabled = True 4. Escalation Level 1: - Create new explanation for Dr. Ahmed's manager - Link to original explanation - Send email to manager - escalation_level = 1 5. If manager doesn't respond in 48 hours: - Escalate Level 2: Manager's manager 6. Continue until max_escalation_levels (default: 3) ``` --- ## 14. Configuration Management ### Creating SLA Configurations **Via Admin:** ```python from apps.complaints.models import ComplaintSLAConfig # Critical severity, high priority: 24 hours ComplaintSLAConfig.objects.create( hospital=hospital, severity='critical', priority='high', sla_hours=24, reminder_hours_before=12, second_reminder_enabled=True, second_reminder_hours_before=6, is_active=True ) # Medium severity, medium priority: 48 hours ComplaintSLAConfig.objects.create( hospital=hospital, severity='medium', priority='medium', sla_hours=48, reminder_hours_before=24, second_reminder_enabled=True, second_reminder_hours_before=6, is_active=True ) # Low severity, low priority: 72 hours ComplaintSLAConfig.objects.create( hospital=hospital, severity='low', priority='low', sla_hours=72, reminder_hours_before=48, second_reminder_enabled=False, is_active=True ) ``` ### Creating Escalation Rules ```python from apps.complaints.models import EscalationRule # Level 1: Department Manager EscalationRule.objects.create( hospital=hospital, name="Level 1: Department Manager", escalation_level=1, max_escalation_level=3, trigger_on_overdue=True, trigger_hours_overdue=24, reminder_escalation_enabled=True, reminder_escalation_hours=12, escalate_to_role='department_manager', severity_filter='', # All severities priority_filter='', # All priorities order=1, is_active=True ) # Level 2: Hospital Admin EscalationRule.objects.create( hospital=hospital, name="Level 2: Hospital Admin", escalation_level=2, max_escalation_level=3, trigger_on_overdue=True, trigger_hours_overdue=48, escalate_to_role='hospital_admin', order=2, is_active=True ) # Level 3: PX Admin EscalationRule.objects.create( hospital=hospital, name="Level 3: PX Admin", escalation_level=3, max_escalation_level=3, trigger_on_overdue=True, trigger_hours_overdue=72, escalate_to_role='px_admin', order=3, is_active=True ) ``` --- ## 15. Troubleshooting ### SLA Not Calculating **Check:** 1. Does ComplaintSLAConfig exist for hospital+severity+priority? 2. Is the config `is_active=True`? 3. Is `settings.SLA_DEFAULTS` defined? ### Reminders Not Sending **Check:** 1. Is Celery Beat running? 2. Is `send_sla_reminders` in beat schedule? 3. Is complaint status OPEN or IN_PROGRESS? 4. Is `reminder_sent_at` NULL? ### Escalation Not Triggering **Check:** 1. Is EscalationRule configured? 2. Is rule `is_active=True`? 3. Does severity/priority match filters? 4. Has `trigger_hours_overdue` elapsed? 5. Has escalation level reached max? ### AI Not Classifying Correctly **Check:** 1. Is AI service configured (API key)? 2. Is Celery worker running? 3. Check logs for AI errors 4. Verify complaint description quality --- ## 16. Best Practices ### SLA Configuration 1. **Set realistic deadlines** based on complexity 2. **Enable second reminders** for high-priority items 3. **Configure multi-level escalation** for critical issues 4. **Regularly review** SLA performance metrics ### Escalation Rules 1. **Start with department managers** (Level 1) 2. **Escalate to admin** for persistent issues (Level 2) 3. **Final escalation** to CXO/PX Admin (Level 3) 4. **Set appropriate time gaps** between levels (24-48 hours) ### Monitoring 1. **Monitor overdue rates** monthly 2. **Track escalation frequency** per department 3. **Review AI classification accuracy** 4. **Analyze response times** by severity/priority --- ## Summary The SLA system is designed to be: - **Flexible**: Hospital-specific, severity-based, priority-driven - **Automated**: Minimal manual intervention required - **Intelligent**: AI-powered classification and escalation - **Transparent**: Clear timeline and audit logs - **Configurable**: Easy to adjust SLA settings via admin or code The system ensures complaints are addressed within appropriate timeframes, with automatic escalation when deadlines are missed, while intelligently handling appreciations differently to focus resources on actual complaints.