From b5240f0eece8e041d3c3b947ffa8a681ce1a5acb Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Thu, 27 Apr 2023 22:29:45 +0530 Subject: [PATCH 1/5] feat: class improvements --- lms/lms/doctype/lms_class/lms_class.py | 17 +-- lms/public/css/style.css | 4 +- lms/www/classes/class.html | 73 ++++++++----- lms/www/classes/class.js | 138 +++++++++++++++++++++---- lms/www/classes/class.py | 2 +- lms/www/classes/index.html | 55 ++++++++-- lms/www/classes/index.py | 13 ++- 7 files changed, 223 insertions(+), 79 deletions(-) diff --git a/lms/lms/doctype/lms_class/lms_class.py b/lms/lms/doctype/lms_class/lms_class.py index cd5d2586c..fd8b6a9fb 100644 --- a/lms/lms/doctype/lms_class/lms_class.py +++ b/lms/lms/doctype/lms_class/lms_class.py @@ -73,21 +73,8 @@ def remove_student(student, class_name): @frappe.whitelist() -def update_course(class_name, course, value): - if cint(value): - doc = frappe.get_doc( - { - "doctype": "Class Course", - "parent": class_name, - "course": course, - "parenttype": "LMS Class", - "parentfield": "courses", - } - ) - doc.save() - else: - frappe.db.delete("Class Course", {"parent": class_name, "course": course}) - return True +def remove_course(course, parent): + frappe.db.delete("Class Course", {"name": course, "parent": parent}) @frappe.whitelist() diff --git a/lms/public/css/style.css b/lms/public/css/style.css index 66dcaf1df..7fdba3eca 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -1989,8 +1989,8 @@ select { display: flex; flex-direction: column; border-radius: 0.75rem; - /* border: 1px solid var(--gray-200); */ - box-shadow: var(--shadow-sm); + border: 1px solid var(--gray-300); + /* box-shadow: var(--shadow-sm); */ padding: 0.5rem; height: 100%; position: relative; diff --git a/lms/www/classes/class.html b/lms/www/classes/class.html index 1df30575f..1738e6d70 100644 --- a/lms/www/classes/class.html +++ b/lms/www/classes/class.html @@ -112,26 +112,51 @@ {% macro CoursesSection(class_info, class_courses, published_courses) %} -
- {% if published_courses | length %} - {% for course in published_courses %} - {% set checked = course.name in class_courses %} - - {% endfor %} - {% endif %} -
+
+
+ {% if is_moderator %} + + {% endif %} +
+ {{ _("Courses") }} +
+
+ +
+ {% for course in class_courses %} +
+
+ + + +
+
+ {{ course.title }} +
+
+ {% endfor %} +
+ +
+ {% endmacro %} {% macro StudentsSection(class_info, class_students) %}
- {% if is_moderator %} - {{ AddStudents() }} - {% endif %} + +
+
+ +
+
+ {{ _("Students") }} +
+
{% if class_students | length %} @@ -171,23 +196,13 @@
{% else %} -

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

+

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

{% endif %}
{% endmacro %} -{% macro AddStudents() %} -
-
- -
-{% endmacro %} - - {% macro LiveClassSection(class_info, live_classes) %}
{{ CreateLiveClass(class_info) }} @@ -304,3 +319,9 @@ {{ include_script('controls.bundle.js') }} {% endblock %} + +{% set classes = frappe.get_all("LMS Class", filters = { + "start_date": [">", frappe.utils.getdate()], + "title": ["like", "%ERPNext%"] + }, fields=["name", "title", "start_date", "end_date"]) +%} \ No newline at end of file diff --git a/lms/www/classes/class.js b/lms/www/classes/class.js index ac2668567..54c8f4876 100644 --- a/lms/www/classes/class.js +++ b/lms/www/classes/class.js @@ -1,24 +1,16 @@ frappe.ready(() => { - $("#submit-student").click((e) => { - submit_student(e); + $(".btn-add-student").click((e) => { + show_student_modal(e); }); $(".remove-student").click((e) => { remove_student(e); }); - $(".class-course").click((e) => { - update_course(e); - }); - if ($("#live-class-form").length) { make_live_class_form(); } - if ($(".add-students").length) { - make_add_students_section(); - } - $("#open-class-modal").click((e) => { e.preventDefault(); $("#live-class-modal").modal("show"); @@ -27,6 +19,14 @@ frappe.ready(() => { $("#create-live-class").click((e) => { create_live_class(e); }); + + $(".btn-add-course").click((e) => { + show_course_modal(e); + }); + + $(".btn-remove-course").click((e) => { + remove_course(e); + }); }); const submit_student = (e) => { @@ -77,17 +77,6 @@ const remove_student = (e) => { ); }; -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"), - }, - }); -}; - const create_live_class = (e) => { let class_name = $(".class-details").data("class"); frappe.call({ @@ -353,3 +342,110 @@ const make_add_students_section = () => { $(".add-students .form-section:last").removeClass("empty-section"); $(".add-students .frappe-control").removeClass("hide-control"); }; + +const show_course_modal = () => { + let course_modal = new frappe.ui.Dialog({ + title: "Add Course", + fields: [ + { + fieldtype: "Link", + options: "LMS Course", + label: __("Course"), + fieldname: "course", + reqd: 1, + }, + ], + 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(); + }, + }); + course_modal.hide(); + }, + }); + course_modal.show(); +}; + +const remove_course = (e) => { + frappe.call({ + method: "lms.lms.doctype.lms_class.lms_class.remove_course", + args: { + course: $(e.target).data("course"), + parent: $(".class-details").data("class"), + }, + callback(r) { + frappe.show_alert( + { + message: __("Course Removed"), + indicator: "green", + }, + 3 + ); + window.location.reload(); + }, + }); +}; + +const show_student_modal = () => { + let student_modal = new frappe.ui.Dialog({ + title: "Add Student", + fields: [ + { + fieldtype: "Link", + options: "User", + label: __("Student"), + fieldname: "student", + reqd: 1, + filters: { + ignore_user_type: 1, + }, + }, + ], + 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(); + }, + }); + student_modal.hide(); + }, + }); + student_modal.show(); +}; diff --git a/lms/www/classes/class.py b/lms/www/classes/class.py index 09a98a9ca..3f218d93c 100644 --- a/lms/www/classes/class.py +++ b/lms/www/classes/class.py @@ -22,7 +22,7 @@ def get_context(context): ) context.class_courses = frappe.get_all( - "Class Course", {"parent": class_name}, pluck="course" + "Class Course", {"parent": class_name}, ["name", "course", "title"] ) class_students = frappe.get_all( diff --git a/lms/www/classes/index.html b/lms/www/classes/index.html index 69b62d2fa..8ffe6c23d 100644 --- a/lms/www/classes/index.html +++ b/lms/www/classes/index.html @@ -12,8 +12,8 @@ {% endif %}
{{ _("All Classes") }}
- {% if classes %} - {{ ClassCards(classes) }} + {% if past_classes | length or upcoming_classes | length %} + {{ ClassTabs(past_classes, upcoming_classes) }} {% else %}
@@ -27,15 +27,38 @@
{% endblock %} -{%- block script %} - {{ super() }} - {{ include_script('controls.bundle.js') }} - {% if is_moderator %} - - {% endif %} -{% endblock %} +{% macro ClassTabs(past_classes, upcoming_classes) %} + +{% endmacro %} {% macro ClassCards(classes) %}
@@ -79,3 +102,13 @@ {% endfor %}
{% endmacro %} + +{%- block script %} + {{ super() }} + {{ include_script('controls.bundle.js') }} + {% if is_moderator %} + + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/lms/www/classes/index.py b/lms/www/classes/index.py index 90875fc0f..c97d81b54 100644 --- a/lms/www/classes/index.py +++ b/lms/www/classes/index.py @@ -6,8 +6,15 @@ def get_context(context): context.no_cache = 1 context.is_moderator = has_course_moderator_role() - context.classes = frappe.get_all( + classes = frappe.get_all( "LMS Class", - {"end_date": [">=", getdate()]}, - ["name", "title", "start_date", "end_date"], + fields=["name", "title", "start_date", "end_date"], ) + past_classes, upcoming_classes = [], [] + for class_ in classes: + if getdate(class_.end_date) < getdate(): + past_classes.append(class_) + else: + upcoming_classes.append(class_) + context.past_classes = past_classes + context.upcoming_classes = upcoming_classes From b8471dd753c9c94e83304e50b5a08f8386c7b227 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Wed, 3 May 2023 20:55:49 +0530 Subject: [PATCH 2/5] feat: paid classes and seat count --- .../doctype/class_student_detail/__init__.py | 0 .../class_student_detail.js | 8 +++ .../class_student_detail.json | 51 +++++++++++++++++++ .../class_student_detail.py | 9 ++++ .../test_class_student_detail.py | 9 ++++ lms/lms/doctype/lms_class/lms_class.json | 15 +++++- lms/www/classes/index.html | 23 +++++++-- lms/www/classes/index.py | 13 ++++- 8 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 lms/lms/doctype/class_student_detail/__init__.py create mode 100644 lms/lms/doctype/class_student_detail/class_student_detail.js create mode 100644 lms/lms/doctype/class_student_detail/class_student_detail.json create mode 100644 lms/lms/doctype/class_student_detail/class_student_detail.py create mode 100644 lms/lms/doctype/class_student_detail/test_class_student_detail.py diff --git a/lms/lms/doctype/class_student_detail/__init__.py b/lms/lms/doctype/class_student_detail/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lms/lms/doctype/class_student_detail/class_student_detail.js b/lms/lms/doctype/class_student_detail/class_student_detail.js new file mode 100644 index 000000000..5ee5c29a8 --- /dev/null +++ b/lms/lms/doctype/class_student_detail/class_student_detail.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, Frappe and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Class Student Detail", { +// refresh(frm) { + +// }, +// }); diff --git a/lms/lms/doctype/class_student_detail/class_student_detail.json b/lms/lms/doctype/class_student_detail/class_student_detail.json new file mode 100644 index 000000000..039d8d8f7 --- /dev/null +++ b/lms/lms/doctype/class_student_detail/class_student_detail.json @@ -0,0 +1,51 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-05-03 20:52:46.950991", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user", + "full_name" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User" + }, + { + "fieldname": "full_name", + "fieldtype": "Data", + "label": "Full Name" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-05-03 20:52:46.950991", + "modified_by": "Administrator", + "module": "LMS", + "name": "Class Student Detail", + "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", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/lms/lms/doctype/class_student_detail/class_student_detail.py b/lms/lms/doctype/class_student_detail/class_student_detail.py new file mode 100644 index 000000000..86dd6138e --- /dev/null +++ b/lms/lms/doctype/class_student_detail/class_student_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class ClassStudentDetail(Document): + pass diff --git a/lms/lms/doctype/class_student_detail/test_class_student_detail.py b/lms/lms/doctype/class_student_detail/test_class_student_detail.py new file mode 100644 index 000000000..bbdcb88f7 --- /dev/null +++ b/lms/lms/doctype/class_student_detail/test_class_student_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestClassStudentDetail(FrappeTestCase): + pass diff --git a/lms/lms/doctype/lms_class/lms_class.json b/lms/lms/doctype/lms_class/lms_class.json index a2a513305..ffcf5c31d 100644 --- a/lms/lms/doctype/lms_class/lms_class.json +++ b/lms/lms/doctype/lms_class/lms_class.json @@ -11,7 +11,9 @@ "title", "start_date", "end_date", + "paid_class", "column_break_4", + "seat_count", "description", "section_break_6", "students", @@ -71,11 +73,22 @@ "fieldtype": "Code", "label": "Custom Component", "options": "HTML" + }, + { + "default": "0", + "fieldname": "paid_class", + "fieldtype": "Check", + "label": "Paid Class" + }, + { + "fieldname": "seat_count", + "fieldtype": "Int", + "label": "Seat Count" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-03-02 22:47:13.139289", + "modified": "2023-05-03 17:52:28.226169", "modified_by": "Administrator", "module": "LMS", "name": "LMS Class", diff --git a/lms/www/classes/index.html b/lms/www/classes/index.html index 8ffe6c23d..c8438ac6c 100644 --- a/lms/www/classes/index.html +++ b/lms/www/classes/index.html @@ -68,11 +68,7 @@
-
- {{ class.title }} -
- -
+
{% if course_count %} {{ course_count }} {{ _("Courses") }} @@ -84,6 +80,16 @@ {{ student_count }} {{ _("Students") }} {% endif %} + + {% if class.seats_left %} + + {{ class.seats_left }} {{ _("seats left") }} + + {% endif %} +
+ +
+ {{ class.title }}
@@ -97,6 +103,13 @@ {{ frappe.utils.format_date(class.end_date, "long") }}
+ + {% if class.paid_class and class.start_date > frappe.utils.getdate() %} + + {{ _("Register") }} + + {% endif %} +
{% endfor %} diff --git a/lms/www/classes/index.py b/lms/www/classes/index.py index c97d81b54..58e7510da 100644 --- a/lms/www/classes/index.py +++ b/lms/www/classes/index.py @@ -8,13 +8,22 @@ def get_context(context): context.is_moderator = has_course_moderator_role() classes = frappe.get_all( "LMS Class", - fields=["name", "title", "start_date", "end_date"], + fields=["name", "title", "start_date", "end_date", "paid_class", "seat_count"], ) + past_classes, upcoming_classes = [], [] for class_ in classes: - if getdate(class_.end_date) < getdate(): + if class_.seat_count: + filled_seats = frappe.db.count( + "Class Student", + filters={"parent": class_.name}, + ) + class_.seats_left = class_.seat_count - filled_seats + + if getdate(class_.start_date) < getdate(): past_classes.append(class_) else: upcoming_classes.append(class_) + context.past_classes = past_classes context.upcoming_classes = upcoming_classes From ae4282877112594272eb6607f2492b1dddb82cbe Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Tue, 16 May 2023 16:14:13 +0530 Subject: [PATCH 3/5] fix: course and student interactions in class --- lms/lms/doctype/lms_class/lms_class.py | 3 +- lms/public/css/style.css | 57 ++-------- lms/www/classes/class.html | 139 +++++++++++++------------ lms/www/classes/class.js | 78 +++++++------- lms/www/classes/class.py | 10 +- lms/www/classes/index.html | 79 +++++++------- lms/www/classes/index.py | 6 -- lms/www/courses/create.html | 2 +- lms/www/jobs/index.html | 2 +- lms/www/jobs/job.html | 6 +- 10 files changed, 183 insertions(+), 199 deletions(-) diff --git a/lms/lms/doctype/lms_class/lms_class.py b/lms/lms/doctype/lms_class/lms_class.py index 203ab4a42..607264344 100644 --- a/lms/lms/doctype/lms_class/lms_class.py +++ b/lms/lms/doctype/lms_class/lms_class.py @@ -75,12 +75,11 @@ def add_student(email, class_name): @frappe.whitelist() def remove_student(student, class_name): frappe.db.delete("Class Student", {"student": student, "parent": class_name}) - return True @frappe.whitelist() def remove_course(course, parent): - frappe.db.delete("Class Course", {"name": course, "parent": parent}) + frappe.db.delete("Class Course", {"course": course, "parent": parent}) @frappe.whitelist() diff --git a/lms/public/css/style.css b/lms/public/css/style.css index e4f532bdb..c04f26b1c 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -124,7 +124,7 @@ textarea.field-input { border-bottom: none; } -.level { +.outline-lesson .level { justify-content: start; } @@ -526,24 +526,6 @@ input[type=checkbox] { margin: 0.25rem 0; } -.button { - border-radius: var(--border-radius); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - padding: 0.25rem 1.25rem; - font-size: var(--text-md); - line-height: 20px; - box-shadow: var(--btn-shadow); - border: none; - width: fit-content; -} - -.button:disabled { - cursor: not-allowed; -} - .wide-button { padding: 0.3rem 4rem; width: 100%; @@ -555,35 +537,6 @@ input[type=checkbox] { } } -.is-secondary { - background: #FFFFFF; - color: var(--gray-900); -} - -.is-secondary:hover { - text-decoration: none; - color: var(--gray-900); -} - -.is-default { - background: var(--gray-100); - color: var(--gray-900); -} - -.is-default:disabled { - color: var(--gray-500); -} - -.is-primary { - background: var(--primary-color); - color: #FFFFFF; -} - -.is-primary:hover { - text-decoration: none; - color: #FFFFFF; -} - .course-home-page .course-home-outline { padding-bottom: 4rem; } @@ -2194,3 +2147,11 @@ select { background-color: var(--red-50); } +.list-row { + border-bottom: 1px solid var(--gray-300); + padding: 0.75rem 0; +} + +.list-row:last-child { + border-bottom: none; +} \ No newline at end of file diff --git a/lms/www/classes/class.html b/lms/www/classes/class.html index 1738e6d70..e115cb311 100644 --- a/lms/www/classes/class.html +++ b/lms/www/classes/class.html @@ -31,10 +31,10 @@ {% macro ClassDetails(class_info) %}
-
+
{{ class_info.title }}
-
+
@@ -46,7 +46,7 @@
{% if class_info.description %} -
+
{{ class_info.description }}
{% endif %} @@ -62,20 +62,28 @@ {% macro ClassSections(class_info, class_courses, class_students, published_courses) %}
- + {% endif %}
+ {% endmacro %} {% macro LiveClassSection(class_info, live_classes) %} -
+
+
+
+ {{ _("Live Class") }} +
+ {% if is_moderator %} + + {% endif %} +
{{ CreateLiveClass(class_info) }} {{ LiveClassList(class_info, live_classes) }}
@@ -214,9 +230,6 @@ {% macro CreateLiveClass(class_info) %} {% if is_moderator %} -