[UPDT] RECRUITMENT: Updated recruitment app by adding find class method for horilla models
This commit is contained in:
@@ -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))",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}}, {% 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}},
|
||||
{% 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>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<form
|
||||
hx-post="{% url 'recruitment-survey-question-template-create' %}"
|
||||
hx-target="#templateModalBody"
|
||||
method="post"
|
||||
>
|
||||
{% csrf_token %} {{form.as_p}}
|
||||
</form>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user