feat: review card style
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,3 +5,6 @@
|
|||||||
tags
|
tags
|
||||||
community/docs/current
|
community/docs/current
|
||||||
community/public/dist
|
community/public/dist
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|||||||
@@ -165,7 +165,8 @@ whitelist = [
|
|||||||
"/add-a-new-batch",
|
"/add-a-new-batch",
|
||||||
"/new-sign-up",
|
"/new-sign-up",
|
||||||
"/message",
|
"/message",
|
||||||
"/about"
|
"/about",
|
||||||
|
"/lms-course-review"
|
||||||
]
|
]
|
||||||
whitelist_rules = [{"from_route": p, "to_route": p[1:]} for p in whitelist]
|
whitelist_rules = [{"from_route": p, "to_route": p[1:]} for p in whitelist]
|
||||||
|
|
||||||
@@ -187,6 +188,10 @@ update_website_context = 'community.widgets.update_website_context'
|
|||||||
## subclass of community.community.plugins.PageExtension
|
## subclass of community.community.plugins.PageExtension
|
||||||
# community_lesson_page_extension = None
|
# community_lesson_page_extension = None
|
||||||
|
|
||||||
|
community_lesson_page_extensions = [
|
||||||
|
"community.plugins.LiveCodeExtension"
|
||||||
|
]
|
||||||
|
|
||||||
## Markdown Macros for Lessons
|
## Markdown Macros for Lessons
|
||||||
community_markdown_macro_renderers = {
|
community_markdown_macro_renderers = {
|
||||||
"Exercise": "community.plugins.exercise_renderer",
|
"Exercise": "community.plugins.exercise_renderer",
|
||||||
|
|||||||
@@ -6,12 +6,17 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"exercise",
|
"exercise",
|
||||||
"solution",
|
"status",
|
||||||
|
"batch",
|
||||||
|
"column_break_4",
|
||||||
"exercise_title",
|
"exercise_title",
|
||||||
"course",
|
"course",
|
||||||
"batch",
|
|
||||||
"lesson",
|
"lesson",
|
||||||
"image"
|
"section_break_8",
|
||||||
|
"solution",
|
||||||
|
"image",
|
||||||
|
"test_results",
|
||||||
|
"comments"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -21,12 +26,6 @@
|
|||||||
"label": "Exercise",
|
"label": "Exercise",
|
||||||
"options": "Exercise"
|
"options": "Exercise"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "solution",
|
|
||||||
"fieldtype": "Code",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Solution"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fetch_from": "exercise.title",
|
"fetch_from": "exercise.title",
|
||||||
"fieldname": "exercise_title",
|
"fieldname": "exercise_title",
|
||||||
@@ -61,11 +60,41 @@
|
|||||||
"fieldtype": "Code",
|
"fieldtype": "Code",
|
||||||
"label": "Image",
|
"label": "Image",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Correct\nIncorrect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "test_results",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Test Results"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "comments",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Comments"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "solution",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Solution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_4",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_8",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-05-21 11:28:45.833018",
|
"modified": "2021-06-24 16:22:50.570845",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "Exercise Submission",
|
"name": "Exercise Submission",
|
||||||
|
|||||||
@@ -199,7 +199,12 @@ class LMSCourse(Document):
|
|||||||
}
|
}
|
||||||
if batch:
|
if batch:
|
||||||
filters["batch"] = batch
|
filters["batch"] = batch
|
||||||
membership = frappe.db.get_value("LMS Batch Membership", filters, ["name","batch", "current_lesson"], as_dict=True)
|
|
||||||
|
membership = frappe.db.get_value("LMS Batch Membership",
|
||||||
|
filters,
|
||||||
|
["name", "batch", "current_lesson", "member_type"],
|
||||||
|
as_dict=True)
|
||||||
|
|
||||||
if membership and membership.batch:
|
if membership and membership.batch:
|
||||||
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
|
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
|
||||||
return membership
|
return membership
|
||||||
@@ -244,6 +249,32 @@ class LMSCourse(Document):
|
|||||||
def get_tags(self):
|
def get_tags(self):
|
||||||
return self.tags.split(",") if self.tags else []
|
return self.tags.split(",") if self.tags else []
|
||||||
|
|
||||||
|
def get_reviews(self):
|
||||||
|
reviews = frappe.get_all("LMS Course Review",
|
||||||
|
{
|
||||||
|
"course": self.name
|
||||||
|
},
|
||||||
|
["review", "rating", "owner"],
|
||||||
|
order_by= "creation desc")
|
||||||
|
|
||||||
|
for review in reviews:
|
||||||
|
review.owner_details = frappe.get_doc("User", review.owner)
|
||||||
|
|
||||||
|
return reviews
|
||||||
|
|
||||||
|
def is_eligible_to_review(self, membership):
|
||||||
|
""" Checks if user is eligible to review the course """
|
||||||
|
if not membership:
|
||||||
|
return False
|
||||||
|
if frappe.db.count("LMS Course Review",
|
||||||
|
{
|
||||||
|
"course": self.name,
|
||||||
|
"owner": frappe.session.user
|
||||||
|
}):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_outline(self):
|
def get_outline(self):
|
||||||
return CourseOutline(self)
|
return CourseOutline(self)
|
||||||
|
|
||||||
|
|||||||
0
community/lms/doctype/lms_course_review/__init__.py
Normal file
0
community/lms/doctype/lms_course_review/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('LMS Course Review', {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
});
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2021-06-28 13:36:36.146718",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"review",
|
||||||
|
"rating",
|
||||||
|
"course"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "review",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Review"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "rating",
|
||||||
|
"fieldtype": "Rating",
|
||||||
|
"label": "Rating"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Course",
|
||||||
|
"options": "LMS Course"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-06-28 15:00:35.146196",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "LMS Course Review",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
@@ -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 LMSCourseReview(Document):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestLMSCourseReview(unittest.TestCase):
|
||||||
|
pass
|
||||||
64
community/lms/widgets/Reviews.html
Normal file
64
community/lms/widgets/Reviews.html
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<div>
|
||||||
|
<div class="reviews-heading">
|
||||||
|
<div class="reviews-title mb-5">Review</div>
|
||||||
|
{% if course.is_eligible_to_review(membership) %}
|
||||||
|
<a href="/lms-course-review?new=1">
|
||||||
|
Provide your Feedback
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% for review in course.get_reviews() %}
|
||||||
|
<div class="common-card-style review-card">
|
||||||
|
<div class="review-content"> {{ review.review }} </div>
|
||||||
|
<div class="card-divider"></div>
|
||||||
|
<div class="review-card-footer">
|
||||||
|
<div>
|
||||||
|
{{ widgets.Avatar(member=review.owner_details, avatar_class="avatar-small") }}
|
||||||
|
<span class="course-instructor">
|
||||||
|
{{ course.get_instructor().full_name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="rating pull-right">
|
||||||
|
{% for i in [1, 2, 3, 4, 5] %}
|
||||||
|
<svg class="icon icon-md {% if i <= review.rating %} star-click {% endif %}" data-rating="{{ i }}">
|
||||||
|
<use href="#icon-star"></use>
|
||||||
|
</svg>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="review-modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Modal title</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<iframe src="/lms-course-review?new=1"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary">Save changes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
frappe.ready(() => {
|
||||||
|
frappe.provide('frappe.ui');
|
||||||
|
console.log(frappe.ui)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
@@ -67,6 +67,25 @@ class ProfileTab:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
class LiveCodeExtension(PageExtension):
|
||||||
|
def render_header(self):
|
||||||
|
livecode_url = frappe.get_value("LMS Settings", None, "livecode_url")
|
||||||
|
context = {
|
||||||
|
"livecode_url": livecode_url
|
||||||
|
}
|
||||||
|
return frappe.render_template(
|
||||||
|
"templates/livecode/extension_header.html",
|
||||||
|
context)
|
||||||
|
|
||||||
|
def render_footer(self):
|
||||||
|
livecode_url = frappe.get_value("LMS Settings", None, "livecode_url")
|
||||||
|
context = {
|
||||||
|
"livecode_url": livecode_url
|
||||||
|
}
|
||||||
|
return frappe.render_template(
|
||||||
|
"templates/livecode/extension_footer.html",
|
||||||
|
context)
|
||||||
|
|
||||||
def quiz_renderer(quiz_name):
|
def quiz_renderer(quiz_name):
|
||||||
quiz = frappe.get_doc("LMS Quiz", quiz_name)
|
quiz = frappe.get_doc("LMS Quiz", quiz_name)
|
||||||
context = dict(quiz=quiz)
|
context = dict(quiz=quiz)
|
||||||
|
|||||||
@@ -296,10 +296,13 @@ input[type=checkbox] {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
background: #FFFFFF;
|
background: #FFFFFF;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 5px 10px rgb(0 0 0 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-card {
|
||||||
width: 352px;
|
width: 352px;
|
||||||
height: 380px;
|
height: 380px;
|
||||||
margin: 0px 16px 32px;
|
margin: 0px 16px 32px;
|
||||||
box-shadow: 0px 5px 10px rgb(0 0 0 / 10%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@@ -317,13 +320,14 @@ input[type=checkbox] {
|
|||||||
.course-card-meta {
|
.course-card-meta {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 135%;
|
line-height: 135%;
|
||||||
margin: 12px 16px 8px;
|
margin: 16px 0px 8px;
|
||||||
color: var(--muted-text);
|
color: var(--muted-text);
|
||||||
height: 15px;
|
height: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.course-card-content {
|
.course-card-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: 0px 24px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.course-card-title {
|
.course-card-title {
|
||||||
@@ -333,17 +337,17 @@ input[type=checkbox] {
|
|||||||
letter-spacing: -0.014em;
|
letter-spacing: -0.014em;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
margin: 0px 16px 16px;
|
margin-bottom: 16px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-divider {
|
.card-divider {
|
||||||
border: 1px solid #E2E6E9;
|
border: 1px solid #E2E6E9;
|
||||||
margin: 0px 16px 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.course-card-meta-2 {
|
.course-card-meta-2 {
|
||||||
margin: 0px 16px 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.course-instructor {
|
.course-instructor {
|
||||||
@@ -362,7 +366,6 @@ input[type=checkbox] {
|
|||||||
|
|
||||||
.view-course-link {
|
.view-course-link {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
margin: 0px 16px 16px;
|
|
||||||
background: var(--button-background);
|
background: var(--button-background);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -432,3 +435,32 @@ input[type=checkbox] {
|
|||||||
.small-margin {
|
.small-margin {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reviews-heading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reviews-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 145%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-card {
|
||||||
|
width: 300px;
|
||||||
|
height: 200px;
|
||||||
|
margin: 0px 16px 32px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-card-footer {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-content {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 135%;
|
||||||
|
}
|
||||||
|
|||||||
168
community/templates/livecode/extension_footer.html
Normal file
168
community/templates/livecode/extension_footer.html
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<script type="text/javascript" src="/assets/frappe/node_modules/moment/min/moment-with-locales.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/assets/frappe/node_modules/moment-timezone/builds/moment-timezone-with-data.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/assets/frappe/js/frappe/utils/datetime.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
// comment_when is failing because of this
|
||||||
|
if (!frappe.sys_defaults) {
|
||||||
|
frappe.sys_defaults = {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="{{ livecode_url }}/static/livecode.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/assets/mon_school/js/livecode-files.js"></script>
|
||||||
|
|
||||||
|
<template id="livecode-template">
|
||||||
|
<div class="livecode-editor livecode-editor-inline">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8 col-md-6">
|
||||||
|
<div class="controls">
|
||||||
|
<button class="run">Run</button>
|
||||||
|
|
||||||
|
<div class="exercise-controls pull-right">
|
||||||
|
<span style="padding-right: 10px;"><span class="last-submitted human-time" data-timestamp=""></span></span>
|
||||||
|
<button class="submit btn-primary">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="code-editor">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8 col-md-6">
|
||||||
|
<div class="code-wrapper">
|
||||||
|
<textarea class="code"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4 col-md-6 canvas-wrapper">
|
||||||
|
<div class="svg-image" width="300" height="300"></div>
|
||||||
|
<pre class="output"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function getLiveCodeOptions() {
|
||||||
|
return {
|
||||||
|
base_url: "{{ livecode_url }}",
|
||||||
|
runtime: "python",
|
||||||
|
files: LIVECODE_FILES, // loaded from livecode-files.js
|
||||||
|
command: ["python", "start.py"],
|
||||||
|
codemirror: true,
|
||||||
|
onMessage: {
|
||||||
|
image: function(editor, msg) {
|
||||||
|
const element = editor.parent.querySelector(".svg-image");
|
||||||
|
element.innerHTML = msg.image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
var editorLookup = {};
|
||||||
|
|
||||||
|
$("pre.example, pre.exercise").each((i, e) => {
|
||||||
|
var code = $(e).text();
|
||||||
|
var template = document.querySelector('#livecode-template');
|
||||||
|
var clone = template.content.cloneNode(true);
|
||||||
|
|
||||||
|
$(e)
|
||||||
|
.wrap('<div></div>')
|
||||||
|
.hide()
|
||||||
|
.parent()
|
||||||
|
.append(clone)
|
||||||
|
.find("textarea.code")
|
||||||
|
.val(code);
|
||||||
|
|
||||||
|
if ($(e).hasClass("exercise")) {
|
||||||
|
var last_submitted = $(e).data("last-submitted");
|
||||||
|
if (last_submitted) {
|
||||||
|
$(e).parent().find(".last-submitted")
|
||||||
|
.data("timestamp", last_submitted)
|
||||||
|
.html(__("Submitted {0}", [comment_when(last_submitted)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$(e).parent().find(".exercise-controls").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
var editor = new LiveCodeEditor(e.parentElement, {
|
||||||
|
...getLiveCodeOptions(),
|
||||||
|
codemirror: true,
|
||||||
|
onMessage: {
|
||||||
|
image: function(editor, msg) {
|
||||||
|
const canvasElement = editor.parent.querySelector("div.svg-image");
|
||||||
|
canvasElement.innerHTML = msg.image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(e).parent().find(".submit").on('click', function() {
|
||||||
|
var name = $(e).data("name");
|
||||||
|
let code = editor.codemirror.doc.getValue();
|
||||||
|
|
||||||
|
frappe.call("community.lms.api.submit_solution", {
|
||||||
|
"exercise": name,
|
||||||
|
"code": code
|
||||||
|
}).then(r => {
|
||||||
|
if (r.message.name) {
|
||||||
|
frappe.msgprint("Submitted successfully!");
|
||||||
|
|
||||||
|
let d = r.message.creation;
|
||||||
|
$(e).parent().find(".human-time").html(__("Submitted {0}", [comment_when(d)]));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".exercise-image").each((i, e) => {
|
||||||
|
var svg = JSON.parse($(e).data("image"));
|
||||||
|
$(e).html(svg);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("pre.exercise").each((i, e) => {
|
||||||
|
var svg = JSON.parse($(e).data("image"));
|
||||||
|
$(e).parent().find(".svg-image").html(svg);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
.svg-image {
|
||||||
|
border: 5px solid #ddd;
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
width: 310px;
|
||||||
|
height: 310px;
|
||||||
|
}
|
||||||
|
.livecode-editor {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.livecode-editor-small .svg-image {
|
||||||
|
border: 5px solid #ddd;
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
width: 210px;
|
||||||
|
height: 210px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* work-in-progress styles for showing admonition */
|
||||||
|
.admonition {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-left: .5rem solid #888;
|
||||||
|
border-radius: .3em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin: 1.5em 0;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
}
|
||||||
|
.admonition-title {
|
||||||
|
padding: 0.5em 0px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-top:
|
||||||
|
}
|
||||||
|
</style>
|
||||||
8
community/templates/livecode/extension_header.html
Normal file
8
community/templates/livecode/extension_header.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<link rel="stylesheet" href="{{ livecode_url }}/static/codemirror/lib/codemirror.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>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<h2 id="course-title" data-course="{{course.name}}">{{course.title}}</h2>
|
<h2 id="course-title" data-course="{{course.name}}">{{course.title}}</h2>
|
||||||
{% if not course.disable_self_learning and not course.is_mentor(frappe.session.user) %}
|
{% if not course.disable_self_learning and not membership %}
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-primary join-batch" data-course="{{ course.name | urlencode }}"> Start Learning </button>
|
<button class="btn btn-primary join-batch" data-course="{{ course.name | urlencode }}"> Start Learning </button>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
{{ widgets.InstructorSection(instructor=course.get_instructor()) }}
|
{{ widgets.InstructorSection(instructor=course.get_instructor()) }}
|
||||||
{{ BatchSection(course) }}
|
{{ BatchSection(course) }}
|
||||||
{{ widgets.CourseOutline(course=course, show_link=membership) }}
|
{{ widgets.CourseOutline(course=course, show_link=membership) }}
|
||||||
|
{{ widgets.Reviews(course=course, membership=membership) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ def get_context(context):
|
|||||||
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
|
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
|
||||||
context.membership = membership
|
context.membership = membership
|
||||||
if not course.is_mentor(frappe.session.user) and membership:
|
if not course.is_mentor(frappe.session.user) and membership:
|
||||||
frappe.local.flags.redirect_location = f"/courses/{course.name}/learn"
|
""" frappe.local.flags.redirect_location = f"/courses/{course.name}/learn"
|
||||||
raise frappe.Redirect
|
raise frappe.Redirect """
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
|
|
||||||
{% macro course_card(course) %}
|
{% macro course_card(course) %}
|
||||||
<div class="common-card-style">
|
<div class="common-card-style course-card">
|
||||||
<div class="course-image" style="background-image: url({{ course.image }});">
|
<div class="course-image" style="background-image: url({{ course.image }});">
|
||||||
<div class="course-tags">
|
<div class="course-tags">
|
||||||
{% for tag in course.get_tags() %}
|
{% for tag in course.get_tags() %}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
{% macro LiveCodeEditorLarge(name, code) %}
|
{% macro LiveCodeEditorLarge(name, code) %}
|
||||||
<div class="livecode-editor livecode-editor-large" id="editor-{{name}}">
|
<div class="livecode-editor livecode-editor-large" id="editor-{{name}}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
Reference in New Issue
Block a user