From a22c94c24625450a64ae5542bfab11ed5f258a64 Mon Sep 17 00:00:00 2001 From: Manas Solanki Date: Mon, 18 Sep 2017 16:59:54 +0530 Subject: [PATCH] refactored assessment result tool (#10633) * save the assessment instead of submit * Added comments in the artool * remove the cur_frm and message for submitted result * link field for the assessment result --- .../js/schools/assessment_result_tool.html | 112 ++++++---- erpnext/schools/api.py | 99 +++++++-- .../assessment_result/assessment_result.json | 4 +- .../assessment_result/assessment_result.py | 17 +- .../assessment_result_tool.js | 210 +++++++++++------- .../assessment_result_tool.py | 2 +- 6 files changed, 298 insertions(+), 146 deletions(-) diff --git a/erpnext/public/js/schools/assessment_result_tool.html b/erpnext/public/js/schools/assessment_result_tool.html index 3c09ccd6ee..9fc17f7be1 100644 --- a/erpnext/public/js/schools/assessment_result_tool.html +++ b/erpnext/public/js/schools/assessment_result_tool.html @@ -1,44 +1,72 @@ - - - - - {% for c in criteria %} - - {% endfor %} - - - - - {% for c in criteria %} - - {% endfor %} - - - - - {% for s in students %} - - - - {% for c in criteria %} - - {% endfor %} - - - {% endfor %} - + + + + + {% for c in criteria %} + + {% endfor %} + + + + + + {% for c in criteria %} + + {% endfor %} + + + + + {% for s in students %} + + + + + {% for c in criteria %} + + {% endfor %} + + + {% endfor %} +
StudentStudent Name{{ c.assessment_criteria }}Total Marks
{{ c.maximum_score }}{{max_total_score}}
{{ s.student }}{{ s.student_name }} - - - {% if(s.assessment_details) { %} {{s.assessment_details.total_score}} {% } %} -
StudentStudent Name{{ c.assessment_criteria }}CommentsTotal Marks
Score ({{ c.maximum_score }})Score ({{max_total_score}})
{{ s.student }}{{ s.student_name }} + + {% if(s.assessment_details) { %} + {{s.assessment_details[c.assessment_criteria][1]}} + {% } %} + + + + + + + {% if(s.assessment_details) { %} + {{s.assessment_details.total_score[1]}} + {% } %} + + + {% if(s.assessment_details) { %} + {{s.assessment_details.total_score[0]}} + {% } %} + + + + + + +
\ No newline at end of file diff --git a/erpnext/schools/api.py b/erpnext/schools/api.py index ff2da07a30..41d4a0dcb8 100644 --- a/erpnext/schools/api.py +++ b/erpnext/schools/api.py @@ -18,6 +18,7 @@ def get_course(program): (program), as_dict=1) return courses + @frappe.whitelist() def enroll_student(source_name): """Creates a Student Record and returns a Program Enrollment. @@ -40,6 +41,7 @@ def enroll_student(source_name): frappe.publish_realtime('enroll_student_progress', {"progress": [4, 4]}, user=frappe.session.user) return program_enrollment + @frappe.whitelist() def check_attendance_records_exist(course_schedule=None, student_group=None, date=None): """Check if Attendance Records are made against the specified Course Schedule or Student Group for given date. @@ -53,6 +55,7 @@ def check_attendance_records_exist(course_schedule=None, student_group=None, dat else: return frappe.get_list("Student Attendance", filters={"student_group": student_group, "date": date}) + @frappe.whitelist() def mark_attendance(students_present, students_absent, course_schedule=None, student_group=None, date=None): """Creates Multiple Attendance Records. @@ -76,6 +79,7 @@ def mark_attendance(students_present, students_absent, course_schedule=None, stu frappe.db.commit() frappe.msgprint(_("Attendance has been marked successfully.")) + def make_attendance_records(student, student_name, status, course_schedule=None, student_group=None, date=None): """Creates/Update Attendance Record. @@ -103,6 +107,7 @@ def make_attendance_records(student, student_name, status, course_schedule=None, student_attendance.status = status student_attendance.save() + @frappe.whitelist() def get_student_guardians(student): """Returns List of Guardians of a Student. @@ -113,6 +118,7 @@ def get_student_guardians(student): filters={"parent": student}) return guardians + @frappe.whitelist() def get_student_group_students(student_group, include_inactive=0): """Returns List of student, student_name in Student Group. @@ -127,6 +133,7 @@ def get_student_group_students(student_group, include_inactive=0): filters={"parent": student_group, "active": 1}, order_by= "group_roll_number") return students + @frappe.whitelist() def get_fee_structure(program, academic_term=None): """Returns Fee Structure. @@ -138,6 +145,7 @@ def get_fee_structure(program, academic_term=None): "academic_term": academic_term}, 'name', as_dict=True) return fee_structure[0].name if fee_structure else None + @frappe.whitelist() def get_fee_components(fee_structure): """Returns Fee Components. @@ -148,6 +156,7 @@ def get_fee_components(fee_structure): fs = frappe.get_list("Fee Component", fields=["fees_category", "amount"] , filters={"parent": fee_structure}, order_by= "idx") return fs + @frappe.whitelist() def get_fee_schedule(program, student_category=None): """Returns Fee Schedule. @@ -159,6 +168,7 @@ def get_fee_schedule(program, student_category=None): filters={"parent": program, "student_category": student_category }, order_by= "idx") return fs + @frappe.whitelist() def collect_fees(fees, amt): paid_amount = flt(amt) + flt(frappe.db.get_value("Fees", fees, "paid_amount")) @@ -167,6 +177,7 @@ def collect_fees(fees, amt): frappe.db.set_value("Fees", fees, "outstanding_amount", (total_amount - paid_amount)) return paid_amount + @frappe.whitelist() def get_course_schedule_events(start, end, filters=None): """Returns events for Course Schedule Calendar view rendering. @@ -191,6 +202,7 @@ def get_course_schedule_events(start, end, filters=None): return data + @frappe.whitelist() def get_assessment_criteria(course): """Returns Assessmemt Criteria and their Weightage from Course Master. @@ -200,22 +212,30 @@ def get_assessment_criteria(course): return frappe.get_list("Course Assessment Criteria", \ fields=["assessment_criteria", "weightage"], filters={"parent": course}, order_by= "idx") + @frappe.whitelist() def get_assessment_students(assessment_plan, student_group): - student_list = get_student_group_students(student_group) for i, student in enumerate(student_list): result = get_result(student.student, assessment_plan) if result: student_result = {} for d in result.details: - student_result.update({d.assessment_criteria: cstr(d.score) + " ("+ d.grade + ")"}) - student_result.update({"total_score": cstr(result.total_score) + " (" + result.grade + ")"}) - student.update({'assessment_details': student_result}) + student_result.update({d.assessment_criteria: [cstr(d.score), d.grade]}) + student_result.update({ + "total_score": [cstr(result.total_score), result.grade], + "comment": result.comment + }) + student.update({ + "assessment_details": student_result, + "docstatus": result.docstatus, + "name": result.name + }) else: student.update({'assessment_details': None}) return student_list + @frappe.whitelist() def get_assessment_details(assessment_plan): """Returns Assessment Criteria and Maximum Score from Assessment Plan Master. @@ -223,7 +243,8 @@ def get_assessment_details(assessment_plan): :param Assessment Plan: Assessment Plan """ return frappe.get_list("Assessment Plan Criteria", \ - fields=["assessment_criteria", "maximum_score"], filters={"parent": assessment_plan}, order_by= "idx") + fields=["assessment_criteria", "maximum_score", "docstatus"], filters={"parent": assessment_plan}, order_by= "idx") + @frappe.whitelist() def get_result(student, assessment_plan): @@ -232,12 +253,14 @@ def get_result(student, assessment_plan): :param Student: Student :param Assessment Plan: Assessment Plan """ - results = frappe.get_all("Assessment Result", filters={"student": student, "assessment_plan": assessment_plan, "docstatus": 1}) + results = frappe.get_all("Assessment Result", filters={"student": student, + "assessment_plan": assessment_plan, "docstatus": ("!=", 2)}) if results: return frappe.get_doc("Assessment Result", results[0]) else: return None + @frappe.whitelist() def get_grade(grading_scale, percentage): """Returns Grade based on the Grading Scale and Score. @@ -257,25 +280,63 @@ def get_grade(grading_scale, percentage): grade = "" return grade + @frappe.whitelist() -def mark_assessment_result(student, assessment_plan, scores): - student_score = json.loads(scores) - details = [] - for s in student_score.keys(): - details.append({ - "assessment_criteria": s, - "score": flt(student_score[s]) +def mark_assessment_result(assessment_plan, scores): + student_score = json.loads(scores); + assessment_details = [] + for criteria in student_score.get("assessment_details"): + assessment_details.append({ + "assessment_criteria": criteria, + "score": flt(student_score["assessment_details"][criteria]) }) - assessment_result = frappe.new_doc("Assessment Result") + assessment_result = get_assessment_result_doc(student_score["student"], assessment_plan) assessment_result.update({ - "student": student, - "student_name": frappe.db.get_value("Student", student, "title"), + "student": student_score.get("student"), "assessment_plan": assessment_plan, - "details": details + "comment": student_score.get("comment"), + "total_score":student_score.get("total_score"), + "details": assessment_details }) assessment_result.save() - assessment_result.submit() - return assessment_result + details = {} + for d in assessment_result.details: + details.update({d.assessment_criteria: d.grade}) + assessment_result_dict = { + "name": assessment_result.name, + "student": assessment_result.student, + "total_score": assessment_result.total_score, + "grade": assessment_result.grade, + "details": details + } + return assessment_result_dict + + +@frappe.whitelist() +def submit_assessment_results(assessment_plan, student_group): + total_result = 0 + student_list = get_student_group_students(student_group) + for i, student in enumerate(student_list): + doc = get_result(student.student, assessment_plan) + if doc and doc.docstatus==0: + total_result += 1 + doc.submit() + return total_result + + +def get_assessment_result_doc(student, assessment_plan): + assessment_result = frappe.get_all("Assessment Result", filters={"student": student, + "assessment_plan": assessment_plan, "docstatus": ("!=", 2)}) + if assessment_result: + doc = frappe.get_doc("Assessment Result", assessment_result[0]) + if doc.docstatus == 0: + return doc + elif doc.docstatus == 1: + frappe.msgprint("Result already Submitted") + return None + else: + return frappe.new_doc("Assessment Result") + @frappe.whitelist() def update_email_group(doctype, name): diff --git a/erpnext/schools/doctype/assessment_result/assessment_result.json b/erpnext/schools/doctype/assessment_result/assessment_result.json index c6b3c44579..13b927c367 100644 --- a/erpnext/schools/doctype/assessment_result/assessment_result.json +++ b/erpnext/schools/doctype/assessment_result/assessment_result.json @@ -410,7 +410,7 @@ "collapsible": 0, "columns": 0, "fieldname": "comment", - "fieldtype": "Long Text", + "fieldtype": "Small Text", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -474,7 +474,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-30 08:21:46.875594", + "modified": "2017-08-31 15:39:24.813328", "modified_by": "Administrator", "module": "Schools", "name": "Assessment Result", diff --git a/erpnext/schools/doctype/assessment_result/assessment_result.py b/erpnext/schools/doctype/assessment_result/assessment_result.py index c878ec301a..3c036dd56f 100644 --- a/erpnext/schools/doctype/assessment_result/assessment_result.py +++ b/erpnext/schools/doctype/assessment_result/assessment_result.py @@ -9,13 +9,18 @@ from frappe.utils import flt from frappe.model.document import Document from erpnext.schools.api import get_grade from erpnext.schools.api import get_assessment_details +from frappe.utils.csvutils import getlink + class AssessmentResult(Document): def validate(self): + if self.student and not self.student_name: + self.student_name = frappe.db.get_value("Student", self.student, "title") self.grading_scale = frappe.db.get_value("Assessment Plan", self.assessment_plan, "grading_scale") self.validate_maximum_score() self.validate_grade() - + self.validate_duplicate() + def validate_maximum_score(self): self.maximum_score = frappe.db.get_value("Assessment Plan", self.assessment_plan, "maximum_assessment_score") assessment_details = get_assessment_details(self.assessment_plan) @@ -34,3 +39,13 @@ class AssessmentResult(Document): d.grade = get_grade(self.grading_scale, (flt(d.score)/d.maximum_score)*100) self.total_score += d.score self.grade = get_grade(self.grading_scale, (self.total_score/self.maximum_score)*100) + + def validate_duplicate(self): + assessment_result = frappe.get_list("Assessment Result", filters={"name": ("not in", [self.name]), + "student":self.student, "assessment_plan":self.assessment_plan, "docstatus":("!=", 2)}) + if assessment_result: + frappe.throw(_("Assessment Result record {0} already exists.".format(getlink("Assessment Result",assessment_result[0].name)))) + + + + diff --git a/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.js b/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.js index a2eeceff25..dfa7b142f7 100644 --- a/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.js +++ b/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.js @@ -1,12 +1,13 @@ - // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -cur_frm.add_fetch("assessment_plan", "student_group", "student_group"); frappe.ui.form.on('Assessment Result Tool', { + setup: function(frm) { + frm.add_fetch("assessment_plan", "student_group", "student_group"); + }, + refresh: function(frm) { - frm.trigger("assessment_plan"); if (frappe.route_options) { frm.set_value("student_group", frappe.route_options.student_group); frm.set_value("assessment_plan", frappe.route_options.assessment_plan); @@ -14,98 +15,145 @@ frappe.ui.form.on('Assessment Result Tool', { } frm.disable_save(); frm.page.clear_indicator(); + frm.trigger("assessment_plan"); }, assessment_plan: function(frm) { - if(!frm.doc.student_group) return; - frappe.call({ - method: "erpnext.schools.api.get_assessment_students", - args: { - "assessment_plan": frm.doc.assessment_plan, - "student_group": frm.doc.student_group - }, - callback: function(r) { - frm.events.render_table(frm, r.message); - } - }); + frm.doc.show_submit = false; + if(frm.doc.assessment_plan) { + if (!frm.doc.student_group) + return + frappe.call({ + method: "erpnext.schools.api.get_assessment_students", + args: { + "assessment_plan": frm.doc.assessment_plan, + "student_group": frm.doc.student_group + }, + callback: function(r) { + frm.doc.students = r.message; + frm.events.render_table(frm); + for (let value of r.message) { + if (!value.docstatus) { + frm.doc.show_submit = true; + break; + } + } + frm.events.submit_result(frm); + } + }); + } }, - render_table: function(frm, students) { + render_table: function(frm) { $(frm.fields_dict.result_html.wrapper).empty(); - var assessment_plan = frm.doc.assessment_plan; - var student_scores = {}; - students.forEach(function(stu) { - student_scores[stu.student] = {} - }); - + let assessment_plan = frm.doc.assessment_plan; frappe.call({ method: "erpnext.schools.api.get_assessment_details", args: { assessment_plan: assessment_plan }, callback: function(r) { - var criteria_list = r.message; - var max_total_score = 0; - criteria_list.forEach(function(c) { - max_total_score += c.maximum_score - }); - var result_table = $(frappe.render_template('assessment_result_tool', { - frm: frm, - students: students, - criteria: criteria_list, - max_total_score: max_total_score - })); - result_table.appendTo(frm.fields_dict.result_html.wrapper) - - result_table.on('change', 'input', function(e) { - var $input = $(e.target); - var max_score = $input.data().maxScore; - var student = $input.data().student; - var criteria = $input.data().criteria; - var value = $input.val(); - if(value < 0) { - $input.val(0); - value = 0; - } - if(value > max_score) { - $input.val(max_score); - value = max_score; - } - student_scores[student][criteria] = value; - if(Object.keys(student_scores[student]).length == criteria_list.length) { - console.log("ok"); - frappe.call(({ - method: "erpnext.schools.api.mark_assessment_result", - args: { - "student": student, - "assessment_plan": assessment_plan, - "scores": student_scores[student] - }, - callback: function(r) { - var doc = r.message; - var student = doc.student; - result_table.find(`[data-student=${student}].total-score`) - .html(doc.total_score + ' ('+ doc.grade + ')'); - var details = doc.details; - result_table.find(`tr[data-student=${student}]`).addClass('text-muted'); - result_table.find(`input[data-student=${student}]`).each(function(el, input) { - var $input = $(input); - var criteria = $input.data().criteria; - var value = $input.val(); - var grade = details.find(function(d) { - return d.assessment_criteria === criteria; - }).grade; - $input.val(`${value} (${grade})`); - $input.attr('disabled', true); - }); - - } - })) - } - }); - + frm.events.get_marks(frm, r.message); } }); }, + get_marks: function(frm, criteria_list) { + let max_total_score = 0; + criteria_list.forEach(function(c) { + max_total_score += c.maximum_score + }); + var result_table = $(frappe.render_template('assessment_result_tool', { + frm: frm, + students: frm.doc.students, + criteria: criteria_list, + max_total_score: max_total_score + })); + result_table.appendTo(frm.fields_dict.result_html.wrapper); + + result_table.on('change', 'input', function(e) { + let $input = $(e.target); + let student = $input.data().student; + let max_score = $input.data().maxScore; + let value = $input.val(); + if(value < 0) { + $input.val(0); + } else if(value > max_score) { + $input.val(max_score); + } + let total_score = 0; + let student_scores = {}; + student_scores["assessment_details"] = {} + result_table.find(`input[data-student=${student}].student-result-data`) + .each(function(el, input) { + let $input = $(input); + let criteria = $input.data().criteria; + let value = parseFloat($input.val()); + if (value) { + student_scores["assessment_details"][criteria] = value; + } + total_score += value; + }); + if(!Number.isNaN(total_score)) { + result_table.find(`span[data-student=${student}].total-score`).html(total_score); + } + if (Object.keys(student_scores["assessment_details"]).length === criteria_list.length) { + student_scores["student"] = student; + student_scores["total_score"] = total_score; + result_table.find(`[data-student=${student}].result-comment`) + .each(function(el, input){ + student_scores["comment"] = $(input).val(); + }); + frappe.call({ + method: "erpnext.schools.api.mark_assessment_result", + args: { + "assessment_plan": frm.doc.assessment_plan, + "scores": student_scores + }, + callback: function(r) { + let assessment_result = r.message; + if (!frm.doc.show_submit) { + frm.doc.show_submit = true; + frm.events.submit_result; + } + for (var criteria of Object.keys(assessment_result.details)) { + result_table.find(`[data-criteria=${criteria}][data-student=${assessment_result + .student}].student-result-grade`).each(function(e1, input) { + $(input).html(assessment_result.details[criteria]); + }); + } + result_table.find(`span[data-student=${assessment_result.student}].total-score-grade`).html(assessment_result.grade); + let link_span = result_table.find(`span[data-student=${assessment_result.student}].total-result-link`); + $(link_span).css("display", "block"); + $(link_span).find("a").attr("href", "#Form/Assessment Result/"+assessment_result.name); + } + }); + } + }); + }, + + submit_result: function(frm) { + if (frm.doc.show_submit) { + frm.page.set_primary_action(__("Submit"), function() { + frappe.call({ + method: "erpnext.schools.api.submit_assessment_results", + args: { + "assessment_plan": frm.doc.assessment_plan, + "student_group": frm.doc.student_group + }, + callback: function(r) { + if (r.message) { + frappe.msgprint(__("{0} Result submittted", [r.message])); + } else { + frappe.msgprint(__("No Result to submit")); + } + frm.events.assessment_plan(frm); + } + }); + }); + } + else { + frm.page.clear_primary_action(); + } + } }); diff --git a/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.py b/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.py index a0d286ccbe..649f420d41 100644 --- a/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.py +++ b/erpnext/schools/doctype/assessment_result_tool/assessment_result_tool.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class AssessmentResultTool(Document): - pass + pass \ No newline at end of file