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
This commit is contained in:
Manas Solanki 2017-09-18 16:59:54 +05:30 committed by Rushabh Mehta
parent aebcb17daf
commit a22c94c246
6 changed files with 298 additions and 146 deletions

View File

@ -1,44 +1,72 @@
<table class="table table-bordered assessment-result-tool">
<thead>
<tr>
<th style="width: 100px" rowspan="2">Student</th>
<th style="width: 200px" rowspan="2">Student Name</th>
{% for c in criteria %}
<th class="score" style="width: 100px">{{ c.assessment_criteria }}</th>
{% endfor %}
<th class="score" style="width: 100px">Total Marks</th>
<!--criteria-->
</tr>
<tr>
{% for c in criteria %}
<th class="score" style="width: 100px">{{ c.maximum_score }}</th>
{% endfor %}
<th class="score" style="width: 100px">{{max_total_score}}</th>
</tr>
</thead>
<tbody>
{% for s in students %}
<tr
{% if(s.assessment_details) { %} class="text-muted" {% } %}
data-student="{{s.student}}">
<td>{{ s.student }}</td>
<td>{{ s.student_name }}</td>
{% for c in criteria %}
<td>
<input type="text"
data-max-score="{{c.maximum_score}}"
data-criteria="{{c.assessment_criteria}}"
data-student="{{s.student}}"
{% if(s.assessment_details) { %}
disabled
value="{{s.assessment_details[c.assessment_criteria]}}"
{% } %}/>
</td>
{% endfor %}
<td data-student="{{s.student}}" class="total-score">
{% if(s.assessment_details) { %} {{s.assessment_details.total_score}} {% } %}
</td>
</tr>
{% endfor %}
</tbody>
<thead>
<tr>
<th style="width: 90px" rowspan="2">Student</th>
<th style="width: 170px" rowspan="2">Student Name</th>
{% for c in criteria %}
<th class="score" style="width: 100px">{{ c.assessment_criteria }}</th>
{% endfor %}
<th class="score" style="width: 170px" rowspan="2">Comments</th>
<th class="score" style="width: 100px">Total Marks</th>
<!--criteria-->
</tr>
<tr>
{% for c in criteria %}
<th class="score" style="width: 100px">Score ({{ c.maximum_score }})</th>
{% endfor %}
<th class="score" style="width: 100px">Score ({{max_total_score}})</th>
</tr>
</thead>
<tbody>
{% for s in students %}
<tr
{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} class="text-muted" {% } %}
data-student="{{s.student}}">
<td>{{ s.student }}</td>
<td>{{ s.student_name }}</td>
{% for c in criteria %}
<td>
<span data-student="{{s.student}}" data-criteria="{{c.assessment_criteria}}" class="student-result-grade badge" >
{% if(s.assessment_details) { %}
{{s.assessment_details[c.assessment_criteria][1]}}
{% } %}
</span>
<input type="number" class="student-result-data" style="width:70%; float:right;"
data-max-score="{{c.maximum_score}}"
data-criteria="{{c.assessment_criteria}}"
data-student="{{s.student}}"
{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} disabled {% } %}
{% if(s.assessment_details) { %}
value="{{s.assessment_details[c.assessment_criteria][0]}}"
{% } %}/>
</td>
{% endfor %}
<td>
<input type="text" class="result-comment" data-student="{{s.student}}"
{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} disabled {% } %}
{% if(s.assessment_details) { %}
value="{{s.assessment_details.comment}}"
{% } %}
</td>
<td>
<span data-student="{{s.student}}" class="total-score-grade badge" style="width:30%; float:left;">
{% if(s.assessment_details) { %}
{{s.assessment_details.total_score[1]}}
{% } %}
</span>
<span data-student="{{s.student}}" class="total-score" style="width:60%; float:center;">
{% if(s.assessment_details) { %}
{{s.assessment_details.total_score[0]}}
{% } %}
</span>
<span data-student="{{s.student}}" class="total-result-link" style="width: 10%; display:{% if(!s.assessment_details) { %}None{% } %}; float:right;">
<a class="btn-open no-decoration" title="Open Link" href="#Form/Assessment Result/{% if(s.assessment_details) { %}{{s.name}}{% } %}">
<i class="octicon octicon-arrow-right"></i>
</a>
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>

View File

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

View File

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

View File

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

View File

@ -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();
}
}
});

View File

@ -7,4 +7,4 @@ import frappe
from frappe.model.document import Document
class AssessmentResultTool(Document):
pass
pass