""" Referrals views for the Tenhal Multidisciplinary Healthcare Platform. This module contains views for referral management including: - Inter-discipline referrals - External provider referrals - Referral tracking and status - Auto-referral rules (e.g., ASD→Pediatrician) """ from django.contrib.auth.mixins import LoginRequiredMixin from django.db.models import Q from django.shortcuts import get_object_or_404, redirect from django.utils import timezone from django.views.generic import ListView, DetailView, CreateView, UpdateView, TemplateView, FormView from django.urls import reverse_lazy from django.contrib import messages from django.http import JsonResponse from django.views import View from core.mixins import ( TenantFilterMixin, RolePermissionMixin, AuditLogMixin, HTMXResponseMixin, SuccessMessageMixin, PaginationMixin, ) from core.models import User, Patient, Tenant from .models import Referral from .forms import ReferralForm, ExternalReferralForm class ReferralListView(LoginRequiredMixin, TenantFilterMixin, PaginationMixin, HTMXResponseMixin, ListView): """ Referral list view. Features: - Filter by patient, from/to discipline, status, urgency - Search by patient name/MRN - Role-based filtering (providers see their referrals) """ model = Referral template_name = 'referrals/referral_list.html' htmx_template_name = 'referrals/partials/referral_list_partial.html' context_object_name = 'referrals' paginate_by = 25 def get_queryset(self): """Get filtered queryset.""" queryset = super().get_queryset() user = self.request.user # Role-based filtering if user.role in [User.Role.DOCTOR, User.Role.NURSE, User.Role.OT, User.Role.SLP, User.Role.ABA]: # Clinical staff see referrals from or to them queryset = queryset.filter( Q(from_provider=user) | Q(to_provider=user) ) # Apply search search_query = self.request.GET.get('search', '').strip() if search_query: queryset = queryset.filter( Q(patient__first_name_en__icontains=search_query) | Q(patient__last_name_en__icontains=search_query) | Q(patient__mrn__icontains=search_query) ) # Apply filters patient_id = self.request.GET.get('patient') if patient_id: queryset = queryset.filter(patient_id=patient_id) from_discipline = self.request.GET.get('from_discipline') if from_discipline: queryset = queryset.filter(from_discipline=from_discipline) to_discipline = self.request.GET.get('to_discipline') if to_discipline: queryset = queryset.filter(to_discipline=to_discipline) status = self.request.GET.get('status') if status: queryset = queryset.filter(status=status) urgency = self.request.GET.get('urgency') if urgency: queryset = queryset.filter(urgency=urgency) date_from = self.request.GET.get('date_from') if date_from: queryset = queryset.filter(created_at__gte=date_from) date_to = self.request.GET.get('date_to') if date_to: queryset = queryset.filter(created_at__lte=date_to) return queryset.select_related( 'patient', 'from_provider', 'to_provider' ).order_by('-created_at') def get_context_data(self, **kwargs): """Add filter options.""" context = super().get_context_data(**kwargs) # Add filter options context['discipline_choices'] = Referral.Discipline.choices context['status_choices'] = Referral.Status.choices context['urgency_choices'] = Referral.Urgency.choices # Add current filters context['current_filters'] = { 'search': self.request.GET.get('search', ''), 'patient': self.request.GET.get('patient', ''), 'from_discipline': self.request.GET.get('from_discipline', ''), 'to_discipline': self.request.GET.get('to_discipline', ''), 'status': self.request.GET.get('status', ''), 'urgency': self.request.GET.get('urgency', ''), 'date_from': self.request.GET.get('date_from', ''), 'date_to': self.request.GET.get('date_to', ''), } # Add statistics queryset = self.get_queryset() context['stats'] = { 'total_referrals': queryset.count(), 'pending': queryset.filter(status=Referral.Status.PENDING).count(), 'accepted': queryset.filter(status=Referral.Status.ACCEPTED).count(), 'urgent': queryset.filter(urgency=Referral.Urgency.URGENT).count(), } return context class ReferralDetailView(LoginRequiredMixin, TenantFilterMixin, DetailView): """ Referral detail view. Features: - Full referral details - From/to discipline and provider - Reason, urgency, status - Response and notes - Status history """ model = Referral template_name = 'referrals/referral_detail.html' context_object_name = 'referral' def get_context_data(self, **kwargs): """Add status history and available actions.""" context = super().get_context_data(**kwargs) referral = self.object # Get status history if available if hasattr(referral, 'history'): context['status_history'] = referral.history.all()[:10] # Check if user can accept/reject user = self.request.user context['can_respond'] = ( referral.status == Referral.Status.PENDING and (user == referral.to_provider or user.role == User.Role.ADMIN) ) # Check if user can cancel context['can_cancel'] = ( referral.status in [Referral.Status.PENDING, Referral.Status.ACCEPTED] and (user == referral.from_provider or user.role == User.Role.ADMIN) ) return context class ReferralCreateView(LoginRequiredMixin, RolePermissionMixin, AuditLogMixin, SuccessMessageMixin, CreateView): """ Referral creation view. Features: - Create inter-discipline or external referral - From/to discipline - Reason, urgency - Auto-referral rules (e.g., ASD→Pediatrician) """ model = Referral form_class = ReferralForm template_name = 'referrals/referral_form.html' success_message = "Referral created successfully!" allowed_roles = [User.Role.ADMIN, User.Role.DOCTOR, User.Role.NURSE, User.Role.OT, User.Role.SLP, User.Role.ABA] def get_success_url(self): """Redirect to referral detail.""" return reverse_lazy('referrals:referral_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): """Set tenant and from_provider.""" # Set tenant form.instance.tenant = self.request.user.tenant # Set from_provider form.instance.from_provider = self.request.user # Set from_discipline based on user role form.instance.from_discipline = self._get_discipline_from_role(self.request.user.role) # Set referral date if not provided if not form.instance.referral_date: form.instance.referral_date = timezone.now().date() # Set initial status form.instance.status = Referral.Status.PENDING # Save referral response = super().form_valid(form) # Send notification to recipient (TODO) self._send_referral_notification() return response def _get_discipline_from_role(self, role): """Map user role to discipline.""" role_to_discipline = { User.Role.DOCTOR: Referral.Discipline.MEDICAL, User.Role.NURSE: Referral.Discipline.NURSING, User.Role.OT: Referral.Discipline.OT, User.Role.SLP: Referral.Discipline.SLP, User.Role.ABA: Referral.Discipline.ABA, } return role_to_discipline.get(role, Referral.Discipline.MEDICAL) def _send_referral_notification(self): """Send notification to recipient.""" # TODO: Implement notification pass def get_context_data(self, **kwargs): """Add form title and patient info.""" context = super().get_context_data(**kwargs) context['form_title'] = 'Create Referral' context['submit_text'] = 'Create Referral' # Get patient if provided patient_id = self.request.GET.get('patient') if patient_id: try: context['patient'] = Patient.objects.get( pk=patient_id, tenant=self.request.user.tenant ) except Patient.DoesNotExist: pass return context class ReferralUpdateView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin, AuditLogMixin, SuccessMessageMixin, UpdateView): """ Referral update view. Features: - Update referral details - Only creator can update pending referrals """ model = Referral form_class = ReferralForm template_name = 'referrals/referral_form.html' success_message = "Referral updated successfully!" allowed_roles = [User.Role.ADMIN, User.Role.DOCTOR, User.Role.NURSE, User.Role.OT, User.Role.SLP, User.Role.ABA] def get_success_url(self): """Redirect to referral detail.""" return reverse_lazy('referrals:referral_detail', kwargs={'pk': self.object.pk}) def get_form(self, form_class=None): """Disable fields if referral is not pending.""" form = super().get_form(form_class) if self.object.status != Referral.Status.PENDING: for field in form.fields: form.fields[field].disabled = True return form def get_context_data(self, **kwargs): """Add form title.""" context = super().get_context_data(**kwargs) context['form_title'] = f'Update Referral - {self.object.patient.mrn}' context['submit_text'] = 'Update Referral' return context class ReferralAcceptView(LoginRequiredMixin, RolePermissionMixin, AuditLogMixin, SuccessMessageMixin, UpdateView): """ Accept referral view. Features: - Accept pending referral - Add response notes - Update status to ACCEPTED """ model = Referral fields = ['response_notes'] template_name = 'referrals/referral_accept.html' success_message = "Referral accepted successfully!" allowed_roles = [User.Role.ADMIN, User.Role.DOCTOR, User.Role.NURSE, User.Role.OT, User.Role.SLP, User.Role.ABA] def get_success_url(self): """Redirect to referral detail.""" return reverse_lazy('referrals:referral_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): """Update status to ACCEPTED.""" referral = form.instance # Check if user can accept if referral.status != Referral.Status.PENDING: messages.error(self.request, 'Only pending referrals can be accepted.') return redirect('referrals:referral_detail', pk=referral.pk) if self.request.user != referral.to_provider and self.request.user.role != User.Role.ADMIN: messages.error(self.request, 'You do not have permission to accept this referral.') return redirect('referrals:referral_detail', pk=referral.pk) # Update status referral.status = Referral.Status.ACCEPTED referral.response_date = timezone.now().date() # Save response = super().form_valid(form) # Send notification to referrer self._send_acceptance_notification(referral) return response def _send_acceptance_notification(self, referral): """Send notification to referrer.""" # TODO: Implement notification pass class ReferralRejectView(LoginRequiredMixin, RolePermissionMixin, AuditLogMixin, SuccessMessageMixin, UpdateView): """ Reject referral view. Features: - Reject pending referral - Add rejection reason - Update status to REJECTED """ model = Referral fields = ['response_notes'] template_name = 'referrals/referral_reject.html' success_message = "Referral rejected." allowed_roles = [User.Role.ADMIN, User.Role.DOCTOR, User.Role.NURSE, User.Role.OT, User.Role.SLP, User.Role.ABA] def get_success_url(self): """Redirect to referral detail.""" return reverse_lazy('referrals:referral_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): """Update status to REJECTED.""" referral = form.instance # Check if user can reject if referral.status != Referral.Status.PENDING: messages.error(self.request, 'Only pending referrals can be rejected.') return redirect('referrals:referral_detail', pk=referral.pk) if self.request.user != referral.to_provider and self.request.user.role != User.Role.ADMIN: messages.error(self.request, 'You do not have permission to reject this referral.') return redirect('referrals:referral_detail', pk=referral.pk) # Update status referral.status = Referral.Status.REJECTED referral.response_date = timezone.now().date() # Save response = super().form_valid(form) # Send notification to referrer self._send_rejection_notification(referral) return response def _send_rejection_notification(self, referral): """Send notification to referrer.""" # TODO: Implement notification pass class ReferralCompleteView(LoginRequiredMixin, RolePermissionMixin, AuditLogMixin, SuccessMessageMixin, UpdateView): """ Complete referral view. Features: - Mark accepted referral as completed - Add completion notes - Update status to COMPLETED """ model = Referral fields = ['completion_notes'] template_name = 'referrals/referral_complete.html' success_message = "Referral marked as completed!" allowed_roles = [User.Role.ADMIN, User.Role.DOCTOR, User.Role.NURSE, User.Role.OT, User.Role.SLP, User.Role.ABA] def get_success_url(self): """Redirect to referral detail.""" return reverse_lazy('referrals:referral_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): """Update status to COMPLETED.""" referral = form.instance # Check if user can complete if referral.status != Referral.Status.ACCEPTED: messages.error(self.request, 'Only accepted referrals can be completed.') return redirect('referrals:referral_detail', pk=referral.pk) if self.request.user != referral.to_provider and self.request.user.role != User.Role.ADMIN: messages.error(self.request, 'You do not have permission to complete this referral.') return redirect('referrals:referral_detail', pk=referral.pk) # Update status referral.status = Referral.Status.COMPLETED referral.completion_date = timezone.now().date() # Save response = super().form_valid(form) # Send notification to referrer self._send_completion_notification(referral) return response def _send_completion_notification(self, referral): """Send notification to referrer.""" # TODO: Implement notification pass class ExternalReferralCreateView(FormView): """ Public view for external centers to submit referrals. Does not require authentication. """ form_class = ExternalReferralForm template_name = 'referrals/external_referral_form.html' success_url = reverse_lazy('referrals:external_referral_success') def get_context_data(self, **kwargs): """Add tenant information to context.""" context = super().get_context_data(**kwargs) # Get tenant from subdomain or settings (you may need to adjust this) # For now, we'll get the first active tenant try: tenant = Tenant.objects.filter(is_active=True).first() context['tenant'] = tenant except Tenant.DoesNotExist: context['tenant'] = None return context def form_valid(self, form): """Process the external referral submission.""" # Get tenant (adjust based on your multi-tenancy setup) tenant = Tenant.objects.filter(is_active=True).first() if not tenant: messages.error(self.request, 'Unable to process referral at this time.') return self.form_invalid(form) # Create or get patient patient_data = { 'first_name_en': form.cleaned_data['patient_first_name'], 'last_name_en': form.cleaned_data['patient_last_name'], 'date_of_birth': form.cleaned_data['patient_date_of_birth'], 'sex': form.cleaned_data['patient_sex'], 'national_id': form.cleaned_data.get('patient_national_id', ''), 'caregiver_name': form.cleaned_data['caregiver_name'], 'caregiver_phone': form.cleaned_data['caregiver_phone'], 'caregiver_relationship': form.cleaned_data['caregiver_relationship'], 'tenant': tenant, } # Try to find existing patient by national_id or create new patient = None if patient_data['national_id']: patient = Patient.objects.filter( tenant=tenant, national_id=patient_data['national_id'] ).first() if not patient: # Generate MRN for new patient from core.services import generate_mrn patient_data['mrn'] = generate_mrn(tenant) patient = Patient.objects.create(**patient_data) # Create referral referral = form.instance referral.tenant = tenant referral.patient = patient # Set external provider information referral.external_provider_name = form.cleaned_data['referring_center_name'] referral.external_provider_contact = ( f"Doctor: {form.cleaned_data['referring_doctor_name']}\n" f"Phone: {form.cleaned_data['referring_contact_phone']}\n" f"Email: {form.cleaned_data['referring_contact_email']}" ) # Set referral details referral.reason = form.cleaned_data['reason'] referral.urgency = form.cleaned_data['urgency'] referral.clinical_summary = form.cleaned_data.get('clinical_summary', '') referral.from_discipline = Referral.Discipline.EXTERNAL referral.status = Referral.Status.PENDING # Map requested specialty to clinic requested_specialty = form.cleaned_data.get('requested_specialty') if requested_specialty: from core.models import Clinic to_clinic = Clinic.objects.filter( tenant=tenant, specialty=requested_specialty, is_active=True ).first() if to_clinic: referral.to_clinic = to_clinic referral.to_discipline = requested_specialty # Store referring center contact info in notes referral.notes = ( f"External Referral from: {form.cleaned_data['referring_center_name']}\n" f"Referring Doctor: {form.cleaned_data['referring_doctor_name']}\n" f"Contact: {form.cleaned_data['referring_contact_phone']} / {form.cleaned_data['referring_contact_email']}" ) referral.save() # Send notification to staff (TODO: implement) self._send_staff_notification(referral, form.cleaned_data) # Send confirmation email to referring center self._send_confirmation_email(referral, form.cleaned_data) messages.success( self.request, 'Referral submitted successfully! We will contact you soon with updates.' ) return super().form_valid(form) def _send_staff_notification(self, referral, form_data): """Send notification to internal staff about new external referral.""" # TODO: Implement notification to staff pass def _send_confirmation_email(self, referral, form_data): """Send confirmation email to referring center.""" # TODO: Implement confirmation email pass class ExternalReferralSuccessView(TemplateView): """Success page after external referral submission.""" template_name = 'referrals/external_referral_success.html' class GetProvidersByClinicView(LoginRequiredMixin, View): """ AJAX endpoint to get providers filtered by clinic. Returns JSON list of providers for the selected clinic. """ def get(self, request, *args, **kwargs): """Get providers for the specified clinic.""" clinic_id = request.GET.get('clinic_id') if not clinic_id: return JsonResponse({'providers': []}) try: # Get providers who have this clinic in their specialties from appointments.models import Provider providers = Provider.objects.filter( tenant=request.user.tenant, specialties__id=clinic_id, is_available=True, user__is_active=True ).select_related('user').values( 'user__id', 'user__first_name', 'user__last_name' ).distinct() # Format provider data provider_list = [ { 'id': str(provider['user__id']), 'name': f"{provider['user__first_name']} {provider['user__last_name']}" } for provider in providers ] return JsonResponse({'providers': provider_list}) except Exception as e: return JsonResponse({'error': str(e)}, status=400)