256 lines
8.5 KiB
Python
256 lines
8.5 KiB
Python
"""
|
|
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') |