from django.core.mail import EmailMultiAlternatives, get_connection from django.template.loader import render_to_string from django.conf import settings 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 CareerEmailService: """Service for handling career-related emails""" def send_application_confirmation(self, application): """ Send confirmation email to applicant """ try: subject = f"Application Received - {application.job.title}" from_email = settings.DEFAULT_FROM_EMAIL to_email = [application.email] # Create context for email template context = { 'applicant_name': application.full_name, 'job_title': application.job.title, 'job_location': application.job.location, 'application_date': application.applied_date, 'logo_url': f'{settings.SITE_URL}/images/logo.png', 'site_url': settings.SITE_URL, } # Render email templates text_content = render_to_string('career/application_confirmation.txt', context) html_content = render_to_string('career/application_confirmation.html', context) # Create email (uses DEFAULT_FROM_EMAIL from settings) email = EmailMultiAlternatives( subject=subject, body=text_content, from_email=from_email, # Uses settings.DEFAULT_FROM_EMAIL to=to_email ) email.attach_alternative(html_content, "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"Confirmation email sent to {application.email}") else: logger.error(f"Failed to send confirmation email to {application.email} after retries") return success except Exception as e: logger.error(f"Failed to send confirmation email: {str(e)}", exc_info=True) return False def send_application_notification_to_admin(self, application): """ Send notification email to company about new application """ try: subject = f"New Job Application: {application.job.title} - {application.full_name}" from_email = settings.DEFAULT_FROM_EMAIL to_email = [settings.COMPANY_EMAIL] # Create context for email template context = { 'applicant_name': application.full_name, 'applicant_email': application.email, 'applicant_phone': application.phone, 'job_title': application.job.title, 'current_position': application.current_position, 'current_company': application.current_company, 'years_of_experience': application.years_of_experience, 'cover_letter': application.cover_letter, 'portfolio_url': application.portfolio_url, 'linkedin_url': application.linkedin_url, 'github_url': application.github_url, 'website_url': application.website_url, 'expected_salary': application.expected_salary, 'salary_currency': application.salary_currency, 'available_from': application.available_from, 'notice_period': application.notice_period, 'application_date': application.applied_date, 'resume_url': application.resume.url if application.resume else None, 'logo_url': f'{settings.SITE_URL}/images/logo.png', 'site_url': settings.SITE_URL, } # Render email templates text_content = render_to_string('career/application_notification.txt', context) html_content = render_to_string('career/application_notification.html', context) # Create email (uses DEFAULT_FROM_EMAIL and COMPANY_EMAIL from settings) email = EmailMultiAlternatives( subject=subject, body=text_content, from_email=from_email, # Uses settings.DEFAULT_FROM_EMAIL to=to_email, # Uses settings.COMPANY_EMAIL reply_to=[application.email] ) email.attach_alternative(html_content, "text/html") # Attach resume if available if application.resume: email.attach_file(application.resume.path) # Send email with retry logic (uses EMAIL_BACKEND and credentials from settings) success = _send_email_with_retry(email) if success: logger.info(f"Admin notification email sent for application from {application.email}") else: logger.error(f"Failed to send admin notification email for application from {application.email} after retries") return success except Exception as e: logger.error(f"Failed to send admin notification email: {str(e)}", exc_info=True) return False