211 lines
6.6 KiB
Python
211 lines
6.6 KiB
Python
"""
|
|
Analytics admin
|
|
"""
|
|
from django.contrib import admin
|
|
from django.utils.html import format_html
|
|
|
|
from .models import KPI, KPIValue
|
|
from .kpi_models import KPIReport, KPIReportMonthlyData, KPIReportDepartmentBreakdown, KPIReportSourceBreakdown
|
|
|
|
|
|
@admin.register(KPI)
|
|
class KPIAdmin(admin.ModelAdmin):
|
|
"""KPI admin"""
|
|
list_display = [
|
|
'name', 'category', 'unit', 'target_value',
|
|
'warning_threshold', 'critical_threshold', 'is_active'
|
|
]
|
|
list_filter = ['category', 'is_active']
|
|
search_fields = ['name', 'name_ar', 'description']
|
|
ordering = ['category', 'name']
|
|
|
|
fieldsets = (
|
|
(None, {
|
|
'fields': ('name', 'name_ar', 'description', 'category')
|
|
}),
|
|
('Measurement', {
|
|
'fields': ('unit', 'calculation_method')
|
|
}),
|
|
('Thresholds', {
|
|
'fields': ('target_value', 'warning_threshold', 'critical_threshold')
|
|
}),
|
|
('Configuration', {
|
|
'fields': ('is_active',)
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('created_at', 'updated_at')
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
|
|
@admin.register(KPIValue)
|
|
class KPIValueAdmin(admin.ModelAdmin):
|
|
"""KPI value admin"""
|
|
list_display = [
|
|
'kpi', 'hospital', 'department', 'value',
|
|
'status_badge', 'period_type', 'period_end'
|
|
]
|
|
list_filter = ['status', 'period_type', 'kpi__category', 'hospital', 'period_end']
|
|
search_fields = ['kpi__name']
|
|
ordering = ['-period_end']
|
|
date_hierarchy = 'period_end'
|
|
|
|
fieldsets = (
|
|
('KPI', {
|
|
'fields': ('kpi',)
|
|
}),
|
|
('Scope', {
|
|
'fields': ('hospital', 'department')
|
|
}),
|
|
('Value', {
|
|
'fields': ('value', 'status')
|
|
}),
|
|
('Period', {
|
|
'fields': ('period_type', 'period_start', 'period_end')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('metadata', 'created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.select_related('kpi', 'hospital', 'department')
|
|
|
|
def status_badge(self, obj):
|
|
"""Display status with badge"""
|
|
colors = {
|
|
'on_target': 'success',
|
|
'warning': 'warning',
|
|
'critical': 'danger',
|
|
}
|
|
color = colors.get(obj.status, 'secondary')
|
|
|
|
return format_html(
|
|
'<span class="badge bg-{}">{}</span>',
|
|
color,
|
|
obj.get_status_display()
|
|
)
|
|
status_badge.short_description = 'Status'
|
|
|
|
|
|
|
|
# Inline for monthly data
|
|
class KPIReportMonthlyDataInline(admin.TabularInline):
|
|
model = KPIReportMonthlyData
|
|
extra = 0
|
|
fields = ['month', 'numerator', 'denominator', 'percentage', 'is_below_target']
|
|
readonly_fields = ['percentage']
|
|
|
|
|
|
# Inline for department breakdown
|
|
class KPIReportDepartmentBreakdownInline(admin.TabularInline):
|
|
model = KPIReportDepartmentBreakdown
|
|
extra = 0
|
|
fields = ['department_category', 'complaint_count', 'resolved_count', 'avg_resolution_days']
|
|
|
|
|
|
# Inline for source breakdown
|
|
class KPIReportSourceBreakdownInline(admin.TabularInline):
|
|
model = KPIReportSourceBreakdown
|
|
extra = 0
|
|
fields = ['source_name', 'complaint_count', 'percentage']
|
|
|
|
|
|
@admin.register(KPIReport)
|
|
class KPIReportAdmin(admin.ModelAdmin):
|
|
"""KPI Report admin"""
|
|
list_display = [
|
|
'kpi_id', 'indicator_title_short', 'hospital', 'report_period_display',
|
|
'overall_result_display', 'status_badge', 'generated_at'
|
|
]
|
|
list_filter = ['report_type', 'status', 'year', 'hospital']
|
|
search_fields = ['hospital__name']
|
|
ordering = ['-year', '-month', 'report_type']
|
|
date_hierarchy = 'report_date'
|
|
|
|
fieldsets = (
|
|
('Report Info', {
|
|
'fields': ('report_type', 'hospital', 'year', 'month', 'status')
|
|
}),
|
|
('Results', {
|
|
'fields': ('target_percentage', 'total_numerator', 'total_denominator', 'overall_result')
|
|
}),
|
|
('Metadata', {
|
|
'fields': ('category', 'kpi_type', 'risk_level', 'dimension',
|
|
'data_collection_method', 'data_collection_frequency', 'reporting_frequency',
|
|
'collector_name', 'analyzer_name')
|
|
}),
|
|
('Generation', {
|
|
'fields': ('generated_by', 'generated_at', 'error_message')
|
|
}),
|
|
)
|
|
|
|
readonly_fields = ['overall_result', 'generated_at', 'error_message']
|
|
|
|
inlines = [KPIReportMonthlyDataInline, KPIReportDepartmentBreakdownInline, KPIReportSourceBreakdownInline]
|
|
|
|
def indicator_title_short(self, obj):
|
|
"""Shortened indicator title"""
|
|
title = obj.indicator_title
|
|
if len(title) > 40:
|
|
return title[:40] + '...'
|
|
return title
|
|
indicator_title_short.short_description = 'Title'
|
|
|
|
def overall_result_display(self, obj):
|
|
"""Display overall result with color"""
|
|
if obj.overall_result >= obj.target_percentage:
|
|
color = 'green'
|
|
else:
|
|
color = 'red'
|
|
return format_html(
|
|
'<span style="color: {}; font-weight: bold;">{}%</span>',
|
|
color, obj.overall_result
|
|
)
|
|
overall_result_display.short_description = 'Result'
|
|
|
|
def status_badge(self, obj):
|
|
"""Display status with badge"""
|
|
colors = {
|
|
'completed': 'success',
|
|
'pending': 'warning',
|
|
'generating': 'info',
|
|
'failed': 'danger',
|
|
}
|
|
color = colors.get(obj.status, 'secondary')
|
|
return format_html(
|
|
'<span class="badge bg-{}">{}</span>',
|
|
color,
|
|
obj.get_status_display()
|
|
)
|
|
status_badge.short_description = 'Status'
|
|
|
|
actions = ['regenerate_reports']
|
|
|
|
def regenerate_reports(self, request, queryset):
|
|
"""Regenerate selected reports"""
|
|
from .kpi_service import KPICalculationService
|
|
|
|
count = 0
|
|
for report in queryset:
|
|
try:
|
|
KPICalculationService.generate_monthly_report(
|
|
report_type=report.report_type,
|
|
hospital=report.hospital,
|
|
year=report.year,
|
|
month=report.month,
|
|
generated_by=request.user
|
|
)
|
|
count += 1
|
|
except Exception as e:
|
|
self.message_user(request, f'Failed to regenerate {report}: {e}', level='ERROR')
|
|
|
|
self.message_user(request, f'{count} report(s) regenerated successfully.')
|
|
regenerate_reports.short_description = 'Regenerate selected reports'
|