449 lines
18 KiB
Python
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
|