diff --git a/recruitment/forms.py b/recruitment/forms.py index 2457534..222bf35 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -1369,43 +1369,70 @@ class CandidateEmailForm(forms.Form): if candidate and candidate.stage == 'Applied': message_parts = [ - f"Than you, for your interest in the {self.job.title} role.", - f"We regret to inform you that you were not selected to move forward to the exam round at this time.", - f"We encourage you to check our career page for further updates and future opportunities:", - f"https://kaauh/careers", - f"Wishing you the best in your job search,", - f"The KAAUH Hiring team" + f"Thank you for your interest in the {self.job.title} position at KAAUH and for taking the time to submit your application.", + f"We have carefully reviewed your qualifications; however, we regret to inform you that your application was not selected to proceed to the examination round at this time.", + f"The selection process was highly competitive, and we had a large number of highly qualified applicants.", + f"We encourage you to review other opportunities and apply for roles that align with your skills on our career portal:", + f"[settings.CAREER_PAGE_URL]", # Use a Django setting for the URL for flexibility + f"We wish you the best of luck in your current job search and future career endeavors.", + f"Sincerely,", + f"The KAAUH Recruitment Team", ] elif candidate and candidate.stage == 'Exam': message_parts = [ - f"Than you,for your interest in the {self.job.title} role.", - f"We're pleased to inform you that your initial screening was successful!", - f"The next step is the mandatory online assessment exam.", - f"Please complete the assessment by using the following link:", - f"https://kaauh/hire/exam", - f"We look forward to reviewing your results.", - f"Best regards, The KAAUH Hiring team" + f"Dear Candidate,", + f"Thank you once again for your continued interest in the **{self.job.title}** position.", + f"We are pleased to inform you that, following a careful review of your application, you have been **selected to proceed to the next phase** of our recruitment process.", + f"The next mandatory step is the **Online Assessment Examination** designed to evaluate essential skills for this role.", + f"\n**Action Required:**", + f"Please click on the link below to access and complete the assessment:", + f"[settings.EXAM_LINK_URL]", # Using a settings variable is a professional best practice + f"\n**Important Details:**", + f"* **Deadline:** The exam must be completed within **72 hours** of receiving this notification.", + f"* **Duration:** The assessment is timed and will take approximately [Insert Time e.g., 60 minutes] to complete.", + f"* **Technical Note:** Please ensure you have a stable internet connection before beginning.", + f"We appreciate your dedication to this process and look forward to reviewing your results.", + f"Sincerely,", + f"The KAAUH Recruitment Team", ] - elif candidate and candidate.stage == 'Interview': + elif candidate and candidate.stage == 'Interview': message_parts = [ - f"Than you, for your interest in the {self.job.title} role.", - f"We're pleased to inform you that you have cleared your exam!", - f"The next step is the mandatory interview.", - f"Please complete the assessment by using the following link:", - f"https://kaauh/hire/exam", - f"We look forward to reviewing your results.", - f"Best regards, The KAAUH Hiring team" + f"Dear Candidate,", + f"Thank you for your performance in the recent assessment for the **{self.job.title}** role.", + f"We are pleased to inform you that you have **successfully cleared the examination phase** and have been selected to proceed to an interview.", + f"The interview is a mandatory step that allows us to learn more about your experience and fit for the role.", + f"\n**Next Steps:**", + f"Our recruitment coordinator will contact you directly within the next 1-2 business days to schedule your interview time and provide the necessary details (such as the interview panel, format, and location/virtual meeting link).", + f"\n**Please ensure your phone number and email address are current.**", + f"We look forward to speaking with you and discussing this exciting opportunity further.", + f"Sincerely,", + f"The KAAUH Recruitment Team", ] - elif candidate and candidate.stage == 'Offer': + elif candidate and candidate.stage == 'Offer': + message_parts = [ + f"Dear Candidate,", + f"We are delighted to extend to you a **formal offer of employment** for the position of **{self.job.title}** at KAAUH.", + f"Congratulations! This is an exciting moment, and we are very enthusiastic about the prospect of you joining our team.", + f"\n**Next Steps & Documentation:**", + f"A comprehensive offer package, detailing your compensation, benefits, and the full terms of employment, will be transmitted to your email address within the next **24 hours**.", + f"Please review this document carefully.", + f"\n**Questions and Support:**", + f"Should you have any immediate questions regarding the offer or the next steps, please do not hesitate to contact our Human Resources department directly at [HR Contact Email/Phone].", + f"\nWe eagerly anticipate your favorable response and officially welcoming you to the KAAUH team!", + f"Sincerely,", + f"The KAAUH Recruitment Team", + ] + elif candidate and candidate.stage == 'Document Review': message_parts = [ - f"Congratulations, ! We are delighted to inform you that we are extending a formal offer of employment for the {self.job.title} role.", - f"This is an exciting moment, and we look forward to having you join the KAAUH team.", - f"A detailed offer letter and compensation package will be sent to you via email within 24 hours.", - f"In the meantime, please contact our HR department at [HR Contact] if you have immediate questions.", - f"Welcome to the team!", - f"Best regards, The KAAUH Hiring team" + f"Congratulations on progressing to the final stage for the {self.job.title} role!", + f"The next critical step is to complete your application by uploading the required employment verification documents.", + f"**Please log into the Candidate Portal immediately** to access the 'Document Upload' section.", + f"Required documents typically include: National ID/Iqama, Academic Transcripts, and Professional Certifications.", + f"You have **7 days** to upload all documents. Failure to do so may delay or invalidate your candidacy.", + f"If you encounter any technical issues, please contact our support team at [Support Email/Phone] immediately.", + f"We appreciate your cooperation as we finalize your employment process.", ] elif candidate and candidate.stage == 'Hired': message_parts = [ @@ -1414,7 +1441,7 @@ class CandidateEmailForm(forms.Form): f"You will receive a separate email shortly with details regarding your start date, first-day instructions, and onboarding documents.", f"We look forward to seeing you at KAAUH.", f"If you have any questions before your start date, please contact [Onboarding Contact].", - f"Best regards, The KAAUH Hiring team" + ] elif candidate: message_parts="" @@ -2155,10 +2182,7 @@ Job: {job.title} else: initial_message += "This is an onsite schedule. Please arrive 10 minutes early.\n\n" - initial_message += """ -Best regards, -KAAUH Hiring Team - """ + self.fields['message'].initial = initial_message diff --git a/recruitment/migrations/0005_alter_settings_value.py b/recruitment/migrations/0005_alter_settings_value.py new file mode 100644 index 0000000..9f4aea7 --- /dev/null +++ b/recruitment/migrations/0005_alter_settings_value.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.7 on 2025-12-16 09:13 + +import secured_fields.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('recruitment', '0004_settings_name'), + ] + + operations = [ + migrations.AlterField( + model_name='settings', + name='value', + field=secured_fields.fields.EncryptedTextField(help_text='Value for the setting', verbose_name='Setting Value'), + ), + ] diff --git a/recruitment/models.py b/recruitment/models.py index 0598ef1..22dcff1 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -17,7 +17,7 @@ from django_countries.fields import CountryField from django_ckeditor_5.fields import CKEditor5Field from django_extensions.db.fields import RandomCharField from django.contrib.postgres.validators import MinValueValidator, MaxValueValidator -from secured_fields import EncryptedCharField +from secured_fields import EncryptedCharField,EncryptedTextField from typing import List, Dict, Any @@ -2606,7 +2606,7 @@ class Settings(Base): verbose_name=_("Setting Key"), help_text=_("Unique key for the setting"), ) - value = models.TextField( + value = EncryptedTextField( verbose_name=_("Setting Value"), help_text=_("Value for the setting"), ) diff --git a/recruitment/tasks.py b/recruitment/tasks.py index 68105e4..1424a44 100644 --- a/recruitment/tasks.py +++ b/recruitment/tasks.py @@ -27,6 +27,8 @@ from django.template.loader import render_to_string from .models import BulkInterviewTemplate, Interview, Message, ScheduledInterview from django.contrib.auth import get_user_model from .utils import get_setting +from pypdf import PdfReader + User = get_user_model() # Add python-docx import for Word document processing @@ -227,6 +229,10 @@ def format_job_description(pk): def ai_handler(prompt): print("model call") + OPENROUTER_API_URL = get_setting("OPENROUTER_API_URL") + OPENROUTER_API_KEY = get_setting("OPENROUTER_API_KEY") + OPENROUTER_MODEL = get_setting("OPENROUTER_MODEL") + print(OPENROUTER_MODEL) response = requests.post( url=OPENROUTER_API_URL, headers={ diff --git a/recruitment/views.py b/recruitment/views.py index d133771..bf3c7b3 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -184,6 +184,7 @@ class PersonListView(StaffRequiredMixin, ListView, LoginRequiredMixin): model = Person template_name = "people/person_list.html" context_object_name = "people_list" + paginate_by=100 def get_queryset(self): queryset = super().get_queryset().select_related("user") @@ -2571,7 +2572,7 @@ def agency_list(request): agencies = agencies.order_by("-created_at") # Pagination - paginator = Paginator(agencies, 10) # Show 10 agencies per page + paginator = Paginator(agencies,20) # Show 10 agencies per page page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) @@ -4520,7 +4521,7 @@ def source_list(request): sources = sources.order_by("-created_at") # Pagination - paginator = Paginator(sources, 15) # Show 15 sources per page + paginator = Paginator(sources, 1) # Show 15 sources per page page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) @@ -4795,7 +4796,7 @@ def interview_list(request): "status_filter": status_filter, "job_filter": job_filter, "search_query": search_query, - "interviews": interviews, + "interviews": page_obj, "jobs": jobs, } return render(request, "interviews/interview_list.html", context) diff --git a/templates/interviews/interview_list.html b/templates/interviews/interview_list.html index 04c1136..e431910 100644 --- a/templates/interviews/interview_list.html +++ b/templates/interviews/interview_list.html @@ -15,6 +15,7 @@ --kaauh-info: #17a2b8; --kaauh-danger: #dc3545; --kaauh-warning: #ffc107; + --kaauh-gray-light: #f8f9fa; /* Added for consistency */ } /* Primary Color Overrides */ @@ -93,14 +94,12 @@ } /* Column Widths */ - .interview-table thead th:nth-child(1) { width: 40px; } + .interview-table thead th:nth-child(1) { width: 18%; } .interview-table thead th:nth-child(2) { width: 15%; } - .interview-table thead th:nth-child(3) { width: 12%; } - .interview-table thead th:nth-child(4) { width: 12%; } + .interview-table thead th:nth-child(3) { width: 15%; } + .interview-table thead th:nth-child(4) { width: 10%; } .interview-table thead th:nth-child(5) { width: 10%; } - .interview-table thead th:nth-child(6) { width: 8%; } - .interview-table thead th:nth-child(7) { width: 8%; } - .interview-table thead th:nth-child(8) { width: 15%; } + .interview-table thead th:nth-child(6) { width: 10%; } /* Candidate and Job Info */ .candidate-name { @@ -130,14 +129,6 @@ font-weight: 600; } - /* Status Colors */ - .bg-scheduled { background-color: #6c757d !important; color: white; } - .bg-confirmed { background-color: var(--kaauh-info) !important; color: white; } - .bg-cancelled { background-color: var(--kaauh-danger) !important; color: white; } - .bg-completed { background-color: var(--kaauh-success) !important; color: white; } - .bg-remote { background-color: #007bff !important; color: white; } - .bg-onsite { background-color: #6f42c1 !important; color: white; } - /* Custom Height Optimization */ .form-control-sm, .btn-sm { @@ -165,7 +156,6 @@ {% block content %}
-

@@ -173,7 +163,8 @@ {% trans "Interview Management" %}

- {% trans "Total Interviews:" %} {{ interviews|length }} + {# Using count() instead of length filter if interviews is the Paginator Page Object #} + {% trans "Total Interviews:" %} {{ interviews.paginator.count }}

@@ -183,19 +174,9 @@
-
- {% comment %} - {% endcomment %}