""" 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