| import asyncio |
| from datetime import datetime, timedelta, timezone |
|
|
| from fastapi import HTTPException |
| from jinja2 import Environment, FileSystemLoader, select_autoescape |
|
|
| from cbh.api.calls.models import CallModel |
| from cbh.api.calls.dto import CallStatus |
| from cbh.core.config import settings |
|
|
|
|
| def can_edit_call(call: CallModel) -> None: |
| """ |
| Check if the call can be edited. |
| """ |
| now = datetime.now(timezone.utc) if call.event.startDate.tzinfo else datetime.now() |
| min_edit_time = now + timedelta(hours=48) |
|
|
| if call.status != CallStatus.SCHEDULED or call.event.startDate < min_edit_time: |
| raise HTTPException(status_code=400, detail="Call cannot be edited.") |
|
|
|
|
| def calculate_sleep_time(call: CallModel) -> int: |
| """ |
| Calculate the sleep time for the reminder. |
| """ |
| now = datetime.now(timezone.utc) if call.event.startDate.tzinfo else datetime.now() |
| reminder_time = call.event.startDate - timedelta(hours=1) |
| sleep_time = (reminder_time - now).total_seconds() |
|
|
| return max(0, int(sleep_time)) |
|
|
|
|
| async def send_customer_booking_email(call: CallModel) -> None: |
| """ |
| Send a booking email. |
| """ |
| if not call.event.isActive: |
| return |
| templates_path = settings.BASE_DIR / "cbh" / "templates" / "emails" |
| env = Environment( |
| loader=FileSystemLoader(templates_path), |
| autoescape=select_autoescape(["html", "xml"]), |
| ) |
| template = env.get_template("bookingConfirmation.html") |
|
|
| template_content = template.render( |
| coach_name=call.coach.name, |
| user_name=call.customer.name, |
| start_date=call.event.startDate.strftime("%d %B %Y, %H:%M"), |
| join_link=f"{settings.Audience}/calls/{call.id}", |
| duration=(call.event.endDate - call.event.startDate).total_seconds() // 60, |
| ) |
|
|
| await settings.EMAIL_CLIENT.send_email( |
| call.customer.email, |
| f"You're all set! Session with {call.coach.name} confirmed!", |
| template_content, |
| ) |
|
|
|
|
| async def send_coach_booking_email(call: CallModel) -> None: |
| """ |
| Send a booking email. |
| """ |
| if not call.event.isActive: |
| return |
| templates_path = settings.BASE_DIR / "cbh" / "templates" / "emails" |
| env = Environment( |
| loader=FileSystemLoader(templates_path), |
| autoescape=select_autoescape(["html", "xml"]), |
| ) |
| template = env.get_template("bookingConfirmationCoach.html") |
| template_content = template.render( |
| coach_name=call.coach.name, |
| user_name=call.customer.name, |
| start_date=call.event.startDate.strftime("%d %B %Y, %H:%M"), |
| join_link=f"{settings.Audience}/calls/{call.id}", |
| duration=(call.event.endDate - call.event.startDate).total_seconds() // 60, |
| ) |
|
|
| await settings.EMAIL_CLIENT.send_email( |
| call.coach.email, |
| f"New session booked with {call.customer.name}!", |
| template_content, |
| ) |
|
|
|
|
| async def send_cancel_customer_email(call: CallModel) -> None: |
| """ |
| Send a cancellation email. |
| """ |
| templates_path = settings.BASE_DIR / "cbh" / "templates" / "emails" |
| env = Environment( |
| loader=FileSystemLoader(templates_path), |
| autoescape=select_autoescape(["html", "xml"]), |
| ) |
| template = env.get_template("sessionCancellation.html") |
| template_content = template.render( |
| coach_name=call.coach.name, |
| user_name=call.customer.name, |
| start_date=call.event.startDate.strftime("%d %B %Y, %H:%M"), |
| duration=(call.event.endDate - call.event.startDate).total_seconds() // 60, |
| ) |
|
|
| await settings.EMAIL_CLIENT.send_email( |
| call.customer.email, |
| f"Your session with {call.coach.name} has been cancelled.", |
| template_content, |
| ) |
|
|
|
|
| async def send_cancel_coach_email(call: CallModel) -> None: |
| """ |
| Send a cancellation email. |
| """ |
| templates_path = settings.BASE_DIR / "cbh" / "templates" / "emails" |
| env = Environment( |
| loader=FileSystemLoader(templates_path), |
| autoescape=select_autoescape(["html", "xml"]), |
| ) |
| template = env.get_template("sessionCancellationCoach.html") |
| template_content = template.render( |
| coach_name=call.coach.name, |
| user_name=call.customer.name, |
| start_date=call.event.startDate.strftime("%d %B %Y, %H:%M"), |
| duration=(call.event.endDate - call.event.startDate).total_seconds() // 60, |
| ) |
|
|
| await settings.EMAIL_CLIENT.send_email( |
| call.coach.email, |
| f"{call.customer.name} cancelled their session.", |
| template_content, |
| ) |
|
|
|
|
| async def send_session_reminder_email(call: CallModel) -> None: |
| """ |
| Send a session reminder email. |
| """ |
| if not call.event.isActive: |
| return |
| sleep_time = calculate_sleep_time(call) |
| await asyncio.sleep(sleep_time) |
|
|
| templates_path = settings.BASE_DIR / "cbh" / "templates" / "emails" |
| env = Environment( |
| loader=FileSystemLoader(templates_path), |
| autoescape=select_autoescape(["html", "xml"]), |
| ) |
| template = env.get_template("sessionReminder.html") |
| template_content = template.render( |
| coach_name=call.coach.name, |
| user_name=call.customer.name, |
| start_date=call.event.startDate.strftime("%d %B %Y, %H:%M"), |
| join_link=f"{settings.Audience}/calls/{call.id}", |
| duration=(call.event.endDate - call.event.startDate).total_seconds() // 60, |
| ) |
|
|
| await settings.EMAIL_CLIENT.send_email( |
| call.customer.email, |
| f"Your session with {call.coach.name} is starting in 1 hour.", |
| template_content, |
| ) |
|
|
|
|
| async def send_session_reminder_coach_email(call: CallModel) -> None: |
| """ |
| Send a session reminder email. |
| """ |
| if not call.event.isActive: |
| return |
| sleep_time = calculate_sleep_time(call) |
| await asyncio.sleep(sleep_time) |
|
|
| templates_path = settings.BASE_DIR / "cbh" / "templates" / "emails" |
| env = Environment( |
| loader=FileSystemLoader(templates_path), |
| autoescape=select_autoescape(["html", "xml"]), |
| ) |
| template = env.get_template("sessionReminderCoach.html") |
| template_content = template.render( |
| coach_name=call.coach.name, |
| user_name=call.customer.name, |
| start_date=call.event.startDate.strftime("%d %B %Y, %H:%M"), |
| join_link=f"{settings.Audience}/calls/{call.id}", |
| duration=(call.event.endDate - call.event.startDate).total_seconds() // 60, |
| ) |
|
|
| await settings.EMAIL_CLIENT.send_email( |
| call.coach.email, |
| f"Your session with {call.customer.name} is starting in 1 hour.", |
| template_content, |
| ) |
|
|
|
|
| async def send_anonymous_booking_email(call: CallModel) -> None: |
| """ |
| Send a anonymous booking email. |
| """ |
| templates_path = settings.BASE_DIR / "cbh" / "templates" / "emails" |
| env = Environment( |
| loader=FileSystemLoader(templates_path), |
| autoescape=select_autoescape(["html", "xml"]), |
| ) |
| template = env.get_template("anonymousBookingConfirmation.html") |
| template_content = template.render( |
| coach_name=call.coach.name, |
| start_date=call.event.startDate.strftime("%d %B %Y, %H:%M"), |
| join_link=f"{settings.Audience}/calls/{call.id}", |
| duration=(call.event.endDate - call.event.startDate).total_seconds() // 60, |
| registration_link=f"{settings.Audience}/signup/complete?accountId={call.customer.id}", |
| ) |
|
|
| await settings.EMAIL_CLIENT.send_email( |
| call.customer.email, |
| f"You're all set! Session with {call.coach.name} confirmed!", |
| template_content, |
| ) |
|
|