HH/apps/callcenter/ui_views.py
Marwan Alwali 2179fbf39a update
2025-12-31 13:16:30 +03:00

580 lines
19 KiB
Python

"""
Call Center Console UI views
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Q, Avg
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views.decorators.http import require_http_methods
from apps.complaints.models import Complaint, ComplaintSource, Inquiry
from apps.core.services import AuditService
from apps.organizations.models import Department, Hospital, Patient, Physician
from .models import CallCenterInteraction
@login_required
def interaction_list(request):
"""Call center interactions list view"""
queryset = CallCenterInteraction.objects.select_related(
'patient', 'hospital', 'department', 'agent'
)
# Apply RBAC filters
user = request.user
if user.is_px_admin():
pass
elif user.hospital:
queryset = queryset.filter(hospital=user.hospital)
else:
queryset = queryset.none()
# Apply filters
call_type_filter = request.GET.get('call_type')
if call_type_filter:
queryset = queryset.filter(call_type=call_type_filter)
hospital_filter = request.GET.get('hospital')
if hospital_filter:
queryset = queryset.filter(hospital_id=hospital_filter)
is_low_rating = request.GET.get('is_low_rating')
if is_low_rating == 'true':
queryset = queryset.filter(is_low_rating=True)
# Search
search_query = request.GET.get('search')
if search_query:
queryset = queryset.filter(
Q(subject__icontains=search_query) |
Q(caller_name__icontains=search_query) |
Q(patient__mrn__icontains=search_query)
)
# Date range
date_from = request.GET.get('date_from')
if date_from:
queryset = queryset.filter(call_started_at__gte=date_from)
date_to = request.GET.get('date_to')
if date_to:
queryset = queryset.filter(call_started_at__lte=date_to)
# Ordering
queryset = queryset.order_by('-call_started_at')
# Pagination
page_size = int(request.GET.get('page_size', 25))
paginator = Paginator(queryset, page_size)
page_number = request.GET.get('page', 1)
page_obj = paginator.get_page(page_number)
# Get filter options
hospitals = Hospital.objects.filter(status='active')
if not user.is_px_admin() and user.hospital:
hospitals = hospitals.filter(id=user.hospital.id)
# Statistics
stats = {
'total': queryset.count(),
'low_rating': queryset.filter(is_low_rating=True).count(),
'avg_satisfaction': queryset.filter(satisfaction_rating__isnull=False).aggregate(
avg=Avg('satisfaction_rating')
)['avg'] or 0,
}
context = {
'page_obj': page_obj,
'interactions': page_obj.object_list,
'stats': stats,
'hospitals': hospitals,
'filters': request.GET,
}
return render(request, 'callcenter/interaction_list.html', context)
@login_required
def interaction_detail(request, pk):
"""Call center interaction detail view"""
interaction = get_object_or_404(
CallCenterInteraction.objects.select_related(
'patient', 'hospital', 'department', 'agent'
),
pk=pk
)
context = {
'interaction': interaction,
}
return render(request, 'callcenter/interaction_detail.html', context)
# ============================================================================
# COMPLAINT CREATION FOR CALL CENTER
# ============================================================================
@login_required
@require_http_methods(["GET", "POST"])
def create_complaint(request):
"""
Create complaint from call center interaction.
Call center staff can create complaints on behalf of patients/callers.
"""
if request.method == 'POST':
try:
# Get form data
patient_id = request.POST.get('patient_id', None)
hospital_id = request.POST.get('hospital_id')
department_id = request.POST.get('department_id', None)
physician_id = request.POST.get('physician_id', None)
title = request.POST.get('title')
description = request.POST.get('description')
category = request.POST.get('category')
subcategory = request.POST.get('subcategory', '')
priority = request.POST.get('priority')
severity = request.POST.get('severity')
encounter_id = request.POST.get('encounter_id', '')
# Call center specific fields
caller_name = request.POST.get('caller_name', '')
caller_phone = request.POST.get('caller_phone', '')
caller_relationship = request.POST.get('caller_relationship', 'patient')
# Validate required fields
if not all([hospital_id, title, description, category, priority, severity]):
messages.error(request, "Please fill in all required fields.")
return redirect('callcenter:create_complaint')
# If no patient selected, we need caller info
if not patient_id and not caller_name:
messages.error(request, "Please provide either patient or caller information.")
return redirect('callcenter:create_complaint')
# Create complaint
complaint = Complaint.objects.create(
patient_id=patient_id if patient_id else None,
hospital_id=hospital_id,
department_id=department_id if department_id else None,
physician_id=physician_id if physician_id else None,
title=title,
description=description,
category=category,
subcategory=subcategory,
priority=priority,
severity=severity,
source=ComplaintSource.CALL_CENTER,
encounter_id=encounter_id,
)
# Create call center interaction record
CallCenterInteraction.objects.create(
patient_id=patient_id if patient_id else None,
caller_name=caller_name,
caller_phone=caller_phone,
caller_relationship=caller_relationship,
hospital_id=hospital_id,
department_id=department_id if department_id else None,
agent=request.user,
call_type='complaint',
subject=title,
notes=description,
metadata={
'complaint_id': str(complaint.id),
'category': category,
'severity': severity,
}
)
# Log audit
AuditService.log_event(
event_type='complaint_created',
description=f"Complaint created via call center: {complaint.title}",
user=request.user,
content_object=complaint,
metadata={
'category': complaint.category,
'severity': complaint.severity,
'source': 'call_center',
'caller_name': caller_name,
}
)
messages.success(request, f"Complaint #{complaint.id} created successfully.")
return redirect('callcenter:complaint_success', pk=complaint.id)
except Exception as e:
messages.error(request, f"Error creating complaint: {str(e)}")
return redirect('callcenter:create_complaint')
# GET request - show form
hospitals = Hospital.objects.filter(status='active')
if not request.user.is_px_admin() and request.user.hospital:
hospitals = hospitals.filter(id=request.user.hospital.id)
context = {
'hospitals': hospitals,
}
return render(request, 'callcenter/complaint_form.html', context)
@login_required
def complaint_success(request, pk):
"""Success page after creating complaint"""
complaint = get_object_or_404(Complaint, pk=pk)
context = {
'complaint': complaint,
}
return render(request, 'callcenter/complaint_success.html', context)
@login_required
def complaint_list(request):
"""List complaints created by call center"""
queryset = Complaint.objects.filter(
source=ComplaintSource.CALL_CENTER
).select_related(
'patient', 'hospital', 'department', 'physician', 'assigned_to'
)
# Apply RBAC filters
user = request.user
if user.is_px_admin():
pass
elif user.hospital:
queryset = queryset.filter(hospital=user.hospital)
else:
queryset = queryset.none()
# Apply filters
status_filter = request.GET.get('status')
if status_filter:
queryset = queryset.filter(status=status_filter)
severity_filter = request.GET.get('severity')
if severity_filter:
queryset = queryset.filter(severity=severity_filter)
hospital_filter = request.GET.get('hospital')
if hospital_filter:
queryset = queryset.filter(hospital_id=hospital_filter)
# Search
search_query = request.GET.get('search')
if search_query:
queryset = queryset.filter(
Q(title__icontains=search_query) |
Q(description__icontains=search_query) |
Q(patient__mrn__icontains=search_query)
)
# Ordering
queryset = queryset.order_by('-created_at')
# Pagination
page_size = int(request.GET.get('page_size', 25))
paginator = Paginator(queryset, page_size)
page_number = request.GET.get('page', 1)
page_obj = paginator.get_page(page_number)
# Get filter options
hospitals = Hospital.objects.filter(status='active')
if not user.is_px_admin() and user.hospital:
hospitals = hospitals.filter(id=user.hospital.id)
# Statistics
stats = {
'total': queryset.count(),
'open': queryset.filter(status='open').count(),
'in_progress': queryset.filter(status='in_progress').count(),
'resolved': queryset.filter(status='resolved').count(),
}
context = {
'page_obj': page_obj,
'complaints': page_obj.object_list,
'stats': stats,
'hospitals': hospitals,
'filters': request.GET,
}
return render(request, 'callcenter/complaint_list.html', context)
# ============================================================================
# INQUIRY CREATION FOR CALL CENTER
# ============================================================================
@login_required
@require_http_methods(["GET", "POST"])
def create_inquiry(request):
"""
Create inquiry from call center interaction.
Call center staff can create inquiries for general questions/requests.
"""
if request.method == 'POST':
try:
# Get form data
patient_id = request.POST.get('patient_id', None)
hospital_id = request.POST.get('hospital_id')
department_id = request.POST.get('department_id', None)
subject = request.POST.get('subject')
message = request.POST.get('message')
category = request.POST.get('category')
# Contact info (if no patient)
contact_name = request.POST.get('contact_name', '')
contact_phone = request.POST.get('contact_phone', '')
contact_email = request.POST.get('contact_email', '')
# Call center specific
caller_relationship = request.POST.get('caller_relationship', 'patient')
# Validate required fields
if not all([hospital_id, subject, message, category]):
messages.error(request, "Please fill in all required fields.")
return redirect('callcenter:create_inquiry')
# If no patient, need contact info
if not patient_id and not contact_name:
messages.error(request, "Please provide either patient or contact information.")
return redirect('callcenter:create_inquiry')
# Create inquiry
inquiry = Inquiry.objects.create(
patient_id=patient_id if patient_id else None,
hospital_id=hospital_id,
department_id=department_id if department_id else None,
subject=subject,
message=message,
category=category,
contact_name=contact_name,
contact_phone=contact_phone,
contact_email=contact_email,
)
# Create call center interaction record
CallCenterInteraction.objects.create(
patient_id=patient_id if patient_id else None,
caller_name=contact_name,
caller_phone=contact_phone,
caller_relationship=caller_relationship,
hospital_id=hospital_id,
department_id=department_id if department_id else None,
agent=request.user,
call_type='inquiry',
subject=subject,
notes=message,
metadata={
'inquiry_id': str(inquiry.id),
'category': category,
}
)
# Log audit
AuditService.log_event(
event_type='inquiry_created',
description=f"Inquiry created via call center: {inquiry.subject}",
user=request.user,
content_object=inquiry,
metadata={
'category': inquiry.category,
'source': 'call_center',
'contact_name': contact_name,
}
)
messages.success(request, f"Inquiry #{inquiry.id} created successfully.")
return redirect('callcenter:inquiry_success', pk=inquiry.id)
except Exception as e:
messages.error(request, f"Error creating inquiry: {str(e)}")
return redirect('callcenter:create_inquiry')
# GET request - show form
hospitals = Hospital.objects.filter(status='active')
if not request.user.is_px_admin() and request.user.hospital:
hospitals = hospitals.filter(id=request.user.hospital.id)
context = {
'hospitals': hospitals,
}
return render(request, 'callcenter/inquiry_form.html', context)
@login_required
def inquiry_success(request, pk):
"""Success page after creating inquiry"""
inquiry = get_object_or_404(Inquiry, pk=pk)
context = {
'inquiry': inquiry,
}
return render(request, 'callcenter/inquiry_success.html', context)
@login_required
def inquiry_list(request):
"""List inquiries created by call center"""
queryset = Inquiry.objects.select_related(
'patient', 'hospital', 'department', 'assigned_to', 'responded_by'
)
# Apply RBAC filters
user = request.user
if user.is_px_admin():
pass
elif user.hospital:
queryset = queryset.filter(hospital=user.hospital)
else:
queryset = queryset.none()
# Apply filters
status_filter = request.GET.get('status')
if status_filter:
queryset = queryset.filter(status=status_filter)
category_filter = request.GET.get('category')
if category_filter:
queryset = queryset.filter(category=category_filter)
hospital_filter = request.GET.get('hospital')
if hospital_filter:
queryset = queryset.filter(hospital_id=hospital_filter)
# Search
search_query = request.GET.get('search')
if search_query:
queryset = queryset.filter(
Q(subject__icontains=search_query) |
Q(message__icontains=search_query) |
Q(contact_name__icontains=search_query)
)
# Ordering
queryset = queryset.order_by('-created_at')
# Pagination
page_size = int(request.GET.get('page_size', 25))
paginator = Paginator(queryset, page_size)
page_number = request.GET.get('page', 1)
page_obj = paginator.get_page(page_number)
# Get filter options
hospitals = Hospital.objects.filter(status='active')
if not user.is_px_admin() and user.hospital:
hospitals = hospitals.filter(id=user.hospital.id)
# Statistics
stats = {
'total': queryset.count(),
'open': queryset.filter(status='open').count(),
'in_progress': queryset.filter(status='in_progress').count(),
'resolved': queryset.filter(status='resolved').count(),
}
context = {
'page_obj': page_obj,
'inquiries': page_obj.object_list,
'stats': stats,
'hospitals': hospitals,
'filters': request.GET,
}
return render(request, 'callcenter/inquiry_list.html', context)
# ============================================================================
# AJAX/API HELPERS
# ============================================================================
@login_required
def get_departments_by_hospital(request):
"""Get departments for a hospital (AJAX)"""
hospital_id = request.GET.get('hospital_id')
if not hospital_id:
return JsonResponse({'departments': []})
departments = Department.objects.filter(
hospital_id=hospital_id,
status='active'
).values('id', 'name_en', 'name_ar')
return JsonResponse({'departments': list(departments)})
@login_required
def get_physicians_by_hospital(request):
"""Get physicians for a hospital (AJAX)"""
hospital_id = request.GET.get('hospital_id')
if not hospital_id:
return JsonResponse({'physicians': []})
physicians = Physician.objects.filter(
hospital_id=hospital_id,
status='active'
).values('id', 'first_name', 'last_name', 'specialty')
# Format physician names
physicians_list = [
{
'id': str(p['id']),
'name': f"Dr. {p['first_name']} {p['last_name']}",
'specialty': p['specialty']
}
for p in physicians
]
return JsonResponse({'physicians': physicians_list})
@login_required
def search_patients(request):
"""Search patients by MRN or name (AJAX)"""
query = request.GET.get('q', '')
hospital_id = request.GET.get('hospital_id', None)
if len(query) < 2:
return JsonResponse({'patients': []})
patients = Patient.objects.filter(
Q(mrn__icontains=query) |
Q(first_name__icontains=query) |
Q(last_name__icontains=query) |
Q(national_id__icontains=query) |
Q(phone__icontains=query)
)
if hospital_id:
patients = patients.filter(hospital_id=hospital_id)
patients = patients[:20]
results = [
{
'id': str(p.id),
'mrn': p.mrn,
'name': p.get_full_name(),
'phone': p.phone,
'email': p.email,
'national_id': p.national_id,
}
for p in patients
]
return JsonResponse({'patients': results})