Skip to content

Instantly share code, notes, and snippets.

@eflukx
Last active September 2, 2023 13:10
Show Gist options
  • Save eflukx/0c202fe245f27f34ed2e8718fbf8293c to your computer and use it in GitHub Desktop.
Save eflukx/0c202fe245f27f34ed2e8718fbf8293c to your computer and use it in GitHub Desktop.
Small script to parse the .xml BOM files exported by KiCad. This allows for easy handling and transforming in a (production) pipeline using Ruby
require 'pry'
require 'json'
require 'nokogiri'
require 'yaml'
require 'hashie'
class GroupedComponents
@@DEFAULT_FIELDS = %w(refs cnt value footprint)
def self.csv_header *properties, separator: ','
line = @@DEFAULT_FIELDS + properties
separator ? line.join(separator) : line
end
attr_reader :refs, :cnt, :value, :footprint, :properties
def self.from_components components, group_by: [:ref_des, :footprint, :value]
grouped = components.group_by { |c| group_by.map { |gb| c.send(gb) } }
grouped.values.map do |group|
new(group)
end
end
def initialize group
@refs = group.map(&:ref)
ref_component = group.first # use the first component in the group tol fill all other fields/properties
@value = ref_component.value
@footprint = ref_component.footprint
@properties = ref_component.properties
end
def cnt
@refs.count
end
def csv_line *fields, separator: ','
line = default_field_values + (properties.fetch_values(*fields) { '' })
separator ? line.join(separator) : line
end
def default_field_values
@@DEFAULT_FIELDS.map do |fieldname|
if fieldname == 'refs'
send(fieldname).join(' ')
else
send(fieldname) rescue ''
end
end
end
end
class Component
@@DEFAULT_FIELDS = %w(ref ref_des ref_num value footprint)
def self.from_xml xml_component
new ComponentHelper.component_to_hash(xml_component)
end
def self.csv_header *properties, separator: ','
line = @@DEFAULT_FIELDS + properties
separator ? line.join(separator) : line
end
def method_missing(symbol, *args)
@properties.send(symbol, *args) || super
end
attr_reader :ref, :ref_des, :ref_num, :value, :footprint, :properties
def initialize h
@value, @footprint = h.fetch_values :value, :footprint
@ref, @ref_des, @ref_num = h.fetch(:ref, '').match(/(\D*)(\d*)/).to_a
@properties = Hashie::Mash.new h.fetch(:properties, {})
end
def csv_line *fields, separator: ','
line = default_field_values + (properties.fetch_values(*fields) { '' })
separator ? line.join(separator) : line
end
def default_field_values
@@DEFAULT_FIELDS.map { |fieldname| send(fieldname) rescue '' }
end
end
module ComponentHelper
extend self
def get_components doc
get_xml_components(doc).map { |xc| Component.from_xml xc }
end
def get_components_as_hash doc
components = get_xml_components doc
components.map { |c| component_to_hash c }
end
def get_xml_components doc
doc.at('components').children.reject { |c| c.is_a? Nokogiri::XML::Text }
end
def component_to_hash comp
reference = comp.attribute('ref').value
value = comp.xpath('value').text
footprint = comp.xpath('footprint').text
{ ref: reference, value: value, footprint: footprint, properties: self.component_properties(comp) }
end
def component_properties c
c.xpath('property').map(&:values).to_h.reject { |_k, v| v.empty? }
end
end
file = ARGV[0]
doc = Nokogiri::XML(File.open(file)) do |config|
config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NOBLANKS
end
components = ComponentHelper.get_components(doc)
property_names = components.inject(Set.new) { |s, c| s.merge c.properties.keys }
comp_hash = ComponentHelper.get_components_as_hash(doc)
binding.pry
def collapsed cpnts
refs = cpnts.inject([]) { |refs, c| refs << c.ref }
end
# puts comp_hash.to_yaml
# puts JSON.pretty_generate comp_hash
fields = ['Manuf', 'PartNr', 'LCSC Part #', 'Note', 'PartComnt']
# puts Component.csv_header *fields
# puts components.reject { |c| c.properties.Place == "NO" }.map { |c| c.csv_line *fields }
grouped = GroupedComponents.from_components components.reject { |c| c.properties.Place == "NO" }
puts GroupedComponents.csv_header *fields
puts grouped.map { |grouped| grouped.csv_line *fields }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment