diff --git a/renderer/fifty_ohm_html_renderer.py b/renderer/fifty_ohm_html_renderer.py index fcc1329..72f929a 100644 --- a/renderer/fifty_ohm_html_renderer.py +++ b/renderer/fifty_ohm_html_renderer.py @@ -1,5 +1,7 @@ +from itertools import chain + from jinja2 import Environment, FileSystemLoader from mistletoe import Document, HtmlRenderer @@ -28,8 +30,9 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): section_url = "section.html" ref_id = 0 - def __init__(self, question_renderer=None, picture_handler=None, photo_handler=None): - super().__init__( + def __init__(self, *extras, question_renderer=None, picture_handler=None, photo_handler=None): + + final_extras = chain(extras,( Dash, BlockComment, Quote, @@ -46,7 +49,10 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): Photo, Table, Qso, - ) + )) + + super().__init__(*final_extras) + self.question_renderer = question_renderer self.picture_handler = picture_handler self.photo_handler = photo_handler diff --git a/renderer/fifty_ohm_html_slide_renderer.py b/renderer/fifty_ohm_html_slide_renderer.py new file mode 100644 index 0000000..54aeb4e --- /dev/null +++ b/renderer/fifty_ohm_html_slide_renderer.py @@ -0,0 +1,35 @@ +from mistletoe import Document + +from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer + +from .slide_break import SlideBreak + + +class FiftyOhmHtmlSlideRenderer(FiftyOhmHtmlRenderer): + + def __init__(self, question_renderer=None, picture_handler=None, photo_handler=None): + super().__init__( + SlideBreak, + question_renderer=question_renderer, + picture_handler=picture_handler, + photo_handler=photo_handler, + ) + + def render_wrapper(self, content): + content = "---\n" + content + return super().render(Document(content)) + + def render_slide_break(self, token): + inner = self.render_inner(token) + + if token.attribute is None: + return f'
\n{inner}\n
\n' + if token.attribute == "attention": + return f'
\n{inner}\n
\n' + elif token.attribute == "danger": + return f'
\n{inner}\n
\n' + elif token.attribute == "unit": + return f'
\n{inner}\n
\n' + else: + return f'
\n{inner}\n
\n' + diff --git a/renderer/slide_break.py b/renderer/slide_break.py new file mode 100644 index 0000000..dadfe08 --- /dev/null +++ b/renderer/slide_break.py @@ -0,0 +1,35 @@ + +# ^\s*---\s*(.*)\s*$ +import re + +from mistletoe.block_token import BlockToken, tokenize + + +class SlideBreak(BlockToken): + @staticmethod + def start(line): + return re.match(r"^\s*---.*$", line) + + @classmethod + def read(cls, lines): + child_lines = [] + first_line = next(lines) # Consume the line that started the slide break + + match = re.match(r"^\s*---\s*(.*)\s*.*$", first_line) + attribute = match.group(1) if match.group(1) else None + + # We have to work with peak here, since a normal for loop would already + # consume the next line, which might be a `---` line that marks the + # beginning of a new slide as well + next_line = lines.peek() + while next_line is not None: + if re.match(r"^\s*---.*$", next_line) : + break + else: + child_lines.append(next(lines)) + next_line = lines.peek() + + return tokenize(child_lines), attribute + + def __init__(self, match): + self.children, self.attribute = match diff --git a/src/build.py b/src/build.py index f5aa932..e726b13 100644 --- a/src/build.py +++ b/src/build.py @@ -135,7 +135,11 @@ class Build: next_chapter_template = self.env.get_template("next_chapter.html") with open(f'build/{edition}_{section["ident"]}.html', 'w') as file: - with FiftyOhmHtmlRenderer(self.__build_question, self.__picture_handler, self.__photo_handler) as renderer: + with FiftyOhmHtmlRenderer( + question_renderer=self.__build_question, + picture_handler=self.__picture_handler, + photo_handler=self.__photo_handler + ) as renderer: section["content"] = renderer.render(Document(section["content"])) result = section_template.render( diff --git a/templates/slide/question.html b/templates/slide/question.html new file mode 100644 index 0000000..78e782b --- /dev/null +++ b/templates/slide/question.html @@ -0,0 +1,24 @@ +{% set answer_label = ["A", "B", "C", "D"] -%} +{% if picture_question or (answer_pictures[0] and layout in ["1", "9", None]) -%} +{# OHM-13: Fix layout for 4x times picture in row to 2x2 field #} +
+{% else -%} +
+{% endif -%} +
{{ number }}: {{ question }}
+ {% if picture_question -%} +
+ {% endif -%} +
+ {% if answers[0] -%} + {% for answer in answers|shuffle_answers -%} + {{ answer_label[loop.index - 1] }}: {{ answer.content }}

+ {% endfor -%} + {% endif -%} + {% if answer_pictures[0] -%} + {% for answer in answer_pictures|shuffle_answers -%} +
{{ answer_label[loop.index - 1] }}:
+ {% endfor -%} + {% endif -%} +
+
diff --git a/test/acceptanceTestSlides.html b/test/acceptanceTestSlides.html new file mode 100644 index 0000000..cf78af5 --- /dev/null +++ b/test/acceptanceTestSlides.html @@ -0,0 +1,129 @@ + + + + DARC Ausbildungsplattform + + + + + + + + + + + + + + + + + +
+
+
+

Slide mit Picture

+

+ +
Abbildung TODO: testbild
+
+

+
+ +
+

Slide mit Photo

+

+ +
Abbildung TODO: Display eines IC9700-Transceivers, hervorgehoben ist das S-Meter, das den aktuellen Empfangspegel anzeigt
+
+

+
+ +
+ +
+
NB201: Welches Bauteil wird durch das Schaltzeichen symbolisiert?
+
+
+

A: Widerstand

+

B: Batterie

+

C: Diode

+

D: Kondensator

+
+
+
+ +
+

Slide Tabelle

+ + + + + + + + + + + + + + + + + + + + + + + + +
WertBereichBedeutungEnglisch
R1 – 5LesbarkeitReadability
S1 – 9SignalstärkeSignal Strength
T1 – 9TonqualitätTone
Die Bestandteile des RST-Rapports
+
+ +
+

Slide Mit Markdown

Das ist ein Text

    +
  • Das ist ein Bullet
  • +
  • Das ist noch ein Bullet
  • +

Hier wird text unterstrichen

+
+ +
+

Slide Unit

Slide C

+
+ +
+

Slide PINK

PINK

+
+ +
+
+ + + + + + + + + \ No newline at end of file diff --git a/test/acceptanceTestSlides.md b/test/acceptanceTestSlides.md new file mode 100644 index 0000000..18290a3 --- /dev/null +++ b/test/acceptanceTestSlides.md @@ -0,0 +1,44 @@ +## Slide mit Picture + +[picture:123:p_test:testbild] + +--- + +## Slide mit Photo + +[photo:123:n_rst_s-meter:Display eines IC9700-Transceivers, hervorgehoben ist das S-Meter, das den aktuellen Empfangspegel anzeigt] + +--- +[question:2482] +--- + +## Slide Tabelle + +| l: Wert | l: Bereich | l: Bedeutung | l: Englisch | +| R | 1 - 5 | Lesbarkeit | Readability | +| S | 1 - 9 | Signalstärke | Signal Strength | +| T | 1 - 9 | Tonqualität | Tone | +[table:n_rst:Die Bestandteile des RST-Rapports] + +--- + +## Slide Mit Markdown + +Das ist ein *Text* + +* Das ist ein Bullet +* Das ist noch ein Bullet + +Hier wird text unterstrichen + +--- unit + +## Slide Unit + +Slide C + +--- data-background="#FF00FF" + +## Slide PINK + +PINK \ No newline at end of file diff --git a/test/test_html.py b/test/test_html.py index 2d3844c..ec20f45 100644 --- a/test/test_html.py +++ b/test/test_html.py @@ -74,7 +74,7 @@ def test_html(capsys): with capsys.disabled(): with open("test/acceptanceTest.md") as file: content = file.read() - with FiftyOhmHtmlRenderer(question_stub) as renderer: + with FiftyOhmHtmlRenderer(question_renderer=question_stub) as renderer: output = renderer.render(Document(content)) with open("test/acceptanceTest.html", "w") as output_file: pretty = BeautifulSoup(output, "html.parser").prettify() diff --git a/test/test_html_slides.py b/test/test_html_slides.py new file mode 100644 index 0000000..f7a9dbd --- /dev/null +++ b/test/test_html_slides.py @@ -0,0 +1,160 @@ +import json +import random + +from jinja2 import Environment, FileSystemLoader + +from renderer.fifty_ohm_html_slide_renderer import FiftyOhmHtmlSlideRenderer + + +def parse_katalog(): + with open("data/fragenkatalog3b.json") as fragenkatalog_file: + fragenkatalog = json.load(fragenkatalog_file) + + questions = {} + + for exampart in fragenkatalog["sections"]: + for chapter in exampart["sections"]: + if "questions" in chapter: + for question in chapter["questions"]: + questions[question["number"]] = question + if "sections" in chapter: + for section in chapter["sections"]: + for question in section["questions"]: + questions[question["number"]] = question + + return questions + +def filter_shuffle_answers(seq): + try: + answers = [] + firstrun = True + for answer in seq: + if firstrun: + answers.append({"content": answer, "correct": True}) + firstrun = False + else: + answers.append({"content": answer, "correct": False}) + random.shuffle(answers) + return answers + except Exception: + return seq + + +env = Environment(loader=FileSystemLoader("templates/")) +env.filters["shuffle_answers"] = filter_shuffle_answers +question_template = env.get_template("slide/question.html") +questions = parse_katalog() + + +def question_stub(input): + """Combines the original question dataset from BNetzA with our internal metadata""" + with open("data/metadata.json") as metadata_file: + metadata = json.load(metadata_file) + number = metadata[f"{input}"]["number"] # Fragennummer z.B. AB123 + + question = questions[number] + metadata = metadata[f"{input}"] + + if "answer_a" in question: + answers = [question["answer_a"], question["answer_b"], question["answer_c"], question["answer_d"]] + else: + answers = [] + + if metadata["picture_a"] != "": + answer_pictures = [ + metadata["picture_a"], + metadata["picture_b"], + metadata["picture_c"], + metadata["picture_d"], + ] + else: + answer_pictures = [] + + if "picture_question" in question: + picture_question = metadata["picture_question"] + else: + picture_question = "" + + return question_template.render( + question=question["question"], + number=number, + layout=metadata["layout"], + picture_question=picture_question, + answers=answers, + answer_pictures=answer_pictures, + ) + + +def print_head(): + return """ + + + DARC Ausbildungsplattform + + + + + + + + + + + + + + + + + +
+
+""" + + # + # + # + +def print_tail(): + return """
+
+ + + + + + + + + """ + +def test_html_slides(capsys): + with capsys.disabled(): + with open("test/acceptanceTestSlides.md") as file: + content = file.read() + with FiftyOhmHtmlSlideRenderer(question_stub) as renderer: + output = "" + output = print_head() + output += renderer.render_wrapper(content) + output += print_tail() + with open("test/acceptanceTestSlides.html", "w") as output_file: + output_file.write(output) diff --git a/test/test_question.py b/test/test_question.py index d45d9ca..526677d 100644 --- a/test/test_question.py +++ b/test/test_question.py @@ -20,7 +20,7 @@ def test_question_html(): def test_function(input): return f"{input}" - with FiftyOhmHtmlRenderer(test_function) as renderer: + with FiftyOhmHtmlRenderer(question_renderer=test_function) as renderer: for assertion in assertions: assert renderer.render(Document(assertion)) == assertions[assertion] @@ -38,7 +38,7 @@ def test_question_translation_html(): metadata = json.load(file) return metadata[f"{input}"].get("number") - with FiftyOhmHtmlRenderer(test_function) as renderer: + with FiftyOhmHtmlRenderer(question_renderer=test_function) as renderer: for assertion in assertions: assert renderer.render(Document(assertion)) == assertions[assertion]