266 lines
9.5 KiB
Python
266 lines
9.5 KiB
Python
"""
|
|
Django admin configuration for compliance_governance app
|
|
"""
|
|
from django.contrib import admin
|
|
from django.utils.html import format_html
|
|
from django.urls import reverse
|
|
from django.utils.safestring import mark_safe
|
|
from django.db.models import Count, Q
|
|
from django.utils import timezone
|
|
from datetime import timedelta
|
|
|
|
from .models import (
|
|
RegulatoryFramework,
|
|
ComplianceRequirement,
|
|
RegulatoryWorkflow,
|
|
WorkflowInstance,
|
|
EvidenceCollection,
|
|
RetentionPolicy,
|
|
ExportRequest,
|
|
ComplianceReport,
|
|
LegalHold,
|
|
)
|
|
|
|
|
|
@admin.register(RegulatoryFramework)
|
|
class RegulatoryFrameworkAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Regulatory Framework"""
|
|
|
|
list_display = [
|
|
'name', 'framework_type', 'version', 'is_active', 'effective_date',
|
|
'requirements_count', 'created_at'
|
|
]
|
|
list_filter = [
|
|
'framework_type', 'is_active', 'effective_date', 'created_at'
|
|
]
|
|
search_fields = ['name', 'description', 'applicable_regions', 'industry_sectors']
|
|
readonly_fields = ['id', 'created_at', 'updated_at']
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': ('id', 'name', 'framework_type', 'version', 'description')
|
|
}),
|
|
('Framework Details', {
|
|
'fields': ('applicable_regions', 'industry_sectors', 'compliance_requirements')
|
|
}),
|
|
('Status and Dates', {
|
|
'fields': ('is_active', 'effective_date', 'review_date')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_by', 'created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def requirements_count(self, obj):
|
|
"""Display count of requirements"""
|
|
count = obj.requirements.count()
|
|
if count > 0:
|
|
url = reverse('admin:compliance_governance_compliancerequirement_changelist')
|
|
return format_html('<a href="{}?framework__id__exact={}">{} requirements</a>', url, obj.id, count)
|
|
return '0 requirements'
|
|
requirements_count.short_description = 'Requirements'
|
|
|
|
|
|
@admin.register(ComplianceRequirement)
|
|
class ComplianceRequirementAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Compliance Requirement"""
|
|
|
|
list_display = [
|
|
'requirement_id', 'title', 'framework', 'requirement_type', 'priority',
|
|
'compliance_status', 'is_implemented', 'assigned_to', 'next_assessment_date'
|
|
]
|
|
list_filter = [
|
|
'framework', 'requirement_type', 'priority', 'compliance_status',
|
|
'is_implemented', 'assigned_to', 'next_assessment_date'
|
|
]
|
|
search_fields = ['requirement_id', 'title', 'description', 'responsible_team']
|
|
readonly_fields = ['id', 'created_at', 'updated_at']
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': ('id', 'framework', 'requirement_id', 'title', 'description')
|
|
}),
|
|
('Requirement Details', {
|
|
'fields': ('requirement_type', 'priority', 'implementation_guidance', 'evidence_requirements', 'testing_procedures')
|
|
}),
|
|
('Compliance Tracking', {
|
|
'fields': ('is_implemented', 'implementation_date', 'last_assessment_date', 'next_assessment_date', 'compliance_status')
|
|
}),
|
|
('Assignment', {
|
|
'fields': ('responsible_team', 'assigned_to')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def get_queryset(self, request):
|
|
return super().get_queryset(request).select_related('framework', 'assigned_to')
|
|
|
|
|
|
@admin.register(RegulatoryWorkflow)
|
|
class RegulatoryWorkflowAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Regulatory Workflow"""
|
|
|
|
list_display = [
|
|
'name', 'workflow_type', 'status', 'version', 'is_template',
|
|
'instances_count', 'created_by', 'created_at'
|
|
]
|
|
list_filter = [
|
|
'workflow_type', 'status', 'is_template', 'created_at'
|
|
]
|
|
search_fields = ['name', 'description']
|
|
readonly_fields = ['id', 'created_at', 'updated_at']
|
|
filter_horizontal = ['applicable_frameworks']
|
|
|
|
def instances_count(self, obj):
|
|
"""Display count of workflow instances"""
|
|
count = obj.instances.count()
|
|
if count > 0:
|
|
url = reverse('admin:compliance_governance_workflowinstance_changelist')
|
|
return format_html('<a href="{}?workflow__id__exact={}">{} instances</a>', url, obj.id, count)
|
|
return '0 instances'
|
|
instances_count.short_description = 'Instances'
|
|
|
|
|
|
@admin.register(WorkflowInstance)
|
|
class WorkflowInstanceAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Workflow Instance"""
|
|
|
|
list_display = [
|
|
'title', 'workflow', 'status', 'assigned_to', 'related_incident',
|
|
'started_at', 'due_date', 'is_overdue'
|
|
]
|
|
list_filter = [
|
|
'workflow', 'status', 'assigned_to', 'started_at', 'due_date'
|
|
]
|
|
search_fields = ['title', 'description', 'current_step']
|
|
readonly_fields = ['id', 'started_at', 'updated_at']
|
|
filter_horizontal = ['stakeholders']
|
|
|
|
def is_overdue(self, obj):
|
|
"""Check if workflow instance is overdue"""
|
|
if obj.due_date and obj.status in ['PENDING', 'IN_PROGRESS']:
|
|
if timezone.now() > obj.due_date:
|
|
return format_html('<span style="color: red;">Yes</span>')
|
|
return 'No'
|
|
is_overdue.short_description = 'Overdue'
|
|
|
|
|
|
@admin.register(EvidenceCollection)
|
|
class EvidenceCollectionAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Evidence Collection"""
|
|
|
|
list_display = [
|
|
'title', 'evidence_type', 'status', 'incident', 'collected_by',
|
|
'collection_timestamp', 'file_size_display'
|
|
]
|
|
list_filter = [
|
|
'evidence_type', 'status', 'collected_by', 'collection_timestamp'
|
|
]
|
|
search_fields = ['title', 'description', 'collection_notes']
|
|
readonly_fields = ['id', 'collection_timestamp', 'created_at', 'updated_at']
|
|
|
|
def file_size_display(self, obj):
|
|
"""Display file size in human readable format"""
|
|
if not obj.file_size:
|
|
return 'N/A'
|
|
|
|
size = obj.file_size
|
|
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
|
if size < 1024.0:
|
|
return f"{size:.1f} {unit}"
|
|
size /= 1024.0
|
|
return f"{size:.1f} PB"
|
|
file_size_display.short_description = 'File Size'
|
|
|
|
|
|
@admin.register(RetentionPolicy)
|
|
class RetentionPolicyAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Retention Policy"""
|
|
|
|
list_display = [
|
|
'name', 'policy_type', 'retention_period', 'retention_unit',
|
|
'is_active', 'effective_date', 'created_by'
|
|
]
|
|
list_filter = [
|
|
'policy_type', 'is_active', 'effective_date', 'created_at'
|
|
]
|
|
search_fields = ['name', 'description']
|
|
readonly_fields = ['id', 'created_at', 'updated_at']
|
|
filter_horizontal = ['applicable_frameworks']
|
|
|
|
|
|
@admin.register(ExportRequest)
|
|
class ExportRequestAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Export Request"""
|
|
|
|
list_display = [
|
|
'title', 'request_type', 'status', 'requester_name', 'requester_organization',
|
|
'requested_at', 'due_date', 'is_overdue'
|
|
]
|
|
list_filter = [
|
|
'request_type', 'status', 'requester_organization', 'requested_at', 'due_date'
|
|
]
|
|
search_fields = ['title', 'description', 'requester_name', 'requester_email']
|
|
readonly_fields = ['id', 'requested_at', 'updated_at']
|
|
filter_horizontal = ['applicable_frameworks']
|
|
|
|
def is_overdue(self, obj):
|
|
"""Check if export request is overdue"""
|
|
if obj.due_date and obj.status not in ['COMPLETED', 'CANCELLED']:
|
|
if timezone.now() > obj.due_date:
|
|
return format_html('<span style="color: red;">Yes</span>')
|
|
return 'No'
|
|
is_overdue.short_description = 'Overdue'
|
|
|
|
|
|
@admin.register(ComplianceReport)
|
|
class ComplianceReportAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Compliance Report"""
|
|
|
|
list_display = [
|
|
'title', 'report_type', 'framework', 'status', 'overall_compliance_score',
|
|
'report_period_start', 'report_period_end', 'prepared_by'
|
|
]
|
|
list_filter = [
|
|
'report_type', 'status', 'framework', 'report_period_start', 'report_period_end'
|
|
]
|
|
search_fields = ['title', 'description', 'executive_summary']
|
|
readonly_fields = ['id', 'created_at', 'updated_at']
|
|
filter_horizontal = ['applicable_requirements']
|
|
|
|
|
|
@admin.register(LegalHold)
|
|
class LegalHoldAdmin(admin.ModelAdmin):
|
|
"""Admin interface for Legal Hold"""
|
|
|
|
list_display = [
|
|
'case_name', 'case_number', 'status', 'legal_counsel', 'hold_date',
|
|
'expiration_date', 'affected_data_count', 'is_active'
|
|
]
|
|
list_filter = [
|
|
'status', 'hold_date', 'expiration_date', 'created_at'
|
|
]
|
|
search_fields = ['case_name', 'case_number', 'description', 'legal_counsel', 'law_firm']
|
|
readonly_fields = ['id', 'created_at', 'updated_at']
|
|
filter_horizontal = ['related_incidents', 'related_evidence']
|
|
|
|
def affected_data_count(self, obj):
|
|
"""Display count of affected data items"""
|
|
count = obj.get_affected_data_count()
|
|
return f"{count} items"
|
|
affected_data_count.short_description = 'Affected Data'
|
|
|
|
def is_active(self, obj):
|
|
"""Check if legal hold is active"""
|
|
if obj.is_active():
|
|
return format_html('<span style="color: green;">Active</span>')
|
|
return format_html('<span style="color: red;">Inactive</span>')
|
|
is_active.short_description = 'Status'
|
|
|
|
|
|
# Custom admin site configuration
|
|
admin.site.site_header = "ETB Compliance & Governance Administration"
|
|
admin.site.site_title = "ETB Compliance Admin"
|
|
admin.site.index_title = "Compliance & Governance Management" |