[ADD] RECRUITMENT: Candidate rating and average rating
This commit is contained in:
@@ -5,6 +5,7 @@ This page is used to register the model with admins site.
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from recruitment.models import (
|
||||
CandidateRating,
|
||||
Stage,
|
||||
Recruitment,
|
||||
Candidate,
|
||||
@@ -23,3 +24,4 @@ admin.site.register(Candidate)
|
||||
admin.site.register(RecruitmentSurveyAnswer)
|
||||
admin.site.register(RecruitmentSurvey)
|
||||
admin.site.register(RecruitmentMailTemplate)
|
||||
admin.site.register(CandidateRating)
|
||||
|
||||
@@ -21,6 +21,7 @@ from employee.models import Employee
|
||||
from base.models import JobPosition, Company
|
||||
from django.core.files.storage import default_storage
|
||||
from base.horilla_company_manager import HorillaCompanyManager
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
|
||||
|
||||
# Create your models here.
|
||||
@@ -513,3 +514,15 @@ class RecruitmentMailTemplate(models.Model):
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.title
|
||||
|
||||
|
||||
class CandidateRating(models.Model):
|
||||
employee_id = models.ForeignKey(Employee,on_delete=models.PROTECT, related_name="candidate_rating")
|
||||
candidate_id = models.ForeignKey(Candidate,on_delete=models.PROTECT, related_name="candidate_rating")
|
||||
rating = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(5)])
|
||||
|
||||
class Meta:
|
||||
unique_together = ['employee_id', 'candidate_id']
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.employee_id} - {self.candidate_id} rating {self.rating}"
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends 'index.html' %} {% load i18n %} {% load yes_no %} {% load static %} {% block content %}
|
||||
{% extends 'index.html' %} {% load i18n %} {% load yes_no %} {% load static %} {% block content %} {% load recruitmentfilters %}
|
||||
|
||||
<style>
|
||||
.enlarge-image-container {
|
||||
@@ -16,6 +16,15 @@
|
||||
height: auto;
|
||||
border-radius: 5%;
|
||||
}
|
||||
.oh-rate:not(:checked)>label {
|
||||
font-size:40px;
|
||||
}
|
||||
.star {
|
||||
font-size:20px !important;
|
||||
}
|
||||
.ul {
|
||||
margin:0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="enlargeImageContainer" class="enlarge-image-container"></div>
|
||||
@@ -27,7 +36,7 @@
|
||||
<div class='d-flex float-end ms-4'>
|
||||
{% if candidate.email in emp_list %}
|
||||
{% else %}
|
||||
<form action="{% url 'candidate-conversion' candidate.id %}" style="color: inherit;text-decoration: none;""
|
||||
<form action="{% url 'candidate-conversion' candidate.id %}" style="color: inherit;text-decoration: none;"
|
||||
onsubmit="return confirm('{% trans "Are you sure you want to convert this candidate into an employee?" %}')"
|
||||
>
|
||||
{% csrf_token %}
|
||||
@@ -68,7 +77,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-8 d-flex align-items-center"
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-8 d-flex align-items-center justify-content-between"
|
||||
>
|
||||
<ul class="oh-profile__info-list">
|
||||
<li class="oh-profile__info-list-item">
|
||||
@@ -87,6 +96,18 @@
|
||||
<span class="oh-profile__info-value">{{candidate.mobile}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="oh-rate">
|
||||
<input type="radio" id="star5" value="5" disabled {% if average_rate == 5 %} checked {% endif %} />
|
||||
<label for="star5" title="5 Stars" class="rating-star">5 {% trans "Stars" %}</label>
|
||||
<input type="radio" id="star4" value="4" disabled {% if average_rate == 4 %} checked {% endif %} />
|
||||
<label for="star4" title="4 Stars" class="rating-star">4 {% trans "Stars" %}</label>
|
||||
<input type="radio" id="star3" value="3" disabled {% if average_rate == 3 %} checked {% endif %} />
|
||||
<label for="star3" title="3 Stars" class="rating-star">3 {% trans "Stars" %}</label>
|
||||
<input type="radio" id="star2" value="2" disabled {% if average_rate == 2 %} checked {% endif %} />
|
||||
<label for="star2" title="2 Stars" class="rating-star">2 {% trans "Stars" %}</label>
|
||||
<input type="radio" id="star1" value="1" disabled {% if average_rate == 1 %} checked {% endif %} />
|
||||
<label for="star1" title="1 Star" class="rating-star">1 {% trans "Star" %}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -139,6 +160,17 @@
|
||||
>{% trans "History" %}</a
|
||||
>
|
||||
</li>
|
||||
{% if request.user|stage_manages:rec or perms.recruitment.change_candidate %}
|
||||
<li class="oh-general__tab">
|
||||
<a
|
||||
href="#"
|
||||
class="oh-general__tab-link"
|
||||
data-action="general-tab"
|
||||
data-target="#rating"
|
||||
>{% trans "Rating" %}</a
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if candidate.hired %}
|
||||
<li class="oh-general__tab">
|
||||
<a
|
||||
@@ -431,6 +463,12 @@
|
||||
>
|
||||
{% include "candidate/history.html" %}
|
||||
</div>
|
||||
<div
|
||||
class="oh-general__tab-target oh-profile__info-tab mb-4 d-none"
|
||||
id="rating"
|
||||
>
|
||||
{% include "candidate/rating_tab.html" %}
|
||||
</div>
|
||||
<div
|
||||
class="oh-general__tab-target oh-profile__info-tab mb-4 d-none"
|
||||
id="resume"
|
||||
|
||||
40
recruitment/templates/candidate/rating_tab.html
Normal file
40
recruitment/templates/candidate/rating_tab.html
Normal file
@@ -0,0 +1,40 @@
|
||||
{% load static %} {% load audit_filters %} {% load i18n %}
|
||||
<div class="row p-4">
|
||||
{% for rate in candidate.candidate_rating.all %}
|
||||
<div class="oh-history__container pb-2">
|
||||
<div class="d-flex">
|
||||
<div class="oh-history_user-img">
|
||||
<img
|
||||
src="{{rate.employee_id.get_avatar}}"
|
||||
alt=""
|
||||
class="oh-history_user-pic"
|
||||
/>
|
||||
<div class="oh-history_user-state oh-user_inactive"></div>
|
||||
</div>
|
||||
<div class="oh-history_user-details">
|
||||
<span class="oh-history__username">{{rate.employee_id}}</span>
|
||||
<ul class="ul ps-0">
|
||||
<div class="oh-rate">
|
||||
<input type="radio" id="star5" value="5" disabled {% if rate.rating == 5 %} checked {% endif %} />
|
||||
<label for="star5" title="5 Stars" class="star">5 {% trans "Stars" %}</label>
|
||||
<input type="radio" id="star4" value="4" disabled {% if rate.rating == 4 %} checked {% endif %} />
|
||||
<label for="star4" title="4 Stars" class="star">4 {% trans "Stars" %}</label>
|
||||
<input type="radio" id="star3" value="3" disabled {% if rate.rating == 3 %} checked {% endif %} />
|
||||
<label for="star3" title="3 Stars" class="star">3 {% trans "Stars" %}</label>
|
||||
<input type="radio" id="star2" value="2" disabled {% if rate.rating == 2 %} checked {% endif %} />
|
||||
<label for="star2" title="2 Stars" class="star">2 {% trans "Stars" %}</label>
|
||||
<input type="radio" id="star1" value="1" disabled {% if rate.rating == 1 %} checked {% endif %} />
|
||||
<label for="star1" title="1 Star" class="star">1 {% trans "Star" %}</label>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
{% comment %} <div class="oh-history_msg-container">
|
||||
<div class="oh-history_task-tracker">
|
||||
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -252,6 +252,11 @@
|
||||
<div class="oh-sticky-table__th oh-table-config__th">
|
||||
<span> {% trans "Contact" %} </span>
|
||||
</div>
|
||||
{% if request.user|stage_manages:rec or perms.recruitment.change_candidate %}
|
||||
<div class="oh-sticky-table__th oh-table-config__th">
|
||||
<span> {% trans "Rating" %} </span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="oh-sticky-table__th oh-table-config__th">{% trans "Stage" %}</div>
|
||||
<div class="oh-sticky-table__th oh-table-config__th" style="width: 200px;" >
|
||||
<select name="bulk_stage" class="oh-select w-100" data-stage-id="{{stage.id}}">
|
||||
@@ -316,6 +321,41 @@
|
||||
<div class="oh-sticky-table__td oh-table-config__td">
|
||||
{{cand.mobile}}
|
||||
</div>
|
||||
{% if request.user|stage_manages:rec or perms.recruitment.change_candidate %}
|
||||
<div class="oh-sticky-table__td oh-table-config__td" onclick="event.stopPropagation()">
|
||||
{% with request.user.employee_get.candidate_rating.all as candidate_ratings %}
|
||||
{% if candidate_ratings|has_candidate_rating:cand %}
|
||||
<form hx-swap="none" hx-post='{% url "update-candidate-rating" cand.id %}' method="post">
|
||||
{% csrf_token %}
|
||||
<div class="d-block mb-0">
|
||||
<div class="oh-rate" onclick="$(this).parents().closest('form').find('button').click()">
|
||||
{% for i in "54321" %}
|
||||
<input type="radio" id="star{{i}}{{cand.id}}" name="rating" class="rating-radio" value="{{i}}" {% if candidate_ratings|rating:cand == i %} checked {% endif %} />
|
||||
<label for="star{{i}}{{cand.id}}" title="{{i}} Stars">5 {% trans "Stars" %}</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="submit" hidden="true"></button>
|
||||
<span id="rating-radio-error"></span>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<form hx-swap="none" hx-post='{% url "create-candidate-rating" cand.id %}' method="post">
|
||||
{% csrf_token %}
|
||||
<div class="d-block mb-0">
|
||||
<div class="oh-rate" onclick="$(this).parents().closest('form').find('button').click()">
|
||||
{% for i in "54321" %}
|
||||
<input type="radio" id="star{{i}}{{cand.id}}" name="rating" class="rating-radio" value="{{i}}" />
|
||||
<label for="star{{i}}{{cand.id}}" title="5 Stars">5 {% trans "Stars" %}</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="submit" hidden="true"></button>
|
||||
<span id="rating-radio-error"></span>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="oh-sticky-table__td oh-table-config__td">
|
||||
<select
|
||||
name=""
|
||||
|
||||
@@ -98,4 +98,12 @@ def generate_id(element, label=""):
|
||||
return element
|
||||
|
||||
|
||||
# @register.filter
|
||||
@register.filter(name='has_candidate_rating')
|
||||
def has_candidate_rating(candidate_ratings, cand):
|
||||
candidate_rating = candidate_ratings.filter(candidate_id=cand.id).first()
|
||||
return candidate_rating
|
||||
|
||||
@register.filter(name='rating')
|
||||
def rating(candidate_ratings,cand):
|
||||
rating = candidate_ratings.filter(candidate_id=cand.id).first().rating
|
||||
return str(rating)
|
||||
@@ -285,4 +285,6 @@ urlpatterns = [
|
||||
path("create-mail-template/", create_letter, name="create-mail-template"),
|
||||
path("delete-mail-template/", delete_mail_templates, name="delete-mail-template"),
|
||||
path("get-template/<int:obj_id>/", get_template, name="get-template"),
|
||||
path("create-candidate-rating/<int:cand_id>/", views.create_candidate_rating, name="create-candidate-rating"),
|
||||
path("update-candidate-rating/<int:cand_id>/", views.update_candidate_rating, name="update-candidate-rating"),
|
||||
]
|
||||
|
||||
@@ -35,6 +35,7 @@ from horilla.decorators import permission_required, login_required, hx_request_r
|
||||
from base.methods import export_data, generate_pdf, get_key_instances
|
||||
from recruitment.views.paginator_qry import paginator_qry
|
||||
from recruitment.models import (
|
||||
CandidateRating,
|
||||
RecruitmentMailTemplate,
|
||||
Recruitment,
|
||||
Candidate,
|
||||
@@ -950,9 +951,17 @@ def candidate_view_individual(request, cand_id, **kwargs):
|
||||
mails= list(Candidate.objects.values_list("email",flat=True))
|
||||
# Query the User model to check if any email is present
|
||||
existing_emails = list(User.objects.filter(username__in=mails).values_list('email', flat=True))
|
||||
ratings = candidate_obj.candidate_rating.all()
|
||||
rating_list = []
|
||||
avg_rate = 0
|
||||
for rating in ratings:
|
||||
rating_list.append(rating.rating)
|
||||
if len(rating_list) != 0:
|
||||
avg_rate = round(sum(rating_list) / len(rating_list))
|
||||
|
||||
|
||||
return render(request, "candidate/individual.html",
|
||||
{"candidate": candidate_obj, "emp_list" : existing_emails })
|
||||
{"candidate": candidate_obj, "emp_list" : existing_emails,"average_rate": avg_rate,})
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -1193,3 +1202,25 @@ def candidate_select_filter(request):
|
||||
context = {"employee_ids": employee_ids, "total_count": total_count}
|
||||
|
||||
return JsonResponse(context)
|
||||
|
||||
|
||||
@login_required
|
||||
def create_candidate_rating(request,cand_id):
|
||||
cand_id = cand_id
|
||||
candidate = Candidate.objects.get(id=cand_id)
|
||||
employee_id = request.user.employee_get
|
||||
rating = request.POST.get("rating")
|
||||
CandidateRating.objects.create(candidate_id=candidate, rating=rating, employee_id=employee_id)
|
||||
return redirect(recruitment_pipeline)
|
||||
|
||||
|
||||
@login_required
|
||||
def update_candidate_rating(request,cand_id):
|
||||
cand_id = cand_id
|
||||
candidate = Candidate.objects.get(id=cand_id)
|
||||
employee_id = request.user.employee_get
|
||||
rating = request.POST.get("rating")
|
||||
rate = CandidateRating.objects.get(candidate_id=candidate, employee_id=employee_id)
|
||||
rate.rating = int(rating)
|
||||
rate.save()
|
||||
return redirect(recruitment_pipeline)
|
||||
Reference in New Issue
Block a user