210 lines
8.0 KiB
Python
210 lines
8.0 KiB
Python
"""
|
|
Email Service for Support Tickets
|
|
Handles sending email notifications for ticket creation and updates
|
|
"""
|
|
|
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
|
from django.template.loader import render_to_string
|
|
from django.conf import settings
|
|
from django.utils.html import strip_tags
|
|
import logging
|
|
import time
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _send_email_with_retry(email_message, max_retries: int = 3, delay: float = 1.0) -> bool:
|
|
"""
|
|
Send email with retry logic for production reliability.
|
|
Uses Django settings from .env file.
|
|
|
|
Args:
|
|
email_message: EmailMultiAlternatives instance
|
|
max_retries: Maximum number of retry attempts
|
|
delay: Delay between retries in seconds
|
|
|
|
Returns:
|
|
bool: True if email was sent successfully, False otherwise
|
|
"""
|
|
for attempt in range(max_retries + 1):
|
|
try:
|
|
# Test connection before sending (uses EMAIL_BACKEND from settings)
|
|
connection = get_connection()
|
|
connection.open()
|
|
connection.close()
|
|
|
|
# Send the email (uses EMAIL_BACKEND and credentials from settings)
|
|
email_message.send()
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.warning(f"Email send attempt {attempt + 1} failed: {str(e)}")
|
|
|
|
if attempt < max_retries:
|
|
time.sleep(delay * (2 ** attempt)) # Exponential backoff
|
|
continue
|
|
else:
|
|
logger.error(f"Failed to send email after {max_retries + 1} attempts: {str(e)}")
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
class SupportEmailService:
|
|
"""Service for sending support ticket related emails"""
|
|
|
|
@staticmethod
|
|
def send_ticket_confirmation_to_user(ticket):
|
|
"""
|
|
Send ticket confirmation email to the user who created the ticket
|
|
|
|
Args:
|
|
ticket: SupportTicket instance
|
|
"""
|
|
try:
|
|
subject = f'Ticket Created: {ticket.ticket_number}'
|
|
|
|
# Context for email template
|
|
context = {
|
|
'ticket': ticket,
|
|
'ticket_number': ticket.ticket_number,
|
|
'user_name': ticket.user_name,
|
|
'title': ticket.title,
|
|
'description': ticket.description,
|
|
'ticket_type': ticket.get_ticket_type_display(),
|
|
'category': ticket.category.name if ticket.category else 'General',
|
|
'priority': ticket.priority.name if ticket.priority else 'Medium',
|
|
'status': ticket.status.name if ticket.status else 'Open',
|
|
'created_at': ticket.created_at.strftime('%B %d, %Y at %I:%M %p'),
|
|
'support_url': f'{settings.SITE_URL}/support-center',
|
|
'logo_url': f'{settings.SITE_URL}/images/logo.png',
|
|
'site_url': settings.SITE_URL,
|
|
}
|
|
|
|
# Render HTML email
|
|
html_message = render_to_string(
|
|
'support/ticket_confirmation_user.html',
|
|
context
|
|
)
|
|
|
|
# Create plain text version
|
|
text_message = render_to_string(
|
|
'support/ticket_confirmation_user.txt',
|
|
context
|
|
)
|
|
|
|
# Create email (uses DEFAULT_FROM_EMAIL from settings)
|
|
email = EmailMultiAlternatives(
|
|
subject=subject,
|
|
body=text_message,
|
|
from_email=settings.DEFAULT_FROM_EMAIL, # From .env file
|
|
to=[ticket.user_email],
|
|
)
|
|
|
|
# Attach HTML version
|
|
email.attach_alternative(html_message, "text/html")
|
|
|
|
# Send email with retry logic (uses EMAIL_BACKEND and credentials from settings)
|
|
success = _send_email_with_retry(email)
|
|
|
|
if success:
|
|
logger.info(f'Ticket confirmation email sent to user: {ticket.user_email} for ticket {ticket.ticket_number}')
|
|
else:
|
|
logger.error(f'Failed to send ticket confirmation email to user: {ticket.user_email} for ticket {ticket.ticket_number} after retries')
|
|
return success
|
|
|
|
except Exception as e:
|
|
logger.error(f'Failed to send ticket confirmation email to user: {str(e)}')
|
|
return False
|
|
|
|
@staticmethod
|
|
def send_ticket_notification_to_company(ticket):
|
|
"""
|
|
Send ticket notification email to company/support team
|
|
|
|
Args:
|
|
ticket: SupportTicket instance
|
|
"""
|
|
try:
|
|
subject = f'New Support Ticket: {ticket.ticket_number} - {ticket.title}'
|
|
|
|
# Get company email from settings
|
|
company_email = getattr(settings, 'SUPPORT_EMAIL', settings.DEFAULT_FROM_EMAIL)
|
|
|
|
# Context for email template
|
|
context = {
|
|
'ticket': ticket,
|
|
'ticket_number': ticket.ticket_number,
|
|
'user_name': ticket.user_name,
|
|
'user_email': ticket.user_email,
|
|
'user_phone': ticket.user_phone,
|
|
'company': ticket.company,
|
|
'title': ticket.title,
|
|
'description': ticket.description,
|
|
'ticket_type': ticket.get_ticket_type_display(),
|
|
'category': ticket.category.name if ticket.category else 'General',
|
|
'priority': ticket.priority.name if ticket.priority else 'Medium',
|
|
'status': ticket.status.name if ticket.status else 'Open',
|
|
'created_at': ticket.created_at.strftime('%B %d, %Y at %I:%M %p'),
|
|
'admin_url': f'{settings.SITE_URL}/admin/support/supportticket/{ticket.id}/change/',
|
|
'logo_url': f'{settings.SITE_URL}/images/logo.png',
|
|
'site_url': settings.SITE_URL,
|
|
}
|
|
|
|
# Render HTML email
|
|
html_message = render_to_string(
|
|
'support/ticket_notification_company.html',
|
|
context
|
|
)
|
|
|
|
# Create plain text version
|
|
text_message = render_to_string(
|
|
'support/ticket_notification_company.txt',
|
|
context
|
|
)
|
|
|
|
# Create email (uses DEFAULT_FROM_EMAIL and SUPPORT_EMAIL from settings)
|
|
email = EmailMultiAlternatives(
|
|
subject=subject,
|
|
body=text_message,
|
|
from_email=settings.DEFAULT_FROM_EMAIL, # From .env file
|
|
to=[company_email], # Uses settings.SUPPORT_EMAIL or settings.DEFAULT_FROM_EMAIL
|
|
reply_to=[ticket.user_email],
|
|
)
|
|
|
|
# Attach HTML version
|
|
email.attach_alternative(html_message, "text/html")
|
|
|
|
# Send email with retry logic (uses EMAIL_BACKEND and credentials from settings)
|
|
success = _send_email_with_retry(email)
|
|
|
|
if success:
|
|
logger.info(f'Ticket notification email sent to company: {company_email} for ticket {ticket.ticket_number}')
|
|
else:
|
|
logger.error(f'Failed to send ticket notification email to company: {company_email} for ticket {ticket.ticket_number} after retries')
|
|
return success
|
|
|
|
except Exception as e:
|
|
logger.error(f'Failed to send ticket notification email to company: {str(e)}')
|
|
return False
|
|
|
|
@staticmethod
|
|
def send_ticket_created_emails(ticket):
|
|
"""
|
|
Send both user confirmation and company notification emails
|
|
|
|
Args:
|
|
ticket: SupportTicket instance
|
|
|
|
Returns:
|
|
dict with status of both emails
|
|
"""
|
|
user_email_sent = SupportEmailService.send_ticket_confirmation_to_user(ticket)
|
|
company_email_sent = SupportEmailService.send_ticket_notification_to_company(ticket)
|
|
|
|
return {
|
|
'user_email_sent': user_email_sent,
|
|
'company_email_sent': company_email_sent,
|
|
}
|
|
|