[UPDT] RECRUITMENT: Optional profile/resume option under recruitment
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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" %}')
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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()}"
|
||||
|
||||
Reference in New Issue
Block a user