""" Integrations views and viewsets """ import logging from rest_framework import status, views, viewsets from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from apps.accounts.permissions import IsPXAdmin from apps.core.services import AuditService from .models import EventMapping, InboundEvent, IntegrationConfig from .serializers import ( EventMappingSerializer, HISPatientDataSerializer, IntegrationConfigSerializer, ) from .services.his_adapter import HISAdapter logger = logging.getLogger('apps.integrations') class HISPatientDataView(views.APIView): """ API View for receiving complete HIS patient data. This replaces the old event-based approach. HIS systems send complete patient data including demographics and visit timeline. The system determines survey type from PatientType and sends appropriate survey via SMS. POST /api/integrations/events/ - Send complete HIS patient data Request Format: { "FetchPatientDataTimeStampList": [{...patient demographics...}], "FetchPatientDataTimeStampVisitDataList": [ {"Type": "Consultation", "BillDate": "05-Jun-2025 11:06"}, ... ], "Code": 200, "Status": "Success" } PatientType Codes: - "1" → Inpatient Survey - "2" or "O" → OPD Survey - "3" or "E" → EMS Survey - "4" or "D" → Day Case Survey Response Format: { "success": true, "message": "Patient data processed successfully", "patient": { "id": 123, "mrn": "878943", "name": "AFAF NASSER ALRAZoooOOQ" }, "patient_type": "1", "survey": { "id": 456, "status": "SENT", "survey_url": "https://..." }, "survey_sent": true } """ permission_classes = [] # Allow public access for HIS integration def post(self, request): """Process HIS patient data and create/send survey""" # Validate HIS data format serializer = HISPatientDataSerializer(data=request.data) if not serializer.is_valid(): return Response( { 'success': False, 'error': 'Invalid HIS data format', 'details': serializer.errors }, status=status.HTTP_400_BAD_REQUEST ) his_data = serializer.validated_data # Log the incoming data patient_list = his_data.get("FetchPatientDataTimeStampList", []) patient_data = patient_list[0] if patient_list else {} patient_id = patient_data.get("PatientID", "Unknown") patient_type = patient_data.get("PatientType", "Unknown") logger.info( f"HIS patient data received: PatientID={patient_id}, " f"PatientType={patient_type}" ) # Process HIS data using HISAdapter result = HISAdapter.process_his_data(his_data) # Create audit log if result['success']: AuditService.log_from_request( event_type='his_integration', description=( f"HIS patient data processed: PatientID={patient_id}, " f"PatientType={patient_type}, " f"Survey Sent={result.get('survey_sent', False)}" ), request=request, metadata={ 'patient_id': patient_id, 'patient_type': patient_type, 'survey_sent': result.get('survey_sent', False), 'survey_id': result.get('survey').id if result.get('survey') else None } ) # Prepare response response_data = { 'success': result['success'], 'message': result['message'] } # Add patient info if available if result.get('patient'): patient = result['patient'] response_data['patient'] = { 'id': patient.id, 'mrn': patient.mrn, 'name': f"{patient.first_name} {patient.last_name}".strip() } response_data['patient_type'] = result.get('patient_type') # Add survey info if available if result.get('survey'): survey = result['survey'] response_data['survey'] = { 'id': survey.id, 'status': survey.status, 'survey_url': survey.get_survey_url() } response_data['survey_sent'] = result.get('survey_sent', False) # Return appropriate status code if result['success']: status_code = status.HTTP_200_OK else: status_code = status.HTTP_400_BAD_REQUEST return Response(response_data, status=status_code) class InboundEventViewSet(viewsets.ModelViewSet): """ Legacy ViewSet for Inbound Events (DEPRECATED). This viewset is kept for backward compatibility but is no longer the primary integration method. Use HISPatientDataView instead. """ queryset = InboundEvent.objects.all() permission_classes = [IsAuthenticated, IsPXAdmin] filterset_fields = ['status', 'source_system', 'event_code', 'encounter_id'] search_fields = ['encounter_id', 'patient_identifier', 'event_code'] ordering_fields = ['received_at', 'processed_at'] ordering = ['-received_at'] def get_serializer_class(self): """Return appropriate serializer based on action""" from .serializers import ( InboundEventSerializer, InboundEventListSerializer, ) if self.action == 'list': return InboundEventListSerializer return InboundEventSerializer @action(detail=False, methods=['post'], permission_classes=[IsPXAdmin]) def bulk_create(self, request): """ Bulk create events. DEPRECATED: This endpoint is kept for backward compatibility. Use HISPatientDataView for new integrations. """ from .serializers import InboundEventCreateSerializer events_data = request.data.get('events', []) if not events_data: return Response( {'error': 'No events provided'}, status=status.HTTP_400_BAD_REQUEST ) created_events = [] errors = [] for event_data in events_data: serializer = InboundEventCreateSerializer(data=event_data) if serializer.is_valid(): event = serializer.save() created_events.append(event) else: errors.append({ 'data': event_data, 'errors': serializer.errors }) return Response({ 'created': len(created_events), 'failed': len(errors), 'errors': errors, 'message': 'This endpoint is deprecated. Use HISPatientDataView for new integrations.' }, status=status.HTTP_201_CREATED if created_events else status.HTTP_400_BAD_REQUEST) class IntegrationConfigViewSet(viewsets.ModelViewSet): """ ViewSet for Integration Configurations. Permissions: - Only PX Admins can manage integration configurations """ queryset = IntegrationConfig.objects.all() serializer_class = IntegrationConfigSerializer permission_classes = [IsPXAdmin] filterset_fields = ['source_system', 'is_active'] search_fields = ['name', 'description'] ordering_fields = ['name', 'created_at'] ordering = ['name'] def get_queryset(self): return super().get_queryset().prefetch_related('event_mappings') class EventMappingViewSet(viewsets.ModelViewSet): """ ViewSet for Event Mappings. Permissions: - Only PX Admins can manage event mappings """ queryset = EventMapping.objects.all() serializer_class = EventMappingSerializer permission_classes = [IsPXAdmin] filterset_fields = ['integration_config', 'is_active'] search_fields = ['external_event_code', 'internal_event_code'] ordering_fields = ['external_event_code'] ordering = ['integration_config', 'external_event_code'] def get_queryset(self): return super().get_queryset().select_related('integration_config')