diff options
-rw-r--r-- | src/ee/bom.py | 67 | ||||
-rw-r--r-- | src/ee/logging.py | 20 | ||||
-rw-r--r-- | src/ee/part/__init__.py | 15 | ||||
-rw-r--r-- | src/ee/tools/__init__.py | 24 |
4 files changed, 89 insertions, 37 deletions
diff --git a/src/ee/bom.py b/src/ee/bom.py index f8cca71..731f5b4 100644 --- a/src/ee/bom.py +++ b/src/ee/bom.py @@ -4,21 +4,32 @@ from pathlib import Path from typing import List, MutableMapping, Optional from ee import EeException +from ee.logging import log from ee.db import ObjDb from ee.part import PartDb, load_db, save_db, Part from ee.project import Project, report, SupplierDescriptor from ee.xml import types +from ee.xml.uris import make_fact_key __all__ = ["create_bom"] +class Hit(object): + def __init__(self, part: Part, method: str): + self.part = part + self.method = method + + class BomPart(object): - def __init__(self, part: types.Part): - self.part = Part(part) + def __init__(self, part: Part): + self.part = part ref = self.part.get_only_schematic_reference() self.ref = ref.referenceProp if ref else None - self.available_from: MutableMapping[str, Part] = {} - self.selected_part = None + self.hits = [] + self.selected_part: Optional[Part] = None + + def add_hit(self, part, method): + self.hits.append(Hit(part, method)) def make_report(out_file, unresolved_parts, bom_parts: ObjDb[BomPart], supplier_parts: ObjDb[Path]): @@ -41,6 +52,7 @@ def create_bom(project: Project, schematic_path: Path, out_path: Path, part_dbs: strategy = pydoc.locate(strategy_name) if not callable(strategy): raise EeException("Not a callable: {}, is a {}".format(strategy_name, type(strategy))) + log.info("Using strategy '{}'".format(strategy_name)) supplier_parts = ObjDb[Part]() supplier_parts.add_unique_index("uri", lambda p: p.uri) @@ -65,40 +77,55 @@ def create_bom(project: Project, schematic_path: Path, out_path: Path, part_dbs: op.part.get_mpns()] if op.part.supplier else None, multiple=True) for sch_part in sch_db.iterparts(): - part = BomPart(sch_part) - part.part = strategy(part.part) - if part.part is None: + part = Part(sch_part) + + part = strategy(part) + if part is None: continue - bom_parts.add(part) + bom_parts.add(BomPart(part)) for bom_part in bom_parts: sch_part_numbers = [pn.valueProp for pn in bom_part.part.get_mpns()] sch_supplier_part_numbers = [spn.valueProp for spn in bom_part.part.get_spns()] + value_fact = bom_part.part.find_fact(make_fact_key("value")) + if value_fact: + value_fact = value_fact.valueProp + for supplier in suppliers: + # Part number search pns = supplier_pn_idx.get(supplier.uri) - spns = supplier_spn_idx.get(supplier.uri) - for sch_pn in sch_part_numbers: for supplier_part in pns.get(sch_pn, []): - bom_part.available_from[supplier_part.uri] = supplier_part + bom_part.add_hit(supplier_part, "pn") + + if value_fact: + for supplier_part in pns.get(value_fact, []): + bom_part.add_hit(supplier_part, "pn_value_fact") + + # Supplier number search + spns = supplier_spn_idx.get(supplier.uri) for sch_spn in sch_supplier_part_numbers: for supplier_part in spns.get(sch_spn, []): - bom_part.available_from[supplier_part.uri] = supplier_part + bom_part.add_hit(supplier_part, "spn") + + if value_fact: + for supplier_part in spns.get(value_fact, []): + bom_part.add_hit(supplier_part, "spn_value_fact") unresolved_parts = [] for bom_part in bom_parts: - af = bom_part.available_from - if len(af) == 0: + if len(bom_part.hits) == 0: unresolved_parts.append(bom_part) - elif len(af) == 1: - bom_part.selected_part = next(iter(af.values())) + elif len(bom_part.hits) == 1: + bom_part.selected_part = bom_part.hits[0].part else: - raise EeException("unimplemented: part ({}) available from multiple suppliers: {}". - format(bom_part.ref, ",".join(af.keys()))) + references = [hit.part.printable_reference for hit in bom_part.hits] + raise EeException("Multiple hits when looking for part: {}". + format(bom_part.ref, ",".join(references))) - bom_parts.add_index("uri", lambda op: op.selected_part.uri if op.selected_part else None) + bom_parts.add_index("uri", lambda bp: bp.selected_part.uri if bp.selected_part else None) bom_parts.add_multi_index("supplier,part", lambda op: ( op.selected_part.supplier, op.selected_part.uri) if op.selected_part else None) @@ -111,6 +138,7 @@ def create_bom(project: Project, schematic_path: Path, out_path: Path, part_dbs: out_parts = PartDb() for bom_part in bom_parts: if not bom_part.selected_part: + log.info("No part selected for {}".format(bom_part.part.printable_reference)) continue supplier_part = bom_part.selected_part @@ -121,4 +149,5 @@ def create_bom(project: Project, schematic_path: Path, out_path: Path, part_dbs: out_parts.add_entry(part, True) + log.info("Found {} of {} parts, missing {}".format(len(out_parts), len(sch_db), len(unresolved_parts))) save_db(out_path, out_parts) diff --git a/src/ee/logging.py b/src/ee/logging.py new file mode 100644 index 0000000..c8fd48a --- /dev/null +++ b/src/ee/logging.py @@ -0,0 +1,20 @@ +from colors import color + +__all__ = ["Log", "log"] + + +class Log(object): + def __init__(self): + pass + + def warn(self, msg): + print(color(msg, "orange")) + + def info(self, msg): + print(color(msg, "white")) + + def debug(self, msg): + print(color(msg, "grey")) + + +log = Log() diff --git a/src/ee/part/__init__.py b/src/ee/part/__init__.py index 43e4cfa..ff6c7f1 100644 --- a/src/ee/part/__init__.py +++ b/src/ee/part/__init__.py @@ -203,6 +203,18 @@ class Part(object): def supplier(self) -> str: return self.xml.supplierProp + @property + def printable_reference(self): + for refs, value in [(self.get_schematic_references(), lambda sr: sr.referenceProp), + (self.get_part_references(), lambda pr: pr.part_uriProp), + (self.get_mpns(), lambda mpn: mpn.valueProp), + (self.get_spns(), lambda spn: spn.valueProp)]: + ref = next(iter(refs), None) + if ref: + return value(ref) + + return self.uri + # Part number ref def add_part_reference(self, uri): @@ -366,6 +378,9 @@ class PartDb(object): def size(self) -> int: return len(self.parts) + def __len__(self): + return self.size() + @property def has_assembly(self): return self._assembly is not None diff --git a/src/ee/tools/__init__.py b/src/ee/tools/__init__.py index b0c1051..46004e9 100644 --- a/src/ee/tools/__init__.py +++ b/src/ee/tools/__init__.py @@ -2,7 +2,12 @@ import os.path from pathlib import Path from typing import Union -from colors import color +from ee.logging import log + +__all__ = [ + "mk_parents", + "log" # for compatibility, should be removed +] def _mkdir_and_open(path): @@ -23,20 +28,3 @@ def mk_parents(path: Union[str, Path]): if not os.path.isdir(dirname): os.mkdir(dirname) - - -class Log(object): - def __init__(self): - pass - - def warn(self, msg): - print(color(msg, "orange")) - - def info(self, msg): - print(color(msg, "white")) - - def debug(self, msg): - print(color(msg, "grey")) - - -log = Log() |