diff --git a/recruitment/__pycache__/models.cpython-312.pyc b/recruitment/__pycache__/models.cpython-312.pyc index d6982ca..853b30c 100644 Binary files a/recruitment/__pycache__/models.cpython-312.pyc and b/recruitment/__pycache__/models.cpython-312.pyc differ diff --git a/recruitment/__pycache__/urls.cpython-312.pyc b/recruitment/__pycache__/urls.cpython-312.pyc index c06a1d5..cdcde39 100644 Binary files a/recruitment/__pycache__/urls.cpython-312.pyc and b/recruitment/__pycache__/urls.cpython-312.pyc differ diff --git a/recruitment/__pycache__/utils.cpython-312.pyc b/recruitment/__pycache__/utils.cpython-312.pyc index 3c1ee40..dc3c1d7 100644 Binary files a/recruitment/__pycache__/utils.cpython-312.pyc and b/recruitment/__pycache__/utils.cpython-312.pyc differ diff --git a/recruitment/__pycache__/views.cpython-312.pyc b/recruitment/__pycache__/views.cpython-312.pyc index 8c83a5d..d32b218 100644 Binary files a/recruitment/__pycache__/views.cpython-312.pyc and b/recruitment/__pycache__/views.cpython-312.pyc differ diff --git a/recruitment/__pycache__/views_frontend.cpython-312.pyc b/recruitment/__pycache__/views_frontend.cpython-312.pyc index 13ae2ea..d448ee0 100644 Binary files a/recruitment/__pycache__/views_frontend.cpython-312.pyc and b/recruitment/__pycache__/views_frontend.cpython-312.pyc differ diff --git a/recruitment/email_service.py b/recruitment/email_service.py index 733d2a3..a048cf9 100644 --- a/recruitment/email_service.py +++ b/recruitment/email_service.py @@ -162,10 +162,17 @@ def send_interview_invitation_email(candidate, job, meeting_details=None, recipi try: # Prepare recipient list recipients = [] - if candidate.email: + 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'} diff --git a/recruitment/models.py b/recruitment/models.py index 802b380..18d9c0e 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -682,6 +682,32 @@ class Candidate(Base): @property def scoring_timeout(self): return timezone.now() <= (self.created_at + timezone.timedelta(minutes=5)) + + @property + def get_interview_date(self): + if hasattr(self, 'scheduled_interview') and self.scheduled_interview: + return self.scheduled_interviews.first().interview_date + return None + + + + + + @property + def get_interview_time(self): + if hasattr(self, 'scheduled_interview') and self.scheduled_interview: + return self.scheduled_interviews.first().interview_time + return None + + + @property + def time_to_hire_days(self): + + if self.hired_date: + time_to_hire_in_days=timedelta(self.hired_date-self.created_at.date()) + print(self.created_at.date) + return time_to_hire_in_days + return 0 class TrainingMaterial(Base): diff --git a/recruitment/urls.py b/recruitment/urls.py index cc4bd99..25c9eed 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -62,6 +62,7 @@ urlpatterns = [ # Form Preview URLs # path('forms/', views.form_list, name='form_list'), + path('forms/builder/', views.form_builder, name='form_builder'), path('forms/builder//', views.form_builder, name='form_builder'), path('forms/', views.form_templates_list, name='form_templates_list'), diff --git a/recruitment/utils.py b/recruitment/utils.py index af759df..afed297 100644 --- a/recruitment/utils.py +++ b/recruitment/utils.py @@ -449,7 +449,7 @@ def schedule_interviews(schedule): interview_date=slot['date'], interview_time=slot['time'] ) - + candidate.interview_date=interview_datetime # Send email to candidate send_interview_email(scheduled_interview) diff --git a/recruitment/views.py b/recruitment/views.py index 85a5b94..c96c524 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -858,13 +858,13 @@ def application_submit_form(request, template_slug): if is_limit_exceeded: messages.error( request, - 'Application limit reached: This job is no longer accepting new applications. Please explore other available positions.' + _('Application limit reached: This job is no longer accepting new applications.') ) return redirect('application_detail',slug=job.slug) if job.is_expired: messages.error( request, - 'Application deadline passed: This job is no longer accepting new applications. Please explore other available positions.' + _('Application deadline passed: This job is no longer accepting new applications.') ) return redirect('application_detail',slug=job.slug) @@ -1422,10 +1422,26 @@ def candidate_set_exam_date(request, slug): def candidate_update_status(request, slug): job = get_object_or_404(JobPosting, slug=slug) mark_as = request.POST.get('mark_as') + if mark_as != '----------': candidate_ids = request.POST.getlist("candidate_ids") + print(candidate_ids) if c := Candidate.objects.filter(pk__in = candidate_ids): - c.update(stage=mark_as,exam_date=timezone.now(),applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") + + if mark_as=='Exam': + c.update(exam_date=timezone.now(),interview_date=None,offer_date=None,hired_date=None,stage=mark_as,applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") + elif mark_as=='Interview': + # interview_date update when scheduling the interview + c.update(stage=mark_as,offer_date=None,hired_date=None,applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") + elif mark_as=='Offer': + c.update(stage=mark_as,offer_date=timezone.now(),hired_date=None,applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") + elif mark_as=='Hired': + print('hired') + c.update(stage=mark_as,hired_date=timezone.now(),applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") + else: + c.update(stage=mark_as,exam_date=None,interview_date=None,offer_date=None,hired_date=None,applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") + + messages.success(request, f"Candidates Updated") response = HttpResponse(redirect("candidate_screening_view", slug=job.slug)) @@ -2971,11 +2987,12 @@ def agency_assignment_create(request,slug=None): messages.success(request, f'Assignment created for {assignment.agency.name} - {assignment.job.title}!') return redirect('agency_assignment_detail', slug=assignment.slug) else: - messages.error(request, 'Please correct the errors below.') + messages.error(request, f'Please correct the errors below.{form.errors.as_text()}') + print(form.errors.as_json()) else: form = AgencyJobAssignmentForm() try: - from django.forms import HiddenInput + # from django.forms import HiddenInput form.initial['agency'] = agency # form.fields['agency'].widget = HiddenInput() except HiringAgency.DoesNotExist: @@ -3084,6 +3101,7 @@ def agency_access_link_detail(request, slug): AgencyAccessLink.objects.select_related('assignment__agency', 'assignment__job'), slug=slug ) + context = { 'access_link': access_link, diff --git a/recruitment/views_frontend.py b/recruitment/views_frontend.py index 729749e..537fde7 100644 --- a/recruitment/views_frontend.py +++ b/recruitment/views_frontend.py @@ -257,6 +257,7 @@ def candidate_detail(request, slug): if request.user.is_staff: stage_form = forms.CandidateStageForm() + # parsed = JSON(json.dumps(parsed), indent=2, highlight=True, skip_keys=False, ensure_ascii=False, check_circular=True, allow_nan=True, default=None, sort_keys=False) # parsed = json_to_markdown_table([parsed]) return render(request, 'recruitment/candidate_detail.html', { @@ -458,19 +459,25 @@ def dashboard_view(request): # B. Efficiency & Conversion Metrics (Scoped) hired_candidates = candidate_queryset.filter( - Q(offer_status="Accepted") | Q(stage='HIRED'), - join_date__isnull=False + stage='Hired' ) + print(hired_candidates) + lst=[c.time_to_hire_days for c in hired_candidates] + print(lst) time_to_hire_query = hired_candidates.annotate( time_diff=ExpressionWrapper( F('join_date') - F('created_at__date'), output_field=fields.DurationField() ) ).aggregate(avg_time_to_hire=Avg('time_diff')) + + print(time_to_hire_query) + avg_time_to_hire_days = ( time_to_hire_query.get('avg_time_to_hire').days if time_to_hire_query.get('avg_time_to_hire') else 0 ) + print(avg_time_to_hire_days) applied_count = candidate_queryset.filter(stage='Applied').count() advanced_count = candidate_queryset.filter(stage__in=['Exam', 'Interview', 'Offer']).count() diff --git a/templates/jobs/application_success.html b/templates/jobs/application_success.html index c7f2634..2a0699f 100644 --- a/templates/jobs/application_success.html +++ b/templates/jobs/application_success.html @@ -4,7 +4,7 @@ - {% translate "Application Submitted - Thank You" %} + {% trans "Application Submitted - Thank You" %}