diff --git a/KiBOM/bom_writer.py b/KiBOM/bom_writer.py
index cba9fb76..a23ae0a5 100644
--- a/KiBOM/bom_writer.py
+++ b/KiBOM/bom_writer.py
@@ -1,7 +1,11 @@
-import csv
+from csv_writer import WriteCSV
+from xml_writer import WriteXML
+from html_writer import WriteHTML
+
import columns
from component import *
from xml.etree import ElementTree
+from preferences import BomPref
import os, shutil
@@ -13,189 +17,59 @@ def TmpFileCopy(filename):
if os.path.exists(filename) and os.path.isfile(filename):
shutil.copyfile(filename, filename + ".tmp")
-def link(text):
- text = str(text)
- for t in ["http","https","ftp","www"]:
- if text.startswith(t):
- return '{t}'.format(t=text)
-
- return text
-
-def WriteXML(filename, groups, source, version, date, headings = columns.ColumnList._COLUMNS_ALL, ignore=[], ignoreDNF = False):
+"""
+Write BoM to file
+filename = output file path
+groups = [list of ComponentGroup groups]
+headings = [list of headings to display in the BoM file]
+prefs = BomPref object
+"""
+def WriteBoM(filename, groups, net, headings = columns.ColumnList._COLUMNS_DEFAULT, prefs=None):
filename = os.path.abspath(filename)
- if not filename.endswith(".xml"):
- return False
-
- headings = [h for h in headings if h not in ignore]
-
- try:
- TmpFileCopy(filename)
-
- xml = ElementTree.Element('"KiCAD Bom"', attrib = {
- '"source"' : source,
- '"version"' : version,
- '"date"' : date,
- '"groups"' : str(len(groups)),
- '"components"' : str(sum([group.getCount() for group in groups]))
- })
-
- for group in groups:
- row = group.getKicadRow(headings)
-
- attrib = {}
-
- for i,h in enumerate(headings):
- attrib['"' + h + '"'] = row[i]
-
- sub = ElementTree.SubElement(xml, "group", attrib=attrib)
-
- #write the document
- tree = ElementTree.ElementTree(xml)
-
- tree.write(filename)
-
- return True
-
- except BaseException as e:
- print(str(e))
- return False
-
- return True
+ #no preferences supplied, use defaults
+ if not prefs:
+ prefs = BomPref()
-def WriteHTML(filename, groups, net, headings = columns.ColumnList._COLUMNS_ALL, ignore=[], ignoreDNF=False, numberRows=True):
-
- filename = os.path.abspath(filename)
+ #remove any headings that appear in the ignore[] list
+ headings = [h for h in headings if not h.lower() in [i.lower() for i in prefs.ignore]]
- headings = [h for h in headings if not h in ignore]
+ #make a temporary copy of the output file
+ TmpFileCopy(filename)
- try:
- TmpFileCopy(filename)
-
- with open(filename,"w") as html:
-
- #header
- html.write("\n")
- html.write("
\n")
-
- #PCB info
- html.write("PCB Information
\n")
- html.write("
Source File: {source}\n".format(source=net.getSource()))
- html.write("
Date: {date}\n".format(date=net.getDate()))
- html.write("
Version: {version}\n".format(version=net.getVersion()))
- html.write("
\n")
- html.write("Component Groups
\n")
-
- #component groups
- html.write('\n')
-
- #row titles:
- html.write("\n")
- if numberRows:
- html.write("\t | \n")
- for h in headings:
- html.write("\t{h} | \n".format(h=h))
- html.write("
\n")
-
- rowCount = 0
-
- for i,group in enumerate(groups):
-
- if ignoreDNF and not group.isFitted(): continue
-
- row = group.getKicadRow(headings)
-
- rowCount += 1
-
- if numberRows:
- row = [rowCount] + row
-
- html.write("\n")
-
- for r in row:
- html.write("\t| {val} | \n".format(val=link(r)))
-
-
- html.write("
\n")
-
- html.write("
\n")
-
- html.write("")
-
-
- except BaseException as e:
- print(str(e))
- return False
-
- return True
+ #if no extension is given, assume .csv (and append!)
-
-
-
-def WriteCSV(filename, groups, source, version, date, headings = columns.ColumnList._COLUMNS_ALL, ignore=[], ignoreDNF=False, numberRows=True):
+ if len(filename.split('.')) < 2:
+ filename += ".csv"
- filename = os.path.abspath(filename)
+ ext = filename.split('.')[-1].lower()
- #delimeter is assumed from file extension
- if filename.endswith(".csv"):
- delimiter = ","
- elif filename.endswith(".tsv") or filename.endswith(".txt"):
- delimiter = "\t"
+ result = False
+
+ #CSV file writing
+ if ext in ["csv","csv","txt"]:
+ if WriteCSV(filename, groups, net, headings, prefs):
+ print("CSV Output -> {fn}".format(fn=filename))
+ result = True
+ else:
+ print("Error writing CSV output")
+
+ elif ext in ["htm","html"]:
+ if WriteHTML(filename, groups, net, headings, prefs):
+ print("HTML Output -> {fn}".format(fn=filename))
+ result = True
+ else:
+ print("Error writing HTML output")
+
+ elif ext in ["xml"]:
+ if WriteXML(filename, groups, net, headings, prefs):
+ print("XML Output -> {fn}".format(fn=filename))
+ result = True
+ else:
+ print("Error writing XML output")
+
else:
- return False
-
- headings = [h for h in headings if h not in ignore] #copy across the headings
-
- try:
- #make a copy of the file
- TmpFileCopy(filename)
-
- with open(filename, "w") as f:
-
- writer = csv.writer(f, delimiter=delimiter, lineterminator="\n")
+ print("Unsupported file extension: {ext}".format(ext=ext))
- if numberRows:
- writer.writerow(["Component"] + headings)
- else:
- writer.writerow(headings)
-
- count = 0
- rowCount = 1
-
- for i, group in enumerate(groups):
- if ignoreDNF and not group.isFitted(): continue
-
- row = group.getKicadRow(headings)
-
- if numberRows:
- row = [rowCount] + row
-
- writer.writerow(row)
-
- try:
- count += group.getCount()
- except:
- pass
-
- rowCount += 1
-
- #blank rows
- for i in range(5):
- writer.writerow([])
-
- writer.writerow(["Component Count:",componentCount])
- writer.writerow(["Component Groups:",len(groups)])
- writer.writerow(["Source:",source])
- writer.writerow(["Version:",version])
- writer.writerow(["Date:",date])
-
- return True
-
-
- except BaseException as e:
- print(str(e))
- return False
-
- return True
-
\ No newline at end of file
+ return result
\ No newline at end of file
diff --git a/KiBOM/csv_writer.py b/KiBOM/csv_writer.py
new file mode 100644
index 00000000..c2f4b82a
--- /dev/null
+++ b/KiBOM/csv_writer.py
@@ -0,0 +1,76 @@
+import csv
+import columns
+from component import *
+import os, shutil
+from preferences import BomPref
+
+"""
+Write BoM out to a CSV file
+filename = path to output file (must be a .csv, .txt or .tsv file)
+groups = [list of ComponentGroup groups]
+net = netlist object
+headings = [list of headings to display in the BoM file]
+prefs = BomPref object
+"""
+
+def WriteCSV(filename, groups, net, headings, prefs):
+
+ filename = os.path.abspath(filename)
+
+ #delimeter is assumed from file extension
+ if filename.endswith(".csv"):
+ delimiter = ","
+ elif filename.endswith(".tsv") or filename.endswith(".txt"):
+ delimiter = "\t"
+ else:
+ return False
+
+ try:
+
+ with open(filename, "w") as f:
+
+ writer = csv.writer(f, delimiter=delimiter, lineterminator="\n")
+
+ if prefs.numberRows:
+ writer.writerow(["Component"] + headings)
+ else:
+ writer.writerow(headings)
+
+ count = 0
+ rowCount = 1
+
+ for i, group in enumerate(groups):
+ if prefs.ignoreDNF and not group.isFitted(): continue
+
+ row = group.getRow(headings)
+
+ if prefs.numberRows:
+ row = [rowCount] + row
+
+ writer.writerow(row)
+
+ try:
+ count += group.getCount()
+ except:
+ pass
+
+ rowCount += 1
+
+ #blank rows
+ for i in range(5):
+ writer.writerow([])
+
+ writer.writerow(["Component Count:",sum([g.getCount() for g in groups])])
+ writer.writerow(["Component Groups:",len(groups)])
+ writer.writerow(["Source:",net.getSource()])
+ writer.writerow(["Version:",net.getVersion()])
+ writer.writerow(["Date:",net.getDate()])
+
+ return True
+
+
+ except BaseException as e:
+ print(str(e))
+ return False
+
+ return True
\ No newline at end of file
diff --git a/KiBOM/html_writer.py b/KiBOM/html_writer.py
new file mode 100644
index 00000000..b2a70225
--- /dev/null
+++ b/KiBOM/html_writer.py
@@ -0,0 +1,114 @@
+import columns
+from component import *
+import os
+
+BG_GEN = "#E6FFEE"
+BG_KICAD = "#FFE6B3"
+BG_USER = "#E6F9FF"
+
+#return a background color for a given column title
+def bgColor(col):
+ #auto-generated columns
+ if col == ColumnList.COL_GRP_QUANTITY:
+ return BG_GEN
+ #kicad protected columns
+ elif col in ColumnList._COLUMNS_PROTECTED:
+ return BG_KICAD
+ #additional user columns
+ else:
+ return BG_USER
+
+def link(text):
+ text = str(text)
+ for t in ["http","https","ftp","www"]:
+ if text.startswith(t):
+ return '{t}'.format(t=text)
+
+ return text
+
+"""
+Write BoM out to a HTML file
+filename = path to output file (must be a .htm or .html file)
+groups = [list of ComponentGroup groups]
+net = netlist object
+headings = [list of headings to display in the BoM file]
+prefs = BomPref object
+"""
+
+def WriteHTML(filename, groups, net, headings, prefs):
+
+ if not filename.endswith(".html") and not filename.endswith(".htm"):
+ print("{fn} is not a valid html file".format(fn=filename))
+ return False
+
+ try:
+
+ with open(filename,"w") as html:
+
+ #header
+ html.write("\n")
+ html.write("\n")
+
+ #PCB info
+ html.write("KiBOM PCB Bill of Materials
\n")
+ html.write("
Source File: {source}\n".format(source=net.getSource()))
+ html.write("
Date: {date}\n".format(date=net.getDate()))
+ html.write("
Schematic Version: {version}\n".format(version=net.getVersion()))
+ html.write("
Total Components: {n}\n".format(n = sum([g.getCount() for g in groups])))
+ html.write("
Component Groups: {n}\n".format(n=len(groups)))
+ html.write("
\n")
+ html.write("Component Groups
\n")
+ html.write('Kicad Fields (default)
\n'.format(bg=BG_KICAD))
+ html.write('Generated Fields
\n'.format(bg=BG_GEN))
+ html.write('User Fields
\n'.format(bg=BG_USER))
+
+ #component groups
+ html.write('\n')
+
+ #row titles:
+ html.write("\n")
+ if prefs.numberRows:
+ html.write("\t | \n")
+ for i,h in enumerate(headings):
+ #cell background color
+ bg = bgColor(h)
+ html.write('\t{h} | \n'.format(
+ h=h,
+ bg = ' bgcolor="{c}"'.format(c=bg) if bg else ''))
+ html.write("
\n")
+
+ rowCount = 0
+
+ for i,group in enumerate(groups):
+
+ if prefs.ignoreDNF and not group.isFitted(): continue
+
+ row = group.getRow(headings)
+
+ rowCount += 1
+
+
+ html.write("\n")
+
+ if prefs.numberRows:
+ html.write("\t| {n} | \n".format(n=rowCount))
+
+ for n, r in enumerate(row):
+ bg = bgColor(headings[n])
+
+ html.write('\t{val} | \n'.format(bg=' bgcolor={c}'.format(c=bg) if bg else '', val=link(r)))
+
+
+ html.write("
\n")
+
+ html.write("
\n")
+ html.write("
\n")
+
+ html.write("")
+
+
+ except BaseException as e:
+ print(str(e))
+ return False
+
+ return True
\ No newline at end of file
diff --git a/KiBOM/xml_writer.py b/KiBOM/xml_writer.py
new file mode 100644
index 00000000..1333ba37
--- /dev/null
+++ b/KiBOM/xml_writer.py
@@ -0,0 +1,58 @@
+import columns
+from component import *
+from xml.etree import ElementTree
+from xml.dom import minidom
+from preferences import BomPref
+
+"""
+Write BoM out to an XML file
+filename = path to output file (must be a .xml)
+groups = [list of ComponentGroup groups]
+net = netlist object
+headings = [list of headings to display in the BoM file]
+prefs = BomPref object
+"""
+
+def WriteXML(filename, groups, net, headings, prefs):
+
+ if not filename.endswith(".xml"):
+ return False
+
+ try:
+ xml = ElementTree.Element('KiCAD_BOM', attrib = {
+ 'source' : net.getSource(),
+ 'version' : net.getVersion(),
+ 'date' : net.getDate(),
+ 'groups' : str(len(groups)),
+ 'components' : str(sum([group.getCount() for group in groups]))
+ })
+
+ for group in groups:
+ if prefs.ignoreDNF and not group.isFitted():
+ continue
+ row = group.getRow(headings)
+
+ attrib = {}
+
+ for i,h in enumerate(headings):
+ h = h.replace(' ','_') #replace spaces, xml no likey
+ h = h.replace('"','')
+ h = h.replace("'",'')
+
+ attrib[h] = row[i]
+
+ sub = ElementTree.SubElement(xml, "group", attrib=attrib)
+
+ with open(filename,"w") as output:
+ out = ElementTree.tostring(xml, 'utf-8')
+
+ output.write(minidom.parseString(out).toprettyxml(indent="\t"))
+
+ return True
+
+ except BaseException as e:
+ print(str(e))
+ return False
+
+ return True
+