From fb79bddbc6faeadf98c5fd6ff4d9f6a28a6c0ab8 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 14:41:07 +0100 Subject: [PATCH 01/69] fix table captions --- .../benchmarking/json_report/templates/macros/table.adoc.j2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 index 9d22dd8b9..baf3eb709 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 @@ -4,13 +4,13 @@ {%set table = table_controller.generate() %} -{% if tableNode.caption %} -.{{tableNode.caption}} -{% endif %} {% if tableNode.style.classnames %} [{% for classname in tableNode.style.classnames %}.{{classname}}{% endfor %}] {% endif %} -- +{% if tableNode.caption %} +.{{tableNode.caption}} +{% endif %} [cols="{{ table_controller.getColsAlignment() }}",options="header"] |=== From 1fbb7305ecf34b5f486ca527104bd28149bdfd93 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 15:00:11 +0100 Subject: [PATCH 02/69] support captions in plots ( workaraound with example blocks ) --- docs/antora/supplemental-ui/css/figures.css | 24 ++++++++++++++++++- .../json_report/schemas/jsonReport.py | 1 + .../templates/json2adoc_report.adoc.j2 | 1 + .../json_report/templates/macros/plot.adoc.j2 | 8 +++++++ .../reframe/schemas/defaultJsonReport.py | 3 ++- 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/docs/antora/supplemental-ui/css/figures.css b/docs/antora/supplemental-ui/css/figures.css index e0aed6204..f7f0b7c35 100644 --- a/docs/antora/supplemental-ui/css/figures.css +++ b/docs/antora/supplemental-ui/css/figures.css @@ -75,4 +75,26 @@ .export-container button:hover { background-color: #005fa3; -} \ No newline at end of file +} + +/* Example block styling for plots */ +.exampleblock.plot { + border: none; + padding: 0; + margin: 0; +} + +.exampleblock.plot > .content { + padding: 0; + border: none; +} + +.exampleblock.plot > .title { + text-align: center; + font-style: italic; + margin-top: 0.5rem; +} + +.exampleblock.example { + border-left: 4px solid var(--brand-primary); +} diff --git a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py index 07fe90c00..6b4228566 100644 --- a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py +++ b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py @@ -30,6 +30,7 @@ class ImageNode(ReportNode): class PlotNode(ReportNode): type: Literal["plot"] + caption: Optional[str] = None plot: Plot diff --git a/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 index 0340d76b6..25e53a353 100644 --- a/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 @@ -1,3 +1,4 @@ +:example-caption: Plot {% if report.description %} :description: {{ report.description }} {% endif %} diff --git a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 index 8660519bf..083ed351e 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 @@ -1,5 +1,12 @@ {% macro render(plotNode,data) %} + + +[example.plot] +{%if plotNode.caption %} +.{{plotNode.caption}} +{% endif %} +==== ++++ {% for figure in FiguresController(data.get(plotNode.ref),plotNode.plot).generateAll() %} @@ -32,5 +39,6 @@ {% endfor %} ++++ +==== {% endmacro %} \ No newline at end of file diff --git a/src/feelpp/benchmarking/reframe/schemas/defaultJsonReport.py b/src/feelpp/benchmarking/reframe/schemas/defaultJsonReport.py index 6db87b007..799edb2b8 100644 --- a/src/feelpp/benchmarking/reframe/schemas/defaultJsonReport.py +++ b/src/feelpp/benchmarking/reframe/schemas/defaultJsonReport.py @@ -80,7 +80,8 @@ def applyDefaultPlots(cls, values): item = { "type": "plot", "ref":"reframe_df", - "plot": DefaultPlot.model_validate(item["plot"]) + "plot": DefaultPlot.model_validate(item["plot"]), + "caption": item.get("caption", None) } elif isinstance(item, dict) and (item.get("type") == "section" or item.get("type")=="grid"): item = cls.applyDefaultPlots(item) From ac5fbc1832435c46fc7f2b801addfb508f3fe385 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 15:09:11 +0100 Subject: [PATCH 03/69] flip caption below plot --- docs/antora/supplemental-ui/css/figures.css | 26 ++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/antora/supplemental-ui/css/figures.css b/docs/antora/supplemental-ui/css/figures.css index f7f0b7c35..cb178f340 100644 --- a/docs/antora/supplemental-ui/css/figures.css +++ b/docs/antora/supplemental-ui/css/figures.css @@ -79,22 +79,26 @@ /* Example block styling for plots */ .exampleblock.plot { - border: none; - padding: 0; - margin: 0; + border: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; } -.exampleblock.plot > .content { - padding: 0; - border: none; +.exampleblock.plot>.content { + order: 1; + padding: 0; + border: none; } -.exampleblock.plot > .title { - text-align: center; - font-style: italic; - margin-top: 0.5rem; +.exampleblock.plot>.title { + order: 2; + text-align: center; + font-style: italic; + margin-top: 0.5rem; } .exampleblock.example { - border-left: 4px solid var(--brand-primary); + border-left: 4px solid var(--brand-primary); } From 8b2ada0f4cf6e62fa78ce9df543da3401f0e690c Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 15:18:50 +0100 Subject: [PATCH 04/69] handle cross references --- .../benchmarking/json_report/templates/macros/image.adoc.j2 | 1 + .../benchmarking/json_report/templates/macros/plot.adoc.j2 | 3 +++ .../benchmarking/json_report/templates/macros/table.adoc.j2 | 1 + 3 files changed, 5 insertions(+) diff --git a/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 index 88c221a09..a094c2e65 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 @@ -4,6 +4,7 @@ {% set styles = imageNode.style | join(' ')%} {% if imageNode.caption %} +[[{{imageNode.caption | replace(' ','-')}}]] image::{{src}}[role="{{styles}}",alt={{alt}},title={{imageNode.caption}}] {% else %} image::{{src}}[role=""{{styles}}"",alt={{alt}}] diff --git a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 index 083ed351e..1280067a8 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 @@ -2,6 +2,9 @@ +{%if plotNode.caption %} +[[{{plotNode.caption | replace(' ','-')}}]] +{% endif %} [example.plot] {%if plotNode.caption %} .{{plotNode.caption}} diff --git a/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 index baf3eb709..3db299227 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 @@ -9,6 +9,7 @@ {% endif %} -- {% if tableNode.caption %} +[[{{tableNode.caption | replace(' ','-')}}]] .{{tableNode.caption}} {% endif %} [cols="{{ table_controller.getColsAlignment() }}",options="header"] From 0bf9a0c136b97e66596ec60d73292bbac8e1f6c0 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 15:33:53 +0100 Subject: [PATCH 05/69] add id for nodes ! --- src/feelpp/benchmarking/json_report/schemas/jsonReport.py | 1 + .../benchmarking/json_report/templates/macros/image.adoc.j2 | 6 +++++- .../benchmarking/json_report/templates/macros/plot.adoc.j2 | 4 +++- .../benchmarking/json_report/templates/macros/table.adoc.j2 | 6 +++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py index 6b4228566..ff1ca3f47 100644 --- a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py +++ b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py @@ -8,6 +8,7 @@ class ReportNode(BaseModel): type:str + id: Optional[str] = None ref: Optional[str] = None model_config = ConfigDict( extra="forbid" ) diff --git a/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 index a094c2e65..938d092b3 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 @@ -3,8 +3,12 @@ {% set alt = imageNode.alt or imageNode.caption or "" %} {% set styles = imageNode.style | join(' ')%} -{% if imageNode.caption %} +{% if imageNode.id %} +[[{{imageNode.id}}]] +{% elif imageNode.caption %} [[{{imageNode.caption | replace(' ','-')}}]] +{% endif %} +{% if imageNode.caption %} image::{{src}}[role="{{styles}}",alt={{alt}},title={{imageNode.caption}}] {% else %} image::{{src}}[role=""{{styles}}"",alt={{alt}}] diff --git a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 index 1280067a8..3a97dc5b2 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 @@ -2,7 +2,9 @@ -{%if plotNode.caption %} +{% if plotNode.id %} +[[{{plotNode.id}}]] +{% elif plotNode.caption %} [[{{plotNode.caption | replace(' ','-')}}]] {% endif %} [example.plot] diff --git a/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 index 3db299227..58506cbf7 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 @@ -8,8 +8,12 @@ [{% for classname in tableNode.style.classnames %}.{{classname}}{% endfor %}] {% endif %} -- -{% if tableNode.caption %} +{% if tableNode.id %} +[[{{tableNode.id}}]] +{% elif tableNode.caption %} [[{{tableNode.caption | replace(' ','-')}}]] +{% endif %} +{% if tableNode.caption %} .{{tableNode.caption}} {% endif %} [cols="{{ table_controller.getColsAlignment() }}",options="header"] From 77febebc72aac5d46ed0ca28da1b628d48670c9b Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 15:36:03 +0100 Subject: [PATCH 06/69] sections can be referenced now via ID --- .../benchmarking/json_report/templates/macros/section.adoc.j2 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/feelpp/benchmarking/json_report/templates/macros/section.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/section.adoc.j2 index d04566a27..2b801fe0b 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/section.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/section.adoc.j2 @@ -17,6 +17,9 @@ {% macro render(sectionNode, report_data, level=1) %} +{% if sectionNode.id %} +[[{{sectionNode.id}}]] +{% endif %} {%if sectionNode.title %} {{ '=' * level }} {{ sectionNode.title }} {%endif%} From 0c764a90178fcfac50e2f13d4fc69c7e7d8cc674 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 16:03:28 +0100 Subject: [PATCH 07/69] support latex refs --- .../benchmarking/json_report/schemas/jsonReport.py | 1 + .../json_report/templates/macros/latex.adoc.j2 | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py index ff1ca3f47..e082eff5e 100644 --- a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py +++ b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py @@ -20,6 +20,7 @@ class TextNode(ReportNode): class LatexNode(ReportNode): type:Literal["latex"] + is_equation: Optional[bool] = False latex: str class ImageNode(ReportNode): diff --git a/src/feelpp/benchmarking/json_report/templates/macros/latex.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/latex.adoc.j2 index cdfebdf1e..795bb455c 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/latex.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/latex.adoc.j2 @@ -1,8 +1,17 @@ {% macro render(latexNode,data) %} +{% if latexNode.id %} +[[{{latexNode.id}}]] +{% endif %} [stem] ++++ +{%if latexNode.is_equation %} +\begin{equation} {{ latexNode.latex }} +\end{equation} +{% else %} +{{ latexNode.latex }} +{% endif %} ++++ {% endmacro %} \ No newline at end of file From 3f773409a92146256699946e9068734a27d065df Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 16:09:13 +0100 Subject: [PATCH 08/69] refs to text and itemize --- .../benchmarking/json_report/templates/macros/itemize.adoc.j2 | 3 +++ .../benchmarking/json_report/templates/macros/text.adoc.j2 | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/feelpp/benchmarking/json_report/templates/macros/itemize.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/itemize.adoc.j2 index 25e2d2e2a..e828e3dd2 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/itemize.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/itemize.adoc.j2 @@ -2,6 +2,9 @@ {% macro render(listNode,data) %} +{% if listNode.id %} +[[{{listNode.id}}]] +{% endif %} {% for item in listNode.items %} - {{text.render(item,data)}} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/templates/macros/text.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/text.adoc.j2 index 37eeda3f2..e952e14d7 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/text.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/text.adoc.j2 @@ -1,3 +1,6 @@ {% macro render(textNode,data) %} +{%if textNode.id %} +[[{{textNode.id}}]] +{% endif %} {{ TextController((data or {}).get(textNode.ref),textNode.text).generate() }} {% endmacro %} \ No newline at end of file From fb64cd91223a673cd1be3bd3126450a9b295aed6 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 22 Jan 2026 16:14:19 +0100 Subject: [PATCH 09/69] up golden files --- .../json_report/tests/data/golden/basic_text.adoc | 1 + .../json_report/tests/data/golden/list_with_data.adoc | 1 + .../json_report/tests/data/golden/plot_features.adoc | 9 ++++++++- .../json_report/tests/data/golden/preprocessor.adoc | 1 + .../json_report/tests/data/golden/report.adoc | 9 +++++++-- .../json_report/tests/data/golden/table_features.adoc | 1 + 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.adoc index 855535a60..e3b1a3103 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.adoc @@ -1,3 +1,4 @@ +:example-caption: Plot :docdatetime: == Introduction diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.adoc index 2586d7ebc..82c8d8220 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.adoc @@ -1,3 +1,4 @@ +:example-caption: Plot :docdatetime: == Metadata diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc index 6c080f5b0..6085fee51 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc @@ -1,7 +1,10 @@ +:example-caption: Plot :docdatetime: == Score Analysis +[example.plot] +==== ++++
@@ -19,9 +22,13 @@
++++ +==== + +[example.plot] +==== ++++
@@ -58,7 +65,7 @@ ++++ - +==== The scatter plot shows the average score per subject for each student, while the stacked bar plot visualizes how many subjects each student passed. diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.adoc index 307df8f72..af3a30d7b 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.adoc @@ -1,3 +1,4 @@ +:example-caption: Plot :docdatetime: Processed Text: Foo Foo Baz diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc index 9b462f10f..058d0cc6b 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc @@ -1,3 +1,4 @@ +:example-caption: Plot :docdatetime: @@ -94,6 +95,8 @@ Placeholders in this report allow dynamic insertion of values from the datasets. == Performance Analysis +[example.plot] +==== ++++
@@ -111,9 +114,11 @@ Placeholders in this report allow dynamic insertion of values from the datasets.
++++ +==== - +[example.plot] +==== ++++
@@ -131,7 +136,7 @@ Placeholders in this report allow dynamic insertion of values from the datasets.
++++ - +==== The scatter plot shows average scores per subject, while the bar chart shows average attendance rates for each student. diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.adoc index 80d5f4e20..034eb6221 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.adoc @@ -1,3 +1,4 @@ +:example-caption: Plot :docdatetime: == Student Performance From 04b1b3b31100ca9cbad1ce23b2d3a65a54e557b8 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 23 Jan 2026 11:04:00 +0100 Subject: [PATCH 10/69] handle grid captions and inner figures --- docs/antora/supplemental-ui/css/figures.css | 6 +-- .../json_report/schemas/jsonReport.py | 1 + .../templates/json2adoc_report.adoc.j2 | 2 +- .../json_report/templates/macros/grid.adoc.j2 | 39 ++++++++++++------- .../templates/macros/image.adoc.j2 | 15 ++++--- 5 files changed, 40 insertions(+), 23 deletions(-) diff --git a/docs/antora/supplemental-ui/css/figures.css b/docs/antora/supplemental-ui/css/figures.css index cb178f340..c122340d2 100644 --- a/docs/antora/supplemental-ui/css/figures.css +++ b/docs/antora/supplemental-ui/css/figures.css @@ -78,7 +78,7 @@ } /* Example block styling for plots */ -.exampleblock.plot { +.exampleblock.plot,.exampleblock.grid,.exampleblock.image { border: none; padding: 0; margin: 0; @@ -86,13 +86,13 @@ flex-direction: column; } -.exampleblock.plot>.content { +.exampleblock.plot>.content,.exampleblock.grid>.content ,.exampleblock.image>.content { order: 1; padding: 0; border: none; } -.exampleblock.plot>.title { +.exampleblock.plot>.title,.exampleblock.grid>.title,.exampleblock.image>.title{ order: 2; text-align: center; font-style: italic; diff --git a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py index e082eff5e..950b2ca5a 100644 --- a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py +++ b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py @@ -71,6 +71,7 @@ class SectionNode(ReportNode): class GridNode(ReportNode): type: Literal["grid"] contents: Optional[List[Node]] = [] + caption: Optional[str] = None columns: Optional[int] = 1 justify: Optional[Literal["start","center","end"]] = "start" align: Optional[Literal["start","center","end"]] = "start" diff --git a/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 index 25e53a353..4fa47bf99 100644 --- a/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 @@ -1,4 +1,4 @@ -:example-caption: Plot +:example-caption: Figure {% if report.description %} :description: {{ report.description }} {% endif %} diff --git a/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 index 42db4b9bd..c67ada82b 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 @@ -5,23 +5,34 @@ {% import "macros/table.adoc.j2" as table %} {% import "macros/itemize.adoc.j2" as itemize %} -{% set handlers = { - "plot": plot.render, - "text": text.render, - "image": image.render, - "latex": latex.render, - "table": table.render, - "itemize": itemize.render -} %} - {% macro render(gridNode,report_data) %} -[.grid.grid-{{gridNode.columns}}.grid-{{gridNode.gap}}.justify-{{gridNode.justify}}.align-{{gridNode.align}}] +{% if gridNode.id %} +[[{{gridNode.id}}]] +{% endif %} +[example.grid] +{% if gridNode.caption %} +.{{gridNode.caption}} +{% endif %} +==== +[.grid.grid-{{gridNode.columns}}.gap-{{gridNode.gap}}.justify-{{gridNode.justify}}.align-{{gridNode.align}}] -- +:figure-caption!: {%for node in gridNode.contents%} -{% set handler = handlers.get(node.type) %} -{% if handler %} -{{ handler(node,report_data.get(node.ref)) }} +{% if node.type == "image" %} +{{ image.render(node,report_data.get(node.ref), make_block=False) }} +{% elif node.type == "plot" %} +{{ plot.render(node,report_data.get(node.ref)) }} +{% elif node.type == "latex" %} +{{ latex.render(node,report_data.get(node.ref)) }} +{% elif node.type == "text" %} +{{ text.render(node,report_data.get(node.ref)) }} +{% elif node.type == "table" %} +{{ table.render(node,report_data.get(node.ref)) }} +{% elif node.type == "itemize" %} +{{ itemize.render(node,report_data.get(node.ref)) }} {% endif %} {% endfor %} +:figure-caption: Figure -- -{% endmacro %} +==== +{% endmacro %} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 index 938d092b3..bf5305e7f 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 @@ -1,4 +1,4 @@ -{% macro render(imageNode,data) %} +{% macro render(imageNode,data,make_block = True) %} {% set src = imageNode.src %} {% set alt = imageNode.alt or imageNode.caption or "" %} {% set styles = imageNode.style | join(' ')%} @@ -6,12 +6,17 @@ {% if imageNode.id %} [[{{imageNode.id}}]] {% elif imageNode.caption %} -[[{{imageNode.caption | replace(' ','-')}}]] +[[{{imageNode.caption | replace(' ','-') | replace('(','') | replace(')','') }}]] {% endif %} {% if imageNode.caption %} -image::{{src}}[role="{{styles}}",alt={{alt}},title={{imageNode.caption}}] +.{{imageNode.caption}} +{% endif %} +{% if make_block %} +[example.image] +==== +image::{{src}}[role="{{styles}}",alt={{alt}}] +==== {% else %} -image::{{src}}[role=""{{styles}}"",alt={{alt}}] +image::{{src}}[role="{{styles}}",alt={{alt}}] {% endif %} - {% endmacro %} From 587512f6cb39b247916e4e9d1c0863a6cc2b07e1 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 23 Jan 2026 11:37:38 +0100 Subject: [PATCH 11/69] improve plot style and support plots in grid #381 --- docs/antora/supplemental-ui/css/figures.css | 87 ++++++++++--------- .../json_report/templates/macros/grid.adoc.j2 | 2 +- .../json_report/templates/macros/plot.adoc.j2 | 11 ++- 3 files changed, 54 insertions(+), 46 deletions(-) diff --git a/docs/antora/supplemental-ui/css/figures.css b/docs/antora/supplemental-ui/css/figures.css index c122340d2..e276dfcaa 100644 --- a/docs/antora/supplemental-ui/css/figures.css +++ b/docs/antora/supplemental-ui/css/figures.css @@ -1,31 +1,26 @@ .figure-container { position: relative; - margin: 1.5rem auto; - padding: 1rem; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08); + width: 100%; + max-width: 100%; + box-sizing: border-box; + margin: 1.2rem 0; + background: none; + box-shadow: none; + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; } .subfigure-container { position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #f9f9f9; - padding: 1rem; - border: 1px solid #ddd; - border-top: none; - border-radius: 0 0 8px 8px; + inset: 0; + background: none; + padding: 0; + border: none; } .subfigure-container.active { position: relative; - opacity: 1; - pointer-events: auto; - z-index: 1; } .subfigure-container.inactive { @@ -37,51 +32,59 @@ /* Tabs container: display buttons inline with a bottom border to indicate grouping */ .tabs-container { display: flex; - border-bottom: 2px solid #007acc; - margin-bottom: 0.5rem; + justify-content: flex-start; + gap: 0; + margin-bottom: 0.4rem; } -/* Figure tab button styling */ + .figure-tab { - background: transparent; + background: none; border: none; - outline: none; - padding: 0.5rem 1rem; - margin-right: 0.3rem; - font-size: 1rem; - color: #007acc; + font-size: 0.9rem; + color: #444; cursor: pointer; - border-radius: 4px 4px 0 0; - transition: background-color 0.2s ease, color 0.2s ease; + border: 1px solid #ccc; + padding: 0.2rem 0.5rem; } -.figure-tab:hover, -.figure-tab.active { - background-color: #007acc; - color: #fff; +.figure-tab:hover { + color: #000; /* subtle darkening on hover */ + background: none; /* no hover background */ +} + + +.export-container { + display: flex; + justify-content: flex-end; + gap: 0.3rem; + margin-bottom: 0.3rem; } .export-container button { - background-color: #007acc; - color: #fff; + background: none; + color: #666; border: none; - padding: 0.4rem 0.8rem; - margin-right: 0.4rem; - border-radius: 4px; + padding: 0; + font-size: 0.75rem; cursor: pointer; - font-size: 0.9rem; - transition: background-color 0.2s ease; + text-decoration: underline; } .export-container button:hover { - background-color: #005fa3; + color: #000; +} + +.exampleblock.plot{ + border: 1px solid #ccc; + border-radius: 4px; } /* Example block styling for plots */ .exampleblock.plot,.exampleblock.grid,.exampleblock.image { border: none; padding: 0; - margin: 0; + margin: 1.5rem 0; display: flex; flex-direction: column; } diff --git a/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 index c67ada82b..1412541b9 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 @@ -21,7 +21,7 @@ {% if node.type == "image" %} {{ image.render(node,report_data.get(node.ref), make_block=False) }} {% elif node.type == "plot" %} -{{ plot.render(node,report_data.get(node.ref)) }} +{{ plot.render(node,report_data.get(node.ref), make_block=False) }} {% elif node.type == "latex" %} {{ latex.render(node,report_data.get(node.ref)) }} {% elif node.type == "text" %} diff --git a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 index 3a97dc5b2..e68e14f0e 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 @@ -1,5 +1,4 @@ -{% macro render(plotNode,data) %} - +{% macro render(plotNode,data, make_block=True) %} {% if plotNode.id %} @@ -7,11 +6,15 @@ {% elif plotNode.caption %} [[{{plotNode.caption | replace(' ','-')}}]] {% endif %} +{% if make_block %} [example.plot] +{% endif %} {%if plotNode.caption %} .{{plotNode.caption}} {% endif %} +{% if make_block %} ==== +{% endif %} ++++ {% for figure in FiguresController(data.get(plotNode.ref),plotNode.plot).generateAll() %} @@ -27,6 +30,7 @@ {% for subfigure in figure.subfigures %}
+ {{subfigure.html}}
{% for export in subfigure.exports %} @@ -36,7 +40,6 @@ {% endfor %}
- {{subfigure.html}}
{% endfor %}
@@ -44,6 +47,8 @@ {% endfor %} ++++ +{% if make_block %} ==== +{% endif %} {% endmacro %} \ No newline at end of file From d42fa86b7bdd2dc288f76018d3e21fe937874239 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 23 Jan 2026 11:53:57 +0100 Subject: [PATCH 12/69] emulate grid figures caption --- docs/antora/supplemental-ui/css/figures.css | 2 +- .../benchmarking/json_report/templates/macros/plot.adoc.j2 | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/antora/supplemental-ui/css/figures.css b/docs/antora/supplemental-ui/css/figures.css index e276dfcaa..206253178 100644 --- a/docs/antora/supplemental-ui/css/figures.css +++ b/docs/antora/supplemental-ui/css/figures.css @@ -95,7 +95,7 @@ border: none; } -.exampleblock.plot>.title,.exampleblock.grid>.title,.exampleblock.image>.title{ +.exampleblock.plot>.title,.exampleblock.grid>.title,.exampleblock.image>.title, .plotly-figure-caption{ order: 2; text-align: center; font-style: italic; diff --git a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 index e68e14f0e..3d1611787 100644 --- a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 @@ -8,11 +8,9 @@ {% endif %} {% if make_block %} [example.plot] -{% endif %} {%if plotNode.caption %} .{{plotNode.caption}} {% endif %} -{% if make_block %} ==== {% endif %} ++++ @@ -42,6 +40,10 @@ {% endfor %} + + {%if plotNode.caption and make_block==False %} +
{{plotNode.caption}}
+ {% endif %} {% endfor %} From c38093dc243434f05fdf9d5b9f5b11ab09a2582c Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 23 Jan 2026 13:14:29 +0100 Subject: [PATCH 13/69] fix golden files --- .../tests/data/golden/basic_text.adoc | 2 +- .../tests/data/golden/list_with_data.adoc | 2 +- .../tests/data/golden/plot_features.adoc | 27 +++++++++---------- .../tests/data/golden/preprocessor.adoc | 2 +- .../json_report/tests/data/golden/report.adoc | 18 ++++++------- .../tests/data/golden/table_features.adoc | 2 +- 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.adoc index e3b1a3103..0eafa3059 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.adoc @@ -1,4 +1,4 @@ -:example-caption: Plot +:example-caption: Figure :docdatetime: == Introduction diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.adoc index 82c8d8220..8992f2fa5 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.adoc @@ -1,4 +1,4 @@ -:example-caption: Plot +:example-caption: Figure :docdatetime: == Metadata diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc index 6085fee51..eef395eda 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc @@ -1,4 +1,4 @@ -:example-caption: Plot +:example-caption: Figure :docdatetime: == Score Analysis @@ -10,14 +10,14 @@
-
- - -
FIGURE
+
+ + +
@@ -38,27 +38,26 @@
- -
- - -
FIGURE
- -
-
-
+ +
+ +
FIGURE
+
+ + +
diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.adoc index af3a30d7b..6b7c4d16c 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.adoc @@ -1,4 +1,4 @@ -:example-caption: Plot +:example-caption: Figure :docdatetime: Processed Text: Foo Foo Baz diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc index 058d0cc6b..0146176db 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc @@ -1,4 +1,4 @@ -:example-caption: Plot +:example-caption: Figure :docdatetime: @@ -102,14 +102,14 @@ Placeholders in this report allow dynamic insertion of values from the datasets.
-
- - -
FIGURE
+
+ + +
@@ -124,14 +124,14 @@ Placeholders in this report allow dynamic insertion of values from the datasets.
-
- - -
FIGURE
+
+ + +
diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.adoc index 034eb6221..c70e2a35c 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.adoc @@ -1,4 +1,4 @@ -:example-caption: Plot +:example-caption: Figure :docdatetime: == Student Performance From 6ba4cadb686a82901bd807a601296bd1f625224f Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 23 Jan 2026 13:51:13 +0100 Subject: [PATCH 14/69] fix api doc and add section on how to use referencing --- .../pages/json_schema/content/figure.adoc | 2 + .../pages/json_schema/content/grid.adoc | 4 +- .../pages/json_schema/content/image.adoc | 2 + .../pages/json_schema/content/latex.adoc | 2 + .../pages/json_schema/content/list.adoc | 1 + .../pages/json_schema/content/section.adoc | 1 + .../pages/json_schema/content/table.adoc | 2 + .../pages/json_schema/content/text.adoc | 1 + .../pages/json_schema/referencing.adoc | 82 +++++++++++++++++++ .../pages/json_schema/report_schema.adoc | 6 +- 10 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 docs/modules/json_report/pages/json_schema/referencing.adoc diff --git a/docs/modules/json_report/pages/json_schema/content/figure.adoc b/docs/modules/json_report/pages/json_schema/content/figure.adoc index 4a684d344..ff85915c7 100644 --- a/docs/modules/json_report/pages/json_schema/content/figure.adoc +++ b/docs/modules/json_report/pages/json_schema/content/figure.adoc @@ -10,7 +10,9 @@ The **Plot Node** defines a data visualization within the report. It specifies t |=== |Field|Type|Description|Default Value +|`id`|string| *Optional.* Identifies the plot node within the report. Used for internal cross referencing |`null` |`type`|string|Must be set to `"plot"`.|`"plot"` +|`caption`|string|*Optional.* A descriptive caption for the figure.|`null` |`ref`|string|The unique `name` of the data file (from the root `data` list) to reference for building the figure.|*Required* |`plot`|object|The nested configuration object (`Plot` schema) defining the figure's dimensions, type, and transformation.|*Required* |=== diff --git a/docs/modules/json_report/pages/json_schema/content/grid.adoc b/docs/modules/json_report/pages/json_schema/content/grid.adoc index 27ea11aa8..2cef35996 100644 --- a/docs/modules/json_report/pages/json_schema/content/grid.adoc +++ b/docs/modules/json_report/pages/json_schema/content/grid.adoc @@ -1,11 +1,13 @@ = Grid Node -The **Grid Node** is a layout node that can be used to structure content into a grid. +The **Grid Node** is a layout node that can be used to structure content into a grid. Mainly used to arrange images, plots, or tables side by side in a visually appealing manner. Sub figures or sub plots can be created using this node, but numbering and labeling must be handled manually within the captions of the child nodes. |=== |Field|Type|Description|Default Value +|`id`|string| *Optional.* Identifies the grid node within the report. Used for internal cross referencing |`null` |`type`|string|Must be set to `"grid"`.|`"grid"` +|`caption`|string|*Optional.* A descriptive caption for the grid.|`null` |`contents`|array of objects| A list of other (non-recursive) content nodes.|[] |columns|int| Number of columns for the grid. Must be between 1 and 4.| 1 |gap|int| Must be beween 1 and 3| 2. diff --git a/docs/modules/json_report/pages/json_schema/content/image.adoc b/docs/modules/json_report/pages/json_schema/content/image.adoc index e2bb8f84d..ec20829b2 100644 --- a/docs/modules/json_report/pages/json_schema/content/image.adoc +++ b/docs/modules/json_report/pages/json_schema/content/image.adoc @@ -12,7 +12,9 @@ The `src` file path is typically resolved relative to the passed configuration f |=== |Field|Type|Description|Default Value +|`id`|string| *Optional.* Identifies the image node within the report. Used for internal cross referencing |`null` |`type`|string|Must be set to `"image"`.|`"image"` +|`caption`|string|*Optional.* A descriptive caption for the image.|`null` |`src`|string|The file path or URL of the image to embed.|*Required* |`caption`|string|An optional caption displayed beneath the image. If provided, the image is rendered using the `title` attribute in AsciiDoc.|`null` |`alt`|string|Alternative text for the image, used for accessibility and when the image cannot be displayed.|`null` diff --git a/docs/modules/json_report/pages/json_schema/content/latex.adoc b/docs/modules/json_report/pages/json_schema/content/latex.adoc index c3d0eea92..78ae79fd1 100644 --- a/docs/modules/json_report/pages/json_schema/content/latex.adoc +++ b/docs/modules/json_report/pages/json_schema/content/latex.adoc @@ -5,6 +5,8 @@ The **LaTeX Node** is designed for the direct embedding of mathematical formulas |=== |Field|Type|Description|Default Value +|`id`|string| *Optional.* Identifies the LaTeX node within the report. Used for internal cross referencing |`null` +|`is_equation`|boolean|*Optional.* Indicates if the LaTeX content represents a standalone equation. It adds `\begin\{equation\}` and `\end\{equation\}` around the content if true.|`false` |`type`|string|Must be set to `"latex"`.|`"latex"` |`latex`|string|The raw LaTeX content (e.g., a mathematical equation or a custom LaTeX block).|*Required* |`ref`|string|The `name` of the data field to reference for dynamic placeholder resolution.|`null` diff --git a/docs/modules/json_report/pages/json_schema/content/list.adoc b/docs/modules/json_report/pages/json_schema/content/list.adoc index e3067e104..de605e914 100644 --- a/docs/modules/json_report/pages/json_schema/content/list.adoc +++ b/docs/modules/json_report/pages/json_schema/content/list.adoc @@ -7,6 +7,7 @@ The **List Node** is used to render ordered lists. Its primary strength is that |=== |Field|Type|Description|Default Value +|`id`|string| *Optional.* Identifies the list node within the report. Used for internal cross referencing |`null` |`type`|string|Must be set to `"itemize"`.|`"itemize"` |`items`|array of objects/strings|The contents of the list. Each entry can be a simple string, a full `Text Node` object, or a `Text Configuration` object.|*Required* |`ref`|string|The `name` of the data field to reference for dynamic placeholder resolution within all list items.|`null` diff --git a/docs/modules/json_report/pages/json_schema/content/section.adoc b/docs/modules/json_report/pages/json_schema/content/section.adoc index 609ade1e9..549c22205 100644 --- a/docs/modules/json_report/pages/json_schema/content/section.adoc +++ b/docs/modules/json_report/pages/json_schema/content/section.adoc @@ -5,6 +5,7 @@ The **Section Node** is the fundamental building block for structuring your repo |=== |Field|Type|Description|Default Value +|`id`|string| *Optional.* Identifies the section node within the report. Used for internal cross referencing |`null` |`type`|string|Must be set to `"section"`.|`"section"` |`title`|string|The title of the section.|*Required* |`contents`|array of objects|A recursive list of other content nodes, including nested `SectionNode`s.| [] diff --git a/docs/modules/json_report/pages/json_schema/content/table.adoc b/docs/modules/json_report/pages/json_schema/content/table.adoc index 5ffd6d3a6..3b82b09a0 100644 --- a/docs/modules/json_report/pages/json_schema/content/table.adoc +++ b/docs/modules/json_report/pages/json_schema/content/table.adoc @@ -10,6 +10,8 @@ The structure is composed of the `TableNode` container and the nested `Table` co |=== |Field|Type|Description|Default Value + +|`id`|string| *Optional.* Identifies the table node within the report. Used for internal cross referencing |`null` |`type`|string|Must be set to `"table"`.|`"table"` |`ref`|string|The unique reference of the data file (from the root `data` list) containing the table to be rendered.|*Required* |`caption`|string|*Optional.* Table caption.| `null` diff --git a/docs/modules/json_report/pages/json_schema/content/text.adoc b/docs/modules/json_report/pages/json_schema/content/text.adoc index 34d543fe4..5cc3de0a9 100644 --- a/docs/modules/json_report/pages/json_schema/content/text.adoc +++ b/docs/modules/json_report/pages/json_schema/content/text.adoc @@ -5,6 +5,7 @@ The **Text Node** is used to insert narrative content into your report. It suppo |=== |Field|Type|Description|Default Value +|`id`|string| *Optional.* Identifies the text node within the report. Used for internal cross referencing |`null` |`type`|string|Must be set to `"text"`.|`"text"` |`text`|string or object|The text content or a nested `Text` object.|*Required* |`ref`|string|The `name` of the data field to reference for dynamic placeholder resolution.|`null` diff --git a/docs/modules/json_report/pages/json_schema/referencing.adoc b/docs/modules/json_report/pages/json_schema/referencing.adoc new file mode 100644 index 000000000..f80007b03 --- /dev/null +++ b/docs/modules/json_report/pages/json_schema/referencing.adoc @@ -0,0 +1,82 @@ += Internal Cross References + +It is possible to assign an optional unique identifier to any content node within the JSON report schema. This identifier can then be used to create internal cross-references within the report, allowing for easy navigation between different sections or elements. + +== Assigning Identifiers + +To assign an identifier to a content node, include the `id` field in the node's JSON object. The value of this field should be a unique string that serves as the identifier for that node. +[source,json] +---- +{ + "type": "section", + "id": "introduction", + "title": "Introduction", + "contents": [ + // Other content nodes + ] +} +---- +In this example, the section node is assigned the identifier `"introduction"`. + +[IMPORTANT] +Ensure that each id contains only alphanumeric characters, underscores, or hyphens, and does not start with a number. This ensures compatibility with AsciiDoc cross-referencing conventions. + +== Figure and Table Numbering + +Tables, figures and images are automatically numbered in the order they appear in the report. However, tables will be prefixed with "Table", figures, plots and images will be prefixed with "Figure". This means that automatic numbering will be independent between tables and figures/images. + +[IMPORTANT] +Elements included inside a grid node are NOT automatically numbered. You must manually include numbering in the caption if desired (e.g. "Figure 1a: ..."). The counter for the automatic numbering will not be incremented for these elements. + +== Creating Cross-References + +To create a cross-reference to a content node with an assigned identifier, use the AsciiDoc cross-referencing syntax. The format is `<>`, where `id` is the identifier of the target node, and `Display Text` is the text that will be displayed as the link in the report. + +[source,asciidoc] +---- +See <> for more details. +---- + +In this example, the text "Introduction Section" will be a clickable link that navigates to the section with the identifier `"introduction"`. + +[TIP] +For images, figures and tables, it is possible to reference via their caption without the use of an explicit id (though it is discouraged for clarity). To do so, remove any parentheses from the reference, and replace spaces with hyphens. For example, if a table has the caption "Performance Metrics", you can reference it using `<>`. + +[IMPORTANT] +Ensure that the identifiers used in cross-references match exactly with those assigned to the content nodes, including case sensitivity. Mismatched identifiers will result in broken links in the final report. + +== Example + +[source,json] +---- +{ + "contents": [ + { + "type":"text", + "id":"intro_text", + "text":"Welcome to the report." + }, + { + "type":"section", + "id":"data_analysis", + "title":"Data Analysis", + "contents":[ + { + "type":"table", + "id":"results_table", + "ref":"results_data", + "caption":"Results Overview" + }, + { + "type":"text", + "text":"Refer to <> for context." + } + ] + }, + { + "type":"text", + "text":"See <> for detailed metrics." + } + ] +} +---- \ No newline at end of file diff --git a/docs/modules/json_report/pages/json_schema/report_schema.adoc b/docs/modules/json_report/pages/json_schema/report_schema.adoc index 2712975c6..f85d9efa3 100644 --- a/docs/modules/json_report/pages/json_schema/report_schema.adoc +++ b/docs/modules/json_report/pages/json_schema/report_schema.adoc @@ -58,7 +58,7 @@ include::data.adoc[leveloffset=+1] include::content/section.adoc[leveloffset=+1] -include::content/section.adoc[leveloffset=+1] +include::content/grid.adoc[leveloffset=+1] include::content/text.adoc[leveloffset=+1] @@ -70,4 +70,6 @@ include::content/image.adoc[leveloffset=+1] include::content/table.adoc[leveloffset=+1] -include::content/figure.adoc[leveloffset=+1] \ No newline at end of file +include::content/figure.adoc[leveloffset=+1] + +include::referencing.adoc[leveloffset=+1] \ No newline at end of file From 88246636caf2ffe1b81de7196c9cab4199b51051 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 23 Jan 2026 13:55:07 +0100 Subject: [PATCH 15/69] syntax fix --- docs/modules/json_report/pages/json_schema/referencing.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/modules/json_report/pages/json_schema/referencing.adoc b/docs/modules/json_report/pages/json_schema/referencing.adoc index f80007b03..c9d590204 100644 --- a/docs/modules/json_report/pages/json_schema/referencing.adoc +++ b/docs/modules/json_report/pages/json_schema/referencing.adoc @@ -5,6 +5,7 @@ It is possible to assign an optional unique identifier to any content node withi == Assigning Identifiers To assign an identifier to a content node, include the `id` field in the node's JSON object. The value of this field should be a unique string that serves as the identifier for that node. + [source,json] ---- { @@ -30,7 +31,7 @@ Elements included inside a grid node are NOT automatically numbered. You must ma == Creating Cross-References -To create a cross-reference to a content node with an assigned identifier, use the AsciiDoc cross-referencing syntax. The format is `<>`, where `id` is the identifier of the target node, and `Display Text` is the text that will be displayed as the link in the report. +To create a cross-reference to a content node with an assigned identifier, use the link:https://docs.asciidoctor.org/asciidoc/latest/macros/xref/#internal-cross-references[AsciiDoc cross-referencing syntax]. The format is `\<>`, where `id` is the identifier of the target node, and `Display Text` is the text that will be displayed as the link in the report. [source,asciidoc] ---- @@ -40,7 +41,7 @@ See <> for more details. In this example, the text "Introduction Section" will be a clickable link that navigates to the section with the identifier `"introduction"`. [TIP] -For images, figures and tables, it is possible to reference via their caption without the use of an explicit id (though it is discouraged for clarity). To do so, remove any parentheses from the reference, and replace spaces with hyphens. For example, if a table has the caption "Performance Metrics", you can reference it using `<>`. +For images, figures and tables, it is possible to reference via their caption without the use of an explicit id (though it is discouraged for clarity). To do so, remove any parentheses from the reference, and replace spaces with hyphens. For example, if a table has the caption "Performance Metrics", you can reference it using `\<>`. [IMPORTANT] Ensure that the identifiers used in cross-references match exactly with those assigned to the content nodes, including case sensitivity. Mismatched identifiers will result in broken links in the final report. From 45cef392e289549e9a11f61037bc21ae0feab91a Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 23 Jan 2026 14:16:11 +0100 Subject: [PATCH 16/69] fix parameter table --- examples/matrixvector/plots.json | 2 +- examples/parallelsum/plots.json | 27 ++++++++------------------- examples/sorting/plots.json | 2 +- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/examples/matrixvector/plots.json b/examples/matrixvector/plots.json index 1f3188743..6d98c7ea4 100644 --- a/examples/matrixvector/plots.json +++ b/examples/matrixvector/plots.json @@ -46,7 +46,7 @@ "contents":[ { "type":"table", - "ref": "reframe_df", + "ref": "parameter_table", "layout":{ "rename":{ "testcases.time_total":"Total Time (s)", diff --git a/examples/parallelsum/plots.json b/examples/parallelsum/plots.json index e239a4058..016514012 100644 --- a/examples/parallelsum/plots.json +++ b/examples/parallelsum/plots.json @@ -47,33 +47,22 @@ "contents": [ { "type": "table", - "ref": "reframe_df", + "ref": "parameter_table", "layout": { - "columns": ["result", "testcases.hashcode", "tasks", "elements", "testcases.time_total"], - "computed_columns":{ - "logs_link":"f'link:logs/{row[\"testcases.hashcode\"]}.html[Logs]'" - }, "rename": { "testcases.time_total": "Total Time (s)", "testcases.hashcode": "Hash", "result": "", "logs_link":"" }, - "group_by": { "columns": ["testcases.hashcode"], "agg": "first" }, - "format": { - "testcases.time_total": "%.3f", - "tasks":"%.0f", - "elements":"%.0f", - "result": { "pass": "🟢", "fail": "🔴" } - }, - "column_order": ["result", "testcases.hashcode", "tasks", "elements", "testcases.time_total","logs_link"], - "style": { - "column_align": { "result": "center" }, - "column_width":{ "result":1,"logs_link":1}, - "classnames": ["scrollable", "sortable"] - } + "column_order": ["result", "testcases.hashcode", "tasks", "elements", "testcases.time_total","logs_link"] }, - "filter": { "placeholder": "Filter testcases..." } + "filter": { "placeholder": "Filter testcases..." }, + "style": { + "column_align": { "result": "center" }, + "column_width":{ "result":1,"logs_link":1}, + "classnames": ["scrollable", "sortable"] + } } ] }, diff --git a/examples/sorting/plots.json b/examples/sorting/plots.json index 9e9deb3ed..4fa4cbe99 100644 --- a/examples/sorting/plots.json +++ b/examples/sorting/plots.json @@ -47,7 +47,7 @@ "contents": [ { "type": "table", - "ref": "reframe_df", + "ref": "parameter_table", "layout": { "rename": { "testcases.time_total": "Execution Time (s)", From 2286da5c3b00fc26c6021c4ea0b1f05a3bae0094 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Wed, 28 Jan 2026 15:03:44 +0100 Subject: [PATCH 17/69] mv adoc to own folder --- src/feelpp/benchmarking/json_report/renderer.py | 2 +- .../json_report/templates/{ => adoc}/json2adoc_report.adoc.j2 | 0 .../json_report/templates/{ => adoc}/macros/grid.adoc.j2 | 0 .../json_report/templates/{ => adoc}/macros/image.adoc.j2 | 0 .../json_report/templates/{ => adoc}/macros/itemize.adoc.j2 | 0 .../json_report/templates/{ => adoc}/macros/latex.adoc.j2 | 0 .../json_report/templates/{ => adoc}/macros/plot.adoc.j2 | 0 .../json_report/templates/{ => adoc}/macros/section.adoc.j2 | 0 .../json_report/templates/{ => adoc}/macros/table.adoc.j2 | 0 .../json_report/templates/{ => adoc}/macros/text.adoc.j2 | 0 10 files changed, 1 insertion(+), 1 deletion(-) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/json2adoc_report.adoc.j2 (100%) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/macros/grid.adoc.j2 (100%) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/macros/image.adoc.j2 (100%) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/macros/itemize.adoc.j2 (100%) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/macros/latex.adoc.j2 (100%) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/macros/plot.adoc.j2 (100%) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/macros/section.adoc.j2 (100%) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/macros/table.adoc.j2 (100%) rename src/feelpp/benchmarking/json_report/templates/{ => adoc}/macros/text.adoc.j2 (100%) diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index 12f00ea35..e1f474743 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -36,7 +36,7 @@ def getTemplatePath( self ): #TODO: add more formats here (latex,html,...) else: raise ValueError(f"Output format '{self.output_format}' not supported.") - return os.path.join(os.path.dirname(__file__),"templates"), template_filename + return os.path.join(os.path.dirname(__file__),"templates",self.output_format), template_filename def initRenderer( self) -> TemplateRenderer: diff --git a/src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/json2adoc_report.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 diff --git a/src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/grid.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/macros/grid.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/macros/grid.adoc.j2 diff --git a/src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/macros/image.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 diff --git a/src/feelpp/benchmarking/json_report/templates/macros/itemize.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/itemize.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/macros/itemize.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/macros/itemize.adoc.j2 diff --git a/src/feelpp/benchmarking/json_report/templates/macros/latex.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/latex.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/macros/latex.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/macros/latex.adoc.j2 diff --git a/src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/macros/plot.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 diff --git a/src/feelpp/benchmarking/json_report/templates/macros/section.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/section.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/macros/section.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/macros/section.adoc.j2 diff --git a/src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/macros/table.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 diff --git a/src/feelpp/benchmarking/json_report/templates/macros/text.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/text.adoc.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/templates/macros/text.adoc.j2 rename to src/feelpp/benchmarking/json_report/templates/adoc/macros/text.adoc.j2 From e617e634c6ef1381f4574feb14356ae3e6816a56 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Wed, 28 Jan 2026 16:11:55 +0100 Subject: [PATCH 18/69] init json to latex --- .../benchmarking/json_report/__main__.py | 2 +- .../benchmarking/json_report/renderer.py | 2 ++ .../json_report/tables/controller.py | 19 +++++++--- .../templates/tex/json2tex_report.tex.j2 | 11 ++++++ .../templates/tex/macros/grid.tex.j2 | 0 .../templates/tex/macros/image.tex.j2 | 23 ++++++++++++ .../templates/tex/macros/itemize.tex.j2 | 11 ++++++ .../templates/tex/macros/latex.tex.j2 | 5 +++ .../templates/tex/macros/plot.tex.j2 | 0 .../templates/tex/macros/section.tex.j2 | 36 +++++++++++++++++++ .../templates/tex/macros/table.tex.j2 | 30 ++++++++++++++++ .../templates/tex/macros/text.tex.j2 | 3 ++ .../json_report/text/controller.py | 12 +++++-- 13 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/macros/itemize.tex.j2 create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/macros/latex.tex.j2 create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 diff --git a/src/feelpp/benchmarking/json_report/__main__.py b/src/feelpp/benchmarking/json_report/__main__.py index 48bf813fa..e3ef27f4a 100644 --- a/src/feelpp/benchmarking/json_report/__main__.py +++ b/src/feelpp/benchmarking/json_report/__main__.py @@ -8,7 +8,7 @@ def main_cli(): parser = ArgumentParser( description="Generates structured documents (like AsciiDoc) from a declarative JSON configuration file.", prog='json-report-render' ) parser.add_argument( 'REPORT_FILE', type=str, help='The path to the declarative JSON report configuration file.' ) parser.add_argument( '-o', '--output-dir', type=str, default='.', help='The directory where the final generated report will be saved.') - parser.add_argument( '-f', '--output-format', type=str, default='adoc', choices=['adoc'], help='The format of the final document to be generated. Currently supports: adoc.' ) + parser.add_argument( '-f', '--output-format', type=str, default='adoc', choices=['adoc','tex'], help='The format of the final document to be generated. Currently supports: adoc.' ) parser.add_argument( '-n', '--output-name', type=str,default=None, help='A specific name for the output file (e.g., final_report.adoc). If not provided, the name is inferred from the REPORT_FILE.' ) args = parser.parse_args() diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index e1f474743..1bd47b9ef 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -33,6 +33,8 @@ def getTemplatePath( self ): template_filename = None if self.output_format == "adoc": template_filename = "json2adoc_report.adoc.j2" + elif self.output_format == "tex": + template_filename = "json2tex_report.tex.j2" #TODO: add more formats here (latex,html,...) else: raise ValueError(f"Output format '{self.output_format}' not supported.") diff --git a/src/feelpp/benchmarking/json_report/tables/controller.py b/src/feelpp/benchmarking/json_report/tables/controller.py index 6ae5ae2a9..1d42eff3b 100644 --- a/src/feelpp/benchmarking/json_report/tables/controller.py +++ b/src/feelpp/benchmarking/json_report/tables/controller.py @@ -25,13 +25,24 @@ def generate(self) -> pd.DataFrame: return df - def getColsAlignment(self) -> str: + def getColsAlignment(self,format="adoc") -> str: """ Generate the AsciiDoc cols="..." string based on column_align in style. Defaults to 'left' if not specified. Returns a string like: "c,l,r,l,r" """ - align_map = {"left": "<", "center": "^", "right": ">"} + if format=="adoc": + align_map = {"left": "<", "center": "^", "right": ">"} + default_align = "<" + separator="," + formatter = lambda align,width : f"{align}{width}" + elif format=="tex": + align_map = {"left": "l", "center": "c", "right": "r"} + default_align = "l" + separator=" " + formatter = lambda align,width : f"{align}" + else: + raise NotImplementedError(f"Format not recognized during table column alignment extraction.. : {format}") cols = [] column_align = self.style.column_align @@ -39,9 +50,9 @@ def getColsAlignment(self) -> str: for col in self.layout.column_order or []: a = column_align.get(col) w = column_width.get(col,3) - cols.append(f"{align_map.get(a,'<')}{w}") + cols.append(formatter(align_map.get(a,default_align),w)) - return ",".join(cols) + return separator.join(cols) def _renameColumns(self, df: pd.DataFrame) -> pd.DataFrame: if self.layout.rename: diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 new file mode 100644 index 000000000..2463a0348 --- /dev/null +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -0,0 +1,11 @@ +\documentclass[11pt]{report} + +\usepackage{graphicx} + +\begin{document} + +{% import "macros/section.tex.j2" as section %} + +{{ section.render(report, report_data, 1) }} + +\end{document} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 new file mode 100644 index 000000000..e69de29bb diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 new file mode 100644 index 000000000..04c3e7bce --- /dev/null +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 @@ -0,0 +1,23 @@ +{% macro render(imageNode, data) %} + +{% set src = imageNode.src %} +{% set caption = imageNode.caption or "" %} +{% set alt = imageNode.alt or "" %} +{% set styles = imageNode.style or [] %} + +\begin{figure}[ht!] + \centering + {% if 'img-fluid' in styles %} + \includegraphics[width=\linewidth]{ {{ src }} } + {% else %} + \includegraphics{ {{ src }} } + {% endif %} + {% if caption %} + \caption{ {{ caption }} } + {% endif %} + {% if alt %} + % Alt text: {{ alt }} + {% endif %} +\end{figure} + +{% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/itemize.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/itemize.tex.j2 new file mode 100644 index 000000000..6eb07a37e --- /dev/null +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/itemize.tex.j2 @@ -0,0 +1,11 @@ +{% import "macros/text.tex.j2" as text %} + +{% macro render(listNode, data) %} + +\begin{itemize} +{% for item in listNode.items %} + \item {{ text.render(item, data) }} +{% endfor %} +\end{itemize} + +{% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/latex.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/latex.tex.j2 new file mode 100644 index 000000000..2949688a8 --- /dev/null +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/latex.tex.j2 @@ -0,0 +1,5 @@ +{% macro render(latexNode, data) %} + +{{ latexNode.latex }} + +{% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 new file mode 100644 index 000000000..e69de29bb diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 new file mode 100644 index 000000000..0153e4fcb --- /dev/null +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 @@ -0,0 +1,36 @@ +{% import "macros/text.tex.j2" as text %} +{% import "macros/table.tex.j2" as table %} +{% import "macros/image.tex.j2" as image %} +{% import "macros/plot.tex.j2" as plot %} +{% import "macros/latex.tex.j2" as latex %} +{% import "macros/itemize.tex.j2" as itemize %} +{% import "macros/grid.tex.j2" as grid %} + +{% set handlers = { + "text": text.render, + "image": image.render, + "latex": latex.render, + "table": table.render, + "itemize": itemize.render +} %} + +{% macro render(sectionNode, report_data, level=1) %} + +{% if sectionNode.title %} +{% if level == 1 %}\chapter{ {{ sectionNode.title }} }{% elif level == 2 %}\section{ {{ sectionNode.title }} }{% elif level == 3 %}\subsection{ {{ sectionNode.title }} }{% else %}\paragraph{ {{ sectionNode.title }} }{% endif %} +{% endif %} + +{% for node in sectionNode.contents %} +{% if node.type == "section" %} +{{ render(node, report_data, level + 1) }} +{% elif node.type == "grid" %} +{{ grid.render(node, report_data ) }} +{% else %} +{% set handler = handlers.get(node.type) %} +{% if handler %} +{{ handler(node, report_data.get(node.ref)) }} +{% endif %} +{% endif %} +{% endfor %} + +{% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 new file mode 100644 index 000000000..b6431aefc --- /dev/null +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -0,0 +1,30 @@ +{% import "macros/text.tex.j2" as text %} + + +{%macro prepareCell(content) %}{{TextController({},"").escapeText(text=content,format="tex")}}{%endmacro%} + +{% macro render(tableNode, data) %} + +{% set table_controller = TableController(data.get(tableNode.ref), tableNode.layout, tableNode.style) %} +{% set table = table_controller.generate() %} +{% set cols_align = table_controller.getColsAlignment(format="tex") %} + + +\begin{table}[ht!] + \centering + {% if tableNode.caption %} + \caption{ {{ tableNode.caption }} } + {% endif %} + \label{table:{{ tableNode.ref }}} + \begin{tabular}{ {{ cols_align }} } + \hline + {% for col in table.columns %}{{ prepareCell(col) }}{% if not loop.last %} & {% endif %}{% endfor %} \\ + \hline + {% for row in table.itertuples(index=False) %} + {% for cell in row %}{{ prepareCell(cell) }}{% if not loop.last %} & {% endif %}{% endfor %} \\ + {% endfor %} + \hline + \end{tabular} +\end{table} + +{% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 new file mode 100644 index 000000000..4ac74aaca --- /dev/null +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 @@ -0,0 +1,3 @@ +{% macro render(textNode,data) %} +{{ TextController((data or {}).get(textNode.ref),textNode.text).generate(format="tex") }} +{% endmacro %} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/text/controller.py b/src/feelpp/benchmarking/json_report/text/controller.py index bce8e4d73..b528c0d30 100644 --- a/src/feelpp/benchmarking/json_report/text/controller.py +++ b/src/feelpp/benchmarking/json_report/text/controller.py @@ -7,7 +7,15 @@ def __init__(self, data, text:Text): self.text_config = text self.data = data - def generate(self): + def escapeText(self,text,format="adoc"): + if format == "adoc": + return str(text) + elif format == "tex": + return str(text).replace("_","\_") + else: + raise NotImplementedError(f"format not recognized during text escaping : {format}") + + def generate(self, format="adoc"): if self.text_config.mode == "static": content = self.text_config.content @@ -23,7 +31,7 @@ def generate(self): else: content = self.text_config.content - return content + return self.escapeText(content,format) def _resolvePlaceHolders(self, match): expr = match.group(1) From f216f0161777ccbcc2125a4ea136de9634392f01 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Wed, 28 Jan 2026 16:57:39 +0100 Subject: [PATCH 19/69] don't actually need prepareText :) --- .../json_report/templates/tex/macros/table.tex.j2 | 5 ++--- .../json_report/templates/tex/macros/text.tex.j2 | 2 +- .../benchmarking/json_report/text/controller.py | 12 ++---------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 index b6431aefc..a93989837 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -1,7 +1,6 @@ {% import "macros/text.tex.j2" as text %} -{%macro prepareCell(content) %}{{TextController({},"").escapeText(text=content,format="tex")}}{%endmacro%} {% macro render(tableNode, data) %} @@ -18,10 +17,10 @@ \label{table:{{ tableNode.ref }}} \begin{tabular}{ {{ cols_align }} } \hline - {% for col in table.columns %}{{ prepareCell(col) }}{% if not loop.last %} & {% endif %}{% endfor %} \\ + {% for col in table.columns %}{{ col }}{% if not loop.last %} & {% endif %}{% endfor %} \\ \hline {% for row in table.itertuples(index=False) %} - {% for cell in row %}{{ prepareCell(cell) }}{% if not loop.last %} & {% endif %}{% endfor %} \\ + {% for cell in row %}{{ cell }}{% if not loop.last %} & {% endif %}{% endfor %} \\ {% endfor %} \hline \end{tabular} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 index 4ac74aaca..37eeda3f2 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 @@ -1,3 +1,3 @@ {% macro render(textNode,data) %} -{{ TextController((data or {}).get(textNode.ref),textNode.text).generate(format="tex") }} +{{ TextController((data or {}).get(textNode.ref),textNode.text).generate() }} {% endmacro %} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/text/controller.py b/src/feelpp/benchmarking/json_report/text/controller.py index b528c0d30..bce8e4d73 100644 --- a/src/feelpp/benchmarking/json_report/text/controller.py +++ b/src/feelpp/benchmarking/json_report/text/controller.py @@ -7,15 +7,7 @@ def __init__(self, data, text:Text): self.text_config = text self.data = data - def escapeText(self,text,format="adoc"): - if format == "adoc": - return str(text) - elif format == "tex": - return str(text).replace("_","\_") - else: - raise NotImplementedError(f"format not recognized during text escaping : {format}") - - def generate(self, format="adoc"): + def generate(self): if self.text_config.mode == "static": content = self.text_config.content @@ -31,7 +23,7 @@ def generate(self, format="adoc"): else: content = self.text_config.content - return self.escapeText(content,format) + return content def _resolvePlaceHolders(self, match): expr = match.group(1) From 119a6ded21c9e4136248d67a6f74a94f3088a5f9 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 09:34:36 +0100 Subject: [PATCH 20/69] start latex plots fix --- .../benchmarking/json_report/figures/base.py | 9 +++++++ .../templates/tikz/scatterChart.tex.j2 | 25 ++++--------------- .../json_report/figures/tikzFigures.py | 4 +++ .../templates/tex/json2tex_report.tex.j2 | 11 ++++++++ .../templates/tex/macros/grid.tex.j2 | 3 +++ .../templates/tex/macros/plot.tex.j2 | 12 +++++++++ .../templates/tex/macros/section.tex.j2 | 1 + .../templates/tex/macros/table.tex.j2 | 8 +++--- 8 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/base.py b/src/feelpp/benchmarking/json_report/figures/base.py index c41a09b71..39649d39e 100644 --- a/src/feelpp/benchmarking/json_report/figures/base.py +++ b/src/feelpp/benchmarking/json_report/figures/base.py @@ -1,3 +1,4 @@ +import os from pandas import MultiIndex class Figure: @@ -64,5 +65,13 @@ def createTex(self, df): def createCsvs(self,df): return self.plotly_figure.createCsvs(df) + def writeCsvs(self,df, path="."): + csvs = self.plotly_figure.createCsvs(df) + os.makedirs(path,exist_ok=True) + for csv in csvs: + with open(os.path.join(path,f"{csv.get('title','data')}.csv"),"w") as f: + f.write(csv["data"]) + + def createFigureHtml(self,df): return self.plotly_figure.createHtml(df) \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 index dc0d831b5..0eb2fc556 100644 --- a/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 @@ -1,16 +1,3 @@ -\documentclass[12pt]{article} - -\usepackage{pgf-pie} % For pie charts -\usepackage{currfile} % Required for getting the current file name -\usepackage{tikz} % Required for drawing graphics -\usepackage{pgfplots} -\usepackage{pgfplotstable} -\pgfplotsset{compat=newest} -\usepgfplotslibrary{fillbetween} -\usepackage{underscore} - -\begin{document} - \newcommand{\plot}[2][]{ \begin{tikzpicture} \begin{axis}[ @@ -21,10 +8,10 @@ xticklabels from table={{'{#2}'}}{{'{'}}{{xaxis.parameter}}{{'}'}}, cycle list name=color list, legend style={at={(0.5,-0.1)},anchor=north} ] - {% for var,name in zip(variables,names) %} - {% if var not in fill_lines %} - \addplot table [x expr=\coordindex, y={{ var }}] {{'{#2}'}} ; - \addlegendentry{ {{name}} } + {% for col in columns %} + {% if col not in fill_lines %} + \addplot table [x expr=\coordindex, y={{ col }}] {{'{#2}'}} ; + \addlegendentry{ {{col}} } {% endif %} {% endfor %} @@ -57,6 +44,4 @@ \plot{\data{{1 | inttouniquestr }}} {% endif %} \caption{ {{caption}} } -\end{figure} - -\end{document} \ No newline at end of file +\end{figure} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/figures/tikzFigures.py b/src/feelpp/benchmarking/json_report/figures/tikzFigures.py index ea8fb682a..9599e248b 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikzFigures.py +++ b/src/feelpp/benchmarking/json_report/figures/tikzFigures.py @@ -47,6 +47,8 @@ def createMultiindexFigure(self, df, **args): xaxis = self.config.xaxis, yaxis = self.config.yaxis, caption = self.config.title, + color_axis = self.config.color_axis, + columns = df.columns.tolist(), secondary_axis = self.config.secondary_axis, anim_dimension_values = [str(dim) for dim in anim_dim_values], csv_filenames = [f"{dim}.csv" for dim in anim_dim_values], @@ -63,7 +65,9 @@ def createSimpleFigure(self, df, **args): return self.renderer.template.render( xaxis = self.config.xaxis, yaxis = self.config.yaxis, + color_axis = self.config.color_axis, caption = self.config.title, + columns = df.columns.tolist(), csv_filenames = [f"{self.config.title}.csv"], **args ) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index 2463a0348..a66a112ef 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -1,6 +1,17 @@ \documentclass[11pt]{report} \usepackage{graphicx} +\usepackage{pgf-pie} % For pie charts +\usepackage{currfile} % Required for getting the current file name +\usepackage{tikz} % Required for drawing graphics +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\usepackage{underscore} + +{% for package in report.extra_packages %} +\usepackage{{"{"}}{{package}}{{"}"}} +{% endfor %} +\pgfplotsset{compat=newest} \begin{document} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 index e69de29bb..113e48023 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 @@ -0,0 +1,3 @@ + +{% macro render(gridNode,report_data) %} +{%endmacro%} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 index e69de29bb..7e397059f 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 @@ -0,0 +1,12 @@ +{% macro render(plotNode,data) %} +{% set controller = FiguresController(data.get(plotNode.ref),plotNode.plot) %} +{% for figure in controller.figures %} +{% for subfigure in figure %} + +{%set _ = subfigure.writeCsvs(controller.df,".") %} + +{{subfigure.createTex(controller.df)}} + +{% endfor %} +{% endfor %} +{% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 index 0153e4fcb..283e76698 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 @@ -7,6 +7,7 @@ {% import "macros/grid.tex.j2" as grid %} {% set handlers = { + "plot": plot.render, "text": text.render, "image": image.render, "latex": latex.render, diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 index a93989837..4ac972980 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -11,10 +11,6 @@ \begin{table}[ht!] \centering - {% if tableNode.caption %} - \caption{ {{ tableNode.caption }} } - {% endif %} - \label{table:{{ tableNode.ref }}} \begin{tabular}{ {{ cols_align }} } \hline {% for col in table.columns %}{{ col }}{% if not loop.last %} & {% endif %}{% endfor %} \\ @@ -24,6 +20,10 @@ {% endfor %} \hline \end{tabular} + {% if tableNode.caption %} + \caption{ {{ tableNode.caption }} } + {% endif %} + \label{table:{{ tableNode.ref }}} \end{table} {% endmacro %} From 6881150c15faaced749284d278d40bc46024ffbd Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 09:41:20 +0100 Subject: [PATCH 21/69] fix table from csv --- .../figures/templates/tikz/tableChart.tex.j2 | 14 +------------- .../templates/tex/json2tex_report.tex.j2 | 1 + 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/tableChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/templates/tikz/tableChart.tex.j2 index 3999b24d4..2eebe6ffd 100644 --- a/src/feelpp/benchmarking/json_report/figures/templates/tikz/tableChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/templates/tikz/tableChart.tex.j2 @@ -1,14 +1,4 @@ -\documentclass[12pt]{article} -\usepackage{pgf-pie} % For pie charts -\usepackage{currfile} % Required for getting the current file name -\usepackage{tikz} % Required for drawing graphics -\usepackage{pgfplots} -\usepackage{pgfplotstable} -\pgfplotsset{compat=newest} -\usepackage{underscore} - -\begin{document} {% for fn in csv_filenames %} \pgfplotstableread[col sep=comma]{{'{'}}{{fn}}{{'}'}}\data{{loop.index | inttouniquestr }} @@ -26,6 +16,4 @@ \pgfplotstabletypeset[]{\data{{1 | inttouniquestr }}} {% endif %} \caption{ {{caption}} } -\end{table} - -\end{document} +\end{table} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index a66a112ef..ecb82a85c 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -7,6 +7,7 @@ \usepackage{pgfplots} \usepackage{pgfplotstable} \usepackage{underscore} +\usepgfplotslibrary{fillbetween} {% for package in report.extra_packages %} \usepackage{{"{"}}{{package}}{{"}"}} From d7ef23c7071dbe422a09de937ca5acdef57dd6f7 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 09:42:42 +0100 Subject: [PATCH 22/69] fix grouped bar --- .../templates/tikz/groupedBarChart.tex.j2 | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 index 3b031fdd4..68e447622 100644 --- a/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 @@ -1,14 +1,3 @@ -\documentclass[12pt]{article} - -\usepackage{pgf-pie} % For pie charts -\usepackage{currfile} % Required for getting the current file name -\usepackage{tikz} % Required for drawing graphics -\usepackage{pgfplots} -\usepackage{pgfplotstable} -\pgfplotsset{compat=newest} -\usepackage{underscore} - -\begin{document} \newcommand{\plot}[2][]{ \begin{tikzpicture} @@ -23,9 +12,9 @@ ybar, legend style={at={(0.5,-0.1)},anchor=north} ] - {% for var,name in zip(variables,names) %} - \addplot table [x expr=\coordindex, y={{ var }}] {{'{#2}'}} ; - \addlegendentry{ {{name}} } + {% for col in columns %} + \addplot table [x expr=\coordindex, y={{ col }}] {{'{#2}'}} ; + \addlegendentry{ {{col}} } {% endfor %} \end{axis} @@ -50,5 +39,3 @@ {% endif %} \caption{ {{caption}} } \end{figure} - -\end{document} From 5b1bcc399137f23d990150edffd548616385703d Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 09:49:06 +0100 Subject: [PATCH 23/69] quick fix for stacked bar ( to redesing ) --- .../templates/tikz/stackedBarChart.tex.j2 | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 index 13eced053..975b8cb37 100644 --- a/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 @@ -1,12 +1,3 @@ -\documentclass[12pt]{article} - -\usepackage{pgf-pie} % For pie charts -\usepackage{currfile} % Required for getting the current file name -\usepackage{tikz} % Required for drawing graphics -\usepackage{pgfplots} -\usepackage{pgfplotstable} -\pgfplotsset{compat=newest} -\usepackage{underscore} \makeatletter \newcommand\resetstackedplots{ @@ -15,8 +6,6 @@ \makeatother } -\begin{document} - {% for fn in csv_filenames %} \pgfplotstableread[col sep=comma]{{'{'}}{{fn}}{{'}'}}\data{{loop.index | inttouniquestr }} {% endfor %} @@ -41,10 +30,10 @@ {% if i > 1 %} \resetstackedplots {% endif %} - {% for var,name,color in zip(variables,names, colors) %} - \addplot+[ybar, bar width=0.2,point meta=y,draw=black,fill={{color}}{% if i > 1 %}, forget plot{% endif %} ] table [x expr=\coordindex+0.25*{{i}}, y={{ var }}] {\data{{i | inttouniquestr}}} ; + {% for col,color in zip(columns, colors) %} + \addplot+[ybar, bar width=0.2,point meta=y,draw=black,fill={{color}}{% if i > 1 %}, forget plot{% endif %} ] table [x expr=\coordindex+0.25*{{i}}, y={{ col }}] {\data{{i | inttouniquestr}}} ; {% if i == 1%} - \addlegendentry{ {{name}} } + \addlegendentry{ {{col}} } {% endif %} {% endfor %} {% endfor %} @@ -54,5 +43,3 @@ \caption{ {{caption}} } \end{figure} - -\end{document} From 1f5180af030096b2f9b23d0ac53d49028bb58201 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 11:38:38 +0100 Subject: [PATCH 24/69] fix latex text formatting --- .../templates/tex/json2tex_report.tex.j2 | 3 ++ .../templates/tex/macros/table.tex.j2 | 19 +++++-- .../templates/tex/macros/text.tex.j2 | 2 +- .../json_report/text/controller.py | 49 ++++++++++++++++++- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index ecb82a85c..13e0e349e 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -7,6 +7,9 @@ \usepackage{pgfplots} \usepackage{pgfplotstable} \usepackage{underscore} +\usepackage{amsfonts} +\usepackage{hyperref} +\usepackage{xurl} \usepgfplotslibrary{fillbetween} {% for package in report.extra_packages %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 index 4ac972980..60a129fe6 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -1,6 +1,19 @@ {% import "macros/text.tex.j2" as text %} - +{% macro escapeSpecialChars(text) -%} +{{ text | string + | replace('\\', '\textbackslash{}') + | replace('{', '\{') + | replace('}', '\}') + | replace('#', '\#') + | replace('$', '\$') + | replace('%', '\%') + | replace('&', '\&') + | replace('_', '\_') + | replace('~', '\textasciitilde{}') + | replace('^', '\^{}') +}} +{%- endmacro %} {% macro render(tableNode, data) %} @@ -13,10 +26,10 @@ \centering \begin{tabular}{ {{ cols_align }} } \hline - {% for col in table.columns %}{{ col }}{% if not loop.last %} & {% endif %}{% endfor %} \\ + {% for col in table.columns %}{{ escapeSpecialChars(col) }}{% if not loop.last %} & {% endif %}{% endfor %} \\ \hline {% for row in table.itertuples(index=False) %} - {% for cell in row %}{{ cell }}{% if not loop.last %} & {% endif %}{% endfor %} \\ + {% for cell in row %}{{ escapeSpecialChars(cell) }}{% if not loop.last %} & {% endif %}{% endfor %} \\ {% endfor %} \hline \end{tabular} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 index 37eeda3f2..4ac74aaca 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/text.tex.j2 @@ -1,3 +1,3 @@ {% macro render(textNode,data) %} -{{ TextController((data or {}).get(textNode.ref),textNode.text).generate() }} +{{ TextController((data or {}).get(textNode.ref),textNode.text).generate(format="tex") }} {% endmacro %} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/text/controller.py b/src/feelpp/benchmarking/json_report/text/controller.py index bce8e4d73..999f78932 100644 --- a/src/feelpp/benchmarking/json_report/text/controller.py +++ b/src/feelpp/benchmarking/json_report/text/controller.py @@ -7,7 +7,52 @@ def __init__(self, data, text:Text): self.text_config = text self.data = data - def generate(self): + @staticmethod + def _asciidoc_to_latex_urls(text: str) -> str: + pattern = re.compile(r'([(<]*)(\S+?)\[(.*?)\]([)>.,;!?:]*)') + + def repl(m): + pre, url, label, post = m.groups() + url = url.strip() + label = label.strip() + if label: + replacement = r'\href{' + url + '}{' + label + '}' + else: + replacement = r'\url{' + url + '}' + return pre + replacement + post + + return pattern.sub(repl, text) + + + def formatText(self,text:str,format = "adoc"): + if format == "adoc": + pass + elif format == "tex": + # stem + text = re.sub( r"stem:\[\s*(.*?)\s*\]", r"$\1$", text ) + + #italics + text = re.sub(r"(? Date: Thu, 29 Jan 2026 12:03:35 +0100 Subject: [PATCH 25/69] use DeclareRobustCommand --- .../json_report/figures/templates/tikz/groupedBarChart.tex.j2 | 2 +- .../json_report/figures/templates/tikz/scatterChart.tex.j2 | 2 +- .../json_report/figures/templates/tikz/stackedBarChart.tex.j2 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 index 68e447622..f5a2710c5 100644 --- a/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 @@ -1,5 +1,5 @@ -\newcommand{\plot}[2][]{ +\DeclareRobustCommand{\plot}[2][]{ \begin{tikzpicture} \begin{axis}[ width=\textwidth, height=0.6172\textwidth, diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 index 0eb2fc556..9687da394 100644 --- a/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 @@ -1,4 +1,4 @@ -\newcommand{\plot}[2][]{ +\DeclareRobustCommand{\plot}[2][]{ \begin{tikzpicture} \begin{axis}[ width=\textwidth, height=0.6172\textwidth, diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 index 975b8cb37..73d1bae79 100644 --- a/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 @@ -1,6 +1,6 @@ \makeatletter -\newcommand\resetstackedplots{ +\DeclareRobustCommand\resetstackedplots{ \makeatletter \pgfplots@stacked@isfirstplottrue \makeatother From 39605fae9352189379722f62d4ebdd6603785433 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 14:37:19 +0100 Subject: [PATCH 26/69] work on refs --- .../json_report/templates/tex/json2tex_report.tex.j2 | 4 ++++ .../json_report/templates/tex/macros/image.tex.j2 | 5 +++++ .../json_report/templates/tex/macros/section.tex.j2 | 11 +++++++++-- .../json_report/templates/tex/macros/table.tex.j2 | 8 ++++++-- .../benchmarking/json_report/text/controller.py | 3 +++ 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index 13e0e349e..9892d815d 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -10,7 +10,11 @@ \usepackage{amsfonts} \usepackage{hyperref} \usepackage{xurl} +\usepackage[parfill]{parskip} + +\setlength{\parindent}{0pt} \usepgfplotslibrary{fillbetween} +\pagestyle{fancy} {% for package in report.extra_packages %} \usepackage{{"{"}}{{package}}{{"}"}} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 index 04c3e7bce..0ece06278 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 @@ -18,6 +18,11 @@ {% if alt %} % Alt text: {{ alt }} {% endif %} + {% if imageNode.id %} + \label{ {{imageNode.id}} } + {% elif imageNode.caption %} + \label{ fig:{{imageNode.caption | replace(' ','-')}} } + {% endif %} \end{figure} {% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 index 283e76698..52d494b89 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/section.tex.j2 @@ -15,10 +15,17 @@ "itemize": itemize.render } %} +{% set levelTag = [ "\\chapter","\\section","\\subsection" ]%} + {% macro render(sectionNode, report_data, level=1) %} -{% if sectionNode.title %} -{% if level == 1 %}\chapter{ {{ sectionNode.title }} }{% elif level == 2 %}\section{ {{ sectionNode.title }} }{% elif level == 3 %}\subsection{ {{ sectionNode.title }} }{% else %}\paragraph{ {{ sectionNode.title }} }{% endif %} +{% if level - 1 < levelTag | length %} +{{levelTag[ level -1 ]}}{{'{'}}{% if sectionNode.title %}{{sectionNode.title}}{% endif %}{{'}'}} +{% else %} +\paragraph{{'{'}} {% if sectionNode.title %}{{sectionNode.title}}{% endif %} {{'}'}} +{% endif %} +{%if sectionNode.id %} +\label{{'{'}}{{sectionNode.id}}{{'}'}} {% endif %} {% for node in sectionNode.contents %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 index 60a129fe6..577da51d3 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -1,7 +1,7 @@ {% import "macros/text.tex.j2" as text %} {% macro escapeSpecialChars(text) -%} -{{ text | string +{{ text | string | replace('\\', '\textbackslash{}') | replace('{', '\{') | replace('}', '\}') @@ -36,7 +36,11 @@ {% if tableNode.caption %} \caption{ {{ tableNode.caption }} } {% endif %} - \label{table:{{ tableNode.ref }}} + {% if tableNode.id %} + \label{{'{'}}{{tableNode.id}}{{'}'}} + {% elif tableNode.caption %} + \label{table:{{tableNode.caption | replace(' ','-')}}} + {% endif %} \end{table} {% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/text/controller.py b/src/feelpp/benchmarking/json_report/text/controller.py index 999f78932..01703bddf 100644 --- a/src/feelpp/benchmarking/json_report/text/controller.py +++ b/src/feelpp/benchmarking/json_report/text/controller.py @@ -43,6 +43,9 @@ def formatText(self,text:str,format = "adoc"): #links text = self._asciidoc_to_latex_urls(text) + #references + text = re.sub(r"<<\s*([^\s,>]+)\s*(?:,\s*([^>]+))?\s*>>", lambda m : f"\\hyperlink{{{m.group(1)}}}{{{m.group(2)}}}" if m.group(2) else f"\\ref{{{m.group(1)}}}", text ) + else: raise NotImplementedError(f"Format not recognized in text controller: {format}") From 1f255ef9a8a7e7eee2971079edb2a6f8c72939b0 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 14:38:37 +0100 Subject: [PATCH 27/69] rm pagestyle --- .../json_report/templates/tex/json2tex_report.tex.j2 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index 9892d815d..b15a81bfc 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -14,7 +14,6 @@ \setlength{\parindent}{0pt} \usepgfplotslibrary{fillbetween} -\pagestyle{fancy} {% for package in report.extra_packages %} \usepackage{{"{"}}{{package}}{{"}"}} From a1d17c50072ad8ea974e12443a55e5198dba88c9 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 14:48:29 +0100 Subject: [PATCH 28/69] support remote images --- .../json_report/schemas/jsonReport.py | 20 +++++++++++++++++++ .../templates/tex/macros/image.tex.j2 | 5 ++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py index 950b2ca5a..e4c2d1e15 100644 --- a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py +++ b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py @@ -1,3 +1,4 @@ +import requests, warnings,os from typing import Literal, Union, Optional, List, Dict, Annotated, Callable, Any from pydantic import ValidationError, BaseModel, field_validator, model_validator, Field, ConfigDict from datetime import datetime @@ -29,6 +30,25 @@ class ImageNode(ReportNode): caption: Optional[str] = None alt: Optional[str] = None style: Optional[List[str]] = ["img-fluid"] + is_remote: Optional[bool] = True + + + def downloadImage(self,dirpath:str=".") -> str: + if not self.is_remote: + warnings.warn("downloadImage() used for local image... Will return the src attr") + return self.src + + response = requests.get(self.src) + image_name = os.path.basename(self.src) + if response.status_code != 200: + warnings.warn(f"Could not download image from {self.src}") + return image_name + + os.makedirs(dirpath,exist_ok=True) + image_path = os.path.join(dirpath,image_name) + with open(image_path,"wb") as f: + f.write(response.content) + return image_path class PlotNode(ReportNode): type: Literal["plot"] diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 index 0ece06278..c98def68a 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 @@ -1,6 +1,5 @@ {% macro render(imageNode, data) %} -{% set src = imageNode.src %} {% set caption = imageNode.caption or "" %} {% set alt = imageNode.alt or "" %} {% set styles = imageNode.style or [] %} @@ -8,9 +7,9 @@ \begin{figure}[ht!] \centering {% if 'img-fluid' in styles %} - \includegraphics[width=\linewidth]{ {{ src }} } + \includegraphics[width=\linewidth]{ {{ imageNode.downloadImage(".") }} } {% else %} - \includegraphics{ {{ src }} } + \includegraphics{ {{ imageNode.downloadImage(".") }} } {% endif %} {% if caption %} \caption{ {{ caption }} } From 71f74ace9625ec1cff3b35c8679d6fe64147f8b4 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 15:12:36 +0100 Subject: [PATCH 29/69] add grid support for latex --- .../templates/tex/json2tex_report.tex.j2 | 1 + .../templates/tex/macros/grid.tex.j2 | 44 ++++++++++++++++++- .../templates/tex/macros/image.tex.j2 | 33 +++++++++----- 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index b15a81bfc..100449843 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -10,6 +10,7 @@ \usepackage{amsfonts} \usepackage{hyperref} \usepackage{xurl} +\usepackage{subcaption} \usepackage[parfill]{parskip} \setlength{\parindent}{0pt} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 index 113e48023..fad930d7b 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/grid.tex.j2 @@ -1,3 +1,43 @@ +{% import "macros/image.tex.j2" as image %} +{% import "macros/table.tex.j2" as table %} +{% import "macros/latex.tex.j2" as latex %} +{% import "macros/text.tex.j2" as text %} +{% import "macros/itemize.tex.j2" as itemize %} -{% macro render(gridNode,report_data) %} -{%endmacro%} \ No newline at end of file +{% macro render(gridNode, report_data) %} + +{% set col_width = 1.0 / gridNode.columns %} + +\begin{figure}[ht!] +\centering + +{% for node in gridNode.contents %} +\begin{minipage}[t]{ {{ "%.3f"|format(col_width) }}\linewidth } + +{% if node.type == "image" %} +{{ image.render(node, report_data.get(node.ref), make_block=False) }} +{% elif node.type == "latex" %} +{{ latex.render(node, report_data.get(node.ref)) }} +{% elif node.type == "text" %} +{{ text.render(node, report_data.get(node.ref)) }} +{% elif node.type == "table" %} +{{ table.render(node, report_data.get(node.ref)) }} +{% elif node.type == "itemize" %} +{{ itemize.render(node, report_data.get(node.ref)) }} +{% endif %} +\end{minipage}% +{% if not loop.last %} +\hfill +{% endif %} +{% endfor %} + +{% if gridNode.caption %} +\caption{ {{ gridNode.caption }} } +{% endif %} +{% if gridNode.id %} +\label{fig:{{ gridNode.id }}} +{% endif %} + +\end{figure} + +{% endmacro %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 index c98def68a..03749180f 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 @@ -1,16 +1,19 @@ -{% macro render(imageNode, data) %} +{% macro includeGraphics(imageNode,styles) %} +{% if 'img-fluid' in styles %} +\includegraphics[width=\linewidth]{ {{ imageNode.downloadImage(".") }} } +{% else %} +\includegraphics{ {{ imageNode.downloadImage(".") }} } +{% endif %} +{% endmacro %} +{% macro render(imageNode, data, make_block = True) %} {% set caption = imageNode.caption or "" %} {% set alt = imageNode.alt or "" %} {% set styles = imageNode.style or [] %} - +{% if make_block %} \begin{figure}[ht!] \centering - {% if 'img-fluid' in styles %} - \includegraphics[width=\linewidth]{ {{ imageNode.downloadImage(".") }} } - {% else %} - \includegraphics{ {{ imageNode.downloadImage(".") }} } - {% endif %} + {{-includeGraphics(imageNode,styles)-}} {% if caption %} \caption{ {{ caption }} } {% endif %} @@ -19,9 +22,17 @@ {% endif %} {% if imageNode.id %} \label{ {{imageNode.id}} } - {% elif imageNode.caption %} - \label{ fig:{{imageNode.caption | replace(' ','-')}} } + {% elif caption %} + \label{{'{'}}fig:{{caption | replace(' ','-')}}{{'}'}} {% endif %} \end{figure} - -{% endmacro %} +{%else%} +{{-includeGraphics(imageNode,styles)-}} +{% if caption %}\subcaption{{'{'}}{{caption}}{{'}'}}{% endif %} +{% if imageNode.id %} +\label{ {{imageNode.id}} } +{% elif caption %} +\label{{'{'}}fig:{{caption | replace(' ','-')}}{{'}'}} +{% endif %} +{% endif %} +{% endmacro %} \ No newline at end of file From 63b46f091cc0c449be8e3f68eb2e2896a5237491 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Thu, 29 Jan 2026 17:28:42 +0100 Subject: [PATCH 30/69] rename transformation factory --- .../benchmarking/json_report/figures/figureFactory.py | 4 ++-- .../json_report/figures/transformationFactory.py | 2 +- .../tests/figures/test_transformationFactory.py | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/figureFactory.py b/src/feelpp/benchmarking/json_report/figures/figureFactory.py index dbc66b67c..9b444b97d 100644 --- a/src/feelpp/benchmarking/json_report/figures/figureFactory.py +++ b/src/feelpp/benchmarking/json_report/figures/figureFactory.py @@ -1,4 +1,4 @@ -from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationStrategyFactory +from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationFactory from feelpp.benchmarking.json_report.figures.base import CompositeFigure from feelpp.benchmarking.json_report.figures.tikzFigures import TikzFigure, TikzScatterFigure, TikzGroupedBarFigure, TikzStackedBarFigure, TikzTableFigure @@ -74,7 +74,7 @@ def create(plot_config): Args: plot_config (Plot). Pydantic object with the plot configuration information """ - strategy = TransformationStrategyFactory.create(plot_config) + strategy = TransformationFactory.create(plot_config) figures = [] for plot_type in plot_config.plot_types: if plot_type in ["scatter","marked_scatter"]: diff --git a/src/feelpp/benchmarking/json_report/figures/transformationFactory.py b/src/feelpp/benchmarking/json_report/figures/transformationFactory.py index 2f49b578d..498b0a6a4 100644 --- a/src/feelpp/benchmarking/json_report/figures/transformationFactory.py +++ b/src/feelpp/benchmarking/json_report/figures/transformationFactory.py @@ -162,7 +162,7 @@ def calculate(self,df): pivot["half-optimal"] = (pivot["optimal"] -1) / 2 + 1 return pivot -class TransformationStrategyFactory: +class TransformationFactory: """ Factory class to dispatch concrete transformation strategies""" @staticmethod def create(plot_config): diff --git a/src/feelpp/benchmarking/json_report/tests/figures/test_transformationFactory.py b/src/feelpp/benchmarking/json_report/tests/figures/test_transformationFactory.py index 081afa1de..67beffb00 100644 --- a/src/feelpp/benchmarking/json_report/tests/figures/test_transformationFactory.py +++ b/src/feelpp/benchmarking/json_report/tests/figures/test_transformationFactory.py @@ -1,4 +1,4 @@ -from feelpp.benchmarking.json_report.figures.transformationFactory import PerformanceStrategy, RelativePerformanceStrategy, SpeedupStrategy, TransformationStrategyFactory +from feelpp.benchmarking.json_report.figures.transformationFactory import PerformanceStrategy, RelativePerformanceStrategy, SpeedupStrategy, TransformationFactory import pytest import pandas as pd import numpy as np @@ -35,10 +35,10 @@ def __init__( ]) def test_strategyFactory(transformation,strategy): if strategy: - assert isinstance(TransformationStrategyFactory.create(PlotConfigMocker(transformation=transformation)),strategy) + assert isinstance(TransformationFactory.create(PlotConfigMocker(transformation=transformation)),strategy) else: with pytest.raises(NotImplementedError): - TransformationStrategyFactory.create(PlotConfigMocker(transformation=transformation)) + TransformationFactory.create(PlotConfigMocker(transformation=transformation)) class MockDataframe: def __init__(self,index_type): @@ -80,7 +80,7 @@ class TestSimpleStrategies: def getCalculatedDf(self,transformation): self.plot_config.transformation = transformation - calculated_df = TransformationStrategyFactory.create(self.plot_config).calculate(self.mock_data) + calculated_df = TransformationFactory.create(self.plot_config).calculate(self.mock_data) assert calculated_df.index.name == "xaxis" assert calculated_df.columns.name == "performance_variable" assert calculated_df.isna().sum().sum() == 0 @@ -136,7 +136,7 @@ class TestComplexStrategies: def getCalculatedDf(self,transformation): self.plot_config.transformation = transformation - calculated_df = TransformationStrategyFactory.create(self.plot_config).calculate(self.mock_data) + calculated_df = TransformationFactory.create(self.plot_config).calculate(self.mock_data) assert calculated_df.index.names[0] == "secondary_axis" assert calculated_df.index.names[1] == "xaxis" assert calculated_df.columns.name == "color_axis" From 098b4c0d25c8b7de378350d57c1308760754d246 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 14:19:50 +0100 Subject: [PATCH 31/69] cascade kwargs on render --- .../dashboardRenderer/component/base.py | 8 ++++---- .../dashboardRenderer/component/leaf.py | 6 +++--- .../dashboardRenderer/core/dashboard.py | 13 +++++++++---- .../dashboardRenderer/core/treeBuilder.py | 6 +++--- .../benchmarking/dashboardRenderer/renderer.py | 8 ++++---- .../dashboardRenderer/repository/leaf.py | 4 ++-- .../benchmarking/dashboardRenderer/views/base.py | 8 ++++---- src/feelpp/benchmarking/json_report/renderer.py | 4 ++-- 8 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/feelpp/benchmarking/dashboardRenderer/component/base.py b/src/feelpp/benchmarking/dashboardRenderer/component/base.py index 9f6fbf8a2..86ae12281 100644 --- a/src/feelpp/benchmarking/dashboardRenderer/component/base.py +++ b/src/feelpp/benchmarking/dashboardRenderer/component/base.py @@ -179,7 +179,7 @@ def getPathToRoot( self ) -> List[List["GraphNode"]]: paths.append([self] + parent_path) return paths - def render( self, base_dir:str, parent_id:str = None, renderLeaves = True ) -> None: + def render( self, base_dir:str, parent_id:str = None, renderLeaves = True, **kwargs) -> None: """ Renders the node's view and recursively calls render on its children. @@ -205,13 +205,13 @@ def render( self, base_dir:str, parent_id:str = None, renderLeaves = True ) -> N ) ) - self.view.renderExtra( component_dir ) - self.view.render( component_dir ) + self.view.renderExtra( component_dir, **kwargs ) + self.view.render( component_dir, **kwargs ) for child in self.children: if not renderLeaves and child.isLeaf(): continue - child.render( component_dir, new_parent_id, renderLeaves ) + child.render( component_dir, new_parent_id, renderLeaves, **kwargs ) def upstreamViewData( self, aggregator: Callable[[str, Optional[str], dict, List], dict] ) -> dict: diff --git a/src/feelpp/benchmarking/dashboardRenderer/component/leaf.py b/src/feelpp/benchmarking/dashboardRenderer/component/leaf.py index ae2b6f9c4..18b2f9f47 100644 --- a/src/feelpp/benchmarking/dashboardRenderer/component/leaf.py +++ b/src/feelpp/benchmarking/dashboardRenderer/component/leaf.py @@ -50,7 +50,7 @@ def getPermParentIdsStr( self ) -> str: branches.append( "-".join([b.id for b in branch[1:][::-1]]) ) return ",".join( branches ) - def render( self, base_dir:str ) -> None: + def render( self, base_dir:str, **kwargs ) -> None: """ Renders the leaf component's view in its dedicated subdirectory. @@ -71,9 +71,9 @@ def render( self, base_dir:str ) -> None: ) ) self.view.copyPartials( leaf_dir, os.path.join(base_dir,"..") ) - self.view.renderExtra( leaf_dir ) + self.view.renderExtra( leaf_dir, **kwargs ) - self.view.render( leaf_dir ) + self.view.render( leaf_dir, **kwargs ) def patchTemplateInfo( self, patch : Union[dict,TemplateDataFile], prefix:str, save:bool = False ) -> None: """ diff --git a/src/feelpp/benchmarking/dashboardRenderer/core/dashboard.py b/src/feelpp/benchmarking/dashboardRenderer/core/dashboard.py index 0a0eb7a89..c0aafe4d9 100644 --- a/src/feelpp/benchmarking/dashboardRenderer/core/dashboard.py +++ b/src/feelpp/benchmarking/dashboardRenderer/core/dashboard.py @@ -51,7 +51,7 @@ def print( self ) -> None: """ Prints the hierarchical structure of the component tree for debugging or review.""" self.tree.print() - def render( self, base_path:str, clean:bool = False ): + def render( self, base_path:str, clean:bool = False, **kwargs ): """ Triggers the rendering process, generating the final dashboard files. Args: @@ -59,11 +59,16 @@ def render( self, base_path:str, clean:bool = False ): clean (bool, optional): If True, deletes the existing 'pages' directory before rendering. Defaults to False. """ pages_dir = os.path.join( base_path, "pages" ) + attachments_dir = os.path.join( base_path, "attachments" ) + attachments_base_url = os.path.join( "/benchmarking/_attachments" ) - if clean and os.path.isdir(pages_dir): - shutil.rmtree(pages_dir) + if clean: + if os.path.isdir(pages_dir): + shutil.rmtree(pages_dir) + if os.path.isdir(attachments_dir): + shutil.rmtree(attachments_dir) - self.tree.render(pages_dir) + self.tree.render(pages_dir, attachments_dirpath = attachments_dir, attachments_base_url = attachments_base_url, **kwargs) def patchTemplateInfo( self, patches:List[str], targets:str, prefix:str, save:bool = False ): """ diff --git a/src/feelpp/benchmarking/dashboardRenderer/core/treeBuilder.py b/src/feelpp/benchmarking/dashboardRenderer/core/treeBuilder.py index f927a0893..5622cc499 100644 --- a/src/feelpp/benchmarking/dashboardRenderer/core/treeBuilder.py +++ b/src/feelpp/benchmarking/dashboardRenderer/core/treeBuilder.py @@ -123,7 +123,7 @@ def _buildSubtree( parent_node: TreeNode, subtree: dict, depth:int = 0 ): c.parents.remove(b[-1]) b[-1].children.clear() - def render( self, base_path:str ) -> None: + def render( self, base_path:str, **kwargs) -> None: """ Renders the entire component tree structure to disk. It renders all repository and node pages (via the base class render) and then delegates the rendering of all leaf components to the LeafComponentRepository. @@ -131,8 +131,8 @@ def render( self, base_path:str ) -> None: Args: base_path (str): The root directory where the dashboard will be generated. """ - super().render(base_path,None,renderLeaves=False) - self.leaf_repository.render(base_path) + super().render(base_path,None,renderLeaves=False, **kwargs) + self.leaf_repository.render(base_path, **kwargs) def patchTemplateInfo( self, patches:list[str], targets:str, prefix:str, save:bool = False ): """ diff --git a/src/feelpp/benchmarking/dashboardRenderer/renderer.py b/src/feelpp/benchmarking/dashboardRenderer/renderer.py index a1506454e..0dcac8019 100644 --- a/src/feelpp/benchmarking/dashboardRenderer/renderer.py +++ b/src/feelpp/benchmarking/dashboardRenderer/renderer.py @@ -29,7 +29,7 @@ def __init__( self, template_paths: Union[List[str],str], template_filename:str self.template = self.env.get_template( template_filename ) - def render( self, output_filepath:str, data:Dict[str,Any]={} ) -> None: + def render( self, output_filepath:str, data:Dict[str,Any]={}, **kwargs ) -> None: """ Render the provided data into the primary template and write the output to the specified file path. @@ -42,7 +42,7 @@ def render( self, output_filepath:str, data:Dict[str,Any]={} ) -> None: data.update({"self_dirpath":os.path.dirname(output_filepath)}) with open( output_filepath, 'w' ) as f: - f.write( self.template.render(data) ) + f.write( self.template.render(data,**kwargs) ) def setGlobals( self ) -> None: """Configures global variables available to all templates loaded by this environment.""" @@ -71,7 +71,7 @@ def stripQuotes( value:Any ) -> Any: return value.strip('"') return value - def renderTemplate( self, template_path:str, data:Dict[str,Any], destination:str ) -> None: + def renderTemplate( self, template_path:str, data:Dict[str,Any], destination:str, **kwargs ) -> None: """ Renders an arbitrary, secondary template file within the environment to a specific destination. @@ -87,7 +87,7 @@ def renderTemplate( self, template_path:str, data:Dict[str,Any], destination:str if not os.path.isdir( dest_dir ): os.makedirs( dest_dir ) with open( destination, 'w' ) as f: - f.write( template.render( data ) ) + f.write( template.render( data, **kwargs ) ) class BaseRendererFactory: """ diff --git a/src/feelpp/benchmarking/dashboardRenderer/repository/leaf.py b/src/feelpp/benchmarking/dashboardRenderer/repository/leaf.py index b585a2b42..d1d40a6c9 100644 --- a/src/feelpp/benchmarking/dashboardRenderer/repository/leaf.py +++ b/src/feelpp/benchmarking/dashboardRenderer/repository/leaf.py @@ -50,7 +50,7 @@ def collectMetadata( mapping:Dict[str,Any], path:Optional[List[str]] = [] ) -> L collected += LeafComponentRepository.collectMetadata( v, path + [k] ) return collected - def render( self, base_dir:str ) -> None: + def render( self, base_dir:str, **kwargs ) -> None: """ Renders all Leaf Components stored in the repository. @@ -63,5 +63,5 @@ def render( self, base_dir:str ) -> None: if not os.path.isdir( leaves_dir ): os.mkdir( leaves_dir ) for leaf in self.data: - leaf.render( leaves_dir ) + leaf.render( leaves_dir, **kwargs ) diff --git a/src/feelpp/benchmarking/dashboardRenderer/views/base.py b/src/feelpp/benchmarking/dashboardRenderer/views/base.py index b3425cda0..5f6f9d500 100644 --- a/src/feelpp/benchmarking/dashboardRenderer/views/base.py +++ b/src/feelpp/benchmarking/dashboardRenderer/views/base.py @@ -130,7 +130,7 @@ def copyPartials( self, base_dir:str, pages_dir:str ) -> None: shutil.copytree(path, local_partial_path,dirs_exist_ok=True) self.updateTemplateData({prefix:os.path.relpath(local_partial_path,pages_dir)}) - def renderExtra( self, base_dir:str ) -> None: + def renderExtra( self, base_dir:str, **kwargs) -> None: """ Renders all extra renderers associated with this view. Each extra renderer is expected to have its own rendering logic and output path. @@ -140,12 +140,12 @@ def renderExtra( self, base_dir:str ) -> None: """ extra_renders = [] for prefix,renderer in self.extra_renderers.items(): - output_filepath = renderer.render( base_dir ) + output_filepath = renderer.render( base_dir, **kwargs ) self.updateTemplateData({prefix:os.path.relpath(output_filepath,base_dir)}) extra_renders.append(os.path.relpath(output_filepath,base_dir)) self.updateTemplateData({"extra_renders":extra_renders}) - def render( self, output_dirpath:str, filename:Optional[str] = None ) -> None: + def render( self, output_dirpath:str, filename:Optional[str] = None, **kwargs ) -> None: """ Executes the final rendering step, writing the output to a file. Args: @@ -161,7 +161,7 @@ def render( self, output_dirpath:str, filename:Optional[str] = None ) -> None: if not os.path.isdir(output_dirpath): os.mkdir(output_dirpath) - self.renderer.render(os.path.join(output_dirpath,filename),self.template_data) + self.renderer.render(os.path.join(output_dirpath,filename),self.template_data,**kwargs) def processPlugins( self ) -> None: """ diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index 1bd47b9ef..b6ee9c6e8 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -67,7 +67,7 @@ def loadReportData( self ): return data - def render(self, output_dirpath: str, output_filename:str = None ) -> str: + def render(self, output_dirpath: str, output_filename:str = None, **kwargs ) -> str: if not os.path.exists( output_dirpath ): os.makedirs( output_dirpath ) @@ -76,6 +76,6 @@ def render(self, output_dirpath: str, output_filename:str = None ) -> str: output_filepath = os.path.join( output_dirpath, os.path.basename(output_filename) ) - self.renderer.render( output_filepath, dict(report=self.report, report_data = self.data )) + self.renderer.render( output_filepath, dict(report=self.report, report_data = self.data, **kwargs)) return os.path.abspath(output_filepath) \ No newline at end of file From d4d1530e187fc042646635135dfbd539c32a24dd Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 14:50:49 +0100 Subject: [PATCH 32/69] auto extract project name (not very elegant :/ ) --- .../dashboardRenderer/core/dashboard.py | 4 +- src/feelpp/benchmarking/report/__main__.py | 41 ++++++++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/feelpp/benchmarking/dashboardRenderer/core/dashboard.py b/src/feelpp/benchmarking/dashboardRenderer/core/dashboard.py index c0aafe4d9..e84621e88 100644 --- a/src/feelpp/benchmarking/dashboardRenderer/core/dashboard.py +++ b/src/feelpp/benchmarking/dashboardRenderer/core/dashboard.py @@ -51,7 +51,7 @@ def print( self ) -> None: """ Prints the hierarchical structure of the component tree for debugging or review.""" self.tree.print() - def render( self, base_path:str, clean:bool = False, **kwargs ): + def render( self, base_path:str, clean:bool = False, project_name="", **kwargs ): """ Triggers the rendering process, generating the final dashboard files. Args: @@ -60,7 +60,7 @@ def render( self, base_path:str, clean:bool = False, **kwargs ): """ pages_dir = os.path.join( base_path, "pages" ) attachments_dir = os.path.join( base_path, "attachments" ) - attachments_base_url = os.path.join( "/benchmarking/_attachments" ) + attachments_base_url = os.path.join( f"/{project_name}/_attachments" ) if clean: if os.path.isdir(pages_dir): diff --git a/src/feelpp/benchmarking/report/__main__.py b/src/feelpp/benchmarking/report/__main__.py index e8dac265e..a23243757 100644 --- a/src/feelpp/benchmarking/report/__main__.py +++ b/src/feelpp/benchmarking/report/__main__.py @@ -1,7 +1,7 @@ from feelpp.benchmarking.dashboardRenderer.core.dashboard import Dashboard from feelpp.benchmarking.report.parser import ReportArgParser -import os, subprocess +import os, subprocess, yaml import feelpp.benchmarking from feelpp.benchmarking.report.plugins.reframeReport import ReframeReportPlugin @@ -11,6 +11,40 @@ class VersionPlugin: def process(template_data): return {"feelpp_benchmarking_version":feelpp.benchmarking.__version__} + + +def extractAntoraProjectName(antora_basepath): + if not os.path.isdir(antora_basepath): + raise FileNotFoundError(f"Could not find antora base path. Recieved: {antora_basepath}") + + with open(os.path.join(antora_basepath,"site.yml"),"r") as f: + site_content = yaml.safe_load(f) + + sources = site_content['content']['sources'] + start_path = None + + for source in sources: + if "HEAD" in source.get("branches"): + start_path = source.get("start_path") + + if not start_path: + raise ImportError("Could not find the start_path of the head branch in site.yml") + + antora_yml_path = os.path.join(antora_basepath,start_path,"antora.yml") + with open(antora_yml_path,"r") as f: + antora_yml = yaml.safe_load(f) + + project_name = antora_yml.get("name") + + if not project_name: + raise ImportError(f"Could not find the name field inside in {antora_yml_path}") + + return project_name + + + + + def main_cli(): parser = ReportArgParser() @@ -24,8 +58,11 @@ def main_cli(): dashboard.print() + #MOVE ELSEWHERE + project_name = extractAntoraProjectName(parser.args.antora_basepath) + dashboard.tree.upstreamViewData(ReframeReportPlugin.aggregator) - dashboard.render(parser.args.module_path,clean=parser.args.reset_docs) + dashboard.render(parser.args.module_path,clean=parser.args.reset_docs, project_name = project_name) if parser.args.website: os.chdir(parser.args.antora_basepath) From 6fe2d98cbece9900a962e3018e1936d7e498cb25 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 15:14:08 +0100 Subject: [PATCH 33/69] add pyyaml --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fc727efdc..5acafb30b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,8 @@ dependencies = [ "Jinja2", "numpy", "pandas", - "IPython" + "IPython", + "pyyaml" ] [project.urls] From 0f4b255ed67e9519e0a4ade1c6e21bfcdf60b2ef Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 15:14:16 +0100 Subject: [PATCH 34/69] ignore attachments --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4e95b6f92..3cbdfa021 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ project/report/* #Docs docs/modules/ROOT/pages/ +docs/modules/ROOT/attachments/ !docs/modules/ROOT/pages/index.adoc reports/ reframe/* From 214594c60ea7a4e7d9780b0d43b20d4e4fd034f2 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 16:13:51 +0100 Subject: [PATCH 35/69] separate data from adoc in figures --- .../json_report/figures/controller.py | 131 +++++++++++------- .../benchmarking/json_report/renderer.py | 1 + .../templates/adoc/json2adoc_report.adoc.j2 | 9 +- .../templates/adoc/macros/grid.adoc.j2 | 14 +- .../templates/adoc/macros/image.adoc.j2 | 2 +- .../templates/adoc/macros/itemize.adoc.j2 | 4 +- .../templates/adoc/macros/latex.adoc.j2 | 2 +- .../templates/adoc/macros/plot.adoc.j2 | 49 ++++--- .../templates/adoc/macros/section.adoc.j2 | 8 +- .../templates/adoc/macros/table.adoc.j2 | 2 +- .../templates/adoc/macros/text.adoc.j2 | 2 +- 11 files changed, 137 insertions(+), 87 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/controller.py b/src/feelpp/benchmarking/json_report/figures/controller.py index 0ad4fad02..910249215 100644 --- a/src/feelpp/benchmarking/json_report/figures/controller.py +++ b/src/feelpp/benchmarking/json_report/figures/controller.py @@ -1,54 +1,91 @@ +import os, zipfile +from typing import List, Dict, Union +import pandas as pd +from uuid import uuid4 + +from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationFactory from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory from feelpp.benchmarking.json_report.figures.schemas.plot import Plot -from typing import List, Dict, Union class Controller: - """ Controller component , it orchestrates the model with the view""" - def __init__(self, df, plots_config: Union[List[Dict],Dict,Plot,List[Plot]]): + def __init__(self, data:pd.DataFrame, plot_config: Union[Dict,Plot]): """ - Args: - model (pd.DataFrame): The atomic report model component - view (AtomicReportView): The atomic report view component """ - self.df = df - if isinstance(plots_config,Plot): - self.plots_config = [plots_config] - elif isinstance(plots_config,Dict): - self.plots_config = [Plot(**plots_config)] - elif isinstance(plots_config,List): - if all(isinstance(d,Plot) for d in plots_config): - self.plots_config = plots_config - else: - self.plots_config = [Plot(**d) for d in plots_config] + self.id = uuid4().hex + if not isinstance(data,pd.DataFrame): + raise NotImplementedError(f"Data type {type(data)} not supported for Figures") + + if isinstance(plot_config,Plot): + self.plot_config = plot_config + elif isinstance(plot_config,Dict): + self.plot_config = Plot(**plot_config) else: - raise TypeError(f"plots_config must be a Dict or List of Dicts, got {type(plots_config)}") - self.figures = [FigureFactory.create(plot_config) for plot_config in self.plots_config] - - def generateAll(self): - if self.df is None or self.df.empty: - return [] - return [ - self.generateFigure(figure,plot_config.plot_types) - for figure,plot_config in zip(self.figures,self.plots_config) - ] - - def generateFigure(self,figure,plot_types): - return { - "plot_types": plot_types, - "subfigures": [self.generateSubfigure(subfigure) for subfigure in figure] - } - - def generateSubfigure(self, subfigure): - return { - "exports": [ - { "display_text":"CSV", "data":[ - { "format":"csv", "prefix":"data","content":subfigure.createCsvs(self.df)} - ]}, - { "display_text":"LaTeX", "data":[ - {"format":"tex","content":[{ "data":subfigure.createTex(self.df), "title":"figures" }]}, - {"format":"csv","content":subfigure.createCsvs(self.df)} - - ]}, - ], - "html": subfigure.createFigureHtml(self.df) - } \ No newline at end of file + raise TypeError(f"plot_config must be a Dict or a Plot model, got {type(plot_config)}") + + self.data = data #TransformationFactory.create(self.plot_config).calculate(data) + + self.figures = FigureFactory.create(self.plot_config) + + + def dumpFigureJsons(self,outdir:str = ".") -> str: + """ Returns the path relative to outdir """ + plotly_figs = [fig.createFigure(self.data) for fig in self.figures] + filepaths = [] + for plotly_fig,plot_type in zip(plotly_figs,self.plot_config.plot_types): + fig_relpath = os.path.join(self.id,f"{plot_type}.json") + filepath = os.path.join(outdir,fig_relpath) + + os.makedirs(os.path.dirname(filepath),exist_ok=True) + + with open(filepath,"w") as f: + f.write(plotly_fig.to_json()) + + filepaths.append(fig_relpath) + return filepaths + + + def dumpFigureCsvs(self, outdir:str = ".") -> str: + """ Returns the path relative to outdir """ + filepaths = [] + for figure,plot_type in zip(self.figures,self.plot_config.plot_types): + fig_relpath = os.path.join(self.id,f"{plot_type}.zip") + filepath = os.path.join(outdir,fig_relpath) + + os.makedirs(os.path.dirname(filepath), exist_ok=True) + + csvs = figure.createCsvs(self.data) + with zipfile.ZipFile( file=filepath, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zip_archive: + for csv in csvs: + zip_archive.writestr(zinfo_or_arcname=f"{csv["title"]}.csv",data=csv["data"]) + + filepaths.append(fig_relpath) + return filepaths + + # def generateAll(self): + # if self.df is None or self.df.empty: + # return [] + # return [ + # self.generateFigure(figure,plot_config.plot_types) + # for figure,plot_config in zip(self.figures,self.plots_config) + # ] + + # def generateFigure(self,figure,plot_types): + # return { + # "plot_types": plot_types, + # "subfigures": [self.generateSubfigure(subfigure) for subfigure in figure] + # } + + # def generateSubfigure(self, subfigure): + # return { + # "exports": [ + # { "display_text":"CSV", "data":[ + # { "format":"csv", "prefix":"data","content":subfigure.createCsvs(self.df)} + # ]}, + # { "display_text":"LaTeX", "data":[ + # {"format":"tex","content":[{ "data":subfigure.createTex(self.df), "title":"figures" }]}, + # {"format":"csv","content":subfigure.createCsvs(self.df)} + + # ]}, + # ], + # "html": subfigure.createFigureHtml(self.df) + # } \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index b6ee9c6e8..ebfff9516 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -45,6 +45,7 @@ def initRenderer( self) -> TemplateRenderer: template_path, template_filename = self.getTemplatePath( ) renderer = TemplateRenderer( template_paths=template_path, template_filename=template_filename ) renderer.env.globals.update( { + "zip":zip, "FiguresController":FiguresController, "TableController":TableController, "TextController":TextController diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 index 4fa47bf99..5cf284c39 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 @@ -6,6 +6,11 @@ :docdatetime: {{ report.datetime }} {% endif %} -{% import "macros/section.adoc.j2" as section %} -{{ section.render(report, report_data, 1) }} \ No newline at end of file +{% set context = { + "attachments_dirpath": attachments_dirpath, + "attachments_base_url": attachments_base_url +} %} + +{% import "macros/section.adoc.j2" as section %} +{{ section.render(report, report_data, 1, context=context) }} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/grid.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/grid.adoc.j2 index 1412541b9..f0105a2ed 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/grid.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/grid.adoc.j2 @@ -5,7 +5,7 @@ {% import "macros/table.adoc.j2" as table %} {% import "macros/itemize.adoc.j2" as itemize %} -{% macro render(gridNode,report_data) %} +{% macro render(gridNode,report_data, context={}) %} {% if gridNode.id %} [[{{gridNode.id}}]] {% endif %} @@ -19,17 +19,17 @@ :figure-caption!: {%for node in gridNode.contents%} {% if node.type == "image" %} -{{ image.render(node,report_data.get(node.ref), make_block=False) }} +{{ image.render(node,report_data.get(node.ref), make_block=False, context=context) }} {% elif node.type == "plot" %} -{{ plot.render(node,report_data.get(node.ref), make_block=False) }} +{{ plot.render(node,report_data.get(node.ref), make_block=False, context=context) }} {% elif node.type == "latex" %} -{{ latex.render(node,report_data.get(node.ref)) }} +{{ latex.render(node,report_data.get(node.ref), context=context) }} {% elif node.type == "text" %} -{{ text.render(node,report_data.get(node.ref)) }} +{{ text.render(node,report_data.get(node.ref), context=context) }} {% elif node.type == "table" %} -{{ table.render(node,report_data.get(node.ref)) }} +{{ table.render(node,report_data.get(node.ref), context=context) }} {% elif node.type == "itemize" %} -{{ itemize.render(node,report_data.get(node.ref)) }} +{{ itemize.render(node,report_data.get(node.ref), context=context) }} {% endif %} {% endfor %} :figure-caption: Figure diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 index bf5305e7f..10d7efc29 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 @@ -1,4 +1,4 @@ -{% macro render(imageNode,data,make_block = True) %} +{% macro render(imageNode,data,make_block = True, context={}) %} {% set src = imageNode.src %} {% set alt = imageNode.alt or imageNode.caption or "" %} {% set styles = imageNode.style | join(' ')%} diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/itemize.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/itemize.adoc.j2 index e828e3dd2..fc30938c8 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/itemize.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/itemize.adoc.j2 @@ -1,12 +1,12 @@ {% import "macros/text.adoc.j2" as text %} -{% macro render(listNode,data) %} +{% macro render(listNode,data,context={}) %} {% if listNode.id %} [[{{listNode.id}}]] {% endif %} {% for item in listNode.items %} -- {{text.render(item,data)}} +- {{text.render(item,data,context=context)}} {% endfor %} {% endmacro %} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/latex.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/latex.adoc.j2 index 795bb455c..6bc512afc 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/latex.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/latex.adoc.j2 @@ -1,4 +1,4 @@ -{% macro render(latexNode,data) %} +{% macro render(latexNode,data,context={}) %} {% if latexNode.id %} [[{{latexNode.id}}]] diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 index 3d1611787..a8b991098 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 @@ -1,4 +1,4 @@ -{% macro render(plotNode,data, make_block=True) %} +{% macro render(plotNode,data, make_block=True, context={}) %} {% if plotNode.id %} @@ -15,38 +15,45 @@ {% endif %} ++++ -{% for figure in FiguresController(data.get(plotNode.ref),plotNode.plot).generateAll() %} +{% set figure_ctrl = FiguresController(data.get(plotNode.ref),plotNode.plot) %} +{% set figure_json_paths = figure_ctrl.dumpFigureJsons(context.attachments_dirpath) %} +{% set figure_csv_paths = figure_ctrl.dumpFigureCsvs(context.attachments_dirpath) %}
- {% if figure.plot_types | length > 1%} + {% if figure_ctrl.plot_config.plot_types | length > 1%}
- {% for plot_type in figure.plot_types %} + {% for plot_type in figure_ctrl.plot_config.plot_types %} {% set plot_type_i = loop.index %} {% endfor %}
{% endif %} - {% for subfigure in figure.subfigures %} -
- {{subfigure.html}} - -
- {% for export in subfigure.exports %} - - {% endfor %} -
- +{% for subfigure_json_path, subfigure_csv_path in zip(figure_json_paths,figure_csv_paths) %} +
+
- {% endfor %} - - {%if plotNode.caption and make_block==False %} -
{{plotNode.caption}}
- {% endif %} + +{% endfor %}
+ + + +{%if plotNode.caption and make_block==False %} +
{{plotNode.caption}}
+{% endif %} + ++++ {% if make_block %} diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/section.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/section.adoc.j2 index 2b801fe0b..cbfb819f8 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/section.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/section.adoc.j2 @@ -15,7 +15,7 @@ "itemize": itemize.render } %} -{% macro render(sectionNode, report_data, level=1) %} +{% macro render(sectionNode, report_data, level=1, context={}) %} {% if sectionNode.id %} [[{{sectionNode.id}}]] @@ -26,13 +26,13 @@ {% for node in sectionNode.contents %} {% if node.type == "section" %} -{{ render(node, report_data, level + 1) }} +{{ render(node, report_data, level + 1,context=context) }} {% elif node.type == "grid" %} -{{ grid.render(node, report_data ) }} +{{ grid.render(node, report_data,context=context) }} {% else %} {% set handler = handlers.get(node.type) %} {% if handler %} -{{ handler(node,report_data.get(node.ref)) }} +{{ handler(node,report_data.get(node.ref),context=context) }} {% endif %} {% endif %} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 index 58506cbf7..418d7db50 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 @@ -1,4 +1,4 @@ -{% macro render(tableNode, data) %} +{% macro render(tableNode, data, context={}) %} {%set table_controller = TableController(data.get(tableNode.ref),tableNode.layout, tableNode.style) %} {%set table = table_controller.generate() %} diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/text.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/text.adoc.j2 index e952e14d7..bd0ff6f74 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/text.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/text.adoc.j2 @@ -1,4 +1,4 @@ -{% macro render(textNode,data) %} +{% macro render(textNode,data,context={}) %} {%if textNode.id %} [[{{textNode.id}}]] {% endif %} From 94b797f47ea7447d2834df81a4f186e4a3226f02 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 16:33:41 +0100 Subject: [PATCH 36/69] add default attachments_dirpath --- src/feelpp/benchmarking/json_report/renderer.py | 15 +++++++++++++-- .../json_report/tests/test_jsonReport.py | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index ebfff9516..c63a0881d 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -68,7 +68,7 @@ def loadReportData( self ): return data - def render(self, output_dirpath: str, output_filename:str = None, **kwargs ) -> str: + def render(self, output_dirpath: str, output_filename:str = None, attachments_dirpath:str=None, **kwargs ) -> str: if not os.path.exists( output_dirpath ): os.makedirs( output_dirpath ) @@ -77,6 +77,17 @@ def render(self, output_dirpath: str, output_filename:str = None, **kwargs ) -> output_filepath = os.path.join( output_dirpath, os.path.basename(output_filename) ) - self.renderer.render( output_filepath, dict(report=self.report, report_data = self.data, **kwargs)) + if not attachments_dirpath: + attachments_dirpath = os.path.dirname(output_filepath) + + self.renderer.render( + output_filepath, + dict( + report=self.report, + report_data = self.data, + attachments_dirpath = attachments_dirpath, + **kwargs + ) + ) return os.path.abspath(output_filepath) \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py b/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py index 00eedfca1..2e06ae67f 100644 --- a/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py +++ b/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py @@ -54,6 +54,6 @@ def test_jsonReportCases(report_filename,output_format="adoc"): ) with tempfile.TemporaryDirectory() as tmpdir: - output_file = controller.render(output_dirpath=os.path.join(data_dir,tmpdir)) + output_file = controller.render(output_dirpath=os.path.join(data_dir,"outputs")) golden_file = os.path.join(data_dir,"golden",report_filename.replace(".json",f".{output_format}")) assert_report_matches_golden(output_file,golden_file) \ No newline at end of file From 6c99ec54c281daf41340240e139e129e22439e58 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 16:34:39 +0100 Subject: [PATCH 37/69] syntax bug --- .../benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 index a8b991098..b683d8891 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 @@ -33,7 +33,7 @@
{% endfor %} From 67a365147c1346b669a0a7cc7ae62b6b1b7418bd Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 16:40:04 +0100 Subject: [PATCH 38/69] fix export container --- .../json_report/templates/adoc/macros/plot.adoc.j2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 index b683d8891..5d6aa0311 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 @@ -31,9 +31,9 @@ {% for subfigure_json_path, subfigure_csv_path in zip(figure_json_paths,figure_csv_paths) %}
-
-
- CSV +
+ CSV +
{% endfor %} From db4ee41705771d7a368fbcbbb3a46319b161d94a Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 16:48:21 +0100 Subject: [PATCH 39/69] data is default attachments dir inside rendered dir --- src/feelpp/benchmarking/json_report/renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index c63a0881d..b9752f85e 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -78,7 +78,7 @@ def render(self, output_dirpath: str, output_filename:str = None, attachments_di output_filepath = os.path.join( output_dirpath, os.path.basename(output_filename) ) if not attachments_dirpath: - attachments_dirpath = os.path.dirname(output_filepath) + attachments_dirpath = os.path.join(os.path.dirname(output_filepath),"data") self.renderer.render( output_filepath, From b612a79374caaf0552ab2824f4ea1c21f3d62520 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 16:48:49 +0100 Subject: [PATCH 40/69] fix tests --- .../tests/data/golden/plot_features.adoc | 61 ++++++++++++------- .../json_report/tests/data/golden/report.adoc | 40 +++++++----- .../json_report/tests/test_jsonReport.py | 23 +++---- 3 files changed, 76 insertions(+), 48 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc index eef395eda..fa470923b 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc @@ -9,18 +9,25 @@
-
-
-
FIGURE
- -
+
+
- - + CSV
+ + + + ++++ ==== @@ -37,32 +44,40 @@
-
-
-
FIGURE
- -
+ +
+
- - + CSV
-
-
-
-
FIGURE
- -
+
+
- - + CSV
-
+
+ + + ++++ ==== diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc index 0146176db..532508747 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc @@ -101,18 +101,24 @@ Placeholders in this report allow dynamic insertion of values from the datasets.
-
-
-
FIGURE
- -
+
+
- - + CSV
+ + + ++++ ==== @@ -123,18 +129,24 @@ Placeholders in this report allow dynamic insertion of values from the datasets.
-
-
-
FIGURE
- -
+
+
- - + CSV
+ + + ++++ ==== diff --git a/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py b/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py index 2e06ae67f..391180d23 100644 --- a/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py +++ b/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py @@ -8,20 +8,21 @@ def normalizeReport(content: str) -> str: content = re.sub( r"^:docdatetime: .*?$", ":docdatetime: ", content, flags=re.MULTILINE ) content = re.sub( - r"]*?>\s*(.*?)\s*", - lambda match : f"", + r"graph-div-.*?-", + "", content, flags=re.DOTALL ) content = re.sub( - r"]*>\s*]*>
\s*\s*
", - """ -
-
FIGURE
- -
""", - content, - flags=re.DOTALL + r"CSV", + "", + content, flags=re.DOTALL ) + content = re.sub( + r"fetch\('.*?'\)", + "", + content, flags=re.DOTALL + ) + lines = [line.rstrip() for line in content.splitlines() if line.strip()] return "\n".join(lines) @@ -54,6 +55,6 @@ def test_jsonReportCases(report_filename,output_format="adoc"): ) with tempfile.TemporaryDirectory() as tmpdir: - output_file = controller.render(output_dirpath=os.path.join(data_dir,"outputs")) + output_file = controller.render(output_dirpath=os.path.join(data_dir,tmpdir)) golden_file = os.path.join(data_dir,"golden",report_filename.replace(".json",f".{output_format}")) assert_report_matches_golden(output_file,golden_file) \ No newline at end of file From 61fb9de3d3c4721312932f53dc0b17c2404af487 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 16:52:32 +0100 Subject: [PATCH 41/69] quotes fix --- src/feelpp/benchmarking/json_report/figures/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feelpp/benchmarking/json_report/figures/controller.py b/src/feelpp/benchmarking/json_report/figures/controller.py index 910249215..bedcd2a05 100644 --- a/src/feelpp/benchmarking/json_report/figures/controller.py +++ b/src/feelpp/benchmarking/json_report/figures/controller.py @@ -56,7 +56,7 @@ def dumpFigureCsvs(self, outdir:str = ".") -> str: csvs = figure.createCsvs(self.data) with zipfile.ZipFile( file=filepath, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zip_archive: for csv in csvs: - zip_archive.writestr(zinfo_or_arcname=f"{csv["title"]}.csv",data=csv["data"]) + zip_archive.writestr(zinfo_or_arcname=f"{csv['title']}.csv",data=csv['data']) filepaths.append(fig_relpath) return filepaths From a44bd707299f3eb097c56f77cc5c195f8af3aa97 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Fri, 30 Jan 2026 17:24:07 +0100 Subject: [PATCH 42/69] some advancements on adding a latex render button Still need to fix report uuids --- .../pages/json_schema/content/figure.adoc | 8 ++++---- .../tutorial/pages/configurationfiles/plots.adoc | 8 ++++---- src/feelpp/benchmarking/json_report/renderer.py | 16 ++++++++++++++++ .../templates/adoc/json2adoc_report.adoc.j2 | 10 +++++++++- .../templates/tex/json2tex_report.tex.j2 | 7 ++++++- .../json_report/templates/tex/macros/grid.tex.j2 | 12 ++++++------ .../templates/tex/macros/image.tex.j2 | 12 ++++++------ .../templates/tex/macros/itemize.tex.j2 | 4 ++-- .../templates/tex/macros/latex.tex.j2 | 2 +- .../json_report/templates/tex/macros/plot.tex.j2 | 8 +++----- .../templates/tex/macros/section.tex.j2 | 8 ++++---- .../templates/tex/macros/table.tex.j2 | 2 +- .../json_report/templates/tex/macros/text.tex.j2 | 2 +- src/feelpp/benchmarking/report/__main__.py | 2 +- 14 files changed, 64 insertions(+), 37 deletions(-) diff --git a/docs/modules/json_report/pages/json_schema/content/figure.adoc b/docs/modules/json_report/pages/json_schema/content/figure.adoc index ff85915c7..fb78f292d 100644 --- a/docs/modules/json_report/pages/json_schema/content/figure.adoc +++ b/docs/modules/json_report/pages/json_schema/content/figure.adoc @@ -137,7 +137,7 @@ For the given example, it produces the following dataframe [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationStrategyFactory +from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationFactory from feelpp.benchmarking.json_report.figures.schemas.plot import Plot plot_config = Plot(**{ "title": "Absolute performance", @@ -147,7 +147,7 @@ plot_config = Plot(**{ "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{"parameter":"elements", "label":"N"} }) -strategy = TransformationStrategyFactory.create(plot_config) +strategy = TransformationFactory.create(plot_config) df = strategy.calculate(master_df) print(df) ---- @@ -168,7 +168,7 @@ plot_config = Plot(**{ "secondary_axis":{ "parameter":"elements", "label":"N" } }) -strategy = TransformationStrategyFactory.create(plot_config) +strategy = TransformationFactory.create(plot_config) df = strategy.calculate(master_df) print(df) ---- @@ -191,7 +191,7 @@ plot_config = Plot(**{ "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{"parameter":"elements", "label":"N"} }) -strategy = TransformationStrategyFactory.create(plot_config) +strategy = TransformationFactory.create(plot_config) df = strategy.calculate(master_df) print(df) ---- diff --git a/docs/modules/tutorial/pages/configurationfiles/plots.adoc b/docs/modules/tutorial/pages/configurationfiles/plots.adoc index a2f94ed81..7044ce7cb 100644 --- a/docs/modules/tutorial/pages/configurationfiles/plots.adoc +++ b/docs/modules/tutorial/pages/configurationfiles/plots.adoc @@ -116,7 +116,7 @@ For the given example, it produces the following dataframe [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationStrategyFactory +from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationFactory from feelpp.benchmarking.reframe.schemas.benchmarkSchemas import DefaultPlot plot_config = DefaultPlot(**{ "title": "Absolute performance", @@ -125,7 +125,7 @@ plot_config = DefaultPlot(**{ "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"} }) -strategy = TransformationStrategyFactory.create(plot_config) +strategy = TransformationFactory.create(plot_config) df = strategy.calculate(master_df) print(df) ---- @@ -146,7 +146,7 @@ plot_config = DefaultPlot(**{ "secondary_axis":{ "parameter":"elements", "label":"N" } }) -strategy = TransformationStrategyFactory.create(plot_config) +strategy = TransformationFactory.create(plot_config) df = strategy.calculate(master_df) print(df) ---- @@ -168,7 +168,7 @@ plot_config = DefaultPlot(**{ "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"} }) -strategy = TransformationStrategyFactory.create(plot_config) +strategy = TransformationFactory.create(plot_config) df = strategy.calculate(master_df) print(df) ---- diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index b9752f85e..690ac6b36 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -19,6 +19,21 @@ def __init__(self, report_filepath: str, output_format:str = "adoc") -> None: self.exposed:dict = dict() self.data:dict = self.loadReportData() + + @classmethod + def initFromLoaded( cls, report: JsonReportSchema, report_data:dict, output_format:str="adoc" )->"JsonReportController": + json_report_ctrl = cls.__new__(cls) + json_report_ctrl.data = report_data + json_report_ctrl.report = report + json_report_ctrl.output_format = output_format + json_report_ctrl.report_filepath = None + + json_report_ctrl.exposed = {} + + json_report_ctrl.renderer = json_report_ctrl.initRenderer() + + return json_report_ctrl + def loadReport( self, report_filepath: str ) -> JsonReportSchema: if not os.path.exists( report_filepath ): warnings.warn(f"Report file '{report_filepath}' does not exist.") @@ -46,6 +61,7 @@ def initRenderer( self) -> TemplateRenderer: renderer = TemplateRenderer( template_paths=template_path, template_filename=template_filename ) renderer.env.globals.update( { "zip":zip, + "JsonReportController":JsonReportController, "FiguresController":FiguresController, "TableController":TableController, "TextController":TextController diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 index 5cf284c39..43809dc3f 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 @@ -9,8 +9,16 @@ {% set context = { "attachments_dirpath": attachments_dirpath, - "attachments_base_url": attachments_base_url + "attachments_base_url": attachments_base_url, } %} +{% if include_latex %} +{%set json_report_ctrl = JsonReportController.initFromLoaded( report, report_data, output_format="tex" )%} +{% set rendered_latex_path = json_report_ctrl.render(attachments_dirpath,"report.tex",attachments_dirpath=attachments_dirpath~"/data" ) %} +++++ + Date: Mon, 2 Feb 2026 08:34:46 +0100 Subject: [PATCH 43/69] up version to current --- CITATION.cff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CITATION.cff b/CITATION.cff index a1dd5d448..c55a49445 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -14,7 +14,7 @@ authors: given-names: "Vincent" affiliation: "Feel++ Consortium" orcid: "https://orcid.org/0009-0005-3602-3524" -version: "3.0.2" +version: "4.0.0" doi: "10.5281/zenodo.15013241" date-released: "2025-03-12" url: "https://github.com/feelpp/benchmarking" From 96f5230023dd947741c0099c1f2506339f8cf631 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Mon, 2 Feb 2026 09:33:33 +0100 Subject: [PATCH 44/69] save attachments with report uuid --- .../json_report/figures/controller.py | 15 ++++++++++++--- src/feelpp/benchmarking/json_report/renderer.py | 5 +++-- .../templates/adoc/json2adoc_report.adoc.j2 | 11 ++--------- .../templates/adoc/macros/plot.adoc.j2 | 2 +- .../json_report/templates/tex/macros/plot.tex.j2 | 2 +- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/controller.py b/src/feelpp/benchmarking/json_report/figures/controller.py index bedcd2a05..fa883144b 100644 --- a/src/feelpp/benchmarking/json_report/figures/controller.py +++ b/src/feelpp/benchmarking/json_report/figures/controller.py @@ -8,9 +8,10 @@ from feelpp.benchmarking.json_report.figures.schemas.plot import Plot class Controller: - def __init__(self, data:pd.DataFrame, plot_config: Union[Dict,Plot]): + def __init__(self, data:pd.DataFrame, plot_config: Union[Dict,Plot], report_uuid:str = None): """ """ + self.report_uuid = report_uuid self.id = uuid4().hex if not isinstance(data,pd.DataFrame): raise NotImplementedError(f"Data type {type(data)} not supported for Figures") @@ -32,7 +33,11 @@ def dumpFigureJsons(self,outdir:str = ".") -> str: plotly_figs = [fig.createFigure(self.data) for fig in self.figures] filepaths = [] for plotly_fig,plot_type in zip(plotly_figs,self.plot_config.plot_types): - fig_relpath = os.path.join(self.id,f"{plot_type}.json") + if self.report_uuid: + fig_relpath = os.path.join(self.report_uuid,self.id,f"{plot_type}.json") + else: + fig_relpath = os.path.join(self.id,f"{plot_type}.json") + filepath = os.path.join(outdir,fig_relpath) os.makedirs(os.path.dirname(filepath),exist_ok=True) @@ -48,7 +53,11 @@ def dumpFigureCsvs(self, outdir:str = ".") -> str: """ Returns the path relative to outdir """ filepaths = [] for figure,plot_type in zip(self.figures,self.plot_config.plot_types): - fig_relpath = os.path.join(self.id,f"{plot_type}.zip") + if self.report_uuid: + fig_relpath = os.path.join(self.report_uuid,self.id,f"{plot_type}.zip") + else: + fig_relpath = os.path.join(self.id,f"{plot_type}.zip") + filepath = os.path.join(outdir,fig_relpath) os.makedirs(os.path.dirname(filepath), exist_ok=True) diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index 690ac6b36..7aba2b91f 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -1,5 +1,5 @@ import json, os, warnings -import pandas as pd +from uuid import uuid4 from feelpp.benchmarking.jsonWithComments import JSONWithCommentsDecoder from feelpp.benchmarking.json_report.schemas.jsonReport import JsonReportSchema @@ -18,6 +18,7 @@ def __init__(self, report_filepath: str, output_format:str = "adoc") -> None: self.exposed:dict = dict() self.data:dict = self.loadReportData() + self.id = uuid4().hex @classmethod @@ -61,7 +62,6 @@ def initRenderer( self) -> TemplateRenderer: renderer = TemplateRenderer( template_paths=template_path, template_filename=template_filename ) renderer.env.globals.update( { "zip":zip, - "JsonReportController":JsonReportController, "FiguresController":FiguresController, "TableController":TableController, "TextController":TextController @@ -99,6 +99,7 @@ def render(self, output_dirpath: str, output_filename:str = None, attachments_di self.renderer.render( output_filepath, dict( + uuid = self.id, report=self.report, report_data = self.data, attachments_dirpath = attachments_dirpath, diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 index 43809dc3f..8d999a63a 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 @@ -8,17 +8,10 @@ {% set context = { + "uuid": uuid, "attachments_dirpath": attachments_dirpath, - "attachments_base_url": attachments_base_url, + "attachments_base_url": attachments_base_url } %} -{% if include_latex %} -{%set json_report_ctrl = JsonReportController.initFromLoaded( report, report_data, output_format="tex" )%} -{% set rendered_latex_path = json_report_ctrl.render(attachments_dirpath,"report.tex",attachments_dirpath=attachments_dirpath~"/data" ) %} -++++ - diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 index cc71ce85d..ed1e31ecd 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 @@ -1,5 +1,5 @@ {% macro render(plotNode,data,context={}) %} -{% set controller = FiguresController(data.get(plotNode.ref),plotNode.plot) %} +{% set controller = FiguresController(data.get(plotNode.ref),plotNode.plot,context.uuid) %} {% for figure in controller.figures %} {%set _ = figure.writeCsvs(controller.data,context.attachments_dirpath) %} From b90584c7c68a95c501dbab9d8e2c488843bee4bd Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Mon, 2 Feb 2026 15:51:52 +0100 Subject: [PATCH 45/69] simplify figures to json + latex --- .../benchmarking/json_report/figures/base.py | 42 +---- .../json_report/figures/controller.py | 152 ++++++++---------- .../json_report/figures/figureFactory.py | 107 ------------ .../figures/plotly/figureFactory.py | 40 +++++ .../figures/{ => plotly}/plotlyFigures.py | 117 +++++++------- .../json_report/figures/tikz/figureFactory.py | 25 +++ .../templates}/groupedBarChart.tex.j2 | 0 .../templates}/scatterChart.tex.j2 | 0 .../templates}/stackedBarChart.tex.j2 | 0 .../tikz => tikz/templates}/tableChart.tex.j2 | 0 .../figures/{ => tikz}/tikzFigures.py | 41 ++--- .../templates/adoc/macros/plot.adoc.j2 | 52 +++--- .../templates/tex/json2tex_download.tex.j2 | 6 + .../templates/tex/json2tex_report.tex.j2 | 1 + .../templates/tex/macros/plot.tex.j2 | 23 ++- .../tests/figures/test_figureFactory.py | 33 ---- .../tests/test_jsonReportController.py | 1 + 17 files changed, 281 insertions(+), 359 deletions(-) delete mode 100644 src/feelpp/benchmarking/json_report/figures/figureFactory.py create mode 100644 src/feelpp/benchmarking/json_report/figures/plotly/figureFactory.py rename src/feelpp/benchmarking/json_report/figures/{ => plotly}/plotlyFigures.py (83%) create mode 100644 src/feelpp/benchmarking/json_report/figures/tikz/figureFactory.py rename src/feelpp/benchmarking/json_report/figures/{templates/tikz => tikz/templates}/groupedBarChart.tex.j2 (100%) rename src/feelpp/benchmarking/json_report/figures/{templates/tikz => tikz/templates}/scatterChart.tex.j2 (100%) rename src/feelpp/benchmarking/json_report/figures/{templates/tikz => tikz/templates}/stackedBarChart.tex.j2 (100%) rename src/feelpp/benchmarking/json_report/figures/{templates/tikz => tikz/templates}/tableChart.tex.j2 (100%) rename src/feelpp/benchmarking/json_report/figures/{ => tikz}/tikzFigures.py (70%) create mode 100644 src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 delete mode 100644 src/feelpp/benchmarking/json_report/tests/figures/test_figureFactory.py diff --git a/src/feelpp/benchmarking/json_report/figures/base.py b/src/feelpp/benchmarking/json_report/figures/base.py index 39649d39e..432fcf3c2 100644 --- a/src/feelpp/benchmarking/json_report/figures/base.py +++ b/src/feelpp/benchmarking/json_report/figures/base.py @@ -2,9 +2,8 @@ from pandas import MultiIndex class Figure: - def __init__(self,plot_config,transformation_strategy): + def __init__(self,plot_config): self.config = plot_config - self.transformation_strategy = transformation_strategy def getIdealRange(self,df): """ Computes the [(min - eps), (max+eps)] interval for optimal y-axis display @@ -15,10 +14,10 @@ def getIdealRange(self,df): range_epsilon= 0.01 return [ df.min().min() - df.min().min()*range_epsilon, df.max().max() + df.min().min()*range_epsilon ] - def createMultiindexFigure(self,df): + def createMultiindexFigure(self,df,data_dirpath): raise NotImplementedError("Pure virtual function. Not to be called from the base class") - def createSimpleFigure(self,df): + def createSimpleFigure(self,df,data_dirpath): raise NotImplementedError("Pure virtual function. Not to be called from the base class") def createCsvs(self,df): @@ -29,49 +28,24 @@ def createCsvs(self,df): list[dict[str,str]]: A list of dictionaries containing the csv strings and their corresponding titles. Schema: [{"title":str, "data":str}] """ - df = self.transformation_strategy.calculate(df) if isinstance(df.index,MultiIndex): return [{"title":key, "data":df.xs(key, level=0).to_csv()} for key in df.index.levels[0]] else: return [{"title":self.config.title, "data":df.to_csv()}] - def createFigure(self,df, **args): + def createFigure(self,df,data_dirpath, **args): """ Creates a figure from the master dataframe Args: df (pd.DataFrame). The master dataframe containing all reframe test data Returns: go.Figure: Plotly figure corresponding to the grouped Bar type """ - df = self.transformation_strategy.calculate(df) if isinstance(df.index,MultiIndex): - figure = self.createMultiindexFigure(df, **args) + figure = self.createMultiindexFigure(df,data_dirpath, **args) else: - figure = self.createSimpleFigure(df, **args) + figure = self.createSimpleFigure(df, data_dirpath, **args) return figure - -class CompositeFigure: - def createFigure(self, df): - return self.plotly_figure.createFigure(df) - - def createTex(self, df): - if self.tikz_figure is not None: - return self.tikz_figure.createFigure(df) - else: - print(f"Warning: Tikz figure not implemented for plot type {self.__class__.__name__}") - return None - - def createCsvs(self,df): - return self.plotly_figure.createCsvs(df) - - def writeCsvs(self,df, path="."): - csvs = self.plotly_figure.createCsvs(df) - os.makedirs(path,exist_ok=True) - for csv in csvs: - with open(os.path.join(path,f"{csv.get('title','data')}.csv"),"w") as f: - f.write(csv["data"]) - - - def createFigureHtml(self,df): - return self.plotly_figure.createHtml(df) \ No newline at end of file + def createJson(self,df): + raise NotImplementedError diff --git a/src/feelpp/benchmarking/json_report/figures/controller.py b/src/feelpp/benchmarking/json_report/figures/controller.py index fa883144b..a449700af 100644 --- a/src/feelpp/benchmarking/json_report/figures/controller.py +++ b/src/feelpp/benchmarking/json_report/figures/controller.py @@ -4,97 +4,85 @@ from uuid import uuid4 from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationFactory -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory as PlotlyFigureFactory +from feelpp.benchmarking.json_report.figures.tikz.figureFactory import FigureFactory as TikzFigureFactory from feelpp.benchmarking.json_report.figures.schemas.plot import Plot class Controller: + """ + One semantic figure: + - same data + - multiple transformations + - multiple views + - multiple exports + """ def __init__(self, data:pd.DataFrame, plot_config: Union[Dict,Plot], report_uuid:str = None): - """ - """ self.report_uuid = report_uuid self.id = uuid4().hex if not isinstance(data,pd.DataFrame): raise NotImplementedError(f"Data type {type(data)} not supported for Figures") - - if isinstance(plot_config,Plot): - self.plot_config = plot_config - elif isinstance(plot_config,Dict): - self.plot_config = Plot(**plot_config) + self.plot_config = self.coercePlotConfig(plot_config) + + #TODO: allow multiple transformations in the future + self.transformed = { self.plot_config.transformation : TransformationFactory.create(self.plot_config).calculate(data) } + + self.figure_views = { + plot_type : { + "plotly": PlotlyFigureFactory.create(plot_type,plot_config=self.plot_config), + "latex": TikzFigureFactory.create(plot_type,plot_config=self.plot_config), + } + for plot_type in self.plot_config.plot_types + } + + def coercePlotConfig(self, config): + if isinstance(config,Plot): + plot_config = config + elif isinstance(config,Dict): + plot_config = Plot(**config) else: - raise TypeError(f"plot_config must be a Dict or a Plot model, got {type(plot_config)}") - - self.data = data #TransformationFactory.create(self.plot_config).calculate(data) - - self.figures = FigureFactory.create(self.plot_config) - - - def dumpFigureJsons(self,outdir:str = ".") -> str: - """ Returns the path relative to outdir """ - plotly_figs = [fig.createFigure(self.data) for fig in self.figures] - filepaths = [] - for plotly_fig,plot_type in zip(plotly_figs,self.plot_config.plot_types): - if self.report_uuid: - fig_relpath = os.path.join(self.report_uuid,self.id,f"{plot_type}.json") - else: - fig_relpath = os.path.join(self.id,f"{plot_type}.json") - - filepath = os.path.join(outdir,fig_relpath) - - os.makedirs(os.path.dirname(filepath),exist_ok=True) - - with open(filepath,"w") as f: - f.write(plotly_fig.to_json()) - - filepaths.append(fig_relpath) - return filepaths - - - def dumpFigureCsvs(self, outdir:str = ".") -> str: - """ Returns the path relative to outdir """ - filepaths = [] - for figure,plot_type in zip(self.figures,self.plot_config.plot_types): - if self.report_uuid: - fig_relpath = os.path.join(self.report_uuid,self.id,f"{plot_type}.zip") - else: - fig_relpath = os.path.join(self.id,f"{plot_type}.zip") - - filepath = os.path.join(outdir,fig_relpath) - - os.makedirs(os.path.dirname(filepath), exist_ok=True) - - csvs = figure.createCsvs(self.data) - with zipfile.ZipFile( file=filepath, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zip_archive: + raise TypeError(f"plot_config must be a Dict or a Plot model, got {type(config)}") + return plot_config + + + def renderFigure(self, plot_type, backend, transformation, data_dir = "." ): + figure = self.figure_views[plot_type][backend] + if not figure: + return None + return figure.createFigure(self.transformed[transformation],data_dir) + + def exportFigureData(self, plot_type, backend, transformation, formats=["csv"], outdir:str = ".") -> list[dict[str,str]]: + figure = self.figure_views[plot_type][backend] + if not figure: + return None + if self.report_uuid: + relpath = os.path.join(self.report_uuid,self.id,f"{plot_type}") + else: + relpath = os.path.join(self.id,f"{plot_type}") + + filepath = os.path.join(outdir,relpath) + + os.makedirs(os.path.dirname(filepath),exist_ok=True) + + exported_paths = {} + if "json" in formats: + with open(f"{filepath}.json","w") as f: + f.write(figure.createJson(self.transformed[transformation])) + + exported_paths["json"] = f"{relpath}.json" + if "csv" in formats: + os.mkdir(filepath) + csvs = figure.createCsvs(self.transformed[transformation]) + for csv in csvs: + csv_fn = os.path.join(filepath,f"{csv['title']}.csv") + with open(csv_fn,"w") as f: + f.write(csv['data']) + exported_paths["csv"] = relpath + + if "zip_csv" in formats: + csvs = figure.createCsvs( self.transformed[transformation] ) + with zipfile.ZipFile( file=f"{filepath}.zip", mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zip_archive: for csv in csvs: zip_archive.writestr(zinfo_or_arcname=f"{csv['title']}.csv",data=csv['data']) + exported_paths["zip_csv"] = f"{relpath}.zip" - filepaths.append(fig_relpath) - return filepaths - - # def generateAll(self): - # if self.df is None or self.df.empty: - # return [] - # return [ - # self.generateFigure(figure,plot_config.plot_types) - # for figure,plot_config in zip(self.figures,self.plots_config) - # ] - - # def generateFigure(self,figure,plot_types): - # return { - # "plot_types": plot_types, - # "subfigures": [self.generateSubfigure(subfigure) for subfigure in figure] - # } - - # def generateSubfigure(self, subfigure): - # return { - # "exports": [ - # { "display_text":"CSV", "data":[ - # { "format":"csv", "prefix":"data","content":subfigure.createCsvs(self.df)} - # ]}, - # { "display_text":"LaTeX", "data":[ - # {"format":"tex","content":[{ "data":subfigure.createTex(self.df), "title":"figures" }]}, - # {"format":"csv","content":subfigure.createCsvs(self.df)} - - # ]}, - # ], - # "html": subfigure.createFigureHtml(self.df) - # } \ No newline at end of file + return exported_paths \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/figures/figureFactory.py b/src/feelpp/benchmarking/json_report/figures/figureFactory.py deleted file mode 100644 index 9b444b97d..000000000 --- a/src/feelpp/benchmarking/json_report/figures/figureFactory.py +++ /dev/null @@ -1,107 +0,0 @@ -from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationFactory - -from feelpp.benchmarking.json_report.figures.base import CompositeFigure -from feelpp.benchmarking.json_report.figures.tikzFigures import TikzFigure, TikzScatterFigure, TikzGroupedBarFigure, TikzStackedBarFigure, TikzTableFigure -from feelpp.benchmarking.json_report.figures.plotlyFigures import PlotlyFigure, PlotlyScatterFigure, PlotlyGroupedBarFigure, PlotlyStackedBarFigure, PlotlyTableFigure, PlotlyHeatmapFigure, PlotlySunburstFigure, PlotlyScatter3DFigure, PlotlySurface3DFigure, PlotlyParallelcoordinatesFigure, PlotlyMarkedScatter - - - -class ScatterFigure(CompositeFigure): - """ Composite figure class for scatter figures""" - def __init__(self, plot_config, transformation_strategy, fill_lines=[]): - self.plotly_figure = PlotlyScatterFigure(plot_config,transformation_strategy,fill_lines) - self.tikz_figure = TikzScatterFigure(plot_config,transformation_strategy,fill_lines) - -class TableFigure(CompositeFigure): - """ Composite figure class for table figures""" - def __init__(self, plot_config, transformation_strategy): - self.plotly_figure = PlotlyTableFigure(plot_config,transformation_strategy) - self.tikz_figure = TikzTableFigure(plot_config,transformation_strategy) - -class StackedBarFigure(CompositeFigure): - """ Composite figure class for stacked bar figures""" - def __init__(self, plot_config, transformation_strategy): - self.plotly_figure = PlotlyStackedBarFigure(plot_config,transformation_strategy) - self.tikz_figure = TikzStackedBarFigure(plot_config,transformation_strategy) - -class GroupedBarFigure(CompositeFigure): - """ Composite figure class for grouped bar figures""" - def __init__(self, plot_config, transformation_strategy): - self.plotly_figure = PlotlyGroupedBarFigure(plot_config,transformation_strategy) - self.tikz_figure = TikzGroupedBarFigure(plot_config,transformation_strategy) - -class HeatmapFigure(CompositeFigure): - """ Composite figure class for heatmap figures""" - def __init__(self, plot_config, transformation_strategy): - self.plotly_figure = PlotlyHeatmapFigure(plot_config,transformation_strategy) - self.tikz_figure = None - -class SunburstFigure(CompositeFigure): - """ Composite figure class for sunburst figures""" - def __init__(self, plot_config, transformation_strategy): - self.plotly_figure = PlotlySunburstFigure(plot_config,transformation_strategy) - self.tikz_figure = None - -class Scatter3DFigure(CompositeFigure): - """ Composite figure class for 3D scatter figures""" - def __init__(self, plot_config, transformation_strategy): - self.plotly_figure = PlotlyScatter3DFigure(plot_config,transformation_strategy) - self.tikz_figure = None - -class Surface3DFigure(CompositeFigure): - """ Composite figure class for 3D surface figures""" - def __init__(self, plot_config, transformation_strategy): - self.plotly_figure = PlotlySurface3DFigure(plot_config,transformation_strategy) - self.tikz_figure = None - -class ParallelcoordinatesFigure(CompositeFigure): - """ Composite figure class for parallel coordinates figures""" - def __init__(self, plot_config, transformation_strategy): - self.plotly_figure = PlotlyParallelcoordinatesFigure(plot_config,transformation_strategy) - self.tikz_figure = None - -class MarkedScatterFigure(CompositeFigure): - def __init__(self, plot_config, transformation_strategy,fill_lines): - self.plotly_figure = PlotlyMarkedScatter(plot_config,transformation_strategy,fill_lines) - self.tikz_figure = None - - -class FigureFactory: - """ Factory class to dispatch concrete figure elements""" - @staticmethod - def create(plot_config): - """ Creates a concrete composite figure element - Args: - plot_config (Plot). Pydantic object with the plot configuration information - """ - strategy = TransformationFactory.create(plot_config) - figures = [] - for plot_type in plot_config.plot_types: - if plot_type in ["scatter","marked_scatter"]: - fill_lines = [] - if plot_config.transformation=="speedup": - fill_lines = ["optimal","half-optimal"] - if plot_type == "scatter": - figures.append(ScatterFigure(plot_config,strategy, fill_lines)) - elif plot_type == "marked_scatter": - figures.append(MarkedScatterFigure(plot_config,strategy, fill_lines)) - elif plot_type == "table": - figures.append(TableFigure(plot_config,strategy)) - elif plot_type == "stacked_bar": - figures.append(StackedBarFigure(plot_config,strategy)) - elif plot_type == "grouped_bar": - figures.append(GroupedBarFigure(plot_config,strategy)) - elif plot_type == "heatmap": - figures.append(HeatmapFigure(plot_config,strategy)) - elif plot_type == "sunburst": - figures.append(SunburstFigure(plot_config,strategy)) - elif plot_type == "scatter3d": - figures.append(Scatter3DFigure(plot_config,strategy)) - elif plot_type == "surface3d": - figures.append(Surface3DFigure(plot_config,strategy)) - elif plot_type == "parallelcoordinates": - figures.append(ParallelcoordinatesFigure(plot_config,strategy)) - else: - raise NotImplementedError - - return figures \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/figures/plotly/figureFactory.py b/src/feelpp/benchmarking/json_report/figures/plotly/figureFactory.py new file mode 100644 index 000000000..75c3709ec --- /dev/null +++ b/src/feelpp/benchmarking/json_report/figures/plotly/figureFactory.py @@ -0,0 +1,40 @@ +import warnings +from feelpp.benchmarking.json_report.figures.plotly.plotlyFigures import * + + + +class FigureFactory: + """ Factory class to dispatch concrete figure elements""" + + @staticmethod + def create(plot_type, plot_config) -> PlotlyFigure: + """ Creates a concrete figure element + Args: + plot_config (Plot). Pydantic object with the plot configuration information + """ + if plot_type in ["scatter","marked_scatter"]: + fill_lines = ["optimal","half-optimal"] if plot_config.transformation=="speedup" else [] + + if plot_type == "scatter": + return PlotlyScatterFigure(plot_config, fill_lines) + elif plot_type == "marked_scatter": + return PlotlyMarkedScatter(plot_config, fill_lines) + elif plot_type == "table": + return PlotlyTableFigure(plot_config) + elif plot_type == "stacked_bar": + return PlotlyStackedBarFigure(plot_config) + elif plot_type == "grouped_bar": + return PlotlyGroupedBarFigure(plot_config) + elif plot_type == "heatmap": + return PlotlyHeatmapFigure(plot_config) + elif plot_type == "sunburst": + return PlotlySunburstFigure(plot_config) + elif plot_type == "scatter3d": + return PlotlyScatter3DFigure(plot_config) + elif plot_type == "surface3d": + return PlotlySurface3DFigure(plot_config) + elif plot_type == "parallelcoordinates": + return PlotlyParallelcoordinatesFigure(plot_config) + else: + warnings.warn(f"Figure type note implemented {plot_type}") + return None \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/figures/plotlyFigures.py b/src/feelpp/benchmarking/json_report/figures/plotly/plotlyFigures.py similarity index 83% rename from src/feelpp/benchmarking/json_report/figures/plotlyFigures.py rename to src/feelpp/benchmarking/json_report/figures/plotly/plotlyFigures.py index 239fa27c1..d718fd3ad 100644 --- a/src/feelpp/benchmarking/json_report/figures/plotlyFigures.py +++ b/src/feelpp/benchmarking/json_report/figures/plotly/plotlyFigures.py @@ -8,8 +8,11 @@ class PlotlyFigure(Figure): """ Base class for a Plotly figure """ - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) + + def createJson(self,df): + return self.createFigure(df).to_json() def createTraces(self,df): raise NotImplementedError("Pure virtual function. Not to be called from the base class") @@ -37,7 +40,7 @@ def createSliderAnimation(self,df): """ frames = [] ranges=[] - secondary_axis = self.transformation_strategy.dimensions["secondary_axis"].parameter + secondary_axis = self.config.secondary_axis.parameter anim_dimension_values = df.index.get_level_values(secondary_axis).unique().values for dim in anim_dimension_values: @@ -65,7 +68,7 @@ def createSliderAnimation(self,df): return fig - def createMultiindexFigure(self,df): + def createMultiindexFigure(self,df,data_dirpath="."): """ Creates a plotly figure from a multiIndex dataframe Args: df (pd.DataFrame). The transformed dataframe (must be multiindex) @@ -74,7 +77,7 @@ def createMultiindexFigure(self,df): """ return self.createSliderAnimation(df) - def createSimpleFigure(self,df): + def createSimpleFigure(self,df,data_dirpath="."): """ Creates a plotly figure from a given dataframe Args: df (pd.DataFrame). The transformed dataframe @@ -83,26 +86,26 @@ def createSimpleFigure(self,df): """ return go.Figure(self.createTraces(df)) - def createFigure(self,df): + def createFigure(self,df, data_dirpath = "."): """ Creates a figure from the master dataframe Args: df (pd.DataFrame). The master dataframe containing all reframe test data Returns: go.Figure: Plotly figure corresponding to the grouped Bar type """ - figure = super().createFigure(df) + figure = super().createFigure(df,data_dirpath) figure.update_layout(self.config.layout_modifiers) figure = self.updateLayout(figure) return figure - def createHtml(self,df): - return self.createFigure(df).to_html(auto_play=False,include_plotlyjs=False, full_html=False) + def createHtml(self,df,data_dirpath="."): + return self.createFigure(df,data_dirpath).to_html(auto_play=False,include_plotlyjs=False, full_html=False) class PlotlyScatterFigure(PlotlyFigure): """ Concrete Figure class for scatter figures """ - def __init__(self, plot_config,transformation_strategy,fill_lines=[]): - super().__init__(plot_config,transformation_strategy) + def __init__(self, plot_config,fill_lines=[]): + super().__init__(plot_config) self.fill_lines = fill_lines def createTraces(self,df): @@ -121,28 +124,28 @@ def createTraces(self,df): class PlotlyMarkedScatter(PlotlyFigure): """ Concrete Figure class for marked scatter figures """ - def __init__(self, plot_config,transformation_strategy,fill_lines=[]): - super().__init__(plot_config,transformation_strategy) + def __init__(self, plot_config,fill_lines=[]): + super().__init__(plot_config) self.fill_lines = fill_lines self.marks = ["circle","square","diamond","cross","x","triangle-up","triangle-down","triangle-left","triangle-right","pentagon","hexagon","octagon","star","hexagram","star-triangle-up","star-triangle-down","star-square","star-diamond","diamond-tall","diamond-wide","hourglass","bowtie"] self.colors = ["red","blue","green","orange","purple","brown","pink","gray","cyan","magenta","yellow","darkblue","darkred","darkgreen","darkorange","darkpurple","darkbrown","darkpink","darkgray","darkcyan","darkmagenta","darkyellow","lightblue","lightred","lightgreen","lightorange","lightpurple","lightbrown","lightpink","lightgray","lightcyan","lightmagenta","lightyellow","black"] - if len(self.transformation_strategy.dimensions["extra_axes"])>0: - self.mark_axis = self.transformation_strategy.dimensions["extra_axes"][0].parameter + if len(self.config.extra_axes)>0: + self.mark_axis = self.config.extra_axes[0].parameter self.mark_axis_label = self.config.extra_axes[0].label else: - self.mark_axis = self.transformation_strategy.dimensions["secondary_axis"].parameter + self.mark_axis = self.config.secondary_axis.parameter self.mark_axis_label = self.config.secondary_axis.label if self.mark_axis else self.config.color_axis.label if self.config.color_axis else "" - def createMultiindexFigure(self, df): + def createMultiindexFigure(self, df, data_dirpath="."): if len(df.index.names) == 2: - return super().createSimpleFigure(df) + return super().createSimpleFigure(df,data_dirpath) elif len(df.index.names) == 3: - return super().createMultiindexFigure(df) + return super().createMultiindexFigure(df,data_dirpath) else: raise ValueError("Marked scatter figures can only be created from 2 or 3 level multiindex dataframes") - def createSimpleFigure(self, df): + def createSimpleFigure(self, df, data_dirpath="."): return go.Figure(self.createMarkTraces(df)) def createTraces(self,df): @@ -198,8 +201,8 @@ def updateLayout(self, fig): class PlotlyTableFigure(PlotlyFigure): """ Concrete Figure class for scatter figures """ - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) self.precision = 3 def cellFormat(self,df): @@ -211,7 +214,7 @@ def cellFormat(self,df): """ return [f'.{self.precision}' if t == float64 else '' for t in [df.index.dtype] + df.dtypes.values.tolist()] - def createMultiindexFigure(self,df): + def createMultiindexFigure(self,df,data_dirpath="."): """ Creates a plotly table from a multiindex dataframe Args: df (pd.DataFrame). The transformed dataframe (must be multiindex) @@ -228,7 +231,7 @@ def createMultiindexFigure(self,df): ) ) - def createSimpleFigure(self,df): + def createSimpleFigure(self,df,data_dirpath="."): """ Creates a simple plotly table from a dataframe Args: df (pd.DataFrame). The transformed dataframe @@ -253,18 +256,18 @@ def updateLayout(self, fig): class PlotlyStackedBarFigure(PlotlyFigure): """ Concrete Figure class for stacked bar charts""" - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) - def createMultiindexFigure(self,df): + def createMultiindexFigure(self,df,data_dirpath="."): """ Creates a stacked and grouped plotly bar chart from a multiindex dataframe Args: df (pd.DataFrame). The transformed dataframe (must be multiindex) Returns: go.Figure. Containing a stacked and grouped bar traces for a multiindex dataframe """ - xaxis = self.transformation_strategy.dimensions["xaxis"].parameter - secondary = self.transformation_strategy.dimensions["secondary_axis"].parameter + xaxis = self.config.xaxis.parameter + secondary = self.config.secondary_axis.parameter df2 = df.reset_index() @@ -278,7 +281,7 @@ def createMultiindexFigure(self,df): fig.update_layout(barmode="stack") return fig - def createSimpleFigure(self,df): + def createSimpleFigure(self,df,data_dirpath="."): """ Creates a stacked plotly bar chart from a single indexed dataframe Args: df (pd.DataFrame). The transformed dataframe @@ -304,8 +307,8 @@ def updateLayout(self,fig): class PlotlyGroupedBarFigure(PlotlyFigure): - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) def createTraces(self,df): """ Creates the Bar traces for a given dataframe. Useful for animation creation. @@ -319,8 +322,8 @@ def createTraces(self,df): ] class PlotlyHeatmapFigure(PlotlyFigure): - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) def createTraces(self, df): """ Creates the Heatmap traces for a given dataframe. Useful for animation creation. @@ -348,10 +351,10 @@ def updateLayout(self, fig): class PlotlySunburstFigure(PlotlyFigure): - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) - def createMultiindexFigure(self, df): + def createMultiindexFigure(self, df,data_dirpath="."): """ Creates the Sunburst traces for a given dataframe. Useful for animation creation. Args: - df (pd.DataFrame): The dataframe containing the figure data. @@ -363,7 +366,7 @@ def createMultiindexFigure(self, df): values = "value" ) - def createSimpleFigure(self, df): + def createSimpleFigure(self, df, data_dirpath="."): """ Creates a Plotly Sunburst figure from a given dataframe Args: df (pd.DataFrame). The transformed dataframe @@ -384,8 +387,8 @@ def updateLayout(self, fig): return fig class PlotlyParallelcoordinatesFigure(PlotlyFigure): - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) @staticmethod def encodeFactorize(df): @@ -395,7 +398,7 @@ def encodeFactorize(df): melted_factorized[column],_ = pd_factorize(df[column]) return melted_factorized - def createSimpleFigure(self, df): + def createSimpleFigure(self, df,data_dirpath="."): melted = df.reset_index().melt(value_vars=df.columns, id_vars=df.index.name) melted_factorized = self.encodeFactorize(melted) @@ -411,7 +414,7 @@ def createSimpleFigure(self, df): ) ) - def createMultiindexFigure(self, df): + def createMultiindexFigure(self, df,data_dirpath="."): melted = df.reset_index().melt(value_vars=df.columns,id_vars=df.index.names) melted_factorized = self.encodeFactorize(melted) @@ -437,24 +440,24 @@ def updateLayout(self, fig): class Plotly3DFigure(PlotlyFigure): - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) - if len(self.transformation_strategy.dimensions["extra_axes"])>0: - self.y_axis = self.transformation_strategy.dimensions["extra_axes"][0].parameter + def __init__(self, plot_config): + super().__init__(plot_config) + if len(self.config.extra_axes)>0: + self.y_axis = self.config.extra_axes[0].parameter self.y_axis_label = self.config.extra_axes[0].label else: - self.y_axis = self.transformation_strategy.dimensions["secondary_axis"].parameter + self.y_axis = self.config.secondary_axis.parameter self.y_axis_label = self.config.secondary_axis.label if self.y_axis else self.config.color_axis.label if self.config.color_axis else "" - def createMultiindexFigure(self, df): + def createMultiindexFigure(self, df, data_dirpath="."): if len(df.index.names) == 2: - return super().createSimpleFigure(df) #3D simple figure is equivalent to a multiindex 2D figure + return super().createSimpleFigure(df,data_dirpath) #3D simple figure is equivalent to a multiindex 2D figure elif len(df.index.names) == 3: - return super().createMultiindexFigure(df) + return super().createMultiindexFigure(df,data_dirpath) else: raise ValueError("3D figures can only be created from 2 or 3 level multiindex dataframes") - def createSimpleFigure(self, df): + def createSimpleFigure(self, df,data_dirpath="."): if not df.empty: raise ValueError("Secondary axis must be specified for 3d Figures") return go.Figure() @@ -473,8 +476,8 @@ def updateLayout(self, fig): return fig class PlotlyScatter3DFigure(Plotly3DFigure): - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) def createTraces(self, df): """ Creates a 3D scatter plot traces @@ -485,7 +488,7 @@ def createTraces(self, df): """ return [ go.Scatter3d( - x=df.index.get_level_values(self.transformation_strategy.dimensions["xaxis"].parameter), + x=df.index.get_level_values(self.config.xaxis.parameter), y=df.index.get_level_values(self.y_axis), z=df[col], mode='markers', name=col @@ -495,8 +498,8 @@ def createTraces(self, df): class PlotlySurface3DFigure(Plotly3DFigure): - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy) + def __init__(self, plot_config): + super().__init__(plot_config) def createTraces(self, df): """ Creates a 3D surface plot traces @@ -507,7 +510,7 @@ def createTraces(self, df): """ return [ go.Mesh3d( - x=df.index.get_level_values(self.transformation_strategy.dimensions["xaxis"].parameter), + x=df.index.get_level_values(self.config.xaxis.parameter), y=df.index.get_level_values(self.y_axis), z=df[col], opacity=0.5, name=col diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/figureFactory.py b/src/feelpp/benchmarking/json_report/figures/tikz/figureFactory.py new file mode 100644 index 000000000..a19edf0e4 --- /dev/null +++ b/src/feelpp/benchmarking/json_report/figures/tikz/figureFactory.py @@ -0,0 +1,25 @@ +import warnings +from feelpp.benchmarking.json_report.figures.tikz.tikzFigures import TikzFigure, TikzScatterFigure, TikzGroupedBarFigure, TikzStackedBarFigure, TikzTableFigure + +class FigureFactory: + """ Factory class to dispatch concrete figure elements""" + @staticmethod + def create(plot_type,plot_config) -> TikzFigure: + """ Creates a concrete figure element + Args: + plot_config (Plot). Pydantic object with the plot configuration information + """ + if plot_type in ["scatter","marked_scatter"]: + fill_lines = ["optimal","half-optimal"] if plot_config.transformation=="speedup" else [] + + if plot_type == "scatter": + return TikzScatterFigure(plot_config, fill_lines) + elif plot_type == "table": + return TikzTableFigure(plot_config) + elif plot_type == "stacked_bar": + return TikzStackedBarFigure(plot_config) + elif plot_type == "grouped_bar": + return TikzGroupedBarFigure(plot_config) + else: + warnings.warn(f"Figure type note implemented {plot_type}") + return None \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/figures/templates/tikz/groupedBarChart.tex.j2 rename to src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/figures/templates/tikz/scatterChart.tex.j2 rename to src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/figures/templates/tikz/stackedBarChart.tex.j2 rename to src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 diff --git a/src/feelpp/benchmarking/json_report/figures/templates/tikz/tableChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 similarity index 100% rename from src/feelpp/benchmarking/json_report/figures/templates/tikz/tableChart.tex.j2 rename to src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 diff --git a/src/feelpp/benchmarking/json_report/figures/tikzFigures.py b/src/feelpp/benchmarking/json_report/figures/tikz/tikzFigures.py similarity index 70% rename from src/feelpp/benchmarking/json_report/figures/tikzFigures.py rename to src/feelpp/benchmarking/json_report/figures/tikz/tikzFigures.py index 9599e248b..404c5f47f 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikzFigures.py +++ b/src/feelpp/benchmarking/json_report/figures/tikz/tikzFigures.py @@ -1,3 +1,4 @@ +import os from jinja2 import Environment, FileSystemLoader from feelpp.benchmarking.json_report.figures.base import Figure @@ -26,22 +27,22 @@ def intToUniqueStr(n): class TikzFigure(Figure): """Base class for Tikz figures""" - def __init__(self,plot_config, transformation_strategy, renderer_filename): - super().__init__(plot_config, transformation_strategy) - self.template_dirpath = f"{Path(__file__).resolve().parent}/templates/tikz/" + def __init__(self,plot_config, renderer_filename): + super().__init__(plot_config) + self.template_dirpath = f"{Path(__file__).resolve().parent}/templates/" self.renderer = Renderer(self.template_dirpath,renderer_filename) self.xcolors = ["red","green","blue","magenta","yellow","black","gray","white","darkgray","lightgray","olive","orange","pink","purple","teal","violet","cyan","brown","lime"] - def createMultiindexFigure(self, df, **args): + def createMultiindexFigure(self, df, data_dirpath, **args): """ Creates a latex tikz (pgfplots) figure from a multiIndex dataframe Args: df (pd.DataFrame). The transformed dataframe (must be multiindex) Returns: str: latex file content where containing multiple pgfplots figures, for each value of secondary axis """ - secondary_axis = self.transformation_strategy.dimensions["secondary_axis"].parameter + secondary_axis = self.config.secondary_axis.parameter anim_dim_values = df.index.get_level_values(secondary_axis).unique().values return self.renderer.template.render( xaxis = self.config.xaxis, @@ -51,11 +52,11 @@ def createMultiindexFigure(self, df, **args): columns = df.columns.tolist(), secondary_axis = self.config.secondary_axis, anim_dimension_values = [str(dim) for dim in anim_dim_values], - csv_filenames = [f"{dim}.csv" for dim in anim_dim_values], + csv_filenames = [os.path.join(data_dirpath,f"{dim}.csv") for dim in anim_dim_values], **args ) - def createSimpleFigure(self, df, **args): + def createSimpleFigure(self, df,data_dirpath, **args): """ Creates a latex tikz (pgfplots) figure from a given dataframe Args: df (pd.DataFrame). The transformed dataframe @@ -68,34 +69,34 @@ def createSimpleFigure(self, df, **args): color_axis = self.config.color_axis, caption = self.config.title, columns = df.columns.tolist(), - csv_filenames = [f"{self.config.title}.csv"], + csv_filenames = [os.path.join(data_dirpath,f"{self.config.title}.csv")], **args ) class TikzScatterFigure(TikzFigure): """ Concrete Figure class for pgfplots scatter figure""" - def __init__(self, plot_config, transformation_strategy, fill_lines = []): - super().__init__(plot_config, transformation_strategy, "scatterChart.tex.j2") + def __init__(self, plot_config, fill_lines = []): + super().__init__(plot_config, "scatterChart.tex.j2") self.fill_lines = fill_lines - def createFigure(self, df): - return super().createFigure(df, fill_lines = self.fill_lines) + def createFigure(self, df, data_dir): + return super().createFigure(df, data_dir, fill_lines = self.fill_lines) class TikzTableFigure(TikzFigure): """ Concrete Figure class for pgfplots table""" - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy, "tableChart.tex.j2") + def __init__(self, plot_config): + super().__init__(plot_config, "tableChart.tex.j2") class TikzStackedBarFigure(TikzFigure): """ Concrete Figure class for pgfplots stacked bar figure""" - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy, "stackedBarChart.tex.j2") + def __init__(self, plot_config): + super().__init__(plot_config, "stackedBarChart.tex.j2") - def createFigure(self,df): - return super().createFigure(df, colors=self.xcolors[:len(df.columns)]) + def createFigure(self,df, data_dir): + return super().createFigure(df,data_dir, colors=self.xcolors[:len(df.columns)]) class TikzGroupedBarFigure(TikzFigure): """ Concrete Figure class for pgfplots grouped bar figure""" - def __init__(self, plot_config, transformation_strategy): - super().__init__(plot_config, transformation_strategy, "groupedBarChart.tex.j2") + def __init__(self, plot_config): + super().__init__(plot_config, "groupedBarChart.tex.j2") diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 index f6d2d870f..459427945 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 @@ -16,39 +16,47 @@ ++++ {% set figure_ctrl = FiguresController(data.get(plotNode.ref),plotNode.plot, context.uuid) %} -{% set figure_json_paths = figure_ctrl.dumpFigureJsons(context.attachments_dirpath) %} -{% set figure_csv_paths = figure_ctrl.dumpFigureCsvs(context.attachments_dirpath) %} + - {%if plotNode.caption and make_block==False %}
{{plotNode.caption}}
diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 new file mode 100644 index 000000000..4ccc55439 --- /dev/null +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 @@ -0,0 +1,6 @@ + +{%set json_report_ctrl = JsonReportController.initFromLoaded( report, report_data, output_format="tex" )%} +{% set rendered_latex_path = json_report_ctrl.render(attachments_dirpath,"report.tex",attachments_dirpath=attachments_dirpath~"/data" ) %} +++++ + Date: Mon, 2 Feb 2026 16:00:08 +0100 Subject: [PATCH 46/69] fix golden files --- .../tests/data/golden/plot_features.adoc | 58 ++++++++----------- .../json_report/tests/data/golden/report.adoc | 42 +++++--------- 2 files changed, 41 insertions(+), 59 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc index fa470923b..5e6cb0915 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.adoc @@ -11,21 +11,18 @@ + +
- ++++ @@ -47,35 +44,30 @@
-
- CSV -
+
CSV
+ +
-
- CSV -
-
+
CSV
- + + - + ++++ diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc b/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc index 532508747..ee6bbdc6d 100644 --- a/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/report.adoc @@ -103,21 +103,16 @@ Placeholders in this report allow dynamic insertion of values from the datasets.
-
- CSV -
+
CSV
- - + - + ++++ ==== @@ -131,21 +126,16 @@ Placeholders in this report allow dynamic insertion of values from the datasets.
-
- CSV -
+
CSV
- - + - + ++++ ==== From 49756e6fecfb5779ef3183c9c9490f5330b3e822 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Mon, 2 Feb 2026 16:23:10 +0100 Subject: [PATCH 47/69] fix docs --- .../pages/json_schema/content/figure.adoc | 90 ++++++++++--------- .../pages/configurationfiles/plots.adoc | 76 +++++++++------- 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/docs/modules/json_report/pages/json_schema/content/figure.adoc b/docs/modules/json_report/pages/json_schema/content/figure.adoc index fb78f292d..95b22a140 100644 --- a/docs/modules/json_report/pages/json_schema/content/figure.adoc +++ b/docs/modules/json_report/pages/json_schema/content/figure.adoc @@ -263,16 +263,17 @@ Axis definition: [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Scatter Plot", "plot_types": [ "scatter" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("scatter",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -284,16 +285,17 @@ Axis definition: [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Marked Scatter Plot", "plot_types": [ "marked_scatter" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("marked_scatter",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -306,16 +308,17 @@ Axis definition: [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Stacked Bar Plot", "plot_types": [ "stacked_bar" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("stacked_bar",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -327,16 +330,17 @@ Axis definition: [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Grouped Bar Plot", "plot_types": [ "grouped_bar" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("grouped_bar",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -351,16 +355,17 @@ Axis definition: [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Heatmap", "plot_types": [ "heatmap" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("heatmap",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -377,16 +382,17 @@ Cell values correspond to `yaxis`. [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Table", "plot_types": [ "table" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("table",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -398,16 +404,17 @@ fig.show() [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Sunburst", "plot_types": [ "sunburst" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("sunburst",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -423,16 +430,17 @@ The `yaxis` will be shown in the line color. [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Parallel Coordinates", "plot_types": [ "parallelcoordinates" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("parallelcoordinates",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -460,16 +468,17 @@ These plots are used for visualizing three or four variables. **At least three d [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Scatter 3D", "plot_types": [ "scatter3d" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("scatter3d",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -478,16 +487,17 @@ fig.show() [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(Plot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = Plot(**{ "title": "Absolute performance - Surface 3D", "plot_types": [ "surface3d" ], "xaxis":{ "parameter":"tasks", "label":"Number of tasks" }, "yaxis":{ "parameter":"value", "label":"Execution time (s)" }, "color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }, "secondary_axis":{ "parameter":"elements", "label":"N" } -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("surface3d",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- diff --git a/docs/modules/tutorial/pages/configurationfiles/plots.adoc b/docs/modules/tutorial/pages/configurationfiles/plots.adoc index 7044ce7cb..dcb1163bc 100644 --- a/docs/modules/tutorial/pages/configurationfiles/plots.adoc +++ b/docs/modules/tutorial/pages/configurationfiles/plots.adoc @@ -181,15 +181,16 @@ Considering the same example axis as above, the software can generate the follow [%dynamic%open%hide_code,python] ---- -from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory -figures = FigureFactory.create(DefaultPlot(**{ +from feelpp.benchmarking.json_report.figures.plotly.figureFactory import FigureFactory +plot_config = DefaultPlot(**{ "title": "Absolute performance - Scatter Plot", "plot_types": [ "scatter" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("scatter",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -204,14 +205,15 @@ This plot type will behave as follows: [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ +plot_config = DefaultPlot(**{ "title": "Absolute performance - Marked Scatter Plot", "plot_types": [ "marked_scatter" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("marked_scatter",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -219,14 +221,15 @@ fig.show() [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ +plot_config = DefaultPlot(**{ "title": "Absolute performance - Stacked Bar Plot", "plot_types": [ "stacked_bar" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("stacked_bar",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -235,14 +238,15 @@ fig.show() [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ +plot_config = DefaultPlot(**{ "title": "Absolute performance - Grouped Bar Plot", "plot_types": [ "grouped_bar" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("grouped_bar",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -252,15 +256,16 @@ For this case, we will consider the `elements` (N) as `color_axis` and `performa [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ +plot_config = DefaultPlot(**{ "title": "Absolute performance - HeatMap", "plot_types": [ "heatmap" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"perfvalue", "label":"Performance Variable"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"}, "color_axis":{"parameter":"elements", "label":"N"}, -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("heatmap",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -268,14 +273,15 @@ fig.show() [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ +plot_config = DefaultPlot(**{ "title": "Absolute performance - Table", "plot_types": [ "table" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("table",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -288,14 +294,15 @@ The `secondary_axis` and `xaxis` parameter are present respectively on the inner [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ +plot_config = DefaultPlot(**{ "title": "Absolute performance - Sunburst Plot", "plot_types": [ "sunburst" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("sunburst",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -306,14 +313,15 @@ Axes will be shown on the following order: `secondary_axis`, `xaxis`, all additi [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ +plot_config = DefaultPlot(**{ "title": "Absolute performance - Parallel Coordinates Plot", "plot_types": [ "parallelcoordinates" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("parallelcoordinates",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -333,14 +341,15 @@ Axes correspondance is as follows: [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ - "title": "Absolute performance - Scatter 3D", +plot_config = DefaultPlot(**{ + "title": "absolute performance - Scatter 3D", "plot_types": [ "scatter3d" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("scatter3d",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- @@ -348,14 +357,15 @@ fig.show() [%dynamic%open%hide_code,python] ---- -figures = FigureFactory.create(DefaultPlot(**{ - "title": "Absolute performance - Surface 3D", +plot_config = DefaultPlot(**{ + "title": "absolute performance - Surface 3D", "plot_types": [ "surface3d" ], "yaxis":{"label":"Execution time (s)"}, "secondary_axis":{"parameter":"elements", "label":"N"}, "xaxis":{"parameter":"tasks", "label":"Number of tasks"} -})) -fig = figures[0].createFigure(master_df) +}) +figures = FigureFactory.create("surface3d",plot_config) +fig = figures.createFigure(TransformationFactory.create(plot_config).calculate(master_df)) fig.show() ---- From e49a9a938e0a76704c2f2af9d5485e161aedc475 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Mon, 2 Feb 2026 17:44:20 +0100 Subject: [PATCH 48/69] add report latex export btn --- .../benchmarking/json_report/renderer.py | 22 ++++++++++++++++--- .../templates/tex/json2tex_download.tex.j2 | 6 ++--- .../templates/tex/macros/plot.tex.j2 | 4 +++- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/renderer.py b/src/feelpp/benchmarking/json_report/renderer.py index 7aba2b91f..339bdc214 100644 --- a/src/feelpp/benchmarking/json_report/renderer.py +++ b/src/feelpp/benchmarking/json_report/renderer.py @@ -1,4 +1,4 @@ -import json, os, warnings +import json, os, warnings, tempfile, shutil from uuid import uuid4 from feelpp.benchmarking.jsonWithComments import JSONWithCommentsDecoder @@ -22,8 +22,9 @@ def __init__(self, report_filepath: str, output_format:str = "adoc") -> None: @classmethod - def initFromLoaded( cls, report: JsonReportSchema, report_data:dict, output_format:str="adoc" )->"JsonReportController": + def initFromLoaded( cls, id, report: JsonReportSchema, report_data:dict, output_format:str="adoc" )->"JsonReportController": json_report_ctrl = cls.__new__(cls) + json_report_ctrl.id = id json_report_ctrl.data = report_data json_report_ctrl.report = report json_report_ctrl.output_format = output_format @@ -62,6 +63,7 @@ def initRenderer( self) -> TemplateRenderer: renderer = TemplateRenderer( template_paths=template_path, template_filename=template_filename ) renderer.env.globals.update( { "zip":zip, + "JsonReportController":JsonReportController, "FiguresController":FiguresController, "TableController":TableController, "TextController":TextController @@ -107,4 +109,18 @@ def render(self, output_dirpath: str, output_filename:str = None, attachments_di ) ) - return os.path.abspath(output_filepath) \ No newline at end of file + return os.path.abspath(output_filepath) + + def exportAsZip( self, output_dirpath: str, output_filename:str = None, **kwargs ) -> str: + with tempfile.TemporaryDirectory() as tmpdir: + report_path = self.render( + output_dirpath=tmpdir, + output_filename=f"{output_filename}.{self.output_format}", + attachments_dirpath=os.path.join(tmpdir,"data"), + attachments_base_url = "./data", + **kwargs + ) + + shutil.make_archive(os.path.join(output_dirpath,output_filename),"zip",tmpdir) + + return os.path.join(output_dirpath,output_filename) \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 index 4ccc55439..3bbba64aa 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 @@ -1,6 +1,6 @@ -{%set json_report_ctrl = JsonReportController.initFromLoaded( report, report_data, output_format="tex" )%} -{% set rendered_latex_path = json_report_ctrl.render(attachments_dirpath,"report.tex",attachments_dirpath=attachments_dirpath~"/data" ) %} +{%set json_report_ctrl = JsonReportController.initFromLoaded(uuid, report, report_data, output_format="tex" )%} +{% set rendered_latex_path = json_report_ctrl.exportAsZip(attachments_dirpath~"/"~uuid,"report" ) %} ++++ - LaTeX ++++ diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 index 7c4ceb6d6..fee118f7b 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 @@ -10,11 +10,13 @@ formats = ["csv"], outdir = context.attachments_dirpath )%} +{% set data_url = (context.attachments_base_url or context.attachments_dirpath)~"/"~exported_paths["csv"] %} + {%set fig = figure_ctrl.renderFigure( plot_type = plot_type, backend = "latex", transformation = plotNode.plot.transformation, - data_dir = context.attachments_dirpath~"/"~exported_paths["csv"] + data_dir = data_url ) %} {%if fig %} From a48695539b466a549a09c7a7dcc66cd89203967e Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 08:53:17 +0100 Subject: [PATCH 49/69] fix package data --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5acafb30b..7ff1f5de3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" where = ["src"] [tool.setuptools.package-data] -'feelpp.benchmarking' = ['json_report/templates/**','dashboardRenderer/templates/**','json_report/figures/templates/**','report/templates/**','scripts/data/*','scripts/data/website_images/*', 'reframe/templates/**'] +'feelpp.benchmarking' = ['json_report/templates/**','dashboardRenderer/templates/**','json_report/figures/tikz/templates/**','report/templates/**','scripts/data/*','scripts/data/website_images/*', 'reframe/templates/**'] [project] name = "feelpp-benchmarking" From a1a46b04027625ca8ad0ace76a85764539229ecd Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 09:10:59 +0100 Subject: [PATCH 50/69] filenames latex friendy --- src/feelpp/benchmarking/json_report/figures/base.py | 11 ++++++++--- .../benchmarking/json_report/figures/controller.py | 4 ++-- .../json_report/figures/tikz/tikzFigures.py | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/base.py b/src/feelpp/benchmarking/json_report/figures/base.py index 432fcf3c2..694e08507 100644 --- a/src/feelpp/benchmarking/json_report/figures/base.py +++ b/src/feelpp/benchmarking/json_report/figures/base.py @@ -20,18 +20,23 @@ def createMultiindexFigure(self,df,data_dirpath): def createSimpleFigure(self,df,data_dirpath): raise NotImplementedError("Pure virtual function. Not to be called from the base class") + def sanitizeFilename(self,title:str): + """Creates a FS friendly filename. Mostly used to be latex compatible""" + t = str(title).replace(" ","-").replace("_","-") + return "".join(x for x in t if x.isalnum() or x in ["-","."]) + def createCsvs(self,df): """Creates the corresponding csv strings for the figure Args: df (pd.DataFrame). The master dataframe containing all reframe test data Returns: list[dict[str,str]]: A list of dictionaries containing the csv strings and their corresponding titles. - Schema: [{"title":str, "data":str}] + Schema: [{"filename":str, "data":str}] """ if isinstance(df.index,MultiIndex): - return [{"title":key, "data":df.xs(key, level=0).to_csv()} for key in df.index.levels[0]] + return [{"filename":f"{self.sanitizeFilename(key)}.csv", "data":df.xs(key, level=0).to_csv()} for key in df.index.levels[0]] else: - return [{"title":self.config.title, "data":df.to_csv()}] + return [{"filename":f"{self.sanitizeFilename(self.config.title)}.csv", "data":df.to_csv()}] def createFigure(self,df,data_dirpath, **args): """ Creates a figure from the master dataframe diff --git a/src/feelpp/benchmarking/json_report/figures/controller.py b/src/feelpp/benchmarking/json_report/figures/controller.py index a449700af..498eb7b40 100644 --- a/src/feelpp/benchmarking/json_report/figures/controller.py +++ b/src/feelpp/benchmarking/json_report/figures/controller.py @@ -73,7 +73,7 @@ def exportFigureData(self, plot_type, backend, transformation, formats=["csv"], os.mkdir(filepath) csvs = figure.createCsvs(self.transformed[transformation]) for csv in csvs: - csv_fn = os.path.join(filepath,f"{csv['title']}.csv") + csv_fn = os.path.join(filepath,f"{csv['filename']}") with open(csv_fn,"w") as f: f.write(csv['data']) exported_paths["csv"] = relpath @@ -82,7 +82,7 @@ def exportFigureData(self, plot_type, backend, transformation, formats=["csv"], csvs = figure.createCsvs( self.transformed[transformation] ) with zipfile.ZipFile( file=f"{filepath}.zip", mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zip_archive: for csv in csvs: - zip_archive.writestr(zinfo_or_arcname=f"{csv['title']}.csv",data=csv['data']) + zip_archive.writestr(zinfo_or_arcname=f"{csv['filename']}",data=csv['data']) exported_paths["zip_csv"] = f"{relpath}.zip" return exported_paths \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/tikzFigures.py b/src/feelpp/benchmarking/json_report/figures/tikz/tikzFigures.py index 404c5f47f..9937dc1c8 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/tikzFigures.py +++ b/src/feelpp/benchmarking/json_report/figures/tikz/tikzFigures.py @@ -52,7 +52,7 @@ def createMultiindexFigure(self, df, data_dirpath, **args): columns = df.columns.tolist(), secondary_axis = self.config.secondary_axis, anim_dimension_values = [str(dim) for dim in anim_dim_values], - csv_filenames = [os.path.join(data_dirpath,f"{dim}.csv") for dim in anim_dim_values], + csv_filenames = [os.path.join(data_dirpath,self.sanitizeFilename(f"{dim}.csv")) for dim in anim_dim_values], **args ) @@ -69,7 +69,7 @@ def createSimpleFigure(self, df,data_dirpath, **args): color_axis = self.config.color_axis, caption = self.config.title, columns = df.columns.tolist(), - csv_filenames = [os.path.join(data_dirpath,f"{self.config.title}.csv")], + csv_filenames = [os.path.join(data_dirpath,self.sanitizeFilename(f"{self.config.title}.csv"))], **args ) From 545a15eb9193d1d0c949d35b74d97d4ab99c7329 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 09:17:17 +0100 Subject: [PATCH 51/69] detokenize and accept _ --- src/feelpp/benchmarking/json_report/figures/base.py | 4 ++-- .../figures/tikz/templates/groupedBarChart.tex.j2 | 2 +- .../figures/tikz/templates/scatterChart.tex.j2 | 2 +- .../figures/tikz/templates/stackedBarChart.tex.j2 | 2 +- .../json_report/figures/tikz/templates/tableChart.tex.j2 | 2 +- .../json_report/templates/adoc/json2adoc_report.adoc.j2 | 9 ++++++++- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/base.py b/src/feelpp/benchmarking/json_report/figures/base.py index 694e08507..f177c4db2 100644 --- a/src/feelpp/benchmarking/json_report/figures/base.py +++ b/src/feelpp/benchmarking/json_report/figures/base.py @@ -22,8 +22,8 @@ def createSimpleFigure(self,df,data_dirpath): def sanitizeFilename(self,title:str): """Creates a FS friendly filename. Mostly used to be latex compatible""" - t = str(title).replace(" ","-").replace("_","-") - return "".join(x for x in t if x.isalnum() or x in ["-","."]) + t = str(title).replace(" ","-") + return "".join(x for x in t if x.isalnum() or x in ["_","-","."]) def createCsvs(self,df): """Creates the corresponding csv strings for the figure diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 index f5a2710c5..7063a770b 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 @@ -23,7 +23,7 @@ {% for fn in csv_filenames %} -\pgfplotstableread[col sep=comma]{{'{'}}{{fn}}{{'}'}}\data{{loop.index | inttouniquestr }} +\pgfplotstableread[col sep=comma]{{'{\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 index 9687da394..ea4e79b5e 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 @@ -29,7 +29,7 @@ {% for fn in csv_filenames %} -\pgfplotstableread[col sep=comma]{{'{'}}{{fn}}{{'}'}}\data{{loop.index | inttouniquestr }} +\pgfplotstableread[col sep=comma]{{'{\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 index 73d1bae79..0ebe8f696 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 @@ -7,7 +7,7 @@ } {% for fn in csv_filenames %} -\pgfplotstableread[col sep=comma]{{'{'}}{{fn}}{{'}'}}\data{{loop.index | inttouniquestr }} +\pgfplotstableread[col sep=comma]{{'{\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 index 2eebe6ffd..2aa98f61b 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 @@ -1,7 +1,7 @@ {% for fn in csv_filenames %} -\pgfplotstableread[col sep=comma]{{'{'}}{{fn}}{{'}'}}\data{{loop.index | inttouniquestr }} +\pgfplotstableread[col sep=comma]{{'{\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 index 8d999a63a..87dfeee01 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 @@ -14,4 +14,11 @@ } %} {% import "macros/section.adoc.j2" as section %} -{{ section.render(report, report_data, 1, context=context) }} \ No newline at end of file +{{ section.render(report, report_data, 1, context=context) }} + + +{%set json_report_ctrl = JsonReportController.initFromLoaded(uuid, report, report_data, output_format="tex" )%} +{% set rendered_latex_path = json_report_ctrl.exportAsZip(attachments_dirpath~"/"~uuid,"report" ) %} +++++ + LaTeX +++++ From af8944569ae2d002c071fc6b98a56f5c9d527995 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 09:34:12 +0100 Subject: [PATCH 52/69] fix and style latex download byn --- docs/antora/supplemental-ui/css/figures.css | 10 ++++++++++ .../templates/adoc/json2adoc_report.adoc.j2 | 7 ++++--- .../json_report/templates/tex/json2tex_download.tex.j2 | 6 ------ src/feelpp/benchmarking/report/__main__.py | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) delete mode 100644 src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 diff --git a/docs/antora/supplemental-ui/css/figures.css b/docs/antora/supplemental-ui/css/figures.css index 206253178..efeaf04e7 100644 --- a/docs/antora/supplemental-ui/css/figures.css +++ b/docs/antora/supplemental-ui/css/figures.css @@ -105,3 +105,13 @@ .exampleblock.example { border-left: 4px solid var(--brand-primary); } + + +.download-btn.latex-btn { + position: absolute; + top: 12%; + right: 0; + margin: 16px; + border: solid 1px; + padding: 8px; +} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 index 87dfeee01..a48831afc 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/json2adoc_report.adoc.j2 @@ -16,9 +16,10 @@ {% import "macros/section.adoc.j2" as section %} {{ section.render(report, report_data, 1, context=context) }} - -{%set json_report_ctrl = JsonReportController.initFromLoaded(uuid, report, report_data, output_format="tex" )%} +{% if include_latex_download %} +{% set json_report_ctrl = JsonReportController.initFromLoaded(uuid, report, report_data, output_format="tex" ) %} {% set rendered_latex_path = json_report_ctrl.exportAsZip(attachments_dirpath~"/"~uuid,"report" ) %} ++++ - LaTeX + LaTeX ++++ +{% endif %} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 deleted file mode 100644 index 3bbba64aa..000000000 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_download.tex.j2 +++ /dev/null @@ -1,6 +0,0 @@ - -{%set json_report_ctrl = JsonReportController.initFromLoaded(uuid, report, report_data, output_format="tex" )%} -{% set rendered_latex_path = json_report_ctrl.exportAsZip(attachments_dirpath~"/"~uuid,"report" ) %} -++++ - LaTeX -++++ diff --git a/src/feelpp/benchmarking/report/__main__.py b/src/feelpp/benchmarking/report/__main__.py index adbdd002b..1ffb4050f 100644 --- a/src/feelpp/benchmarking/report/__main__.py +++ b/src/feelpp/benchmarking/report/__main__.py @@ -62,7 +62,7 @@ def main_cli(): project_name = extractAntoraProjectName(parser.args.antora_basepath) dashboard.tree.upstreamViewData(ReframeReportPlugin.aggregator) - dashboard.render(parser.args.module_path,clean=parser.args.reset_docs, project_name = project_name, include_latex=True) + dashboard.render(parser.args.module_path,clean=parser.args.reset_docs, project_name = project_name, include_latex_download=True) if parser.args.website: os.chdir(parser.args.antora_basepath) From 5b9987fd10ac8a5b04fd9eccf1a4a806762bdf5d Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 09:55:03 +0100 Subject: [PATCH 53/69] lazy loading for figures --- src/feelpp/benchmarking/json_report/figures/controller.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/controller.py b/src/feelpp/benchmarking/json_report/figures/controller.py index 498eb7b40..952924a18 100644 --- a/src/feelpp/benchmarking/json_report/figures/controller.py +++ b/src/feelpp/benchmarking/json_report/figures/controller.py @@ -28,8 +28,8 @@ def __init__(self, data:pd.DataFrame, plot_config: Union[Dict,Plot], report_uuid self.figure_views = { plot_type : { - "plotly": PlotlyFigureFactory.create(plot_type,plot_config=self.plot_config), - "latex": TikzFigureFactory.create(plot_type,plot_config=self.plot_config), + "plotly": lambda pt=plot_type : PlotlyFigureFactory.create(pt,plot_config=self.plot_config), + "latex": lambda pt=plot_type : TikzFigureFactory.create(pt,plot_config=self.plot_config) } for plot_type in self.plot_config.plot_types } @@ -45,13 +45,13 @@ def coercePlotConfig(self, config): def renderFigure(self, plot_type, backend, transformation, data_dir = "." ): - figure = self.figure_views[plot_type][backend] + figure = self.figure_views[plot_type][backend]() if not figure: return None return figure.createFigure(self.transformed[transformation],data_dir) def exportFigureData(self, plot_type, backend, transformation, formats=["csv"], outdir:str = ".") -> list[dict[str,str]]: - figure = self.figure_views[plot_type][backend] + figure = self.figure_views[plot_type][backend]() if not figure: return None if self.report_uuid: From 3092bac94231aeb9762eaf44f05e7d791b1b29b0 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 10:28:51 +0100 Subject: [PATCH 54/69] fix img download --- src/feelpp/benchmarking/json_report/schemas/jsonReport.py | 2 +- .../json_report/templates/tex/macros/image.tex.j2 | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py index e4c2d1e15..56722a7ec 100644 --- a/src/feelpp/benchmarking/json_report/schemas/jsonReport.py +++ b/src/feelpp/benchmarking/json_report/schemas/jsonReport.py @@ -48,7 +48,7 @@ def downloadImage(self,dirpath:str=".") -> str: image_path = os.path.join(dirpath,image_name) with open(image_path,"wb") as f: f.write(response.content) - return image_path + return image_name class PlotNode(ReportNode): type: Literal["plot"] diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 index ab80a8a5a..8034277ac 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 @@ -1,8 +1,10 @@ {% macro includeGraphics(imageNode,styles,context={}) %} +{% set img_filename = imageNode.downloadImage(context.attachments_dirpath~"/"~context.uuid) %} +{% set img_url = (context.attachments_base_url or context.attachments_dirpath)~"/"~context.uuid~"/"~img_filename %} {% if 'img-fluid' in styles %} -\includegraphics[width=\linewidth]{ {{ imageNode.downloadImage(context.attachments_dirpath) }} } +\includegraphics[width=\linewidth]{ {{img_url}} } {% else %} -\includegraphics{ {{ imageNode.downloadImage(context.attachments_dirpath) }} } +\includegraphics{ {{img_url}} } {% endif %} {% endmacro %} From ed43c717ac4e07f94bb178b3745135a0ca352e0e Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 10:36:12 +0100 Subject: [PATCH 55/69] dont allow captions as labels/ids --- docs/modules/json_report/pages/json_schema/referencing.adoc | 2 -- .../json_report/templates/adoc/macros/image.adoc.j2 | 2 -- .../json_report/templates/adoc/macros/plot.adoc.j2 | 2 -- .../json_report/templates/adoc/macros/table.adoc.j2 | 2 -- .../json_report/templates/tex/macros/image.tex.j2 | 4 ---- .../json_report/templates/tex/macros/table.tex.j2 | 2 -- 6 files changed, 14 deletions(-) diff --git a/docs/modules/json_report/pages/json_schema/referencing.adoc b/docs/modules/json_report/pages/json_schema/referencing.adoc index c9d590204..be8fbd378 100644 --- a/docs/modules/json_report/pages/json_schema/referencing.adoc +++ b/docs/modules/json_report/pages/json_schema/referencing.adoc @@ -40,8 +40,6 @@ See <> for more details. In this example, the text "Introduction Section" will be a clickable link that navigates to the section with the identifier `"introduction"`. -[TIP] -For images, figures and tables, it is possible to reference via their caption without the use of an explicit id (though it is discouraged for clarity). To do so, remove any parentheses from the reference, and replace spaces with hyphens. For example, if a table has the caption "Performance Metrics", you can reference it using `\<>`. [IMPORTANT] Ensure that the identifiers used in cross-references match exactly with those assigned to the content nodes, including case sensitivity. Mismatched identifiers will result in broken links in the final report. diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 index 10d7efc29..a9619f7df 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/image.adoc.j2 @@ -5,8 +5,6 @@ {% if imageNode.id %} [[{{imageNode.id}}]] -{% elif imageNode.caption %} -[[{{imageNode.caption | replace(' ','-') | replace('(','') | replace(')','') }}]] {% endif %} {% if imageNode.caption %} .{{imageNode.caption}} diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 index 459427945..cbf1a25b2 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/plot.adoc.j2 @@ -3,8 +3,6 @@ {% if plotNode.id %} [[{{plotNode.id}}]] -{% elif plotNode.caption %} -[[{{plotNode.caption | replace(' ','-')}}]] {% endif %} {% if make_block %} [example.plot] diff --git a/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 b/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 index 418d7db50..647d4f669 100644 --- a/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 +++ b/src/feelpp/benchmarking/json_report/templates/adoc/macros/table.adoc.j2 @@ -10,8 +10,6 @@ -- {% if tableNode.id %} [[{{tableNode.id}}]] -{% elif tableNode.caption %} -[[{{tableNode.caption | replace(' ','-')}}]] {% endif %} {% if tableNode.caption %} .{{tableNode.caption}} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 index 8034277ac..83b13ffd3 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/image.tex.j2 @@ -24,8 +24,6 @@ {% endif %} {% if imageNode.id %} \label{ {{imageNode.id}} } - {% elif caption %} - \label{{'{'}}fig:{{caption | replace(' ','-')}}{{'}'}} {% endif %} \end{figure} {%else%} @@ -33,8 +31,6 @@ {% if caption %}\subcaption{{'{'}}{{caption}}{{'}'}}{% endif %} {% if imageNode.id %} \label{ {{imageNode.id}} } -{% elif caption %} -\label{{'{'}}fig:{{caption | replace(' ','-')}}{{'}'}} {% endif %} {% endif %} {% endmacro %} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 index a82ad3707..5dc18bcf2 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -38,8 +38,6 @@ {% endif %} {% if tableNode.id %} \label{{'{'}}{{tableNode.id}}{{'}'}} - {% elif tableNode.caption %} - \label{table:{{tableNode.caption | replace(' ','-')}}} {% endif %} \end{table} From b73481c405c75d74198d50362df1c0e050ff371d Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 11:10:20 +0100 Subject: [PATCH 56/69] add amsmath pkg --- .../json_report/templates/tex/json2tex_report.tex.j2 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index dae78b201..8219052bd 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -17,6 +17,7 @@ \usepackage{hyperref} \usepackage{xurl} \usepackage{subcaption} +\usepackage{amsmath} \usepackage[parfill]{parskip} \setlength{\parindent}{0pt} From cde23c29ae203ee45b7ace952442e00cfd74a5f1 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 11:10:33 +0100 Subject: [PATCH 57/69] fix no italics in math mode --- src/feelpp/benchmarking/json_report/text/controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/text/controller.py b/src/feelpp/benchmarking/json_report/text/controller.py index 01703bddf..cc16183f4 100644 --- a/src/feelpp/benchmarking/json_report/text/controller.py +++ b/src/feelpp/benchmarking/json_report/text/controller.py @@ -9,7 +9,7 @@ def __init__(self, data, text:Text): @staticmethod def _asciidoc_to_latex_urls(text: str) -> str: - pattern = re.compile(r'([(<]*)(\S+?)\[(.*?)\]([)>.,;!?:]*)') + pattern = re.compile(r'([(<]*)([a-zA-Z][a-zA-Z0-9+.-]*:\S+?)\[(.*?)\]([)>.,;!?:]*)') def repl(m): pre, url, label, post = m.groups() @@ -32,7 +32,7 @@ def formatText(self,text:str,format = "adoc"): text = re.sub( r"stem:\[\s*(.*?)\s*\]", r"$\1$", text ) #italics - text = re.sub(r"(? Date: Tue, 3 Feb 2026 11:53:52 +0100 Subject: [PATCH 58/69] fix figure and table float location --- .../json_report/figures/tikz/templates/groupedBarChart.tex.j2 | 2 +- .../json_report/figures/tikz/templates/scatterChart.tex.j2 | 2 +- .../json_report/figures/tikz/templates/stackedBarChart.tex.j2 | 2 +- .../json_report/figures/tikz/templates/tableChart.tex.j2 | 2 +- .../json_report/templates/tex/json2tex_report.tex.j2 | 1 + .../benchmarking/json_report/templates/tex/macros/table.tex.j2 | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 index 7063a770b..1c11d02c5 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 @@ -27,7 +27,7 @@ {% endfor %} -\begin{figure} +\begin{figure}[H] {% if anim_dimension_values %} {% for dim in anim_dimension_values %} diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 index ea4e79b5e..1c51834fe 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 @@ -33,7 +33,7 @@ {% endfor %} -\begin{figure} +\begin{figure}[H] {% if anim_dimension_values %} {% for dim in anim_dimension_values %} diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 index 0ebe8f696..ad2bb9e75 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 @@ -11,7 +11,7 @@ {% endfor %} -\begin{figure} +\begin{figure}[H] \begin{tikzpicture} \begin{axis}[ diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 index 2aa98f61b..038c569ca 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 @@ -5,7 +5,7 @@ {% endfor %} -\begin{table} +\begin{table}[H] {% if anim_dimension_values %} {% for dim in anim_dimension_values %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index 8219052bd..1db2a449f 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -18,6 +18,7 @@ \usepackage{xurl} \usepackage{subcaption} \usepackage{amsmath} +\usepackage{float} \usepackage[parfill]{parskip} \setlength{\parindent}{0pt} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 index 5dc18bcf2..dfe1cc8db 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -22,7 +22,7 @@ {% set cols_align = table_controller.getColsAlignment(format="tex") %} -\begin{table}[ht!] +\begin{table}[H] \centering \begin{tabular}{ {{ cols_align }} } \hline From 1f9ad1c34417cb2fb6a3d9d1991ba47cb6dc9b0f Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 12:03:30 +0100 Subject: [PATCH 59/69] fix latex table size --- .../json_report/templates/tex/macros/table.tex.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 index dfe1cc8db..abbde3a54 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -24,7 +24,7 @@ \begin{table}[H] \centering - \begin{tabular}{ {{ cols_align }} } + \resizebox{\textwidth}{!}{\begin{tabular}{ {{ cols_align }} } \hline {% for col in table.columns %}{{ escapeSpecialChars(col) }}{% if not loop.last %} & {% endif %}{% endfor %} \\ \hline @@ -32,7 +32,7 @@ {% for cell in row %}{{ escapeSpecialChars(cell) }}{% if not loop.last %} & {% endif %}{% endfor %} \\ {% endfor %} \hline - \end{tabular} + \end{tabular}} {% if tableNode.caption %} \caption{ {{ tableNode.caption }} } {% endif %} From 18525b23de82d68c2e43226dadaa520a01fb70de Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 14:14:43 +0100 Subject: [PATCH 60/69] improve scatter plot --- .../tikz/templates/scatterChart.tex.j2 | 43 +++++++++++++------ .../templates/tex/macros/plot.tex.j2 | 4 +- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 index 1c51834fe..5f935119d 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 @@ -1,16 +1,21 @@ \DeclareRobustCommand{\plot}[2][]{ \begin{tikzpicture} \begin{axis}[ - width=\textwidth, height=0.6172\textwidth, + width=0.95\textwidth, height=\linewidth, xlabel={ {{xaxis.label}} }, ylabel={ {{yaxis.label}} }, xtick=data, xtick align=outside, + ylabel style={font=\small}, ymajorgrids=true, yminorgrids=true, xticklabels from table={{'{#2}'}}{{'{'}}{{xaxis.parameter}}{{'}'}}, - cycle list name=color list, legend style={at={(0.5,-0.1)},anchor=north} + xticklabel style={rotate=45, anchor=east, font=\scriptsize}, + yticklabel style={font=\scriptsize}, + cycle list name=color list, + legend to name=customlegend, + legend style={font=\small} ] {% for col in columns %} {% if col not in fill_lines %} - \addplot table [x expr=\coordindex, y={{ col }}] {{'{#2}'}} ; + \addplot+[mark=*,mark size=0.7pt] table [x expr=\coordindex, y={{ col }}] {{'{#2}'}} ; \addlegendentry{ {{col}} } {% endif %} {% endfor %} @@ -34,14 +39,28 @@ \begin{figure}[H] + \centering + {% if anim_dimension_values %} + {% set n = anim_dimension_values | length %} + {% set ncols = (n**0.5) | round(0, 'ceil') %} + {% set nrows = (n / ncols) | round(0, 'ceil') %} + {% set subfig_width = ((0.95 / ncols) | round(3)) %} -{% if anim_dimension_values %} - {% for dim in anim_dimension_values %} - \plot{\data{{ loop.index | inttouniquestr }}} - \caption{ {{secondary_axis.label}}={{dim}} } - {% endfor %} -{% else %} - \plot{\data{{1 | inttouniquestr }}} -{% endif %} -\caption{ {{caption}} } + {% for dim in anim_dimension_values %} + \begin{subfigure}[t]{ {{subfig_width}}\textwidth} % adjust width as needed + \centering + \plot{\data{{ loop.index | inttouniquestr }}} + \caption{ {{secondary_axis.label}}={{dim}} } + \end{subfigure} + {% if (loop.index0 + 1) % ncols == 0 %} + \par\vspace{1em} + {% endif %} + {% endfor %} + {% else %} + \plot{\data{{1 | inttouniquestr }}} + {% endif %} + \vspace{1em} + \centering + \pgfplotslegendfromname{customlegend} + \caption{ {{caption}} } \end{figure} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 index fee118f7b..3879ada85 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/plot.tex.j2 @@ -2,7 +2,8 @@ {% set figure_ctrl = FiguresController(data.get(plotNode.ref),plotNode.plot, context.uuid) %} -{% for plot_type in figure_ctrl.figure_views.keys() %} +{# CURRENTLY ONLY THE FIRST PLOT TYPE IS EXPORTED #} +{% set plot_type = plotNode.plot.plot_types[0] %} {% set exported_paths = figure_ctrl.exportFigureData( plot_type = plot_type, backend = "latex", @@ -23,5 +24,4 @@ {{fig}} {% endif %} -{% endfor %} {% endmacro %} From f07aa36de4c37977b1d21d36e4b53f4b592c332f Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 14:22:00 +0100 Subject: [PATCH 61/69] escape latex comments --- src/feelpp/benchmarking/json_report/text/controller.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/feelpp/benchmarking/json_report/text/controller.py b/src/feelpp/benchmarking/json_report/text/controller.py index cc16183f4..6b5191967 100644 --- a/src/feelpp/benchmarking/json_report/text/controller.py +++ b/src/feelpp/benchmarking/json_report/text/controller.py @@ -28,6 +28,9 @@ def formatText(self,text:str,format = "adoc"): if format == "adoc": pass elif format == "tex": + #Escape comments + text = text.replace("%","\%") + # stem text = re.sub( r"stem:\[\s*(.*?)\s*\]", r"$\1$", text ) From 3669275329bbf60c34d32c047424932acb4f28f3 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 14:22:15 +0100 Subject: [PATCH 62/69] smaller font --- .../json_report/figures/tikz/templates/scatterChart.tex.j2 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 index 5f935119d..ed3206e1f 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 @@ -4,7 +4,8 @@ width=0.95\textwidth, height=\linewidth, xlabel={ {{xaxis.label}} }, ylabel={ {{yaxis.label}} }, xtick=data, xtick align=outside, - ylabel style={font=\small}, + ylabel style={font=\scriptsize}, + xlabel style={font=\scriptsize}, ymajorgrids=true, yminorgrids=true, xticklabels from table={{'{#2}'}}{{'{'}}{{xaxis.parameter}}{{'}'}}, xticklabel style={rotate=45, anchor=east, font=\scriptsize}, From eeabff60d96aa26d8094f89e887620b836cf1e3d Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 14:23:22 +0100 Subject: [PATCH 63/69] fix escape comments --- src/feelpp/benchmarking/json_report/text/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feelpp/benchmarking/json_report/text/controller.py b/src/feelpp/benchmarking/json_report/text/controller.py index 6b5191967..49cb3dd1b 100644 --- a/src/feelpp/benchmarking/json_report/text/controller.py +++ b/src/feelpp/benchmarking/json_report/text/controller.py @@ -29,7 +29,7 @@ def formatText(self,text:str,format = "adoc"): pass elif format == "tex": #Escape comments - text = text.replace("%","\%") + text = text.replace("%","\\%") # stem text = re.sub( r"stem:\[\s*(.*?)\s*\]", r"$\1$", text ) From bd0f3103d74f70f7864d27828e7180922ccd4475 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Tue, 3 Feb 2026 14:31:05 +0100 Subject: [PATCH 64/69] fixed grouped bar figs --- .../tikz/templates/groupedBarChart.tex.j2 | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 index 1c11d02c5..f708acf53 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 @@ -1,16 +1,20 @@ - \DeclareRobustCommand{\plot}[2][]{ \begin{tikzpicture} \begin{axis}[ - width=\textwidth, height=0.6172\textwidth, + width=0.95\textwidth, height=0.6172\textwidth, xlabel={ {{xaxis.label}} }, ylabel={ {{yaxis.label}} }, xtick=data, xtick align=outside, + ylabel style={font=\scriptsize}, + xlabel style={font=\scriptsize}, xticklabels from table={{'{#2}'}}{{'{'}}{{xaxis.parameter}}{{'}'}}, + xticklabel style={rotate=45, anchor=east, font=\scriptsize}, + yticklabel style={font=\scriptsize}, ymajorgrids=true, yminorgrids=true, bar width=7pt, cycle list name=color list, ybar, - legend style={at={(0.5,-0.1)},anchor=north} + legend to name=customlegend, + legend style={font=\small} ] {% for col in columns %} \addplot table [x expr=\coordindex, y={{ col }}] {{'{#2}'}} ; @@ -28,14 +32,28 @@ \begin{figure}[H] + \centering + {% if anim_dimension_values %} + {% set n = anim_dimension_values | length %} + {% set ncols = (n**0.5) | round(0, 'ceil') %} + {% set nrows = (n / ncols) | round(0, 'ceil') %} + {% set subfig_width = ((0.95 / ncols) | round(3)) %} -{% if anim_dimension_values %} - {% for dim in anim_dimension_values %} - \plot{\data{{ loop.index | inttouniquestr }}} - \caption{ {{secondary_axis.label}}={{dim}} } - {% endfor %} -{% else %} - \plot{\data{{1 | inttouniquestr }}} -{% endif %} -\caption{ {{caption}} } + {% for dim in anim_dimension_values %} + \begin{subfigure}[t]{ {{subfig_width}}\textwidth} + \centering + \plot{\data{{ loop.index | inttouniquestr }}} + \caption{ {{secondary_axis.label}}={{dim}} } + \end{subfigure} + {% if (loop.index0 + 1) % ncols == 0 %} + \par\vspace{1em} + {% endif %} + {% endfor %} + {% else %} + \plot{\data{{1 | inttouniquestr }}} + {% endif %} + \vspace{1em} + \centering + \pgfplotslegendfromname{customlegend} + \caption{ {{caption}} } \end{figure} From fad5c035cd6d3450db7e047f1f93792a1a502ff3 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Wed, 4 Feb 2026 14:14:08 +0100 Subject: [PATCH 65/69] add image node tests --- .../tests/images/test_imageDownload.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/feelpp/benchmarking/json_report/tests/images/test_imageDownload.py diff --git a/src/feelpp/benchmarking/json_report/tests/images/test_imageDownload.py b/src/feelpp/benchmarking/json_report/tests/images/test_imageDownload.py new file mode 100644 index 000000000..b5a04f656 --- /dev/null +++ b/src/feelpp/benchmarking/json_report/tests/images/test_imageDownload.py @@ -0,0 +1,61 @@ +import pytest +from feelpp.benchmarking.json_report.schemas.jsonReport import ImageNode +from pydantic import ValidationError + +class TestImageNodeDownloadFeature: + """Tests for image node download functionality""" + + def test_image_node_with_id(self): + """Test ImageNode with optional id field for cross-referencing""" + node = ImageNode( + type="image", + src="https://example.com/diagram.png", + id="architecture_diagram", + caption="System Architecture" + ) + assert node.id == "architecture_diagram" + assert node.caption == "System Architecture" + + def test_image_node_remote_false_explicit(self): + """Test ImageNode with explicit is_remote=False""" + node = ImageNode(type="image", src="./images/test.png", is_remote=False) + assert node.is_remote is False + + def test_image_node_download_image_local_warning(self): + """Test that downloadImage warns when used on local image""" + node = ImageNode(type="image", src="local.png", is_remote=False) + with pytest.warns(UserWarning): + result = node.downloadImage() + assert result == "local.png" + + def test_image_node_download_image_local_returns_src(self): + """Test downloadImage returns src for local images""" + node = ImageNode(type="image", src="path/to/image.png", is_remote=False) + with pytest.warns(UserWarning): + result = node.downloadImage() + assert result == "path/to/image.png" + + def test_image_node_validation(self): + """Test ImageNode validation""" + with pytest.raises(ValidationError): + ImageNode.model_validate({"type": "image"}) + + def test_image_download(self, tmp_path): + """Test downloadImage downloads remote image to temporary directory""" + from unittest.mock import patch, MagicMock + + node = ImageNode(type="image", src="https://example.com/test.png", is_remote=True) + + # Mock the actual HTTP download to avoid network calls + mock_response = MagicMock() + mock_response.content = b"PNG_DATA_HERE" + mock_response.status_code = 200 + + with patch('requests.get', return_value=mock_response): + result = node.downloadImage(str(tmp_path)) + + assert result == "test.png" + + downloaded_file = tmp_path / "test.png" + assert downloaded_file.exists() + assert downloaded_file.read_bytes() == b"PNG_DATA_HERE" From 6f09eb44b51cc5f3c5058d677e729ea4241b1d32 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Wed, 4 Feb 2026 14:14:18 +0100 Subject: [PATCH 66/69] add some latex generation tests --- .../tests/text/test_textController.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/feelpp/benchmarking/json_report/tests/text/test_textController.py b/src/feelpp/benchmarking/json_report/tests/text/test_textController.py index fe611aabf..17c7e2ce9 100644 --- a/src/feelpp/benchmarking/json_report/tests/text/test_textController.py +++ b/src/feelpp/benchmarking/json_report/tests/text/test_textController.py @@ -100,3 +100,33 @@ def test_dynamic_mode_invalid_operation_ignored(self): result = ctrl.generate() # unknown op ignored, value replaced normally assert result == "Value: test" + + # ------------------------------------------------------------------ + # LaTeX formatting: escaping and conversions + # ------------------------------------------------------------------ + @pytest.mark.parametrize("text,expected", [ + (r"50% complete", r"50\% complete"), + ("This is *bold* text", "This is \\textbf{bold} text"), + ("*first* and *second*", "\\textbf{first} and \\textbf{second}"), + ("stem:[x = \\frac{1}{2}]", "$x = \\frac{1}{2}$"), + ("Math: stem:[ a^2 + b^2 ] here", "Math: $a^2 + b^2$ here"), + ("https://example.com[Site]", "\\href{https://example.com}{Site}"), + ("https://example.com[]", "\\url{https://example.com}"), + ("<>", "\\hyperlink{section_id}{Label}"), + ("<>", "\\ref{equation_1}"), + ("*Bold* with stem:[x^2] and https://s.com[link]", + "\\textbf{Bold} with $x^2$ and \\href{https://s.com}{link}"), + ]) + def test_latex_formatting_conversions(self, text, expected): + """Test LaTeX format conversions from AsciiDoc syntax""" + ctrl = Controller({}, Text(content=text)) + result = ctrl.generate(format="tex") + assert result == expected + + def test_unknown_format_raises_error(self): + """Unknown format should raise NotImplementedError""" + ctrl = Controller({}, Text(content="test")) + with pytest.raises(NotImplementedError): + ctrl.generate(format="unknown") + + From a5cbe70d1a5bc8b913364f0d6e7ced6e01675355 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Wed, 4 Feb 2026 14:35:02 +0100 Subject: [PATCH 67/69] rm comment --- .../json_report/templates/tex/json2tex_report.tex.j2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 index 1db2a449f..84d3d5aa4 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/json2tex_report.tex.j2 @@ -7,9 +7,9 @@ \documentclass[11pt]{report} \usepackage{graphicx} -\usepackage{pgf-pie} % For pie charts -\usepackage{currfile} % Required for getting the current file name -\usepackage{tikz} % Required for drawing graphics +\usepackage{pgf-pie} +\usepackage{currfile} +\usepackage{tikz} \usepackage{pgfplots} \usepackage{pgfplotstable} \usepackage{underscore} From d0ad8de21a3ea50b701cb47094247b7c6b13abe5 Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Wed, 4 Feb 2026 14:48:09 +0100 Subject: [PATCH 68/69] escape backslash --- .../tikz/templates/groupedBarChart.tex.j2 | 2 +- .../figures/tikz/templates/scatterChart.tex.j2 | 2 +- .../tikz/templates/stackedBarChart.tex.j2 | 2 +- .../figures/tikz/templates/tableChart.tex.j2 | 2 +- .../templates/tex/macros/table.tex.j2 | 18 +++++++++--------- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 index f708acf53..aaa4f79f0 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/groupedBarChart.tex.j2 @@ -27,7 +27,7 @@ {% for fn in csv_filenames %} -\pgfplotstableread[col sep=comma]{{'{\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} +\pgfplotstableread[col sep=comma]{{'{\\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 index ed3206e1f..606e5dd41 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/scatterChart.tex.j2 @@ -35,7 +35,7 @@ {% for fn in csv_filenames %} -\pgfplotstableread[col sep=comma]{{'{\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} +\pgfplotstableread[col sep=comma]{{'{\\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 index ad2bb9e75..4dd536b43 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/stackedBarChart.tex.j2 @@ -7,7 +7,7 @@ } {% for fn in csv_filenames %} -\pgfplotstableread[col sep=comma]{{'{\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} +\pgfplotstableread[col sep=comma]{{'{\\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 b/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 index 038c569ca..cc26822d2 100644 --- a/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 +++ b/src/feelpp/benchmarking/json_report/figures/tikz/templates/tableChart.tex.j2 @@ -1,7 +1,7 @@ {% for fn in csv_filenames %} -\pgfplotstableread[col sep=comma]{{'{\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} +\pgfplotstableread[col sep=comma]{{'{\\detokenize{'}}{{fn}}{{'}}'}}\data{{loop.index | inttouniquestr }} {% endfor %} diff --git a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 index abbde3a54..61ab89348 100644 --- a/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 +++ b/src/feelpp/benchmarking/json_report/templates/tex/macros/table.tex.j2 @@ -3,15 +3,15 @@ {% macro escapeSpecialChars(text) -%} {{ text | string | replace('\\', '\textbackslash{}') - | replace('{', '\{') - | replace('}', '\}') - | replace('#', '\#') - | replace('$', '\$') - | replace('%', '\%') - | replace('&', '\&') - | replace('_', '\_') - | replace('~', '\textasciitilde{}') - | replace('^', '\^{}') + | replace('{', '\\{') + | replace('}', '\\}') + | replace('#', '\\#') + | replace('$', '\\$') + | replace('%', '\\%') + | replace('&', '\\&') + | replace('_', '\\_') + | replace('~', '\\textasciitilde{}') + | replace('^', '\\^{}') }} {%- endmacro %} From 18d67f7672809e657e750af1215785bd9236ec1c Mon Sep 17 00:00:00 2001 From: Javier Cladellas Date: Wed, 4 Feb 2026 14:48:23 +0100 Subject: [PATCH 69/69] add latex golden files --- .../tests/data/golden/basic_text.tex | 80 ++++++ .../tests/data/golden/list_with_data.tex | 35 +++ .../tests/data/golden/plot_features.tex | 129 ++++++++++ .../tests/data/golden/preprocessor.tex | 27 ++ .../json_report/tests/data/golden/report.tex | 163 +++++++++++++ .../tests/data/golden/table_features.tex | 230 ++++++++++++++++++ .../json_report/tests/test_jsonReport.py | 11 +- 7 files changed, 673 insertions(+), 2 deletions(-) create mode 100644 src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.tex create mode 100644 src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.tex create mode 100644 src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.tex create mode 100644 src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.tex create mode 100644 src/feelpp/benchmarking/json_report/tests/data/golden/report.tex create mode 100644 src/feelpp/benchmarking/json_report/tests/data/golden/table_features.tex diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.tex b/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.tex new file mode 100644 index 000000000..0ad15f7d6 --- /dev/null +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/basic_text.tex @@ -0,0 +1,80 @@ +\documentclass[11pt]{report} +\usepackage{graphicx} +\usepackage{pgf-pie} +\usepackage{currfile} +\usepackage{tikz} +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\usepackage{underscore} +\usepackage{amsfonts} +\usepackage{hyperref} +\usepackage{xurl} +\usepackage{subcaption} +\usepackage{amsmath} +\usepackage{float} +\usepackage[parfill]{parskip} + +\setlength{\parindent}{0pt} +\usepgfplotslibrary{fillbetween} + +\pgfplotsset{compat=newest} + +\begin{document} + +\chapter{} + +\section{Introduction} + +This is a basic end-to-end test to verify that plain text and sections render correctly. + +\subsection{Subsection} + +Nested sections should appear with correct formatting. + +\section{Raw Object} + +Custom from file : SOME RAW TEST + +Custom from inline : Some data i want to refactor across nodes + +\section{Object from file} + +Number of students 2 + +\begin{itemize} + \item Javier: 100 (B) + \item Oscar: 90 (C) +\end{itemize} + +\section{Inline Object} + +Value my data value + +\section{References} + +Unchanged reference : SOME RAW TEST + +Reference lower : some raw test + +Inline ref : Some data i want to refactor across nodes + +Inline UPPER : SOME DATA I WANT TO REFACTOR ACROSS NODES + + +\begin{itemize} + \item Javier Copy: 100 (B) + + \item Oscar Copy: 90 (C) + +\end{itemize} + + +Dumped json : {"students": {"javier": {"score": "100", "grade": "B"}, "oscar": {"score": "90", "grade": "C"}}} + +Inline object from ref: my data value + +Object Casted to raw: {"data1": "my data value"} + + + +\end{document} diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.tex b/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.tex new file mode 100644 index 000000000..3465e693a --- /dev/null +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/list_with_data.tex @@ -0,0 +1,35 @@ +\documentclass[11pt]{report} + +\usepackage{graphicx} +\usepackage{pgf-pie} +\usepackage{currfile} +\usepackage{tikz} +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\usepackage{underscore} +\usepackage{amsfonts} +\usepackage{hyperref} +\usepackage{xurl} +\usepackage{subcaption} +\usepackage{amsmath} +\usepackage{float} +\usepackage[parfill]{parskip} + +\setlength{\parindent}{0pt} +\usepgfplotslibrary{fillbetween} + +\pgfplotsset{compat=newest} + +\begin{document} + +\chapter{} + +\section{Metadata} + +\begin{itemize} + \item Hostname: machine-01 + \item Author: tester + \item Version: 1.0 +\end{itemize} + +\end{document} diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.tex b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.tex new file mode 100644 index 000000000..288678ec8 --- /dev/null +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/plot_features.tex @@ -0,0 +1,129 @@ + +\documentclass[11pt]{report} + +\usepackage{graphicx} +\usepackage{pgf-pie} +\usepackage{currfile} +\usepackage{tikz} +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\usepackage{underscore} +\usepackage{amsfonts} +\usepackage{hyperref} +\usepackage{xurl} +\usepackage{subcaption} +\usepackage{amsmath} +\usepackage{float} +\usepackage[parfill]{parskip} + +\setlength{\parindent}{0pt} +\usepgfplotslibrary{fillbetween} + +\pgfplotsset{compat=newest} + +\begin{document} + + +\chapter{} + + +\section{Score Analysis} + + +\DeclareRobustCommand{\plot}[2][]{ + \begin{tikzpicture} + \begin{axis}[ + width=0.95\textwidth, height=\linewidth, + xlabel={ Subject }, ylabel={ Average Score }, + xtick=data, xtick align=outside, + ylabel style={font=\scriptsize}, + xlabel style={font=\scriptsize}, + ymajorgrids=true, yminorgrids=true, + xticklabels from table={#2}{subject}, + xticklabel style={rotate=45, anchor=east, font=\scriptsize}, + yticklabel style={font=\scriptsize}, + cycle list name=color list, + legend to name=customlegend, + legend style={font=\small} + ] + \addplot+[mark=*,mark size=0.7pt] table [x expr=\coordindex, y=Alice] {#2} ; + \addlegendentry{ Alice } + \addplot+[mark=*,mark size=0.7pt] table [x expr=\coordindex, y=Bob] {#2} ; + \addlegendentry{ Bob } + \addplot+[mark=*,mark size=0.7pt] table [x expr=\coordindex, y=Charlie] {#2} ; + \addlegendentry{ Charlie } + + + \end{axis} + \end{tikzpicture} +} + + +\pgfplotstableread[col sep=comma]{\detokenize{}}\dataA + + +\begin{figure}[H] + \centering + \plot{\dataA} + \vspace{1em} + \centering + \pgfplotslegendfromname{customlegend} + \caption{ Scores by Subject } +\end{figure} + + + + + + +\DeclareRobustCommand{\plot}[2][]{ + \begin{tikzpicture} + \begin{axis}[ + width=0.95\textwidth, height=0.6172\textwidth, + xlabel={ Subject }, ylabel={ Number of Passes }, + xtick=data, xtick align=outside, + ylabel style={font=\scriptsize}, + xlabel style={font=\scriptsize}, + xticklabels from table={#2}{subject}, + xticklabel style={rotate=45, anchor=east, font=\scriptsize}, + yticklabel style={font=\scriptsize}, + ymajorgrids=true, yminorgrids=true, + bar width=7pt, + cycle list name=color list, + ybar, + legend to name=customlegend, + legend style={font=\small} + ] + \addplot table [x expr=\coordindex, y=Alice] {#2} ; + \addlegendentry{ Alice } + \addplot table [x expr=\coordindex, y=Bob] {#2} ; + \addlegendentry{ Bob } + \addplot table [x expr=\coordindex, y=Charlie] {#2} ; + \addlegendentry{ Charlie } + + \end{axis} + \end{tikzpicture} +} + + +\pgfplotstableread[col sep=comma]{\detokenize{}}\dataA + + +\begin{figure}[H] + \centering + \plot{\dataA} + \vspace{1em} + \centering + \pgfplotslegendfromname{customlegend} + \caption{ Pass Rate per Subject } +\end{figure} + + +The scatter plot shows the average score per subject for each student, while the stacked bar plot visualizes how many subjects each student passed. + + + + + + +\end{document} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.tex b/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.tex new file mode 100644 index 000000000..f9535aa1e --- /dev/null +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/preprocessor.tex @@ -0,0 +1,27 @@ +\documentclass[11pt]{report} +\usepackage{graphicx} +\usepackage{pgf-pie} +\usepackage{currfile} +\usepackage{tikz} +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\usepackage{underscore} +\usepackage{amsfonts} +\usepackage{hyperref} +\usepackage{xurl} +\usepackage{subcaption} +\usepackage{amsmath} +\usepackage{float} +\usepackage[parfill]{parskip} + +\setlength{\parindent}{0pt} +\usepgfplotslibrary{fillbetween} +\pgfplotsset{compat=newest} + +\begin{document} + +\chapter{} + +Processed Text: Foo Foo Baz + +\end{document} diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/report.tex b/src/feelpp/benchmarking/json_report/tests/data/golden/report.tex new file mode 100644 index 000000000..207e24b09 --- /dev/null +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/report.tex @@ -0,0 +1,163 @@ +\documentclass[11pt]{report} + +\usepackage{graphicx} +\usepackage{pgf-pie} +\usepackage{currfile} +\usepackage{tikz} +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\usepackage{underscore} +\usepackage{amsfonts} +\usepackage{hyperref} +\usepackage{xurl} +\usepackage{subcaption} +\usepackage{amsmath} +\usepackage{float} +\usepackage[parfill]{parskip} + + +\setlength{\parindent}{0pt} +\usepgfplotslibrary{fillbetween} +\pgfplotsset{compat=newest} + + +\begin{document} + +\chapter{} + +\section{Report Overview} + +This report analyzes students' performance, attendance, and remarks across multiple subjects. We summarize scores, compute averages, and visualize trends. + +Placeholders in this report allow dynamic insertion of values from the datasets. For example, total students: 9. + +\section{Student Table} + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l r c c } + \hline +name & subject \\ + \hline +ALICE & Math \\ +BOB & Math \\ +CHARLIE & Math \\ +ALICE & Physics \\ +BOB & Physics \\ +CHARLIE & Physics \\ +ALICE & Chemistry \\ +BOB & Chemistry \\ +CHARLIE & Chemistry \\ + \hline + \end{tabular}} +\end{table} + +\section{Attendance} + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l l l l } + \hline +name & subject \\ + \hline +Alice & Math \\ +Bob & Math \\ +Charlie & Math \\ +Alice & Physics \\ +Bob & Physics \\ +Charlie & Physics \\ +Alice & Chemistry \\ +Bob & Chemistry \\ +Charlie & Chemistry \\ + \hline + \end{tabular}} +\end{table} + +\section{Performance Analysis} + +\DeclareRobustCommand{\plot}[2][]{ + \begin{tikzpicture} + \begin{axis}[ + width=0.95\textwidth, height=\linewidth, + xlabel={ Subject }, ylabel={ Average Score }, + xtick=data, xtick align=outside, + ylabel style={font=\scriptsize}, + xlabel style={font=\scriptsize}, + ymajorgrids=true, yminorgrids=true, + xticklabels from table={#2}{subject}, + xticklabel style={rotate=45, anchor=east, font=\scriptsize}, + yticklabel style={font=\scriptsize}, + cycle list name=color list, + legend to name=customlegend, + legend style={font=\small} + ] + \addplot+[mark=*,mark size=0.7pt] table [x expr=\coordindex, y=ALICE] {#2} ; + \addlegendentry{ ALICE } + \addplot+[mark=*,mark size=0.7pt] table [x expr=\coordindex, y=BOB] {#2} ; + \addlegendentry{ BOB } + \addplot+[mark=*,mark size=0.7pt] table [x expr=\coordindex, y=CHARLIE] {#2} ; + \addlegendentry{ CHARLIE } + + + \end{axis} + \end{tikzpicture} +} + +\pgfplotstableread[col sep=comma]{\detokenize{}}\dataA + +\begin{figure}[H] + \centering + \plot{\dataA} + \vspace{1em} + \centering + \pgfplotslegendfromname{customlegend} + \caption{ Average Scores by Subject } +\end{figure} + +\DeclareRobustCommand{\plot}[2][]{ + \begin{tikzpicture} + \begin{axis}[ + width=0.95\textwidth, height=0.6172\textwidth, + xlabel={ Student }, ylabel={ Attendance (%) }, + xtick=data, xtick align=outside, + ylabel style={font=\scriptsize}, + xlabel style={font=\scriptsize}, + xticklabels from table={#2}{name}, + xticklabel style={rotate=45, anchor=east, font=\scriptsize}, + yticklabel style={font=\scriptsize}, + ymajorgrids=true, yminorgrids=true, + bar width=7pt, + cycle list name=color list, + ybar, + legend to name=customlegend, + legend style={font=\small} + ] + \addplot table [x expr=\coordindex, y=Chemistry] {#2} ; + \addlegendentry{ Chemistry } + \addplot table [x expr=\coordindex, y=Math] {#2} ; + \addlegendentry{ Math } + \addplot table [x expr=\coordindex, y=Physics] {#2} ; + \addlegendentry{ Physics } + + \end{axis} + \end{tikzpicture} +} + +\pgfplotstableread[col sep=comma]{\detokenize{}}\dataA + +\begin{figure}[H] + \centering + \plot{\dataA} + \vspace{1em} + \centering + \pgfplotslegendfromname{customlegend} + \caption{ Attendance Rate by Student } +\end{figure} + +The scatter plot shows average scores per subject, while the bar chart shows average attendance rates for each student. + +\section{Teacher Notes} + +Teacher remarks (loaded from a raw text file with preprocessing applied): Excellent performance by Alice in most subjects. Bob shows consistent improvement. Charlie needs attention in Math but is improving in Physics and Chemistry. + +\end{document} diff --git a/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.tex b/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.tex new file mode 100644 index 000000000..b4afabf3d --- /dev/null +++ b/src/feelpp/benchmarking/json_report/tests/data/golden/table_features.tex @@ -0,0 +1,230 @@ +\documentclass[11pt]{report} + +\usepackage{graphicx} +\usepackage{pgf-pie} +\usepackage{currfile} +\usepackage{tikz} +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\usepackage{underscore} +\usepackage{amsfonts} +\usepackage{hyperref} +\usepackage{xurl} +\usepackage{subcaption} +\usepackage{amsmath} +\usepackage{float} +\usepackage[parfill]{parskip} + +\setlength{\parindent}{0pt} +\usepgfplotslibrary{fillbetween} +\pgfplotsset{compat=newest} + +\begin{document} + + +\chapter{} + +\section{Student Performance} + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l l c c } + \hline +Student & Subject & Score & Grade & Passed \\ + \hline +Alice & Math & 85.0 & B & ✔️ \\ +Bob & Math & 78.0 & C & ✔️ \\ +Charlie & Math & 62.0 & D & ✔️ \\ +Alice & Physics & 92.0 & A & ✔️ \\ +Bob & Physics & 88.0 & B & ✔️ \\ +Charlie & Physics & 55.0 & D & ❌ \\ +Alice & Chemistry & 70.0 & C & ✔️ \\ +Bob & Chemistry & 65.0 & D & ✔️ \\ +Charlie & Chemistry & 45.0 & D & ❌ \\ + \hline + \end{tabular}} +\end{table} + +\section{Inline Table} + +\subsection{Default inline table} + + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l l l l } + \hline +time & power & temperature & comfort & power\_temp\_sum \\ + \hline +1.2 & 10 & 22.5 & 0.5 & 32.5 \\ +2.3 & 20 & 23.0 & 0.6 & 43.0 \\ +3.4 & 30 & 21.3 & 0.7 & 51.3 \\ +4.5 & 40 & 22.1 & 0.8 & 62.1 \\ + \hline + \end{tabular}} +\end{table} + + +\subsection{Styled inline table} + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ c l l l l } + \hline +time & power & temperature & power\_temp\_sum & comfort \\ + \hline +1.2 & 10 & 22.5 & 32.5 & 0.5 \\ +2.3 & 20 & 23.0 & 43.0 & 0.6 \\ +3.4 & 30 & 21.3 & 51.3 & 0.7 \\ +4.5 & 40 & 22.1 & 62.1 & 0.8 \\ + \hline + \end{tabular}} +\end{table} + + + + +\subsection{Ref to inline} + + + + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l l l l } + \hline +time & power & temperature & comfort & power\_temp\_sum \\ + \hline +2.3 & 20 & 23.0 & 0.6 & 43.0 \\ +3.4 & 30 & 21.3 & 0.7 & 51.3 \\ +4.5 & 40 & 22.1 & 0.8 & 62.1 \\ + \hline + \end{tabular}} +\end{table} + + + + + + + +\section{File Table} + + +\subsection{Default file table} + + + + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l l l l } + \hline +Scenario & Time\_s & Memory\_MB & result & memory\_per\_time \\ + \hline +Run A & 12 & 1024 & 🟢 & 81.92 \\ +Run B & 25 & 2048 & 🔴 & 81.59 \\ +Run C & 15 & 1536 & 🟢 & 96.60 \\ + \hline + \end{tabular}} +\end{table} + + + + + +\subsection{Styled file table} + + + + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ c l l } + \hline +result & Scenario & memory\_per\_time \\ + \hline +🟢 & Run A & 81.92 \\ +🔴 & Run B & 81.59 \\ +🟢 & Run C & 96.60 \\ + \hline + \end{tabular}} +\end{table} + + + +\subsection{Ref to file table} + + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l l } + \hline +result & Scenario & Memory\_MB \\ + \hline +🔴 & 1 & 2048 \\ +🟢 & 2 & 2560 \\ + \hline + \end{tabular}} +\end{table} + + + +\section{Table from Json} + + +\subsection{Default table from json} + + + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l l } + \hline +subject & days\_absent & days\_present \\ + \hline +Chemistry & 4.3 & 15.7 \\ +Math & 4.7 & 15.3 \\ +Physics & 4.7 & 15.3 \\ + \hline + \end{tabular}} +\end{table} + + +\subsection{Styled table from json} + + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l r r } + \hline +subject & Absents (Days) & Present (Days) \\ + \hline +Chemistry & 4.3 & 15.7 \\ +Math & 4.7 & 15.3 \\ +Physics & 4.7 & 15.3 \\ + \hline + \end{tabular}} +\end{table} + + +\subsection{Ref to json table} + + +\begin{table}[H] + \centering + \resizebox{\textwidth}{!}{\begin{tabular}{ l l l } + \hline +subject & days\_absent & days\_present \\ + \hline +Math & 4.7 & 15.3 \\ +Physics & 4.7 & 15.3 \\ +Chemistry & 4.3 & 15.7 \\ + \hline + \end{tabular}} +\end{table} + + + + +\end{document} \ No newline at end of file diff --git a/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py b/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py index 391180d23..a3309b98a 100644 --- a/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py +++ b/src/feelpp/benchmarking/json_report/tests/test_jsonReport.py @@ -23,6 +23,12 @@ def normalizeReport(content: str) -> str: content, flags=re.DOTALL ) + content = re.sub( + r"\\detokenize\{[^}]*\}", + r"\\detokenize{}", + content, flags=re.DOTALL + ) + lines = [line.rstrip() for line in content.splitlines() if line.strip()] return "\n".join(lines) @@ -47,7 +53,8 @@ def assert_report_matches_golden(output_file: str, golden_file: str): @pytest.mark.parametrize("report_filename", ["basic_text.json", "list_with_data.json", "table_features.json", "plot_features.json","preprocessor.json","report.json" ] ) -def test_jsonReportCases(report_filename,output_format="adoc"): +@pytest.mark.parametrize("output_format",["adoc","tex"]) +def test_jsonReportCases(report_filename,output_format): data_dir = os.path.join(os.path.dirname(__file__),"data") controller = JsonReportController( report_filepath=os.path.join(data_dir,report_filename), @@ -55,6 +62,6 @@ def test_jsonReportCases(report_filename,output_format="adoc"): ) with tempfile.TemporaryDirectory() as tmpdir: - output_file = controller.render(output_dirpath=os.path.join(data_dir,tmpdir)) + output_file = controller.render(output_dirpath=os.path.join(data_dir,"outputs")) golden_file = os.path.join(data_dir,"golden",report_filename.replace(".json",f".{output_format}")) assert_report_matches_golden(output_file,golden_file) \ No newline at end of file