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 .models import (
QualityIndicator, QualityMeasurement, IncidentReport,
RiskAssessment, AuditPlan, AuditFinding, ImprovementProject
)
class QualityMeasurementInline(admin.TabularInline):
"""Inline for quality measurements"""
model = QualityMeasurement
extra = 0
fields = ['measurement_date', 'value', 'numerator', 'denominator', 'status', 'verified_by']
readonly_fields = ['created_by', 'created_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related('verified_by', 'created_by')
@admin.register(QualityIndicator)
class QualityIndicatorAdmin(admin.ModelAdmin):
"""Admin for quality indicators"""
list_display = [
'name', 'category', 'type', 'frequency', 'target_value',
'current_status_display', 'responsible_department', 'is_active'
]
list_filter = [
'category', 'type', 'frequency', 'is_active',
'regulatory_requirement', 'responsible_department'
]
search_fields = ['name', 'description', 'data_source']
readonly_fields = ['created_at', 'updated_at']
fieldsets = [
('Basic Information', {
'fields': ['name', 'description', 'category', 'type']
}),
('Measurement Details', {
'fields': ['measurement_unit', 'target_value', 'threshold_warning', 'threshold_critical']
}),
('Calculation', {
'fields': ['calculation_method', 'data_source', 'frequency']
}),
('Responsibility', {
'fields': ['responsible_department', 'responsible_user']
}),
('Settings', {
'fields': ['is_active', 'regulatory_requirement']
}),
('Timestamps', {
'fields': ['created_at', 'updated_at'],
'classes': ['collapse']
})
]
inlines = [QualityMeasurementInline]
def current_status_display(self, obj):
"""Display current status with color coding"""
status = obj.current_status
colors = {
'within_target': 'green',
'warning': 'orange',
'critical': 'red',
'improving': 'blue',
'declining': 'purple',
'no_data': 'gray'
}
return format_html(
'{}',
colors.get(status, 'black'),
status.replace('_', ' ').title()
)
current_status_display.short_description = 'Current Status'
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'responsible_department', 'responsible_user'
).prefetch_related('measurements')
@admin.register(QualityMeasurement)
class QualityMeasurementAdmin(admin.ModelAdmin):
"""Admin for quality measurements"""
list_display = [
'indicator', 'measurement_date', 'value', 'status_display',
'verified_by', 'created_by'
]
list_filter = [
'status', 'measurement_date', 'verified_by',
'indicator__category', 'indicator__type'
]
search_fields = ['indicator__name', 'notes', 'data_source_reference']
readonly_fields = ['created_at', 'updated_at']
date_hierarchy = 'measurement_date'
fieldsets = [
('Measurement', {
'fields': ['indicator', 'measurement_date', 'value']
}),
('Rate Calculation', {
'fields': ['numerator', 'denominator'],
'classes': ['collapse']
}),
('Status and Notes', {
'fields': ['status', 'notes', 'data_source_reference']
}),
('Verification', {
'fields': ['verified_by', 'verified_at']
}),
('Audit', {
'fields': ['created_by', 'created_at', 'updated_at'],
'classes': ['collapse']
})
]
def status_display(self, obj):
"""Display status with color coding"""
colors = {
'within_target': 'green',
'warning': 'orange',
'critical': 'red',
'improving': 'blue',
'declining': 'purple'
}
return format_html(
'{}',
colors.get(obj.status, 'black'),
obj.get_status_display()
)
status_display.short_description = 'Status'
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'indicator', 'verified_by', 'created_by'
)
class RiskAssessmentInline(admin.TabularInline):
"""Inline for risk assessments"""
model = RiskAssessment
extra = 0
fields = ['title', 'risk_category', 'risk_level', 'status', 'responsible_person']
readonly_fields = ['risk_score', ]
@admin.register(IncidentReport)
class IncidentReportAdmin(admin.ModelAdmin):
"""Admin for incident reports"""
list_display = [
'incident_number', 'title', 'incident_type', 'severity_display',
'status', 'assigned_to', 'incident_date'
]
list_filter = [
'incident_type', 'severity', 'category', 'status', 'priority',
'incident_date', 'is_confidential', 'regulatory_notification'
]
search_fields = ['incident_number', 'title', 'description', 'location']
readonly_fields = ['incident_number', 'created_at', 'updated_at']
date_hierarchy = 'incident_date'
fieldsets = [
('Incident Details', {
'fields': ['incident_number', 'title', 'description']
}),
('Classification', {
'fields': ['incident_type', 'severity', 'category', 'location']
}),
('Timeline', {
'fields': ['incident_date', 'discovered_date']
}),
('People Involved', {
'fields': ['patient', 'reported_by']
}),
('Investigation', {
'fields': ['status', 'priority', 'assigned_to', 'due_date']
}),
('Analysis', {
'fields': ['root_cause', 'contributing_factors'],
'classes': ['collapse']
}),
('Actions', {
'fields': ['corrective_actions', 'preventive_actions'],
'classes': ['collapse']
}),
('Settings', {
'fields': ['is_confidential', 'regulatory_notification']
}),
('Closure', {
'fields': ['closed_date'],
'classes': ['collapse']
}),
('Audit', {
'fields': ['created_at', 'updated_at'],
'classes': ['collapse']
})
]
inlines = [RiskAssessmentInline]
def severity_display(self, obj):
"""Display severity with color coding"""
colors = {
'no_harm': 'green',
'minor_harm': 'yellow',
'moderate_harm': 'orange',
'severe_harm': 'red',
'death': 'darkred'
}
return format_html(
'{}',
colors.get(obj.severity, 'black'),
obj.get_severity_display()
)
severity_display.short_description = 'Severity'
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'patient', 'reported_by', 'assigned_to'
)
@admin.register(RiskAssessment)
class RiskAssessmentAdmin(admin.ModelAdmin):
"""Admin for risk assessments"""
list_display = [
'title', 'risk_category', 'risk_level_display',
'status', 'responsible_person', 'review_date'
]
list_filter = [
'risk_category', 'risk_type', 'risk_level',
'status', 'control_effectiveness', 'review_date'
]
search_fields = ['title', 'description', 'current_controls', 'mitigation_plan']
readonly_fields = ['risk_score', 'risk_level', 'created_at', 'updated_at']
date_hierarchy = 'review_date'
fieldsets = [
('Risk Information', {
'fields': ['title', 'description', 'risk_category', 'risk_type']
}),
('Initial Risk Assessment', {
'fields': ['likelihood', 'impact', 'risk_score', 'risk_level']
}),
('Current Controls', {
'fields': ['current_controls', 'control_effectiveness']
}),
('Residual Risk', {
'fields': ['residual_likelihood', 'residual_impact', 'residual_risk_score', 'residual_risk_level']
}),
('Mitigation', {
'fields': ['mitigation_plan', 'responsible_person', 'review_date']
}),
('Status', {
'fields': ['status', 'incident_report']
}),
('Audit', {
'fields': ['created_by', 'created_at', 'updated_at'],
'classes': ['collapse']
})
]
def risk_level_display(self, obj):
"""Display risk level with color coding"""
colors = {
'low': 'green',
'medium': 'yellow',
'high': 'orange',
'critical': 'red'
}
return format_html(
'{}',
colors.get(obj.risk_level, 'black'),
obj.get_risk_level_display()
)
risk_level_display.short_description = 'Risk Level'
def residual_risk_level_display(self, obj):
"""Display residual risk level with color coding"""
colors = {
'low': 'green',
'medium': 'yellow',
'high': 'orange',
'critical': 'red'
}
return format_html(
'{}',
colors.get(obj.residual_risk_level, 'black'),
obj.get_residual_risk_level_display()
)
residual_risk_level_display.short_description = 'Residual Risk Level'
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'responsible_person', 'incident_report', 'created_by'
)
class AuditFindingInline(admin.TabularInline):
"""Inline for audit findings"""
model = AuditFinding
extra = 0
fields = ['finding_number', 'title', 'finding_type', 'severity', 'status', 'responsible_person']
readonly_fields = ['finding_number']
@admin.register(AuditPlan)
class AuditPlanAdmin(admin.ModelAdmin):
"""Admin for audit plans"""
list_display = [
'title', 'audit_type', 'department', 'auditor',
'planned_start_date', 'status', 'findings_count_display'
]
list_filter = [
'audit_type', 'status', 'priority', 'regulatory_requirement',
'planned_start_date', 'department'
]
search_fields = ['title', 'description', 'scope', 'criteria', 'accreditation_body']
readonly_fields = ['created_at', 'updated_at']
date_hierarchy = 'planned_start_date'
filter_horizontal = ['audit_team']
fieldsets = [
('Audit Information', {
'fields': ['title', 'description', 'audit_type']
}),
('Scope and Criteria', {
'fields': ['scope', 'criteria', 'department']
}),
('Team', {
'fields': ['auditor', 'audit_team']
}),
('Schedule', {
'fields': ['planned_start_date', 'planned_end_date', 'actual_start_date', 'actual_end_date']
}),
('Status', {
'fields': ['status', 'priority']
}),
('Regulatory', {
'fields': ['regulatory_requirement', 'accreditation_body'],
'classes': ['collapse']
}),
('Audit', {
'fields': ['created_by', 'created_at', 'updated_at'],
'classes': ['collapse']
})
]
inlines = [AuditFindingInline]
def findings_count_display(self, obj):
"""Display findings count with link"""
count = obj.findings_count
if count > 0:
url = reverse('admin:quality_auditfinding_changelist') + f'?audit_plan__id__exact={obj.id}'
return format_html('{} findings', url, count)
return '0 findings'
findings_count_display.short_description = 'Findings'
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'department', 'auditor', 'created_by'
).prefetch_related('audit_team', 'findings')
@admin.register(AuditFinding)
class AuditFindingAdmin(admin.ModelAdmin):
"""Admin for audit findings"""
list_display = [
'finding_number', 'title', 'audit_plan', 'finding_type',
'severity_display', 'status', 'responsible_person', 'target_completion_date'
]
list_filter = [
'finding_type', 'severity', 'category', 'status',
'corrective_action_required', 'target_completion_date', 'audit_plan__audit_type'
]
search_fields = ['finding_number', 'title', 'description', 'evidence', 'criteria_reference']
readonly_fields = ['finding_number', 'created_at', 'updated_at']
date_hierarchy = 'target_completion_date'
fieldsets = [
('Finding Information', {
'fields': ['audit_plan', 'finding_number', 'title', 'description']
}),
('Classification', {
'fields': ['finding_type', 'severity', 'category', 'criteria_reference']
}),
('Evidence and Analysis', {
'fields': ['evidence', 'root_cause']
}),
('Corrective Action', {
'fields': ['corrective_action_required', 'corrective_action_plan', 'responsible_person']
}),
('Timeline', {
'fields': ['target_completion_date', 'actual_completion_date']
}),
('Status and Verification', {
'fields': ['status', 'verification_method', 'verified_by', 'verified_date']
}),
('Audit', {
'fields': ['created_by', 'created_at', 'updated_at'],
'classes': ['collapse']
})
]
def severity_display(self, obj):
"""Display severity with color coding"""
colors = {
'minor': 'green',
'major': 'orange',
'critical': 'red'
}
return format_html(
'{}',
colors.get(obj.severity, 'black'),
obj.get_severity_display()
)
severity_display.short_description = 'Severity'
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'audit_plan', 'responsible_person', 'verified_by', 'created_by'
)
@admin.register(ImprovementProject)
class ImprovementProjectAdmin(admin.ModelAdmin):
"""Admin for improvement projects"""
list_display = [
'project_number', 'title', 'project_type', 'status',
'project_manager', 'planned_start_date', 'phase'
]
list_filter = [
'project_type', 'methodology', 'status', 'phase',
'planned_start_date', 'department'
]
search_fields = ['project_number', 'title', 'description', 'problem_statement', 'goal_statement']
readonly_fields = ['project_number', 'duration_planned', 'duration_actual', 'created_at', 'updated_at']
date_hierarchy = 'planned_start_date'
filter_horizontal = ['project_team']
fieldsets = [
('Project Information', {
'fields': ['project_number', 'title', 'description', 'project_type', 'methodology']
}),
('Problem and Goals', {
'fields': ['problem_statement', 'goal_statement', 'scope']
}),
('Metrics', {
'fields': ['success_metrics', 'baseline_data', 'target_metrics']
}),
('Team', {
'fields': ['project_manager', 'project_team', 'sponsor', 'department']
}),
('Timeline', {
'fields': ['planned_start_date', 'planned_end_date', 'actual_start_date', 'actual_end_date', 'duration_planned', 'duration_actual']
}),
('Status', {
'fields': ['status', 'phase']
}),
('Budget and ROI', {
'fields': ['budget', 'actual_cost', 'roi_expected', 'roi_actual'],
'classes': ['collapse']
}),
('Outcomes', {
'fields': ['lessons_learned', 'sustainability_plan'],
'classes': ['collapse']
}),
('Audit', {
'fields': ['created_by', 'created_at', 'updated_at'],
'classes': ['collapse']
})
]
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'project_manager', 'sponsor', 'department', 'created_by'
).prefetch_related('project_team')
# Register models with custom admin classes
admin.site.site_header = "Hospital Management System - Quality Administration"
admin.site.site_title = "Quality Admin"
admin.site.index_title = "Quality Management Administration"