HH/docs/SLA_SYSTEM_CONFIGURATION_ANALYSIS.md
2026-01-24 15:27:27 +03:00

24 KiB

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:

# 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:

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

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

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:

# 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

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()

@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()

@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

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

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

@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:

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

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.