From b72ab43a5e38b5d24f3974c6facfc8b5e5ed0865 Mon Sep 17 00:00:00 2001 From: Faheed Date: Sun, 14 Dec 2025 19:25:01 +0300 Subject: [PATCH] email fixed --- .env | 6 +- NorahUniversity/settings.py | 12 +- recruitment/services/email_service.py | 40 +- recruitment/views.py | 458 +++++++++++++---------- templates/recruitment/settings_list.html | 4 +- 5 files changed, 300 insertions(+), 220 deletions(-) diff --git a/.env b/.env index b9e2bf0..8d7fbd5 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -DB_NAME=norahuniversity -DB_USER=norahuniversity -DB_PASSWORD=norahuniversity \ No newline at end of file +DB_NAME=haikal_db +DB_USER=faheed +DB_PASSWORD=Faheed@215 \ No newline at end of file diff --git a/NorahUniversity/settings.py b/NorahUniversity/settings.py index 3cb69bf..775dc13 100644 --- a/NorahUniversity/settings.py +++ b/NorahUniversity/settings.py @@ -208,7 +208,9 @@ ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True ACCOUNT_FORMS = {"signup": "recruitment.forms.StaffSignupForm"} -EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +MAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +EMAIL_HOST = "10.10.1.110" +EMAIL_PORT = 2225 # EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI") @@ -217,10 +219,10 @@ EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" # EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net" # EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI" # EMAIL_USE_TLS = True -EMAIL_HOST = 'sandbox.smtp.mailtrap.io' -EMAIL_HOST_USER = '38e5179debe69a' -EMAIL_HOST_PASSWORD = 'ffa75647d01ecb' -EMAIL_PORT = '2525' +# EMAIL_HOST = 'sandbox.smtp.mailtrap.io' +# EMAIL_HOST_USER = '38e5179debe69a' +# EMAIL_HOST_PASSWORD = 'ffa75647d01ecb' +# EMAIL_PORT = '2525' # Crispy Forms Configuration diff --git a/recruitment/services/email_service.py b/recruitment/services/email_service.py index 412be4f..35b3214 100644 --- a/recruitment/services/email_service.py +++ b/recruitment/services/email_service.py @@ -3,6 +3,7 @@ from django.core.mail import send_mail, EmailMessage from django.contrib.auth import get_user_model from django.template.loader import render_to_string from django.conf import settings # To access EMAIL_HOST_USER, etc. +from recruitment.models import Message UserModel = get_user_model() User = UserModel # Type alias for clarity @@ -17,28 +18,37 @@ class EmailService: subject: str, body: str, recipient_list: List[str], + context:dict, from_email: str = settings.DEFAULT_FROM_EMAIL, - html_content: Union[str, None] = None + html_content: Union[str, None] = None, ) -> int: """ Internal method to handle the actual sending using Django's email backend. """ + try: # Using EmailMessage for more control (e.g., HTML content) - email = EmailMessage( - subject=subject, - body=body, - from_email=from_email, - to=recipient_list, - ) + + for recipient in recipient_list: + email = EmailMessage( + subject=subject, + body=body, + from_email=from_email, + to=[recipient], + ) - if html_content: - email.content_subtype = "html" # Main content is HTML - email.body = html_content # Overwrite body with HTML + if html_content: + email.content_subtype = "html" # Main content is HTML + email.body = html_content # Overwrite body with HTML - # Returns the number of successfully sent emails (usually 1 or the count of recipients) - sent_count = email.send(fail_silently=False) - return sent_count + # Returns the number of successfully sent emails (usually 1 or the count of recipients) + result=email.send(fail_silently=False) + recipient_user=User.objects.filter(email=recipient).first() + if result and recipient_user and not context["message_created"]: + Message.objects.create(sender=context['sender_user'],recipient=recipient_user,job=context['job'],subject=subject,content=context['email_message'],message_type='DIRECT',is_read=False) + + + return len(recipient_list) except Exception as e: # Log the error (in a real app, use Django's logger) @@ -98,8 +108,10 @@ class EmailService: subject=subject, body="", recipient_list=recipient_emails, + context=context, from_email=from_email, - html_content=html_content + html_content=html_content, + ) # Return the count of recipients if successful, or 0 if failure diff --git a/recruitment/views.py b/recruitment/views.py index 7f8d897..89f25c6 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -3653,10 +3653,8 @@ def message_detail(request, message_id): @login_required def message_create(request): """Create a new message""" - from .email_service import EmailService - from .services.email_service import UnifiedEmailService - from .dto.email_dto import EmailConfig, BulkEmailConfig, EmailPriority - + from django.conf import settings + from django_q.tasks import async_task if request.method == "POST": form = MessageForm(request.user, request.POST) @@ -3679,22 +3677,25 @@ def message_create(request): # from .services.email_service import UnifiedEmailService # from .dto.email_dto import EmailConfig, EmailPriority - service = UnifiedEmailService() - - # Create email configuration - config = EmailConfig( - to_email=message.recipient.email, - subject=message.subject, - html_content=body, - attachments=None, - sender=request.user - if request and hasattr(request, "user") - else None, - priority=EmailPriority.NORMAL, + + + email_addresses = [message.recipient.email] + subject=message.subject + + email_result=async_task( + "recruitment.tasks.send_bulk_email_task", + email_addresses, + subject, + # message, + "emails/email_template.html", + { + "email_message": body, + "logo_url": settings.STATIC_URL + "image/kaauh.png", + "message_created":True + }, ) - # Send email using unified service - email_result = service.send_email(config) + if email_result: messages.success( request, "Message sent successfully via email!" @@ -4389,161 +4390,85 @@ def api_application_detail(request, candidate_id): return JsonResponse({"success": False, "error": str(e)}) -@login_required -@staff_user_required -def compose_application_email(request, slug): - """Compose email to participants about a candidate""" - from .email_service import send_bulk_email - from .services.email_service import EmailService - from .dto.email_dto import BulkEmailConfig, EmailPriority +# @login_required +# @staff_user_required +# def compose_application_email(request, slug): +# """Compose email to participants about a candidate""" +# from django.conf import settings - job = get_object_or_404(JobPosting, slug=slug) - candidate_ids = request.GET.getlist("candidate_ids") - candidates = Application.objects.filter(id__in=candidate_ids) +# job = get_object_or_404(JobPosting, slug=slug) +# candidate_ids = request.GET.getlist("candidate_ids") +# candidates = Application.objects.filter(id__in=candidate_ids) - if request.method == "POST": - candidate_ids = request.POST.getlist("candidate_ids") +# if request.method == "POST": +# candidate_ids = request.POST.getlist("candidate_ids") - applications = Application.objects.filter(id__in=candidate_ids) - form = CandidateEmailForm(job, applications, request.POST) +# applications = Application.objects.filter(id__in=candidate_ids) +# form = CandidateEmailForm(job, applications, request.POST) - if form.is_valid(): - # Get email addresses - email_addresses = form.get_email_addresses() +# if form.is_valid(): +# # Get email addresses +# email_addresses = form.get_email_addresses() - if not email_addresses: - messages.error(request, "No email selected") - referer = request.META.get("HTTP_REFERER") +# if not email_addresses: +# messages.error(request, "No email selected") +# referer = request.META.get("HTTP_REFERER") - if referer: - # Redirect back to the referring page - return redirect(referer) - else: - return redirect("dashboard") +# if referer: +# # Redirect back to the referring page +# return redirect(referer) +# else: +# return redirect("dashboard") - subject = form.cleaned_data.get("subject") - message = form.get_formatted_message() +# subject = form.cleaned_data.get("subject") +# message = form.get_formatted_message() - service = EmailService() + +# async_task( +# "recruitment.tasks.send_bulk_email_task", +# email_addresses, +# subject, +# # message, +# "emails/email_template.html", +# { +# "job": job, +# "applications": applications, +# "email_message": message, +# "logo_url": settings.STATIC_URL + "image/kaauh.png", +# }, +# ) +# return redirect(request.path) + - # Prepare recipients data for bulk email - # recipients_data = [] - # for email_addr in email_addresses: - # recipients_data.append( - # { - # "email": email_addr, - # "name": email_addr.split("@")[0] - # if "@" in email_addr - # else email_addr, - # } - # ) - from django.conf import settings - async_task( - "recruitment.tasks.send_bulk_email_task", - email_addresses, - subject, - # message, - "emails/email_template.html", - { - "job": job, - "applications": applications, - "email_message": message, - "logo_url": settings.STATIC_URL + "image/kaauh.png", - }, - ) - # Create bulk email configuration - # bulk_config = BulkEmailConfig( - # subject=subject, - # recipients_data=recipients_data, - # attachments=None, - # sender=request.user if request and hasattr(request, "user") else None, - # job=job, - # priority=EmailPriority.NORMAL, - # async_send=True, - # ) +# else: +# # Form validation errors +# messages.error(request, "Please correct the errors below.") - # Send bulk emails - # if email_result["success"]: - # for application in applications: - # if hasattr(application, "person") and application.person: - # try: - # Message.objects.create( - # sender=request.user, - # recipient=application.person.user, - # subject=subject, - # content=message, - # job=job, - # message_type="job_related", - # is_email_sent=True, - # email_address=application.person.email - # if application.person.email - # else application.email, - # ) +# # For HTMX requests, return error response +# if "HX-Request" in request.headers: +# return JsonResponse( +# { +# "success": False, +# "error": "Please correct the form errors and try again.", +# } +# ) - # except Exception as e: - # # Log error but don't fail the entire process - # print(f"Error creating message") +# return render( +# request, +# "includes/email_compose_form.html", +# {"form": form, "job": job, "candidates": candidates}, +# ) - # messages.success( - # request, - # f"Email will be sent shortly to recipient(s)", - # ) - # response = HttpResponse(status=200) - # response.headers["HX-Refresh"] = "true" - # return response - # # return redirect("applications_interview_view", slug=job.slug) - # else: - # messages.error( - # request, - # f"Failed to send email: {email_result.get('message', 'Unknown error')}", - # ) +# else: +# # GET request - show the form +# form = CandidateEmailForm(job, candidates) - # # For HTMX requests, return error response - # if "HX-Request" in request.headers: - # return JsonResponse( - # { - # "success": False, - # "error": email_result.get( - # "message", "Failed to send email" - # ), - # } - # ) - - # return render( - # request, - # "includes/email_compose_form.html", - # {"form": form, "job": job, "candidate": candidates}, - # ) - - else: - # Form validation errors - messages.error(request, "Please correct the errors below.") - - # For HTMX requests, return error response - if "HX-Request" in request.headers: - return JsonResponse( - { - "success": False, - "error": "Please correct the form errors and try again.", - } - ) - - return render( - request, - "includes/email_compose_form.html", - {"form": form, "job": job, "candidates": candidates}, - ) - - else: - # GET request - show the form - form = CandidateEmailForm(job, candidates) - - return render( - request, - "includes/email_compose_form.html", - # {"form": form, "job": job, "candidates": candidates}, - {"form": form, "job": job}, - ) +# return render( +# request, +# "includes/email_compose_form.html", +# # {"form": form, "job": job, "candidates": candidates}, +# {"form": form, "job": job}, +# ) # Source CRUD Views @@ -6487,55 +6412,196 @@ def sync_history(request, job_slug=None): return render(request, "recruitment/sync_history.html", context) +# def send_interview_email(request, slug): +# from django.conf import settings +# schedule = get_object_or_404(ScheduledInterview, slug=slug) +# application = schedule.application +# job = application.job +# form = InterviewEmailForm(job, application, schedule) +# if request.method == "POST": +# form = InterviewEmailForm(job, application, schedule, request.POST) +# if form.is_valid(): +# recipient = form.cleaned_data.get("to").strip() +# body_message = form.cleaned_data.get("message") +# subject = form.cleaned_data.get("subject") +# sender_user = request.user +# job = job +# try: + +# # Send email using background task +# email_result= async_task( +# "recruitment.tasks.send_bulk_email_task", +# recipient, +# subject, +# # message, +# "emails/email_template.html", +# { +# "job": job, +# "applications": application, +# "email_message":body_message, +# "logo_url": settings.STATIC_URL + "image/kaauh.png", +# }, +# ) + +# if email_result: +# messages.success(request, "Message sent successfully via email!") +# else: +# messages.warning( +# request, +# f"email failed: {email_result.get('message', 'Unknown error')}", +# ) + +# except Exception as e: +# messages.warning( +# request, f"Message saved but email sending failed: {str(e)}" +# ) +# else: +# form = InterviewEmailForm(job, application, schedule) +# else: # GET request +# form = InterviewEmailForm(job, application, schedule) + +# # This is the final return, which handles GET requests and invalid POST requests. +# return redirect("interview_detail", slug=schedule.slug) + + def send_interview_email(request, slug): + from django.conf import settings + from django_q.tasks import async_task + schedule = get_object_or_404(ScheduledInterview, slug=slug) application = schedule.application job = application.job - form = InterviewEmailForm(job, application, schedule) + if request.method == "POST": form = InterviewEmailForm(job, application, schedule, request.POST) if form.is_valid(): - recipient = form.cleaned_data.get("to").strip() + # 1. Ensure recipient is a list (fixes the "@" error) + recipient_str = form.cleaned_data.get("to").strip() + recipient_list = [recipient_str] + body_message = form.cleaned_data.get("message") subject = form.cleaned_data.get("subject") - sender = request.user - job = job + try: - # Use new unified email service for background processing - from .services.email_service import UnifiedEmailService - from .dto.email_dto import EmailConfig, EmailPriority - - service = UnifiedEmailService() - - # Create email configuration - config = EmailConfig( - to_email=recipient, - subject=subject, - html_content=body_message, - attachments=None, - sender=sender, - job=job, - priority=EmailPriority.NORMAL, + # 2. Match the context expected by your task/service + # We pass IDs for the sender/job to avoid serialization issues + async_task( + "recruitment.tasks.send_bulk_email_task", + recipient_list, + subject, + "emails/email_template.html", + { + "job": job, # Useful for Message creation + "sender_user": request.user, + "applications": application, + "email_message": body_message, + "message_created":False, + "logo_url": settings.STATIC_URL + "image/kaauh.png", + }, ) - - # Send email using background task - email_result = service.send_email(config) - if email_result: - messages.success(request, "Message sent successfully via email!") - else: - messages.warning( - request, - f"email failed: {email_result.get('message', 'Unknown error')}", - ) + + messages.success(request, "Interview email enqueued successfully!") + return redirect("interview_detail", slug=schedule.slug) except Exception as e: - messages.warning( - request, f"Message saved but email sending failed: {str(e)}" - ) + messages.error(request, f"Task scheduling failed: {str(e)}") else: - form = InterviewEmailForm(job, application, schedule) - else: # GET request + messages.error(request, "Please correct the errors in the form.") + else: + # GET request form = InterviewEmailForm(job, application, schedule) - # This is the final return, which handles GET requests and invalid POST requests. - return redirect("interview_detail", slug=schedule.slug) + # 3. FIX: Instead of always redirecting, render the template + # This allows users to see validation errors. + return render( + request, + "recruitment/interview_email_form.html", # Replace with your actual template path + { + "form": form, + "schedule": schedule, + "job": job + } + ) + +@login_required +@staff_user_required +def compose_application_email(request, slug): + """Compose email to participants about a candidate""" + from django.conf import settings + + job = get_object_or_404(JobPosting, slug=slug) + candidate_ids = request.GET.getlist("candidate_ids") + candidates = Application.objects.filter(id__in=candidate_ids) + + if request.method == "POST": + candidate_ids = request.POST.getlist("candidate_ids") + + applications = Application.objects.filter(id__in=candidate_ids) + form = CandidateEmailForm(job, applications, request.POST) + + if form.is_valid(): + # Get email addresses + email_addresses = form.get_email_addresses() + + if not email_addresses: + messages.error(request, "No email selected") + referer = request.META.get("HTTP_REFERER") + + if referer: + # Redirect back to the referring page + return redirect(referer) + else: + return redirect("dashboard") + + subject = form.cleaned_data.get("subject") + message = form.get_formatted_message() + + + async_task( + "recruitment.tasks.send_bulk_email_task", + email_addresses, + subject, + # message, + "emails/email_template.html", + { + "job": job, + "sender_user": request.user, + "applications": applications, + "email_message": message, + "message_created":False, + "logo_url": settings.STATIC_URL + "image/kaauh.png", + + }, + ) + return redirect(request.path) + + + else: + # Form validation errors + messages.error(request, "Please correct the errors below.") + + # For HTMX requests, return error response + if "HX-Request" in request.headers: + return JsonResponse( + { + "success": False, + "error": "Please correct the form errors and try again.", + } + ) + + return render( + request, + "includes/email_compose_form.html", + {"form": form, "job": job, "candidates": candidates}, + ) + + else: + # GET request - show the form + form = CandidateEmailForm(job, candidates) + + return render( + request, + "includes/email_compose_form.html", + # {"form": form, "job": job, "candidates": candidates}, + {"form": form, "job": job}, + ) diff --git a/templates/recruitment/settings_list.html b/templates/recruitment/settings_list.html index 0f3365a..3eef67a 100644 --- a/templates/recruitment/settings_list.html +++ b/templates/recruitment/settings_list.html @@ -92,14 +92,14 @@ class="btn btn-sm btn-outline-secondary" title="Edit Setting"> -
{% csrf_token %} -
+ {% endcomment %} -- 2.39.5