From c895e6c051cfda77a22b31367cf5c0bbedce4249 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sun, 13 Aug 2017 11:22:07 +0200 Subject: o Going more jupyter. --- src/ee/kicad/bom/__init__.py | 77 ++++++++++++++++++++++++-- src/ee/kicad/bom_tool/__init__.py | 110 ++++++++++++++++++++++++++++++++++++++ src/ee/kicad/bom_tool/predef.py | 6 +++ 3 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 src/ee/kicad/bom_tool/__init__.py create mode 100644 src/ee/kicad/bom_tool/predef.py (limited to 'src') diff --git a/src/ee/kicad/bom/__init__.py b/src/ee/kicad/bom/__init__.py index 1808357..d0798a4 100644 --- a/src/ee/kicad/bom/__init__.py +++ b/src/ee/kicad/bom/__init__.py @@ -1,10 +1,34 @@ +import re +import sys + __all__ = [ 'Part', 'Library', 'Bom', 'Comp', + 'split_ref', ] +def split_ref(ref): + """Split "C12" into a tuple that's useful for sorting by component reference. + + For example: "C12" => ("C", 12, None). "Cfoo" => ("C", sys.maxsize, ""). + """ + m = split_ref.r.match(ref) + if not m: + return (ref, sys.maxsize, "") + groups = m.groups() + ref = groups[0] + val = groups[1] + rest = groups[2] + try: + return (ref, int(val), rest) + except ValueError: + pass + + return (ref, val, rest) +split_ref.r = re.compile("([A-Za-z]+)([0-9]+)(.*)") + class Part: def __init__(self, name): self.name = name @@ -22,19 +46,26 @@ class Library: self.parts[part] = p return p -class Bom: +class Bom(object): def __init__(self): self.libraries = {} - self.components = {} + self._components = {} def add_component(self, component): - self.components[component.ref] = component + self._components[component.ref] = component def get_component(self, name): - return self.components[name] + return self._components[name] def get_components(self): - return self.components + return self._components + + def all_field_names(self): + fields = set(['ref', 'value']) + for c in self._components.values(): + for f in c.fields: + fields.add(f) + return fields def find_library(self, name): try: @@ -44,6 +75,27 @@ class Bom: self.libraries[name] = lib return lib + def to_pandas(self, ref_field_name = None, value_field_name = None): + import pandas + + ref_field_name = ref_field_name or "ref" + value_field_name = value_field_name or "value" + + fields = self.all_field_names() + data = {k: [] for k in fields} + refs = [] + values = [] + for ref, c in self.get_components().items(): + refs.append(c.ref) + values.append(c.value) + for field in fields: + data[field].append(c[field] if field in c else None) + +# del data[ref_field_name] + data[ref_field_name] = refs + data[value_field_name] = values + return pandas.DataFrame(data=data, index=refs) + class Comp: def __init__(self, ref, value, library, part, footprint): self.ref = ref @@ -56,3 +108,18 @@ class Comp: def add_field(self, key, value): self.fields[key] = value + def __contains__(self, key): + if key == 'ref': + return self.ref is not None + elif key == 'value': + return self.value is not None + else: + return key in self.fields + + def __getitem__(self, key): + if key == 'ref': + return self.ref + elif key == 'value': + return self.value + else: + return self.fields[key] diff --git a/src/ee/kicad/bom_tool/__init__.py b/src/ee/kicad/bom_tool/__init__.py new file mode 100644 index 0000000..9f450c9 --- /dev/null +++ b/src/ee/kicad/bom_tool/__init__.py @@ -0,0 +1,110 @@ +from ee.kicad.bom import * +import functools +import itertools +import pandas as pd + +def _none_if_empty(l): + return l if l is not None and len(l) > 0 else None + +class Supplier(): + def __init__(self, name, bom_field = None): + self._name = name + self._bom_field = bom_field if not None else name + + @property + def bom_field(self): + return self._bom_field + +class Settings(): + def __init__(self, suppliers = None, part_field = None): + self._suppliers = suppliers if suppliers is not None else [] + self._part_field = part_field if part_field is not None else 'part' + + @property + def suppliers(self): + return self._suppliers + + @property + def part_field(self): + return self._part_field + + @part_field.setter + def part_field(self, value): + self._part_field = value + +class CsvFormat(): + def __init__(self, supplier = None, group_by_fields = None, required_fields = None): + self._supplier = supplier + self._group_by_fields = _none_if_empty(group_by_fields) + self._required_fields = _none_if_empty(required_fields) + + @property + def supplier(self): + return self._supplier + + @property + def group_by_fields(self): + return self._group_by_fields + + @property + def required_fields(self): + return self._required_fields + +def _all(fs, c): + for f in fs: + if not f(c): + return False + return True + +def to_panda(bom, bom_settings, csv_format): # type: (Bom, BomSettings, CsvFormat) -> None + filters = [] + + print("csv_format.supplier.bom_field={}".format(csv_format.supplier.bom_field)) + if csv_format.supplier is not None: + filters.append(lambda c: csv_format.supplier.bom_field in c) + + if csv_format.group_by_fields is not None: + filters.append(lambda c: all([field in c for field in csv_format.group_by_fields])) + + if csv_format.required_fields is not None: + filters.append(lambda c: all([field in c for field in csv_format.required_fields])) + + f = functools.partial(_all, filters) + print("len(filters)={}".format(len(filters))) + print("len(bom.get_components())={}".format(len(bom.get_components()))) + + filtered = [c for ref, c in bom.get_components().items() if f(c)] + print("filtered:, len={}".format(len(filtered))) + filtered.sort(key=lambda c: c.ref) + print("sorted filtered:, len={}".format(len(filtered))) + + counts=None + parts=[] + + frame = {} + for n in bom.all_field_names(): + frame[n] = [] + + if csv_format.group_by_fields is not None: + counts, parts, refs=([],[],[]) + for group, comps in itertools.groupby(filtered, key=lambda c: [field for field in c for c in csv_format.group_by_fields]): + gs = list(group) + cs = list(comps) +# counts.append(len(cs)) +# dpns.append(part) +# mpns.append(part) +# refs.append(functools.reduce(lambda a, b: a + ' ' + b, [c.ref for c in cs], '')) + print("group={}".format(gs)) + +# return pd.DataFrame(data={ +# 'count': counts, +# 'mpn': mpns, +# 'dpn': dpns, +# 'refs': refs, +# }) + else: + for ref, c in filtered: + for key, value in c: + frame[key] = value + + return pd.DataFrame(data=frame) diff --git a/src/ee/kicad/bom_tool/predef.py b/src/ee/kicad/bom_tool/predef.py new file mode 100644 index 0000000..717f6d3 --- /dev/null +++ b/src/ee/kicad/bom_tool/predef.py @@ -0,0 +1,6 @@ +from ee.kicad.bom_tool import * + +digikey = Supplier('Digi-Key', bom_field = 'digikey') + +def digikeyCsvFormat(digikey_supplier): + return CsvFormat(supplier = digikey, group_by_fields = [digikey_supplier.bom_field]) -- cgit v1.2.3