""" Email service for sending notifications related to agency messaging. """ from django.core.mail import send_mail, EmailMultiAlternatives from django.conf import settings from django.template.loader import render_to_string from django.utils.html import strip_tags import logging logger = logging.getLogger(__name__) class EmailService: """ Service class for handling email notifications """ def send_email(self, recipient_email, subject, body, html_body=None): """ Send email using Django's send_mail function Args: recipient_email: Email address to send to subject: Email subject body: Plain text email body html_body: HTML email body (optional) Returns: dict: Result with success status and error message if failed """ try: send_mail( subject=subject, message=body, from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@kaauh.edu.sa'), recipient_list=[recipient_email], html_message=html_body, fail_silently=False, ) logger.info(f"Email sent successfully to {recipient_email}") return {'success': True} except Exception as e: error_msg = f"Failed to send email to {recipient_email}: {str(e)}" logger.error(error_msg) return {'success': False, 'error': error_msg} def send_agency_welcome_email(agency, access_link=None): """ Send welcome email to a new agency with portal access information. Args: agency: HiringAgency instance access_link: AgencyAccessLink instance (optional) Returns: bool: True if email was sent successfully, False otherwise """ try: if not agency.email: logger.warning(f"No email found for agency {agency.id}") return False context = { 'agency': agency, 'access_link': access_link, 'portal_url': getattr(settings, 'AGENCY_PORTAL_URL', 'https://kaauh.edu.sa/portal/'), } # Render email templates html_message = render_to_string('recruitment/emails/agency_welcome.html', context) plain_message = strip_tags(html_message) # Send email send_mail( subject='Welcome to KAAUH Recruitment Portal', message=plain_message, from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@kaauh.edu.sa'), recipient_list=[agency.email], html_message=html_message, fail_silently=False, ) logger.info(f"Welcome email sent to agency {agency.email}") return True except Exception as e: logger.error(f"Failed to send agency welcome email: {str(e)}") return False def send_assignment_notification_email(assignment, message_type='created'): """ Send email notification about assignment changes. Args: assignment: AgencyJobAssignment instance message_type: Type of notification ('created', 'updated', 'deadline_extended') Returns: bool: True if email was sent successfully, False otherwise """ try: if not assignment.agency.email: logger.warning(f"No email found for agency {assignment.agency.id}") return False context = { 'assignment': assignment, 'agency': assignment.agency, 'job': assignment.job, 'message_type': message_type, 'portal_url': getattr(settings, 'AGENCY_PORTAL_URL', 'https://kaauh.edu.sa/portal/'), } # Render email templates html_message = render_to_string('recruitment/emails/assignment_notification.html', context) plain_message = strip_tags(html_message) # Determine subject based on message type subjects = { 'created': f'New Job Assignment: {assignment.job.title}', 'updated': f'Assignment Updated: {assignment.job.title}', 'deadline_extended': f'Deadline Extended: {assignment.job.title}', } subject = subjects.get(message_type, f'Assignment Notification: {assignment.job.title}') # Send email send_mail( subject=subject, message=plain_message, from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@kaauh.edu.sa'), recipient_list=[assignment.agency.email], html_message=html_message, fail_silently=False, ) logger.info(f"Assignment notification email sent to {assignment.agency.email} for {message_type}") return True except Exception as e: logger.error(f"Failed to send assignment notification email: {str(e)}") return False def send_interview_invitation_email(candidate, job, meeting_details=None, recipient_list=None): """ Send interview invitation email using HTML template. Args: candidate: Candidate instance job: Job instance meeting_details: Dictionary with meeting information (optional) recipient_list: List of additional email addresses (optional) Returns: dict: Result with success status and error message if failed """ try: # Prepare recipient list recipients = [] if candidate.hiring_source == "Agency": try: recipients.append(candidate.hiring_agency.email) except : pass else: recipients.append(candidate.email) if recipient_list: recipients.extend(recipient_list) if not recipients: return {'success': False, 'error': 'No recipient email addresses provided'} # Prepare context for template context = { 'candidate_name': candidate.full_name or candidate.name, 'candidate_email': candidate.email, 'candidate_phone': candidate.phone or '', 'job_title': job.title, 'department': getattr(job, 'department', ''), 'company_name': getattr(settings, 'COMPANY_NAME', 'Norah University'), } # Add meeting details if provided if meeting_details: context.update({ 'meeting_topic': meeting_details.get('topic', f'Interview for {job.title}'), 'meeting_date_time': meeting_details.get('date_time', ''), 'meeting_duration': meeting_details.get('duration', '60 minutes'), 'join_url': meeting_details.get('join_url', ''), }) # Render HTML template html_message = render_to_string('emails/interview_invitation.html', context) plain_message = strip_tags(html_message) # Create email with both HTML and plain text versions email = EmailMultiAlternatives( subject=f'Interview Invitation: {job.title}', body=plain_message, from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@kaauh.edu.sa'), to=recipients, ) email.attach_alternative(html_message, "text/html") # Send email email.send(fail_silently=False) logger.info(f"Interview invitation email sent successfully to {', '.join(recipients)}") return { 'success': True, 'recipients_count': len(recipients), 'message': f'Interview invitation sent successfully to {len(recipients)} recipient(s)' } except Exception as e: error_msg = f"Failed to send interview invitation email: {str(e)}" logger.error(error_msg, exc_info=True) return {'success': False, 'error': error_msg} def send_bulk_email(subject, message, recipient_list, request=None, attachments=None, async_task_=False): """ Send bulk email to multiple recipients with HTML support and attachments. Args: subject: Email subject message: Email message (can be HTML) recipient_list: List of email addresses request: Django request object (optional) attachments: List of file attachments (optional) async_task: Whether to run as background task (default: False) Returns: dict: Result with success status and error message if failed """ # Handle async task execution if async_task_: print("hereeeeeee") from django_q.tasks import async_task # Process attachments for background task serialization # processed_attachments = [] # if attachments: # for attachment in attachments: # if hasattr(attachment, 'read'): # # File-like object - save to temporary file # filename = getattr(attachment, 'name', 'attachment') # content_type = getattr(attachment, 'content_type', 'application/octet-stream') # # Create temporary file # with tempfile.NamedTemporaryFile(delete=False, suffix=f'_{filename}') as temp_file: # content = attachment.read() # temp_file.write(content) # temp_file_path = temp_file.name # # Store file info for background task # processed_attachments.append({ # 'file_path': temp_file_path, # 'filename': filename, # 'content_type': content_type # }) # elif isinstance(attachment, tuple) and len(attachment) == 3: # # (filename, content, content_type) tuple - can be serialized directly # processed_attachments.append(attachment) # Queue the email sending as a background task task_id = async_task( 'recruitment.tasks.send_bulk_email_task', subject, message, recipient_list, request, ) logger.info(f"Bulk email queued as background task with ID: {task_id}") return { 'success': True, 'async': True, 'task_id': task_id, 'message': f'Email queued for background sending to {len(recipient_list)} recipient(s)' } # Synchronous execution (default behavior) try: if not recipient_list: return {'success': False, 'error': 'No recipients provided'} # Clean recipient list and remove duplicates clean_recipients = [] seen_emails = set() for recipient in recipient_list: email = recipient.strip().lower() if email and email not in seen_emails: clean_recipients.append(email) seen_emails.add(email) if not clean_recipients: return {'success': False, 'error': 'No valid email addresses found'} # Prepare email content from_email = getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@kaauh.edu.sa') # Check if message contains HTML tags is_html = '<' in message and '>' in message if is_html: # Create HTML email with plain text fallback plain_message = strip_tags(message) # Create email with both HTML and plain text versions email = EmailMultiAlternatives( subject=subject, body=plain_message, from_email=from_email, to=clean_recipients, ) email.attach_alternative(message, "text/html") else: # Plain text email email = EmailMultiAlternatives( subject=subject, body=message, from_email=from_email, to=clean_recipients, ) # Add attachments if provided # if attachments: # for attachment in attachments: # if hasattr(attachment, 'read'): # # File-like object # filename = getattr(attachment, 'name', 'attachment') # content = attachment.read() # content_type = getattr(attachment, 'content_type', 'application/octet-stream') # email.attach(filename, content, content_type) # elif isinstance(attachment, tuple) and len(attachment) == 3: # # (filename, content, content_type) tuple # filename, content, content_type = attachment # email.attach(filename, content, content_type) # Send email email.send(fail_silently=False) logger.info(f"Bulk email sent successfully to {len(clean_recipients)} recipients") return { 'success': True, 'recipients_count': len(clean_recipients), 'message': f'Email sent successfully to {len(clean_recipients)} recipient(s)' } except Exception as e: error_msg = f"Failed to send bulk email: {str(e)}" logger.error(error_msg, exc_info=True) return {'success': False, 'error': error_msg}