""" views.py This module contains the view functions for handling HTTP requests and rendering responses in your application. Each view function corresponds to a specific URL route and performs the necessary actions to handle the request, process data, and generate a response. This module is part of the recruitment project and is intended to provide the main entry points for interacting with the application's functionality. """ import contextlib import json from django.http import JsonResponse, HttpResponse, HttpResponseRedirect from django.shortcuts import render, redirect from django.contrib.auth.models import Permission from django.core import serializers from django.contrib import messages from django.views.decorators.http import require_http_methods from django.core.paginator import Paginator from django.core.mail import send_mail from django.utils.translation import gettext_lazy as _ from notifications.signals import notify from horilla import settings from horilla.decorators import permission_required, login_required, hx_request_required from base.methods import sortby from base.backends import ConfiguredEmailBackend from employee.models import Employee from recruitment.models import Recruitment, Candidate, Stage, StageNote from recruitment.filters import CandidateFilter, RecruitmentFilter, StageFilter from recruitment.methods import recruitment_manages from recruitment.decorators import manager_can_enter, recruitment_manager_can_enter from recruitment.forms import ( RecruitmentCreationForm, CandidateCreationForm, StageCreationForm, ApplicationForm, RecruitmentDropDownForm, StageDropDownForm, CandidateDropDownForm, StageNoteForm, ) def is_stagemanager(request, stage_id=False): """ This method is used to identify the employee is a stage manager or not, if stage_id is passed through args, method will check the employee is manager to the corresponding stage, return tuple with boolean and all stages that employee is manager. if called this method without stage_id args it will return boolean with all the stage that the employee is stage manager Args: request : django http request stage_id : stage instance id """ user = request.user employee = user.employee_get if not stage_id: return ( employee.stage_set.exists() or user.is_superuser, employee.stage_set.all(), ) stage_obj = Stage.objects.get(id=stage_id) return ( employee in stage_obj.stage_managers.all() or user.is_superuser or is_recruitmentmanager(request, rec_id=stage_obj.recruitment_id.id)[0], employee.stage_set.all(), ) def is_recruitmentmanager(request, rec_id=False): """ This method is used to identify the employee is a recruitment manager or not, if rec_id is passed through args, method will check the employee is manager to the corresponding recruitment, return tuple with boolean and all recruitment that employee is manager. if called this method without recruitment args it will return boolean with all the recruitment that the employee is recruitment manager Args: request : django http request rec_id : recruitment instance id """ user = request.user employee = user.employee_get if not rec_id: return ( employee.recruitment_set.exists() or user.is_superuser, employee.recruitment_set.all(), ) recruitment_obj = Recruitment.objects.get(id=rec_id) return ( employee in recruitment_obj.recruitment_managers.all() or user.is_superuser, employee.recruitment_set.all(), ) def paginator_qry(qryset, page_number): """ This method is used to generate common paginator limit. """ paginator = Paginator(qryset, 50) qryset = paginator.get_page(page_number) return qryset @login_required @permission_required(perm="recruitment.add_recruitment") def recruitment(request): """ This method is used to create recruitment, when create recruitment this method add recruitment view,create candidate, change stage sequence and so on, some of the permission is checking manually instead of using django permission permission to the recruitment managers """ form = RecruitmentCreationForm() if request.method == "POST": form = RecruitmentCreationForm(request.POST) if form.is_valid(): recruitment_obj = form.save() messages.success(request, _("Recruitment added.")) with contextlib.suppress(Exception): managers = recruitment_obj.recruitment_managers.select_related( "employee_user_id" ) users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb="You are chosen as one of recruitment manager", verb_ar="تم اختيارك كأحد مديري التوظيف", verb_de="Sie wurden als einer der Personalvermittler ausgewählt", verb_es="Has sido elegido/a como uno de los gerentes de contratación", verb_fr="Vous êtes choisi(e) comme l'un des responsables du recrutement", icon="people-circle", redirect="/recruitment/pipeline", ) response = render( request, "recruitment/recruitment_form.html", {"form": form} ) return HttpResponse( response.content.decode("utf-8") + "" ) return render(request, "recruitment/recruitment_form.html", {"form": form}) @login_required @permission_required(perm="recruitment.change_recruitment") @require_http_methods(["POST"]) def remove_recruitment_manager(request, mid, rid): """ This method is used to remove selected manager from the recruitment, when remove the manager permissions also removed if the employee is not exists in more stage manager or recruitment manager Args: mid : employee manager_id in the recruitment rid : recruitment_id """ recruitment_obj = Recruitment.objects.get(id=rid) manager = Employee.objects.get(id=mid) recruitment_obj.recruitment_managers.remove(manager) messages.success(request, _("Recruitment manager removed successfully.")) notify.send( request.user.employee_get, recipient=manager.employee_user_id, verb=f"You are removed from recruitment manager from {recruitment_obj}", verb_ar=f"تمت إزالتك من وظيفة مدير التوظيف في {recruitment_obj}", verb_de=f"Sie wurden als Personalvermittler von {recruitment_obj} entfernt", verb_es=f"Has sido eliminado/a como gerente de contratación de {recruitment_obj}", verb_fr=f"Vous avez été supprimé(e) en tant que responsable\ du recrutement de {recruitment_obj}", icon="person-remove", redirect="", ) recruitment_queryset = Recruitment.objects.all() previous_data = request.environ["QUERY_STRING"] return render( request, "recruitment/recruitment_component.html", { "data": paginator_qry(recruitment_queryset, request.GET.get("page")), "pd": previous_data, }, ) @login_required @permission_required(perm="recruitment.view_recruitment") def recruitment_view(request): """ This method is used to render all recruitment to view """ if not request.GET: request.GET.copy().update({"is_active": "on"}) form = RecruitmentCreationForm() filter_obj = RecruitmentFilter(request.GET, queryset=Recruitment.objects.all()) return render( request, "recruitment/recruitment_view.html", { "data": paginator_qry(filter_obj.qs, request.GET.get("page")), "f": filter_obj, "form": form, }, ) @login_required @permission_required(perm="recruitment.view_recruitment") def recruitment_search(request): """ This method is used to search recruitment """ filter_obj = RecruitmentFilter(request.GET) previous_data = request.environ["QUERY_STRING"] recruitment_obj = sortby(request, filter_obj.qs, "orderby") return render( request, "recruitment/recruitment_component.html", { "data": paginator_qry(recruitment_obj, request.GET.get("page")), "pd": previous_data, }, ) @login_required @permission_required(perm="recruitment.change_recruitment") @hx_request_required def recruitment_update(request, rec_id): """ This method is used to update the recruitment, when updating the recruitment, any changes in manager is exists then permissions also assigned to the manager Args: id : recruitment_id """ recruitment_obj = Recruitment.objects.get(id=rec_id) form = RecruitmentCreationForm(instance=recruitment_obj) if request.method == "POST": form = RecruitmentCreationForm(request.POST, instance=recruitment_obj) if form.is_valid(): recruitment_obj = form.save() messages.success(request, _("Recruitment Updated.")) response = render( request, "recruitment/recruitment_form.html", {"form": form} ) with contextlib.suppress(Exception): managers = recruitment_obj.recruitment_managers.select_related( "employee_user_id" ) users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb=f"{recruitment_obj} is updated, You are chosen as one of the managers", verb_ar=f"{recruitment_obj} تم تحديثه، تم اختيارك كأحد المديرين", verb_de=f"{recruitment_obj} wurde aktualisiert. Sie wurden als\ einer der Manager ausgewählt", verb_es=f"{recruitment_obj} ha sido actualizado/a. Has sido elegido\ a como uno de los gerentes", verb_fr=f"{recruitment_obj} a été mis(e) à jour. Vous êtes choisi(e) comme l'un des responsables", icon="people-circle", redirect="/recruitment/pipeline", ) return HttpResponse( response.content.decode("utf-8") + "" ) return render(request, "recruitment/recruitment_update_form.html", {"form": form}) @login_required @permission_required(perm="recruitment.delete_recruitment") @require_http_methods(["POST"]) def recruitment_delete(request, rec_id): """ This method is used to permanently delete the recruitment Args: id : recruitment_id """ recruitment_obj = Recruitment.objects.get(id=rec_id) recruitment_mangers = recruitment_obj.recruitment_managers.all() all_stage_permissions = Permission.objects.filter( content_type__app_label="recruitment", content_type__model="stage" ) all_candidate_permissions = Permission.objects.filter( content_type__app_label="recruitment", content_type__model="candidate" ) for manager in recruitment_mangers: all_this_manger = manager.recruitment_set.all() if len(all_this_manger) == 1: for stage_permission in all_candidate_permissions: manager.employee_user_id.user_permissions.remove(stage_permission.id) for candidate_permission in all_stage_permissions: manager.employee_user_id.user_permissions.remove( candidate_permission.id ) try: recruitment_obj.delete() messages.success(request, _("Recruitment deleted successfully.")) except Exception as error: messages.error(request, error) messages.error(request, _("You cannot delete this recruitment")) recruitment_obj = Recruitment.objects.all() return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) @login_required @manager_can_enter(perm="recruitment.view_recruitment") def recruitment_pipeline(request): """ This method is used to filter out candidate through pipeline structure """ view = request.GET.get("view") template = "pipeline/pipeline.html" if view == "card": template = "pipeline/pipeline_card.html" recruitment_form = RecruitmentDropDownForm() stage_form = StageDropDownForm() candidate_form = CandidateDropDownForm() recruitment_obj = Recruitment.objects.filter(is_active=True, closed=False) if request.method == "POST": if request.POST.get( "recruitment_managers" ) is not None and request.user.has_perm("add_recruitment"): recruitment_form = RecruitmentDropDownForm(request.POST) if recruitment_form.is_valid(): recruitment_obj = recruitment_form.save() recruitment_form = RecruitmentDropDownForm() messages.success(request, _("Recruitment added.")) with contextlib.suppress(Exception): managers = recruitment_obj.recruitment_managers.select_related( "employee_user_id" ) users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb=f"You are chosen as recruitment manager for\ the recruitment {recruitment_obj}", verb_ar=f"تم اختيارك كمدير توظيف للتوظيف {recruitment_obj}", verb_de=f"Sie wurden als Personalvermittler für die Rekrutierung\ {recruitment_obj} ausgewählt", verb_es=f"Has sido elegido/a como gerente de contratación para la contratación {recruitment_obj}", verb_fr=f"Vous êtes choisi(e) comme responsable du recrutement pour le recrutement {recruitment_obj}", icon="people-circle", redirect="/recruitment/pipeline", ) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) elif request.FILES.get("resume") is not None: if request.user.has_perm("add_candidate") or is_stagemanager( request, ): candidate_form = CandidateDropDownForm(request.POST, request.FILES) if candidate_form.is_valid(): candidate_obj = candidate_form.save() candidate_form = CandidateDropDownForm() with contextlib.suppress(Exception): managers = candidate_obj.stage_id.stage_managers.select_related( "employee_user_id" ) users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb=f"New candidate arrived on stage {candidate_obj.stage_id.stage}", verb_ar=f"وصل مرشح جديد إلى المرحلة {candidate_obj.stage_id.stage}", verb_de=f"Neuer Kandidat ist auf der Stufe {candidate_obj.stage_id.stage} angekommen", verb_es=f"Nuevo candidato llegó a la etapa {candidate_obj.stage_id.stage}", verb_fr=f"Nouveau candidat arrivé à l'étape {candidate_obj.stage_id.stage}", icon="person-add", redirect="/recruitment/pipeline", ) messages.success(request, _("Candidate added.")) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) elif request.POST.get("stage_managers") and request.user.has_perm("add_stage"): stage_form = StageDropDownForm(request.POST) if stage_form.is_valid(): if recruitment_manages( request, stage_form.instance.recruitment_id ) or request.user.has_perm("recruitment.add_stage"): stage_obj = stage_form.save() stage_form = StageDropDownForm() messages.success(request, _("Stage added.")) with contextlib.suppress(Exception): managers = stage_obj.stage_managers.select_related( "employee_user_id" ) users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb=f"You are chosen as a stage manager on the stage {stage_obj.stage} in recruitment {stage_obj.recruitment_id}", verb_ar=f"لقد تم اختيارك كمدير مرحلة في المرحلة {stage_obj.stage} في التوظيف {stage_obj.recruitment_id}", verb_de=f"Sie wurden als Bühnenmanager für die Stufe {stage_obj.stage} in der Rekrutierung {stage_obj.recruitment_id} ausgewählt", verb_es=f"Has sido elegido/a como gerente de etapa en la etapa {stage_obj.stage} en la contratación {stage_obj.recruitment_id}", verb_fr=f"Vous avez été choisi(e) comme responsable de l'étape {stage_obj.stage} dans le recrutement {stage_obj.recruitment_id}", icon="people-circle", redirect="/recruitment/pipeline", ) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) messages.info(request, _("You dont have access")) return render( request, template, { "recruitment": recruitment_obj, "recruitment_form": recruitment_form, "stage_form": stage_form, "candidate_form": candidate_form, }, ) @login_required @permission_required(perm="recruitment.view_recruitment") def recruitment_pipeline_card(request): """ This method is used to render pipeline card structure. """ search = request.GET.get("search") search = search if search is not None else "" recruitment_obj = Recruitment.objects.all() candidates = Candidate.objects.filter(name__icontains=search, is_active=True) stages = Stage.objects.all() return render( request, "pipeline/pipeline_components/pipeline_card_view.html", {"recruitment": recruitment_obj, "candidates": candidates, "stages": stages}, ) @login_required @permission_required(perm="recruitment.view_candidate") def pipeline_candidate_search(request): """ This method is used to search candidate """ template = "pipeline/pipeline_components/kanban_tabs.html" if request.GET.get("view") == "card": template = "pipeline/pipeline_components/kanban_tabs.html" return render(request, template) @login_required @recruitment_manager_can_enter(perm="recruitment.change_stage") def stage_update_pipeline(request, stage_id): """ This method is used to update stage from pipeline view """ stage_obj = Stage.objects.get(id=stage_id) form = StageCreationForm(instance=stage_obj) if request.POST: form = StageCreationForm(request.POST, instance=stage_obj) if form.is_valid(): stage_obj = form.save() messages.success(request, _("Stage updated.")) with contextlib.suppress(Exception): managers = stage_obj.stage_managers.select_related("employee_user_id") users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb=f"{stage_obj.stage} stage in recruitment {stage_obj.recruitment_id}\ is updated, You are chosen as one of the managers", verb_ar=f"تم تحديث مرحلة {stage_obj.stage} في التوظيف {stage_obj.recruitment_id}\ ، تم اختيارك كأحد المديرين", verb_de=f"Die Stufe {stage_obj.stage} in der Rekrutierung {stage_obj.recruitment_id}\ wurde aktualisiert. Sie wurden als einer der Manager ausgewählt", verb_es=f"Se ha actualizado la etapa {stage_obj.stage} en la contratación {stage_obj.recruitment_id}.\ Has sido elegido/a como uno de los gerentes", verb_fr=f"L'étape {stage_obj.stage} dans le recrutement {stage_obj.recruitment_id} a été mise à jour.\ Vous avez été choisi(e) comme l'un des responsables", icon="people-circle", redirect="/recruitment/pipeline", ) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) return render(request, "pipeline/form/stage_update.html", {"form": form}) @login_required @recruitment_manager_can_enter(perm="recruitment.change_recruitment") def recruitment_update_pipeline(request, rec_id): """ This method is used to update recruitment from pipeline view """ recruitment_obj = Recruitment.objects.get(id=rec_id) form = RecruitmentCreationForm(instance=recruitment_obj) if request.POST: form = RecruitmentCreationForm(request.POST, instance=recruitment_obj) if form.is_valid(): recruitment_obj = form.save() messages.success(request, _("Recruitment updated.")) with contextlib.suppress(Exception): managers = recruitment_obj.recruitment_managers.select_related( "employee_user_id" ) users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb=f"{recruitment_obj} is updated, You are chosen as one of the managers", verb_ar=f"تم تحديث {recruitment_obj}، تم اختيارك كأحد المديرين", verb_de=f"{recruitment_obj} wurde aktualisiert. Sie wurden als einer der Manager ausgewählt", verb_es=f"{recruitment_obj} ha sido actualizado/a. Has sido elegido\ a como uno de los gerentes", verb_fr=f"{recruitment_obj} a été mis(e) à jour. Vous avez été\ choisi(e) comme l'un des responsables", icon="people-circle", redirect="/recruitment/pipeline", ) response = render( request, "pipeline/form/recruitment_update.html", {"form": form} ) return HttpResponse( response.content.decode("utf-8") + "" ) return render(request, "pipeline/form/recruitment_update.html", {"form": form}) @login_required @permission_required(perm="recruitment.delete_recruitment") @require_http_methods(["POST"]) def recruitment_delete_pipeline(request, rec_id): """This method is used to delete the recruitment instance Args: id: recruitment instance id Returns: HttpResponseRedirect: Used to refresh the page """ recruitment_obj = Recruitment.objects.get(id=rec_id) try: recruitment_obj.delete() messages.success(request, _("Recruitment deleted.")) except Exception as error: messages.error(request, error) messages.error(request, _("Recruitment already in use.")) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) @login_required @manager_can_enter(perm="recruitment.change_candidate") def candidate_stage_update(request, cand_id): """ This method is a ajax method used to update candidate stage when drag and drop the candidate from one stage to another on the pipeline template Args: id : candidate_id """ stage_id = request.POST["stageId"] candidate_obj = Candidate.objects.get(id=cand_id) history_queryset = candidate_obj.candidate_history.all().first() stage_obj = Stage.objects.get(id=stage_id) previous_stage = history_queryset.stage_id if previous_stage == stage_obj: return JsonResponse({"type": "info", "message": _("Sequence updated.")}) # Here set the last updated schedule date on this stage if schedule exists in history history_queryset = candidate_obj.candidate_history.filter(stage_id=stage_obj) schedule_date = None if history_queryset.exists(): # this condition is executed when a candidate dropped back to any previous # stage, if there any scheduled date then set it back schedule_date = history_queryset.first().schedule_date stage_manager_on_this_recruitment = ( is_stagemanager(request)[1] .filter(recruitment_id=stage_obj.recruitment_id) .exists() ) if ( stage_manager_on_this_recruitment or request.user.is_superuser or is_recruitmentmanager(rec_id=stage_obj.recruitment_id.id)[0] ): candidate_obj.stage_id = stage_obj candidate_obj.schedule_date = None candidate_obj.hired = stage_obj.stage_type == "hired" candidate_obj.schedule_date = schedule_date candidate_obj.start_onboard = False candidate_obj.save() with contextlib.suppress(Exception): managers = stage_obj.stage_managers.select_related("employee_user_id") users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb=f"New candidate arrived on stage {stage_obj.stage}", verb_ar=f"وصل مرشح جديد إلى المرحلة {stage_obj.stage}", verb_de=f"Neuer Kandidat ist auf der Stufe {stage_obj.stage} angekommen", verb_es=f"Nuevo candidato llegó a la etapa {stage_obj.stage}", verb_fr=f"Nouveau candidat arrivé à l'étape {stage_obj.stage}", icon="person-add", redirect="/recruitment/pipeline", ) return JsonResponse( {"type": "success", "message": _("Candidate stage updated")} ) return JsonResponse( {"type": "danger", "message": _("Something went wrong, Try agian.")} ) @login_required @hx_request_required @manager_can_enter(perm="recruitment.add_stagenote") def add_note(request, cand_id=None): """ This method renders template component to add candidate remark """ form = StageNoteForm(initial={"candidate_id": cand_id}) if request.method == "POST": form = StageNoteForm( request.POST, ) if form.is_valid(): note = form.save(commit=False) note.stage_id = note.candidate_id.stage_id note.updated_by = request.user.employee_get note.save() messages.success(request, _("Note added successfully..")) response = render( request, "pipeline/pipeline_components/add_note.html", {"form": form} ) return HttpResponse( response.content.decode("utf-8") + "" ) return render( request, "pipeline/pipeline_components/add_note.html", { "note_form": form, }, ) @login_required @hx_request_required @manager_can_enter(perm="recruitment.view_stagenote") def view_note(request, cand_id): """ This method renders a template components to view candidate remark or note Args: id : candidate instance id """ candidate_obj = Candidate.objects.get(id=cand_id) return render( request, "pipeline/pipeline_components/view_note.html", {"cand": candidate_obj} ) @login_required @permission_required(perm="recruitment.change_stagenote") def note_update(request, note_id): """ This method is used to update the stage not Args: id : stage note instance id """ note = StageNote.objects.get(id=note_id) form = StageNoteForm(instance=note) if request.POST: form = StageNoteForm(request.POST, instance=note) if form.is_valid(): form.save() messages.success(request, _("Note updated successfully...")) response = render( request, "pipeline/pipeline_components/update_note.html", {"form": form} ) return HttpResponse( response.content.decode("utf-8") + "" ) return render( request, "pipeline/pipeline_components/update_note.html", {"form": form} ) @login_required @permission_required(perm="recruitment.delete_stagenote") def note_delete(request, note_id): """ This method is used to delete the stage note """ note = StageNote.objects.get(id=note_id) candidate_obj = note.candidate_id try: note.delete() messages.success(request, _("Note deleted")) except Exception as error: messages.error(request, error) messages.error(request, _("You cannot delete this note.")) return render( request, "pipeline/pipeline_components/view_note.html", {"cand": candidate_obj} ) @login_required @require_http_methods(["DELETE"]) @permission_required(perm="recruitment.delete_stagenote") def candidate_remark_delete(request, note_id): """ This method is used to delete the candidate stage note Args: id : stage note instance id """ stage_note = StageNote.objects.get(id=note_id) candidate_obj = stage_note.candidate_note_id.candidate_id try: stage_note.delete() messages.success(request, _("Note deleted")) except Exception as error: messages.error(request, error) messages.error(request, _("You cannot delete this note.")) return render( request, "pipeline/pipeline_components/candidate_remark_view.html", {"cand": candidate_obj}, ) @login_required @permission_required(perm="recruitment.change_candidate") def candidate_schedule_date_update(request): """ This is a an ajax method to update schedule date for a candidate """ candidate_id = request.POST["candidateId"] schedule_date = request.POST["date"] candidate_obj = Candidate.objects.get(id=candidate_id) candidate_obj.schedule_date = schedule_date candidate_obj.save() return JsonResponse({"message": "congratulations"}) @login_required @permission_required(perm="recruitment.add_stage") def stage(request): """ This method is used to create stages, also several permission assigned to the stage managers """ form = StageCreationForm() if request.method == "POST": form = StageCreationForm(request.POST) if form.is_valid(): stage_obj = form.save() recruitment_obj = stage_obj.recruitment_id rec_stages = ( Stage.objects.filter(recruitment_id=recruitment_obj, is_active=True) .order_by("sequence") .last() ) if rec_stages.sequence is None: stage_obj.sequence = 1 else: stage_obj.sequence = rec_stages.sequence + 1 stage_obj.save() messages.success(request, _("Stage added.")) with contextlib.suppress(Exception): managers = stage_obj.stage_managers.select_related("employee_user_id") users = [employee.employee_user_id for employee in managers] notify.send( request.user.employee_get, recipient=users, verb=f"Stage {stage_obj} is updated on recruitment {stage_obj.recruitment_id}, You are chosen as one of the managers", verb_ar=f"تم تحديث المرحلة {stage_obj} في التوظيف {stage_obj.recruitment_id}، تم اختيارك كأحد المديرين", verb_de=f"Stufe {stage_obj} wurde in der Rekrutierung {stage_obj.recruitment_id} aktualisiert. Sie wurden als einer der Manager ausgewählt", verb_es=f"La etapa {stage_obj} ha sido actualizada en la contratación {stage_obj.recruitment_id}. Has sido elegido/a como uno de los gerentes", verb_fr=f"L'étape {stage_obj} a été mise à jour dans le recrutement {stage_obj.recruitment_id}. Vous avez été choisi(e) comme l'un des responsables", icon="people-circle", redirect="/recruitment/pipeline", ) response = render(request, "stage/stage_form.html", {"form": form}) return HttpResponse( response.content.decode("utf-8") + "" ) return render(request, "stage/stage_form.html", {"form": form}) @login_required @permission_required(perm="recruitment.view_stage") def stage_view(request): """ This method is used to render all stages to a template """ stages = Stage.objects.filter() filter_obj = StageFilter() form = StageCreationForm() return render( request, "stage/stage_view.html", { "data": paginator_qry(stages, request.GET.get("page")), "form": form, "f": filter_obj, }, ) @login_required @permission_required(perm="recruitment.view_stage") def stage_search(request): """ This method is used to search stage """ stages = StageFilter(request.GET).qs previous_data = request.environ["QUERY_STRING"] stages = sortby(request, stages, "orderby") return render( request, "stage/stage_component.html", {"data": paginator_qry(stages, request.GET.get("page")), "pd": previous_data}, ) @login_required @permission_required(perm="recruitment.change_stage") def remove_stage_manager(request, mid, sid): """ This method is used to remove selected stage manager and also removing the given permission if the employee is not exists in more stage manager or recruitment manager Args: mid : manager_id in the stage sid : stage_id """ stage_obj = Stage.objects.get(id=sid) manager = Employee.objects.get(id=mid) notify.send( request.user.employee_get, recipient=manager.employee_user_id, verb=f"You are removed from stage managers from stage {stage_obj}", verb_ar=f"تمت إزالتك من مديري المرحلة من المرحلة {stage_obj}", verb_de=f"Sie wurden als Bühnenmanager von der Stufe {stage_obj} entfernt", verb_es=f"Has sido eliminado/a de los gerentes de etapa de la etapa {stage_obj}", verb_fr=f"Vous avez été supprimé(e) en tant que responsable de l'étape {stage_obj}", icon="person-remove", redirect="", ) stage_obj.stage_managers.remove(manager) messages.success(request, _("Stage manager removed successfully.")) stages = Stage.objects.all() previous_data = request.environ["QUERY_STRING"] return render( request, "stage/stage_component.html", {"data": paginator_qry(stages, request.GET.get("page")), "pd": previous_data}, ) @login_required @permission_required(perm="recruitment.change_stage") @hx_request_required def stage_update(request, stage_id): """ This method is used to update stage, if the managers changed then\ permission assigned to new managers also Args: id : stage_id """ stages = Stage.objects.get(id=stage_id) form = StageCreationForm(instance=stages) if request.method == "POST": form = StageCreationForm(request.POST, instance=stages) if form.is_valid(): form.save() messages.success(request, _("Stage updated.")) response = render( request, "recruitment/recruitment_form.html", {"form": form} ) return HttpResponse( response.content.decode("utf-8") + "" ) return render(request, "stage/stage_update_form.html", {"form": form}) @login_required @require_http_methods(["POST"]) @hx_request_required def stage_name_update(request, stage_id): """ This method is used to update the name of recruitment stage """ stage_obj = Stage.objects.get(id=stage_id) stage_obj.stage = request.POST["stage"] stage_obj.save() return HttpResponse({"message": "success"}) @login_required @permission_required(perm="recruitment.delete_stage") @require_http_methods(["POST", "DELETE"]) def stage_delete(request, stage_id): """ This method is used to delete stage permanently Args: id : stage_id """ stage_obj = Stage.objects.get(id=stage_id) stage_managers = stage_obj.stage_managers.all() for manager in stage_managers: all_this_manger = manager.stage_set.all() if len(all_this_manger) == 1: view_recruitment = Permission.objects.get(codename="view_recruitment") manager.employee_user_id.user_permissions.remove(view_recruitment.id) initial_stage_manager = all_this_manger.filter(stage_type="initial") if len(initial_stage_manager) == 1: add_candidate = Permission.objects.get(codename="add_candidate") change_candidate = Permission.objects.get(codename="change_candidate") manager.employee_user_id.user_permissions.remove(add_candidate.id) manager.employee_user_id.user_permissions.remove(change_candidate.id) stage_obj.stage_managers.remove(manager) try: stage_obj.delete() messages.success(request, _("Stage deleted successfully.")) except Exception as error: messages.error(request, error) messages.error(request, _("You cannot delete this stage")) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) @login_required @permission_required(perm="recruitment.add_candidate") def candidate(request): """ This method used to create candidate """ form = CandidateCreationForm() open_recruitment = Recruitment.objects.filter(closed=False, is_active=True) path = "/recruitment/candidate-view" if request.method == "POST": form = CandidateCreationForm(request.POST, request.FILES) if form.is_valid(): candidate_obj = form.save(commit=False) candidate_obj.start_onboard = False if candidate_obj.stage_id is None: candidate_obj.stage_id = Stage.objects.filter( recruitment_id=candidate_obj.recruitment_id, stage_type="initial" ).first() # when creating new candidate from onboarding view if request.GET.get("onboarding") == "True": candidate_obj.hired = True path = "/onboarding/candidates-view" candidate_obj.save() messages.success(request, _("Candidate added.")) return redirect(path) return render( request, "candidate/candidate_create_form.html", {"form": form, "open_recruitment": open_recruitment}, ) @login_required @permission_required(perm="recruitment.add_candidate") def recruitment_stage_get(_, rec_id): """ This method returns all stages as json Args: id: recruitment_id """ recruitment_obj = Recruitment.objects.get(id=rec_id) all_stages = recruitment_obj.stage_set.all() all_stage_json = serializers.serialize("json", all_stages) return JsonResponse({"stages": all_stage_json}) @login_required @permission_required(perm="recruitment.view_candidate") def candidate_view(request): """ This method render all candidate to the template """ previous_data = request.environ["QUERY_STRING"] candidates = Candidate.objects.filter(is_active=True) filter_obj = CandidateFilter(queryset=candidates) return render( request, "candidate/candidate_view.html", { "data": paginator_qry(filter_obj.qs, request.GET.get("page")), "pd": previous_data, "f": filter_obj, }, ) @login_required @permission_required(perm="recruitment.view_candidate") def candidate_filter_view(request): """ This method is used for filter,pagination and search candidate. """ previous_data = request.environ["QUERY_STRING"] filter_obj = CandidateFilter( request.GET, queryset=Candidate.objects.filter(is_active=True) ) paginator = Paginator(filter_obj.qs, 24) page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) return render( request, "candidate/candidate_card.html", {"data": page_obj, "pd": previous_data}, ) @login_required @permission_required(perm="recruitment.view_candidate") def candidate_search(request): """ This method is used to search candidate model and return matching objects """ previous_data = request.environ["QUERY_STRING"] search = request.GET.get("search") if search is None: search = "" candidates = Candidate.objects.filter(name__icontains=search) candidates = CandidateFilter(request.GET, queryset=candidates).qs template = "candidate/candidate_card.html" if request.GET.get("view") == "list": template = "candidate/candidate_list.html" candidates = sortby(request, candidates, "orderby") candidates = paginator_qry(candidates, request.GET.get("page")) return render(request, template, {"data": candidates, "pd": previous_data}) @login_required @permission_required(perm="recruitment.view_candidate") def candidate_view_list(request): """ This method renders all candidate on candidate_list.html template """ previous_data = request.environ["QUERY_STRING"] candidates = Candidate.objects.all() if request.GET.get("is_active") is None: candidates = candidates.filter(is_active=True) candidates = CandidateFilter(request.GET, queryset=candidates).qs return render( request, "candidate/candidate_list.html", { "data": paginator_qry(candidates, request.GET.get("page")), "pd": previous_data, }, ) @login_required @permission_required(perm="recruitment.view_candidate") def candidate_view_card(request): """ This method renders all candidate on candidate_card.html template """ previous_data = request.environ["QUERY_STRING"] candidates = Candidate.objects.all() if request.GET.get("is_active") is None: candidates = candidates.filter(is_active=True) candidates = CandidateFilter(request.GET, queryset=candidates).qs return render( request, "candidate/candidate_card.html", { "data": paginator_qry(candidates, request.GET.get("page")), "pd": previous_data, }, ) @login_required @permission_required(perm="recruitment.view_candidate") def candidate_view_individual(request, cand_id): """ This method is used to view profile of candidate. """ candidate_obj = Candidate.objects.get(id=cand_id) return render(request, "candidate/individual.html", {"candidate": candidate_obj}) @login_required @manager_can_enter(perm="recruitment.change_candidate") def candidate_update(request, cand_id): """ Used to update or change the candidate Args: id : candidate_id """ candidate_obj = Candidate.objects.get(id=cand_id) form = CandidateCreationForm(instance=candidate_obj) path = "/recruitment/candidate-view" if request.method == "POST": form = CandidateCreationForm( request.POST, request.FILES, instance=candidate_obj ) if form.is_valid(): candidate_obj = form.save() if candidate_obj.stage_id is None: candidate_obj.stage_id = Stage.objects.filter( recruitment_id=candidate_obj.recruitment_id, stage_type="initial" ).first() if candidate_obj.stage_id is not None: if ( candidate_obj.stage_id.recruitment_id != candidate_obj.recruitment_id ): candidate_obj.stage_id = ( candidate_obj.recruitment_id.stage_set.filter( stage_type="initial" ).first() ) if request.GET.get("onboarding") == "True": candidate_obj.hired = True path = "/onboarding/candidates-view" candidate_obj.save() messages.success(request, _("Candidate Updated Successfully.")) return redirect(path) return render(request, "candidate/candidate_create_form.html", {"form": form}) @login_required @permission_required(perm="recruitment.delete_candidate") @require_http_methods(["DELETE", "POST"]) def candidate_delete(request, cand_id): """ This method is used to delete candidate permanently Args: id : candidate_id """ try: Candidate.objects.get(id=cand_id).delete() messages.success(request, _("Candidate deleted successfully.")) except Exception as error: messages.error(request, error) messages.error(request, _("You cannot delete this candidate")) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) @login_required @permission_required(perm="recruitment.delete_candidate") def candidate_archive(request, cand_id): """ This method is used to archive or un-archive candidates """ candidate_obj = Candidate.objects.get(id=cand_id) candidate_obj.is_active = not candidate_obj.is_active candidate_obj.save() return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) @login_required @permission_required(perm="recruitment.delete_candidate") @require_http_methods(["POST"]) def candidate_bulk_delete(request): """ This method is used to bulk delete candidates """ ids = request.POST["ids"] ids = json.loads(ids) for cand_id in ids: candidate_obj = Candidate.objects.get(id=cand_id) try: candidate_obj.delete() messages.success( request, _("%(candidate_obj)s deleted.") % {"candidate": candidate_obj} ) except Exception as error: messages.error( request, _("You cannot delete %(candidate_obj)s") % {"candidate": candidate_obj}, ) messages.error(request, error) return JsonResponse({"message": "Success"}) @login_required @permission_required(perm="recruitment.delete_candidate") @require_http_methods(["POST"]) def candidate_bulk_archive(request): """ This method is used to archive/un-archive bulk candidates """ ids = request.POST["ids"] ids = json.loads(ids) is_active = True message = "un-archived" if request.GET.get("is_active") == "False": is_active = False message = "archived" for cand_id in ids: candidate_obj = Candidate.objects.get(id=cand_id) candidate_obj.is_active = is_active candidate_obj.save() messages.success(request, f"{candidate_obj} is {message}") return JsonResponse({"message": "Success"}) @login_required @permission_required(perm="recruitment.view_history") def candidate_history(request, cand_id): """ This method is used to view candidate stage changes Args: id : candidate_id """ candidate_obj = Candidate.objects.get(id=cand_id) candidate_history_queryset = candidate_obj.history.all() return render( request, "candidate/candidate_history.html", {"history": candidate_history_queryset}, ) def application_form(request): """ This method renders candidate form to create candidate """ form = ApplicationForm() if request.POST: form = ApplicationForm(request.POST, request.FILES) if form.is_valid(): candidate_obj = form.save(commit=False) recruitment_obj = candidate_obj.recruitment_id stages = recruitment_obj.stage_set.all() if stages.filter(stage_type="initial").exists(): candidate_obj.stage_id = stages.filter(stage_type="initial").first() else: candidate_obj.stage_id = stages.order_by("sequence").first() candidate_obj.save() messages.success(request, _("Application saved.")) return render(request, "candidate/success.html") form.fields["job_position_id"].queryset = ( form.instance.recruitment_id.open_positions.all() ) return render(request, "candidate/application_form.html", {"form": form}) @login_required @hx_request_required @manager_can_enter(perm="recruitment.change_candidate") def form_send_mail(request, cand_id): """ This method is used to render the bootstrap modal content body form """ candidate_obj = Candidate.objects.get(id=cand_id) return render( request, "pipeline/pipeline_components/send_mail.html", {"cand": candidate_obj} ) @login_required @manager_can_enter(perm="recruitment.change_candidate") def send_acknowledgement(request): """ This method is used to send acknowledgement mail to the candidate """ with contextlib.suppress(Exception): send_to = request.POST.get("to") subject = request.POST.get("subject") bdy = request.POST.get("body") email_backend = ConfiguredEmailBackend() res = send_mail( subject, bdy, email_backend.dynamic_username_with_display_name, [send_to], fail_silently=False, ) if res == 1: return HttpResponse( """
Mail sent.
""" ) return HttpResponse( """
Sorry,\ Something went wrong.
""" ) @login_required @manager_can_enter(perm="recruitment.view_recruitment") def dashboard(request): """ This method is used to render individual dashboard for recruitment module """ candidates = Candidate.objects.all() hired_candidates = candidates.filter(hired=True) total_candidates = len(candidates) total_hired_candidates = len(hired_candidates) hire_ratio = 0 if total_candidates != 0: hire_ratio = f"{((total_hired_candidates / total_candidates) * 100):.1f}" return render( request, "dashboard/dashboard.html", { "total_candidates": total_candidates, "total_hired_candidates": total_hired_candidates, "hire_ratio": hire_ratio, "onboard_candidates": hired_candidates.filter(start_onboard=True), }, ) def stage_type_candidate_count(rec, stage_type): """ This method is used find the count of candidate in recruitment """ candidates_count = 0 for stage_obj in rec.stage_set.filter(stage_type=stage_type): candidates_count = candidates_count + len( stage_obj.candidate_set.filter(is_active=True) ) return candidates_count @login_required @manager_can_enter(perm="recruitment.view_recruitment") def dashboard_pipeline(_): """ This method is used generate recruitment dataset for the dashboard """ recruitment_obj = Recruitment.objects.filter(closed=False) data_set = [] labels = [type[1] for type in Stage.stage_types] for rec in recruitment_obj: data = [stage_type_candidate_count(rec, type[0]) for type in Stage.stage_types] data_set.append( { "label": ( rec.title if rec.title is not None else f"""{rec.job_position_id} {rec.start_date}""" ), "data": data, } ) return JsonResponse({"dataSet": data_set, "labels": labels}) def get_open_position(request): """ This is an ajax method to render the open position to the recruitment Returns: obj: it returns the list of job positions """ rec_id = request.GET["recId"] recruitment_obj = Recruitment.objects.get(id=rec_id) queryset = recruitment_obj.open_positions.all() job_info = serializers.serialize("json", queryset) rec_info = serializers.serialize("json", [recruitment_obj]) return JsonResponse({"openPositions": job_info, "recruitmentInfo": rec_info}) @login_required @manager_can_enter("recruitment.change_candidate") def candidate_sequence_update(request): """ This method is used to update the sequence of candidate """ sequence_data = json.loads(request.POST["sequenceData"]) for cand_id, seq in sequence_data.items(): cand = Candidate.objects.get(id=cand_id) cand.sequence = seq cand.save() return JsonResponse({"message": "Sequence updated", "type": "info"}) @login_required @recruitment_manager_can_enter("recruitment.change_stage") def stage_sequence_update(request): """ This method is used to update the sequence of the stages """ sequence_data = json.loads(request.POST["sequence"]) for stage_id, seq in sequence_data.items(): stage = Stage.objects.get(id=stage_id) stage.sequence = seq stage.save() return JsonResponse({"type": "success", "message": "Stage sequence updated"})