Compare commits
29 Commits
redesign-f
...
chapter-le
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a667643681 | ||
|
|
f278e4b6a5 | ||
|
|
33a12c2dec | ||
|
|
508f90f459 | ||
|
|
709f0c2274 | ||
|
|
be47700e7c | ||
|
|
40842830a4 | ||
|
|
11d070fa0d | ||
|
|
dd2f830a33 | ||
|
|
324033e9ee | ||
|
|
86596d0cfe | ||
|
|
9323cfd748 | ||
|
|
d125b02cec | ||
|
|
276d64a66a | ||
|
|
79eb381a41 | ||
|
|
44f9c0dfd3 | ||
|
|
0ca4cd724e | ||
|
|
8a3e31f021 | ||
|
|
9be8a1af0b | ||
|
|
b9cac20613 | ||
|
|
e6d5e6d37b | ||
|
|
0abfcac7da | ||
|
|
b70e8b9acc | ||
|
|
3b1e1aa3c3 | ||
|
|
8f74c74d50 | ||
|
|
d2f435016c | ||
|
|
389b35802b | ||
|
|
a9192a74f9 | ||
|
|
5ecae0df61 |
@@ -2,7 +2,15 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Chapter', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
onload: function (frm) {
|
||||
frm.set_query("lesson", "lessons", function () {
|
||||
return {
|
||||
filters: {
|
||||
"chapter": frm.doc.name,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
"course",
|
||||
"title",
|
||||
"description",
|
||||
"locked",
|
||||
"index_"
|
||||
"lessons"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -24,12 +23,6 @@
|
||||
"fieldtype": "Markdown Editor",
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "locked",
|
||||
"fieldtype": "Check",
|
||||
"label": "Locked"
|
||||
},
|
||||
{
|
||||
"fieldname": "course",
|
||||
"fieldtype": "Link",
|
||||
@@ -38,10 +31,10 @@
|
||||
"options": "LMS Course"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "index_",
|
||||
"fieldtype": "Int",
|
||||
"label": "Index"
|
||||
"fieldname": "lessons",
|
||||
"fieldtype": "Table",
|
||||
"label": "Lessons",
|
||||
"options": "Lessons"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
@@ -52,7 +45,7 @@
|
||||
"link_fieldname": "chapter"
|
||||
}
|
||||
],
|
||||
"modified": "2021-05-13 21:05:20.531890",
|
||||
"modified": "2021-07-27 16:28:08.667964",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Chapter",
|
||||
|
||||
@@ -5,15 +5,6 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from ...utils import slugify
|
||||
|
||||
class Chapter(Document):
|
||||
def get_lessons(self):
|
||||
rows = frappe.db.get_all("Lesson",
|
||||
filters={"chapter": self.name},
|
||||
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)
|
||||
pass
|
||||
|
||||
32
community/lms/doctype/chapters/chapters.json
Normal file
32
community/lms/doctype/chapters/chapters.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2021-07-27 16:25:02.903245",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"chapter"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "chapter",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Chapter",
|
||||
"options": "Chapter",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-27 16:25:02.903245",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Chapters",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
8
community/lms/doctype/chapters/chapters.py
Normal file
8
community/lms/doctype/chapters/chapters.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2021, FOSS United and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Chapters(Document):
|
||||
pass
|
||||
@@ -30,10 +30,13 @@ class InviteRequest(Document):
|
||||
return user
|
||||
|
||||
def send_email(self):
|
||||
subject = _("Your request has been approved.")
|
||||
site_name = "Mon.School"
|
||||
subject = _("Welcome to {0}!").format(site_name)
|
||||
|
||||
args = {
|
||||
"full_name": self.full_name,
|
||||
"signup_form_link": "/new-sign-up?invite_code={0}".format(self.name),
|
||||
"site_name": site_name,
|
||||
"site_url": frappe.utils.get_url()
|
||||
}
|
||||
frappe.sendmail(
|
||||
@@ -42,7 +45,8 @@ class InviteRequest(Document):
|
||||
subject=subject,
|
||||
header=[subject, "green"],
|
||||
template = "lms_invite_request_approved",
|
||||
args=args)
|
||||
args=args,
|
||||
now=True)
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def create_invite_request(invite_email):
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"include_in_preview",
|
||||
"column_break_4",
|
||||
"title",
|
||||
"index_",
|
||||
"index_label",
|
||||
"section_break_6",
|
||||
"body",
|
||||
@@ -31,13 +30,6 @@
|
||||
"in_list_view": 1,
|
||||
"label": "Title"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "index_",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "Index"
|
||||
},
|
||||
{
|
||||
"fieldname": "body",
|
||||
"fieldtype": "Markdown Editor",
|
||||
@@ -75,7 +67,7 @@
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-29 13:34:49.077363",
|
||||
"modified": "2021-07-27 16:28:29.203624",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Lesson",
|
||||
|
||||
31
community/lms/doctype/lessons/lessons.json
Normal file
31
community/lms/doctype/lessons/lessons.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2021-07-27 16:25:48.269536",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"lesson"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "lesson",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Lesson",
|
||||
"options": "Lesson"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-27 16:53:52.732191",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Lessons",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
8
community/lms/doctype/lessons/lessons.py
Normal file
8
community/lms/doctype/lessons/lessons.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2021, FOSS United and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Lessons(Document):
|
||||
pass
|
||||
@@ -2,7 +2,15 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('LMS Course', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
onload: function (frm) {
|
||||
frm.set_query("chapter", "chapters", function () {
|
||||
return {
|
||||
filters: {
|
||||
"course": frm.doc.name,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"action": "community.lms.doctype.lms_course.lms_course.reindex_lessons",
|
||||
"action_type": "Server Action",
|
||||
"group": "Reindex",
|
||||
"label": "Reindex Lessons"
|
||||
},
|
||||
{
|
||||
"action": "community.lms.doctype.lms_course.lms_course.reindex_exercises",
|
||||
"action_type": "Server Action",
|
||||
@@ -21,16 +15,17 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"short_code",
|
||||
"video_link",
|
||||
"column_break_3",
|
||||
"is_published",
|
||||
"disable_self_learning",
|
||||
"image",
|
||||
"section_break_5",
|
||||
"column_break_3",
|
||||
"tags",
|
||||
"is_published",
|
||||
"upcoming",
|
||||
"disable_self_learning",
|
||||
"section_break_5",
|
||||
"short_introduction",
|
||||
"description"
|
||||
"description",
|
||||
"chapters"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -53,11 +48,6 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Published"
|
||||
},
|
||||
{
|
||||
"fieldname": "short_code",
|
||||
"fieldtype": "Data",
|
||||
"label": "Short Code"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
@@ -92,6 +82,18 @@
|
||||
"fieldname": "tags",
|
||||
"fieldtype": "Data",
|
||||
"label": "Tags"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "upcoming",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is an Upcoming Course"
|
||||
},
|
||||
{
|
||||
"fieldname": "chapters",
|
||||
"fieldtype": "Table",
|
||||
"label": "Chapters",
|
||||
"options": "Chapters"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
@@ -111,14 +113,9 @@
|
||||
"group": "Mentors",
|
||||
"link_doctype": "LMS Course Mentor Mapping",
|
||||
"link_fieldname": "course"
|
||||
},
|
||||
{
|
||||
"group": "Mentors",
|
||||
"link_doctype": "LMS Mentor Request",
|
||||
"link_fieldname": "course"
|
||||
}
|
||||
],
|
||||
"modified": "2021-07-09 15:05:05.372430",
|
||||
"modified": "2021-07-28 19:01:50.677445",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Course",
|
||||
@@ -141,6 +138,5 @@
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "title",
|
||||
"track_changes": 1,
|
||||
"track_views": 1
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -9,8 +9,10 @@ import json
|
||||
from ...utils import slugify
|
||||
from community.query import find, find_all
|
||||
from frappe.utils import flt, cint
|
||||
from ...utils import slugify
|
||||
|
||||
class LMSCourse(Document):
|
||||
|
||||
@staticmethod
|
||||
def find(name):
|
||||
"""Returns the course with specified name.
|
||||
@@ -112,17 +114,42 @@ class LMSCourse(Document):
|
||||
def get_chapters(self):
|
||||
"""Returns all chapters of this course.
|
||||
"""
|
||||
# TODO: chapters should have a way to specify the order
|
||||
return find_all("Chapter", course=self.name, order_by="index_")
|
||||
chapters = []
|
||||
for row in self.chapters:
|
||||
chapter_details = frappe.db.get_value("Chapter", row.chapter,
|
||||
["name", "title", "description"],
|
||||
as_dict=True)
|
||||
chapter_details.idx = row.idx
|
||||
chapters.append(chapter_details)
|
||||
return chapters
|
||||
|
||||
def get_lessons(self):
|
||||
""" Returns all lessons of this course """
|
||||
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 = []
|
||||
chapters = self.get_chapters()
|
||||
for chapter in chapters:
|
||||
lessons.append(frappe.get_all("Lesson", {"chapter": chapter.name}))
|
||||
|
||||
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("Lessons", {"parent": chapter.name},
|
||||
["lesson", "idx"], order_by="idx")
|
||||
for row in lesson_list:
|
||||
lesson_details = frappe.get_doc("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_course_progress(self):
|
||||
""" Returns the course progress of the session user """
|
||||
lesson_count = len(self.get_lessons())
|
||||
@@ -160,38 +187,12 @@ class LMSCourse(Document):
|
||||
visibility="Public")
|
||||
return batches
|
||||
|
||||
def get_chapter(self, index):
|
||||
return find("Chapter", course=self.name, index_=index)
|
||||
|
||||
def get_lesson(self, chapter_index, lesson_index):
|
||||
chapter_name = frappe.get_value(
|
||||
"Chapter",
|
||||
{"course": self.name, "index_": chapter_index},
|
||||
"name")
|
||||
lesson_name = chapter_name and frappe.get_value(
|
||||
"Lesson",
|
||||
{"chapter": chapter_name, "index_": lesson_index},
|
||||
"name")
|
||||
return lesson_name and frappe.get_doc("Lesson", lesson_name)
|
||||
|
||||
def get_lesson_index(self, lesson_name):
|
||||
"""Returns the {chapter_index}.{lesson_index} for the lesson.
|
||||
"""
|
||||
lesson = frappe.get_doc("Lesson", lesson_name)
|
||||
chapter = frappe.get_doc("Chapter", lesson.chapter)
|
||||
return f"{chapter.index_}.{lesson.index_}"
|
||||
|
||||
def reindex_lessons(self):
|
||||
for i, c in enumerate(self.get_chapters(), start=1):
|
||||
c.index_ = i
|
||||
c.save()
|
||||
self._reindex_lessons_in_chapter(c)
|
||||
|
||||
def _reindex_lessons_in_chapter(self, c):
|
||||
for i, lesson in enumerate(c.get_lessons(), start=1):
|
||||
lesson.index = i
|
||||
lesson.index_label = f"{c.index_}.{i}"
|
||||
lesson.save()
|
||||
lesson = frappe.db.get_value("Lessons", {"lesson": lesson_name}, ["idx", "parent"], as_dict=True)
|
||||
chapter = frappe.db.get_value("Chapters", {"chapter": lesson.parent}, ["idx"], as_dict=True)
|
||||
return f"{chapter.idx}.{lesson.idx}"
|
||||
|
||||
def reindex_exercises(self):
|
||||
for i, c in enumerate(self.get_chapters(), start=1):
|
||||
@@ -202,7 +203,7 @@ class LMSCourse(Document):
|
||||
|
||||
def _reindex_exercises_in_chapter(self, c):
|
||||
i = 1
|
||||
for lesson in c.get_lessons():
|
||||
for lesson in self.get_lessons(c):
|
||||
for exercise in lesson.get_exercises():
|
||||
exercise.index_ = i
|
||||
exercise.index_label = f"{c.index_}.{i}"
|
||||
@@ -302,9 +303,6 @@ class LMSCourse(Document):
|
||||
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",
|
||||
{
|
||||
@@ -314,55 +312,14 @@ class LMSCourse(Document):
|
||||
},
|
||||
["status"])
|
||||
|
||||
class CourseOutline:
|
||||
def __init__(self, course):
|
||||
self.course = course
|
||||
self.chapters = self.get_chapters()
|
||||
self.lessons = self.get_lessons()
|
||||
|
||||
def get_next(self, current):
|
||||
def get_neighbours(self, current, lessons):
|
||||
current = flt(current)
|
||||
numbers = sorted(lesson['number'] for lesson in self.lessons)
|
||||
try:
|
||||
numbers = sorted(lesson.number for lesson in lessons)
|
||||
index = numbers.index(current)
|
||||
return numbers[index+1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def get_prev(self, current):
|
||||
current = flt(current)
|
||||
numbers = sorted(lesson['number'] for lesson in self.lessons)
|
||||
try:
|
||||
index = numbers.index(current)
|
||||
if index == 0:
|
||||
return None
|
||||
return numbers[index-1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def get_chapters(self):
|
||||
return frappe.db.get_all("Chapter",
|
||||
filters={"course": self.course.name},
|
||||
fields=["name", "title", "index_"],
|
||||
order_by="index_")
|
||||
|
||||
def get_lessons(self):
|
||||
chapters = [c['name'] for c in self.chapters]
|
||||
lessons = frappe.db.get_all("Lesson",
|
||||
filters={"chapter": ["IN", chapters]},
|
||||
fields=["name", "title", "chapter", "index_"])
|
||||
|
||||
chapter_numbers = {c['name']: c['index_'] for c in self.chapters}
|
||||
for lesson in lessons:
|
||||
lesson['number'] = flt("{}.{}".format(chapter_numbers[lesson['chapter']], lesson['index_']))
|
||||
return lessons
|
||||
|
||||
@frappe.whitelist()
|
||||
def reindex_lessons(doc):
|
||||
course_data = json.loads(doc)
|
||||
course = frappe.get_doc("LMS Course", course_data['name'])
|
||||
course.reindex_lessons()
|
||||
frappe.msgprint("All lessons in this course have been re-indexed.")
|
||||
return {
|
||||
"prev": numbers[index-1] if index-1 >= 0 else None,
|
||||
"next": numbers[index+1] if index+1 < len(numbers) else None
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def reindex_exercises(doc):
|
||||
|
||||
@@ -27,12 +27,13 @@
|
||||
"fieldname": "lesson",
|
||||
"fieldtype": "Link",
|
||||
"label": "Lesson",
|
||||
"options": "Lesson"
|
||||
"options": "Lesson",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-23 17:58:57.642873",
|
||||
"modified": "2021-07-23 19:06:12.551633",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Quiz",
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
<div class="mt-5">
|
||||
<a class="anchor_style" href="/courses">Courses</a> /{% if course.is_mentor(frappe.session.user) %} <a
|
||||
class="anchor_style" href="/courses/{{ course.name }}"> {{ course.title }}</a> {% else %} <span class="text-muted">
|
||||
{{ course.title }}</span> {% endif %}
|
||||
{% set all_memberships = course.get_all_memberships(frappe.session.user) %}
|
||||
{% if membership and membership.batch and all_memberships | length > 1 %}
|
||||
<a class="pull-right dropdown-item border rounded" style="width: 10rem;" href="#" id="navbarDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{ membership.batch_title }}
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
{% for data in all_memberships %}
|
||||
{% if data.batch != membership.batch %}
|
||||
<a class="dropdown-item switch-batch"
|
||||
href="/courses/{{ course.name }}/home?batch={{ data.batch }}">{{ data.batch_title }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if not membership %}
|
||||
{% set display_class = "hide" %}
|
||||
{% else %}
|
||||
{% set display_class = "" %}
|
||||
{% endif %}
|
||||
|
||||
<ul class="nav nav-tabs mt-4">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="home" href="/courses/{{course.name}}/home{{ course.query_parameter }}">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
{% set lesson_index = course.get_lesson_index(membership.current_lesson) if membership and membership.current_lesson
|
||||
else '1.1' %}
|
||||
<a class="nav-link" id="learn"
|
||||
href="{{ course.get_learn_url(lesson_index) }}{{ course.query_parameter }}">Lessons</a>
|
||||
</li>
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link" id="schedule" href="/courses/{{course.name}}/schedule">Schedule</a>
|
||||
</li> -->
|
||||
<li class="nav-item {{ display_class }}">
|
||||
<a class="nav-link" id="members" href="/courses/{{course.name}}/members{{ course.query_parameter }}">Members</a>
|
||||
</li>
|
||||
<!-- <li class="nav-item {{ display_class }}">
|
||||
<a class="nav-link" id="discussion" href="/courses/{{course.name}}/discuss">Discussion</a>
|
||||
</li> -->
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link" id="about" href="/courses/{{course.name}}/about">About</a>
|
||||
</li> -->
|
||||
{% if membership and membership.batch and course.is_mentor(frappe.session.user) %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="progress" href="/courses/{{course.name}}/progress{{ course.query_parameter }}">Progress</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% block script %}
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
var selector = document.querySelector(`a[href="${decodeURIComponent(window.location.pathname)}{{ course.query_parameter }}"]`)
|
||||
if (selector) {
|
||||
selector.classList.add('active');
|
||||
}
|
||||
else {
|
||||
$("#learn").addClass('active')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="course-instructor breadcrumb">
|
||||
<div class="breadcrumb">
|
||||
<a class="dark-links" href="/courses">All Courses</a>
|
||||
<img class="ml-1 mr-1" src="/assets/community/icons/chevron-right.svg">
|
||||
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
<div>
|
||||
|
||||
<div class="small-title chapter-title" data-target="#{{ chapter.get_slugified_chapter_title() }}"
|
||||
<div class="small-title chapter-title" data-target="#{{ course.get_slugified_chapter_title(chapter.title) }}"
|
||||
data-toggle="collapse" aria-expanded="false">
|
||||
<img class="chapter-icon" src="/assets/community/icons/chevron-right.svg">
|
||||
{{ index }}. {{ chapter.title }}
|
||||
</div>
|
||||
|
||||
<div class="chapter-content collapse navbar-collapse" id="{{ chapter.get_slugified_chapter_title() }}">
|
||||
<div class="chapter-content collapse navbar-collapse" id="{{ course.get_slugified_chapter_title(chapter.title) }}">
|
||||
|
||||
{% if chapter.description %}
|
||||
<div class="chapter-description muted-text">
|
||||
{{ chapter.description }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="lessons">
|
||||
|
||||
{% for lesson in chapter.get_lessons() %}
|
||||
{% for lesson in course.get_lessons(chapter) %}
|
||||
|
||||
<div class="lesson-info{% if membership.current_lesson == lesson.name %} active-lesson {% endif %}">
|
||||
|
||||
{% if membership or lesson.include_in_preview %}
|
||||
<a class="lesson-links"
|
||||
href="{{ course.get_learn_url(course.get_lesson_index(lesson.name)) }}{{course.query_parameter}}"
|
||||
href="{{ course.get_learn_url(lesson.number) }}{{course.query_parameter}}"
|
||||
data-course="{{ course.name }}">
|
||||
{{ lesson.title }}
|
||||
|
||||
|
||||
@@ -53,15 +53,19 @@
|
||||
|
||||
{% set query_parameter = "?batch=" + membership.batch if membership and membership.batch else "" %}
|
||||
|
||||
{% if membership %}
|
||||
{% if course.upcoming %}
|
||||
<div class="view-course-link is-default">
|
||||
Upcoming Course <img class="ml-3" src="/assets/community/icons/black-arrow.svg" />
|
||||
</div>
|
||||
<a class="stretched-link" href="/courses/{{ course.name }}"></a>
|
||||
|
||||
{% elif membership %}
|
||||
<div class="view-course-link is-primary">
|
||||
Continue Course <img class="ml-3" src="/assets/community/icons/white-arrow.svg" />
|
||||
</div>
|
||||
<a class="stretched-link" href="{{ course.get_learn_url(lesson_index) }}{{ query_parameter }}"></a>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="view-course-link">
|
||||
View Course <img class="ml-3" src="/assets/community/icons/black-arrow.svg" />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% if course.get_reviews() | length %}
|
||||
<div class="reviews-parent col">
|
||||
<div class="reviews-parent">
|
||||
<div class="reviews-heading">
|
||||
<div class="course-home-headings">Student Review</div>
|
||||
{% if course.is_eligible_to_review(membership) %}
|
||||
|
||||
@@ -10,7 +10,8 @@ class CustomUser(User):
|
||||
"""
|
||||
return frappe.get_all(
|
||||
'LMS Course', {
|
||||
'owner': self.name
|
||||
'owner': self.name,
|
||||
'is_published': True
|
||||
})
|
||||
|
||||
def get_palette(self):
|
||||
@@ -62,9 +63,16 @@ class CustomUser(User):
|
||||
|
||||
def get_mentored_courses(self):
|
||||
""" Returns all courses mentored by this user """
|
||||
return frappe.get_all("LMS Course Mentor Mapping",
|
||||
mentored_courses = []
|
||||
mapping = frappe.get_all("LMS Course Mentor Mapping",
|
||||
{
|
||||
"mentor": self.name
|
||||
"mentor": self.name,
|
||||
},
|
||||
["name", "course"]
|
||||
)
|
||||
|
||||
for map in mapping:
|
||||
if frappe.db.get_value("LMS Course", map.course, "is_published"):
|
||||
mentored_courses.append(map)
|
||||
|
||||
return mentored_courses
|
||||
|
||||
@@ -6,3 +6,4 @@ community.patches.replace_member_with_user_in_batch_membership
|
||||
community.patches.replace_member_with_user_in_course_mentor_mapping
|
||||
community.patches.replace_member_with_user_in_lms_message
|
||||
community.patches.replace_member_with_user_in_mentor_request
|
||||
community.patches.v0_0.chapter_lesson_index_table
|
||||
|
||||
50
community/patches/v0_0/chapter_lesson_index_table.py
Normal file
50
community/patches/v0_0/chapter_lesson_index_table.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_course")
|
||||
frappe.reload_doc("lms", "doctype", "chapter")
|
||||
frappe.reload_doc("lms", "doctype", "lesson")
|
||||
frappe.reload_doc("lms", "doctype", "lessons")
|
||||
frappe.reload_doc("lms", "doctype", "chapters")
|
||||
|
||||
update_chapters()
|
||||
update_lessons()
|
||||
|
||||
def update_chapters():
|
||||
courses = frappe.get_all("LMS Course", pluck="name")
|
||||
for course in courses:
|
||||
course_details = frappe.get_doc("LMS Course", course)
|
||||
chapters = frappe.get_all("Chapter",
|
||||
{
|
||||
"course": course
|
||||
},
|
||||
["name"],
|
||||
order_by= "index_"
|
||||
)
|
||||
for chapter in chapters:
|
||||
course_details.append("chapters",
|
||||
{
|
||||
"chapter": chapter.name
|
||||
})
|
||||
|
||||
course_details.save()
|
||||
|
||||
def update_lessons():
|
||||
chapters = frappe.get_all("Chapter", pluck="name")
|
||||
for chapter in chapters:
|
||||
chapter_details = frappe.get_doc("Chapter", chapter)
|
||||
lessons = frappe.get_all("Lesson",
|
||||
{
|
||||
"chapter": chapter
|
||||
},
|
||||
["name"],
|
||||
order_by= "index_"
|
||||
)
|
||||
for lesson in lessons:
|
||||
chapter_details.append("lessons",
|
||||
{
|
||||
"lesson": lesson.name
|
||||
})
|
||||
|
||||
chapter_details.save()
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
@import "./style.css";
|
||||
@import "./vars.css";
|
||||
@import "./style.less";
|
||||
|
||||
@@ -259,11 +259,11 @@ input[type=checkbox] {
|
||||
|
||||
.common-page-style {
|
||||
background: #F4F5F6;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.common-card-style {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
background: #FFFFFF;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
@@ -292,6 +292,12 @@ input[type=checkbox] {
|
||||
padding: 0px 24px 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
.course-card-content {
|
||||
padding: 0px 10px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.course-card-title {
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
@@ -308,6 +314,7 @@ input[type=checkbox] {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-divider {
|
||||
border: 1px solid #F4F5F6;
|
||||
margin-bottom: 16px;
|
||||
@@ -358,7 +365,7 @@ input[type=checkbox] {
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.cards-parent {
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 336px));
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
-moz-column-gap: 16px;
|
||||
column-gap: 16px;
|
||||
row-gap: 16px;
|
||||
@@ -367,7 +374,7 @@ input[type=checkbox] {
|
||||
|
||||
@media (max-width: 375px) {
|
||||
.cards-parent {
|
||||
grid-template-columns: repeat(auto-fill, minmax(100%, 312px));
|
||||
grid-template-columns: repeat(auto-fill, minmax(100%, 1fr));
|
||||
-moz-column-gap: 24px;
|
||||
column-gap: 24px;
|
||||
row-gap: 24px;
|
||||
@@ -375,7 +382,7 @@ input[type=checkbox] {
|
||||
}
|
||||
|
||||
.courses-header {
|
||||
padding: 50px 30px 20px;
|
||||
padding: 50px 20px 20px;
|
||||
color: var(--text-color);
|
||||
font-weight: 600;
|
||||
font-size: 22px;
|
||||
@@ -383,22 +390,10 @@ input[type=checkbox] {
|
||||
letter-spacing: -0.0175em
|
||||
}
|
||||
|
||||
.course-top-section {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
max-width: 1150px;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.course-top-section {
|
||||
max-width: 720px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.course-top-section {
|
||||
max-width: 342px;
|
||||
@media (min-width: 576px) and (max-width: 992px) {
|
||||
.container {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +491,6 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.course-card-wide {
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 24px;
|
||||
@@ -528,6 +522,7 @@ div.custom-checkbox>label>input:checked+img {
|
||||
margin-right: 32px;
|
||||
border-radius: 5px;
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -546,23 +541,10 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.course-home-page {
|
||||
max-width: 1150px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.course-home-page {
|
||||
max-width: 688px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.course-home-page {
|
||||
width: 100%;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,6 +552,7 @@ div.custom-checkbox>label>input:checked+img {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 2;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -670,6 +653,12 @@ div.custom-checkbox>label>input:checked+img {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.is-default {
|
||||
background-color: white;
|
||||
border: 1px solid #C8CFD5;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.course-home-outline {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
@@ -689,6 +678,7 @@ div.custom-checkbox>label>input:checked+img {
|
||||
.chapter-description {
|
||||
height: fit-content;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.chapter-icon {
|
||||
@@ -696,14 +686,39 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.course-outline-instructor-parent {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
display: grid;
|
||||
grid-gap: 2rem;
|
||||
grid-template-columns: 4fr 1fr;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.course-outline-instructor-parent {
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.course-outline-instructor-parent {
|
||||
flex-direction: column;
|
||||
padding: 0px 9px 0px;
|
||||
padding: 0px 24px 0px;
|
||||
grid-template-columns: none;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-parent-section {
|
||||
display: grid;
|
||||
grid-gap: 2rem;
|
||||
grid-template-columns: 4fr 1fr;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.profile-parent-section {
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.profile-parent-section {
|
||||
grid-template-columns: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,7 +757,7 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.lesson-links {
|
||||
display: block;
|
||||
display: flex;
|
||||
padding: 0 1rem;
|
||||
margin-bottom: .25rem;
|
||||
color: inherit;
|
||||
@@ -892,14 +907,18 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.description-card {
|
||||
padding: 24px;
|
||||
padding: 1.5rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.overview-card {
|
||||
padding: 8px 24px 8px;
|
||||
padding: 1.5rem;
|
||||
width: 256px;
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
-moz-column-gap: 1rem;
|
||||
column-gap: 1rem;
|
||||
row-gap: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -910,7 +929,7 @@ div.custom-checkbox>label>input:checked+img {
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.overview-card {
|
||||
padding: 8px 9px 8px;
|
||||
padding: 24px 26px 24px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -932,10 +951,6 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
}
|
||||
|
||||
.overtime-item {
|
||||
margin: 16px 0px 16px;
|
||||
}
|
||||
|
||||
.view-all-mentors {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@@ -995,20 +1010,16 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
padding: 1rem 0.5rem 0;
|
||||
padding: 1rem 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
line-height: 135%;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.course-details-outline {
|
||||
width: 352px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 1025px) {
|
||||
.course-details-outline {
|
||||
width: 100%;
|
||||
}
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.lesson-content-card {
|
||||
@@ -1017,14 +1028,15 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.course-content-parent {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap-reverse;
|
||||
display: grid;
|
||||
grid-gap: 2rem;
|
||||
grid-template-columns: 2fr minmax(600px, 5fr);
|
||||
}
|
||||
|
||||
@media (max-width: 375px) {
|
||||
@media (max-width: 1024px) {
|
||||
.course-content-parent {
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1039,13 +1051,12 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.lesson-pagination-parent {
|
||||
width: 736px;
|
||||
margin-top: 16px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 1025px) {
|
||||
@media (max-width: 768px) {
|
||||
.lesson-pagination-parent {
|
||||
width: 100%;
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1053,28 +1064,6 @@ div.custom-checkbox>label>input:checked+img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.course-details-page {
|
||||
padding: 0px 0px 80px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1120px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (max-width: 1025px) {
|
||||
.course-details-page {
|
||||
padding: 24px 0px 24px;
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.course-details-page {
|
||||
padding: 24px 0px 24px;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
.active-lesson {
|
||||
background-color: #EBF5FF;
|
||||
border-radius: 4px;
|
||||
@@ -1091,16 +1080,14 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.profile-page {
|
||||
max-width: 1150px;
|
||||
margin: 0 auto;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.profile-banner {
|
||||
width: 100%;
|
||||
height: 248px;
|
||||
border-radius: 12px 12px 0px 0px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
@@ -1109,6 +1096,11 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
}
|
||||
|
||||
.profile-about-section {
|
||||
flex: 1;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.profile-info {
|
||||
height: 68px;
|
||||
background: #ffffff;
|
||||
@@ -1143,7 +1135,7 @@ div.custom-checkbox>label>input:checked+img {
|
||||
line-height: 156%;
|
||||
letter-spacing: -0.0175em;
|
||||
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.64);
|
||||
padding: 20px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 375px) {
|
||||
@@ -1167,7 +1159,7 @@ div.custom-checkbox>label>input:checked+img {
|
||||
height: fit-content;
|
||||
box-shadow: 0px 1px 1px rgb(0 0 0 / 16%);
|
||||
border-radius: 4px;
|
||||
margin-top: 25px;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 375px) {
|
||||
@@ -1183,6 +1175,7 @@ div.custom-checkbox>label>input:checked+img {
|
||||
left: 174px;
|
||||
font-size: 12px;
|
||||
line-height: 165%;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
@@ -1200,7 +1193,6 @@ div.custom-checkbox>label>input:checked+img {
|
||||
}
|
||||
|
||||
.social-icons {
|
||||
float: right;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
@@ -1220,18 +1212,6 @@ div.custom-checkbox>label>input:checked+img {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.profile-courses {
|
||||
padding: 0px 24px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.profile-courses {
|
||||
padding: 0px 10px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 12px;
|
||||
line-height: 165%;
|
||||
|
||||
@@ -1,307 +0,0 @@
|
||||
h2 {
|
||||
margin: 20px 0px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.teaser {
|
||||
background: white;
|
||||
border-radius: 9px;
|
||||
border: 1px solid #C4C4C4;
|
||||
|
||||
.teaser-body {
|
||||
padding: 20px;
|
||||
box-shadow: 0px 5px 10px rgb(0 0 0 / 10%)
|
||||
}
|
||||
.teaser-footer {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.sketch-teaser {
|
||||
.teaser();
|
||||
width: 220px;
|
||||
margin-bottom: 30px;
|
||||
margin-top: 30px;
|
||||
|
||||
svg {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
.sketch-image {
|
||||
padding: 10px;
|
||||
}
|
||||
.sketch-footer {
|
||||
border-top: 1px solid#C4C4C4;
|
||||
padding: 10px;
|
||||
background: #F6F6F6;
|
||||
border-radius: 0px 0px 10px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.course-teaser {
|
||||
.teaser();
|
||||
color: #444;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 20px;
|
||||
|
||||
h3, h4 {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.course-body, .course-footer {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.course-body {
|
||||
min-height: 8em;
|
||||
}
|
||||
|
||||
.course-footer {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
a, a:hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.anchor_style {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.anchor_style:hover {
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 60px 0px;
|
||||
}
|
||||
|
||||
section h2 {
|
||||
margin-bottom: 40px;
|
||||
font-size: 48px;
|
||||
line-height: 58px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
section.lightgray {
|
||||
background: #F6F6F6;
|
||||
}
|
||||
|
||||
#hero .jumbotron {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.chapter-teaser {
|
||||
.teaser();
|
||||
color: #444;
|
||||
margin: 20px 0px;
|
||||
|
||||
h3, h4 {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.field-width {
|
||||
width: 40%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.footer-grouped-links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.footer-info {
|
||||
border-top: 0px;
|
||||
margin-top: 0px;
|
||||
|
||||
.footer-col-right {
|
||||
padding-top: 1.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.web-footer {
|
||||
border-top: 1px solid #E2E6E9;
|
||||
padding: 0px;
|
||||
padding: 2rem 0px;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.course-type {
|
||||
text-transform: uppercase;
|
||||
font-size: 1.0em;
|
||||
font-weight: bold;
|
||||
color: var(--tag-color);
|
||||
}
|
||||
|
||||
.instructor-title {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.instructor-subtitle {
|
||||
font-size: 0.8em;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
// .mentors-wrapper {
|
||||
// .gray-section();
|
||||
// }
|
||||
|
||||
|
||||
.chapter-number {
|
||||
background: var(--text-color);
|
||||
color: white;
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background: var(--sidebar-bg);
|
||||
border: 1px solid var(--sidebar-border);
|
||||
margin: 20px 0px;
|
||||
border-radius: 10px;
|
||||
padding: 1px 20px 20px 20px;
|
||||
}
|
||||
|
||||
.sidebar h3 {
|
||||
margin-top: 20px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.sidebar-batch {
|
||||
background: var(--sidebar-bg);
|
||||
color: var(--text-color);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar-batch a {
|
||||
padding: 16px 8px 8px 16px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.sidebar .notice {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px dashed var(--text-color);
|
||||
}
|
||||
|
||||
.sidebar .notice a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
// LiveCode editor
|
||||
|
||||
.livecode-editor {
|
||||
|
||||
.CodeMirror {
|
||||
border: 1px solid #ddd;
|
||||
background: #ffe;
|
||||
height: auto;
|
||||
}
|
||||
.CodeMirror-scroll {
|
||||
max-height: 310px;
|
||||
min-height: 310px;
|
||||
}
|
||||
.controls {
|
||||
padding: 10px 0px;
|
||||
}
|
||||
canvas {
|
||||
border: 5px solid #ddd;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.output {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 300px;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
max-height: 300px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
margin: 0px;
|
||||
margin-left: 20px;
|
||||
padding: 4px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.canvas-wrapper {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.code-wrapper {
|
||||
min-height: 50px;
|
||||
}
|
||||
.CodeMirror {
|
||||
min-height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sketch-header {
|
||||
input#sketch-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.lesson-teaser {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
#hero h1 {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.lesson-page {
|
||||
margin: 20px 0px;
|
||||
}
|
||||
|
||||
.exercise-image svg {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border: 1px solid #ddd;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.svg-200 svg {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.livecode-editor-small .livecode-editor {
|
||||
.CodeMirror-scroll {
|
||||
max-height: 160px;
|
||||
min-height: 160px;
|
||||
}
|
||||
|
||||
canvas {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.mentor-dashboard {
|
||||
margin-top: 20px;
|
||||
|
||||
.submission {
|
||||
margin: 40px 0px 0px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-preview-message {
|
||||
width: fit-content;
|
||||
margin: 50px 0px 50px;
|
||||
color: black;
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
<div>
|
||||
{% set site_link = "<a href='" + site_url + "'>" + site_url + "</a>" %}
|
||||
<p>{{_("Dear Community Member,")}}</p>
|
||||
<p>{{_("Your Invite Request to be a part of {0} has
|
||||
been approved.").format(site_link)}}</p>
|
||||
<p>Click on the link below to complete your Sign up and set a new password</p>
|
||||
{% set site_link = "<a href='" + site_url + "'>" + site_name + "</a>" %}
|
||||
<p>{{_("Hi,")}}</p>
|
||||
<p>{{_("Welcome to {0}!").format(site_name)}}</p>
|
||||
<p>Click on the link below to complete your sign up and set a new password</p>
|
||||
<p style="margin: 15px 0px;">
|
||||
<a href="{{ signup_form_link }}" rel="nofollow" class="btn btn-primary">{{ _("Complete Sign Up") }}</a>
|
||||
</p>
|
||||
@@ -14,5 +13,5 @@
|
||||
</p>
|
||||
<br>
|
||||
<p>Thanks and Regards,</p>
|
||||
<p>Your Community.</p>
|
||||
<p>{{site_name}}</p>
|
||||
</div>
|
||||
@@ -1,40 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %}About{% endblock %}
|
||||
{% block head_include %}
|
||||
<meta name="description" content="Courses" />
|
||||
<meta name="keywords" content="" />
|
||||
|
||||
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
{{ widgets.BatchTabs(course=course, batch=batch) }}
|
||||
<div class="tab-content" id="about">
|
||||
{{ CourseBasicDetail(course)}}
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="col-lg-4 col-md-12">
|
||||
<div class="sidebar">
|
||||
{{ widgets.InstructorSection(instructor=course.get_instructor()) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro CourseBasicDetail(course) %}
|
||||
<h2>{{course.title}}</h2>
|
||||
<div class="course-description">
|
||||
{{course.short_introduction}}
|
||||
</div>
|
||||
{% if course.video_link %}
|
||||
<div class="preview-video">
|
||||
<iframe width="560" height="315" src="{{course.video_link}}" title="YouTube video player" frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen></iframe>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h2>About the Course</h2>
|
||||
<div>{{frappe.utils.md_to_html(course.description)}}</div>
|
||||
{% endmacro %}
|
||||
@@ -1,5 +0,0 @@
|
||||
import frappe
|
||||
from . import utils
|
||||
|
||||
def get_context(context):
|
||||
utils.get_common_context(context)
|
||||
@@ -11,7 +11,6 @@
|
||||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
{{ widgets.BatchTabs(course=course, membership=membership) }}
|
||||
<div class="messages-container mt-5">
|
||||
{{ widgets.BatchHeader(batch_name=batch.title, member_count=member_count)}}
|
||||
<ol class="messages">
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %} Batch {% endblock %}
|
||||
|
||||
{% block head_include %}
|
||||
<meta name="description" content="Courses" />
|
||||
<meta name="keywords" content="" />
|
||||
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-5">
|
||||
{{ widgets.BatchTabs(course=course, membership=membership) }}
|
||||
<div class="course-details mt-5">
|
||||
{{ widgets.CourseOutline(course=course, batch=batch, show_link=membership, show_progress=True) }}
|
||||
</div>
|
||||
|
||||
{% if batch %}
|
||||
<div class="w-25">
|
||||
<h3>Batch Schedule</h3>
|
||||
{{ widgets.RenderBatch(course=course, batch=batch) }}
|
||||
</div>
|
||||
|
||||
{% if batch.description %}
|
||||
<div class="mt-5">
|
||||
<h3>Batch Details</h3>
|
||||
{{ frappe.utils.md_to_html(batch.description) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if course.is_mentor(frappe.session.user) %}
|
||||
{% set invite_link = frappe.utils.get_url() + "/courses/" + course.name + "/join?batch=" + batch.name %}
|
||||
<div class="">
|
||||
<h3> Invite Members </h3>
|
||||
<a href="" class="" id="invite-link" data-link="{{ invite_link }}">Get Batch Invitation
|
||||
Link</a>
|
||||
<small id="copy-message" class="text-muted" style="display: none;">Copied to Clipboard.</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
$("#invite-link").click((e) => {
|
||||
e.preventDefault();
|
||||
var link_element = $("#invite-link");
|
||||
var input_element = document.createElement("input");
|
||||
input_element.value = link_element.attr("data-link")
|
||||
document.body.appendChild(input_element);
|
||||
input_element.select();
|
||||
document.execCommand("copy");
|
||||
input_element.remove();
|
||||
$("#copy-message").slideDown(function () {
|
||||
setTimeout(function () {
|
||||
$("#copy-message").slideUp();
|
||||
}, 2000);
|
||||
});
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,5 +0,0 @@
|
||||
import frappe
|
||||
from . import utils
|
||||
|
||||
def get_context(context):
|
||||
utils.get_common_context(context)
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="common-page-style">
|
||||
<div class="course-details-page">
|
||||
<div class="container course-details-page">
|
||||
{{ widgets.BreadCrumb(course=course, lesson=lesson) }}
|
||||
<div class="course-content-parent">
|
||||
<div class="course-details-outline">
|
||||
@@ -30,7 +30,7 @@
|
||||
<div class="lesson-pagination-parent">
|
||||
{{ LessonContent(lesson) }}
|
||||
{% if membership %}
|
||||
{{ pagination(prev_chap, prev_url, next_chap, next_url) }}
|
||||
{{ pagination(prev_url, next_url) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,18 +47,17 @@
|
||||
</div>
|
||||
|
||||
{% if membership or lesson.include_in_preview %}
|
||||
<div class="common-card-style lesson-content-card">{{ lesson.render_html() }}</div>
|
||||
<div class="common-card-style lesson-content-card from-markdown">{{ lesson.render_html() }}</div>
|
||||
{% else %}
|
||||
<div class="no-preview-message">
|
||||
<span>This lesson is not available for Preview. Please join the course to access this lesson.</span>
|
||||
<a href="/courses/{{ course.name }}">Checkout Course Details.</a>
|
||||
<div class="common-card-style lesson-content-card">
|
||||
<span>This lesson is not available for Preview. Please join the course to access this lesson. <a href="/courses/{{ course.name }}">Checkout Course Details.</a></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro pagination(prev_chap, prev_url, next_chap, next_url) %}
|
||||
{% macro pagination(prev_url, next_url) %}
|
||||
<div class="lesson-pagination">
|
||||
|
||||
<div>
|
||||
|
||||
@@ -12,7 +12,6 @@ def get_context(context):
|
||||
lesson_index = frappe.form_dict.get("lesson")
|
||||
lesson_number = f"{chapter_index}.{lesson_index}"
|
||||
|
||||
course_name = context.course.name
|
||||
if not chapter_index or not lesson_index:
|
||||
if context.batch:
|
||||
index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1"
|
||||
@@ -21,20 +20,12 @@ def get_context(context):
|
||||
frappe.local.flags.redirect_location = context.course.get_learn_url(index_) + context.course.query_parameter
|
||||
raise frappe.Redirect
|
||||
|
||||
context.lesson = context.course.get_lesson(chapter_index, lesson_index)
|
||||
context.lesson_index = lesson_index
|
||||
context.chapter_index = chapter_index
|
||||
|
||||
outline = context.course.get_outline()
|
||||
prev_ = outline.get_prev(lesson_number)
|
||||
next_ = outline.get_next(lesson_number)
|
||||
context.prev_chap = get_chapter_title(course_name, prev_)
|
||||
context.next_chap = get_chapter_title(course_name, next_)
|
||||
context.next_url = context.course.get_learn_url(next_) and context.course.get_learn_url(next_) + context.course.query_parameter
|
||||
context.prev_url = context.course.get_learn_url(prev_) and context.course.get_learn_url(prev_) + context.course.query_parameter
|
||||
context.lesson = list(filter(lambda x: cstr(x.number) == lesson_number, context.lessons))[0]
|
||||
neighbours = context.course.get_neighbours(lesson_number, context.lessons)
|
||||
context.next_url = get_learn_url(neighbours["next"], context.course)
|
||||
context.prev_url = get_learn_url(neighbours["prev"], context.course)
|
||||
|
||||
context.page_extensions = get_page_extensions()
|
||||
|
||||
context.page_context = {
|
||||
"course": context.course.name,
|
||||
"batch": context.get("batch") and context.batch.name,
|
||||
@@ -42,6 +33,9 @@ def get_context(context):
|
||||
"is_member": context.membership is not None
|
||||
}
|
||||
|
||||
def get_learn_url(lesson_number, course):
|
||||
return course.get_learn_url(lesson_number) and course.get_learn_url(lesson_number) + course.query_parameter
|
||||
|
||||
def get_chapter_title(course_name, lesson_number):
|
||||
if not lesson_number:
|
||||
return
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
|
||||
{% block title %}Members{% endblock %}
|
||||
{% block head_include %}
|
||||
<meta name="description" content="Courses" />
|
||||
<meta name="keywords" content="" />
|
||||
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
{{ widgets.BatchTabs(course=course, membership=membership) }}
|
||||
{{ MembersList(members)}}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% macro MembersList(members) %}
|
||||
<div class="mt-5">
|
||||
{% for member in members %}
|
||||
<div class="d-flex align-items-center">
|
||||
{{ widgets.Avatar(member=member, avatar_class="avatar-medium") }}
|
||||
<div class="d-flex flex-column ml-2">
|
||||
<div class="d-flex">
|
||||
<a class="anchor_style ml-2" href="/{{member.username}}">
|
||||
<div class="review-content">{{ member.full_name }}</div>
|
||||
</a>
|
||||
{% if course.is_mentor(member.name) %}
|
||||
<div class="badge badge-success ml-2 align-self-start">Mentor</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if loop.index != member_count %}
|
||||
<hr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -1,7 +0,0 @@
|
||||
import frappe
|
||||
from . import utils
|
||||
|
||||
def get_context(context):
|
||||
utils.get_common_context(context)
|
||||
if not context.membership:
|
||||
utils.redirect_to_lesson(context.course)
|
||||
@@ -1,51 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% from "www/macros/livecode.html" import LiveCodeEditorJS, LiveCodeEditor with context %}
|
||||
{% block title %}{{ course.title }} - Batch Dashboard{% endblock %}
|
||||
|
||||
{% block head_include %}
|
||||
<meta name="description" content="{{course.title}} - Batch Dashboard" />
|
||||
<meta name="keywords" content="{{course.title}} - Batch Dashboard" />
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="{{ livecode_url }}/static/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="/assets/css/lms.css">
|
||||
|
||||
<script src="{{ livecode_url }}/static/codemirror/lib/codemirror.js"></script>
|
||||
<script src="{{ livecode_url }}/static/codemirror/mode/python/python.js"></script>
|
||||
<script src="{{ livecode_url }}/static/codemirror/keymap/sublime.js"></script>
|
||||
|
||||
<script src="{{ livecode_url }}/static/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="{{ livecode_url }}/static/codemirror/addon/comment/comment.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
{{ widgets.BatchTabs(course=course, membership=membership) }}
|
||||
<div class="mentor-dashboard">
|
||||
<h3>Batch Progress</h3>
|
||||
{% for exercise in report.exercises %}
|
||||
<div class="exercise-submissions">
|
||||
<h2>Exercise {{exercise.index_label}}: {{exercise.title}}</h2>
|
||||
{% for s in report.get_submissions_of_exercise(exercise.name) %}
|
||||
<div class="submission">
|
||||
<h4><a href="/{{s.owner.username}}">{{s.owner.full_name}}</a></h4>
|
||||
<div class="livecode-editor-small">
|
||||
{{ LiveCodeEditor(name=s.name, code=s.solution, reset_code=s.solution) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{%- block script %}
|
||||
{{ super() }}
|
||||
{{ LiveCodeEditorJS() }}
|
||||
{% endblock %}
|
||||
@@ -1,62 +0,0 @@
|
||||
import frappe
|
||||
from community.lms.models import Course
|
||||
from collections import defaultdict
|
||||
from . import utils
|
||||
|
||||
def get_context(context):
|
||||
utils.get_common_context(context)
|
||||
|
||||
exercise_name = frappe.form_dict.get("exercise")
|
||||
if exercise_name:
|
||||
exercise = frappe.get_doc("Exercise", exercise_name)
|
||||
else:
|
||||
exercise = None
|
||||
|
||||
context.exercise = exercise
|
||||
context.report = BatchReport(context.course, context.batch)
|
||||
|
||||
class BatchReport:
|
||||
def __init__(self, course, batch):
|
||||
self.submissions = get_submissions(course, batch)
|
||||
self.exercises = self.get_exercises(course.name)
|
||||
self.submissions_by_exercise = defaultdict(list)
|
||||
for s in self.submissions:
|
||||
self.submissions_by_exercise[s.exercise].append(s)
|
||||
|
||||
def get_exercises(self, course_name):
|
||||
return frappe.get_all("Exercise", {"course": course_name, "lesson": ["!=", ""]}, ["name", "title", "index_label"], order_by="index_label")
|
||||
|
||||
def get_submissions_of_exercise(self, exercise_name):
|
||||
return self.submissions_by_exercise[exercise_name]
|
||||
|
||||
def get_submissions(course, batch):
|
||||
students = course.get_students(batch.name)
|
||||
if not len(students):
|
||||
return []
|
||||
students_map = {s.email: s for s in students}
|
||||
names, values = nparams("s", students_map.keys())
|
||||
sql = """
|
||||
select owner, exercise, name, solution, creation, image
|
||||
from (
|
||||
select owner, exercise, name, solution, creation, image,
|
||||
row_number() over (partition by owner, exercise order by creation desc) as ix
|
||||
from `tabExercise Submission`) as t
|
||||
where t.ix=1 and owner IN {}
|
||||
""".format(names)
|
||||
|
||||
data = frappe.db.sql(sql, values=values, as_dict=True)
|
||||
for row in data:
|
||||
row['owner'] = students_map[row['owner']]
|
||||
return data
|
||||
|
||||
def nparams(name, values):
|
||||
"""Creates n paramters from a list of values for a db query.
|
||||
|
||||
>>> nparams("name", ["a", "b])
|
||||
("(%(name_1)s, %(name_2)s)", {"name_1": "a", "name_2": "b"})
|
||||
"""
|
||||
keys = [f"{name}_{i}" for i, _ in enumerate(values, start=1)]
|
||||
param_names = [f"%({k})s" for k in keys]
|
||||
param_values = dict(zip(keys, values))
|
||||
joined_names = "(" + ", ".join(param_names) + ")"
|
||||
return joined_names, param_values
|
||||
@@ -1,17 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %}Schedule{% endblock %}
|
||||
|
||||
{% block head_include %}
|
||||
<meta name="description" content="Courses" />
|
||||
<meta name="keywords" content="" />
|
||||
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
{{ widgets.BatchTabs(course=course, batch=batch) }}
|
||||
<h3>
|
||||
Schedule
|
||||
</h3>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,20 +0,0 @@
|
||||
import frappe
|
||||
from community.lms.models import Course
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
course_name = frappe.form_dict["course"]
|
||||
batch_name = frappe.form_dict["batch"]
|
||||
|
||||
course = Course.find(course_name)
|
||||
if not course:
|
||||
context.template = "www/404.html"
|
||||
return
|
||||
|
||||
batch = course.get_batch(batch_name)
|
||||
if not batch:
|
||||
frappe.local.flags.redirect_location = "/courses/" + course_name
|
||||
raise frappe.Redirect
|
||||
|
||||
context.course = course
|
||||
context.batch = batch
|
||||
@@ -10,12 +10,12 @@ def get_common_context(context):
|
||||
except KeyError:
|
||||
batch_name = None
|
||||
|
||||
course = Course.find(course_name)
|
||||
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:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="common-page-style">
|
||||
<div class="col course-home-page">
|
||||
<div class="container course-home-page">
|
||||
{{ widgets.BreadCrumb(course=course) }}
|
||||
{{ CourseCardWide(course) }}
|
||||
{{ CourseOutlineAndCreator(course) }}
|
||||
@@ -32,12 +32,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-card-wide-content">
|
||||
<div class="course-info">
|
||||
<div class="course-card-wide-title">
|
||||
{{ course.title }}
|
||||
</div>
|
||||
<div class="course-card-wide-intro">
|
||||
{{ course.short_introduction }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-buttons">
|
||||
{% if not course.disable_self_learning and not membership %}
|
||||
<div class="button wide-button start-learning is-primary join-batch" data-course="{{ course.name | urlencode }}">
|
||||
@@ -88,10 +90,10 @@
|
||||
|
||||
{% macro CourseOutlineAndCreator(course) %}
|
||||
<div class="course-outline-instructor-parent">
|
||||
<div class="course-home-outline col">
|
||||
<div class="course-home-outline">
|
||||
{{ widgets.CourseOutline(course=course, membership=membership) }}
|
||||
</div>
|
||||
<div class="course-creator-progress-parent col-sm-auto">
|
||||
<div class="course-creator-progress-parent">
|
||||
<div class="course-creator-section">
|
||||
<div class="course-home-headings">
|
||||
Creator
|
||||
@@ -151,7 +153,7 @@
|
||||
|
||||
{% macro CourseDescriptionAndOverview(course) %}
|
||||
<div class="course-outline-instructor-parent">
|
||||
<div class="course-description-section col">
|
||||
<div class="course-description-section">
|
||||
<div class="course-home-headings">
|
||||
Course Description
|
||||
</div>
|
||||
@@ -161,7 +163,7 @@
|
||||
</div>
|
||||
{% set avg_rating = course.get_average_rating() %}
|
||||
{% if course.get_students() | length or avg_rating %}
|
||||
<div class="course-overview-section col-sm-auto">
|
||||
<div class="course-overview-section">
|
||||
<div class="course-home-headings">
|
||||
Overview
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import frappe
|
||||
from community.lms.models import Course
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
@@ -10,7 +9,7 @@ def get_context(context):
|
||||
frappe.local.flags.redirect_location = "/courses"
|
||||
raise frappe.Redirect
|
||||
|
||||
course = Course.find(course_name)
|
||||
course = frappe.get_doc("LMS Course", course_name)
|
||||
if course is None:
|
||||
frappe.local.flags.redirect_location = "/courses"
|
||||
raise frappe.Redirect
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="common-page-style">
|
||||
<div class="col course-top-section">
|
||||
<div class="container">
|
||||
<div class="courses-header">
|
||||
{{ 'All Courses' }}
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ def get_context(context):
|
||||
context.courses = get_courses()
|
||||
|
||||
def get_courses():
|
||||
course_names = frappe.get_all("LMS Course", pluck="name")
|
||||
course_names = frappe.get_all("LMS Course", filters={"is_published": True}, pluck="name")
|
||||
courses = []
|
||||
for course in course_names:
|
||||
courses.append(frappe.get_doc("LMS Course", course))
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% from "www/hackathons/macros/card.html" import null_card %}
|
||||
|
||||
{% block title %}{{ 'My Courses' }}{% endblock %}
|
||||
{% block head_include %}
|
||||
<meta name="description" content="My Courses" />
|
||||
<meta name="keywords" content="" />
|
||||
<style>
|
||||
div.card-hero-img {
|
||||
height: 220px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: rgb(250, 251, 252);
|
||||
}
|
||||
|
||||
.card-image-wrapper {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 220px;
|
||||
background-color: rgb(250, 251, 252);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-body {
|
||||
align-self: center;
|
||||
color: #d1d8dd;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.no-courses {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 5rem 0 5rem 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% macro card(course) %}
|
||||
<div class="col-sm-4 mb-4 text-left">
|
||||
<a href="//courses/course?course={{course.name}}" class="no-decoration no-underline">
|
||||
<div class="card h-100">
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>{{ course.title }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class='container'>
|
||||
{% if frappe.session.user != "Guest" %}
|
||||
{% for course in my_courses %}
|
||||
{{ card(course) }}
|
||||
{% endfor %}
|
||||
{% if my_courses %}
|
||||
{% for n in range( (3 - (my_courses|length)) %3) %}
|
||||
{{ null_card() }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="no-courses">You haven't enrolled in any Course yet. <a href="/courses">Check out the availabe
|
||||
courses.</a></div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="no-courses">
|
||||
<p>Please sign up to access this page.</p>
|
||||
<a id="signup" class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -1,15 +0,0 @@
|
||||
import frappe
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
context.my_courses = get_my_courses()
|
||||
|
||||
def get_my_courses():
|
||||
my_courses = []
|
||||
courses = frappe.get_all("LMS Course Enrollment", {"owner": frappe.session.user}, ["course"])
|
||||
for course in courses:
|
||||
my_courses.append({
|
||||
"name": course.course,
|
||||
"title": frappe.db.get_value("LMS Course", course.course, ["title"])
|
||||
})
|
||||
return my_courses
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="common-page-style">
|
||||
<div class="col profile-page">
|
||||
<div class="container profile-page">
|
||||
{{ ProfileBanner(member) }}
|
||||
{{ AboutOverviewSection(member) }}
|
||||
{{ CoursesCreated(member) }}
|
||||
@@ -18,8 +18,8 @@
|
||||
{% endblock %}
|
||||
|
||||
{% macro ProfileBanner(member) %}
|
||||
<div class="col">
|
||||
<div class="profile-banner" style="background: url(/assets/community/images/profile-banner.png)">
|
||||
<div class="">
|
||||
<div class="profile-banner" style="background-image: url(/assets/community/images/profile-banner.png)">
|
||||
<div class="profile-avatar">
|
||||
{{ widgets.Avatar(member=member, avatar_class="avatar-xl") }}
|
||||
<div class="profile-name"> {{ member.full_name }} </div>
|
||||
@@ -29,10 +29,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-info">
|
||||
<div class="profile-profession">
|
||||
{% if member.profession %}
|
||||
<span class="profile-profession"> {{ member.profession }} </span>
|
||||
<span class=""> {{ member.profession }} </span>
|
||||
{% endif %}
|
||||
<div class="social-icons">
|
||||
<span class="social-icons">
|
||||
{% if member.linkedin %}
|
||||
<a class="linkedin" href="{{ member.linkedin }}">
|
||||
<img src="/assets/community/images/linkedin.png">
|
||||
@@ -48,15 +49,16 @@
|
||||
<img src="/assets/community/icons/github.svg">
|
||||
</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro AboutOverviewSection(member) %}
|
||||
<div class="course-outline-instructor-parent">
|
||||
<div class="profile-parent-section">
|
||||
{% if member.bio %}
|
||||
<div class="course-overview-section col">
|
||||
<div class="profile-about-section">
|
||||
<div class="course-home-headings">
|
||||
About
|
||||
</div>
|
||||
@@ -65,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="course-overview-section col-sm-auto">
|
||||
<div class="course-overview-section">
|
||||
<div class="course-home-headings">
|
||||
Overview
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user