Moved some repeated code in BoM writers.

All the writers computed some stats. Now they are computed in one place
avoiding repetition.
This commit is contained in:
Salvador E. Tropea 2020-08-01 16:55:33 -03:00
parent 670e379f65
commit 64576e3975
7 changed files with 42 additions and 50 deletions

View File

@ -32,6 +32,11 @@ def write_bom(filename, ext, groups, headings, cfg):
# Allow renaming the columns # Allow renaming the columns
head_names = [h if h.lower() not in cfg.column_rename else cfg.column_rename[h.lower()] for h in headings] head_names = [h if h.lower() not in cfg.column_rename else cfg.column_rename[h.lower()] for h in headings]
result = False result = False
# Some stats
cfg.n_groups = len(groups)
cfg.n_total = sum([g.get_count() for g in groups])
cfg.n_fitted = sum([g.get_count() for g in groups if g.is_fitted()])
cfg.n_build = cfg.n_fitted * cfg.number
# CSV file writing # CSV file writing
if ext in ["csv", "tsv", "txt"]: if ext in ["csv", "tsv", "txt"]:
result = write_csv(filename, ext, groups, headings, head_names, cfg) result = write_csv(filename, ext, groups, headings, head_names, cfg)

View File

@ -27,11 +27,6 @@ def write_csv(filename, ext, groups, headings, head_names, cfg):
elif ext == "tsv" or ext == "txt": elif ext == "tsv" or ext == "txt":
delimiter = "\t" delimiter = "\t"
n_groups = len(groups)
n_total = sum([g.get_count() for g in groups])
n_fitted = sum([g.get_count() for g in groups if g.is_fitted()])
n_build = n_fitted * cfg.number
with open(filename, "wt") as f: with open(filename, "wt") as f:
writer = csv.writer(f, delimiter=delimiter, lineterminator="\n") writer = csv.writer(f, delimiter=delimiter, lineterminator="\n")
# Headers # Headers
@ -63,11 +58,11 @@ def write_csv(filename, ext, groups, headings, head_names, cfg):
for i in range(5): for i in range(5):
writer.writerow([]) writer.writerow([])
writer.writerow(["Component Groups:", n_groups]) writer.writerow(["Component Groups:", cfg.n_groups])
writer.writerow(["Component Count:", n_total]) writer.writerow(["Component Count:", cfg.n_total])
writer.writerow(["Fitted Components:", n_fitted]) writer.writerow(["Fitted Components:", cfg.n_fitted])
writer.writerow(["Number of PCBs:", cfg.number]) writer.writerow(["Number of PCBs:", cfg.number])
writer.writerow(["Total components:", n_build]) writer.writerow(["Total components:", cfg.n_build])
writer.writerow(["Schematic Revision:", cfg.revision]) writer.writerow(["Schematic Revision:", cfg.revision])
writer.writerow(["Schematic Date:", cfg.date]) writer.writerow(["Schematic Date:", cfg.date])
writer.writerow(["PCB Variant:", ' + '.join(cfg.variant)]) writer.writerow(["PCB Variant:", ' + '.join(cfg.variant)])

View File

@ -84,11 +84,6 @@ def write_html(filename, groups, headings, head_names, cfg):
head_names = [list of headings to display in the BoM file] head_names = [list of headings to display in the BoM file]
prefs = BomPref object prefs = BomPref object
""" """
n_groups = len(groups)
n_total = sum([g.get_count() for g in groups])
n_fitted = sum([g.get_count() for g in groups if g.is_fitted()])
n_build = n_fitted * cfg.number
link_datasheet = cfg.datasheet_as_link link_datasheet = cfg.datasheet_as_link
link_digikey = None link_digikey = None
if cfg.digikey_link: if cfg.digikey_link:
@ -107,18 +102,18 @@ def write_html(filename, groups, headings, head_names, cfg):
html.write("<h2>KiBoM PCB Bill of Materials</h2>\n") html.write("<h2>KiBoM PCB Bill of Materials</h2>\n")
if not cfg.hide_pcb_info: if not cfg.hide_pcb_info:
html.write('<table border="1">\n') html.write('<table border="1">\n')
html.write("<tr><td>Source File</td><td>{source}</td></tr>\n".format(source=cfg.source)) html.write("<tr><td>Source File</td><td>{}</td></tr>\n".format(cfg.source))
# html.write("<tr><td>BoM Date</td><td>{date}</td></tr>\n".format(date=cfg.date)) Same as schematic # html.write("<tr><td>BoM Date</td><td>{date}</td></tr>\n".format(date=cfg.date)) Same as schematic
html.write("<tr><td>Schematic Revision</td><td>{version}</td></tr>\n".format(version=cfg.revision)) html.write("<tr><td>Schematic Revision</td><td>{}</td></tr>\n".format(cfg.revision))
html.write("<tr><td>Schematic Date</td><td>{date}</td></tr>\n".format(date=cfg.date)) html.write("<tr><td>Schematic Date</td><td>{}</td></tr>\n".format(cfg.date))
html.write("<tr><td>PCB Variant</td><td>{variant}</td></tr>\n".format(variant=', '.join(cfg.variant))) html.write("<tr><td>PCB Variant</td><td>{}</td></tr>\n".format(', '.join(cfg.variant)))
# html.write("<tr><td>KiCad Version</td><td>{version}</td></tr>\n".format(version=net.getTool())) TODO? # html.write("<tr><td>KiCad Version</td><td>{version}</td></tr>\n".format(version=net.getTool())) TODO?
html.write("<tr><td>Component Groups</td><td>{n}</td></tr>\n".format(n=n_groups)) html.write("<tr><td>Component Groups</td><td>{}</td></tr>\n".format(cfg.n_groups))
html.write("<tr><td>Component Count (per PCB)</td><td>{n}</td></tr>\n".format(n=n_total)) html.write("<tr><td>Component Count (per PCB)</td><td>{}</td></tr>\n".format(cfg.n_total))
html.write("<tr><td>Fitted Components (per PCB)</td><td>{n}</td></tr>\n".format(n=n_fitted)) html.write("<tr><td>Fitted Components (per PCB)</td><td>{}</td></tr>\n".format(cfg.n_fitted))
html.write("<tr><td>Number of PCBs</td><td>{n}</td></tr>\n".format(n=cfg.number)) html.write("<tr><td>Number of PCBs</td><td>{}</td></tr>\n".format(cfg.number))
html.write("<tr><td>Total Component Count<br>(for {n} PCBs)</td><td>{t}</td></tr>\n". html.write("<tr><td>Total Component Count<br>(for {n} PCBs)</td><td>{t}</td></tr>\n".
format(n=cfg.number, t=n_build)) format(n=cfg.number, t=cfg.n_build))
html.write("</table>\n") html.write("</table>\n")
html.write("<br>\n") html.write("<br>\n")

View File

@ -65,11 +65,6 @@ def write_xlsx(filename, groups, col_fields, head_names, cfg):
logger.error('Python xlsxwriter module not installed (Debian: python3-xlsxwriter)') logger.error('Python xlsxwriter module not installed (Debian: python3-xlsxwriter)')
return False return False
n_groups = len(groups)
n_total = sum([g.get_count() for g in groups])
n_fitted = sum([g.get_count() for g in groups if g.is_fitted()])
n_build = n_fitted * cfg.number
workbook = Workbook(filename) workbook = Workbook(filename)
worksheet = workbook.add_worksheet() worksheet = workbook.add_worksheet()
@ -136,11 +131,11 @@ def write_xlsx(filename, groups, col_fields, head_names, cfg):
title_fmt.set_bold() title_fmt.set_bold()
formats = [title_fmt, cellformat_left] formats = [title_fmt, cellformat_left]
row_count = add_info(worksheet, column_widths, row_count, formats, "Component Groups:", n_groups) row_count = add_info(worksheet, column_widths, row_count, formats, "Component Groups:", cfg.n_groups)
row_count = add_info(worksheet, column_widths, row_count, formats, "Component Count:", n_total) row_count = add_info(worksheet, column_widths, row_count, formats, "Component Count:", cfg.n_total)
row_count = add_info(worksheet, column_widths, row_count, formats, "Fitted Components:", n_fitted) row_count = add_info(worksheet, column_widths, row_count, formats, "Fitted Components:", cfg.n_fitted)
row_count = add_info(worksheet, column_widths, row_count, formats, "Number of PCBs:", cfg.number) row_count = add_info(worksheet, column_widths, row_count, formats, "Number of PCBs:", cfg.number)
row_count = add_info(worksheet, column_widths, row_count, formats, "Total components:", n_build) row_count = add_info(worksheet, column_widths, row_count, formats, "Total components:", cfg.n_build)
row_count = add_info(worksheet, column_widths, row_count, formats, "Schematic Revision:", cfg.revision) row_count = add_info(worksheet, column_widths, row_count, formats, "Schematic Revision:", cfg.revision)
row_count = add_info(worksheet, column_widths, row_count, formats, "Schematic Date:", cfg.date) row_count = add_info(worksheet, column_widths, row_count, formats, "Schematic Date:", cfg.date)
# row_count = add_info(worksheet, column_widths, row_count, formats, "BoM Date:", cfg.date) Same as sch # row_count = add_info(worksheet, column_widths, row_count, formats, "BoM Date:", cfg.date) Same as sch

View File

@ -17,11 +17,6 @@ def write_xml(filename, groups, headings, head_names, cfg):
headings = [list of headings to display in the BoM file] headings = [list of headings to display in the BoM file]
cfg = BomPref object cfg = BomPref object
""" """
n_groups = len(groups)
n_total = sum([g.get_count() for g in groups])
n_fitted = sum([g.get_count() for g in groups if g.is_fitted()])
n_build = n_fitted * cfg.number
attrib = {} attrib = {}
attrib['Schematic_Source'] = cfg.source attrib['Schematic_Source'] = cfg.source
attrib['Schematic_Revision'] = cfg.revision attrib['Schematic_Revision'] = cfg.revision
@ -29,11 +24,11 @@ def write_xml(filename, groups, headings, head_names, cfg):
attrib['PCB_Variant'] = ', '.join(cfg.variant) attrib['PCB_Variant'] = ', '.join(cfg.variant)
# attrib['BOM_Date'] = net.getDate() same as schematic # attrib['BOM_Date'] = net.getDate() same as schematic
# attrib['KiCad_Version'] = net.getTool() TODO? # attrib['KiCad_Version'] = net.getTool() TODO?
attrib['Component_Groups'] = str(n_groups) attrib['Component_Groups'] = str(cfg.n_groups)
attrib['Component_Count'] = str(n_total) attrib['Component_Count'] = str(cfg.n_total)
attrib['Fitted_Components'] = str(n_fitted) attrib['Fitted_Components'] = str(cfg.n_fitted)
attrib['Number_of_PCBs'] = str(cfg.number) attrib['Number_of_PCBs'] = str(cfg.number)
attrib['Total_Components'] = str(n_build) attrib['Total_Components'] = str(cfg.n_build)
xml = ElementTree.Element('KiCad_BOM', attrib=attrib, encoding='utf-8') xml = ElementTree.Element('KiCad_BOM', attrib=attrib, encoding='utf-8')
for group in groups: for group in groups:

View File

@ -1,5 +1,9 @@
""" """
KiCad configuration classes. KiCad configuration classes.
Notes about coverage:
I'm excluding all the Darwin and Windows code from coverage.
I don't know how to test it on GitHub CI/CD.
""" """
import os import os
import re import re
@ -14,7 +18,8 @@ from .. import log
# Check python version to determine which version of ConfirParser to import # Check python version to determine which version of ConfirParser to import
if sys.version_info.major >= 3: if sys.version_info.major >= 3:
import configparser as ConfigParser import configparser as ConfigParser
else: else: # pragma: no cover
# For future Python 2 support
import ConfigParser import ConfigParser
logger = log.get_logger(__name__) logger = log.get_logger(__name__)
@ -71,10 +76,10 @@ class LibAlias(object):
lib.descr = un_quote(m.group(5)) lib.descr = un_quote(m.group(5))
return lib return lib
def __str__(self): # def __str__(self):
if not self.name: # if not self.name:
return 'empty LibAlias' # return 'empty LibAlias'
return self.name+' -> `'+self.uri+'`' # return self.name+' -> `'+self.uri+'`'
class KiConf(object): class KiConf(object):
@ -125,7 +130,7 @@ class KiConf(object):
cfg = os.path.join(home, '.config', 'kicad', KICAD_COMMON) cfg = os.path.join(home, '.config', 'kicad', KICAD_COMMON)
if os.path.isfile(cfg): if os.path.isfile(cfg):
return cfg return cfg
elif system == 'Darwin': elif system == 'Darwin': # pragma: no cover
# MacOSX: ~/Library/Preferences/kicad/ # MacOSX: ~/Library/Preferences/kicad/
home = os.environ.get('HOME') home = os.environ.get('HOME')
if not home: if not home:
@ -134,7 +139,7 @@ class KiConf(object):
cfg = os.path.join(home, 'Library', 'Preferences', 'kicad', KICAD_COMMON) cfg = os.path.join(home, 'Library', 'Preferences', 'kicad', KICAD_COMMON)
if os.path.isfile(cfg): if os.path.isfile(cfg):
return cfg return cfg
elif system == 'Windows': elif system == 'Windows': # pragma: no cover
# Windows: C:\Users\username\AppData\Roaming\kicad # Windows: C:\Users\username\AppData\Roaming\kicad
# or C:\Documents and Settings\username\Application Data\kicad # or C:\Documents and Settings\username\Application Data\kicad
username = os.environ.get('username') username = os.environ.get('username')
@ -173,7 +178,7 @@ class KiConf(object):
dir = os.path.join(sysconfig.get_path('data', 'posix_prefix'), share) dir = os.path.join(sysconfig.get_path('data', 'posix_prefix'), share)
if os.path.isdir(dir): if os.path.isdir(dir):
return dir return dir
elif system == 'Darwin': elif system == 'Darwin': # pragma: no cover
app_data = os.path.join('Library', 'Application Support', 'kicad', 'library') app_data = os.path.join('Library', 'Application Support', 'kicad', 'library')
home = os.environ.get('HOME') home = os.environ.get('HOME')
if home: if home:
@ -183,7 +188,7 @@ class KiConf(object):
dir = os.path.join('/', app_data) dir = os.path.join('/', app_data)
if os.path.isdir(dir): if os.path.isdir(dir):
return dir return dir
elif system == 'Windows': elif system == 'Windows': # pragma: no cover
dir = os.path.join('C:', 'Program Files', 'KiCad', share) dir = os.path.join('C:', 'Program Files', 'KiCad', share)
if os.path.isdir(dir): if os.path.isdir(dir):
return dir return dir

View File

@ -262,6 +262,8 @@ class BoM(BaseOutput): # noqa: F821
Used to generate the BoM in CSV, HTML, TSV, TXT, XML or XLSX format using the internal BoM. Used to generate the BoM in CSV, HTML, TSV, TXT, XML or XLSX format using the internal BoM.
Is compatible with KiBoM, but doesn't need to update the XML netlist because the components Is compatible with KiBoM, but doesn't need to update the XML netlist because the components
are loaded from the schematic. are loaded from the schematic.
Important differences with KiBoM output:
- All options are in the main `options` section, not in `conf` subsection.
This output is what you get from the 'Tools/Generate Bill of Materials' menu in eeschema. """ This output is what you get from the 'Tools/Generate Bill of Materials' menu in eeschema. """
def __init__(self): def __init__(self):
super().__init__() super().__init__()