303 lines
11 KiB
Python
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"
|