From 238bc2585e0c0f1c03065d76b5122b01a11a798a Mon Sep 17 00:00:00 2001 From: Daniel Rojas Date: Fri, 5 Aug 2022 16:52:24 +0200 Subject: [PATCH] Implement bundle part number rendering --- src/wireviz/wv_bom.py | 42 +++++++++++++++++++---- src/wireviz/wv_dataclasses.py | 37 +++++++++----------- src/wireviz/wv_graphviz.py | 63 +++++++++-------------------------- 3 files changed, 67 insertions(+), 75 deletions(-) diff --git a/src/wireviz/wv_bom.py b/src/wireviz/wv_bom.py index cb18dff..01fbac3 100644 --- a/src/wireviz/wv_bom.py +++ b/src/wireviz/wv_bom.py @@ -42,14 +42,42 @@ PART_NUMBER_HEADERS = PartNumberInfo( ) -def partnumbers_to_list(partnumbers: PartNumberInfo) -> List[str]: - cell_contents = [ - pn_info_string(PART_NUMBER_HEADERS.pn, None, partnumbers.pn), - pn_info_string( +def partnumbers2list( + partnumbers: PartNumberInfo, parent_partnumbers: PartNumberInfo = None +) -> List[str]: + if parent_partnumbers is None: + _is_toplevel = True + parent_partnumbers = partnumbers + else: + _is_toplevel = False + + # Note: != operator used as XOR in the following section (https://stackoverflow.com/a/433161) + + if _is_toplevel != isinstance(parent_partnumbers.pn, List): + # top level and not a list, or wire level and list + cell_pn = pn_info_string(PART_NUMBER_HEADERS.pn, None, partnumbers.pn) + else: + # top level and list -> do per wire later + # wire level and not list -> already done at top level + cell_pn = None + + if _is_toplevel != isinstance(parent_partnumbers.mpn, List): + # TODO: edge case: different manufacturers, but same MPN? + cell_mpn = pn_info_string( PART_NUMBER_HEADERS.mpn, partnumbers.manufacturer, partnumbers.mpn - ), - pn_info_string(PART_NUMBER_HEADERS.spn, partnumbers.supplier, partnumbers.spn), - ] + ) + else: + cell_mpn = None + + if _is_toplevel != isinstance(parent_partnumbers.spn, List): + # TODO: edge case: different suppliers, but same SPN? + cell_spn = pn_info_string( + PART_NUMBER_HEADERS.spn, partnumbers.supplier, partnumbers.spn + ) + else: + cell_spn = None + + cell_contents = [cell_pn, cell_mpn, cell_spn] if any(cell_contents): return [html_line_breaks(cell) for cell in cell_contents] else: diff --git a/src/wireviz/wv_dataclasses.py b/src/wireviz/wv_dataclasses.py index d555845..ba529dd 100644 --- a/src/wireviz/wv_dataclasses.py +++ b/src/wireviz/wv_dataclasses.py @@ -632,18 +632,20 @@ class Cable(TopLevelGraphicalComponent): return desc def _get_wire_partnumber(self, idx) -> PartNumberInfo: + def _get_correct_element(inp, idx): + return inp[idx] if isinstance(inp, List) else inp + # TODO: possibly make more robust/elegant if self.category == "bundle": - if isinstance(self.partnumbers.pn, List): - return PartNumberInfo( - self.partnumbers.pn[idx], - self.partnumbers.manufacturer[idx], - self.partnumbers.mpn[idx], - self.partnumbers.supplier[idx], - self.partnumbers.spn[idx], - ) - else: - return None + return PartNumberInfo( + _get_correct_element(self.partnumbers.pn, idx), + _get_correct_element(self.partnumbers.manufacturer, idx), + _get_correct_element(self.partnumbers.mpn, idx), + _get_correct_element(self.partnumbers.supplier, idx), + _get_correct_element(self.partnumbers.spn, idx), + ) + else: + return None # non-bundles do not support lists of part data def __post_init__(self) -> None: @@ -656,6 +658,10 @@ class Cable(TopLevelGraphicalComponent): if isinstance(self.image, dict): self.image = Image(**self.image) + # TODO: + # allow gauge, length, and other fields to be lists too (like part numbers), + # and assign them the same way to bundles. + self.gauge = self.parse_number_and_unit(self.gauge, "mm2") self.length = self.parse_number_and_unit(self.length, "m") self.amount = self.length # for BOM @@ -692,17 +698,6 @@ class Cable(TopLevelGraphicalComponent): # if lists of part numbers are provided, # check this is a bundle and that it matches the wirecount. - - # TODO: - # if it is a bundle, each wire will show up as separate BOM entry. - # therefore, for any bundle, pn info should be inherited into the wire objects. - # if a field is a list, then assign each wire its corresponding entry from that list, by index. - # if a field is not a list, assign all wires in the cable the same field value, assumed to be valid for all wires. - # then, checking whether bundle or not is the only check required to decide how to handle bom info. - - # TODO: - # allow gauge, length, and other fields to be lists too, and assign them the same way to bundles. - for idfield in [self.manufacturer, self.mpn, self.supplier, self.spn, self.pn]: if isinstance(idfield, list): if self.category == "bundle": diff --git a/src/wireviz/wv_graphviz.py b/src/wireviz/wv_graphviz.py index 47bafa5..8c6a5dd 100644 --- a/src/wireviz/wv_graphviz.py +++ b/src/wireviz/wv_graphviz.py @@ -5,7 +5,7 @@ from itertools import zip_longest from typing import Any, List, Optional, Union from wireviz import APP_NAME, APP_URL, __version__ -from wireviz.wv_bom import partnumbers_to_list +from wireviz.wv_bom import partnumbers2list from wireviz.wv_colors import MultiColor from wireviz.wv_dataclasses import ( ArrowDirection, @@ -37,7 +37,7 @@ def gv_node_component(component: Component) -> Table: else: line_name = None - line_pn = partnumbers_to_list(component.partnumbers) + line_pn = partnumbers2list(component.partnumbers) is_simple_connector = ( isinstance(component, Connector) and component.style == "simple" @@ -283,7 +283,6 @@ def gv_conductor_table(cable) -> Table: cells_above = [ Td(" " + ", ".join(ins), align="left"), - # Td(":-)"), Td(bom_bubble(wire.bom_id)) if cable.category == "bundle" else None, Td(":".join([wi for wi in wireinfo if wi is not None and wi != ""])), Td(", ".join(outs) + " ", align="right"), @@ -295,8 +294,20 @@ def gv_conductor_table(cable) -> Table: rows.append(Tr(gv_wire_cell(wire, len(cells_above)))) # row below the wire - # TODO: PN stuff for bundles - # wire_pn_stuff() see below + if wire.partnumbers: + cells_below = partnumbers2list( + wire.partnumbers, parent_partnumbers=cable.partnumbers + ) + if cells_below is not None and len(cells_below) > 0: + table_below = ( + Table( + Tr([Td(cell) for cell in cells_below]), + border=0, + cellborder=0, + cellspacing=0, + ), + ) + rows.append(Tr(Td(table_below, colspan=len(cells_above)))) rows.append(Tr(Td(" "))) # spacer row on bottom tbl = Table(rows, border=0, cellborder=0, cellspacing=0) @@ -334,48 +345,6 @@ def gv_wire_cell(wire: Union[WireClass, ShieldClass], colspan: int) -> Td: return wire_outer_cell -def wire_pn_stuff(): - # # for bundles, individual wires can have part information - # if cable.category == "bundle": - # # create a list of wire parameters - # wireidentification = [] - # if isinstance(cable.pn, list): - # wireidentification.append( - # pn_info_string( - # HEADER_PN, None, remove_links(cable.pn[i - 1]) - # ) - # ) - # manufacturer_info = pn_info_string( - # HEADER_MPN, - # cable.manufacturer[i - 1] - # if isinstance(cable.manufacturer, list) - # else None, - # cable.mpn[i - 1] if isinstance(cable.mpn, list) else None, - # ) - # supplier_info = pn_info_string( - # HEADER_SPN, - # cable.supplier[i - 1] - # if isinstance(cable.supplier, list) - # else None, - # cable.spn[i - 1] if isinstance(cable.spn, list) else None, - # ) - # if manufacturer_info: - # wireidentification.append(html_line_breaks(manufacturer_info)) - # if supplier_info: - # wireidentification.append(html_line_breaks(supplier_info)) - # # print parameters into a table row under the wire - # if len(wireidentification) > 0: - # # fmt: off - # wirehtml.append(' ') - # wirehtml.append(' ') - # for attrib in wireidentification: - # wirehtml.append(f" ") - # wirehtml.append("
{attrib}
") - # wirehtml.append(" ") - # # fmt: on - pass - - def gv_edge_wire(harness, cable, connection) -> (str, str, str): if connection.via.color: # check if it's an actual wire and not a shield