HH/apps/notifications/settings_service.py

449 lines
18 KiB
Python

"""
Notification Settings Service
Integrates notification settings with the notification service.
Checks settings before sending notifications.
"""
import logging
from functools import wraps
from django.utils import timezone
from .settings_models import HospitalNotificationSettings
logger = logging.getLogger(__name__)
def check_notification_settings(event, hospital_id=None):
"""
Decorator to check notification settings before sending.
Usage:
@check_notification_settings('explanation_requested', 'hospital_id')
def send_explanation_request(..., hospital_id=None):
...
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Extract hospital_id from kwargs or args
hid = kwargs.get(hospital_id)
if hid is None and len(args) > 0:
# Try to find hospital_id in args based on function signature
import inspect
sig = inspect.signature(func)
params = list(sig.parameters.keys())
if hospital_id in params:
idx = params.index(hospital_id)
if idx < len(args):
hid = args[idx]
if hid is None:
# Try to get from related_object
related_object = kwargs.get('related_object')
if related_object and hasattr(related_object, 'hospital_id'):
hid = related_object.hospital_id
if hid:
settings = HospitalNotificationSettings.get_for_hospital(hid)
# Check if notifications are enabled
if not settings.notifications_enabled:
logger.info(f"Notifications disabled for hospital {hid}")
return None
# Check if this event is enabled for the channel
# Channel is determined by the function name or a 'channel' kwarg
channel = kwargs.get('channel', 'email')
if 'sms' in func.__name__:
channel = 'sms'
elif 'whatsapp' in func.__name__:
channel = 'whatsapp'
elif 'email' in func.__name__:
channel = 'email'
if not settings.is_channel_enabled(event, channel):
logger.info(f"Notification {event} via {channel} disabled for hospital {hid}")
return None
# Check quiet hours for SMS/WhatsApp
if channel in ['sms', 'whatsapp'] and settings.is_quiet_hours():
logger.info(f"Notification {event} queued due to quiet hours for hospital {hid}")
# TODO: Implement queueing for quiet hours
# For now, we just log and continue
return func(*args, **kwargs)
return wrapper
return decorator
class NotificationServiceWithSettings:
"""
Wrapper around NotificationService that respects hospital settings.
Usage:
from apps.notifications.settings_service import NotificationServiceWithSettings
NotificationServiceWithSettings.send_explanation_requested(
staff_email, complaint, hospital_id=hospital.id
)
"""
@staticmethod
def _get_hospital_id_from_complaint(complaint):
"""Extract hospital_id from complaint object"""
if hasattr(complaint, 'hospital_id'):
return complaint.hospital_id
return None
@staticmethod
def send_explanation_requested(staff_email, complaint, custom_message=None):
"""
Send explanation request notification to staff.
Respects hospital notification settings.
"""
from .services import NotificationService
hospital_id = NotificationServiceWithSettings._get_hospital_id_from_complaint(complaint)
if not hospital_id:
# Fallback: send without settings check
return NotificationService.send_email(
email=staff_email,
subject=f"Explanation Request - Complaint #{complaint.id}",
message=custom_message or f"Please provide explanation for complaint #{complaint.id}",
related_object=complaint
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
# Check master switch
if not settings.notifications_enabled:
logger.info(f"Notifications disabled for hospital {hospital_id}")
return results
# Email
if settings.explanation_requested_email:
result = NotificationService.send_email(
email=staff_email,
subject=f"Explanation Request - Complaint #{complaint.id}",
message=custom_message or f"Please provide explanation for complaint #{complaint.id}",
related_object=complaint
)
results.append(('email', result))
# SMS (check quiet hours)
if settings.explanation_requested_sms and hasattr(complaint, 'staff') and complaint.staff and complaint.staff.phone:
if not settings.is_quiet_hours():
result = NotificationService.send_sms(
phone=complaint.staff.phone,
message=f"PX360: Explanation requested for Complaint #{complaint.id}. Check your email for details.",
related_object=complaint
)
results.append(('sms', result))
# WhatsApp
if settings.explanation_requested_whatsapp and hasattr(complaint, 'staff') and complaint.staff and complaint.staff.phone:
if not settings.is_quiet_hours():
result = NotificationService.send_whatsapp(
phone=complaint.staff.phone,
message=f"PX360: Explanation requested for Complaint #{complaint.id}. Check your email for details.",
related_object=complaint
)
results.append(('whatsapp', result))
return results
@staticmethod
def send_explanation_reminder(staff_email, complaint):
"""Send explanation reminder notification"""
from .services import NotificationService
hospital_id = NotificationServiceWithSettings._get_hospital_id_from_complaint(complaint)
if not hospital_id:
return NotificationService.send_email(
email=staff_email,
subject=f"Reminder: Explanation Due - Complaint #{complaint.id}",
message=f"Reminder: Please submit your explanation for complaint #{complaint.id} within 24 hours.",
related_object=complaint
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
if not settings.notifications_enabled:
return results
if settings.explanation_reminder_email:
result = NotificationService.send_email(
email=staff_email,
subject=f"Reminder: Explanation Due - Complaint #{complaint.id}",
message=f"Reminder: Please submit your explanation for complaint #{complaint.id} within 24 hours.",
related_object=complaint
)
results.append(('email', result))
# SMS reminder
if settings.explanation_reminder_sms and hasattr(complaint, 'staff') and complaint.staff and complaint.staff.phone:
if not settings.is_quiet_hours():
result = NotificationService.send_sms(
phone=complaint.staff.phone,
message=f"PX360 Reminder: Explanation due for Complaint #{complaint.id} in 24h.",
related_object=complaint
)
results.append(('sms', result))
return results
@staticmethod
def send_explanation_overdue(manager_email, complaint, staff_name):
"""Send explanation overdue/escalation notification to manager"""
from .services import NotificationService
hospital_id = NotificationServiceWithSettings._get_hospital_id_from_complaint(complaint)
if not hospital_id:
return NotificationService.send_email(
email=manager_email,
subject=f"ESCALATION: Explanation Overdue - Complaint #{complaint.id}",
message=f"Staff member {staff_name} has not submitted explanation for complaint #{complaint.id}.",
related_object=complaint
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
if not settings.notifications_enabled:
return results
if settings.explanation_overdue_email:
result = NotificationService.send_email(
email=manager_email,
subject=f"ESCALATION: Explanation Overdue - Complaint #{complaint.id}",
message=f"Staff member {staff_name} has not submitted explanation for complaint #{complaint.id}.",
related_object=complaint
)
results.append(('email', result))
if settings.explanation_overdue_sms:
# Get manager phone if available
result = NotificationService.send_sms(
phone=manager_email, # This would need actual phone lookup
message=f"PX360 ESCALATION: Explanation overdue for Complaint #{complaint.id} by {staff_name}",
related_object=complaint
)
results.append(('sms', result))
return results
@staticmethod
def send_explanation_received(assignee_email, complaint, explanation):
"""Send notification when explanation is received"""
from .services import NotificationService
hospital_id = NotificationServiceWithSettings._get_hospital_id_from_complaint(complaint)
if not hospital_id:
return NotificationService.send_email(
email=assignee_email,
subject=f"New Explanation Received - Complaint #{complaint.id}",
message=f"A new explanation has been submitted for complaint #{complaint.id}.",
related_object=complaint
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
if not settings.notifications_enabled:
return results
if settings.explanation_received_email:
result = NotificationService.send_email(
email=assignee_email,
subject=f"New Explanation Received - Complaint #{complaint.id}",
message=f"A new explanation has been submitted for complaint #{complaint.id}.",
related_object=complaint
)
results.append(('email', result))
return results
@staticmethod
def send_complaint_assigned(assignee_email, complaint):
"""Send notification when complaint is assigned"""
from .services import NotificationService
hospital_id = NotificationServiceWithSettings._get_hospital_id_from_complaint(complaint)
if not hospital_id:
return NotificationService.send_email(
email=assignee_email,
subject=f"Complaint Assigned - #{complaint.id}",
message=f"You have been assigned to complaint #{complaint.id}: {complaint.title}",
related_object=complaint
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
if not settings.notifications_enabled:
return results
if settings.complaint_assigned_email:
result = NotificationService.send_email(
email=assignee_email,
subject=f"Complaint Assigned - #{complaint.id}",
message=f"You have been assigned to complaint #{complaint.id}: {complaint.title}",
related_object=complaint
)
results.append(('email', result))
return results
@staticmethod
def send_onboarding_invitation(user_email, provisional_user, custom_message=None):
"""
Send onboarding invitation to new provisional user.
Respects hospital notification settings.
"""
from .services import NotificationService
hospital_id = getattr(provisional_user, 'hospital_id', None)
if not hospital_id:
# Fallback: send without settings check
return NotificationService.send_email(
email=user_email,
subject='Welcome to PX360 - Complete Your Registration',
message=custom_message or f'Please complete your registration at PX360.',
related_object=provisional_user
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
# Check master switch
if not settings.notifications_enabled:
logger.info(f"Notifications disabled for hospital {hospital_id}")
return results
# Email invitation
if settings.onboarding_invitation_email:
result = NotificationService.send_email(
email=user_email,
subject='Welcome to PX360 - Complete Your Registration',
message=custom_message or f'Please complete your registration at PX360.',
related_object=provisional_user
)
results.append(('email', result))
# SMS invitation (check quiet hours)
if settings.onboarding_invitation_sms and provisional_user.phone:
if not settings.is_quiet_hours():
result = NotificationService.send_sms(
phone=provisional_user.phone,
message='Welcome to PX360! Check your email to complete your registration.',
related_object=provisional_user
)
results.append(('sms', result))
return results
@staticmethod
def send_onboarding_reminder(user_email, provisional_user):
"""Send onboarding reminder notification"""
from .services import NotificationService
hospital_id = getattr(provisional_user, 'hospital_id', None)
if not hospital_id:
return NotificationService.send_email(
email=user_email,
subject='Reminder: Complete Your PX360 Registration',
message='Please complete your PX360 registration. Your invitation will expire soon.',
related_object=provisional_user
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
if not settings.notifications_enabled:
return results
if settings.onboarding_reminder_email:
result = NotificationService.send_email(
email=user_email,
subject='Reminder: Complete Your PX360 Registration',
message='Please complete your PX360 registration. Your invitation will expire soon.',
related_object=provisional_user
)
results.append(('email', result))
if settings.onboarding_reminder_sms and provisional_user.phone:
if not settings.is_quiet_hours():
result = NotificationService.send_sms(
phone=provisional_user.phone,
message='Reminder: Complete your PX360 registration. Invitation expires soon.',
related_object=provisional_user
)
results.append(('sms', result))
return results
@staticmethod
def send_onboarding_completion_notification(admin_email, completed_user):
"""Send notification to admin when user completes onboarding"""
from .services import NotificationService
hospital_id = getattr(completed_user, 'hospital_id', None)
if not hospital_id:
return NotificationService.send_email(
email=admin_email,
subject=f'Onboarding Complete - {completed_user.get_full_name()}',
message=f'{completed_user.get_full_name()} has completed their onboarding.',
related_object=completed_user
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
if not settings.notifications_enabled:
return results
if settings.onboarding_completion_email:
result = NotificationService.send_email(
email=admin_email,
subject=f'Onboarding Complete - {completed_user.get_full_name()}',
message=f'{completed_user.get_full_name()} has completed their onboarding.',
related_object=completed_user
)
results.append(('email', result))
return results
@staticmethod
def send_complaint_status_changed(recipient_email, complaint, old_status, new_status):
"""Send notification when complaint status changes"""
from .services import NotificationService
hospital_id = NotificationServiceWithSettings._get_hospital_id_from_complaint(complaint)
if not hospital_id:
return NotificationService.send_email(
email=recipient_email,
subject=f"Complaint Status Updated - #{complaint.id}",
message=f"Complaint #{complaint.id} status changed from {old_status} to {new_status}.",
related_object=complaint
)
settings = HospitalNotificationSettings.get_for_hospital(hospital_id)
results = []
if not settings.notifications_enabled:
return results
if settings.complaint_status_changed_email:
result = NotificationService.send_email(
email=recipient_email,
subject=f"Complaint Status Updated - #{complaint.id}",
message=f"Complaint #{complaint.id} status changed from {old_status} to {new_status}.",
related_object=complaint
)
results.append(('email', result))
return results