""" Complaints UI views - Server-rendered templates for complaints console """ 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, Count, Prefetch 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.accounts.models import User from apps.core.services import AuditService from apps.organizations.models import Department, Hospital, Physician from .models import ( Complaint, ComplaintAttachment, ComplaintStatus, ComplaintUpdate, ) @login_required def complaint_list(request): """ Complaints list view with advanced filters and pagination. Features: - Server-side pagination - Advanced filters (status, severity, priority, hospital, department, etc.) - Search by title, description, patient MRN - Bulk actions support - Export capability """ # Base queryset with optimizations queryset = Complaint.objects.select_related( 'patient', 'hospital', 'department', 'physician', 'assigned_to', 'resolved_by', 'closed_by' ) # Apply RBAC filters user = request.user if user.is_px_admin(): pass # See all elif user.is_hospital_admin() and user.hospital: queryset = queryset.filter(hospital=user.hospital) elif user.is_department_manager() and user.department: queryset = queryset.filter(department=user.department) elif user.hospital: queryset = queryset.filter(hospital=user.hospital) else: queryset = queryset.none() # Apply filters from request 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) priority_filter = request.GET.get('priority') if priority_filter: queryset = queryset.filter(priority=priority_filter) category_filter = request.GET.get('category') if category_filter: queryset = queryset.filter(category=category_filter) source_filter = request.GET.get('source') if source_filter: queryset = queryset.filter(source=source_filter) hospital_filter = request.GET.get('hospital') if hospital_filter: queryset = queryset.filter(hospital_id=hospital_filter) department_filter = request.GET.get('department') if department_filter: queryset = queryset.filter(department_id=department_filter) physician_filter = request.GET.get('physician') if physician_filter: queryset = queryset.filter(physician_id=physician_filter) assigned_to_filter = request.GET.get('assigned_to') if assigned_to_filter: queryset = queryset.filter(assigned_to_id=assigned_to_filter) overdue_filter = request.GET.get('is_overdue') if overdue_filter == 'true': queryset = queryset.filter(is_overdue=True) # 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) | Q(patient__first_name__icontains=search_query) | Q(patient__last_name__icontains=search_query) ) # Date range filters date_from = request.GET.get('date_from') if date_from: queryset = queryset.filter(created_at__gte=date_from) date_to = request.GET.get('date_to') if date_to: queryset = queryset.filter(created_at__lte=date_to) # Ordering order_by = request.GET.get('order_by', '-created_at') queryset = queryset.order_by(order_by) # 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) departments = Department.objects.filter(status='active') if not user.is_px_admin() and user.hospital: departments = departments.filter(hospital=user.hospital) # Get assignable users assignable_users = User.objects.filter(is_active=True) if user.hospital: assignable_users = assignable_users.filter(hospital=user.hospital) # Statistics stats = { 'total': queryset.count(), 'open': queryset.filter(status=ComplaintStatus.OPEN).count(), 'in_progress': queryset.filter(status=ComplaintStatus.IN_PROGRESS).count(), 'overdue': queryset.filter(is_overdue=True).count(), } context = { 'page_obj': page_obj, 'complaints': page_obj.object_list, 'stats': stats, 'hospitals': hospitals, 'departments': departments, 'assignable_users': assignable_users, 'status_choices': ComplaintStatus.choices, 'filters': request.GET, } return render(request, 'complaints/complaint_list.html', context) @login_required def complaint_detail(request, pk): """ Complaint detail view with timeline, attachments, and actions. Features: - Full complaint details - Timeline of all updates - Attachments management - Related surveys and journey - Linked PX actions - Workflow actions (assign, status change, add note) """ complaint = get_object_or_404( Complaint.objects.select_related( 'patient', 'hospital', 'department', 'physician', 'assigned_to', 'resolved_by', 'closed_by', 'resolution_survey' ).prefetch_related( 'attachments', 'updates__created_by' ), pk=pk ) # Check access user = request.user if not user.is_px_admin(): if user.is_hospital_admin() and complaint.hospital != user.hospital: messages.error(request, "You don't have permission to view this complaint.") return redirect('complaints:complaint_list') elif user.is_department_manager() and complaint.department != user.department: messages.error(request, "You don't have permission to view this complaint.") return redirect('complaints:complaint_list') elif user.hospital and complaint.hospital != user.hospital: messages.error(request, "You don't have permission to view this complaint.") return redirect('complaints:complaint_list') # Get timeline (updates) timeline = complaint.updates.all().order_by('-created_at') # Get attachments attachments = complaint.attachments.all().order_by('-created_at') # Get related PX actions (using ContentType since PXAction uses GenericForeignKey) from django.contrib.contenttypes.models import ContentType from apps.px_action_center.models import PXAction complaint_ct = ContentType.objects.get_for_model(Complaint) px_actions = PXAction.objects.filter( content_type=complaint_ct, object_id=complaint.id ).order_by('-created_at') # Get assignable users assignable_users = User.objects.filter(is_active=True) if complaint.hospital: assignable_users = assignable_users.filter(hospital=complaint.hospital) # Check if overdue complaint.check_overdue() context = { 'complaint': complaint, 'timeline': timeline, 'attachments': attachments, 'px_actions': px_actions, 'assignable_users': assignable_users, 'status_choices': ComplaintStatus.choices, 'can_edit': user.is_px_admin() or user.is_hospital_admin(), } return render(request, 'complaints/complaint_detail.html', context) @login_required @require_http_methods(["GET", "POST"]) def complaint_create(request): """Create new complaint""" if request.method == 'POST': # Handle form submission try: from apps.organizations.models import Patient # Get form data patient_id = request.POST.get('patient_id') 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') source = request.POST.get('source') encounter_id = request.POST.get('encounter_id', '') # Validate required fields if not all([patient_id, hospital_id, title, description, category, priority, severity, source]): messages.error(request, "Please fill in all required fields.") return redirect('complaints:complaint_create') # Create complaint complaint = Complaint.objects.create( patient_id=patient_id, 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=source, encounter_id=encounter_id, ) # Log audit AuditService.log_event( event_type='complaint_created', description=f"Complaint created: {complaint.title}", user=request.user, content_object=complaint, metadata={ 'category': complaint.category, 'severity': complaint.severity, 'patient_mrn': complaint.patient.mrn } ) messages.success(request, f"Complaint #{complaint.id} created successfully.") return redirect('complaints:complaint_detail', pk=complaint.id) except Exception as e: messages.error(request, f"Error creating complaint: {str(e)}") return redirect('complaints:complaint_create') # 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, 'complaints/complaint_form.html', context) @login_required @require_http_methods(["POST"]) def complaint_assign(request, pk): """Assign complaint to user""" complaint = get_object_or_404(Complaint, pk=pk) # Check permission user = request.user if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to assign complaints.") return redirect('complaints:complaint_detail', pk=pk) user_id = request.POST.get('user_id') if not user_id: messages.error(request, "Please select a user to assign.") return redirect('complaints:complaint_detail', pk=pk) try: assignee = User.objects.get(id=user_id) complaint.assigned_to = assignee complaint.assigned_at = timezone.now() complaint.save(update_fields=['assigned_to', 'assigned_at']) # Create update ComplaintUpdate.objects.create( complaint=complaint, update_type='assignment', message=f"Assigned to {assignee.get_full_name()}", created_by=request.user ) # Log audit AuditService.log_event( event_type='assignment', description=f"Complaint assigned to {assignee.get_full_name()}", user=request.user, content_object=complaint ) messages.success(request, f"Complaint assigned to {assignee.get_full_name()}.") except User.DoesNotExist: messages.error(request, "User not found.") return redirect('complaints:complaint_detail', pk=pk) @login_required @require_http_methods(["POST"]) def complaint_change_status(request, pk): """Change complaint status""" complaint = get_object_or_404(Complaint, pk=pk) # Check permission user = request.user if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to change complaint status.") return redirect('complaints:complaint_detail', pk=pk) new_status = request.POST.get('status') note = request.POST.get('note', '') if not new_status: messages.error(request, "Please select a status.") return redirect('complaints:complaint_detail', pk=pk) old_status = complaint.status complaint.status = new_status # Handle status-specific logic if new_status == ComplaintStatus.RESOLVED: complaint.resolved_at = timezone.now() complaint.resolved_by = request.user elif new_status == ComplaintStatus.CLOSED: complaint.closed_at = timezone.now() complaint.closed_by = request.user # Trigger resolution satisfaction survey from apps.complaints.tasks import send_complaint_resolution_survey send_complaint_resolution_survey.delay(str(complaint.id)) complaint.save() # Create update ComplaintUpdate.objects.create( complaint=complaint, update_type='status_change', message=note or f"Status changed from {old_status} to {new_status}", created_by=request.user, old_status=old_status, new_status=new_status ) # Log audit AuditService.log_event( event_type='status_change', description=f"Complaint status changed from {old_status} to {new_status}", user=request.user, content_object=complaint, metadata={'old_status': old_status, 'new_status': new_status} ) messages.success(request, f"Complaint status changed to {new_status}.") return redirect('complaints:complaint_detail', pk=pk) @login_required @require_http_methods(["POST"]) def complaint_add_note(request, pk): """Add note to complaint""" complaint = get_object_or_404(Complaint, pk=pk) note = request.POST.get('note') if not note: messages.error(request, "Please enter a note.") return redirect('complaints:complaint_detail', pk=pk) # Create update ComplaintUpdate.objects.create( complaint=complaint, update_type='note', message=note, created_by=request.user ) messages.success(request, "Note added successfully.") return redirect('complaints:complaint_detail', pk=pk) @login_required @require_http_methods(["POST"]) def complaint_escalate(request, pk): """Escalate complaint""" complaint = get_object_or_404(Complaint, pk=pk) # Check permission user = request.user if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to escalate complaints.") return redirect('complaints:complaint_detail', pk=pk) reason = request.POST.get('reason', '') # Mark as escalated complaint.escalated_at = timezone.now() complaint.save(update_fields=['escalated_at']) # Create update ComplaintUpdate.objects.create( complaint=complaint, update_type='escalation', message=f"Complaint escalated. Reason: {reason}", created_by=request.user ) # Log audit AuditService.log_event( event_type='escalation', description="Complaint escalated", user=request.user, content_object=complaint, metadata={'reason': reason} ) messages.success(request, "Complaint escalated successfully.") return redirect('complaints:complaint_detail', pk=pk) @login_required def complaint_export_csv(request): """Export complaints to CSV""" from apps.complaints.utils import export_complaints_csv # Get filtered queryset (reuse list view logic) queryset = Complaint.objects.select_related( 'patient', 'hospital', 'department', 'physician', 'assigned_to', 'resolved_by', 'closed_by' ) # Apply RBAC filters user = request.user if user.is_px_admin(): pass elif user.is_hospital_admin() and user.hospital: queryset = queryset.filter(hospital=user.hospital) elif user.is_department_manager() and user.department: queryset = queryset.filter(department=user.department) elif user.hospital: queryset = queryset.filter(hospital=user.hospital) else: queryset = queryset.none() # Apply filters from request 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) priority_filter = request.GET.get('priority') if priority_filter: queryset = queryset.filter(priority=priority_filter) hospital_filter = request.GET.get('hospital') if hospital_filter: queryset = queryset.filter(hospital_id=hospital_filter) department_filter = request.GET.get('department') if department_filter: queryset = queryset.filter(department_id=department_filter) overdue_filter = request.GET.get('is_overdue') if overdue_filter == 'true': queryset = queryset.filter(is_overdue=True) 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) ) return export_complaints_csv(queryset, request.GET.dict()) @login_required def complaint_export_excel(request): """Export complaints to Excel""" from apps.complaints.utils import export_complaints_excel # Get filtered queryset (same as CSV) queryset = Complaint.objects.select_related( 'patient', 'hospital', 'department', 'physician', 'assigned_to', 'resolved_by', 'closed_by' ) # Apply RBAC filters user = request.user if user.is_px_admin(): pass elif user.is_hospital_admin() and user.hospital: queryset = queryset.filter(hospital=user.hospital) elif user.is_department_manager() and user.department: queryset = queryset.filter(department=user.department) elif user.hospital: queryset = queryset.filter(hospital=user.hospital) else: queryset = queryset.none() # Apply filters from request 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) priority_filter = request.GET.get('priority') if priority_filter: queryset = queryset.filter(priority=priority_filter) hospital_filter = request.GET.get('hospital') if hospital_filter: queryset = queryset.filter(hospital_id=hospital_filter) department_filter = request.GET.get('department') if department_filter: queryset = queryset.filter(department_id=department_filter) overdue_filter = request.GET.get('is_overdue') if overdue_filter == 'true': queryset = queryset.filter(is_overdue=True) 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) ) return export_complaints_excel(queryset, request.GET.dict()) @login_required @require_http_methods(["POST"]) def complaint_bulk_assign(request): """Bulk assign complaints""" from apps.complaints.utils import bulk_assign_complaints import json # Check permission if not (request.user.is_px_admin() or request.user.is_hospital_admin()): return JsonResponse({'success': False, 'error': 'Permission denied'}, status=403) try: data = json.loads(request.body) complaint_ids = data.get('complaint_ids', []) user_id = data.get('user_id') if not complaint_ids or not user_id: return JsonResponse({'success': False, 'error': 'Missing required fields'}, status=400) result = bulk_assign_complaints(complaint_ids, user_id, request.user) if result['success']: messages.success(request, f"Successfully assigned {result['success_count']} complaints.") return JsonResponse(result) except json.JSONDecodeError: return JsonResponse({'success': False, 'error': 'Invalid JSON'}, status=400) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=500) @login_required @require_http_methods(["POST"]) def complaint_bulk_status(request): """Bulk change complaint status""" from apps.complaints.utils import bulk_change_status import json # Check permission if not (request.user.is_px_admin() or request.user.is_hospital_admin()): return JsonResponse({'success': False, 'error': 'Permission denied'}, status=403) try: data = json.loads(request.body) complaint_ids = data.get('complaint_ids', []) new_status = data.get('status') note = data.get('note', '') if not complaint_ids or not new_status: return JsonResponse({'success': False, 'error': 'Missing required fields'}, status=400) result = bulk_change_status(complaint_ids, new_status, request.user, note) if result['success']: messages.success(request, f"Successfully updated {result['success_count']} complaints.") return JsonResponse(result) except json.JSONDecodeError: return JsonResponse({'success': False, 'error': 'Invalid JSON'}, status=400) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=500) @login_required @require_http_methods(["POST"]) def complaint_bulk_escalate(request): """Bulk escalate complaints""" from apps.complaints.utils import bulk_escalate_complaints import json # Check permission if not (request.user.is_px_admin() or request.user.is_hospital_admin()): return JsonResponse({'success': False, 'error': 'Permission denied'}, status=403) try: data = json.loads(request.body) complaint_ids = data.get('complaint_ids', []) reason = data.get('reason', '') if not complaint_ids: return JsonResponse({'success': False, 'error': 'No complaints selected'}, status=400) result = bulk_escalate_complaints(complaint_ids, request.user, reason) if result['success']: messages.success(request, f"Successfully escalated {result['success_count']} complaints.") return JsonResponse(result) except json.JSONDecodeError: return JsonResponse({'success': False, 'error': 'Invalid JSON'}, status=400) except Exception as e: return JsonResponse({'success': False, 'error': str(e)}, status=500) # ============================================================================ # INQUIRIES VIEWS # ============================================================================ @login_required def inquiry_list(request): """ Inquiries list view with filters and pagination. """ from .models import Inquiry # Base queryset with optimizations queryset = Inquiry.objects.select_related( 'patient', 'hospital', 'department', 'assigned_to', 'responded_by' ) # Apply RBAC filters user = request.user if user.is_px_admin(): pass # See all elif user.is_hospital_admin() and user.hospital: queryset = queryset.filter(hospital=user.hospital) elif user.is_department_manager() and user.department: queryset = queryset.filter(department=user.department) 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) department_filter = request.GET.get('department') if department_filter: queryset = queryset.filter(department_id=department_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) | Q(contact_email__icontains=search_query) ) # Ordering order_by = request.GET.get('order_by', '-created_at') queryset = queryset.order_by(order_by) # 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) departments = Department.objects.filter(status='active') if not user.is_px_admin() and user.hospital: departments = departments.filter(hospital=user.hospital) # 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, 'departments': departments, 'filters': request.GET, } return render(request, 'complaints/inquiry_list.html', context) @login_required def inquiry_detail(request, pk): """ Inquiry detail view. """ from .models import Inquiry inquiry = get_object_or_404( Inquiry.objects.select_related( 'patient', 'hospital', 'department', 'assigned_to', 'responded_by' ), pk=pk ) # Check access user = request.user if not user.is_px_admin(): if user.is_hospital_admin() and inquiry.hospital != user.hospital: messages.error(request, "You don't have permission to view this inquiry.") return redirect('complaints:inquiry_list') elif user.hospital and inquiry.hospital != user.hospital: messages.error(request, "You don't have permission to view this inquiry.") return redirect('complaints:inquiry_list') # Get assignable users assignable_users = User.objects.filter(is_active=True) if inquiry.hospital: assignable_users = assignable_users.filter(hospital=inquiry.hospital) context = { 'inquiry': inquiry, 'assignable_users': assignable_users, 'can_edit': user.is_px_admin() or user.is_hospital_admin(), } return render(request, 'complaints/inquiry_detail.html', context) @login_required @require_http_methods(["GET", "POST"]) def inquiry_create(request): """Create new inquiry""" from .models import Inquiry from apps.organizations.models import Patient 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', '') # Validate required fields if not all([hospital_id, subject, message, category]): messages.error(request, "Please fill in all required fields.") return redirect('complaints:inquiry_create') # 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, ) # Log audit AuditService.log_event( event_type='inquiry_created', description=f"Inquiry created: {inquiry.subject}", user=request.user, content_object=inquiry, metadata={'category': inquiry.category} ) messages.success(request, f"Inquiry #{inquiry.id} created successfully.") return redirect('complaints:inquiry_detail', pk=inquiry.id) except Exception as e: messages.error(request, f"Error creating inquiry: {str(e)}") return redirect('complaints:inquiry_create') # 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, 'complaints/inquiry_form.html', context) @login_required @require_http_methods(["POST"]) def inquiry_respond(request, pk): """Respond to inquiry""" from .models import Inquiry inquiry = get_object_or_404(Inquiry, pk=pk) # Check permission user = request.user if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to respond to inquiries.") return redirect('complaints:inquiry_detail', pk=pk) response = request.POST.get('response') if not response: messages.error(request, "Please enter a response.") return redirect('complaints:inquiry_detail', pk=pk) inquiry.response = response inquiry.responded_at = timezone.now() inquiry.responded_by = request.user inquiry.status = 'resolved' inquiry.save() # Log audit AuditService.log_event( event_type='inquiry_responded', description=f"Inquiry responded to: {inquiry.subject}", user=request.user, content_object=inquiry ) messages.success(request, "Response sent successfully.") return redirect('complaints:inquiry_detail', pk=pk) # ============================================================================ # ANALYTICS VIEWS # ============================================================================ @login_required def complaints_analytics(request): """ Complaints analytics dashboard. """ from .analytics import ComplaintAnalytics user = request.user hospital = None # Apply RBAC if not user.is_px_admin() and user.hospital: hospital = user.hospital # Get date range from request date_range = int(request.GET.get('date_range', 30)) # Get analytics data dashboard_summary = ComplaintAnalytics.get_dashboard_summary(hospital) trends = ComplaintAnalytics.get_complaint_trends(hospital, date_range) sla_compliance = ComplaintAnalytics.get_sla_compliance(hospital, date_range) resolution_rate = ComplaintAnalytics.get_resolution_rate(hospital, date_range) top_categories = ComplaintAnalytics.get_top_categories(hospital, date_range) overdue_complaints = ComplaintAnalytics.get_overdue_complaints(hospital) context = { 'dashboard_summary': dashboard_summary, 'trends': trends, 'sla_compliance': sla_compliance, 'resolution_rate': resolution_rate, 'top_categories': top_categories, 'overdue_complaints': overdue_complaints, 'date_range': date_range, } return render(request, 'complaints/analytics.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', 'name_ar') return JsonResponse({'departments': list(departments)}) @login_required def get_physicians_by_department(request): """Get physicians for a department (AJAX)""" department_id = request.GET.get('department_id') if not department_id: return JsonResponse({'physicians': []}) from apps.organizations.models import Physician physicians = Physician.objects.filter( department_id=department_id, status='active' ).values('id', 'first_name', 'last_name') return JsonResponse({'physicians': list(physicians)}) @login_required def search_patients(request): """Search patients by MRN or name (AJAX)""" from apps.organizations.models import Patient query = request.GET.get('q', '') 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) )[:10] results = [ { 'id': str(p.id), 'mrn': p.mrn, 'name': p.get_full_name(), 'phone': p.phone, 'email': p.email, } for p in patients ] return JsonResponse({'patients': results})