""" Views for Collaboration & War Rooms module """ from rest_framework import viewsets, status, permissions from rest_framework.decorators import action from rest_framework.response import Response from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import SearchFilter, OrderingFilter from django.db.models import Q from django.utils import timezone from ..models import ( WarRoom, ConferenceBridge, IncidentCommandRole, TimelineEvent, WarRoomMessage, IncidentDecision, MessageReaction, ChatFile, ChatCommand, ChatBot ) from ..serializers.collaboration import ( WarRoomSerializer, ConferenceBridgeSerializer, IncidentCommandRoleSerializer, TimelineEventSerializer, WarRoomMessageSerializer, IncidentDecisionSerializer, WarRoomSummarySerializer, TimelineEventSummarySerializer, MessageReactionSerializer, ChatFileSerializer, ChatCommandSerializer, ChatBotSerializer ) from incident_intelligence.models import Incident class WarRoomViewSet(viewsets.ModelViewSet): """ViewSet for WarRoom model""" queryset = WarRoom.objects.all() serializer_class = WarRoomSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['status', 'privacy_level', 'incident__severity'] search_fields = ['name', 'description', 'incident__title'] ordering_fields = ['created_at', 'last_activity', 'message_count'] ordering = ['-created_at'] def get_queryset(self): """Filter war rooms based on user access""" queryset = super().get_queryset() user = self.request.user # Filter by user access accessible_war_rooms = [] for war_room in queryset: if war_room.can_user_access(user): accessible_war_rooms.append(war_room.id) return queryset.filter(id__in=accessible_war_rooms) def get_serializer_class(self): """Return appropriate serializer based on action""" if self.action == 'list': return WarRoomSummarySerializer return WarRoomSerializer @action(detail=True, methods=['post']) def add_participant(self, request, pk=None): """Add a participant to the war room""" war_room = 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 ) try: from django.contrib.auth import get_user_model User = get_user_model() user = User.objects.get(id=user_id) if war_room.can_user_access(user): war_room.add_participant(user) return Response({'message': 'Participant added successfully'}) else: return Response( {'error': 'User does not have access to this war room'}, status=status.HTTP_403_FORBIDDEN ) except User.DoesNotExist: return Response( {'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND ) @action(detail=True, methods=['post']) def remove_participant(self, request, pk=None): """Remove a participant from the war room""" war_room = 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 ) try: from django.contrib.auth import get_user_model User = get_user_model() user = User.objects.get(id=user_id) war_room.remove_participant(user) return Response({'message': 'Participant removed successfully'}) except User.DoesNotExist: return Response( {'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND ) @action(detail=True, methods=['get']) def messages(self, request, pk=None): """Get messages for a war room""" war_room = self.get_object() messages = war_room.messages.all().order_by('created_at') # Apply pagination page = self.paginate_queryset(messages) if page is not None: serializer = WarRoomMessageSerializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = WarRoomMessageSerializer(messages, many=True) return Response(serializer.data) @action(detail=True, methods=['get']) def pinned_messages(self, request, pk=None): """Get pinned messages for a war room""" war_room = self.get_object() pinned_messages = war_room.messages.filter(is_pinned=True).order_by('-pinned_at') serializer = WarRoomMessageSerializer(pinned_messages, many=True) return Response(serializer.data) @action(detail=True, methods=['post']) def create_chat_room(self, request, pk=None): """Auto-create chat room for incident""" incident = self.get_object() # Check if war room already exists for this incident existing_war_room = WarRoom.objects.filter(incident=incident).first() if existing_war_room: return Response( {'message': 'War room already exists for this incident', 'war_room_id': existing_war_room.id}, status=status.HTTP_200_OK ) # Create new war room war_room = WarRoom.objects.create( name=f"Incident Chat - {incident.title}", description=f"Chat room for incident: {incident.title}", incident=incident, created_by=request.user, privacy_level='PRIVATE' ) # Add incident reporter and assignee to war room if incident.reporter: war_room.add_participant(incident.reporter) if incident.assigned_to: war_room.add_participant(incident.assigned_to) # Create timeline event TimelineEvent.create_system_event( incident=incident, event_type='WAR_ROOM_CREATED', title='War Room Created', description=f'War room "{war_room.name}" was automatically created for incident collaboration', event_data={'war_room_id': str(war_room.id)} ) serializer = WarRoomSerializer(war_room) return Response(serializer.data, status=status.HTTP_201_CREATED) class ConferenceBridgeViewSet(viewsets.ModelViewSet): """ViewSet for ConferenceBridge model""" queryset = ConferenceBridge.objects.all() serializer_class = ConferenceBridgeSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['bridge_type', 'status', 'incident__severity'] search_fields = ['name', 'description', 'incident__title'] ordering_fields = ['scheduled_start', 'created_at'] ordering = ['-scheduled_start'] def get_queryset(self): """Filter conference bridges based on user access""" queryset = super().get_queryset() user = self.request.user # Filter by user access accessible_conferences = [] for conference in queryset: if conference.can_user_join(user): accessible_conferences.append(conference.id) return queryset.filter(id__in=accessible_conferences) @action(detail=True, methods=['post']) def join_conference(self, request, pk=None): """Join a conference""" conference = self.get_object() user = request.user if conference.can_user_join(user): conference.add_participant(user) return Response({'message': 'Successfully joined conference'}) else: return Response( {'error': 'You do not have permission to join this conference'}, status=status.HTTP_403_FORBIDDEN ) @action(detail=True, methods=['post']) def start_conference(self, request, pk=None): """Start a conference""" conference = self.get_object() if conference.status == 'SCHEDULED': conference.status = 'ACTIVE' conference.actual_start = timezone.now() conference.save() return Response({'message': 'Conference started successfully'}) else: return Response( {'error': 'Conference cannot be started in current status'}, status=status.HTTP_400_BAD_REQUEST ) @action(detail=True, methods=['post']) def end_conference(self, request, pk=None): """End a conference""" conference = self.get_object() if conference.status == 'ACTIVE': conference.status = 'ENDED' conference.actual_end = timezone.now() conference.save() return Response({'message': 'Conference ended successfully'}) else: return Response( {'error': 'Conference cannot be ended in current status'}, status=status.HTTP_400_BAD_REQUEST ) class IncidentCommandRoleViewSet(viewsets.ModelViewSet): """ViewSet for IncidentCommandRole model""" queryset = IncidentCommandRole.objects.all() serializer_class = IncidentCommandRoleSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['role_type', 'status', 'incident__severity'] search_fields = ['incident__title', 'assigned_user__username'] ordering_fields = ['assigned_at', 'created_at'] ordering = ['-assigned_at'] def get_queryset(self): """Filter command roles based on user access""" queryset = super().get_queryset() user = self.request.user # Filter by incident access accessible_incidents = [] for role in queryset: if role.incident.is_accessible_by_user(user): accessible_incidents.append(role.incident.id) return queryset.filter(incident_id__in=accessible_incidents) @action(detail=True, methods=['post']) def reassign_role(self, request, pk=None): """Reassign a command role to a new user""" command_role = self.get_object() new_user_id = request.data.get('new_user_id') notes = request.data.get('notes', '') if not new_user_id: return Response( {'error': 'new_user_id is required'}, status=status.HTTP_400_BAD_REQUEST ) try: from django.contrib.auth import get_user_model User = get_user_model() new_user = User.objects.get(id=new_user_id) command_role.reassign_role(new_user, request.user, notes) return Response({'message': 'Role reassigned successfully'}) except User.DoesNotExist: return Response( {'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND ) class TimelineEventViewSet(viewsets.ReadOnlyModelViewSet): """ViewSet for TimelineEvent model (read-only)""" queryset = TimelineEvent.objects.all() serializer_class = TimelineEventSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['event_type', 'source_type', 'is_critical_event', 'incident__severity'] search_fields = ['title', 'description', 'incident__title'] ordering_fields = ['event_time', 'created_at'] ordering = ['event_time'] def get_serializer_class(self): """Return appropriate serializer based on action""" if self.action == 'list': return TimelineEventSummarySerializer return TimelineEventSerializer def get_queryset(self): """Filter timeline events based on user access""" queryset = super().get_queryset() user = self.request.user # Filter by incident access accessible_incidents = [] for event in queryset: if event.incident.is_accessible_by_user(user): accessible_incidents.append(event.incident.id) return queryset.filter(incident_id__in=accessible_incidents) @action(detail=False, methods=['get']) def critical_events(self, request): """Get critical events for postmortem analysis""" queryset = self.get_queryset().filter(is_critical_event=True) # Apply pagination page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) class WarRoomMessageViewSet(viewsets.ModelViewSet): """ViewSet for WarRoomMessage model""" queryset = WarRoomMessage.objects.all() serializer_class = WarRoomMessageSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['message_type', 'war_room', 'sender', 'is_pinned'] search_fields = ['content', 'sender_name'] ordering_fields = ['created_at', 'pinned_at'] ordering = ['created_at'] def get_queryset(self): """Filter messages based on war room access""" queryset = super().get_queryset() user = self.request.user # Filter by war room access accessible_war_rooms = [] for message in queryset: if message.war_room.can_user_access(user): accessible_war_rooms.append(message.war_room.id) return queryset.filter(war_room_id__in=accessible_war_rooms) @action(detail=True, methods=['post']) def pin_message(self, request, pk=None): """Pin a message""" message = self.get_object() message.pin_message(request.user) # Create timeline event TimelineEvent.create_user_event( incident=message.war_room.incident, user=request.user, event_type='MANUAL_EVENT', title='Message Pinned', description=f'Message "{message.content[:50]}..." was pinned by {request.user.username}', event_data={'message_id': str(message.id), 'action': 'pinned'} ) return Response({'message': 'Message pinned successfully'}) @action(detail=True, methods=['post']) def unpin_message(self, request, pk=None): """Unpin a message""" message = self.get_object() message.unpin_message() return Response({'message': 'Message unpinned successfully'}) @action(detail=True, methods=['post']) def add_reaction(self, request, pk=None): """Add a reaction to a message""" message = self.get_object() emoji = request.data.get('emoji') if not emoji: return Response( {'error': 'emoji is required'}, status=status.HTTP_400_BAD_REQUEST ) reaction = message.add_reaction(request.user, emoji) serializer = MessageReactionSerializer(reaction) return Response(serializer.data) @action(detail=True, methods=['post']) def remove_reaction(self, request, pk=None): """Remove a reaction from a message""" message = self.get_object() emoji = request.data.get('emoji') if not emoji: return Response( {'error': 'emoji is required'}, status=status.HTTP_400_BAD_REQUEST ) message.remove_reaction(request.user, emoji) return Response({'message': 'Reaction removed successfully'}) @action(detail=True, methods=['post']) def execute_command(self, request, pk=None): """Execute a ChatOps command""" message = self.get_object() command_text = request.data.get('command_text') if not command_text: return Response( {'error': 'command_text is required'}, status=status.HTTP_400_BAD_REQUEST ) # Parse command command_type = self._parse_command_type(command_text) parameters = self._parse_command_parameters(command_text) # Create chat command chat_command = ChatCommand.objects.create( message=message, command_type=command_type, command_text=command_text, parameters=parameters ) # Execute command result = chat_command.execute_command(request.user) serializer = ChatCommandSerializer(chat_command) return Response(serializer.data) def _parse_command_type(self, command_text): """Parse command type from command text""" command_text = command_text.lower().strip() if command_text.startswith('/status'): return 'STATUS' elif command_text.startswith('/runbook'): return 'RUNBOOK' elif command_text.startswith('/escalate'): return 'ESCALATE' elif command_text.startswith('/assign'): return 'ASSIGN' elif command_text.startswith('/update'): return 'UPDATE' else: return 'CUSTOM' def _parse_command_parameters(self, command_text): """Parse command parameters from command text""" parts = command_text.split() if len(parts) > 1: return {'args': parts[1:]} return {} class IncidentDecisionViewSet(viewsets.ModelViewSet): """ViewSet for IncidentDecision model""" queryset = IncidentDecision.objects.all() serializer_class = IncidentDecisionSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['decision_type', 'status', 'incident__severity'] search_fields = ['title', 'description', 'incident__title'] ordering_fields = ['created_at', 'approved_at', 'implemented_at'] ordering = ['-created_at'] def get_queryset(self): """Filter decisions based on incident access""" queryset = super().get_queryset() user = self.request.user # Filter by incident access accessible_incidents = [] for decision in queryset: if decision.incident.is_accessible_by_user(user): accessible_incidents.append(decision.incident.id) return queryset.filter(incident_id__in=accessible_incidents) @action(detail=True, methods=['post']) def approve_decision(self, request, pk=None): """Approve a decision""" decision = self.get_object() if decision.status == 'PENDING': decision.approve(request.user) return Response({'message': 'Decision approved successfully'}) else: return Response( {'error': 'Decision cannot be approved in current status'}, status=status.HTTP_400_BAD_REQUEST ) @action(detail=True, methods=['post']) def implement_decision(self, request, pk=None): """Mark a decision as implemented""" decision = self.get_object() notes = request.data.get('notes', '') if decision.status == 'APPROVED': decision.implement(request.user, notes) return Response({'message': 'Decision implemented successfully'}) else: return Response( {'error': 'Decision must be approved before implementation'}, status=status.HTTP_400_BAD_REQUEST ) class MessageReactionViewSet(viewsets.ModelViewSet): """ViewSet for MessageReaction model""" queryset = MessageReaction.objects.all() serializer_class = MessageReactionSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['message', 'user', 'emoji'] ordering_fields = ['created_at'] ordering = ['created_at'] class ChatFileViewSet(viewsets.ModelViewSet): """ViewSet for ChatFile model""" queryset = ChatFile.objects.all() serializer_class = ChatFileSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['message', 'file_type', 'data_classification'] search_fields = ['filename', 'original_filename'] ordering_fields = ['uploaded_at', 'file_size'] ordering = ['-uploaded_at'] def get_queryset(self): """Filter files based on message access""" queryset = super().get_queryset() user = self.request.user # Filter by message access accessible_messages = [] for file_obj in queryset: if file_obj.message.war_room.can_user_access(user): accessible_messages.append(file_obj.message.id) return queryset.filter(message_id__in=accessible_messages) @action(detail=True, methods=['post']) def log_access(self, request, pk=None): """Log file access for audit trail""" file_obj = self.get_object() file_obj.log_access(request.user) return Response({'message': 'Access logged successfully'}) class ChatCommandViewSet(viewsets.ReadOnlyModelViewSet): """ViewSet for ChatCommand model (read-only)""" queryset = ChatCommand.objects.all() serializer_class = ChatCommandSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['command_type', 'execution_status', 'executed_by'] search_fields = ['command_text'] ordering_fields = ['executed_at', 'created_at'] ordering = ['-executed_at'] def get_queryset(self): """Filter commands based on message access""" queryset = super().get_queryset() user = self.request.user # Filter by message access accessible_messages = [] for command in queryset: if command.message.war_room.can_user_access(user): accessible_messages.append(command.message.id) return queryset.filter(message_id__in=accessible_messages) class ChatBotViewSet(viewsets.ModelViewSet): """ViewSet for ChatBot model""" queryset = ChatBot.objects.all() serializer_class = ChatBotSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['bot_type', 'is_active'] search_fields = ['name', 'description'] ordering_fields = ['name', 'created_at'] ordering = ['name'] @action(detail=True, methods=['post']) def generate_response(self, request, pk=None): """Generate AI response to a message""" bot = self.get_object() message_id = request.data.get('message_id') if not message_id: return Response( {'error': 'message_id is required'}, status=status.HTTP_400_BAD_REQUEST ) try: message = WarRoomMessage.objects.get(id=message_id) response = bot.generate_response(message, request.data.get('context', {})) return Response(response) except WarRoomMessage.DoesNotExist: return Response( {'error': 'Message not found'}, status=status.HTTP_404_NOT_FOUND )