diff --git a/erpnext/config/education.py b/erpnext/config/education.py index d5f9e2dca4..4efaaa65cd 100644 --- a/erpnext/config/education.py +++ b/erpnext/config/education.py @@ -33,6 +33,10 @@ def get_data(): "type": "doctype", "name": "Student Applicant" }, + { + "type": "doctype", + "name": "Web Academy Applicant" + }, { "type": "doctype", "name": "Student Admission" @@ -180,6 +184,10 @@ def get_data(): { "label": _("Masters"), "items": [ + { + "type": "doctype", + "name": "Program", + }, { "type": "doctype", "name": "Course", @@ -187,7 +195,7 @@ def get_data(): }, { "type": "doctype", - "name": "Program" + "name": "Topic", }, { "type": "doctype", @@ -201,6 +209,40 @@ def get_data(): } ] }, + { + "label": _("Content Masters"), + "items": [ + { + "type": "doctype", + "name": "Article" + }, + { + "type": "doctype", + "name": "Video" + }, + { + "type": "doctype", + "name": "Quiz" + } + ] + }, + { + "label": _("LMS Activity"), + "items": [ + { + "type": "doctype", + "name": "Course Enrollment" + }, + { + "type": "doctype", + "name": "Course Activity" + }, + { + "type": "doctype", + "name": "Quiz Activity" + } + ] + }, { "label": _("Settings"), "items": [ diff --git a/erpnext/domains/education.py b/erpnext/domains/education.py index c640576457..55e4eed801 100644 --- a/erpnext/domains/education.py +++ b/erpnext/domains/education.py @@ -14,7 +14,7 @@ data = { 'Student Attendance Tool', 'Student Applicant' ], - 'default_portal_role': 'Guardian', + 'default_portal_role': 'LMS User', 'restricted_roles': [ 'Student', 'Instructor', diff --git a/erpnext/education/api.py b/erpnext/education/api.py index 30d5588073..1a19716b50 100644 --- a/erpnext/education/api.py +++ b/erpnext/education/api.py @@ -38,7 +38,7 @@ def enroll_student(source_name): program_enrollment.student = student.name program_enrollment.student_name = student.title program_enrollment.program = frappe.db.get_value("Student Applicant", source_name, "program") - frappe.publish_realtime('enroll_student_progress', {"progress": [4, 4]}, user=frappe.session.user) + frappe.publish_realtime('enroll_student_progress', {"progress": [2, 4]}, user=frappe.session.user) return program_enrollment @@ -69,7 +69,7 @@ def mark_attendance(students_present, students_absent, course_schedule=None, stu present = json.loads(students_present) absent = json.loads(students_absent) - + for d in present: make_attendance_records(d["student"], d["student_name"], "Present", course_schedule, student_group, date) @@ -89,7 +89,7 @@ def make_attendance_records(student, student_name, status, course_schedule=None, :param status: Status (Present/Absent) """ student_attendance = frappe.get_doc({ - "doctype": "Student Attendance", + "doctype": "Student Attendance", "student": student, "course_schedule": course_schedule, "student_group": student_group, @@ -112,7 +112,7 @@ def get_student_guardians(student): :param student: Student. """ - guardians = frappe.get_list("Student Guardian", fields=["guardian"] , + guardians = frappe.get_list("Student Guardian", fields=["guardian"] , filters={"parent": student}) return guardians @@ -353,7 +353,7 @@ def update_email_group(doctype, name): for guard in get_student_guardians(stud.student): email = frappe.db.get_value("Guardian", guard.guardian, "email_address") if email: - email_list.append(email) + email_list.append(email) add_subscribers(name, email_list) @frappe.whitelist() diff --git a/erpnext/education/doctype/article/__init__.py b/erpnext/education/doctype/article/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/article/article.js b/erpnext/education/doctype/article/article.js new file mode 100644 index 0000000000..4c9c6f01f0 --- /dev/null +++ b/erpnext/education/doctype/article/article.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Article', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/article/article.json b/erpnext/education/doctype/article/article.json new file mode 100644 index 0000000000..c30cd189d6 --- /dev/null +++ b/erpnext/education/doctype/article/article.json @@ -0,0 +1,230 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "field:title", + "beta": 0, + "creation": "2018-10-17 05:45:38.471670", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Title", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "author", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Author", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Content", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "publish_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Publish Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 19:06:56.016865", + "modified_by": "Administrator", + "module": "Education", + "name": "Article", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/article/article.py b/erpnext/education/doctype/article/article.py new file mode 100644 index 0000000000..7dc850be37 --- /dev/null +++ b/erpnext/education/doctype/article/article.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class Article(Document): + + + def get_article(self): + pass + + diff --git a/erpnext/education/doctype/article/test_article.js b/erpnext/education/doctype/article/test_article.js new file mode 100644 index 0000000000..9dbf063e84 --- /dev/null +++ b/erpnext/education/doctype/article/test_article.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Article", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Article + () => frappe.tests.make('Article', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/article/test_article.py b/erpnext/education/doctype/article/test_article.py new file mode 100644 index 0000000000..2fce07f82c --- /dev/null +++ b/erpnext/education/doctype/article/test_article.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestArticle(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/content_activity/__init__.py b/erpnext/education/doctype/content_activity/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/content_activity/content_activity.json b/erpnext/education/doctype/content_activity/content_activity.json new file mode 100644 index 0000000000..b4c95dad9c --- /dev/null +++ b/erpnext/education/doctype/content_activity/content_activity.json @@ -0,0 +1,141 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-16 03:55:53.283893", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Content", + "length": 0, + "no_copy": 0, + "options": "Content", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "content.content_type", + "fieldname": "content_type", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Content Type", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "last_activity", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Last Activity ", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-16 03:55:58.202436", + "modified_by": "Administrator", + "module": "Education", + "name": "Content Activity", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/content_activity/content_activity.py b/erpnext/education/doctype/content_activity/content_activity.py new file mode 100644 index 0000000000..2ae7a5c94c --- /dev/null +++ b/erpnext/education/doctype/content_activity/content_activity.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class ContentActivity(Document): + pass diff --git a/erpnext/education/doctype/content_question/__init__.py b/erpnext/education/doctype/content_question/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/content_question/content_question.js b/erpnext/education/doctype/content_question/content_question.js new file mode 100644 index 0000000000..7615f5efcf --- /dev/null +++ b/erpnext/education/doctype/content_question/content_question.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Content Question', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/content_question/content_question.json b/erpnext/education/doctype/content_question/content_question.json new file mode 100644 index 0000000000..d390e8ef70 --- /dev/null +++ b/erpnext/education/doctype/content_question/content_question.json @@ -0,0 +1,76 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-15 14:35:40.728454", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "question_link", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Question Link", + "length": 0, + "no_copy": 0, + "options": "Question", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-15 14:41:31.729083", + "modified_by": "Administrator", + "module": "Education", + "name": "Content Question", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/content_question/content_question.py b/erpnext/education/doctype/content_question/content_question.py new file mode 100644 index 0000000000..b239d211a3 --- /dev/null +++ b/erpnext/education/doctype/content_question/content_question.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class ContentQuestion(Document): + pass diff --git a/erpnext/education/doctype/content_question/test_content_question.js b/erpnext/education/doctype/content_question/test_content_question.js new file mode 100644 index 0000000000..cc869a87fc --- /dev/null +++ b/erpnext/education/doctype/content_question/test_content_question.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Content Question", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Content Question + () => frappe.tests.make('Content Question', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/content_question/test_content_question.py b/erpnext/education/doctype/content_question/test_content_question.py new file mode 100644 index 0000000000..268b9be2e7 --- /dev/null +++ b/erpnext/education/doctype/content_question/test_content_question.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestContentQuestion(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/course/course.json b/erpnext/education/doctype/course/course.json index 15360f8d58..072e8b4afb 100644 --- a/erpnext/education/doctype/course/course.json +++ b/erpnext/education/doctype/course/course.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -15,10 +16,12 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "course_name", "fieldtype": "Data", "hidden": 0, @@ -41,14 +44,17 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "department", "fieldtype": "Link", "hidden": 0, @@ -72,14 +78,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "parent_course", "fieldtype": "Data", "hidden": 0, @@ -102,14 +111,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -131,14 +143,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "course_code", "fieldtype": "Data", "hidden": 0, @@ -161,14 +176,17 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "unique": 0 + "translatable": 0, + "unique": 1 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "course_abbreviation", "fieldtype": "Data", "hidden": 0, @@ -191,14 +209,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_6", "fieldtype": "Section Break", "hidden": 0, @@ -220,16 +241,53 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, + "fieldname": "topics", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Topics", + "length": 0, + "no_copy": 0, + "options": "Course Topic", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "course_intro", - "fieldtype": "Text Editor", + "fieldtype": "Small Text", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -250,14 +308,50 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, + "fieldname": "hero_image", + "fieldtype": "Attach Image", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Hero Image", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "assessment", "fieldtype": "Section Break", "hidden": 0, @@ -280,14 +374,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "default_grading_scale", "fieldtype": "Link", "hidden": 0, @@ -311,14 +408,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "assessment_criteria", "fieldtype": "Table", "hidden": 0, @@ -342,21 +442,20 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "idx": 0, - "image_view": 0, "in_create": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-11-10 19:06:28.909585", + "modified": "2019-04-09 11:35:27.354877", "modified_by": "Administrator", "module": "Education", "name": "Course", @@ -365,7 +464,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -385,7 +483,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -397,7 +494,7 @@ "print": 1, "read": 1, "report": 1, - "role": "HR Manager", + "role": "Instructor", "set_user_permissions": 0, "share": 1, "submit": 0, @@ -406,12 +503,12 @@ ], "quick_entry": 0, "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Education", "search_fields": "course_name", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 0, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/education/doctype/course/course.py b/erpnext/education/doctype/course/course.py index 69d2fca63e..987823a01e 100644 --- a/erpnext/education/doctype/course/course.py +++ b/erpnext/education/doctype/course/course.py @@ -10,7 +10,7 @@ from frappe import _ class Course(Document): def validate(self): self.validate_assessment_criteria() - + def validate_assessment_criteria(self): if self.assessment_criteria: total_weightage = 0 @@ -18,3 +18,11 @@ class Course(Document): total_weightage += criteria.weightage or 0 if total_weightage != 100: frappe.throw(_("Total Weightage of all Assessment Criteria must be 100%")) + + def get_topics(self): + try: + topic_list = self.get_all_children() + topic_data = [frappe.get_doc("Topic", topic.topic) for topic in topic_list] + except frappe.DoesNotExistError: + return None + return topic_data \ No newline at end of file diff --git a/erpnext/education/doctype/course/test_course.py b/erpnext/education/doctype/course/test_course.py index b18f4a94be..a24ba8a527 100644 --- a/erpnext/education/doctype/course/test_course.py +++ b/erpnext/education/doctype/course/test_course.py @@ -2,6 +2,7 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt from __future__ import unicode_literals +from erpnext.education.doctype.topic.test_topic import make_topic import frappe import unittest @@ -9,4 +10,35 @@ import unittest # test_records = frappe.get_test_records('Course') class TestCourse(unittest.TestCase): - pass + def setUp(self): + make_course_and_linked_topic("_Test Course 1", ["_Test Topic 1", "_Test Topic 2"]) + + def test_get_topics(self): + course = frappe.get_doc("Course", "_Test Course 1") + topics = course.get_topics() + self.assertEqual(topics[0].name, "_Test Topic 1") + self.assertEqual(topics[1].name, "_Test Topic 2") + frappe.db.rollback() + +def make_course(name): + try: + course = frappe.get_doc("Course", name) + except frappe.DoesNotExistError: + course = frappe.get_doc({ + "doctype": "Course", + "course_name": name, + "course_code": name + }).insert() + return course.name + +def make_course_and_linked_topic(course_name, topic_name_list): + try: + course = frappe.get_doc("Course", course_name) + except frappe.DoesNotExistError: + make_course(course_name) + course = frappe.get_doc("Course", course_name) + topic_list = [make_topic(topic_name) for topic_name in topic_name_list] + for topic in topic_list: + course.append("topics", {"topic": topic}) + course.save() + return course diff --git a/erpnext/education/doctype/course_activity/__init__.py b/erpnext/education/doctype/course_activity/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/course_activity/course_activity.js b/erpnext/education/doctype/course_activity/course_activity.js new file mode 100644 index 0000000000..5115fc415e --- /dev/null +++ b/erpnext/education/doctype/course_activity/course_activity.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Course Activity', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/course_activity/course_activity.json b/erpnext/education/doctype/course_activity/course_activity.json new file mode 100644 index 0000000000..99ae9aee56 --- /dev/null +++ b/erpnext/education/doctype/course_activity/course_activity.json @@ -0,0 +1,301 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "format:EDU-CA-{YYYY}-{#####}", + "beta": 1, + "creation": "2018-10-01 17:35:54.391413", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enrollment", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Enrollment", + "length": 0, + "no_copy": 0, + "options": "Course Enrollment", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "enrollment.course", + "fieldname": "course", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Course", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "enrollment.student", + "fieldname": "student", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Student", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content_type", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Content Type", + "length": 0, + "no_copy": 0, + "options": "\nArticle\nVideo", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content", + "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Content", + "length": 0, + "no_copy": 0, + "options": "content_type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "activity_date", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Activity Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-12-06 11:53:08.006123", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Activity", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 1, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/course_activity/course_activity.py b/erpnext/education/doctype/course_activity/course_activity.py new file mode 100644 index 0000000000..054b192097 --- /dev/null +++ b/erpnext/education/doctype/course_activity/course_activity.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document + +class CourseActivity(Document): + def validate(self): + self.check_if_enrolled() + + + def check_if_enrolled(self): + if frappe.db.exists("Course Enrollment", self.enrollment): + return True + else: + frappe.throw(_("Course Enrollment {0} does not exists".format(self.enrollment))) \ No newline at end of file diff --git a/erpnext/education/doctype/course_activity/test_course_activity.js b/erpnext/education/doctype/course_activity/test_course_activity.js new file mode 100644 index 0000000000..c89c89e5d3 --- /dev/null +++ b/erpnext/education/doctype/course_activity/test_course_activity.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Course Activity", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Course Activity + () => frappe.tests.make('Course Activity', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/course_activity/test_course_activity.py b/erpnext/education/doctype/course_activity/test_course_activity.py new file mode 100644 index 0000000000..5269a6b71a --- /dev/null +++ b/erpnext/education/doctype/course_activity/test_course_activity.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestCourseActivity(unittest.TestCase): + pass + +def make_course_activity(enrollment, content_type, content): + activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment, 'content_type': content_type, 'content': content}) + try: + activity = frappe.get_doc("Course Activity", activity[0]['name']) + except (IndexError, frappe.DoesNotExistError): + activity = frappe.get_doc({ + "doctype": "Course Activity", + "enrollment": enrollment, + "content_type": content_type, + "content": content, + "activity_date": frappe.utils.datetime.datetime.now() + }).insert() + return activity diff --git a/erpnext/education/doctype/course_content/__init__.py b/erpnext/education/doctype/course_content/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/course_content/course_content.js b/erpnext/education/doctype/course_content/course_content.js new file mode 100644 index 0000000000..b9faf99aaf --- /dev/null +++ b/erpnext/education/doctype/course_content/course_content.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Course Content', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/course_content/course_content.json b/erpnext/education/doctype/course_content/course_content.json new file mode 100644 index 0000000000..378e56005f --- /dev/null +++ b/erpnext/education/doctype/course_content/course_content.json @@ -0,0 +1,142 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-01 13:04:09.313771", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "", + "fieldname": "content_type", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Content Type", + "length": 0, + "no_copy": 0, + "options": "\nArticle\nVideo\nQuiz", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content", + "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Content", + "length": 0, + "no_copy": 0, + "options": "content_type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-17 07:36:04.029818", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Content", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/course_content/course_content.py b/erpnext/education/doctype/course_content/course_content.py new file mode 100644 index 0000000000..0d2f85ab50 --- /dev/null +++ b/erpnext/education/doctype/course_content/course_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class CourseContent(Document): + pass diff --git a/erpnext/education/doctype/course_content/test_course_content.js b/erpnext/education/doctype/course_content/test_course_content.js new file mode 100644 index 0000000000..786e67e9a3 --- /dev/null +++ b/erpnext/education/doctype/course_content/test_course_content.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Course Content", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Course Content + () => frappe.tests.make('Course Content', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/course_content/test_course_content.py b/erpnext/education/doctype/course_content/test_course_content.py new file mode 100644 index 0000000000..9be4b1f5ce --- /dev/null +++ b/erpnext/education/doctype/course_content/test_course_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestCourseContent(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/course_enrollment/__init__.py b/erpnext/education/doctype/course_enrollment/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.js b/erpnext/education/doctype/course_enrollment/course_enrollment.js new file mode 100644 index 0000000000..b5d3cc56e5 --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/course_enrollment.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Course Enrollment', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.json b/erpnext/education/doctype/course_enrollment/course_enrollment.json new file mode 100644 index 0000000000..6286ec1e66 --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/course_enrollment.json @@ -0,0 +1,233 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "format:EDU-CE-{YYYY}-{#####}", + "beta": 1, + "creation": "2018-10-15 15:35:39.375161", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "program_enrollment", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Program Enrollment", + "length": 0, + "no_copy": 0, + "options": "Program Enrollment", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "student", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "Student", + "length": 0, + "no_copy": 0, + "options": "Student", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "course", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Course", + "length": 0, + "no_copy": 0, + "options": "Course", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enrollment_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Enrollment Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 18:59:01.742377", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Enrollment", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.py b/erpnext/education/doctype/course_enrollment/course_enrollment.py new file mode 100644 index 0000000000..9508636d2f --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/course_enrollment.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document +from functools import reduce + +class CourseEnrollment(Document): + def get_progress(self, student): + """ + Returns Progress of given student for a particular course enrollment + + :param self: Course Enrollment Object + :param student: Student Object + """ + course = frappe.get_doc('Course', self.course) + topics = course.get_topics() + progress = [] + for topic in topics: + progress.append(student.get_topic_progress(self.name, topic)) + return reduce(lambda x,y: x+y, progress) # Flatten out the List + + def validate_duplication(self): + enrollment = frappe.get_all("Course Enrollment", filters={ + "student": self.student, + "course": self.course, + "program_enrollment": self.program_enrollment + }) + if enrollment: + frappe.throw(_("Student is already enrolled.")) + + def add_quiz_activity(self, quiz_name, quiz_response,answers, score, status): + result = {k: ('Correct' if v else 'Wrong') for k,v in answers.items()} + result_data = [] + for key in answers: + item = {} + item['question'] = key + item['quiz_result'] = result[key] + try: + if isinstance(quiz_response[key], list): + item['selected_option'] = ', '.join(frappe.get_value('Options', res, 'option') for res in quiz_response[key]) + else: + item['selected_option'] = frappe.get_value('Options', quiz_response[key], 'option') + except KeyError: + item['selected_option'] = "Unattempted" + result_data.append(item) + + quiz_activity = frappe.get_doc({ + "doctype": "Quiz Activity", + "enrollment": self.name, + "quiz": quiz_name, + "activity_date": frappe.utils.datetime.datetime.now(), + "result": result_data, + "score": score, + "status": status + }).insert() + + def add_activity(self, content_type, content): + if check_activity_exists(self.name, content_type, content): + pass + else: + activity = frappe.get_doc({ + "doctype": "Course Activity", + "enrollment": self.name, + "content_type": content_type, + "content": content, + "activity_date": frappe.utils.datetime.datetime.now() + }) + activity.insert() + +def check_activity_exists(enrollment, content_type, content): + activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment, 'content_type': content_type, 'content': content}) + return bool(activity) \ No newline at end of file diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.js b/erpnext/education/doctype/course_enrollment/test_course_enrollment.js new file mode 100644 index 0000000000..216cc30799 --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/test_course_enrollment.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Course Enrollment", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Course Enrollment + () => frappe.tests.make('Course Enrollment', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py new file mode 100644 index 0000000000..5ecace2a60 --- /dev/null +++ b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + + +from erpnext.education.doctype.student.test_student import create_student +from erpnext.education.doctype.student.test_student import get_student +from erpnext.education.doctype.program.test_program import setup_program +from erpnext.education.doctype.course_activity.test_course_activity import make_course_activity + +class TestCourseEnrollment(unittest.TestCase): + def setUp(self): + setup_program() + student = create_student({"first_name": "_Test First", "last_name": "_Test Last", "email": "_test_student_1@example.com"}) + program_enrollment = student.enroll_in_program("_Test Program") + course_enrollment = student.enroll_in_course("_Test Course 1", program_enrollment.name) + make_course_activity(course_enrollment.name, "Article", "_Test Article 1-1") + + def test_get_progress(self): + student = get_student("_test_student_1@example.com") + program_enrollment_name = frappe.get_list("Program Enrollment", filters={"student": student.name, "Program": "_Test Program"})[0].name + course_enrollment_name = frappe.get_list("Course Enrollment", filters={"student": student.name, "course": "_Test Course 1", "program_enrollment": program_enrollment_name})[0].name + course_enrollment = frappe.get_doc("Course Enrollment", course_enrollment_name) + progress = course_enrollment.get_progress(student) + finished = {'content': '_Test Article 1-1', 'content_type': 'Article', 'is_complete': True} + self.assertTrue(finished in progress) + frappe.db.rollback() + + + diff --git a/erpnext/education/doctype/course_topic/__init__.py b/erpnext/education/doctype/course_topic/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/course_topic/course_topic.js b/erpnext/education/doctype/course_topic/course_topic.js new file mode 100644 index 0000000000..7d03ba32ff --- /dev/null +++ b/erpnext/education/doctype/course_topic/course_topic.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Course Topic', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/course_topic/course_topic.json b/erpnext/education/doctype/course_topic/course_topic.json new file mode 100644 index 0000000000..3fcddc169e --- /dev/null +++ b/erpnext/education/doctype/course_topic/course_topic.json @@ -0,0 +1,109 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-12-12 11:51:25.952740", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "topic", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Topic", + "length": 0, + "no_copy": 0, + "options": "Topic", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "topic.topic_name", + "fieldname": "topic_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Topic Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-12-12 13:01:58.960425", + "modified_by": "Administrator", + "module": "Education", + "name": "Course Topic", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/course_topic/course_topic.py b/erpnext/education/doctype/course_topic/course_topic.py new file mode 100644 index 0000000000..2364f17a49 --- /dev/null +++ b/erpnext/education/doctype/course_topic/course_topic.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class CourseTopic(Document): + pass diff --git a/erpnext/education/doctype/course_topic/test_course_topic.js b/erpnext/education/doctype/course_topic/test_course_topic.js new file mode 100644 index 0000000000..d8d154fb9c --- /dev/null +++ b/erpnext/education/doctype/course_topic/test_course_topic.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Course Topic", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Course Topic + () => frappe.tests.make('Course Topic', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/course_topic/test_course_topic.py b/erpnext/education/doctype/course_topic/test_course_topic.py new file mode 100644 index 0000000000..7ce46d28ad --- /dev/null +++ b/erpnext/education/doctype/course_topic/test_course_topic.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestCourseTopic(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/education_settings/education_settings.json b/erpnext/education/doctype/education_settings/education_settings.json index c1eaa11284..3be4988d21 100644 --- a/erpnext/education/doctype/education_settings/education_settings.json +++ b/erpnext/education/doctype/education_settings/education_settings.json @@ -1,359 +1,492 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-04-05 13:33:04.519313", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-04-05 13:33:04.519313", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_academic_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Current Academic Year", - "length": 0, - "no_copy": 0, - "options": "Academic Year", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "current_academic_year", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Current Academic Year", + "length": 0, + "no_copy": 0, + "options": "Academic Year", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_academic_term", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Current Academic Term", - "length": 0, - "no_copy": 0, - "options": "Academic Term", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "current_academic_term", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Current Academic Term", + "length": 0, + "no_copy": 0, + "options": "Academic Term", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "attendance_freeze_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Attendance Freeze Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "attendance_freeze_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Attendance Freeze Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.", - "fieldname": "validate_batch", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Validate Batch for Students in Student Group", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.", + "fieldname": "validate_batch", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Validate Batch for Students in Student Group", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.", - "fieldname": "validate_course", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Validate Enrolled Course for Students in Student Group", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.", + "fieldname": "validate_course", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Validate Enrolled Course for Students in Student Group", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "description": "If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.", - "fieldname": "academic_term_reqd", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Make Academic Term Mandatory", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "description": "If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.", + "fieldname": "academic_term_reqd", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Make Academic Term Mandatory", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Full Name", - "fieldname": "instructor_created_by", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Instructor Records to be created by", - "length": 0, - "no_copy": 0, - "options": "Full Name\nNaming Series\nEmployee Number", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Full Name", + "fieldname": "instructor_created_by", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Instructor Records to be created by", + "length": 0, + "no_copy": 0, + "options": "Full Name\nNaming Series\nEmployee Number", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "web_academy_settings_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "LMS Settings", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "portal_title", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Portal Title", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-26 04:43:35.406690", - "modified_by": "Administrator", - "module": "Education", - "name": "Education Settings", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2018-12-11 15:49:15.045116", + "modified_by": "Administrator", + "module": "Education", + "name": "Education Settings", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Education Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Education Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Guest", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Education", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/education/doctype/options/__init__.py b/erpnext/education/doctype/options/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/options/options.json b/erpnext/education/doctype/options/options.json new file mode 100644 index 0000000000..59deab7837 --- /dev/null +++ b/erpnext/education/doctype/options/options.json @@ -0,0 +1,107 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-15 14:05:28.601274", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "option", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Option", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_correct", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Is Correct", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-15 14:16:18.303156", + "modified_by": "Administrator", + "module": "Education", + "name": "Options", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/options/options.py b/erpnext/education/doctype/options/options.py new file mode 100644 index 0000000000..a11d77afb2 --- /dev/null +++ b/erpnext/education/doctype/options/options.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class Options(Document): + pass diff --git a/erpnext/education/doctype/program/program.js b/erpnext/education/doctype/program/program.js index 5146a19322..98263b55a1 100644 --- a/erpnext/education/doctype/program/program.js +++ b/erpnext/education/doctype/program/program.js @@ -4,40 +4,5 @@ cur_frm.add_fetch('fee_structure', 'total_amount', 'amount'); frappe.ui.form.on("Program", "refresh", function(frm) { - if(!frm.doc.__islocal) { - frm.add_custom_button(__("Student Applicant"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Student Applicant"); - }); - - frm.add_custom_button(__("Program Enrollment"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Program Enrollment"); - }); - - frm.add_custom_button(__("Student Group"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Student Group"); - }); - - frm.add_custom_button(__("Fee Structure"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Fee Structure"); - }); - - frm.add_custom_button(__("Fees"), function() { - frappe.route_options = { - program: frm.doc.name - } - frappe.set_route("List", "Fees"); - }); - } + }); \ No newline at end of file diff --git a/erpnext/education/doctype/program/program.json b/erpnext/education/doctype/program/program.json index 05e35a2a53..cb8d7786e1 100644 --- a/erpnext/education/doctype/program/program.json +++ b/erpnext/education/doctype/program/program.json @@ -1,277 +1,627 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:program_code", - "beta": 0, - "creation": "2015-09-07 12:54:03.609282", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:program_code", + "beta": 0, + "creation": "2015-09-07 12:54:03.609282", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 0, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Program Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "program_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Program Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Program Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "program_code", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Program Code", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "program_abbreviation", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Program Abbreviation", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_abbreviation", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Program Abbreviation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fetch_if_empty": 0, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Portal Settings", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Course", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "courses", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Courses", + "length": 0, + "no_copy": 0, + "options": "Program Course", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "courses", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Courses", - "length": 0, - "no_copy": 0, - "options": "Program Course", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "intro_video", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Intro Video", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "hero_image", + "fieldtype": "Attach Image", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Hero Image", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_11", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fetch_if_empty": 0, + "fieldname": "is_published", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Is Published", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fetch_if_empty": 0, + "fieldname": "is_featured", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Is Featured", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2017-11-10 18:56:18.413911", - "modified_by": "Administrator", - "module": "Education", - "name": "Program", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "menu_index": 0, + "modified": "2019-03-18 15:26:56.737903", + "modified_by": "Administrator", + "module": "Education", + "name": "Program", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Guest", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Student", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "search_fields": "program_name", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "restrict_to_domain": "Education", + "route": "", + "search_fields": "program_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/education/doctype/program/program.py b/erpnext/education/doctype/program/program.py index f626880b2a..dbeda40775 100644 --- a/erpnext/education/doctype/program/program.py +++ b/erpnext/education/doctype/program/program.py @@ -7,4 +7,8 @@ import frappe from frappe.model.document import Document class Program(Document): - pass \ No newline at end of file + + def get_course_list(self): + program_course_list = self.get_all_children() + course_list = [frappe.get_doc("Course", program_course.course) for program_course in program_course_list] + return course_list \ No newline at end of file diff --git a/erpnext/education/doctype/program/program_dashboard.py b/erpnext/education/doctype/program/program_dashboard.py new file mode 100644 index 0000000000..cb8f74207e --- /dev/null +++ b/erpnext/education/doctype/program/program_dashboard.py @@ -0,0 +1,20 @@ +from frappe import _ + +def get_data(): + return { + 'fieldname': 'program', + 'transactions': [ + { + 'label': _('Admission and Enrollment'), + 'items': ['Student Applicant', 'Program Enrollment'] + }, + { + 'label': _('Student Activity'), + 'items': ['Student Group' ] + }, + { + 'label': _('Fee'), + 'items': ['Fees','Fee Structure'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/program/test_program.py b/erpnext/education/doctype/program/test_program.py index a4accdaaf7..3bcca3a7f1 100644 --- a/erpnext/education/doctype/program/test_program.py +++ b/erpnext/education/doctype/program/test_program.py @@ -2,11 +2,85 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt from __future__ import unicode_literals +from erpnext.education.doctype.course.test_course import make_course +from erpnext.education.doctype.topic.test_topic import make_topic_and_linked_content +from erpnext.education.doctype.course.test_course import make_course_and_linked_topic import frappe import unittest -# test_records = frappe.get_test_records('Program') +test_data = { + "program_name": "_Test Program", + "description": "_Test Program", + "course": [{ + "course_name": "_Test Course 1", + "topic": [{ + "topic_name": "_Test Topic 1-1", + "content": [{ + "type": "Article", + "name": "_Test Article 1-1" + }, { + "type": "Article", + "name": "_Test Article 1-2" + }] + }, + { + "topic_name": "_Test Topic 1-2", + "content": [{ + "type": "Article", + "name": "_Test Article 1-3" + }, { + "type": "Article", + "name": "_Test Article 1-4" + }] + } + ] + }] +} class TestProgram(unittest.TestCase): - pass + def setUp(self): + make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"]) + + def test_get_course_list(self): + program = frappe.get_doc("Program", "_Test Program 1") + course = program.get_course_list() + self.assertEqual(course[0].name, "_Test Course 1") + self.assertEqual(course[1].name, "_Test Course 2") + frappe.db.rollback() + +def make_program(name): + program = frappe.get_doc({ + "doctype": "Program", + "program_name": name, + "program_code": name, + "description": "_test description", + "is_published": True, + "is_featured": True, + }).insert() + return program.name + +def make_program_and_linked_courses(program_name, course_name_list): + try: + program = frappe.get_doc("Program", program_name) + except frappe.DoesNotExistError: + make_program(program_name) + program = frappe.get_doc("Program", program_name) + course_list = [make_course(course_name) for course_name in course_name_list] + for course in course_list: + program.append("courses", {"course": course}) + program.save() + return program + +def setup_program(): + topic_list = [course['topic'] for course in test_data['course']] + for topic in topic_list[0]: + make_topic_and_linked_content(topic['topic_name'], topic['content']) + + all_courses_list = [{'course': course['course_name'], 'topic': [topic['topic_name'] for topic in course['topic']]} for course in test_data['course']] # returns [{'course': 'Applied Math', 'topic': ['Trignometry', 'Geometry']}] + for course in all_courses_list: + make_course_and_linked_topic(course['course'], course['topic']) + + course_list = [course['course_name'] for course in test_data['course']] + program = make_program_and_linked_courses(test_data['program_name'], course_list) + return program \ No newline at end of file diff --git a/erpnext/education/doctype/program/test_records.json b/erpnext/education/doctype/program/test_records.json index e5eda70982..7901db3225 100644 --- a/erpnext/education/doctype/program/test_records.json +++ b/erpnext/education/doctype/program/test_records.json @@ -1,12 +1,14 @@ [ { - "program_name": "_Test Program", + "program_name": "_Test Program 1", "program_code": "_TP1", + "description": "Test Description", "program_abbreviation": "TP1" }, { "program_name": "_Test Program 2", "program_code": "_TP2", + "description": "Test Description", "program_abbreviation": "TP2" } -] +] \ No newline at end of file diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.json b/erpnext/education/doctype/program_enrollment/program_enrollment.json index 5f4621f368..1d8a4344a7 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.json +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, @@ -714,7 +715,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-08-21 16:15:35.014952", + "modified": "2018-11-07 21:13:06.502279", "modified_by": "Administrator", "module": "Education", "name": "Program Enrollment", @@ -739,6 +740,25 @@ "share": 1, "submit": 1, "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 } ], "quick_entry": 0, diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py index 320a58a924..22cca86fcf 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py @@ -8,6 +8,7 @@ from frappe import msgprint, _ from frappe.model.document import Document from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.utils import comma_and +import erpnext.www.lms as lms class ProgramEnrollment(Document): def validate(self): @@ -16,11 +17,12 @@ class ProgramEnrollment(Document): self.student_name = frappe.db.get_value("Student", self.student, "title") if not self.courses: self.extend("courses", self.get_courses()) - + def on_submit(self): self.update_student_joining_date() self.make_fee_records() - + self.create_course_enrollments() + def validate_duplication(self): enrollment = frappe.get_all("Program Enrollment", filters={ "student": self.student, @@ -31,11 +33,11 @@ class ProgramEnrollment(Document): }) if enrollment: frappe.throw(_("Student is already enrolled.")) - + def update_student_joining_date(self): date = frappe.db.sql("select min(enrollment_date) from `tabProgram Enrollment` where student= %s", self.student) frappe.db.set_value("Student", self.student, "joining_date", date) - + def make_fee_records(self): from erpnext.education.api import get_fee_components fee_list = [] @@ -54,7 +56,7 @@ class ProgramEnrollment(Document): "program_enrollment": self.name, "components": fee_components }) - + fees.save() fees.submit() fee_list.append(fees.name) @@ -66,6 +68,56 @@ class ProgramEnrollment(Document): def get_courses(self): return frappe.db.sql('''select course, course_name from `tabProgram Course` where parent = %s and required = 1''', (self.program), as_dict=1) + def create_course_enrollments(self): + student = frappe.get_doc("Student", self.student) + program = frappe.get_doc("Program", self.program) + course_list = [course.course for course in program.get_all_children()] + for course_name in course_list: + student.enroll_in_course(course_name=course_name, program_enrollment=self.name) + + def get_all_course_enrollments(self): + course_enrollment_names = frappe.get_list("Course Enrollment", filters={'program_enrollment': self.name}) + return [frappe.get_doc('Course Enrollment', course_enrollment.name) for course_enrollment in course_enrollment_names] + + def get_quiz_progress(self): + student = frappe.get_doc("Student", self.student) + quiz_progress = frappe._dict() + progress_list = [] + for course_enrollment in self.get_all_course_enrollments(): + course_progress = course_enrollment.get_progress(student) + for progress_item in course_progress: + if progress_item['content_type'] == "Quiz": + progress_item['course'] = course_enrollment.course + progress_list.append(progress_item) + if not progress_list: + return None + quiz_progress.quiz_attempt = progress_list + quiz_progress.name = self.program + quiz_progress.program = self.program + return quiz_progress + + def get_program_progress(self): + import math + program = frappe.get_doc("Program", self.program) + program_progress = {} + progress = [] + for course in program.get_all_children(): + course_progress = lms.get_student_course_details(course.course, self.program) + is_complete = False + if course_progress['flag'] == "Completed": + is_complete = True + progress.append({'course_name': course.course_name, 'name': course.course, 'is_complete': is_complete}) + + program_progress['progress'] = progress + program_progress['name'] = self.program + program_progress['program'] = frappe.get_value("Program", self.program, 'program_name') + + try: + program_progress['percentage'] = math.ceil((sum([item['is_complete'] for item in progress] * 100)/len(progress))) + except ZeroDivisionError: + program_progress['percentage'] = 0 + + return program_progress @frappe.whitelist() def get_program_courses(doctype, txt, searchfield, start, page_len, filters): @@ -102,11 +154,11 @@ def get_students(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select name, title from tabStudent - where + where name not in (%s) - and + and `%s` LIKE %s - order by + order by idx desc, name limit %s, %s"""%( ", ".join(['%s']*len(students)), searchfield, "%s", "%s", "%s"), diff --git a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py index c26e89920b..c6cbee1b75 100644 --- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py @@ -6,7 +6,21 @@ from __future__ import unicode_literals import frappe import unittest -# test_records = frappe.get_test_records('Program Enrollment') +from erpnext.education.doctype.student.test_student import create_student +from erpnext.education.doctype.student.test_student import get_student +from erpnext.education.doctype.program.test_program import make_program_and_linked_courses +from erpnext.education.doctype.course_activity.test_course_activity import make_course_activity class TestProgramEnrollment(unittest.TestCase): - pass + + def setUp(self): + create_student({"first_name": "_Test Name", "last_name": "_Test Last Name", "email": "_test_student@example.com"}) + make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"]) + + def test_create_course_enrollments(self): + student = get_student("_test_student@example.com") + enrollment = student.enroll_in_program("_Test Program 1") + course_enrollments = student.get_all_course_enrollments() + self.assertTrue("_Test Course 1" in course_enrollments.keys()) + self.assertTrue("_Test Course 2" in course_enrollments.keys()) + frappe.db.rollback() \ No newline at end of file diff --git a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py index db23ac744d..9f8f9f4dc0 100644 --- a/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py +++ b/erpnext/education/doctype/program_enrollment_tool/program_enrollment_tool.py @@ -48,7 +48,7 @@ class ProgramEnrollmentTool(Document): return students else: frappe.throw(_("No students Found")) - + def enroll_students(self): total = len(self.students) for i, stud in enumerate(self.students): diff --git a/erpnext/education/doctype/question/__init__.py b/erpnext/education/doctype/question/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/question/question.js b/erpnext/education/doctype/question/question.js new file mode 100644 index 0000000000..01b3091b58 --- /dev/null +++ b/erpnext/education/doctype/question/question.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Question', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/question/question.json b/erpnext/education/doctype/question/question.json new file mode 100644 index 0000000000..14a9f3ce92 --- /dev/null +++ b/erpnext/education/doctype/question/question.json @@ -0,0 +1,167 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "format:QUESTION-{#####}", + "beta": 0, + "creation": "2018-10-01 15:58:00.696815", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "question", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Question", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fetch_if_empty": 0, + "fieldname": "options", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Options", + "length": 0, + "no_copy": 0, + "options": "Options", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-04-22 14:02:08.140652", + "modified_by": "Administrator", + "module": "Education", + "name": "Question", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/question/question.py b/erpnext/education/doctype/question/question.py new file mode 100644 index 0000000000..8cd23983ce --- /dev/null +++ b/erpnext/education/doctype/question/question.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document + +class Question(Document): + + def validate(self): + self.check_at_least_one_option() + self.check_minimum_one_correct_answer() + + def check_at_least_one_option(self): + if len(self.options) <= 1: + frappe.throw(_("A question must have more than one options")) + else: + pass + + def check_minimum_one_correct_answer(self): + correct_options = [option.is_correct for option in self.options] + if bool(sum(correct_options)): + pass + else: + frappe.throw(_("A qustion must have at least one correct options")) + + def get_answer(self): + options = self.options + answers = [item.name for item in options if item.is_correct == True] + if len(answers) == 0: + frappe.throw("No correct answer is set for {0}".format(self.name)) + return None + elif len(answers) == 1: + return answers[0] + else: + return answers \ No newline at end of file diff --git a/erpnext/education/doctype/question/test_question.js b/erpnext/education/doctype/question/test_question.js new file mode 100644 index 0000000000..509939c6b5 --- /dev/null +++ b/erpnext/education/doctype/question/test_question.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Question", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Question + () => frappe.tests.make('Question', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/question/test_question.py b/erpnext/education/doctype/question/test_question.py new file mode 100644 index 0000000000..552872e15f --- /dev/null +++ b/erpnext/education/doctype/question/test_question.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestQuestion(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/quiz/__init__.py b/erpnext/education/doctype/quiz/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/quiz/quiz.js b/erpnext/education/doctype/quiz/quiz.js new file mode 100644 index 0000000000..122cf37818 --- /dev/null +++ b/erpnext/education/doctype/quiz/quiz.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Quiz', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/quiz/quiz.json b/erpnext/education/doctype/quiz/quiz.json new file mode 100644 index 0000000000..f91bc0f021 --- /dev/null +++ b/erpnext/education/doctype/quiz/quiz.json @@ -0,0 +1,299 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:title", + "beta": 0, + "creation": "2018-10-17 05:52:50.149904", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Title", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "question", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Question", + "length": 0, + "no_copy": 0, + "options": "Quiz Question", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "quiz_configuration_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Quiz Configuration", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "passing_score", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Passing Score", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "description": "Enter 0 to waive limit", + "fieldname": "max_attempts", + "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Max Attempts", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Last Highest Score", + "fieldname": "grading_basis", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Grading Basis", + "length": 0, + "no_copy": 0, + "options": "\nLast Attempt\nLast Highest Score", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 19:07:36.190116", + "modified_by": "Administrator", + "module": "Education", + "name": "Quiz", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/quiz/quiz.py b/erpnext/education/doctype/quiz/quiz.py new file mode 100644 index 0000000000..6da50a6e25 --- /dev/null +++ b/erpnext/education/doctype/quiz/quiz.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class Quiz(Document): + + def validate_quiz_attempts(self, enrollment, quiz_name): + if self.max_attempts > 0: + try: + if len(frappe.get_all("Quiz Activity", {'enrollment': enrollment.name, 'quiz': quiz_name})) >= self.max_attempts: + frappe.throw('Maximum attempts reached!') + except Exception as e: + pass + + + def evaluate(self, response_dict, quiz_name): + # self.validate_quiz_attempts(enrollment, quiz_name) + questions = [frappe.get_doc('Question', question.question_link) for question in self.question] + answers = {q.name:q.get_answer() for q in questions} + correct_answers = {} + for key in answers: + try: + if isinstance(response_dict[key], list): + result = compare_list_elementwise(response_dict[key], answers[key]) + else: + result = (response_dict[key] == answers[key]) + except: + result = False + correct_answers[key] = result + score = (sum(correct_answers.values()) * 100 ) / len(answers) + if score >= self.passing_score: + status = "Pass" + else: + status = "Fail" + return correct_answers, score, status + + + def get_questions(self): + quiz_question = self.get_all_children() + if quiz_question: + questions = [frappe.get_doc('Question', question.question_link).as_dict() for question in quiz_question] + for question in questions: + correct_options = [option.is_correct for option in question.options] + if sum(correct_options) > 1: + question['type'] = "MultipleChoice" + else: + question['type'] = "SingleChoice" + return questions + else: + return None + +def compare_list_elementwise(*args): + try: + if all(len(args[0]) == len(_arg) for _arg in args[1:]): + return all(all([element in (item) for element in args[0]]) for item in args[1:]) + else: + return False + except TypeError: + frappe.throw("Compare List function takes on list arguments") + diff --git a/erpnext/education/doctype/quiz/test_quiz.js b/erpnext/education/doctype/quiz/test_quiz.js new file mode 100644 index 0000000000..147d13952a --- /dev/null +++ b/erpnext/education/doctype/quiz/test_quiz.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Quiz", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Quiz + () => frappe.tests.make('Quiz', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/quiz/test_quiz.py b/erpnext/education/doctype/quiz/test_quiz.py new file mode 100644 index 0000000000..344fd544ee --- /dev/null +++ b/erpnext/education/doctype/quiz/test_quiz.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestQuiz(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/quiz_activity/__init__.py b/erpnext/education/doctype/quiz_activity/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.js b/erpnext/education/doctype/quiz_activity/quiz_activity.js new file mode 100644 index 0000000000..f6ba12ca4d --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/quiz_activity.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Quiz Activity', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.json b/erpnext/education/doctype/quiz_activity/quiz_activity.json new file mode 100644 index 0000000000..e78db42f7d --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/quiz_activity.json @@ -0,0 +1,490 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "format:EDU-QA-{YYYY}-{#####}", + "beta": 1, + "creation": "2018-10-15 15:48:40.482821", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enrollment", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Enrollment", + "length": 0, + "no_copy": 0, + "options": "Course Enrollment", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "enrollment.student", + "fieldname": "student", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Student", + "length": 0, + "no_copy": 0, + "options": "Student", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "enrollment.course", + "fieldname": "course", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Course", + "length": 0, + "no_copy": 0, + "options": "Course", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "quiz", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Quiz", + "length": 0, + "no_copy": 0, + "options": "Quiz", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "status", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Status", + "length": 0, + "no_copy": 0, + "options": "\nPass\nFail", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "result", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Result", + "length": 0, + "no_copy": 0, + "options": "Quiz Result", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "activity_date", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Activity Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "score", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Score", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 19:05:52.434437", + "modified_by": "Administrator", + "module": "Education", + "name": "Quiz Activity", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/quiz_activity/quiz_activity.py b/erpnext/education/doctype/quiz_activity/quiz_activity.py new file mode 100644 index 0000000000..24c7175397 --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/quiz_activity.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class QuizActivity(Document): + pass diff --git a/erpnext/education/doctype/quiz_activity/test_quiz_activity.js b/erpnext/education/doctype/quiz_activity/test_quiz_activity.js new file mode 100644 index 0000000000..94b5ab796a --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/test_quiz_activity.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Quiz Activity", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Quiz Activity + () => frappe.tests.make('Quiz Activity', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/quiz_activity/test_quiz_activity.py b/erpnext/education/doctype/quiz_activity/test_quiz_activity.py new file mode 100644 index 0000000000..fb0425d809 --- /dev/null +++ b/erpnext/education/doctype/quiz_activity/test_quiz_activity.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestQuizActivity(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/quiz_question/__init__.py b/erpnext/education/doctype/quiz_question/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/quiz_question/quiz_question.json b/erpnext/education/doctype/quiz_question/quiz_question.json new file mode 100644 index 0000000000..3857c5ca8d --- /dev/null +++ b/erpnext/education/doctype/quiz_question/quiz_question.json @@ -0,0 +1,110 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-17 06:13:00.098883", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "question_link", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Question Link", + "length": 0, + "no_copy": 0, + "options": "Question", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "question_link.question", + "fieldname": "question", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Question", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-10-18 15:35:12.195250", + "modified_by": "Administrator", + "module": "Education", + "name": "Quiz Question", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/quiz_question/quiz_question.py b/erpnext/education/doctype/quiz_question/quiz_question.py new file mode 100644 index 0000000000..317e75b2cb --- /dev/null +++ b/erpnext/education/doctype/quiz_question/quiz_question.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class QuizQuestion(Document): + pass diff --git a/erpnext/education/doctype/quiz_result/__init__.py b/erpnext/education/doctype/quiz_result/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/quiz_result/quiz_result.js b/erpnext/education/doctype/quiz_result/quiz_result.js new file mode 100644 index 0000000000..a018749e69 --- /dev/null +++ b/erpnext/education/doctype/quiz_result/quiz_result.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Quiz Result', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/quiz_result/quiz_result.json b/erpnext/education/doctype/quiz_result/quiz_result.json new file mode 100644 index 0000000000..86505ac756 --- /dev/null +++ b/erpnext/education/doctype/quiz_result/quiz_result.json @@ -0,0 +1,145 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-10-15 15:52:25.766374", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "question", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Question", + "length": 0, + "no_copy": 0, + "options": "Question", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "selected_option", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Selected Option", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "quiz_result", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Result", + "length": 0, + "no_copy": 0, + "options": "\nCorrect\nWrong", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2019-03-27 17:58:54.388848", + "modified_by": "Administrator", + "module": "Education", + "name": "Quiz Result", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/quiz_result/quiz_result.py b/erpnext/education/doctype/quiz_result/quiz_result.py new file mode 100644 index 0000000000..a4fd9f062f --- /dev/null +++ b/erpnext/education/doctype/quiz_result/quiz_result.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class QuizResult(Document): + pass diff --git a/erpnext/education/doctype/quiz_result/test_quiz_result.js b/erpnext/education/doctype/quiz_result/test_quiz_result.js new file mode 100644 index 0000000000..43f53a1dc7 --- /dev/null +++ b/erpnext/education/doctype/quiz_result/test_quiz_result.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Quiz Result", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Quiz Result + () => frappe.tests.make('Quiz Result', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/quiz_result/test_quiz_result.py b/erpnext/education/doctype/quiz_result/test_quiz_result.py new file mode 100644 index 0000000000..86ee52d87d --- /dev/null +++ b/erpnext/education/doctype/quiz_result/test_quiz_result.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestQuizResult(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/student/student.json b/erpnext/education/doctype/student/student.json index af6c8b16bc..bee915e91d 100644 --- a/erpnext/education/doctype/student/student.json +++ b/erpnext/education/doctype/student/student.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_1", "fieldtype": "Section Break", "hidden": 0, @@ -52,6 +54,7 @@ "collapsible": 0, "columns": 0, "default": "1", + "fetch_if_empty": 0, "fieldname": "enabled", "fieldtype": "Check", "hidden": 0, @@ -84,6 +87,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_3", "fieldtype": "Section Break", "hidden": 0, @@ -115,6 +119,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "first_name", "fieldtype": "Data", "hidden": 0, @@ -147,6 +152,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "middle_name", "fieldtype": "Data", "hidden": 0, @@ -179,6 +185,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "last_name", "fieldtype": "Data", "hidden": 0, @@ -211,6 +218,41 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, + "fieldname": "user", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "User ID", + "length": 0, + "no_copy": 0, + "options": "User", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_4", "fieldtype": "Column Break", "hidden": 0, @@ -243,6 +285,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", "hidden": 0, @@ -276,6 +319,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "student_email_id", "fieldtype": "Data", "hidden": 0, @@ -295,7 +339,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -308,6 +352,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "student_mobile_number", "fieldtype": "Data", "hidden": 0, @@ -342,6 +387,7 @@ "collapsible": 0, "columns": 0, "default": "Today", + "fetch_if_empty": 0, "fieldname": "joining_date", "fieldtype": "Date", "hidden": 0, @@ -374,6 +420,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "image", "fieldtype": "Attach Image", "hidden": 1, @@ -408,6 +455,7 @@ "collapsible": 0, "collapsible_depends_on": "", "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_7", "fieldtype": "Section Break", "hidden": 0, @@ -440,6 +488,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "date_of_birth", "fieldtype": "Date", "hidden": 0, @@ -472,6 +521,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "blood_group", "fieldtype": "Select", "hidden": 0, @@ -505,6 +555,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -536,6 +587,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "gender", "fieldtype": "Select", "hidden": 0, @@ -569,6 +621,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "nationality", "fieldtype": "Data", "hidden": 0, @@ -602,6 +655,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "student_applicant", "fieldtype": "Link", "hidden": 0, @@ -635,6 +689,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_22", "fieldtype": "Section Break", "hidden": 0, @@ -667,6 +722,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "address_line_1", "fieldtype": "Data", "hidden": 0, @@ -699,6 +755,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "address_line_2", "fieldtype": "Data", "hidden": 0, @@ -731,6 +788,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "pincode", "fieldtype": "Data", "hidden": 0, @@ -763,6 +821,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_20", "fieldtype": "Column Break", "hidden": 0, @@ -794,6 +853,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "city", "fieldtype": "Data", "hidden": 0, @@ -826,6 +886,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "state", "fieldtype": "Data", "hidden": 0, @@ -858,6 +919,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_18", "fieldtype": "Section Break", "hidden": 0, @@ -890,6 +952,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "guardians", "fieldtype": "Table", "hidden": 0, @@ -923,6 +986,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_20", "fieldtype": "Section Break", "hidden": 0, @@ -956,6 +1020,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "siblings", "fieldtype": "Table", "hidden": 0, @@ -989,6 +1054,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "exit", "fieldtype": "Section Break", "hidden": 0, @@ -1021,6 +1087,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "date_of_leaving", "fieldtype": "Date", "hidden": 0, @@ -1053,6 +1120,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "leaving_certificate_number", "fieldtype": "Data", "hidden": 0, @@ -1085,6 +1153,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_31", "fieldtype": "Column Break", "hidden": 0, @@ -1116,6 +1185,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "reason_for_leaving", "fieldtype": "Text", "hidden": 0, @@ -1149,6 +1219,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -1176,18 +1247,16 @@ } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_field": "image", - "image_view": 0, "in_create": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-08-21 14:44:35.278833", + "modified": "2019-04-10 17:46:26.893020", "modified_by": "Administrator", "module": "Education", "name": "Student", @@ -1231,11 +1300,48 @@ "share": 1, "submit": 0, "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Student", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 } ], "quick_entry": 0, "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Education", "show_name_in_global_search": 1, "sort_field": "modified", diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py index 53bf6f7273..cf8407c4ea 100644 --- a/erpnext/education/doctype/student/student.py +++ b/erpnext/education/doctype/student/student.py @@ -7,7 +7,7 @@ import frappe from frappe.model.document import Document from frappe import _ from frappe.desk.form.linked_with import get_linked_doctypes - +from erpnext.education.utils import check_content_completion, check_quiz_completion class Student(Document): def validate(self): self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) @@ -39,11 +39,99 @@ class Student(Document): if student: frappe.throw(_("Student {0} exist against student applicant {1}").format(student[0][0], self.student_applicant)) + def after_insert(self): + self.create_student_user() + + def create_student_user(self): + """Create a website user for student creation if not already exists""" + if not frappe.db.exists("User", self.student_email_id): + student_user = frappe.get_doc({ + 'doctype':'User', + 'first_name': self.first_name, + 'last_name': self.last_name, + 'email': self.student_email_id, + 'gender': self.gender, + 'send_welcome_email': 1, + 'user_type': 'Website User' + }) + student_user.add_roles("Student", "LMS User") + student_user.save() + update_password_link = student_user.reset_password() + def update_applicant_status(self): """Updates Student Applicant status to Admitted""" if self.student_applicant: frappe.db.set_value("Student Applicant", self.student_applicant, "application_status", "Admitted") + def get_all_course_enrollments(self): + """Returns a list of course enrollments linked with the current student""" + course_enrollments = frappe.get_all("Course Enrollment", filters={"student": self.name}, fields=['course', 'name']) + if not course_enrollments: + return None + else: + enrollments = {item['course']:item['name'] for item in course_enrollments} + return enrollments + + def get_program_enrollments(self): + """Returns a list of course enrollments linked with the current student""" + program_enrollments = frappe.get_all("Program Enrollment", filters={"student": self.name}, fields=['program']) + if not program_enrollments: + return None + else: + enrollments = [item['program'] for item in program_enrollments] + return enrollments + + def get_topic_progress(self, course_enrollment_name, topic): + """ + Get Progress Dictionary of a student for a particular topic + :param self: Student Object + :param course_enrollment_name: Name of the Course Enrollment + :param topic: Topic DocType Object + """ + contents = topic.get_contents() + progress = [] + for content in contents: + if content.doctype in ('Article', 'Video'): + status = check_content_completion(content.name, content.doctype, course_enrollment_name) + progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status}) + elif content.doctype == 'Quiz': + status, score, result = check_quiz_completion(content, course_enrollment_name) + progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status, 'score': score, 'result': result}) + return progress + + def enroll_in_program(self, program_name): + try: + enrollment = frappe.get_doc({ + "doctype": "Program Enrollment", + "student": self.name, + "academic_year": frappe.get_last_doc("Academic Year").name, + "program": program_name, + "enrollment_date": frappe.utils.datetime.datetime.now() + }) + enrollment.save(ignore_permissions=True) + except frappe.exceptions.ValidationError: + enrollment_name = frappe.get_list("Program Enrollment", filters={"student": self.name, "Program": program_name})[0].name + return frappe.get_doc("Program Enrollment", enrollment_name) + else: + enrollment.submit() + return enrollment + + def enroll_in_course(self, course_name, program_enrollment, enrollment_date=frappe.utils.datetime.datetime.now()): + try: + enrollment = frappe.get_doc({ + "doctype": "Course Enrollment", + "student": self.name, + "course": course_name, + "program_enrollment": program_enrollment, + "enrollment_date": enrollment_date + }) + enrollment.save(ignore_permissions=True) + except frappe.exceptions.ValidationError: + enrollment_name = frappe.get_list("Course Enrollment", filters={"student": self.name, "course": course_name, "program_enrollment": program_enrollment})[0].name + return frappe.get_doc("Program Enrollment", enrollment_name) + else: + return enrollment + def get_timeline_data(doctype, name): '''Return timeline for attendance''' return dict(frappe.db.sql('''select unix_timestamp(`date`), count(*) diff --git a/erpnext/education/doctype/student/student_dashboard.py b/erpnext/education/doctype/student/student_dashboard.py index d86f4f231c..0cbd17b8a4 100644 --- a/erpnext/education/doctype/student/student_dashboard.py +++ b/erpnext/education/doctype/student/student_dashboard.py @@ -9,7 +9,7 @@ def get_data(): 'transactions': [ { 'label': _('Admission'), - 'items': ['Program Enrollment'] + 'items': ['Program Enrollment', 'Course Enrollment'] }, { 'label': _('Student Activity'), @@ -19,6 +19,10 @@ def get_data(): 'label': _('Assessment'), 'items': ['Assessment Result'] }, + { + 'label': _('Student LMS Activity'), + 'items': ['Course Activity', 'Quiz Activity' ] + }, { 'label': _('Attendance'), 'items': ['Student Attendance', 'Student Leave Application'] diff --git a/erpnext/education/doctype/student/test_records.json b/erpnext/education/doctype/student/test_records.json index 6acc4b688f..8ad3afa6a8 100644 --- a/erpnext/education/doctype/student/test_records.json +++ b/erpnext/education/doctype/student/test_records.json @@ -6,6 +6,7 @@ "program": "TC101", "date_of_birth": "2000-01-01", "gender": "Male", + "student_email_id": "_test_student@example.com", "blood_group": "A+" }, @@ -16,6 +17,7 @@ "program": "TC101", "date_of_birth": "2000-01-01", "gender": "Male", + "student_email_id": "_test_student_1@example.com", "blood_group": "A+" }, @@ -25,27 +27,8 @@ "last_name": "Name 2", "program": "TC101", "date_of_birth": "2000-01-01", - "gender": "Male", - "blood_group": "A+" - - }, - { - "first_name": "_Test", - "middle_name": "Student", - "last_name": "Name 3", - "program": "TC101", - "date_of_birth": "2000-01-01", - "gender": "Male", - "blood_group": "A+" - - }, - { - "first_name": "_Test", - "middle_name": "Student", - "last_name": "Name 4", - "program": "TC101", - "date_of_birth": "2000-01-01", - "gender": "Male", + "gender": "Female", + "student_email_id": "_test_student_2@example.com", "blood_group": "A+" } diff --git a/erpnext/education/doctype/student/test_student.py b/erpnext/education/doctype/student/test_student.py index cc6537f865..8610edbf28 100644 --- a/erpnext/education/doctype/student/test_student.py +++ b/erpnext/education/doctype/student/test_student.py @@ -2,11 +2,60 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt from __future__ import unicode_literals +from frappe.test_runner import make_test_records +from erpnext.education.doctype.program.test_program import make_program_and_linked_courses +from erpnext.education.doctype.course.test_course import make_course import frappe import unittest -# test_records = frappe.get_test_records('Student') - +test_records = frappe.get_test_records('Student') class TestStudent(unittest.TestCase): - pass + def setUp(self): + create_student({"first_name": "_Test Name", "last_name": "_Test Last Name", "email": "_test_student@example.com"}) + make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"]) + + def test_create_student_user(self): + self.assertTrue(bool(frappe.db.exists("User", "_test_student@example.com"))) + frappe.db.rollback() + + def test_enroll_in_program(self): + student = get_student("_test_student@example.com") + enrollment = student.enroll_in_program("_Test Program 1") + test_enrollment = frappe.get_all("Program Enrollment", filters={"student": student.name, "Program": "_Test Program 1"}) + self.assertTrue(len(test_enrollment)) + self.assertEqual(test_enrollment[0]['name'], enrollment.name) + frappe.db.rollback() + + def test_get_program_enrollments(self): + student = get_student("_test_student@example.com") + enrollment = student.enroll_in_program("_Test Program 1") + program_enrollments = student.get_program_enrollments() + self.assertTrue("_Test Program 1" in program_enrollments) + frappe.db.rollback() + + def test_get_all_course_enrollments(self): + student = get_student("_test_student@example.com") + enrollment = student.enroll_in_program("_Test Program 1") + course_enrollments = student.get_all_course_enrollments() + self.assertTrue("_Test Course 1" in course_enrollments.keys()) + self.assertTrue("_Test Course 2" in course_enrollments.keys()) + frappe.db.rollback() + +def create_student(student_dict): + student = get_student(student_dict['email']) + if not student: + student = frappe.get_doc({ + "doctype": "Student", + "first_name": student_dict['first_name'], + "last_name": student_dict['last_name'], + "student_email_id": student_dict['email'] + }).insert() + return student + +def get_student(email): + try: + student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name + return frappe.get_doc("Student", student_id) + except IndexError: + return None \ No newline at end of file diff --git a/erpnext/education/doctype/student_applicant/student_applicant.json b/erpnext/education/doctype/student_applicant/student_applicant.json index 297821f753..71134e0907 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.json +++ b/erpnext/education/doctype/student_applicant/student_applicant.json @@ -143,6 +143,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "lms_only", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "LMS Only", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -627,7 +660,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -1160,7 +1193,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-08-21 14:44:30.146264", + "modified": "2018-10-05 13:15:59.283862", "modified_by": "Administrator", "module": "Education", "name": "Student Applicant", diff --git a/erpnext/education/doctype/topic/__init__.py b/erpnext/education/doctype/topic/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/topic/test_topic.js b/erpnext/education/doctype/topic/test_topic.js new file mode 100644 index 0000000000..4460b79478 --- /dev/null +++ b/erpnext/education/doctype/topic/test_topic.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Topic", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Topic + () => frappe.tests.make('Topic', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/topic/test_topic.py b/erpnext/education/doctype/topic/test_topic.py new file mode 100644 index 0000000000..d03db1cb93 --- /dev/null +++ b/erpnext/education/doctype/topic/test_topic.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestTopic(unittest.TestCase): + def setUp(self): + make_topic_and_linked_content("_Test Topic 1", [{"type":"Article", "name": "_Test Article 1"}]) + + def test_get_contents(self): + topic = frappe.get_doc("Topic", "_Test Topic 1") + contents = topic.get_contents() + self.assertEqual(contents[0].doctype, "Article") + self.assertEqual(contents[0].name, "_Test Article 1") + frappe.db.rollback() + +def make_topic(name): + try: + topic = frappe.get_doc("Topic", name) + except frappe.DoesNotExistError: + topic = frappe.get_doc({ + "doctype": "Topic", + "topic_name": name, + "topic_code": name, + }).insert() + return topic.name + +def make_topic_and_linked_content(topic_name, content_dict_list): + try: + topic = frappe.get_doc("Topic", topic_name) + except frappe.DoesNotExistError: + make_topic(topic_name) + topic = frappe.get_doc("Topic", topic_name) + content_list = [make_content(content['type'], content['name']) for content in content_dict_list] + for content in content_list: + topic.append("topic_content", {"content": content.title, "content_type": content.doctype}) + topic.save() + return topic + + +def make_content(type, name): + try: + content = frappe.get_doc(type, name) + except frappe.DoesNotExistError: + content = frappe.get_doc({"doctype": type, "title": name}).insert() + return content diff --git a/erpnext/education/doctype/topic/topic.js b/erpnext/education/doctype/topic/topic.js new file mode 100644 index 0000000000..695c17476c --- /dev/null +++ b/erpnext/education/doctype/topic/topic.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Topic', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/topic/topic.json b/erpnext/education/doctype/topic/topic.json new file mode 100644 index 0000000000..f47b10d780 --- /dev/null +++ b/erpnext/education/doctype/topic/topic.json @@ -0,0 +1,297 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:topic_code", + "beta": 0, + "creation": "2018-12-12 11:37:39.917760", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "topic_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "topic_code", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Code", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "topic_content", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Topic Content", + "length": 0, + "no_copy": 0, + "options": "Topic Content", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "hero_image", + "fieldtype": "Attach Image", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Hero Image", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "menu_index": 0, + "modified": "2019-04-09 11:35:34.137040", + "modified_by": "Administrator", + "module": "Education", + "name": "Topic", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/topic/topic.py b/erpnext/education/doctype/topic/topic.py new file mode 100644 index 0000000000..339fc7d887 --- /dev/null +++ b/erpnext/education/doctype/topic/topic.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class Topic(Document): + def get_contents(self): + try: + topic_content_list = self.get_all_children() + content_data = [frappe.get_doc(topic_content.content_type, topic_content.content) for topic_content in topic_content_list] + except Exception as e: + frappe.log_error(frappe.get_traceback()) + return None + return content_data \ No newline at end of file diff --git a/erpnext/education/doctype/topic_content/__init__.py b/erpnext/education/doctype/topic_content/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/topic_content/test_topic_content.js b/erpnext/education/doctype/topic_content/test_topic_content.js new file mode 100644 index 0000000000..bf9a62d037 --- /dev/null +++ b/erpnext/education/doctype/topic_content/test_topic_content.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Topic Content", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Topic Content + () => frappe.tests.make('Topic Content', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/topic_content/test_topic_content.py b/erpnext/education/doctype/topic_content/test_topic_content.py new file mode 100644 index 0000000000..cf304f60bc --- /dev/null +++ b/erpnext/education/doctype/topic_content/test_topic_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestTopicContent(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/topic_content/topic_content.js b/erpnext/education/doctype/topic_content/topic_content.js new file mode 100644 index 0000000000..9cda0cabb2 --- /dev/null +++ b/erpnext/education/doctype/topic_content/topic_content.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Topic Content', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/topic_content/topic_content.json b/erpnext/education/doctype/topic_content/topic_content.json new file mode 100644 index 0000000000..52207882e0 --- /dev/null +++ b/erpnext/education/doctype/topic_content/topic_content.json @@ -0,0 +1,140 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-12-12 11:42:57.987434", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content_type", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Content Type", + "length": 0, + "no_copy": 0, + "options": "\nArticle\nVideo\nQuiz", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "content", + "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Content", + "length": 0, + "no_copy": 0, + "options": "content_type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2018-12-12 11:46:46.112018", + "modified_by": "Administrator", + "module": "Education", + "name": "Topic Content", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/topic_content/topic_content.py b/erpnext/education/doctype/topic_content/topic_content.py new file mode 100644 index 0000000000..9b2c90bb4f --- /dev/null +++ b/erpnext/education/doctype/topic_content/topic_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class TopicContent(Document): + pass diff --git a/erpnext/education/doctype/video/__init__.py b/erpnext/education/doctype/video/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/education/doctype/video/test_video.js b/erpnext/education/doctype/video/test_video.js new file mode 100644 index 0000000000..a82a221319 --- /dev/null +++ b/erpnext/education/doctype/video/test_video.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Video", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Video + () => frappe.tests.make('Video', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/education/doctype/video/test_video.py b/erpnext/education/doctype/video/test_video.py new file mode 100644 index 0000000000..ecb09a2f9e --- /dev/null +++ b/erpnext/education/doctype/video/test_video.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestVideo(unittest.TestCase): + pass diff --git a/erpnext/education/doctype/video/video.js b/erpnext/education/doctype/video/video.js new file mode 100644 index 0000000000..c35c19b0c1 --- /dev/null +++ b/erpnext/education/doctype/video/video.js @@ -0,0 +1,8 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Video', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/education/doctype/video/video.json b/erpnext/education/doctype/video/video.json new file mode 100644 index 0000000000..cc8f718ba4 --- /dev/null +++ b/erpnext/education/doctype/video/video.json @@ -0,0 +1,262 @@ +{ + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "field:title", + "beta": 0, + "creation": "2018-10-17 05:47:13.087395", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Title", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "duration", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Duration", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "url", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "URL", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "publish_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Publish Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2018-11-25 19:07:17.134288", + "modified_by": "Administrator", + "module": "Education", + "name": "Video", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/education/doctype/video/video.py b/erpnext/education/doctype/video/video.py new file mode 100644 index 0000000000..b19f81258c --- /dev/null +++ b/erpnext/education/doctype/video/video.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class Video(Document): + + + def get_video(self): + pass diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py index 1b93c9d71e..bf766adc09 100644 --- a/erpnext/education/utils.py +++ b/erpnext/education/utils.py @@ -2,7 +2,7 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For lice -from __future__ import unicode_literals +from __future__ import unicode_literals, division import frappe from frappe import _ @@ -10,19 +10,19 @@ class OverlapError(frappe.ValidationError): pass def validate_overlap_for(doc, doctype, fieldname, value=None): """Checks overlap for specified field. - - :param fieldname: Checks Overlap for this field + + :param fieldname: Checks Overlap for this field """ - + existing = get_overlap_for(doc, doctype, fieldname, value) if existing: frappe.throw(_("This {0} conflicts with {1} for {2} {3}").format(doc.doctype, existing.name, doc.meta.get_label(fieldname) if not value else fieldname , value or doc.get(fieldname)), OverlapError) - + def get_overlap_for(doc, doctype, fieldname, value=None): """Returns overlaping document for specified field. - - :param fieldname: Checks Overlap for this field + + :param fieldname: Checks Overlap for this field """ existing = frappe.db.sql("""select name, from_time, to_time from `tab{0}` @@ -42,7 +42,8 @@ def get_overlap_for(doc, doctype, fieldname, value=None): }, as_dict=True) return existing[0] if existing else None - + + def validate_duplicate_student(students): unique_students= [] for stud in students: @@ -51,3 +52,93 @@ def validate_duplicate_student(students): .format(stud.student, stud.student_name, unique_students.index(stud.student)+1, stud.idx)) else: unique_students.append(stud.student) + + return None + +# LMS Utils +def get_current_student(): + """ + Returns student user name, example EDU-STU-2018-00001 (Based on the naming series). + Takes email from from frappe.session.user + """ + email = frappe.session.user + if email in ('Administrator', 'Guest'): + return None + try: + student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name + return frappe.get_doc("Student", student_id) + except (IndexError, frappe.DoesNotExistError): + return None + +def check_super_access(): + current_user = frappe.get_doc('User', frappe.session.user) + roles = set([role.role for role in current_user.roles]) + return bool(roles & {'Administrator', 'Instructor', 'Education Manager', 'System Manager', 'Academic User'}) + +def get_program_enrollment(program_name): + """ + Function to get program enrollments for a particular student for a program + """ + student = get_current_student() + if not student: + return None + else: + enrollment = frappe.get_all("Program Enrollment", filters={'student':student.name, 'program': program_name}) + if enrollment: + return enrollment[0].name + else: + return None + +def get_program_and_enrollment_status(program_name): + program = frappe.get_doc('Program', program_name) + is_enrolled = bool(get_program_enrollment(program_name)) or check_super_access() + return {'program': program, 'is_enrolled': is_enrolled} + +def get_course_enrollment(course_name): + student = get_current_student() + if not student: + return None + enrollment_name = frappe.get_all("Course Enrollment", filters={'student': student.name, 'course':course_name}) + try: + name = enrollment_name[0].name + enrollment = frappe.get_doc("Course Enrollment", name) + return enrollment + except: + return None + +def create_student_from_current_user(): + user = frappe.get_doc("User", frappe.session.user) + student = frappe.get_doc({ + "doctype": "Student", + "first_name": user.first_name, + "last_name": user.last_name, + "student_email_id": user.email, + "user": frappe.session.user + }) + student.save(ignore_permissions=True) + return student + +def enroll_in_course(course_name, program_name): + student = get_current_student() + return student.enroll_in_course(course_name=course_name, program_enrollment=get_program_enrollment(program_name)) + +def check_content_completion(content_name, content_type, enrollment_name): + activity = frappe.get_all("Course Activity", filters={'enrollment': enrollment_name, 'content_type': content_type, 'content': content_name}) + if activity: + return True + else: + return False + +def check_quiz_completion(quiz, enrollment_name): + attempts = frappe.get_all("Quiz Activity", filters={'enrollment': enrollment_name, 'quiz': quiz.name}, fields=["name", "activity_date", "score", "status"]) + status = False if quiz.max_attempts == 0 else bool(len(attempts) == quiz.max_attempts) + score = None + result = None + if attempts: + if quiz.grading_basis == 'Last Highest Score': + attempts = sorted(attempts, key = lambda i: int(i.score), reverse=True) + score = attempts[0]['score'] + result = attempts[0]['status'] + if result == 'Pass': + status = True + return status, score, result \ No newline at end of file diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 608e8b2225..ecc961186a 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -11,6 +11,7 @@ app_email = "info@erpnext.com" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" + develop_version = '12.x.x-develop' error_report_email = "support@erpnext.com" @@ -168,6 +169,10 @@ default_roles = [ {'role': 'Student', 'doctype':'Student', 'email_field': 'student_email_id'}, ] +role_home_page = { + "LMS User": "/lms" +} + has_website_permission = { "Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission", "Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission", diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 60e72dad71..45de6eb294 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -54,5 +54,8 @@ "stock/dashboard/item_dashboard.html", "stock/dashboard/item_dashboard_list.html", "stock/dashboard/item_dashboard.js" + ], + "js/lms.min.js": [ + "public/js/education/lms/lms.js" ] } diff --git a/erpnext/public/js/education/lms/call.js b/erpnext/public/js/education/lms/call.js new file mode 100644 index 0000000000..e35acbdd75 --- /dev/null +++ b/erpnext/public/js/education/lms/call.js @@ -0,0 +1,15 @@ +frappe.ready(() => { + frappe.provide('lms'); + + lms.call = (method, args) => { + const method_path = 'erpnext.www.lms.' + method; + return new Promise((resolve, reject) => { + return frappe.call({ + method: method_path, + args, + }) + .then(r => resolve(r.message)) + .fail(reject); + }); + }; +}); \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/Article.vue b/erpnext/public/js/education/lms/components/Article.vue new file mode 100644 index 0000000000..eab1424455 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Article.vue @@ -0,0 +1,44 @@ + + diff --git a/erpnext/public/js/education/lms/components/Breadcrumb.vue b/erpnext/public/js/education/lms/components/Breadcrumb.vue new file mode 100644 index 0000000000..e7c0fc34bb --- /dev/null +++ b/erpnext/public/js/education/lms/components/Breadcrumb.vue @@ -0,0 +1,49 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/Button.vue b/erpnext/public/js/education/lms/components/Button.vue new file mode 100644 index 0000000000..4d8df4b314 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Button.vue @@ -0,0 +1,25 @@ + + diff --git a/erpnext/public/js/education/lms/components/CardList.vue b/erpnext/public/js/education/lms/components/CardList.vue new file mode 100644 index 0000000000..298627f757 --- /dev/null +++ b/erpnext/public/js/education/lms/components/CardList.vue @@ -0,0 +1,28 @@ + + + diff --git a/erpnext/public/js/education/lms/components/ContentNavigation.vue b/erpnext/public/js/education/lms/components/ContentNavigation.vue new file mode 100644 index 0000000000..a07c0f85f4 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ContentNavigation.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/ContentTitle.vue b/erpnext/public/js/education/lms/components/ContentTitle.vue new file mode 100644 index 0000000000..a488ab85c3 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ContentTitle.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/CourseCard.vue b/erpnext/public/js/education/lms/components/CourseCard.vue new file mode 100644 index 0000000000..223654f27e --- /dev/null +++ b/erpnext/public/js/education/lms/components/CourseCard.vue @@ -0,0 +1,92 @@ + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/Navbar.vue b/erpnext/public/js/education/lms/components/Navbar.vue new file mode 100644 index 0000000000..f3f3ce4cbb --- /dev/null +++ b/erpnext/public/js/education/lms/components/Navbar.vue @@ -0,0 +1,85 @@ + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/ProfileInfo.vue b/erpnext/public/js/education/lms/components/ProfileInfo.vue new file mode 100644 index 0000000000..6f3e8f1266 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ProfileInfo.vue @@ -0,0 +1,85 @@ + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/ProgramCard.vue b/erpnext/public/js/education/lms/components/ProgramCard.vue new file mode 100644 index 0000000000..26d3882721 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ProgramCard.vue @@ -0,0 +1,82 @@ + + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/ProgressCard.vue b/erpnext/public/js/education/lms/components/ProgressCard.vue new file mode 100644 index 0000000000..77fb6ef5c1 --- /dev/null +++ b/erpnext/public/js/education/lms/components/ProgressCard.vue @@ -0,0 +1,89 @@ + + + diff --git a/erpnext/public/js/education/lms/components/Quiz.vue b/erpnext/public/js/education/lms/components/Quiz.vue new file mode 100644 index 0000000000..0a6199a756 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Quiz.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/Quiz/QuizMultipleChoice.vue b/erpnext/public/js/education/lms/components/Quiz/QuizMultipleChoice.vue new file mode 100644 index 0000000000..338b1ac0c5 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Quiz/QuizMultipleChoice.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/Quiz/QuizSingleChoice.vue b/erpnext/public/js/education/lms/components/Quiz/QuizSingleChoice.vue new file mode 100644 index 0000000000..235cbce4ae --- /dev/null +++ b/erpnext/public/js/education/lms/components/Quiz/QuizSingleChoice.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/erpnext/public/js/education/lms/components/ScoreCard.vue b/erpnext/public/js/education/lms/components/ScoreCard.vue new file mode 100644 index 0000000000..1cf53ef54e --- /dev/null +++ b/erpnext/public/js/education/lms/components/ScoreCard.vue @@ -0,0 +1,60 @@ + + + diff --git a/erpnext/public/js/education/lms/components/TopSection.vue b/erpnext/public/js/education/lms/components/TopSection.vue new file mode 100644 index 0000000000..c27d0031ef --- /dev/null +++ b/erpnext/public/js/education/lms/components/TopSection.vue @@ -0,0 +1,27 @@ + + + diff --git a/erpnext/public/js/education/lms/components/TopSectionButton.vue b/erpnext/public/js/education/lms/components/TopSectionButton.vue new file mode 100644 index 0000000000..0fa49d4da5 --- /dev/null +++ b/erpnext/public/js/education/lms/components/TopSectionButton.vue @@ -0,0 +1,49 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/TopicCard.vue b/erpnext/public/js/education/lms/components/TopicCard.vue new file mode 100644 index 0000000000..3e930df07b --- /dev/null +++ b/erpnext/public/js/education/lms/components/TopicCard.vue @@ -0,0 +1,112 @@ + + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/Video.vue b/erpnext/public/js/education/lms/components/Video.vue new file mode 100644 index 0000000000..27f922f487 --- /dev/null +++ b/erpnext/public/js/education/lms/components/Video.vue @@ -0,0 +1,64 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/VideoModal.vue b/erpnext/public/js/education/lms/components/VideoModal.vue new file mode 100644 index 0000000000..71227ade2c --- /dev/null +++ b/erpnext/public/js/education/lms/components/VideoModal.vue @@ -0,0 +1,35 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/components/YoutubePlayer.vue b/erpnext/public/js/education/lms/components/YoutubePlayer.vue new file mode 100644 index 0000000000..9377b57d3b --- /dev/null +++ b/erpnext/public/js/education/lms/components/YoutubePlayer.vue @@ -0,0 +1,36 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/lms.js b/erpnext/public/js/education/lms/lms.js new file mode 100644 index 0000000000..4665b144c2 --- /dev/null +++ b/erpnext/public/js/education/lms/lms.js @@ -0,0 +1,81 @@ +import Vue from 'vue/dist/vue.js'; +import VueRouter from 'vue-router/dist/vue-router.js'; +import moment from 'moment/min/moment.min.js'; + +import lmsRoot from "./lmsRoot.vue"; +import routes from './routes'; +import './call'; + +Vue.use(VueRouter); + +var store = { + enrolledPrograms: [], + enrolledCourses: [] +}; + +// let profile_page = ` LMS Profile ` +// document.querySelector('#website-post-login > ul').innerHTML += profile_page + +frappe.ready(() => { + frappe.provide('lms'); + + lms.moment = moment; + + lms.store = new Vue({ + data: store, + methods: { + updateEnrolledPrograms() { + if(this.checkLogin()) { + lms.call("get_program_enrollments").then(data => { + this.enrolledPrograms = data; + }); + } + }, + updateEnrolledCourses() { + if(this.checkLogin()) { + lms.call("get_all_course_enrollments").then(data => { + this.enrolledCourses = data; + }); + } + }, + checkLogin() { + return frappe.is_user_logged_in(); + }, + updateState() { + this.checkLogin(); + this.updateEnrolledPrograms(); + this.updateEnrolledCourses(); + }, + checkProgramEnrollment(programName) { + if(this.checkLogin()){ + if(this.enrolledPrograms) { + if(this.enrolledPrograms.includes(programName)) { + return true; + } + else { + return false; + } + } + else { + return false; + } + } + else { + return false; + } + } + } + }); + lms.view = new Vue({ + el: "#lms-app", + router: new VueRouter({ routes }), + template: "", + components: { lmsRoot }, + mounted() { + lms.store.updateState(); + } + }); + lms.view.$router.afterEach((to, from) => { + window.scrollTo(0,0); + }); +}); \ No newline at end of file diff --git a/erpnext/public/js/education/lms/lmsRoot.vue b/erpnext/public/js/education/lms/lmsRoot.vue new file mode 100644 index 0000000000..d359265c58 --- /dev/null +++ b/erpnext/public/js/education/lms/lmsRoot.vue @@ -0,0 +1,45 @@ + + + diff --git a/erpnext/public/js/education/lms/pages/ContentPage.vue b/erpnext/public/js/education/lms/pages/ContentPage.vue new file mode 100644 index 0000000000..542e937112 --- /dev/null +++ b/erpnext/public/js/education/lms/pages/ContentPage.vue @@ -0,0 +1,87 @@ + + + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/CoursePage.vue b/erpnext/public/js/education/lms/pages/CoursePage.vue new file mode 100644 index 0000000000..9aaf8a9858 --- /dev/null +++ b/erpnext/public/js/education/lms/pages/CoursePage.vue @@ -0,0 +1,49 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/Home.vue b/erpnext/public/js/education/lms/pages/Home.vue new file mode 100644 index 0000000000..569008674f --- /dev/null +++ b/erpnext/public/js/education/lms/pages/Home.vue @@ -0,0 +1,48 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/ListPage.vue b/erpnext/public/js/education/lms/pages/ListPage.vue new file mode 100644 index 0000000000..07681910e0 --- /dev/null +++ b/erpnext/public/js/education/lms/pages/ListPage.vue @@ -0,0 +1,53 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/ProfilePage.vue b/erpnext/public/js/education/lms/pages/ProfilePage.vue new file mode 100644 index 0000000000..c926463fcd --- /dev/null +++ b/erpnext/public/js/education/lms/pages/ProfilePage.vue @@ -0,0 +1,50 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/pages/ProgramPage.vue b/erpnext/public/js/education/lms/pages/ProgramPage.vue new file mode 100644 index 0000000000..2a136614df --- /dev/null +++ b/erpnext/public/js/education/lms/pages/ProgramPage.vue @@ -0,0 +1,49 @@ + + \ No newline at end of file diff --git a/erpnext/public/js/education/lms/routes.js b/erpnext/public/js/education/lms/routes.js new file mode 100644 index 0000000000..483f2220c3 --- /dev/null +++ b/erpnext/public/js/education/lms/routes.js @@ -0,0 +1,92 @@ +import Home from "./pages/Home.vue"; +import ProgramPage from "./pages/ProgramPage.vue"; +import CoursePage from "./pages/CoursePage.vue"; +import ContentPage from "./pages/ContentPage.vue"; +import ListPage from "./pages/ListPage.vue"; +import ProfilePage from "./pages/ProfilePage.vue"; + +const routes = [{ + name: 'home', + path: '', + component: Home +}, +{ + name: 'program', + path: '/Program/:program_name', + component: ProgramPage, + props: true +}, +{ + name: 'course', + path: '/Program/:program_name/:course_name/', + component: CoursePage, + props: true, +}, +{ + name: 'content', + path: '/Program/:program_name/:course_name/:topic/:type/:content', + component: ContentPage, + props: true, + beforeRouteUpdate (to, from, next) { + if (lms.store.checkProgramEnrollment(to.params.program_name)) { + next(); + } else { + next({ + name: 'program', + params: { + program_name: to.params.program_name + } + }); + } + } +}, +{ + name: 'list', + path: '/List/:master', + component: ListPage, + props: true +}, +{ + name: 'signup', + path: '/Signup', + beforeEnter(to, from, next) { + window.location = window.location.origin.toString() + '/login#signup'; + }, + component: Home, + props: true +}, +{ + name: 'login', + path: '/Login', + beforeEnter(to, from, next) { + window.location = window.location.origin.toString() + '/login#login'; + }, + component: Home, + props: true +}, +{ + name: 'logout', + path: '/Logout', + beforeEnter(to, from, next) { + window.location = window.location.origin.toString() + '/?cmd=web_logout'; + }, + component: Home, + props: true +}, +{ + name: 'profile', + path: '/Profile', + component: ProfilePage, + props: true, + beforeEnter: (to, from, next) => { + if (!lms.store.checkLogin()) { + next({ + name: 'home' + }); + } else { + next(); + } + } +}]; + +export default routes; \ No newline at end of file diff --git a/erpnext/www/lms.html b/erpnext/www/lms.html new file mode 100644 index 0000000000..1796194585 --- /dev/null +++ b/erpnext/www/lms.html @@ -0,0 +1,10 @@ +{% extends "templates/web.html" %} + +{% block title %}{{ heading or "LMS"}}{% endblock %} + +{% block navbar %}{% endblock %} + +{% block content %} +
+ +{% endblock %} \ No newline at end of file diff --git a/erpnext/www/lms.py b/erpnext/www/lms.py new file mode 100644 index 0000000000..7561d73d20 --- /dev/null +++ b/erpnext/www/lms.py @@ -0,0 +1,242 @@ +from __future__ import unicode_literals +import erpnext.education.utils as utils +import frappe +from frappe import _ + +# LMS Utils to Update State for Vue Store +@frappe.whitelist() +def get_program_enrollments(): + student = utils.get_current_student() + if student == None: + return None + return student.get_program_enrollments() + +@frappe.whitelist() +def get_all_course_enrollments(): + student = utils.get_current_student() + if student == None: + return None + return student.get_all_course_enrollments() + +# Vue Client Functions +@frappe.whitelist(allow_guest=True) +def get_portal_details(): + """ + Returns portal details from Education Settings Doctype. This contains the Title and Description for LMS amoung other things. + """ + from erpnext import get_default_company + + settings = frappe.get_doc("Education Settings") + title = settings.portal_title or get_default_company() + description = settings.description + return dict(title=title, description=description) + +@frappe.whitelist(allow_guest=True) +def get_featured_programs(): + featured_program_names = frappe.get_all("Program", filters={"is_published": True, "is_featured": True}) + if featured_program_names: + featured_list = [utils.get_program_and_enrollment_status(program['name']) for program in featured_program_names] + return featured_list + else: + return get_all_programs()[:2] + +@frappe.whitelist(allow_guest=True) +def get_all_programs(): + program_names = frappe.get_all("Program", filters={"is_published": True}) + if program_names: + program_list = [utils.get_program_and_enrollment_status(program['name']) for program in program_names] + return program_list + +@frappe.whitelist(allow_guest=True) +def get_program(program_name): + try: + return frappe.get_doc('Program', program_name) + except frappe.DoesNotExistError: + frappe.throw(_("Program {0} does not exist.".format(program_name))) + +# Functions to get program & course details +@frappe.whitelist(allow_guest=True) +def get_courses(program_name): + program = frappe.get_doc('Program', program_name) + courses = program.get_course_list() + return courses + +@frappe.whitelist() +def get_next_content(current_content, current_content_type, topic): + if frappe.session.user == "Guest": + return None + topic = frappe.get_doc("Topic", topic) + content_list = [{'content_type':item.doctype, 'content':item.name} for item in topic.get_contents()] + current_index = content_list.index({'content': current_content, 'content_type': current_content_type}) + try: + return content_list[current_index + 1] + except IndexError: + return None + +def get_quiz_with_answers(quiz_name): + try: + quiz = frappe.get_doc("Quiz", quiz_name).get_questions() + quiz_output = [{'name':question.name, 'question':question.question, 'options':[{'name': option.name, 'option':option.option, 'is_correct':option.is_correct} for option in question.options]} for question in quiz] + return quiz_output + except: + frappe.throw("Quiz {0} does not exist".format(quiz_name)) + return None + +@frappe.whitelist() +def get_quiz_without_answers(quiz_name, course_name): + try: + quiz = frappe.get_doc("Quiz", quiz_name) + questions = quiz.get_questions() + except: + frappe.throw("Quiz {0} does not exist".format(quiz_name)) + return None + + if utils.check_super_access(): + quiz_output = [{'name':question.name, 'question':question.question, 'type': question.type, 'options':[{'name': option.name, 'option':option.option} for option in question.options]} for question in questions] + return { 'quizData': quiz_output, 'status': None} + + enrollment = utils.get_course_enrollment(course_name).name + quiz_progress = {} + quiz_progress['is_complete'], quiz_progress['score'], quiz_progress['result'] = utils.check_quiz_completion(quiz, enrollment) + quiz_output = [{'name':question.name, 'question':question.question, 'type': question.type, 'options':[{'name': option.name, 'option':option.option} for option in question.options]} for question in questions] + return { 'quizData': quiz_output, 'status': quiz_progress} + +@frappe.whitelist() +def evaluate_quiz(course, quiz_response, quiz_name): + """LMS Function: Evaluates a simple multiple choice quiz. + :param course: name of the course + :param quiz_response: contains user selected choices for a quiz in the form of a string formatted as a dictionary. The function uses `json.loads()` to convert it to a python dictionary. + :param quiz_name: Name of the quiz attempted + """ + import json + quiz_response = json.loads(quiz_response) + quiz = frappe.get_doc("Quiz", quiz_name) + answers, score, status = quiz.evaluate(quiz_response, quiz_name) + print(answers) + + course_enrollment = utils.get_course_enrollment(course) + if course_enrollment: + course_enrollment.add_quiz_activity(quiz_name, quiz_response, answers, score, status) + + return score + +@frappe.whitelist() +def enroll_in_program(program_name): + student = utils.get_current_student() + if not student: + student = utils.create_student_from_current_user() + program_enrollment = student.enroll_in_program(program_name) + return program_name + +# Academdy Activity +@frappe.whitelist() +def add_activity(course, content_type, content): + if not utils.get_current_student(): + return + enrollment = utils.get_course_enrollment(course) + enrollment.add_activity(content_type, content) + +@frappe.whitelist() +def get_student_course_details(course_name, program_name): + """ + Return the porgress of a course in a program as well as the content to continue from. + :param course_name: + :param program_name: + """ + student = utils.get_current_student() + if not student: + return {'flag':'Start Course' } + + course_enrollment = utils.get_course_enrollment(course_name) + program_enrollment = utils.get_program_enrollment(program_name) + + if not program_enrollment: + return None + + if not course_enrollment: + course_enrollment = utils.enroll_in_course(course_name, program_name) + + progress = course_enrollment.get_progress(student) + count = sum([activity['is_complete'] for activity in progress]) + if count == 0: + return {'flag':'Start Course'} + elif count == len(progress): + return {'flag':'Completed'} + elif count < len(progress): + next_item = next(item for item in progress if item['is_complete']==False) + return {'flag':'Continue'} + +@frappe.whitelist() +def get_student_topic_details(topic_name, course_name): + """ + Return the porgress of a course in a program as well as the content to continue from. + :param topic_name: + :param course_name: + """ + topic = frappe.get_doc("Topic", topic_name) + student = utils.get_current_student() + if not student: + topic_content = topic.get_all_children() + if topic_content: + return {'flag':'Start Course', 'content_type': topic_content[0].content_type, 'content': topic_content[0].content} + else: + return None + course_enrollment = utils.get_course_enrollment(course_name) + progress = student.get_topic_progress(course_enrollment.name, topic) + if not progress: + return { 'flag':'Start Topic', 'content_type': None, 'content': None } + count = sum([activity['is_complete'] for activity in progress]) + if count == 0: + return {'flag':'Start Topic', 'content_type': progress[0]['content_type'], 'content': progress[0]['content']} + elif count == len(progress): + return {'flag':'Completed', 'content_type': progress[0]['content_type'], 'content': progress[0]['content']} + elif count < len(progress): + next_item = next(item for item in progress if item['is_complete']==False) + return {'flag':'Continue', 'content_type': next_item['content_type'], 'content': next_item['content']} + +@frappe.whitelist() +def get_program_progress(program_name): + program_enrollment = frappe.get_doc("Program Enrollment", utils.get_program_enrollment(program_name)) + if not program_enrollment: + return None + else: + return program_enrollment.get_program_progress() + +@frappe.whitelist() +def get_joining_date(): + student = utils.get_current_student() + if student: + return student.joining_date + +@frappe.whitelist() +def get_quiz_progress_of_program(program_name): + program_enrollment = frappe.get_doc("Program Enrollment", utils.get_program_enrollment(program_name)) + if not program_enrollment: + return None + else: + return program_enrollment.get_quiz_progress() + + +@frappe.whitelist(allow_guest=True) +def get_course_details(course_name): + try: + course = frappe.get_doc('Course', course_name) + return course + except: + return None + +# Functions to get program & course details +@frappe.whitelist(allow_guest=True) +def get_topics(course_name): + try: + course = frappe.get_doc('Course', course_name) + return course.get_topics() + except frappe.DoesNotExistError: + frappe.throw(_("Course {0} does not exist.".format(course_name))) + +@frappe.whitelist() +def get_content(content_type, content): + try: + return frappe.get_doc(content_type, content) + except frappe.DoesNotExistError: + frappe.throw(_("{0} {1} does not exist.".format(content_type, content))) \ No newline at end of file diff --git a/erpnext/www/test_lms.py b/erpnext/www/test_lms.py new file mode 100644 index 0000000000..e63f4c913e --- /dev/null +++ b/erpnext/www/test_lms.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals +from erpnext.education.doctype.program.test_program import make_program_and_linked_courses + +import frappe +import unittest + +class TestLms(unittest.TestCase): + pass \ No newline at end of file