diff --git a/school/community/widgets/Avatar.html b/school/community/widgets/Avatar.html index 6450ac9f..f4b7194c 100644 --- a/school/community/widgets/Avatar.html +++ b/school/community/widgets/Avatar.html @@ -1,14 +1,14 @@ -{% set color = member.get_palette() %} - - - {% if member.user_image %} - - - {% else %} - - {{ frappe.utils.get_abbr(member.full_name) }} - - {% endif %} +{% set color = get_palette(member.full_name) %} + + + {% if member.user_image %} + + + {% else %} + + {{ frappe.utils.get_abbr(member.full_name) }} + {% endif %} + diff --git a/school/hooks.py b/school/hooks.py index 5f305c20..605ff7d4 100644 --- a/school/hooks.py +++ b/school/hooks.py @@ -161,7 +161,27 @@ update_website_context = [ jinja = { "methods": [ - "school.page_renderers.get_profile_url" + "school.page_renderers.get_profile_url", + "school.overrides.user.get_authored_courses", + "school.overrides.user.get_palette", + "school.lms.utils.get_membership", + "school.lms.utils.get_lessons", + "school.lms.utils.get_tags", + "school.lms.utils.get_instructors", + "school.lms.utils.get_students", + "school.lms.utils.get_average_rating", + "school.lms.utils.is_certified", + "school.lms.utils.get_lesson_index", + "school.lms.utils.get_lesson_url", + "school.lms.utils.get_chapters", + "school.lms.utils.get_slugified_chapter_title", + "school.lms.utils.get_progress", + "school.lms.utils.render_html", + "school.lms.utils.is_mentor", + "school.lms.utils.is_cohort_staff", + "school.lms.utils.get_mentors", + "school.lms.utils.get_reviews", + "school.lms.utils.is_eligible_to_review", ], "filters": [] } diff --git a/school/lms/doctype/course_instructor/__init__.py b/school/lms/doctype/course_instructor/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/school/lms/doctype/course_instructor/course_instructor.json b/school/lms/doctype/course_instructor/course_instructor.json new file mode 100644 index 00000000..3ef8630a --- /dev/null +++ b/school/lms/doctype/course_instructor/course_instructor.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2022-02-07 11:39:59.998762", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "instructor" + ], + "fields": [ + { + "fieldname": "instructor", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Instructor", + "options": "User" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2022-02-07 11:41:42.943250", + "modified_by": "Administrator", + "module": "LMS", + "name": "Course Instructor", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/school/lms/doctype/course_instructor/course_instructor.py b/school/lms/doctype/course_instructor/course_instructor.py new file mode 100644 index 00000000..d229178c --- /dev/null +++ b/school/lms/doctype/course_instructor/course_instructor.py @@ -0,0 +1,8 @@ +# Copyright (c) 2022, Frappe and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class CourseInstructor(Document): + pass diff --git a/school/lms/doctype/course_lesson/course_lesson.py b/school/lms/doctype/course_lesson/course_lesson.py index 0be2e853..38adb6a3 100644 --- a/school/lms/doctype/course_lesson/course_lesson.py +++ b/school/lms/doctype/course_lesson/course_lesson.py @@ -5,7 +5,8 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -from ...md import markdown_to_html, find_macros +from ...md import find_macros +from school.lms.utils import get_course_progress class CourseLesson(Document): def validate(self): @@ -57,9 +58,6 @@ class CourseLesson(Document): folder = frappe.get_doc(args) folder.save(ignore_permissions=True) - def render_html(self): - return markdown_to_html(self.body) - def get_exercises(self): if not self.body: return [] @@ -107,7 +105,6 @@ def save_progress(lesson, course, status): "status": status, }).save(ignore_permissions=True) - course_details = frappe.get_doc("LMS Course", course) - progress = course_details.get_course_progress() + progress = get_course_progress(course) frappe.db.set_value("LMS Batch Membership", membership, "progress", progress) return progress diff --git a/school/lms/doctype/exercise/exercise.py b/school/lms/doctype/exercise/exercise.py index faccd1b7..eb6c7bcb 100644 --- a/school/lms/doctype/exercise/exercise.py +++ b/school/lms/doctype/exercise/exercise.py @@ -3,6 +3,7 @@ import frappe from frappe.model.document import Document +from school.lms.utils import get_membership class Exercise(Document): def get_user_submission(self): @@ -35,8 +36,7 @@ class Exercise(Document): if old_submission and old_submission.solution == code: return old_submission - course = frappe.get_doc("LMS Course", self.course) - member = course.get_membership(frappe.session.user) + member = get_membership(self.course, frappe.session.user) doc = frappe.get_doc( doctype="Exercise Submission", diff --git a/school/lms/doctype/lms_batch/lms_batch.py b/school/lms/doctype/lms_batch/lms_batch.py index a7fbcb68..9643ddf4 100644 --- a/school/lms/doctype/lms_batch/lms_batch.py +++ b/school/lms/doctype/lms_batch/lms_batch.py @@ -8,14 +8,14 @@ from frappe.model.document import Document from frappe import _ from school.lms.doctype.lms_batch_membership.lms_batch_membership import create_membership from school.query import find, find_all +from school.lms.utils import is_mentor class LMSBatch(Document): def validate(self): self.validate_if_mentor() def validate_if_mentor(self): - course = frappe.get_doc("LMS Course", self.course) - if not course.is_mentor(frappe.session.user): + if not is_mentor(self.course, frappe.session.user): frappe.throw(_("You are not a mentor of the course {0}").format(course.title)) def after_insert(self): diff --git a/school/lms/doctype/lms_batch_membership/test_lms_batch_membership.py b/school/lms/doctype/lms_batch_membership/test_lms_batch_membership.py index 0463e9a6..a33efd36 100644 --- a/school/lms/doctype/lms_batch_membership/test_lms_batch_membership.py +++ b/school/lms/doctype/lms_batch_membership/test_lms_batch_membership.py @@ -23,7 +23,7 @@ class TestLMSBatchMembership(unittest.TestCase): "short_introduction": "Test Course", "description": "Test Course" }) - course.insert() + course.insert(ignore_permissions=True) self.new_user("mentor@test.com", "Test Mentor") # without this, the creating batch will fail diff --git a/school/lms/doctype/lms_course/lms_course.json b/school/lms/doctype/lms_course/lms_course.json index 86b7e5d1..5b63e616 100644 --- a/school/lms/doctype/lms_course/lms_course.json +++ b/school/lms/doctype/lms_course/lms_course.json @@ -19,7 +19,7 @@ "video_link", "image", "column_break_3", - "instructor", + "instructors", "tags", "section_break_7", "is_published", @@ -104,12 +104,11 @@ "options": "Chapter Reference" }, { - "fieldname": "instructor", - "fieldtype": "Link", - "in_list_view": 1, + "fieldname": "instructors", + "fieldtype": "Table MultiSelect", "in_standard_filter": 1, - "label": "Instructor", - "options": "User" + "label": "Instructors", + "options": "Course Instructor" }, { "fieldname": "section_break_7", @@ -168,7 +167,7 @@ "link_fieldname": "course" } ], - "modified": "2021-09-30 10:36:48.759995", + "modified": "2022-02-07 11:41:39.735325", "modified_by": "Administrator", "module": "LMS", "name": "LMS Course", @@ -190,6 +189,7 @@ "search_fields": "title", "sort_field": "creation", "sort_order": "DESC", + "states": [], "title_field": "title", "track_changes": 1 -} \ No newline at end of file +} diff --git a/school/lms/doctype/lms_course/lms_course.py b/school/lms/doctype/lms_course/lms_course.py index 31ed35e0..cefb5c6a 100644 --- a/school/lms/doctype/lms_course/lms_course.py +++ b/school/lms/doctype/lms_course/lms_course.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2021, FOSS United and contributors +# Copyright (c) 2021, Frappe and contributors # For license information, please see license.txt from __future__ import unicode_literals @@ -9,7 +8,7 @@ import json from ...utils import slugify from school.query import find, find_all from frappe.utils import flt, cint -from ...utils import slugify +from school.lms.utils import get_chapters class LMSCourse(Document): @@ -99,31 +98,7 @@ class LMSCourse(Document): }) doc.insert() - def get_mentors(self): - """Returns the list of all mentors for this course. - """ - course_mentors = [] - mentors = frappe.get_all("LMS Course Mentor Mapping", {"course": self.name}, ["mentor"]) - for mentor in mentors: - member = frappe.get_doc("User", mentor.mentor) - member.batch_count = frappe.db.count("LMS Batch Membership", - { - "member": member.name, - "member_type": "Mentor" - }) - course_mentors.append(member) - return course_mentors - def is_mentor(self, email): - """Checks if given user is a mentor for this course. - """ - if not email: - return False - return frappe.db.count("LMS Course Mentor Mapping", - { - "course": self.name, - "mentor": email - }) def get_student_batch(self, email): """Returns the batch the given student is part of. @@ -143,53 +118,6 @@ class LMSCourse(Document): fieldname="batch") return batch_name and frappe.get_doc("LMS Batch", batch_name) - def get_instructor(self): - if self.instructor: - return frappe.get_doc("User", self.instructor) - return frappe.get_doc("User", self.owner) - - def get_chapters(self): - """Returns all chapters of this course. - """ - chapters = [] - for row in self.chapters: - chapter_details = frappe.db.get_value("Course Chapter", row.chapter, - ["name", "title", "description"], - as_dict=True) - chapter_details.idx = row.idx - chapters.append(chapter_details) - return chapters - - def get_lessons(self, chapter=None): - """ If chapter is passed, returns lessons of only that chapter. - Else returns lessons of all chapters of the course """ - lessons = [] - - if chapter: - return self.get_lesson_details(chapter) - - for chapter in self.get_chapters(): - lesson = self.get_lesson_details(chapter) - lessons += lesson - - return lessons - - def get_lesson_details(self, chapter): - lessons = [] - lesson_list = frappe.get_all("Lesson Reference", {"parent": chapter.name}, - ["lesson", "idx"], order_by="idx") - for row in lesson_list: - lesson_details = frappe.get_doc("Course Lesson", row.lesson) - lesson_details.number = flt("{}.{}".format(chapter.idx, row.idx)) - lessons.append(lesson_details) - return lessons - - def get_slugified_chapter_title(self, chapter): - return slugify(chapter) - - def get_batch(self, batch_name): - return find("LMS Batch", name=batch_name, course=self.name) - def get_batches(self, mentor=None): batches = find_all("LMS Batch", course=self.name) if mentor: @@ -201,15 +129,6 @@ class LMSCourse(Document): batch_names = {m.batch for m in memberships} return [b for b in batches if b.name in batch_names] - def get_upcoming_batches(self): - now = frappe.utils.nowdate() - batches = find_all("LMS Batch", - course=self.name, - start_date=[">", now], - status="Active", - visibility="Public") - return batches - def get_cohorts(self): return find_all("Cohort", course=self.name, order_by="creation") @@ -217,36 +136,8 @@ class LMSCourse(Document): name = frappe.get_value("Cohort", {"course": self.name, "slug": cohort_slug}) return name and frappe.get_doc("Cohort", name) - def is_cohort_staff(self, user_email): - """Returns True if the user is either a mentor or a staff for one or more active cohorts of this course. - """ - q1 = { - "doctype": "Cohort Staff", - "course": self.name, - "email": user_email - } - q2 = { - "doctype": "Cohort Mentor", - "course": self.name, - "email": user_email - } - return frappe.db.exists(q1) or frappe.db.exists(q2) - - def get_lesson_index(self, lesson_name): - """Returns the {chapter_index}.{lesson_index} for the lesson. - """ - lesson = frappe.db.get_value("Lesson Reference", {"lesson": lesson_name}, ["idx", "parent"], as_dict=True) - if not lesson: - return None - - chapter = frappe.db.get_value("Chapter Reference", {"chapter": lesson.parent}, ["idx"], as_dict=True) - if not chapter: - return None - - return f"{chapter.idx}.{lesson.idx}" - def reindex_exercises(self): - for i, c in enumerate(self.get_chapters(), start=1): + for i, c in enumerate(get_chapters(self.name), start=1): self._reindex_exercises_in_chapter(c, i) def _reindex_exercises_in_chapter(self, c, index): @@ -258,133 +149,12 @@ class LMSCourse(Document): exercise.save() i += 1 - def get_learn_url(self, lesson_number): - if not lesson_number: - return - return f"/courses/{self.name}/learn/{lesson_number}" - - def get_membership(self, member, batch=None): - filters = { - "member": member, - "course": self.name - } - if batch: - filters["batch"] = batch - - membership = frappe.db.get_value("LMS Batch Membership", - filters, - ["name", "batch", "current_lesson", "member_type", "progress"], - as_dict=True) - - if membership and membership.batch: - membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title") - return membership - def get_all_memberships(self, member): all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name}, ["batch"]) for membership in all_memberships: membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title") return all_memberships - def get_students(self, batch=None): - """Returns (email, full_name, username) of all the students of this batch as a list of dict. - """ - filters = { - "course": self.name, - "member_type": "Student" - } - if batch: - filters["batch"] = batch - memberships = frappe.get_all( - "LMS Batch Membership", - filters, - ["member"]) - 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") - out_of_ratings = frappe.db.get_all("DocField", - { - "parent": "LMS Course Review", - "fieldtype": "Rating" - }, - ["options"]) - out_of_ratings = (len(out_of_ratings) and out_of_ratings[0].options) or 5 - for review in reviews: - review.rating = review.rating * out_of_ratings - 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_progress(self, lesson): - return frappe.db.get_value("LMS Course Progress", - { - "course": self.name, - "owner": frappe.session.user, - "lesson": lesson - }, - ["status"]) - - def get_course_progress(self, member=None): - """ Returns the course progress of the session user """ - lesson_count = len(self.get_lessons()) - if not lesson_count: - return 0 - completed_lessons = frappe.db.count("LMS Course Progress", - { - "course": self.name, - "owner": member or frappe.session.user, - "status": "Complete" - }) - precision = cint(frappe.db.get_default("float_precision")) or 3 - return flt(((completed_lessons/lesson_count) * 100), precision) - - def get_neighbours(self, current, lessons): - current = flt(current) - numbers = sorted(lesson.number for lesson in lessons) - index = numbers.index(current) - return { - "prev": numbers[index-1] if index-1 >= 0 else None, - "next": numbers[index+1] if index+1 < len(numbers) else None - } - - def is_certified(self): - certificate = frappe.get_all("LMS Certification", - { - "student": frappe.session.user, - "course": self.name - }) - if len(certificate): - return certificate[0].name - return - @frappe.whitelist() def reindex_exercises(doc): course_data = json.loads(doc) diff --git a/school/lms/doctype/lms_course/test_lms_course.py b/school/lms/doctype/lms_course/test_lms_course.py index 81193128..2cf891eb 100644 --- a/school/lms/doctype/lms_course/test_lms_course.py +++ b/school/lms/doctype/lms_course/test_lms_course.py @@ -19,7 +19,7 @@ class TestLMSCourse(unittest.TestCase): "short_introduction": title, "description": title }) - doc.insert() + doc.insert(ignore_permissions=True) return doc def test_new_course(self): diff --git a/school/lms/doctype/lms_course_review/lms_course_review.py b/school/lms/doctype/lms_course_review/lms_course_review.py index ffe100b9..8e274450 100644 --- a/school/lms/doctype/lms_course_review/lms_course_review.py +++ b/school/lms/doctype/lms_course_review/lms_course_review.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021, FOSS United and contributors +# Copyright (c) 2021, Frappe and contributors # For license information, please see license.txt import frappe diff --git a/school/lms/doctype/lms_quiz/test_lms_quiz.py b/school/lms/doctype/lms_quiz/test_lms_quiz.py index 77ca72c3..e778aab8 100644 --- a/school/lms/doctype/lms_quiz/test_lms_quiz.py +++ b/school/lms/doctype/lms_quiz/test_lms_quiz.py @@ -12,7 +12,7 @@ class TestLMSQuiz(unittest.TestCase): frappe.get_doc({ "doctype": "LMS Quiz", "title": "Test Quiz" - }).save() + }).save(ignore_permissions=True) def test_with_multiple_options(self): quiz = frappe.get_doc("LMS Quiz", "Test Quiz") diff --git a/school/lms/utils.py b/school/lms/utils.py index 9ea8ae5c..52f17a5d 100644 --- a/school/lms/utils.py +++ b/school/lms/utils.py @@ -1,4 +1,7 @@ import re +import frappe +from frappe.utils import flt, cint +from school.lms.md import markdown_to_html RE_SLUG_NOTALLOWED = re.compile("[^a-z0-9]+") @@ -28,3 +31,230 @@ def slugify(title, used_slugs=[]): return new_slug count = count+1 +def get_membership(course, member, batch=None): + filters = { + "member": member, + "course": course + } + if batch: + filters["batch"] = batch + + membership = frappe.db.get_value("LMS Batch Membership", + filters, + ["name", "batch", "current_lesson", "member_type", "progress"], + as_dict=True) + + if membership and membership.batch: + membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title") + return membership + +def get_chapters(course): + """Returns all chapters of this course. + """ + chapters = frappe.get_all("Chapter Reference", {"parent": course}, + ["idx", "chapter"], order_by="idx") + for chapter in chapters: + chapter_details = frappe.db.get_value("Course Chapter", { "name": chapter.chapter }, + ["name", "title", "description"], as_dict=True) + chapter.update(chapter_details) + return chapters + +def get_lessons(course, chapter=None): + """ If chapter is passed, returns lessons of only that chapter. + Else returns lessons of all chapters of the course """ + lessons = [] + if chapter: + return get_lesson_details(chapter) + + for chapter in get_chapters(course): + lesson = get_lesson_details(chapter) + lessons += lesson + + return lessons + +def get_lesson_details(chapter): + lessons = [] + lesson_list = frappe.get_all("Lesson Reference", + {"parent": chapter.name}, + ["lesson", "idx"], + order_by="idx") + + for row in lesson_list: + lesson_details = frappe.db.get_value("Course Lesson", row.lesson, + ["name", "title", "include_in_preview", "body"], as_dict=True) + lesson_details.number = flt("{}.{}".format(chapter.idx, row.idx)) + lessons.append(lesson_details) + return lessons + +def get_tags(course): + tags = frappe.db.get_value("LMS Course", course, "tags") + return tags.split(",") if tags else [] + +def get_instructors(course): + instructor_details = [] + instructors = frappe.get_all("Course Instructor", {"parent": course}, + ["instructor"], order_by="idx") + if not instructors: + instructors = frappe.db.get_value("LMS Course", course, "owner").split(" ") + for instructor in instructors: + instructor_details.append(frappe.db.get_value("User", + instructor.instructor, + ["name", "username", "full_name", "user_image"], + as_dict=True)) + return instructor_details + +def get_students(course, batch=None): + """Returns (email, full_name, username) of all the students of this batch as a list of dict. + """ + filters = { + "course": course, + "member_type": "Student" + } + if batch: + filters["batch"] = batch + return frappe.get_all( + "LMS Batch Membership", + filters, + ["member"]) + +def get_average_rating(course): + ratings = [review.rating for review in get_reviews(course)] + if not len(ratings): + return None + return sum(ratings)/len(ratings) + +def get_reviews(course): + reviews = frappe.get_all("LMS Course Review", + { + "course": course + }, + ["review", "rating", "owner"], + order_by= "creation desc") + out_of_ratings = frappe.db.get_all("DocField", + { + "parent": "LMS Course Review", + "fieldtype": "Rating" + }, + ["options"]) + out_of_ratings = (len(out_of_ratings) and out_of_ratings[0].options) or 5 + for review in reviews: + review.rating = review.rating * out_of_ratings + review.owner_details = frappe.db.get_value("User", + review.owner, ["name", "username", "full_name", "user_image"], as_dict=True) + + return reviews + +def is_certified(course): + certificate = frappe.get_all("LMS Certification", + { + "student": frappe.session.user, + "course": course + }) + if len(certificate): + return certificate[0].name + return + +def get_lesson_index(lesson_name): + """Returns the {chapter_index}.{lesson_index} for the lesson. + """ + lesson = frappe.db.get_value("Lesson Reference", + {"lesson": lesson_name}, ["idx", "parent"], as_dict=True) + if not lesson: + return None + + chapter = frappe.db.get_value("Chapter Reference", + {"chapter": lesson.parent}, ["idx"], as_dict=True) + if not chapter: + return None + + return f"{chapter.idx}.{lesson.idx}" + +def get_lesson_url(course, lesson_number): + if not lesson_number: + return + return f"/courses/{course}/learn/{lesson_number}" + +def get_batch(course, batch_name): + return frappe.get_all("LMS Batch", {"name": batch_name, "course": course}) + +def get_slugified_chapter_title(chapter): + return slugify(chapter) + +def get_progress(course, lesson): + return frappe.db.get_value("LMS Course Progress", { + "course": course, + "owner": frappe.session.user, + "lesson": lesson + }, + ["status"]) + +def render_html(body): + return markdown_to_html(body) + +def is_mentor(course, email): + """Checks if given user is a mentor for this course. + """ + if not email: + return False + return frappe.db.count("LMS Course Mentor Mapping", + { + "course": course, + "mentor": email + }) + +def is_cohort_staff(course, user_email): + """Returns True if the user is either a mentor or a staff for one or more active cohorts of this course. + """ + staff = { + "doctype": "Cohort Staff", + "course": course, + "email": user_email + } + mentor = { + "doctype": "Cohort Mentor", + "course": course, + "email": user_email + } + return frappe.db.exists(staff) or frappe.db.exists(mentor) + +def get_mentors(course): + """Returns the list of all mentors for this course. + """ + course_mentors = [] + mentors = frappe.get_all("LMS Course Mentor Mapping", {"course": course}, ["mentor"]) + for mentor in mentors: + member = frappe.db.get_value("User", mentor.mentor, + ["name", "username", "full_name", "user_image"]) + member.batch_count = frappe.db.count("LMS Batch Membership", + { + "member": member.name, + "member_type": "Mentor" + }) + course_mentors.append(member) + return course_mentors + +def is_eligible_to_review(course, membership): + """ Checks if user is eligible to review the course """ + if not membership: + return False + if frappe.db.count("LMS Course Review", + { + "course": course, + "owner": frappe.session.user + }): + return False + return True + +def get_course_progress(course, member=None): + """ Returns the course progress of the session user """ + lesson_count = len(get_lessons(course)) + if not lesson_count: + return 0 + completed_lessons = frappe.db.count("LMS Course Progress", + { + "course": course, + "owner": member or frappe.session.user, + "status": "Complete" + }) + precision = cint(frappe.db.get_default("float_precision")) or 3 + return flt(((completed_lessons/lesson_count) * 100), precision) diff --git a/school/lms/web_template/course_cards/course_cards.html b/school/lms/web_template/course_cards/course_cards.html index e65a8efb..cbf9563a 100644 --- a/school/lms/web_template/course_cards/course_cards.html +++ b/school/lms/web_template/course_cards/course_cards.html @@ -2,7 +2,8 @@

{{ title }}

{% for course_row in courses %} - {% set course = frappe.get_doc("LMS Course", course_row.course) %} + {% set course = frappe.db.get_value("LMS Course", course_row.course, + ["name", "upcoming", "title", "image", "enable_certification"], as_dict=True) %} {{ widgets.CourseCard(course=course, read_only=False) }} {% endfor %}
diff --git a/school/lms/widgets/ChapterTeaser.html b/school/lms/widgets/ChapterTeaser.html index 6bdcc5e3..7376acfd 100644 --- a/school/lms/widgets/ChapterTeaser.html +++ b/school/lms/widgets/ChapterTeaser.html @@ -1,12 +1,12 @@
- -