Added support to split the topic description into sections.

We are using special markup to split the description into sections.

Each section is marked as:

    {{ section(type="example", id="foo") }}
    ...
    {{ end }}

This is the first-cut implementation and requires cleanup.
This commit is contained in:
Anand Chitipothu
2021-03-12 12:30:50 +00:00
parent ce7175040f
commit faf102bdec
5 changed files with 108 additions and 8 deletions

View File

@@ -8,7 +8,8 @@
"type",
"contents",
"code",
"attrs"
"attrs",
"index"
],
"fields": [
{
@@ -37,12 +38,17 @@
"fieldname": "attrs",
"fieldtype": "Long Text",
"label": "attrs"
},
{
"fieldname": "index",
"fieldtype": "Int",
"label": "Index"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-03-05 17:12:57.997046",
"modified": "2021-03-12 17:56:23.118854",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Section",

View File

@@ -7,4 +7,5 @@ from __future__ import unicode_literals
from frappe.model.document import Document
class LMSSection(Document):
pass
def __repr__(self):
return f"<LMSSection {self.label!r}>"

View File

@@ -3,8 +3,22 @@
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
import frappe
from frappe.model.document import Document
from .section_parser import SectionParser
class LMSTopic(Document):
pass
def before_save(self):
sections = SectionParser().parse(self.description)
self.sections = [self.make_lms_section(i, s) for i, s in enumerate(sections)]
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

View File

@@ -0,0 +1,79 @@
"""Utility to split the text in the topic into multiple sections.
"""
from __future__ import annotations
from dataclasses import dataclass
import re
from typing import List, Tuple, Dict, Iterator
RE_SECTION = re.compile(r"^\{\{\s(\w+)\s*(?:\((.*)\))?\s*\}\}\s*")
class SectionParser:
def parse(self, text: str) -> Iterator[Section]:
"""Parses given text into sections and return an iterator over sections.
"""
lines = text.splitlines()
marked_lines = self.parse_lines(lines)
return self.group_sections(marked_lines)
def parse_lines(self, lines: List[str]) -> List[Tuple[str, str, str]]:
for line in lines:
m = RE_SECTION.match(line)
if m:
yield m.group(1), self.parse_attrs(m.group(2)), None
else:
yield None, None, line
def parse_attrs(self, attrs_str: str) -> Dict[str, str]:
# XXX-Anand: Hack
code = "dict({})".format(attrs_str or "")
return eval(code)
def group_sections(self, marked_lines) -> Iterator[Section]:
index = 0
def make_section(type='text', id=None, label=None, **attrs):
nonlocal index
index += 1
id = id or f"section-{index}"
label = label or id
return Section(
type=type,
id=id,
label=label,
attrs=attrs)
section = make_section("text")
for mark, attrs, line in marked_lines:
if not mark:
section.append(line)
continue
yield section
if mark == 'end':
section = make_section(type='text')
else:
section = make_section(**attrs)
yield section
@dataclass
class Section:
"""One section of the Topic.
"""
type: str
id: str
label: str
contents: str = ""
attrs: dict = None
def append(self, line):
if not line.endswith("\n"):
line = line + "\n"
self.contents += line
def __repr__(self):
attrs = dict(type=self.type, id=self.id, label=self.label, **self.attrs)
attrs_str = ", ".join(f'{k}="{v}"' for k, v in attrs.items())
return f'<Section({attrs_str})>'

View File

@@ -30,7 +30,7 @@
<h1>{{ topic.title }}</h1>
{% for s in topic.sections %}
{% for s in topic.get_sections() %}
<div class="section section-{{ s.type }}">
{{ render_section(s) }}
</div>
@@ -43,8 +43,8 @@
{% macro render_section(s) %}
{% if s.type == "text" %}
{{ render_section_text(s) }}
{% elif s.type == "code" %}
{{ LiveCodeEditor(s.name, s.code) }}
{% elif s.type == "example" or s.type == "code" %}
{{ LiveCodeEditor(s.name, s.contents) }}
{% else %}
<div>Unknown section type: {{s.type}}</div>
{% endif %}