HH/apps/notifications/settings_models.py

502 lines
16 KiB
Python

"""
Notification Settings Models - Configurable notification preferences
This module provides per-hospital notification configuration allowing
admins to toggle which notifications are sent via which channels.
"""
from django.db import models
from django.core.cache import cache
from apps.core.models import BaseChoices, TimeStampedModel, UUIDModel
class NotificationCategory(BaseChoices):
"""Categories of notifications"""
COMPLAINT = 'complaint', 'Complaint Notifications'
SURVEY = 'survey', 'Survey Notifications'
ACTION = 'action', 'Action Notifications'
SYSTEM = 'system', 'System Notifications'
class NotificationEvent(BaseChoices):
"""
All notification events that can be configured.
Each event can have different channels enabled/disabled.
"""
# Complaint Events
COMPLAINT_ACKNOWLEDGMENT = 'complaint_acknowledgment', 'Complaint Acknowledgment'
COMPLAINT_ASSIGNED = 'complaint_assigned', 'Complaint Assigned'
COMPLAINT_STATUS_CHANGED = 'complaint_status_changed', 'Complaint Status Changed'
COMPLAINT_RESOLVED = 'complaint_resolved', 'Complaint Resolved'
COMPLAINT_CLOSED = 'complaint_closed', 'Complaint Closed'
# Explanation Events
EXPLANATION_REQUESTED = 'explanation_requested', 'Explanation Requested'
EXPLANATION_REMINDER = 'explanation_reminder', 'Explanation Reminder (24h)'
EXPLANATION_OVERDUE = 'explanation_overdue', 'Explanation Overdue/Escalation'
EXPLANATION_RECEIVED = 'explanation_received', 'Explanation Received'
# Survey Events
SURVEY_INVITATION = 'survey_invitation', 'Survey Invitation'
SURVEY_REMINDER = 'survey_reminder', 'Survey Reminder'
SURVEY_COMPLETED = 'survey_completed', 'Survey Completed'
# Action Events
ACTION_ASSIGNED = 'action_assigned', 'Action Assigned'
ACTION_DUE_SOON = 'action_due_soon', 'Action Due Soon'
ACTION_OVERDUE = 'action_overdue', 'Action Overdue'
# SLA Events
SLA_REMINDER = 'sla_reminder', 'SLA Reminder'
SLA_BREACH = 'sla_breach', 'SLA Breach Alert'
# Onboarding Events
ONBOARDING_INVITATION = 'onboarding_invitation', 'Onboarding Invitation'
ONBOARDING_REMINDER = 'onboarding_reminder', 'Onboarding Reminder'
ONBOARDING_COMPLETION = 'onboarding_completion', 'Onboarding Completion'
class HospitalNotificationSettings(UUIDModel, TimeStampedModel):
"""
Per-hospital notification configuration.
Allows hospital admins to customize which notifications are sent
and via which channels (Email, SMS, WhatsApp).
"""
hospital = models.OneToOneField(
'organizations.Hospital',
on_delete=models.CASCADE,
related_name='notification_settings',
help_text='Hospital these settings apply to'
)
# ==================== COMPLAINT NOTIFICATIONS ====================
# Complaint Acknowledgment (to patient)
complaint_acknowledgment_email = models.BooleanField(
default=True,
help_text='Send email when complaint is acknowledged'
)
complaint_acknowledgment_sms = models.BooleanField(
default=False,
help_text='Send SMS when complaint is acknowledged'
)
complaint_acknowledgment_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when complaint is acknowledged'
)
# Complaint Assigned (to assignee)
complaint_assigned_email = models.BooleanField(
default=True,
help_text='Send email when complaint is assigned'
)
complaint_assigned_sms = models.BooleanField(
default=False,
help_text='Send SMS when complaint is assigned'
)
complaint_assigned_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when complaint is assigned'
)
# Complaint Status Changed (to relevant parties)
complaint_status_changed_email = models.BooleanField(
default=True,
help_text='Send email when complaint status changes'
)
complaint_status_changed_sms = models.BooleanField(
default=False,
help_text='Send SMS when complaint status changes'
)
complaint_status_changed_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when complaint status changes'
)
# Complaint Resolved (to patient)
complaint_resolved_email = models.BooleanField(
default=True,
help_text='Send email when complaint is resolved'
)
complaint_resolved_sms = models.BooleanField(
default=False,
help_text='Send SMS when complaint is resolved'
)
complaint_resolved_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when complaint is resolved'
)
# Complaint Closed (to patient)
complaint_closed_email = models.BooleanField(
default=True,
help_text='Send email when complaint is closed'
)
complaint_closed_sms = models.BooleanField(
default=False,
help_text='Send SMS when complaint is closed'
)
complaint_closed_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when complaint is closed'
)
# ==================== EXPLANATION NOTIFICATIONS ====================
# Explanation Requested (to staff)
explanation_requested_email = models.BooleanField(
default=True,
help_text='Send email when explanation is requested from staff'
)
explanation_requested_sms = models.BooleanField(
default=False,
help_text='Send SMS when explanation is requested from staff'
)
explanation_requested_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when explanation is requested from staff'
)
# Explanation Reminder at 24h (to staff)
explanation_reminder_email = models.BooleanField(
default=True,
help_text='Send email reminder at 24h before SLA expires'
)
explanation_reminder_sms = models.BooleanField(
default=False,
help_text='Send SMS reminder at 24h before SLA expires'
)
explanation_reminder_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp reminder at 24h before SLA expires'
)
# Explanation Overdue/Escalation (to manager)
explanation_overdue_email = models.BooleanField(
default=True,
help_text='Send email when explanation is overdue (escalation)'
)
explanation_overdue_sms = models.BooleanField(
default=False,
help_text='Send SMS when explanation is overdue (escalation)'
)
explanation_overdue_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when explanation is overdue (escalation)'
)
# Explanation Received (to assignee)
explanation_received_email = models.BooleanField(
default=True,
help_text='Send email when staff submits explanation'
)
explanation_received_sms = models.BooleanField(
default=False,
help_text='Send SMS when staff submits explanation'
)
explanation_received_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when staff submits explanation'
)
# Manager CC on Explanation Request
explanation_manager_cc = models.BooleanField(
default=False,
help_text='CC manager when explanation is requested from staff'
)
# ==================== SURVEY NOTIFICATIONS ====================
# Survey Invitation (to patient)
survey_invitation_email = models.BooleanField(
default=True,
help_text='Send email survey invitation to patient'
)
survey_invitation_sms = models.BooleanField(
default=True,
help_text='Send SMS survey invitation to patient'
)
survey_invitation_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp survey invitation to patient'
)
# Survey Reminder (to patient)
survey_reminder_email = models.BooleanField(
default=True,
help_text='Send email survey reminder'
)
survey_reminder_sms = models.BooleanField(
default=True,
help_text='Send SMS survey reminder'
)
survey_reminder_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp survey reminder'
)
# Survey Completed (to admin)
survey_completed_email = models.BooleanField(
default=True,
help_text='Send email when patient completes survey'
)
survey_completed_sms = models.BooleanField(
default=False,
help_text='Send SMS when patient completes survey'
)
# ==================== ACTION NOTIFICATIONS ====================
# Action Assigned (to assignee)
action_assigned_email = models.BooleanField(
default=True,
help_text='Send email when action is assigned'
)
action_assigned_sms = models.BooleanField(
default=False,
help_text='Send SMS when action is assigned'
)
action_assigned_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when action is assigned'
)
# Action Due Soon (to assignee)
action_due_soon_email = models.BooleanField(
default=True,
help_text='Send email when action is due soon'
)
action_due_soon_sms = models.BooleanField(
default=False,
help_text='Send SMS when action is due soon'
)
# Action Overdue (to manager)
action_overdue_email = models.BooleanField(
default=True,
help_text='Send email when action is overdue'
)
action_overdue_sms = models.BooleanField(
default=False,
help_text='Send SMS when action is overdue'
)
# ==================== SLA NOTIFICATIONS ====================
# SLA Reminder (to assignee)
sla_reminder_email = models.BooleanField(
default=True,
help_text='Send email SLA reminder'
)
sla_reminder_sms = models.BooleanField(
default=False,
help_text='Send SMS SLA reminder'
)
# SLA Breach (to manager)
sla_breach_email = models.BooleanField(
default=True,
help_text='Send email when SLA is breached'
)
sla_breach_sms = models.BooleanField(
default=False,
help_text='Send SMS when SLA is breached'
)
sla_breach_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp when SLA is breached'
)
# ==================== ONBOARDING NOTIFICATIONS ====================
# Onboarding Invitation (to new user)
onboarding_invitation_email = models.BooleanField(
default=True,
help_text='Send email invitation to new provisional users'
)
onboarding_invitation_sms = models.BooleanField(
default=False,
help_text='Send SMS invitation to new provisional users'
)
onboarding_invitation_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp invitation to new provisional users'
)
# Onboarding Reminder (to pending user)
onboarding_reminder_email = models.BooleanField(
default=True,
help_text='Send email reminder to complete onboarding'
)
onboarding_reminder_sms = models.BooleanField(
default=False,
help_text='Send SMS reminder to complete onboarding'
)
onboarding_reminder_whatsapp = models.BooleanField(
default=False,
help_text='Send WhatsApp reminder to complete onboarding'
)
# Onboarding Completion (to admins)
onboarding_completion_email = models.BooleanField(
default=True,
help_text='Send email notification to admins when user completes onboarding'
)
onboarding_completion_sms = models.BooleanField(
default=False,
help_text='Send SMS notification to admins when user completes onboarding'
)
# ==================== GLOBAL SETTINGS ====================
# Master switches
notifications_enabled = models.BooleanField(
default=True,
help_text='Master switch to enable/disable all notifications'
)
# Quiet hours
quiet_hours_enabled = models.BooleanField(
default=False,
help_text='Enable quiet hours (no SMS/WhatsApp during these hours)'
)
quiet_hours_start = models.TimeField(
default='22:00',
help_text='Start of quiet hours (e.g., 22:00 for 10 PM)'
)
quiet_hours_end = models.TimeField(
default='08:00',
help_text='End of quiet hours (e.g., 08:00 for 8 AM)'
)
# Retry configuration
retry_failed_notifications = models.BooleanField(
default=True,
help_text='Automatically retry failed notifications'
)
max_retries = models.IntegerField(
default=3,
help_text='Maximum number of retry attempts'
)
class Meta:
verbose_name = 'Hospital Notification Settings'
verbose_name_plural = 'Hospital Notification Settings'
def __str__(self):
return f"Notification Settings for {self.hospital}"
def save(self, *args, **kwargs):
"""Clear cache when settings are updated"""
super().save(*args, **kwargs)
cache.delete(f'notification_settings_{self.hospital_id}')
@classmethod
def get_for_hospital(cls, hospital_id):
"""
Get or create settings for a hospital with caching.
"""
cache_key = f'notification_settings_{hospital_id}'
settings = cache.get(cache_key)
if settings is None:
settings, created = cls.objects.get_or_create(
hospital_id=hospital_id,
defaults={}
)
cache.set(cache_key, settings, 300) # Cache for 5 minutes
return settings
def is_channel_enabled(self, event, channel):
"""
Check if a specific channel is enabled for an event.
Args:
event: NotificationEvent value (e.g., 'explanation_requested')
channel: 'email', 'sms', or 'whatsapp'
Returns:
bool: True if channel is enabled for this event
"""
if not self.notifications_enabled:
return False
field_name = f"{event}_{channel}"
return getattr(self, field_name, False)
def get_enabled_channels(self, event):
"""
Get list of enabled channels for a specific event.
Args:
event: NotificationEvent value
Returns:
list: List of enabled channel names ['email', 'sms', ...]
"""
if not self.notifications_enabled:
return []
channels = []
for channel in ['email', 'sms', 'whatsapp']:
if self.is_channel_enabled(event, channel):
channels.append(channel)
return channels
def is_quiet_hours(self, current_time=None):
"""
Check if current time is within quiet hours.
Returns:
bool: True if quiet hours (SMS/WhatsApp should not be sent)
"""
if not self.quiet_hours_enabled:
return False
from django.utils import timezone
if current_time is None:
current_time = timezone.now().time()
start = self.quiet_hours_start
end = self.quiet_hours_end
# Handle overnight quiet hours (e.g., 22:00 - 08:00)
if start > end:
return current_time >= start or current_time <= end
else:
return start <= current_time <= end
class NotificationSettingsLog(UUIDModel, TimeStampedModel):
"""
Audit log for notification settings changes.
Tracks who changed what and when.
"""
hospital = models.ForeignKey(
'organizations.Hospital',
on_delete=models.CASCADE,
related_name='notification_settings_logs'
)
changed_by = models.ForeignKey(
'accounts.User',
on_delete=models.SET_NULL,
null=True,
blank=True
)
field_name = models.CharField(max_length=100)
old_value = models.BooleanField(null=True)
new_value = models.BooleanField(null=True)
class Meta:
ordering = ['-created_at']
verbose_name = 'Notification Settings Change Log'
verbose_name_plural = 'Notification Settings Change Logs'