""" Survey Console UI views - Server-rendered templates for survey management """ 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, Prefetch 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.core.services import AuditService from apps.organizations.models import Department, Hospital from .models import SurveyInstance, SurveyTemplate from .tasks import send_satisfaction_feedback @login_required def survey_instance_list(request): """ Survey instances list view with filters. Features: - Server-side pagination - Filters (status, journey type, hospital, date range) - Search by patient MRN - Score display """ # Base queryset with optimizations queryset = SurveyInstance.objects.select_related( 'survey_template', 'patient', 'journey_instance__journey_template', 'journey_stage_instance__stage_template' ).prefetch_related( 'responses__question' ) # 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(survey_template__hospital=user.hospital) elif user.hospital: queryset = queryset.filter(survey_template__hospital=user.hospital) else: queryset = queryset.none() # Apply filters status_filter = request.GET.get('status') if status_filter: queryset = queryset.filter(status=status_filter) survey_type = request.GET.get('survey_type') if survey_type: queryset = queryset.filter(survey_template__survey_type=survey_type) is_negative = request.GET.get('is_negative') if is_negative == 'true': queryset = queryset.filter(is_negative=True) hospital_filter = request.GET.get('hospital') if hospital_filter: queryset = queryset.filter(survey_template__hospital_id=hospital_filter) # Search search_query = request.GET.get('search') if search_query: queryset = queryset.filter( Q(patient__mrn__icontains=search_query) | Q(patient__first_name__icontains=search_query) | Q(patient__last_name__icontains=search_query) | Q(encounter_id__icontains=search_query) ) # Date range date_from = request.GET.get('date_from') if date_from: queryset = queryset.filter(sent_at__gte=date_from) date_to = request.GET.get('date_to') if date_to: queryset = queryset.filter(sent_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) # Statistics stats = { 'total': queryset.count(), 'sent': queryset.filter(status='sent').count(), 'completed': queryset.filter(status='completed').count(), 'negative': queryset.filter(is_negative=True).count(), } context = { 'page_obj': page_obj, 'surveys': page_obj.object_list, 'stats': stats, 'hospitals': hospitals, 'filters': request.GET, } return render(request, 'surveys/instance_list.html', context) @login_required def survey_instance_detail(request, pk): """ Survey instance detail view with responses. Features: - Full survey details - All responses - Score breakdown - Related journey/stage info """ survey = get_object_or_404( SurveyInstance.objects.select_related( 'survey_template', 'patient', 'journey_instance__journey_template', 'journey_stage_instance__stage_template' ).prefetch_related( 'responses__question' ), pk=pk ) # Get responses responses = survey.responses.all().order_by('question__order') context = { 'survey': survey, 'responses': responses, } return render(request, 'surveys/instance_detail.html', context) @login_required def survey_template_list(request): """Survey templates list view""" queryset = SurveyTemplate.objects.select_related('hospital').prefetch_related('questions') # 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 survey_type = request.GET.get('survey_type') if survey_type: queryset = queryset.filter(survey_type=survey_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', 'survey_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, 'surveys/template_list.html', context) @login_required @require_http_methods(["POST"]) def survey_log_patient_contact(request, pk): """ Log patient contact for negative survey. This records that the user contacted the patient to discuss the negative survey feedback. """ survey = get_object_or_404(SurveyInstance, pk=pk) # Check permission user = request.user if not user.is_px_admin() and not user.is_hospital_admin(): if user.hospital and survey.survey_template.hospital != user.hospital: messages.error(request, "You don't have permission to modify this survey.") return redirect('surveys:instance_detail', pk=pk) # Check if survey is negative if not survey.is_negative: messages.warning(request, "This survey is not marked as negative.") return redirect('surveys:instance_detail', pk=pk) # Get form data contact_notes = request.POST.get('contact_notes', '') issue_resolved = request.POST.get('issue_resolved') == 'on' if not contact_notes: messages.error(request, "Please provide contact notes.") return redirect('surveys:instance_detail', pk=pk) try: # Update survey survey.patient_contacted = True survey.patient_contacted_at = timezone.now() survey.patient_contacted_by = user survey.contact_notes = contact_notes survey.issue_resolved = issue_resolved survey.save(update_fields=[ 'patient_contacted', 'patient_contacted_at', 'patient_contacted_by', 'contact_notes', 'issue_resolved' ]) # Log audit AuditService.log_event( event_type='survey_patient_contacted', description=f"Patient contacted for negative survey by {user.get_full_name()}", user=user, content_object=survey, metadata={ 'contact_notes': contact_notes, 'issue_resolved': issue_resolved, 'survey_score': float(survey.total_score) if survey.total_score else None } ) status = "resolved" if issue_resolved else "discussed" messages.success(request, f"Patient contact logged successfully. Issue marked as {status}.") except Exception as e: messages.error(request, f"Error logging patient contact: {str(e)}") return redirect('surveys:instance_detail', pk=pk) @login_required @require_http_methods(["POST"]) def survey_send_satisfaction_feedback(request, pk): """ Send satisfaction feedback form to patient. This creates and sends a feedback form to assess patient satisfaction with how their negative survey concerns were addressed. """ survey = get_object_or_404(SurveyInstance, pk=pk) # Check permission user = request.user if not user.is_px_admin() and not user.is_hospital_admin(): if user.hospital and survey.survey_template.hospital != user.hospital: messages.error(request, "You don't have permission to modify this survey.") return redirect('surveys:instance_detail', pk=pk) # Check if survey is negative if not survey.is_negative: messages.warning(request, "This survey is not marked as negative.") return redirect('surveys:instance_detail', pk=pk) # Check if patient was contacted if not survey.patient_contacted: messages.error(request, "Please log patient contact before sending satisfaction feedback.") return redirect('surveys:instance_detail', pk=pk) # Check if already sent if survey.satisfaction_feedback_sent: messages.warning(request, "Satisfaction feedback has already been sent for this survey.") return redirect('surveys:instance_detail', pk=pk) try: # Trigger async task to send satisfaction feedback send_satisfaction_feedback.delay(str(survey.id), str(user.id)) messages.success( request, "Satisfaction feedback form is being sent to the patient. " "They will receive a link to provide their feedback." ) except Exception as e: messages.error(request, f"Error sending satisfaction feedback: {str(e)}") return redirect('surveys:instance_detail', pk=pk)