diff --git a/lms/hooks.py b/lms/hooks.py index ea03c729..52a1a34f 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -96,7 +96,7 @@ override_doctype_class = { doc_events = { "Discussion Reply": {"after_insert": "lms.lms.utils.create_notification_log"}, - "Course Lesson": {"on_update": "lms.lms.doctype.lms_quiz.lms_quiz.update_lesson_info"} + "Course Lesson": {"on_update": "lms.lms.doctype.lms_quiz.lms_quiz.update_lesson_info"}, } # Scheduled Tasks @@ -138,29 +138,40 @@ website_route_rules = [ {"from_route": "/courses/", "to_route": "courses/course"}, {"from_route": "/courses//", "to_route": "courses/certificate"}, {"from_route": "/courses//learn", "to_route": "batch/learn"}, - {"from_route": "/courses//learn/.", - "to_route": "batch/learn"}, + { + "from_route": "/courses//learn/.", + "to_route": "batch/learn", + }, {"from_route": "/quizzes", "to_route": "batch/quiz_list"}, {"from_route": "/quizzes/", "to_route": "batch/quiz"}, {"from_route": "/classes/", "to_route": "classes/class"}, {"from_route": "/courses//progress", "to_route": "batch/progress"}, {"from_route": "/courses//join", "to_route": "batch/join"}, {"from_route": "/courses//manage", "to_route": "cohorts"}, - {"from_route": "/courses//cohorts/", - "to_route": "cohorts/cohort"}, - {"from_route": "/courses//cohorts//", - "to_route": "cohorts/cohort"}, - {"from_route": "/courses//subgroups//", - "to_route": "cohorts/subgroup"}, - {"from_route": "/courses//subgroups///", - "to_route": "cohorts/subgroup"}, - {"from_route": "/courses//join///", - "to_route": "cohorts/join"}, + {"from_route": "/courses//cohorts/", "to_route": "cohorts/cohort"}, + { + "from_route": "/courses//cohorts//", + "to_route": "cohorts/cohort", + }, + { + "from_route": "/courses//subgroups//", + "to_route": "cohorts/subgroup", + }, + { + "from_route": "/courses//subgroups///", + "to_route": "cohorts/subgroup", + }, + { + "from_route": "/courses//join///", + "to_route": "cohorts/join", + }, {"from_route": "/users", "to_route": "profiles/profile"}, {"from_route": "/jobs/", "to_route": "jobs/job"}, - {"from_route": "/classes//students/", - "to_route": "/classes/progress"}, - {"from_route": "/assignments/", "to_route": "assignments/assignment"} + { + "from_route": "/classes//students/", + "to_route": "/classes/progress", + }, + {"from_route": "/assignments/", "to_route": "assignments/assignment"}, ] website_redirects = [ diff --git a/lms/lms/doctype/class_course/class_course.py b/lms/lms/doctype/class_course/class_course.py index 17aac44b..64b9f410 100644 --- a/lms/lms/doctype/class_course/class_course.py +++ b/lms/lms/doctype/class_course/class_course.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class ClassCourse(Document): pass diff --git a/lms/lms/doctype/class_student/class_student.js b/lms/lms/doctype/class_student/class_student.js index 20668248..69567d23 100644 --- a/lms/lms/doctype/class_student/class_student.js +++ b/lms/lms/doctype/class_student/class_student.js @@ -1,8 +1,7 @@ // Copyright (c) 2022, Frappe and contributors // For license information, please see license.txt -frappe.ui.form.on('Class Student', { +frappe.ui.form.on("Class Student", { // refresh: function(frm) { - // } }); diff --git a/lms/lms/doctype/class_student/class_student.py b/lms/lms/doctype/class_student/class_student.py index 74c8df36..cfe37fa1 100644 --- a/lms/lms/doctype/class_student/class_student.py +++ b/lms/lms/doctype/class_student/class_student.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class ClassStudent(Document): pass diff --git a/lms/lms/doctype/lesson_assignment/lesson_assignment.py b/lms/lms/doctype/lesson_assignment/lesson_assignment.py index 426ee0ae..83507aba 100644 --- a/lms/lms/doctype/lesson_assignment/lesson_assignment.py +++ b/lms/lms/doctype/lesson_assignment/lesson_assignment.py @@ -12,7 +12,8 @@ class LessonAssignment(Document): def validate_duplicates(self): if frappe.db.exists( - "Lesson Assignment", {"lesson": self.lesson, "member": self.member, "name": ["!=", self.name]} + "Lesson Assignment", + {"lesson": self.lesson, "member": self.member, "name": ["!=", self.name]}, ): lesson_title = frappe.db.get_value("Course Lesson", self.lesson, "title") frappe.throw( @@ -50,3 +51,11 @@ def get_assignment(lesson): "File", {"file_url": assignment.assignment}, "file_name" ) return assignment + + +@frappe.whitelist() +def grade_assignment(name, result, comments): + doc = frappe.get_doc("Lesson Assignment", name) + doc.status = result + doc.comments = comments + doc.save() diff --git a/lms/lms/doctype/lms_class/lms_class.js b/lms/lms/doctype/lms_class/lms_class.js index dca66f76..78508cbf 100644 --- a/lms/lms/doctype/lms_class/lms_class.js +++ b/lms/lms/doctype/lms_class/lms_class.js @@ -1,8 +1,7 @@ // Copyright (c) 2022, Frappe and contributors // For license information, please see license.txt -frappe.ui.form.on('LMS Class', { +frappe.ui.form.on("LMS Class", { // refresh: function(frm) { - // } }); diff --git a/lms/lms/doctype/lms_class/lms_class.py b/lms/lms/doctype/lms_class/lms_class.py index d7495aff..281bbe78 100644 --- a/lms/lms/doctype/lms_class/lms_class.py +++ b/lms/lms/doctype/lms_class/lms_class.py @@ -6,8 +6,8 @@ from frappe.model.document import Document from frappe import _ from frappe.utils import cint -class LMSClass(Document): +class LMSClass(Document): def validate(self): validate_membership(self) @@ -18,7 +18,7 @@ def validate_membership(self): filters = { "doctype": "LMS Batch Membership", "member": student.student, - "course": course.course + "course": course.course, } if not frappe.db.exists(filters): frappe.get_doc(filters).save() @@ -29,36 +29,37 @@ def add_student(email, class_name): if not frappe.db.exists("User", email): frappe.throw(_("There is no such user. Please create a user with this Email ID.")) - frappe.get_doc({ - "doctype": "Class Student", - "student": email, - "student_name": frappe.db.get_value("User", email, "full_name"), - "parent": class_name, - "parenttype": "LMS Class", - "parentfield": "students" - }).save() + frappe.get_doc( + { + "doctype": "Class Student", + "student": email, + "student_name": frappe.db.get_value("User", email, "full_name"), + "parent": class_name, + "parenttype": "LMS Class", + "parentfield": "students", + } + ).save() return True @frappe.whitelist() def remove_student(student, class_name): - frappe.db.delete("Class Student", { - "student": student, - "parent": class_name - }) + frappe.db.delete("Class Student", {"student": student, "parent": class_name}) return True @frappe.whitelist() def update_course(class_name, course, value): if cint(value): - doc = frappe.get_doc({ + doc = frappe.get_doc( + { "doctype": "Class Course", "parent": class_name, "course": course, "parenttype": "LMS Class", - "parentfield": "courses" - }) + "parentfield": "courses", + } + ) doc.save() else: frappe.db.delete("Class Course", {"parent": class_name, "course": course}) diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.py b/lms/lms/doctype/lms_quiz/lms_quiz.py index 539eb33d..6d5b4a47 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.py +++ b/lms/lms/doctype/lms_quiz/lms_quiz.py @@ -62,10 +62,10 @@ class LMSQuiz(Document): def update_lesson_info(doc, method): if doc.quiz_id: - frappe.db.set_value("LMS Quiz", doc.quiz_id, { - "lesson": doc.name, - "course": doc.course - }) + frappe.db.set_value( + "LMS Quiz", doc.quiz_id, {"lesson": doc.name, "course": doc.course} + ) + @frappe.whitelist() def quiz_summary(quiz, results): diff --git a/lms/lms/web_form/class/class.js b/lms/lms/web_form/class/class.js index 699703c5..8f56ebb3 100644 --- a/lms/lms/web_form/class/class.js +++ b/lms/lms/web_form/class/class.js @@ -1,3 +1,3 @@ -frappe.ready(function() { +frappe.ready(function () { // bind events here -}) \ No newline at end of file +}); diff --git a/lms/lms/web_form/class/class.py b/lms/lms/web_form/class/class.py index e1ada619..80b7b873 100644 --- a/lms/lms/web_form/class/class.py +++ b/lms/lms/web_form/class/class.py @@ -1,5 +1,6 @@ import frappe + def get_context(context): # do your magic here pass diff --git a/lms/patches/v0_0/change_role_names.py b/lms/patches/v0_0/change_role_names.py index 9653ef6f..3e01336f 100644 --- a/lms/patches/v0_0/change_role_names.py +++ b/lms/patches/v0_0/change_role_names.py @@ -1,5 +1,6 @@ import frappe + def execute(): frappe.rename_doc("Role", "Course Instructor", "Instructor") frappe.rename_doc("Role", "Course Moderator", "Moderator") diff --git a/lms/plugins.py b/lms/plugins.py index f20f94dc..f542143f 100644 --- a/lms/plugins.py +++ b/lms/plugins.py @@ -152,7 +152,8 @@ def assignment_renderer(detail): file_type = detail.split("-")[1] accept = supported_types[file_type] if file_type else "" return frappe.render_template( - "templates/assignment.html", {"question": question, "accept": accept, "file_type": file_type} + "templates/assignment.html", + {"question": question, "accept": accept, "file_type": file_type}, ) diff --git a/lms/public/css/style.css b/lms/public/css/style.css index 5c7b7ca2..acd82e96 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -895,6 +895,7 @@ pre { .column-card { flex-direction: column; padding: 1.25rem; + height: 100%; } .empty-state { @@ -1674,9 +1675,9 @@ li { } .indicator-pill::before { - width: 0; - height: 0; - margin-right: 0; + width: 0 !important; + height: 0 !important; + margin-right: 0 !important; } .role { @@ -1855,4 +1856,22 @@ select { color: var(--gray-900); font-weight: 500; border-bottom: 1px solid var(--gray-300); + border-top: none; +} + +.lms-dropdown { + border: 1px solid var(--gray-400); + border-radius: var(--border-radius-sm); + padding: 0.25rem 2rem; + cursor: pointer; + text-align: center; +} + +.lms-menu { + background-image: url(/assets/lms/icons/down-arrow.svg); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 0.75rem; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; } diff --git a/lms/public/js/common_functions.js b/lms/public/js/common_functions.js index 542c32e3..9b15f6a9 100644 --- a/lms/public/js/common_functions.js +++ b/lms/public/js/common_functions.js @@ -205,7 +205,6 @@ const render_chart = (data, chart_name, element, type) => { }); }; - const generate_course_completion_graph = () => { frappe.call({ method: "lms.lms.utils.get_course_completion_data", @@ -220,12 +219,10 @@ const generate_course_completion_graph = () => { }); }; - const change_hash = (e) => { window.location.hash = $(e.currentTarget).attr("href"); }; - const open_tab = () => { $(`a[href="${window.location.hash}"]`).click(); }; diff --git a/lms/www/assignments/__init__.py b/lms/www/assignments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lms/www/assignments/assignment.html b/lms/www/assignments/assignment.html index 7165a6c8..f56cb38a 100644 --- a/lms/www/assignments/assignment.html +++ b/lms/www/assignments/assignment.html @@ -8,26 +8,53 @@
-
{{ _("Save") }}
-
{{ _("Assignments") }}
-
-
- -
- +
{{ _("Assignment Grading") }}
+
+ + {{ _("Open Attachment") }} + + + +
+ + {{ _("Member") }}: + + + {{ assignment.member_name }} + +
+
+ {% set course_title = frappe.db.get_value("LMS Course", assignment.course, "title") %} + + {{ _("Course") }}: + + + {{ course_title }} + +
+
+ {% set lesson_title = frappe.db.get_value("Course Lesson", assignment.lesson, "title") %} + {{ _("Lesson") }}: + + {{ lesson_title }} + +
+ +
{{ _("Comments") }}:
+
+
+
-
-
{% if assignment.comments %}{{ assignment.comments }}{% endif %}
+ +
+ {{ _("Save") }}
diff --git a/lms/www/assignments/assignment.js b/lms/www/assignments/assignment.js new file mode 100644 index 00000000..7995e672 --- /dev/null +++ b/lms/www/assignments/assignment.js @@ -0,0 +1,50 @@ +frappe.ready(() => { + + this.result; + let self = this; + + set_result(); + + $("#save-assignment").click((e) => { + save_assignment(e); + }); + + $("#result").change((e) => { + $("#result option:selected").each(function () { + self.result = $(this).val(); + }); + }); + +}); + + +const set_result = () => { + let self = this; + let result = $("#result").data("type"); + if (result) { + $("#result option").each((i, elem) => { + if ($(elem).val() == result) { + $(elem).attr("selected", true); + self.result = result; + } + }); + } +}; + + +const save_assignment = (e) => { + frappe.call({ + method: "lms.lms.doctype.lesson_assignment.lesson_assignment.grade_assignment", + args: { + "name": $(e.currentTarget).data("assignment"), + "result": self.result, + "comments": $("#comments").val(), + }, + callback: (data) => { + frappe.show_alert({ + message: __("Saved"), + indicator: "green", + }); + } + }) +} diff --git a/lms/www/assignments/assignment.py b/lms/www/assignments/assignment.py index ebf38eed..dbbd5509 100644 --- a/lms/www/assignments/assignment.py +++ b/lms/www/assignments/assignment.py @@ -2,6 +2,7 @@ import frappe from lms.lms.utils import has_course_moderator_role from frappe import _ + def get_context(context): context.no_cache = 1 assignment = frappe.form_dict["assignment"] @@ -13,4 +14,9 @@ def get_context(context): raise frappe.PermissionError(_(message)) - context.assignment = frappe.db.get_value("Lesson Assignment", assignment, ["assignment", "comments", "status", "name"], as_dict=True) + context.assignment = frappe.db.get_value( + "Lesson Assignment", + assignment, + ["assignment", "comments", "status", "name", "member_name", "course", "lesson"], + as_dict=True, + ) diff --git a/lms/www/classes/class.html b/lms/www/classes/class.html index 31f650fe..d2e1ccd2 100644 --- a/lms/www/classes/class.html +++ b/lms/www/classes/class.html @@ -33,9 +33,11 @@
{{ class_info.title }}
+ {% if class_info.description %}
{{ class_info.description }}
+ {% endif %}
{% if class_info.start_date %} @@ -108,18 +110,19 @@ {% if class_students | length %}
{% for student in class_students %} -
- {{ student.student_name }} + {% if not loop.last %}
{% endif %} {% endfor %}
{% else %} -

{{ _("No Students are added to this class.") }}

+

{{ _("No Students are added to this class.") }}

{% endif %}
{% endmacro %} diff --git a/lms/www/classes/class.js b/lms/www/classes/class.js index 3e9ce267..23b9c3ca 100644 --- a/lms/www/classes/class.js +++ b/lms/www/classes/class.js @@ -1,5 +1,4 @@ frappe.ready(() => { - $("#submit-student").click((e) => { submit_student(e); }); @@ -11,57 +10,61 @@ frappe.ready(() => { $(".class-course").click((e) => { update_course(e); }); - }); - const submit_student = (e) => { e.preventDefault(); frappe.call({ method: "lms.lms.doctype.lms_class.lms_class.add_student", args: { - "email": $("#student-email").val(), - "class_name": $(".class-details").data("class") + email: $("#student-email").val(), + class_name: $(".class-details").data("class"), }, callback: (data) => { - frappe.show_alert({ + frappe.show_alert( + { message: __("Student added successfully"), indicator: "green", - } ,3); + }, + 3 + ); window.location.reload(); - } - }) + }, + }); }; - const remove_student = (e) => { - frappe.confirm("Are you sure you want to remove this student from the class?", - () => { - frappe.call({ - method: "lms.lms.doctype.lms_class.lms_class.remove_student", - args: { - "student": $(e.currentTarget).data("student"), - "class_name": $(".class-details").data("class") - }, - callback: (data) => { - frappe.show_alert({ - message: __("Student removed successfully"), - indicator: "green", - }, 3); - window.location.reload(); - } - }); - }) -} - + frappe.confirm( + "Are you sure you want to remove this student from the class?", + () => { + frappe.call({ + method: "lms.lms.doctype.lms_class.lms_class.remove_student", + args: { + student: $(e.currentTarget).data("student"), + class_name: $(".class-details").data("class"), + }, + callback: (data) => { + frappe.show_alert( + { + message: __("Student removed successfully"), + indicator: "green", + }, + 3 + ); + window.location.reload(); + }, + }); + } + ); +}; const update_course = (e) => { frappe.call({ method: "lms.lms.doctype.lms_class.lms_class.update_course", args: { - "course": $(e.currentTarget).data("course"), - "value": $(e.currentTarget).children("input").prop("checked") ? 1 : 0, - "class_name": $(".class-details").data("class") - } - }) + course: $(e.currentTarget).data("course"), + value: $(e.currentTarget).children("input").prop("checked") ? 1 : 0, + class_name: $(".class-details").data("class"), + }, + }); }; diff --git a/lms/www/classes/class.py b/lms/www/classes/class.py index 7eda4d07..14f598db 100644 --- a/lms/www/classes/class.py +++ b/lms/www/classes/class.py @@ -1,7 +1,6 @@ import frappe from lms.lms.utils import has_course_moderator_role from frappe import _ -from lms.lms.utils import has_course_moderator_role def get_context(context): @@ -16,13 +15,20 @@ def get_context(context): class_name = frappe.form_dict["classname"] - context.class_info = frappe.db.get_value("LMS Class", class_name, ["name", "title", "start_date", "end_date", "description"], as_dict=True) - context.published_courses = frappe.get_all("LMS Course", {"published": 1}, ["name", "title"]) + context.class_info = frappe.db.get_value( + "LMS Class", + class_name, + ["name", "title", "start_date", "end_date", "description"], + as_dict=True, + ) + context.published_courses = frappe.get_all( + "LMS Course", {"published": 1}, ["name", "title"] + ) - context.class_courses = frappe.get_all("Class Course", { - "parent": class_name - }, pluck="course") + context.class_courses = frappe.get_all( + "Class Course", {"parent": class_name}, pluck="course" + ) - context.class_students = frappe.get_all("Class Student", { - "parent": class_name - }, ["student", "student_name", "username"]) + context.class_students = frappe.get_all( + "Class Student", {"parent": class_name}, ["student", "student_name", "username"] + ) diff --git a/lms/www/classes/index.py b/lms/www/classes/index.py index 0f0c030f..7f5153f6 100644 --- a/lms/www/classes/index.py +++ b/lms/www/classes/index.py @@ -13,4 +13,6 @@ def get_context(context): raise frappe.PermissionError(_(message)) - context.classes = frappe.get_all("LMS Class", fields=["name", "title", "start_date", "end_date"]) + context.classes = frappe.get_all( + "LMS Class", fields=["name", "title", "start_date", "end_date"] + ) diff --git a/lms/www/classes/progress.html b/lms/www/classes/progress.html index 0fdacccc..0eb1f5b4 100644 --- a/lms/www/classes/progress.html +++ b/lms/www/classes/progress.html @@ -33,6 +33,7 @@ {% macro Progress(class_info, student) %}
{% for course in class_courses %} + {% set progress = course.membership.progress %}
{{ course.title }}
@@ -56,11 +57,12 @@ {{ _("Last Attempt Date") }} - {% for quiz in course.quizzes %} + {% for quiz in course.quizzes %} {% set filters = { "member": student.name, "course": course.course } %} {% set has_submitted = frappe.db.exists("LMS Quiz Submission", filters) %} {% set submission = frappe.db.get_value("LMS Quiz Submission", filters, ["score", "creation"], as_dict=True) %} + {% set total_questions = frappe.db.count("LMS Quiz Question", {"parent": quiz.name}) %} @@ -71,7 +73,7 @@ {% if has_submitted %} - {{ submission.score }} + {{ submission.score }}/{{ total_questions }} {{ frappe.utils.format_date(submission.creation, "medium") }} diff --git a/lms/www/classes/progress.py b/lms/www/classes/progress.py index ba2fd726..88b7397f 100644 --- a/lms/www/classes/progress.py +++ b/lms/www/classes/progress.py @@ -2,6 +2,7 @@ import frappe from lms.lms.utils import has_course_moderator_role from frappe import _ + def get_context(context): context.no_cache = 1 @@ -15,19 +16,31 @@ def get_context(context): student = frappe.form_dict["username"] classname = frappe.form_dict["classname"] - context.student = frappe.db.get_value("User", {"username": student}, ["first_name", "full_name", "name"], as_dict=True) - context.class_info = frappe.db.get_value("LMS Class", classname, ["name"], as_dict=True) + context.student = frappe.db.get_value( + "User", {"username": student}, ["first_name", "full_name", "name"], as_dict=True + ) + context.class_info = frappe.db.get_value( + "LMS Class", classname, ["name"], as_dict=True + ) - class_courses = frappe.get_all("Class Course", { - "parent": classname - }, ["course", "title"]) + class_courses = frappe.get_all( + "Class Course", {"parent": classname}, ["course", "title"] + ) for course in class_courses: - course.membership = frappe.db.get_value("LMS Batch Membership", { - "member": context.student.name, - "course": course.course - }, ["progress"], as_dict=True) - course.quizzes = frappe.get_all("LMS Quiz", {"course": course.course}, ["name", "title"]) - course.assignments = frappe.get_all("Course Lesson", {"course": course.course, "question": ["is", "set"]}, ["name", "title"]) + course.membership = frappe.db.get_value( + "LMS Batch Membership", + {"member": context.student.name, "course": course.course}, + ["progress"], + as_dict=True, + ) + course.quizzes = frappe.get_all( + "LMS Quiz", {"course": course.course}, ["name", "title"] + ) + course.assignments = frappe.get_all( + "Course Lesson", + {"course": course.course, "question": ["is", "set"]}, + ["name", "title"], + ) context.class_courses = class_courses diff --git a/lms/www/courses/index.js b/lms/www/courses/index.js index a78fd836..d6b84fca 100644 --- a/lms/www/courses/index.js +++ b/lms/www/courses/index.js @@ -1,6 +1,4 @@ -frappe.ready(() => { - -}); +frappe.ready(() => {}); const change_hash = (e) => { window.location.hash = $(e.currentTarget).attr("href");