diff --git a/community/hooks.py b/community/hooks.py index 6d04ed83..eca23eac 100644 --- a/community/hooks.py +++ b/community/hooks.py @@ -161,10 +161,10 @@ whitelist = [ "/socket.io", "/hackathons", "/dashboard", - "/join-request" + "/join-request", "/add-a-new-batch", "/new-sign-up", - "/message" + "/message", "/about" ] whitelist_rules = [{"from_route": p, "to_route": p[1:]} for p in whitelist] @@ -188,4 +188,8 @@ update_website_context = 'community.widgets.update_website_context' # community_lesson_page_extension = None ## Markdown Macros for Lessons -# community_markdown_macro_renderers = {"Exercise": "myapp.mymodule.plugins.render_exercise"} +community_markdown_macro_renderers = { + "Exercise": "community.plugins.exercise_renderer", + "Quiz": "community.plugins.quiz_renderer", + "YouTubeVideo": "community.plugins.youtube_video_renderer", +} diff --git a/community/lms/doctype/lesson/lesson.json b/community/lms/doctype/lesson/lesson.json index f7222275..582596de 100644 --- a/community/lms/doctype/lesson/lesson.json +++ b/community/lms/doctype/lesson/lesson.json @@ -7,7 +7,6 @@ "engine": "InnoDB", "field_order": [ "chapter", - "lesson_type", "include_in_preview", "column_break_4", "title", @@ -24,14 +23,6 @@ "label": "Chapter", "options": "Chapter" }, - { - "default": "Video", - "fieldname": "lesson_type", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Lesson Type", - "options": "Video\nText\nQuiz" - }, { "fieldname": "title", "fieldtype": "Data", @@ -73,7 +64,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-06-11 19:03:23.138165", + "modified": "2021-06-23 17:59:52.946515", "modified_by": "Administrator", "module": "LMS", "name": "Lesson", @@ -95,4 +86,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/community/lms/doctype/lesson/lesson.py b/community/lms/doctype/lesson/lesson.py index 53fe5d89..d90fa2c1 100644 --- a/community/lms/doctype/lesson/lesson.py +++ b/community/lms/doctype/lesson/lesson.py @@ -9,27 +9,35 @@ from ...md import markdown_to_html, find_macros class Lesson(Document): def before_save(self): - macros = find_macros(self.body) - exercises = [value for name, value in macros if name == "Exercise"] + dynamic_documents = ["Exercise", "Quiz"] + for section in dynamic_documents: + self.update_lesson_name_in_document(section) + def update_lesson_name_in_document(self, section): + doctype_map= { + "Exercise": "Exercise", + "Quiz": "LMS Quiz" + } + macros = find_macros(self.body) + documents = [value for name, value in macros if name == section] index = 1 - for name in exercises: - e = frappe.get_doc("Exercise", name) + for name in documents: + e = frappe.get_doc(doctype_map[section], name) e.lesson = self.name e.index_ = index e.save() index += 1 - self.update_orphan_exercises(exercises) + self.update_orphan_documents(doctype_map[section], documents) - def update_orphan_exercises(self, active_exercises): - """Updates the exercises that were previously part of this lesson, + def update_orphan_documents(self, doctype, documents): + """Updates the documents that were previously part of this lesson, but not any more. """ - linked_exercises = {row['name'] for row in frappe.get_all('Exercise', {"lesson": self.name})} - active_exercises = set(active_exercises) - orphan_exercises = linked_exercises - active_exercises - for name in orphan_exercises: - ex = frappe.get_doc("Exercise", name) + linked_documents = {row['name'] for row in frappe.get_all(doctype, {"lesson": self.name})} + active_documents = set(documents) + orphan_documents = linked_documents - active_documents + for name in orphan_documents: + ex = frappe.get_doc(doctype, name) ex.lesson = None ex.index_ = 0 ex.index_label = "" @@ -92,13 +100,30 @@ def update_progress(lesson): course_progress.save(ignore_permissions=True) def all_dynamic_content_submitted(lesson, user): + all_exercises_submitted = check_all_exercise_submission(lesson, user) + all_quiz_submitted = check_all_quiz_submitted(lesson, user) + return all_exercises_submitted and all_quiz_submitted + +def check_all_exercise_submission(lesson, user): exercise_names = frappe.get_list("Exercise", {"lesson": lesson}, pluck="name", ignore_permissions=True) - all_exercises_submitted = False + if not len(exercise_names): + return True query = { "exercise": ["in", exercise_names], "owner": user } if frappe.db.count("Exercise Submission", query) == len(exercise_names): - all_exercises_submitted = True + return True + return False - return all_exercises_submitted +def check_all_quiz_submitted(lesson, user): + quizzes = frappe.get_list("LMS Quiz", {"lesson": lesson}, pluck="name", ignore_permissions=True) + if not len(quizzes): + return True + query = { + "quiz": ["in", quizzes], + "owner": user + } + if frappe.db.count("LMS Quiz Submission", query) == len(quizzes): + return True + return False diff --git a/community/lms/doctype/lms_course/lms_course.py b/community/lms/doctype/lms_course/lms_course.py index 7f77cd5d..a98a5e63 100644 --- a/community/lms/doctype/lms_course/lms_course.py +++ b/community/lms/doctype/lms_course/lms_course.py @@ -199,13 +199,15 @@ class LMSCourse(Document): } if batch: filters["batch"] = batch - return frappe.db.get_value("LMS Batch Membership", filters, ["name","batch", "current_lesson"], as_dict=True) + membership = frappe.db.get_value("LMS Batch Membership", filters, ["name","batch", "current_lesson"], as_dict=True) + if membership and membership.batch: + membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title") + return membership - def get_all_memberships(self, member=frappe.session.user): + def get_all_memberships(self, member): all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name}, ["batch"]) for membership in all_memberships: membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title") - print(all_memberships) return all_memberships def get_mentors(self, batch=None): diff --git a/community/lms/doctype/lms_quiz/lms_quiz.json b/community/lms/doctype/lms_quiz/lms_quiz.json index 3eb1222d..85cd2c42 100644 --- a/community/lms/doctype/lms_quiz/lms_quiz.json +++ b/community/lms/doctype/lms_quiz/lms_quiz.json @@ -7,7 +7,8 @@ "engine": "InnoDB", "field_order": [ "title", - "questions" + "questions", + "lesson" ], "fields": [ { @@ -21,11 +22,17 @@ "fieldtype": "Table", "label": "Questions", "options": "LMS Quiz Question" + }, + { + "fieldname": "lesson", + "fieldtype": "Link", + "label": "Lesson", + "options": "Lesson" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-06-07 12:22:37.333289", + "modified": "2021-06-23 17:58:57.642873", "modified_by": "Administrator", "module": "LMS", "name": "LMS Quiz", diff --git a/community/lms/doctype/lms_quiz/lms_quiz.py b/community/lms/doctype/lms_quiz/lms_quiz.py index 2d24280d..de002046 100644 --- a/community/lms/doctype/lms_quiz/lms_quiz.py +++ b/community/lms/doctype/lms_quiz/lms_quiz.py @@ -1,36 +1,79 @@ # Copyright (c) 2021, FOSS United and contributors # For license information, please see license.txt +from community.lms.doctype.lesson.lesson import update_progress import frappe from frappe.model.document import Document import json +from frappe import _ +from ..lesson.lesson import update_progress class LMSQuiz(Document): - pass + def validate(self): + self.validate_correct_answers() + + def validate_correct_answers(self): + for question in self.questions: + correct_options = self.get_correct_options(question) + + if len(correct_options) > 1: + question.multiple = 1 + + if not len(correct_options): + frappe.throw(_("At least one answer must be correct for this question: {0}").format(frappe.bold(question.question))) + + def get_correct_options(self, question): + correct_option_fields = ["is_correct_1", "is_correct_2", "is_correct_3", "is_correct_4"] + return list(filter(lambda x: question.get(x) == 1, correct_option_fields)) + + def get_last_submission_details(self): + """Returns the latest submission for this user. + """ + user = frappe.session.user + if not user or user == "Guest": + return + + result = frappe.get_all('LMS Quiz Submission', + fields="*", + filters={ + "owner": user, + "quiz": self.name + }, + order_by="creation desc", + page_length=1) + + if result: + return result[0] @frappe.whitelist() def submit(quiz, result): score = 0 + answer_map = { + "is_correct_1": "option_1", + "is_correct_2": "option_2", + "is_correct_3": "option_3", + "is_correct_4": "option_4" + } result = json.loads(result) quiz_details = frappe.get_doc("LMS Quiz", quiz) - print(result, type(result)) + for response in result: match = list(filter(lambda x: x.question == response.get("question"), quiz_details.questions))[0] - response["users_response"] = ("").join([ ans for ans in response.get("answer") ]).replace(" ", ", ") - del response["answer"] - print(response.get("users_response"), match.answer) - if response.get("users_response") == match.answer: + correct_options = quiz_details.get_correct_options(match) + correct_answers = [ match.get(answer_map[option]) for option in correct_options ] + + if response.get("answer") == correct_answers: response["result"] = "Right" score += 1 else: response["result"] = "Wrong" + response["answer"] = ("").join([ ans if idx == len(response.get("answer")) -1 else ans + ", " for idx, ans in enumerate(response.get("answer")) ]) frappe.get_doc({ "doctype": "LMS Quiz Submission", "quiz": quiz, "result": result, "score": score - }).save() + }).save(ignore_permissions=True) + update_progress(quiz_details.lesson) return score - - diff --git a/community/lms/doctype/lms_quiz_question/lms_quiz_question.json b/community/lms/doctype/lms_quiz_question/lms_quiz_question.json index d861b138..b42c6bad 100644 --- a/community/lms/doctype/lms_quiz_question/lms_quiz_question.json +++ b/community/lms/doctype/lms_quiz_question/lms_quiz_question.json @@ -6,41 +6,107 @@ "engine": "InnoDB", "field_order": [ "question", - "options", - "answer", - "multiple_correct_answers" + "options_section", + "option_1", + "is_correct_1", + "section_break_5", + "option_2", + "is_correct_2", + "column_break_4", + "option_3", + "is_correct_3", + "section_break_11", + "option_4", + "is_correct_4", + "multiple" ], "fields": [ { "fieldname": "question", "fieldtype": "Text", "in_list_view": 1, - "label": "Question" + "label": "Question", + "reqd": 1 }, { - "fieldname": "options", - "fieldtype": "Text", - "in_list_view": 1, - "label": "Options" + "fieldname": "option_1", + "fieldtype": "Data", + "label": "Option 1", + "reqd": 1 + }, + { + "fieldname": "option_2", + "fieldtype": "Data", + "label": "Option 2", + "reqd": 1 + }, + { + "fieldname": "option_3", + "fieldtype": "Data", + "label": "Option 3" + }, + { + "fieldname": "option_4", + "fieldtype": "Data", + "label": "Option 4" }, { "default": "0", - "fieldname": "multiple_correct_answers", + "depends_on": "option_1", + "fieldname": "is_correct_1", "fieldtype": "Check", - "in_list_view": 1, - "label": "Multiple Correct Answers" + "label": "Is Correct" }, { - "fieldname": "answer", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Answer" + "default": "0", + "depends_on": "option_2", + "fieldname": "is_correct_2", + "fieldtype": "Check", + "label": "Is Correct" + }, + { + "default": "0", + "depends_on": "option_3", + "fieldname": "is_correct_3", + "fieldtype": "Check", + "label": "Is Correct" + }, + { + "default": "0", + "depends_on": "option_4", + "fieldname": "is_correct_4", + "fieldtype": "Check", + "label": "Is Correct" + }, + { + "default": "0", + "fieldname": "multiple", + "fieldtype": "Check", + "hidden": 1, + "label": "Multiple Correct Answers", + "read_only": 1 + }, + { + "fieldname": "options_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-06-07 12:47:22.787160", + "modified": "2021-06-22 16:54:13.133859", "modified_by": "Administrator", "module": "LMS", "name": "LMS Quiz Question", diff --git a/community/lms/doctype/lms_quiz_questions/__init__.py b/community/lms/doctype/lms_quiz_questions/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/community/lms/doctype/lms_quiz_questions/lms_quiz_questions.json b/community/lms/doctype/lms_quiz_questions/lms_quiz_questions.json deleted file mode 100644 index 4e147664..00000000 --- a/community/lms/doctype/lms_quiz_questions/lms_quiz_questions.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "actions": [], - "creation": "2021-06-07 10:48:57.994714", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "question", - "options", - "multiple_correct_answers" - ], - "fields": [ - { - "fieldname": "question", - "fieldtype": "Data", - "label": "Question" - }, - { - "fieldname": "options", - "fieldtype": "Table", - "label": "Options", - "options": "LMS Option" - }, - { - "default": "0", - "fieldname": "multiple_correct_answers", - "fieldtype": "Check", - "label": "Multiple Correct Answers" - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-06-07 10:48:57.994714", - "modified_by": "Administrator", - "module": "LMS", - "name": "LMS Quiz Questions", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/community/lms/doctype/lms_quiz_questions/lms_quiz_questions.py b/community/lms/doctype/lms_quiz_questions/lms_quiz_questions.py deleted file mode 100644 index f1d7948e..00000000 --- a/community/lms/doctype/lms_quiz_questions/lms_quiz_questions.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2021, FOSS United and contributors -# For license information, please see license.txt - -# import frappe -from frappe.model.document import Document - -class LMSQuizQuestions(Document): - pass diff --git a/community/lms/doctype/lms_quiz_result/lms_quiz_result.json b/community/lms/doctype/lms_quiz_result/lms_quiz_result.json index 12346e2f..255b8a9a 100644 --- a/community/lms/doctype/lms_quiz_result/lms_quiz_result.json +++ b/community/lms/doctype/lms_quiz_result/lms_quiz_result.json @@ -6,7 +6,7 @@ "engine": "InnoDB", "field_order": [ "question", - "users_response", + "answer", "result" ], "fields": [ @@ -16,24 +16,24 @@ "in_list_view": 1, "label": "Question" }, - { - "fieldname": "users_response", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Users Response" - }, { "fieldname": "result", "fieldtype": "Select", "in_list_view": 1, "label": "Result", "options": "Right\nWrong" + }, + { + "fieldname": "answer", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Users Response" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-06-07 14:21:07.768039", + "modified": "2021-06-22 18:32:28.813159", "modified_by": "Administrator", "module": "LMS", "name": "LMS Quiz Result", diff --git a/community/lms/widgets/BatchTabs.html b/community/lms/widgets/BatchTabs.html index 1b6d61d9..07c4c71a 100644 --- a/community/lms/widgets/BatchTabs.html +++ b/community/lms/widgets/BatchTabs.html @@ -2,16 +2,17 @@ Courses /{% if course.is_mentor(frappe.session.user) %} {{ course.title }} {% else %} {{ course.title }} {% endif %} - {% set all_memberships = course.get_all_memberships() %} - {% if all_memberships | length > 1 %} - - Switch Batch + {% set all_memberships = course.get_all_memberships(frappe.session.user) %} + {% if membership and membership.batch and all_memberships | length > 1 %} + + {{ membership.batch_title }}
@@ -28,7 +29,8 @@ Home{{ submission.solution if submission else exercise.code }}
+