[UPDT] RECRUITMENT: Updated recruitment app by adding find class method for horilla models

This commit is contained in:
Horilla
2024-07-08 14:54:48 +05:30
parent 697f05d721
commit e3b687bf16
10 changed files with 840 additions and 472 deletions

View File

@@ -50,6 +50,8 @@ from recruitment.models import (
RecruitmentSurvey,
RejectedCandidate,
RejectReason,
Resume,
Skill,
SkillZone,
SkillZoneCandidate,
Stage,
@@ -257,6 +259,7 @@ class RecruitmentCreationForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
reload_queryset(self.fields)
if not self.instance.pk:
self.fields["recruitment_managers"] = HorillaMultiSelectField(
@@ -271,11 +274,31 @@ class RecruitmentCreationForm(ModelForm):
label="Employee",
)
skill_choices = [("", _("---Choose Skills---"))] + list(
self.fields["skills"].queryset.values_list("id", "title")
)
self.fields["skills"].choices = skill_choices
self.fields["skills"].choices += [("create", _("Create new skill "))]
# def create_option(self, *args,**kwargs):
# option = super().create_option(*args,**kwargs)
# if option.get('value') == "create":
# option['attrs']['class'] = 'text-danger'
# return option
def clean(self):
if isinstance(self.fields["recruitment_managers"], HorillaMultiSelectField):
ids = self.data.getlist("recruitment_managers")
if ids:
self.errors.pop("recruitment_managers", None)
open_positions = self.cleaned_data.get("open_positions")
is_published = self.cleaned_data.get("is_published")
if is_published and not open_positions:
raise forms.ValidationError(
_("Job position is required if the recruitment is publishing.")
)
super().clean()
@@ -423,6 +446,9 @@ class CandidateCreationForm(ModelForm):
return super().clean()
from horilla.horilla_middlewares import _thread_locals
class ApplicationForm(RegistrationForm):
"""
Form for create Candidate
@@ -468,8 +494,23 @@ class ApplicationForm(RegistrationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
request = getattr(_thread_locals, "request", None)
self.fields["recruitment_id"].widget.attrs = {"data-widget": "ajax-widget"}
self.fields["job_position_id"].widget.attrs = {"data-widget": "ajax-widget"}
if request and request.user.has_perm("recruitment.add_candidate"):
self.fields["profile"].required = False
def clean(self, *args, **kwargs):
name = self.cleaned_data["name"]
request = getattr(_thread_locals, "request", None)
if request and request.user.has_perm("recruitment.add_candidate"):
profile_pic_url = f"https://ui-avatars.com/api/?name={name}"
self.cleaned_data["profile"] = profile_pic_url
super().clean()
return self.cleaned_data
class RecruitmentDropDownForm(DropDownForm):
@@ -665,7 +706,9 @@ class QuestionForm(ModelForm):
required=False,
label=_("Recruitment"),
)
options = forms.CharField(widget=forms.TextInput, label=_("Options"), required=True)
options = forms.CharField(
widget=forms.TextInput, label=_("Options"), required=False
)
class Meta:
"""
@@ -697,7 +740,7 @@ class QuestionForm(ModelForm):
cleaned_data = super().clean()
recruitment = self.cleaned_data["recruitment"]
question_type = self.cleaned_data["type"]
options = self.cleaned_data["options"]
options = self.cleaned_data.get("options")
if not recruitment.exists(): # or jobs.exists()):
raise ValidationError(
{"recruitment": _("Choose any recruitment to apply this question")}
@@ -740,7 +783,7 @@ class QuestionForm(ModelForm):
}
),
label=_("Options"),
required=True,
required=False,
initial=initial,
)
@@ -834,7 +877,20 @@ class AddQuestionForm(Form):
return table_html
exclude_fields = ["id", "profile", "portfolio", "resume", "sequence", "schedule_date"]
exclude_fields = [
"id",
"profile",
"portfolio",
"resume",
"sequence",
"schedule_date",
"created_at",
"created_by",
"modified_by",
"is_active",
"last_updated",
"horilla_history",
]
class CandidateExportForm(forms.Form):
@@ -1160,3 +1216,24 @@ class ScheduleInterviewForm(ModelForm):
context = {"form": self}
table_html = render_to_string("common_form.html", context)
return table_html
class SkillsForm(ModelForm):
class Meta:
model = Skill
fields = ["title"]
class ResumeForm(ModelForm):
class Meta:
model = Resume
fields = ["file", "recruitment_id"]
widgets = {"recruitment_id": forms.HiddenInput()}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["file"].widget.attrs.update(
{
"onchange": "submitForm($(this))",
}
)

View File

@@ -80,6 +80,18 @@ class SurveyTemplate(HorillaModel):
return self.title
class Skill(HorillaModel):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
title = self.title
self.title = title.capitalize()
super().save(*args, **kwargs)
class Recruitment(HorillaModel):
"""
Recruitment model
@@ -136,6 +148,7 @@ class Recruitment(HorillaModel):
)
start_date = models.DateField(default=django.utils.timezone.now)
end_date = models.DateField(blank=True, null=True)
skills = models.ManyToManyField(Skill, blank=True)
objects = HorillaCompanyManager()
default = models.manager.Manager()
@@ -175,14 +188,20 @@ class Recruitment(HorillaModel):
def clean(self):
if self.title is None:
raise ValidationError({"title": _("This field is required")})
if self.is_published:
if self.vacancy <= 0:
raise ValidationError(
_(
"Vacancy must be greater than zero if the recruitment is publishing."
)
)
if self.end_date is not None and (
self.start_date is not None and self.start_date > self.end_date
):
raise ValidationError(
{"end_date": _("End date cannot be less than start date.")}
)
# if not self.is_event_based and self.job_position_id is None:
# raise ValidationError({"job_position_id": _("This field is required")})
return super().clean()
def save(self, *args, **kwargs):
@@ -888,3 +907,19 @@ class InterviewSchedule(HorillaModel):
def __str__(self) -> str:
return f"{self.candidate_id} -Interview."
class Resume(models.Model):
file = models.FileField(
upload_to="recruitment/resume",
validators=[
validate_pdf,
],
)
recruitment_id = models.ForeignKey(
Recruitment, on_delete=models.CASCADE, related_name="resume"
)
is_candidate = models.BooleanField(default=False)
def __str__(self):
return f"{self.recruitment_id} - Resume {self.pk}"

View File

@@ -6,39 +6,12 @@
aria-labelledby="candidateExport"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="candidateExportLavel">
{% trans "Export Candidates" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
<div
class="oh-modal__dialog-body p-0 pt-2"
id="candidateExportModalBody"
>
<form
action="{%url 'candidate-info-export' %}"
method="get"
onsubmit="event.stopPropagation();$(this).parents().find('.oh-modal--show').last().toggleClass('oh-modal--show');"
id="candidateExportForm"
>
{% csrf_token %} {% include 'candidate/export_filter.html'%}
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100">
{% trans "Export" %}
</button>
</div>
</form>
</div>
</div>
</div>
<div class="oh-modal__dialog" id="candidateExportModalBody"></div>
</div>
<section
class="oh-wrapper oh-main__topbar"
x-data="{searchShow: false}"
>
>
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">
{% trans "Candidates" %}
@@ -48,7 +21,7 @@ x-data="{searchShow: false}"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon"
@@ -179,6 +152,8 @@ x-data="{searchShow: false}"
id="candidate-info-export"
data-toggle="oh-modal-toggle"
data-target="#candidateExport"
hx-get="{% url 'candidate-info-export' %}"
hx-target="#candidateExportModalBody"
>{% trans "Export" %}</a
>
{% endif %}

View File

@@ -1,170 +1,197 @@
{% load static %} {% load i18n %}
<div class="oh-dropdown__filter-body" id="export_attendance_form">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Excel columns" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">
<input type="checkbox" id="select-all-fields" /> {% trans "Select All" %}
</label>
</div>
</div>
</div>
<div class="row">
{% for field in export_fields.selected_fields %}
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label"> {{ field }} </label>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Candidates" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Phone" %}</label>
{{export_obj.form.mobile}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Interview Date" %}</label>
{{export_obj.form.interview_date}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Country" %}</label>
{{export_obj.form.country}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Is Hired" %}?</label>
{{export_obj.form.hired}}
</div>
<div class="col-sm-12 col-md-12 mb-2">
<div class="oh-input-group">
<label class="oh-label">{% trans "Reject Reason" %}</label>
{{export_obj.form.rejected_candidate__reject_reason_id}}
</div>
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Email" %}</label>
{{export_obj.form.email}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Gender" %}</label>
{{export_obj.form.gender}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "State" %}</label>
{{export_obj.form.state}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Is Canceled" %}?</label>
{{export_obj.form.canceled}}
</div>
<div class="col-sm-12 col-md-12 mb-2">
<div class="oh-input-group">
<label class="oh-label">{% trans "Offer Status" %}</label>
{{export_obj.form.offer_letter_status}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Recruitment" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Recruitment" %}</label>
{{export_obj.form.recruitment_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Job Position" %}</label>
{{export_obj.form.job_position_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Start Date" %}</label>
{{export_obj.form.start_date}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Is Closed" %}?</label>
{{export_obj.form.recruitment_id__closed}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage Type" %}</label>
{{export_obj.form.stage_id__stage_type}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage Managers" %}</label>
{{export_obj.form.stage_id__stage_managers}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
{{export_obj.form.stage_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Department" %}</label>
{{export_obj.form.job_position_id__department_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Company" %}</label>
{{export_obj.form.recruitment_id__company_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Recruitment Managers" %}</label>
{{export_obj.form.recruitment_id__recruitment_managers}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "End Date" %}</label>
{{export_obj.form.end_date}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Interview From" %}</label>
{{export_obj.form.scheduled_from}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Is Active" %}?</label>
{{export_obj.form.is_active}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Interview Till" %}</label>
{{export_obj.form.scheduled_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="candidateExportLavel">
{% trans "Export Candidates" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body">
<form
action="{%url 'candidate-info-export' %}"
method="get"
class="oh-profile-section"
onsubmit="event.stopPropagation();$(this).parents().find('.oh-modal--show').last().toggleClass('oh-modal--show');"
id="candidateExportForm"
>
{% csrf_token %}
<div class="oh-dropdown__filter-body" id="export_attendance_form">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Excel columns" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">
<input type="checkbox" id="select-all-fields" /> {% trans "Select All" %}
</label>
</div>
</div>
</div>
<div class="row">
{% for field in export_column.selected_fields %}
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label"> {{ field }} </label>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Candidates" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Phone" %}</label>
{{export_filter.form.mobile}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Interview Date" %}</label>
{{export_filter.form.interview_date}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Country" %}</label>
{{export_filter.form.country}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Is Hired" %}?</label>
{{export_filter.form.hired}}
</div>
<div class="col-sm-12 col-md-12 mb-2">
<div class="oh-input-group">
<label class="oh-label">{% trans "Reject Reason" %}</label>
{{export_filter.form.rejected_candidate__reject_reason_id}}
</div>
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Email" %}</label>
{{export_filter.form.email}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Gender" %}</label>
{{export_filter.form.gender}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "State" %}</label>
{{export_filter.form.state}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Is Canceled" %}?</label>
{{export_filter.form.canceled}}
</div>
<div class="col-sm-12 col-md-12 mb-2">
<div class="oh-input-group">
<label class="oh-label">{% trans "Offer Status" %}</label>
{{export_filter.form.offer_letter_status}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Recruitment" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Recruitment" %}</label>
{{export_filter.form.recruitment_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Job Position" %}</label>
{{export_filter.form.job_position_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Start Date" %}</label>
{{export_filter.form.start_date}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Is Closed" %}?</label>
{{export_filter.form.recruitment_id__closed}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage Type" %}</label>
{{export_filter.form.stage_id__stage_type}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage Managers" %}</label>
{{export_filter.form.stage_id__stage_managers}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
{{export_filter.form.stage_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Department" %}</label>
{{export_filter.form.job_position_id__department_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Company" %}</label>
{{export_filter.form.recruitment_id__company_id}}
</div>
<div class="oh-input-group">
<label class="oh-label"
>{% trans "Recruitment Managers" %}</label
>
{{export_filter.form.recruitment_id__recruitment_managers}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "End Date" %}</label>
{{export_filter.form.end_date}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Interview From" %}</label>
{{export_filter.form.scheduled_from}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Is Active" %}?</label>
{{export_filter.form.is_active}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Interview Till" %}</label>
{{export_filter.form.scheduled_till}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-modal__dialog-footer p-0 mt-3">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Export" %}
</button>
</div>
</form>
</div>

View File

@@ -1,83 +1,161 @@
{% load i18n %} {% if messages %}
{% load i18n %}
<script>
function skillChange(selectElement) {
var selectedSkill = selectElement.val();
var parentForm = selectElement.parents().closest("form");
if (selectedSkill && selectedSkill.includes("create")) {
let dynamicskills = $("#dynamicSkills");
var view = parentForm.serialize();
dynamicskills.attr("hx-vals", `{"data":"${view}"}`);
dynamicskills.click();
}
}
$(document).ready(function(){
$("[name= 'skills']").on("change", function(){
skillChange($(this))
})
});
{% if dynamic %}
setTimeout(function () {
$('#dynamicCreateModal').removeClass('oh-modal--show');
}, 500);
{% endif %}
</script>
<script>
$(document).ready(function() {
function formatOption(option) {
if (!option.id) {
return option.text;
}
var $option = $('<span></span>').text(option.text);
if (option.id === 'create') {
$option.addClass('text-info');
}
return $option;
}
$('[name = "skills"]').select2({
templateResult: formatOption,
templateSelection: formatOption
});
});
</script>
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">{{ message }}</div>
{% endfor %}
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<form
hx-post="{% url 'recruitment-update' form.instance.id %}"
class="oh-general__tab-target oh-profile-section"
>
{% csrf_token %}
<div>
{% csrf_token %}
<div class="row" id="recruitmentUpdateContainer">
<div class="col-12 col-sm-12 col-md-12 col-lg-12">
<label class="oh-label" for=""
>{% trans "Title" %} <span class="text-danger">*</span></label
>
{{form.title}} {{form.title.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-12">
<label class="oh-label" for=""
>{% trans "Description" %} <span class="text-danger">*</span></label
>
{{form.description}} {{form.description.errors}}
</div>
<div
class="col-12 col-sm-12 col-md-12 col-lg-6"
id="openPositionUpdateContainer"
>
<label class="oh-label" for=""
>{% trans "Job Position" %} <span class="text-danger">*</span></label
>
{{form.open_positions}} {{form.open_positions.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for=""
>{% trans "Managers" %} <span class="text-danger">*</span></label
>
{{form.recruitment_managers}} {{form.recruitment_managers.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="">{% trans "Start Date" %}</label>
{{form.start_date}} {{form.start_date.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="">{% trans "End Date" %}</label>
{{form.end_date}} {{form.end_date.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="">{% trans "Vacancy" %}</label>
{{form.vacancy}} {{form.vacancy.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="">{% trans "Company" %}</label>
{{form.company_id}} {{form.company_id.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="id_is_published"
>{% trans "Survey Templates" %}</label
>
{{form.survey_templates}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label
class="oh-label"
for="id_is_published"
title="{{form.is_published.help_text|safe}}"
>{% trans "Is Published?" %}</label
>
<div class="w-100 d-flex" style="align-items: center">
<div class="oh-switch">{{form.is_published}}</div>
</div>
</div>
</div>
</div>
<div class="d-flex flex-row-reverse">
<button type="submit" class="mt-4 pl-5 pr-5 oh-btn oh-btn--secondary">
{% trans "Save" %}
<div class="oh-modal__dialog-header">
<h5 class="oh-modal__dialog-title" id="addRecruitmentModalLabel"
>{% trans "Edit Recruitment" %}</span
>
<button class="oh-modal__close" aria-label="Close" >
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
</form>
</div>
<div class="oh-modal__dialog-body" id="addRecruitmentModalBody">
<form
hx-post="{% url 'recruitment-update' form.instance.id %}"
hx-target="#updateFormContainer"
class="oh-general__tab-target oh-profile-section"
>
{% csrf_token %}
<div>
{% csrf_token %}
<div class="row" id="recruitmentUpdateContainer">
{% for error in form.non_field_errors %}
<ul class="errorlist">
<li>{{error}}</li>
</ul>
{% endfor %}
<div class="col-12 col-sm-12 col-md-12 col-lg-12">
<label class="oh-label" for=""
>{% trans "Title" %}
<span class="text-danger">*</span></label
>
{{form.title}} {{form.title.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-12">
<label class="oh-label" for=""
>{% trans "Description" %}
<span class="text-danger">*</span></label
>
{{form.description}} {{form.description.errors}}
</div>
<div
class="col-12 col-sm-12 col-md-12 col-lg-6"
id="openPositionUpdateContainer"
>
<label class="oh-label" for=""
>{% trans "Job Position" %}
<span class="text-danger">*</span></label
>
{{form.open_positions}} {{form.open_positions.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for=""
>{% trans "Managers" %}
<span class="text-danger">*</span></label
>
{{form.recruitment_managers}}
{{form.recruitment_managers.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for=""
>{% trans "Start Date" %}</label
>
{{form.start_date}} {{form.start_date.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for=""
>{% trans "End Date" %}</label
>
{{form.end_date}} {{form.end_date.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="">{% trans "Vacancy" %}</label>
{{form.vacancy}} {{form.vacancy.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="">{% trans "Company" %}</label>
{{form.company_id}} {{form.company_id.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="id_is_published"
>{% trans "Survey Templates" %}</label
>
{{form.survey_templates}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label class="oh-label" for="{{form.skills.id_for_lable}}"
>{% trans "Skills" %}</label
>
{{form.skills}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<label
class="oh-label"
for="id_is_published"
title="{{form.is_published.help_text|safe}}"
>{% trans "Is Published?" %}</label
>
<div class="w-100 d-flex" style="align-items: center">
<div class="oh-switch">{{form.is_published}}</div>
</div>
</div>
</div>
</div>
<div class="d-flex flex-row-reverse">
<button
type="submit"
class="mt-4 pl-5 pr-5 oh-btn oh-btn--secondary"
>
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -1,6 +1,5 @@
{% load i18n %}{% load widget_tweaks %} {% load attendancefilters %}
{% load basefilters %}
{{form.option_count}}
<div class="oh-general__tab-target oh-profile-section" id="personal">
{% if form.verbose_name %}
<div class="oh-payslip__header">

View File

@@ -9,146 +9,147 @@
<div class="oh-tabs__content oh-tabs__content--active" id="templateTab">
{% include "survey/template_accordion.html" %}
</div>
{% comment %} <div class="oh-tabs__content" id="template">
{% include "survey/templates.html" %}
</div> {% endcomment %}
<div class="oh-tabs__content" id="questionTab">
{% if questions %}
<div>
<div class="oh-layout--grid-3">
{% for question in questions %}
<div
class="oh-kanban-card"
data-toggle="oh-modal-toggle"
data-target="#detailSurvey"
hx-get="{% url 'single-survey-view' question.id %}?instances_ids={{requests_ids}}"
hx-target="#detailSurveyModalBody"
>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{question}}</span>
<span class="oh-kanban-card__subtitle">
{% for rec in question.recruitment_ids.all %} {{rec}},&nbsp; {% endfor %}
</span>
</div>
{% if perms.recruitment.delete_recruitmentsurvey or perms.recruitment.change_recruitmentsurvey %}
<div class="oh-kanban-card__dots">
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3"
@click="show = !show" title={% trans "Actions" %}
onclick="event.stopPropagation()" >
<ion-icon name="ellipsis-vertical-sharp"></ion-icon>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right"
x-show="show"
@click.outside="show = false"
>
<ul class="oh-dropdown__items">
{% if perms.recruitment.change_recruitmentsurvey %}
<li class="oh-dropdown__item">
<a
hx-get="{% url 'recruitment-survey-question-template-edit' question.id %}"
data-toggle="oh-modal-toggle"
data-target="#updateSurvey"
hx-target="#updateSurveyModalBody"
class="oh-dropdown__link"
>{% trans "Edit" %}
</a>
</li>
{% endif %}
{% if perms.recruitment.change_recruitmentsurvey %}
<li class="oh-dropdown__item">
<a
hx-get="{% url 'recruitment-survey-question-template-duplicate' question.id %}"
data-toggle="oh-modal-toggle"
data-target="#addSurvey"
hx-target="#addSurveyModalBody"
class="oh-dropdown__link"
>{% trans "Duplicate" %}
</a>
</li>
{% endif %}
{% if perms.recruitment.delete_recruitmentsurvey %}
<li class="oh-dropdown__item">
<a href="{% url 'recruitment-survey-question-template-delete' question.id %}"
onclick="event.preventDefault();event.stopPropagation(); confirm('{% trans "Are you sure want to delete?" %}')"
class="text-danger" >{% trans "Delete" %}
</a>
</li>
{% endif %}
</ul>
</div>
<div>
<div class="oh-layout--grid-3">
{% for question in questions %}
<div
class="oh-kanban-card"
data-toggle="oh-modal-toggle"
data-target="#detailSurvey"
hx-get="{% url 'single-survey-view' question.id %}?instances_ids={{requests_ids}}"
hx-target="#detailSurveyModalBody"
>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{question}}</span>
<span class="oh-kanban-card__subtitle">
{% for rec in question.recruitment_ids.all %} {{rec}},&nbsp;
{% endfor %}
</span>
</div>
{% if perms.recruitment.delete_recruitmentsurvey or perms.recruitment.change_recruitmentsurvey %}
<div class="oh-kanban-card__dots">
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3" @click="show = !show" title={% trans "Actions" %} onclick="event.stopPropagation()" >
<ion-icon name="ellipsis-vertical-sharp"></ion-icon>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right"
x-show="show"
@click.outside="show = false"
>
<ul class="oh-dropdown__items">
{% if perms.recruitment.change_recruitmentsurvey %}
<li class="oh-dropdown__item">
<a
hx-get="{% url 'recruitment-survey-question-template-edit' question.id %}"
data-toggle="oh-modal-toggle"
data-target="#updateSurvey"
hx-target="#updateSurveyModalBody"
class="oh-dropdown__link"
>{% trans "Edit" %}
</a>
</li>
{% endif %} {% if perms.recruitment.change_recruitmentsurvey %}
<li class="oh-dropdown__item">
<a
hx-get="{% url 'recruitment-survey-question-template-duplicate' question.id %}"
data-toggle="oh-modal-toggle"
data-target="#addSurvey"
hx-target="#addSurveyModalBody"
class="oh-dropdown__link"
>{% trans "Duplicate" %}
</a>
</li>
{% endif %} {% if perms.recruitment.delete_recruitmentsurvey %}
<li class="oh-dropdown__item">
<a href="{% url 'recruitment-survey-question-template-delete' question.id %}" onclick="event.preventDefault();event.stopPropagation(); confirm('{% trans "Are you sure want to delete?" %}')"
class="text-danger" >
{% trans "Delete" %}
</a>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
</div>
{% endfor %}
{% endif %}
</div>
{% endfor %}
</div>
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ questions.number }} {% trans "of" %} {{ questions.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{questions.number}}"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}"
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ questions.number }} {% trans "of" %} {{ questions.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{questions.number}}"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}"
hx-target="#view-container"
min="1"
/>
<span class="oh-pagination__label">
{% trans "of" %} {{questions.paginator.num_pages}}
</span>
</div>
<ul class="oh-pagination__items">
{% if questions.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#view-container"
min="1"
/>
<span class="oh-pagination__label">
{% trans "of" %} {{questions.paginator.num_pages}}
</span>
</div>
<ul class="oh-pagination__items">
{% if questions.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#view-container"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}&page=1"
class="oh-pagination__link"
>{% trans "First" %}
</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#view-container"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}&page={{ questions.previous_page_number }}"
class="oh-pagination__link"
>{% trans "Previous" %}
</a>
</li>
{% endif %} {% if questions.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#view-container"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}&page={{ questions.next_page_number }}"
class="oh-pagination__link"
>{% trans "Next" %}
</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#view-container"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}&page={{ questions.paginator.num_pages }}"
class="oh-pagination__link"
>{% trans "Last" %}
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
hx-get="{% url 'rec-filter-survey' %}?{{pd}}&page=1"
class="oh-pagination__link"
>{% trans "First" %}
</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#view-container"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}&page={{ questions.previous_page_number }}"
class="oh-pagination__link"
>{% trans "Previous" %}
</a>
</li>
{% endif %} {% if questions.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#view-container"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}&page={{ questions.next_page_number }}"
class="oh-pagination__link"
>{% trans "Next" %}
</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#view-container"
hx-get="{% url 'rec-filter-survey' %}?{{pd}}&page={{ questions.paginator.num_pages }}"
class="oh-pagination__link"
>{% trans "Last" %}
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
{% else %}
<div class="oh-wrapper">
<img style="width: 150px; height: 150px; display: block; margin: 0 auto;" src="{% static 'images/ui/editor.png' %}" class="oh-404__image mb-4" alt="Page not found. 404." />
<h5 class="oh-404__subtitle">{% trans "No questions have been established yet." %}</h5>
</div>
<div class="oh-wrapper">
<img
style="width: 150px; height: 150px; display: block; margin: 0 auto"
src="{% static 'images/ui/editor.png' %}"
class="oh-404__image mb-4"
alt="Page not found. 404."
/>
<h5 class="oh-404__subtitle">
{% trans "No questions have been established yet." %}
</h5>
</div>
{% endif %}
</div>

View File

@@ -1,7 +1,6 @@
<form
hx-post="{% url 'recruitment-survey-question-template-create' %}"
hx-target="#templateModalBody"
method="post"
>
{% csrf_token %} {{form.as_p}}
</form>

View File

@@ -136,17 +136,6 @@
</div>
</div>
</div>
{% comment %} {% if perms.recruitment.add_recruitmentsurvey %}
<button
class="oh-btn oh-btn--secondary ml-2"
data-toggle="oh-modal-toggle"
data-target="#addSurvey"
hx-get="{% url 'recruitment-survey-question-template-create' %}"
hx-target="#addSurveyModalBody"
>
<ion-icon name="add-sharp" class="mr-1"></ion-icon>{% trans "Create" %}
</button>
{% endif %} {% endcomment %}
</div>
</section>
<div class="oh-tabs oh-wrapper">
@@ -196,30 +185,6 @@
</button>
{% endif %}
</li>
{% comment %} <li
class="oh-tabs__tab"
data-target="#template"
>
{% trans "Templates" %}
{% if perms.recruitment.add_recruitmentsurvey %}
<button onclick="event.stopPropagation()" style="display: inline-block;padding: 0px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 28px;"
class="oh-btn oh-btn--secondary-outline float-end ms-3"
hx-get="{% url 'survey-template-create' %}"
hx-target="#templateModalBody"
data-toggle="oh-modal-toggle"
data-target="#templateModal"
title="{% trans "Create template group" %}"
>
<ion-icon name="add-outline" class="m-0 md hydrated" role="img" aria-label="add outline"></ion-icon>
</button>
{% endif %}
</li> {% endcomment %}
</ul>
<div class="oh-tabs__contents" id="view-container">
{% include "survey/survey_card.html" %}
@@ -259,5 +224,16 @@
<div class="oh-modal__dialog-body" id="templateModalBody"></div>
</div>
</div>
<script>
$(document).ready(function() {
var activeTab = localStorage.getItem("activeSurveyTab")
$(".oh-tabs__tab--active").removeClass("oh-tabs__tab--active");
$(`[data-target='${activeTab}']`).addClass("oh-tabs__tab--active");
$(`[data-target='${activeTab}']`).click();
$(".oh-tabs__tab").on("click", function (e) {
var dataTarget = $(this).data('target');
localStorage.setItem("activeSurveyTab",dataTarget)
});
});
</script>
{% endblock content %}

View File

@@ -32,7 +32,7 @@ from django.core.mail import EmailMessage
from django.core.paginator import Paginator
from django.db.models import ProtectedError, Q
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import redirect, render
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
@@ -70,7 +70,9 @@ from recruitment.forms import (
OfferLetterForm,
RecruitmentCreationForm,
RejectReasonForm,
ResumeForm,
ScheduleInterviewForm,
SkillsForm,
SkillZoneCandidateForm,
SkillZoneCreateForm,
StageCreationForm,
@@ -88,6 +90,8 @@ from recruitment.models import (
RecruitmentMailTemplate,
RecruitmentSurvey,
RejectReason,
Resume,
Skill,
SkillZone,
SkillZoneCandidate,
Stage,
@@ -209,6 +213,11 @@ def recruitment(request):
to the recruitment managers
"""
form = RecruitmentCreationForm()
if request.GET:
form = RecruitmentCreationForm(request.GET)
dynamic = (
request.GET.get("dynamic") if request.GET.get("dynamic") != "None" else None
)
if request.method == "POST":
form = RecruitmentCreationForm(request.POST)
if form.is_valid():
@@ -242,7 +251,9 @@ def recruitment(request):
redirect=reverse("pipeline"),
)
return HttpResponse("<script>location.reload();</script>")
return render(request, "recruitment/recruitment_form.html", {"form": form})
return render(
request, "recruitment/recruitment_form.html", {"form": form, "dynamic": dynamic}
)
@login_required
@@ -255,7 +266,7 @@ def recruitment_view(request):
request.GET.copy().update({"is_active": "on"})
form = RecruitmentCreationForm()
queryset = Recruitment.objects.filter(is_active=True)
if queryset.exists():
if Recruitment.objects.all():
template = "recruitment/recruitment_view.html"
else:
template = "recruitment/recruitment_empty.html"
@@ -303,6 +314,11 @@ def recruitment_update(request, rec_id):
for survey in survey_templates:
survey_template_list.append(survey.template_id.all())
form = RecruitmentCreationForm(instance=recruitment_obj)
if request.GET:
form = RecruitmentCreationForm(request.GET)
dynamic = (
request.GET.get("dynamic") if request.GET.get("dynamic") != "None" else None
)
if request.method == "POST":
form = RecruitmentCreationForm(request.POST, instance=recruitment_obj)
if form.is_valid():
@@ -338,7 +354,11 @@ def recruitment_update(request, rec_id):
return HttpResponse(
response.content.decode("utf-8") + "<script>location.reload();</script>"
)
return render(request, "recruitment/recruitment_update_form.html", {"form": form})
return render(
request,
"recruitment/recruitment_update_form.html",
{"form": form, "dynamic": dynamic},
)
def paginator_qry_recruitment_limited(qryset, page_number):
@@ -1313,8 +1333,6 @@ def candidate_view(request):
)
filter_obj = CandidateFilter(request.GET, queryset=candidates)
export_fields = CandidateExportForm()
export_obj = CandidateFilter(request.GET, queryset=candidates)
if Candidate.objects.exists():
template = "candidate/candidate_view.html"
else:
@@ -1332,8 +1350,6 @@ def candidate_view(request):
"data": paginator_qry(filter_obj.qs, request.GET.get("page")),
"pd": previous_data,
"f": filter_obj,
"export_fields": export_fields,
"export_obj": export_obj,
"view_type": view_type,
"filter_dict": data_dict,
"gp_fields": CandidateReGroup.fields,
@@ -1431,6 +1447,14 @@ def candidate_export(request):
"""
This method is used to Export candidate data
"""
if request.META.get("HTTP_HX_REQUEST"):
export_column = CandidateExportForm()
export_filter = CandidateFilter()
content = {
"export_filter": export_filter,
"export_column": export_column,
}
return render(request, "candidate/export_filter.html", context=content)
return export_data(
request=request,
model=Candidate,
@@ -1489,7 +1513,10 @@ def candidate_view_individual(request, cand_id, **kwargs):
"""
This method is used to view profile of candidate.
"""
candidate_obj = Candidate.objects.get(id=cand_id)
candidate_obj = Candidate.find(cand_id)
if not candidate_obj:
messages.error(request, _("Candidate not found"))
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
mails = list(Candidate.objects.values_list("email", flat=True))
# Query the User model to check if any email is present
@@ -1600,7 +1627,10 @@ def candidate_conversion(request, cand_id, **kwargs):
Args:
cand_id : candidate instance id
"""
candidate_obj = Candidate.objects.filter(id=cand_id).first()
candidate_obj = Candidate.find(cand_id)
if not candidate_obj:
messages.error(request, _("Candidate not found"))
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
can_name = candidate_obj.name
can_mob = candidate_obj.mobile
can_job = candidate_obj.job_position_id
@@ -2149,10 +2179,12 @@ def skill_zone_delete(request, sz_id):
GET : return Skill zone view template
"""
try:
SkillZone.objects.get(id=sz_id).delete()
messages.success(request, _("Skill zone deleted successfully.."))
except SkillZone.DoesNotExist:
messages.error(request, _("Skill zone not found."))
skill_zone = SkillZone.find(sz_id)
if skill_zone:
skill_zone.delete()
messages.success(request, _("Skill zone deleted successfully.."))
else:
messages.error(request, _("Skill zone not found."))
except ProtectedError:
messages.error(request, _("Related entries exists"))
return redirect(skill_zone_view)
@@ -2171,8 +2203,8 @@ def skill_zone_archive(request, sz_id):
Returns:
GET : return Skill zone view template
"""
try:
skill_zone = SkillZone.objects.get(id=sz_id)
skill_zone = SkillZone.find(sz_id)
if skill_zone:
is_active = skill_zone.is_active
if is_active == True:
skill_zone.is_active = False
@@ -2183,7 +2215,6 @@ def skill_zone_archive(request, sz_id):
i.is_active = False
i.save()
messages.success(request, _("Skill zone archived successfully.."))
else:
skill_zone.is_active = True
skill_zone_candidates = SkillZoneCandidate.objects.filter(
@@ -2193,11 +2224,9 @@ def skill_zone_archive(request, sz_id):
i.is_active = True
i.save()
messages.success(request, _("Skill zone unarchived successfully.."))
skill_zone.save()
except SkillZone.DoesNotExist:
else:
messages.error(request, _("Skill zone not found."))
return redirect(skill_zone_view)
@@ -2767,3 +2796,175 @@ def check_vaccancy(request):
if stage and stage.recruitment_id.is_vacancy_filled():
message = _("Vaccancy is filled")
return JsonResponse({"message": message})
@login_required
def create_skills(request):
instance_id = eval(str(request.GET.get("instance_id")))
dynamic = request.GET.get("dynamic")
hx_vals = request.GET.get("data")
instance = None
if instance_id:
instance = Skill.objects.get(id=instance_id)
form = SkillsForm(instance=instance)
if request.method == "POST":
form = SkillsForm(request.POST, instance=instance)
if form.is_valid():
form.save()
messages.success(request, "Skill created successfully")
if request.GET.get("dynamic") == "True":
from django.urls import reverse
url = reverse("recruitment-create")
instance = Skill.objects.all().last()
mutable_get = request.GET.copy()
skills = mutable_get.getlist("skills")
skills.remove("create")
skills.append(str(instance.id))
mutable_get["skills"] = skills[-1]
skills.pop()
data = mutable_get.urlencode()
try:
for item in skills:
data += f"&skills={item}"
except:
pass
return redirect(f"{url}?{data}")
return HttpResponse("<script>window.location.reload()</script>")
context = {
"form": form,
"dynamic": dynamic,
"hx_vals": hx_vals,
}
return render(request, "settings/skills/skills_form.html", context=context)
@login_required
@permission_required("recruitment.delete_rejectreason")
def delete_skills(request):
"""
This method is used to delete the reject reasons
"""
ids = request.GET.getlist("ids")
skills = Skill.objects.filter(id__in=ids)
for skill in skills:
skill.delete()
messages.success(request, f"{skill.title} is deleted.")
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
def view_bulk_resumes(request):
rec_id = eval(str(request.GET.get("rec_id")))
resumes = Resume.objects.filter(recruitment_id=rec_id)
return render(
request, "pipeline/bulk_resume.html", {"resumes": resumes, "rec_id": rec_id}
)
def add_bulk_resumes(request):
rec_id = eval(str(request.GET.get("rec_id")))
recruitment = Recruitment.objects.get(id=rec_id)
if request.method == "POST":
files = request.FILES.getlist("files")
for file in files:
Resume.objects.create(
file=file,
recruitment_id=recruitment,
)
url = reverse("view-bulk-resume")
query_params = f"?rec_id={rec_id}"
return redirect(f"{url}{query_params}")
@login_required
def delete_resume_file(request):
"""
Used to delete attachment
"""
ids = request.GET.getlist("ids")
rec_id = request.GET.get("rec_id")
Resume.objects.filter(id__in=ids).delete()
url = reverse("view-bulk-resume")
query_params = f"?rec_id={rec_id}"
return redirect(f"{url}{query_params}")
def extract_words_from_pdf(pdf_file):
# Open the PDF file
pdf_document = fitz.open(pdf_file.path)
words = []
for page_num in range(len(pdf_document)):
page = pdf_document.load_page(page_num)
page_text = page.get_text()
# Use regular expression to extract words
page_words = re.findall(r"\b\w+\b", page_text.lower())
words.extend(page_words)
pdf_document.close()
return words
@login_required
def matching_resumes(request, rec_id):
recruitment = Recruitment.objects.filter(id=rec_id).first()
skills = recruitment.skills.values_list("title", flat=True)
resumes = recruitment.resume.all()
is_candidate = resumes.filter(is_candidate=True)
is_candidate_ids = set(is_candidate.values_list("id", flat=True))
resume_ranks = []
for resume in resumes:
words = extract_words_from_pdf(resume.file)
matching_skills_count = sum(skill.lower() in words for skill in skills)
resume_ranks.append(
{"resume": resume, "matching_skills_count": matching_skills_count}
)
candidate_resumes = [
rank for rank in resume_ranks if rank["resume"].id in is_candidate_ids
]
non_candidate_resumes = [
rank for rank in resume_ranks if rank["resume"].id not in is_candidate_ids
]
non_candidate_resumes = sorted(
non_candidate_resumes, key=lambda x: x["matching_skills_count"], reverse=True
)
candidate_resumes = sorted(
candidate_resumes, key=lambda x: x["matching_skills_count"], reverse=True
)
ranked_resumes = non_candidate_resumes + candidate_resumes
return render(
request,
"pipeline/matching_resumes.html",
{
"matched_resumes": ranked_resumes,
"rec_id": rec_id,
},
)
def matching_resume_completion(request):
resume_id = request.GET.get("resume_id")
resume_obj = get_object_or_404(Resume, id=resume_id)
resume_file = resume_obj.file
contact_info = extract_info(resume_file)
return JsonResponse(contact_info)