From 641b44abf768a15241977c7b8c6b2c6c905b91f1 Mon Sep 17 00:00:00 2001 From: Laurier Loiselle Date: Mon, 23 Jan 2023 15:47:45 -0500 Subject: [PATCH] jinja2: use jinja2 for html template --- requirements.txt | 1 + setup.py | 1 + src/wireviz/templates/din-6771.html | 100 +++----------------------- src/wireviz/templates/simple.html | 14 ++-- src/wireviz/templates/titleblock.html | 78 ++++++++++++++++++++ src/wireviz/tools/build_examples.py | 21 ++++-- src/wireviz/wv_bom.py | 7 +- src/wireviz/wv_cli.py | 9 ++- src/wireviz/wv_dataclasses.py | 97 +++++++++++++------------ src/wireviz/wv_output.py | 85 ++++++++++------------ 10 files changed, 209 insertions(+), 204 deletions(-) create mode 100644 src/wireviz/templates/titleblock.html diff --git a/requirements.txt b/requirements.txt index 9405dd1..8e752be 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ pillow pyyaml setuptools tabulate +jinja2 diff --git a/setup.py b/setup.py index 4d0320a..6bf765c 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ setup( "pillow", "pyyaml", "tabulate", + "jinja2", ], license="GPLv3", keywords="cable connector hardware harness wiring wiring-diagram wiring-harness", diff --git a/src/wireviz/templates/din-6771.html b/src/wireviz/templates/din-6771.html index 1c5fc10..2859471 100644 --- a/src/wireviz/templates/din-6771.html +++ b/src/wireviz/templates/din-6771.html @@ -3,12 +3,12 @@ - - <!-- %title% --> + + {{ title }} - -

+ +

{{ title }}

Diagram

- + {{ description }}
- + {{ diagram }}
- + {{ notes }}

Bill of Materials

- + {{ bom }}
diff --git a/src/wireviz/templates/titleblock.html b/src/wireviz/templates/titleblock.html new file mode 100644 index 0000000..936e27d --- /dev/null +++ b/src/wireviz/templates/titleblock.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ revisions_8 }} {{ revisions_8_changelog }} {{ revisions_8_date }} {{ revisions_8_name }}DateName {{ title }}
{{ revisions_7 }} {{ revisions_7_changelog }} {{ revisions_7_date }} {{ revisions_7_name }} {{ authors_1 }} {{ authors_1_date }} {{ authors_1_name }}
{{ revisions_6 }} {{ revisions_6_changelog }} {{ revisions_6_date }} {{ revisions_6_name }} {{ authors_2 }} {{ authors_2_date }} {{ authors_2_name }}
{{ revisions_5 }} {{ revisions_5_changelog }} {{ revisions_5_date }} {{ revisions_5_name }} {{ authors_3 }} {{ authors_3_date }} {{ authors_3_name }}
{{ revisions_4 }} {{ revisions_4_changelog }} {{ revisions_4_date }} {{ revisions_4_name }}
{{ revisions_3 }} {{ revisions_3_changelog }} {{ revisions_3_date }} {{ revisions_3_name }} {{ company }} {{ pn }}Sheet
{{ sheet_current }}
{{ revisions_2 }} {{ revisions_2_changelog }} {{ revisions_2_date }} {{ revisions_2_name }}
{{ revisions_1 }} {{ revisions_1_changelog }} {{ revisions_1_date }} {{ revisions_1_name }}of {{ sheet_total }}
RevChangelogDateName
diff --git a/src/wireviz/tools/build_examples.py b/src/wireviz/tools/build_examples.py index fe9ba4a..fa651cd 100755 --- a/src/wireviz/tools/build_examples.py +++ b/src/wireviz/tools/build_examples.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- import argparse +from click.testing import CliRunner import os import sys from pathlib import Path @@ -9,26 +10,27 @@ from pathlib import Path script_path = Path(__file__).absolute() sys.path.insert(0, str(script_path.parent.parent.parent)) # to find wireviz module -from wireviz import APP_NAME, __version__, wireviz +from wireviz import APP_NAME, __version__ +from wireviz.wv_cli import cli from wireviz.wv_utils import open_file_append, open_file_read, open_file_write -dir = script_path.parent.parent.parent.parent +base_dir = script_path.parent.parent.parent.parent readme = "readme.md" groups = { "examples": { - "path": dir / "examples", + "path": base_dir / "examples", "prefix": "ex", readme: [], # Include no files "title": "Example Gallery", }, "tutorial": { - "path": dir / "tutorial", + "path": base_dir / "tutorial", "prefix": "tutorial", readme: ["md", "yml"], # Include .md and .yml files "title": f"{APP_NAME} Tutorial", }, "demos": { - "path": dir / "examples", + "path": base_dir / "examples", "prefix": "demo", }, } @@ -51,6 +53,7 @@ def collect_filenames(description, groupkey, ext_list): def build_generated(groupkeys): + runner = CliRunner() for key in groupkeys: # preparation path = groups[key]["path"] @@ -62,9 +65,13 @@ def build_generated(groupkeys): out.write(f'# {groups[key]["title"]}\n\n') # collect and iterate input YAML files for yaml_file in collect_filenames("Building", key, input_extensions): - print(f' "{yaml_file}"') - wireviz.parse(yaml_file, output_formats=("gv", "html", "png", "svg", "tsv")) + res = runner.invoke(cli, args=[ + '--format', 'ghpst', + str(yaml_file) + ]) + if res.exit_code != 0: + raise RuntimeError(f'Cli failed for {yaml_file} with result: {res}') from res.exception if build_readme: i = "".join(filter(str.isdigit, yaml_file.stem)) diff --git a/src/wireviz/wv_bom.py b/src/wireviz/wv_bom.py index 03f0508..165038f 100644 --- a/src/wireviz/wv_bom.py +++ b/src/wireviz/wv_bom.py @@ -111,8 +111,7 @@ def bom_list(bom): all_designators = sorted(entry["designators"]) if len(all_designators) > MAX_DESIGNATORS: - all_designators = all_designators[:MAX_DESIGNATORS] + ['...'] - + all_designators = all_designators[:MAX_DESIGNATORS] + ["..."] cells = [ entry["id"], @@ -129,8 +128,8 @@ def bom_list(bom): hash.partnumbers.pn, hash.partnumbers.manufacturer, hash.partnumbers.mpn, - None, #hash.partnumbers.supplier, - None, #hash.partnumbers.spn, + None, # hash.partnumbers.supplier, + None, # hash.partnumbers.spn, ] ) else: diff --git a/src/wireviz/wv_cli.py b/src/wireviz/wv_cli.py index 5d54e68..23255bc 100644 --- a/src/wireviz/wv_cli.py +++ b/src/wireviz/wv_cli.py @@ -129,11 +129,10 @@ def cli(file, format, prepend, output_dir, output_name, version): raise Exception(f"Path is not a file:\n{file}") extra_metadata = {} - extra_metadata['name'] = file.stem - extra_metadata['sheet_total'] = len(filepaths) - extra_metadata['sheet_current'] = sheet_current - sheet_current +=1 - + extra_metadata["name"] = file.stem + extra_metadata["sheet_total"] = len(filepaths) + extra_metadata["sheet_current"] = sheet_current + sheet_current += 1 # file_out = file.with_suffix("") if not output_file else output_file _output_dir = file.parent if not output_dir else output_dir diff --git a/src/wireviz/wv_dataclasses.py b/src/wireviz/wv_dataclasses.py index db3a43b..bd2975c 100644 --- a/src/wireviz/wv_dataclasses.py +++ b/src/wireviz/wv_dataclasses.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- +import logging from collections import namedtuple from dataclasses import dataclass, field from enum import Enum from itertools import zip_longest -import logging from typing import Any, Dict, List, Optional, Tuple, Union from wireviz.wv_bom import ( @@ -576,7 +576,9 @@ class Cable(TopLevelGraphicalComponent): def gauge_str(self): if not self.gauge: return None - number = int(self.gauge.number) if self.gauge.unit == 'AWG' else self.gauge.number + number = ( + int(self.gauge.number) if self.gauge.unit == "AWG" else self.gauge.number + ) actual_gauge = f"{number} {self.gauge.unit}" actual_gauge = actual_gauge.replace("mm2", "mm\u00B2") return actual_gauge @@ -631,59 +633,60 @@ class Cable(TopLevelGraphicalComponent): return desc belden_color = { - 'BN': '001', - 'RD': '002', - 'OG': '003', - 'YE': '004', - 'GN': '005', - 'TQ': '006', # (For Belden: light blue. For WireViz: turquoise) - 'VT': '007', - 'GY': '008', - 'WH': '009', - 'BK': '010', - 'BG': '011', - 'PK': '012', - 'BU': '013', - 'BKRD':'015', # (for Belden: white/red) - 'BKGN':'016', # (for Belden: white/green) - 'BKYE':'017', # (for Belden: white/yellow) - 'BKBU':'018', # (for Belden: white/blue) - 'BKBN':'019', # (for Belden: white/brown) - 'BKOG':'020', # (for Belden: white/orange) - 'BKGY':'021', # (for Belden: white/gray) - 'BKVT':'022', # (for Belden: white/purple) + "BN": "001", + "RD": "002", + "OG": "003", + "YE": "004", + "GN": "005", + "TQ": "006", # (For Belden: light blue. For WireViz: turquoise) + "VT": "007", + "GY": "008", + "WH": "009", + "BK": "010", + "BG": "011", + "PK": "012", + "BU": "013", + "BKRD": "015", # (for Belden: white/red) + "BKGN": "016", # (for Belden: white/green) + "BKYE": "017", # (for Belden: white/yellow) + "BKBU": "018", # (for Belden: white/blue) + "BKBN": "019", # (for Belden: white/brown) + "BKOG": "020", # (for Belden: white/orange) + "BKGY": "021", # (for Belden: white/gray) + "BKVT": "022", # (for Belden: white/purple) # (1) Why use BKRD instead of WHRD, since Belden only sells white/red? # - WHRD is impractical to use in Wireviz (white wire sides on white background does not help with identification) # - BKRD is clearly distinguishable, and comes handy to use in Wireviz, as the representation of a GND rail associated (even twisted, if applicable) with a specific RD signal wire. # (2) For all wire colors see: # https://www.belden.com/dfsmedia/f1e38517e0cd4caa8b1acb6619890f5e/7806-source/options/view/cabling-solutions-for-industrial-applications-catalog-belden-09-2020#page=153 - } belden_tfe_base_mpn = { # Leftmost in list is the prefered MPN # NOTE (lal 2022-12-20): this prefered MPN is arbitrary ATM - '16 AWG': ['83030', '83010'], - '18 AWG': ['83029', '83009'], - '20 AWG': ['83028', '83027', '83007', '83008'], - '22 AWG': ['83025', '83026', '83005', '83006', '83049', '83050'], - '24 AWG': ['83023', '83003', '83004', '83047', '83048'], - '26 AWG': ['83002', '83046'], - '28 AWG': ['83001', '83045'], - '30 AWG': ['83000', '83043'], - '32 AWG': ['83041'], + "16 AWG": ["83030", "83010"], + "18 AWG": ["83029", "83009"], + "20 AWG": ["83028", "83027", "83007", "83008"], + "22 AWG": ["83025", "83026", "83005", "83006", "83049", "83050"], + "24 AWG": ["83023", "83003", "83004", "83047", "83048"], + "26 AWG": ["83002", "83046"], + "28 AWG": ["83001", "83045"], + "30 AWG": ["83000", "83043"], + "32 AWG": ["83041"], # see: https://www.belden.com/dfsmedia/f1e38517e0cd4caa8b1acb6619890f5e/7806-source/options/view/cabling-solutions-for-industrial-applications-catalog-belden-09-2020#page=136 } @property def is_belden(self): - if 'belden' in self.manufacturer.lower(): + if "belden" in self.manufacturer.lower(): return True return False def get_belden_color(self, color): if color not in self.belden_color: - logging.warn(f'No color found in belden colors {list(self.belden_color.keys())} matching {self.color}, defaulting to BK') - return self.belden_color['BK'] + logging.warn( + f"No color found in belden colors {list(self.belden_color.keys())} matching {self.color}, defaulting to BK" + ) + return self.belden_color["BK"] return self.belden_color[color] def gen_belden_cable_with_alternate(self, color): @@ -691,16 +694,18 @@ class Cable(TopLevelGraphicalComponent): try: parts = self.belden_tfe_base_mpn[self.gauge_str] except KeyError: - raise ValueError(f'Couldn\'t find a belden TFE wire for wire of {self.gauge_str}') + raise ValueError( + f"Couldn't find a belden TFE wire for wire of {self.gauge_str}" + ) color = self.get_belden_color(color) if not color: - raise ValueError(f'Failed to find a color for property: {self.description}') + raise ValueError(f"Failed to find a color for property: {self.description}") # Create the list of mpn roll_length = 100 - mpn_list = [f'{mpn} {color}{roll_length}' for mpn in parts] + mpn_list = [f"{mpn} {color}{roll_length}" for mpn in parts] main_part = mpn_list[0] alternates = mpn_list[1:] if len(mpn_list) > 1 else [] @@ -712,11 +717,15 @@ class Cable(TopLevelGraphicalComponent): main_part, alternates = self.gen_belden_cable_with_alternate(color) return main_part if alternates: - logging.info(f'Alternate part{"s" if len(alternates) > 1 else ""} available for {self.gauge_str}, color {self.color}: {alternates}') + logging.info( + f'Alternate part{"s" if len(alternates) > 1 else ""} available for {self.gauge_str}, color {self.color}: {alternates}' + ) else: - logging.info(f'Not updating part for manufacturer {self.manufacturer}, only "belden" supported') + logging.info( + f'Not updating part for manufacturer {self.manufacturer}, only "belden" supported' + ) else: - logging.info(f'Not updating part, no manufacturer provided') + logging.info(f"Not updating part, no manufacturer provided") return mpn def _get_wire_partnumber(self, idx, color) -> PartNumberInfo: @@ -725,8 +734,8 @@ class Cable(TopLevelGraphicalComponent): # TODO: possibly make more robust/elegant if self.category == "bundle": - manufacturer = _get_correct_element(self.partnumbers.manufacturer, idx), - mpn = _get_correct_element(self.partnumbers.mpn, idx), + manufacturer = (_get_correct_element(self.partnumbers.manufacturer, idx),) + mpn = (_get_correct_element(self.partnumbers.mpn, idx),) if color is not None: mpn = self.get_mpn_if_belden(manufacturer, mpn, color.code_en) diff --git a/src/wireviz/wv_output.py b/src/wireviz/wv_output.py index 40429eb..1fd1423 100644 --- a/src/wireviz/wv_output.py +++ b/src/wireviz/wv_output.py @@ -5,15 +5,12 @@ import re from pathlib import Path from typing import Dict, List, Union +import jinja2 + import wireviz # for doing wireviz.__file__ from wireviz import APP_NAME, APP_URL, __version__ from wireviz.wv_dataclasses import Metadata, Options -from wireviz.wv_utils import ( - html_line_breaks, - open_file_read, - open_file_write, - smart_file_resolve, -) +from wireviz.wv_utils import html_line_breaks, open_file_read, open_file_write mime_subtype_replacements = {"jpg": "jpeg", "tif": "tiff"} @@ -62,27 +59,24 @@ def embed_svg_images_file( filename_out.replace(filename_in) +def get_template_html(template_name): + template_file_path = jinja2.FileSystemLoader( + Path(wireviz.__file__).parent / "templates" + ) + jinja_env = jinja2.Environment(loader=template_file_path) + + return jinja_env.get_template(template_name + ".html") + + def generate_html_output( filename: Union[str, Path], bom: List[List[str]], metadata: Metadata, options: Options, ): - - # load HTML template - templatename = metadata.get("template", {}).get("name") - if templatename: - # if relative path to template was provided, - # check directory of YAML file first, fall back to built-in template directory - templatefile = smart_file_resolve( - f"{templatename}.html", - [Path(filename).parent, Path(__file__).parent / "templates"], - ) - else: - # fall back to built-in simple template if no template was provided - templatefile = Path(wireviz.__file__).parent / "templates/simple.html" - - html = open_file_read(templatefile).read() + print("Generating html output") + template_name = metadata.get("template", {}).get("name", "simple") + page_template = get_template_html(template_name) # embed SVG diagram with open_file_read(f"{filename}.tmp.svg") as file: @@ -122,51 +116,46 @@ def generate_html_output( ) if metadata: - sheet_current = metadata['sheet_current'] - sheet_total = metadata['sheet_total'] + sheet_current = metadata["sheet_current"] + sheet_total = metadata["sheet_total"] else: sheet_current = 1 sheet_total = 1 - # prepare simple replacements replacements = { - "": f"{APP_NAME} {__version__} - {APP_URL}", - "": options.fontname, - "": options.bgcolor.html, - "": svgdata, - "": bom_html, - "": bom_html_reversed, - "": sheet_current, - "": sheet_total, + "title": "pizza", + "generator": f"{APP_NAME} {__version__} - {APP_URL}", + "fontname": options.fontname, + "bgcolor": options.bgcolor.html, + "diagram": svgdata, + "bom": bom_html, + "bom_reversed": bom_html_reversed, + "sheet_current": sheet_current, + "sheet_total": sheet_total, } # prepare metadata replacements if metadata: for item, contents in metadata.items(): if isinstance(contents, (str, int, float)): - replacements[f""] = html_line_breaks(str(contents)) + replacements[str(item)] = html_line_breaks(str(contents)) elif isinstance(contents, Dict): # useful for authors, revisions for index, (category, entry) in enumerate(contents.items()): if isinstance(entry, Dict): - replacements[f""] = str(category) + replacements[f"{item}_{index+1}"] = str(category) for entry_key, entry_value in entry.items(): replacements[ - f"" + f"{item}_{index+1}_{entry_key}" ] = html_line_breaks(str(entry_value)) - replacements['"sheetsize_default"'] = '"{}"'.format( - metadata.get("template", {}).get("sheetsize", "") - ) + replacements[ + "sheetsize_default" + ] = f'{metadata.get("template", {}).get("sheetsize", "sheetsize_default")}' # include quotes so no replacement happens within