From 59045c7096856400d8947abb232003db838e4ba7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 13 Mar 2020 12:11:49 +1100 Subject: [PATCH 1/6] Add 'dnc' (do not change) as per #84 --- .gitignore | 2 +- bomlib/component.py | 51 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index b72b7d8..30e5569 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ __pycache__/ *$py.class *.pyc -*.kicad_mod +*.patch .idea/ .env diff --git a/bomlib/component.py b/bomlib/component.py index 818e2f6..8c264df 100644 --- a/bomlib/component.py +++ b/bomlib/component.py @@ -8,6 +8,7 @@ import re import sys +# String matches for marking a component as "do not fit" DNF = [ "dnf", "dnl", @@ -25,6 +26,9 @@ "no stuff", ] +# String matches for marking a component as "do not change" +DNC = ["dnc", "do not change"] + class Component(): """Class for a component, aka 'comp' in the xml netlist file. @@ -102,6 +106,10 @@ def __eq__(self, other): if self.isFitted() != other.isFitted(): return False + # 'fixed' value must be the same for both parts + if self.isFixed() != other.isFixed(): + return False + if len(self.prefs.groups) == 0: return False @@ -301,6 +309,41 @@ def isFitted(self): return include and not exclude + # Determine if a component is FIXED or not + def isFixed(self): + + check = self.getField(self.prefs.configField).lower() + + # Check the value field first + if self.getValue().lower() in DNC: + return False + + # Empty is not fixed + if check == "": + return True + + opts = check.split(" ") + for opt in opts: + if opt.lower() in DNC: + return False + + opts = check.split(",") + + result = False + + for opt in opts: + # Options that start with '-' are explicitly removed from certain configurations + if opt.startswith('-') and opt[1:].lower() == self.prefs.pcbConfig.lower(): + result = False + break + if opt.startswith("+"): + result = False + if opt[1:].lower() == self.prefs.pcbConfig.lower(): + result = True + + # by default, part is not fixed + return result + # Test if this part should be included, based on any regex expressions provided in the preferences def testRegExclude(self): @@ -475,6 +518,9 @@ def addComponent(self, c): def isFitted(self): return any([c.isFitted() for c in self.components]) + def isFixed(self): + return any([c.isFixed() for c in self.components]) + def getRefs(self): # Return a list of the components return " ".join([c.getRef() for c in self.components]) @@ -533,9 +579,10 @@ def updateFields(self, usealt=False, wrapN=None): self.fields[ColumnList.COL_REFERENCE] = self.getRefs() q = self.getCount() - self.fields[ColumnList.COL_GRP_QUANTITY] = "{n}{dnf}".format( + self.fields[ColumnList.COL_GRP_QUANTITY] = "{n}{dnf}{dnc}".format( n=q, - dnf=" (DNF)" if not self.isFitted() else "") + dnf=" (DNF)" if not self.isFitted() else "", + dnc=" (DNC)" if not self.isFixed() else "") self.fields[ColumnList.COL_GRP_BUILD_QUANTITY] = str(q * self.prefs.boards) if self.isFitted() else "0" self.fields[ColumnList.COL_VALUE] = self.components[0].getValue() From 0bd8702d2bada403631edeec13786bb61338c229 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 13 Mar 2020 12:13:34 +1100 Subject: [PATCH 2/6] Add more options for marking as 'dnc' --- bomlib/component.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bomlib/component.py b/bomlib/component.py index 8c264df..6fad80a 100644 --- a/bomlib/component.py +++ b/bomlib/component.py @@ -26,8 +26,13 @@ "no stuff", ] -# String matches for marking a component as "do not change" -DNC = ["dnc", "do not change"] +# String matches for marking a component as "do not change" or "fixed" +DNC = [ + "dnc", + "do not change", + "no change", + "fixed" +] class Component(): From 9dfa3b8b96841ffcc18423cc0a7914dfa7139b3d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 13 Mar 2020 12:14:56 +1100 Subject: [PATCH 3/6] Document new feature in README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 7eb4e0c..0d65855 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,10 @@ To specify a part as DNF (do not fit), the *fit_field* field can be set to one o * "noload" * "do not load" +**DNC Parts** + +Parts can be marked as *do not change* or *fixed* by specifying the `dnc` attribute in the *fit_field* field. + **Note:** If the *Value* field for the component contains any of these values, the component will also not be included From 0853c2d0ab8606e8bf64932d12084a8d99166f27 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 13 Mar 2020 12:15:19 +1100 Subject: [PATCH 4/6] Bump version number --- KiBOM_CLI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KiBOM_CLI.py b/KiBOM_CLI.py index 43cd746..51628b7 100755 --- a/KiBOM_CLI.py +++ b/KiBOM_CLI.py @@ -35,7 +35,7 @@ else: xlsxwriter_available = True -KIBOM_VERSION = "1.6.0" +KIBOM_VERSION = "1.6.1" here = os.path.abspath(os.path.dirname(sys.argv[0])) From 013a0db5a5b2b630089ea476f2a41eec2ed1f408 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 13 Mar 2020 12:31:43 +1100 Subject: [PATCH 5/6] Add a python script to validate the BOM outpuf file --- run-tests.sh | 3 +++ test/kibom-test.xml | 10 ++++++-- test/test_bom.py | 58 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 test/test_bom.py diff --git a/run-tests.sh b/run-tests.sh index 565cad4..268023e 100644 --- a/run-tests.sh +++ b/run-tests.sh @@ -17,6 +17,9 @@ coverage run -a KiBOM_CLI.py test/kibom-test.xml test/bom-out.xml # Generate an XLSX file coverage run -a KiBOM_CLI.py test/kibom-test.xml test/bom-out.xlsx +# Run the sanity checker on the output BOM files +coverage run -a test/test_bom.py + # Generate HTML code coverage output coverage html diff --git a/test/kibom-test.xml b/test/kibom-test.xml index dafb637..0d867f2 100644 --- a/test/kibom-test.xml +++ b/test/kibom-test.xml @@ -2,7 +2,7 @@ C:\KiCad\KiBoM\test\kibom-test.sch - 12/03/2020 11:34:46 PM + 13/03/2020 12:28:18 PM Eeschema (5.1.5)-2 @@ -20,7 +20,7 @@ - 10K + 10000 Resistor_SMD:R_0805_2012Metric ~ @@ -63,6 +63,9 @@ 4K7 Resistor_SMD:R_0805_2012Metric ~ + + DNF + 5E6A3CA0 @@ -71,6 +74,9 @@ 4700 Resistor_SMD:R_0805_2012Metric ~ + + DNC + 5E6A3F38 diff --git a/test/test_bom.py b/test/test_bom.py new file mode 100644 index 0000000..da353af --- /dev/null +++ b/test/test_bom.py @@ -0,0 +1,58 @@ +from __future__ import print_function + +import csv +import os +import sys + + +print("Checking generated BOM...") + +BOM_FILE = "bom-out_bom_A.csv" + +BOM_FILE = os.path.join(os.path.dirname(__file__), BOM_FILE) + +lines = [] + +with open(BOM_FILE, 'r') as bom_file: + reader = csv.reader(bom_file, delimiter=',') + + lines = [line for line in reader] + +# Check that the header row contains the expected information +assert 'Component' in lines[0] + +component_rows = [] + +idx = 1 + +while idx < len(lines): + row = lines[idx] + + # Break on the first 'empty' row + if len(row) == 0: + break + + component_rows.append(row) + + idx += 1 + +# We know how many component rows there should be +assert len(component_rows) == 5 + +# Create a list of components +component_refs = [] + +for row in component_rows: + refs = row[3].split(" ") + + for ref in refs: + # Ensure no component is duplicated in the BOM! + if ref in component_refs: + raise AssertionError("Component {ref} is duplicated".format(ref=ref)) + +# R6 should be excluded from the BOM (marked as DNF) +assert 'R6' not in component_refs + +print("All tests passed... OK...") + +sys.exit(0) From 634e2fb606696afbedd9f7e5f7182d49a5bf64f9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 13 Mar 2020 12:32:25 +1100 Subject: [PATCH 6/6] Run travis scripts against multiple python versions --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ced3810..b9e4929 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,11 @@ dist: xenial language: python python: + - 2.7 - 3.6 install: - - pip3 install -r test/requirements.txt + - pip install -r test/requirements.txt addons: apt: @@ -20,9 +21,6 @@ before_install: script: # Check Python code for style-guide - flake8 . - # Test that the export script works with both Python2 and Python3 - - python2 KiBOM_CLI.py test/kibom-test.xml test/out2.csv - - python3 KiBOM_CLI.py test/kibom-test.xml test/out3.csv # Run the coverage tests - bash ./run-tests.sh