""" Analytics app admin configuration. """ 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 decimal import Decimal from datetime import datetime, timedelta from .models import ( Dashboard, DashboardWidget, DataSource, Report, ReportExecution, MetricDefinition, MetricValue ) class DashboardWidgetInline(admin.TabularInline): """ Inline admin for dashboard widgets. """ model = DashboardWidget extra = 0 fields = [ 'name', 'widget_type', 'chart_type', 'data_source', 'position_x', 'position_y', 'width', 'height', 'is_active' ] readonly_fields = ['widget_id'] @admin.register(Dashboard) class DashboardAdmin(admin.ModelAdmin): """ Admin interface for dashboards. """ list_display = [ 'name', 'dashboard_type', 'is_public', 'is_default', 'is_active', 'refresh_interval_display', 'created_at' ] list_filter = [ 'tenant', 'dashboard_type', 'is_public', 'is_default', 'is_active' ] search_fields = [ 'name', 'description' ] readonly_fields = [ 'dashboard_id', 'created_at', 'updated_at' ] fieldsets = [ ('Dashboard Information', { 'fields': [ 'dashboard_id', 'tenant', 'name', 'description' ] }), ('Dashboard Type', { 'fields': [ 'dashboard_type' ] }), ('Layout Configuration', { 'fields': [ 'layout_config', 'refresh_interval' ] }), ('Access Control', { 'fields': [ 'is_public', 'allowed_users', 'allowed_roles' ] }), ('Dashboard Status', { 'fields': [ 'is_active', 'is_default' ] }), # ('Summary Information', { # 'fields': [ # 'widget_count' # ], # 'classes': ['collapse'] # }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] inlines = [DashboardWidgetInline] filter_horizontal = ['allowed_users'] # def widget_count_display(self, obj): # """Display widget count.""" # return obj.value_count # widget_count_display.short_description = 'Widgets' def refresh_interval_display(self, obj): """Display refresh interval.""" if obj.refresh_interval >= 3600: hours = obj.refresh_interval // 3600 return f"{hours}h" elif obj.refresh_interval >= 60: minutes = obj.refresh_interval // 60 return f"{minutes}m" return f"{obj.refresh_interval}s" refresh_interval_display.short_description = 'Refresh' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(tenant=request.user.tenant) return qs.prefetch_related('widgets') @admin.register(DashboardWidget) class DashboardWidgetAdmin(admin.ModelAdmin): """ Admin interface for dashboard widgets. """ list_display = [ 'name', 'dashboard_name', 'widget_type', 'chart_type', 'data_source', 'position_display', 'size_display', 'auto_refresh', 'is_active' ] list_filter = [ 'dashboard__tenant', 'widget_type', 'chart_type', 'auto_refresh', 'is_active' ] search_fields = [ 'name', 'description', 'dashboard__name' ] readonly_fields = [ 'widget_id', 'created_at', 'updated_at' ] fieldsets = [ ('Widget Information', { 'fields': [ 'widget_id', 'dashboard', 'name', 'description' ] }), ('Widget Type', { 'fields': [ 'widget_type', 'chart_type' ] }), ('Data Source', { 'fields': [ 'data_source', 'query_config' ] }), ('Layout', { 'fields': [ 'position_x', 'position_y', 'width', 'height' ] }), ('Display Configuration', { 'fields': [ 'display_config', 'color_scheme' ] }), ('Refresh Settings', { 'fields': [ 'auto_refresh', 'refresh_interval' ] }), ('Widget Status', { 'fields': [ 'is_active' ] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at' ], 'classes': ['collapse'] }) ] def dashboard_name(self, obj): """Display dashboard name.""" return obj.dashboard.name dashboard_name.short_description = 'Dashboard' def position_display(self, obj): """Display position.""" return f"({obj.position_x}, {obj.position_y})" position_display.short_description = 'Position' def size_display(self, obj): """Display size.""" return f"{obj.width}×{obj.height}" size_display.short_description = 'Size' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(dashboard__tenant=request.user.tenant) return qs.select_related('dashboard', 'data_source') @admin.register(DataSource) class DataSourceAdmin(admin.ModelAdmin): """ Admin interface for data sources. """ list_display = [ 'name', 'source_type', 'connection_type', 'health_status_display', 'last_health_check', 'cache_duration_display', 'is_active' ] list_filter = [ 'tenant', 'source_type', 'connection_type', 'is_healthy', 'is_active' ] search_fields = [ 'name', 'description' ] readonly_fields = [ 'source_id', 'is_healthy', 'last_health_check', 'created_at', 'updated_at' ] fieldsets = [ ('Data Source Information', { 'fields': [ 'source_id', 'tenant', 'name', 'description' ] }), ('Source Configuration', { 'fields': [ 'source_type', 'connection_type' ] }), ('Connection Details', { 'fields': [ 'connection_config', 'authentication_config' ] }), ('Query Configuration', { 'fields': [ 'query_template', 'parameters' ] }), ('Data Processing', { 'fields': [ 'data_transformation', 'cache_duration' ] }), ('Health Monitoring', { 'fields': [ 'is_healthy', 'last_health_check', 'health_check_interval' ] }), ('Source Status', { 'fields': [ 'is_active' ] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] def health_status_display(self, obj): """Display health status with color coding.""" if obj.is_healthy: return format_html('✓ Healthy') return format_html('✗ Unhealthy') health_status_display.short_description = 'Health' def cache_duration_display(self, obj): """Display cache duration.""" if obj.cache_duration >= 3600: hours = obj.cache_duration // 3600 return f"{hours}h" elif obj.cache_duration >= 60: minutes = obj.cache_duration // 60 return f"{minutes}m" return f"{obj.cache_duration}s" cache_duration_display.short_description = 'Cache' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(tenant=request.user.tenant) return qs class ReportExecutionInline(admin.TabularInline): """ Inline admin for report executions. """ model = ReportExecution extra = 0 fields = [ 'execution_type', 'started_at', 'status', 'duration_seconds', 'record_count' ] readonly_fields = [ 'execution_id', 'started_at', 'completed_at', 'duration_seconds', 'record_count' ] @admin.register(Report) class ReportAdmin(admin.ModelAdmin): """ Admin interface for reports. """ list_display = [ 'name', 'report_type', 'output_format', 'schedule_type', 'next_execution', 'execution_count_display', 'is_active' ] list_filter = [ 'tenant', 'report_type', 'output_format', 'schedule_type', 'is_active' ] search_fields = [ 'name', 'description' ] readonly_fields = [ 'report_id', 'created_at', 'updated_at' ] fieldsets = [ ('Report Information', { 'fields': [ 'report_id', 'tenant', 'name', 'description' ] }), ('Report Configuration', { 'fields': [ 'report_type', 'data_source', 'query_config' ] }), ('Output Configuration', { 'fields': [ 'output_format', 'template_config' ] }), ('Scheduling', { 'fields': [ 'schedule_type', 'schedule_config', 'next_execution' ] }), ('Distribution', { 'fields': [ 'recipients', 'distribution_config' ] }), ('Report Status', { 'fields': [ 'is_active' ] }), ('Summary Information', { 'fields': [ 'execution_count' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] inlines = [ReportExecutionInline] def execution_count_display(self, obj): """Display execution count.""" return obj.execution_count execution_count_display.short_description = 'Executions' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(tenant=request.user.tenant) return qs.select_related('data_source') @admin.register(ReportExecution) class ReportExecutionAdmin(admin.ModelAdmin): """ Admin interface for report executions. """ list_display = [ 'report_name', 'execution_type', 'started_at', 'status', 'duration_display', 'record_count', 'output_size_display' ] list_filter = [ 'report__tenant', 'execution_type', 'status', 'started_at' ] search_fields = [ 'report__name', 'executed_by__username' ] readonly_fields = [ 'execution_id', 'started_at', 'completed_at', 'duration_seconds', 'output_size_bytes', 'record_count' ] fieldsets = [ ('Execution Information', { 'fields': [ 'execution_id', 'report', 'execution_type' ] }), ('Timing', { 'fields': [ 'started_at', 'completed_at', 'duration_seconds' ] }), ('Status and Results', { 'fields': [ 'status', 'error_message' ] }), ('Output', { 'fields': [ 'output_file_path', 'output_size_bytes', 'record_count' ] }), ('Parameters', { 'fields': [ 'execution_parameters' ] }), ('Metadata', { 'fields': [ 'executed_by' ], 'classes': ['collapse'] }) ] date_hierarchy = 'started_at' def report_name(self, obj): """Display report name.""" return obj.report.name report_name.short_description = 'Report' def duration_display(self, obj): """Display duration.""" if obj.duration_seconds: if obj.duration_seconds >= 3600: hours = obj.duration_seconds // 3600 minutes = (obj.duration_seconds % 3600) // 60 return f"{hours}h {minutes}m" elif obj.duration_seconds >= 60: minutes = obj.duration_seconds // 60 seconds = obj.duration_seconds % 60 return f"{minutes}m {seconds}s" return f"{obj.duration_seconds}s" return "-" duration_display.short_description = 'Duration' def output_size_display(self, obj): """Display output size.""" if obj.output_size_bytes: if obj.output_size_bytes >= 1024 * 1024: mb = obj.output_size_bytes / (1024 * 1024) return f"{mb:.1f} MB" elif obj.output_size_bytes >= 1024: kb = obj.output_size_bytes / 1024 return f"{kb:.1f} KB" return f"{obj.output_size_bytes} B" return "-" output_size_display.short_description = 'Size' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(report__tenant=request.user.tenant) return qs.select_related('report', 'executed_by') class MetricValueInline(admin.TabularInline): """ Inline admin for metric values. """ model = MetricValue extra = 0 fields = [ 'value', 'period_start', 'period_end', 'data_quality_score', 'confidence_level' ] readonly_fields = [ 'value_id', 'calculation_timestamp', 'calculation_duration_ms' ] @admin.register(MetricDefinition) class MetricDefinitionAdmin(admin.ModelAdmin): """ Admin interface for metric definitions. """ list_display = [ 'name', 'metric_type', 'aggregation_period', 'target_value', 'unit_of_measure', 'value_count_display', 'is_active' ] list_filter = [ 'tenant', 'metric_type', 'aggregation_period', 'is_active' ] search_fields = [ 'name', 'description' ] readonly_fields = [ 'metric_id', 'created_at', 'updated_at' ] fieldsets = [ ('Metric Information', { 'fields': [ 'metric_id', 'tenant', 'name', 'description' ] }), ('Metric Configuration', { 'fields': [ 'metric_type', 'data_source', 'calculation_config' ] }), ('Aggregation', { 'fields': [ 'aggregation_period', 'aggregation_config' ] }), ('Thresholds and Targets', { 'fields': [ 'target_value', 'warning_threshold', 'critical_threshold' ] }), ('Display Configuration', { 'fields': [ 'unit_of_measure', 'decimal_places', 'display_format' ] }), ('Metric Status', { 'fields': [ 'is_active' ] }), ('Summary Information', { 'fields': [ 'value_count' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] inlines = [MetricValueInline] def value_count_display(self, obj): """Display value count.""" return obj.value_count value_count_display.short_description = 'Values' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(tenant=request.user.tenant) return qs.select_related('data_source') @admin.register(MetricValue) class MetricValueAdmin(admin.ModelAdmin): """ Admin interface for metric values. """ list_display = [ 'metric_name', 'value', 'period_start', 'period_end', 'threshold_status_display', 'data_quality_score', 'calculation_timestamp' ] list_filter = [ 'metric_definition__tenant', 'metric_definition__metric_type', 'period_start', 'calculation_timestamp' ] search_fields = [ 'metric_definition__name' ] readonly_fields = [ 'value_id', 'is_above_target', 'threshold_status', 'calculation_timestamp', 'calculation_duration_ms' ] fieldsets = [ ('Metric Value Information', { 'fields': [ 'value_id', 'metric_definition' ] }), ('Value Details', { 'fields': [ 'value', 'period_start', 'period_end' ] }), ('Context', { 'fields': [ 'dimensions', 'metadata' ] }), ('Quality Indicators', { 'fields': [ 'data_quality_score', 'confidence_level' ] }), ('Calculation Details', { 'fields': [ 'calculation_timestamp', 'calculation_duration_ms' ] }), ('Analysis', { 'fields': [ 'is_above_target', 'threshold_status' ], 'classes': ['collapse'] }) ] date_hierarchy = 'period_start' def metric_name(self, obj): """Display metric name.""" return obj.metric_definition.name metric_name.short_description = 'Metric' def threshold_status_display(self, obj): """Display threshold status with color coding.""" status = obj.threshold_status if status == 'CRITICAL': return format_html('🔴 Critical') elif status == 'WARNING': return format_html('🟡 Warning') return format_html('🟢 Normal') threshold_status_display.short_description = 'Status' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(metric_definition__tenant=request.user.tenant) return qs.select_related('metric_definition') # Customize admin site admin.site.site_header = "Hospital Management System - Analytics" admin.site.site_title = "Analytics Admin" admin.site.index_title = "Analytics Administration"