From 5fd1143f76eeb07965f6b23eed127a3864b4917e Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 2 Jun 2021 13:52:50 +0530 Subject: [PATCH 1/3] feat: lesson progress --- community/lms/api.py | 2 +- community/lms/doctype/exercise/exercise.py | 5 ++ community/lms/doctype/lesson/lesson.py | 51 ++++++++++++ .../doctype/lms_course_progress/__init__.py | 0 .../lms_course_progress.js | 8 ++ .../lms_course_progress.json | 78 ++++++++++++++++++ .../lms_course_progress.py | 8 ++ .../test_lms_course_progress.py | 8 ++ community/lms/widgets/ChapterTeaser.html | 3 + community/lms/widgets/CourseOutline.html | 2 +- community/public/css/style.css | 33 ++++++++ community/public/css/style.less | 2 +- community/public/images/Vector.png | Bin 0 -> 206 bytes community/www/batch/home.html | 2 +- community/www/batch/learn.html | 2 +- community/www/batch/learn.js | 11 +++ 16 files changed, 210 insertions(+), 5 deletions(-) create mode 100644 community/lms/doctype/lms_course_progress/__init__.py create mode 100644 community/lms/doctype/lms_course_progress/lms_course_progress.js create mode 100644 community/lms/doctype/lms_course_progress/lms_course_progress.json create mode 100644 community/lms/doctype/lms_course_progress/lms_course_progress.py create mode 100644 community/lms/doctype/lms_course_progress/test_lms_course_progress.py create mode 100644 community/public/images/Vector.png create mode 100644 community/www/batch/learn.js diff --git a/community/lms/api.py b/community/lms/api.py index 9cf2fc9d..1ed198e1 100644 --- a/community/lms/api.py +++ b/community/lms/api.py @@ -43,7 +43,7 @@ def save_current_lesson(batch_name, lesson_name): doctype="LMS Batch Membership", filters={ "batch": batch_name, - "member_email": frappe.session.user + "member": frappe.session.user }, fieldname="name") if not name: diff --git a/community/lms/doctype/exercise/exercise.py b/community/lms/doctype/exercise/exercise.py index 151b993a..7b7439d6 100644 --- a/community/lms/doctype/exercise/exercise.py +++ b/community/lms/doctype/exercise/exercise.py @@ -4,6 +4,7 @@ import frappe from frappe.model.document import Document from ..lms_sketch.livecode import livecode_to_svg +from ..lesson.lesson import update_progress class Exercise(Document): def before_save(self): @@ -55,5 +56,9 @@ class Exercise(Document): image=image, solution=code) doc.insert(ignore_permissions=True) + + if not course.is_mentor(frappe.session.user): + update_progress(self.lesson) + return doc diff --git a/community/lms/doctype/lesson/lesson.py b/community/lms/doctype/lesson/lesson.py index 979515ea..f22592ca 100644 --- a/community/lms/doctype/lesson/lesson.py +++ b/community/lms/doctype/lesson/lesson.py @@ -43,3 +43,54 @@ class Lesson(Document): The return value would be like 1.2, 2.1 etc. It will be None if there is no next lesson. """ + + def get_progress(self): + return frappe.db.get_value("LMS Course Progress", {"lesson": self.name, "owner": frappe.session.user}, "status") + + def get_slugified_class(self): + if self.get_progress(): + return ("").join([ s for s in self.get_progress().lower().split() ]) + return + +@frappe.whitelist() +def save_progress(lesson): + if frappe.db.exists("LMS Course Progress", + { + "lesson": lesson, + "owner": frappe.session.user + }): + return + + lesson_details = frappe.get_doc("Lesson", lesson) + dynamic_content = frappe.db.count("LMS Section", + filters={ + "type": ["not in", ["example", "text"]], + "parent": lesson_details.name + }) + + status = "Complete" + if dynamic_content: + status = "Partially Complete" + + frappe.get_doc({ + "doctype": "LMS Course Progress", + "lesson": lesson_details.name, + "status": status + }).save(ignore_permissions=True) + +def update_progress(lesson): + user = frappe.session.user + if not all_dynamic_content_submitted(lesson, user): + return + course_progress = frappe.get_doc("LMS Course Progress", {"lesson": lesson, "owner": user}) + course_progress.status = "Complete" + course_progress.save() + +def all_dynamic_content_submitted(lesson, user): + exercises = frappe.get_all("Exercise", {"lesson": lesson}, ["name"]) + all_exercises_submitted = True + for exercise in exercises: + if not frappe.db.count("Exercise Submission", {"exercise": exercise.name, "owner": user}): + all_exercises_submitted = False + + return all_exercises_submitted diff --git a/community/lms/doctype/lms_course_progress/__init__.py b/community/lms/doctype/lms_course_progress/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/community/lms/doctype/lms_course_progress/lms_course_progress.js b/community/lms/doctype/lms_course_progress/lms_course_progress.js new file mode 100644 index 00000000..77ef3c16 --- /dev/null +++ b/community/lms/doctype/lms_course_progress/lms_course_progress.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, FOSS United and contributors +// For license information, please see license.txt + +frappe.ui.form.on('LMS Course Progress', { + // refresh: function(frm) { + + // } +}); diff --git a/community/lms/doctype/lms_course_progress/lms_course_progress.json b/community/lms/doctype/lms_course_progress/lms_course_progress.json new file mode 100644 index 00000000..33d785f6 --- /dev/null +++ b/community/lms/doctype/lms_course_progress/lms_course_progress.json @@ -0,0 +1,78 @@ +{ + "actions": [], + "creation": "2021-05-31 17:20:13.388453", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "status", + "column_break_3", + "lesson", + "chapter", + "course" + ], + "fields": [ + { + "fetch_from": "chapter.course", + "fieldname": "course", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Course", + "options": "LMS Course", + "read_only": 1 + }, + { + "fetch_from": "lesson.chapter", + "fieldname": "chapter", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Chapter", + "options": "Chapter", + "read_only": 1 + }, + { + "fieldname": "lesson", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Lesson", + "options": "Lesson" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Complete\nPartially Complete\nIncomplete" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-06-02 13:05:31.114939", + "modified_by": "Administrator", + "module": "LMS", + "name": "LMS Course Progress", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/community/lms/doctype/lms_course_progress/lms_course_progress.py b/community/lms/doctype/lms_course_progress/lms_course_progress.py new file mode 100644 index 00000000..98a022b7 --- /dev/null +++ b/community/lms/doctype/lms_course_progress/lms_course_progress.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, FOSS United and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class LMSCourseProgress(Document): + pass diff --git a/community/lms/doctype/lms_course_progress/test_lms_course_progress.py b/community/lms/doctype/lms_course_progress/test_lms_course_progress.py new file mode 100644 index 00000000..9eabd5d3 --- /dev/null +++ b/community/lms/doctype/lms_course_progress/test_lms_course_progress.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, FOSS United and Contributors +# See license.txt + +# import frappe +import unittest + +class TestLMSCourseProgress(unittest.TestCase): + pass diff --git a/community/lms/widgets/ChapterTeaser.html b/community/lms/widgets/ChapterTeaser.html index 49f3af78..1678b1bb 100644 --- a/community/lms/widgets/ChapterTeaser.html +++ b/community/lms/widgets/ChapterTeaser.html @@ -8,6 +8,9 @@ {% for lesson in chapter.get_lessons() %}
{{ lesson.title }} + {% if show_progress and not course.is_mentor(frappe.session.user) and lesson.get_progress() %} + {{ lesson.get_progress() }} + {% endif %}
{% endfor %} diff --git a/community/lms/widgets/CourseOutline.html b/community/lms/widgets/CourseOutline.html index 9dd26835..249013ff 100644 --- a/community/lms/widgets/CourseOutline.html +++ b/community/lms/widgets/CourseOutline.html @@ -1,5 +1,5 @@

Course Outline

{% for chapter in course.get_chapters() %} -{{ widgets.ChapterTeaser(index=loop.index, chapter=chapter, course=course, batch=batch, show_link=show_link)}} +{{ widgets.ChapterTeaser(index=loop.index, chapter=chapter, course=course, batch=batch, show_link=show_link, show_progress=show_progress)}} {% endfor %} diff --git a/community/public/css/style.css b/community/public/css/style.css index 60eb4ed2..699d8dfc 100644 --- a/community/public/css/style.css +++ b/community/public/css/style.css @@ -238,3 +238,36 @@ section { .page-card .btn { margin-top: 30px; } + +.partiallycomplete { + background: #FEF4E2; + color: #976417; +} + +.partiallycomplete img { + background: #976417; +} + +.complete { + background: #EAF5EE; + color: #38A160; +} + +.complete img { + background: #38A160; +} + +.incomplete { + background: #FEECEC; + color: #E24C4C; +} + +.incomplete img { + background: #E24C4C; +} + +.progress-image { + margin-right: 3px; + border-radius: 50px; + padding: 5px; +} diff --git a/community/public/css/style.less b/community/public/css/style.less index f95f643c..c265dafe 100644 --- a/community/public/css/style.less +++ b/community/public/css/style.less @@ -285,7 +285,7 @@ section.lightgray { } .lesson-teaser { - line-height: 35px; + line-height: 40px; } #hero h1 { diff --git a/community/public/images/Vector.png b/community/public/images/Vector.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3cbc7f216d7b9986fb24adfa3f949b4977335d GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CF!3HE7boT!OQk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFKz_2Pi(^Oy{{ batch.title }}
- {{ widgets.CourseOutline(course=course, batch=batch, show_link=True) }} + {{ widgets.CourseOutline(course=course, batch=batch, show_link=True, show_progress=True) }}

Batch Schedule

diff --git a/community/www/batch/learn.html b/community/www/batch/learn.html index 36136ddb..9eae61bd 100644 --- a/community/www/batch/learn.html +++ b/community/www/batch/learn.html @@ -28,7 +28,7 @@ {{ widgets.BatchTabs(course=course, batch=batch) }}
-

{{ lesson.title }}

+

{{ lesson.title }}

{% for s in lesson.get_sections() %}
diff --git a/community/www/batch/learn.js b/community/www/batch/learn.js new file mode 100644 index 00000000..c3a6519d --- /dev/null +++ b/community/www/batch/learn.js @@ -0,0 +1,11 @@ +frappe.ready(() => { + console.log($(".title").hasClass("is_mentor")) + if (!$(".title").hasClass("is_mentor")) { + frappe.call({ + method: "community.lms.doctype.lesson.lesson.save_progress", + args: { + lesson: $(".title").attr("data-name") + } + }) + } +}) From 4fd7af053be6ff65ca95b3fd8b8a8fd88c57f906 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 2 Jun 2021 16:47:17 +0530 Subject: [PATCH 2/3] fix: tests --- community/lms/doctype/exercise/exercise.py | 2 +- .../web_form/add_a_new_batch/add_a_new_batch.json | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/community/lms/doctype/exercise/exercise.py b/community/lms/doctype/exercise/exercise.py index 7b7439d6..ed92d173 100644 --- a/community/lms/doctype/exercise/exercise.py +++ b/community/lms/doctype/exercise/exercise.py @@ -57,7 +57,7 @@ class Exercise(Document): solution=code) doc.insert(ignore_permissions=True) - if not course.is_mentor(frappe.session.user): + if not (course.is_mentor(frappe.session.user) or frappe.flags.in_test): update_progress(self.lesson) return doc diff --git a/community/lms/web_form/add_a_new_batch/add_a_new_batch.json b/community/lms/web_form/add_a_new_batch/add_a_new_batch.json index e1785069..76735390 100644 --- a/community/lms/web_form/add_a_new_batch/add_a_new_batch.json +++ b/community/lms/web_form/add_a_new_batch/add_a_new_batch.json @@ -19,7 +19,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2021-04-30 11:22:18.188712", + "modified": "2021-06-02 15:52:06.383260", "modified_by": "Administrator", "module": "LMS", "name": "add-a-new-batch", @@ -38,13 +38,13 @@ { "allow_read_on_all_link_options": 0, "fieldname": "course", - "fieldtype": "Data", - "hidden": 0, + "fieldtype": "Link", + "hidden": 1, "label": "Course", "max_length": 0, "max_value": 0, - "options": "", - "read_only": 1, + "options": "LMS Course", + "read_only": 0, "reqd": 0, "show_in_filter": 0 }, @@ -111,4 +111,4 @@ "show_in_filter": 0 } ] -} +} \ No newline at end of file From 671b4a065091d191ed5239c5314eeff8c566e46e Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 2 Jun 2021 20:19:36 +0530 Subject: [PATCH 3/3] fix: api and orm --- community/lms/doctype/exercise/exercise.py | 4 --- .../exercise_submission.py | 9 ++++-- community/lms/doctype/lesson/lesson.py | 29 +++++++++++++------ community/www/batch/learn.html | 2 +- community/www/batch/learn.js | 4 +-- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/community/lms/doctype/exercise/exercise.py b/community/lms/doctype/exercise/exercise.py index ed92d173..09b5be8b 100644 --- a/community/lms/doctype/exercise/exercise.py +++ b/community/lms/doctype/exercise/exercise.py @@ -4,7 +4,6 @@ import frappe from frappe.model.document import Document from ..lms_sketch.livecode import livecode_to_svg -from ..lesson.lesson import update_progress class Exercise(Document): def before_save(self): @@ -57,8 +56,5 @@ class Exercise(Document): solution=code) doc.insert(ignore_permissions=True) - if not (course.is_mentor(frappe.session.user) or frappe.flags.in_test): - update_progress(self.lesson) - return doc diff --git a/community/lms/doctype/exercise_submission/exercise_submission.py b/community/lms/doctype/exercise_submission/exercise_submission.py index fd631eb8..886de170 100644 --- a/community/lms/doctype/exercise_submission/exercise_submission.py +++ b/community/lms/doctype/exercise_submission/exercise_submission.py @@ -1,8 +1,13 @@ # Copyright (c) 2021, FOSS United and contributors # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document +from ..lesson.lesson import update_progress class ExerciseSubmission(Document): - pass + + def after_insert(self): + course_details = frappe.get_doc("LMS Course", self.course) + if not (course_details.is_mentor(frappe.session.user) or frappe.flags.in_test): + update_progress(self.lesson) diff --git a/community/lms/doctype/lesson/lesson.py b/community/lms/doctype/lesson/lesson.py index f22592ca..5129e878 100644 --- a/community/lms/doctype/lesson/lesson.py +++ b/community/lms/doctype/lesson/lesson.py @@ -53,7 +53,13 @@ class Lesson(Document): return @frappe.whitelist() -def save_progress(lesson): +def save_progress(lesson, batch): + if not frappe.db.exists("LMS Batch Membership", + { + "member": frappe.session.user, + "batch": batch + }): + return if frappe.db.exists("LMS Course Progress", { "lesson": lesson, @@ -82,15 +88,20 @@ def update_progress(lesson): user = frappe.session.user if not all_dynamic_content_submitted(lesson, user): return - course_progress = frappe.get_doc("LMS Course Progress", {"lesson": lesson, "owner": user}) - course_progress.status = "Complete" - course_progress.save() + if frappe.db.exists("LMS Course Progress", {"lesson": lesson, "owner": user}): + course_progress = frappe.get_doc("LMS Course Progress", {"lesson": lesson, "owner": user}) + course_progress.status = "Complete" + course_progress.save() def all_dynamic_content_submitted(lesson, user): - exercises = frappe.get_all("Exercise", {"lesson": lesson}, ["name"]) - all_exercises_submitted = True - for exercise in exercises: - if not frappe.db.count("Exercise Submission", {"exercise": exercise.name, "owner": user}): - all_exercises_submitted = False + exercise_names = frappe.get_list("Exercise", {"lesson": lesson}, ["name"], pluck="name") + all_exercises_submitted = False + print(exercise_names) + query = { + "exercise": ["in", exercise_names], + "owner": user + } + if frappe.db.count("Exercise Submission", query) == len(exercise_names): + all_exercises_submitted = True return all_exercises_submitted diff --git a/community/www/batch/learn.html b/community/www/batch/learn.html index 9eae61bd..d0271c61 100644 --- a/community/www/batch/learn.html +++ b/community/www/batch/learn.html @@ -28,7 +28,7 @@ {{ widgets.BatchTabs(course=course, batch=batch) }}
-

{{ lesson.title }}

+

{{ lesson.title }}

{% for s in lesson.get_sections() %}
diff --git a/community/www/batch/learn.js b/community/www/batch/learn.js index c3a6519d..4410fb08 100644 --- a/community/www/batch/learn.js +++ b/community/www/batch/learn.js @@ -1,10 +1,10 @@ frappe.ready(() => { - console.log($(".title").hasClass("is_mentor")) if (!$(".title").hasClass("is_mentor")) { frappe.call({ method: "community.lms.doctype.lesson.lesson.save_progress", args: { - lesson: $(".title").attr("data-name") + lesson: $(".title").attr("data-name"), + batch: $(".title").attr("data-batch") } }) }