[UPDT] RECRUITMENT: Added resume matching to candidate view
This commit is contained in:
@@ -145,7 +145,7 @@ x-data="{searchShow: false}"
|
||||
>
|
||||
<ul class="oh-dropdown__items">
|
||||
<li class="oh-dropdown__item">
|
||||
{% if perms.delete_candidates %}
|
||||
{% if perms.recruitment.delete_candidates %}
|
||||
<a
|
||||
href="#"
|
||||
class="oh-dropdown__link"
|
||||
@@ -158,7 +158,7 @@ x-data="{searchShow: false}"
|
||||
>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% if perms.change_candidates or request.user|is_stagemanager %}
|
||||
{% if perms.recruitment.change_candidates or request.user|is_stagemanager %}
|
||||
<li class="oh-dropdown__item">
|
||||
<a href="#" class="oh-dropdown__link "
|
||||
hx-get='{% url "send-mail" %}?stage_id={{stage.id}}' hx-target="#objectDetailsModalTarget"
|
||||
@@ -168,7 +168,37 @@ x-data="{searchShow: false}"
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.delete_candidates %}
|
||||
{% if perms.recruitment.add_candidate or request.user|recruitment_manages:rec %}
|
||||
<li class="oh-dropdown__item">
|
||||
<a
|
||||
role="button"
|
||||
class="oh-dropdown__link"
|
||||
>{% trans "Resume Shortlisting" %}</a
|
||||
>
|
||||
<form hx-get = "{% url 'view-bulk-resume' %}"
|
||||
hx-target = "#bulkResumeUploadBody">
|
||||
|
||||
<input type="submit"
|
||||
id="resume_button"
|
||||
hidden
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#bulkResumeUpload" />
|
||||
|
||||
<select name="rec_id"
|
||||
class="oh-select w-75 mt-2"
|
||||
onclick="event.stopPropagation()"
|
||||
onchange="$('#resume_button').click()"
|
||||
>
|
||||
<option value="">------</option>
|
||||
{% for rec in recruitments %}
|
||||
<option value="{{rec.id}}">{{rec}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.recruitment.delete_candidates %}
|
||||
<li class="oh-dropdown__item">
|
||||
<a href="#" class="oh-dropdown__link " id="archiveCandidates"
|
||||
|
||||
@@ -177,7 +207,7 @@ x-data="{searchShow: false}"
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.delete_candidates %}
|
||||
{% if perms.recruitment.delete_candidates %}
|
||||
<li class="oh-dropdown__item">
|
||||
<a href="#" class="oh-dropdown__link "
|
||||
|
||||
@@ -187,7 +217,7 @@ x-data="{searchShow: false}"
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.delete_candidates %}
|
||||
{% if perms.recruitment.delete_candidates %}
|
||||
<li class="oh-dropdown__item">
|
||||
<a
|
||||
href="#"
|
||||
@@ -216,6 +246,32 @@ x-data="{searchShow: false}"
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<div
|
||||
class="oh-modal"
|
||||
id="bulkResumeUpload"
|
||||
role="dialog"
|
||||
aria-labelledby="bulkResumeUpload"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="oh-modal__dialog">
|
||||
<div class="oh-modal__dialog-header">
|
||||
<h2 class="oh-modal__dialog-title" id="bulkResumeUploadLabel">
|
||||
{% trans "Upload Resumes" %}
|
||||
</h2>
|
||||
<button class="oh-modal__close" aria-label="Close">
|
||||
<ion-icon name="close-outline"></ion-icon>
|
||||
</button>
|
||||
<div
|
||||
class="oh-modal__dialog-body mb-4 p-0 pt-2"
|
||||
id="bulkResumeUploadBody"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
|
||||
@@ -23,6 +23,13 @@
|
||||
.oh-rate:not(:checked)>label:hover, .oh-rate:not(:checked)>label:hover~label{
|
||||
color: #ccc
|
||||
}
|
||||
#enlargeImageContainer {
|
||||
position: absolute;
|
||||
left: -300px;
|
||||
top: 100px;
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
{% include 'candidate/candidate_nav.html' %}
|
||||
<div class="oh-wrapper">
|
||||
@@ -53,7 +60,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function submitForm(elem) {
|
||||
$(elem).siblings(".add_more_submit").click();
|
||||
}
|
||||
|
||||
function enlargeImage(src) {
|
||||
var enlargeImageContainer = $('#enlargeImageContainer');
|
||||
enlargeImageContainer.empty();
|
||||
style = "width:100%; height:90%; box-shadow: 0 10px 10px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.2); background:white"
|
||||
var enlargedImage = $('<iframe>').attr({'src': src,'style': style,});
|
||||
var name =$('<span>').text(src.split('/').pop().replace(/_/g, " "))
|
||||
enlargeImageContainer.append(enlargedImage);
|
||||
enlargeImageContainer.append(name);
|
||||
setTimeout(function() {
|
||||
enlargeImageContainer.show();
|
||||
|
||||
const iframe = document.querySelector("iframe").contentWindow;
|
||||
var iframe_document = iframe.document
|
||||
iframe_image = iframe_document.getElementsByTagName('img')[0]
|
||||
$(iframe_image).attr("style","width:100%; height:100%;")
|
||||
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function hideEnlargeImage() {
|
||||
var enlargeImageContainer = $('#enlargeImageContainer');
|
||||
enlargeImageContainer.empty();
|
||||
}
|
||||
|
||||
$(document).on('click', function(event){
|
||||
if (!$(event.target).closest('#enlargeImageContainer').length) {
|
||||
hideEnlargeImage();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="{% static '/candidate/bulk.js' %}"></script>
|
||||
|
||||
|
||||
@@ -47,15 +47,16 @@
|
||||
type="file"
|
||||
name="files"
|
||||
class="d-none"
|
||||
accept=".pdf"
|
||||
multiple="true"
|
||||
id="addFile"
|
||||
onchange="submitForm(this)"
|
||||
/>
|
||||
/>
|
||||
<input type="submit" class="d-none add_more_submit" value="save" />
|
||||
</form>
|
||||
</div>
|
||||
<div style="width: 50%">
|
||||
<div id="enlargeImageContainer" class="enlargeImageContainer"></div>
|
||||
<div id="enlargeImageContainer" style="width:300px; height:400px;" class="enlargeImageContainer"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -64,4 +65,4 @@
|
||||
|
||||
<div class="" id="matchingResume" hx-get="{% url 'matching-resumes' rec_id %}" hx-target="#matchingResume" hx-trigger="load">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
{% for resume in matched_resumes %}
|
||||
<div class="oh-sticky-table__tr" draggable="true">
|
||||
<div class="oh-sticky-table__td" align="center">
|
||||
<a href="{{ resume.resume.file.url }}" rel="noopener noreferrer" target="_blank"> {{resume.resume}} </a>
|
||||
<a href="{{ resume.resume.file.url }}" onmouseover="enlargeImage('{{ resume.resume.file.url }}',$(this))" rel="noopener noreferrer" target="_blank"> {{resume.resume}} </a>
|
||||
</div>
|
||||
<div class="oh-sticky-table__td" align="center">{{resume.matching_skills_count}}</div>
|
||||
<div class="oh-sticky-table__td" align="center">{% if resume.image_pdf %}<p class="text-danger">{% trans "Need verification" %}</p>{% else %}{{resume.matching_skills_count}}{% endif %}</div>
|
||||
{% if perms.base.change_department or perms.base.delete_department %}
|
||||
<div class="oh-sticky-table__td">
|
||||
{% if resume.resume.is_candidate %}
|
||||
<a class="oh-btn oh-btn--success"
|
||||
<a class="oh-btn oh-btn--success"
|
||||
href="#"
|
||||
>
|
||||
{% trans "Candidated Added" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="oh-btn oh-btn--info"
|
||||
<a class="oh-btn oh-btn--info"
|
||||
target="_blank"
|
||||
href="{% url 'application-form' %}?recruitmentId={{rec_id}}&resumeId={{resume.resume.pk}}"
|
||||
>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.recruitment.change_recruitment or request.user|recruitment_manages:rec %}
|
||||
{% if perms.recruitment.add_candidate or request.user|recruitment_manages:rec %}
|
||||
<li class="oh-dropdown__item">
|
||||
<a hx-get = "{% url 'view-bulk-resume'%}?rec_id={{rec.id}}"
|
||||
hx-target = "#bulkResumeUploadBody"
|
||||
|
||||
@@ -1324,7 +1324,7 @@ def candidate_view(request):
|
||||
view_type = request.GET.get("view")
|
||||
previous_data = request.GET.urlencode()
|
||||
candidates = Candidate.objects.filter(is_active=True)
|
||||
candidate_all = Candidate.objects.all()
|
||||
recruitments = Recruitment.objects.filter(closed=False, is_active=True)
|
||||
|
||||
mails = list(Candidate.objects.values_list("email", flat=True))
|
||||
# Query the User model to check if any email is present
|
||||
@@ -1354,6 +1354,7 @@ def candidate_view(request):
|
||||
"filter_dict": data_dict,
|
||||
"gp_fields": CandidateReGroup.fields,
|
||||
"emp_list": existing_emails,
|
||||
"recruitments": recruitments,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -2653,6 +2654,11 @@ def delete_reject_reason(request):
|
||||
|
||||
|
||||
def extract_text_with_font_info(pdf):
|
||||
"""
|
||||
This method is used to extract text from the pdf and create a list of dictionaries containing details about the extracted text.
|
||||
Args:
|
||||
pdf (): pdf file to extract text from
|
||||
"""
|
||||
pdf_bytes = pdf.read()
|
||||
pdf_doc = io.BytesIO(pdf_bytes)
|
||||
doc = fitz.open("pdf", pdf_doc)
|
||||
@@ -2682,6 +2688,15 @@ def extract_text_with_font_info(pdf):
|
||||
|
||||
|
||||
def rank_text(text_info):
|
||||
"""
|
||||
This method is used to rank the text
|
||||
|
||||
Args:
|
||||
text_info: List of dictionary containing the details
|
||||
|
||||
Returns:
|
||||
Returns a sorted list
|
||||
"""
|
||||
ranked_text = sorted(
|
||||
text_info, key=lambda x: (x["font_size"], x["capitalization"]), reverse=True
|
||||
)
|
||||
@@ -2689,6 +2704,15 @@ def rank_text(text_info):
|
||||
|
||||
|
||||
def dob_matching(dob):
|
||||
"""
|
||||
This method is used to change the date format to YYYY-MM-DD
|
||||
|
||||
Args:
|
||||
dob: Date
|
||||
|
||||
Returns:
|
||||
Return date in YYYY-MM-DD
|
||||
"""
|
||||
date_formats = [
|
||||
"%Y-%m-%d",
|
||||
"%Y/%m/%d",
|
||||
@@ -2709,6 +2733,12 @@ def dob_matching(dob):
|
||||
|
||||
|
||||
def extract_info(pdf):
|
||||
"""
|
||||
This method creates the contact information dictionary from the provided pdf file
|
||||
Args:
|
||||
pdf_file: pdf file
|
||||
"""
|
||||
|
||||
text_info = extract_text_with_font_info(pdf)
|
||||
ranked_text = rank_text(text_info)
|
||||
|
||||
@@ -2780,6 +2810,9 @@ def extract_info(pdf):
|
||||
|
||||
|
||||
def resume_completion(request):
|
||||
"""
|
||||
This function is returns the data for completing the candidate creation form
|
||||
"""
|
||||
resume_file = request.FILES["resume"]
|
||||
contact_info = extract_info(resume_file)
|
||||
|
||||
@@ -2800,6 +2833,9 @@ def check_vaccancy(request):
|
||||
|
||||
@login_required
|
||||
def create_skills(request):
|
||||
"""
|
||||
This method is used to create the skills
|
||||
"""
|
||||
instance_id = eval(str(request.GET.get("instance_id")))
|
||||
dynamic = request.GET.get("dynamic")
|
||||
hx_vals = request.GET.get("data")
|
||||
@@ -2847,7 +2883,7 @@ def create_skills(request):
|
||||
@permission_required("recruitment.delete_rejectreason")
|
||||
def delete_skills(request):
|
||||
"""
|
||||
This method is used to delete the reject reasons
|
||||
This method is used to delete the skills
|
||||
"""
|
||||
ids = request.GET.getlist("ids")
|
||||
skills = Skill.objects.filter(id__in=ids)
|
||||
@@ -2857,7 +2893,13 @@ def delete_skills(request):
|
||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
|
||||
|
||||
|
||||
@login_required
|
||||
@hx_request_required
|
||||
@manager_can_enter("recruitment.add_candidate")
|
||||
def view_bulk_resumes(request):
|
||||
"""
|
||||
This function returns the bulk_resume.html page to the modal
|
||||
"""
|
||||
rec_id = eval(str(request.GET.get("rec_id")))
|
||||
resumes = Resume.objects.filter(recruitment_id=rec_id)
|
||||
|
||||
@@ -2866,7 +2908,13 @@ def view_bulk_resumes(request):
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@hx_request_required
|
||||
@manager_can_enter("recruitment.add_candidate")
|
||||
def add_bulk_resumes(request):
|
||||
"""
|
||||
This function is used to create bulk resume
|
||||
"""
|
||||
rec_id = eval(str(request.GET.get("rec_id")))
|
||||
recruitment = Recruitment.objects.get(id=rec_id)
|
||||
if request.method == "POST":
|
||||
@@ -2884,9 +2932,11 @@ def add_bulk_resumes(request):
|
||||
|
||||
|
||||
@login_required
|
||||
@hx_request_required
|
||||
@manager_can_enter("recruitment.add_candidate")
|
||||
def delete_resume_file(request):
|
||||
"""
|
||||
Used to delete attachment
|
||||
Used to delete resume
|
||||
"""
|
||||
ids = request.GET.getlist("ids")
|
||||
rec_id = request.GET.get("rec_id")
|
||||
@@ -2899,7 +2949,12 @@ def delete_resume_file(request):
|
||||
|
||||
|
||||
def extract_words_from_pdf(pdf_file):
|
||||
# Open the PDF file
|
||||
"""
|
||||
This method is used to extract the words from the pdf file into a list.
|
||||
Args:
|
||||
pdf_file: pdf file
|
||||
|
||||
"""
|
||||
pdf_document = fitz.open(pdf_file.path)
|
||||
|
||||
words = []
|
||||
@@ -2908,7 +2963,6 @@ def extract_words_from_pdf(pdf_file):
|
||||
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)
|
||||
@@ -2919,7 +2973,16 @@ def extract_words_from_pdf(pdf_file):
|
||||
|
||||
|
||||
@login_required
|
||||
@hx_request_required
|
||||
@manager_can_enter("recruitment.add_candidate")
|
||||
def matching_resumes(request, rec_id):
|
||||
"""
|
||||
This function returns the matching resume table after sorting the resumes according to their scores
|
||||
|
||||
Args:
|
||||
rec_id: Recruitment ID
|
||||
|
||||
"""
|
||||
recruitment = Recruitment.objects.filter(id=rec_id).first()
|
||||
skills = recruitment.skills.values_list("title", flat=True)
|
||||
resumes = recruitment.resume.all()
|
||||
@@ -2931,9 +2994,11 @@ def matching_resumes(request, rec_id):
|
||||
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}
|
||||
)
|
||||
item = {"resume": resume, "matching_skills_count": matching_skills_count}
|
||||
if not len(words):
|
||||
item["image_pdf"] = True
|
||||
|
||||
resume_ranks.append(item)
|
||||
|
||||
candidate_resumes = [
|
||||
rank for rank in resume_ranks if rank["resume"].id in is_candidate_ids
|
||||
@@ -2961,7 +3026,12 @@ def matching_resumes(request, rec_id):
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@manager_can_enter("recruitment.add_candidate")
|
||||
def matching_resume_completion(request):
|
||||
"""
|
||||
This function is returns the data for completing the candidate creation form
|
||||
"""
|
||||
resume_id = request.GET.get("resume_id")
|
||||
resume_obj = get_object_or_404(Resume, id=resume_id)
|
||||
resume_file = resume_obj.file
|
||||
|
||||
Reference in New Issue
Block a user