fix: ui, preview, progress, batches
This commit is contained in:
@@ -15,13 +15,6 @@ def autosave_section(section, code):
|
||||
doc.insert()
|
||||
return {"name": doc.name}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_section(name):
|
||||
"""Saves the code edited in one of the sections.
|
||||
"""
|
||||
doc = frappe.get_doc("LMS Section", name)
|
||||
return doc and doc.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def submit_solution(exercise, code):
|
||||
"""Submits a solution.
|
||||
|
||||
@@ -12,5 +12,4 @@ class Chapter(Document):
|
||||
filters={"chapter": self.name},
|
||||
fields='name',
|
||||
order_by="index_")
|
||||
print("rows", rows)
|
||||
return [frappe.get_doc('Lesson', row['name']) for row in rows]
|
||||
|
||||
@@ -58,7 +58,8 @@ def create_invite_request(invite_email):
|
||||
|
||||
frappe.get_doc({
|
||||
"doctype": "Invite Request",
|
||||
"invite_email": invite_email
|
||||
"invite_email": invite_email,
|
||||
"status": "Approved"
|
||||
}).save(ignore_permissions=True)
|
||||
return "OK"
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
"field_order": [
|
||||
"chapter",
|
||||
"lesson_type",
|
||||
"include_in_preview",
|
||||
"column_break_4",
|
||||
"title",
|
||||
"index_",
|
||||
"index_label",
|
||||
"body",
|
||||
"sections",
|
||||
"include_in_preview"
|
||||
"section_break_6",
|
||||
"body"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -48,12 +49,6 @@
|
||||
"fieldtype": "Markdown Editor",
|
||||
"label": "Body"
|
||||
},
|
||||
{
|
||||
"fieldname": "sections",
|
||||
"fieldtype": "Table",
|
||||
"label": "Sections",
|
||||
"options": "LMS Section"
|
||||
},
|
||||
{
|
||||
"fieldname": "index_label",
|
||||
"fieldtype": "Data",
|
||||
@@ -66,11 +61,19 @@
|
||||
"fieldname": "include_in_preview",
|
||||
"fieldtype": "Check",
|
||||
"label": "Include In Preview"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-10 10:06:39.176891",
|
||||
"modified": "2021-06-11 19:03:23.138165",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Lesson",
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from ...section_parser import SectionParser
|
||||
from ...md import markdown_to_html, find_macros
|
||||
|
||||
class Lesson(Document):
|
||||
@@ -39,9 +38,6 @@ class Lesson(Document):
|
||||
def render_html(self):
|
||||
return markdown_to_html(self.body)
|
||||
|
||||
def get_sections(self):
|
||||
return sorted(self.get('sections'), key=lambda s: s.index)
|
||||
|
||||
def get_exercises(self):
|
||||
if not self.body:
|
||||
return []
|
||||
@@ -50,30 +46,6 @@ class Lesson(Document):
|
||||
exercises = [value for name, value in macros if name == "Exercise"]
|
||||
return [frappe.get_doc("Exercise", name) for name in exercises]
|
||||
|
||||
def make_lms_section(self, index, section):
|
||||
s = frappe.new_doc('LMS Section', parent_doc=self, parentfield='sections')
|
||||
s.type = section.type
|
||||
s.id = section.id
|
||||
s.label = section.label
|
||||
s.contents = section.contents
|
||||
s.index = index
|
||||
return s
|
||||
|
||||
def get_next(self):
|
||||
"""Returns the number for the next lesson.
|
||||
|
||||
The return value would be like 1.2, 2.1 etc.
|
||||
It will be None if there is no next lesson.
|
||||
"""
|
||||
|
||||
|
||||
def get_prev(self):
|
||||
"""Returns the number for the prev lesson.
|
||||
|
||||
The return value would be like 1.2, 2.1 etc.
|
||||
It will be None if there is no next lesson.
|
||||
"""
|
||||
|
||||
def get_progress(self):
|
||||
return frappe.db.get_value("LMS Course Progress", {"lesson": self.name, "owner": frappe.session.user}, "status")
|
||||
|
||||
@@ -98,11 +70,7 @@ def save_progress(lesson, batch):
|
||||
return
|
||||
|
||||
lesson_details = frappe.get_doc("Lesson", lesson)
|
||||
dynamic_content = frappe.db.count("LMS Section",
|
||||
filters={
|
||||
"type": ["not in", ["example", "text"]],
|
||||
"parent": lesson_details.name
|
||||
})
|
||||
dynamic_content = find_macros(lesson_details.body)
|
||||
|
||||
status = "Complete"
|
||||
if dynamic_content:
|
||||
@@ -121,12 +89,11 @@ def update_progress(lesson):
|
||||
if frappe.db.exists("LMS Course Progress", {"lesson": lesson, "owner": user}):
|
||||
course_progress = frappe.get_doc("LMS Course Progress", {"lesson": lesson, "owner": user})
|
||||
course_progress.status = "Complete"
|
||||
course_progress.save()
|
||||
course_progress.save(ignore_permissions=True)
|
||||
|
||||
def all_dynamic_content_submitted(lesson, user):
|
||||
exercise_names = frappe.get_list("Exercise", {"lesson": lesson}, ["name"], pluck="name")
|
||||
exercise_names = frappe.get_list("Exercise", {"lesson": lesson}, pluck="name")
|
||||
all_exercises_submitted = False
|
||||
print(exercise_names)
|
||||
query = {
|
||||
"exercise": ["in", exercise_names],
|
||||
"owner": user
|
||||
|
||||
@@ -80,11 +80,6 @@ class LMSBatch(Document):
|
||||
membership = self.get_membership(user)
|
||||
return membership and membership.current_lesson
|
||||
|
||||
def get_learn_url(self, lesson_number):
|
||||
if not lesson_number:
|
||||
return
|
||||
return f"/courses/{self.course}/learn/{lesson_number}"
|
||||
|
||||
@frappe.whitelist()
|
||||
def save_message(message, batch):
|
||||
doc = frappe.get_doc({
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"course",
|
||||
"member_type",
|
||||
"role",
|
||||
"current_lesson"
|
||||
"current_lesson",
|
||||
"is_current"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -80,11 +81,19 @@
|
||||
"fieldtype": "Data",
|
||||
"label": "Memeber Username",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_current",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Is Currently Being Used",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-05-24 12:40:57.125694",
|
||||
"modified": "2021-06-14 10:24:35.425498",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Batch Membership",
|
||||
|
||||
@@ -43,9 +43,6 @@ class LMSBatchMembership(Document):
|
||||
member_name = frappe.db.get_value("User", self.member, "full_name")
|
||||
frappe.throw(_("{0} is already a {1} of {2} course through {3} batch").format(member_name, membership.member_type, course, membership.batch))
|
||||
|
||||
def get_user_batch(course, user=frappe.session.user):
|
||||
return frappe.db.get_value("LMS Batch Membership", {"member": user, "course": course}, "batch")
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_membership(batch, member=None, member_type="Student", role="Member"):
|
||||
frappe.get_doc({
|
||||
@@ -56,3 +53,13 @@ def create_membership(batch, member=None, member_type="Student", role="Member"):
|
||||
"member": member or frappe.session.user
|
||||
}).save(ignore_permissions=True)
|
||||
return "OK"
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_current_membership(batch, course, member=frappe.session.user):
|
||||
all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": course})
|
||||
for membership in all_memberships:
|
||||
frappe.db.set_value("LMS Batch Membership", membership.name, "is_current", 0)
|
||||
|
||||
current_membership = frappe.get_all("LMS Batch Membership", {"batch": batch, "member": member})
|
||||
if len(current_membership):
|
||||
frappe.db.set_value("LMS Batch Membership", current_membership[0].name, "is_current", 1)
|
||||
|
||||
@@ -187,6 +187,22 @@ 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_current_batch(self, member=frappe.session.user):
|
||||
current_membership = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name, "is_current": 1}, pluck="batch")
|
||||
if len(current_membership):
|
||||
return current_membership[0]
|
||||
return frappe.db.get_value("LMS Batch Membership", {"member": member, "course": self.name}, "batch")
|
||||
|
||||
def get_all_memberships(self, member=frappe.session.user):
|
||||
all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name}, ["batch", "is_current"])
|
||||
for membership in all_memberships:
|
||||
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
|
||||
return all_memberships
|
||||
|
||||
def get_outline(self):
|
||||
return CourseOutline(self)
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2021-03-05 15:10:53.906006",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"label",
|
||||
"type",
|
||||
"contents",
|
||||
"code",
|
||||
"attrs",
|
||||
"index",
|
||||
"id"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Label"
|
||||
},
|
||||
{
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Type"
|
||||
},
|
||||
{
|
||||
"fieldname": "contents",
|
||||
"fieldtype": "Markdown Editor",
|
||||
"label": "Contents"
|
||||
},
|
||||
{
|
||||
"fieldname": "code",
|
||||
"fieldtype": "Code",
|
||||
"label": "Code"
|
||||
},
|
||||
{
|
||||
"fieldname": "attrs",
|
||||
"fieldtype": "Long Text",
|
||||
"label": "attrs"
|
||||
},
|
||||
{
|
||||
"fieldname": "index",
|
||||
"fieldtype": "Int",
|
||||
"label": "Index"
|
||||
},
|
||||
{
|
||||
"fieldname": "id",
|
||||
"fieldtype": "Data",
|
||||
"label": "id"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-05-19 18:55:26.019625",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Section",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2021, FOSS United and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class LMSSection(Document):
|
||||
def __repr__(self):
|
||||
return f"<LMSSection {self.label!r}>"
|
||||
|
||||
def get_exercise(self):
|
||||
if self.type == "exercise":
|
||||
return frappe.get_doc("Exercise", self.id)
|
||||
|
||||
def get_latest_code_for_user(self):
|
||||
"""Returns the latest code for the logged in user.
|
||||
"""
|
||||
if not frappe.session.user or frappe.session.user == "Guest":
|
||||
return self.contents
|
||||
result = frappe.get_all('Code Revision',
|
||||
fields=["code"],
|
||||
filters={
|
||||
"author": frappe.session.user,
|
||||
"section": self.name
|
||||
},
|
||||
order_by="creation desc",
|
||||
page_length=1)
|
||||
if result:
|
||||
return result[0]['code']
|
||||
else:
|
||||
return self.contents
|
||||
@@ -1,84 +0,0 @@
|
||||
"""Utility to split the text in the topic into multiple sections.
|
||||
|
||||
{{ section(type="example", id="foo") }}
|
||||
circle(100, 100, 50)
|
||||
{{ end }}
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import re
|
||||
from typing import List, Tuple, Dict, Iterator
|
||||
|
||||
RE_SECTION = re.compile(r"^\{\{\s(\w+)\s*(?:\((.*)\))?\s*\}\}\s*")
|
||||
class SectionParser:
|
||||
def parse(self, text: str) -> Iterator[Section]:
|
||||
"""Parses given text into sections and return an iterator over sections.
|
||||
"""
|
||||
lines = text.splitlines()
|
||||
marked_lines = self.parse_lines(lines)
|
||||
return self.group_sections(marked_lines)
|
||||
|
||||
def parse_lines(self, lines: List[str]) -> List[Tuple[str, str, str]]:
|
||||
for line in lines:
|
||||
m = RE_SECTION.match(line)
|
||||
if m:
|
||||
yield m.group(1), self.parse_attrs(m.group(2)), None
|
||||
else:
|
||||
yield None, None, line
|
||||
|
||||
def parse_attrs(self, attrs_str: str) -> Dict[str, str]:
|
||||
# XXX-Anand: Hack
|
||||
code = "dict({})".format(attrs_str or "")
|
||||
return eval(code)
|
||||
|
||||
def group_sections(self, marked_lines) -> Iterator[Section]:
|
||||
index = 0
|
||||
|
||||
def make_section(type='text', id=None, label=None, **attrs):
|
||||
nonlocal index
|
||||
index += 1
|
||||
|
||||
id = id or f"section-{index}"
|
||||
label = label or id
|
||||
return Section(
|
||||
type=type,
|
||||
id=id,
|
||||
label=label,
|
||||
attrs=attrs)
|
||||
|
||||
section = make_section("text")
|
||||
|
||||
for mark, attrs, line in marked_lines:
|
||||
if not mark:
|
||||
section.append(line)
|
||||
continue
|
||||
|
||||
yield section
|
||||
|
||||
if mark == 'end':
|
||||
section = make_section(type='text')
|
||||
else:
|
||||
section = make_section(**attrs)
|
||||
|
||||
yield section
|
||||
|
||||
@dataclass
|
||||
class Section:
|
||||
"""One section of the Topic.
|
||||
"""
|
||||
type: str
|
||||
id: str
|
||||
label: str
|
||||
contents: str = ""
|
||||
attrs: dict = None
|
||||
|
||||
def append(self, line):
|
||||
if not line.endswith("\n"):
|
||||
line = line + "\n"
|
||||
self.contents += line
|
||||
|
||||
def __repr__(self):
|
||||
attrs = dict(type=self.type, id=self.id, label=self.label, **self.attrs)
|
||||
attrs_str = ", ".join(f'{k}="{v}"' for k, v in attrs.items())
|
||||
return f'<Section({attrs_str})>'
|
||||
@@ -11,7 +11,7 @@
|
||||
"apply_document_permissions": 0,
|
||||
"button_label": "Save",
|
||||
"creation": "2021-04-20 11:37:49.135114",
|
||||
"custom_css": ".datepicker.active {\n background-color: white;\n}",
|
||||
"custom_css": ".datepicker.active {\n background-color: white;\n}\n\n[data-doctype=\"Web Form\"] {\n max-width: 720px;\n margin: 6rem auto;\n}",
|
||||
"doc_type": "LMS Batch",
|
||||
"docstatus": 0,
|
||||
"doctype": "Web Form",
|
||||
@@ -19,7 +19,7 @@
|
||||
"is_standard": 1,
|
||||
"login_required": 1,
|
||||
"max_attachment_size": 0,
|
||||
"modified": "2021-06-02 15:52:06.383260",
|
||||
"modified": "2021-06-14 15:28:08.206622",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "add-a-new-batch",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
frappe.ready(function() {
|
||||
// bind events here
|
||||
})
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"accept_payment": 0,
|
||||
"allow_comments": 0,
|
||||
"allow_delete": 0,
|
||||
"allow_edit": 0,
|
||||
"allow_incomplete": 0,
|
||||
"allow_multiple": 0,
|
||||
"allow_print": 0,
|
||||
"amount": 0.0,
|
||||
"amount_based_on_field": 0,
|
||||
"apply_document_permissions": 0,
|
||||
"button_label": "Save",
|
||||
"creation": "2021-04-15 13:32:14.171328",
|
||||
"doc_type": "LMS Batch Membership",
|
||||
"docstatus": 0,
|
||||
"doctype": "Web Form",
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"login_required": 1,
|
||||
"max_attachment_size": 0,
|
||||
"modified": "2021-04-15 13:32:14.171328",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "join-a-batch",
|
||||
"owner": "Administrator",
|
||||
"payment_button_label": "Buy Now",
|
||||
"published": 1,
|
||||
"route": "join-a-batch",
|
||||
"route_to_success_link": 0,
|
||||
"show_attachments": 0,
|
||||
"show_in_grid": 0,
|
||||
"show_sidebar": 0,
|
||||
"sidebar_items": [],
|
||||
"success_url": "/join-a-batch",
|
||||
"title": "Join a Batch",
|
||||
"web_form_fields": [
|
||||
{
|
||||
"allow_read_on_all_link_options": 0,
|
||||
"fieldtype": "Attach",
|
||||
"hidden": 0,
|
||||
"max_length": 0,
|
||||
"max_value": 0,
|
||||
"read_only": 0,
|
||||
"reqd": 0,
|
||||
"show_in_filter": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
|
||||
def get_context(context):
|
||||
# do your magic here
|
||||
pass
|
||||
@@ -2,9 +2,28 @@
|
||||
<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() %}
|
||||
{% if all_memberships | length > 1 %}
|
||||
<a class="nav-link pull-right" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
Switch Batch
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
{% for membership in all_memberships %}
|
||||
{% if not membership.is_current %}
|
||||
<a class="dropdown-item switch-batch" href="#" data-batch="{{ membership.batch | urlencode }}" data-course="{{ course.name | urlencode }}">{{ membership.batch_title }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if not batch %}
|
||||
{% set display_class = "hide" %}
|
||||
{% else %}
|
||||
{% set display_class = "" %}
|
||||
{% endif %}
|
||||
<ul class="nav nav-tabs mt-4">
|
||||
<li class="nav-item">
|
||||
<li class="nav-item {{ display_class }}">
|
||||
<a class="nav-link" id="home" href="/courses/{{course.name}}/home">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
@@ -13,16 +32,16 @@
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link" id="schedule" href="/courses/{{course.name}}/schedule">Schedule</a>
|
||||
</li> -->
|
||||
<li class="nav-item">
|
||||
<li class="nav-item {{ display_class }}">
|
||||
<a class="nav-link" id="members" href="/courses/{{course.name}}/members">Members</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<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 batch.is_member(frappe.session.user, member_type="Mentor") %}
|
||||
{% if batch and batch.is_member(frappe.session.user, member_type="Mentor") %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="progress" href="/courses/{{course.name}}/progress">Progress</a>
|
||||
</li>
|
||||
@@ -37,5 +56,21 @@
|
||||
else {
|
||||
$("#learn").addClass('active')
|
||||
}
|
||||
|
||||
$(".switch-batch").click((e) => {
|
||||
e.preventDefault();
|
||||
var batch = decodeURIComponent($(e.currentTarget).attr("data-batch"));
|
||||
var course = decodeURIComponent($(e.currentTarget).attr("data-course"));
|
||||
frappe.call({
|
||||
method: "community.lms.doctype.lms_batch_membership.lms_batch_membership.update_current_membership",
|
||||
args: {
|
||||
batch: batch,
|
||||
course: course
|
||||
},
|
||||
callback: (data) => {
|
||||
window.location.reload();
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -7,12 +7,46 @@
|
||||
<div class="chapter-lessons">
|
||||
{% for lesson in chapter.get_lessons() %}
|
||||
<div class="lesson-teaser">
|
||||
<a {% if show_link %} href="{{ batch.get_learn_url(course.get_lesson_index(lesson.name)) }}" {% endif %}>{{ lesson.title }}</a>
|
||||
<a {% if show_link or lesson.include_in_preview %}
|
||||
href="{{ course.get_learn_url(course.get_lesson_index(lesson.name)) }}" {% else %} href="" class="no-preview"
|
||||
{% endif %} data-course="{{ course.name }}">{{ lesson.title }}</a>
|
||||
{% if show_progress and not course.is_mentor(frappe.session.user) and lesson.get_progress() %}
|
||||
<a class="ml-5 badge p-1 {{ lesson.get_slugified_class() }}"> <img class="progress-image" src="/assets/community/images/Vector.png"> {{ lesson.get_progress() }}</a>
|
||||
<a class="ml-5 badge p-1 {{ lesson.get_slugified_class() }}"> <img class="progress-image"
|
||||
src="/assets/community/images/Vector.png"> {{ lesson.get_progress() }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
var d;
|
||||
$(".no-preview").click((e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
var message = __("Please enroll for this course to access the lesson.");
|
||||
var label = __("Checkout Upcoming Batches");
|
||||
var action = "checkout_upcoming_batches";
|
||||
d = frappe.msgprint({
|
||||
title: __("This lesson is not available for preview!"),
|
||||
message: message,
|
||||
primary_action: {
|
||||
"label": label,
|
||||
"client_action": action,
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
window.redirect_to_login = () => {
|
||||
window.location.href = `/login?redirect-to=/courses/${$(".no-preview").attr("data-course")}`
|
||||
}
|
||||
|
||||
window.checkout_upcoming_batches = () => {
|
||||
$('html,body').animate({scrollTop: $(".upcoming").offset().top}, 300);
|
||||
frappe.hide_msgprint();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="batch">
|
||||
<div class="batch-details">
|
||||
<div>Session every {{batch.sessions_on}}</div>
|
||||
<div class="sesion-details">Session every {{batch.sessions_on}}</div>
|
||||
<div>{{frappe.utils.format_time(batch.start_time, "short")}} -
|
||||
{{frappe.utils.format_time(batch.end_time, "short")}}
|
||||
</div>
|
||||
@@ -18,7 +18,8 @@
|
||||
<div class="cta">
|
||||
<div class="">
|
||||
{% if can_manage %}
|
||||
<a href="/courses/{{course.name}}/{{batch.name}}/home" class="btn btn-primary">Manage</a>
|
||||
<a href="" class="btn btn-primary manage-batch" data-batch="{{ batch.name | urlencode }}"
|
||||
data-course="{{ course.name | urlencode }}">Manage</a>
|
||||
{% elif can_join %}
|
||||
<button class="join-batch btn btn-primary" data-batch="{{ batch.name | urlencode }}"
|
||||
data-course="{{ course.name | urlencode }}">Join this Batch</button>
|
||||
|
||||
@@ -157,19 +157,6 @@ img.profile-photo {
|
||||
line-height: 51px;
|
||||
}
|
||||
|
||||
.anchor_style {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.anchor_style:hover {
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 5rem 0 5rem 0;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,20 @@ h2 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.anchor_style {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.anchor_style:hover {
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
.no-preview:hover {
|
||||
cursor: pointer;
|
||||
color: #2490ef;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 60px 0px;
|
||||
}
|
||||
@@ -331,3 +345,9 @@ section.lightgray {
|
||||
margin: 40px 0px 0px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-preview-message {
|
||||
width: fit-content;
|
||||
margin: 50px 0px 50px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,16 @@
|
||||
{{ widgets.BatchTabs(course=course, batch=batch) }}
|
||||
<div class="lesson-page">
|
||||
|
||||
<h2 class="title {% if course.is_mentor(frappe.session.user) %} is_mentor {% endif %}" data-name="{{ lesson.name }}" data-batch="{{ batch.name }}">{{ lesson.title }}</h2>
|
||||
<h2 class="title {% if course.is_mentor(frappe.session.user) %} is_mentor {% endif %}" data-name="{{ lesson.name }}" {% if batch %} data-batch="{{ batch.name }}" {% endif %}>{{ lesson.title }}</h2>
|
||||
|
||||
{% if batch or lesson.include_in_preview %}
|
||||
{{ lesson.render_html() }}
|
||||
{% else %}
|
||||
<div class="no-preview-message">
|
||||
<span>This lesson is not available for Preview. Please join a batch to access the complete course.</span>
|
||||
<a href="/courses/{{ course.name }}">Checkout Upcoming Batches</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ pagination(prev_chap, prev_url, next_chap, next_url) }}
|
||||
</div>
|
||||
@@ -53,18 +60,6 @@
|
||||
|
||||
{%- block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
var batch_name = "{{ batch.name }}";
|
||||
var lesson_name = "{{ lesson.name }}";
|
||||
|
||||
frappe.call("community.lms.api.save_current_lesson", {
|
||||
"batch_name": batch_name,
|
||||
"lesson_name": lesson_name
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
{% for ext in page_extensions %}
|
||||
{{ ext.render_footer() }}
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
frappe.ready(() => {
|
||||
if (!$(".title").hasClass("is_mentor")) {
|
||||
if ($(".title").attr("data-batch") && !$(".title").hasClass("is_mentor")) {
|
||||
frappe.call({
|
||||
method: "community.lms.doctype.lesson.lesson.save_progress",
|
||||
args: {
|
||||
@@ -8,4 +8,10 @@ frappe.ready(() => {
|
||||
}
|
||||
})
|
||||
}
|
||||
if ($(".title").attr("data-batch")) {
|
||||
frappe.call("community.lms.api.save_current_lesson", {
|
||||
"batch_name": $(".title").attr("data-batch"),
|
||||
"lesson_name": $(".title").attr("data-name")
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,6 +3,8 @@ import frappe
|
||||
from . import utils
|
||||
from frappe.utils import cstr
|
||||
|
||||
from community.www import batch
|
||||
|
||||
def get_context(context):
|
||||
utils.get_common_context(context)
|
||||
|
||||
@@ -11,11 +13,12 @@ def get_context(context):
|
||||
lesson_number = f"{chapter_index}.{lesson_index}"
|
||||
|
||||
course_name = context.course.name
|
||||
print(chapter_index, lesson_index)
|
||||
if not chapter_index or not lesson_index:
|
||||
index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1"
|
||||
print(index_)
|
||||
frappe.local.flags.redirect_location = context.batch.get_learn_url(index_)
|
||||
if context.batch:
|
||||
index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1"
|
||||
else:
|
||||
index_ = "1.1"
|
||||
frappe.local.flags.redirect_location = context.course.get_learn_url(index_)
|
||||
raise frappe.Redirect
|
||||
|
||||
context.lesson = context.course.get_lesson(chapter_index, lesson_index)
|
||||
@@ -27,8 +30,8 @@ def get_context(context):
|
||||
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.batch.get_learn_url(next_)
|
||||
context.prev_url = context.batch.get_learn_url(prev_)
|
||||
context.next_url = context.course.get_learn_url(next_)
|
||||
context.prev_url = context.course.get_learn_url(prev_)
|
||||
|
||||
context.page_extensions = get_page_extensions()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import frappe
|
||||
from community.lms.models import Course, Membership
|
||||
from community.lms.models import Course
|
||||
|
||||
def get_common_context(context):
|
||||
context.no_cache = 1
|
||||
@@ -10,16 +10,16 @@ def get_common_context(context):
|
||||
if not course:
|
||||
context.template = "www/404.html"
|
||||
return
|
||||
batch_name = Membership.get_user_batch(course_name)
|
||||
|
||||
batch_name = course.get_current_batch()
|
||||
batch = course.get_batch(batch_name)
|
||||
""" if not batch or not batch.is_member(frappe.session.user):
|
||||
frappe.local.flags.redirect_location = "/courses/" + course_name
|
||||
raise frappe.Redirect """
|
||||
context.batch = batch
|
||||
if batch_name:
|
||||
context.members = batch.get_mentors() + batch.get_students()
|
||||
context.member_count = len(context.members)
|
||||
|
||||
|
||||
context.course = course
|
||||
context.batch = batch
|
||||
context.members = batch.get_mentors() + batch.get_students()
|
||||
context.member_count = len(context.members)
|
||||
context.livecode_url = get_livecode_url()
|
||||
|
||||
def get_livecode_url():
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
{% macro BatchSectionForStudents(course, upcoming_batches) %}
|
||||
{% if upcoming_batches %}
|
||||
<div class="mt-5">
|
||||
<h3>Upcoming Batches</h3>
|
||||
<h3 class="upcoming">Upcoming Batches</h3>
|
||||
<div class="row">
|
||||
{% for batch in upcoming_batches %}
|
||||
<div class="col-lg-4 col-md-6">
|
||||
|
||||
@@ -1,72 +1,92 @@
|
||||
frappe.ready(() => {
|
||||
if (frappe.session.user != "Guest") {
|
||||
frappe.call({
|
||||
'method': 'community.lms.doctype.lms_mentor_request.lms_mentor_request.has_requested',
|
||||
'args': {
|
||||
course: decodeURIComponent($("#course-title").attr("data-course")),
|
||||
},
|
||||
'callback': (data) => {
|
||||
if (data.message > 0) {
|
||||
$("#mentor-request").addClass("hide");
|
||||
$("#already-applied").removeClass("hide")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (frappe.session.user != "Guest") {
|
||||
frappe.call({
|
||||
'method': 'community.lms.doctype.lms_mentor_request.lms_mentor_request.has_requested',
|
||||
'args': {
|
||||
course: decodeURIComponent($("#course-title").attr("data-course")),
|
||||
},
|
||||
'callback': (data) => {
|
||||
if (data.message > 0) {
|
||||
$("#mentor-request").addClass("hide");
|
||||
$("#already-applied").removeClass("hide")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$("#apply-now").click((e) => {
|
||||
$("#apply-now").click((e) => {
|
||||
e.preventDefault();
|
||||
if (frappe.session.user == "Guest") {
|
||||
window.location.href = `/login?redirect-to=/courses/${$(e.currentTarget).attr("data-course")}`;
|
||||
return;
|
||||
}
|
||||
frappe.call({
|
||||
"method": "community.lms.doctype.lms_mentor_request.lms_mentor_request.create_request",
|
||||
"args": {
|
||||
"course": decodeURIComponent($(e.currentTarget).attr("data-course"))
|
||||
},
|
||||
"callback": (data) => {
|
||||
if (data.message == "OK") {
|
||||
$("#mentor-request").addClass("hide");
|
||||
$("#already-applied").removeClass("hide")
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
if (frappe.session.user == "Guest") {
|
||||
window.location.href = `/login?redirect-to=/courses/${$(e.currentTarget).attr("data-course")}`;
|
||||
return;
|
||||
}
|
||||
frappe.call({
|
||||
"method": "community.lms.doctype.lms_mentor_request.lms_mentor_request.create_request",
|
||||
"args": {
|
||||
"course": decodeURIComponent($(e.currentTarget).attr("data-course"))
|
||||
},
|
||||
"callback": (data) => {
|
||||
if (data.message == "OK") {
|
||||
$("#mentor-request").addClass("hide");
|
||||
$("#already-applied").removeClass("hide")
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
$("#cancel-request").click((e) => {
|
||||
$("#cancel-request").click((e) => {
|
||||
e.preventDefault()
|
||||
frappe.call({
|
||||
"method": "community.lms.doctype.lms_mentor_request.lms_mentor_request.cancel_request",
|
||||
"args": {
|
||||
"course": decodeURIComponent($(e.currentTarget).attr("data-course"))
|
||||
},
|
||||
"callback": (data) => {
|
||||
if (data.message == "OK") {
|
||||
$("#mentor-request").removeClass("hide");
|
||||
$("#already-applied").addClass("hide")
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
frappe.call({
|
||||
"method": "community.lms.doctype.lms_mentor_request.lms_mentor_request.cancel_request",
|
||||
"args": {
|
||||
"course": decodeURIComponent($(e.currentTarget).attr("data-course"))
|
||||
},
|
||||
"callback": (data) => {
|
||||
if (data.message == "OK") {
|
||||
$("#mentor-request").removeClass("hide");
|
||||
$("#already-applied").addClass("hide")
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
$(".join-batch").click((e) => {
|
||||
e.preventDefault()
|
||||
if (frappe.session.user == "Guest") {
|
||||
window.location.href = `/login?redirect-to=/courses/${$(e.currentTarget).attr("data-course")}`;
|
||||
return;
|
||||
}
|
||||
batch = decodeURIComponent($(e.currentTarget).attr("data-batch"))
|
||||
frappe.call({
|
||||
"method": "community.lms.doctype.lms_batch_membership.lms_batch_membership.create_membership",
|
||||
"args": {
|
||||
"batch": batch
|
||||
},
|
||||
"callback": (data) => {
|
||||
if (data.message == "OK") {
|
||||
frappe.msgprint(__("You are now a student of this course."))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
$(".join-batch").click((e) => {
|
||||
e.preventDefault();
|
||||
var course = $(e.currentTarget).attr("data-course")
|
||||
if (frappe.session.user == "Guest") {
|
||||
window.location.href = `/login?redirect-to=/courses/${course}`;
|
||||
return;
|
||||
}
|
||||
batch = decodeURIComponent($(e.currentTarget).attr("data-batch"))
|
||||
frappe.call({
|
||||
"method": "community.lms.doctype.lms_batch_membership.lms_batch_membership.create_membership",
|
||||
"args": {
|
||||
"batch": batch
|
||||
},
|
||||
"callback": (data) => {
|
||||
if (data.message == "OK") {
|
||||
frappe.msgprint(__("You are now a student of this course."));
|
||||
setTimeout(function () {
|
||||
window.location.href = `/courses/${course}/home`;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
$(".manage-batch").click((e) => {
|
||||
e.preventDefault();
|
||||
var batch = decodeURIComponent($(e.currentTarget).attr("data-batch"));
|
||||
var course = decodeURIComponent($(e.currentTarget).attr("data-course"));
|
||||
frappe.call({
|
||||
method: "community.lms.doctype.lms_batch_membership.lms_batch_membership.update_current_membership",
|
||||
args: {
|
||||
batch: batch,
|
||||
course: course
|
||||
},
|
||||
callback: (data) => {
|
||||
window.location.href = `/courses/${course}/home`;
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,6 +19,6 @@ def get_context(context):
|
||||
|
||||
batch = course.get_student_batch(frappe.session.user)
|
||||
if batch:
|
||||
frappe.local.flags.redirect_location = f"/courses/{course.name}/{batch.name}/learn"
|
||||
frappe.local.flags.redirect_location = f"/courses/{course.name}/learn"
|
||||
raise frappe.Redirect
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
{% macro course_card(course) %}
|
||||
<div class="col-sm-4 mb-4 text-left">
|
||||
<a class="card-links" style="color: inherit;" href="/courses/{{course.name}}">
|
||||
<a class="anchor_style" style="color: inherit;" href="/courses/{{course.name}}">
|
||||
<div class="card h-100" style="box-shadow: 0px 5px 10px rgb(0 0 0 / 10%);">
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>{{ course.title }}</h5>
|
||||
|
||||
Reference in New Issue
Block a user