[UPDT] RECRUITMENT: Optional profile/resume option under recruitment

This commit is contained in:
Horilla
2024-08-28 12:00:34 +05:30
parent f6a8a18fe6
commit a614facf0a
9 changed files with 124 additions and 28 deletions

View File

@@ -35,10 +35,10 @@ from django.utils.translation import gettext_lazy as _
from base.forms import Form
from base.methods import reload_queryset
from base.models import HorillaMailTemplate
from employee.filters import EmployeeFilter
from employee.models import Employee
from horilla import horilla_middlewares
from horilla.horilla_middlewares import _thread_locals
from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField
from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget
from recruitment import widgets
@@ -352,11 +352,15 @@ class CandidateCreationForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["profile"].widget.attrs["accept"] = ".jpg, .jpeg, .png"
self.fields["profile"].required = False
self.fields["resume"].widget.attrs["accept"] = ".pdf"
self.fields["resume"].required = False
if self.instance.recruitment_id is not None:
if self.instance is not None:
self.fields["job_position_id"] = forms.ModelChoiceField(
queryset=self.instance.recruitment_id.open_positions.all(),
# additional field options
label="Job Position",
)
self.fields["recruitment_id"].widget.attrs = {"data-widget": "ajax-widget"}
self.fields["job_position_id"].widget.attrs = {"data-widget": "ajax-widget"}
@@ -429,26 +433,31 @@ class CandidateCreationForm(ModelForm):
return table_html
def clean(self):
errors = {}
profile = self.cleaned_data["profile"]
resume = self.cleaned_data["resume"]
recruitment: Recruitment = self.cleaned_data["recruitment_id"]
if not resume and not recruitment.optional_resume:
errors["resume"] = _("This field is required")
if not profile and not recruitment.optional_profile_image:
errors["profile"] = _("This field is required")
if self.instance.name is not None:
self.errors.pop("job_position_id", None)
if (
self.instance.job_position_id is None
or self.data.get("job_position_id") == ""
):
raise forms.ValidationError(
{"job_position_id": "This field is required"}
)
errors["job_position_id"] = _("This field is required")
if (
self.instance.job_position_id
not in self.instance.recruitment_id.open_positions.all()
):
raise forms.ValidationError({"job_position_id": "Choose valid choice"})
errors["job_position_id"] = _("Choose valid choice")
if errors:
raise ValidationError(errors)
return super().clean()
from horilla.horilla_middlewares import _thread_locals
class ApplicationForm(RegistrationForm):
"""
Form for create Candidate
@@ -495,6 +504,10 @@ class ApplicationForm(RegistrationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
request = getattr(_thread_locals, "request", None)
self.fields["profile"].widget.attrs["accept"] = ".jpg, .jpeg, .png"
self.fields["profile"].required = False
self.fields["resume"].widget.attrs["accept"] = ".pdf"
self.fields["resume"].required = False
self.fields["recruitment_id"].widget.attrs = {"data-widget": "ajax-widget"}
self.fields["job_position_id"].widget.attrs = {"data-widget": "ajax-widget"}
@@ -505,10 +518,23 @@ class ApplicationForm(RegistrationForm):
name = self.cleaned_data["name"]
request = getattr(_thread_locals, "request", None)
if request and request.user.has_perm("recruitment.add_candidate"):
errors = {}
profile = self.cleaned_data["profile"]
resume = self.cleaned_data["resume"]
recruitment: Recruitment = self.cleaned_data["recruitment_id"]
if not resume and not recruitment.optional_resume:
errors["resume"] = _("This field is required")
if not profile and not recruitment.optional_profile_image:
errors["profile"] = _("This field is required")
if errors:
raise ValidationError(errors)
if (
not profile
and 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
@@ -574,6 +600,12 @@ class AddCandidateForm(ModelForm):
recruitment_id=recruitment
)
self.fields["job_position_id"].queryset = recruitment.open_positions
self.fields["profile"].widget.attrs["accept"] = ".jpg, .jpeg, .png"
self.fields["resume"].widget.attrs["accept"] = ".pdf"
if recruitment.optional_profile_image:
self.fields["profile"].required = False
if recruitment.optional_resume:
self.fields["resume"].required = False
self.fields["gender"].empty_label = None
self.fields["job_position_id"].empty_label = None
self.fields["stage_id"].empty_label = None

View File

@@ -158,6 +158,12 @@ class Recruitment(HorillaModel):
skills = models.ManyToManyField(Skill, blank=True)
objects = HorillaCompanyManager()
default = models.manager.Manager()
optional_profile_image = models.BooleanField(
default=False, help_text=_("Profile image not mandatory for candidate creation")
)
optional_resume = models.BooleanField(
default=False, help_text=_("Resume not mandatory for candidate creation")
)
class Meta:
"""

View File

@@ -11,10 +11,11 @@ today = datetime.now()
def recruitment_close():
from recruitment.models import Recruitment
today_date = today.date()
recruitments = Recruitment.objects.filter(closed = False)
recruitments = Recruitment.objects.filter(closed=False)
for rec in recruitments:
if rec.end_date:
if rec.end_date == today_date:
@@ -22,6 +23,7 @@ def recruitment_close():
rec.is_published = False
rec.save()
scheduler = BackgroundScheduler()
scheduler.add_job(recruitment_close, "interval", hours=1)

View File

@@ -202,7 +202,7 @@
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label required-star" for="resume"
<label class="oh-label" for="resume"
>{% trans "Resume" %}</label
>
{% if resume.file %}

View File

@@ -278,10 +278,11 @@
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="newspaper-outline"></ion-icon>
</button>
<a class="oh-btn oh-btn--light" href="{{cand.resume.url}}"
<a class="oh-btn oh-btn--light {% if not cand.resume.url %}oh-btn--disabled{% endif %}" href="{{cand.resume.url}}"
target="_blank" title="{% trans " Resume" %}" rel="noopener noreferrer"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon
name="document-outline"></ion-icon></a>
name="document-outline"></ion-icon>
</a>
</div>
</div>
</div>
@@ -289,6 +290,7 @@
</div>
</div>
</div>
ffff
<script>
$("#stageCount{{stage.id}}").text({{candidates.paginator.count}})
$("#stageCount{{stage.id}}").attr("title",'{{candidates.paginator.count}} {% trans "Candidates" %}')

View File

@@ -65,12 +65,12 @@
{{form.company_id}} {{form.company_id.errors}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<div class="col-12 col-sm-12 col-md-12 col-lg-12">
<label class="oh-label" for="id_survey_templates">{% trans "Survey Templates" %}</label>
{{form.survey_templates}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label class="oh-label" for="id_is_published" title="{{form.is_published.help_text|safe}}">
{% trans "Is Published?" %}
</label>
@@ -78,6 +78,22 @@
<div class="oh-switch">{{form.is_published}}</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label class="oh-label" for="id_optional_profile_image" title="{{form.optional_profile_image.help_text|safe}}">
{% trans "Optional Profile Image?" %}
</label>
<div class="w-100 d-flex" style="align-items: center">
<div class="oh-switch">{{form.optional_profile_image}}</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label class="oh-label" for="id_optional_resume" title="{{form.optional_resume.help_text|safe}}">
{% trans "Optional Resume?" %}
</label>
<div class="w-100 d-flex" style="align-items: center">
<div class="oh-switch">{{form.optional_resume}}</div>
</div>
</div>
</div>
</div>
<div class="d-flex flex-row-reverse">

View File

@@ -146,7 +146,7 @@
>
{{form.skills}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label
class="oh-label"
for="id_is_published"
@@ -157,6 +157,28 @@
<div class="oh-switch">{{form.is_published}}</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label
class="oh-label"
for="id_optional_profile_image"
title="{{form.optional_profile_image.help_text|safe}}"
>{% trans "Optional Profile Image?" %}</label
>
<div class="w-100 d-flex" style="align-items: center">
<div class="oh-switch">{{form.optional_profile_image}}</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label
class="oh-label"
for="id_optional_optional_resume"
title="{{form.optional_resume.help_text|safe}}"
>{% trans "Optional Resume?" %}</label
>
<div class="w-100 d-flex" style="align-items: center">
<div class="oh-switch">{{form.optional_resume}}</div>
</div>
</div>
</div>
</div>
<div class="d-flex flex-row-reverse w-100 align-items-right mt-4">

View File

@@ -132,7 +132,7 @@
>
{{form.skills}}
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6">
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label
class="oh-label"
for="id_is_published"
@@ -143,6 +143,22 @@
<div class="oh-switch">{{form.is_published}}</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label class="oh-label" for="id_optional_profile_image" title="{{form.optional_profile_image.help_text|safe}}">
{% trans "Optional Profile Image?" %}
</label>
<div class="w-100 d-flex" style="align-items: center">
<div class="oh-switch">{{form.optional_profile_image}}</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-4">
<label class="oh-label" for="id_optional_resume" title="{{form.optional_resume.help_text|safe}}">
{% trans "Optional Resume?" %}
</label>
<div class="w-100 d-flex" style="align-items: center">
<div class="oh-switch">{{form.optional_resume}}</div>
</div>
</div>
</div>
</div>
<div class="d-flex flex-row-reverse">

View File

@@ -319,15 +319,15 @@ def application_form(request):
candidate_obj.stage_id = stages.order_by("sequence").first()
messages.success(request, _("Application saved."))
resume = request.FILES["resume"]
resume = request.FILES.get("resume")
if resume:
resume_path = f"recruitment/resume/{resume.name}"
resume_path = f"recruitment/resume/{resume.name}"
with default_storage.open(resume_path, "wb+") as destination:
for chunk in resume.chunks():
destination.write(chunk)
with default_storage.open(resume_path, "wb+") as destination:
for chunk in resume.chunks():
destination.write(chunk)
candidate_obj.resume = resume_path
candidate_obj.resume = resume_path
try:
profile = request.FILES["profile"] if request.FILES["profile"] else None
profile_path = f"recruitment/profile/{candidate_obj.name.replace(' ', '_')}_{profile.name}_{uuid4()}"