Skip to content

Commit 53ffa2e

Browse files
committed
Fixes for extra fields handling
Add purpose class ExtraFieldData that parsers should return. It can store field data by index instead of by ref to work around the issue of duplicate references. Issue #385
1 parent 42a0227 commit 53ffa2e

File tree

7 files changed

+90
-60
lines changed

7 files changed

+90
-60
lines changed

InteractiveHtmlBom/core/ibom.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,12 @@ def natural_sort(lst):
138138
group_key.append(f.footprint)
139139
group_key.append(f.attr)
140140
else:
141-
fields.append(f.extra_fields.get(field, ''))
141+
field_key = field
142+
if config.normalize_field_case:
143+
field_key = field.lower()
144+
fields.append(f.extra_fields.get(field_key, ''))
142145
if field in group_by:
143-
group_key.append(f.extra_fields.get(field, ''))
146+
group_key.append(f.extra_fields.get(field_key, ''))
144147

145148
index_to_fields[i] = fields
146149
refs = part_groups.setdefault(tuple(group_key), [])

InteractiveHtmlBom/dialog/dialog_base.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.
546546
self.fieldsGrid.Bind( wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnGridCellClicked )
547547
self.m_btnUp.Bind( wx.EVT_BUTTON, self.OnFieldsUp )
548548
self.m_btnDown.Bind( wx.EVT_BUTTON, self.OnFieldsDown )
549-
self.normalizeCaseCheckbox.Bind( wx.EVT_CHECKBOX, self.OnNetlistFileChanged )
549+
self.normalizeCaseCheckbox.Bind( wx.EVT_CHECKBOX, self.OnExtraDataFileChanged )
550550
self.boardVariantFieldBox.Bind( wx.EVT_COMBOBOX, self.OnBoardVariantFieldChange )
551551

552552
def __del__( self ):
@@ -569,8 +569,6 @@ def OnFieldsUp( self, event ):
569569
def OnFieldsDown( self, event ):
570570
event.Skip()
571571

572-
def OnNetlistFileChanged( self, event ):
573-
event.Skip()
574572

575573
def OnBoardVariantFieldChange( self, event ):
576574
event.Skip()

InteractiveHtmlBom/dialog/settings_dialog.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ class FieldsPanel(dialog_base.FieldsPanelBase):
222222

223223
def __init__(self, parent, extra_data_func, extra_data_wildcard):
224224
dialog_base.FieldsPanelBase.__init__(self, parent)
225+
self.show_fields = []
226+
self.group_fields = []
227+
225228
self.extra_data_func = extra_data_func
226229
self.extra_field_data = None
227230

@@ -333,8 +336,9 @@ def OnExtraDataFileChanged(self, event):
333336
self.extraDataFilePicker.Path = ''
334337

335338
if self.extra_field_data is not None:
336-
field_list = list(self.extra_field_data[0])
339+
field_list = list(self.extra_field_data.fields)
337340
self._setFieldsList(["Value", "Footprint"] + field_list)
341+
self.SetCheckedFields()
338342
field_list.append(self.NONE_STRING)
339343
self.boardVariantFieldBox.SetItems(field_list)
340344
self.boardVariantFieldBox.SetStringSelection(self.NONE_STRING)
@@ -351,7 +355,7 @@ def OnBoardVariantFieldChange(self, event):
351355
self.boardVariantBlacklist.Clear()
352356
return
353357
variant_set = set()
354-
for _, field_dict in self.extra_field_data[1].items():
358+
for _, field_dict in self.extra_field_data.fields_by_ref.items():
355359
if selection in field_dict:
356360
variant_set.add(field_dict[selection])
357361
self.boardVariantWhitelist.SetItems(list(variant_set))
@@ -377,14 +381,20 @@ def GetGroupFields(self):
377381
result.append(self.fieldsGrid.GetCellValue(row, 2))
378382
return result
379383

380-
def SetCheckedFields(self, show, group):
381-
group = [s for s in group if s in show]
384+
def SetCheckedFields(self, show=None, group=None):
385+
self.show_fields = show or self.show_fields
386+
self.group_fields = group or self.group_fields
387+
self.group_fields = [
388+
s for s in self.group_fields if s in self.show_fields
389+
]
382390
current = []
383391
for row in range(self.fieldsGrid.NumberRows):
384392
current.append(self.fieldsGrid.GetCellValue(row, 2))
385-
new = [s for s in current if s not in show]
386-
self._setFieldsList(show + new)
393+
new = [s for s in current if s not in self.show_fields]
394+
self._setFieldsList(self.show_fields + new)
387395
for row in range(self.fieldsGrid.NumberRows):
388396
field = self.fieldsGrid.GetCellValue(row, 2)
389-
self.fieldsGrid.SetCellValue(row, 0, "1" if field in show else "")
390-
self.fieldsGrid.SetCellValue(row, 1, "1" if field in group else "")
397+
self.fieldsGrid.SetCellValue(
398+
row, 0, "1" if field in self.show_fields else "")
399+
self.fieldsGrid.SetCellValue(
400+
row, 1, "1" if field in self.group_fields else "")

InteractiveHtmlBom/ecad/common.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
from .svgpath import parse_path
44

55

6+
class ExtraFieldData(object):
7+
def __init__(self, fields, fields_by_ref, fields_by_index=None):
8+
self.fields = fields
9+
self.fields_by_ref = fields_by_ref
10+
self.fields_by_index = fields_by_index
11+
12+
613
class EcadParser(object):
714

815
def __init__(self, file_name, config, logger):
@@ -28,33 +35,28 @@ def parse(self):
2835

2936
@staticmethod
3037
def normalize_field_names(data):
31-
field_map = {f.lower(): f for f in reversed(data[0])}
32-
38+
# type: (ExtraFieldData) -> ExtraFieldData
3339
def remap(ref_fields):
34-
return {field_map[f.lower()]: v for (f, v) in
35-
sorted(ref_fields.items(), reverse=True)}
40+
return {f.lower(): v for (f, v) in
41+
sorted(ref_fields.items(), reverse=True) if v}
42+
43+
by_ref = {r: remap(d) for (r, d) in data.fields_by_ref.items()}
44+
if data.fields_by_index:
45+
by_index = {i: remap(d) for (i, d) in data.fields_by_index.items()}
46+
print([a.get("blah", "") for a in by_index.values()])
47+
else:
48+
by_index = None
3649

37-
field_data = {r: remap(d) for (r, d) in data[1].items()}
38-
return field_map.values(), field_data
50+
field_map = {f.lower(): f for f in sorted(data.fields, reverse=True)}
51+
return ExtraFieldData(field_map.values(), by_ref, by_index)
3952

4053
def get_extra_field_data(self, file_name):
4154
"""
4255
Abstract method that may be overridden in implementations that support
4356
extra field data.
44-
:return: tuple of the format
45-
(
46-
[field_name1, field_name2,... ],
47-
{
48-
ref1: {
49-
field_name1: field_value1,
50-
field_name2: field_value2,
51-
...
52-
],
53-
ref2: ...
54-
}
55-
)
57+
:return: ExtraFieldData
5658
"""
57-
return [], {}
59+
return ExtraFieldData([], {})
5860

5961
def parse_extra_data(self, file_name, normalize_case):
6062
"""
@@ -67,7 +69,8 @@ def parse_extra_data(self, file_name, normalize_case):
6769
data = self.get_extra_field_data(file_name)
6870
if normalize_case:
6971
data = self.normalize_field_names(data)
70-
return sorted(data[0]), data[1]
72+
return ExtraFieldData(
73+
sorted(data.fields), data.fields_by_ref, data.fields_by_index)
7174

7275
def latest_extra_data(self, extra_dirs=None):
7376
"""

InteractiveHtmlBom/ecad/genericjson.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os.path
44
from jsonschema import validate, ValidationError
55

6-
from .common import EcadParser, Component, BoundingBox
6+
from .common import EcadParser, Component, BoundingBox, ExtraFieldData
77
from ..core.fontparser import FontParser
88
from ..errors import ParsingException
99

@@ -32,7 +32,11 @@ def get_extra_field_data(self, file_name):
3232
field_set.add(k)
3333
ref_fields[k] = v
3434

35-
return list(field_set), comp_dict
35+
by_index = {
36+
i: components[i].extra_fields for i in range(len(components))
37+
}
38+
39+
return ExtraFieldData(list(field_set), comp_dict, by_index)
3640

3741
def get_generic_json_pcb(self):
3842
with io.open(self.file_name, 'r', encoding='utf-8') as f:

InteractiveHtmlBom/ecad/kicad.py

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import pcbnew
55

6-
from .common import EcadParser, Component
6+
from .common import EcadParser, Component, ExtraFieldData
77
from .kicad_extra import find_latest_schematic_data, parse_schematic_data
88
from .svgpath import create_path
99
from ..core import ibom
@@ -29,22 +29,29 @@ def get_extra_field_data(self, file_name):
2929
return self.parse_extra_data_from_pcb()
3030
if os.path.splitext(file_name)[1] == '.kicad_pcb':
3131
return None
32-
return parse_schematic_data(file_name)
32+
33+
data = parse_schematic_data(file_name)
34+
35+
return ExtraFieldData(data[0], data[1])
3336

3437
def parse_extra_data_from_pcb(self):
3538
field_set = set()
36-
comp_dict = {}
39+
by_ref = {}
3740

3841
for f in self.footprints: # type: pcbnew.FOOTPRINT
3942
props = f.GetProperties()
4043
ref = f.GetReference()
41-
ref_fields = comp_dict.setdefault(ref, {})
44+
ref_fields = by_ref.setdefault(ref, {})
4245

4346
for k, v in props.items():
4447
field_set.add(k)
4548
ref_fields[k] = v
4649

47-
return list(field_set), comp_dict
50+
by_index = {
51+
i: f.GetProperties() for (i, f) in enumerate(self.footprints)
52+
}
53+
54+
return ExtraFieldData(list(field_set), by_ref, by_index)
4855

4956
def latest_extra_data(self, extra_dirs=None):
5057
base_name = os.path.splitext(os.path.basename(self.file_name))[0]
@@ -685,8 +692,6 @@ def parse(self):
685692
raise ParsingException(
686693
'Failed parsing %s' % self.config.extra_data_file)
687694

688-
extra_field_data = extra_field_data[1] if extra_field_data else None
689-
690695
title_block = self.board.GetTitleBlock()
691696
title = title_block.GetTitle()
692697
revision = title_block.GetRevision()
@@ -752,26 +757,33 @@ def parse(self):
752757
if self.config.include_nets and hasattr(self.board, "GetNetInfo"):
753758
pcbdata["nets"] = self.parse_netlist(self.board.GetNetInfo())
754759

755-
warning_shown = False
756760
if extra_field_data and need_extra_fields:
757-
e = []
758-
for f in self.footprints:
759-
e.append(extra_field_data.get(f.GetReference(), {}))
760-
if f.GetReference() not in extra_field_data:
761-
# Some components are on pcb but not in schematic data.
762-
# Show a warning about possibly outdated netlist/xml file.
763-
self.logger.warn(
764-
'Component %s is missing from schematic data.'
765-
% f.GetReference())
766-
warning_shown = True
761+
extra_fields = extra_field_data.fields_by_index
762+
if extra_fields:
763+
extra_fields = extra_fields.values()
764+
765+
if extra_fields is None:
766+
extra_fields = []
767+
field_map = extra_field_data.fields_by_ref
768+
warning_shown = False
769+
770+
for f in self.footprints:
771+
extra_fields.append(field_map.get(f.GetReference(), {}))
772+
if f.GetReference() not in field_map:
773+
# Some components are on pcb but not in schematic data.
774+
# Show a warning about outdated extra data file.
775+
self.logger.warn(
776+
'Component %s is missing from schematic data.'
777+
% f.GetReference())
778+
warning_shown = True
779+
780+
if warning_shown:
781+
self.logger.warn('Netlist/xml file is likely out of date.')
767782
else:
768-
e = [{}] * len(self.footprints)
769-
770-
if warning_shown:
771-
self.logger.warn('Netlist/xml file is likely out of date.')
783+
extra_fields = [{}] * len(self.footprints)
772784

773-
components = [self.footprint_to_component(f, ee)
774-
for (f, ee) in zip(self.footprints, e)]
785+
components = [self.footprint_to_component(f, e)
786+
for (f, e) in zip(self.footprints, extra_fields)]
775787

776788
return pcbdata, components
777789

settings_dialog.fbp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3284,7 +3284,7 @@
32843284
<property name="window_extra_style"></property>
32853285
<property name="window_name"></property>
32863286
<property name="window_style"></property>
3287-
<event name="OnCheckBox">OnNetlistFileChanged</event>
3287+
<event name="OnCheckBox">OnExtraDataFileChanged</event>
32883288
</object>
32893289
</object>
32903290
</object>

0 commit comments

Comments
 (0)