bom_csv_grouped_by_value.py 4.48 KB
Newer Older
1 2 3 4 5 6
#
# Example python script to generate a BOM from a KiCad generic netlist
#
# Example: Sorted and Grouped CSV BOM
#

7 8
from __future__ import print_function

9
# Import the KiCad python helper module and the csv formatter
10
import kicad_netlist_reader
11 12 13
import csv
import sys

14 15 16 17 18 19

if len(sys.argv) != 3:
    print("Usage ", __file__, "<generic_netlist.xml> <output.csv>", file=sys.stderr)
    sys.exit(1)


20 21
# Generate an instance of a generic netlist, and load the netlist tree from
# the command line option. If the file doesn't exist, execution will stop
22
net = kicad_netlist_reader.netlist(sys.argv[1])
23 24 25 26 27 28

# Open a file to write to, if the file cannot be opened output to stdout
# instead
try:
    f = open(sys.argv[2], 'w')
except IOError:
29
    e = "Can't open output file for writing: " + sys.argv[2]
30
    print(__file__, ":", e, file=sys.stderr)
31
    f = sys.stdout
32

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
# subset the components to those wanted in the BOM, controlled
# by <configure> block in kicad_netlist_reader.py
components = net.getInterestingComponents()

compfields = net.gatherComponentFieldUnion(components)
partfields = net.gatherLibPartFieldUnion()

# remove Reference, Value, Datasheet, and Footprint, they will come from 'columns' below
partfields -= set( ['Reference', 'Value', 'Datasheet', 'Footprint'] )

columnset = compfields | partfields     # union

# prepend an initial 'hard coded' list and put the enchillada into list 'columns'
columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))

48
# Create a new csv writer object to use as the output formatter
49
out = csv.writer(f, lineterminator='\n', delimiter=',', quotechar='\"', quoting=csv.QUOTE_MINIMAL)
50

51 52 53 54 55 56 57
# override csv.writer's writerow() to support utf8 encoding:
def writerow( acsvwriter, columns ):
    utf8row = []
    for col in columns:
        utf8row.append( str(col).encode('utf8') )
    acsvwriter.writerow( utf8row )

58
# Output a set of rows as a header providing general information
59 60 61 62 63 64 65 66
writerow( out, ['Source:', net.getSource()] )
writerow( out, ['Date:', net.getDate()] )
writerow( out, ['Tool:', net.getTool()] )
writerow( out, ['Component Count:', len(components)] )
writerow( out, [] )
writerow( out, ['Individual Components:'] )
writerow( out, [] )                        # blank line
writerow( out, columns )
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

# Output all the interesting components individually first:
row = []
for c in components:
    del row[:]
    row.append('')                                      # item is blank in individual table
    row.append('')                                      # Qty is always 1, why print it
    row.append( c.getRef() )                            # Reference
    row.append( c.getValue() )                          # Value
    row.append( c.getLibName() + ":" + c.getPartName() ) # LibPart
    #row.append( c.getDescription() )
    row.append( c.getFootprint() )
    row.append( c.getDatasheet() )

    # from column 7 upwards, use the fieldnames to grab the data
    for field in columns[7:]:
        row.append( c.getField( field ) );

85
    writerow( out, row )
86 87


88 89 90
writerow( out, [] )                        # blank line
writerow( out, [] )                        # blank line
writerow( out, [] )                        # blank line
91

92 93 94
writerow( out, ['Collated Components:'] )
writerow( out, [] )                        # blank line
writerow( out, columns )                   # reuse same columns
95 96


97

98
# Get all of the components in groups of matching parts + values
99 100
# (see kicad_netlist_reader.py)
grouped = net.groupComponents(components)
101

102 103 104

# Output component information organized by group, aka as collated:
item = 0
105
for group in grouped:
106
    del row[:]
107 108
    refs = ""

109
    # Add the reference of every component in the group and keep a reference
110 111
    # to the component so that the other data can be filled in once per group
    for component in group:
112 113 114
        if len(refs) > 0:
            refs += ", "
        refs += component.getRef()
115 116 117
        c = component

    # Fill in the component groups common data
118 119 120 121 122 123 124 125 126 127 128 129 130
    # columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))
    item += 1
    row.append( item )
    row.append( len(group) )
    row.append( refs );
    row.append( c.getValue() )
    row.append( c.getLibName() + ":" + c.getPartName() )
    row.append( net.getGroupFootprint(group) )
    row.append( net.getGroupDatasheet(group) )

    # from column 7 upwards, use the fieldnames to grab the data
    for field in columns[7:]:
        row.append( net.getGroupField(group, field) );
131

132
    writerow( out,  row  )
133

134
f.close()