From 43d6d19f632eb96f60ea8dd2a72022b2d780c69e Mon Sep 17 00:00:00 2001 From: Laurier Loiselle Date: Wed, 19 Apr 2023 17:39:00 -0400 Subject: [PATCH] cleanup --- src/wireviz/tools/build_examples.py | 19 ++++---- src/wireviz/wv_bom.py | 1 - src/wireviz/wv_cli.py | 40 +++++++++++----- src/wireviz/wv_dataclasses.py | 73 +++++++++++++++++------------ src/wireviz/wv_harness.py | 6 ++- src/wireviz/wv_harness_quantity.py | 26 ++++++---- src/wireviz/wv_output.py | 72 ++++++++++++++-------------- src/wireviz/wv_templates.py | 6 +-- 8 files changed, 142 insertions(+), 101 deletions(-) diff --git a/src/wireviz/tools/build_examples.py b/src/wireviz/tools/build_examples.py index 575f8a4..6662b77 100755 --- a/src/wireviz/tools/build_examples.py +++ b/src/wireviz/tools/build_examples.py @@ -65,15 +65,17 @@ def build_generated(groupkeys): # collect and iterate input YAML files yaml_files = [f for f in collect_filenames("Building", key, input_extensions)] try: - res = cli([ - "--formats", "ghpstb", # no pdf for now - "--prepend", yaml_files[0].parent / "metadata.yml", - *[str(f) for f in yaml_files], - ]) + res = cli( + [ + "--formats", + "ghpstb", # no pdf for now + "--prepend", + yaml_files[0].parent / "metadata.yml", + *[str(f) for f in yaml_files], + ] + ) except BaseException as e: - if str(e) != "0" and not isinstance( - e, (click.ClickException, SystemExit) - ): + if str(e) != "0" and not isinstance(e, (click.ClickException, SystemExit)): raise if build_readme: @@ -111,6 +113,7 @@ def clean_generated(groupkeys): print(f' rm "{filename}"') filename.unlink() + def parse_args(): parser = argparse.ArgumentParser( description=f"{APP_NAME} Example Manager", diff --git a/src/wireviz/wv_bom.py b/src/wireviz/wv_bom.py index 0dd0eb8..20291db 100644 --- a/src/wireviz/wv_bom.py +++ b/src/wireviz/wv_bom.py @@ -22,7 +22,6 @@ def partnumbers2list( if not isinstance(partnumbers, list): partnumbers = [partnumbers] - # if there's no parent, fold if parent_partnumbers is None: return PartNumberInfo.list_keep_only_eq(partnumbers).str_list diff --git a/src/wireviz/wv_cli.py b/src/wireviz/wv_cli.py index 22656d7..aadc4a0 100644 --- a/src/wireviz/wv_cli.py +++ b/src/wireviz/wv_cli.py @@ -14,7 +14,12 @@ import yaml import wireviz.wireviz as wv from wireviz import APP_NAME, __version__ -from wireviz.wv_output import generate_pdf_output, generate_html_output, generate_shared_bom, generate_titlepage +from wireviz.wv_output import ( + generate_pdf_output, + generate_html_output, + generate_shared_bom, + generate_titlepage, +) from wireviz.wv_dataclasses import AUTOGENERATED_PREFIX, Metadata, Options format_codes = { @@ -108,11 +113,20 @@ epilog = ( @click.option( "-m", "--multiplier-file-name", - default='quantity_multipliers.txt', + default="quantity_multipliers.txt", type=str, help="name of file used to fetch the qty_multipliers", ) -def cli(files, formats, prepend, output_dir, output_name, version, use_qty_multipliers, multiplier_file_name): +def cli( + files, + formats, + prepend, + output_dir, + output_name, + version, + use_qty_multipliers, + multiplier_file_name, +): """ Parses the provided FILE and generates the specified outputs. """ @@ -141,8 +155,8 @@ def cli(files, formats, prepend, output_dir, output_name, version, use_qty_multi extra_metadata["sheet_current"] += 1 extra_metadata["sheet_total"] += 1 - if 'pdf' in harness_output_formats: - harness_output_formats.remove('pdf') + if "pdf" in harness_output_formats: + harness_output_formats.remove("pdf") # run WireVIz on each input file for _file in files: @@ -170,7 +184,6 @@ def cli(files, formats, prepend, output_dir, output_name, version, use_qty_multi ) shared_bom = ret["shared_bom"] - shared_bom_base = None if "shared_bom" in output_formats: shared_bom_base, shared_bomlist = generate_shared_bom( @@ -181,24 +194,27 @@ def cli(files, formats, prepend, output_dir, output_name, version, use_qty_multi multiplier_file_name=multiplier_file_name, ) - if ('html' in output_formats) and create_titlepage: + if ("html" in output_formats) and create_titlepage: # TODO: yaml data parsing shared yaml_data_str = "\n".join(f.open("r").read() for f in prepend) yaml_data = yaml.safe_load(yaml_data_str) generate_titlepage(yaml_data, extra_metadata, shared_bom) - if 'pdf' in output_formats: - extra_metadata["titlepage"] = extra_metadata["titlepage"].with_suffix('_for_pdf') + if "pdf" in output_formats: + extra_metadata["titlepage"] = extra_metadata["titlepage"].with_suffix( + "_for_pdf" + ) if create_titlepage: - extra_metadata["output_names"].insert(0, extra_metadata["titlepage"].with_suffix('.html')) + extra_metadata["output_names"].insert( + 0, extra_metadata["titlepage"].with_suffix(".html") + ) generate_titlepage(yaml_data, extra_metadata, shared_bom, for_pdf=True) - if 'pdf' in output_formats: + if "pdf" in output_formats: generate_pdf_output([_output_dir / p for p in extra_metadata["output_names"]]) - print() # blank line after execution diff --git a/src/wireviz/wv_dataclasses.py b/src/wireviz/wv_dataclasses.py index a769e94..ffd36a7 100644 --- a/src/wireviz/wv_dataclasses.py +++ b/src/wireviz/wv_dataclasses.py @@ -157,7 +157,9 @@ class PartNumberInfo: } def __bool__(self): - return bool(self.pn or self.manufacturer or self.mpn or self.supplier or self.spn) + return bool( + self.pn or self.manufacturer or self.mpn or self.supplier or self.spn + ) def __hash__(self): return hash((self.pn, self.manufacturer, self.mpn, self.supplier, self.spn)) @@ -175,7 +177,7 @@ class PartNumberInfo: empty_if_none = lambda x: "" if x is None else str(x) if isinstance(self.pn, list): - raise ValueError(f'pn ({self.pn}) should not be a list') + raise ValueError(f"pn ({self.pn}) should not be a list") self.pn = empty_if_none(self.pn) self.manufacturer = empty_if_none(self.manufacturer) self.mpn = empty_if_none(self.mpn) @@ -222,33 +224,33 @@ class PartNumberInfo: part = self.copy() if other is None: - if op == '==': + if op == "==": return part - elif op == '!=': + elif op == "!=": return None else: - raise NotImplementedError(f'op {op} not supported') + raise NotImplementedError(f"op {op} not supported") if isinstance(other, list): for item in other: part = part.clear_per_field(op, item) else: for k in ["pn", "manufacturer", "mpn", "supplier", "spn"]: - if op == '==': + if op == "==": if part[k] == other[k]: part[k] = "" - elif op == '!=': + elif op == "!=": if part[k] != other[k]: part[k] = "" else: - raise NotImplementedError(f'op {op} not supported') + raise NotImplementedError(f"op {op} not supported") return part def keep_only_eq(self, other): - return self.clear_per_field('!=', other) + return self.clear_per_field("!=", other) def remove_eq(self, other): - return self.clear_per_field('==', other) + return self.clear_per_field("==", other) @staticmethod def list_keep_only_eq(partnumbers): @@ -257,6 +259,7 @@ class PartNumberInfo: pn = pn.keep_only_eq(p) return pn + @dataclass class BomEntry: qty: NumberAndUnit @@ -277,7 +280,6 @@ class BomEntry: MAX_PRINTED_DESIGNATORS: int = 2 restrict_printed_lengths: bool = True - scaled_per_harness = False # Map a bom key to the header @@ -291,7 +293,7 @@ class BomEntry: } def __repr__(self): - return f'{id}: {self.partnumbers}, {self.qty}' + return f"{id}: {self.partnumbers}, {self.qty}" def __hash__(self): return hash((self.partnumbers, self.description)) @@ -323,9 +325,7 @@ class BomEntry: setattr(self, key, value) def __post_init__(self): - assert isinstance( - self.qty, NumberAndUnit - ), f"Unexpected qty type {self.qty}" + assert isinstance(self.qty, NumberAndUnit), f"Unexpected qty type {self.qty}" assert isinstance( self.partnumbers, PartNumberInfo ), f"Unexpected partnumbers type {self.partnumbers}" @@ -342,11 +342,13 @@ class BomEntry: if not isinstance(self.qty_multiplier, str): self.qty *= float(self.qty_multiplier) - @property def description_str(self): description = self.description - if self.restrict_printed_lengths and len(description) > self.MAX_PRINTED_DESCRIPTION: + if ( + self.restrict_printed_lengths + and len(description) > self.MAX_PRINTED_DESCRIPTION + ): description = f"{description[:self.MAX_PRINTED_DESCRIPTION]} (...)" return description @@ -356,7 +358,10 @@ class BomEntry: return "" all_designators = sorted(self.designators) - if self.restrict_printed_lengths and len(all_designators) > self.MAX_PRINTED_DESIGNATORS: + if ( + self.restrict_printed_lengths + and len(all_designators) > self.MAX_PRINTED_DESIGNATORS + ): all_designators = all_designators[: self.MAX_PRINTED_DESIGNATORS] + ["..."] return ", ".join(all_designators) @@ -403,18 +408,22 @@ class BomEntry: def scale_per_harness(self, qty_multipliers): if self.scaled_per_harness: - logging.warn('{self}: Already scaled') + logging.warn("{self}: Already scaled") qty = NumberAndUnit(0, self.qty.unit_str) for name, info in self.per_harness.items(): multiplier_name = [k for k in qty_multipliers.keys() if name.endswith(k)] if len(multiplier_name) == 0: - raise ValueError(f'No multiplier found for harness {name} in {qty_multipliers}') + raise ValueError( + f"No multiplier found for harness {name} in {qty_multipliers}" + ) if len(multiplier_name) > 1: - raise ValueError(f'Conflicting multipliers found ({multiplier_name}) for harness {name} in {qty_multipliers}') + raise ValueError( + f"Conflicting multipliers found ({multiplier_name}) for harness {name} in {qty_multipliers}" + ) - info['qty'] *= qty_multipliers[multiplier_name[0]] - qty += info['qty'] + info["qty"] *= qty_multipliers[multiplier_name[0]] + qty += info["qty"] self.qty = qty self.scaled_per_harness = True @@ -563,7 +572,7 @@ class Component: self.qty = NumberAndUnit.to_number_and_unit(self.qty) self.amount = NumberAndUnit.to_number_and_unit(self.amount) if isinstance(self.pn, list): - raise RuntimeError(f'PN ({self.pn}) should not be a list') + raise RuntimeError(f"PN ({self.pn}) should not be a list") for i, item in enumerate(self.additional_components): if isinstance(item, Component): @@ -766,15 +775,21 @@ class Connector(GraphicalComponent): } for subitem in self.additional_components: if isinstance(subitem.qty_multiplier, str): - computed_factor = qty_multipliers_computed[subitem.qty_multiplier.upper()] - #if isinstance(subitem.qty_multiplier, QtyMultiplierConnector): + computed_factor = qty_multipliers_computed[ + subitem.qty_multiplier.upper() + ] + # if isinstance(subitem.qty_multiplier, QtyMultiplierConnector): # computed_factor = qty_multipliers_computed[subitem.qty_multiplier.name.upper()] - #elif isinstance(subitem.qty_multiplier, QtyMultiplierCable): + # elif isinstance(subitem.qty_multiplier, QtyMultiplierCable): # raise Exception("Used a cable multiplier in a connector!") - elif isinstance(subitem.qty_multiplier, int) or isinstance(subitem.qty_multiplier, float): + elif isinstance(subitem.qty_multiplier, int) or isinstance( + subitem.qty_multiplier, float + ): computed_factor = subitem.qty_multiplier else: - raise ValueError(f'Unexpected qty multiplier "{subitem.qty_multiplier}"') + raise ValueError( + f'Unexpected qty multiplier "{subitem.qty_multiplier}"' + ) subitem._qty_multiplier_computed = computed_factor diff --git a/src/wireviz/wv_harness.py b/src/wireviz/wv_harness.py index 61071fa..f48fb54 100644 --- a/src/wireviz/wv_harness.py +++ b/src/wireviz/wv_harness.py @@ -30,7 +30,11 @@ from wireviz.wv_graphviz import ( parse_arrow_str, set_dot_basics, ) -from wireviz.wv_output import embed_svg_images_file, generate_html_output, generate_pdf_output +from wireviz.wv_output import ( + embed_svg_images_file, + generate_html_output, + generate_pdf_output, +) from wireviz.wv_utils import bom2tsv diff --git a/src/wireviz/wv_harness_quantity.py b/src/wireviz/wv_harness_quantity.py index 98c326f..40a6651 100644 --- a/src/wireviz/wv_harness_quantity.py +++ b/src/wireviz/wv_harness_quantity.py @@ -4,9 +4,11 @@ import click import json import logging -class HarnessQuantity(): - def __init__(self, harnesses, multiplier_file="quantity_multipliers.txt", output_dir=None): +class HarnessQuantity: + def __init__( + self, harnesses, multiplier_file="quantity_multipliers.txt", output_dir=None + ): self.harness_names = [harness.stem for harness in harnesses] self.multipliers = {} self.folder = output_dir if output_dir is not None else harnesses[0].parent @@ -17,11 +19,13 @@ class HarnessQuantity(): def fetch_qty_multipliers_from_file(self): if self.qty_multipliers.is_file(): - with open(self.qty_multipliers, 'r') as f: + with open(self.qty_multipliers, "r") as f: try: self.multipliers = json.load(f) except json.decoder.JSONDecodeError as err: - raise ValueError(f'Invalid format for file {self.qty_multipliers}, error: {err}') + raise ValueError( + f"Invalid format for file {self.qty_multipliers}, error: {err}" + ) else: self.get_qty_multipliers_from_user() self.save_qty_multipliers_to_file() @@ -29,19 +33,22 @@ class HarnessQuantity(): def check_all_multipliers_defined(self): for name in self.harness_names: - assert name in self.multipliers, \ - f"No multiplier defined for harness {name}, maybe delete the multiplier_file {self.qty_multipliers}" + assert ( + name in self.multipliers + ), f"No multiplier defined for harness {name}, maybe delete the multiplier_file {self.qty_multipliers}" def get_qty_multipliers_from_user(self): for name in self.harness_names: try: - self.multipliers[name] = int(input("Quantity multiplier for {}? ".format(name))) + self.multipliers[name] = int( + input("Quantity multiplier for {}? ".format(name)) + ) except ValueError: logging.warning("Quantity multiplier must be an integer!") break def save_qty_multipliers_to_file(self): - with open(self.qty_multipliers,"w") as f: + with open(self.qty_multipliers, "w") as f: json.dump(self.multipliers, f) def retrieve_harness_qty_multiplier(self, bom_file): @@ -63,7 +70,7 @@ class HarnessQuantity(): @click.option( "-m", "--multiplier-file-name", - default='quantity_multipliers.txt', + default="quantity_multipliers.txt", type=str, help="name of file used to fetch or save the qty_multipliers", ) @@ -82,4 +89,3 @@ def qty_multipliers(files, multiplier_file_name, force_new): harnesses.fetch_qty_multipliers_from_file() qty_multipliers = harnesses.multipliers return - diff --git a/src/wireviz/wv_output.py b/src/wireviz/wv_output.py index d40d789..9ad21d0 100644 --- a/src/wireviz/wv_output.py +++ b/src/wireviz/wv_output.py @@ -64,26 +64,27 @@ def embed_svg_images_file( def generate_pdf_output( filename_list: List[Path], - options: Dict=None, + options: Dict = None, ): - '''Generate a pdf output, options are ignored for now, expect the formatting - to be done within the html files - ''' + """Generate a pdf output, options are ignored for now, expect the formatting + to be done within the html files + """ if isinstance(filename_list, Path): filename_list = [filename_list] - output_path = filename_list[0].with_suffix('.pdf') + output_path = filename_list[0].with_suffix(".pdf") else: output_dir = filename_list[0].parent - output_path = (output_dir / output_dir.name).with_suffix('.pdf') + output_path = (output_dir / output_dir.name).with_suffix(".pdf") - filepath_list = [f.with_suffix('.html') for f in filename_list] + filepath_list = [f.with_suffix(".html") for f in filename_list] - print(f'Generating pdf output: {output_path}') + print(f"Generating pdf output: {output_path}") files_html = [HTML(path) for path in filepath_list] documents = [f.render() for f in files_html] all_pages = [p for doc in documents for p in doc.pages] documents[0].copy(all_pages).write_pdf(output_path) + def generate_shared_bom( output_dir, shared_bom, @@ -93,13 +94,13 @@ def generate_shared_bom( ): shared_bom_base = output_dir / "shared_bom" shared_bom_file = shared_bom_base.with_suffix(".tsv") - print(f'Generating shared bom at {shared_bom_base}') + print(f"Generating shared bom at {shared_bom_base}") if use_qty_multipliers: harnesses = HarnessQuantity(files, multiplier_file_name, output_dir=output_dir) harnesses.fetch_qty_multipliers_from_file() qty_multipliers = harnesses.multipliers - print(f'Using quantity multipliers: {qty_multipliers}') + print(f"Using quantity multipliers: {qty_multipliers}") for bom_item in shared_bom.values(): bom_item.scale_per_harness(qty_multipliers) @@ -121,7 +122,7 @@ def generate_html_output( template_name = metadata.get("template", {}).get("name", "simple") svgdata = None - if template_name != 'titlepage': + if template_name != "titlepage": # embed SVG diagram for all but the titlepage with filename.with_suffix(".svg").open("r") as f: svgdata = re.sub( @@ -133,7 +134,9 @@ def generate_html_output( # generate BOM table # generate BOM header (may be at the top or bottom of the table) - bom_reversed = False if template_name == "simple" or template_name == "titlepage" else True + bom_reversed = ( + False if template_name == "simple" or template_name == "titlepage" else True + ) bom_header = bom[0] bom_columns = [ "bom_col_{}".format("id" if c == "#" else c.lower()) for c in bom_header @@ -142,7 +145,6 @@ def generate_html_output( if bom_reversed: bom_content.reverse() - if metadata: sheet_current = metadata["sheet_current"] sheet_total = metadata["sheet_total"] @@ -217,39 +219,37 @@ def generate_html_output( # save generated file filename.with_suffix(".html").open("w").write(page_rendered) + def generate_titlepage(yaml_data, extra_metadata, shared_bom, for_pdf=False): - print('Generating titlepage') + print("Generating titlepage") index_table_content = [] - index_table_content.append(( - 1, extra_metadata['titlepage'], '' - )) - - for index, page_name in enumerate(extra_metadata['output_names']): - index_table_content.append(( - index+2, page_name, '' - )) + index_table_content.append((1, extra_metadata["titlepage"], "")) + for index, page_name in enumerate(extra_metadata["output_names"]): + index_table_content.append((index + 2, page_name, "")) if not for_pdf: - index_table_content = [( - p[0], - f"{p[1]}", - p[2], - ) for p in index_table_content] + index_table_content = [ + ( + p[0], + f"{p[1]}", + p[2], + ) + for p in index_table_content + ] - - #if create_titlepage: + # if create_titlepage: # extra_metadata["index_table_content"].append([ # sheet_current, # f"{extra_metadata['sheet_name']}", # "", # ]) - #index_table_content.insert(0, [ + # index_table_content.insert(0, [ # 1, # f"Title Page", # '' - #]) + # ]) titlepage_metadata = { **yaml_data.get("metadata", {}), @@ -262,7 +262,7 @@ def generate_titlepage(yaml_data, extra_metadata, shared_bom, for_pdf=False): "bom_updated_position": "top: 20mm; left: 10mm", "notes_width": "200mm", } - titlepage_metadata['template']['name'] = 'titlepage' + titlepage_metadata["template"]["name"] = "titlepage" titlepage_options = { "show_bom": True, "show_index_table": True, @@ -270,8 +270,8 @@ def generate_titlepage(yaml_data, extra_metadata, shared_bom, for_pdf=False): **yaml_data.get("options", {}), } generate_html_output( - extra_metadata['output_dir']/ extra_metadata['titlepage'], - bom = bom_list(shared_bom), - metadata = Metadata(**titlepage_metadata), # TBD what we need to add here - options = Options(**titlepage_options), + extra_metadata["output_dir"] / extra_metadata["titlepage"], + bom=bom_list(shared_bom), + metadata=Metadata(**titlepage_metadata), # TBD what we need to add here + options=Options(**titlepage_options), ) diff --git a/src/wireviz/wv_templates.py b/src/wireviz/wv_templates.py index e48e6ce..766fcb0 100644 --- a/src/wireviz/wv_templates.py +++ b/src/wireviz/wv_templates.py @@ -1,11 +1,9 @@ from pathlib import Path import jinja2 + def get_template(template_name, extension=""): - template_file_path = jinja2.FileSystemLoader( - Path(__file__).parent / "templates" - ) + template_file_path = jinja2.FileSystemLoader(Path(__file__).parent / "templates") jinja_env = jinja2.Environment(loader=template_file_path) return jinja_env.get_template(template_name + extension) -