""" 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__hospital=user.hospital) # Others see responses for their hospital if user.hospital: return queryset.filter(survey_instance__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, )