diff --git a/docs/syntax.md b/docs/syntax.md index fa433c3d..b50014a7 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -102,8 +102,10 @@ tweak: # optional tweaking of .gv output # product information (all optional) ignore_in_bom: # if set to true the connector is not added to the BOM pn: # [internal] part number - mpn: # manufacturer part number manufacturer: # manufacturer name + mpn: # manufacturer part number + supplier: # supplier name + spn: # supplier part number additional_components: # additional components - # additional component (see below) @@ -175,8 +177,10 @@ Since the auto-incremented and auto-assigned designator is not known to the user # product information (all optional) ignore_in_bom: # if set to true the cable or wires are not added to the BOM pn: # [internal] part number - mpn: # manufacturer part number manufacturer: # manufacturer name + mpn: # manufacturer part number + supplier: # supplier name + spn: # supplier part number additional_components: # additional components - # additional component (see below) @@ -311,8 +315,10 @@ Parts can be added to a connector or cable in the section ` pn: # [internal] part number - mpn: # manufacturer part number manufacturer: # manufacturer name + mpn: # manufacturer part number + supplier: # supplier name + spn: # supplier part number ``` Alternatively items can be added to just the BOM by putting them in the section `` above. @@ -325,8 +331,10 @@ Alternatively items can be added to just the BOM by putting them in the section unit: designators: pn: # [internal] part number - mpn: # manufacturer part number manufacturer: # manufacturer name + mpn: # manufacturer part number + supplier: # supplier name + spn: # supplier part number ``` ## GraphViz tweaking (experimental) @@ -429,6 +437,8 @@ The following attributes accept multiline strings: - `notes` - `manufacturer` - `mpn` +- `supplier` +- `spn` - `image.caption` - `tweak.append` diff --git a/src/wireviz/DataClasses.py b/src/wireviz/DataClasses.py index 95325d18..44c063cf 100644 --- a/src/wireviz/DataClasses.py +++ b/src/wireviz/DataClasses.py @@ -107,6 +107,8 @@ class AdditionalComponent: subtype: Optional[MultilineHypertext] = None manufacturer: Optional[MultilineHypertext] = None mpn: Optional[MultilineHypertext] = None + supplier: Optional[MultilineHypertext] = None + spn: Optional[MultilineHypertext] = None pn: Optional[Hypertext] = None qty: float = 1 unit: Optional[str] = None @@ -122,6 +124,8 @@ class Connector: name: Designator manufacturer: Optional[MultilineHypertext] = None mpn: Optional[MultilineHypertext] = None + supplier: Optional[MultilineHypertext] = None + spn: Optional[MultilineHypertext] = None pn: Optional[Hypertext] = None style: Optional[str] = None category: Optional[str] = None @@ -204,6 +208,8 @@ class Cable: name: Designator manufacturer: Union[MultilineHypertext, List[MultilineHypertext], None] = None mpn: Union[MultilineHypertext, List[MultilineHypertext], None] = None + supplier: Union[MultilineHypertext, List[MultilineHypertext], None] = None + spn: Union[MultilineHypertext, List[MultilineHypertext], None] = None pn: Union[Hypertext, List[Hypertext], None] = None category: Optional[str] = None type: Optional[MultilineHypertext] = None @@ -294,7 +300,7 @@ def __post_init__(self) -> None: raise Exception('"s" may not be used as a wire label for a shielded cable.') # if lists of part numbers are provided check this is a bundle and that it matches the wirecount. - for idfield in [self.manufacturer, self.mpn, self.pn]: + for idfield in [self.manufacturer, self.mpn, self.supplier, self.spn, self.pn]: if isinstance(idfield, list): if self.category == "bundle": # check the length diff --git a/src/wireviz/Harness.py b/src/wireviz/Harness.py index f4937f7f..edd8046c 100644 --- a/src/wireviz/Harness.py +++ b/src/wireviz/Harness.py @@ -14,8 +14,9 @@ from wireviz.wv_colors import get_color_hex, translate_color from wireviz.wv_gv_html import nested_html_table, html_colorbar, html_image, \ html_caption, remove_links, html_line_breaks -from wireviz.wv_bom import manufacturer_info_field, component_table_entry, \ - get_additional_component_table, bom_list, generate_bom +from wireviz.wv_bom import pn_info_string, component_table_entry, \ + get_additional_component_table, bom_list, generate_bom, \ + HEADER_PN, HEADER_MPN, HEADER_SPN from wireviz.wv_html import generate_html_output from wireviz.wv_helper import awg_equiv, mm2_equiv, tuplelist2tsv, flatten2d, \ open_file_read, open_file_write @@ -125,8 +126,9 @@ def create_graph(self) -> Graph: html = [] rows = [[remove_links(connector.name) if connector.show_name else None], - [f'P/N: {remove_links(connector.pn)}' if connector.pn else None, - html_line_breaks(manufacturer_info_field(connector.manufacturer, connector.mpn))], + [pn_info_string(HEADER_PN, None, remove_links(connector.pn)), + html_line_breaks(pn_info_string(HEADER_MPN, connector.manufacturer, connector.mpn)), + html_line_breaks(pn_info_string(HEADER_SPN, connector.supplier, connector.spn))], [html_line_breaks(connector.type), html_line_breaks(connector.subtype), f'{connector.pincount}-pin' if connector.show_pincount else None, @@ -208,10 +210,14 @@ def create_graph(self) -> Graph: awg_fmt = f' ({mm2_equiv(cable.gauge)} mm\u00B2)' rows = [[remove_links(cable.name) if cable.show_name else None], - [f'P/N: {remove_links(cable.pn)}' if (cable.pn and not isinstance(cable.pn, list)) else None, - html_line_breaks(manufacturer_info_field( + [pn_info_string(HEADER_PN, None, + remove_links(cable.pn)) if not isinstance(cable.pn, list) else None, + html_line_breaks(pn_info_string(HEADER_MPN, cable.manufacturer if not isinstance(cable.manufacturer, list) else None, - cable.mpn if not isinstance(cable.mpn, list) else None))], + cable.mpn if not isinstance(cable.mpn, list) else None)), + html_line_breaks(pn_info_string(HEADER_SPN, + cable.supplier if not isinstance(cable.supplier, list) else None, + cable.spn if not isinstance(cable.spn, list) else None))], [html_line_breaks(cable.type), f'{cable.wirecount}x' if cable.show_wirecount else None, f'{cable.gauge} {cable.gauge_unit}{awg_fmt}' if cable.gauge else None, @@ -263,12 +269,17 @@ def create_graph(self) -> Graph: # create a list of wire parameters wireidentification = [] if isinstance(cable.pn, list): - wireidentification.append(f'P/N: {remove_links(cable.pn[i - 1])}') - manufacturer_info = manufacturer_info_field( + 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 : wirehtml.append(' ') diff --git a/src/wireviz/wv_bom.py b/src/wireviz/wv_bom.py index 76ba4a1d..bd7aedeb 100644 --- a/src/wireviz/wv_bom.py +++ b/src/wireviz/wv_bom.py @@ -11,9 +11,13 @@ from wireviz.wv_helper import clean_whitespace BOM_COLUMNS_ALWAYS = ('id', 'description', 'qty', 'unit', 'designators') -BOM_COLUMNS_OPTIONAL = ('pn', 'manufacturer', 'mpn') +BOM_COLUMNS_OPTIONAL = ('pn', 'manufacturer', 'mpn', 'supplier', 'spn') BOM_COLUMNS_IN_KEY = ('description', 'unit') + BOM_COLUMNS_OPTIONAL +HEADER_PN = 'P/N' +HEADER_MPN = 'MPN' +HEADER_SPN = 'SPN' + BOMKey = Tuple[str, ...] BOMColumn = str # = Literal[*BOM_COLUMNS_ALWAYS, *BOM_COLUMNS_OPTIONAL] BOMEntry = Dict[BOMColumn, Union[str, int, float, List[str], None]] @@ -143,8 +147,9 @@ def bom_list(bom: List[BOMEntry]) -> List[List[str]]: # Custom mapping from internal name to BOM column headers. # Headers not specified here are generated by capitilising the internal name. bom_headings = { - "pn": "P/N", - "mpn": "MPN" + 'pn': HEADER_PN, + 'mpn': HEADER_MPN, + 'spn': HEADER_SPN, } return ([[bom_headings.get(k, k.capitalize()) for k in keys]] + # Create header row with key names [[make_str(entry.get(k)) for k in keys] for entry in bom]) # Create string list for each entry row @@ -156,26 +161,31 @@ def component_table_entry( pn: Optional[str] = None, manufacturer: Optional[str] = None, mpn: Optional[str] = None, + supplier: Optional[str] = None, + spn: Optional[str] = None, ) -> str: """Return a diagram node table row string with an additional component.""" - manufacturer_str = manufacturer_info_field(manufacturer, mpn) + part_number_list = [ + pn_info_string(HEADER_PN, None, pn), + pn_info_string(HEADER_MPN, manufacturer, mpn), + pn_info_string(HEADER_SPN, supplier, spn), + ] output = (f'{qty}' + (f' {unit}' if unit else '') + f' x {type}' - + ('
' if pn or manufacturer_str else '') - + (f'P/N: {pn}' if pn else '') - + (', ' if pn and manufacturer_str else '') - + (manufacturer_str or '')) + + ('
' if any(part_number_list) else '') + + (', '.join([pn for pn in part_number_list if pn]))) # format the above output as left aligned text in a single visible cell # indent is set to two to match the indent in the generated html table return f'''
{html_line_breaks(output)}
''' -def manufacturer_info_field(manufacturer: Optional[str], mpn: Optional[str]) -> Optional[str]: - """Return the manufacturer and/or the mpn in one single string or None otherwise.""" - if manufacturer or mpn: - return f'{manufacturer if manufacturer else "MPN"}{": " + str(mpn) if mpn else ""}' +def pn_info_string(header: str, name: Optional[str], number: Optional[str]) -> Optional[str]: + """Return the company name and/or the part number in one single string or None otherwise.""" + number = str(number).strip() if number is not None else '' + if name or number: + return f'{name if name else header}{": " + number if number else ""}' else: return None diff --git a/tutorial/tutorial08.yml b/tutorial/tutorial08.yml index 2dc55443..1e5e4f20 100644 --- a/tutorial/tutorial08.yml +++ b/tutorial/tutorial08.yml @@ -1,3 +1,6 @@ +options: + mini_bom_mode: false + connectors: X1: &template1 # define a template for later use type: Molex KK 254 @@ -5,6 +8,8 @@ connectors: subtype: female manufacturer: 'Molex' # set manufacter name mpn: '22013047' # set manufacturer part number + supplier: Digimouse + spn: 1234 # add a list of additional components to a part (shown in graph) additional_components: - @@ -13,6 +18,14 @@ connectors: qty_multiplier: populated # multipier for quantity (number of populated pins) manufacturer: Molex # set manufacter name mpn: 08500030 # set manufacturer part number + - + type: Test + qty: 1 + pn: ABC + manufacturer: Molex + mpn: 45454 + supplier: Mousikey + spn: 9999 X2: <<: *template1 # reuse template pn: CON4 # set an internal part number for just this connector @@ -27,6 +40,8 @@ cables: color_code: IEC manufacturer: CablesCo mpn: ABC123 + supplier: Cables R Us + spn: 999-888-777 pn: CAB1 W2: category: bundle @@ -35,6 +50,8 @@ cables: colors: [YE, BK, BK, RD] manufacturer: [WiresCo,WiresCo,WiresCo,WiresCo] # set a manufacter per wire mpn: [W1-YE,W1-BK,W1-BK,W1-RD] + supplier: [WireShack,WireShack,WireShack,WireShack] + spn: [1001,1002,1002,1009] pn: [WIRE1,WIRE2,WIRE2,WIRE3] # add a list of additional components to a part (shown in graph) additional_components: