From 0e1b91f1ec431af63fcb7afd58198e7a0a623ab9 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 26 Jun 2023 21:16:27 +0530 Subject: [PATCH] fix: validate duplication --- .../lms_assignment/lms_assignment.json | 3 +- lms/lms/doctype/lms_class/lms_class.py | 87 +++--- lms/patches.txt | 4 +- lms/public/css/style.css | 9 + lms/www/classes/class.html | 131 ++------- lms/www/classes/class.js | 252 +++++++++--------- lms/www/classes/class.py | 2 +- lms/www/quiz_submission/__init__.py | 0 8 files changed, 201 insertions(+), 287 deletions(-) create mode 100644 lms/www/quiz_submission/__init__.py diff --git a/lms/lms/doctype/lms_assignment/lms_assignment.json b/lms/lms/doctype/lms_assignment/lms_assignment.json index 0dde9649..6cb778a1 100644 --- a/lms/lms/doctype/lms_assignment/lms_assignment.json +++ b/lms/lms/doctype/lms_assignment/lms_assignment.json @@ -46,7 +46,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-06-23 12:34:21.314971", + "modified": "2023-06-26 18:09:29.809564", "modified_by": "Administrator", "module": "LMS", "name": "LMS Assignment", @@ -78,6 +78,7 @@ "write": 1 } ], + "show_title_field_in_link": 1, "sort_field": "modified", "sort_order": "DESC", "states": [], diff --git a/lms/lms/doctype/lms_class/lms_class.py b/lms/lms/doctype/lms_class/lms_class.py index 5c7a3cae..9001066f 100644 --- a/lms/lms/doctype/lms_class/lms_class.py +++ b/lms/lms/doctype/lms_class/lms_class.py @@ -15,7 +15,9 @@ class LMSClass(Document): def validate(self): if self.seat_count: self.validate_seats_left() + self.validate_duplicate_courses() self.validate_duplicate_students() + self.validate_duplicate_assessments() self.validate_membership() def validate_duplicate_students(self): @@ -28,6 +30,28 @@ class LMSClass(Document): ) ) + def validate_duplicate_courses(self): + courses = [row.course for row in self.courses] + duplicates = {course for course in courses if courses.count(course) > 1} + if len(duplicates): + title = frappe.db.get_value("LMS Course", next(iter(duplicates)), "title") + frappe.throw( + _("Course {0} has already been added to this class.").format(frappe.bold(title)) + ) + + def validate_duplicate_assessments(self): + assessments = [row.assessment_name for row in self.assessment] + duplicates = { + assessment for assessment in assessments if assessments.count(assessment) > 1 + } + if len(duplicates): + title = frappe.db.get_value("LMS Assessment", next(iter(duplicates)), "title") + frappe.throw( + _("Assessment {0} has already been added to this class.").format( + frappe.bold(next(iter(duplicates))) + ) + ) + def validate_membership(self): for course in self.courses: for student in self.students: @@ -44,51 +68,30 @@ class LMSClass(Document): frappe.throw(_("There are no seats available in this class.")) -@frappe.whitelist() -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.")) - - filters = { - "student": email, - "parent": class_name, - "parenttype": "LMS Class", - "parentfield": "students", - } - if frappe.db.exists("Class Student", filters): - frappe.throw( - _("Student {0} has already been added to this class.").format(frappe.bold(email)) - ) - - 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.only_for("Moderator") frappe.db.delete("Class Student", {"student": student, "parent": class_name}) @frappe.whitelist() def remove_course(course, parent): + frappe.only_for("Moderator") frappe.db.delete("Class Course", {"course": course, "parent": parent}) +@frappe.whitelist() +def remove_assessment(assessment, parent): + frappe.only_for("Moderator") + frappe.db.delete("LMS Assessment", {"assessment_name": assessment, "parent": parent}) + + @frappe.whitelist() def create_live_class( class_name, title, duration, date, time, timezone, auto_recording, description=None ): date = format_date(date, "yyyy-mm-dd", True) - + frappe.only_for("Moderator") payload = { "topic": title, "start_time": format_datetime(f"{date} {time}", "yyyy-MM-ddTHH:mm:ssZ"), @@ -165,6 +168,7 @@ def create_class( category=None, name=None, ): + frappe.only_for("Moderator") if name: class_details = frappe.get_doc("LMS Class", name) else: @@ -185,26 +189,3 @@ def create_class( ) class_details.save() return class_details - - -@frappe.whitelist() -def update_assessment(type, name, value, class_name): - if not has_course_moderator_role(): - return - - value = cint(value) - filters = { - "assessment_type": type, - "assessment_name": name, - "parent": class_name, - "parenttype": "LMS Class", - "parentfield": "assessment", - } - exists = frappe.db.exists("LMS Assessment", filters) - - if exists and not value: - frappe.db.delete("LMS Assessment", exists) - elif not exists and value: - doc = frappe.new_doc("LMS Assessment") - doc.update(filters) - doc.insert() diff --git a/lms/patches.txt b/lms/patches.txt index eb99f28e..22e92dab 100644 --- a/lms/patches.txt +++ b/lms/patches.txt @@ -53,4 +53,6 @@ lms.patches.v0_0.share_certificates execute:frappe.delete_doc("Web Form", "class", ignore_missing=True, force=True) lms.patches.v0_0.amend_course_and_lesson_editor_fields lms.patches.v0_0.convert_course_description_to_html #11-05-2023 -lms.patches.v1_0.rename_assignment_doctype \ No newline at end of file +lms.patches.v1_0.rename_assignment_doctype +execute:frappe.permissions.reset_perms("LMS Assignment") +execute:frappe.permissions.reset_perms("LMS Quiz") diff --git a/lms/public/css/style.css b/lms/public/css/style.css index d8afb208..7c6510be 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -2195,4 +2195,13 @@ select { display: grid; grid-template-columns: repeat(auto-fill, 150px); grid-gap: 1rem; +} + +.btn-remove-course { + opacity: 0; + margin-top: 0.25rem; +} + +.btn-remove-course:hover { + opacity: 1; } \ No newline at end of file diff --git a/lms/www/classes/class.html b/lms/www/classes/class.html index 2b415517..3193e05a 100644 --- a/lms/www/classes/class.html +++ b/lms/www/classes/class.html @@ -180,7 +180,15 @@ {% if class_courses | length %}
{% for course in class_courses %} - {{ widgets.CourseCard(course=course, read_only=False) }} +
+ {{ widgets.CourseCard(course=course, read_only=False) }} + +
+ {% endfor %}
{% else %} @@ -230,7 +238,7 @@ {% if is_moderator %}
- +
@@ -255,7 +263,7 @@ {{ student.assessments_graded }}
- {{ frappe.utils.format_datetime(student.last_active, "medium") }} + {{ frappe.utils.pretty_date(student.last_active) }}
{% if is_moderator %}
@@ -265,17 +273,7 @@
{% endif %} - - - - - {% endfor %} {% else %} @@ -297,107 +295,10 @@ {% endif %} - {{ AssessmentList(assessments) }} {% endmacro %} - -{% macro ManageAssessments() %} -{% if is_moderator %} - -{% endif %} -{% endmacro %} - {% macro AssessmentList(assessments) %} {% if assessments | length %}
@@ -410,6 +311,11 @@
{{ _("Type") }}
+
+ + + +
@@ -425,6 +331,11 @@
{{ assessment.assessment_type.split("LMS ")[1] }}
+
+ + + +
{% endfor %} diff --git a/lms/www/classes/class.js b/lms/www/classes/class.js index 5f361a81..d51442da 100644 --- a/lms/www/classes/class.js +++ b/lms/www/classes/class.js @@ -30,14 +30,13 @@ frappe.ready(() => { remove_course(e); }); + $(".btn-remove-assessment").click((e) => { + remove_assessment(e); + }); + $("#open-assessment-modal").click((e) => { e.preventDefault(); show_assessment_modal(); - /* $("#assessment-modal").modal("show"); */ - }); - - $(".assessment-item").click((e) => { - update_assessment(e); }); $(".btn-close").click((e) => { @@ -45,54 +44,6 @@ frappe.ready(() => { }); }); -const submit_student = (e) => { - e.preventDefault(); - if ($('input[data-fieldname="student_input"]').val()) { - frappe.call({ - method: "lms.lms.doctype.lms_class.lms_class.add_student", - args: { - email: $('input[data-fieldname="student_input"]').val(), - class_name: $(".class-details").data("class"), - }, - callback: (data) => { - frappe.show_alert( - { - message: __("Student added successfully"), - indicator: "green", - }, - 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(); - }, - }); - } - ); -}; - const create_live_class = (e) => { let class_name = $(".class-details").data("class"); frappe.call({ @@ -353,34 +304,38 @@ const show_course_modal = () => { ], primary_action_label: __("Add"), primary_action(values) { - frappe.call({ - method: "frappe.client.insert", - args: { - doc: { - doctype: "Class Course", - course: values.course, - parenttype: "LMS Class", - parentfield: "courses", - parent: $(".class-details").data("class"), - }, - }, - callback(r) { - frappe.show_alert( - { - message: __("Course Added"), - indicator: "green", - }, - 3 - ); - window.location.reload(); - }, - }); + add_course(values); course_modal.hide(); }, }); course_modal.show(); }; +const add_course = (values) => { + frappe.call({ + method: "frappe.client.insert", + args: { + doc: { + doctype: "Class Course", + course: values.course, + parenttype: "LMS Class", + parentfield: "courses", + parent: $(".class-details").data("class"), + }, + }, + callback(r) { + frappe.show_alert( + { + message: __("Course Added"), + indicator: "green", + }, + 2000 + ); + window.location.reload(); + }, + }); +}; + const remove_course = (e) => { frappe.confirm("Are you sure you want to remove this course?", () => { frappe.call({ @@ -395,7 +350,7 @@ const remove_course = (e) => { message: __("Course Removed"), indicator: "green", }, - 3 + 2000 ); window.location.reload(); }, @@ -420,34 +375,63 @@ const show_student_modal = () => { ], primary_action_label: __("Add"), primary_action(values) { - frappe.call({ - method: "frappe.client.insert", - args: { - doc: { - doctype: "Class Student", - student: values.student, - parenttype: "LMS Class", - parentfield: "students", - parent: $(".class-details").data("class"), - }, - }, - callback(r) { - frappe.show_alert( - { - message: __("Student Added"), - indicator: "green", - }, - 3 - ); - window.location.reload(); - }, - }); + add_student(values); student_modal.hide(); }, }); student_modal.show(); }; +const add_student = (values) => { + frappe.call({ + method: "frappe.client.insert", + args: { + doc: { + doctype: "Class Student", + student: values.student, + parenttype: "LMS Class", + parentfield: "students", + parent: $(".class-details").data("class"), + }, + }, + callback(r) { + frappe.show_alert( + { + message: __("Student Added"), + indicator: "green", + }, + 2000 + ); + 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", + }, + 2000 + ); + window.location.reload(); + }, + }); + } + ); +}; + const show_assessment_modal = (e) => { let assessment_modal = new frappe.ui.Dialog({ title: "Manage Assessments", @@ -476,31 +460,57 @@ const show_assessment_modal = (e) => { ], primary_action_label: __("Add"), primary_action(values) { - frappe.call({ - method: "frappe.client.insert", - args: { - doc: { - doctype: "LMS Assessment", - assessment_type: values.assessment_type, - assessment_name: values.assessment_name, - parenttype: "LMS Class", - parentfield: "assessments", - parent: $(".class-details").data("class"), - }, - }, - callback(r) { - frappe.show_alert( - { - message: __("Assessment Added"), - indicator: "green", - }, - 3 - ); - window.location.reload(); - }, - }); + add_addessment(values); assessment_modal.hide(); }, }); assessment_modal.show(); }; + +const add_addessment = (values) => { + frappe.call({ + method: "frappe.client.insert", + args: { + doc: { + doctype: "LMS Assessment", + assessment_type: values.assessment_type, + assessment_name: values.assessment_name, + parenttype: "LMS Class", + parentfield: "assessment", + parent: $(".class-details").data("class"), + }, + }, + callback(r) { + frappe.show_alert( + { + message: __("Assessment Added"), + indicator: "green", + }, + 2000 + ); + window.location.reload(); + }, + }); +}; + +const remove_assessment = (e) => { + frappe.confirm("Are you sure you want to remove this assessment?", () => { + frappe.call({ + method: "lms.lms.doctype.lms_class.lms_class.remove_assessment", + args: { + assessment: $(e.currentTarget).data("assessment"), + parent: $(".class-details").data("class"), + }, + callback(r) { + frappe.show_alert( + { + message: __("Assessment Removed"), + indicator: "green", + }, + 2000 + ); + window.location.reload(); + }, + }); + }); +}; diff --git a/lms/www/classes/class.py b/lms/www/classes/class.py index 99f0f572..59932854 100644 --- a/lms/www/classes/class.py +++ b/lms/www/classes/class.py @@ -48,7 +48,7 @@ def get_context(context): class_students = frappe.get_all( "Class Student", {"parent": class_name}, - ["student", "student_name", "username"], + ["name", "student", "student_name", "username"], order_by="creation desc", ) diff --git a/lms/www/quiz_submission/__init__.py b/lms/www/quiz_submission/__init__.py new file mode 100644 index 00000000..e69de29b