diff --git a/.gitignore b/.gitignore
index f6a99e30..222921e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,6 @@
tags
community/docs/current
community/public/dist
+__pycache__/
+*.py[cod]
+*$py.class
diff --git a/community/fixtures/custom_field.json b/community/fixtures/custom_field.json
new file mode 100644
index 00000000..d3bb2124
--- /dev/null
+++ b/community/fixtures/custom_field.json
@@ -0,0 +1,108 @@
+[
+ {
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "doctype": "Custom Field",
+ "dt": "User",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "linkedin",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "mobile_no",
+ "label": "LinkedIn ID",
+ "length": 0,
+ "mandatory_depends_on": null,
+ "modified": "2021-06-30 14:46:55.834145",
+ "name": "User-linkedin",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ },
+ {
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": null,
+ "columns": 0,
+ "default": null,
+ "depends_on": null,
+ "description": null,
+ "docstatus": 0,
+ "doctype": "Custom Field",
+ "dt": "User",
+ "fetch_from": null,
+ "fetch_if_empty": 0,
+ "fieldname": "github",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "hide_border": 0,
+ "hide_days": 0,
+ "hide_seconds": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_preview": 0,
+ "in_standard_filter": 0,
+ "insert_after": "linkedin",
+ "label": "Github ID",
+ "length": 0,
+ "mandatory_depends_on": null,
+ "modified": "2021-06-30 14:46:55.834145",
+ "name": "User-github",
+ "no_copy": 0,
+ "non_negative": 0,
+ "options": null,
+ "parent": null,
+ "parentfield": null,
+ "parenttype": null,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": null,
+ "read_only": 0,
+ "read_only_depends_on": null,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "translatable": 1,
+ "unique": 0,
+ "width": null
+ }
+]
diff --git a/community/hooks.py b/community/hooks.py
index 5666dd53..61b681f5 100644
--- a/community/hooks.py
+++ b/community/hooks.py
@@ -104,6 +104,8 @@ doc_events = {
# ]
#}
+fixtures = ["Custom Field"]
+
# Testing
# -------
diff --git a/community/lms/doctype/chapter/chapter.py b/community/lms/doctype/chapter/chapter.py
index b616de2c..a563c7e0 100644
--- a/community/lms/doctype/chapter/chapter.py
+++ b/community/lms/doctype/chapter/chapter.py
@@ -5,6 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from ...utils import slugify
class Chapter(Document):
def get_lessons(self):
@@ -13,3 +14,6 @@ class Chapter(Document):
fields='name',
order_by="index_")
return [frappe.get_doc('Lesson', row['name']) for row in rows]
+
+ def get_slugified_chapter_title(self):
+ return slugify(self.title)
diff --git a/community/lms/doctype/lesson/lesson.py b/community/lms/doctype/lesson/lesson.py
index d90fa2c1..3b66ac0c 100644
--- a/community/lms/doctype/lesson/lesson.py
+++ b/community/lms/doctype/lesson/lesson.py
@@ -63,32 +63,35 @@ class Lesson(Document):
return
@frappe.whitelist()
-def save_progress(lesson, course):
+def save_progress(lesson, course, status):
if not frappe.db.exists("LMS Batch Membership",
{
"member": frappe.session.user,
"course": course
}):
return
+
if frappe.db.exists("LMS Course Progress",
{
"lesson": lesson,
- "owner": frappe.session.user
+ "owner": frappe.session.user,
+ "course": course
}):
- return
-
- lesson_details = frappe.get_doc("Lesson", lesson)
- dynamic_content = find_macros(lesson_details.body)
-
- 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)
+ doc = frappe.get_doc("LMS Course Progress",
+ {
+ "lesson": lesson,
+ "owner": frappe.session.user,
+ "course": course
+ })
+ doc.status = status
+ doc.save(ignore_permissions=True)
+ else:
+ frappe.get_doc({
+ "doctype": "LMS Course Progress",
+ "lesson": lesson,
+ "status": status,
+ }).save(ignore_permissions=True)
+ return "OK"
def update_progress(lesson):
user = frappe.session.user
diff --git a/community/lms/doctype/lms_batch_membership/lms_batch_membership.js b/community/lms/doctype/lms_batch_membership/lms_batch_membership.js
index 0a20239d..68ac4f55 100644
--- a/community/lms/doctype/lms_batch_membership/lms_batch_membership.js
+++ b/community/lms/doctype/lms_batch_membership/lms_batch_membership.js
@@ -10,5 +10,5 @@ frappe.ui.form.on('LMS Batch Membership', {
}
};
});
- },
+ }
});
diff --git a/community/lms/doctype/lms_batch_membership/lms_batch_membership.json b/community/lms/doctype/lms_batch_membership/lms_batch_membership.json
index ba43cd69..f7f1f2dc 100644
--- a/community/lms/doctype/lms_batch_membership/lms_batch_membership.json
+++ b/community/lms/doctype/lms_batch_membership/lms_batch_membership.json
@@ -65,8 +65,7 @@
"fieldname": "course",
"fieldtype": "Data",
"in_list_view": 1,
- "label": "Course",
- "read_only": 1
+ "label": "Course"
},
{
"fieldname": "current_lesson",
@@ -84,7 +83,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2021-06-21 12:10:28.808803",
+ "modified": "2021-07-06 20:50:46.885325",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Batch Membership",
diff --git a/community/lms/doctype/lms_course/lms_course.json b/community/lms/doctype/lms_course/lms_course.json
index 8b46e3ec..a654c9e0 100644
--- a/community/lms/doctype/lms_course/lms_course.json
+++ b/community/lms/doctype/lms_course/lms_course.json
@@ -21,12 +21,14 @@
"engine": "InnoDB",
"field_order": [
"title",
- "is_published",
- "disable_self_learning",
- "column_break_3",
"short_code",
"video_link",
+ "column_break_3",
+ "is_published",
+ "disable_self_learning",
+ "image",
"section_break_5",
+ "tags",
"short_introduction",
"description"
],
@@ -80,6 +82,16 @@
"fieldname": "disable_self_learning",
"fieldtype": "Check",
"label": "Disable Self Learning"
+ },
+ {
+ "fieldname": "image",
+ "fieldtype": "Attach Image",
+ "label": "Preview Image"
+ },
+ {
+ "fieldname": "tags",
+ "fieldtype": "Data",
+ "label": "Tags"
}
],
"index_web_pages_for_search": 1,
@@ -106,7 +118,7 @@
"link_fieldname": "course"
}
],
- "modified": "2021-06-21 11:34:04.552376",
+ "modified": "2021-07-09 15:05:05.372430",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Course",
diff --git a/community/lms/doctype/lms_course/lms_course.py b/community/lms/doctype/lms_course/lms_course.py
index a98a5e63..73b1d5e3 100644
--- a/community/lms/doctype/lms_course/lms_course.py
+++ b/community/lms/doctype/lms_course/lms_course.py
@@ -8,7 +8,7 @@ from frappe.model.document import Document
import json
from ...utils import slugify
from community.query import find, find_all
-from frappe.utils import flt
+from frappe.utils import flt, cint
class LMSCourse(Document):
@staticmethod
@@ -115,6 +115,28 @@ class LMSCourse(Document):
# TODO: chapters should have a way to specify the order
return find_all("Chapter", course=self.name, order_by="index_")
+ def get_lessons(self):
+ """ Returns all lessons of this course """
+ lessons = []
+ chapters = self.get_chapters()
+ for chapter in chapters:
+ lessons.append(frappe.get_all("Lesson", {"chapter": chapter.name}))
+ return lessons
+
+ def get_course_progress(self):
+ """ Returns the course progress of the session user """
+ lesson_count = len(self.get_lessons())
+ completed_lessons = frappe.db.count("LMS Course Progress",
+ {
+ "course": self.name,
+ "owner": frappe.session.user,
+ "status": "Complete"
+ })
+ precision = cint(frappe.db.get_default("float_precision")) or 3
+ if not lesson_count:
+ return 0
+ return flt(((completed_lessons/lesson_count) * 100), precision)
+
def get_batch(self, batch_name):
return find("LMS Batch", name=batch_name, course=self.name)
@@ -199,7 +221,12 @@ class LMSCourse(Document):
}
if batch:
filters["batch"] = batch
- membership = frappe.db.get_value("LMS Batch Membership", filters, ["name","batch", "current_lesson"], as_dict=True)
+
+ membership = frappe.db.get_value("LMS Batch Membership",
+ filters,
+ ["name", "batch", "current_lesson", "member_type"],
+ as_dict=True)
+
if membership and membership.batch:
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
return membership
@@ -241,9 +268,52 @@ class LMSCourse(Document):
member_names = [m['member'] for m in memberships]
return find_all("User", name=["IN", member_names])
+ def get_tags(self):
+ return self.tags.split(",") if self.tags else []
+
+ def get_reviews(self):
+ reviews = frappe.get_all("LMS Course Review",
+ {
+ "course": self.name
+ },
+ ["review", "rating", "owner"],
+ order_by= "creation desc")
+
+ for review in reviews:
+ review.owner_details = frappe.get_doc("User", review.owner)
+
+ return reviews
+
+ def is_eligible_to_review(self, membership):
+ """ Checks if user is eligible to review the course """
+ if not membership:
+ return False
+ if frappe.db.count("LMS Course Review",
+ {
+ "course": self.name,
+ "owner": frappe.session.user
+ }):
+ return False
+ return True
+
+ def get_average_rating(self):
+ ratings = [review.rating for review in self.get_reviews()]
+ if not len(ratings):
+ return None
+ return sum(ratings)/len(ratings)
+
def get_outline(self):
return CourseOutline(self)
+ def get_progress(self, lesson):
+ return frappe.db.get_value("LMS Course Progress",
+ {
+ "course": self.name,
+ "owner": frappe.session.user,
+ "lesson": lesson
+ },
+ ["status"])
+
class CourseOutline:
def __init__(self, course):
self.course = course
diff --git a/community/lms/doctype/lms_course_mentor_mapping/lms_course_mentor_mapping.js b/community/lms/doctype/lms_course_mentor_mapping/lms_course_mentor_mapping.js
index 5b17f61a..c995587f 100644
--- a/community/lms/doctype/lms_course_mentor_mapping/lms_course_mentor_mapping.js
+++ b/community/lms/doctype/lms_course_mentor_mapping/lms_course_mentor_mapping.js
@@ -2,7 +2,13 @@
// For license information, please see license.txt
frappe.ui.form.on('LMS Course Mentor Mapping', {
- // refresh: function(frm) {
-
- // }
+ onload: function(frm) {
+ frm.set_query('mentor', function(doc) {
+ return {
+ filters: {
+ "ignore_user_type": 1,
+ }
+ };
+ });
+ },
});
diff --git a/community/lms/doctype/lms_course_review/__init__.py b/community/lms/doctype/lms_course_review/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/community/lms/doctype/lms_course_review/lms_course_review.js b/community/lms/doctype/lms_course_review/lms_course_review.js
new file mode 100644
index 00000000..8382eb52
--- /dev/null
+++ b/community/lms/doctype/lms_course_review/lms_course_review.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 Review', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/community/lms/doctype/lms_course_review/lms_course_review.json b/community/lms/doctype/lms_course_review/lms_course_review.json
new file mode 100644
index 00000000..f2b78b5d
--- /dev/null
+++ b/community/lms/doctype/lms_course_review/lms_course_review.json
@@ -0,0 +1,57 @@
+{
+ "actions": [],
+ "creation": "2021-06-28 13:36:36.146718",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "review",
+ "rating",
+ "course"
+ ],
+ "fields": [
+ {
+ "fieldname": "review",
+ "fieldtype": "Small Text",
+ "in_list_view": 1,
+ "label": "Review"
+ },
+ {
+ "fieldname": "rating",
+ "fieldtype": "Rating",
+ "in_list_view": 1,
+ "label": "Rating"
+ },
+ {
+ "fieldname": "course",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Course",
+ "options": "LMS Course"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-07-05 14:57:03.841430",
+ "modified_by": "Administrator",
+ "module": "LMS",
+ "name": "LMS Course Review",
+ "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_review/lms_course_review.py b/community/lms/doctype/lms_course_review/lms_course_review.py
new file mode 100644
index 00000000..2c4199a4
--- /dev/null
+++ b/community/lms/doctype/lms_course_review/lms_course_review.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2021, FOSS United and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.model.document import Document
+
+class LMSCourseReview(Document):
+ pass
+
+@frappe.whitelist()
+def submit_review(rating, review, course):
+ frappe.get_doc({
+ "doctype": "LMS Course Review",
+ "rating": rating,
+ "review": review,
+ "course": course
+ }).save(ignore_permissions=True)
+ return "OK"
diff --git a/community/lms/doctype/lms_course_review/test_lms_course_review.py b/community/lms/doctype/lms_course_review/test_lms_course_review.py
new file mode 100644
index 00000000..da8a1450
--- /dev/null
+++ b/community/lms/doctype/lms_course_review/test_lms_course_review.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, FOSS United and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestLMSCourseReview(unittest.TestCase):
+ pass
diff --git a/community/lms/md.py b/community/lms/md.py
index 0658ca82..73be75d9 100644
--- a/community/lms/md.py
+++ b/community/lms/md.py
@@ -93,11 +93,11 @@ class MacroInlineProcessor(InlineProcessor):
macro = m.group(1)
arg = m.group(2)
html = render_macro(macro, arg)
- html = sanitize_html(str(html))
+ html = sanitize_html(str(html), macro)
e = etree.fromstring(html)
return e, m.start(0), m.end(0)
-def sanitize_html(html):
+def sanitize_html(html, macro):
"""Sanotize the html using BeautifulSoup.
The markdown processor request the correct markup and crashes on
@@ -106,4 +106,7 @@ def sanitize_html(html):
"""
soup = BeautifulSoup(html, features="lxml")
nodes = soup.body.children
- return "
" + "\n".join(str(node) for node in nodes) + "
"
+ classname = ""
+ if macro == "YouTubeVideo":
+ classname = "lesson-video"
+ return "" + "\n".join(str(node) for node in nodes) + "
"
diff --git a/community/lms/web_form/profile/__init__.py b/community/lms/web_form/profile/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/community/lms/web_form/profile/profile.js b/community/lms/web_form/profile/profile.js
new file mode 100644
index 00000000..699703c5
--- /dev/null
+++ b/community/lms/web_form/profile/profile.js
@@ -0,0 +1,3 @@
+frappe.ready(function() {
+ // bind events here
+})
\ No newline at end of file
diff --git a/community/lms/web_form/profile/profile.json b/community/lms/web_form/profile/profile.json
new file mode 100644
index 00000000..c4962a94
--- /dev/null
+++ b/community/lms/web_form/profile/profile.json
@@ -0,0 +1,125 @@
+{
+ "accept_payment": 0,
+ "allow_comments": 0,
+ "allow_delete": 0,
+ "allow_edit": 1,
+ "allow_incomplete": 0,
+ "allow_multiple": 0,
+ "allow_print": 0,
+ "amount": 0.0,
+ "amount_based_on_field": 0,
+ "apply_document_permissions": 0,
+ "breadcrumbs": "",
+ "button_label": "Save",
+ "creation": "2021-06-30 13:48:13.682851",
+ "doc_type": "User",
+ "docstatus": 0,
+ "doctype": "Web Form",
+ "idx": 0,
+ "is_standard": 1,
+ "login_required": 1,
+ "max_attachment_size": 0,
+ "modified": "2021-06-30 15:53:20.967466",
+ "modified_by": "Administrator",
+ "module": "LMS",
+ "name": "profile",
+ "owner": "Administrator",
+ "payment_button_label": "Buy Now",
+ "published": 1,
+ "route": "profile",
+ "route_to_success_link": 0,
+ "show_attachments": 0,
+ "show_in_grid": 0,
+ "show_sidebar": 0,
+ "sidebar_items": [],
+ "success_url": "/profile",
+ "title": "Profile",
+ "web_form_fields": [
+ {
+ "allow_read_on_all_link_options": 0,
+ "fieldname": "first_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "First Name",
+ "max_length": 0,
+ "max_value": 0,
+ "read_only": 0,
+ "reqd": 1,
+ "show_in_filter": 0
+ },
+ {
+ "allow_read_on_all_link_options": 0,
+ "fieldname": "middle_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Middle Name (Optional)",
+ "max_length": 0,
+ "max_value": 0,
+ "read_only": 0,
+ "reqd": 0,
+ "show_in_filter": 0
+ },
+ {
+ "allow_read_on_all_link_options": 0,
+ "fieldname": "last_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Last Name",
+ "max_length": 0,
+ "max_value": 0,
+ "read_only": 0,
+ "reqd": 0,
+ "show_in_filter": 0
+ },
+ {
+ "allow_read_on_all_link_options": 0,
+ "description": "Get your globally recognized avatar from Gravatar.com",
+ "fieldname": "user_image",
+ "fieldtype": "Attach Image",
+ "hidden": 0,
+ "label": "User Image",
+ "max_length": 0,
+ "max_value": 0,
+ "read_only": 0,
+ "reqd": 0,
+ "show_in_filter": 0
+ },
+ {
+ "allow_read_on_all_link_options": 0,
+ "fieldname": "username",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Username",
+ "max_length": 0,
+ "max_value": 0,
+ "read_only": 0,
+ "reqd": 0,
+ "show_in_filter": 0
+ },
+ {
+ "allow_read_on_all_link_options": 0,
+ "fieldname": "mobile_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Mobile No",
+ "max_length": 0,
+ "max_value": 0,
+ "options": "Phone",
+ "read_only": 0,
+ "reqd": 0,
+ "show_in_filter": 0
+ },
+ {
+ "allow_read_on_all_link_options": 0,
+ "fieldname": "bio",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "label": "Bio",
+ "max_length": 0,
+ "max_value": 0,
+ "read_only": 0,
+ "reqd": 0,
+ "show_in_filter": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/community/lms/web_form/profile/profile.py b/community/lms/web_form/profile/profile.py
new file mode 100644
index 00000000..e1ada619
--- /dev/null
+++ b/community/lms/web_form/profile/profile.py
@@ -0,0 +1,5 @@
+import frappe
+
+def get_context(context):
+ # do your magic here
+ pass
diff --git a/community/lms/widgets/BreadCrumb.html b/community/lms/widgets/BreadCrumb.html
new file mode 100644
index 00000000..750ca9ef
--- /dev/null
+++ b/community/lms/widgets/BreadCrumb.html
@@ -0,0 +1,19 @@
+
+
All Courses
+

+
+ {% if course %}
+ {% if lesson %}
+
{{ course.title }}
+

+
{{ lesson.title }}
+ {% else %}
+
{{ course.title }}
+ {% endif %}
+ {% endif %}
+
+ {% if member_name %}
+
{{ member_name }}
+ {% endif %}
+
+
diff --git a/community/lms/widgets/ChapterTeaser.html b/community/lms/widgets/ChapterTeaser.html
index 5a49f5b3..526e92fb 100644
--- a/community/lms/widgets/ChapterTeaser.html
+++ b/community/lms/widgets/ChapterTeaser.html
@@ -1,28 +1,98 @@
-
-
-
{{index}}. {{ chapter.title }}
-
- {{ chapter.description or "" }}
+
+
+
+

+ {{ index }}. {{ chapter.title }}
+
+
+
+
+
+ {{ chapter.description }}
-
+
+
+
{% for lesson in chapter.get_lessons() %}
-
+
+{% if index != course.get_chapters() | length %}
+
+{% endif %}
+
+
diff --git a/community/lms/widgets/CourseCard.html b/community/lms/widgets/CourseCard.html
new file mode 100644
index 00000000..b45246bf
--- /dev/null
+++ b/community/lms/widgets/CourseCard.html
@@ -0,0 +1,63 @@
+
+
+
+
+ {% if course.get_chapters() | length %}
+
+ {{ course.get_chapters() | length }} Chapters
+
+ {% endif %}
+ {% if course.get_chapters() | length and course.get_upcoming_batches() | length %}
+ .
+ {% endif %}
+ {% if course.get_upcoming_batches() | length %}
+
+ {{ course.get_upcoming_batches() | length }} Open Batches
+
+ {% endif %}
+
+
{{ course.title }}
+
+
+ {% set membership = course.get_membership(frappe.session.user) %}
+
+ {% set lesson_index = course.get_lesson_index(membership.current_lesson) if membership and
+ membership.current_lesson
+ else '1.1' %}
+
+ {% set query_parameter = "?batch=" + membership.batch if membership and membership.batch else "" %}
+
+ {% if membership %}
+
+
+ Continue Course

+
+
+
+ {% else %}
+
+
+ View Course

+
+
+
+ {% endif %}
+
+
diff --git a/community/lms/widgets/CourseOutline.html b/community/lms/widgets/CourseOutline.html
index b4b5629c..60524e0c 100644
--- a/community/lms/widgets/CourseOutline.html
+++ b/community/lms/widgets/CourseOutline.html
@@ -1,7 +1,12 @@
-
-
Course Outline
-
- {% for chapter in course.get_chapters() %}
- {{ widgets.ChapterTeaser(index=loop.index, chapter=chapter, course=course, batch=batch, show_link=show_link, show_progress=show_progress)}}
- {% endfor %}
+{% if course.get_chapters() | length %}
+
+
+ Course Outline
+
+
+ {% for chapter in course.get_chapters() %}
+ {{ widgets.ChapterTeaser(index=loop.index, chapter=chapter, course=course, batch=batch, membership=membership) }}
+ {% endfor %}
+
+{% endif %}
diff --git a/community/lms/widgets/MemberCard.html b/community/lms/widgets/MemberCard.html
new file mode 100644
index 00000000..0ecc2d72
--- /dev/null
+++ b/community/lms/widgets/MemberCard.html
@@ -0,0 +1,15 @@
+
+ {% set avatar_class = "avatar-large" if not dimension_class else "avatar-xl"%}
+ {{ widgets.Avatar(member=member, avatar_class=avatar_class) }}
+
+ {{ member.full_name }}
+
+ {% set course_count = member.get_authored_courses() | length %}
+ {% if show_course_count and course_count > 0 %}
+ {% set suffix = "Courses" if course_count > 1 else "Course" %}
+
+ Created {{ course_count }} {{ suffix }}
+
+ {% endif %}
+
+
diff --git a/community/lms/widgets/Reviews.html b/community/lms/widgets/Reviews.html
new file mode 100644
index 00000000..aa46d7c1
--- /dev/null
+++ b/community/lms/widgets/Reviews.html
@@ -0,0 +1,85 @@
+{% if course.get_reviews() | length %}
+
+
+
+ {% for review in course.get_reviews() %}
+
+
{{ review.review }}
+
+
+ {% endfor %}
+
+
+
+
+{% endif %}
diff --git a/community/overrides/user.py b/community/overrides/user.py
index 500a9285..8588a350 100644
--- a/community/overrides/user.py
+++ b/community/overrides/user.py
@@ -5,15 +5,18 @@ import hashlib
class CustomUser(User):
- def get_course_count(self) -> int:
+ def get_authored_courses(self) -> int:
"""Returns the number of courses authored by this user.
"""
- return frappe.db.count(
+ return frappe.get_all(
'LMS Course', {
- 'owner': self.email
+ 'owner': self.name
})
def get_palette(self):
+ """
+ Returns a color unique to each member for Avatar """
+
palette = [
['--orange-avatar-bg', '--orange-avatar-color'],
['--pink-avatar-bg', '--pink-avatar-color'],
@@ -40,3 +43,19 @@ class CustomUser(User):
'member_type': 'Mentor'
})
+ def get_user_reviews(self):
+ """ Returns the reviews created by user """
+ return frappe.get_all("LMS Course Review",
+ {
+ "owner": self.name
+ })
+
+ def get_course_membership(self, member_type=None):
+ """ Returns all memberships of the user """
+ filters = {
+ "member": self.name
+ }
+ if member_type:
+ filters["member_type"] = member_type
+
+ return frappe.get_all("LMS Batch Membership", filters, ["name", "course"])
diff --git a/community/plugins.py b/community/plugins.py
index eb9b74fc..4c3066fb 100644
--- a/community/plugins.py
+++ b/community/plugins.py
@@ -98,7 +98,7 @@ def exercise_renderer(argument):
def youtube_video_renderer(video_id):
return f"""
-