-
diff --git a/erpnext/domains/education.py b/erpnext/domains/education.py
index 55e4eed801..bbaa6e55d9 100644
--- a/erpnext/domains/education.py
+++ b/erpnext/domains/education.py
@@ -14,7 +14,7 @@ data = {
'Student Attendance Tool',
'Student Applicant'
],
- 'default_portal_role': 'LMS User',
+ 'default_portal_role': 'Student',
'restricted_roles': [
'Student',
'Instructor',
diff --git a/erpnext/education/doctype/course/course.json b/erpnext/education/doctype/course/course.json
index 072e8b4afb..7d8b07397e 100644
--- a/erpnext/education/doctype/course/course.json
+++ b/erpnext/education/doctype/course/course.json
@@ -1,514 +1,135 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:course_code",
- "beta": 0,
- "creation": "2015-09-07 12:39:55.181893",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:course_code",
+ "creation": "2015-09-07 12:39:55.181893",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "course_name",
+ "department",
+ "parent_course",
+ "column_break_3",
+ "course_code",
+ "course_abbreviation",
+ "section_break_6",
+ "topics",
+ "description",
+ "hero_image",
+ "assessment",
+ "default_grading_scale",
+ "assessment_criteria"
+ ],
"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,
- "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 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
- },
+ "fieldname": "course_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Course Name",
+ "reqd": 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": "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
- },
+ "fieldname": "department",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Department",
+ "options": "Department"
+ },
{
- "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,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Parent Course (Leave blank, if this isn't part of Parent 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,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "parent_course",
+ "fieldtype": "Data",
+ "label": "Parent Course (Leave blank, if this isn't part of Parent Course)"
+ },
{
- "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
- },
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
{
- "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,
- "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 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,
+ "fieldname": "course_code",
+ "fieldtype": "Data",
+ "label": "Course Code",
+ "reqd": 1,
"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,
- "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 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
- },
+ "fieldname": "course_abbreviation",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Course Abbreviation"
+ },
{
- "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,
- "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
- },
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break"
+ },
{
- "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
- },
+ "fieldname": "topics",
+ "fieldtype": "Table",
+ "label": "Topics",
+ "options": "Course Topic"
+ },
{
- "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": "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": "Course Intro",
- "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
- },
+ "fieldname": "hero_image",
+ "fieldtype": "Attach Image",
+ "label": "Hero Image"
+ },
{
- "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
- },
+ "fieldname": "assessment",
+ "fieldtype": "Section Break",
+ "label": "Assessment"
+ },
{
- "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,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Assessment",
- "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
- },
+ "fieldname": "default_grading_scale",
+ "fieldtype": "Link",
+ "label": "Default Grading Scale",
+ "options": "Grading Scale"
+ },
{
- "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,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Default Grading Scale",
- "length": 0,
- "no_copy": 0,
- "options": "Grading Scale",
- "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
- },
+ "fieldname": "assessment_criteria",
+ "fieldtype": "Table",
+ "label": "Assessment Criteria",
+ "options": "Course Assessment Criteria"
+ },
{
- "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,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Assessment Criteria",
- "length": 0,
- "no_copy": 0,
- "options": "Course Assessment Criteria",
- "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
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "label": "Description"
}
- ],
- "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:27.354877",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "modified": "2019-06-05 18:39:11.870605",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Course",
+ "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,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Academics User",
+ "share": 1,
"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,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Instructor",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 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_views": 0
+ ],
+ "restrict_to_domain": "Education",
+ "search_fields": "course_name",
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ 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
index 6f2bb0db1f..b082be2aa2 100644
--- a/erpnext/education/doctype/course_enrollment/course_enrollment.py
+++ b/erpnext/education/doctype/course_enrollment/course_enrollment.py
@@ -35,7 +35,7 @@ class CourseEnrollment(Document):
if enrollment:
frappe.throw(_("Student is already enrolled."))
- def add_quiz_activity(self, quiz_name, quiz_response,answers, score, status):
+ 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:
@@ -43,7 +43,9 @@ class CourseEnrollment(Document):
item['question'] = key
item['quiz_result'] = result[key]
try:
- if isinstance(quiz_response[key], list):
+ if not quiz_response[key]:
+ item['selected_option'] = "Unattempted"
+ elif 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')
@@ -59,11 +61,12 @@ class CourseEnrollment(Document):
"result": result_data,
"score": score,
"status": status
- }).insert()
+ }).insert(ignore_permissions = True)
def add_activity(self, content_type, content):
- if check_activity_exists(self.name, content_type, content):
- pass
+ activity = check_activity_exists(self.name, content_type, content)
+ if activity:
+ return activity
else:
activity = frappe.get_doc({
"doctype": "Course Activity",
@@ -71,9 +74,14 @@ class CourseEnrollment(Document):
"content_type": content_type,
"content": content,
"activity_date": frappe.utils.datetime.datetime.now()
- })
- activity.insert()
+ })
+
+ activity.insert(ignore_permissions=True)
+ return activity.name
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
+ if activity:
+ return activity[0].name
+ else:
+ return None
\ No newline at end of file
diff --git a/erpnext/education/doctype/program/program.json b/erpnext/education/doctype/program/program.json
index cb8d7786e1..a0a2aa2e2b 100644
--- a/erpnext/education/doctype/program/program.json
+++ b/erpnext/education/doctype/program/program.json
@@ -1,627 +1,181 @@
{
- "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",
+ "field_order": [
+ "program_name",
+ "department",
+ "column_break_3",
+ "program_code",
+ "program_abbreviation",
+ "section_break_5",
+ "courses",
+ "section_break_9",
+ "description",
+ "intro_video",
+ "hero_image",
+ "column_break_11",
+ "is_published",
+ "is_featured",
+ "allow_self_enroll"
+ ],
"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": "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
+ "reqd": 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": "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
+ "options": "Department"
},
{
- "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
+ "fieldtype": "Column Break"
},
{
- "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
+ "label": "Program Abbreviation"
},
{
- "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
+ "label": "Portal Settings"
},
{
- "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
+ "options": "Program Course"
},
{
- "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
+ "label": "LMS Settings"
},
{
- "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
+ "label": "Description"
},
{
- "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
+ "label": "Intro Video"
},
{
- "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
+ "label": "Hero Image"
},
{
- "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
+ "fieldtype": "Column Break"
},
{
- "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
+ "label": "Is Published"
},
{
- "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,
+ "depends_on": "eval: doc.is_published == 1",
"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
+ "label": "Is Featured"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval: doc.is_published == 1",
+ "description": "Allow students to enroll themselves from the portal",
+ "fieldname": "allow_self_enroll",
+ "fieldtype": "Check",
+ "label": "Allow Self Enroll"
}
],
- "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": "2019-06-05 17:47:26.877296",
"modified_by": "Administrator",
"module": "Education",
"name": "Program",
- "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": "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
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Academics User",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Instructor",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Guest",
+ "share": 1
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Student",
+ "share": 1
}
],
- "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
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py
index 22cca86fcf..d232e47245 100644
--- a/erpnext/education/doctype/program_enrollment/program_enrollment.py
+++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py
@@ -96,29 +96,6 @@ class ProgramEnrollment(Document):
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):
if filters.get('program'):
diff --git a/erpnext/education/doctype/question/question.json b/erpnext/education/doctype/question/question.json
index 14a9f3ce92..b3a161daa0 100644
--- a/erpnext/education/doctype/question/question.json
+++ b/erpnext/education/doctype/question/question.json
@@ -1,167 +1,80 @@
{
- "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",
+ "allow_import": 1,
+ "autoname": "format:QUESTION-{#####}",
+ "creation": "2018-10-01 15:58:00.696815",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "question",
+ "options",
+ "question_type"
+ ],
"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
- },
+ "fieldname": "question",
+ "fieldtype": "Small Text",
+ "in_list_view": 1,
+ "label": "Question",
+ "reqd": 1
+ },
{
- "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
+ "fieldname": "options",
+ "fieldtype": "Table",
+ "label": "Options",
+ "options": "Options",
+ "reqd": 1
+ },
+ {
+ "fieldname": "question_type",
+ "fieldtype": "Select",
+ "in_standard_filter": 1,
+ "label": "Type",
+ "options": "\nSingle Correct Answer\nMultiple Correct Answer",
+ "read_only": 1
}
- ],
- "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",
+ ],
+ "modified": "2019-05-30 18:39:21.880974",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Question",
+ "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,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Academics User",
+ "share": 1,
"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,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Instructor",
+ "share": 1,
"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
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "LMS User",
+ "share": 1
}
- ],
- "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
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/education/doctype/question/question.py b/erpnext/education/doctype/question/question.py
index 8cd23983ce..b8221081fa 100644
--- a/erpnext/education/doctype/question/question.py
+++ b/erpnext/education/doctype/question/question.py
@@ -12,6 +12,7 @@ class Question(Document):
def validate(self):
self.check_at_least_one_option()
self.check_minimum_one_correct_answer()
+ self.set_question_type()
def check_at_least_one_option(self):
if len(self.options) <= 1:
@@ -26,6 +27,13 @@ class Question(Document):
else:
frappe.throw(_("A qustion must have at least one correct options"))
+ def set_question_type(self):
+ correct_options = [option for option in self.options if option.is_correct]
+ if len(correct_options) > 1:
+ self.question_type = "Multiple Correct Answer"
+ else:
+ self.question_type = "Single Correct Answer"
+
def get_answer(self):
options = self.options
answers = [item.name for item in options if item.is_correct == True]
diff --git a/erpnext/education/doctype/quiz/quiz.json b/erpnext/education/doctype/quiz/quiz.json
index f91bc0f021..b4903fc285 100644
--- a/erpnext/education/doctype/quiz/quiz.json
+++ b/erpnext/education/doctype/quiz/quiz.json
@@ -1,299 +1,105 @@
{
- "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",
+ "field_order": [
+ "title",
+ "question",
+ "quiz_configuration_section",
+ "passing_score",
+ "max_attempts",
+ "grading_basis"
+ ],
"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
+ "reqd": 1
},
{
- "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
+ "label": "Quiz Configuration"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "default": "75",
+ "description": "Score out of 100",
"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
+ "reqd": 1
},
{
- "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
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Last Highest Score",
+ "default": "Latest 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
+ "options": "Latest Highest Score\nLatest Attempt"
}
],
- "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": "2019-05-30 18:50:54.218571",
"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
+ "share": 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,
- "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
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/education/doctype/quiz/quiz.py b/erpnext/education/doctype/quiz/quiz.py
index 6da50a6e25..8e54745464 100644
--- a/erpnext/education/doctype/quiz/quiz.py
+++ b/erpnext/education/doctype/quiz/quiz.py
@@ -7,51 +7,47 @@ import frappe
from frappe.model.document import Document
class Quiz(Document):
+ def validate(self):
+ if self.passing_score > 100:
+ frappe.throw("Passing Score value should be between 0 and 100")
- 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 allowed_attempt(self, enrollment, quiz_name):
+ if self.max_attempts == 0:
+ return True
+
+ try:
+ if len(frappe.get_all("Quiz Activity", {'enrollment': enrollment.name, 'quiz': quiz_name})) >= self.max_attempts:
+ frappe.msgprint("Maximum attempts for this quiz reached!")
+ return False
+ else:
+ return True
+ except Exception as e:
+ return False
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 = {}
+ result = {}
for key in answers:
try:
if isinstance(response_dict[key], list):
- result = compare_list_elementwise(response_dict[key], answers[key])
+ is_correct = 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)
+ is_correct = (response_dict[key] == answers[key])
+ except Exception as e:
+ is_correct = False
+ result[key] = is_correct
+ score = (sum(result.values()) * 100 ) / len(answers)
if score >= self.passing_score:
status = "Pass"
else:
status = "Fail"
- return correct_answers, score, status
+ return result, 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
+ return [frappe.get_doc('Question', question.question_link) for question in self.question]
def compare_list_elementwise(*args):
try:
diff --git a/erpnext/education/doctype/quiz_result/quiz_result.json b/erpnext/education/doctype/quiz_result/quiz_result.json
index 86505ac756..67c7e2d449 100644
--- a/erpnext/education/doctype/quiz_result/quiz_result.json
+++ b/erpnext/education/doctype/quiz_result/quiz_result.json
@@ -1,145 +1,52 @@
{
- "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",
+ "creation": "2018-10-15 15:52:25.766374",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "question",
+ "selected_option",
+ "quiz_result"
+ ],
"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
- },
+ "fieldname": "question",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Question",
+ "options": "Question",
+ "read_only": 1,
+ "reqd": 1,
+ "set_only_once": 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": "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
- },
+ "fieldname": "selected_option",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Selected Option",
+ "read_only": 1,
+ "set_only_once": 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": "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
+ "fieldname": "quiz_result",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Result",
+ "options": "\nCorrect\nWrong",
+ "read_only": 1,
+ "reqd": 1,
+ "set_only_once": 1
}
- ],
- "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
+ ],
+ "istable": 1,
+ "modified": "2019-06-03 12:52:32.267392",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Quiz Result",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index 529f78d4f5..da25880c81 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -54,7 +54,7 @@ class Student(Document):
'send_welcome_email': 1,
'user_type': 'Website User'
})
- student_user.add_roles("Student", "LMS User")
+ student_user.add_roles("Student")
student_user.save()
update_password_link = student_user.reset_password()
diff --git a/erpnext/education/doctype/topic/topic.json b/erpnext/education/doctype/topic/topic.json
index f47b10d780..6e748fddce 100644
--- a/erpnext/education/doctype/topic/topic.json
+++ b/erpnext/education/doctype/topic/topic.json
@@ -1,297 +1,104 @@
{
- "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",
+ "autoname": "field:topic_code",
+ "creation": "2018-12-12 11:37:39.917760",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "topic_name",
+ "column_break_2",
+ "topic_code",
+ "section_break_4",
+ "topic_content",
+ "description",
+ "hero_image"
+ ],
"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
- },
+ "fieldname": "topic_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Name",
+ "reqd": 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": "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
- },
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
{
- "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,
+ "fieldname": "topic_code",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Code",
+ "reqd": 1,
"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
- },
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break"
+ },
{
- "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
- },
+ "fieldname": "topic_content",
+ "fieldtype": "Table",
+ "label": "Topic Content",
+ "options": "Topic Content"
+ },
{
- "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
+ "fieldname": "hero_image",
+ "fieldtype": "Attach Image",
+ "label": "Hero Image"
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "label": "Description"
}
- ],
- "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",
+ ],
+ "modified": "2019-06-05 18:38:44.029711",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Topic",
+ "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,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"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,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Administrator",
+ "share": 1,
"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,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Instructor",
+ "share": 1,
"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
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/education/doctype/video/video.json b/erpnext/education/doctype/video/video.json
index cc8f718ba4..3d11bd256f 100644
--- a/erpnext/education/doctype/video/video.json
+++ b/erpnext/education/doctype/video/video.json
@@ -1,262 +1,102 @@
{
- "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",
+ "field_order": [
+ "title",
+ "description",
+ "duration",
+ "provider",
+ "url",
+ "publish_date"
+ ],
"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,
+ "in_list_view": 1,
"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,
+ "reqd": 1,
"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,
+ "in_list_view": 1,
"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
+ "reqd": 1
},
{
- "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
+ "label": "Duration"
},
{
- "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,
+ "in_list_view": 1,
"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
+ "reqd": 1
},
{
- "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
+ "label": "Publish Date"
+ },
+ {
+ "fieldname": "provider",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Provider",
+ "options": "YouTube\nVimeo",
+ "reqd": 1
}
],
- "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": "2019-05-20 15:11:53.075093",
"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
+ "share": 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
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/education/setup.py b/erpnext/education/setup.py
index ed1d69e80d..5c4092849a 100644
--- a/erpnext/education/setup.py
+++ b/erpnext/education/setup.py
@@ -9,7 +9,8 @@ from erpnext.setup.utils import insert_record
def setup_education():
- if frappe.db.exists('Academic Year', '2015-16'):
+ disable_desk_access_for_student_role()
+ if frappe.db.exists("Academic Year", "2015-16"):
# already setup
return
create_academic_sessions()
@@ -26,3 +27,22 @@ def create_academic_sessions():
{"doctype": "Academic Term", "academic_year": "2017-18", "term_name": "Semester 2"}
]
insert_record(data)
+
+def disable_desk_access_for_student_role():
+ try:
+ student_role = frappe.get_doc("Role", "Student")
+ except frappe.DoesNotExistError:
+ create_student_role()
+ return
+
+ student_role.desk_access = 0
+ student_role.save()
+
+def create_student_role():
+ student_role = frappe.get_doc({
+ "doctype": "Role",
+ "role_name": "Student",
+ "desk_access": 0,
+ "restrict_to_domain": "Education"
+ })
+ student_role.insert()
diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py
index bf766adc09..0e02712acb 100644
--- a/erpnext/education/utils.py
+++ b/erpnext/education/utils.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
-# For lice
from __future__ import unicode_literals, division
import frappe
@@ -57,9 +56,10 @@ def validate_duplicate_student(students):
# 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
+ """Returns current student from frappe.session.user
+
+ Returns:
+ object: Student Document
"""
email = frappe.session.user
if email in ('Administrator', 'Guest'):
@@ -70,44 +70,266 @@ def get_current_student():
except (IndexError, frappe.DoesNotExistError):
return None
-def check_super_access():
+def get_portal_programs():
+ """Returns a list of all program to be displayed on the portal
+ Programs are returned based on the following logic
+ is_published and (student_is_enrolled or student_can_self_enroll)
+
+ Returns:
+ list of dictionary: List of all programs and to be displayed on the portal along with access rights
+ """
+ published_programs = frappe.get_all("Program", filters={"is_published": True})
+ if not published_programs:
+ return None
+
+ program_list = [frappe.get_doc("Program", program) for program in published_programs]
+ portal_programs = [{'program': program, 'has_access': allowed_program_access(program.name)} for program in program_list if allowed_program_access(program.name) or program.allow_self_enroll]
+
+ return portal_programs
+
+def allowed_program_access(program, student=None):
+ """Returns enrollment status for current student
+
+ Args:
+ program (string): Name of the program
+ student (object): instance of Student document
+
+ Returns:
+ bool: Is current user enrolled or not
+ """
+ if has_super_access():
+ return True
+ if not student:
+ student = get_current_student()
+ if student and get_enrollment('program', program, student.name):
+ return True
+ else:
+ return False
+
+def get_enrollment(master, document, student):
+ """Gets enrollment for course or program
+
+ Args:
+ master (string): can either be program or course
+ document (string): program or course name
+ student (string): Student ID
+
+ Returns:
+ string: Enrollment Name if exists else returns empty string
+ """
+ if master == 'program':
+ enrollments = frappe.get_all("Program Enrollment", filters={'student':student, 'program': document, 'docstatus': 1})
+ if master == 'course':
+ enrollments = frappe.get_all("Course Enrollment", filters={'student':student, 'course': document})
+
+ if enrollments:
+ return enrollments[0].name
+ else:
+ return None
+
+@frappe.whitelist()
+def enroll_in_program(program_name, student=None):
+ """Enroll student in program
+
+ Args:
+ program_name (string): Name of the program to be enrolled into
+ student (string, optional): name of student who has to be enrolled, if not
+ provided, a student will be created from the current user
+
+ Returns:
+ string: name of the program enrollment document
+ """
+ if has_super_access():
+ return
+
+ if not student == None:
+ student = frappe.get_doc("Student", student)
+ else:
+ # Check if self enrollment in allowed
+ program = frappe.get_doc('Program', program_name)
+ if not program.allow_self_enroll:
+ return frappe.throw("You are not allowed to enroll for this course")
+
+ student = get_current_student()
+ if not student:
+ student = create_student_from_current_user()
+
+ # Check if student is already enrolled in program
+ enrollment = get_enrollment('program', program_name, student.name)
+ if enrollment:
+ return enrollment
+
+ # Check if self enrollment in allowed
+ program = frappe.get_doc('Program', program_name)
+ if not program.allow_self_enroll:
+ return frappe.throw("You are not allowed to enroll for this course")
+
+ # Enroll in program
+ program_enrollment = student.enroll_in_program(program_name)
+ return program_enrollment.name
+
+def has_super_access():
+ """Check if user has a role that allows full access to LMS
+
+ Returns:
+ bool: true if user has access to all lms content
+ """
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
- """
+@frappe.whitelist()
+def add_activity(course, content_type, content, program):
+ if has_super_access():
+ return None
+
student = get_current_student()
if not student:
- return None
+ return frappe.throw("Student with email {0} does not exist".format(frappe.session.user), frappe.DoesNotExistError)
+
+ enrollment = get_or_create_course_enrollment(course, program)
+ if content_type == 'Quiz':
+ return
else:
- enrollment = frappe.get_all("Program Enrollment", filters={'student':student.name, 'program': program_name})
- if enrollment:
- return enrollment[0].name
+ return enrollment.add_activity(content_type, content)
+
+@frappe.whitelist()
+def evaluate_quiz(quiz_response, quiz_name, course, program):
+ import json
+
+ student = get_current_student()
+
+ quiz_response = json.loads(quiz_response)
+ quiz = frappe.get_doc("Quiz", quiz_name)
+ result, score, status = quiz.evaluate(quiz_response, quiz_name)
+
+ if has_super_access():
+ return {'result': result, 'score': score, 'status': status}
+
+ if student:
+ enrollment = get_or_create_course_enrollment(course, program)
+ if quiz.allowed_attempt(enrollment, quiz_name):
+ enrollment.add_quiz_activity(quiz_name, quiz_response, result, score, status)
+ return {'result': result, 'score': score, 'status': status}
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}
+@frappe.whitelist()
+def get_quiz(quiz_name, course):
+ 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
-def get_course_enrollment(course_name):
+ questions = [{
+ 'name': question.name,
+ 'question': question.question,
+ 'type': question.question_type,
+ 'options': [{'name': option.name, 'option': option.option}
+ for option in question.options],
+ } for question in questions]
+
+ if has_super_access():
+ return {'questions': questions, 'activity': None}
+
+ student = get_current_student()
+ course_enrollment = get_enrollment("course", course, student.name)
+ status, score, result = check_quiz_completion(quiz, course_enrollment)
+ return {'questions': questions, 'activity': {'is_complete': status, 'score': score, 'result': result}}
+
+def get_topic_progress(topic, course_name, program):
+ """
+ Return the porgress of a course in a program as well as the content to continue from.
+ :param topic_name:
+ :param 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:
+ course_enrollment = get_or_create_course_enrollment(course_name, program)
+ progress = student.get_topic_progress(course_enrollment.name, topic)
+ if not progress:
return None
+ count = sum([activity['is_complete'] for activity in progress])
+ if count == 0:
+ return {'completed': False, 'started': False}
+ elif count == len(progress):
+ return {'completed': True, 'started': True}
+ elif count < len(progress):
+ return {'completed': False, 'started': True}
+
+def get_course_progress(course, program):
+ """
+ Return the porgress of a course in a program as well as the content to continue from.
+ :param topic_name:
+ :param course_name:
+ """
+ course_progress = []
+ for course_topic in course.topics:
+ topic = frappe.get_doc("Topic", course_topic.topic)
+ progress = get_topic_progress(topic, course.name, program)
+ if progress:
+ course_progress.append(progress)
+ if course_progress:
+ number_of_completed_topics = sum([activity['completed'] for activity in course_progress])
+ total_topics = len(course_progress)
+ if total_topics == 1:
+ return course_progress[0]
+ if number_of_completed_topics == 0:
+ return {'completed': False, 'started': False}
+ if number_of_completed_topics == total_topics:
+ return {'completed': True, 'started': True}
+ if number_of_completed_topics < total_topics:
+ return {'completed': False, 'started': True}
+
+ return None
+
+def get_program_progress(program):
+ program_progress = []
+ if not program.courses:
+ return None
+ for program_course in program.courses:
+ course = frappe.get_doc("Course", program_course.course)
+ progress = get_course_progress(course, program.name)
+ if progress:
+ progress['name'] = course.name
+ progress['course'] = course.course_name
+ program_progress.append(progress)
+
+ if program_progress:
+ return program_progress
+
+ return None
+
+def get_program_completion(program):
+ topics = frappe.db.sql("""select `tabcourse topic`.topic, `tabcourse topic`.parent
+ from `tabcourse topic`,
+ `tabprogram course`
+ where `tabcourse topic`.parent = `tabprogram course`.course
+ and `tabprogram course`.parent = %s""", program.name)
+
+ progress = []
+ for topic in topics:
+ topic_doc = frappe.get_doc('Topic', topic[0])
+ topic_progress = get_topic_progress(topic_doc, topic[1], program.name)
+ if topic_progress:
+ progress.append(topic_progress)
+
+ if progress:
+ number_of_completed_topics = sum([activity['completed'] for activity in progress if activity])
+ total_topics = len(progress)
+ try:
+ return int((float(number_of_completed_topics)/total_topics)*100)
+ except ZeroDivisionError:
+ return 0
+
+ return 0
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,
@@ -115,12 +337,21 @@ def create_student_from_current_user():
"student_email_id": user.email,
"user": frappe.session.user
})
+
student.save(ignore_permissions=True)
return student
-def enroll_in_course(course_name, program_name):
+def get_or_create_course_enrollment(course, program):
student = get_current_student()
- return student.enroll_in_course(course_name=course_name, program_enrollment=get_program_enrollment(program_name))
+ course_enrollment = get_enrollment("course", course, student.name)
+ if not course_enrollment:
+ program_enrollment = get_enrollment('program', program, student.name)
+ if not program_enrollment:
+ frappe.throw("You are not enrolled in program {0}".format(program))
+ return
+ return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program, student.name))
+ else:
+ return frappe.get_doc('Course Enrollment', course_enrollment)
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})
@@ -131,7 +362,7 @@ def check_content_completion(content_name, content_type, enrollment_name):
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)
+ status = False if quiz.max_attempts == 0 else bool(len(attempts) >= quiz.max_attempts)
score = None
result = None
if attempts:
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index bb1980321b..be7189bb96 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -55,8 +55,5 @@
"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
deleted file mode 100644
index e35acbdd75..0000000000
--- a/erpnext/public/js/education/lms/call.js
+++ /dev/null
@@ -1,15 +0,0 @@
-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
deleted file mode 100644
index eab1424455..0000000000
--- a/erpnext/public/js/education/lms/components/Article.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
- {{ title }}
-
- Published on {{ publishDate }}
- — {{ author }}
-
-
- {{ course.course_name }}
-
- {{ course.course_intro.substring(0,120) }}
-
- {{ fullName }}
-
-
-
-
- None
- {{ program.program_name }}
- {{ programData.program }}
-
-
-
- {{ question.question }}
-
-{{ question.question }}
-
-{{ quizData.program }}
-
-
- - Unattempted
- {{ topic.topic_name }}
-
- Content
-
-
-
- {{ contentData.name }}
-
- {{ contentData.duration }} Mins — Published on {{ contentData.publish_date }}.
-
-
-
{{_("The page you are looking for is missing. This could be because it is moved or there is a typo in the link.")}}
- -{{ _("Error Code: {0}").format('404') }}
- -{% endif %} -{% endblock %} \ No newline at end of file diff --git a/erpnext/www/lms.py b/erpnext/www/lms.py deleted file mode 100644 index 7561d73d20..0000000000 --- a/erpnext/www/lms.py +++ /dev/null @@ -1,242 +0,0 @@ -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/lms/__init__.py b/erpnext/www/lms/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html new file mode 100644 index 0000000000..9b8c45cb9b --- /dev/null +++ b/erpnext/www/lms/content.html @@ -0,0 +1,208 @@ +{% extends "templates/base.html" %} +{% block title %}{{ content.name or 'Content Page' }}{% endblock %} + +{% block head_include %} + + +{% endblock %} + +{% macro title() %} + +{{ education_settings.description }}
++ {% if frappe.session.user == 'Guest' %} + Start Learning + {% endif %} +
+{{ description or ''}}
++ {% if frappe.session.user == 'Guest' %} + Sign Up + {% elif not has_access %} + + {% endif %} +
+{{ student.name }}
+