diff --git a/src/wireviz/DataClasses.py b/src/wireviz/DataClasses.py index a27ae0ea..3e01ae1d 100644 --- a/src/wireviz/DataClasses.py +++ b/src/wireviz/DataClasses.py @@ -88,6 +88,7 @@ class Cable: show_name: bool = True show_wirecount: bool = True ignore_in_bom: bool = False + additional_components: List[Any] = None def __post_init__(self): diff --git a/src/wireviz/Harness.py b/src/wireviz/Harness.py index 449aa87e..f4077217 100644 --- a/src/wireviz/Harness.py +++ b/src/wireviz/Harness.py @@ -113,25 +113,25 @@ def create_graph(self) -> Graph: ''] if connector.additional_components is not None: rows.append(["Additional components"]) - for extra in connector.additional_components: - if 'qty' in extra: - if isinstance(extra['qty'], int) or isinstance(extra['qty'], float): - qty = extra['qty'] - else: # check for special quantities - if extra['qty'] == 'pincount': - qty = connector.pincount - elif extra['qty'] == 'connectioncount': - qty = sum(1 for value in connector.visible_pins.values() if value is True) - else: - raise ValueError('invalid aty parameter') - else: - qty = 1 - rows.append([extra["type"], qty]) - rows.append([extra["manufacturer"], - f'MPN: {extra["manufacturer_part_number"]}' if "manufacturer_part_number" in extra else None, - f'IPN: {extra["internal_part_number"]}' if "internal_part_number" in extra else None],) - rows.append([html_line_breaks(connector.notes)]) - html = nested_html_table(rows) + for extra in connector.additional_components: + if 'qty' in extra: + if isinstance(extra['qty'], int) or isinstance(extra['qty'], float): + qty = extra['qty'] + else: # check for special quantities + if extra['qty'] == 'pincount': + qty = connector.pincount + elif extra['qty'] == 'connectioncount': + qty = sum(1 for value in connector.visible_pins.values() if value is True) + else: + raise ValueError('invalid aty parameter') + else: + qty = 1 + rows.append([extra["type"], qty]) + rows.append([extra["manufacturer"], + f'MPN: {extra["manufacturer_part_number"]}' if "manufacturer_part_number" in extra else None, + f'IPN: {extra["internal_part_number"]}' if "internal_part_number" in extra else None],) + rows.append([html_line_breaks(connector.notes)]) + html = nested_html_table(rows) pinouts = [] for pinnumber, pinname in zip(connector.pinnumbers, connector.pinout): @@ -263,6 +263,44 @@ def create_graph(self) -> Graph: html = f'{html}' # conductor table html = f'{html}' # main table + + if cable.additional_components is not None: + html = f'{html}Additional components' # notes table + for extra in cable.additional_components: + if 'qty' in extra: + if isinstance(extra['qty'], int) or isinstance(extra['qty'], float): + qty = extra['qty'] + else: # check for special quantities + if extra['qty'] == 'wirecount': + qty = cable.wirecount + elif extra['qty'] == 'terminations': + qty = len(cable.connections) + elif extra['qty'] == 'length': + qty = cable.length + elif extra['qty'] == 'total_length': + qty = cable.length * cable.wirecount + else: + raise ValueError('invalid aty parameter {}'.format(extra["qty"])) + else: + qty = 1 + + html = f'{html}' + html = f'{html}' + html = f'{html}' + html = f'{html}
{extra["type"]}{qty}
' + + identification = [extra.get("manufacturer", None), + f'MPN: {extra["manufacturer_part_number"]}' if "manufacturer_part_number" in extra else None, + f'IPN: {extra["internal_part_number"]}' if "internal_part_number" in extra else None] + identification = list(filter(None, identification)) + if(len(identification) > 0): # print an identification row if values specified + html = f'{html}' + for attrib in identification[0:-1]: + html = f'{html}' # all columns except last have a border on the right (sides="R") + if len(identification) > 0: + html = f'{html}' # last column has no border on the right because the enclosing table borders it + html = f'{html}
{attrib}{identification[-1]}
' # end identification row + if cable.notes: html = f'{html}{html_line_breaks(cable.notes)}' # notes table html = f'{html} ' # spacer at the end @@ -363,6 +401,7 @@ def bom(self): bom_connectors = [] bom_connectors_extra = [] bom_cables = [] + bom_cables_extra = [] bom_extra = [] # connectors connector_group = lambda c: (c.type, c.subtype, c.pincount, c.manufacturer, c.manufacturer_part_number, c.internal_part_number) @@ -471,6 +510,52 @@ def bom(self): bom_cables = sorted(bom_cables, key=lambda k: k['item']) # sort list of dicts by their values (https://stackoverflow.com/a/73050) bom.extend(bom_cables) + cables_extra = [] + for cable in self.cables.values(): + if cable.additional_components: + for part in cable.additional_components: + if 'qty' in part: + if isinstance(part['qty'], int) or isinstance(part['qty'], float): + qty = part['qty'] + else: # check for special quantities + if part['qty'] == 'wirecount': + qty = cable.wirecount + elif part['qty'] == 'terminations': + qty = len(cable.connections) + elif part['qty'] == 'length': + qty = cable.length + elif part['qty'] == 'total_length': + qty = cable.length * cable.wirecount + else: + raise ValueError('invalid aty parameter') + else: + qty = 1 + cables_extra.append( + { + 'type': part.get('type', None), + 'qty': qty, + 'unit': part.get('unit', None), + 'manufacturer': part.get('manufacturer', None), + 'manufacturer part number': part.get('manufacturer_part_number', None), + 'internal part number': part.get('internal_part_number', None), + 'designator': connector.name + } + ) + cables_extra_group = lambda ce: (ce['type'], ce['qty'], ce['unit'], ce['manufacturer'], ce['manufacturer part number'], ce['internal part number']) + for group in Counter([connector_extra_group(v) for v in cables_extra]): + items = [v for v in cables_extra if connector_extra_group(v) == group] + shared = items[0] + designators = [i['designator'] for i in items] + designators = list(dict.fromkeys(designators)) # remove duplicates + designators.sort() + total_qty = sum(i['qty'] for i in items) + + item = {'item': shared['type'], 'qty': round(total_qty, 3), 'unit': shared['unit'], 'designators': designators, + 'manufacturer': shared['manufacturer'], 'manufacturer part number': shared['manufacturer part number'], 'internal part number': shared['internal part number']} + bom_cables_extra.append(item) + bom_cables_extra = sorted(bom_cables_extra, key=lambda k: k['item']) # sort list of dicts by their values (https://stackoverflow.com/a/73050) + bom.extend(bom_cables_extra) + for item in self.additional_bom_items: name = item['description'] if item.get('description', None) else '' if isinstance(item.get('designators', None), List):