Skip to content

Commit 836692b

Browse files
committed
HTML/Javascript batch processing reports
thanks to AG Grid
1 parent 7153c87 commit 836692b

File tree

2 files changed

+99
-90
lines changed

2 files changed

+99
-90
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ __pycache__/
55

66
.venv/
77

8-
from_python/*.md
8+
from_python/*.md
9+
from_python/*.html

from_python/batch_processing_analysis.py

+97-89
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from rich.console import Console
88
from rich.traceback import install
99
import logging
10+
from time import localtime, strftime
1011

1112
# Add root of HexMeshWorkshop project folder in path
1213
project_root = str(Path(__file__).parent.parent.absolute())
@@ -17,109 +18,116 @@
1718

1819
from modules.data_folder_types import *
1920

20-
nb_CAD = 0
21-
22-
nb_meshes_fail = 0
23-
nb_meshes_success = 0
24-
25-
missing_execution: list[str] = list()
26-
labelings_fail: list[str] = list()
27-
invalid_labelings: list[str] = list()
28-
valid_labelings_but_non_monotone: list[str] = list()
29-
valid_and_all_monotone_labelings: list[str] = list()
30-
31-
per_labeling_feature_edges_stats: dict = dict()
21+
logging.getLogger().setLevel(logging.INFO)
22+
23+
report_name = strftime('%Y-%m-%d_%Hh%M_report', localtime())
24+
25+
# https://www.ag-grid.com/react-data-grid/getting-started/
26+
HTML_report = f"""<!DOCTYPE html>
27+
<html lang="en">
28+
<head>
29+
<title>{report_name}</title>
30+
<meta charSet="UTF-8"/>
31+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
32+
<style media="only screen">
33+
html, body {{
34+
height: 100%;
35+
width: 100%;
36+
margin: 0;
37+
box-sizing: border-box;
38+
-webkit-overflow-scrolling: touch;
39+
}}
40+
41+
html {{
42+
position: absolute;
43+
top: 0;
44+
left: 0;
45+
padding: 0;
46+
overflow: auto;
47+
}}
48+
49+
body {{
50+
padding: 16px;
51+
overflow: auto;
52+
background-color: transparent
53+
}}
54+
</style>
55+
</head>
56+
<body style="text-align: center;">
57+
<div id="myGrid" style="width: 100%; height: 100%" class="ag-theme-alpine-dark"></div>
58+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ag-grid-community.min.js"></script>
59+
<script>
60+
// Grid API: Access to Grid API methods
61+
let gridApi;
62+
63+
// Grid Options: Contains all of the grid configurations
64+
const gridOptions = {{
65+
// Row Data: The data to be displayed.
66+
rowData: [
67+
"""
3268

3369
root_folder = root()
3470
for level_minus_1_folder in [x for x in root_folder.path.iterdir() if x.is_dir()]:
71+
CAD_name = level_minus_1_folder.name
3572
if not (level_minus_1_folder / step.FILENAMES.STEP).exists():
3673
logging.warning(f"Folder {level_minus_1_folder} has no {step.FILENAMES.STEP}")
3774
continue
38-
nb_CAD += 1
3975
if (level_minus_1_folder / 'Gmsh_0.1/').exists() and (level_minus_1_folder / 'Gmsh_0.1' / tet_mesh.FILENAMES.SURFACE_MESH_OBJ).exists():
40-
tet_folder = AbstractDataFolder.instantiate(level_minus_1_folder / 'Gmsh_0.1')
41-
nb_meshes_success += 1
76+
tet_folder: tet_mesh = AbstractDataFolder.instantiate(level_minus_1_folder / 'Gmsh_0.1')
77+
avg_edge_length = float(tet_folder.get_mesh_stats_dict()['edges']['length']['avg'])
4278
labeling_subfolders_generated_by_automatic_polycube: list[Path] = tet_folder.get_subfolders_generated_by('automatic_polycube')
4379
assert(len(labeling_subfolders_generated_by_automatic_polycube) <= 1)
4480
if len(labeling_subfolders_generated_by_automatic_polycube) == 0:
45-
missing_execution.append(str(level_minus_1_folder / 'Gmsh_0.1/'))
81+
HTML_report += f"""\n{{ name: "{CAD_name}", avg_edge_length: {avg_edge_length:.3f}, valid: null, nb_turning_points: null, percentage_removed: null, percentage_lost: null, percentage_preserved: null }},"""
4682
continue
4783
relative_path = labeling_subfolders_generated_by_automatic_polycube[0].relative_to(root_folder.path)
4884
if not (labeling_subfolders_generated_by_automatic_polycube[0] / labeling.FILENAMES.SURFACE_LABELING_TXT).exists():
49-
logging.warning(f"Folder {labeling_subfolders_generated_by_automatic_polycube[0] / labeling.FILENAMES.SURFACE_LABELING_TXT} has no {labeling.FILENAMES.SURFACE_LABELING_TXT}")
50-
labelings_fail.append(relative_path)
85+
HTML_report += f"""\n{{ name: "{CAD_name}", avg_edge_length: {avg_edge_length:.3f}, valid: null, nb_turning_points: null, percentage_removed: null, percentage_lost: null, percentage_preserved: null }},"""
5186
continue
5287
labeling_folder: labeling = AbstractDataFolder.instantiate(labeling_subfolders_generated_by_automatic_polycube[0])
5388
assert(labeling_folder.type() == 'labeling')
54-
stats = labeling_folder.get_labeling_stats_dict()
55-
per_labeling_feature_edges_stats[relative_path] = stats['feature-edges']
56-
if not labeling_folder.has_valid_labeling():
57-
invalid_labelings.append(relative_path)
58-
continue
59-
# valid labeling
60-
if labeling_folder.nb_turning_points() > 0:
61-
valid_labelings_but_non_monotone.append(relative_path)
62-
continue
63-
valid_and_all_monotone_labelings.append(relative_path)
89+
feature_edges_stats = labeling_folder.get_labeling_stats_dict()['feature-edges']
90+
total_feature_edges = feature_edges_stats['removed'] + feature_edges_stats['lost'] + feature_edges_stats['preserved']
91+
HTML_report += f"""\n{{ name: "{CAD_name}", avg_edge_length: {avg_edge_length:.3f}, valid: {str(labeling_folder.has_valid_labeling()).lower()}, nb_turning_points: {labeling_folder.nb_turning_points()}, percentage_removed: {feature_edges_stats['removed']/total_feature_edges*100:.2f}, percentage_lost: {feature_edges_stats['lost']/total_feature_edges*100:.2f}, percentage_preserved: {feature_edges_stats['preserved']/total_feature_edges*100:.2f} }},"""
6492
else:
65-
nb_meshes_fail += 1
66-
67-
print('# CAD')
68-
print('')
69-
print(f'nb CAD = {nb_CAD}')
70-
print('')
71-
72-
print('# Tet meshes')
73-
print('')
74-
print(f'nb meshes fail = {nb_meshes_fail} -> {nb_meshes_fail/nb_CAD*100} %')
75-
print(f'nb meshes success = {nb_meshes_success} -> {nb_meshes_success/nb_CAD*100} %')
76-
print('')
77-
78-
print('# Labelings')
79-
print('')
80-
81-
print('## Missing execution')
82-
print('')
83-
print(f'nb = {len(missing_execution)} -> {len(missing_execution)/nb_CAD*100} %')
84-
for rel_path in missing_execution:
85-
print(f'- {rel_path}')
86-
print('')
87-
88-
print('## Fails')
89-
print('')
90-
print(f'nb = {len(labelings_fail)} -> {len(labelings_fail)/nb_CAD*100} %')
91-
for rel_path in labelings_fail:
92-
print(f'- {rel_path}')
93-
print('')
94-
95-
print('## Invalid')
96-
print('')
97-
print(f'nb = {len(invalid_labelings)} -> {len(invalid_labelings)/nb_CAD*100} %')
98-
for rel_path in invalid_labelings:
99-
print(f'- {rel_path}')
100-
print('')
101-
102-
print('## Valid but non-monotone boundaries')
103-
print('')
104-
print(f'nb = {len(valid_labelings_but_non_monotone)} -> {len(valid_labelings_but_non_monotone)/nb_CAD*100} %')
105-
for rel_path in valid_labelings_but_non_monotone:
106-
print(f'- {rel_path}')
107-
print('')
108-
109-
print('## Valid and non-monotone boundaries')
110-
print('')
111-
print(f'nb = {len(valid_and_all_monotone_labelings)} -> {len(valid_and_all_monotone_labelings)/nb_CAD*100} %')
112-
for rel_path in valid_and_all_monotone_labelings:
113-
print(f'- {rel_path}')
114-
print('')
115-
116-
print('# Feature edges preservation')
117-
118-
for rel_path,feature_edges_stats in per_labeling_feature_edges_stats.items():
119-
total_nb_feature_edges = feature_edges_stats['removed']+feature_edges_stats['lost']+feature_edges_stats['preserved']
120-
print(f'## Feature edges in {rel_path}')
121-
print('')
122-
print(f"- nb removed : {feature_edges_stats['removed']} -> {feature_edges_stats['removed']/total_nb_feature_edges*100} %")
123-
print(f"- nb lost : {feature_edges_stats['lost']} -> {feature_edges_stats['lost']/total_nb_feature_edges*100} %")
124-
print(f"- nb preserved : {feature_edges_stats['preserved']} -> {feature_edges_stats['preserved']/total_nb_feature_edges*100} %")
125-
print('') # separation with empty line
93+
HTML_report += f"""\n{{ name: "{CAD_name}", avg_edge_length: null, valid: null, nb_turning_points: null, percentage_removed: null, percentage_lost: null, percentage_preserved: null }},"""
94+
95+
HTML_report += """
96+
],
97+
// Column Definitions: Defines & controls grid columns.
98+
columnDefs: [
99+
{ field: "name", headerName: "CAD model", cellDataType: 'text' },
100+
{
101+
headerName: 'tet mesh',
102+
children: [
103+
{ field: "avg_edge_length", headerName: "avg. edge length", cellDataType: 'number' },
104+
]
105+
},
106+
{
107+
headerName: 'labeling',
108+
children: [
109+
{ field: "valid", headerName: "valid", cellDataType: 'boolean' },
110+
{ field: "nb_turning_points", headerName: "#turning-points", cellDataType: 'number' },
111+
]
112+
},
113+
{
114+
headerName: 'feature edges',
115+
children: [
116+
{ field: "percentage_removed", headerName: "%age removed", cellDataType: 'number' },
117+
{ field: "percentage_lost", headerName: "%age lost", cellDataType: 'number' },
118+
{ field: "percentage_preserved", headerName: "%age preserved", cellDataType: 'number' },
119+
]
120+
},
121+
],
122+
};
123+
124+
// Create Grid: Create new grid within the #myGrid div, using the Grid Options object
125+
gridApi = agGrid.createGrid(document.querySelector('#myGrid'), gridOptions);
126+
</script>
127+
</body>
128+
</html>
129+
"""
130+
131+
with open(f'{report_name}.html','w') as HTML_file:
132+
logging.info(f'Writing {report_name}.html...')
133+
HTML_file.write(HTML_report)

0 commit comments

Comments
 (0)