Updates
This commit is contained in:
0
ETB-API/compliance_governance/views/__init__.py
Normal file
0
ETB-API/compliance_governance/views/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
732
ETB-API/compliance_governance/views/compliance.py
Normal file
732
ETB-API/compliance_governance/views/compliance.py
Normal file
@@ -0,0 +1,732 @@
|
||||
"""
|
||||
Views for Compliance & Governance API endpoints
|
||||
"""
|
||||
from rest_framework import viewsets, status, permissions
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django_filters import rest_framework as filters
|
||||
from django.db.models import Q, Count, Avg
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
|
||||
from ..models import (
|
||||
RegulatoryFramework,
|
||||
ComplianceRequirement,
|
||||
RegulatoryWorkflow,
|
||||
WorkflowInstance,
|
||||
EvidenceCollection,
|
||||
RetentionPolicy,
|
||||
ExportRequest,
|
||||
ComplianceReport,
|
||||
LegalHold,
|
||||
)
|
||||
from ..serializers.compliance import (
|
||||
RegulatoryFrameworkSerializer,
|
||||
ComplianceRequirementSerializer,
|
||||
ComplianceRequirementDetailSerializer,
|
||||
RegulatoryWorkflowSerializer,
|
||||
WorkflowInstanceSerializer,
|
||||
WorkflowInstanceDetailSerializer,
|
||||
EvidenceCollectionSerializer,
|
||||
EvidenceCollectionDetailSerializer,
|
||||
RetentionPolicySerializer,
|
||||
ExportRequestSerializer,
|
||||
ExportRequestDetailSerializer,
|
||||
ComplianceReportSerializer,
|
||||
ComplianceReportDetailSerializer,
|
||||
LegalHoldSerializer,
|
||||
LegalHoldDetailSerializer,
|
||||
)
|
||||
|
||||
|
||||
class CompliancePagination(PageNumberPagination):
|
||||
"""Custom pagination for compliance endpoints"""
|
||||
page_size = 20
|
||||
page_size_query_param = 'page_size'
|
||||
max_page_size = 100
|
||||
|
||||
|
||||
class RegulatoryFrameworkFilter(filters.FilterSet):
|
||||
"""Filter for Regulatory Framework"""
|
||||
|
||||
framework_type = filters.ChoiceFilter(choices=RegulatoryFramework.FRAMEWORK_TYPES)
|
||||
is_active = filters.BooleanFilter()
|
||||
effective_date_after = filters.DateFilter(field_name='effective_date', lookup_expr='gte')
|
||||
effective_date_before = filters.DateFilter(field_name='effective_date', lookup_expr='lte')
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = RegulatoryFramework
|
||||
fields = ['framework_type', 'is_active', 'effective_date_after', 'effective_date_before', 'search']
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(applicable_regions__icontains=value) |
|
||||
Q(industry_sectors__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class RegulatoryFrameworkViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Regulatory Framework"""
|
||||
|
||||
queryset = RegulatoryFramework.objects.all()
|
||||
serializer_class = RegulatoryFrameworkSerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = RegulatoryFrameworkFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return RegulatoryFramework.objects.select_related('created_by').prefetch_related('requirements')
|
||||
|
||||
@action(detail=True, methods=['get'])
|
||||
def requirements(self, request, pk=None):
|
||||
"""Get all requirements for a framework"""
|
||||
framework = self.get_object()
|
||||
requirements = framework.requirements.all()
|
||||
|
||||
# Apply filters if provided
|
||||
status_filter = request.query_params.get('compliance_status')
|
||||
if status_filter:
|
||||
requirements = requirements.filter(compliance_status=status_filter)
|
||||
|
||||
priority_filter = request.query_params.get('priority')
|
||||
if priority_filter:
|
||||
requirements = requirements.filter(priority=priority_filter)
|
||||
|
||||
serializer = ComplianceRequirementSerializer(requirements, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['get'])
|
||||
def compliance_summary(self, request, pk=None):
|
||||
"""Get compliance summary for a framework"""
|
||||
framework = self.get_object()
|
||||
requirements = framework.requirements.all()
|
||||
|
||||
summary = {
|
||||
'total_requirements': requirements.count(),
|
||||
'compliant': requirements.filter(compliance_status='COMPLIANT').count(),
|
||||
'partially_compliant': requirements.filter(compliance_status='PARTIALLY_COMPLIANT').count(),
|
||||
'non_compliant': requirements.filter(compliance_status='NON_COMPLIANT').count(),
|
||||
'not_assessed': requirements.filter(compliance_status='NOT_ASSESSED').count(),
|
||||
'implemented': requirements.filter(is_implemented=True).count(),
|
||||
'overdue_assessments': requirements.filter(
|
||||
next_assessment_date__lt=timezone.now().date()
|
||||
).count(),
|
||||
}
|
||||
|
||||
# Calculate compliance percentage
|
||||
assessed_count = summary['compliant'] + summary['partially_compliant'] + summary['non_compliant']
|
||||
if assessed_count > 0:
|
||||
summary['compliance_percentage'] = (summary['compliant'] / assessed_count) * 100
|
||||
else:
|
||||
summary['compliance_percentage'] = 0
|
||||
|
||||
return Response(summary)
|
||||
|
||||
|
||||
class ComplianceRequirementFilter(filters.FilterSet):
|
||||
"""Filter for Compliance Requirement"""
|
||||
|
||||
framework = filters.UUIDFilter()
|
||||
compliance_status = filters.ChoiceFilter(choices=ComplianceRequirement._meta.get_field('compliance_status').choices)
|
||||
requirement_type = filters.ChoiceFilter(choices=ComplianceRequirement.REQUIREMENT_TYPES)
|
||||
priority = filters.ChoiceFilter(choices=ComplianceRequirement.PRIORITY_LEVELS)
|
||||
is_implemented = filters.BooleanFilter()
|
||||
assigned_to = filters.UUIDFilter()
|
||||
overdue = filters.BooleanFilter(method='filter_overdue')
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = ComplianceRequirement
|
||||
fields = ['framework', 'compliance_status', 'requirement_type', 'priority', 'is_implemented', 'assigned_to', 'overdue', 'search']
|
||||
|
||||
def filter_overdue(self, queryset, name, value):
|
||||
if value:
|
||||
return queryset.filter(next_assessment_date__lt=timezone.now().date())
|
||||
return queryset
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(title__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(requirement_id__icontains=value) |
|
||||
Q(responsible_team__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class ComplianceRequirementViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Compliance Requirement"""
|
||||
|
||||
queryset = ComplianceRequirement.objects.all()
|
||||
serializer_class = ComplianceRequirementSerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = ComplianceRequirementFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return ComplianceRequirement.objects.select_related(
|
||||
'framework', 'assigned_to'
|
||||
).prefetch_related('evidence_collection', 'workflow_instances')
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
return ComplianceRequirementDetailSerializer
|
||||
return ComplianceRequirementSerializer
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def update_status(self, request, pk=None):
|
||||
"""Update compliance status"""
|
||||
requirement = self.get_object()
|
||||
new_status = request.data.get('compliance_status')
|
||||
|
||||
if new_status not in dict(ComplianceRequirement._meta.get_field('compliance_status').choices):
|
||||
return Response(
|
||||
{'error': 'Invalid compliance status'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
requirement.compliance_status = new_status
|
||||
requirement.last_assessment_date = timezone.now().date()
|
||||
requirement.save()
|
||||
|
||||
serializer = self.get_serializer(requirement)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def schedule_assessment(self, request, pk=None):
|
||||
"""Schedule next assessment"""
|
||||
requirement = self.get_object()
|
||||
assessment_date = request.data.get('assessment_date')
|
||||
|
||||
if not assessment_date:
|
||||
return Response(
|
||||
{'error': 'Assessment date is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
requirement.next_assessment_date = assessment_date
|
||||
requirement.save()
|
||||
|
||||
serializer = self.get_serializer(requirement)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class RegulatoryWorkflowFilter(filters.FilterSet):
|
||||
"""Filter for Regulatory Workflow"""
|
||||
|
||||
workflow_type = filters.ChoiceFilter(choices=RegulatoryWorkflow.WORKFLOW_TYPES)
|
||||
status = filters.ChoiceFilter(choices=RegulatoryWorkflow.STATUS_CHOICES)
|
||||
is_template = filters.BooleanFilter()
|
||||
applicable_frameworks = filters.UUIDFilter(field_name='applicable_frameworks')
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = RegulatoryWorkflow
|
||||
fields = ['workflow_type', 'status', 'is_template', 'applicable_frameworks', 'search']
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(description__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class RegulatoryWorkflowViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Regulatory Workflow"""
|
||||
|
||||
queryset = RegulatoryWorkflow.objects.all()
|
||||
serializer_class = RegulatoryWorkflowSerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = RegulatoryWorkflowFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return RegulatoryWorkflow.objects.select_related('created_by').prefetch_related(
|
||||
'applicable_frameworks', 'instances'
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def create_instance(self, request, pk=None):
|
||||
"""Create a new workflow instance"""
|
||||
workflow = self.get_object()
|
||||
|
||||
if workflow.status != 'ACTIVE':
|
||||
return Response(
|
||||
{'error': 'Cannot create instance from inactive workflow'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
instance_data = request.data.copy()
|
||||
instance_data['workflow'] = workflow.id
|
||||
instance_data['created_by'] = request.user.id
|
||||
|
||||
serializer = WorkflowInstanceSerializer(data=instance_data)
|
||||
if serializer.is_valid():
|
||||
instance = serializer.save()
|
||||
return Response(
|
||||
WorkflowInstanceSerializer(instance).data,
|
||||
status=status.HTTP_201_CREATED
|
||||
)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class WorkflowInstanceFilter(filters.FilterSet):
|
||||
"""Filter for Workflow Instance"""
|
||||
|
||||
workflow = filters.UUIDFilter()
|
||||
status = filters.ChoiceFilter(choices=WorkflowInstance.STATUS_CHOICES)
|
||||
assigned_to = filters.UUIDFilter()
|
||||
related_incident = filters.UUIDFilter()
|
||||
overdue = filters.BooleanFilter(method='filter_overdue')
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = WorkflowInstance
|
||||
fields = ['workflow', 'status', 'assigned_to', 'related_incident', 'overdue', 'search']
|
||||
|
||||
def filter_overdue(self, queryset, name, value):
|
||||
if value:
|
||||
return queryset.filter(
|
||||
due_date__lt=timezone.now(),
|
||||
status__in=['PENDING', 'IN_PROGRESS']
|
||||
)
|
||||
return queryset
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(title__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(current_step__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class WorkflowInstanceViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Workflow Instance"""
|
||||
|
||||
queryset = WorkflowInstance.objects.all()
|
||||
serializer_class = WorkflowInstanceSerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = WorkflowInstanceFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return WorkflowInstance.objects.select_related(
|
||||
'workflow', 'assigned_to', 'created_by', 'related_incident', 'related_requirement'
|
||||
).prefetch_related('stakeholders', 'evidence_collection')
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
return WorkflowInstanceDetailSerializer
|
||||
return WorkflowInstanceSerializer
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def advance_step(self, request, pk=None):
|
||||
"""Advance workflow to next step"""
|
||||
instance = self.get_object()
|
||||
next_step = request.data.get('next_step')
|
||||
step_data = request.data.get('step_data', {})
|
||||
|
||||
if not next_step:
|
||||
return Response(
|
||||
{'error': 'Next step is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# Add current step to completed steps
|
||||
if instance.current_step:
|
||||
instance.completed_steps.append({
|
||||
'step': instance.current_step,
|
||||
'completed_at': timezone.now().isoformat(),
|
||||
'completed_by': request.user.id
|
||||
})
|
||||
|
||||
# Update current step and execution data
|
||||
instance.current_step = next_step
|
||||
instance.execution_data.update(step_data)
|
||||
instance.updated_at = timezone.now()
|
||||
|
||||
# Check if workflow is complete
|
||||
workflow_definition = instance.workflow.workflow_definition
|
||||
if next_step in workflow_definition.get('end_steps', []):
|
||||
instance.status = 'COMPLETED'
|
||||
instance.completed_at = timezone.now()
|
||||
|
||||
instance.save()
|
||||
|
||||
serializer = self.get_serializer(instance)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def assign_stakeholder(self, request, pk=None):
|
||||
"""Assign stakeholder to workflow instance"""
|
||||
instance = self.get_object()
|
||||
stakeholder_id = request.data.get('stakeholder_id')
|
||||
|
||||
if not stakeholder_id:
|
||||
return Response(
|
||||
{'error': 'Stakeholder ID is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
User = get_user_model()
|
||||
|
||||
try:
|
||||
stakeholder = User.objects.get(id=stakeholder_id)
|
||||
instance.stakeholders.add(stakeholder)
|
||||
return Response({'message': 'Stakeholder assigned successfully'})
|
||||
except User.DoesNotExist:
|
||||
return Response(
|
||||
{'error': 'User not found'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
|
||||
class EvidenceCollectionFilter(filters.FilterSet):
|
||||
"""Filter for Evidence Collection"""
|
||||
|
||||
incident = filters.UUIDFilter()
|
||||
evidence_type = filters.ChoiceFilter(choices=EvidenceCollection.EVIDENCE_TYPES)
|
||||
status = filters.ChoiceFilter(choices=EvidenceCollection.STATUS_CHOICES)
|
||||
collected_by = filters.UUIDFilter()
|
||||
compliance_requirement = filters.UUIDFilter()
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = EvidenceCollection
|
||||
fields = ['incident', 'evidence_type', 'status', 'collected_by', 'compliance_requirement', 'search']
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(title__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(collection_notes__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class EvidenceCollectionViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Evidence Collection"""
|
||||
|
||||
queryset = EvidenceCollection.objects.all()
|
||||
serializer_class = EvidenceCollectionSerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = EvidenceCollectionFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return EvidenceCollection.objects.select_related(
|
||||
'incident', 'collected_by', 'verified_by', 'workflow_instance', 'compliance_requirement'
|
||||
)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
return EvidenceCollectionDetailSerializer
|
||||
return EvidenceCollectionSerializer
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def add_custody_record(self, request, pk=None):
|
||||
"""Add custody record to evidence"""
|
||||
evidence = self.get_object()
|
||||
action = request.data.get('action')
|
||||
notes = request.data.get('notes', '')
|
||||
|
||||
if not action:
|
||||
return Response(
|
||||
{'error': 'Action is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
evidence.add_custody_record(request.user, action, notes)
|
||||
|
||||
serializer = self.get_serializer(evidence)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def verify_evidence(self, request, pk=None):
|
||||
"""Verify evidence"""
|
||||
evidence = self.get_object()
|
||||
verification_notes = request.data.get('verification_notes', '')
|
||||
|
||||
evidence.status = 'VERIFIED'
|
||||
evidence.verified_by = request.user
|
||||
evidence.updated_at = timezone.now()
|
||||
evidence.save()
|
||||
|
||||
# Add custody record
|
||||
evidence.add_custody_record(request.user, 'VERIFIED', verification_notes)
|
||||
|
||||
serializer = self.get_serializer(evidence)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class RetentionPolicyFilter(filters.FilterSet):
|
||||
"""Filter for Retention Policy"""
|
||||
|
||||
policy_type = filters.ChoiceFilter(choices=RetentionPolicy.POLICY_TYPES)
|
||||
is_active = filters.BooleanFilter()
|
||||
applicable_frameworks = filters.UUIDFilter(field_name='applicable_frameworks')
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = RetentionPolicy
|
||||
fields = ['policy_type', 'is_active', 'applicable_frameworks', 'search']
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(description__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class RetentionPolicyViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Retention Policy"""
|
||||
|
||||
queryset = RetentionPolicy.objects.all()
|
||||
serializer_class = RetentionPolicySerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = RetentionPolicyFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return RetentionPolicy.objects.select_related('created_by').prefetch_related('applicable_frameworks')
|
||||
|
||||
|
||||
class ExportRequestFilter(filters.FilterSet):
|
||||
"""Filter for Export Request"""
|
||||
|
||||
request_type = filters.ChoiceFilter(choices=ExportRequest.REQUEST_TYPES)
|
||||
status = filters.ChoiceFilter(choices=ExportRequest.STATUS_CHOICES)
|
||||
requester_email = filters.CharFilter(lookup_expr='icontains')
|
||||
overdue = filters.BooleanFilter(method='filter_overdue')
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = ExportRequest
|
||||
fields = ['request_type', 'status', 'requester_email', 'overdue', 'search']
|
||||
|
||||
def filter_overdue(self, queryset, name, value):
|
||||
if value:
|
||||
return queryset.filter(
|
||||
due_date__lt=timezone.now(),
|
||||
status__in=['PENDING', 'APPROVED', 'IN_PROGRESS']
|
||||
)
|
||||
return queryset
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(title__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(requester_name__icontains=value) |
|
||||
Q(requester_organization__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class ExportRequestViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Export Request"""
|
||||
|
||||
queryset = ExportRequest.objects.all()
|
||||
serializer_class = ExportRequestSerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = ExportRequestFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return ExportRequest.objects.select_related(
|
||||
'approved_by', 'executed_by', 'created_by'
|
||||
).prefetch_related('applicable_frameworks')
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
return ExportRequestDetailSerializer
|
||||
return ExportRequestSerializer
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def approve(self, request, pk=None):
|
||||
"""Approve export request"""
|
||||
export_request = self.get_object()
|
||||
approval_notes = request.data.get('approval_notes', '')
|
||||
|
||||
if export_request.status != 'PENDING':
|
||||
return Response(
|
||||
{'error': 'Only pending requests can be approved'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
export_request.status = 'APPROVED'
|
||||
export_request.approved_by = request.user
|
||||
export_request.approved_at = timezone.now()
|
||||
export_request.approval_notes = approval_notes
|
||||
export_request.save()
|
||||
|
||||
serializer = self.get_serializer(export_request)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def execute(self, request, pk=None):
|
||||
"""Execute export request"""
|
||||
export_request = self.get_object()
|
||||
|
||||
if export_request.status != 'APPROVED':
|
||||
return Response(
|
||||
{'error': 'Only approved requests can be executed'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# This would typically trigger an async task to generate the export
|
||||
export_request.status = 'IN_PROGRESS'
|
||||
export_request.executed_by = request.user
|
||||
export_request.save()
|
||||
|
||||
# TODO: Implement actual export generation logic
|
||||
|
||||
serializer = self.get_serializer(export_request)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class ComplianceReportFilter(filters.FilterSet):
|
||||
"""Filter for Compliance Report"""
|
||||
|
||||
framework = filters.UUIDFilter()
|
||||
report_type = filters.ChoiceFilter(choices=ComplianceReport.REPORT_TYPES)
|
||||
status = filters.ChoiceFilter(choices=ComplianceReport.STATUS_CHOICES)
|
||||
prepared_by = filters.UUIDFilter()
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = ComplianceReport
|
||||
fields = ['framework', 'report_type', 'status', 'prepared_by', 'search']
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(title__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(executive_summary__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class ComplianceReportViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Compliance Report"""
|
||||
|
||||
queryset = ComplianceReport.objects.all()
|
||||
serializer_class = ComplianceReportSerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = ComplianceReportFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return ComplianceReport.objects.select_related(
|
||||
'framework', 'prepared_by', 'reviewed_by', 'approved_by'
|
||||
).prefetch_related('applicable_requirements')
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
return ComplianceReportDetailSerializer
|
||||
return ComplianceReportSerializer
|
||||
|
||||
|
||||
class LegalHoldFilter(filters.FilterSet):
|
||||
"""Filter for Legal Hold"""
|
||||
|
||||
status = filters.ChoiceFilter(choices=LegalHold.STATUS_CHOICES)
|
||||
active = filters.BooleanFilter(method='filter_active')
|
||||
search = filters.CharFilter(method='filter_search')
|
||||
|
||||
class Meta:
|
||||
model = LegalHold
|
||||
fields = ['status', 'active', 'search']
|
||||
|
||||
def filter_active(self, queryset, name, value):
|
||||
if value:
|
||||
return queryset.filter(
|
||||
status='ACTIVE',
|
||||
expiration_date__gt=timezone.now().date()
|
||||
)
|
||||
return queryset
|
||||
|
||||
def filter_search(self, queryset, name, value):
|
||||
return queryset.filter(
|
||||
Q(case_name__icontains=value) |
|
||||
Q(case_number__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(legal_counsel__icontains=value) |
|
||||
Q(law_firm__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class LegalHoldViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for Legal Hold"""
|
||||
|
||||
queryset = LegalHold.objects.all()
|
||||
serializer_class = LegalHoldSerializer
|
||||
pagination_class = CompliancePagination
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = LegalHoldFilter
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
return LegalHold.objects.select_related('created_by').prefetch_related(
|
||||
'related_incidents', 'related_evidence'
|
||||
)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
return LegalHoldDetailSerializer
|
||||
return LegalHoldSerializer
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def add_incident(self, request, pk=None):
|
||||
"""Add incident to legal hold"""
|
||||
legal_hold = self.get_object()
|
||||
incident_id = request.data.get('incident_id')
|
||||
|
||||
if not incident_id:
|
||||
return Response(
|
||||
{'error': 'Incident ID is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
from incident_intelligence.models import Incident
|
||||
|
||||
try:
|
||||
incident = Incident.objects.get(id=incident_id)
|
||||
legal_hold.related_incidents.add(incident)
|
||||
return Response({'message': 'Incident added to legal hold successfully'})
|
||||
except Incident.DoesNotExist:
|
||||
return Response(
|
||||
{'error': 'Incident not found'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def add_evidence(self, request, pk=None):
|
||||
"""Add evidence to legal hold"""
|
||||
legal_hold = self.get_object()
|
||||
evidence_id = request.data.get('evidence_id')
|
||||
|
||||
if not evidence_id:
|
||||
return Response(
|
||||
{'error': 'Evidence ID is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
try:
|
||||
evidence = EvidenceCollection.objects.get(id=evidence_id)
|
||||
legal_hold.related_evidence.add(evidence)
|
||||
return Response({'message': 'Evidence added to legal hold successfully'})
|
||||
except EvidenceCollection.DoesNotExist:
|
||||
return Response(
|
||||
{'error': 'Evidence not found'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
Reference in New Issue
Block a user