258 lines
8.4 KiB
Python
258 lines
8.4 KiB
Python
"""
|
|
PX Action Center admin
|
|
"""
|
|
|
|
from django.contrib import admin
|
|
from django.utils.html import format_html, mark_safe
|
|
|
|
from .models import PXAction, PXActionAttachment, PXActionLog, PXActionSLAConfig, RoutingRule
|
|
|
|
|
|
class PXActionLogInline(admin.TabularInline):
|
|
"""Inline admin for action logs"""
|
|
|
|
model = PXActionLog
|
|
extra = 1
|
|
fields = ["log_type", "message", "created_by", "created_at"]
|
|
readonly_fields = ["created_at"]
|
|
ordering = ["-created_at"]
|
|
|
|
|
|
class PXActionAttachmentInline(admin.TabularInline):
|
|
"""Inline admin for action attachments"""
|
|
|
|
model = PXActionAttachment
|
|
extra = 0
|
|
fields = ["file", "filename", "is_evidence", "uploaded_by", "description"]
|
|
readonly_fields = ["file_size"]
|
|
|
|
|
|
@admin.register(PXAction)
|
|
class PXActionAdmin(admin.ModelAdmin):
|
|
"""PX Action admin"""
|
|
|
|
list_display = [
|
|
"title_preview",
|
|
"source_type",
|
|
"hospital",
|
|
"category",
|
|
"severity_badge",
|
|
"status_badge",
|
|
"sla_indicator",
|
|
"assigned_to",
|
|
"escalation_level",
|
|
"created_at",
|
|
]
|
|
list_filter = [
|
|
"status",
|
|
"source_type",
|
|
"severity",
|
|
"priority",
|
|
"category",
|
|
"is_overdue",
|
|
"requires_approval",
|
|
"hospital",
|
|
"created_at",
|
|
]
|
|
search_fields = ["title", "description", "metadata"]
|
|
ordering = ["-created_at"]
|
|
date_hierarchy = "created_at"
|
|
inlines = [PXActionLogInline, PXActionAttachmentInline]
|
|
|
|
fieldsets = (
|
|
("Source", {"fields": ("source_type", "content_type", "object_id")}),
|
|
("Action Details", {"fields": ("title", "description", "category")}),
|
|
("Organization", {"fields": ("hospital", "department")}),
|
|
("Classification", {"fields": ("priority", "severity")}),
|
|
("Status & Assignment", {"fields": ("status", "assigned_to", "assigned_at")}),
|
|
("SLA Tracking", {"fields": ("due_at", "is_overdue", "reminder_sent_at", "escalated_at", "escalation_level")}),
|
|
("Approval", {"fields": ("requires_approval", "approved_by", "approved_at")}),
|
|
("Closure", {"fields": ("closed_at", "closed_by")}),
|
|
("Action Plan & Outcome", {"fields": ("action_plan", "outcome"), "classes": ("collapse",)}),
|
|
("Metadata", {"fields": ("metadata", "created_at", "updated_at"), "classes": ("collapse",)}),
|
|
)
|
|
|
|
readonly_fields = [
|
|
"assigned_at",
|
|
"reminder_sent_at",
|
|
"escalated_at",
|
|
"approved_at",
|
|
"closed_at",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.select_related("hospital", "department", "assigned_to", "approved_by", "closed_by", "content_type")
|
|
|
|
def title_preview(self, obj):
|
|
"""Show preview of title"""
|
|
return obj.title[:60] + "..." if len(obj.title) > 60 else obj.title
|
|
|
|
title_preview.short_description = "Title"
|
|
|
|
def severity_badge(self, obj):
|
|
"""Display severity with color badge"""
|
|
colors = {
|
|
"low": "info",
|
|
"medium": "warning",
|
|
"high": "danger",
|
|
"critical": "danger",
|
|
}
|
|
color = colors.get(obj.severity, "secondary")
|
|
return format_html('<span class="badge bg-{}">{}</span>', color, obj.get_severity_display())
|
|
|
|
severity_badge.short_description = "Severity"
|
|
|
|
def status_badge(self, obj):
|
|
"""Display status with color badge"""
|
|
colors = {
|
|
"open": "danger",
|
|
"in_progress": "warning",
|
|
"pending_approval": "info",
|
|
"approved": "success",
|
|
"closed": "success",
|
|
"cancelled": "secondary",
|
|
}
|
|
color = colors.get(obj.status, "secondary")
|
|
return format_html('<span class="badge bg-{}">{}</span>', color, obj.get_status_display())
|
|
|
|
status_badge.short_description = "Status"
|
|
|
|
def sla_indicator(self, obj):
|
|
"""Display SLA status"""
|
|
if obj.is_overdue:
|
|
return format_html('<span class="badge bg-danger">OVERDUE (Level {})</span>', obj.escalation_level)
|
|
|
|
from django.utils import timezone
|
|
|
|
time_remaining = obj.due_at - timezone.now()
|
|
hours_remaining = time_remaining.total_seconds() / 3600
|
|
|
|
if hours_remaining < 4:
|
|
return mark_safe('<span class="badge bg-warning">DUE SOON</span>')
|
|
else:
|
|
return mark_safe('<span class="badge bg-success">ON TIME</span>')
|
|
|
|
sla_indicator.short_description = "SLA"
|
|
|
|
|
|
@admin.register(PXActionLog)
|
|
class PXActionLogAdmin(admin.ModelAdmin):
|
|
"""PX Action log admin"""
|
|
|
|
list_display = ["action", "log_type", "message_preview", "created_by", "created_at"]
|
|
list_filter = ["log_type", "created_at"]
|
|
search_fields = ["message", "action__title"]
|
|
ordering = ["-created_at"]
|
|
|
|
fieldsets = (
|
|
(None, {"fields": ("action", "log_type", "message")}),
|
|
("Status Change", {"fields": ("old_status", "new_status"), "classes": ("collapse",)}),
|
|
("Details", {"fields": ("created_by", "metadata")}),
|
|
("Metadata", {"fields": ("created_at", "updated_at")}),
|
|
)
|
|
|
|
readonly_fields = ["created_at", "updated_at"]
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.select_related("action", "created_by")
|
|
|
|
def message_preview(self, obj):
|
|
"""Show preview of message"""
|
|
return obj.message[:100] + "..." if len(obj.message) > 100 else obj.message
|
|
|
|
message_preview.short_description = "Message"
|
|
|
|
|
|
@admin.register(PXActionAttachment)
|
|
class PXActionAttachmentAdmin(admin.ModelAdmin):
|
|
"""PX Action attachment admin"""
|
|
|
|
list_display = ["action", "filename", "is_evidence", "file_size", "uploaded_by", "created_at"]
|
|
list_filter = ["is_evidence", "file_type", "created_at"]
|
|
search_fields = ["filename", "description", "action__title"]
|
|
ordering = ["-created_at"]
|
|
|
|
fieldsets = (
|
|
(None, {"fields": ("action", "file", "filename", "file_type", "file_size")}),
|
|
("Details", {"fields": ("is_evidence", "uploaded_by", "description")}),
|
|
("Metadata", {"fields": ("created_at", "updated_at")}),
|
|
)
|
|
|
|
readonly_fields = ["file_size", "created_at", "updated_at"]
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.select_related("action", "uploaded_by")
|
|
|
|
|
|
@admin.register(PXActionSLAConfig)
|
|
class PXActionSLAConfigAdmin(admin.ModelAdmin):
|
|
"""PX Action SLA configuration admin"""
|
|
|
|
list_display = [
|
|
"name",
|
|
"hospital",
|
|
"department",
|
|
"critical_hours",
|
|
"high_hours",
|
|
"medium_hours",
|
|
"low_hours",
|
|
"auto_escalate",
|
|
"is_active",
|
|
]
|
|
list_filter = ["is_active", "auto_escalate", "hospital"]
|
|
search_fields = ["name"]
|
|
ordering = ["hospital", "name"]
|
|
|
|
fieldsets = (
|
|
(None, {"fields": ("name", "hospital", "department")}),
|
|
("SLA Durations (hours)", {"fields": ("critical_hours", "high_hours", "medium_hours", "low_hours")}),
|
|
("Reminder Configuration", {"fields": ("reminder_hours_before",)}),
|
|
("Escalation Configuration", {"fields": ("auto_escalate", "escalation_delay_hours")}),
|
|
("Status", {"fields": ("is_active",)}),
|
|
("Metadata", {"fields": ("created_at", "updated_at")}),
|
|
)
|
|
|
|
readonly_fields = ["created_at", "updated_at"]
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.select_related("hospital", "department")
|
|
|
|
|
|
@admin.register(RoutingRule)
|
|
class RoutingRuleAdmin(admin.ModelAdmin):
|
|
"""Routing rule admin"""
|
|
|
|
list_display = [
|
|
"name",
|
|
"source_type",
|
|
"category",
|
|
"severity",
|
|
"hospital",
|
|
"assign_to_role",
|
|
"priority",
|
|
"is_active",
|
|
]
|
|
list_filter = ["source_type", "severity", "is_active", "hospital"]
|
|
search_fields = ["name", "description"]
|
|
ordering = ["-priority", "name"]
|
|
|
|
fieldsets = (
|
|
(None, {"fields": ("name", "description", "priority")}),
|
|
("Conditions", {"fields": ("source_type", "category", "severity", "hospital", "department")}),
|
|
("Routing Target", {"fields": ("assign_to_role", "assign_to_user", "assign_to_department")}),
|
|
("Status", {"fields": ("is_active",)}),
|
|
("Metadata", {"fields": ("created_at", "updated_at")}),
|
|
)
|
|
|
|
readonly_fields = ["created_at", "updated_at"]
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.select_related("hospital", "department", "assign_to_user", "assign_to_department")
|