230 lines
8.2 KiB
Python
230 lines
8.2 KiB
Python
"""
|
|
Call Center models - Call center interaction tracking and ratings
|
|
|
|
This module implements call center tracking that:
|
|
- Records call center interactions
|
|
- Tracks agent performance
|
|
- Monitors wait times and satisfaction
|
|
- Creates PX actions for low ratings
|
|
"""
|
|
from django.db import models
|
|
|
|
from apps.core.models import TimeStampedModel, UUIDModel
|
|
|
|
|
|
class CallCenterInteraction(UUIDModel, TimeStampedModel):
|
|
"""
|
|
Call center interaction - tracks calls with patients.
|
|
|
|
Low ratings trigger PX action creation.
|
|
"""
|
|
# Patient information
|
|
patient = models.ForeignKey(
|
|
'organizations.Patient',
|
|
on_delete=models.CASCADE,
|
|
null=True,
|
|
blank=True,
|
|
related_name='call_center_interactions'
|
|
)
|
|
|
|
# Caller information (if not a patient)
|
|
caller_name = models.CharField(max_length=200, blank=True)
|
|
caller_phone = models.CharField(max_length=20, blank=True)
|
|
caller_relationship = models.CharField(
|
|
max_length=50,
|
|
choices=[
|
|
('patient', 'Patient'),
|
|
('family', 'Family Member'),
|
|
('other', 'Other'),
|
|
],
|
|
default='patient'
|
|
)
|
|
|
|
# Organization
|
|
hospital = models.ForeignKey(
|
|
'organizations.Hospital',
|
|
on_delete=models.CASCADE,
|
|
related_name='call_center_interactions'
|
|
)
|
|
department = models.ForeignKey(
|
|
'organizations.Department',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='call_center_interactions'
|
|
)
|
|
|
|
# Agent information
|
|
agent = models.ForeignKey(
|
|
'accounts.User',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='call_center_interactions'
|
|
)
|
|
|
|
# Call details
|
|
call_type = models.CharField(
|
|
max_length=50,
|
|
choices=[
|
|
('inquiry', 'Inquiry'),
|
|
('complaint', 'Complaint'),
|
|
('appointment', 'Appointment'),
|
|
('follow_up', 'Follow-up'),
|
|
('feedback', 'Feedback'),
|
|
('other', 'Other'),
|
|
],
|
|
db_index=True
|
|
)
|
|
|
|
subject = models.CharField(max_length=500)
|
|
notes = models.TextField(blank=True)
|
|
|
|
# Metrics
|
|
wait_time_seconds = models.IntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text="Time caller waited before agent answered"
|
|
)
|
|
call_duration_seconds = models.IntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text="Total call duration"
|
|
)
|
|
|
|
# Rating (1-5 scale)
|
|
satisfaction_rating = models.IntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text="Caller satisfaction rating (1-5)"
|
|
)
|
|
is_low_rating = models.BooleanField(
|
|
default=False,
|
|
db_index=True,
|
|
help_text="True if rating below threshold (< 3)"
|
|
)
|
|
|
|
# Resolution
|
|
resolved = models.BooleanField(default=False)
|
|
resolution_notes = models.TextField(blank=True)
|
|
|
|
# Timestamps
|
|
call_started_at = models.DateTimeField(auto_now_add=True)
|
|
call_ended_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
ordering = ['-call_started_at']
|
|
indexes = [
|
|
models.Index(fields=['hospital', '-call_started_at']),
|
|
models.Index(fields=['agent', '-call_started_at']),
|
|
models.Index(fields=['is_low_rating', '-call_started_at']),
|
|
]
|
|
|
|
def __str__(self):
|
|
caller = self.patient.get_full_name() if self.patient else self.caller_name
|
|
return f"{caller} - {self.call_type} ({self.call_started_at.strftime('%Y-%m-%d %H:%M')})"
|
|
|
|
def save(self, *args, **kwargs):
|
|
"""Check if rating is low"""
|
|
if self.satisfaction_rating and self.satisfaction_rating < 3:
|
|
self.is_low_rating = True
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
class CallRecord(UUIDModel, TimeStampedModel):
|
|
"""
|
|
Call Record - Tracks call center recordings imported from CSV.
|
|
|
|
Stores all call metadata from the call recording system including:
|
|
- Call details (start, end, duration)
|
|
- Caller information
|
|
- Department and extension
|
|
- Recording file information
|
|
- Inbound/Outbound call details
|
|
"""
|
|
# Core identifiers
|
|
media_id = models.UUIDField(unique=True, db_index=True, help_text="Unique media ID from recording system")
|
|
media_type = models.CharField(max_length=50, default="Calls", help_text="Type of media (e.g., Calls)")
|
|
chain = models.CharField(max_length=255, blank=True, help_text="Chain identifier")
|
|
evaluated = models.BooleanField(default=False, help_text="Whether the call has been evaluated")
|
|
|
|
# Call timing
|
|
call_start = models.DateTimeField(help_text="Call start time")
|
|
call_end = models.DateTimeField(null=True, blank=True, help_text="Call end time")
|
|
call_length = models.CharField(max_length=20, blank=True, help_text="Call length as HH:MM:SS")
|
|
call_duration_seconds = models.IntegerField(null=True, blank=True, help_text="Call duration in seconds")
|
|
|
|
# Caller information
|
|
first_name = models.CharField(max_length=100, blank=True, help_text="Caller first name")
|
|
last_name = models.CharField(max_length=100, blank=True, help_text="Caller last name")
|
|
|
|
# Department information
|
|
extension = models.CharField(max_length=20, blank=True, help_text="Extension number")
|
|
department = models.CharField(max_length=255, blank=True, help_text="Department name")
|
|
location = models.CharField(max_length=255, blank=True, help_text="Location")
|
|
|
|
# Inbound call details
|
|
inbound_id = models.CharField(max_length=50, blank=True, help_text="Inbound call ID")
|
|
inbound_name = models.CharField(max_length=255, blank=True, help_text="Inbound caller name/number")
|
|
dnis = models.CharField(max_length=50, blank=True, help_text="Dialed Number Identification Service")
|
|
|
|
# Outbound call details
|
|
outbound_id = models.CharField(max_length=50, blank=True, help_text="Outbound call ID")
|
|
outbound_name = models.CharField(max_length=255, blank=True, help_text="Outbound caller name/number")
|
|
|
|
# Flag information
|
|
flag_name = models.CharField(max_length=100, blank=True, help_text="Flag name")
|
|
flag_value = models.CharField(max_length=100, blank=True, help_text="Flag value")
|
|
|
|
# Recording file information
|
|
file_location = models.CharField(max_length=500, blank=True, help_text="File system location")
|
|
file_name = models.CharField(max_length=500, blank=True, help_text="Recording file name")
|
|
file_hash = models.CharField(max_length=64, blank=True, help_text="File hash for integrity")
|
|
|
|
# Additional metadata
|
|
external_ref = models.CharField(max_length=100, blank=True, help_text="External reference number")
|
|
transfer_from = models.CharField(max_length=255, blank=True, help_text="Transfer source")
|
|
recorded_by = models.CharField(max_length=255, blank=True, help_text="Recording system/user")
|
|
time_zone = models.CharField(max_length=50, default="03:00:00", help_text="Time zone offset")
|
|
recording_server_name = models.CharField(max_length=100, blank=True, help_text="Recording server name")
|
|
|
|
# Hospital link (for multi-hospital systems)
|
|
hospital = models.ForeignKey(
|
|
'organizations.Hospital',
|
|
on_delete=models.CASCADE,
|
|
null=True,
|
|
blank=True,
|
|
related_name='call_records'
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['-call_start']
|
|
indexes = [
|
|
models.Index(fields=['-call_start']),
|
|
models.Index(fields=['media_id']),
|
|
models.Index(fields=['department']),
|
|
models.Index(fields=['evaluated']),
|
|
models.Index(fields=['hospital', '-call_start']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.first_name} {self.last_name} - {self.call_start.strftime('%Y-%m-%d %H:%M')}"
|
|
|
|
@property
|
|
def caller_full_name(self):
|
|
"""Get full caller name"""
|
|
return f"{self.first_name} {self.last_name}".strip()
|
|
|
|
@property
|
|
def is_inbound(self):
|
|
"""Check if call is inbound"""
|
|
return bool(self.inbound_id or self.inbound_name)
|
|
|
|
@property
|
|
def is_outbound(self):
|
|
"""Check if call is outbound"""
|
|
return bool(self.outbound_id or self.outbound_name)
|