import logging from pathlib import Path from typing import Union from namedlist import namedlist from ee.doit import DoItConfig from ee.ds import DataSet, create_message logger = logging.getLogger(__name__) doit_config = DoItConfig() class BomComponent(object): def __init__(self, ref, mpn): self.ref = ref self.mpn = mpn def to_object(self, ds): return ds.create_object("bom-component", self.ref). \ set("ref", self.ref). \ set("mpn", self.mpn) def task_bom(): """ Creates 'bom-component' from 'component'. Takes all schematic components, filters out all virtual/non- physical components (like power flags and ground components) and creates 'bom-component' objects. """ out_data_set, in_data_sets = doit_config.data_sets_for(task_bom) def action(): in_ds = doit_config.dsm.load_data_sets(in_data_sets) with doit_config.dsm.create_rw(out_data_set, clean=True) as output: components = [o for o in in_ds.items() if o.object_type.name == "component"] bom_components = {} for c in components: ref = c.get("ref") mpn = c.get("mpn") if not ref: raise Exception("Missing ref") if not mpn: create_message(output, "Missing required field 'mpn' on component ref={}".format(ref), "error") continue if ref in bom_components: raise Exception("Duplicate ref '{}'".format("ref")) bom_components[ref] = BomComponent(ref, mpn) [c.to_object(output) for c in bom_components.values()] return { "file_dep": [doit_config.dsm.cookie_for_ds(ds) for ds in in_data_sets], "actions": [action], "targets": [doit_config.dsm.cookie_for_ds(out_data_set)], } doit_config.set_data_sets_for(task_bom, "bom", "components") def order_csv(count: int, style: str, output_file: Path, out_ds: DataSet, data_sets): ds = doit_config.dsm.load_data_sets(data_sets) csv_ds = DataSet() parts = {} # noinspection PyPep8Naming Part = namedlist("Part", "mpn, cnt, refs, digikey_pn") digikey_parts = [o for o in ds.items() if o.object_type.name == "component-to-part-mapping" and o.get("seller") == "digikey"] for c in [o for o in ds.items() if o.object_type.name == "bom-component"]: ref = c.get("ref") mpn = c.get("mpn") digikey_pn = None if style == "digikey": digikey_pns = [] for o in (o for o in digikey_parts if o.get("ref") == ref): t = o.values("part-number", strip=True, required=True) if not t: create_message(out_ds, "Missing required field value for field part-number, object key={}". format(o.key), level="error") part_number = t[0] digikey_pns.append(part_number) # The actual DK part should depend on the price breaks for the different packagings, but we don't have that # info here. Luckily the DK store proposes cheaper packagings when ordering so that is something. digikey_pns = set(digikey_pns) if len(digikey_pns) == 0: create_message(out_ds, "No part for component: ref={}, mpn={}".format(ref, mpn), "error") continue elif len(digikey_pns) > 1: create_message(out_ds, "Multiple parts for component: ref={}, mpn={}. Don't know which one to select.". format(ref, mpn), "error") continue else: digikey_pn = next(iter(digikey_pns)) digikey_pn = digikey_pn.strip() if len(digikey_pn) == 0: raise Exception("Missing digikey part number for ref={}".format(ref)) if mpn in parts: part = parts[mpn] if digikey_pn: if part.digikey_pn != digikey_pn: raise Exception("Bad data, inconsistent digikey-pn for mpn '{}'. First digikey part number='{}', " "new digikey part number='{}'".format(mpn, part.digikey_pn, digikey_pn)) part.cnt += 1 part.refs.append(ref) else: parts[mpn] = Part(mpn=mpn, cnt=1, refs=[ref], digikey_pn=digikey_pn) mpn_field = "MPN" count_field = "Count" refs_field = "References" if style == "digikey": count_field = "Quantity" refs_field = "Customer Reference" for part in sorted(parts.values(), key=lambda p: p.mpn): o = csv_ds.create_object("row", part.mpn). \ set(mpn_field, part.mpn). \ set(count_field, part.cnt * count). \ set(refs_field, ",".join(part.refs)) if style == "digikey": o.set("Digi-Key Part Number", part.digikey_pn) fields = None include_extra_fields = True if style == "digikey": fields = ["Digi-Key Part Number", refs_field, count_field, mpn_field] include_extra_fields = False doit_config.dsm.store_csv(output_file, csv_ds, "row", order_by=mpn_field, fields=fields, include_extra_fields=include_extra_fields) def create_task_order_csv(*, style: str = None, output_file: Union[str, Path], out_data_set, data_sets, count: int = 1): def action(): with doit_config.dsm.create_rw(out_data_set, clean=True) as out: order_csv(count, style, Path(output_file), out, data_sets) return { "name": "order-{}".format(count) if not style else "order-{}-{}".format(style, count), "actions": [action], "file_dep": [doit_config.dsm.cookie_for_ds(ds) for ds in data_sets], "targets": [doit_config.dsm.cookie_for_ds(out_data_set), output_file], }