-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
717 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,79 @@ | ||
from __future__ import annotations | ||
|
||
from dataclasses import dataclass | ||
from enum import Enum | ||
from pathlib import Path | ||
|
||
from jade.post.plotter import PlotType | ||
import yaml | ||
|
||
from jade.helper.aux_functions import PathLike | ||
|
||
|
||
@dataclass | ||
class ConfigAtlasProcessor: | ||
benchmark: str | ||
plots: list[PlotConfig] | ||
|
||
@classmethod | ||
def from_yaml(cls, config_file: PathLike) -> ConfigAtlasProcessor: | ||
"""Create a ConfigExcelProcessor object from a yaml file. | ||
Parameters | ||
---------- | ||
config_file : PathLike | ||
path to the configuration file | ||
Returns | ||
------- | ||
ConfigAtlasProcessor | ||
The configuration object | ||
""" | ||
with open(config_file) as f: | ||
cfg = yaml.safe_load(f) | ||
|
||
benchmark = Path(config_file).name.split(".")[0] | ||
|
||
plots = [] | ||
for table_name, dict in cfg.items(): | ||
plots.append(PlotConfig.from_dict(dict, table_name)) | ||
|
||
return cls(benchmark=benchmark, plots=plots) | ||
|
||
|
||
@dataclass | ||
class PlotConfig: | ||
name: str | ||
results: list[int | str] | ||
plot_type: PlotType | ||
title: str | ||
x_label: str | ||
y_labels: list[str] | ||
x: str | ||
y: str | ||
# optionals | ||
expand_runs: bool = True | ||
|
||
@classmethod | ||
def from_dict(cls, dictionary: dict, name: str) -> PlotConfig: | ||
return cls( | ||
name=name, | ||
results=dictionary["results"], | ||
plot_type=PlotType(dictionary["plot_type"]), | ||
title=dictionary["title"], | ||
x_label=dictionary["x_label"], | ||
y_labels=dictionary["y_labels"], | ||
x=dictionary["x"], | ||
y=dictionary["y"], | ||
expand_runs=dictionary.get("expand_runs", True), | ||
) | ||
|
||
|
||
class PlotType(Enum): | ||
BINNED = "binned" | ||
RATIO = "ratio" | ||
EXP = "exp points" | ||
EXP_GROUP = "exp points group" | ||
CE_EXP_GROUP = "exp points group CE" | ||
DISCRETE_EXP = "discrete exp points" | ||
GROUPED_BARS = "grouped bars" | ||
WAVES = "waves" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
import logging | ||
import os | ||
|
||
# import win32com.client | ||
# import aspose.words | ||
import docx | ||
import pandas as pd | ||
from docx.enum.table import WD_ALIGN_VERTICAL | ||
from docx.enum.text import WD_ALIGN_PARAGRAPH | ||
from docx.oxml import OxmlElement, parse_xml | ||
from docx.oxml.ns import nsdecls, qn | ||
from docx.shared import Inches | ||
|
||
from jade.helper.aux_functions import PathLike | ||
|
||
|
||
class Atlas: | ||
def __init__(self, template: PathLike, name: str): | ||
""" | ||
Atlas of plots for post-processing | ||
Parameters | ||
---------- | ||
template : PathLike | ||
path to the word template file. | ||
name : str | ||
name of the atlas file. | ||
Returns | ||
------- | ||
None. | ||
""" | ||
self.name = name # Name of the Atlas (from libraries) | ||
# Open The Atlas template | ||
doc = docx.Document(template) | ||
doc.add_heading("JADE ATLAS: " + name, level=0) | ||
self.outname = "atlas_" + name # Name for the outfile | ||
self.doc = doc # Word Document | ||
|
||
def insert_img(self, img, width=Inches(7.5)): | ||
"""Insert an image in the word document | ||
Parameters | ||
---------- | ||
img : _type_ | ||
_description_ | ||
width : _type_, optional | ||
_description_, by default Inches(7.5) | ||
""" | ||
self.doc.add_picture(img, width=width) | ||
last_paragraph = self.doc.paragraphs[-1] | ||
last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER | ||
|
||
# def insert_df( | ||
# self, | ||
# df, | ||
# caption=None, | ||
# highlight=False, | ||
# tablestyle=None, # , template_idx=None, | ||
# ): | ||
# """ | ||
# Inser a dataframe as a table in a Word file | ||
|
||
# Parameters | ||
# ---------- | ||
# df : pd.DataFrame | ||
# dataframe to insert. | ||
# caption : str, optional | ||
# caption of the table. The default is None | ||
# highlight : bool, optional | ||
# Very specific for stress assessment. The default is False. | ||
# # template_idx : int, optional | ||
# # index of the template table to use. The default is None | ||
# tablestyle : str, optional | ||
# table word style to apply. The default is None | ||
|
||
# Returns | ||
# ------- | ||
# table : docx.Table | ||
# table inserted. | ||
|
||
# """ | ||
# # Create the table or inherit a template | ||
# template_idx = None # other possibilities not implemented anymore | ||
# if template_idx is None: | ||
# table = self.doc.add_table(1, len(df.columns)) | ||
# else: | ||
# template = self.template_tables[template_idx] | ||
# table = template.insert(self.doc) | ||
|
||
# # Assign style if provided | ||
# if table.style is not None: | ||
# table.style = tablestyle | ||
|
||
# # If template is not provided, the header row must be filled | ||
# if template_idx is None: | ||
# # add the header rows. | ||
# for j in range(df.shape[-1]): | ||
# table.cell(0, j).text = df.columns[j] | ||
|
||
# # Add the rest of the data frame | ||
# # val = df.values | ||
# for i, (idx, row) in enumerate(df.iterrows()): | ||
# # Understand is safety margin is barely acceptable | ||
# flag_almost = False | ||
# try: | ||
# sm = float(row["Safety Margin"]) | ||
# if sm > 1 and sm < 1.1: | ||
# flag_almost = True | ||
# except KeyError: | ||
# pass | ||
# except ValueError: | ||
# pass | ||
# except TypeError: | ||
# # cannot convert to float | ||
# pass | ||
|
||
# row_cells = table.add_row().cells | ||
# for j, item in enumerate(row): | ||
# cell = row_cells[j] | ||
# cell.text = str(item) | ||
# cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER | ||
# for par in cell.paragraphs: | ||
# par.style = "Table" | ||
# if highlight is not None: | ||
# if cell.text == "NOK": | ||
# self._highlightCell(cell) | ||
# elif cell.text == "OK" and flag_almost: | ||
# self._highlightCell(cell, color="FFFF46") | ||
|
||
# if caption is not None: | ||
# paragraph = self.doc.add_paragraph("Table ", style="Didascalia") | ||
# self._wrapper(paragraph, "table") | ||
# paragraph.add_run(" - " + caption) | ||
# # paragraph = doc.add_paragraph('Figure Text', style='Didascalia') | ||
|
||
# return table | ||
|
||
def save(self, outpath, pdfprint=True): | ||
""" | ||
Save word atlas and possibly export PDF | ||
Parameters | ||
---------- | ||
outpath : str/path | ||
path to export the atlas | ||
pdfprint : Boolean, optional | ||
If True export also in PDF format | ||
Returns | ||
------- | ||
None. | ||
""" | ||
outpath_word = os.path.join(outpath, self.outname + ".docx") | ||
# outpath_pdf = os.path.join(outpath, self.outname + ".pdf") | ||
if len(outpath_word) > 259: | ||
logging.warning( | ||
"The path to the word document is too long, the file will be truncated" | ||
) | ||
outpath_word = outpath_word[:254] + ".docx" | ||
|
||
try: | ||
self.doc.save(outpath_word) | ||
except FileNotFoundError as e: | ||
# If there is still an error it may be due to special char | ||
# print the original exception | ||
print(" The following is the original exception:") | ||
print(e) | ||
print( | ||
"\n it may be due to invalid characters in the file name or a path too long" | ||
) | ||
|
||
# Remove PDF printing. If required, word document can be saved manually. | ||
if pdfprint: | ||
pass | ||
|
||
@staticmethod | ||
def _wrapper(paragraph, ptype): | ||
""" | ||
Wrap a paragraph in order to add cross reference | ||
Parameters | ||
---------- | ||
paragraph : docx.Paragraph | ||
image to wrap. | ||
ptype : str | ||
type of paragraph to wrap | ||
Returns | ||
------- | ||
None. | ||
""" | ||
if ptype == "table": | ||
instruction = " SEQ Table \\* ARABIC" | ||
elif ptype == "figure": | ||
instruction = " SEQ Figure \\* ARABIC" | ||
else: | ||
raise ValueError(ptype + " is not a supported paragraph type") | ||
|
||
run = run = paragraph.add_run() | ||
r = run._r | ||
fldChar = OxmlElement("w:fldChar") | ||
fldChar.set(qn("w:fldCharType"), "begin") | ||
r.append(fldChar) | ||
instrText = OxmlElement("w:instrText") | ||
instrText.text = instruction | ||
r.append(instrText) | ||
fldChar = OxmlElement("w:fldChar") | ||
fldChar.set(qn("w:fldCharType"), "end") | ||
r.append(fldChar) | ||
|
||
# @staticmethod | ||
# def _highlightCell(cell, color="FBD4B4"): | ||
# shading_elm_1 = parse_xml( | ||
# r'<w:shd {} w:fill="'.format(nsdecls("w")) + color + r'"/>' | ||
# ) | ||
# cell._tc.get_or_add_tcPr().append(shading_elm_1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
from __future__ import annotations | ||
|
||
import logging | ||
from copy import deepcopy | ||
from pathlib import Path | ||
|
||
from jade.config.atlas_config import ConfigAtlasProcessor | ||
from jade.helper.aux_functions import PathLike, print_code_lib | ||
from jade.helper.constants import CODE | ||
from jade.post.atlas import Atlas | ||
from jade.post.excel_processor import ExcelProcessor | ||
from jade.post.plotter import PlotFactory | ||
|
||
|
||
class AtlasProcessor: | ||
def __init__( | ||
self, | ||
raw_root: PathLike, | ||
atlas_folder_path: PathLike, | ||
cfg: ConfigAtlasProcessor, | ||
codelibs: list[tuple[str, str]], | ||
) -> None: | ||
"""Object responsible to produce the excel comparison results for a given | ||
benchmark. | ||
Parameters | ||
---------- | ||
raw_root : PathLike | ||
path to the raw data folder root | ||
atlas_folder_path : PathLike | ||
path to the atlas folder where the results will be stored | ||
cfg : ConfigAtlasProcessor | ||
configuration options for the excel processor | ||
codelibs : list[tuple[str, str]] | ||
list of code-lib results that should be compared. The first one is | ||
interpreted as the reference data. | ||
""" | ||
self.atlas_folder_path = atlas_folder_path | ||
self.raw_root = raw_root | ||
self.cfg = cfg | ||
self.codelibs = codelibs | ||
|
||
def process(self) -> None: | ||
"""Process the atlas comparison for the given benchmark. It will produce one | ||
atlas file comparing all requested code-lib results in each plot. | ||
""" | ||
# instantiate the atlas | ||
# atlas = Atlas(self.atlas_folder_path, self.cfg.benchmark) | ||
|
||
for plot_cfg in self.cfg.plots: | ||
dfs = [] | ||
# get global df results for each code-lib | ||
for code_tag, lib in self.codelibs: | ||
code = CODE(code_tag) | ||
codelib = print_code_lib(code, lib) | ||
logging.info("Parsing reference data") | ||
raw_folder = Path(self.raw_root, codelib, self.cfg.benchmark) | ||
|
||
df = ExcelProcessor._get_table_df(plot_cfg.results, raw_folder) | ||
if plot_cfg.expand_runs: | ||
run_dfs = [] | ||
df = df.reset_index() | ||
for run in df["Case"].unique(): | ||
run_df = df[df["Case"] == run] | ||
run_dfs.append((run, run_df)) | ||
dfs.append((plot_cfg.name, run_dfs)) | ||
else: | ||
dfs.append((plot_cfg.name, df.reset_index())) | ||
|
||
# create the plot | ||
if plot_cfg.expand_runs: # one plot for each case/run | ||
for case, df in dfs: | ||
cfg = deepcopy(plot_cfg) | ||
cfg.name = f"{cfg.name} {case}" | ||
plot = PlotFactory.create_plot(plot_cfg, df) | ||
else: | ||
plot = PlotFactory.create_plot(plot_cfg, dfs) |
Oops, something went wrong.