From 5d0084bac56b504131add2a49325412f83f16a57 Mon Sep 17 00:00:00 2001 From: "Prof. Dr. Matthias Jung DL9MJ" Date: Sun, 1 Jun 2025 09:15:49 +0200 Subject: [PATCH 1/5] feature: add ability to download includes --- src/download.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/download.py b/src/download.py index 72f37b4..521a3c2 100644 --- a/src/download.py +++ b/src/download.py @@ -131,3 +131,13 @@ class Download: Parallel(n_jobs=multiprocessing.cpu_count())( delayed(build_picture)(picture) for picture in tqdm(pictures, desc="Build All Pictures") ) + + def download_includes(self): + includes = self.api.get("items/include", params={"limit": -1}) + + result = {} + for include in tqdm(includes, desc="Downloading includes"): + result[include["ident"]] = include["content"] + + with open("data/includes.json", "w") as f: + json.dump(result, f, indent=4) From 2d0dc3e1fc079dc4da745dd4db0019a69682ffeb Mon Sep 17 00:00:00 2001 From: "Prof. Dr. Matthias Jung DL9MJ" Date: Sun, 1 Jun 2025 09:27:52 +0200 Subject: [PATCH 2/5] feature: ability to detect includes --- renderer/fifty_ohm_html_renderer.py | 14 +++++++++++++- renderer/include.py | 13 +++++++++++++ test/test_include.py | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 renderer/include.py create mode 100644 test/test_include.py diff --git a/renderer/fifty_ohm_html_renderer.py b/renderer/fifty_ohm_html_renderer.py index fcc1329..9e34035 100644 --- a/renderer/fifty_ohm_html_renderer.py +++ b/renderer/fifty_ohm_html_renderer.py @@ -6,6 +6,7 @@ from mistletoe import Document, HtmlRenderer from .comment import BlockComment from .dash import Dash from .halfwidth_spaces import HalfwidthSpaces +from .include import Include from .morse import Morse from .nonbreaking_spaces import NonbreakingSpaces, NonbreakingSpacesDots from .photo import Photo @@ -28,7 +29,13 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): section_url = "section.html" ref_id = 0 - def __init__(self, question_renderer=None, picture_handler=None, photo_handler=None): + def __init__( + self, + question_renderer=None, + picture_handler=None, + photo_handler=None, + include_handler=None + ) : super().__init__( Dash, BlockComment, @@ -46,10 +53,12 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): Photo, Table, Qso, + Include ) self.question_renderer = question_renderer self.picture_handler = picture_handler self.photo_handler = photo_handler + self.include_handler = include_handler def render_dash(self, token): return " – " @@ -227,3 +236,6 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): table += "" return table + + def render_include(self, token): + return self.include_handler(token.content) diff --git a/renderer/include.py b/renderer/include.py new file mode 100644 index 0000000..5b96f02 --- /dev/null +++ b/renderer/include.py @@ -0,0 +1,13 @@ +import re + +from mistletoe.span_token import SpanToken + + +class Include(SpanToken): + """ + Span token ("[include:ident]"). + Includes raw html or javascript code from a file with the given identifier. + """ + + parse_inner = False + pattern = re.compile(r'\[include:\s*([^\]]+)\]') diff --git a/test/test_include.py b/test/test_include.py new file mode 100644 index 0000000..e8a9dfb --- /dev/null +++ b/test/test_include.py @@ -0,0 +1,18 @@ +import pytest +from mistletoe import Document + +from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer + + +@pytest.mark.html +def test_include_html(): + assertions = { + '[include:ident]': "

ident

\n", + } + + def test_function(input): + return f"{input}" + + with FiftyOhmHtmlRenderer(include_handler=test_function) as renderer: + for assertion in assertions: + assert renderer.render(Document(assertion)) == assertions[assertion] \ No newline at end of file From 87113ddd461f7e485df328f1205479b1d5143016 Mon Sep 17 00:00:00 2001 From: "Prof. Dr. Matthias Jung DL9MJ" Date: Sun, 1 Jun 2025 09:28:43 +0200 Subject: [PATCH 3/5] feature: add include to acceptance test --- test/acceptanceTest.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/acceptanceTest.md b/test/acceptanceTest.md index 97e5bcd..24f9f58 100644 --- a/test/acceptanceTest.md +++ b/test/acceptanceTest.md @@ -78,6 +78,8 @@ CQ CQ hier ist DL1PZ mit einem allgemeinen Anruf, hier ist DL1PZ und hört. *QSO* ist eine Abkürzung, die ursprünglich aus der Morsetelegrafie stammt und "ich habe direkten Kontakt zu Station X" bedeutet. Daran angelehnt, sprechen Funkamateure oftmals von einem QSO, wenn sie eine erfolgreiche Funkverbindung meinen. +[include:test] + | c: | l: | c: | l: | c: | l: | | | | | | | | From e248d29a7e9e706769c55eafc90ff02a0d1b6094 Mon Sep 17 00:00:00 2001 From: "Prof. Dr. Matthias Jung DL9MJ" Date: Sun, 1 Jun 2025 09:50:46 +0200 Subject: [PATCH 4/5] feature: added ability to render includes --- build.py | 1 + src/build.py | 12 +++++++++++- test/acceptanceTest.md | 4 ++-- test/test_html.py | 9 ++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/build.py b/build.py index ee88e46..435fa6c 100644 --- a/build.py +++ b/build.py @@ -15,6 +15,7 @@ dl.download_edition("N") dl.download_question_metadata() dl.download_pictures() dl.download_photos() +dl.download_includes() # Build edition: bd.build_edition("N") diff --git a/src/build.py b/src/build.py index f5aa932..a6f8ebc 100644 --- a/src/build.py +++ b/src/build.py @@ -128,6 +128,11 @@ class Build: result = BeautifulSoup(result, "html.parser").prettify() file.write(result) + def __include_handler(self, include): + with open("data/includes.json") as file: + includes = json.load(file) + return includes.get(include) + # cached def __build_section(self, edition, edition_name, section, chapter, next_section=None, next_chapter=None): section_template = self.env.get_template("section.html") @@ -135,7 +140,12 @@ 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( + self.__build_question, + self.__picture_handler, + self.__photo_handler, + self.__include_handler + ) as renderer: section["content"] = renderer.render(Document(section["content"])) result = section_template.render( diff --git a/test/acceptanceTest.md b/test/acceptanceTest.md index 24f9f58..0fb2698 100644 --- a/test/acceptanceTest.md +++ b/test/acceptanceTest.md @@ -1,5 +1,7 @@ % Kommentar: DARC-DOWN-TEST +[include:test] + Das ist eine Item-Liste: * Erster Eintrag @@ -78,8 +80,6 @@ CQ CQ hier ist DL1PZ mit einem allgemeinen Anruf, hier ist DL1PZ und hört. *QSO* ist eine Abkürzung, die ursprünglich aus der Morsetelegrafie stammt und "ich habe direkten Kontakt zu Station X" bedeutet. Daran angelehnt, sprechen Funkamateure oftmals von einem QSO, wenn sie eine erfolgreiche Funkverbindung meinen. -[include:test] - | c: | l: | c: | l: | c: | l: | | | | | | | | diff --git a/test/test_html.py b/test/test_html.py index 2d3844c..05b8964 100644 --- a/test/test_html.py +++ b/test/test_html.py @@ -69,12 +69,19 @@ def question_stub(input): answer_pictures=answer_pictures, ) +def include_stub(include): + with open("data/includes.json") as file: + includes = json.load(file) + return includes.get(include) 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, + include_handler=include_stub, + ) as renderer: output = renderer.render(Document(content)) with open("test/acceptanceTest.html", "w") as output_file: pretty = BeautifulSoup(output, "html.parser").prettify() From c2209c5cc26cae10271faff2e7ea57f58e9dd8ec Mon Sep 17 00:00:00 2001 From: "Prof. Dr. Matthias Jung DL9MJ" Date: Mon, 2 Jun 2025 21:47:32 +0200 Subject: [PATCH 5/5] refactor: converted to block token --- renderer/fifty_ohm_html_renderer.py | 2 +- renderer/include.py | 21 ++++++++++++++++----- test/test_include.py | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/renderer/fifty_ohm_html_renderer.py b/renderer/fifty_ohm_html_renderer.py index 9e34035..e0d73b7 100644 --- a/renderer/fifty_ohm_html_renderer.py +++ b/renderer/fifty_ohm_html_renderer.py @@ -238,4 +238,4 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): return table def render_include(self, token): - return self.include_handler(token.content) + return self.include_handler(token.ident) diff --git a/renderer/include.py b/renderer/include.py index 5b96f02..9f2a690 100644 --- a/renderer/include.py +++ b/renderer/include.py @@ -1,13 +1,24 @@ import re -from mistletoe.span_token import SpanToken +from mistletoe.block_token import BlockToken -class Include(SpanToken): +class Include(BlockToken): """ - Span token ("[include:ident]"). + Block token ("[include:ident]"). Includes raw html or javascript code from a file with the given identifier. """ - parse_inner = False - pattern = re.compile(r'\[include:\s*([^\]]+)\]') + @staticmethod + def start(line): + return re.match(r"^\s*\[include:\s*[^\]]+\]", line) + + @classmethod + def read(cls, lines): + re.compile(r'') + line = next(lines) # Consume the line with the include directive + ident = re.match(r"^\s*\[include:\s*([^\]]+)\]", line).group(1) + return ident + + def __init__(self, match): + self.ident = match \ No newline at end of file diff --git a/test/test_include.py b/test/test_include.py index e8a9dfb..24c02f7 100644 --- a/test/test_include.py +++ b/test/test_include.py @@ -7,7 +7,7 @@ from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer @pytest.mark.html def test_include_html(): assertions = { - '[include:ident]': "

ident

\n", + '[include:ident]': "ident\n", } def test_function(input):