""" Surveys views and viewsets """ from django.shortcuts import get_object_or_404 from django.utils import timezone from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from apps.accounts.permissions import IsPXAdminOrHospitalAdmin from apps.core.services import AuditService from .models import SurveyInstance, SurveyQuestion, SurveyResponse, SurveyTemplate from .serializers import ( PublicSurveySerializer, SurveyInstanceSerializer, SurveyQuestionSerializer, SurveyResponseSerializer, SurveySubmissionSerializer, SurveyTemplateSerializer, ) class SurveyTemplateViewSet(viewsets.ModelViewSet): """ ViewSet for Survey Templates. Permissions: - PX Admins and Hospital Admins can manage templates - Others can view templates """ queryset = SurveyTemplate.objects.all() serializer_class = SurveyTemplateSerializer permission_classes = [IsAuthenticated] filterset_fields = ['survey_type', 'hospital', 'is_active', 'hospital__organization'] search_fields = ['name', 'name_ar', 'description'] ordering_fields = ['name', 'created_at'] ordering = ['hospital', 'name'] def get_queryset(self): """Filter templates based on user role""" queryset = super().get_queryset().select_related('hospital').prefetch_related('questions') user = self.request.user # PX Admins see all templates if user.is_px_admin(): return queryset # Hospital Admins see templates for their hospital if user.is_hospital_admin() and user.hospital: return queryset.filter(hospital=user.hospital) # Others see templates for their hospital if user.hospital: return queryset.filter(hospital=user.hospital) return queryset.none() class SurveyQuestionViewSet(viewsets.ModelViewSet): """ ViewSet for Survey Questions. Permissions: - PX Admins and Hospital Admins can manage questions """ queryset = SurveyQuestion.objects.all() serializer_class = SurveyQuestionSerializer permission_classes = [IsAuthenticated, IsPXAdminOrHospitalAdmin] filterset_fields = ['survey_template', 'question_type', 'is_required'] search_fields = ['text', 'text_ar'] ordering_fields = ['order', 'created_at'] ordering = ['survey_template', 'order'] def get_queryset(self): queryset = super().get_queryset().select_related('survey_template') user = self.request.user # PX Admins see all questions if user.is_px_admin(): return queryset # Hospital Admins see questions for their hospital if user.is_hospital_admin() and user.hospital: return queryset.filter(survey_template__hospital=user.hospital) return queryset.none() class SurveyInstanceViewSet(viewsets.ModelViewSet): """ ViewSet for Survey Instances. Permissions: - All authenticated users can view survey instances - PX Admins and Hospital Admins can create/manage instances """ queryset = SurveyInstance.objects.all() serializer_class = SurveyInstanceSerializer permission_classes = [IsAuthenticated] filterset_fields = [ 'survey_template', 'patient', 'status', 'delivery_channel', 'is_negative', 'journey_instance', 'survey_template__hospital__organization' ] search_fields = ['patient__mrn', 'patient__first_name', 'patient__last_name', 'encounter_id'] ordering_fields = ['sent_at', 'completed_at', 'created_at'] ordering = ['-created_at'] def get_queryset(self): """Filter survey instances based on user role""" queryset = super().get_queryset().select_related( 'survey_template', 'patient', 'journey_instance' ).prefetch_related('responses') user = self.request.user # PX Admins see all survey instances if user.is_px_admin(): return queryset # Hospital Admins see instances for their hospital if user.is_hospital_admin() and user.hospital: return queryset.filter(survey_template__hospital=user.hospital) # Others see instances for their hospital if user.hospital: return queryset.filter(survey_template__hospital=user.hospital) return queryset.none() @action(detail=True, methods=['post']) def resend(self, request, pk=None): """Resend survey invitation""" survey_instance = self.get_object() if survey_instance.status == 'completed': return Response( {'error': 'Cannot resend completed survey'}, status=status.HTTP_400_BAD_REQUEST ) # Queue survey send task from apps.surveys.tasks import send_survey_reminder send_survey_reminder.delay(str(survey_instance.id)) return Response({'message': 'Survey invitation queued for resend'}) class SurveyResponseViewSet(viewsets.ReadOnlyModelViewSet): """ ViewSet for Survey Responses (read-only). Responses are created via the public survey submission endpoint. """ queryset = SurveyResponse.objects.all() serializer_class = SurveyResponseSerializer permission_classes = [IsAuthenticated] filterset_fields = ['survey_instance', 'question'] ordering = ['survey_instance', 'question__order'] def get_queryset(self): queryset = super().get_queryset().select_related('survey_instance', 'question') user = self.request.user # PX Admins see all responses if user.is_px_admin(): return queryset # Hospital Admins see responses for their hospital if user.is_hospital_admin() and user.hospital: return queryset.filter(survey_instance__survey_template__hospital=user.hospital) # Others see responses for their hospital if user.hospital: return queryset.filter(survey_instance__survey_template__hospital=user.hospital) return queryset.none() class PublicSurveyViewSet(viewsets.GenericViewSet): """ Public survey viewset for patient-facing survey access. No authentication required - uses secure token. """ permission_classes = [AllowAny] def retrieve(self, request, token=None): """ Get survey by access token. GET /api/surveys/public/{token}/ """ survey_instance = get_object_or_404( SurveyInstance.objects.select_related('survey_template').prefetch_related( 'survey_template__questions' ), access_token=token ) # Check if token expired if survey_instance.token_expires_at and survey_instance.token_expires_at < timezone.now(): return Response( {'error': 'Survey link has expired'}, status=status.HTTP_410_GONE ) # Check if already completed if survey_instance.status == 'completed': return Response( {'error': 'Survey already completed', 'completed_at': survey_instance.completed_at}, status=status.HTTP_410_GONE ) # Mark as opened if first time if not survey_instance.opened_at: survey_instance.opened_at = timezone.now() survey_instance.save(update_fields=['opened_at']) serializer = PublicSurveySerializer(survey_instance) return Response(serializer.data) @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/submit') def submit(self, request, token=None): """ Submit survey responses. POST /api/surveys/public/{token}/submit/ Body: { "responses": [ {"question_id": "uuid", "numeric_value": 5}, {"question_id": "uuid", "text_value": "Great service!"} ] } """ survey_instance = get_object_or_404( SurveyInstance, access_token=token ) # Check if token expired if survey_instance.token_expires_at and survey_instance.token_expires_at < timezone.now(): return Response( {'error': 'Survey link has expired'}, status=status.HTTP_410_GONE ) # Check if already completed if survey_instance.status == 'completed': return Response( {'error': 'Survey already completed'}, status=status.HTTP_400_BAD_REQUEST ) # Validate and create responses serializer = SurveySubmissionSerializer( data=request.data, context={'survey_instance': survey_instance} ) serializer.is_valid(raise_exception=True) serializer.save() return Response({ 'message': 'Survey submitted successfully', 'score': float(survey_instance.total_score) if survey_instance.total_score else None }, status=status.HTTP_201_CREATED)