""" Journey Console UI views - Server-rendered templates for journey monitoring """ 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.shortcuts import get_object_or_404, redirect, render from django.utils import timezone from apps.organizations.models import Department, Hospital from .forms import ( PatientJourneyStageTemplateFormSet, PatientJourneyTemplateForm, ) from .models import ( PatientJourneyInstance, PatientJourneyStageInstance, PatientJourneyStageTemplate, PatientJourneyTemplate, StageStatus, ) @login_required def journey_instance_list(request): """ Journey instances list view with filters. Features: - Server-side pagination - Filters (journey type, status, hospital, department, date range) - Search by encounter ID, patient MRN - Completion percentage display """ # Base queryset with optimizations queryset = PatientJourneyInstance.objects.select_related( 'journey_template', 'patient', 'hospital', 'department' ).prefetch_related( 'stage_instances__stage_template', 'stage_instances__staff', 'stage_instances__department' ) # 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 journey_type = request.GET.get('journey_type') if journey_type: queryset = queryset.filter(journey_template__journey_type=journey_type) status_filter = request.GET.get('status') if status_filter: queryset = queryset.filter(status=status_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(encounter_id__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 date_from = request.GET.get('date_from') if date_from: queryset = queryset.filter(started_at__gte=date_from) date_to = request.GET.get('date_to') if date_to: queryset = queryset.filter(started_at__lte=date_to) # Ordering order_by = request.GET.get('order_by', '-started_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(), 'active': queryset.filter(status='active').count(), 'completed': queryset.filter(status='completed').count(), } context = { 'page_obj': page_obj, 'journeys': page_obj.object_list, 'stats': stats, 'hospitals': hospitals, 'departments': departments, 'filters': request.GET, } return render(request, 'journeys/instance_list.html', context) @login_required def journey_instance_detail(request, pk): """ Journey instance detail view with stage progress. Features: - Stage progress stepper - Each stage shows status, timestamp, physician, survey - Event history timeline - Completion percentage """ journey = get_object_or_404( PatientJourneyInstance.objects.select_related( 'journey_template', 'patient', 'hospital', 'department' ).prefetch_related( 'stage_instances__stage_template', 'stage_instances__staff', 'stage_instances__department' ), pk=pk ) # Get stages in order stages = journey.stage_instances.all().order_by('stage_template__order') # Get completion percentage completion_percentage = journey.get_completion_percentage() context = { 'journey': journey, 'stages': stages, 'completion_percentage': completion_percentage, } return render(request, 'journeys/instance_detail.html', context) @login_required def journey_template_list(request): """ Journey templates list view. Simple list of templates with basic info. Template builder is complex and can be added later. """ # Base queryset queryset = PatientJourneyTemplate.objects.select_related( 'hospital' ).prefetch_related( 'stages' ) # Apply RBAC filters user = request.user if user.is_px_admin(): pass # See all elif user.hospital: queryset = queryset.filter(hospital=user.hospital) else: queryset = queryset.none() # Apply filters journey_type = request.GET.get('journey_type') if journey_type: queryset = queryset.filter(journey_type=journey_type) hospital_filter = request.GET.get('hospital') if hospital_filter: queryset = queryset.filter(hospital_id=hospital_filter) is_active = request.GET.get('is_active') if is_active == 'true': queryset = queryset.filter(is_active=True) elif is_active == 'false': queryset = queryset.filter(is_active=False) # Ordering queryset = queryset.order_by('hospital', 'journey_type', 'name') # 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) context = { 'page_obj': page_obj, 'templates': page_obj.object_list, 'hospitals': hospitals, 'filters': request.GET, } return render(request, 'journeys/template_list.html', context) @login_required def journey_template_create(request): """Create a new journey template with stages""" # Check permission user = request.user if not user.is_px_admin() and not user.is_hospital_admin(): messages.error(request, "You don't have permission to create journey templates.") return redirect('journeys:template_list') if request.method == 'POST': form = PatientJourneyTemplateForm(request.POST) formset = PatientJourneyStageTemplateFormSet(request.POST) if form.is_valid() and formset.is_valid(): template = form.save(commit=False) template.save() stages = formset.save(commit=False) for stage in stages: stage.journey_template = template stage.save() messages.success(request, "Journey template created successfully.") return redirect('journeys:template_detail', pk=template.pk) else: form = PatientJourneyTemplateForm() formset = PatientJourneyStageTemplateFormSet() context = { 'form': form, 'formset': formset, } return render(request, 'journeys/template_form.html', context) @login_required def journey_template_detail(request, pk): """View journey template details""" template = get_object_or_404( PatientJourneyTemplate.objects.select_related('hospital').prefetch_related( 'stages__survey_template' ), pk=pk ) # Check permission user = request.user if not user.is_px_admin() and not user.is_hospital_admin(): if user.hospital and template.hospital != user.hospital: messages.error(request, "You don't have permission to view this template.") return redirect('journeys:template_list') # Get statistics total_instances = template.instances.count() active_instances = template.instances.filter(status='active').count() completed_instances = template.instances.filter(status='completed').count() stages = template.stages.all().order_by('order') context = { 'template': template, 'stages': stages, 'stats': { 'total_instances': total_instances, 'active_instances': active_instances, 'completed_instances': completed_instances, } } return render(request, 'journeys/template_detail.html', context) @login_required def journey_template_edit(request, pk): """Edit an existing journey template with stages""" template = get_object_or_404(PatientJourneyTemplate, pk=pk) # Check permission user = request.user if not user.is_px_admin() and not user.is_hospital_admin(): if user.hospital and template.hospital != user.hospital: messages.error(request, "You don't have permission to edit this template.") return redirect('journeys:template_list') if request.method == 'POST': form = PatientJourneyTemplateForm(request.POST, instance=template) formset = PatientJourneyStageTemplateFormSet(request.POST, instance=template) if form.is_valid() and formset.is_valid(): form.save() formset.save() messages.success(request, "Journey template updated successfully.") return redirect('journeys:template_detail', pk=template.pk) else: form = PatientJourneyTemplateForm(instance=template) formset = PatientJourneyStageTemplateFormSet(instance=template) context = { 'form': form, 'formset': formset, 'template': template, } return render(request, 'journeys/template_form.html', context) @login_required def journey_template_delete(request, pk): """Delete a journey template""" template = get_object_or_404(PatientJourneyTemplate, pk=pk) # Check permission user = request.user if not user.is_px_admin() and not user.is_hospital_admin(): if user.hospital and template.hospital != user.hospital: messages.error(request, "You don't have permission to delete this template.") return redirect('journeys:template_list') if request.method == 'POST': template_name = template.name template.delete() messages.success(request, f"Journey template '{template_name}' deleted successfully.") return redirect('journeys:template_list') context = { 'template': template, } return render(request, 'journeys/template_confirm_delete.html', context)