From 5d7451a951d69a1e2e97d9ab2bfb0d1aac88cb95 Mon Sep 17 00:00:00 2001 From: Horilla Date: Tue, 23 Jan 2024 15:29:05 +0530 Subject: [PATCH] [ADD] ONBOARDING: Probation management --- onboarding/models.py | 6 +- .../onboarding/candidate_filter.html | 14 +- .../templates/onboarding/candidates.html | 26 ++- .../templates/onboarding/candidates_view.html | 18 +- onboarding/urls.py | 1 + onboarding/views.py | 171 +++++++++++------- recruitment/models.py | 61 ++++--- 7 files changed, 192 insertions(+), 105 deletions(-) diff --git a/onboarding/models.py b/onboarding/models.py index 0244e8b53..6a9116795 100644 --- a/onboarding/models.py +++ b/onboarding/models.py @@ -222,5 +222,7 @@ class OnboardingStageThread(threading.Thread): c_task.stage_id = c_task.onboarding_task_id.stage_id c_task.save() - -OnboardingStageThread().start() \ No newline at end of file +try: + OnboardingStageThread().start() +except: + pass \ No newline at end of file diff --git a/onboarding/templates/onboarding/candidate_filter.html b/onboarding/templates/onboarding/candidate_filter.html index 78bc78fad..b11203c02 100644 --- a/onboarding/templates/onboarding/candidate_filter.html +++ b/onboarding/templates/onboarding/candidate_filter.html @@ -31,10 +31,22 @@
- + {{form.scheduled_till}}
+
+
+ + {{form.probation_end_from}} +
+
+
+
+ + {{form.probation_end_till}} +
+
diff --git a/onboarding/templates/onboarding/candidates.html b/onboarding/templates/onboarding/candidates.html index fd9447081..2ca0bda39 100644 --- a/onboarding/templates/onboarding/candidates.html +++ b/onboarding/templates/onboarding/candidates.html @@ -18,6 +18,7 @@
{% trans "Candidate" %}
{% trans "Email" %}
{% trans "Date of joining" %}
+
{% trans "Probation ends" %}
{% trans "Job position" %}
{% trans "Recruitment" %}
{% trans "Actions" %}
@@ -47,9 +48,13 @@
{{candidate.email}} - + + + {{candidate.job_position_id}} {{candidate.recruitment_id}}
@@ -119,6 +124,25 @@
" ) - return render(request, "onboarding/task_form.html", {"form": form,"stage_id":stage_id}) + return render( + request, "onboarding/task_form.html", {"form": form, "stage_id": stage_id} + ) @login_required @hx_request_required @stage_manager_can_enter("onboarding.change_onboardingtask") -def task_update(request, task_id,): +def task_update( + request, + task_id, +): """ function used to update onboarding task. @@ -291,7 +305,10 @@ def task_update(request, task_id,): response = render( request, "onboarding/task_update.html", - {"form": form, "task_id": task_id,}, + { + "form": form, + "task_id": task_id, + }, ) return HttpResponse( response.content.decode("utf-8") + "" @@ -300,7 +317,10 @@ def task_update(request, task_id,): return render( request, "onboarding/task_update.html", - {"form": form, "task_id": task_id,}, + { + "form": form, + "task_id": task_id, + }, ) @@ -456,7 +476,7 @@ def candidates_single_view(request, id, **kwargs): "recruitment": recruitment, "choices": choices, "candidate": candidate, - 'single_view':True + "single_view": True, } return render( request, @@ -698,9 +718,7 @@ def onboarding_view(request): onboarding_stages = OnboardingStage.objects.all() choices = CandidateTask.choice previous_data = request.GET.urlencode() - filter_obj = RecruitmentFilter( - request.GET, queryset=recruitments - ) + filter_obj = RecruitmentFilter(request.GET, queryset=recruitments) paginator = Paginator(filter_obj.qs, 4) page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) @@ -746,10 +764,10 @@ def kanban_view(request): # if not CandidateTask.objects.filter( # candidate_id=candidate, onboarding_task_id=task # ).exists(): - # pass - # CandidateTask( - # candidate_id=candidate, onboarding_task_id=task - # ).save() + # pass + # CandidateTask( + # candidate_id=candidate, onboarding_task_id=task + # ).save() recruitments = Recruitment.objects.filter(closed=False) status = "closed" @@ -763,14 +781,11 @@ def kanban_view(request): stage_form = OnboardingViewStageForm() previous_data = request.GET.urlencode() - - filter_obj = RecruitmentFilter( - request.GET, queryset=recruitments - ) + + filter_obj = RecruitmentFilter(request.GET, queryset=recruitments) paginator = Paginator(filter_obj.qs, 4) page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) - return render( request, @@ -783,7 +798,7 @@ def kanban_view(request): "stage_form": stage_form, "status": status, "choices": choices, - "pd":previous_data, + "pd": previous_data, }, ) @@ -1050,8 +1065,10 @@ def candidate_task_update(request, taskId): else: canId = request.POST.get("candId") onboarding_task = OnboardingTask.objects.get(id=taskId) - candidate= Candidate.objects.get(id=canId) - candidate_task = CandidateTask.objects.filter(candidate_id=candidate,onboarding_task_id=onboarding_task).first() + candidate = Candidate.objects.get(id=canId) + candidate_task = CandidateTask.objects.filter( + candidate_id=candidate, onboarding_task_id=onboarding_task + ).first() candidate_task.status = status candidate_task.save() users = [ @@ -1074,8 +1091,9 @@ def candidate_task_update(request, taskId): {"message": _("Candidate onboarding stage updated"), "type": "success"} ) + @login_required -def get_status(request,task_id): +def get_status(request, task_id): """ htmx function that return the status of candidate task @@ -1086,29 +1104,34 @@ def get_status(request,task_id): Returns: POST : return candidate task template """ - cand_id = request.GET.get('cand_id') - cand_stage = request.GET.get('cand_stage') - cand_stage_obj=CandidateStage.objects.get(id=cand_stage) + cand_id = request.GET.get("cand_id") + cand_stage = request.GET.get("cand_stage") + cand_stage_obj = CandidateStage.objects.get(id=cand_stage) onboarding_task = OnboardingTask.objects.get(id=task_id) - candidate= Candidate.objects.get(id=cand_id) - candidate_task = CandidateTask.objects.filter(candidate_id=candidate,onboarding_task_id=onboarding_task).first() + candidate = Candidate.objects.get(id=cand_id) + candidate_task = CandidateTask.objects.filter( + candidate_id=candidate, onboarding_task_id=onboarding_task + ).first() status = candidate_task.status - return render(request,'onboarding/candidate_task.html', - { - 'status':status, - 'task':onboarding_task, - 'candidate':cand_stage_obj, - 'second_load':True, - 'choices':CandidateTask.choice - } - ) + return render( + request, + "onboarding/candidate_task.html", + { + "status": status, + "task": onboarding_task, + "candidate": cand_stage_obj, + "second_load": True, + "choices": CandidateTask.choice, + }, + ) + @login_required @all_manager_can_enter("onboarding.change_candidatetask") -def assign_task(request,task_id): +def assign_task(request, task_id): """ - htmx function that used to assign a onboarding task to candidate + htmx function that used to assign a onboarding task to candidate Parameters: request (HttpRequest): The HTTP request object. @@ -1117,29 +1140,31 @@ def assign_task(request,task_id): Returns: POST : return candidate task template """ - stage_id = request.GET.get('stage_id') - cand_id = request.GET.get('cand_id') - cand_stage = request.GET.get('cand_stage') - cand_stage_obj=CandidateStage.objects.get(id=cand_stage) - onboarding_task =OnboardingTask.objects.get(id=task_id) - candidate=Candidate.objects.get(id=cand_id) + stage_id = request.GET.get("stage_id") + cand_id = request.GET.get("cand_id") + cand_stage = request.GET.get("cand_stage") + cand_stage_obj = CandidateStage.objects.get(id=cand_stage) + onboarding_task = OnboardingTask.objects.get(id=task_id) + candidate = Candidate.objects.get(id=cand_id) onboarding_stage = OnboardingStage.objects.get(id=stage_id) - cand_task,created= CandidateTask.objects.get_or_create( + cand_task, created = CandidateTask.objects.get_or_create( candidate_id=candidate, - stage_id = onboarding_stage, - onboarding_task_id = onboarding_task + stage_id=onboarding_stage, + onboarding_task_id=onboarding_task, ) cand_task.save() onboarding_task.candidates.add(candidate) - return render(request,'onboarding/candidate_task.html', - { - 'status':cand_task.status, - 'task':onboarding_task, - 'candidate':cand_stage_obj, - 'second_load':True, - 'choices':CandidateTask.choice - } - ) + return render( + request, + "onboarding/candidate_task.html", + { + "status": cand_task.status, + "task": onboarding_task, + "candidate": cand_stage_obj, + "second_load": True, + "choices": CandidateTask.choice, + }, + ) @login_required @@ -1487,3 +1512,15 @@ def onboarding_send_mail(request, candidate_id): ) return response + + +@login_required +@stage_manager_can_enter("recruitment.change_stage") +def update_probation_end(request): + """ + This method is used to update the probotion end date + """ + candidate_id = request.GET.getlist("candidate_id") + probation_end = request.GET["probation_end"] + Candidate.objects.filter(id__in=candidate_id).update(probation_end=probation_end) + return JsonResponse({"message":"Probation end date updated","type":"success"}) diff --git a/recruitment/models.py b/recruitment/models.py index c914a20fc..b81c7dd50 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -77,7 +77,7 @@ class Recruitment(models.Model): "To close the recruitment, If closed then not visible on pipeline view." ), ) - is_published = models.BooleanField(default = True) + is_published = models.BooleanField(default=True) is_active = models.BooleanField( default=True, help_text=_( @@ -225,7 +225,11 @@ class Candidate(models.Model): """ choices = [("male", _("Male")), ("female", _("Female")), ("other", _("Other"))] - source_choices = [("application", _("Application Form")), ("software", _("Inside software")), ("other", _("Other"))] + source_choices = [ + ("application", _("Application Form")), + ("software", _("Inside software")), + ("other", _("Other")), + ] name = models.CharField(max_length=100, null=True, verbose_name=_("Name")) profile = models.ImageField(upload_to="recruitment/profile", null=True) portfolio = models.URLField(max_length=200, blank=True) @@ -293,7 +297,11 @@ class Candidate(models.Model): max_length=15, choices=choices, null=True, verbose_name=_("Gender") ) source = models.CharField( - max_length=20, choices=source_choices, null=True, blank=True, verbose_name=_("Source") + max_length=20, + choices=source_choices, + null=True, + blank=True, + verbose_name=_("Source"), ) start_onboard = models.BooleanField(default=False, verbose_name=_("Start Onboard")) hired = models.BooleanField(default=False, verbose_name=_("Hired")) @@ -319,6 +327,7 @@ class Candidate(models.Model): ], default="not_sent", ) + probation_end = models.DateField(null=True, editable=False) objects = HorillaCompanyManager(related_company_field="recruitment_id__company_id") def __str__(self): @@ -409,6 +418,7 @@ class StageFiles(models.Model): def __str__(self): return self.files.name.split("/")[-1] + class StageNote(models.Model): """ StageNote model @@ -518,27 +528,24 @@ class RecruitmentMailTemplate(models.Model): title = models.CharField(max_length=25, unique=True) body = models.TextField() company_id = models.ForeignKey( - Company, null=True, blank=True, on_delete=models.CASCADE,verbose_name="Company" + Company, null=True, blank=True, on_delete=models.CASCADE, verbose_name="Company" ) class SkillZone(models.Model): - """" + """ " Model for talent pool """ + title = models.CharField(max_length=50, verbose_name="Skill Zone") description = models.TextField(verbose_name=_("Description")) - created_on = models.DateField( - default=django.utils.timezone.now - ) + created_on = models.DateField(default=django.utils.timezone.now) is_active = models.BooleanField(default=True, verbose_name=_("Is Active")) objects = HorillaCompanyManager(related_company_field="recruitment_id__company_id") - - def get_active(self): - return SkillZoneCandidate.objects.filter(is_active=True,skill_zone_id=self) - + return SkillZoneCandidate.objects.filter(is_active=True, skill_zone_id=self) + def __str__(self) -> str: return self.title @@ -547,19 +554,20 @@ class SkillZoneCandidate(models.Model): """ Model for saving candidate data's for future recruitment """ + skill_zone_id = models.ForeignKey( SkillZone, verbose_name=_("Skill Zone"), - related_name="skillzonecandidate_set", + related_name="skillzonecandidate_set", on_delete=models.PROTECT, - null=True + null=True, ) candidate_id = models.ForeignKey( Candidate, - on_delete= models.PROTECT, + on_delete=models.PROTECT, null=True, related_name="skillzonecandidate_set", - verbose_name=_("Candidate") + verbose_name=_("Candidate"), ) # job_position_id=models.ForeignKey( # JobPosition, @@ -569,10 +577,7 @@ class SkillZoneCandidate(models.Model): # verbose_name=_("Job Position") # ) - reason = models.CharField( - max_length=200, - verbose_name=_("Reason") - ) + reason = models.CharField(max_length=200, verbose_name=_("Reason")) is_active = models.BooleanField(default=True, verbose_name=_("Is Active")) added_on = models.DateField( default=django.utils.timezone.now, @@ -595,12 +600,18 @@ class SkillZoneCandidate(models.Model): class CandidateRating(models.Model): - employee_id = models.ForeignKey(Employee,on_delete=models.PROTECT, related_name="candidate_rating") - candidate_id = models.ForeignKey(Candidate,on_delete=models.PROTECT, related_name="candidate_rating") - rating = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(5)]) - + employee_id = models.ForeignKey( + Employee, on_delete=models.PROTECT, related_name="candidate_rating" + ) + candidate_id = models.ForeignKey( + Candidate, on_delete=models.PROTECT, related_name="candidate_rating" + ) + rating = models.IntegerField( + validators=[MinValueValidator(0), MaxValueValidator(5)] + ) + class Meta: - unique_together = ['employee_id', 'candidate_id'] + unique_together = ["employee_id", "candidate_id"] def __str__(self) -> str: return f"{self.employee_id} - {self.candidate_id} rating {self.rating}"