Skip to content

Add href attribute to connectors, cables, and additional_bom_items #168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions examples/demo02.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ templates: # defining templates to be used later on
- &molex_f
type: Molex KK 254
subtype: female
url: "https://www.molex.com/molex/products/family/kk_254_rpc_connector_system"
- &con_i2c
pinlabels: [GND, +5V, SCL, SDA]
- &wire_i2c
category: bundle
gauge: 0.14 mm2
colors: [BK, RD, YE, GN]
url: [bk.html, rd.html, ye.html, gn.html]

connectors:
X1:
Expand Down Expand Up @@ -67,3 +69,15 @@ connections:
- ferrule_crimp
- W4: [1,2]
- X4: [1,2]

additional_bom_items:
- # define an additional item to add to the bill of materials
description: Label, pinout information
qty: 2
designators:
- X2
- X3
manufacturer: generic company
mpn: Label1
pn: Label-ID-1
url: "https://www.bradyid.com/wire-cable-labels/bmp71-bmp61-m611-tls-2200-nylon-cloth-wire-general-id-labels-cps-2958789"
3 changes: 3 additions & 0 deletions src/wireviz/DataClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class AdditionalComponent:
manufacturer: Optional[MultilineHypertext] = None
mpn: Optional[MultilineHypertext] = None
pn: Optional[Hypertext] = None
url: Optional[PlainText] = None
qty: float = 1
unit: Optional[str] = None
qty_multiplier: Union[ConnectorMultiplier, CableMultiplier, None] = None
Expand All @@ -88,6 +89,7 @@ class Connector:
manufacturer: Optional[MultilineHypertext] = None
mpn: Optional[MultilineHypertext] = None
pn: Optional[Hypertext] = None
url: Optional[PlainText] = None
style: Optional[str] = None
category: Optional[str] = None
type: Optional[MultilineHypertext] = None
Expand Down Expand Up @@ -170,6 +172,7 @@ class Cable:
manufacturer: Union[MultilineHypertext, List[MultilineHypertext], None] = None
mpn: Union[MultilineHypertext, List[MultilineHypertext], None] = None
pn: Union[Hypertext, List[Hypertext], None] = None
url: Optional[PlainText] = None
category: Optional[str] = None
type: Optional[MultilineHypertext] = None
gauge: Optional[float] = None
Expand Down
13 changes: 9 additions & 4 deletions src/wireviz/Harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
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
get_additional_component_table, bom_list, generate_bom, index_if_list
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
Expand Down Expand Up @@ -159,7 +159,8 @@ def create_graph(self) -> Graph:
html = [row.replace('<!-- connector table -->', '\n'.join(pinhtml)) for row in html]

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

if len(connector.loops) > 0:
dot.attr('edge', color='#000000:#ffffff:#000000')
Expand Down Expand Up @@ -287,11 +288,14 @@ def create_graph(self) -> Graph:

# connections
for connection in cable.connections:
# Avoid href=None for edges as it seems that Graphviz then uses the href value from a previous edge!
if isinstance(connection.via_port, int): # check if it's an actual wire and not a shield
dot.attr('edge', color=':'.join(['#000000'] + wv_colors.get_color_hex(cable.colors[connection.via_port - 1], pad=pad) + ['#000000']))
dot.attr('edge', color=':'.join(['#000000'] + wv_colors.get_color_hex(cable.colors[connection.via_port - 1], pad=pad) + ['#000000']),
href=index_if_list(cable.url, connection.via_port - 1) or '')
else: # it's a shield connection
# shield is shown with specified color and black borders, or as a thin black wire otherwise
dot.attr('edge', color=':'.join(['#000000', shield_color_hex, '#000000']) if isinstance(cable.shield, str) else '#000000')
dot.attr('edge', color=':'.join(['#000000', shield_color_hex, '#000000']) if isinstance(cable.shield, str) else '#000000',
href=cable.url if isinstance(cable.url, str) else '')
if connection.from_port is not None: # connect to left
from_connector = self.connectors[connection.from_name]
from_port = f':p{connection.from_port}r' if from_connector.style != 'simple' else ''
Expand Down Expand Up @@ -327,6 +331,7 @@ def create_graph(self) -> Graph:

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

return dot
Expand Down
28 changes: 16 additions & 12 deletions src/wireviz/wv_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def get_additional_component_table(harness, component: Union[Connector, Cable])
for extra in component.additional_components:
qty = extra.qty * component.get_qty_multiplier(extra.qty_multiplier)
if harness.mini_bom_mode:
id = get_bom_index(harness, extra.description, extra.unit, extra.manufacturer, extra.mpn, extra.pn)
id = get_bom_index(harness, extra.description, extra.unit, extra.manufacturer, extra.mpn, extra.pn, extra.url)
rows.append(component_table_entry(f'#{id} ({extra.type.rstrip()})', qty, extra.unit))
else:
rows.append(component_table_entry(extra.description, qty, extra.unit, extra.pn, extra.manufacturer, extra.mpn))
Expand All @@ -32,7 +32,8 @@ def get_additional_component_bom(component: Union[Connector, Cable]) -> List[dic
'manufacturer': part.manufacturer,
'mpn': part.mpn,
'pn': part.pn,
'designators': component.name if component.show_name else None
'designators': component.name if component.show_name else None,
'url': part.url,
})
return(bom_entries)

Expand All @@ -49,7 +50,7 @@ def generate_bom(harness):
+ (f', {connector.color}' if connector.color else ''))
bom_entries.append({
'item': description, 'qty': 1, 'unit': None, 'designators': connector.name if connector.show_name else None,
'manufacturer': connector.manufacturer, 'mpn': connector.mpn, 'pn': connector.pn
'manufacturer': connector.manufacturer, 'mpn': connector.mpn, 'pn': connector.pn, 'url': connector.url,
})

# add connectors aditional components to bom
Expand All @@ -68,7 +69,8 @@ def generate_bom(harness):
+ (' shielded' if cable.shield else ''))
bom_entries.append({
'item': description, 'qty': cable.length, 'unit': cable.length_unit, 'designators': cable.name if cable.show_name else None,
'manufacturer': cable.manufacturer, 'mpn': cable.mpn, 'pn': cable.pn
'manufacturer': cable.manufacturer, 'mpn': cable.mpn, 'pn': cable.pn,
'url': cable.url if isinstance(cable.url, str) else None,
})
else:
# add each wire from the bundle to the bom
Expand All @@ -80,7 +82,8 @@ def generate_bom(harness):
bom_entries.append({
'item': description, 'qty': cable.length, 'unit': cable.length_unit, 'designators': cable.name if cable.show_name else None,
'manufacturer': index_if_list(cable.manufacturer, index),
'mpn': index_if_list(cable.mpn, index), 'pn': index_if_list(cable.pn, index)
'mpn': index_if_list(cable.mpn, index), 'pn': index_if_list(cable.pn, index),
'url': index_if_list(cable.url, index),
})

# add cable/bundles aditional components to bom
Expand All @@ -89,15 +92,15 @@ def generate_bom(harness):
for item in harness.additional_bom_items:
bom_entries.append({
'item': item.get('description', ''), 'qty': item.get('qty', 1), 'unit': item.get('unit'), 'designators': item.get('designators'),
'manufacturer': item.get('manufacturer'), 'mpn': item.get('mpn'), 'pn': item.get('pn')
'manufacturer': item.get('manufacturer'), 'mpn': item.get('mpn'), 'pn': item.get('pn'), 'url': item.get('url'),
})

# remove line breaks if present and cleanup any resulting whitespace issues
bom_entries = [{k: clean_whitespace(v) for k, v in entry.items()} for entry in bom_entries]

# deduplicate bom
bom = []
bom_types_group = lambda bt: (bt['item'], bt['unit'], bt['manufacturer'], bt['mpn'], bt['pn'])
bom_types_group = lambda bt: (bt['item'], bt['unit'], bt['manufacturer'], bt['mpn'], bt['pn'], bt['url'])
for group in Counter([bom_types_group(v) for v in bom_entries]):
group_entries = [v for v in bom_entries if bom_types_group(v) == group]
designators = []
Expand All @@ -118,24 +121,25 @@ def generate_bom(harness):
bom = [{**entry, 'id': index} for index, entry in enumerate(bom, 1)]
return bom

def get_bom_index(harness, item, unit, manufacturer, mpn, pn):
def get_bom_index(harness, item, unit, manufacturer, mpn, pn, url):
# Remove linebreaks and clean whitespace of values in search
target = tuple(clean_whitespace(v) for v in (item, unit, manufacturer, mpn, pn))
target = tuple(clean_whitespace(v) for v in (item, unit, manufacturer, mpn, pn, url))
for entry in harness.bom():
if (entry['item'], entry['unit'], entry['manufacturer'], entry['mpn'], entry['pn']) == target:
if (entry['item'], entry['unit'], entry['manufacturer'], entry['mpn'], entry['pn'], entry['url']) == target:
return entry['id']
return None

def bom_list(bom):
keys = ['id', 'item', 'qty', 'unit', 'designators'] # these BOM columns will always be included
for fieldname in ['pn', 'manufacturer', 'mpn']: # these optional BOM columns will only be included if at least one BOM item actually uses them
for fieldname in ['pn', 'manufacturer', 'mpn', 'url']: # these optional BOM columns will only be included if at least one BOM item actually uses them
if any(entry.get(fieldname) for entry in bom):
keys.append(fieldname)
bom_list = []
# list of staic bom header names, headers not specified here are generated by capitilising the internal name
bom_headings = {
"pn": "P/N",
"mpn": "MPN"
"mpn": "MPN",
"url": "URL",
}
bom_list.append([(bom_headings[k] if k in bom_headings else k.capitalize()) for k in keys]) # create header row with keys
for item in bom:
Expand Down
2 changes: 2 additions & 0 deletions src/wireviz/wv_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def generate_html_output(filename: (str, Path), bom_list):
file.write('<tr>')
for i, item in enumerate(row):
item_str = item.replace('\u00b2', '&sup2;')
if listy[0][i] == 'URL':
item_str = f'<a href="{item}">{item_str}</a>'
align = 'text-align:right; ' if listy[0][i] == 'Qty' else ''
file.write(f'<td style="{align}border:1px solid #000000; padding: 4px">{item_str}</td>')
file.write('</tr>')
Expand Down