Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

210 combine plot types into a single figure #224

Merged
merged 6 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/feelpp/benchmarking/report/atomicReports/atomicReport.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,8 @@ def createReport(self, base_dir, renderer):
"""
hash_params_headers, flat_hash_params = self.parseHashMap()

model=AtomicReportModel( self.runs )
view=AtomicReportView( self.plots_config )
controller=AtomicReportController(model,view)
controller=AtomicReportController(self.model,view)

renderer.render(
f"{base_dir}/{self.filename()}.adoc",
Expand All @@ -211,9 +210,7 @@ def createReport(self, base_dir, renderer):
flat_hash_param_map = flat_hash_params,
hash_params_headers = hash_params_headers,
description_path = self.description_path,
figures = controller.generateData("html"),
figure_csvs = controller.generateData("csv"),
figure_pgfs = controller.generateData("pgf")
figures = controller.generateAll()
)
)

Expand Down
4 changes: 1 addition & 3 deletions src/feelpp/benchmarking/report/base/baseComponent.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,7 @@ def createOverview(self,base_dir,renderer,parents,plots_config,master_df):
data = dict(
parent_catalogs = "-".join([parent.id for parent in parents]),
parents = parents,
figures = controller.generateData("html"),
figure_csvs = controller.generateData("csv"),
figure_pgfs = controller.generateData("pgf")
figures = controller.generateAll()
)
)

Expand Down
58 changes: 20 additions & 38 deletions src/feelpp/benchmarking/report/base/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,23 @@ def __init__(self, model, view):
self.model = model
self.view = view

def generatePlotly(self):
""" Creates plotly figures for each plot specified on the view config file
Returns a list of plotly figures.
"""
return [ figure.createFigure(self.model.master_df) for figure in self.view.figures ]

def generatePlotlyHtml(self):
""" Creates plotly figures in html for each plot specified on the view config file
Returns a list of plotly HTML figures
"""
return [ figure.createFigureHtml(self.model.master_df) for figure in self.view.figures ]

def generateTikz(self):
""" Creates Tikz/Pgf figures for each plot specified on the view config file
Returns:
list[str] LaTeX pgf plots.
"""
return [ figure.createTex(self.model.master_df) for figure in self.view.figures ]

def generateCsv(self):
""" Create a list containing the data for each plot specified on the view config in CSV format.
Returns (list[str]): List of csv data.
"""
return [ figure.createCsv(self.model.master_df) for figure in self.view.figures ]


def generateData(self,format):
""" Creates a list of data depending on the desired format, using the plot configuration and the model's master dataframe"""
if format == "plotly":
return self.generatePlotly()
elif format == "html":
return self.generatePlotlyHtml()
elif format in ["tikz", "pgf"]:
return self.generateTikz()
elif format == "csv":
return self.generateCsv()
else:
raise NotImplementedError
def generateAll(self):
return [
self.generateFigure(figure,plot_config.plot_types)
for figure,plot_config in zip(self.view.figures,self.view.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", "filename":"data.csv", "data":subfigure.createCsv(self.model.master_df) },
{ "display_text":"LaTeX", "filename":"figure.tex", "data":subfigure.createTex(self.model.master_df) },
],
"html": subfigure.createFigureHtml(self.model.master_df)
}
5 changes: 1 addition & 4 deletions src/feelpp/benchmarking/report/base/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,4 @@ def __init__(self,plots_config):
plots_config list[dict]. List with dictionaries specifying plots configuration.
"""
self.plots_config = [Plot(**d) for d in plots_config]

self.figures = []
for plot_config in self.plots_config:
self.figures.extend(FigureFactory.create(plot_config))
self.figures = [FigureFactory.create(plot_config) for plot_config in self.plots_config]
12 changes: 11 additions & 1 deletion src/feelpp/benchmarking/report/templates/atomicOverview.adoc.j2
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,14 @@
- {{parent.type.title()}} : {{parent.display_name}}
{% endfor %}

{%include "figures.html.j2" %}
++++
<style>
{%include "css/figures.css" %}
</style>

{%include "figures.html.j2" %}

<script type="text/javascript">
{%include "js/figureHelpers.js"%}
</script>
++++
19 changes: 18 additions & 1 deletion src/feelpp/benchmarking/report/templates/benchmark.adoc.j2
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ include::ROOT:{{description_path}}[leveloffset=+1]
* Total Tests: {{ session_info.num_cases }}
* Failures: {{ session_info.num_failures }}


++++
<style>
.scrollable {
overflow-x: auto;
}
</style>
++++
[.scrollable]
--
|===
{% for header in hash_params_headers %} | {% if header == 'partial_filepath' or header == 'logs_filepath' %} {% else %} {{ header }} {% endif %} {% endfor %}

Expand All @@ -32,9 +40,18 @@ include::ROOT:{{description_path}}[leveloffset=+1]

{% endfor %}
|===
--

{% if not empty %}
++++
<style>
{%include "css/figures.css" %}
</style>

{%include "figures.html.j2" %}

<script type="text/javascript">
{%include "js/figureHelpers.js"%}
</script>
++++
{% endif %}
78 changes: 78 additions & 0 deletions src/feelpp/benchmarking/report/templates/css/figures.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
.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);
}

.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;
}

.subfigure-container.active {
position: relative;
opacity: 1;
pointer-events: auto;
z-index: 1;
}

.subfigure-container.inactive {
opacity: 0;
pointer-events: none;
z-index: 0;
}

/* 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;
}

/* Figure tab button styling */
.figure-tab {
background: transparent;
border: none;
outline: none;
padding: 0.5rem 1rem;
margin-right: 0.3rem;
font-size: 1rem;
color: #007acc;
cursor: pointer;
border-radius: 4px 4px 0 0;
transition: background-color 0.2s ease, color 0.2s ease;
}

.figure-tab:hover,
.figure-tab.active {
background-color: #007acc;
color: #fff;
}

.export-container button {
background-color: #007acc;
color: #fff;
border: none;
padding: 0.4rem 0.8rem;
margin-right: 0.4rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
transition: background-color 0.2s ease;
}

.export-container button:hover {
background-color: #005fa3;
}
57 changes: 25 additions & 32 deletions src/feelpp/benchmarking/report/templates/figures.html.j2
Original file line number Diff line number Diff line change
@@ -1,36 +1,29 @@
{% for figure in figures %}
++++
<button id="download-csv-{{parent_catalogs}}-{{loop.index}}" > CSV </button>
<button id="download-pgf-{{parent_catalogs}}-{{loop.index}}" > LaTeX </button>
{{figure}}
++++
{% endfor %}
{% set figure_i = loop.index %}
<div class='figure-container'>
{% if figure.plot_types | length > 1%}
<div class='tabs-container'>
{% for plot_type in figure.plot_types %}
{% set plot_type_i = loop.index %}
<button class='figure-tab' onclick='switchTab({{figure_i}},{{plot_type_i}})' >{{plot_type}}</button>
{% endfor %}
</div>
{% endif %}

{% for subfigure in figure.subfigures %}
{% set subfigure_i = loop.index %}
<div class="subfigure-container {% if loop.first %}active{% else %}inactive{% endif %}" id="subfig_{{ figure_i }}_{{ loop.index }}">
<div class='export-container'>
{% for export in subfigure.exports %}
<button onclick='downloadString(`{{export.data | tojson | stripquotes}}`,"{{export.filename}}","plain/text")' >
{{export.display_text}}
</button>
{% endfor %}
</div>

++++
<script>
function downloadString(data, filename, mimeType ) {
const blob = new Blob([data], { type: `${mimeType};charset=utf-8` });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
{{subfigure.html}}
</div>
{% endfor %}
</div>

function addDownloadListener( button_id, data, filename, mimeType = 'text/plain') {
document.getElementById(button_id).addEventListener('click', function() {
downloadString(data,filename,mimeType);
});
}

{% for csv,pgf in zip(figure_csvs,figure_pgfs) %}
addDownloadListener('download-csv-{{parent_catalogs}}-{{loop.index}}' , `{{csv | tojson | stripquotes}}`, 'data.csv', 'text/csv')
addDownloadListener('download-pgf-{{parent_catalogs}}-{{loop.index}}' , `{{pgf | tojson | stripquotes}}`, 'figure.tex' )
{% endfor %}

</script>
++++
{% endfor %}
26 changes: 26 additions & 0 deletions src/feelpp/benchmarking/report/templates/js/figureHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

function downloadString(data, filename, mimeType ) {
const blob = new Blob([data], { type: `${mimeType};charset=utf-8` });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}

function switchTab(figureIndex, tabIndex) {
let subfigures = document.querySelectorAll(`[id^="subfig_${figureIndex}_"]`);
subfigures.forEach(subfig => {
subfig.classList.remove('active');
subfig.classList.add('inactive');
});

let activeSubfig = document.getElementById(`subfig_${figureIndex}_${tabIndex}`);
if(activeSubfig){
activeSubfig.classList.remove('inactive');
activeSubfig.classList.add('active');
}
}
Loading