[UPDT] RECRUITMENT: Added resume matching to candidate view

This commit is contained in:
Horilla
2024-07-10 16:14:07 +05:30
parent aa802553dc
commit e07edd41da
6 changed files with 189 additions and 21 deletions

View File

@@ -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 () {

View File

@@ -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>

View File

@@ -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>

View File

@@ -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}}"
>

View File

@@ -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"

View File

@@ -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