#!/usr/bin/env python

# NOTE: This file is based on one from the KiCad source:
# scripts/bom-in-python/bom_csv_grouped_by_value.py
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#
# Example python script to generate a BOM from a KiCad generic netlist
#
# Example: Sorted and Grouped CSV BOM
#
"""
    @package
    Generate a csv BOM list.
    Components are sorted by ref and grouped by value
    Fields are (if exist)
    Item, Qty, Reference(s), Value, LibPart, Footprint, Datasheet
"""

from __future__ import print_function

# Import the KiCad python helper module and the csv formatter
import argparse

import kicad_netlist_reader
import csv
import sys

PART_NUMBER_FIELD = 'DK#'

def part_number_eq(self, other):
    if self.getField(PART_NUMBER_FIELD):
        return self.getField(PART_NUMBER_FIELD) == other.getField(PART_NUMBER_FIELD)
    return (
        self.getValue() == other.getValue() and
        self.getPartName() == other.getPartName() and
        self.getFootprint() == other.getFootprint()
    )

# Override the component equivalence operator - it is important to do this
# before loading the netlist, otherwise all components will have the original
# equivalency operator.
kicad_netlist_reader.comp.__eq__ = part_number_eq


parser = argparse.ArgumentParser()
parser.add_argument('output')
parser.add_argument('netlist', nargs='+')
args = parser.parse_args()

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

dates = []
tools = []
components = []
columnset = set()

for netlist_file in args.netlist:
    # 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
    net = kicad_netlist_reader.netlist(netlist_file)

    dates.append(net.getDate())
    tools.append(net.getTool())

    # subset the components to those wanted in the BOM, controlled
    # by <configure> block in kicad_netlist_reader.py
    components += net.getInterestingComponents()

    compfields = kicad_netlist_reader.netlist.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
    columnset |= partfields


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

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

# override csv.writer's writerow() to support encoding conversion (initial encoding is utf8):
def writerow( acsvwriter, columns ):
    utf8row = []
    for col in columns:
        utf8row.append( str(col) )  # currently, no change
    acsvwriter.writerow( utf8row )

# Output a set of rows as a header providing general information
writerow( out, ['Source:', '; '.join(args.netlist)] )
writerow( out, ['Date:', '; '.join(dates)] )
writerow( out, ['Tool:', '; '.join(tools)] )
writerow( out, ['Generator:', sys.argv[0]] )
writerow( out, ['Component Count:', len(components)] )
writerow( out, [] )
writerow( out, columns )

# Get all of the components in groups of matching parts + values
# (see kicad_netlist_reader.py)
grouped = kicad_netlist_reader.netlist.groupComponents(components)


# Output component information organized by group, aka as collated:
item = 0
for group in grouped:
    row = []
    refs = []
    values = set()

    # Add the reference of every component in the group and keep a reference
    # to the component so that the other data can be filled in once per group
    for component in group:
        refs.append(component.getRef())
        values.add(component.getValue())
        c = component

    # Fill in the component groups common data
    # columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))
    item += 1
    row.append(item)
    row.append(len(group))
    row.append(', '.join(refs))
    row.append(', '.join(sorted(list(values))))
    row.append(c.getLibName() + ":" + c.getPartName())
    row.append(kicad_netlist_reader.netlist.getGroupFootprint(group))

    for field in custom_fields:
        row.append(kicad_netlist_reader.netlist.getGroupField(group, field))

    writerow( out, row  )

f.close()

#!/usr/bin/env python

# NOTE: This file is based on one from the KiCad source:
# scripts/bom-in-python/bom_csv_grouped_by_value.py
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#
# Example python script to generate a BOM from a KiCad generic netlist
#
# Example: Sorted and Grouped CSV BOM
#
"""
    @package
    Generate a csv BOM list.
    Components are sorted by ref and grouped by value
    Fields are (if exist)
    Item, Qty, Reference(s), Value, LibPart, Footprint, Datasheet
"""

from __future__ import print_function

# Import the KiCad python helper module and the csv formatter
import argparse

import kicad_netlist_reader
import csv
import sys

PART_NUMBER_FIELD = 'DK#'

def part_number_eq(self, other):
    if self.getField(PART_NUMBER_FIELD):
        return self.getField(PART_NUMBER_FIELD) == other.getField(PART_NUMBER_FIELD)
    return (
        self.getValue() == other.getValue() and
        self.getPartName() == other.getPartName() and
        self.getFootprint() == other.getFootprint()
    )

# Override the component equivalence operator - it is important to do this
# before loading the netlist, otherwise all components will have the original
# equivalency operator.
kicad_netlist_reader.comp.__eq__ = part_number_eq


parser = argparse.ArgumentParser()
parser.add_argument('output')
parser.add_argument('netlist', nargs='+')
args = parser.parse_args()

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

dates = []
tools = []
components = []
columnset = set()

for netlist_file in args.netlist:
    # 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
    net = kicad_netlist_reader.netlist(netlist_file)

    dates.append(net.getDate())
    tools.append(net.getTool())

    # subset the components to those wanted in the BOM, controlled
    # by <configure> block in kicad_netlist_reader.py
    components += net.getInterestingComponents()

    compfields = kicad_netlist_reader.netlist.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
    columnset |= partfields


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

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

# override csv.writer's writerow() to support encoding conversion (initial encoding is utf8):
def writerow( acsvwriter, columns ):
    utf8row = []
    for col in columns:
        utf8row.append( str(col) )  # currently, no change
    acsvwriter.writerow( utf8row )

# Output a set of rows as a header providing general information
writerow( out, ['Source:', '; '.join(args.netlist)] )
writerow( out, ['Date:', '; '.join(dates)] )
writerow( out, ['Tool:', '; '.join(tools)] )
writerow( out, ['Generator:', sys.argv[0]] )
writerow( out, ['Component Count:', len(components)] )
writerow( out, [] )
writerow( out, columns )

# Get all of the components in groups of matching parts + values
# (see kicad_netlist_reader.py)
grouped = kicad_netlist_reader.netlist.groupComponents(components)


# Output component information organized by group, aka as collated:
item = 0
for group in grouped:
    row = []
    refs = []
    values = set()

    # Add the reference of every component in the group and keep a reference
    # to the component so that the other data can be filled in once per group
    for component in group:
        refs.append(component.getRef())
        values.add(component.getValue())
        c = component

    # Fill in the component groups common data
    # columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))
    item += 1
    row.append(item)
    row.append(len(group))
    row.append(', '.join(refs))
    row.append(', '.join(sorted(list(values))))
    row.append(c.getLibName() + ":" + c.getPartName())
    row.append(kicad_netlist_reader.netlist.getGroupFootprint(group))

    for field in custom_fields:
        row.append(kicad_netlist_reader.netlist.getGroupField(group, field))

    writerow( out, row  )

f.close()

#!/usr/bin/env python # NOTE: This file is based on one from the KiCad source: # scripts/bom-in-python/bom_csv_grouped_by_value.py # #    This program is free software: you can redistribute it and/or modify #    it under the terms of the GNU General Public License as published by #    the Free Software Foundation, either version 3 of the License, or #    (at your option) any later version. # #    This program is distributed in the hope that it will be useful, #    but WITHOUT ANY WARRANTY; without even the implied warranty of #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the #    GNU General Public License for more details. # #    You should have received a copy of the GNU General Public License #    along with this program.  If not, see <http://www.gnu.org/licenses/>. # # Example python script to generate a BOM from a KiCad generic netlist # # Example: Sorted and Grouped CSV BOM # @package     Generate a csv BOM list.     Components are sorted by ref and grouped by value     Fields are (if exist)     Item, Qty, Reference(s), Value, LibPart, Footprint, Datasheet # Import the KiCad python helper module and the csv formatter #' # Override the component equivalence operator - it is important to do this # before loading the netlist, otherwise all components will have the original # equivalency operator. # Open a file to write to, if the file cannot be opened output to stdout # instead # 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 # subset the components to those wanted in the BOM, controlled # by <configure> block in kicad_netlist_reader.py # remove Reference, Value, Datasheet, and Footprint, they will come from 'columns' below # prepend an initial 'hard coded' list and put the enchillada into list 'columns' # Create a new csv writer object to use as the output formatter # override csv.writer's writerow() to support encoding conversion (initial encoding is utf8): # currently, no change # Output a set of rows as a header providing general information # Get all of the components in groups of matching parts + values # (see kicad_netlist_reader.py) # Output component information organized by group, aka as collated: # Add the reference of every component in the group and keep a reference # to the component so that the other data can be filled in once per group # Fill in the component groups common data # columns = ['Item', 'Qty', 'Reference(s)', 'Value', 'LibPart', 'Footprint', 'Datasheet'] + sorted(list(columnset))