HH/apps/notifications/admin.py
2026-03-28 14:03:56 +03:00

303 lines
11 KiB
Python

"""
Notifications admin
"""
from django.contrib import admin
from django.utils.html import format_html
from .models import NotificationLog, NotificationTemplate, UserNotification
from .settings_models import HospitalNotificationSettings, NotificationSettingsLog
@admin.register(NotificationLog)
class NotificationLogAdmin(admin.ModelAdmin):
"""Notification log admin"""
list_display = [
"channel",
"recipient",
"subject_preview",
"status_badge",
"provider",
"retry_count",
"sent_at",
"created_at",
]
list_filter = ["channel", "status", "provider", "created_at", "sent_at"]
search_fields = ["recipient", "subject", "message", "provider_message_id"]
ordering = ["-created_at"]
date_hierarchy = "created_at"
fieldsets = (
("Delivery", {"fields": ("channel", "recipient", "subject", "message")}),
("Status", {"fields": ("status", "sent_at", "delivered_at", "error", "retry_count")}),
("Provider", {"fields": ("provider", "provider_message_id", "provider_response"), "classes": ("collapse",)}),
("Related Object", {"fields": ("content_type", "object_id"), "classes": ("collapse",)}),
("Metadata", {"fields": ("metadata", "created_at", "updated_at"), "classes": ("collapse",)}),
)
readonly_fields = [
"sent_at",
"delivered_at",
"provider_message_id",
"provider_response",
"created_at",
"updated_at",
]
def has_add_permission(self, request):
# Notifications should only be created programmatically
return False
def has_delete_permission(self, request, obj=None):
# Keep notification logs for compliance
return False
def subject_preview(self, obj):
"""Show preview of subject"""
if obj.subject:
return obj.subject[:50] + "..." if len(obj.subject) > 50 else obj.subject
return "-"
subject_preview.short_description = "Subject"
def status_badge(self, obj):
"""Display status with color badge"""
colors = {
"pending": "warning",
"sending": "info",
"sent": "success",
"delivered": "success",
"failed": "danger",
"bounced": "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"
@admin.register(NotificationTemplate)
class NotificationTemplateAdmin(admin.ModelAdmin):
"""Notification template admin"""
list_display = ["name", "template_type", "is_active", "created_at"]
list_filter = ["template_type", "is_active"]
search_fields = ["name", "description"]
ordering = ["name"]
fieldsets = (
(None, {"fields": ("name", "template_type", "description")}),
("SMS Templates", {"fields": ("sms_template", "sms_template_ar"), "classes": ("collapse",)}),
("WhatsApp Templates", {"fields": ("whatsapp_template", "whatsapp_template_ar"), "classes": ("collapse",)}),
(
"Email Templates",
{
"fields": ("email_subject", "email_subject_ar", "email_template", "email_template_ar"),
"classes": ("collapse",),
},
),
("Status", {"fields": ("is_active",)}),
("Metadata", {"fields": ("created_at", "updated_at")}),
)
readonly_fields = ["created_at", "updated_at"]
@admin.register(HospitalNotificationSettings)
class HospitalNotificationSettingsAdmin(admin.ModelAdmin):
"""Hospital notification settings admin"""
list_display = ["hospital", "notifications_enabled", "updated_at"]
list_filter = ["notifications_enabled", "updated_at"]
search_fields = ["hospital__name"]
fieldsets = (
("Hospital", {"fields": ("hospital",)}),
("Master Control", {"fields": ("notifications_enabled",)}),
(
"Complaint Notifications",
{
"fields": (
"complaint_acknowledgment_email",
"complaint_acknowledgment_sms",
"complaint_acknowledgment_whatsapp",
"complaint_assigned_email",
"complaint_assigned_sms",
"complaint_assigned_whatsapp",
"complaint_status_changed_email",
"complaint_status_changed_sms",
"complaint_status_changed_whatsapp",
"complaint_resolved_email",
"complaint_resolved_sms",
"complaint_resolved_whatsapp",
"complaint_closed_email",
"complaint_closed_sms",
"complaint_closed_whatsapp",
),
"classes": ("collapse",),
},
),
(
"Explanation Notifications",
{
"fields": (
"explanation_requested_email",
"explanation_requested_sms",
"explanation_requested_whatsapp",
"explanation_reminder_email",
"explanation_reminder_sms",
"explanation_reminder_whatsapp",
"explanation_overdue_email",
"explanation_overdue_sms",
"explanation_overdue_whatsapp",
"explanation_received_email",
"explanation_received_sms",
"explanation_received_whatsapp",
"explanation_manager_cc",
),
"classes": ("collapse",),
},
),
(
"Survey Notifications",
{
"fields": (
"survey_invitation_email",
"survey_invitation_sms",
"survey_invitation_whatsapp",
"survey_reminder_email",
"survey_reminder_sms",
"survey_reminder_whatsapp",
"survey_completed_email",
"survey_completed_sms",
),
"classes": ("collapse",),
},
),
(
"Action Notifications",
{
"fields": (
"action_assigned_email",
"action_assigned_sms",
"action_assigned_whatsapp",
"action_due_soon_email",
"action_due_soon_sms",
"action_overdue_email",
"action_overdue_sms",
),
"classes": ("collapse",),
},
),
(
"SLA Notifications",
{
"fields": (
"sla_reminder_email",
"sla_reminder_sms",
"sla_breach_email",
"sla_breach_sms",
"sla_breach_whatsapp",
),
"classes": ("collapse",),
},
),
(
"Onboarding Notifications",
{
"fields": (
"onboarding_invitation_email",
"onboarding_invitation_sms",
"onboarding_invitation_whatsapp",
"onboarding_reminder_email",
"onboarding_reminder_sms",
"onboarding_reminder_whatsapp",
"onboarding_completion_email",
"onboarding_completion_sms",
),
"classes": ("collapse",),
},
),
(
"Quiet Hours",
{
"fields": ("quiet_hours_enabled", "quiet_hours_start", "quiet_hours_end"),
},
),
(
"Retry Settings",
{
"fields": ("retry_failed_notifications", "max_retries"),
},
),
)
readonly_fields = ["created_at", "updated_at"]
@admin.register(NotificationSettingsLog)
class NotificationSettingsLogAdmin(admin.ModelAdmin):
"""Notification settings change log admin"""
list_display = ["hospital", "field_name", "old_value", "new_value", "changed_by", "created_at"]
list_filter = ["hospital", "created_at"]
search_fields = ["field_name", "changed_by__email", "changed_by__first_name", "changed_by__last_name"]
ordering = ["-created_at"]
readonly_fields = ["hospital", "changed_by", "field_name", "old_value", "new_value", "created_at"]
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
@admin.register(UserNotification)
class UserNotificationAdmin(admin.ModelAdmin):
"""User notification admin"""
list_display = ["user", "notification_type", "title_preview", "is_read", "is_dismissed", "created_at"]
list_filter = ["notification_type", "is_read", "is_dismissed", "created_at"]
search_fields = ["user__email", "title", "message"]
ordering = ["-created_at"]
date_hierarchy = "created_at"
fieldsets = (
("User", {"fields": ("user",)}),
("Content", {"fields": ("title", "title_ar", "message", "message_ar")}),
("Type & Status", {"fields": ("notification_type", "is_read", "read_at", "is_dismissed", "dismissed_at")}),
("Links", {"fields": ("action_url", "email_log"), "classes": ("collapse",)}),
("Related Object", {"fields": ("content_type", "object_id"), "classes": ("collapse",)}),
("Timestamps", {"fields": ("created_at", "updated_at"), "classes": ("collapse",)}),
)
readonly_fields = ["created_at", "updated_at", "read_at", "dismissed_at"]
def title_preview(self, obj):
"""Short title preview"""
return obj.title[:50] + "..." if len(obj.title) > 50 else obj.title
title_preview.short_description = "Title"
actions = ["mark_as_read", "mark_as_dismissed"]
def mark_as_read(self, request, queryset):
"""Mark selected notifications as read"""
for notification in queryset:
notification.mark_as_read()
self.message_user(request, f"{queryset.count()} notifications marked as read")
mark_as_read.short_description = "Mark selected as read"
def mark_as_dismissed(self, request, queryset):
"""Mark selected notifications as dismissed"""
for notification in queryset:
notification.mark_as_dismissed()
self.message_user(request, f"{queryset.count()} notifications dismissed")
mark_as_dismissed.short_description = "Dismiss selected"