Merge pull request #29506 from ruchamahabal/fix-interview-ratings
This commit is contained in:
commit
cc4b4046ce
@ -7,7 +7,7 @@ import datetime
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr, get_datetime, get_link_to_form
|
||||
from frappe.utils import cstr, flt, get_datetime, get_link_to_form
|
||||
|
||||
|
||||
class DuplicateInterviewRoundError(frappe.ValidationError):
|
||||
@ -18,6 +18,7 @@ class Interview(Document):
|
||||
self.validate_duplicate_interview()
|
||||
self.validate_designation()
|
||||
self.validate_overlap()
|
||||
self.set_average_rating()
|
||||
|
||||
def on_submit(self):
|
||||
if self.status not in ['Cleared', 'Rejected']:
|
||||
@ -67,6 +68,13 @@ class Interview(Document):
|
||||
overlapping_details = _('Interview overlaps with {0}').format(get_link_to_form('Interview', overlaps[0][0]))
|
||||
frappe.throw(overlapping_details, title=_('Overlap'))
|
||||
|
||||
def set_average_rating(self):
|
||||
total_rating = 0
|
||||
for entry in self.interview_details:
|
||||
if entry.average_rating:
|
||||
total_rating += entry.average_rating
|
||||
|
||||
self.average_rating = flt(total_rating / len(self.interview_details) if len(self.interview_details) else 0)
|
||||
|
||||
@frappe.whitelist()
|
||||
def reschedule_interview(self, scheduled_on, from_time, to_time):
|
||||
|
@ -12,6 +12,7 @@ from frappe.utils import add_days, getdate, nowtime
|
||||
|
||||
from erpnext.hr.doctype.designation.test_designation import create_designation
|
||||
from erpnext.hr.doctype.interview.interview import DuplicateInterviewRoundError
|
||||
from erpnext.hr.doctype.job_applicant.job_applicant import get_interview_details
|
||||
from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
|
||||
|
||||
|
||||
@ -70,6 +71,20 @@ class TestInterview(unittest.TestCase):
|
||||
email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
|
||||
self.assertTrue("Subject: Interview Feedback Reminder" in email_queue[0].message)
|
||||
|
||||
def test_get_interview_details_for_applicant_dashboard(self):
|
||||
job_applicant = create_job_applicant()
|
||||
interview = create_interview_and_dependencies(job_applicant.name)
|
||||
|
||||
details = get_interview_details(job_applicant.name)
|
||||
self.assertEqual(details.get('stars'), 5)
|
||||
self.assertEqual(details.get('interviews').get(interview.name), {
|
||||
'name': interview.name,
|
||||
'interview_round': interview.interview_round,
|
||||
'expected_average_rating': interview.expected_average_rating * 5,
|
||||
'average_rating': interview.average_rating * 5,
|
||||
'status': 'Pending'
|
||||
})
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
||||
@ -106,7 +121,8 @@ def create_interview_round(name, skill_set, interviewers=[], designation=None, s
|
||||
interview_round = frappe.new_doc("Interview Round")
|
||||
interview_round.round_name = name
|
||||
interview_round.interview_type = create_interview_type()
|
||||
interview_round.expected_average_rating = 4
|
||||
# average rating = 4
|
||||
interview_round.expected_average_rating = 0.8
|
||||
if designation:
|
||||
interview_round.designation = designation
|
||||
|
||||
|
@ -57,7 +57,6 @@ class InterviewFeedback(Document):
|
||||
|
||||
def update_interview_details(self):
|
||||
doc = frappe.get_doc('Interview', self.interview)
|
||||
total_rating = 0
|
||||
|
||||
if self.docstatus == 2:
|
||||
for entry in doc.interview_details:
|
||||
@ -72,10 +71,6 @@ class InterviewFeedback(Document):
|
||||
entry.comments = self.feedback
|
||||
entry.result = self.result
|
||||
|
||||
if entry.average_rating:
|
||||
total_rating += entry.average_rating
|
||||
|
||||
doc.average_rating = flt(total_rating / len(doc.interview_details) if len(doc.interview_details) else 0)
|
||||
doc.save()
|
||||
doc.notify_update()
|
||||
|
||||
|
@ -24,7 +24,7 @@ class TestInterviewFeedback(unittest.TestCase):
|
||||
create_skill_set(['Leadership'])
|
||||
|
||||
interview_feedback = create_interview_feedback(interview.name, interviewer, skill_ratings)
|
||||
interview_feedback.append("skill_assessment", {"skill": 'Leadership', 'rating': 4})
|
||||
interview_feedback.append("skill_assessment", {"skill": 'Leadership', 'rating': 0.8})
|
||||
frappe.set_user(interviewer)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, interview_feedback.save)
|
||||
@ -50,7 +50,7 @@ class TestInterviewFeedback(unittest.TestCase):
|
||||
|
||||
avg_rating = flt(total_rating / len(feedback_1.skill_assessment) if len(feedback_1.skill_assessment) else 0)
|
||||
|
||||
self.assertEqual(flt(avg_rating, 3), feedback_1.average_rating)
|
||||
self.assertEqual(flt(avg_rating, 2), flt(feedback_1.average_rating, 2))
|
||||
|
||||
avg_on_interview_detail = frappe.db.get_value('Interview Detail', {
|
||||
'parent': feedback_1.interview,
|
||||
@ -59,7 +59,7 @@ class TestInterviewFeedback(unittest.TestCase):
|
||||
}, 'average_rating')
|
||||
|
||||
# 1. average should be reflected in Interview Detail.
|
||||
self.assertEqual(avg_on_interview_detail, feedback_1.average_rating)
|
||||
self.assertEqual(flt(avg_on_interview_detail, 2), flt(feedback_1.average_rating, 2))
|
||||
|
||||
'''For Second Interviewer Feedback'''
|
||||
interviewer = interview.interview_details[1].interviewer
|
||||
@ -97,5 +97,5 @@ def get_skills_rating(interview_round):
|
||||
|
||||
skills = frappe.get_all("Expected Skill Set", filters={"parent": interview_round}, fields = ["skill"])
|
||||
for d in skills:
|
||||
d["rating"] = random.randint(1, 5)
|
||||
d["rating"] = random.random()
|
||||
return skills
|
||||
|
@ -21,9 +21,9 @@ frappe.ui.form.on("Job Applicant", {
|
||||
|
||||
create_custom_buttons: function(frm) {
|
||||
if (!frm.doc.__islocal && frm.doc.status !== "Rejected" && frm.doc.status !== "Accepted") {
|
||||
frm.add_custom_button(__("Create Interview"), function() {
|
||||
frm.add_custom_button(__("Interview"), function() {
|
||||
frm.events.create_dialog(frm);
|
||||
});
|
||||
}, __("Create"));
|
||||
}
|
||||
|
||||
if (!frm.doc.__islocal) {
|
||||
@ -40,10 +40,10 @@ frappe.ui.form.on("Job Applicant", {
|
||||
frappe.route_options = {
|
||||
"job_applicant": frm.doc.name,
|
||||
"applicant_name": frm.doc.applicant_name,
|
||||
"designation": frm.doc.job_opening,
|
||||
"designation": frm.doc.job_opening || frm.doc.designation,
|
||||
};
|
||||
frappe.new_doc("Job Offer");
|
||||
});
|
||||
}, __("Create"));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -55,13 +55,16 @@ frappe.ui.form.on("Job Applicant", {
|
||||
job_applicant: frm.doc.name
|
||||
},
|
||||
callback: function(r) {
|
||||
$("div").remove(".form-dashboard-section.custom");
|
||||
frm.dashboard.add_section(
|
||||
frappe.render_template('job_applicant_dashboard', {
|
||||
data: r.message
|
||||
}),
|
||||
__("Interview Summary")
|
||||
);
|
||||
if (r.message) {
|
||||
$("div").remove(".form-dashboard-section.custom");
|
||||
frm.dashboard.add_section(
|
||||
frappe.render_template("job_applicant_dashboard", {
|
||||
data: r.message.interviews,
|
||||
number_of_stars: r.message.stars
|
||||
}),
|
||||
__("Interview Summary")
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -81,8 +81,13 @@ def get_interview_details(job_applicant):
|
||||
fields=["name", "interview_round", "expected_average_rating", "average_rating", "status"]
|
||||
)
|
||||
interview_detail_map = {}
|
||||
meta = frappe.get_meta("Interview")
|
||||
number_of_stars = meta.get_options("expected_average_rating") or 5
|
||||
|
||||
for detail in interview_details:
|
||||
detail.expected_average_rating = detail.expected_average_rating * number_of_stars if detail.expected_average_rating else 0
|
||||
detail.average_rating = detail.average_rating * number_of_stars if detail.average_rating else 0
|
||||
|
||||
interview_detail_map[detail.name] = detail
|
||||
|
||||
return interview_detail_map
|
||||
return {"interviews": interview_detail_map, "stars": number_of_stars}
|
||||
|
@ -17,24 +17,33 @@
|
||||
<td class="text-left"> {%= key %} </td>
|
||||
<td class="text-left"> {%= value["interview_round"] %} </td>
|
||||
<td class="text-left"> {%= value["status"] %} </td>
|
||||
<td class="text-left">
|
||||
{% for (i = 0; i < value["expected_average_rating"]; i++) { %}
|
||||
<span class="fa fa-star " style="color: #F6C35E;"></span>
|
||||
{% } %}
|
||||
{% for (i = 0; i < (5-value["expected_average_rating"]); i++) { %}
|
||||
<span class="fa fa-star " style="color: #E7E9EB;"></span>
|
||||
{% } %}
|
||||
</td>
|
||||
<td class="text-left">
|
||||
{% if(value["average_rating"]){ %}
|
||||
{% for (i = 0; i < value["average_rating"]; i++) { %}
|
||||
<span class="fa fa-star " style="color: #F6C35E;"></span>
|
||||
{% } %}
|
||||
{% for (i = 0; i < (5-value["average_rating"]); i++) { %}
|
||||
<span class="fa fa-star " style="color: #E7E9EB;"></span>
|
||||
{% } %}
|
||||
{% } %}
|
||||
</td>
|
||||
{% let right_class = ''; %}
|
||||
{% let left_class = ''; %}
|
||||
|
||||
{% $.each([value["expected_average_rating"], value["average_rating"]], (_, val) => { %}
|
||||
<td class="text-left">
|
||||
<div class="rating">
|
||||
{% for (let i = 1; i <= number_of_stars; i++) { %}
|
||||
{% if (i <= val) { %}
|
||||
{% right_class = 'star-click'; %}
|
||||
{% } else { %}
|
||||
{% right_class = ''; %}
|
||||
{% } %}
|
||||
|
||||
{% if ((i <= val) || ((i - 0.5) == val)) { %}
|
||||
{% left_class = 'star-click'; %}
|
||||
{% } else { %}
|
||||
{% left_class = ''; %}
|
||||
{% } %}
|
||||
|
||||
<svg class="icon icon-md" data-rating={{i}} viewBox="0 0 24 24" fill="none">
|
||||
<path class="right-half {{ right_class }}" d="M11.9987 3.00011C12.177 3.00011 12.3554 3.09303 12.4471 3.27888L14.8213 8.09112C14.8941 8.23872 15.0349 8.34102 15.1978 8.3647L20.5069 9.13641C20.917 9.19602 21.0807 9.69992 20.7841 9.9892L16.9421 13.7354C16.8243 13.8503 16.7706 14.0157 16.7984 14.1779L17.7053 19.4674C17.7753 19.8759 17.3466 20.1874 16.9798 19.9945L12.2314 17.4973C12.1586 17.459 12.0786 17.4398 11.9987 17.4398V3.00011Z" fill="var(--star-fill)" stroke="var(--star-fill)"/>
|
||||
<path class="left-half {{ left_class }}" d="M11.9987 3.00011C11.8207 3.00011 11.6428 3.09261 11.5509 3.27762L9.15562 8.09836C9.08253 8.24546 8.94185 8.34728 8.77927 8.37075L3.42887 9.14298C3.01771 9.20233 2.85405 9.70811 3.1525 9.99707L7.01978 13.7414C7.13858 13.8564 7.19283 14.0228 7.16469 14.1857L6.25116 19.4762C6.18071 19.8842 6.6083 20.1961 6.97531 20.0045L11.7672 17.5022C11.8397 17.4643 11.9192 17.4454 11.9987 17.4454V3.00011Z" fill="var(--star-fill)" stroke="var(--star-fill)"/>
|
||||
</svg>
|
||||
{% } %}
|
||||
</div>
|
||||
</td>
|
||||
{% }); %}
|
||||
</tr>
|
||||
{% } %}
|
||||
</tbody>
|
||||
|
Loading…
x
Reference in New Issue
Block a user