From 480774755fc6b3581aeef8c7822154da509059ce Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 9 Mar 2021 11:50:45 +0000 Subject: [PATCH 01/12] Moved the css to assets --- community/public/build.json | 5 +++ community/public/css/lms.css | 57 +++++++++++++++++++++++++ community/www/courses/topic.html | 71 +------------------------------- 3 files changed, 63 insertions(+), 70 deletions(-) create mode 100644 community/public/build.json create mode 100644 community/public/css/lms.css diff --git a/community/public/build.json b/community/public/build.json new file mode 100644 index 00000000..14ac7230 --- /dev/null +++ b/community/public/build.json @@ -0,0 +1,5 @@ +{ + "css/lms.css": [ + "public/css/lms.css" + ] +} diff --git a/community/public/css/lms.css b/community/public/css/lms.css new file mode 100644 index 00000000..67b08a94 --- /dev/null +++ b/community/public/css/lms.css @@ -0,0 +1,57 @@ + +.canvas-wrapper { + position: relative; + padding: 10px; +} + +.canvas-editor canvas { + position: relative; + z-index: 0; + border: 1px solid #ddd; + height: 300px; + width: 300px; +} +.canvas-wrapper .output { + position: absolute; + z-index: 1; + width: 100%; + left: 0px; + top: 0px; + background-color: rgba(255, 255, 255, 0); + margin: 15px; +} + +.canvas-editor .code { + width: 100%; + padding: 5px; + min-height: 330px; + resize: none; +} +.canvas-editor .output { + padding: 5px; +} + +.heading { + background: #eee; + padding: 10px; + clear: both; + color: #212529; + border: 1px solid #ddd; +} + +.canvas-editor h2 { + font-size: 1.2em; + text-transform: uppercase; + margin: 0px; + font-weight: normal; +} + +.canvas-editor .run { + float: right; +} + +.canvas-editor .col-sm { + border: 1px solid #ddd; +} + + diff --git a/community/www/courses/topic.html b/community/www/courses/topic.html index b527a444..040b5a1c 100644 --- a/community/www/courses/topic.html +++ b/community/www/courses/topic.html @@ -7,6 +7,7 @@ + @@ -89,73 +90,3 @@ }) {%- endblock %} - -{%- block style %} - {{ super() }} - - -{%- endblock %} From 13030b0b9b6e507913f3b326567b0a1d6e6f65f6 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 9 Mar 2021 11:51:09 +0000 Subject: [PATCH 02/12] Cleanup: moved LiveCodeEditor into a macro --- community/www/courses/topic.html | 25 ++----------------------- community/www/macros/livecode.html | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 community/www/macros/livecode.html diff --git a/community/www/courses/topic.html b/community/www/courses/topic.html index 040b5a1c..0f3280cc 100644 --- a/community/www/courses/topic.html +++ b/community/www/courses/topic.html @@ -1,4 +1,5 @@ {% extends "templates/base.html" %} +{% from "www/macros/livecode.html" import LiveCodeEditor %} {% block title %}{{topic.title}} ({{course.title}}){% endblock %} {% block head_include %} @@ -43,7 +44,7 @@ {% if s.type == "text" %} {{ render_section_text(s) }} {% elif s.type == "code" %} - {{ render_section_code(s) }} + {{ LiveCodeEditor(s.name, s.code) }} {% else %}
Unknown section type: {{s.type}}
{% endif %} @@ -53,28 +54,6 @@ {{ s.contents | markdown }} {% endmacro %} -{% macro render_section_code(s) %} -
-
-
- -

Editor

-
- -
-
-
-

Output

-
-
- Dashed box -

-    
- -
-
-{% endmacro %} - {%- block script %} {{ super() }} diff --git a/community/www/macros/livecode.html b/community/www/macros/livecode.html new file mode 100644 index 00000000..8d6b99d3 --- /dev/null +++ b/community/www/macros/livecode.html @@ -0,0 +1,22 @@ + +{% macro LiveCodeEditor(name, code) %} +
+
+
+ +

Editor

+
+ +
+
+
+

Output

+
+
+ +

+    
+ +
+
+{% endmacro %} From 3583cd084d564af2994963e966ec5f0b2bcce023 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 9 Mar 2021 12:34:48 +0000 Subject: [PATCH 03/12] Fixed error on topic pages The macro was imported wihout "with context" flag. --- community/www/courses/topic.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community/www/courses/topic.html b/community/www/courses/topic.html index 0f3280cc..0cc30d38 100644 --- a/community/www/courses/topic.html +++ b/community/www/courses/topic.html @@ -1,5 +1,5 @@ {% extends "templates/base.html" %} -{% from "www/macros/livecode.html" import LiveCodeEditor %} +{% from "www/macros/livecode.html" import LiveCodeEditor with context %} {% block title %}{{topic.title}} ({{course.title}}){% endblock %} {% block head_include %} From ff15e7058bbc7042e9629bb5f5c5b732739b33b5 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 9 Mar 2021 12:36:41 +0000 Subject: [PATCH 04/12] Added sketch doctype. --- community/lms/doctype/lms_sketch/__init__.py | 0 .../lms/doctype/lms_sketch/lms_sketch.js | 8 +++ .../lms/doctype/lms_sketch/lms_sketch.json | 55 +++++++++++++++++++ .../lms/doctype/lms_sketch/lms_sketch.py | 10 ++++ .../lms/doctype/lms_sketch/test_lms_sketch.py | 10 ++++ 5 files changed, 83 insertions(+) create mode 100644 community/lms/doctype/lms_sketch/__init__.py create mode 100644 community/lms/doctype/lms_sketch/lms_sketch.js create mode 100644 community/lms/doctype/lms_sketch/lms_sketch.json create mode 100644 community/lms/doctype/lms_sketch/lms_sketch.py create mode 100644 community/lms/doctype/lms_sketch/test_lms_sketch.py diff --git a/community/lms/doctype/lms_sketch/__init__.py b/community/lms/doctype/lms_sketch/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/community/lms/doctype/lms_sketch/lms_sketch.js b/community/lms/doctype/lms_sketch/lms_sketch.js new file mode 100644 index 00000000..020c5909 --- /dev/null +++ b/community/lms/doctype/lms_sketch/lms_sketch.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, FOSS United and contributors +// For license information, please see license.txt + +frappe.ui.form.on('LMS Sketch', { + // refresh: function(frm) { + + // } +}); diff --git a/community/lms/doctype/lms_sketch/lms_sketch.json b/community/lms/doctype/lms_sketch/lms_sketch.json new file mode 100644 index 00000000..a3a6d9f7 --- /dev/null +++ b/community/lms/doctype/lms_sketch/lms_sketch.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "autoname": "format:SKETCH-{#}", + "creation": "2021-03-09 16:31:50.523524", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "runtime", + "code" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title" + }, + { + "fieldname": "runtime", + "fieldtype": "Data", + "label": "Runtime" + }, + { + "fieldname": "code", + "fieldtype": "Code", + "label": "Code" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-03-09 16:34:12.200724", + "modified_by": "Administrator", + "module": "LMS", + "name": "LMS Sketch", + "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, + "track_views": 1 +} \ No newline at end of file diff --git a/community/lms/doctype/lms_sketch/lms_sketch.py b/community/lms/doctype/lms_sketch/lms_sketch.py new file mode 100644 index 00000000..9bf57313 --- /dev/null +++ b/community/lms/doctype/lms_sketch/lms_sketch.py @@ -0,0 +1,10 @@ +# -*- 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 LMSSketch(Document): + pass diff --git a/community/lms/doctype/lms_sketch/test_lms_sketch.py b/community/lms/doctype/lms_sketch/test_lms_sketch.py new file mode 100644 index 00000000..b07262a6 --- /dev/null +++ b/community/lms/doctype/lms_sketch/test_lms_sketch.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, FOSS United and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestLMSSketch(unittest.TestCase): + pass From deea539c7d59db8fbae1fd0730635e1a177a2e84 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 9 Mar 2021 12:36:55 +0000 Subject: [PATCH 05/12] Added sketch pages. --- community/public/css/lms.css | 7 +++++ community/www/macros/livecode.html | 16 +++++++++++ community/www/sketches/index.html | 26 ++++++++++++++++++ community/www/sketches/index.py | 16 +++++++++++ community/www/sketches/sketch.html | 44 ++++++++++++++++++++++++++++++ community/www/sketches/sketch.py | 27 ++++++++++++++++++ 6 files changed, 136 insertions(+) create mode 100644 community/www/sketches/index.html create mode 100644 community/www/sketches/index.py create mode 100644 community/www/sketches/sketch.html create mode 100644 community/www/sketches/sketch.py diff --git a/community/public/css/lms.css b/community/public/css/lms.css index 67b08a94..590f8160 100644 --- a/community/public/css/lms.css +++ b/community/public/css/lms.css @@ -54,4 +54,11 @@ border: 1px solid #ddd; } +.sketch-header h1 { + font-size: 1.5em; + margin-bottom: 0px; +} +.sketch-header { + margin-bottom: 1em; +} diff --git a/community/www/macros/livecode.html b/community/www/macros/livecode.html index 8d6b99d3..21715887 100644 --- a/community/www/macros/livecode.html +++ b/community/www/macros/livecode.html @@ -20,3 +20,19 @@ {% endmacro %} + + +{% macro LiveCodeEditorJS(name, code) %} + + +{% endmacro %} diff --git a/community/www/sketches/index.html b/community/www/sketches/index.html new file mode 100644 index 00000000..167fb0de --- /dev/null +++ b/community/www/sketches/index.html @@ -0,0 +1,26 @@ +{% extends "templates/base.html" %} +{% from "www/macros/livecode.html" import LiveCodeEditor, LiveCodeEditorJS %} + +{% block title %}Sketches{% endblock %} +{% block head_include %} + + + +{% endblock %} + +{% block content %} +
+
+

Recent Sketches

+
+
+ {% for sketch in sketches %} +
+ {{ sketch.modified }} + {{sketch.title}} + By {{sketch.owner}} +
+ {% endfor %} +
+
+{% endblock %} diff --git a/community/www/sketches/index.py b/community/www/sketches/index.py new file mode 100644 index 00000000..156604e9 --- /dev/null +++ b/community/www/sketches/index.py @@ -0,0 +1,16 @@ +import frappe + +def get_context(context): + context.no_cache = 1 + context.sketches = get_sketches() + +def get_sketches(): + sketches = frappe.get_all( + "LMS Sketch", + fields=['name', 'title', 'owner', 'modified'], + order_by='modified desc', + page_length=100 + ) + for s in sketches: + s['owner'] = s['owner'].split("@")[0] + return sketches diff --git a/community/www/sketches/sketch.html b/community/www/sketches/sketch.html new file mode 100644 index 00000000..57b08c53 --- /dev/null +++ b/community/www/sketches/sketch.html @@ -0,0 +1,44 @@ +{% extends "templates/base.html" %} +{% from "www/macros/livecode.html" import LiveCodeEditor, LiveCodeEditorJS with context %} + +{% block title %}{{sketch.title}}{% endblock %} +{% block head_include %} + + + + + + + + + + + + + +{% endblock %} + +{% block content %} +
+
+ + +
+

{{ sketch.title }}

+
By {{sketch.owner}}
+
+ + {{LiveCodeEditor(sketch.name, sketch.code) }} +
+{% endblock %} + +{%- block script %} + {{ super() }} + {{ LiveCodeEditorJS() }} +{%- endblock %} + diff --git a/community/www/sketches/sketch.py b/community/www/sketches/sketch.py new file mode 100644 index 00000000..68750024 --- /dev/null +++ b/community/www/sketches/sketch.py @@ -0,0 +1,27 @@ +import frappe + +def get_context(context): + context.no_cache = 1 + course_name = get_queryparam("sketch", '/sketches') + context.sketch = get_sketch(course_name) + context.livecode_url = get_livecode_url() + +def get_livecode_url(): + doc = frappe.get_doc("LMS Settings") + return doc.livecode_url + +def get_queryparam(name, redirect_when_not_found): + try: + return frappe.form_dict[name] + except KeyError: + frappe.local.flags.redirect_location = redirect_when_not_found + raise frappe.Redirect + +def get_sketch(name): + try: + sketch = frappe.get_doc('LMS Sketch', name) + except frappe.exceptions.DoesNotExistError: + raise frappe.NotFound + + sketch.owner = sketch.owner.split("@")[0] + return sketch From 936a3fcfffd5516035223470b7910375a3076a9a Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 9 Mar 2021 12:53:11 +0000 Subject: [PATCH 06/12] Added partial support for new sketches. New Sketches can't be saved yet. --- community/www/sketches/index.html | 2 ++ community/www/sketches/sketch.html | 6 ++++++ community/www/sketches/sketch.py | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/community/www/sketches/index.html b/community/www/sketches/index.html index 167fb0de..c9db0f4d 100644 --- a/community/www/sketches/index.html +++ b/community/www/sketches/index.html @@ -12,6 +12,8 @@

Recent Sketches

+ + Create a New Sketch
{% for sketch in sketches %} diff --git a/community/www/sketches/sketch.html b/community/www/sketches/sketch.html index 57b08c53..98a9274a 100644 --- a/community/www/sketches/sketch.html +++ b/community/www/sketches/sketch.html @@ -33,6 +33,12 @@
By {{sketch.owner}}
+ {% if not sketch.name %} +
+ Saving sketches is not yet implemented. Coming soon! +
+ {% endif %} + {{LiveCodeEditor(sketch.name, sketch.code) }}
{% endblock %} diff --git a/community/www/sketches/sketch.py b/community/www/sketches/sketch.py index 68750024..691f55d0 100644 --- a/community/www/sketches/sketch.py +++ b/community/www/sketches/sketch.py @@ -18,6 +18,13 @@ def get_queryparam(name, redirect_when_not_found): raise frappe.Redirect def get_sketch(name): + if name == 'new': + sketch = frappe.new_doc('LMS Sketch') + sketch.title = "New Sketch" + sketch.code = "circle(100, 100, 50)" + sketch.owner = frappe.session.user.split("@")[0] + return sketch + try: sketch = frappe.get_doc('LMS Sketch', name) except frappe.exceptions.DoesNotExistError: From 406244ff69635de5de51160156a6ea9c40f8cd84 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Wed, 10 Mar 2021 12:25:14 +0000 Subject: [PATCH 07/12] Added the ability to save sketches Implemented by exposing an RPC method to save the sketch and calling from JS using `frappe.call`. Any logged-in user can create new sketches and the owner can edit his/her own sketches. --- .../lms/doctype/lms_sketch/lms_sketch.py | 33 +++++++- community/www/macros/livecode.html | 3 + community/www/sketches/sketch.html | 75 +++++++++++++++++-- community/www/sketches/sketch.py | 15 +++- 4 files changed, 112 insertions(+), 14 deletions(-) diff --git a/community/lms/doctype/lms_sketch/lms_sketch.py b/community/lms/doctype/lms_sketch/lms_sketch.py index 9bf57313..29742edb 100644 --- a/community/lms/doctype/lms_sketch/lms_sketch.py +++ b/community/lms/doctype/lms_sketch/lms_sketch.py @@ -3,8 +3,37 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe +import frappe from frappe.model.document import Document class LMSSketch(Document): - pass + def get_owner_name(self): + return self.owner.split("@")[0] + +@frappe.whitelist() +def save_sketch(name, title, code): + if not name or name == "new": + doc = frappe.new_doc('LMS Sketch') + doc.title = title + doc.code = code + doc.runtime = 'python-canvas' + doc.insert() + status = "created" + else: + doc = frappe.get_doc("LMS Sketch", name) + + if doc.owner != frappe.session.user: + return { + "ok": False, + "error": "Permission Denied" + } + doc.title = title + doc.code = code + doc.save() + status = "updated" + return { + "ok": True, + "status": status, + "name": doc.name, + } + diff --git a/community/www/macros/livecode.html b/community/www/macros/livecode.html index 21715887..6d9feff9 100644 --- a/community/www/macros/livecode.html +++ b/community/www/macros/livecode.html @@ -25,6 +25,8 @@ {% macro LiveCodeEditorJS(name, code) %} diff --git a/community/www/sketches/sketch.html b/community/www/sketches/sketch.html index 98a9274a..a0509ef1 100644 --- a/community/www/sketches/sketch.html +++ b/community/www/sketches/sketch.html @@ -29,22 +29,81 @@
-

{{ sketch.title }}

-
By {{sketch.owner}}
+ {% if editable %} + +

+ +

+ {% else %} +

{{sketch.title}}

+ {% endif %} +
By {{sketch.get_owner_name()}}
- {% if not sketch.name %} -
- Saving sketches is not yet implemented. Coming soon! -
- {% endif %} + {% if sketch.is_new() and not editable %} +
+ Please login to save this sketch. +
+ {% endif %} - {{LiveCodeEditor(sketch.name, sketch.code) }} +
+ {{LiveCodeEditor(sketch.name, sketch.code) }} +
{% endblock %} {%- block script %} {{ super() }} {{ LiveCodeEditorJS() }} + + {%- endblock %} diff --git a/community/www/sketches/sketch.py b/community/www/sketches/sketch.py index 691f55d0..4991e185 100644 --- a/community/www/sketches/sketch.py +++ b/community/www/sketches/sketch.py @@ -5,6 +5,15 @@ def get_context(context): course_name = get_queryparam("sketch", '/sketches') context.sketch = get_sketch(course_name) context.livecode_url = get_livecode_url() + context.editable = is_editable(context.sketch, frappe.sesson.user) + +def is_editable(sketch, user): + if sketch.name == "new": + # new sketches can be editable by any logged in user + return user != "Guest" + else: + # existing sketches are editable by the owner + return sketch.owner == user def get_livecode_url(): doc = frappe.get_doc("LMS Settings") @@ -20,15 +29,13 @@ def get_queryparam(name, redirect_when_not_found): def get_sketch(name): if name == 'new': sketch = frappe.new_doc('LMS Sketch') + sketch.name = "new" sketch.title = "New Sketch" sketch.code = "circle(100, 100, 50)" - sketch.owner = frappe.session.user.split("@")[0] return sketch try: - sketch = frappe.get_doc('LMS Sketch', name) + return frappe.get_doc('LMS Sketch', name) except frappe.exceptions.DoesNotExistError: raise frappe.NotFound - sketch.owner = sketch.owner.split("@")[0] - return sketch From 4fe91422f4a7cb2e5cd7a1f1087835f6b699d2f1 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Wed, 10 Mar 2021 12:35:31 +0000 Subject: [PATCH 08/12] Fixed an error in finding if the sketch is editable --- community/www/sketches/sketch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/community/www/sketches/sketch.py b/community/www/sketches/sketch.py index 4991e185..e94f76e7 100644 --- a/community/www/sketches/sketch.py +++ b/community/www/sketches/sketch.py @@ -5,10 +5,10 @@ def get_context(context): course_name = get_queryparam("sketch", '/sketches') context.sketch = get_sketch(course_name) context.livecode_url = get_livecode_url() - context.editable = is_editable(context.sketch, frappe.sesson.user) + context.editable = is_editable(context.sketch, frappe.session.user) def is_editable(sketch, user): - if sketch.name == "new": + if sketch.is_new(): # new sketches can be editable by any logged in user return user != "Guest" else: From 5f8de7612b689a336442608009e2e201d03eefd6 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Thu, 11 Mar 2021 11:53:06 +0000 Subject: [PATCH 09/12] Using the user fullname when rendering a sketch. Also refactored the portal page for sketches and moved the common code to the lms_sketch doctype module. --- .../lms/doctype/lms_sketch/lms_sketch.py | 34 ++++++++++++++++++- community/www/sketches/index.html | 2 +- community/www/sketches/index.py | 13 ++----- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/community/lms/doctype/lms_sketch/lms_sketch.py b/community/lms/doctype/lms_sketch/lms_sketch.py index 29742edb..2ef8f989 100644 --- a/community/lms/doctype/lms_sketch/lms_sketch.py +++ b/community/lms/doctype/lms_sketch/lms_sketch.py @@ -8,7 +8,7 @@ from frappe.model.document import Document class LMSSketch(Document): def get_owner_name(self): - return self.owner.split("@")[0] + return get_userinfo(self.owner)['full_name'] @frappe.whitelist() def save_sketch(name, title, code): @@ -37,3 +37,35 @@ def save_sketch(name, title, code): "name": doc.name, } +def get_recent_sketches(): + """Returns the recent sketches. + + The return value will be a list of dicts with each entry containing + the following fields: + - name + - title + - owner + - owner_name + - modified + """ + sketches = frappe.get_all( + "LMS Sketch", + fields=['name', 'title', 'owner', 'modified'], + order_by='modified desc', + page_length=100 + ) + for s in sketches: + s['owner_name'] = get_userinfo(s['owner'])['full_name'] + return sketches + +def get_userinfo(email): + """Returns the username and fullname of a user. + + Please note that the email could be "Administrator" or "Guest" + as a special case to denote the system admin and guest user respectively. + """ + user = frappe.get_doc("User", email) + return { + "full_name": user.full_name, + "username": user.username + } diff --git a/community/www/sketches/index.html b/community/www/sketches/index.html index c9db0f4d..f67ef2a7 100644 --- a/community/www/sketches/index.html +++ b/community/www/sketches/index.html @@ -20,7 +20,7 @@
{{ sketch.modified }} {{sketch.title}} - By {{sketch.owner}} + By {{sketch.owner_name}}
{% endfor %} diff --git a/community/www/sketches/index.py b/community/www/sketches/index.py index 156604e9..3ab688e1 100644 --- a/community/www/sketches/index.py +++ b/community/www/sketches/index.py @@ -1,16 +1,7 @@ import frappe +from ...lms.doctype.lms_sketch.lms_sketch import get_recent_sketches def get_context(context): context.no_cache = 1 - context.sketches = get_sketches() + context.sketches = get_recent_sketches() -def get_sketches(): - sketches = frappe.get_all( - "LMS Sketch", - fields=['name', 'title', 'owner', 'modified'], - order_by='modified desc', - page_length=100 - ) - for s in sketches: - s['owner'] = s['owner'].split("@")[0] - return sketches From 31aec86c0be26ef9769abcf45ad5cc14463adf53 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Thu, 11 Mar 2021 11:54:43 +0000 Subject: [PATCH 10/12] Fixed broken html in LiveCodeEditor macro. --- community/www/macros/livecode.html | 1 - 1 file changed, 1 deletion(-) diff --git a/community/www/macros/livecode.html b/community/www/macros/livecode.html index 6d9feff9..8035cbd7 100644 --- a/community/www/macros/livecode.html +++ b/community/www/macros/livecode.html @@ -16,7 +16,6 @@

     
-    
   
 
 {% endmacro %}

From fe63ded98c71c27197ee89b4e828e068a2692748 Mon Sep 17 00:00:00 2001
From: Anand Chitipothu 
Date: Fri, 12 Mar 2021 05:02:57 +0000
Subject: [PATCH 11/12] Added svg field to the sketch to store the image of the
 sketch.

---
 community/lms/doctype/lms_sketch/lms_sketch.json | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/community/lms/doctype/lms_sketch/lms_sketch.json b/community/lms/doctype/lms_sketch/lms_sketch.json
index a3a6d9f7..c236db10 100644
--- a/community/lms/doctype/lms_sketch/lms_sketch.json
+++ b/community/lms/doctype/lms_sketch/lms_sketch.json
@@ -8,7 +8,8 @@
  "field_order": [
   "title",
   "runtime",
-  "code"
+  "code",
+  "svg"
  ],
  "fields": [
   {
@@ -25,11 +26,17 @@
    "fieldname": "code",
    "fieldtype": "Code",
    "label": "Code"
+  },
+  {
+   "fieldname": "svg",
+   "fieldtype": "Code",
+   "label": "SVG",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2021-03-09 16:34:12.200724",
+ "modified": "2021-03-12 08:42:56.671658",
  "modified_by": "Administrator",
  "module": "LMS",
  "name": "LMS Sketch",

From dca8c9d5db3b7e7b525f6ca60dcce7b594d4ca01 Mon Sep 17 00:00:00 2001
From: Anand Chitipothu 
Date: Fri, 12 Mar 2021 05:37:24 +0000
Subject: [PATCH 12/12] Added thumbnails to the recent sketches page.

- Added an svg field to store the thumbnail
- The thumbnail is computed and cached in redis
---
 community/lms/doctype/lms_sketch/livecode.py  | 44 +++++++++++++++++++
 .../lms/doctype/lms_sketch/lms_sketch.py      | 36 ++++++++++++++-
 community/www/sketches/index.html             | 27 ++++++++++--
 requirements.txt                              |  4 +-
 4 files changed, 104 insertions(+), 7 deletions(-)
 create mode 100644 community/lms/doctype/lms_sketch/livecode.py

diff --git a/community/lms/doctype/lms_sketch/livecode.py b/community/lms/doctype/lms_sketch/livecode.py
new file mode 100644
index 00000000..1eb804ad
--- /dev/null
+++ b/community/lms/doctype/lms_sketch/livecode.py
@@ -0,0 +1,44 @@
+"""Utilities to work with livecode service.
+"""
+import websocket
+import json
+import drawSvg as draw
+
+def livecode_to_svg(livecode_ws_url, code, *, timeout=1):
+    """Renders the code as svg.
+    """
+    print("livecode_to_svg")
+    ws = websocket.WebSocket()
+    ws.settimeout(timeout)
+    ws.connect(livecode_ws_url)
+
+    msg = {
+        "msgtype": "exec",
+        "runtime": "python-canvas",
+        "code": code
+    }
+    ws.send(json.dumps(msg))
+
+    messages = _read_messages(ws)
+    commands = [m['cmd'] for m in messages if m['msgtype'] == 'draw']
+    img = draw_image(commands)
+    return img.asSvg()
+
+def _read_messages(ws):
+    messages = []
+    try:
+        while True:
+            msg = ws.recv()
+            if not msg:
+                break
+            messages.append(json.loads(msg))
+    except websocket.WebSocketTimeoutException:
+        pass
+    return messages
+
+def draw_image(commands):
+    img = draw.Drawing(300, 300, fill='none', stroke='black')
+    for c in commands:
+        if c['function'] == 'circle':
+            img.append(draw.Circle(cx=c['x'], cy=c['y'], r=c['d']/2))
+    return img
diff --git a/community/lms/doctype/lms_sketch/lms_sketch.py b/community/lms/doctype/lms_sketch/lms_sketch.py
index 2ef8f989..21257e49 100644
--- a/community/lms/doctype/lms_sketch/lms_sketch.py
+++ b/community/lms/doctype/lms_sketch/lms_sketch.py
@@ -3,13 +3,44 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
+import hashlib
+from urllib.parse import urlparse
 import frappe
 from frappe.model.document import Document
+from . import livecode
 
 class LMSSketch(Document):
     def get_owner_name(self):
         return get_userinfo(self.owner)['full_name']
 
+    def get_livecode_url(self):
+        doc = frappe.get_cached_doc("LMS Settings")
+        return doc.livecode_url
+
+    def get_livecode_ws_url(self):
+        url = urlparse(self.get_livecode_url())
+        protocol = "wss" if url.scheme == "https" else "ws"
+        return protocol + "://" + url.netloc + "/livecode"
+
+    def to_svg(self):
+        return self.svg or self.render_svg()
+
+    def render_svg(self):
+        h = hashlib.md5(self.code.encode('utf-8')).hexdigest()
+        cache = frappe.cache()
+        key = "sketch-" + h
+        value = cache.get(key)
+        if value:
+            value = value.decode('utf-8')
+        else:
+            ws_url = self.get_livecode_ws_url()
+            value = livecode.livecode_to_svg(ws_url, self.code)
+            cache.set(key, value)
+        return value
+
+    def __repr__(self):
+        return f""
+
 @frappe.whitelist()
 def save_sketch(name, title, code):
     if not name or name == "new":
@@ -29,6 +60,7 @@ def save_sketch(name, title, code):
             }
         doc.title = title
         doc.code = code
+        doc.svg = ''
         doc.save()
         status = "updated"
     return {
@@ -50,13 +82,13 @@ def get_recent_sketches():
     """
     sketches = frappe.get_all(
         "LMS Sketch",
-        fields=['name', 'title', 'owner', 'modified'],
+        fields='*',
         order_by='modified desc',
         page_length=100
     )
     for s in sketches:
         s['owner_name'] = get_userinfo(s['owner'])['full_name']
-    return sketches
+    return [frappe.get_doc(doctype='LMS Sketch', **doc) for doc in sketches]
 
 def get_userinfo(email):
     """Returns the username and fullname of a user.
diff --git a/community/www/sketches/index.html b/community/www/sketches/index.html
index f67ef2a7..5da1037e 100644
--- a/community/www/sketches/index.html
+++ b/community/www/sketches/index.html
@@ -16,13 +16,32 @@
     Create a New Sketch
 	
 	
+
{% for sketch in sketches %} -
- {{ sketch.modified }} - {{sketch.title}} - By {{sketch.owner_name}} +
+
+ + +
+
{% endfor %} +
{% endblock %} + +{% block style %} + {{super()}} + +{% endblock %} diff --git a/requirements.txt b/requirements.txt index 5ac1c812..6da7c51b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ -frappe \ No newline at end of file +frappe +websocket_client +drawSvg