feat: Timer in LMS Quiz (#24246)
* feat: new fields in quiz doctypes * feat: timer in lms quiz * fix: variable initialisation * fix: context, exception fix * fix:sider * fix:sider * fix: indentation * fix: timer * fix: sider * fix: return value and format * fix: show time taken only after all attempts are over * fix: sider Co-authored-by: pateljannat <jannatpatel@MacBook-Air.local> Co-authored-by: Marica <maricadsouza221197@gmail.com>
This commit is contained in:
parent
7eac4a250d
commit
dcdd3bebbe
@ -41,7 +41,7 @@ class CourseEnrollment(Document):
|
|||||||
frappe.throw(_("Student is already enrolled via Course Enrollment {0}").format(
|
frappe.throw(_("Student is already enrolled via Course Enrollment {0}").format(
|
||||||
get_link_to_form("Course Enrollment", enrollment)), title=_('Duplicate Entry'))
|
get_link_to_form("Course Enrollment", enrollment)), title=_('Duplicate Entry'))
|
||||||
|
|
||||||
def add_quiz_activity(self, quiz_name, quiz_response, answers, score, status):
|
def add_quiz_activity(self, quiz_name, quiz_response, answers, score, status, time_taken):
|
||||||
result = {k: ('Correct' if v else 'Wrong') for k,v in answers.items()}
|
result = {k: ('Correct' if v else 'Wrong') for k,v in answers.items()}
|
||||||
result_data = []
|
result_data = []
|
||||||
for key in answers:
|
for key in answers:
|
||||||
@ -66,7 +66,8 @@ class CourseEnrollment(Document):
|
|||||||
"activity_date": frappe.utils.datetime.datetime.now(),
|
"activity_date": frappe.utils.datetime.datetime.now(),
|
||||||
"result": result_data,
|
"result": result_data,
|
||||||
"score": score,
|
"score": score,
|
||||||
"status": status
|
"status": status,
|
||||||
|
"time_taken": time_taken
|
||||||
}).insert(ignore_permissions = True)
|
}).insert(ignore_permissions = True)
|
||||||
|
|
||||||
def add_activity(self, content_type, content):
|
def add_activity(self, content_type, content):
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "field:title",
|
"autoname": "field:title",
|
||||||
@ -12,7 +13,10 @@
|
|||||||
"quiz_configuration_section",
|
"quiz_configuration_section",
|
||||||
"passing_score",
|
"passing_score",
|
||||||
"max_attempts",
|
"max_attempts",
|
||||||
"grading_basis"
|
"grading_basis",
|
||||||
|
"column_break_7",
|
||||||
|
"is_time_bound",
|
||||||
|
"duration"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -58,9 +62,26 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Grading Basis",
|
"label": "Grading Basis",
|
||||||
"options": "Latest Highest Score\nLatest Attempt"
|
"options": "Latest Highest Score\nLatest Attempt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "is_time_bound",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Is Time-Bound"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "is_time_bound",
|
||||||
|
"fieldname": "duration",
|
||||||
|
"fieldtype": "Duration",
|
||||||
|
"label": "Duration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_7",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-06-12 12:23:57.020508",
|
"links": [],
|
||||||
|
"modified": "2020-12-24 15:41:35.043262",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Quiz",
|
"name": "Quiz",
|
||||||
|
@ -1,490 +1,163 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "format:EDU-QA-{YYYY}-{#####}",
|
"autoname": "format:EDU-QA-{YYYY}-{#####}",
|
||||||
"beta": 1,
|
"beta": 1,
|
||||||
"creation": "2018-10-15 15:48:40.482821",
|
"creation": "2018-10-15 15:48:40.482821",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"enrollment",
|
||||||
|
"student",
|
||||||
|
"column_break_3",
|
||||||
|
"course",
|
||||||
|
"section_break_5",
|
||||||
|
"quiz",
|
||||||
|
"column_break_7",
|
||||||
|
"status",
|
||||||
|
"section_break_9",
|
||||||
|
"result",
|
||||||
|
"section_break_11",
|
||||||
|
"activity_date",
|
||||||
|
"score",
|
||||||
|
"column_break_14",
|
||||||
|
"time_taken"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "enrollment",
|
"fieldname": "enrollment",
|
||||||
"fieldtype": "Link",
|
"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",
|
"label": "Enrollment",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Course Enrollment",
|
"options": "Course Enrollment",
|
||||||
"permlevel": 0,
|
"set_only_once": 1
|
||||||
"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",
|
"fetch_from": "enrollment.student",
|
||||||
"fieldname": "student",
|
"fieldname": "student",
|
||||||
"fieldtype": "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_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Student",
|
"label": "Student",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Student",
|
"options": "Student",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"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",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"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",
|
"fetch_from": "enrollment.course",
|
||||||
"fieldname": "course",
|
"fieldname": "course",
|
||||||
"fieldtype": "Link",
|
"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",
|
"label": "Course",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Course",
|
"options": "Course",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
"set_only_once": 1
|
||||||
"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",
|
"fieldname": "section_break_5",
|
||||||
"fieldtype": "Section Break",
|
"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",
|
"fieldname": "quiz",
|
||||||
"fieldtype": "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_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Quiz",
|
"label": "Quiz",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Quiz",
|
"options": "Quiz",
|
||||||
"permlevel": 0,
|
"set_only_once": 1
|
||||||
"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",
|
"fieldname": "column_break_7",
|
||||||
"fieldtype": "Column Break",
|
"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",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"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",
|
"label": "Status",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nPass\nFail",
|
"options": "\nPass\nFail",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"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",
|
"fieldname": "section_break_9",
|
||||||
"fieldtype": "Section Break",
|
"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",
|
"fieldname": "result",
|
||||||
"fieldtype": "Table",
|
"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",
|
"label": "Result",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Quiz Result",
|
"options": "Quiz Result",
|
||||||
"permlevel": 0,
|
"set_only_once": 1
|
||||||
"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",
|
"fieldname": "activity_date",
|
||||||
"fieldtype": "Data",
|
"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",
|
"label": "Activity Date",
|
||||||
"length": 0,
|
"set_only_once": 1
|
||||||
"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",
|
"fieldname": "score",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Score",
|
"label": "Score",
|
||||||
"length": 0,
|
"set_only_once": 1
|
||||||
"no_copy": 0,
|
},
|
||||||
"permlevel": 0,
|
{
|
||||||
"precision": "",
|
"fieldname": "time_taken",
|
||||||
"print_hide": 0,
|
"fieldtype": "Duration",
|
||||||
"print_hide_if_no_value": 0,
|
"label": "Time Taken",
|
||||||
"read_only": 0,
|
"set_only_once": 1
|
||||||
"remember_last_selected_value": 0,
|
},
|
||||||
"report_hide": 0,
|
{
|
||||||
"reqd": 0,
|
"fieldname": "section_break_11",
|
||||||
"search_index": 0,
|
"fieldtype": "Section Break"
|
||||||
"set_only_once": 1,
|
},
|
||||||
"translatable": 0,
|
{
|
||||||
"unique": 0
|
"fieldname": "column_break_14",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"links": [],
|
||||||
"hide_heading": 0,
|
"modified": "2020-12-24 15:41:20.085380",
|
||||||
"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",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Quiz Activity",
|
"name": "Quiz Activity",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Academics User",
|
"role": "Academics User",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "LMS User",
|
"role": "LMS User",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Instructor",
|
"role": "Instructor",
|
||||||
"set_user_permissions": 0,
|
"share": 1
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -114,7 +114,7 @@ class Student(Document):
|
|||||||
status = check_content_completion(content.name, content.doctype, course_enrollment_name)
|
status = check_content_completion(content.name, content.doctype, course_enrollment_name)
|
||||||
progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status})
|
progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status})
|
||||||
elif content.doctype == 'Quiz':
|
elif content.doctype == 'Quiz':
|
||||||
status, score, result = check_quiz_completion(content, course_enrollment_name)
|
status, score, result, time_taken = check_quiz_completion(content, course_enrollment_name)
|
||||||
progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status, 'score': score, 'result': result})
|
progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status, 'score': score, 'result': result})
|
||||||
return progress
|
return progress
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ def add_activity(course, content_type, content, program):
|
|||||||
return enrollment.add_activity(content_type, content)
|
return enrollment.add_activity(content_type, content)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def evaluate_quiz(quiz_response, quiz_name, course, program):
|
def evaluate_quiz(quiz_response, quiz_name, course, program, time_taken):
|
||||||
import json
|
import json
|
||||||
|
|
||||||
student = get_current_student()
|
student = get_current_student()
|
||||||
@ -209,7 +209,7 @@ def evaluate_quiz(quiz_response, quiz_name, course, program):
|
|||||||
if student:
|
if student:
|
||||||
enrollment = get_or_create_course_enrollment(course, program)
|
enrollment = get_or_create_course_enrollment(course, program)
|
||||||
if quiz.allowed_attempt(enrollment, quiz_name):
|
if quiz.allowed_attempt(enrollment, quiz_name):
|
||||||
enrollment.add_quiz_activity(quiz_name, quiz_response, result, score, status)
|
enrollment.add_quiz_activity(quiz_name, quiz_response, result, score, status, time_taken)
|
||||||
return {'result': result, 'score': score, 'status': status}
|
return {'result': result, 'score': score, 'status': status}
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
@ -219,8 +219,9 @@ def get_quiz(quiz_name, course):
|
|||||||
try:
|
try:
|
||||||
quiz = frappe.get_doc("Quiz", quiz_name)
|
quiz = frappe.get_doc("Quiz", quiz_name)
|
||||||
questions = quiz.get_questions()
|
questions = quiz.get_questions()
|
||||||
|
duration = quiz.duration
|
||||||
except:
|
except:
|
||||||
frappe.throw(_("Quiz {0} does not exist").format(quiz_name))
|
frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
questions = [{
|
questions = [{
|
||||||
@ -232,12 +233,20 @@ def get_quiz(quiz_name, course):
|
|||||||
} for question in questions]
|
} for question in questions]
|
||||||
|
|
||||||
if has_super_access():
|
if has_super_access():
|
||||||
return {'questions': questions, 'activity': None}
|
return {
|
||||||
|
'questions': questions,
|
||||||
|
'activity': None,
|
||||||
|
'duration':duration
|
||||||
|
}
|
||||||
|
|
||||||
student = get_current_student()
|
student = get_current_student()
|
||||||
course_enrollment = get_enrollment("course", course, student.name)
|
course_enrollment = get_enrollment("course", course, student.name)
|
||||||
status, score, result = check_quiz_completion(quiz, course_enrollment)
|
status, score, result, time_taken = check_quiz_completion(quiz, course_enrollment)
|
||||||
return {'questions': questions, 'activity': {'is_complete': status, 'score': score, 'result': result}}
|
return {
|
||||||
|
'questions': questions,
|
||||||
|
'activity': {'is_complete': status, 'score': score, 'result': result, 'time_taken': time_taken},
|
||||||
|
'duration': quiz.duration
|
||||||
|
}
|
||||||
|
|
||||||
def get_topic_progress(topic, course_name, program):
|
def get_topic_progress(topic, course_name, program):
|
||||||
"""
|
"""
|
||||||
@ -361,15 +370,23 @@ def check_content_completion(content_name, content_type, enrollment_name):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def check_quiz_completion(quiz, 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"])
|
attempts = frappe.get_all("Quiz Activity",
|
||||||
|
filters={
|
||||||
|
'enrollment': enrollment_name,
|
||||||
|
'quiz': quiz.name
|
||||||
|
},
|
||||||
|
fields=["name", "activity_date", "score", "status", "time_taken"]
|
||||||
|
)
|
||||||
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
|
score = None
|
||||||
result = None
|
result = None
|
||||||
|
time_taken = None
|
||||||
if attempts:
|
if attempts:
|
||||||
if quiz.grading_basis == 'Last Highest Score':
|
if quiz.grading_basis == 'Last Highest Score':
|
||||||
attempts = sorted(attempts, key = lambda i: int(i.score), reverse=True)
|
attempts = sorted(attempts, key = lambda i: int(i.score), reverse=True)
|
||||||
score = attempts[0]['score']
|
score = attempts[0]['score']
|
||||||
result = attempts[0]['status']
|
result = attempts[0]['status']
|
||||||
|
time_taken = attempts[0]['time_taken']
|
||||||
if result == 'Pass':
|
if result == 'Pass':
|
||||||
status = True
|
status = True
|
||||||
return status, score, result
|
return status, score, result, time_taken
|
@ -20,6 +20,16 @@ class Quiz {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make(data) {
|
make(data) {
|
||||||
|
if (data.duration) {
|
||||||
|
const timer_display = document.createElement("div");
|
||||||
|
timer_display.classList.add("lms-timer", "float-right", "font-weight-bold");
|
||||||
|
document.getElementsByClassName("lms-title")[0].appendChild(timer_display);
|
||||||
|
if (!data.activity || (data.activity && !data.activity.is_complete)) {
|
||||||
|
this.initialiseTimer(data.duration);
|
||||||
|
this.is_time_bound = true;
|
||||||
|
this.time_taken = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
data.questions.forEach(question_data => {
|
data.questions.forEach(question_data => {
|
||||||
let question_wrapper = document.createElement('div');
|
let question_wrapper = document.createElement('div');
|
||||||
let question = new Question({
|
let question = new Question({
|
||||||
@ -37,12 +47,51 @@ class Quiz {
|
|||||||
indicator = 'green'
|
indicator = 'green'
|
||||||
message = 'You have already cleared the quiz.'
|
message = 'You have already cleared the quiz.'
|
||||||
}
|
}
|
||||||
|
if (data.activity.time_taken) {
|
||||||
|
this.calculate_and_display_time(data.activity.time_taken, "Time Taken - ");
|
||||||
|
}
|
||||||
this.set_quiz_footer(message, indicator, data.activity.score)
|
this.set_quiz_footer(message, indicator, data.activity.score)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.make_actions();
|
this.make_actions();
|
||||||
}
|
}
|
||||||
|
window.addEventListener('beforeunload', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.returnValue = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initialiseTimer(duration) {
|
||||||
|
this.time_left = duration;
|
||||||
|
var self = this;
|
||||||
|
var old_diff;
|
||||||
|
this.calculate_and_display_time(this.time_left, "Time Left - ");
|
||||||
|
this.start_time = new Date().getTime();
|
||||||
|
this.timer = setInterval(function () {
|
||||||
|
var diff = (new Date().getTime() - self.start_time)/1000;
|
||||||
|
var variation = old_diff ? diff - old_diff : diff;
|
||||||
|
old_diff = diff;
|
||||||
|
self.time_left -= variation;
|
||||||
|
self.time_taken += variation;
|
||||||
|
self.calculate_and_display_time(self.time_left, "Time Left - ");
|
||||||
|
if (self.time_left <= 0) {
|
||||||
|
clearInterval(self.timer);
|
||||||
|
self.time_taken -= 1;
|
||||||
|
self.submit();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculate_and_display_time(second, text) {
|
||||||
|
var timer_display = document.getElementsByClassName("lms-timer")[0];
|
||||||
|
var hours = this.append_zero(Math.floor(second / 3600));
|
||||||
|
var minutes = this.append_zero(Math.floor(second % 3600 / 60));
|
||||||
|
var seconds = this.append_zero(Math.ceil(second % 3600 % 60));
|
||||||
|
timer_display.innerText = text + hours + ":" + minutes + ":" + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
append_zero(time) {
|
||||||
|
return time > 9 ? time : "0" + time;
|
||||||
}
|
}
|
||||||
|
|
||||||
make_actions() {
|
make_actions() {
|
||||||
@ -57,6 +106,10 @@ class Quiz {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
|
if (this.is_time_bound) {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
$(".lms-timer").text("");
|
||||||
|
}
|
||||||
this.submit_btn.innerText = 'Evaluating..'
|
this.submit_btn.innerText = 'Evaluating..'
|
||||||
this.submit_btn.disabled = true
|
this.submit_btn.disabled = true
|
||||||
this.disable()
|
this.disable()
|
||||||
@ -64,7 +117,8 @@ class Quiz {
|
|||||||
quiz_name: this.name,
|
quiz_name: this.name,
|
||||||
quiz_response: this.get_selected(),
|
quiz_response: this.get_selected(),
|
||||||
course: this.course,
|
course: this.course,
|
||||||
program: this.program
|
program: this.program,
|
||||||
|
time_taken: this.is_time_bound ? this.time_taken : ""
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
this.submit_btn.remove()
|
this.submit_btn.remove()
|
||||||
if (!res.message) {
|
if (!res.message) {
|
||||||
@ -157,7 +211,7 @@ class Question {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
let make_label = function(name, value) {
|
let make_label = function (name, value) {
|
||||||
let label = document.createElement('label');
|
let label = document.createElement('label');
|
||||||
label.classList.add('form-check-label');
|
label.classList.add('form-check-label');
|
||||||
label.htmlFor = name;
|
label.htmlFor = name;
|
||||||
@ -166,14 +220,14 @@ class Question {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let make_option = function (wrapper, option) {
|
let make_option = function (wrapper, option) {
|
||||||
let option_div = document.createElement('div')
|
let option_div = document.createElement('div');
|
||||||
option_div.classList.add('form-check', 'pb-1')
|
option_div.classList.add('form-check', 'pb-1');
|
||||||
let input = make_input(option.name, option.option);
|
let input = make_input(option.name, option.option);
|
||||||
let label = make_label(option.name, option.option);
|
let label = make_label(option.name, option.option);
|
||||||
option_div.appendChild(input)
|
option_div.appendChild(input);
|
||||||
option_div.appendChild(label)
|
option_div.appendChild(label);
|
||||||
wrapper.appendChild(option_div)
|
wrapper.appendChild(option_div);
|
||||||
return {input: input, ...option}
|
return { input: input, ...option };
|
||||||
}
|
}
|
||||||
|
|
||||||
let options_wrapper = document.createElement('div')
|
let options_wrapper = document.createElement('div')
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
{{_('Back to Course')}}
|
{{_('Back to Course')}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="lms-title">
|
||||||
<h2>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h2>
|
<h2>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h2>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
@ -169,14 +169,51 @@
|
|||||||
const next_url = '/lms/course?name={{ course }}&program={{ program }}'
|
const next_url = '/lms/course?name={{ course }}&program={{ program }}'
|
||||||
{% endif %}
|
{% endif %}
|
||||||
frappe.ready(() => {
|
frappe.ready(() => {
|
||||||
const quiz = new Quiz(document.getElementById('quiz-wrapper'), {
|
{% if content.is_time_bound %}
|
||||||
name: '{{ content.name }}',
|
var duration = get_duration("{{content.duration}}")
|
||||||
course: '{{ course }}',
|
var d = frappe.msgprint({
|
||||||
program: '{{ program }}',
|
title: __('Important Notice'),
|
||||||
quiz_exit_button: quiz_exit_button,
|
indicator: "red",
|
||||||
next_url: next_url
|
message: __(`This is a Time-Bound Quiz. <br><br>
|
||||||
})
|
A timer for <b>${duration}</b> will start, once you click on <b>Proceed</b>. <br><br>
|
||||||
window.quiz = quiz;
|
If you fail to submit before the time is up, the Quiz will be submitted automatically.`),
|
||||||
|
primary_action: {
|
||||||
|
label: __("Proceed"),
|
||||||
|
action: () => {
|
||||||
|
create_quiz();
|
||||||
|
d.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
secondary_action: {
|
||||||
|
action: () => {
|
||||||
|
d.hide();
|
||||||
|
window.location.href = "/lms/course?name={{ course }}&program={{ program }}";
|
||||||
|
},
|
||||||
|
label: __("Go Back"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% else %}
|
||||||
|
create_quiz();
|
||||||
|
{% endif %}
|
||||||
|
function create_quiz() {
|
||||||
|
const quiz = new Quiz(document.getElementById('quiz-wrapper'), {
|
||||||
|
name: '{{ content.name }}',
|
||||||
|
course: '{{ course }}',
|
||||||
|
program: '{{ program }}',
|
||||||
|
quiz_exit_button: quiz_exit_button,
|
||||||
|
next_url: next_url
|
||||||
|
})
|
||||||
|
window.quiz = quiz;
|
||||||
|
}
|
||||||
|
function get_duration(seconds){
|
||||||
|
var hours = append_zero(Math.floor(seconds / 3600));
|
||||||
|
var minutes = append_zero(Math.floor(seconds % 3600 / 60));
|
||||||
|
var seconds = append_zero(Math.floor(seconds % 3600 % 60));
|
||||||
|
return `${hours}:${minutes}:${seconds}`;
|
||||||
|
}
|
||||||
|
function append_zero(time) {
|
||||||
|
return time > 9 ? time : "0" + time;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -42,7 +42,9 @@
|
|||||||
<section class="top-section" style="padding: 6rem 0rem;">
|
<section class="top-section" style="padding: 6rem 0rem;">
|
||||||
<div class='container pb-5'>
|
<div class='container pb-5'>
|
||||||
<h1>{{ education_settings.portal_title }}</h1>
|
<h1>{{ education_settings.portal_title }}</h1>
|
||||||
<p class='lead'>{{ education_settings.description }}</p>
|
{% if education_settings.description %}
|
||||||
|
<p class='lead'>{{ education_settings.description }}</p>
|
||||||
|
{% endif %}
|
||||||
<p class="mt-4">
|
<p class="mt-4">
|
||||||
{% if frappe.session.user == 'Guest' %}
|
{% if frappe.session.user == 'Guest' %}
|
||||||
<a class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
|
<a class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
|
||||||
@ -51,13 +53,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class='container'>
|
<div class='container'>
|
||||||
<div class="row mt-5">
|
<div class="row mt-5">
|
||||||
{% for program in featured_programs %}
|
|
||||||
{{ program_card(program.program, program.has_access) }}
|
|
||||||
{% endfor %}
|
|
||||||
{% if featured_programs %}
|
{% if featured_programs %}
|
||||||
|
{% for program in featured_programs %}
|
||||||
|
{{ program_card(program.program, program.has_access) }}
|
||||||
|
{% endfor %}
|
||||||
{% for n in range( (3 - (featured_programs|length)) %3) %}
|
{% for n in range( (3 - (featured_programs|length)) %3) %}
|
||||||
{{ null_card() }}
|
{{ null_card() }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<p class="lead">You have not enrolled in any program. Contact your Instructor.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,7 +35,7 @@ def get_contents(topic, course, program):
|
|||||||
progress.append({'content': content, 'content_type': content.doctype, 'completed': status})
|
progress.append({'content': content, 'content_type': content.doctype, 'completed': status})
|
||||||
elif content.doctype == 'Quiz':
|
elif content.doctype == 'Quiz':
|
||||||
if student:
|
if student:
|
||||||
status, score, result = utils.check_quiz_completion(content, course_enrollment.name)
|
status, score, result, time_taken = utils.check_quiz_completion(content, course_enrollment.name)
|
||||||
else:
|
else:
|
||||||
status = False
|
status = False
|
||||||
score = None
|
score = None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user