Merge pull request #29506 from ruchamahabal/fix-interview-ratings
diff --git a/erpnext/hr/doctype/interview/interview.py b/erpnext/hr/doctype/interview/interview.py
index 4bb003d..a3b111c 100644
--- a/erpnext/hr/doctype/interview/interview.py
+++ b/erpnext/hr/doctype/interview/interview.py
@@ -7,7 +7,7 @@
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 @@
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 @@
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):
diff --git a/erpnext/hr/doctype/interview/test_interview.py b/erpnext/hr/doctype/interview/test_interview.py
index 1a2257a..fdb11af 100644
--- a/erpnext/hr/doctype/interview/test_interview.py
+++ b/erpnext/hr/doctype/interview/test_interview.py
@@ -12,6 +12,7 @@
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 @@
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 @@
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
diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.py b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
index d046458..2ff00c1 100644
--- a/erpnext/hr/doctype/interview_feedback/interview_feedback.py
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
@@ -57,7 +57,6 @@
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 @@
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()
diff --git a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
index d2ec5b9..19c4642 100644
--- a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
+++ b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
@@ -24,7 +24,7 @@
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 @@
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 @@
}, '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 @@
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
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.js b/erpnext/hr/doctype/job_applicant/job_applicant.js
index d7b1c6c..c1e8257 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.js
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.js
@@ -21,9 +21,9 @@
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.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 @@
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")
+ );
+ }
}
});
},
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py
index 5b3d9bf..ccc21ce 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.py
@@ -81,8 +81,13 @@
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}
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html
index c286787..734b2fe 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html
@@ -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>