from rest_framework import viewsets, permissions, status from rest_framework.decorators import action from rest_framework.response import Response from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters from django.db.models import Q, Count, Avg from django.utils import timezone from datetime import timedelta from ..models import ( ImagingStudy, ImagingSeries, DICOMImage, RadiologyReport, ReportTemplate, ImagingOrder ) from .serializers import ( ImagingOrderSerializer, ImagingStudySerializer, ImagingSeriesSerializer, DICOMImageSerializer, RadiologyReportSerializer, ReportTemplateSerializer, RadiologyStatsSerializer, StudySchedulingSerializer, StudyCompletionSerializer, ReportDictationSerializer ) from core.utils import AuditLogger class BaseViewSet(viewsets.ModelViewSet): """Base ViewSet with common functionality""" permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] def get_queryset(self): # Filter by tenant if user has one if hasattr(self.request.user, 'tenant') and self.request.user.tenant: return self.queryset.filter(tenant=self.request.user.tenant) return self.queryset def perform_create(self, serializer): if hasattr(self.request.user, 'tenant'): serializer.save(tenant=self.request.user.tenant) else: serializer.save() class ImagingOrderViewSet(BaseViewSet): """ViewSet for ImagingOrder model""" queryset = ImagingOrder.objects.all() serializer_class = ImagingOrderSerializer filterset_fields = [ 'status', 'priority', 'modality', 'patient', 'ordering_provider', 'contrast_required', 'location' ] search_fields = [ 'order_number', 'patient__first_name', 'patient__last_name', 'patient__mrn', 'body_part', 'clinical_indication' ] ordering_fields = ['order_date', 'scheduled_date', 'priority'] ordering = ['-order_date'] @action(detail=False, methods=['get']) def pending(self, request): """Get pending orders""" queryset = self.get_queryset().filter(status='PENDING') serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) @action(detail=False, methods=['get']) def scheduled_today(self, request): """Get orders scheduled for today""" today = timezone.now().date() queryset = self.get_queryset().filter( scheduled_date__date=today, status='SCHEDULED' ) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) @action(detail=True, methods=['post']) def schedule(self, request, pk=None): """Schedule an imaging order""" order = self.get_object() serializer = StudySchedulingSerializer(data=request.data) if serializer.is_valid(): order.scheduled_date = serializer.validated_data['scheduled_date'] order.location = serializer.validated_data['location'] order.room_number = serializer.validated_data['room_number'] order.equipment_id = serializer.validated_data['equipment_id'] order.special_instructions = serializer.validated_data.get('special_instructions', '') order.status = 'SCHEDULED' order.save() # Log the action AuditLogger.log_action( user=request.user, action='ORDER_SCHEDULED', model='ImagingOrder', object_id=str(order.order_id), details={ 'order_number': order.order_number, 'scheduled_date': str(serializer.validated_data['scheduled_date']) } ) return Response({'message': 'Order scheduled successfully'}) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @action(detail=True, methods=['post']) def cancel(self, request, pk=None): """Cancel an imaging order""" order = self.get_object() reason = request.data.get('reason', '') order.status = 'CANCELLED' order.radiologist_notes = f"Cancelled: {reason}" order.save() # Log the action AuditLogger.log_action( user=request.user, action='ORDER_CANCELLED', model='ImagingOrder', object_id=str(order.order_id), details={ 'order_number': order.order_number, 'reason': reason } ) return Response({'message': 'Order cancelled successfully'}) class ImagingStudyViewSet(BaseViewSet): """ViewSet for ImagingStudy model""" queryset = ImagingStudy.objects.all() serializer_class = ImagingStudySerializer filterset_fields = [ 'status', 'modality', 'imaging_order', 'performed_by', 'contrast_used' ] search_fields = [ 'accession_number', 'study_instance_uid', 'study_description', 'imaging_order__order_number', 'imaging_order__patient__first_name', 'imaging_order__patient__last_name' ] ordering_fields = ['study_date', 'study_time'] ordering = ['-study_date', '-study_time'] @action(detail=False, methods=['get']) def in_progress(self, request): """Get studies in progress""" queryset = self.get_queryset().filter(status='IN_PROGRESS') serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) @action(detail=False, methods=['get']) def completed_today(self, request): """Get studies completed today""" today = timezone.now().date() queryset = self.get_queryset().filter( study_date=today, status='COMPLETED' ) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) @action(detail=True, methods=['post']) def start(self, request, pk=None): """Start an imaging study""" study = self.get_object() study.status = 'IN_PROGRESS' study.performed_by = request.user study.save() # Update order status study.imaging_order.status = 'IN_PROGRESS' study.imaging_order.save() # Log the action AuditLogger.log_action( user=request.user, action='STUDY_STARTED', model='ImagingStudy', object_id=str(study.study_id), details={'accession_number': study.accession_number} ) return Response({'message': 'Study started successfully'}) @action(detail=True, methods=['post']) def complete(self, request, pk=None): """Complete an imaging study""" study = self.get_object() serializer = StudyCompletionSerializer(data=request.data) if serializer.is_valid(): study.status = 'COMPLETED' study.contrast_used = serializer.validated_data.get('contrast_used', False) study.contrast_agent = serializer.validated_data.get('contrast_agent', '') study.contrast_volume = serializer.validated_data.get('contrast_volume') study.radiation_dose = serializer.validated_data.get('radiation_dose') study.study_comments = serializer.validated_data.get('study_comments', '') study.save() # Update order status study.imaging_order.status = 'COMPLETED' study.imaging_order.completed_date = timezone.now() study.imaging_order.save() # Log the action AuditLogger.log_action( user=request.user, action='STUDY_COMPLETED', model='ImagingStudy', object_id=str(study.study_id), details={'accession_number': study.accession_number} ) return Response({'message': 'Study completed successfully'}) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class ImagingSeriesViewSet(viewsets.ReadOnlyModelViewSet): """ViewSet for ImagingSeries model (read-only)""" queryset = ImagingSeries.objects.all() serializer_class = ImagingSeriesSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['imaging_study', 'modality', 'body_part_examined'] search_fields = ['series_instance_uid', 'series_description'] ordering_fields = ['series_number', 'series_date', 'series_time'] ordering = ['series_number'] def get_queryset(self): if hasattr(self.request.user, 'tenant') and self.request.user.tenant: return self.queryset.filter(tenant=self.request.user.tenant) return self.queryset class DICOMImageViewSet(viewsets.ReadOnlyModelViewSet): """ViewSet for DICOMImage model (read-only)""" queryset = DICOMImage.objects.all() serializer_class = DICOMImageSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['imaging_series'] search_fields = ['sop_instance_uid'] ordering_fields = ['instance_number', 'slice_location'] ordering = ['instance_number'] def get_queryset(self): if hasattr(self.request.user, 'tenant') and self.request.user.tenant: return self.queryset.filter(tenant=self.request.user.tenant) return self.queryset class ReportTemplateViewSet(BaseViewSet): """ViewSet for ReportTemplate model""" queryset = ReportTemplate.objects.all() serializer_class = ReportTemplateSerializer filterset_fields = ['modality', 'body_part', 'procedure_type', 'is_active'] search_fields = ['name', 'description', 'template_content'] ordering_fields = ['name', 'modality'] ordering = ['name'] @action(detail=False, methods=['get']) def by_modality(self, request): """Get templates by modality""" modality = request.query_params.get('modality') if modality: queryset = self.get_queryset().filter(modality=modality, is_active=True) else: queryset = self.get_queryset().filter(is_active=True) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) class RadiologyReportViewSet(BaseViewSet): """ViewSet for RadiologyReport model""" queryset = RadiologyReport.objects.all() serializer_class = RadiologyReportSerializer filterset_fields = [ 'status', 'radiologist', 'imaging_study', 'critical_result' ] search_fields = [ 'imaging_study__accession_number', 'findings', 'impression', 'imaging_study__imaging_order__patient__first_name', 'imaging_study__imaging_order__patient__last_name' ] ordering_fields = ['dictated_date', 'signed_date'] ordering = ['-dictated_date'] @action(detail=False, methods=['post']) def dictate(self, request): """Dictate a radiology report""" serializer = ReportDictationSerializer(data=request.data) if serializer.is_valid(): study = ImagingStudy.objects.get(id=serializer.validated_data['study_id']) template_id = serializer.validated_data.get('template_id') template = None if template_id: template = ReportTemplate.objects.get(id=template_id) # Create report report = RadiologyReport.objects.create( imaging_study=study, report_template=template, radiologist=request.user, dictated_date=timezone.now(), status='DRAFT', clinical_history=serializer.validated_data['clinical_history'], technique=serializer.validated_data['technique'], findings=serializer.validated_data['findings'], impression=serializer.validated_data['impression'], recommendations=serializer.validated_data.get('recommendations', ''), comparison_studies=serializer.validated_data.get('comparison_studies', ''), critical_result=serializer.validated_data.get('critical_result', False), tenant=getattr(request.user, 'tenant', None) ) # Log the action AuditLogger.log_action( user=request.user, action='REPORT_DICTATED', model='RadiologyReport', object_id=str(report.report_id), details={ 'study_accession': study.accession_number, 'critical_result': report.critical_result } ) return Response({ 'message': 'Report dictated successfully', 'report': RadiologyReportSerializer(report).data }) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @action(detail=False, methods=['get']) def pending_signature(self, request): """Get reports pending signature""" queryset = self.get_queryset().filter(status='TRANSCRIBED') serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) @action(detail=False, methods=['get']) def critical_results(self, request): """Get critical results""" queryset = self.get_queryset().filter( critical_result=True, status='SIGNED' ) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) @action(detail=True, methods=['post']) def sign(self, request, pk=None): """Sign a radiology report""" report = self.get_object() if report.status != 'TRANSCRIBED': return Response( {'error': 'Report must be transcribed before signing'}, status=status.HTTP_400_BAD_REQUEST ) report.status = 'SIGNED' report.signed_date = timezone.now() report.save() # Log the action AuditLogger.log_action( user=request.user, action='REPORT_SIGNED', model='RadiologyReport', object_id=str(report.report_id), details={ 'study_accession': report.imaging_study.accession_number, 'critical_result': report.critical_result } ) return Response({'message': 'Report signed successfully'}) @action(detail=True, methods=['post']) def add_addendum(self, request, pk=None): """Add addendum to a report""" report = self.get_object() addendum_text = request.data.get('addendum', '') if not addendum_text: return Response( {'error': 'Addendum text is required'}, status=status.HTTP_400_BAD_REQUEST ) current_addendum = report.addendum or '' timestamp = timezone.now().strftime('%Y-%m-%d %H:%M:%S') new_addendum = f"{current_addendum}\n\nADDENDUM ({timestamp}):\n{addendum_text}" report.addendum = new_addendum report.save() # Log the action AuditLogger.log_action( user=request.user, action='REPORT_ADDENDUM_ADDED', model='RadiologyReport', object_id=str(report.report_id), details={'study_accession': report.imaging_study.accession_number} ) return Response({'message': 'Addendum added successfully'}) class RadiologyStatsViewSet(viewsets.ViewSet): """ViewSet for radiology statistics""" permission_classes = [permissions.IsAuthenticated] @action(detail=False, methods=['get']) def dashboard(self, request): """Get radiology dashboard statistics""" tenant_filter = {} if hasattr(request.user, 'tenant') and request.user.tenant: tenant_filter['tenant'] = request.user.tenant today = timezone.now().date() # Order statistics orders = ImagingOrder.objects.filter(**tenant_filter) total_orders = orders.count() pending_orders = orders.filter(status='PENDING').count() completed_today = orders.filter( completed_date__date=today ).count() # Study statistics studies = ImagingStudy.objects.filter(**tenant_filter) studies_performed = studies.filter( study_date=today ).count() # Report statistics reports = RadiologyReport.objects.filter(**tenant_filter) reports_pending = reports.filter(status__in=['DRAFT', 'TRANSCRIBED']).count() critical_results = reports.filter( critical_result=True, signed_date__date=today ).count() # Turnaround time completed_reports = reports.filter( signed_date__date=today ) turnaround_times = [] for report in completed_reports: if report.imaging_study.study_date and report.signed_date: study_datetime = timezone.make_aware( timezone.datetime.combine( report.imaging_study.study_date, report.imaging_study.study_time or timezone.datetime.min.time() ) ) delta = report.signed_date - study_datetime turnaround_times.append(delta.total_seconds() / 3600) # hours average_turnaround = sum(turnaround_times) / len(turnaround_times) if turnaround_times else 0 # Equipment utilization (mock data) equipment_utilization = { 'CT Scanner 1': 85.5, 'MRI 1': 92.3, 'X-Ray Room 1': 67.8, 'Ultrasound 1': 78.9 } # Modality breakdown modality_breakdown = orders.values('modality').annotate( count=Count('id') ).order_by('-count') # Status breakdown status_breakdown = orders.values('status').annotate( count=Count('id') ).order_by('-count') stats = { 'total_orders': total_orders, 'pending_orders': pending_orders, 'completed_today': completed_today, 'studies_performed': studies_performed, 'reports_pending': reports_pending, 'critical_results': critical_results, 'average_turnaround': round(average_turnaround, 2), 'equipment_utilization': equipment_utilization, 'modality_breakdown': {item['modality']: item['count'] for item in modality_breakdown}, 'status_breakdown': {item['status']: item['count'] for item in status_breakdown} } serializer = RadiologyStatsSerializer(stats) return Response(serializer.data)