From 995219a8eaeeecfa644bcf69c05d9e57e388ee6c Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Wed, 1 Dec 2021 18:07:41 -0300 Subject: [PATCH] Added option to add text to the `join` in the internal BoM Related to #104 --- CHANGELOG.md | 14 +++-- README.md | 14 ++++- docs/samples/generic_plot.kibot.yaml | 24 ++++++-- kibot/bom/bom.py | 2 +- kibot/out_bom.py | 63 ++++++++++++++++++-- tests/test_plot/test_int_bom.py | 24 ++++++++ tests/yaml_samples/int_bom_join_2.kibot.yaml | 27 +++++++++ 7 files changed, 151 insertions(+), 17 deletions(-) create mode 100644 tests/yaml_samples/int_bom_join_2.kibot.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 99e966cb..5dc42034 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `exclude_filter`. https://forum.kicad.info/t/fab-drawing-for-only-through-hole-parts/ - PCB PDF Print: mechanism to change the block title. (#102) -- Internal BoM: option to avoid merging components with empty fields. - Is named `merge_both_blank` and defaults to true. -- Internal BoM: when a `Value` field can't be interpreted as a `number+unit`, - and it contain at least one space, now we try to use the text before the - space. This helps for cases like "10K 1%". -- Internal BoM: `count_smd_tht` option to compute SMD/THT stats. (#113) +- Internal BoM: + - option to avoid merging components with empty fields. + Is named `merge_both_blank` and defaults to true. + - when a `Value` field can't be interpreted as a `number+unit`, + and it contain at least one space, now we try to use the text before the + space. This helps for cases like "10K 1%". + - `count_smd_tht` option to compute SMD/THT stats. (#113) + - option to add text to the `join` list. (#108) - Generic filter: options to match if a field is/isn't defined. - Excellon drill: added `route_mode_for_oval_holes` option. - Default global `dir` option. diff --git a/README.md b/README.md index a69a3d7d..924cf7cd 100644 --- a/README.md +++ b/README.md @@ -694,7 +694,12 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] Used as explanation for this column. The XLSX output uses it. - `field`: [string=''] Name of the field to use for this column. - - `join`: [list(string)|string=''] List of fields to join to this column. + - `join`: [list(dict)|list(string)|string=''] List of fields to join to this column. + * Valid keys: + - `field`: [string=''] Name of the field. + - `text`: [string=''] Text to use instead of a field. This option is incompatible with the `field` option. + - `text_after`: [string=''] Text to add after the field content. Will be added only if the field isn't empty. + - `text_before`: [string=''] Text to add before the field content. Will be added only if the field isn't empty. - `level`: [number=0] Used to group columns. The XLSX output uses it to collapse columns. - `name`: [string=''] Name to display in the header. The field is used when empty. - `component_aliases`: [list(list(string))] A series of values which are considered to be equivalent for the part name. @@ -712,7 +717,12 @@ Next time you need this list just use an alias, like this: * Valid keys: - `comment`: [string=''] Used as explanation for this column. The XLSX output uses it. - `field`: [string=''] Name of the field to use for this column. - - `join`: [list(string)|string=''] List of fields to join to this column. + - `join`: [list(dict)|list(string)|string=''] List of fields to join to this column. + * Valid keys: + - `field`: [string=''] Name of the field. + - `text`: [string=''] Text to use instead of a field. This option is incompatible with the `field` option. + - `text_after`: [string=''] Text to add after the field content. Will be added only if the field isn't empty. + - `text_before`: [string=''] Text to add before the field content. Will be added only if the field isn't empty. - `level`: [number=0] Used to group columns. The XLSX output uses it to collapse columns. - `name`: [string=''] Name to display in the header. The field is used when empty. - `count_smd_tht`: [boolean=false] Show the stats about how many of the components are SMD/THT. You must provide the PCB. diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index 79546061..721867ac 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -69,8 +69,16 @@ outputs: - comment: '' # [string=''] Name of the field to use for this column field: 'Row' - # [list(string)|string=''] List of fields to join to this column - join: '' + # [list(dict)|list(string)|string=''] List of fields to join to this column + join: + # [string=''] Name of the field + - field: 'Voltage' + # [string=''] Text to use instead of a field. This option is incompatible with the `field` option + text: '' + # [string=''] Text to add after the field content. Will be added only if the field isn't empty + text_after: '' + # [string=''] Text to add before the field content. Will be added only if the field isn't empty + text_before: '' # [number=0] Used to group columns. The XLSX output uses it to collapse columns level: 0 # [string=''] Name to display in the header. The field is used when empty @@ -93,8 +101,16 @@ outputs: - comment: '' # [string=''] Name of the field to use for this column field: 'Row' - # [list(string)|string=''] List of fields to join to this column - join: '' + # [list(dict)|list(string)|string=''] List of fields to join to this column + join: + # [string=''] Name of the field + - field: 'Voltage' + # [string=''] Text to use instead of a field. This option is incompatible with the `field` option + text: '' + # [string=''] Text to add after the field content. Will be added only if the field isn't empty + text_after: '' + # [string=''] Text to add before the field content. Will be added only if the field isn't empty + text_before: '' # [number=0] Used to group columns. The XLSX output uses it to collapse columns level: 0 # [string=''] Name to display in the header. The field is used when empty diff --git a/kibot/bom/bom.py b/kibot/bom/bom.py index f33e9ada..1fe1a42f 100644 --- a/kibot/bom/bom.py +++ b/kibot/bom/bom.py @@ -348,7 +348,7 @@ class ComponentGroup(object): if elements > 1 and target == key: # Append data from the other fields for source in join_l[1:]: - v = self.get_field(source) + v = source.get_text(self.get_field) if v: val = val + ' ' + v row.append(val) diff --git a/kibot/out_bom.py b/kibot/out_bom.py index 66d0ce2e..58f54f41 100644 --- a/kibot/out_bom.py +++ b/kibot/out_bom.py @@ -36,6 +36,55 @@ DEFAULT_ALIASES = [['r', 'r_small', 'res', 'resistor'], ] +class BoMJoinField(Optionable): + """ Fields to join """ + def __init__(self, field=None): + super().__init__() + if field: + self.field = field.lower() + self.text = None + self.text_before = '' + self.text_after = '' + return + self._unkown_is_error = True + with document: + self.field = '' + """ Name of the field """ + self.text = '' + """ Text to use instead of a field. This option is incompatible with the `field` option """ + self.text_before = '' + """ Text to add before the field content. Will be added only if the field isn't empty """ + self.text_after = '' + """ Text to add after the field content. Will be added only if the field isn't empty """ + self._field_example = 'Voltage' + + def config(self, parent): + super().config(parent) + if not self.field and not self.text: + raise KiPlotConfigurationError("Missing or empty `field` and `text` in join list ({})".format(str(self._tree))) + if self.field and self.text: + raise KiPlotConfigurationError("You can't specify a `field` and a `text` in a join list ({})". + format(str(self._tree))) + self.field = self.field.lower() + if self.text_before is None: + self.text_before = '' + if self.text_after is None: + self.text_after = '' + + def get_text(self, field_getter): + if self.text: + return self.text + value = field_getter(self.field) + if not value: + return None + return self.text_before + value + self.text_after + + def __repr__(self): + if self.text: + return '`{}`'.format(self.text) + return '`{}`+{}+`{}`'.format(self.text_before, self.field, self.text_after) + + class BoMColumns(Optionable): """ Information for the BoM columns """ def __init__(self): @@ -46,8 +95,8 @@ class BoMColumns(Optionable): """ Name of the field to use for this column """ self.name = '' """ Name to display in the header. The field is used when empty """ - self.join = Optionable - """ [list(string)|string=''] List of fields to join to this column """ + self.join = BoMJoinField + """ [list(dict)|list(string)|string=''] List of fields to join to this column """ self.level = 0 """ Used to group columns. The XLSX output uses it to collapse columns """ self.comment = '' @@ -66,11 +115,17 @@ class BoMColumns(Optionable): self.join = None elif isinstance(self.join, str): if self.join: - self.join = [field, self.join.lower()] + self.join = [field, BoMJoinField(self.join)] else: self.join = None else: - self.join = [field]+[c.lower() for c in self.join] + join = [field] + for c in self.join: + if isinstance(c, str): + join.append(BoMJoinField(c)) + else: + join.append(c) + self.join = join class BoMLinkable(Optionable): diff --git a/tests/test_plot/test_int_bom.py b/tests/test_plot/test_int_bom.py index db3692bb..91936ea4 100644 --- a/tests/test_plot/test_int_bom.py +++ b/tests/test_plot/test_int_bom.py @@ -638,6 +638,30 @@ def test_int_bom_join_1(test_dir): ctx.clean_up() +def test_int_bom_join_2(test_dir): + prj = 'join' + ext = 'csv' + ctx = context.TestContextSCH(test_dir, 'test_int_bom_join_2', prj, 'int_bom_join_2', BOM_DIR) + ctx.run() + out = prj + '-bom.' + ext + rows, header, info = ctx.load_csv(out) + assert header == [COMP_COLUMN_NAME, REF_COLUMN_NAME, 'Part', 'Value', 'manf', 'digikey#', QTY_COLUMN_NAME] + ref_column = header.index(REF_COLUMN_NAME) + manf_column = header.index('manf') + value_column = header.index('Value') + check_kibom_test_netlist(rows, ref_column, LINKS_GROUPS+1, [], LINKS_EXCLUDE+LINKS_COMPONENTS) + assert rows[0][ref_column] == 'C1' + assert rows[0][value_column] == '1nF 10% - (50V)' + assert rows[0][manf_column] == 'KEMET C0805C102K5RACTU' + assert rows[1][ref_column] == 'J1 J2' + assert rows[1][value_column] == 'Molex KK -' + assert rows[1][manf_column] == 'Molex 0022232021' + assert rows[2][ref_column] == 'R1' + assert rows[2][value_column] == '1k 5% -' + assert rows[2][manf_column] == 'Bourns CR0805-JW-102ELF' + ctx.clean_up() + + def test_int_include_dnf(test_dir): """ ignore_dnf: false """ prj = 'kibom-test' diff --git a/tests/yaml_samples/int_bom_join_2.kibot.yaml b/tests/yaml_samples/int_bom_join_2.kibot.yaml new file mode 100644 index 00000000..7a796ed7 --- /dev/null +++ b/tests/yaml_samples/int_bom_join_2.kibot.yaml @@ -0,0 +1,27 @@ +# Example KiBot config file +kibot: + version: 1 + +outputs: + - name: 'bom_internal' + comment: "Bill of Materials in CSV format" + type: bom + dir: BoM + options: + columns: + - Row + - References + - Part + - field: Value + join: + - 'Tolerance' + - text: '-' + - field: 'Voltage' + text_before: '(' + text_after: ')' + - field: manf + join: manf# + - field: digikey# + join: '' + - Quantity Per PCB +