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

906 lines
24 KiB
Markdown

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