feat(Education): added form dashboards and refactored custom buttons for better linking (#22727)

* feat: add form dashboards to all forms in Education Module

* feat: Add Course to Programs button in Course DocType

* refactor: custom buttons in Education module forms

* feat: buttons to add topic, article, quiz to their respective parent doctypes

* feat: add charts to form dashboards

* fix: code cleanup
This commit is contained in:
Rucha Mahabal 2020-07-23 15:57:27 +05:30 committed by GitHub
parent 2826fd3c20
commit 5142fc9365
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1249 additions and 699 deletions

View File

@ -0,0 +1,25 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'academic_term',
'transactions': [
{
'label': _('Student'),
'items': ['Student Applicant', 'Student Group', 'Student Log']
},
{
'label': _('Fee'),
'items': ['Fees', 'Fee Schedule', 'Fee Structure']
},
{
'label': _('Program'),
'items': ['Program Enrollment']
},
{
'label': _('Assessment'),
'items': ['Assessment Plan', 'Assessment Result']
}
]
}

View File

@ -1,10 +1,2 @@
frappe.ui.form.on("Academic Year", "refresh", function(frm) {
if(!frm.doc.__islocal) {
frm.add_custom_button(__("Student Group"), function() {
frappe.route_options = {
academic_year: frm.doc.name
}
frappe.set_route("List", "Student Group");
});
}
frappe.ui.form.on("Academic Year", {
});

View File

@ -0,0 +1,25 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'academic_year',
'transactions': [
{
'label': _('Student'),
'items': ['Student Admission', 'Student Applicant', 'Student Group', 'Student Log']
},
{
'label': _('Fee'),
'items': ['Fees', 'Fee Schedule', 'Fee Structure']
},
{
'label': _('Academic Term and Program'),
'items': ['Academic Term', 'Program Enrollment']
},
{
'label': _('Assessment'),
'items': ['Assessment Plan', 'Assessment Result']
}
]
}

View File

@ -3,6 +3,54 @@
frappe.ui.form.on('Article', {
refresh: function(frm) {
if (!frm.doc.__islocal) {
frm.add_custom_button(__('Add to Topics'), function() {
frm.trigger('add_article_to_topics');
}, __('Action'));
}
},
add_article_to_topics: function(frm) {
get_topics_without_article(frm.doc.name).then(r => {
if (r.message.length) {
frappe.prompt([
{
fieldname: 'topics',
label: __('Topics'),
fieldtype: 'MultiSelectPills',
get_data: function() {
return r.message;
}
}
],
function(data) {
frappe.call({
method: 'erpnext.education.doctype.topic.topic.add_content_to_topics',
args: {
'content_type': 'Article',
'content': frm.doc.name,
'topics': data.topics,
},
callback: function(r) {
if (!r.exc) {
frm.reload_doc();
}
},
freeze: true,
freeze_message: __('...Adding Article to Topics')
});
}, __('Add Article to Topics'), __('Add'));
} else {
frappe.msgprint(__('This article is already added to the existing topics'));
}
});
}
});
let get_topics_without_article = function(article) {
return frappe.call({
type: 'GET',
method: 'erpnext.education.doctype.article.article.get_topics_without_article',
args: {'article': article}
});
};

View File

@ -7,9 +7,15 @@ import frappe
from frappe.model.document import Document
class Article(Document):
def get_article(self):
pass
@frappe.whitelist()
def get_topics_without_article(article):
data = []
for entry in frappe.db.get_all('Topic'):
topic = frappe.get_doc('Topic', entry.name)
topic_contents = [tc.content for tc in topic.topic_content]
if not topic_contents or article not in topic_contents:
data.append(topic.name)
return data

View File

@ -0,0 +1,15 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'assessment_group',
'transactions': [
{
'label': _('Assessment'),
'items': ['Assessment Plan', 'Assessment Result']
}
]
}

View File

@ -2,9 +2,9 @@
// For license information, please see license.txt
frappe.ui.form.on("Assessment Plan", {
frappe.ui.form.on('Assessment Plan', {
onload: function(frm) {
frm.set_query("assessment_group", function(doc, cdt, cdn) {
frm.set_query('assessment_group', function(doc, cdt, cdn) {
return{
filters: {
'is_group': 0
@ -22,20 +22,20 @@ frappe.ui.form.on("Assessment Plan", {
refresh: function(frm) {
if (frm.doc.docstatus == 1) {
frm.add_custom_button(__("Assessment Result"), function() {
frm.add_custom_button(__('Assessment Result Tool'), function() {
frappe.route_options = {
assessment_plan: frm.doc.name,
student_group: frm.doc.student_group
}
frappe.set_route("Form", "Assessment Result Tool");
});
frappe.set_route('Form', 'Assessment Result Tool');
}, __('Tools'));
}
},
course: function(frm) {
if (frm.doc.course && frm.doc.maximum_assessment_score) {
frappe.call({
method: "erpnext.education.api.get_assessment_criteria",
method: 'erpnext.education.api.get_assessment_criteria',
args: {
course: frm.doc.course
},
@ -43,12 +43,12 @@ frappe.ui.form.on("Assessment Plan", {
if (r.message) {
frm.doc.assessment_criteria = [];
$.each(r.message, function(i, d) {
var row = frappe.model.add_child(frm.doc, "Assessment Plan Criteria", "assessment_criteria");
var row = frappe.model.add_child(frm.doc, 'Assessment Plan Criteria', 'assessment_criteria');
row.assessment_criteria = d.assessment_criteria;
row.maximum_score = d.weightage / 100 * frm.doc.maximum_assessment_score;
});
}
refresh_field("assessment_criteria");
refresh_field('assessment_criteria');
}
});
@ -56,6 +56,6 @@ frappe.ui.form.on("Assessment Plan", {
},
maximum_assessment_score: function(frm) {
frm.trigger("course");
frm.trigger('course');
}
});

View File

@ -6,12 +6,16 @@ from frappe import _
def get_data():
return {
'fieldname': 'assessment_plan',
'non_standard_fieldnames': {
},
'transactions': [
{
'label': _('Assessment'),
'items': ['Assessment Result']
}
],
'reports': [
{
'label': _('Report'),
'items': ['Assessment Plan Status']
}
]
}

View File

@ -1,7 +1,13 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Assessment Result", {
frappe.ui.form.on('Assessment Result', {
refresh: function(frm) {
if (!frm.doc.__islocal) {
frm.trigger('setup_chart');
}
},
onload: function(frm) {
frm.set_query('assessment_plan', function(){
return {
@ -15,7 +21,7 @@ frappe.ui.form.on("Assessment Result", {
assessment_plan: function(frm) {
if (frm.doc.assessment_plan) {
frappe.call({
method: "erpnext.education.api.get_assessment_details",
method: 'erpnext.education.api.get_assessment_details',
args: {
assessment_plan: frm.doc.assessment_plan
},
@ -23,40 +29,75 @@ frappe.ui.form.on("Assessment Result", {
if (r.message) {
frm.doc.details = [];
$.each(r.message, function(i, d) {
var row = frappe.model.add_child(frm.doc, "Assessment Result Detail", "details");
var row = frappe.model.add_child(frm.doc, 'Assessment Result Detail', 'details');
row.assessment_criteria = d.assessment_criteria;
row.maximum_score = d.maximum_score;
});
}
refresh_field("details");
refresh_field('details');
}
});
}
},
setup_chart: function(frm) {
let labels = [];
let maximum_scores = [];
let scores = [];
$.each(frm.doc.details, function(_i, e) {
labels.push(e.assessment_criteria);
maximum_scores.push(e.maximum_score);
scores.push(e.score);
});
if (labels.length && maximum_scores.length && scores.length) {
frm.dashboard.chart_area.empty().removeClass('hidden');
new frappe.Chart('.form-graph', {
title: 'Assessment Results',
data: {
labels: labels,
datasets: [
{
name: 'Maximum Score',
chartType: 'bar',
values: maximum_scores,
},
{
name: 'Score Obtained',
chartType: 'bar',
values: scores,
}
]
},
colors: ['#4CA746', '#98D85B'],
type: 'bar'
});
}
}
});
frappe.ui.form.on("Assessment Result Detail", {
frappe.ui.form.on('Assessment Result Detail', {
score: function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
if(!d.maximum_score || !frm.doc.grading_scale) {
d.score = "";
frappe.throw(__("Please fill in all the details to generate Assessment Result."));
d.score = '';
frappe.throw(__('Please fill in all the details to generate Assessment Result.'));
}
if (d.score > d.maximum_score) {
frappe.throw(__("Score cannot be greater than Maximum Score"));
frappe.throw(__('Score cannot be greater than Maximum Score'));
}
else {
frappe.call({
method: "erpnext.education.api.get_grade",
method: 'erpnext.education.api.get_grade',
args: {
grading_scale: frm.doc.grading_scale,
percentage: ((d.score/d.maximum_score) * 100)
},
callback: function(r) {
if (r.message) {
frappe.model.set_value(cdt, cdn, "grade", r.message);
frappe.model.set_value(cdt, cdn, 'grade', r.message);
}
}
});

View File

@ -0,0 +1,14 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'reports': [
{
'label': _('Reports'),
'items': ['Final Assessment Grades', 'Course wise Assessment Report']
}
]
}

View File

@ -1,41 +1,60 @@
frappe.ui.form.on("Course", "refresh", function(frm) {
if(!cur_frm.doc.__islocal) {
frm.add_custom_button(__("Program"), function() {
frappe.route_options = {
"Program Course.course": frm.doc.name
}
frappe.set_route("List", "Program");
});
frappe.ui.form.on('Course', {
refresh: function(frm) {
if (!cur_frm.doc.__islocal) {
frm.add_custom_button(__('Add to Programs'), function() {
frm.trigger('add_course_to_programs')
}, __('Action'));
}
frm.add_custom_button(__("Student Group"), function() {
frappe.route_options = {
course: frm.doc.name
frm.set_query('default_grading_scale', function(){
return {
filters: {
docstatus: 1
}
}
frappe.set_route("List", "Student Group");
});
},
frm.add_custom_button(__("Course Schedule"), function() {
frappe.route_options = {
course: frm.doc.name
add_course_to_programs: function(frm) {
get_programs_without_course(frm.doc.name).then(r => {
if (r.message.length) {
frappe.prompt([
{
fieldname: 'programs',
label: __('Programs'),
fieldtype: 'MultiSelectPills',
get_data: function() {
return r.message;
}
},
{
fieldtype: 'Check',
label: __('Is Mandatory'),
fieldname: 'mandatory',
}
],
function(data) {
frappe.call({
method: 'erpnext.education.doctype.course.course.add_course_to_programs',
args: {
'course': frm.doc.name,
'programs': data.programs,
'mandatory': data.mandatory
},
callback: function(r) {
if (!r.exc) {
frm.reload_doc();
}
},
freeze: true,
freeze_message: __('...Adding Course to Programs')
})
}, __('Add Course to Programs'), __('Add'));
} else {
frappe.msgprint(__('This course is already added to the existing programs'));
}
frappe.set_route("List", "Course Schedule");
});
frm.add_custom_button(__("Assessment Plan"), function() {
frappe.route_options = {
course: frm.doc.name
}
frappe.set_route("List", "Assessment Plan");
});
}
frm.set_query('default_grading_scale', function(){
return {
filters: {
docstatus: 1
}
}
});
});
frappe.ui.form.on('Course Topic', {
@ -50,3 +69,11 @@ frappe.ui.form.on('Course Topic', {
};
}
});
let get_programs_without_course = function(course) {
return frappe.call({
type: 'GET',
method: 'erpnext.education.doctype.course.course.get_programs_without_course',
args: {'course': course}
});
}

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
import json
from frappe.model.document import Document
from frappe import _
@ -17,12 +18,39 @@ class Course(Document):
for criteria in self.assessment_criteria:
total_weightage += criteria.weightage or 0
if total_weightage != 100:
frappe.throw(_("Total Weightage of all Assessment Criteria must be 100%"))
frappe.throw(_('Total Weightage of all Assessment Criteria must be 100%'))
def get_topics(self):
topic_data= []
for topic in self.topics:
topic_doc = frappe.get_doc("Topic", topic.topic)
topic_doc = frappe.get_doc('Topic', topic.topic)
if topic_doc.topic_content:
topic_data.append(topic_doc)
return topic_data
return topic_data
@frappe.whitelist()
def add_course_to_programs(course, programs, mandatory=False):
programs = json.loads(programs)
for entry in programs:
program = frappe.get_doc('Program', entry)
program.append('courses', {
'course': course,
'course_name': course,
'mandatory': mandatory
})
program.flags.ignore_mandatory = True
program.save()
frappe.db.commit()
frappe.msgprint(_('Course {0} has been added to all the selected programs successfully.').format(frappe.bold(course)),
title=_('Programs updated'), indicator='green')
@frappe.whitelist()
def get_programs_without_course(course):
data = []
for entry in frappe.db.get_all('Program'):
program = frappe.get_doc('Program', entry.name)
courses = [c.course for c in program.courses]
if not courses or course not in courses:
data.append(program.name)
return data

View File

@ -6,12 +6,10 @@ from frappe import _
def get_data():
return {
'fieldname': 'course',
'non_standard_fieldnames': {
},
'transactions': [
{
'label': _('Course'),
'items': ['Course Enrollment', 'Course Schedule']
'label': _('Program and Course'),
'items': ['Program', 'Course Enrollment', 'Course Schedule']
},
{
'label': _('Student'),
@ -19,7 +17,7 @@ def get_data():
},
{
'label': _('Assessment'),
'items': ['Assessment Plan']
'items': ['Assessment Plan', 'Assessment Result']
},
]
}

View File

@ -0,0 +1,15 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'enrollment',
'transactions': [
{
'label': _('Activity'),
'items': ['Course Activity', 'Quiz Activity']
}
]
}

View File

@ -4,13 +4,13 @@ cur_frm.add_fetch("student_group", "course", "course")
frappe.ui.form.on("Course Schedule", {
refresh: function(frm) {
if (!frm.doc.__islocal) {
frm.add_custom_button(__("Attendance"), function() {
frm.add_custom_button(__("Mark Attendance"), function() {
frappe.route_options = {
based_on: "Course Schedule",
course_schedule: frm.doc.name
}
frappe.set_route("Form", "Student Attendance Tool");
});
}).addClass("btn-primary");
}
}
});

View File

@ -0,0 +1,15 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'course_schedule',
'transactions': [
{
'label': _('Attendance'),
'items': ['Student Attendance']
}
]
}

View File

@ -3,13 +3,13 @@
frappe.ui.form.on('Fee Schedule', {
setup: function(frm) {
frm.add_fetch("fee_structure", "receivable_account", "receivable_account");
frm.add_fetch("fee_structure", "income_account", "income_account");
frm.add_fetch("fee_structure", "cost_center", "cost_center");
frm.add_fetch('fee_structure', 'receivable_account', 'receivable_account');
frm.add_fetch('fee_structure', 'income_account', 'income_account');
frm.add_fetch('fee_structure', 'cost_center', 'cost_center');
},
onload: function(frm) {
frm.set_query("receivable_account", function(doc) {
frm.set_query('receivable_account', function(doc) {
return {
filters: {
'account_type': 'Receivable',
@ -18,7 +18,8 @@ frappe.ui.form.on('Fee Schedule', {
}
};
});
frm.set_query("income_account", function(doc) {
frm.set_query('income_account', function(doc) {
return {
filters: {
'account_type': 'Income Account',
@ -27,57 +28,59 @@ frappe.ui.form.on('Fee Schedule', {
}
};
});
frm.set_query("student_group", "student_groups", function() {
frm.set_query('student_group', 'student_groups', function() {
return {
"program": frm.doc.program,
"academic_term": frm.doc.academic_term,
"academic_year": frm.doc.academic_year,
"disabled": 0
'program': frm.doc.program,
'academic_term': frm.doc.academic_term,
'academic_year': frm.doc.academic_year,
'disabled': 0
};
});
frappe.realtime.on("fee_schedule_progress", function(data) {
frappe.realtime.on('fee_schedule_progress', function(data) {
if (data.reload && data.reload === 1) {
frm.reload_doc();
}
if (data.progress) {
let progress_bar = $(cur_frm.dashboard.progress_area).find(".progress-bar");
let progress_bar = $(cur_frm.dashboard.progress_area).find('.progress-bar');
if (progress_bar) {
$(progress_bar).removeClass("progress-bar-danger").addClass("progress-bar-success progress-bar-striped");
$(progress_bar).css("width", data.progress+"%");
$(progress_bar).removeClass('progress-bar-danger').addClass('progress-bar-success progress-bar-striped');
$(progress_bar).css('width', data.progress+'%');
}
}
});
},
refresh: function(frm) {
if(!frm.doc.__islocal && frm.doc.__onload && frm.doc.__onload.dashboard_info &&
frm.doc.fee_creation_status=="Successful") {
if (!frm.doc.__islocal && frm.doc.__onload && frm.doc.__onload.dashboard_info &&
frm.doc.fee_creation_status === 'Successful') {
var info = frm.doc.__onload.dashboard_info;
frm.dashboard.add_indicator(__('Total Collected: {0}', [format_currency(info.total_paid,
info.currency)]), 'blue');
frm.dashboard.add_indicator(__('Total Outstanding: {0}', [format_currency(info.total_unpaid,
info.currency)]), info.total_unpaid ? 'orange' : 'green');
}
if (frm.doc.fee_creation_status=="In Process") {
frm.dashboard.add_progress("Fee Creation Status", "0");
if (frm.doc.fee_creation_status === 'In Process') {
frm.dashboard.add_progress('Fee Creation Status', '0');
}
if (frm.doc.docstatus==1 && !frm.doc.fee_creation_status || frm.doc.fee_creation_status == "Failed") {
if (frm.doc.docstatus === 1 && !frm.doc.fee_creation_status || frm.doc.fee_creation_status === 'Failed') {
frm.add_custom_button(__('Create Fees'), function() {
frappe.call({
method: "create_fees",
method: 'create_fees',
doc: frm.doc,
callback: function() {
frm.refresh();
}
});
}, "fa fa-play", "btn-success");
}).addClass('btn-primary');;
}
if (frm.doc.fee_creation_status == "Successful") {
frm.add_custom_button(__("View Fees Records"), function() {
if (frm.doc.fee_creation_status === 'Successful') {
frm.add_custom_button(__('View Fees Records'), function() {
frappe.route_options = {
fee_schedule: frm.doc.name
};
frappe.set_route("List", "Fees");
frappe.set_route('List', 'Fees');
});
}
@ -86,35 +89,35 @@ frappe.ui.form.on('Fee Schedule', {
fee_structure: function(frm) {
if (frm.doc.fee_structure) {
frappe.call({
method: "erpnext.education.doctype.fee_schedule.fee_schedule.get_fee_structure",
method: 'erpnext.education.doctype.fee_schedule.fee_schedule.get_fee_structure',
args: {
"target_doc": frm.doc.name,
"source_name": frm.doc.fee_structure
'target_doc': frm.doc.name,
'source_name': frm.doc.fee_structure
},
callback: function(r) {
var doc = frappe.model.sync(r.message);
frappe.set_route("Form", doc[0].doctype, doc[0].name);
frappe.set_route('Form', doc[0].doctype, doc[0].name);
}
});
}
}
});
frappe.ui.form.on("Fee Schedule Student Group", {
frappe.ui.form.on('Fee Schedule Student Group', {
student_group: function(frm, cdt, cdn) {
var row = locals[cdt][cdn];
if (row.student_group && frm.doc.academic_year) {
frappe.call({
method: "erpnext.education.doctype.fee_schedule.fee_schedule.get_total_students",
method: 'erpnext.education.doctype.fee_schedule.fee_schedule.get_total_students',
args: {
"student_group": row.student_group,
"academic_year": frm.doc.academic_year,
"academic_term": frm.doc.academic_term,
"student_category": frm.doc.student_category
'student_group': row.student_group,
'academic_year': frm.doc.academic_year,
'academic_term': frm.doc.academic_term,
'student_category': frm.doc.student_category
},
callback: function(r) {
if(!r.exc) {
frappe.model.set_value(cdt, cdn, "total_students", r.message);
if (!r.exc) {
frappe.model.set_value(cdt, cdn, 'total_students', r.message);
}
}
});

View File

@ -0,0 +1,13 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
def get_data():
return {
'fieldname': 'fee_schedule',
'transactions': [
{
'items': ['Fees']
}
]
}

View File

@ -3,21 +3,21 @@
frappe.ui.form.on('Fee Structure', {
setup: function(frm) {
frm.add_fetch("company", "default_receivable_account", "receivable_account");
frm.add_fetch("company", "default_income_account", "income_account");
frm.add_fetch("company", "cost_center", "cost_center");
frm.add_fetch('company', 'default_receivable_account', 'receivable_account');
frm.add_fetch('company', 'default_income_account', 'income_account');
frm.add_fetch('company', 'cost_center', 'cost_center');
},
onload: function(frm) {
frm.set_query("academic_term", function() {
frm.set_query('academic_term', function() {
return {
"filters": {
"academic_year": frm.doc.academic_year
'filters': {
'academic_year': frm.doc.academic_year
}
};
});
frm.set_query("receivable_account", function(doc) {
frm.set_query('receivable_account', function(doc) {
return {
filters: {
'account_type': 'Receivable',
@ -26,7 +26,7 @@ frappe.ui.form.on('Fee Structure', {
}
};
});
frm.set_query("income_account", function(doc) {
frm.set_query('income_account', function(doc) {
return {
filters: {
'account_type': 'Income Account',
@ -38,27 +38,27 @@ frappe.ui.form.on('Fee Structure', {
},
refresh: function(frm) {
if(frm.doc.docstatus === 1) {
if (frm.doc.docstatus === 1) {
frm.add_custom_button(__('Create Fee Schedule'), function() {
frm.events.make_fee_schedule(frm);
});
}).addClass('btn-primary');
}
},
make_fee_schedule: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.education.doctype.fee_structure.fee_structure.make_fee_schedule",
method: 'erpnext.education.doctype.fee_structure.fee_structure.make_fee_schedule',
frm: frm
});
}
});
frappe.ui.form.on("Fee Component", {
frappe.ui.form.on('Fee Component', {
amount: function(frm) {
var total_amount = 0;
for(var i=0;i<frm.doc.components.length;i++) {
for (var i=0;i<frm.doc.components.length;i++) {
total_amount += frm.doc.components[i].amount;
}
frm.set_value("total_amount", total_amount);
frm.set_value('total_amount', total_amount);
}
});

View File

@ -0,0 +1,15 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'fee_structure',
'transactions': [
{
'label': _('Fee'),
'items': ['Fees', 'Fee Schedule']
}
]
}

View File

@ -0,0 +1,20 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'grading_scale',
'non_standard_fieldnames': {
'Course': 'default_grading_scale'
},
'transactions': [
{
'label': _('Course'),
'items': ['Course']
},
{
'label': _('Assessment'),
'items': ['Assessment Plan', 'Assessment Result']
}
]
}

View File

@ -3,8 +3,8 @@ cur_frm.add_fetch("employee", "image", "image");
frappe.ui.form.on("Instructor", {
employee: function(frm) {
if(!frm.doc.employee) return;
frappe.db.get_value('Employee', {name: frm.doc.employee}, 'company', (d) => {
if (!frm.doc.employee) return;
frappe.db.get_value("Employee", {name: frm.doc.employee}, "company", (d) => {
frm.set_query("department", function() {
return {
"filters": {
@ -22,30 +22,16 @@ frappe.ui.form.on("Instructor", {
});
},
refresh: function(frm) {
if(!frm.doc.__islocal) {
frm.add_custom_button(__("Student Group"), function() {
frappe.route_options = {
instructor: frm.doc.name
}
frappe.set_route("List", "Student Group");
});
frm.add_custom_button(__("Course Schedule"), function() {
frappe.route_options = {
instructor: frm.doc.name
}
frappe.set_route("List", "Course Schedule");
});
if (!frm.doc.__islocal) {
frm.add_custom_button(__("As Examiner"), function() {
frappe.route_options = {
frappe.new_doc("Assessment Plan", {
examiner: frm.doc.name
}
frappe.set_route("List", "Assessment Plan");
});
}, __("Assessment Plan"));
frm.add_custom_button(__("As Supervisor"), function() {
frappe.route_options = {
frappe.new_doc("Assessment Plan", {
supervisor: frm.doc.name
}
frappe.set_route("List", "Assessment Plan");
});
}, __("Assessment Plan"));
}
frm.set_query("employee", function(doc) {

View File

@ -30,4 +30,14 @@ class Instructor(Document):
if self.employee and frappe.db.get_value("Instructor", {'employee': self.employee, 'name': ['!=', self.name]}, 'name'):
frappe.throw(_("Employee ID is linked with another instructor"))
def get_timeline_data(doctype, name):
"""Return timeline for course schedule"""
return dict(frappe.db.sql(
"""
SELECT unix_timestamp(`schedule_date`), count(*)
FROM `tabCourse Schedule`
WHERE
instructor=%s and
`schedule_date` > date_sub(curdate(), interval 1 year)
GROUP BY schedule_date
""", name))

View File

@ -0,0 +1,24 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'heatmap': True,
'heatmap_message': _('This is based on the course schedules of this Instructor'),
'fieldname': 'instructor',
'non_standard_fieldnames': {
'Assessment Plan': 'supervisor'
},
'transactions': [
{
'label': _('Course and Assessment'),
'items': ['Course Schedule', 'Assessment Plan']
},
{
'label': _('Students'),
'items': ['Student Group']
}
]
}

View File

@ -10,11 +10,15 @@ def get_data():
},
{
'label': _('Student Activity'),
'items': ['Student Group' ]
'items': ['Student Group', 'Student Log']
},
{
'label': _('Fee'),
'items': ['Fees','Fee Structure']
'items': ['Fees','Fee Structure', 'Fee Schedule']
},
{
'label': _('Assessment'),
'items': ['Assessment Plan', 'Assessment Result']
}
]
}

View File

@ -0,0 +1,19 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'program_enrollment',
'transactions': [
{
'label': _('Course and Fee'),
'items': ['Course Enrollment', 'Fees']
}
],
'reports': [
{
'label': _('Report'),
'items': ['Student and Guardian Contact Details']
}
]
}

View File

@ -3,11 +3,17 @@
frappe.ui.form.on('Quiz', {
refresh: function(frm) {
if (!frm.doc.__islocal) {
frm.add_custom_button(__('Add to Topics'), function() {
frm.trigger('add_quiz_to_topics');
}, __('Action'));
}
},
validate: function(frm){
frm.events.check_duplicate_question(frm.doc.question);
},
check_duplicate_question: function(questions_data){
var questions = [];
questions_data.forEach(function(q){
@ -15,7 +21,51 @@ frappe.ui.form.on('Quiz', {
});
var questions_set = new Set(questions);
if (questions.length != questions_set.size) {
frappe.throw(__("The question cannot be duplicate"));
frappe.throw(__('The question cannot be duplicate'));
}
},
add_quiz_to_topics: function(frm) {
get_topics_without_quiz(frm.doc.name).then(r => {
if (r.message.length) {
frappe.prompt([
{
fieldname: 'topics',
label: __('Topics'),
fieldtype: 'MultiSelectPills',
get_data: function() {
return r.message;
}
}
],
function(data) {
frappe.call({
method: 'erpnext.education.doctype.topic.topic.add_content_to_topics',
args: {
'content_type': 'Quiz',
'content': frm.doc.name,
'topics': data.topics,
},
callback: function(r) {
if (!r.exc) {
frm.reload_doc();
}
},
freeze: true,
freeze_message: __('...Adding Quiz to Topics')
});
}, __('Add Quiz to Topics'), __('Add'));
} else {
frappe.msgprint(__('This quiz is already added to the existing topics'));
}
});
}
});
});
let get_topics_without_quiz = function(quiz) {
return frappe.call({
type: 'GET',
method: 'erpnext.education.doctype.quiz.quiz.get_topics_without_quiz',
args: {'quiz': quiz}
});
};

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
import json
from frappe import _
from frappe.model.document import Document
@ -59,3 +60,12 @@ def compare_list_elementwise(*args):
except TypeError:
frappe.throw(_("Compare List function takes on list arguments"))
@frappe.whitelist()
def get_topics_without_quiz(quiz):
data = []
for entry in frappe.db.get_all('Topic'):
topic = frappe.get_doc('Topic', entry.name)
topic_contents = [tc.content for tc in topic.topic_content]
if not topic_contents or quiz not in topic_contents:
data.append(topic.name)
return data

View File

@ -1,10 +1,2 @@
frappe.ui.form.on("Room", "refresh", function(frm) {
if(!cur_frm.doc.__islocal) {
frm.add_custom_button(__("Course Schedule"), function() {
frappe.route_options = {
room: frm.doc.name
}
frappe.set_route("List", "Course Schedule");
});
}
frappe.ui.form.on("Room", {
});

View File

@ -0,0 +1,19 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'room',
'transactions': [
{
'label': _('Course'),
'items': ['Course Schedule']
},
{
'label': _('Assessment'),
'items': ['Assessment Plan']
}
]
}

View File

@ -0,0 +1,12 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'reports': [
{
'label': _('Reports'),
'items': ['Student Monthly Attendance Sheet', 'Student Batch-Wise Attendance']
}
]
}

View File

@ -0,0 +1,13 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'student_category',
'transactions': [
{
'label': _('Fee'),
'items': ['Fee Structure', 'Fee Schedule', 'Fees']
}
]
}

View File

@ -1,18 +1,18 @@
cur_frm.add_fetch("student", "title", "student_name");
cur_frm.add_fetch('student', 'title', 'student_name');
frappe.ui.form.on("Student Group", {
frappe.ui.form.on('Student Group', {
onload: function(frm) {
frm.set_query("academic_term", function() {
frm.set_query('academic_term', function() {
return {
"filters": {
"academic_year": (frm.doc.academic_year)
filters: {
'academic_year': (frm.doc.academic_year)
}
};
});
if (!frm.__islocal) {
frm.set_query("student", "students", function() {
frm.set_query('student', 'students', function() {
return{
query: "erpnext.education.doctype.student_group.student_group.fetch_students",
query: 'erpnext.education.doctype.student_group.student_group.fetch_students',
filters: {
'academic_year': frm.doc.academic_year,
'group_based_on': frm.doc.group_based_on,
@ -30,87 +30,86 @@ frappe.ui.form.on("Student Group", {
refresh: function(frm) {
if (!frm.doc.__islocal) {
frm.add_custom_button(__("Attendance"), function() {
frappe.route_options = {
based_on: "Student Group",
student_group: frm.doc.name
}
frappe.set_route("List", "Student Attendance Tool");
});
frm.add_custom_button(__("Course Schedule"), function() {
frappe.route_options = {
student_group: frm.doc.name
}
frappe.set_route("List", "Course Schedule");
});
frm.add_custom_button(__("Assessment Plan"), function() {
frappe.route_options = {
student_group: frm.doc.name
}
frappe.set_route("List", "Assessment Plan");
});
frm.add_custom_button(__("Update Email Group"), function() {
frm.add_custom_button(__('Add Guardians to Email Group'), function() {
frappe.call({
method: "erpnext.education.api.update_email_group",
method: 'erpnext.education.api.update_email_group',
args: {
"doctype": "Student Group",
"name": frm.doc.name
'doctype': 'Student Group',
'name': frm.doc.name
}
});
});
frm.add_custom_button(__("Newsletter"), function() {
}, __('Actions'));
frm.add_custom_button(__('Student Attendance Tool'), function() {
frappe.route_options = {
"Newsletter Email Group.email_group": frm.doc.name
based_on: 'Student Group',
student_group: frm.doc.name
}
frappe.set_route("List", "Newsletter");
});
frappe.set_route('Form', 'Student Attendance Tool', 'Student Attendance Tool');
}, __('Tools'));
frm.add_custom_button(__('Course Scheduling Tool'), function() {
frappe.route_options = {
student_group: frm.doc.name
}
frappe.set_route('Form', 'Course Scheduling Tool', 'Course Scheduling Tool');
}, __('Tools'));
frm.add_custom_button(__('Newsletter'), function() {
frappe.route_options = {
'Newsletter Email Group.email_group': frm.doc.name
}
frappe.set_route('List', 'Newsletter');
}, __('View'));
}
},
group_based_on: function(frm) {
if (frm.doc.group_based_on == "Batch") {
if (frm.doc.group_based_on == 'Batch') {
frm.doc.course = null;
frm.set_df_property('program', 'reqd', 1);
frm.set_df_property('course', 'reqd', 0);
}
else if (frm.doc.group_based_on == "Course") {
else if (frm.doc.group_based_on == 'Course') {
frm.set_df_property('program', 'reqd', 0);
frm.set_df_property('course', 'reqd', 1);
}
else if (frm.doc.group_based_on == "Activity") {
else if (frm.doc.group_based_on == 'Activity') {
frm.set_df_property('program', 'reqd', 0);
frm.set_df_property('course', 'reqd', 0);
}
},
get_students: function(frm) {
if (frm.doc.group_based_on == "Batch" || frm.doc.group_based_on == "Course") {
if (frm.doc.group_based_on == 'Batch' || frm.doc.group_based_on == 'Course') {
var student_list = [];
var max_roll_no = 0;
$.each(frm.doc.students, function(i,d) {
$.each(frm.doc.students, function(_i,d) {
student_list.push(d.student);
if (d.group_roll_number>max_roll_no) {
max_roll_no = d.group_roll_number;
}
});
if(frm.doc.academic_year) {
if (frm.doc.academic_year) {
frappe.call({
method: "erpnext.education.doctype.student_group.student_group.get_students",
method: 'erpnext.education.doctype.student_group.student_group.get_students',
args: {
"academic_year": frm.doc.academic_year,
"academic_term": frm.doc.academic_term,
"group_based_on": frm.doc.group_based_on,
"program": frm.doc.program,
"batch" : frm.doc.batch,
"student_category" : frm.doc.student_category,
"course": frm.doc.course
'academic_year': frm.doc.academic_year,
'academic_term': frm.doc.academic_term,
'group_based_on': frm.doc.group_based_on,
'program': frm.doc.program,
'batch' : frm.doc.batch,
'student_category' : frm.doc.student_category,
'course': frm.doc.course
},
callback: function(r) {
if(r.message) {
if (r.message) {
$.each(r.message, function(i, d) {
if(!in_list(student_list, d.student)) {
var s = frm.add_child("students");
var s = frm.add_child('students');
s.student = d.student;
s.student_name = d.student_name;
if (d.active === 0) {
@ -119,16 +118,16 @@ frappe.ui.form.on("Student Group", {
s.group_roll_number = ++max_roll_no;
}
});
refresh_field("students");
refresh_field('students');
frm.save();
} else {
frappe.msgprint(__("Student Group is already updated."))
frappe.msgprint(__('Student Group is already updated.'))
}
}
})
}
} else {
frappe.msgprint(__("Select students manually for the Activity based Group"));
frappe.msgprint(__('Select students manually for the Activity based Group'));
}
}
});

View File

@ -0,0 +1,19 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'student_group',
'transactions': [
{
'label': _('Assessment'),
'items': ['Assessment Plan', 'Assessment Result']
},
{
'label': _('Course'),
'items': ['Course Schedule']
}
]
}

View File

@ -3,6 +3,53 @@
frappe.ui.form.on('Topic', {
refresh: function(frm) {
if (!cur_frm.doc.__islocal) {
frm.add_custom_button(__('Add to Courses'), function() {
frm.trigger('add_topic_to_courses');
}, __('Action'));
}
},
add_topic_to_courses: function(frm) {
get_courses_without_topic(frm.doc.name).then(r => {
if (r.message.length) {
frappe.prompt([
{
fieldname: 'courses',
label: __('Courses'),
fieldtype: 'MultiSelectPills',
get_data: function() {
return r.message;
}
}
],
function(data) {
frappe.call({
method: 'erpnext.education.doctype.topic.topic.add_topic_to_courses',
args: {
'topic': frm.doc.name,
'courses': data.courses
},
callback: function(r) {
if (!r.exc) {
frm.reload_doc();
}
},
freeze: true,
freeze_message: __('...Adding Topic to Courses')
});
}, __('Add Topic to Courses'), __('Add'));
} else {
frappe.msgprint(__('This topic is already added to the existing courses'));
}
});
}
});
let get_courses_without_topic = function(topic) {
return frappe.call({
type: 'GET',
method: 'erpnext.education.doctype.topic.topic.get_courses_without_topic',
args: {'topic': topic}
});
};

View File

@ -4,6 +4,8 @@
from __future__ import unicode_literals
import frappe
import json
from frappe import _
from frappe.model.document import Document
class Topic(Document):
@ -14,4 +16,44 @@ class Topic(Document):
except Exception as e:
frappe.log_error(frappe.get_traceback())
return None
return content_data
return content_data
@frappe.whitelist()
def get_courses_without_topic(topic):
data = []
for entry in frappe.db.get_all('Course'):
course = frappe.get_doc('Course', entry.name)
topics = [t.topic for t in course.topics]
if not topics or topic not in topics:
data.append(course.name)
return data
@frappe.whitelist()
def add_topic_to_courses(topic, courses, mandatory=False):
courses = json.loads(courses)
for entry in courses:
course = frappe.get_doc('Course', entry)
course.append('topics', {
'topic': topic,
'topic_name': topic
})
course.flags.ignore_mandatory = True
course.save()
frappe.db.commit()
frappe.msgprint(_('Topic {0} has been added to all the selected courses successfully.').format(frappe.bold(topic)),
title=_('Courses updated'), indicator='green')
@frappe.whitelist()
def add_content_to_topics(content_type, content, topics):
topics = json.loads(topics)
for entry in topics:
topic = frappe.get_doc('Topic', entry)
topic.append('topic_content', {
'content_type': content_type,
'content': content,
})
topic.flags.ignore_mandatory = True
topic.save()
frappe.db.commit()
frappe.msgprint(_('{0} {1} has been added to all the selected topics successfully.').format(content_type, frappe.bold(content)),
title=_('Topics updated'), indicator='green')