import csv from pathlib import Path from typing import List, MutableMapping, Optional from ee.db import ObjDb from ee.digikey import DigikeyStore from ee.logging import log from ee.part import Part, load_db from ee.part.bom import load_bom, check_bom, join_refs __all__ = ["create_bom"] class BomLine(object): def __init__(self, uri, part: Part): self.uri = uri self.part = part self.refs = [] def add_ref(self, ref): self.refs.append(ref) @classmethod def header(cls) -> List[str]: return ["Quantity", "Digi-Key Part Number", "Customer Reference"] def to_rows(self) -> List[str]: return [str(len(self.refs)), self.part.get_exactly_one_spn().valueProp, join_refs(self.refs)] def gen_bom(allow_incomplete, bom_parts: ObjDb[Part], supplier_parts: ObjDb[Part]) -> Optional[List[List[str]]]: lines: MutableMapping[str, BomLine] = {} uri_idx = supplier_parts.index("uri") for part in bom_parts.values: pr_obj = part.get_only_part_reference() if allow_incomplete and pr_obj is None: log.debug("Skipping part without part reference: {}".format(part.printable_reference)) continue elif pr_obj is None: log.warn("Unresolved part: {}".format(part.printable_reference)) return part_uri = pr_obj.part_uriProp if part_uri not in lines: lines[part_uri] = line = BomLine(part_uri, uri_idx.get_single(part_uri)) else: line = lines[part_uri] line.add_ref(part.get_exactly_one_schematic_reference().referenceProp) return sorted(line.to_rows() for line in lines.values()) def create_bom(bom_path: Path, part_files: List[Path], out_path: Path, store_code, allow_incomplete): store = DigikeyStore.from_store_code(store_code) bom_parts, supplier_parts = load_bom(bom_path, part_files) check_bom(bom_parts, supplier_parts) bom = gen_bom(allow_incomplete, bom_parts, supplier_parts) if bom is None: return with out_path.open("w") as f: w = csv.writer(f) w.writerow(BomLine.header()) w.writerows(bom)