fix: removing get_doc dependencies for lms course

This commit is contained in:
Jannat Patel
2022-02-08 16:13:38 +05:30
parent 50c624e305
commit 57c69a7d6c
12 changed files with 193 additions and 213 deletions

View File

@@ -1,4 +1,4 @@
{% set color = member.get_palette() %}
{% set color = get_palette(member.full_name) %}
<a class="button-links" href="{{ get_profile_url(member.username) }}">
<span class="avatar {{ avatar_class }}" title="{{ member.full_name }}">
{% if member.user_image %}

View File

@@ -161,7 +161,11 @@ update_website_context = [
jinja = {
"methods": [
"school.page_renderers.get_profile_url"
"school.page_renderers.get_profile_url",
"school.overrides.user.get_palette",
"school.www.utils.get_membership",
"school.www.utils.get_lessons",
"school.www.utils.get_tags"
],
"filters": []
}

View File

@@ -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
@@ -143,46 +142,7 @@ 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)
@@ -201,15 +161,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")
@@ -263,22 +214,7 @@ class LMSCourse(Document):
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"])
@@ -302,8 +238,7 @@ 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",

View File

@@ -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

View File

@@ -105,7 +105,7 @@ def sanitize_html(html, macro):
any broken tags. This makes sures that all those things are fixed
before passing to the etree parser.
"""
soup = BeautifulSoup(html, features="lxml")
soup = BeautifulSoup(html, features="html5lib")
nodes = soup.body.children
classname = ""
if macro == "YouTubeVideo":

View File

@@ -1,47 +1,41 @@
{% set membership = course.get_membership(frappe.session.user) %}
{% set membership = get_membership(course.name, frappe.session.user) %}
{% set progress = frappe.utils.cint(membership.progress) %}
<div class="common-card-style course-card" data-course="{{ course.name }}">
<div class="course-image {% if not course.image %}default-image{% endif %}" {% if course.image %}
style="background-image: url( {{ course.image }} );" {% endif %}>
<div class="course-tags">
{% for tag in course.get_tags() %}
{% for tag in get_tags(course.name) %}
<div class="course-card-pills">{{ tag }}</div>
{% endfor %}
{% if membership and not read_only %}
{% if progress < 100 %} <div class="course-card-pills dark-pills ml-auto">{{ frappe.utils.rounded(progress) }}%
{{ _("Completed") }}
</div>
{% else %}
<div class="course-card-pills dark-pills ml-auto"> <img src="/assets/school/icons/check.svg"> {{ _("Completed")
}}</div>
{% if not course.image %}
<div class="default-image-text">{{ course.title[0] }}</div>
{% endif %}
{% endif %}
</div>
{% if not course.image %}
<div class="default-image-text">{{ course.title[0] }}</div>
{% endif %}
</div>
<div class="course-card-content">
<div class="course-card-meta muted-text">
{% if course.get_chapters() | length %}
<div class="course-card-meta">
{% if get_lessons(course.name) | length %}
<span>
{{ course.get_chapters() | length }} {{ _("Chapters") }}
</span>
{% endif %}
{% if course.get_chapters() | length and course.get_upcoming_batches() | length %}
<span class="font-weight-bold ml-3 mr-3"> . </span>
{% endif %}
{% if course.get_upcoming_batches() | length %}
<span class="">
{{ course.get_upcoming_batches() | length }} {{ _("Open Batches") }}
{{ get_lessons(course.name) | length }} {{ _("Lessons") }}
</span>
{% endif %}
</div>
<div class="card-heading course-card-title">{{ course.title }}</div>
<div {% if not read_only %} class="mb-4" {% endif %}>
<span class="zindex">
{% if membership and not read_only %}
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="{{ progress }}"
aria-valuemin="0" aria-valuemax="100" style="width:{{ progress }}%">
<span class="sr-only"> {{ progress }} Complete</span>
</div>
</div>
<div class="progress-percentage">{{ progress }}% Completed</div>
{% endif %}
<div class="course-card-footer">
<span>
{{ widgets.Avatar(member=course.get_instructor(), avatar_class="avatar-small") }}
<a class="button-links" href="{{ get_profile_url(course.get_instructor().username) }}">
<span class="course-instructor">
@@ -76,38 +70,21 @@
{% set certificate = course.is_certified() %}
{% if certificate %}
<div class="view-course-link is-default">
{{ _("Get Certificate") }}
</div>
<a class="stretched-link" href="/courses/{{ course.name }}/{{ certificate }}"></a>
{% elif course.enable_certification and progress == 100 %}
<div class="view-course-link is-default" id="certification" data-course="{{ course.name }}">
{{ _("Get Certificate") }}
</div>
<a class="stretched-link" id="certification" data-course="{{ course.name }}"></a>
{% elif progress == 100 %}
<div class="view-course-link is-default">
{{ _("Course Completed") }}
</div>
<a class="stretched-link" href="/courses/{{ course.name }}"></a>
{% elif course.upcoming %}
<div class="view-course-link is-secondary border">
{{ _("Upcoming Course") }}
</div>
<a class="stretched-link" href="/courses/{{ course.name }}"></a>
{% elif membership %}
<div class="view-course-link is-primary">
{{ _("Continue Course") }}
</div>
<a class="stretched-link" href="{{ course.get_learn_url(lesson_index) }}{{ query_parameter }}"></a>
{% else %}
<div class="view-course-link is-default">
{{ _("View Course") }}
</div>
<a class="stretched-link" href="/courses/{{ course.name }}"></a>
{% endif %}
@@ -117,21 +94,12 @@
<script>
frappe.ready(() => {
trim_course_titles();
$("#certification").unbind().click((e) => {
create_certificate(e);
});
})
var trim_course_titles = () => {
$(".course-card-title").each((i, element) => {
var title = $(element).text();
var length = $(window).width() <= 375 ? 60 : 65;
var suffix = title.length > length ? "..." : "";
$(element).text(title.substring(0, length) + suffix);
});
}
})
var create_certificate = (e) => {
e.preventDefault();

View File

@@ -93,27 +93,6 @@ class CustomUser(User):
'is_published': True
})
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'],
['--blue-avatar-bg', '--blue-avatar-color'],
['--green-avatar-bg', '--green-avatar-color'],
['--dark-green-avatar-bg', '--dark-green-avatar-color'],
['--red-avatar-bg', '--red-avatar-color'],
['--yellow-avatar-bg', '--yellow-avatar-color'],
['--purple-avatar-bg', '--purple-avatar-color'],
['--gray-avatar-bg', '--gray-avatar-color0']
]
encoded_name = str(self.full_name).encode("utf-8")
hash_name = hashlib.md5(encoded_name).hexdigest()
idx = cint((int(hash_name[4:6], 16) + 1) / 5.33)
return palette[idx % 8]
def get_batch_count(self) -> int:
"""Returns the number of batches authored by this user.
"""
@@ -174,6 +153,27 @@ class CustomUser(User):
"completed": completed
}
def get_palette(full_name):
"""
Returns a color unique to each member for Avatar """
palette = [
['--orange-avatar-bg', '--orange-avatar-color'],
['--pink-avatar-bg', '--pink-avatar-color'],
['--blue-avatar-bg', '--blue-avatar-color'],
['--green-avatar-bg', '--green-avatar-color'],
['--dark-green-avatar-bg', '--dark-green-avatar-color'],
['--red-avatar-bg', '--red-avatar-color'],
['--yellow-avatar-bg', '--yellow-avatar-color'],
['--purple-avatar-bg', '--purple-avatar-color'],
['--gray-avatar-bg', '--gray-avatar-color0']
]
encoded_name = str(full_name).encode("utf-8")
hash_name = hashlib.md5(encoded_name).hexdigest()
idx = cint((int(hash_name[4:6], 16) + 1) / 5.33)
return palette[idx % 8]
@frappe.whitelist(allow_guest=True)
def sign_up(email, full_name, verify_terms):
if is_signup_disabled():

View File

@@ -10,6 +10,7 @@
--text-3xl: 22px;
--text-4xl: 44px;
--navbar-shadow: 0px 1px 8px rgba(0, 0, 0, 0.08)
--gray-750: #505A62;
}
input[type=checkbox] {
@@ -56,15 +57,16 @@ input[type=checkbox] {
.course-card-pills {
background: #ffffff;
margin-left: 0;
margin-right: .5rem;
border-radius: 4px;
padding: 4px 6px;
font-size: 10px;
margin-right: 1rem;
border-radius: 6px;
padding: 3.5px 8px;
font-size: 11px;
text-align: center;
letter-spacing: 0.011em;
text-transform: uppercase;
font-weight: bold;
box-shadow: var(--shadow-base);
font-weight: 600;
box-shadow: var(--popover-box-shadow);
color: var(--gray-900);
}
.dark-pills {
@@ -94,6 +96,8 @@ input[type=checkbox] {
.course-card {
flex-direction: column;
height: 100%;
min-height: 350px;
}
.muted-text {
@@ -102,17 +106,24 @@ input[type=checkbox] {
}
.course-card-meta {
margin: 16px 0px 8px;
margin: 0.75rem 0 0.5rem;
font-size: 14px;
}
.course-card-meta-2 {
color: var(--gray-750);
}
.course-card-content {
width: 100%;
padding: 0px 1rem 1.25rem;
padding: 0 1.25rem 1.25rem;
display: flex;
flex-direction: column;
flex: 1 1 auto;
}
@media (max-width: 350px) {
.course-card-content {
padding: 0px 10px 20px;
padding: 0px 10px 10px;
}
}
@@ -124,8 +135,8 @@ input[type=checkbox] {
.course-card-title {
font-size: 1.125rem;
margin-bottom: 1.5rem;
height: 56px;
font-weight: 600;
margin-bottom: 1.25rem;
}
@media (max-width: 360px) {
@@ -145,16 +156,19 @@ input[type=checkbox] {
}
.course-instructor {
margin-left: 8px;
font-size: 12px;
line-height: 135%;
color: var(--text-color-dark);
margin-left: 0.625rem;
font-size: 0.875rem;
}
.course-student-count {
display: flex;
font-size: 12px;
float: right;
font-weight: 500;
}
.course-card-footer {
margin-top: auto;
}
.view-course-link {
@@ -1181,7 +1195,7 @@ input[type=checkbox] {
.progress {
width: 100%;
height: 8px;
height: 4px;
}
.progress-bar {
@@ -1189,12 +1203,9 @@ input[type=checkbox] {
}
.progress-percentage {
width: 100%;
font-size: 12px;
line-height: 165%;
letter-spacing: 0.02em;
color: #000000;
text-align: center;
margin: 0.5rem 0 1.3rem;
font-size: 0.8rem;
font-weight: 500;
}
pre {

View File

@@ -1,35 +0,0 @@
import frappe
from school.lms.models import Course
def get_common_context(context):
context.no_cache = 1
course_name = frappe.form_dict["course"]
try:
batch_name = frappe.form_dict["batch"]
except KeyError:
batch_name = None
course = frappe.get_doc("LMS Course", course_name)
if not course:
context.template = "www/404.html"
return
context.course = course
context.lessons = course.get_lessons()
membership = course.get_membership(frappe.session.user, batch_name)
context.membership = membership
if membership:
batch = course.get_batch(membership.batch)
if batch:
context.batch = batch
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
context.livecode_url = get_livecode_url()
def get_livecode_url():
return frappe.db.get_single_value("LMS Settings", "livecode_url")
def redirect_to_lesson(course, index_="1.1"):
frappe.local.flags.redirect_location = course.get_learn_url(index_) + course.query_parameter
raise frappe.Redirect

View File

@@ -19,13 +19,13 @@
{% include "school/templates/search_course/search_course.html" %}
<div class="course-list">
{% set title = _("Live Courses") %}
{% set courses = live_courses %}
{% set title = _("All Live Courses ({0})").format(courses | length) %}
{% set classes = "live-courses" %}
{% include "school/templates/course_list.html" %}
{% set title = _("Upcoming Courses") %}
{% set courses = upcoming_courses %}
{% set title = _("All Upcoming Courses ({0})").format(courses | length) %}
{% set classes = "upcoming-courses mt-10" %}
{% include "school/templates/course_list.html" %}
</div>

View File

@@ -6,21 +6,21 @@ def get_context(context):
context.live_courses, context.upcoming_courses = get_courses()
context.restriction = check_profile_restriction()
context.metatags = {
"title": "All Courses",
"title": "All Live Courses",
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
"description": "This page lists all the courses published on our website",
"keywords": "All Courses, Courses, Learn"
}
def get_courses():
course_names = frappe.get_all("LMS Course",
courses = frappe.get_all("LMS Course",
filters={"is_published": True},
fields=["name", "upcoming"])
fields=["name", "upcoming", "title", "image"])
live_courses, upcoming_courses = [], []
for course in course_names:
for course in courses:
if course.upcoming:
upcoming_courses.append(frappe.get_doc("LMS Course", course.name))
upcoming_courses.append(course)
else:
live_courses.append(frappe.get_doc("LMS Course", course.name))
live_courses.append(course)
return live_courses, upcoming_courses

97
school/www/utils.py Normal file
View File

@@ -0,0 +1,97 @@
import frappe
from school.lms.models import Course
from frappe.utils import flt
def get_common_context(context):
context.no_cache = 1
course_name = frappe.form_dict["course"]
try:
batch_name = frappe.form_dict["batch"]
except KeyError:
batch_name = None
course = frappe.get_doc("LMS Course", course_name)
if not course:
context.template = "www/404.html"
return
context.course = course
context.lessons = course.get_lessons()
membership = get_membership(course_name, frappe.session.user, batch_name)
context.membership = membership
if membership:
batch = course.get_batch(membership.batch)
if batch:
context.batch = batch
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
context.livecode_url = get_livecode_url()
def get_livecode_url():
return frappe.db.get_single_value("LMS Settings", "livecode_url")
def redirect_to_lesson(course, index_="1.1"):
frappe.local.flags.redirect_location = course.get_learn_url(index_) + course.query_parameter
raise frappe.Redirect
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("Course Chapter",
{ "course": course },
["name", "title", "description", "idx"])
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", 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_instructor(course):
if self.instructor:
return frappe.get_doc("User", self.instructor)
return frappe.get_doc("User", self.owner)