2025-11-23 00:19:26 +00:00
|
|
|
from django.core.mail import EmailMultiAlternatives
|
|
|
|
|
from django.template.loader import render_to_string
|
|
|
|
|
from django.conf import settings
|
2025-12-05 10:34:19 +00:00
|
|
|
from django.utils import timezone
|
|
|
|
|
import pytz
|
|
|
|
|
from datetime import datetime
|
2025-11-23 00:19:26 +00:00
|
|
|
|
|
|
|
|
class EmailService:
|
|
|
|
|
|
2025-12-05 10:34:19 +00:00
|
|
|
@staticmethod
|
|
|
|
|
def _format_datetime_for_user(dt, user_timezone='UTC'):
|
|
|
|
|
if not dt:
|
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
user_tz = pytz.timezone(user_timezone or 'UTC')
|
|
|
|
|
|
|
|
|
|
if timezone.is_naive(dt):
|
|
|
|
|
dt = timezone.make_aware(dt, pytz.UTC)
|
|
|
|
|
|
|
|
|
|
local_dt = dt.astimezone(user_tz)
|
|
|
|
|
|
|
|
|
|
formatted = local_dt.strftime('%B %d, %Y at %I:%M %p %Z')
|
|
|
|
|
|
|
|
|
|
return formatted
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error formatting datetime: {e}")
|
|
|
|
|
return dt.strftime('%B %d, %Y at %I:%M %p UTC')
|
|
|
|
|
|
2025-11-23 00:19:26 +00:00
|
|
|
@staticmethod
|
|
|
|
|
def send_admin_notification(appointment):
|
|
|
|
|
subject = f"New Appointment Request from {appointment.full_name}"
|
|
|
|
|
|
|
|
|
|
context = {
|
|
|
|
|
'appointment': appointment,
|
|
|
|
|
'preferred_dates': appointment.get_preferred_dates_display(),
|
|
|
|
|
'preferred_times': appointment.get_preferred_time_slots_display(),
|
2025-11-27 21:09:52 +00:00
|
|
|
'admin_dashboard_url': "https://attunehearttherapy.com/admin/dashboard"
|
2025-11-23 00:19:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-23 00:27:44 +00:00
|
|
|
html_message = render_to_string('emails/admin_notification.html', context)
|
2025-11-23 00:19:26 +00:00
|
|
|
|
2025-11-27 17:31:16 +00:00
|
|
|
admin_email = getattr(settings, 'ADMIN_EMAIL', 'admin@attunehearttherapy.com')
|
2025-11-23 00:19:26 +00:00
|
|
|
|
|
|
|
|
try:
|
2025-11-23 00:27:44 +00:00
|
|
|
email = EmailMultiAlternatives(
|
|
|
|
|
subject=subject,
|
2025-12-05 10:34:19 +00:00
|
|
|
body="Please view this email in an HTML-compatible client.",
|
2025-11-23 00:27:44 +00:00
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
|
|
|
to=[admin_email],
|
2025-11-23 00:19:26 +00:00
|
|
|
)
|
2025-11-23 00:27:44 +00:00
|
|
|
email.attach_alternative(html_message, "text/html")
|
|
|
|
|
email.send(fail_silently=False)
|
2025-11-23 00:19:26 +00:00
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Failed to send admin notification: {e}")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def send_appointment_scheduled(appointment):
|
|
|
|
|
subject = "Your Appointment Has Been Scheduled"
|
|
|
|
|
|
2025-12-05 10:34:19 +00:00
|
|
|
user_timezone = getattr(appointment, 'user_timezone', 'UTC')
|
|
|
|
|
|
|
|
|
|
formatted_datetime = EmailService._format_datetime_for_user(
|
|
|
|
|
appointment.scheduled_datetime,
|
|
|
|
|
user_timezone
|
|
|
|
|
)
|
|
|
|
|
|
2025-11-23 00:19:26 +00:00
|
|
|
context = {
|
|
|
|
|
'appointment': appointment,
|
2025-12-05 10:34:19 +00:00
|
|
|
'scheduled_datetime': formatted_datetime,
|
|
|
|
|
'user_dashboard_url': f"{settings.FRONTEND_URL}/dashboard" if hasattr(settings, 'FRONTEND_URL') else '/dashboard/',
|
|
|
|
|
'settings': {
|
|
|
|
|
'SITE_NAME': getattr(settings, 'SITE_NAME', 'Attune Heart Therapy')
|
|
|
|
|
}
|
2025-11-23 00:19:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-23 00:27:44 +00:00
|
|
|
html_message = render_to_string('emails/appointment_scheduled.html', context)
|
2025-11-23 00:19:26 +00:00
|
|
|
|
|
|
|
|
try:
|
2025-11-23 00:27:44 +00:00
|
|
|
email = EmailMultiAlternatives(
|
|
|
|
|
subject=subject,
|
2025-12-05 10:34:19 +00:00
|
|
|
body=f"Your appointment has been scheduled for {formatted_datetime}. Please view this email in an HTML-compatible client for more details.",
|
2025-11-23 00:27:44 +00:00
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
|
|
|
to=[appointment.email],
|
2025-11-23 00:19:26 +00:00
|
|
|
)
|
2025-11-23 00:27:44 +00:00
|
|
|
email.attach_alternative(html_message, "text/html")
|
|
|
|
|
email.send(fail_silently=False)
|
2025-11-23 00:19:26 +00:00
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Failed to send scheduled notification: {e}")
|
|
|
|
|
return False
|
2025-12-05 18:04:11 +00:00
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def send_appointment_rescheduled(appointment):
|
|
|
|
|
subject = "Your Appointment Has Been Rescheduled"
|
|
|
|
|
|
|
|
|
|
user_timezone = getattr(appointment, 'user_timezone', 'UTC')
|
|
|
|
|
|
|
|
|
|
formatted_datetime = EmailService._format_datetime_for_user(
|
|
|
|
|
appointment.scheduled_datetime,
|
|
|
|
|
user_timezone
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
context = {
|
|
|
|
|
'appointment': appointment,
|
|
|
|
|
'scheduled_datetime': formatted_datetime,
|
|
|
|
|
'user_dashboard_url': f"{settings.FRONTEND_URL}/dashboard" if hasattr(settings, 'FRONTEND_URL') else '/dashboard/',
|
|
|
|
|
'settings': {
|
|
|
|
|
'SITE_NAME': getattr(settings, 'SITE_NAME', 'Attune Heart Therapy')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
html_message = render_to_string('emails/appointment_rescheduled.html', context)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
email = EmailMultiAlternatives(
|
|
|
|
|
subject=subject,
|
|
|
|
|
body=f"Your appointment has been rescheduled for {formatted_datetime}. Please view this email in an HTML-compatible client for more details.",
|
|
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
|
|
|
to=[appointment.email],
|
|
|
|
|
)
|
|
|
|
|
email.attach_alternative(html_message, "text/html")
|
|
|
|
|
email.send(fail_silently=False)
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Failed to send rescheduled notification: {e}")
|
|
|
|
|
return False
|
2025-11-23 00:19:26 +00:00
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def send_appointment_rejected(appointment):
|
|
|
|
|
subject = "Update on Your Appointment Request"
|
|
|
|
|
|
|
|
|
|
context = {
|
|
|
|
|
'appointment': appointment,
|
|
|
|
|
'rejection_reason': appointment.rejection_reason or "No specific reason provided.",
|
2025-12-05 10:34:19 +00:00
|
|
|
'user_dashboard_url': f"{settings.FRONTEND_URL}/dashboard" if hasattr(settings, 'FRONTEND_URL') else '/dashboard/',
|
|
|
|
|
'settings': {
|
|
|
|
|
'SITE_NAME': getattr(settings, 'SITE_NAME', 'Attune Heart Therapy')
|
|
|
|
|
}
|
2025-11-23 00:19:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-23 00:27:44 +00:00
|
|
|
html_message = render_to_string('emails/appointment_rejected.html', context)
|
2025-11-23 00:19:26 +00:00
|
|
|
|
|
|
|
|
try:
|
2025-11-23 00:27:44 +00:00
|
|
|
email = EmailMultiAlternatives(
|
|
|
|
|
subject=subject,
|
2025-12-05 10:34:19 +00:00
|
|
|
body="Please view this email in an HTML-compatible client.",
|
2025-11-23 00:27:44 +00:00
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
|
|
|
to=[appointment.email],
|
2025-11-23 00:19:26 +00:00
|
|
|
)
|
2025-11-23 00:27:44 +00:00
|
|
|
email.attach_alternative(html_message, "text/html")
|
|
|
|
|
email.send(fail_silently=False)
|
2025-11-23 00:19:26 +00:00
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Failed to send rejection notification: {e}")
|
2025-12-05 10:34:19 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def send_appointment_reminder(appointment, hours_before=24):
|
|
|
|
|
subject = "Reminder: Your Upcoming Appointment"
|
|
|
|
|
|
|
|
|
|
user_timezone = getattr(appointment, 'user_timezone', 'UTC')
|
|
|
|
|
formatted_datetime = EmailService._format_datetime_for_user(
|
|
|
|
|
appointment.scheduled_datetime,
|
|
|
|
|
user_timezone
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
context = {
|
|
|
|
|
'appointment': appointment,
|
|
|
|
|
'scheduled_datetime': formatted_datetime,
|
|
|
|
|
'hours_before': hours_before,
|
|
|
|
|
'join_url': appointment.get_participant_join_url() if hasattr(appointment, 'get_participant_join_url') else None,
|
|
|
|
|
'settings': {
|
|
|
|
|
'SITE_NAME': getattr(settings, 'SITE_NAME', 'Attune Heart Therapy')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
html_message = render_to_string('emails/appointment_reminder.html', context)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
email = EmailMultiAlternatives(
|
|
|
|
|
subject=subject,
|
|
|
|
|
body=f"This is a reminder that you have an appointment scheduled for {formatted_datetime}.",
|
|
|
|
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
|
|
|
to=[appointment.email],
|
|
|
|
|
)
|
|
|
|
|
email.attach_alternative(html_message, "text/html")
|
|
|
|
|
email.send(fail_silently=False)
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Failed to send reminder notification: {e}")
|
2025-11-23 00:19:26 +00:00
|
|
|
return False
|