HH/apps/journeys/ui_views.py
2026-01-24 15:27:30 +03:00

370 lines
12 KiB
Python

"""
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)