Files
Iliyan Angelov 6b247e5b9f Updates
2025-09-19 11:58:53 +03:00

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"