@@ -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 ' , '²' )
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