Skip to content

Commit c7c0820

Browse files
committed
Add href attribute to connectors, cables, and additional_bom_items
1 parent df90d83 commit c7c0820

File tree

3 files changed

+47
-18
lines changed

3 files changed

+47
-18
lines changed

examples/demo02.yml

+14
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ templates: # defining templates to be used later on
22
- &molex_f
33
type: Molex KK 254
44
subtype: female
5+
href: "https://www.molex.com/molex/products/family/kk_254_rpc_connector_system"
56
- &con_i2c
67
pinlabels: [GND, +5V, SCL, SDA]
78
- &wire_i2c
89
category: bundle
910
gauge: 0.14 mm2
1011
colors: [BK, RD, YE, GN]
12+
href: [a, b, c, d]
1113

1214
connectors:
1315
X1:
@@ -67,3 +69,15 @@ connections:
6769
- ferrule_crimp
6870
- W4: [1,2]
6971
- X4: [1,2]
72+
73+
additional_bom_items:
74+
- # define an additional item to add to the bill of materials
75+
description: Label, pinout information
76+
qty: 2
77+
designators:
78+
- X2
79+
- X3
80+
manufacturer: generic company
81+
mpn: Label1
82+
pn: Label-ID-1
83+
href: "https://www.bradyid.com/wire-cable-labels/bmp71-bmp61-m611-tls-2200-nylon-cloth-wire-general-id-labels-cps-2958789"

src/wireviz/DataClasses.py

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class Connector:
1313
manufacturer: Optional[str] = None
1414
mpn: Optional[str] = None
1515
pn: Optional[str] = None
16+
href: Optional[str] = None
1617
style: Optional[str] = None
1718
category: Optional[str] = None
1819
type: Optional[str] = None
@@ -82,6 +83,7 @@ class Cable:
8283
manufacturer: Optional[Union[str, List[str]]] = None
8384
mpn: Optional[Union[str, List[str]]] = None
8485
pn: Optional[Union[str, List[str]]] = None
86+
href: Optional[str] = None
8587
category: Optional[str] = None
8688
type: Optional[str] = None
8789
gauge: Optional[float] = None

src/wireviz/Harness.py

+31-18
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ def create_graph(self) -> Graph:
126126
html = [row.replace('<!-- connector table -->', '\n'.join(pinhtml)) for row in html]
127127

128128
html = '\n'.join(html)
129-
dot.node(connector.name, label=f'<\n{html}\n>', shape='none', margin='0', style='filled', fillcolor='white')
129+
dot.node(connector.name, label=f'<\n{html}\n>', shape='none', href=connector.href,
130+
margin='0', style='filled', fillcolor='white')
130131

131132
if len(connector.loops) > 0:
132133
dot.attr('edge', color='#000000:#ffffff:#000000')
@@ -243,7 +244,8 @@ def create_graph(self) -> Graph:
243244
# connections
244245
for connection_color in cable.connections:
245246
if isinstance(connection_color.via_port, int): # check if it's an actual wire and not a shield
246-
dot.attr('edge', color=':'.join(['#000000'] + wv_colors.get_color_hex(cable.colors[connection_color.via_port - 1], pad=pad) + ['#000000']))
247+
dot.attr('edge', color=':'.join(['#000000'] + wv_colors.get_color_hex(cable.colors[connection_color.via_port - 1], pad=pad) + ['#000000']),
248+
href=index_if_list(cable.href, connection_color.via_port - 1) if cable.href else '')
247249
else: # it's a shield connection
248250
# shield is shown with specified color and black borders, or as a thin black wire otherwise
249251
dot.attr('edge', color=':'.join(['#000000', shield_color_hex, '#000000']) if isinstance(cable.shield, str) else '#000000')
@@ -264,6 +266,7 @@ def create_graph(self) -> Graph:
264266

265267
html = '\n'.join(html)
266268
dot.node(cable.name, label=f'<\n{html}\n>', shape='box',
269+
href=cable.href if isinstance(cable.href, str) else None,
267270
style='filled,dashed' if cable.category == 'bundle' else '', margin='0', fillcolor='white')
268271

269272
return dot
@@ -322,6 +325,8 @@ def output(self, filename: (str, Path), view: bool = False, cleanup: bool = True
322325
file.write('<tr>')
323326
for i, item in enumerate(row):
324327
item_str = item.replace('\u00b2', '&sup2;')
328+
if listy[0][i] == 'URL':
329+
item_str = f'<a href="{item}">{item_str}</a>'
325330
align = 'align="right"' if listy[0][i] == 'Qty' else ''
326331
file.write(f'<td {align} style="border:1px solid #000000; padding: 4px">{item_str}</td>')
327332
file.write('</tr>')
@@ -335,7 +340,7 @@ def bom(self):
335340
bom_cables = []
336341
bom_extra = []
337342
# connectors
338-
connector_group = lambda c: (c.type, c.subtype, c.pincount, c.manufacturer, c.mpn, c.pn)
343+
connector_group = lambda c: (c.type, c.subtype, c.pincount, c.manufacturer, c.mpn, c.pn, c.href)
339344
for group in Counter([connector_group(v) for v in self.connectors.values()]):
340345
items = {k: v for k, v in self.connectors.items() if connector_group(v) == group}
341346
shared = next(iter(items.values()))
@@ -346,15 +351,18 @@ def bom(self):
346351
conn_pincount = f', {shared.pincount} pins' if shared.style != 'simple' else ''
347352
conn_color = f', {shared.color}' if shared.color else ''
348353
name = f'Connector{conn_type}{conn_subtype}{conn_pincount}{conn_color}'
349-
item = {'item': name, 'qty': len(designators), 'unit': '', 'designators': designators if shared.show_name else '',
350-
'manufacturer': remove_line_breaks(shared.manufacturer), 'mpn': remove_line_breaks(shared.mpn), 'pn': shared.pn}
354+
item = {'item': name, 'qty': len(designators), 'unit': '',
355+
'designators': designators if shared.show_name else '',
356+
'manufacturer': remove_line_breaks(shared.manufacturer),
357+
'mpn': remove_line_breaks(shared.mpn), 'pn': shared.pn,
358+
'href': shared.href}
351359
bom_connectors.append(item)
352360
bom_connectors = sorted(bom_connectors, key=lambda k: k['item']) # https://stackoverflow.com/a/73050
353361
bom.extend(bom_connectors)
354362
# cables
355363
# TODO: If category can have other non-empty values than 'bundle', maybe it should be part of item name?
356364
# The category needs to be included in cable_group to keep the bundles excluded.
357-
cable_group = lambda c: (c.category, c.type, c.gauge, c.gauge_unit, c.wirecount, c.shield, c.manufacturer, c.mpn, c.pn)
365+
cable_group = lambda c: (c.category, c.type, c.gauge, c.gauge_unit, c.wirecount, c.shield, c.manufacturer, c.mpn, c.pn, c.href)
358366
for group in Counter([cable_group(v) for v in self.cables.values() if v.category != 'bundle']):
359367
items = {k: v for k, v in self.cables.items() if cable_group(v) == group}
360368
shared = next(iter(items.values()))
@@ -365,8 +373,11 @@ def bom(self):
365373
gauge_name = f' x {shared.gauge} {shared.gauge_unit}' if shared.gauge else ' wires'
366374
shield_name = ' shielded' if shared.shield else ''
367375
name = f'Cable{cable_type}, {shared.wirecount}{gauge_name}{shield_name}'
368-
item = {'item': name, 'qty': round(total_length, 3), 'unit': 'm', 'designators': designators,
369-
'manufacturer': remove_line_breaks(shared.manufacturer), 'mpn': remove_line_breaks(shared.mpn), 'pn': shared.pn}
376+
item = {'item': name, 'qty': round(total_length, 3), 'unit': 'm',
377+
'designators': designators,
378+
'manufacturer': remove_line_breaks(shared.manufacturer),
379+
'mpn': remove_line_breaks(shared.mpn), 'pn': shared.pn,
380+
'href': shared.href}
370381
bom_cables.append(item)
371382
# bundles (ignores wirecount)
372383
wirelist = []
@@ -378,9 +389,9 @@ def bom(self):
378389
wirelist.append({'type': bundle.type, 'gauge': bundle.gauge, 'gauge_unit': bundle.gauge_unit, 'length': bundle.length, 'color': color, 'designator': bundle.name,
379390
'manufacturer': remove_line_breaks(index_if_list(bundle.manufacturer, index)),
380391
'mpn': remove_line_breaks(index_if_list(bundle.mpn, index)),
381-
'pn': index_if_list(bundle.pn, index)})
392+
'pn': index_if_list(bundle.pn, index), 'href': index_if_list(bundle.href, index)})
382393
# join similar wires from all the bundles to a single BOM item
383-
wire_group = lambda w: (w.get('type', None), w['gauge'], w['gauge_unit'], w['color'], w['manufacturer'], w['mpn'], w['pn'])
394+
wire_group = lambda w: (w.get('type', None), w['gauge'], w['gauge_unit'], w['color'], w['manufacturer'], w['mpn'], w['pn'], w['href'])
384395
for group in Counter([wire_group(v) for v in wirelist]):
385396
items = [v for v in wirelist if wire_group(v) == group]
386397
shared = items[0]
@@ -393,17 +404,18 @@ def bom(self):
393404
gauge_color = f', {shared["color"]}' if 'color' in shared != '' else ''
394405
name = f'Wire{wire_type}{gauge_name}{gauge_color}'
395406
item = {'item': name, 'qty': round(total_length, 3), 'unit': 'm', 'designators': designators,
396-
'manufacturer': shared['manufacturer'], 'mpn': shared['mpn'], 'pn': shared['pn']}
407+
'manufacturer': shared['manufacturer'], 'mpn': shared['mpn'], 'pn': shared['pn'],
408+
'href': shared['href']}
397409
bom_cables.append(item)
398410
bom_cables = sorted(bom_cables, key=lambda k: k['item']) # sort list of dicts by their values (https://stackoverflow.com/a/73050)
399411
bom.extend(bom_cables)
400412

401413
for item in self.additional_bom_items:
402-
name = item['description'] if item.get('description', None) else ''
403-
if isinstance(item.get('designators', None), List):
414+
name = item['description'] if item.get('description') else ''
415+
if isinstance(item.get('designators'), List):
404416
item['designators'].sort() # sort designators if a list is provided
405-
item = {'item': name, 'qty': item.get('qty', None), 'unit': item.get('unit', None), 'designators': item.get('designators', None),
406-
'manufacturer': item.get('manufacturer', None), 'mpn': item.get('mpn', None), 'pn': item.get('pn', None)}
417+
item = {'item': name, 'qty': item.get('qty'), 'unit': item.get('unit'), 'designators': item.get('designators'),
418+
'manufacturer': item.get('manufacturer'), 'mpn': item.get('mpn'), 'pn': item.get('pn'), 'href': item.get('href')}
407419
bom_extra.append(item)
408420
bom_extra = sorted(bom_extra, key=lambda k: k['item'])
409421
bom.extend(bom_extra)
@@ -412,14 +424,15 @@ def bom(self):
412424
def bom_list(self):
413425
bom = self.bom()
414426
keys = ['item', 'qty', 'unit', 'designators'] # these BOM columns will always be included
415-
for fieldname in ['pn', 'manufacturer', 'mpn']: # these optional BOM columns will only be included if at least one BOM item actually uses them
416-
if any(fieldname in x and x.get(fieldname, None) for x in bom):
427+
for fieldname in ['pn', 'manufacturer', 'mpn', 'href']: # these optional BOM columns will only be included if at least one BOM item actually uses them
428+
if any(item.get(fieldname) for item in bom):
417429
keys.append(fieldname)
418430
bom_list = []
419431
# list of staic bom header names, headers not specified here are generated by capitilising the internal name
420432
bom_headings = {
421433
"pn": "P/N",
422-
"mpn": "MPN"
434+
"mpn": "MPN",
435+
"href": "URL",
423436
}
424437
bom_list.append([(bom_headings[k] if k in bom_headings else k.capitalize()) for k in keys]) # create header row with keys
425438
for item in bom:

0 commit comments

Comments
 (0)