From fa85d46af0b91477cf354947df628af0dc0d2800 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 28 Mar 2019 16:38:50 +0100 Subject: ee.xsd: o Renaming to . o Adding on , removing from . A part can have exactly one part. create-order: o Creating anonymous part objects, with two references, one schematic reference and one part-uri reference to the selected part. o Redoing how the order is calculated with the new ObjDb structure. ee.part.Part: o Absorbing bom_file_utils into Part. Much better wrapper object around the xml goop. --- src/ee/part/__init__.py | 159 ++++++++++++++++++++++++-- src/ee/part/create_distributor_search_list.py | 15 +-- 2 files changed, 158 insertions(+), 16 deletions(-) (limited to 'src/ee/part') diff --git a/src/ee/part/__init__.py b/src/ee/part/__init__.py index 4ed6edb..7a58f75 100644 --- a/src/ee/part/__init__.py +++ b/src/ee/part/__init__.py @@ -1,24 +1,154 @@ from pathlib import Path -from typing import List, MutableMapping, Optional, Iterator +from typing import List, MutableMapping, Optional, Iterator, Union -from ee.xml import types, bom_file_utils +from ee import EeException +from ee.money import Money +from ee.xml import types __all__ = [ + "Part", "PartDb", "load_db", "save_db", ] +class Part(object): + def __init__(self, xml: types.Part): + assert type(xml) == types.Part + self.xml = xml + xml.referencesProp = xml.referencesProp if xml.referencesProp is not None else types.ReferencesList() + xml.price_breaksProp = xml.price_breaksProp if xml.price_breaksProp is not None else types.PriceBreakList() + xml.linksProp = xml.linksProp if xml.linksProp is not None else types.LinkList() + xml.factsProp = xml.factsProp if xml.factsProp is not None else types.FactList() + + def clean_xml(self): + x = self.xml + if len(x.referencesProp.part_referenceProp) == 0 and \ + len(x.referencesProp.schematic_referenceProp) == 0 and \ + len(x.referencesProp.part_numberProp) == 0 and \ + len(x.referencesProp.supplier_part_numberProp) == 0: + x.referencesProp = None + if len(x.price_breaksProp.price_break) == 0: + x.price_breaksProp = None + if len(x.linksProp.link) == 0: + x.linksProp = None + if len(x.factsProp.fact) == 0: + x.factsProp = None + + @property + def underlying(self) -> types.Part: + return self.xml + + @property + def uri(self) -> str: + return self.xml.uriProp + + @property + def supplier(self) -> str: + return self.xml.supplierProp + + # Part number ref + + def add_part_reference(self, uri): + self.get_part_references().append(types.PartReference(part_uri=uri)) + + def get_part_references(self) -> List[types.PartReference]: + return self.xml.referencesProp.part_referenceProp + + # Schematic references + + def add_schematic_reference(self, ref): + self.get_schematic_references().append(types.SchematicReference(reference=ref)) + + def get_schematic_references(self) -> List[types.SchematicReference]: + return self.xml.referencesProp.schematic_referenceProp + + def get_only_schematic_reference(self) -> Optional[types.SchematicReference]: + return next(iter(self.get_schematic_references()), None) + + def get_exactly_one_schematic_reference(self) -> types.SchematicReference: + refs = self.get_schematic_references() + if len(refs) == 0: + raise EeException("This part does not contain any schematic references{}". + format(", uri=" + self.uri if self.uri else "")) + if len(refs) != 1: + raise EeException("This part does not contain exactly one schematic reference: {}". + format(", ".join([ref.referenceProp for ref in refs]))) + + return refs[0] + + # MPNs + + def add_mpn(self, mpn: str): + self.get_mpns().append(types.PartNumber(value=mpn)) + + def get_mpns(self) -> List[types.PartNumber]: + return self.xml.referencesProp.part_numberProp + + def get_only_mpn(self) -> Optional[types.PartNumber]: + return next(iter(self.get_mpns()), None) + + def get_exactly_one_mpn(self) -> types.PartNumber: + mpns = self.get_mpns() + if len(mpns) == 0: + raise EeException("This part does not contain any manufacturer part numbers{}". + format(", uri=" + self.uri if self.uri else "")) + if len(mpns) != 1: + raise EeException("This part does not contain exactly one mpn: {}". + format(", ".join([mpn.valueProp for mpn in mpns]))) + + return mpns[0] + + # SPNs + + def add_spn(self, mpn: str): + self.get_spns().append(types.SupplierPartNumber(value=mpn)) + + def get_spns(self) -> List[types.SupplierPartNumber]: + return self.xml.referencesProp.supplier_part_numberProp + + def get_only_spn(self) -> Optional[types.SupplierPartNumber]: + return next(iter(self.get_spns()), None) + + def get_exactly_one_spn(self) -> types.SupplierPartNumber: + spns = self.get_spns() + if len(spns) == 0: + raise EeException("This part does not contain any manufacturer part numbers{}". + format(", uri=" + self.uri if self.uri else "")) + if len(spns) != 1: + raise EeException("This part does not contain exactly one spn: {}". + format(", ".join([spn.valueProp for spn in spns]))) + + return spns[0] + + # Price breaks + + def add_price_break(self, quantity, price: Money): + amount = types.Amount(value=price.amount, currency=price.currency) + pb = types.PriceBreak(quantity=quantity, amount=amount) + self.xml.price_breaksProp.price_break.append(pb) + + # Links + + def get_links(self) -> List[types.Link]: + return self.xml.linksProp.link + + # Facts + + def get_facts(self) -> List[types.Fact]: + return self.xml.factsProp.fact + + def find_fact(self, key: str) -> Optional[types.Fact]: + return next((f for f in self.get_facts() if f.keyProp == key), None) + + class Entry(object): def __init__(self, new: bool, part: types.Part): self.new = new self.part = part - self.pn = next((p.valueProp for p in bom_file_utils.part_numbers(part)), None) - - def dpn(self, uri: str): - return [spn for spn in bom_file_utils.supplier_part_numbers(self.part) if spn.supplierProp == uri] + self.pn = next((p.valueProp for p in Part(part).get_mpns()), None) class PartDb(object): @@ -27,7 +157,10 @@ class PartDb(object): self.pn_index: MutableMapping[str, Entry] = {} self.new_entries = 0 - def add_entry(self, part: types.Part, new: bool): + def add_entry(self, part: Union[Part, types.Part], new: bool): + if isinstance(part, Part): + part = part.underlying + e = Entry(new, part) self.parts.append(e) @@ -63,10 +196,18 @@ def load_db(path: Path) -> PartDb: return db +def find_root_tag(root): + return next((tag for tag, klass in types.GDSClassesMapping.items() if klass == type(root)), None) + + def save_db(path: Path, db: PartDb, sort=False): part_db = types.PartDb() parts = part_db.parts = types.PartList() - parts.partProp.extend(db.iterparts(sort=sort)) + + for part in db.iterparts(sort=sort): + p = Part(part) + p.clean_xml() + parts.partProp.append(p.underlying) with path.open("w") as f: - part_db.export(outfile=f, level=0, name_=bom_file_utils.find_root_tag(part_db)) + part_db.export(outfile=f, level=0, name_=find_root_tag(part_db)) diff --git a/src/ee/part/create_distributor_search_list.py b/src/ee/part/create_distributor_search_list.py index 10160d4..be362a8 100644 --- a/src/ee/part/create_distributor_search_list.py +++ b/src/ee/part/create_distributor_search_list.py @@ -1,7 +1,7 @@ from pathlib import Path -from ee.part import PartDb, load_db, save_db -from ee.xml import types, bom_file_utils +from ee.part import PartDb, load_db, save_db, Part +from ee.xml import types __all__ = ["create_distributor_search_list"] @@ -12,12 +12,13 @@ def create_distributor_search_list(in_path: Path, out_path: Path): print("loaded {} existing parts".format(in_parts.size())) - for part in in_parts.iterparts(): - pn_value = next((p.valueProp for p in bom_file_utils.part_numbers(part)), None) + for xml in in_parts.iterparts(): + part = Part(xml) + pn_value = next((p.valueProp for p in part.get_mpns()), None) if pn_value is None: # TODO: use schematic reference if found - print("Skipping part with no part number: uri={}".format(part.uriProp)) + print("Skipping part with no part number: uri={}".format(xml.uriProp)) continue entry = out_parts.find_by_pn(pn_value) @@ -25,8 +26,8 @@ def create_distributor_search_list(in_path: Path, out_path: Path): if entry is not None: continue - new_part = types.Part(id=pn_value) - new_part.referencesProp = part.referencesProp + new_part = types.Part() + new_part.referencesProp = xml.referencesProp out_parts.add_entry(new_part, True) -- cgit v1.2.3