HH/apps/surveys/services.py

283 lines
11 KiB
Python

"""
Survey Delivery Service - Sends surveys to patients via SMS/Email
This service handles:
- Generating secure survey URLs
- Sending SMS with survey links
- Sending emails with survey links
- Tracking delivery status
Uses NotificationService for all delivery operations.
"""
from django.conf import settings
from django.utils import timezone
import logging
logger = logging.getLogger(__name__)
class SurveyDeliveryService:
"""Service for delivering surveys to patients"""
@staticmethod
def generate_survey_url(survey_instance) -> str:
"""
Generate secure survey URL with access token.
Args:
survey_instance: SurveyInstance object
Returns:
Full survey URL
"""
base_url = getattr(settings, 'SURVEY_BASE_URL', 'http://localhost:8000')
survey_path = survey_instance.get_survey_url()
return f"{base_url}{survey_path}"
@staticmethod
def generate_sms_message(recipient_name: str, survey_url: str, hospital_name: str = None, is_staff: bool = False) -> str:
"""
Generate SMS message with survey link.
Args:
recipient_name: Recipient's first name (patient or staff)
survey_url: Survey link
hospital_name: Optional hospital name
is_staff: Whether recipient is staff member
Returns:
SMS message text
"""
message = f"Dear {recipient_name},\n\n"
if hospital_name:
message += f"Thank you for being part of {hospital_name}. "
if is_staff:
message += "We value your feedback! Please take a moment to complete our staff experience survey:\n\n"
else:
message += "We value your feedback! Please take a moment to complete our patient experience survey:\n\n"
message += f"{survey_url}\n\n"
message += "This survey will take approximately 2-3 minutes.\n"
message += "Thank you for helping us improve our services!"
return message
@staticmethod
def generate_email_message(recipient_name: str, survey_url: str, hospital_name: str = None, is_staff: bool = False) -> str:
"""
Generate email message with survey link.
Args:
recipient_name: Recipient's first name (patient or staff)
survey_url: Survey link
hospital_name: Optional hospital name
is_staff: Whether recipient is staff member
Returns:
Email message text
"""
message = f"Dear {recipient_name},\n\n"
if hospital_name:
message += f"Thank you for being part of {hospital_name}.\n\n"
else:
message += "Thank you for being part of our hospital.\n\n"
if is_staff:
message += "We value your feedback and would appreciate it if you could take a moment to complete our staff experience survey.\n\n"
message += "Your feedback helps us improve our services and create a better work environment.\n\n"
else:
message += "We value your feedback and would appreciate it if you could take a moment to complete our patient experience survey.\n\n"
message += "Your feedback helps us improve our services and provide better care for all our patients.\n\n"
message += f"Survey Link: {survey_url}\n\n"
message += "This survey will take approximately 2-3 minutes to complete.\n\n"
message += "If you have any questions or concerns, please don't hesitate to contact us.\n\n"
message += "Thank you for helping us improve our services!\n\n"
if is_staff:
message += "Best regards,\n"
message += "Hospital Administration Team"
else:
message += "Best regards,\n"
message += "Patient Experience Team"
return message
@staticmethod
def send_survey_sms(survey_instance) -> bool:
"""
Send survey via SMS using NotificationService API.
Args:
survey_instance: SurveyInstance object
Returns:
True if sent successfully, False otherwise
"""
if not survey_instance.recipient_phone:
logger.warning(f"No phone number for survey {survey_instance.id}")
return False
try:
# Generate survey URL and message
survey_url = SurveyDeliveryService.generate_survey_url(survey_instance)
recipient_name = survey_instance.get_recipient_name().split()[0] # First name only
is_staff = survey_instance.staff is not None
hospital_name = survey_instance.hospital.name
message = SurveyDeliveryService.generate_sms_message(
recipient_name, survey_url, hospital_name, is_staff
)
# Use NotificationService for delivery (supports API backend)
from apps.notifications.services import NotificationService
# Build metadata
metadata = {
'survey_id': str(survey_instance.id),
'hospital_id': str(survey_instance.hospital.id),
'is_staff_survey': is_staff
}
if survey_instance.patient:
metadata['patient_id'] = str(survey_instance.patient.id)
if survey_instance.staff:
metadata['staff_id'] = str(survey_instance.staff.id)
notification_log = NotificationService.send_sms_via_api(
message=message,
phone=survey_instance.recipient_phone,
related_object=survey_instance,
metadata=metadata
)
# Update survey instance based on notification status
if notification_log and notification_log.status == 'sent':
from apps.surveys.models import SurveyStatus
survey_instance.status = SurveyStatus.SENT
survey_instance.sent_at = notification_log.sent_at
survey_instance.save(update_fields=['status', 'sent_at'])
logger.info(f"Survey SMS sent successfully to {survey_instance.recipient_phone}")
return True
else:
logger.warning(f"Survey SMS delivery failed for {survey_instance.id}")
return False
except Exception as e:
logger.error(f"Error sending SMS for survey {survey_instance.id}: {str(e)}")
return False
@staticmethod
def send_survey_email(survey_instance) -> bool:
"""
Send survey via Email using NotificationService API.
Args:
survey_instance: SurveyInstance object
Returns:
True if sent successfully, False otherwise
"""
if not survey_instance.recipient_email:
logger.warning(f"No email address for survey {survey_instance.id}")
return False
try:
# Generate survey URL and message
survey_url = SurveyDeliveryService.generate_survey_url(survey_instance)
recipient_name = survey_instance.get_recipient_name().split()[0] # First name only
is_staff = survey_instance.staff is not None
hospital_name = survey_instance.hospital.name
message = SurveyDeliveryService.generate_email_message(
recipient_name, survey_url, hospital_name, is_staff
)
# Use NotificationService for delivery (supports API backend)
from apps.notifications.services import NotificationService
# Build metadata
metadata = {
'survey_id': str(survey_instance.id),
'hospital_id': str(survey_instance.hospital.id),
'is_staff_survey': is_staff
}
if survey_instance.patient:
metadata['patient_id'] = str(survey_instance.patient.id)
if survey_instance.staff:
metadata['staff_id'] = str(survey_instance.staff.id)
# Set email subject
if is_staff:
subject = f'Staff Experience Survey - {survey_instance.hospital.name}'
else:
subject = f'Patient Experience Survey - {survey_instance.hospital.name}'
notification_log = NotificationService.send_email_via_api(
message=message,
email=survey_instance.recipient_email,
subject=subject,
html_message=None, # Plain text for now
related_object=survey_instance,
metadata=metadata
)
# Update survey instance based on notification status
if notification_log and notification_log.status == 'sent':
from apps.surveys.models import SurveyStatus
survey_instance.status = SurveyStatus.SENT
survey_instance.sent_at = notification_log.sent_at
survey_instance.save(update_fields=['status', 'sent_at'])
logger.info(f"Survey email sent successfully to {survey_instance.recipient_email}")
return True
else:
logger.warning(f"Survey email delivery failed for {survey_instance.id}")
return False
except Exception as e:
logger.error(f"Error sending email for survey {survey_instance.id}: {str(e)}")
return False
@staticmethod
def deliver_survey(survey_instance) -> bool:
"""
Deliver survey based on configured delivery channel.
Args:
survey_instance: SurveyInstance object
Returns:
True if delivered successfully, False otherwise
"""
# Normalize delivery channel to lowercase for comparison
delivery_channel = (survey_instance.delivery_channel or '').lower()
# Get recipient (patient or staff)
recipient = survey_instance.get_recipient()
# Ensure contact info is set
if delivery_channel == 'sms':
if recipient:
survey_instance.recipient_phone = survey_instance.recipient_phone or recipient.phone
elif delivery_channel == 'email':
if recipient:
survey_instance.recipient_email = survey_instance.recipient_email or getattr(recipient, 'email', None)
# Save contact info
survey_instance.save()
# Send based on channel
if delivery_channel == 'sms':
return SurveyDeliveryService.send_survey_sms(survey_instance)
elif delivery_channel == 'email':
return SurveyDeliveryService.send_survey_email(survey_instance)
elif delivery_channel == 'whatsapp':
# TODO: Implement WhatsApp delivery
logger.warning(f"WhatsApp delivery not yet implemented for survey {survey_instance.id}")
return False
else:
logger.error(f"Unknown delivery channel: {survey_instance.delivery_channel}")
return False