Compare commits
6 Commits
community-
...
issue-103
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50856fdfa5 | ||
|
|
cac4f2afef | ||
|
|
df431165e8 | ||
|
|
69125e571f | ||
|
|
68f7215b95 | ||
|
|
20adc8079e |
@@ -34,3 +34,21 @@ def submit_solution(exercise, code):
|
|||||||
return
|
return
|
||||||
doc = ex.submit(code)
|
doc = ex.submit(code)
|
||||||
return {"name": doc.name, "creation": doc.creation}
|
return {"name": doc.name, "creation": doc.creation}
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def save_current_lesson(batch_name, lesson_name):
|
||||||
|
"""Saves the current lesson for a student/mentor.
|
||||||
|
"""
|
||||||
|
name = frappe.get_value(
|
||||||
|
doctype="LMS Batch Membership",
|
||||||
|
filters={
|
||||||
|
"batch": batch_name,
|
||||||
|
"member_email": frappe.session.user
|
||||||
|
},
|
||||||
|
fieldname="name")
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
doc = frappe.get_doc("LMS Batch Membership", name)
|
||||||
|
doc.current_lesson = lesson_name
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
return {"current_lesson": doc.current_lesson}
|
||||||
|
|||||||
@@ -69,6 +69,24 @@ class LMSBatch(Document):
|
|||||||
message.is_author = True
|
message.is_author = True
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
|
def get_membership(self, email):
|
||||||
|
"""Returns the membership document of given user.
|
||||||
|
"""
|
||||||
|
name = frappe.get_value(
|
||||||
|
doctype="LMS Batch Membership",
|
||||||
|
filters={
|
||||||
|
"batch": self.name,
|
||||||
|
"member": email
|
||||||
|
},
|
||||||
|
fieldname="name")
|
||||||
|
return frappe.get_doc("LMS Batch Membership", name)
|
||||||
|
|
||||||
|
def get_current_lesson(self, user):
|
||||||
|
"""Returns the name of the current lesson for the given user.
|
||||||
|
"""
|
||||||
|
membership = self.get_membership(user)
|
||||||
|
return membership and membership.current_lesson
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_message(message, batch):
|
def save_message(message, batch):
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc({
|
||||||
|
|||||||
@@ -8,11 +8,12 @@
|
|||||||
"batch",
|
"batch",
|
||||||
"member",
|
"member",
|
||||||
"member_name",
|
"member_name",
|
||||||
"member_email",
|
"member_username",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"course",
|
"course",
|
||||||
"member_type",
|
"member_type",
|
||||||
"role"
|
"role",
|
||||||
|
"current_lesson"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -59,15 +60,6 @@
|
|||||||
"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",
|
"fetch_from": "batch.course",
|
||||||
"fieldname": "course",
|
"fieldname": "course",
|
||||||
@@ -75,11 +67,24 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Course",
|
"label": "Course",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "current_lesson",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Current Lesson",
|
||||||
|
"options": "Lesson"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "member.username",
|
||||||
|
"fieldname": "member_username",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Memeber Username",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-05-24 09:32:04.128620",
|
"modified": "2021-05-24 12:40:57.125694",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Batch Membership",
|
"name": "LMS Batch Membership",
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ class LMSBatchMembership(Document):
|
|||||||
previous_membership = frappe.db.get_value("LMS Batch Membership",
|
previous_membership = frappe.db.get_value("LMS Batch Membership",
|
||||||
filters={
|
filters={
|
||||||
"member": self.member,
|
"member": self.member,
|
||||||
"batch": self.batch, "name": ["!=", self.name]
|
"batch": self.batch,
|
||||||
|
"name": ["!=", self.name]
|
||||||
},
|
},
|
||||||
fieldname=["member_type","member"],
|
fieldname=["member_type","member"],
|
||||||
as_dict=1)
|
as_dict=1)
|
||||||
@@ -30,7 +31,8 @@ class LMSBatchMembership(Document):
|
|||||||
course = frappe.db.get_value("LMS Batch", self.batch, "course")
|
course = frappe.db.get_value("LMS Batch", self.batch, "course")
|
||||||
previous_membership = frappe.get_all("LMS Batch Membership",
|
previous_membership = frappe.get_all("LMS Batch Membership",
|
||||||
filters={
|
filters={
|
||||||
"member": self.member
|
"member": self.member,
|
||||||
|
"name": ["!=", self.name]
|
||||||
},
|
},
|
||||||
fields=["batch", "member_type"]
|
fields=["batch", "member_type"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,8 +3,76 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class TestLMSBatchMembership(unittest.TestCase):
|
class TestLMSBatchMembership(unittest.TestCase):
|
||||||
pass
|
def setUp(self):
|
||||||
|
frappe.db.sql("DELETE FROM `tabLMS Batch Membership`")
|
||||||
|
frappe.db.sql("DELETE FROM `tabLMS Batch`")
|
||||||
|
frappe.db.sql('delete from `tabLMS Course Mentor Mapping`')
|
||||||
|
frappe.db.sql("DELETE FROM `tabLMS Course`")
|
||||||
|
frappe.db.sql("DELETE FROM `tabUser` where email like '%@test.com'")
|
||||||
|
|
||||||
|
def new_course_batch(self):
|
||||||
|
course = frappe.get_doc({
|
||||||
|
"doctype": "LMS Course",
|
||||||
|
"name": "test-course",
|
||||||
|
"title": "Test Course",
|
||||||
|
"short_code": "XX"
|
||||||
|
})
|
||||||
|
course.insert()
|
||||||
|
|
||||||
|
self.new_user("mentor@test.com", "Test Mentor")
|
||||||
|
# without this, the creating batch will fail
|
||||||
|
course.add_mentor("mentor@test.com")
|
||||||
|
|
||||||
|
frappe.session.user = "mentor@test.com"
|
||||||
|
|
||||||
|
batch = frappe.get_doc({
|
||||||
|
"doctype": "LMS Batch",
|
||||||
|
"name": "test-batch",
|
||||||
|
"title": "Test Batch",
|
||||||
|
"course": course.name
|
||||||
|
})
|
||||||
|
batch.insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
frappe.session.user = "Administrator"
|
||||||
|
return course, batch
|
||||||
|
|
||||||
|
def new_user(self, email="test@test.com", full_name="Test User"):
|
||||||
|
user = frappe.get_doc({
|
||||||
|
"doctype": "User",
|
||||||
|
"name": email,
|
||||||
|
"email": email,
|
||||||
|
"first_name": full_name,
|
||||||
|
})
|
||||||
|
user.insert()
|
||||||
|
return user
|
||||||
|
|
||||||
|
def add_membership(self, batch_name, member_name, member_type="Student"):
|
||||||
|
doc = frappe.get_doc({
|
||||||
|
"doctype": "LMS Batch Membership",
|
||||||
|
"batch": batch_name,
|
||||||
|
"member": member_name,
|
||||||
|
"member_type": member_type
|
||||||
|
})
|
||||||
|
doc.insert()
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def test_membership(self):
|
||||||
|
course, batch = self.new_course_batch()
|
||||||
|
member = self.new_user("test01@test.com")
|
||||||
|
membership = self.add_membership(batch.name, member.name)
|
||||||
|
|
||||||
|
assert membership.course == course.name
|
||||||
|
assert membership.member_name == member.full_name
|
||||||
|
|
||||||
|
def test_membership_change_role(self):
|
||||||
|
course, batch = self.new_course_batch()
|
||||||
|
member = self.new_user("test01@test.com")
|
||||||
|
membership = self.add_membership(batch.name, member.name)
|
||||||
|
|
||||||
|
# it should be possible to change role
|
||||||
|
membership.role = "Admin"
|
||||||
|
membership.save()
|
||||||
|
|||||||
@@ -150,6 +150,13 @@ class LMSCourse(Document):
|
|||||||
"name")
|
"name")
|
||||||
return lesson_name and frappe.get_doc("Lesson", lesson_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 get_outline(self):
|
def get_outline(self):
|
||||||
return CourseOutline(self)
|
return CourseOutline(self)
|
||||||
|
|
||||||
|
|||||||
@@ -84,49 +84,16 @@
|
|||||||
{{ super() }}
|
{{ super() }}
|
||||||
{{ LiveCodeEditorJS() }}
|
{{ LiveCodeEditorJS() }}
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
<!-- <script type="text/javascript">
|
|
||||||
$(function() {
|
$(function() {
|
||||||
var editorLookup = {};
|
var batch_name = "{{ batch.name }}";
|
||||||
|
var lesson_name = "{{ lesson.name }}";
|
||||||
|
|
||||||
$(".canvas-editor").each((i, e) => {
|
frappe.call("community.lms.api.save_current_lesson", {
|
||||||
var data = $(e).data();
|
"batch_name": batch_name,
|
||||||
var editor = new LiveCodeEditor(e, {
|
"lesson_name": lesson_name
|
||||||
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> -->
|
</script>
|
||||||
|
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ def get_context(context):
|
|||||||
batch_name = context.batch.name
|
batch_name = context.batch.name
|
||||||
|
|
||||||
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"
|
index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1"
|
||||||
|
frappe.local.flags.redirect_location = get_learn_url(course_name, batch_name, index_)
|
||||||
raise frappe.Redirect
|
raise frappe.Redirect
|
||||||
|
|
||||||
context.lesson = context.course.get_lesson(chapter_index, lesson_index)
|
context.lesson = context.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
|
||||||
print(context.lesson)
|
|
||||||
outline = context.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)
|
||||||
@@ -29,3 +30,9 @@ 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_lesson_index(course, batch, user):
|
||||||
|
lesson = batch.get_current_lesson(user)
|
||||||
|
return lesson and course.get_lesson_index(lesson)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ def get_common_context(context):
|
|||||||
return
|
return
|
||||||
|
|
||||||
batch = course.get_batch(batch_name)
|
batch = course.get_batch(batch_name)
|
||||||
if not batch:
|
if not batch or not batch.is_member(frappe.session.user):
|
||||||
frappe.local.flags.redirect_location = "/courses/" + course_name
|
frappe.local.flags.redirect_location = "/courses/" + course_name
|
||||||
raise frappe.Redirect
|
raise frappe.Redirect
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user