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:
- Database Config (ComplaintSLAConfig) - Checked first
- 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 tasksreminder_sent_at: Timestamp when first reminder was sentsecond_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_atis 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_atcan 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.txttemplates/complaints/emails/sla_reminder_ar.txttemplates/complaints/emails/sla_second_reminder_en.txttemplates/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
-
Database Configuration (Highest Priority)
- ComplaintSLAConfig (per hospital, severity, priority)
- ExplanationSLAConfig (per hospital)
- EscalationRule (per hospital, with conditions)
-
Settings Defaults (Fallback)
- settings.SLA_DEFAULTS["complaint"][severity]
-
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:
- Does ComplaintSLAConfig exist for hospital+severity+priority?
- Is the config
is_active=True? - Is
settings.SLA_DEFAULTSdefined?
Reminders Not Sending
Check:
- Is Celery Beat running?
- Is
send_sla_remindersin beat schedule? - Is complaint status OPEN or IN_PROGRESS?
- Is
reminder_sent_atNULL?
Escalation Not Triggering
Check:
- Is EscalationRule configured?
- Is rule
is_active=True? - Does severity/priority match filters?
- Has
trigger_hours_overdueelapsed? - Has escalation level reached max?
AI Not Classifying Correctly
Check:
- Is AI service configured (API key)?
- Is Celery worker running?
- Check logs for AI errors
- Verify complaint description quality
16. Best Practices
SLA Configuration
- Set realistic deadlines based on complexity
- Enable second reminders for high-priority items
- Configure multi-level escalation for critical issues
- Regularly review SLA performance metrics
Escalation Rules
- Start with department managers (Level 1)
- Escalate to admin for persistent issues (Level 2)
- Final escalation to CXO/PX Admin (Level 3)
- Set appropriate time gaps between levels (24-48 hours)
Monitoring
- Monitor overdue rates monthly
- Track escalation frequency per department
- Review AI classification accuracy
- 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.