From 58d72844a69c8f17b76d30b6a2b6951b9c8f302a Mon Sep 17 00:00:00 2001 From: Faheed Date: Sun, 2 Nov 2025 20:17:21 +0300 Subject: [PATCH] time_to_hire --- .../__pycache__/models.cpython-312.pyc | Bin 83712 -> 84906 bytes recruitment/__pycache__/urls.cpython-312.pyc | Bin 17775 -> 17775 bytes recruitment/__pycache__/utils.cpython-312.pyc | Bin 20414 -> 20441 bytes recruitment/__pycache__/views.cpython-312.pyc | Bin 132796 -> 133698 bytes .../views_frontend.cpython-312.pyc | Bin 46514 -> 46673 bytes recruitment/email_service.py | 9 ++++- recruitment/models.py | 26 ++++++++++++ recruitment/urls.py | 1 + recruitment/utils.py | 2 +- recruitment/views.py | 28 ++++++++++--- recruitment/views_frontend.py | 11 +++++- templates/jobs/application_success.html | 18 ++++----- templates/jobs/job_detail.html | 31 ++++++++------- templates/jobs/job_list.html | 2 +- .../agency_access_link_detail.html | 10 ++--- templates/recruitment/agency_detail.html | 4 +- templates/recruitment/candidate_detail.html | 24 +++++++++--- .../recruitment/candidate_interview_view.html | 37 ++++++++---------- templates/user/admin_settings.html | 4 +- 19 files changed, 140 insertions(+), 67 deletions(-) diff --git a/recruitment/__pycache__/models.cpython-312.pyc b/recruitment/__pycache__/models.cpython-312.pyc index d6982ca34a1d9864d23abba2ec4fd6f557a0cd08..853b30c44e8bd3cd5254824ef1889d42d4b25cfe 100644 GIT binary patch delta 4972 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 P|2M{%wEr&T*ZIEybM{Ik delta 3979 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 diff --git a/recruitment/__pycache__/urls.cpython-312.pyc b/recruitment/__pycache__/urls.cpython-312.pyc index c06a1d528d97ccb40a184c806613a9c7dd90e045..cdcde399a77ece7021b3e132cbca91e97b2870b7 100644 GIT binary patch delta 2148 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 Tl9-x@3zgK}Q)?f|e;)n;Ii;)Z diff --git a/recruitment/__pycache__/utils.cpython-312.pyc b/recruitment/__pycache__/utils.cpython-312.pyc index 3c1ee409550c0176ae9a28a22dd7bb6a211ad2db..dc3c1d77a034331cc079b37187af18f9333fd7cd 100644 GIT binary patch delta 138 zcmdltpYi5=M&8rByj%=Gz?sCJd2A!^R$Znhrpd3}R3^XBy~ucY^DeyzMuQsm8rFDW zkSY);(S*e4!CJH$sA95>(Q(GW d%`C=RjJzKN7?eypIInOhJmHYpY;4jk4FI`NBLx5e delta 110 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 delta 5467 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 diff --git a/recruitment/__pycache__/views_frontend.cpython-312.pyc b/recruitment/__pycache__/views_frontend.cpython-312.pyc index 13ae2ea172d79d8084b22bb72b72960f8abfc90a..d448ee09dac4d70c46f7235e093e58c8596a8d6f 100644 GIT binary patch delta 4033 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 UDO+`HC7Wbbd16p`<@_=BKeg!8LjV8( 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" %}