""" PX Action Center views and viewsets """ from django.utils import timezone from rest_framework import status, 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 PXAction, PXActionAttachment, PXActionLog, PXActionSLAConfig, RoutingRule from .serializers import ( PXActionAttachmentSerializer, PXActionListSerializer, PXActionLogSerializer, PXActionSerializer, PXActionSLAConfigSerializer, RoutingRuleSerializer, ) class PXActionViewSet(viewsets.ModelViewSet): """ ViewSet for PX Actions with workflow actions. Permissions: - All authenticated users can view actions - PX Admins and PX Staff can create/manage actions """ queryset = PXAction.objects.all() permission_classes = [IsAuthenticated] filterset_fields = [ "status", "source_type", "severity", "priority", "category", "hospital", "department", "assigned_to", "is_overdue", "requires_approval", ] search_fields = ["title", "description"] ordering_fields = ["created_at", "due_at", "severity", "escalation_level"] ordering = ["-created_at"] def get_serializer_class(self): """Use simplified serializer for list view""" if self.action == "list": return PXActionListSerializer return PXActionSerializer def get_queryset(self): """Filter actions based on user role""" queryset = ( super() .get_queryset() .select_related("hospital", "department", "assigned_to", "approved_by", "closed_by", "content_type") .prefetch_related("logs", "attachments") ) user = self.request.user # Source Users don't have access to PX Actions if user.is_source_user(): return queryset.none() if user.is_px_admin(): if hasattr(self.request, "tenant_hospital") and self.request.tenant_hospital: return queryset.filter(hospital=self.request.tenant_hospital) return queryset # Hospital Admins see actions for their hospital if user.is_hospital_admin() and user.hospital: return queryset.filter(hospital=user.hospital) # Department Managers see actions for their department if user.is_department_manager() and user.department: return queryset.filter(department=user.department) # Others see actions for their hospital if user.hospital: return queryset.filter(hospital=user.hospital) return queryset.none() def perform_create(self, serializer): """Log action creation""" action = serializer.save() from apps.px_action_center.services import RoutingRuleEvaluationService RoutingRuleEvaluationService.evaluate_and_route(action) AuditService.log_from_request( event_type="action_created", description=f"PX Action created: {action.title}", request=self.request, content_object=action, metadata={"source_type": action.source_type, "category": action.category, "severity": action.severity}, ) @action(detail=True, methods=["post"]) def assign(self, request, pk=None): """Assign action to user""" action = self.get_object() user_id = request.data.get("user_id") if not user_id: return Response({"error": "user_id is required"}, status=status.HTTP_400_BAD_REQUEST) from apps.accounts.models import User try: user = User.objects.get(id=user_id) action.assigned_to = user action.assigned_at = timezone.now() action.save(update_fields=["assigned_to", "assigned_at"]) # Create log PXActionLog.objects.create( action=action, log_type="assignment", message=f"Assigned to {user.get_full_name()}", created_by=request.user, ) AuditService.log_from_request( event_type="assignment", description=f"Action assigned to {user.get_full_name()}", request=request, content_object=action, ) return Response({"message": "Action assigned successfully"}) except User.DoesNotExist: return Response({"error": "User not found"}, status=status.HTTP_404_NOT_FOUND) @action(detail=True, methods=["post"]) def change_status(self, request, pk=None): """Change action status""" action = self.get_object() new_status = request.data.get("status") note = request.data.get("note", "") if not new_status: return Response({"error": "status is required"}, status=status.HTTP_400_BAD_REQUEST) old_status = action.status action.status = new_status # Handle status-specific logic if new_status == "pending_approval": # Check if evidence is required if action.requires_approval: evidence_count = action.attachments.filter(is_evidence=True).count() if evidence_count == 0: return Response( {"error": "Evidence is required before requesting approval"}, status=status.HTTP_400_BAD_REQUEST ) elif new_status == "approved": # Only PX Admins can approve if not request.user.is_px_admin(): return Response({"error": "Only PX Admins can approve actions"}, status=status.HTTP_403_FORBIDDEN) action.approved_by = request.user action.approved_at = timezone.now() elif new_status == "closed": action.closed_at = timezone.now() action.closed_by = request.user action.save() # Create log PXActionLog.objects.create( action=action, log_type="status_change", message=note or f"Status changed from {old_status} to {new_status}", created_by=request.user, old_status=old_status, new_status=new_status, ) AuditService.log_from_request( event_type="status_change", description=f"Action status changed from {old_status} to {new_status}", request=request, content_object=action, metadata={"old_status": old_status, "new_status": new_status}, ) return Response({"message": "Status updated successfully"}) @action(detail=True, methods=["post"]) def add_note(self, request, pk=None): """Add note to action""" action = self.get_object() note = request.data.get("note") if not note: return Response({"error": "note is required"}, status=status.HTTP_400_BAD_REQUEST) # Create log log = PXActionLog.objects.create(action=action, log_type="note", message=note, created_by=request.user) serializer = PXActionLogSerializer(log) return Response(serializer.data, status=status.HTTP_201_CREATED) @action(detail=True, methods=["post"]) def escalate(self, request, pk=None): """Manually escalate action""" action = self.get_object() # Queue escalation task from apps.px_action_center.tasks import escalate_action escalate_action.delay(str(action.id)) return Response({"message": "Action queued for escalation"}) @action(detail=True, methods=["post"]) def reject(self, request, pk=None): """Reject action and reopen with reason""" action = self.get_object() reason = request.data.get("reason") if not reason: return Response({"error": "reason is required"}, status=status.HTTP_400_BAD_REQUEST) if action.status not in ["pending_approval", "approved"]: return Response( {"error": "Can only reject actions in pending_approval or approved status"}, status=status.HTTP_400_BAD_REQUEST, ) old_status = action.status action.status = "open" action.rejected_at = timezone.now() action.rejected_by = request.user action.rejection_reason = reason action.save() PXActionLog.objects.create( action=action, log_type="rejection", message=f"Action rejected: {reason}", created_by=request.user, old_status=old_status, new_status="open", ) AuditService.log_from_request( event_type="action_rejected", description=f"Action rejected and reopened: {action.title}", request=request, content_object=action, metadata={"old_status": old_status, "reason": reason}, ) return Response({"message": "Action rejected and reopened successfully"}) class PXActionSLAConfigViewSet(viewsets.ModelViewSet): """ ViewSet for PX Action SLA Configurations. Permissions: - Only PX Admins can manage SLA configurations """ queryset = PXActionSLAConfig.objects.all() serializer_class = PXActionSLAConfigSerializer permission_classes = [IsPXAdmin] filterset_fields = ["hospital", "department", "is_active"] search_fields = ["name"] ordering = ["hospital", "name"] def get_queryset(self): return super().get_queryset().select_related("hospital", "department") class RoutingRuleViewSet(viewsets.ModelViewSet): """ ViewSet for Routing Rules. Permissions: - Only PX Admins can manage routing rules """ queryset = RoutingRule.objects.all() serializer_class = RoutingRuleSerializer permission_classes = [IsPXAdmin] filterset_fields = ["source_type", "severity", "hospital", "is_active"] search_fields = ["name", "description"] ordering = ["-priority", "name"] def get_queryset(self): return super().get_queryset().select_related("hospital", "department", "assign_to_user", "assign_to_department")