Compare commits
12 Commits
gitignore-
...
resume-cou
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d7963fc60 | ||
|
|
38938ac14b | ||
|
|
4a2ecff15d | ||
|
|
f8d6b5b949 | ||
|
|
c77835b81f | ||
|
|
e04bbb633d | ||
|
|
a2b856aaf8 | ||
|
|
7a650b46ac | ||
|
|
b61ca1d7a2 | ||
|
|
632693c9f8 | ||
|
|
463aec01f8 | ||
|
|
e7d116f31c |
@@ -141,12 +141,13 @@ primary_rules = [
|
|||||||
{"from_route": "/hackathons/<hackathon>/<project>", "to_route": "hackathons/project"},
|
{"from_route": "/hackathons/<hackathon>/<project>", "to_route": "hackathons/project"},
|
||||||
{"from_route": "/dashboard", "to_route": ""},
|
{"from_route": "/dashboard", "to_route": ""},
|
||||||
{"from_route": "/add-a-new-batch", "to_route": "add-a-new-batch"},
|
{"from_route": "/add-a-new-batch", "to_route": "add-a-new-batch"},
|
||||||
{"from_route": "/courses/<course>/<batch>/learn", "to_route": "courses/learn"},
|
{"from_route": "/courses/<course>/<batch>/learn", "to_route": "batch/learn"},
|
||||||
{"from_route": "/courses/<course>/<batch>/learn/<int:chapter>.<int:lesson>", "to_route": "courses/learn"},
|
{"from_route": "/courses/<course>/<batch>/learn/<int:chapter>.<int:lesson>", "to_route": "batch/learn"},
|
||||||
{"from_route": "/courses/<course>/<batch>/schedule", "to_route": "courses/schedule"},
|
{"from_route": "/courses/<course>/<batch>/schedule", "to_route": "batch/schedule"},
|
||||||
{"from_route": "/courses/<course>/<batch>/members", "to_route": "courses/members"},
|
{"from_route": "/courses/<course>/<batch>/members", "to_route": "batch/members"},
|
||||||
{"from_route": "/courses/<course>/<batch>/discuss", "to_route": "courses/discuss"},
|
{"from_route": "/courses/<course>/<batch>/discuss", "to_route": "batch/discuss"},
|
||||||
{"from_route": "/courses/<course>/<batch>/about", "to_route": "courses/about"}
|
{"from_route": "/courses/<course>/<batch>/about", "to_route": "batch/about"},
|
||||||
|
{"from_route": "/courses/<course>/<batch>/progress", "to_route": "batch/progress"}
|
||||||
]
|
]
|
||||||
|
|
||||||
# Any frappe default URL is blocked by profile-rules, add it here to unblock it
|
# Any frappe default URL is blocked by profile-rules, add it here to unblock it
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ class Exercise(Document):
|
|||||||
},
|
},
|
||||||
order_by="creation desc",
|
order_by="creation desc",
|
||||||
page_length=1)
|
page_length=1)
|
||||||
|
|
||||||
|
print("get_user_submission", result)
|
||||||
if result:
|
if result:
|
||||||
return result[0]
|
return result[0]
|
||||||
|
|
||||||
@@ -41,6 +43,8 @@ class Exercise(Document):
|
|||||||
course = frappe.get_doc("LMS Course", self.course)
|
course = frappe.get_doc("LMS Course", self.course)
|
||||||
batch = course.get_student_batch(user)
|
batch = course.get_student_batch(user)
|
||||||
|
|
||||||
|
image = livecode_to_svg(None, code)
|
||||||
|
|
||||||
doc = frappe.get_doc(
|
doc = frappe.get_doc(
|
||||||
doctype="Exercise Submission",
|
doctype="Exercise Submission",
|
||||||
exercise=self.name,
|
exercise=self.name,
|
||||||
@@ -48,7 +52,8 @@ class Exercise(Document):
|
|||||||
course=self.course,
|
course=self.course,
|
||||||
lesson=self.lesson,
|
lesson=self.lesson,
|
||||||
batch=batch and batch.name,
|
batch=batch and batch.name,
|
||||||
|
image=image,
|
||||||
solution=code)
|
solution=code)
|
||||||
doc.insert()
|
doc.insert(ignore_permissions=True)
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"exercise_title",
|
"exercise_title",
|
||||||
"course",
|
"course",
|
||||||
"batch",
|
"batch",
|
||||||
"lesson"
|
"lesson",
|
||||||
|
"image"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -54,11 +55,17 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Lesson",
|
"label": "Lesson",
|
||||||
"options": "Lesson"
|
"options": "Lesson"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "image",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Image",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-05-20 13:30:16.349278",
|
"modified": "2021-05-21 11:28:45.833018",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "Exercise Submission",
|
"name": "Exercise Submission",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from ..lms_topic.section_parser import SectionParser
|
from ...section_parser import SectionParser
|
||||||
|
|
||||||
class Lesson(Document):
|
class Lesson(Document):
|
||||||
def before_save(self):
|
def before_save(self):
|
||||||
|
|||||||
@@ -37,13 +37,37 @@ class LMSBatch(Document):
|
|||||||
member_names = [m['member'] for m in memberships]
|
member_names = [m['member'] for m in memberships]
|
||||||
return find_all("Community Member", name=["IN", member_names])
|
return find_all("Community Member", name=["IN", member_names])
|
||||||
|
|
||||||
def is_member(self, email):
|
def is_member(self, email, member_type=None):
|
||||||
"""Checks if a person is part of a batch.
|
"""Checks if a person is part of a batch.
|
||||||
|
|
||||||
|
If member_type is specified, checks if the person is a Student/Mentor.
|
||||||
"""
|
"""
|
||||||
member = find("Community Member", email=email)
|
member = find("Community Member", email=email)
|
||||||
return member and frappe.db.exists(
|
if not member:
|
||||||
|
return
|
||||||
|
|
||||||
|
filters = {
|
||||||
|
"batch": self.name,
|
||||||
|
"member": member.name
|
||||||
|
}
|
||||||
|
if member_type:
|
||||||
|
filters['member_type'] = member_type
|
||||||
|
return frappe.db.exists("LMS Batch Membership", filters)
|
||||||
|
|
||||||
|
def get_students(self):
|
||||||
|
"""Returns (email, full_name, username) of all the students of this batch as a list of dict.
|
||||||
|
"""
|
||||||
|
memberships = frappe.get_all(
|
||||||
"LMS Batch Membership",
|
"LMS Batch Membership",
|
||||||
{"batch": self.name, "member": member.name})
|
{"batch": self.name, "member_type": "Student"},
|
||||||
|
["member"])
|
||||||
|
member_names = [m['member'] for m in memberships]
|
||||||
|
members = frappe.get_all(
|
||||||
|
"Community Member",
|
||||||
|
{"name": ["IN", member_names]},
|
||||||
|
["email", "full_name", "username"])
|
||||||
|
return members
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_messages(batch):
|
def get_messages(batch):
|
||||||
|
|||||||
@@ -6,11 +6,13 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"batch",
|
"batch",
|
||||||
"role",
|
|
||||||
"column_break_3",
|
|
||||||
"member",
|
"member",
|
||||||
"member_name",
|
"member_name",
|
||||||
"member_type"
|
"member_email",
|
||||||
|
"column_break_3",
|
||||||
|
"course",
|
||||||
|
"member_type",
|
||||||
|
"role"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -30,6 +32,7 @@
|
|||||||
"options": "Community Member"
|
"options": "Community Member"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "Student",
|
||||||
"fieldname": "member_type",
|
"fieldname": "member_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@@ -37,6 +40,7 @@
|
|||||||
"options": "\nStudent\nMentor\nStaff"
|
"options": "\nStudent\nMentor\nStaff"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "Member",
|
||||||
"fieldname": "role",
|
"fieldname": "role",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
@@ -54,11 +58,28 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "member.email",
|
||||||
|
"fieldname": "member_email",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Member Email",
|
||||||
|
"read_only": 1,
|
||||||
|
"read_only_depends_on": "member.email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "batch.course",
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Course",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-26 12:52:59.826509",
|
"modified": "2021-05-24 09:32:04.128620",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Batch Membership",
|
"name": "LMS Batch Membership",
|
||||||
|
|||||||
@@ -35,17 +35,6 @@ class LMSCourse(Document):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Course#{self.name}>"
|
return f"<Course#{self.name}>"
|
||||||
|
|
||||||
def get_topic(self, slug):
|
|
||||||
"""Returns the topic with given slug in this course as a Document.
|
|
||||||
"""
|
|
||||||
result = frappe.get_all(
|
|
||||||
"LMS Topic",
|
|
||||||
filters={"course": self.name, "slug": slug})
|
|
||||||
|
|
||||||
if result:
|
|
||||||
row = result[0]
|
|
||||||
return frappe.get_doc('LMS Topic', row['name'])
|
|
||||||
|
|
||||||
def has_mentor(self, email):
|
def has_mentor(self, email):
|
||||||
"""Checks if this course has a mentor with given email.
|
"""Checks if this course has a mentor with given email.
|
||||||
"""
|
"""
|
||||||
@@ -120,28 +109,17 @@ class LMSCourse(Document):
|
|||||||
Returns None if the student is not part of any batch.
|
Returns None if the student is not part of any batch.
|
||||||
"""
|
"""
|
||||||
if not email:
|
if not email:
|
||||||
return False
|
return
|
||||||
member = self.get_community_member(email)
|
|
||||||
result = frappe.db.get_all(
|
|
||||||
"LMS Batch Membership",
|
|
||||||
filters={
|
|
||||||
"member": member,
|
|
||||||
"member_type": "Student",
|
|
||||||
},
|
|
||||||
fields=['batch']
|
|
||||||
)
|
|
||||||
batches = [row['batch'] for row in result]
|
|
||||||
|
|
||||||
# filter the batches that are for this course
|
batch_name = frappe.get_value(
|
||||||
result = frappe.db.get_all(
|
doctype="LMS Batch Membership",
|
||||||
"LMS Batch",
|
|
||||||
filters={
|
filters={
|
||||||
"course": self.name,
|
"course": self.name,
|
||||||
"name": ["IN", batches]
|
"member_type": "Student",
|
||||||
})
|
"member_email": email
|
||||||
batches = [row['name'] for row in result]
|
},
|
||||||
if batches:
|
fieldname="batch")
|
||||||
return frappe.get_doc("LMS Batch", batches[0])
|
return batch_name and frappe.get_doc("LMS Batch", batch_name)
|
||||||
|
|
||||||
def get_instructor(self):
|
def get_instructor(self):
|
||||||
member_name = self.get_community_member(self.owner)
|
member_name = self.get_community_member(self.owner)
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
|
||||||
// For license information, please see license.txt
|
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Topic', {
|
|
||||||
// refresh: function(frm) {
|
|
||||||
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
{
|
|
||||||
"actions": [],
|
|
||||||
"allow_guest_to_view": 1,
|
|
||||||
"creation": "2021-03-02 07:20:41.686573",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"field_order": [
|
|
||||||
"course",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"preview",
|
|
||||||
"description",
|
|
||||||
"order",
|
|
||||||
"sections"
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldname": "title",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Title",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "description",
|
|
||||||
"fieldtype": "Markdown Editor",
|
|
||||||
"label": "Description"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "course",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Course",
|
|
||||||
"options": "LMS Course",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "order",
|
|
||||||
"fieldtype": "Int",
|
|
||||||
"label": "Order"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "preview",
|
|
||||||
"fieldtype": "Markdown Editor",
|
|
||||||
"label": "Preview"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "sections",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"label": "Sections",
|
|
||||||
"options": "LMS Section"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "The slug of the topic. Autogenerated from the title if not specified.",
|
|
||||||
"fieldname": "slug",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"label": "Slug"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"index_web_pages_for_search": 1,
|
|
||||||
"links": [],
|
|
||||||
"modified": "2021-04-06 14:12:48.514062",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "LMS",
|
|
||||||
"name": "LMS Topic",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"create": 1,
|
|
||||||
"delete": 1,
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"share": 1,
|
|
||||||
"write": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"search_fields": "title",
|
|
||||||
"sort_field": "creation",
|
|
||||||
"sort_order": "ASC",
|
|
||||||
"title_field": "title",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 1,
|
|
||||||
"track_views": 1
|
|
||||||
}
|
|
||||||
@@ -1,43 +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
|
|
||||||
from .section_parser import SectionParser
|
|
||||||
from ...utils import slugify
|
|
||||||
|
|
||||||
class LMSTopic(Document):
|
|
||||||
def before_save(self):
|
|
||||||
course = self.get_course()
|
|
||||||
if not self.slug:
|
|
||||||
self.slug = self.generate_slug(title=self.title)
|
|
||||||
|
|
||||||
sections = SectionParser().parse(self.description or "")
|
|
||||||
self.sections = [self.make_lms_section(i, s) for i, s in enumerate(sections)]
|
|
||||||
|
|
||||||
def get_course(self):
|
|
||||||
return frappe.get_doc("LMS Course", self.course)
|
|
||||||
|
|
||||||
def generate_slug(self, title):
|
|
||||||
result = frappe.get_all(
|
|
||||||
'LMS Topic',
|
|
||||||
filters={'course': self.course},
|
|
||||||
fields=['slug'])
|
|
||||||
slugs = set([row['slug'] for row in result])
|
|
||||||
return slugify(title, used_slugs=slugs)
|
|
||||||
|
|
||||||
def get_sections(self):
|
|
||||||
return sorted(self.sections, key=lambda s: s.index)
|
|
||||||
|
|
||||||
def make_lms_section(self, index, section):
|
|
||||||
s = frappe.new_doc('LMS Section', parent_doc=self, parentfield='sections')
|
|
||||||
s.type = section.type
|
|
||||||
s.label = section.label
|
|
||||||
s.contents = section.contents
|
|
||||||
s.index = index
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and Contributors
|
|
||||||
# See license.txt
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
# import frappe
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
class TestLMSTopic(unittest.TestCase):
|
|
||||||
pass
|
|
||||||
@@ -6,6 +6,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="course-footer">
|
<div class="course-footer">
|
||||||
|
{% set batch = course.get_student_batch(frappe.session.user) %}
|
||||||
|
{% if batch %}
|
||||||
|
<a class="btn btn-secondary pull-right" href="/courses/{{course.name}}/{{batch.name}}/learn">Resume Course</a>
|
||||||
|
{% endif %}
|
||||||
<div class="course-author">
|
<div class="course-author">
|
||||||
{% with author = course.get_instructor() %}
|
{% with author = course.get_instructor() %}
|
||||||
{{ widgets.Avatar(member=author, avatar_class="avatar-medium") }} <a href="/{{author.username}}">{{ author.full_name }}</a>
|
{{ widgets.Avatar(member=author, avatar_class="avatar-medium") }} <a href="/{{author.username}}">{{ author.full_name }}</a>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
{% set submission = exercise.get_user_submission() %}
|
{% set submission = exercise.get_user_submission() %}
|
||||||
|
|
||||||
{{ LiveCodeEditor(exercise.name,
|
{{ LiveCodeEditor(exercise.name,
|
||||||
code=exercise.code,
|
code=submission.solution if submission else exercise.code,
|
||||||
reset_code=exercise.code,
|
reset_code=exercise.code,
|
||||||
is_exercise=True,
|
is_exercise=True,
|
||||||
last_submitted=submission and submission.creation) }}
|
last_submitted=submission and submission.creation) }}
|
||||||
|
|||||||
@@ -23,8 +23,6 @@
|
|||||||
--cta-color: var(--c4);
|
--cta-color: var(--c4);
|
||||||
--send-message: var(--c7);
|
--send-message: var(--c7);
|
||||||
--received-message: var(--c8);
|
--received-message: var(--c8);
|
||||||
|
|
||||||
--primary-color: #08B74F !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@primary-color: #08B74F;
|
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin: 20px 0px;
|
margin: 20px 0px;
|
||||||
color: black;
|
color: black;
|
||||||
@@ -312,3 +310,28 @@ section.lightgray {
|
|||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
margin-bottom: 20px;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* Define all your css variables here. */
|
/* Define all your css variables here. */
|
||||||
:root {
|
:root {
|
||||||
--primary-color: #08B74F;
|
|
||||||
--tag-color: #737373;
|
--tag-color: #737373;
|
||||||
--sidebar-bg: #F6F6F6;
|
--sidebar-bg: #F6F6F6;
|
||||||
--sidebar-border: #C4C4C4;
|
--sidebar-border: #C4C4C4;
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ Sidebar(course.name, batch.name) }}
|
{{ Sidebar(course, batch) }}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{{ CourseBasicDetail(course)}}
|
{{ CourseBasicDetail(course)}}
|
||||||
{{ InstructorsSection(course.get_instructor()) }}
|
{{ InstructorsSection(course.get_instructor()) }}
|
||||||
7
community/www/batch/about.py
Normal file
7
community/www/batch/about.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import frappe
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
utils.get_common_context(context)
|
||||||
|
|
||||||
|
print("context", context)
|
||||||
@@ -11,10 +11,11 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ Sidebar(course_slug, batch_code) }}
|
{{ Sidebar(course, batch) }}
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
<div class="batch-header">
|
<div class="batch-header">
|
||||||
{{ BatchHearder(course.name, member_count) }}
|
{{ BatchHearder(course.title, member_count) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="messages">
|
<div class="messages">
|
||||||
<div class="message-section">
|
<div class="message-section">
|
||||||
9
community/www/batch/discuss.py
Normal file
9
community/www/batch/discuss.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import frappe
|
||||||
|
from . import utils
|
||||||
|
from community.lms.doctype.lms_batch.lms_batch import get_messages
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
utils.get_common_context(context)
|
||||||
|
|
||||||
|
context.members = utils.get_batch_members(context.batch.name)
|
||||||
|
context.member_count = len(context.members)
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ Sidebar(course.name, batch.name) }}
|
{{ Sidebar(course, batch) }}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="lesson-page">
|
<div class="lesson-page">
|
||||||
@@ -1,37 +1,25 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from community.lms.models import Course
|
from . import utils
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.no_cache = 1
|
utils.get_common_context(context)
|
||||||
|
|
||||||
course_name = frappe.form_dict["course"]
|
|
||||||
batch_name = frappe.form_dict["batch"]
|
|
||||||
chapter_index = frappe.form_dict.get("chapter")
|
chapter_index = frappe.form_dict.get("chapter")
|
||||||
lesson_index = frappe.form_dict.get("lesson")
|
lesson_index = frappe.form_dict.get("lesson")
|
||||||
lesson_number = f"{chapter_index}.{lesson_index}"
|
lesson_number = f"{chapter_index}.{lesson_index}"
|
||||||
|
|
||||||
course = Course.find(course_name)
|
course_name = context.course.name
|
||||||
if not course:
|
batch_name = context.batch.name
|
||||||
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
|
|
||||||
|
|
||||||
if not chapter_index or not lesson_index:
|
if not chapter_index or not lesson_index:
|
||||||
frappe.local.flags.redirect_location = f"/courses/{course_name}/{batch_name}/learn/1.1"
|
frappe.local.flags.redirect_location = f"/courses/{course_name}/{batch_name}/learn/1.1"
|
||||||
raise frappe.Redirect
|
raise frappe.Redirect
|
||||||
|
|
||||||
context.course = course
|
context.lesson = context.course.get_lesson(chapter_index, lesson_index)
|
||||||
context.batch = batch
|
|
||||||
context.lesson = course.get_lesson(chapter_index, lesson_index)
|
|
||||||
context.lesson_index = lesson_index
|
context.lesson_index = lesson_index
|
||||||
context.chapter_index = chapter_index
|
context.chapter_index = chapter_index
|
||||||
context.livecode_url = get_livecode_url()
|
|
||||||
|
|
||||||
outline = course.get_outline()
|
outline = context.course.get_outline()
|
||||||
next_ = outline.get_next(lesson_number)
|
next_ = outline.get_next(lesson_number)
|
||||||
prev_ = outline.get_prev(lesson_number)
|
prev_ = outline.get_prev(lesson_number)
|
||||||
context.next_url = get_learn_url(course_name, batch_name, next_)
|
context.next_url = get_learn_url(course_name, batch_name, next_)
|
||||||
@@ -41,6 +29,3 @@ def get_learn_url(course_name, batch_name, lesson_number):
|
|||||||
if not lesson_number:
|
if not lesson_number:
|
||||||
return
|
return
|
||||||
return f"/courses/{course_name}/{batch_name}/learn/{lesson_number}"
|
return f"/courses/{course_name}/{batch_name}/learn/{lesson_number}"
|
||||||
|
|
||||||
def get_livecode_url():
|
|
||||||
return frappe.db.get_single_value("LMS Settings", "livecode_url")
|
|
||||||
@@ -11,9 +11,10 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ Sidebar(course_slug, batch_code) }}
|
{{ Sidebar(course, batch) }}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{{ BatchHearder(course.name, member_count)}}
|
{{ BatchHearder(course.title, member_count)}}
|
||||||
{{ MembersList(members)}}
|
{{ MembersList(members)}}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -36,4 +37,4 @@
|
|||||||
<hr>
|
<hr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
8
community/www/batch/members.py
Normal file
8
community/www/batch/members.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import frappe
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
utils.get_common_context(context)
|
||||||
|
|
||||||
|
context.members = utils.get_batch_members(context.batch.name)
|
||||||
|
context.member_count = len(context.members)
|
||||||
52
community/www/batch/progress.html
Normal file
52
community/www/batch/progress.html
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{% extends "templates/base.html" %}
|
||||||
|
{% from "www/macros/sidebar.html" import Sidebar %}
|
||||||
|
{% 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 %}
|
||||||
|
{{ Sidebar(course, batch) }}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="mentor-dashboard">
|
||||||
|
<h1>Batch Progress</h1>
|
||||||
|
{% for exercise in report.exercises %}
|
||||||
|
<div class="exercise-submissions">
|
||||||
|
<h2>{{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 %}
|
||||||
64
community/www/batch/progress.py
Normal file
64
community/www/batch/progress.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
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(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}, ["name", "title"])
|
||||||
|
|
||||||
|
def get_submissions_of_exercise(self, exercise_name):
|
||||||
|
return self.submissions_by_exercise[exercise_name]
|
||||||
|
|
||||||
|
def get_submissions(batch):
|
||||||
|
students = batch.get_students()
|
||||||
|
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
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ Sidebar(course, batch_code) }}
|
{{ Sidebar(course, batch) }}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -3,7 +3,6 @@ from community.lms.models import Course
|
|||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.no_cache = 1
|
context.no_cache = 1
|
||||||
|
|
||||||
course_name = frappe.form_dict["course"]
|
course_name = frappe.form_dict["course"]
|
||||||
batch_name = frappe.form_dict["batch"]
|
batch_name = frappe.form_dict["batch"]
|
||||||
|
|
||||||
43
community/www/batch/utils.py
Normal file
43
community/www/batch/utils.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import frappe
|
||||||
|
from community.lms.models import Course
|
||||||
|
|
||||||
|
def get_common_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
|
||||||
|
context.livecode_url = get_livecode_url()
|
||||||
|
|
||||||
|
def get_livecode_url():
|
||||||
|
return frappe.db.get_single_value("LMS Settings", "livecode_url")
|
||||||
|
|
||||||
|
def get_batch_members(batch_name):
|
||||||
|
members = []
|
||||||
|
memberships = frappe.get_all("LMS Batch Membership", {"batch": batch_name}, ["member", "member_type"])
|
||||||
|
|
||||||
|
for membership in memberships:
|
||||||
|
member = get_member_with_name(membership.member)
|
||||||
|
if membership.member_type == "Mentor":
|
||||||
|
member.is_mentor = True
|
||||||
|
members.append(member)
|
||||||
|
return members
|
||||||
|
|
||||||
|
def get_member_with_name(name):
|
||||||
|
try:
|
||||||
|
return frappe.get_doc("Community Member", name)
|
||||||
|
except frappe.DoesNotExistError:
|
||||||
|
return
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import frappe
|
|
||||||
from community.www.courses.utils import redirect_if_not_a_member, get_course, get_batch_members, get_batch
|
|
||||||
from community.lms.doctype.lms_batch.lms_batch import get_messages
|
|
||||||
|
|
||||||
def get_context(context):
|
|
||||||
context.no_cache = 1
|
|
||||||
context.course_slug = frappe.form_dict["course"]
|
|
||||||
context.course = get_course(context.course_slug)
|
|
||||||
context.batch_code = frappe.form_dict["batch"]
|
|
||||||
redirect_if_not_a_member(context.course_slug, context.batch_code)
|
|
||||||
|
|
||||||
context.batch = get_batch(context.batch_code)
|
|
||||||
context.members = get_batch_members(context.batch.name)
|
|
||||||
context.member_count = len(context.members)
|
|
||||||
context.messages = get_messages(context.batch.name)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import frappe
|
|
||||||
from community.www.courses.utils import redirect_if_not_a_member, get_batch, get_member_with_name, get_course, get_batch_members
|
|
||||||
|
|
||||||
def get_context(context):
|
|
||||||
context.no_cache = 1
|
|
||||||
context.course_slug = frappe.form_dict["course"]
|
|
||||||
context.course = get_course(context.course_slug)
|
|
||||||
context.batch_code = frappe.form_dict["batch"]
|
|
||||||
redirect_if_not_a_member(context.course_slug, context.batch_code)
|
|
||||||
context.batch = get_batch(context.batch_code)
|
|
||||||
context.members = get_batch_members(context.batch.name)
|
|
||||||
context.member_count = len(context.members)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import frappe
|
|
||||||
from community.www.courses.utils import redirect_if_not_a_member
|
|
||||||
|
|
||||||
def get_context(context):
|
|
||||||
context.no_cache = 1
|
|
||||||
context.course = frappe.form_dict["course"]
|
|
||||||
context.batch_code = frappe.form_dict["batch"]
|
|
||||||
redirect_if_not_a_member(context.course, context.batch_code)
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
{% extends "templates/base.html" %}
|
|
||||||
{% from "www/macros/livecode.html" import LiveCodeEditor with context %}
|
|
||||||
{% block title %}{{topic.title}} ({{course.title}}){% endblock %}
|
|
||||||
{% block head_include %}
|
|
||||||
<meta name="description" content="Topic {{topic.title}} of the course {{course.title}}" />
|
|
||||||
<meta name="keywords" content="course {{course.title}} {{topic.title}}" />
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<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 %}
|
|
||||||
<section class="top-section" style="padding: 1rem 0rem;">
|
|
||||||
<div class='container pb-5'>
|
|
||||||
<nav aria-label="breadcrumb">
|
|
||||||
<ol class="breadcrumb">
|
|
||||||
<li class="breadcrumb-item" aria-current="page"><a href="/courses">Courses</a></li>
|
|
||||||
<li class="breadcrumb-item" aria-current="page"><a href="/courses/{{course.slug}}">{{course.title}}</a></li>
|
|
||||||
</ol>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<h1>{{ topic.title }}</h1>
|
|
||||||
|
|
||||||
{% for s in topic.get_sections() %}
|
|
||||||
<div class="section section-{{ s.type }}">
|
|
||||||
{{ render_section(s) }}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% macro render_section(s) %}
|
|
||||||
{% if s.type == "text" %}
|
|
||||||
{{ render_section_text(s) }}
|
|
||||||
{% elif s.type == "example" or s.type == "code" %}
|
|
||||||
{{ LiveCodeEditor(s.name, s.get_latest_code_for_user()) }}
|
|
||||||
{% else %}
|
|
||||||
<div>Unknown section type: {{s.type}}</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% macro render_section_text(s) %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{{ frappe.utils.md_to_html(s.contents) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{%- block script %}
|
|
||||||
{{ super() }}
|
|
||||||
<script type="text/javascript" src="{{ livecode_url }}/static/livecode.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(function() {
|
|
||||||
var editorLookup = {};
|
|
||||||
|
|
||||||
$(".canvas-editor").each((i, e) => {
|
|
||||||
var data = $(e).data();
|
|
||||||
var editor = new LiveCodeEditor(e, {
|
|
||||||
runtime: "python-canvas",
|
|
||||||
base_url: "{{ livecode_url }}",
|
|
||||||
codemirror: true,
|
|
||||||
userdata: data,
|
|
||||||
autosave: function(editor, code) {
|
|
||||||
// can't autosave when user is Guest
|
|
||||||
if (frappe.session.user == "Guest") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var data = editor.options.userdata;
|
|
||||||
var code = editor.codemirror.doc.getValue();
|
|
||||||
// console.log("autosaving...")
|
|
||||||
frappe.call("community.lms.api.autosave_section", {
|
|
||||||
section: data.section,
|
|
||||||
code: code
|
|
||||||
}).then((r) => {
|
|
||||||
// TODO: verify
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
editorLookup[data.section] = editor;
|
|
||||||
})
|
|
||||||
|
|
||||||
$(".canvas-editor .reset").each((i, e) => {
|
|
||||||
$(e).on("click", function(event) {
|
|
||||||
var data = $(this).parents(".canvas-editor").data();
|
|
||||||
var section = data.section;
|
|
||||||
frappe.call("community.lms.api.get_section", {
|
|
||||||
name: section
|
|
||||||
}).then(r => {
|
|
||||||
var editor = editorLookup[data.section];
|
|
||||||
editor.codemirror.doc.setValue(r.message.contents);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{%- endblock %}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import frappe
|
|
||||||
|
|
||||||
def get_context(context):
|
|
||||||
context.no_cache = 1
|
|
||||||
|
|
||||||
try:
|
|
||||||
course_slug = frappe.form_dict['course']
|
|
||||||
topic_slug = frappe.form_dict['topic']
|
|
||||||
except KeyError:
|
|
||||||
context.template = 'www/404.html'
|
|
||||||
return
|
|
||||||
|
|
||||||
course = get_course(course_slug)
|
|
||||||
topic = course and course.get_topic(topic_slug)
|
|
||||||
|
|
||||||
if not topic:
|
|
||||||
context.template = 'www/404.html'
|
|
||||||
return
|
|
||||||
|
|
||||||
context.course = course
|
|
||||||
context.topic = topic
|
|
||||||
context.livecode_url = get_livecode_url()
|
|
||||||
|
|
||||||
def notfound(context):
|
|
||||||
context.template = 'www/404.html'
|
|
||||||
|
|
||||||
def get_livecode_url():
|
|
||||||
doc = frappe.get_doc("LMS Settings")
|
|
||||||
return doc.livecode_url
|
|
||||||
|
|
||||||
def get_course(slug):
|
|
||||||
course = frappe.db.get_value('LMS Course', {"slug": slug}, ["name"], as_dict=1)
|
|
||||||
return course and frappe.get_doc('LMS Course', course['name'])
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
{% macro Sidebar(course, batch_code) %}
|
{% macro Sidebar(course, batch, is_mentor=False) %}
|
||||||
<div class="sidebar-batch">
|
<div class="sidebar-batch">
|
||||||
<a href=""><i class="fa fa-bars fa-lg"></i></a>
|
<a href=""><i class="fa fa-bars fa-lg"></i></a>
|
||||||
<br>
|
<br>
|
||||||
<a href="/courses/{{course}}/{{batch_code}}/learn"><i class="fa fa-book fa-lg"></i></a>
|
<a href="/courses/{{course.name}}/{{batch.name}}/learn"><i class="fa fa-book fa-lg"></i></a>
|
||||||
<a href="/courses/{{course}}/{{batch_code}}/schedule"><i class="fa fa-calendar fa-lg"></i></a>
|
<a href="/courses/{{course.name}}/{{batch.name}}/schedule"><i class="fa fa-calendar fa-lg"></i></a>
|
||||||
<a href="/courses/{{course}}/{{batch_code}}/members"><i class="fa fa-users fa-lg"></i></a>
|
<a href="/courses/{{course.name}}/{{batch.name}}/members"><i class="fa fa-users fa-lg"></i></a>
|
||||||
<a href="/courses/{{course}}/{{batch_code}}/discuss"><i class="fa fa-comments fa-lg"></i></a>
|
<a href="/courses/{{course.name}}/{{batch.name}}/discuss"><i class="fa fa-comments fa-lg"></i></a>
|
||||||
<a href="/courses/{{course}}/{{batch_code}}/about"><i class="fa fa-info-circle fa-lg"></i></a>
|
<a href="/courses/{{course.name}}/{{batch.name}}/about"><i class="fa fa-info-circle fa-lg"></i></a>
|
||||||
<!-- <a href="#contact"><i class="fas fa-home fa-lg"></i></a> -->
|
{% if batch.is_member(frappe.session.user, member_type="Mentor") %}
|
||||||
|
<a href="/courses/{{course.name}}/{{batch.name}}/progress"><i class="fa fa-flag-checkered fa-lg"></i></a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|||||||
Reference in New Issue
Block a user