From 425bb54057b233e783fa20453391c94742866777 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 29 Aug 2022 09:19:06 +0530 Subject: [PATCH 1/4] feat: notifications tab in dashboard --- lms/templates/notifications.html | 0 lms/www/dashboard/index.html | 7 +++++++ 2 files changed, 7 insertions(+) create mode 100644 lms/templates/notifications.html diff --git a/lms/templates/notifications.html b/lms/templates/notifications.html new file mode 100644 index 00000000..e69de29b diff --git a/lms/www/dashboard/index.html b/lms/www/dashboard/index.html index 1f6e5855..b361e57b 100644 --- a/lms/www/dashboard/index.html +++ b/lms/www/dashboard/index.html @@ -26,6 +26,10 @@ {% endif %} +
@@ -39,6 +43,9 @@ {% include "lms/templates/courses_created.html" %} {% endif %} +
+ {% include "lms/templates/courses_created.html" %} +
From a6156ec863b37d29063ac29d8babe23e64432027 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 26 Sep 2022 22:09:18 +0530 Subject: [PATCH 2/4] feat: get notifications --- lms/lms/widgets/CourseCard.html | 20 +++++------ lms/www/dashboard/index.html | 14 +++++++- lms/www/dashboard/index.py | 59 +++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/lms/lms/widgets/CourseCard.html b/lms/lms/widgets/CourseCard.html index c84ab45d..11af8e13 100644 --- a/lms/lms/widgets/CourseCard.html +++ b/lms/lms/widgets/CourseCard.html @@ -2,17 +2,17 @@ {% set progress = frappe.utils.cint(membership.progress) %}
-
-
- {% for tag in get_tags(course.name) %} -
{{ tag }}
- {% endfor %} +
+
+ {% for tag in get_tags(course.name) %} +
{{ tag }}
+ {% endfor %} +
+ {% if not course.image %} +
{{ course.title[0] }}
+ {% endif %}
- {% if not course.image %} -
{{ course.title[0] }}
- {% endif %} -
diff --git a/lms/www/dashboard/index.html b/lms/www/dashboard/index.html index cd2d8df1..ad6e894b 100644 --- a/lms/www/dashboard/index.html +++ b/lms/www/dashboard/index.html @@ -62,7 +62,19 @@ {% endif %}
- {% include "lms/templates/courses_created.html" %} + {% if notifications | length %} + {% for notification in notifications %} + {{ notification }} + {% endfor %} + {% else %} +
+ +
+
{{ _("No Notifications") }}
+
{{ _("You don't have any notifications.") }}
+
+
+ {% endif %}
diff --git a/lms/www/dashboard/index.py b/lms/www/dashboard/index.py index 68951425..674da52d 100644 --- a/lms/www/dashboard/index.py +++ b/lms/www/dashboard/index.py @@ -1,4 +1,5 @@ import frappe +from datetime import datetime from lms.lms.utils import has_course_instructor_role, has_course_moderator_role @@ -7,3 +8,61 @@ def get_context(context): portal_course_creation = frappe.db.get_single_value("LMS Settings", "portal_course_creation") context.show_creators_section = portal_course_creation == "Anyone" or has_course_instructor_role() context.show_review_section = has_course_moderator_role() + context.notifications = get_notifications() + + +def get_notifications(): + notifications = [] + + notifications += get_notifications_from_lessons_created() + + notifications += get_notifications_from_topics_created() + + if len(notifications): + print(notifications) + notifications = sorted(notifications, key=lambda t: datetime.strptime(frappe.utils.format_datetime(t.creation, "dd-mm-yyyy HH:mm:ss"),"%d/%m/%Y %H:%M:%S")) + return notifications + + +def get_notifications_from_lessons_created(): + + lessons = frappe.get_all("Course Lesson", { + "owner": frappe.session.user + }, ["name"]) + + for lesson in lessons: + topics = frappe.get_all("Discussion Topic", { + "reference_doctype": "Course Lesson", + "reference_docname": lesson.name + }, ["name"]) + + return get_notifications_from_replies(topics) + + +def get_notifications_from_topics_created(): + + topics = frappe.get_all("Discussion Topic", { + "owner": frappe.session.user + }, ["name"]) + + return get_notifications_from_replies(topics) + + + +def get_notifications_from_replies(topics): + notifications = [] + + for topic in topics: + replies = frappe.get_all("Discussion Reply", { + "topic": topic.name + }, ["reply", "owner", "creation"]) + + + for reply in replies: + notification = frappe._dict() + notification["message"] = reply.reply + notification["user"] = reply.owner + notification["creation"] = reply.creation + notifications.append(notification) + + return notifications From a291d738283855050be6c11b8b3086d3b2c7be3e Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Tue, 27 Sep 2022 18:13:46 +0530 Subject: [PATCH 3/4] feat: show notifications from logs --- lms/hooks.py | 8 ++++--- lms/lms/utils.py | 33 +++++++++++++++++++++++++++++ lms/lms/widgets/Avatar.html | 22 +++++++++---------- lms/lms/widgets/Reviews.html | 2 +- lms/public/css/style.css | 36 ++++++++++++++++++++++++-------- lms/templates/notifications.html | 30 ++++++++++++++++++++++++++ lms/www/batch/learn.html | 2 +- lms/www/batch/learn.py | 2 +- lms/www/courses/course.html | 2 +- lms/www/dashboard/index.html | 22 +++++-------------- lms/www/dashboard/index.py | 17 ++++++++++++--- 11 files changed, 129 insertions(+), 47 deletions(-) diff --git a/lms/hooks.py b/lms/hooks.py index fc70e0e6..85ac2ded 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -85,8 +85,8 @@ web_include_js = ["website.bundle.js", "controls.bundle.js"] # Override standard doctype classes override_doctype_class = { - "User": "lms.overrides.user.CustomUser", - "Web Template": "lms.overrides.web_template.CustomWebTemplate" + "User": "lms.overrides.user.CustomUser", + "Web Template": "lms.overrides.web_template.CustomWebTemplate" } # Document Events @@ -94,7 +94,9 @@ override_doctype_class = { # Hook on document methods and events doc_events = { - + "Discussion Reply": { + "after_insert": "lms.lms.utils.create_notification_log" + } } # Scheduled Tasks diff --git a/lms/lms/utils.py b/lms/lms/utils.py index a36f8438..abc197c3 100644 --- a/lms/lms/utils.py +++ b/lms/lms/utils.py @@ -4,6 +4,7 @@ from frappe.utils import flt, cint, cstr, getdate, add_months, fmt_money from lms.lms.md import markdown_to_html, find_macros import string from frappe import _ +from frappe.desk.doctype.notification_log.notification_log import make_notification_logs RE_SLUG_NOTALLOWED = re.compile("[^a-z0-9]+") @@ -469,3 +470,35 @@ def validate_image(path): file.save(ignore_permissions=True) return file.file_url return path + + +def create_notification_log(doc, method): + topic = frappe.db.get_value("Discussion Topic", doc.topic, + ["reference_doctype", "reference_docname", "owner", "title"], as_dict=1) + + if topic.reference_doctype != "Course Lesson": + return + + course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course") + instructors = frappe.db.get_all("Course Instructor", { "parent": course }, pluck="instructor") + + notification = frappe._dict({ + "subject": _("New reply on the topic {0}").format(topic.title), + "email_content": doc.reply, + "document_type": topic.reference_doctype, + "document_name": topic.reference_docname, + "for_user": topic.owner, + "from_user": doc.owner, + "type": "Alert" + }) + + users = [] + if doc.owner != topic.owner: + users.append(topic.owner) + + if doc.owner not in instructors: + users += instructors + make_notification_logs(notification, users) + + + diff --git a/lms/lms/widgets/Avatar.html b/lms/lms/widgets/Avatar.html index f4b7194c..063b2c79 100644 --- a/lms/lms/widgets/Avatar.html +++ b/lms/lms/widgets/Avatar.html @@ -1,14 +1,14 @@ {% set color = get_palette(member.full_name) %} - - {% if member.user_image %} - - - {% else %} - - {{ frappe.utils.get_abbr(member.full_name) }} - - {% endif %} - + + {% if member.user_image %} + + {% else %} + + {{ frappe.utils.get_abbr(member.full_name) }} + + {% endif %} + diff --git a/lms/lms/widgets/Reviews.html b/lms/lms/widgets/Reviews.html index db871e54..1e79dbd3 100644 --- a/lms/lms/widgets/Reviews.html +++ b/lms/lms/widgets/Reviews.html @@ -8,7 +8,7 @@ {{ _("Write a review") }} {% elif not is_instructor(course.name) and frappe.session.user == "Guest" %} - {{ _("Login") }} + {{ _("Login") }} {% elif not is_instructor(course.name) and not membership and course.status == "Approved" %}
{{ _("Start Learning") }}
{% endif %} diff --git a/lms/public/css/style.css b/lms/public/css/style.css index f42a7e66..7e10ceb0 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -1399,15 +1399,17 @@ pre { } .lms-nav .nav-link { - color: var(--text-muted); - padding: var(--padding-md) 0; + padding: var(--padding-sm) 0; margin: 0 var(--margin-md); + font-size: var(--text-base); + color: var(--text-muted); + } .lms-nav .nav-link.active { - font-weight: 600; - border-bottom: 1px solid var(--primary); - color: var(--text-color); + font-weight: 500; + border-bottom: 1px solid var(--primary-color); + color: var(--primary-color); } @media (min-width: 500px) { @@ -1416,10 +1418,6 @@ pre { } } -.lms-nav .nav-link:hover { - color: inherit; -} - .dashboard-button { position: relative; top: -50px; @@ -1708,3 +1706,23 @@ li { width: 2.75rem; height: 2.75rem; } + +.modal .comment-field { + height: 150px !important; + resize: auto !important; +} + +.notification-card { + display: flex; + align-items: center; + margin-bottom: 1.5rem; + position: relative; +} + +.notification-card:last-child { + margin-bottom: 0; +} + +.timestamp { + font-size: var(--text-xs); +} diff --git a/lms/templates/notifications.html b/lms/templates/notifications.html index e69de29b..cd6679d4 100644 --- a/lms/templates/notifications.html +++ b/lms/templates/notifications.html @@ -0,0 +1,30 @@ +{% if notifications | length %} +
+ {% for notification in notifications %} +
+
+ {% set member = frappe.db.get_value("User", notification.from_user, + ["username", "full_name", "user_image"], as_dict=1) %} + {{ widgets.Avatar(member=member, avatar_class="avatar-medium") }} +
+
+
+ {{ notification.subject }} {{ frappe.utils.pretty_date(notification.creation) }} +
+ +
+ +
+ + {% endfor %} +
+ +{% else %} +
+ +
+
{{ _("No Notifications") }}
+
{{ _("You don't have any notifications.") }}
+
+
+{% endif %} diff --git a/lms/www/batch/learn.html b/lms/www/batch/learn.html index f71d1580..67cd8b63 100644 --- a/lms/www/batch/learn.html +++ b/lms/www/batch/learn.html @@ -74,7 +74,7 @@ {% if (is_instructor or has_course_moderator_role()) and not lesson.edit_mode %} - + {% endif %}
diff --git a/lms/www/batch/learn.py b/lms/www/batch/learn.py index a1674156..c8b2aabf 100644 --- a/lms/www/batch/learn.py +++ b/lms/www/batch/learn.py @@ -13,7 +13,7 @@ def get_context(context): context.chapter = frappe.db.get_value("Chapter Reference", { "idx": chapter_index, "parent": context.course.name - }, "chapter") + }, "chapter") if not chapter_index or not lesson_index: if context.batch: diff --git a/lms/www/courses/course.html b/lms/www/courses/course.html index 27e7257a..558bbd5d 100644 --- a/lms/www/courses/course.html +++ b/lms/www/courses/course.html @@ -347,7 +347,7 @@ {% endif %} {% if is_instructor(course.name) or has_course_moderator_role() %} - {{ _("Edit Course") }} + {{ _("Edit Course") }} {% endif %} {% endmacro %} diff --git a/lms/www/dashboard/index.html b/lms/www/dashboard/index.html index ad6e894b..b9d0a501 100644 --- a/lms/www/dashboard/index.html +++ b/lms/www/dashboard/index.html @@ -17,14 +17,14 @@
{% endif %} -
- {% if notifications | length %} - {% for notification in notifications %} - {{ notification }} - {% endfor %} - {% else %} -
- -
-
{{ _("No Notifications") }}
-
{{ _("You don't have any notifications.") }}
-
-
- {% endif %} +
+ {% include "lms/templates/notifications.html" %}
diff --git a/lms/www/dashboard/index.py b/lms/www/dashboard/index.py index 674da52d..b15c0a9e 100644 --- a/lms/www/dashboard/index.py +++ b/lms/www/dashboard/index.py @@ -1,6 +1,6 @@ import frappe from datetime import datetime -from lms.lms.utils import has_course_instructor_role, has_course_moderator_role +from lms.lms.utils import has_course_instructor_role, has_course_moderator_role, get_lesson_index def get_context(context): @@ -12,6 +12,19 @@ def get_context(context): def get_notifications(): + notifications = frappe.get_all("Notification Log", { + "document_type": "Course Lesson", + "for_user": frappe.session.user + }, ["subject", "creation", "from_user", "document_name"]) + + for notification in notifications: + course = frappe.db.get_value("Course Lesson", notification.document_name, "course") + notification.url = "/courses/{0}/learn/{1}".format(course, get_lesson_index(notification.document_name)) + + return notifications + + +def get_notifications_old(): notifications = [] notifications += get_notifications_from_lessons_created() @@ -19,7 +32,6 @@ def get_notifications(): notifications += get_notifications_from_topics_created() if len(notifications): - print(notifications) notifications = sorted(notifications, key=lambda t: datetime.strptime(frappe.utils.format_datetime(t.creation, "dd-mm-yyyy HH:mm:ss"),"%d/%m/%Y %H:%M:%S")) return notifications @@ -57,7 +69,6 @@ def get_notifications_from_replies(topics): "topic": topic.name }, ["reply", "owner", "creation"]) - for reply in replies: notification = frappe._dict() notification["message"] = reply.reply From cf8549bb28681555bc5f8c4f6acfdf56fbb287e4 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Tue, 27 Sep 2022 18:21:57 +0530 Subject: [PATCH 4/4] fix: removed unused functions --- lms/www/dashboard/index.py | 55 -------------------------------------- 1 file changed, 55 deletions(-) diff --git a/lms/www/dashboard/index.py b/lms/www/dashboard/index.py index b15c0a9e..df19be65 100644 --- a/lms/www/dashboard/index.py +++ b/lms/www/dashboard/index.py @@ -22,58 +22,3 @@ def get_notifications(): notification.url = "/courses/{0}/learn/{1}".format(course, get_lesson_index(notification.document_name)) return notifications - - -def get_notifications_old(): - notifications = [] - - notifications += get_notifications_from_lessons_created() - - notifications += get_notifications_from_topics_created() - - if len(notifications): - notifications = sorted(notifications, key=lambda t: datetime.strptime(frappe.utils.format_datetime(t.creation, "dd-mm-yyyy HH:mm:ss"),"%d/%m/%Y %H:%M:%S")) - return notifications - - -def get_notifications_from_lessons_created(): - - lessons = frappe.get_all("Course Lesson", { - "owner": frappe.session.user - }, ["name"]) - - for lesson in lessons: - topics = frappe.get_all("Discussion Topic", { - "reference_doctype": "Course Lesson", - "reference_docname": lesson.name - }, ["name"]) - - return get_notifications_from_replies(topics) - - -def get_notifications_from_topics_created(): - - topics = frappe.get_all("Discussion Topic", { - "owner": frappe.session.user - }, ["name"]) - - return get_notifications_from_replies(topics) - - - -def get_notifications_from_replies(topics): - notifications = [] - - for topic in topics: - replies = frappe.get_all("Discussion Reply", { - "topic": topic.name - }, ["reply", "owner", "creation"]) - - for reply in replies: - notification = frappe._dict() - notification["message"] = reply.reply - notification["user"] = reply.owner - notification["creation"] = reply.creation - notifications.append(notification) - - return notifications