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/db.py | 117 +++++++++++++++---- src/ee/digikey/search_parts.py | 60 +++++----- src/ee/kicad/make_bom.py | 45 +++----- src/ee/order/__init__.py | 115 +++++++++---------- src/ee/order/templates/order.rst.j2 | 28 +++-- src/ee/part/__init__.py | 159 ++++++++++++++++++++++++-- src/ee/part/create_distributor_search_list.py | 15 +-- src/ee/project/__init__.py | 17 +++ src/ee/project/report.py | 16 ++- src/ee/supplier/seeed.py | 22 ++-- src/ee/xml/bom_file_utils.py | 49 -------- src/ee/xml/types.py | 144 +++++++++++------------ 12 files changed, 490 insertions(+), 297 deletions(-) delete mode 100644 src/ee/xml/bom_file_utils.py (limited to 'src/ee') diff --git a/src/ee/db.py b/src/ee/db.py index e471278..57533ef 100644 --- a/src/ee/db.py +++ b/src/ee/db.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Generic, Callable, MutableMapping, List, Iterable, Union, Any +from typing import TypeVar, Generic, Callable, MutableMapping, List, Iterable, Union, Any, Mapping K = TypeVar('K') V = TypeVar('V') @@ -34,9 +34,11 @@ class Index(Generic[K, V]): class ListIndex(Index[K, V]): - def __init__(self, key_callable: KeyCallable): + def __init__(self, name, key_callable: KeyCallable, multiple=False): + self.name = name self.idx: MutableMapping[K, V] = {} self.key_callable = key_callable + self.multiple = multiple def add(self, value: V): keys = self.key_callable(value) @@ -44,6 +46,9 @@ class ListIndex(Index[K, V]): if keys is None: return + if not self.multiple: + keys = [keys] + for key in keys: if key is None: continue @@ -54,8 +59,9 @@ class ListIndex(Index[K, V]): else: self.idx[key] = [value] - def get(self, key: K) -> Iterable[V]: - return [self.idx[key]] + def get(self, key: K) -> List[V]: + items = self.idx.get(key, None) + return self.idx[key] if items is not None else [] def clear(self): return self.idx.clear() @@ -71,12 +77,15 @@ class ListIndex(Index[K, V]): class UniqueIndex(Index[K, V]): - def __init__(self, key_callable: KeyCallable): + def __init__(self, name, key_callable: KeyCallable, multiple=False): + self.name = name self.idx: MutableMapping[K, V] = {} self.key_callable = key_callable + self.multiple = multiple def get(self, key: K) -> Iterable[V]: - return [self.idx[key]] + items = self.idx.get(key, None) + return [self.idx[key]] if items is not None else [] def get_single(self, key: K) -> V: return self.idx[key] @@ -84,16 +93,75 @@ class UniqueIndex(Index[K, V]): def add(self, value: V): keys = self.key_callable(value) - if not isinstance(keys, Iterable): + if keys is None: + return + + if not self.multiple: keys = [keys] for key in keys: - key = self.key_callable(value) present = self.idx.get(key, None) if present is not None: - raise KeyError("Duplicate part in index: {}".format(key)) + raise KeyError("Duplicate key in index '{}': key={}, value={}".format(self.name, key, repr(value))) + + self.idx[key] = value + + def clear(self): + return self.idx.clear() + + def __iter__(self): + return self.idx.__iter__() + + def items(self): + return self.idx.items() + + def values(self): + return self.idx.values() + + +class MultiIndex(Index[K, V]): + def __init__(self, name, key_callable: KeyCallable, multiple=False): + self.name = name + self.idx: MutableMapping[K, V] = {} + self.key_callable = key_callable + self.multiple = multiple + + # TODO: this should return a new index + def get(self, key: K) -> Mapping[K, V]: + items = self.idx.get(key, None) + return self.idx[key] if items is not None else {} - self.idx[key] = True + def get_single(self, key: K) -> V: + return self.idx[key] + + def add(self, value: V): + keys = self.key_callable(value) + + if keys is None: + return + + if not self.multiple: + keys = [keys] + + for tpl in keys: + if not isinstance(tpl, tuple): + raise KeyError("The key must be a tuple, index='{}', key='{}'".format(self.name, repr(tpl))) + + parent_idx = self.idx + for sub_key in tpl[0:-1]: + if sub_key is None: + raise KeyError("Got None sub-key: index='{}', key='{}'".format(self.name, repr(tpl))) + idx = parent_idx.get(sub_key, None) + if idx is None: + idx = {} + parent_idx[sub_key] = idx + parent_idx = idx + + values = parent_idx.get(tpl[-1], None) + if values is None: + values = [] + parent_idx[tpl[-1]] = values + values.append(value) def clear(self): return self.idx.clear() @@ -125,19 +193,28 @@ class ObjDb(Generic[V]): def __len__(self) -> int: return len(self.values) - def add_index(self, name, key_callable: KeyCallable) -> ListIndex[Any, V]: - idx = ListIndex(key_callable) - self._indexes[name] = idx + def add_index(self, name, key_callable: KeyCallable, **kwargs) -> ListIndex[Any, V]: + idx = ListIndex(name, key_callable, **kwargs) + return self._add(name, idx) + + def add_unique_index(self, name, key_callable: KeyCallable, **kwargs) -> UniqueIndex[Any, V]: + idx = UniqueIndex(name, key_callable, **kwargs) + return self._add(name, idx) + + def add_multi_index(self, name, key_callable: KeyCallable, **kwargs) -> MultiIndex[Any, V]: + idx = MultiIndex(name, key_callable, **kwargs) + return self._add(name, idx) + + def index(self, name) -> Index: + return self._indexes[name] + + def _add(self, name, idx): + if name in self._indexes: + raise KeyError("Index already exist: {}".format(name)) for value in self.values: idx.add(value) - return idx - - def add_unique_index(self, name, key_callable: KeyCallable) -> UniqueIndex[Any, V]: - idx = UniqueIndex(key_callable) self._indexes[name] = idx - return idx - def index(self, name) -> Index: - return self._indexes[name] + return idx diff --git a/src/ee/digikey/search_parts.py b/src/ee/digikey/search_parts.py index 358266a..7d19c5b 100644 --- a/src/ee/digikey/search_parts.py +++ b/src/ee/digikey/search_parts.py @@ -2,47 +2,46 @@ from pathlib import Path from typing import List from ee.digikey import Digikey, DigikeyParser, DigikeyClient, SearchResponseTypes, DigikeyProduct -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 from ee.xml.uris import DIGIKEY_URI, make_digikey_fact_key __all__ = ["search_parts"] -def resolved(p: DigikeyProduct) -> types.Part: +def resolved(p: DigikeyProduct) -> Part: # TODO: fix uri - part = types.Part(uri="https://digikey.com/pn#{}".format(p.part_number), - distributor_info=types.DistributorInfo(), - links=types.LinkList(), - facts=types.FactList(), - references=types.ReferencesList()) - part.distributor_infoProp.stateProp = "resolved" - links = part.linksProp.link + xml = types.Part(uri="https://digikey.com/pn#{}".format(p.part_number), + supplier=DIGIKEY_URI, + description=p.description, + distributor_info=types.DistributorInfo(state="resolved"), + links=types.LinkList(), + facts=types.FactList(), + references=types.ReferencesList()) + part = Part(xml) if p.url: - links.append(types.Link(url=p.url, relation="canonical", media_type="text/html")) + part.get_links().append(types.Link(url=p.url, relation="canonical", media_type="text/html")) for d in p.documents: title = "{}: {}".format(d.kind, d.title) - links.append(types.Link(url=d.url, relation="http://purl.org/ee/link-relation#documentation", - media_type="text/html", title=title)) + part.get_links().append(types.Link(url=d.url, relation="http://purl.org/ee/link-relation#documentation", + media_type="text/html", title=title)) - supplier_part_numbers = part.referencesProp.supplier_part_numberProp - supplier_part_numbers.append(types.SupplierPartNumber(value=p.part_number, supplier=DIGIKEY_URI)) + part.add_spn(p.part_number) - part_numbers = part.referencesProp.part_numberProp if p.mpn: - part_numbers.append(types.PartNumber(value=p.mpn)) - facts: List[types.Fact] = part.factsProp.factProp + part.add_mpn(p.mpn) + facts: List[types.Fact] = xml.factsProp.factProp for a in p.attributes: key = make_digikey_fact_key(a.attribute_type.id) facts.append(types.Fact(key=key, label=a.attribute_type.label, value=a.value)) if len(p.price_breaks): - part.price_breaksProp = types.PriceBreakList() + xml.price_breaksProp = types.PriceBreakList() - price_breaks: List[types.PriceBreak] = part.price_breaksProp.price_break + price_breaks: List[types.PriceBreak] = xml.price_breaksProp.price_break for pb in p.price_breaks: amount = types.Amount(value=str(pb.per_piece_price.amount), currency=pb.per_piece_price.currency) price_breaks.append(types.PriceBreak(pb.quantity, amount=amount)) @@ -57,29 +56,34 @@ def search_parts(in_path: Path, out_path: Path, cache_dir: Path): parser = DigikeyParser(Digikey()) client = DigikeyClient(cache_dir) - for part in in_db.iterparts(): - dpn = next((p.valueProp for p in bom_file_utils.supplier_part_numbers(part) - if p.supplierProp == DIGIKEY_URI), None) - mpn = next((p.valueProp for p in bom_file_utils.part_numbers(part)), None) + for xml in in_db.iterparts(): + if xml.supplierProp is not None and xml.supplierProp != DIGIKEY_URI: + continue + + part = Part(xml) + + dpn = part.get_only_spn() + mpn = part.get_only_mpn() is_mpn = query = None if dpn is not None: - query = dpn + query = dpn.valueProp is_mpn = False elif mpn is not None: - query = mpn + query = mpn.valueProp is_mpn = True if query is None: # TODO: use schematic reference - print("could not find pn or dpn: part.uri={}".format(part.uriProp)) + print("could not find pn or dpn: part.uri={}".format(xml.uriProp)) continue out_id = query out_part = types.Part(uri=out_id, distributor_info=types.DistributorInfo(), - references=part.referencesProp) + supplier=DIGIKEY_URI, + references=xml.referencesProp) di = out_part.distributor_infoProp text = client.search(query) diff --git a/src/ee/kicad/make_bom.py b/src/ee/kicad/make_bom.py index 0a8f4bb..ddaa6b9 100644 --- a/src/ee/kicad/make_bom.py +++ b/src/ee/kicad/make_bom.py @@ -8,7 +8,7 @@ from ee import EeException from ee.kicad.model import Component from ee.kicad.read_schematic import read_schematics from ee.kicad.to_bom import to_bom, to_bom_xml -from ee.part import PartDb, save_db +from ee.part import PartDb, save_db, Part from ee.xml import types, uris __all__ = [ @@ -21,61 +21,60 @@ __all__ = [ "make_bom", ] -StrategyCallable = Callable[[Component, types.Part], Optional[types.Part]] +StrategyCallable = Callable[[Component, Part], Optional[Part]] -def apply_strategies(c: Component, part: types.Part, strategies: List[StrategyCallable]): +def apply_strategies(c: Component, part: Part, strategies: List[StrategyCallable]): for strategy in strategies: part = strategy(c, part) if part is None: return - if not isinstance(part, types.Part): - raise EeException("Values returned from strategy must be a types.Part, got {}".format(type(part))) + if not isinstance(part, Part): + raise EeException("Values returned from strategy must be a Part, got {}".format(type(part))) return part -def part_type_strategy(component: Component, part: types.Part) -> types.Part: +def part_type_strategy(component: Component, part: Part) -> Part: fp = component.footprint if fp is None: return part lib, part_name = fp.split(":") + xml = part.underlying if lib == "Capacitor_SMD": - part.part_typeProp = uris.CAPACITOR + xml.part_typeProp = uris.CAPACITOR elif lib == "Resistor_SMD": - part.part_typeProp = uris.RESISTOR + xml.part_typeProp = uris.RESISTOR elif lib == "Diode_SMD": - part.part_typeProp = uris.DIODE + xml.part_typeProp = uris.DIODE elif lib == "Inductor_SMD": - part.part_typeProp = uris.INDUCTOR + xml.part_typeProp = uris.INDUCTOR elif lib == "Crystal": - part.part_typeProp = uris.CRYSTAL + xml.part_typeProp = uris.CRYSTAL return part -def mpn_strategy(component: Component, part: types.Part) -> types.Part: +def mpn_strategy(component: Component, part: Part) -> Part: mpn = component.get_field("mpn") if mpn is not None: - pn = types.PartNumber(value=mpn.value) - part.referencesProp.add_part_number(pn) + part.add_mpn(mpn.value) return part def dpn_strategy_factory(dpn_mappings: Mapping[str, str]) -> StrategyCallable: - def dpn_strategy(component: Component, part: types.Part) -> types.Part: + def dpn_strategy(component: Component, part: Part) -> Part: for field_name, distributor in dpn_mappings: s = component.get_field(field_name) if s is None: continue - pn = types.SupplierPartNumber(value=s.value, supplier=distributor) - part.referencesProp.add_part_number(pn) + part.add_spn(s.value) return part @@ -111,21 +110,15 @@ def work(sch, out: Path, strategy: MakeBomStrategy, new_mode, pretty): parts = PartDb() components = to_bom(sch) for c in components: - part = types.Part() - part.referencesProp = types.ReferencesList() - part.referencesProp.schematic_reference.append(types.SchematicReference(reference=c.ref)) + xml = types.Part() + part = Part(xml) + part.add_schematic_reference(c.ref) part = strategy.process_part(c, part) if part is None: continue - if len(part.referencesProp.schematic_reference) == 0 and \ - len(part.referencesProp.part_number) == 0 and \ - len(part.referencesProp.supplier_part_number) == 0: - # No need to dirty the xml with empty lists - part.referencesProp = None - parts.add_entry(part, True) save_db(out, parts) diff --git a/src/ee/order/__init__.py b/src/ee/order/__init__.py index a384cb7..83723ba 100644 --- a/src/ee/order/__init__.py +++ b/src/ee/order/__init__.py @@ -4,111 +4,104 @@ from typing import List, Tuple from ee import EeException from ee.db import ObjDb -from ee.part import PartDb, load_db, save_db -from ee.project import Project, report -from ee.xml import types, bom_file_utils +from ee.part import PartDb, load_db, save_db, Part +from ee.project import Project, report, SupplierDescriptor +from ee.xml import types __all__ = ["create_order"] class OrderPart(object): def __init__(self, part: types.Part): - self.part = part - self.ref = next((ref.referenceProp for ref in bom_file_utils.schematic_references(part)), None) + self.part = Part(part) + ref = self.part.get_only_schematic_reference() + self.ref = ref.referenceProp if ref else None self.available_from: List[Tuple[str, types.Part]] = [] - self.selected_supplier_and_part = None + self.selected_part = None - rl = part.referencesProp = part.referencesProp or types.ReferencesList() # type: types.ReferencesList - rl.schematic_referenceProp = rl.schematic_referenceProp or [] - rl.part_numberProp = rl.part_numberProp or [] - rl.supplier_part_numberProp = rl.supplier_part_numberProp or [] - - -def make_report(out_file, order_parts: ObjDb[OrderPart], has_unresolved_parts): - parts_by_supplier = {} - - for supplier, order_parts in order_parts.index("selected_part").items(): - parts = {} - for op in order_parts: - p = op.selected_supplier_and_part[1] - parts[p.uriProp] = p - parts_by_supplier[supplier] = parts.values() +def make_report(out_file, has_unresolved_parts, order_parts: ObjDb[OrderPart], supplier_parts: ObjDb[Path]): kwargs = { "order_parts": order_parts, - "parts_by_supplier": parts_by_supplier, + "supplier_parts": supplier_parts, "has_unresolved_parts": has_unresolved_parts, } report.save_report("ee.order", "order.rst.j2", out_file, **kwargs) -def create_order(project: Project, schematic_path: Path, out_path: Path, part_db_dirs: List[Path], +def create_order(project: Project, schematic_path: Path, out_path: Path, part_dbs: List[Path], fail_on_missing_parts: bool): - sch_db = load_db(schematic_path) - - dbs = [(path.parent.name, load_db(path)) for path in part_db_dirs] - - def supplier_index(op: OrderPart): - return [spn.supplierProp for spn in bom_file_utils.supplier_part_numbers(op.part)] - - def selected_part_index(op: OrderPart): - return [op.selected_supplier_and_part[0]] if op.selected_supplier_and_part else None - - def available_from_index(op: OrderPart): - return set([s for s, s_part in op.available_from]) - - def supplier_part_index(op: OrderPart): - return set([s_part.uriProp for s, s_part in op.available_from]) - - def pn_index(op: OrderPart): - return [pn.value for pn in bom_file_utils.part_numbers(op.part)] + supplier_parts = ObjDb[Part]() + supplier_parts.add_unique_index("uri", lambda p: p.uri) + supplier_parts.add_index("spn", lambda p: [ref.valueProp for ref in p.get_spns()], + multiple=True) + supplier_pn_idx = supplier_parts.add_multi_index("supplier,pn", lambda p: [ + (p.supplier, ref.valueProp) for ref in p.get_mpns()], multiple=True) + supplier_spn_idx = supplier_parts.add_multi_index("supplier,spn", lambda p: [ + (p.supplier, ref.valueProp) for ref in p.get_spns()], multiple=True) + + suppliers: List[SupplierDescriptor] = [project.get_supplier_by_key(path.parent.name) for path in part_dbs] + for path in part_dbs: + for xml in load_db(path).iterparts(): + if not xml.supplierProp: + continue + supplier_parts.add(Part(xml)) + sch_db = load_db(schematic_path) order_parts: ObjDb[OrderPart] = ObjDb[OrderPart]() + order_parts.add_multi_index("supplier,pn", lambda op: [ + (op.part.supplierProp, ref.valueProp) for ref in + op.part.get_mpns()] if op.part.supplier else None, multiple=True) + order_parts.add_multi_index("supplier,spn", lambda op: [ + (op.part.supplier, ref.valueProp) for ref in op.part.get_spns()], multiple=True) + for sch_part in sch_db.iterparts(): order_parts.add(OrderPart(sch_part)) for order_part in order_parts: - sch_part_numbers = order_part.part.referencesProp.part_number - sch_supplier_part_numbers: List[types.SupplierPartNumber] = order_part.part.referencesProp.supplier_part_number + sch_part_numbers = [pn.valueProp for pn in order_part.part.get_mpns()] + sch_supplier_part_numbers = [spn.valueProp for spn in order_part.part.get_spns()] - for supplier, db in dbs: - for supplier_part in db.iterparts(sort=True): - for sch_pn in sch_part_numbers: - for pn in bom_file_utils.part_numbers(supplier_part): - if sch_pn.valueProp == pn.valueProp: - order_part.available_from.append((supplier, supplier_part)) + for supplier in suppliers: + pns = supplier_pn_idx.get(supplier.uri) + spns = supplier_spn_idx.get(supplier.uri) - for sch_spn in sch_supplier_part_numbers: - for spn in bom_file_utils.supplier_part_numbers(supplier_part): - if sch_spn.valueProp == spn.valueProp and sch_spn.supplierProp == spn.supplierProp: - order_part.available_from.append((supplier, supplier_part)) + for sch_pn in sch_part_numbers: + for supplier_part in pns.get(sch_pn, []): + order_part.available_from.append((supplier.uri, supplier_part)) + for sch_spn in sch_supplier_part_numbers: + for supplier_part in spns.get(sch_spn, []): + order_part.available_from.append((supplier.uri, supplier_part)) has_unresolved_parts = False for order_part in order_parts: if len(order_part.available_from) == 0: has_unresolved_parts = True elif len(order_part.available_from) == 1: - order_part.selected_supplier_and_part = order_part.available_from[0] + order_part.selected_part = order_part.available_from[0] else: raise EeException("unimplemented: part available from multiple suppliers") + order_parts.add_index("partUri", lambda op: op.selected_part[1].uri if op.selected_part else None) + order_parts.add_multi_index("supplier,part", lambda op: ( + op.selected_part[0], op.selected_part[1].uri) if op.selected_part else None) + if has_unresolved_parts and fail_on_missing_parts: raise EeException("The order has parts that can't be found from any supplier") - order_parts.add_index("selected_part", selected_part_index) - out_file = project.report_dir / (os.path.splitext(out_path.name)[0] + ".rst") - make_report(out_file, order_parts, has_unresolved_parts) + make_report(out_file, has_unresolved_parts, order_parts, supplier_parts) out_parts = PartDb() for order_part in order_parts: - if not order_part.selected_supplier_and_part: + if not order_part.selected_part: continue - supplier, supplier_part = order_part.selected_supplier_and_part + supplier, supplier_part = order_part.selected_part - references = types.ReferencesList(schematic_reference=order_part.part.referencesProp.schematic_reference) - part = types.Part(uri=supplier_part.uri, references=references) + part = Part(types.Part(supplier=supplier_part.supplier)) + part.add_schematic_reference(order_part.part.get_exactly_one_schematic_reference().referenceProp) + part.add_part_reference(supplier_part.uri) out_parts.add_entry(part, True) diff --git a/src/ee/order/templates/order.rst.j2 b/src/ee/order/templates/order.rst.j2 index bc382a2..8dad217 100644 --- a/src/ee/order/templates/order.rst.j2 +++ b/src/ee/order/templates/order.rst.j2 @@ -1,3 +1,4 @@ +{% set order_part_uri_idx = order_parts.index("partUri") -%} Order ===== @@ -6,6 +7,8 @@ Has unresolved parts: {{ "yes" if has_unresolved_parts else "no" }}. Parts for Order =============== {% for op in order_parts %} +.. _ref-{{ op.ref }}: + {{ op.ref | subsection }} {% if op.available_from|length == 0 %} Part not resolved. @@ -23,30 +26,39 @@ MANY Part details ============ -{% for supplier, parts in parts_by_supplier.items() %} +{%- set part_by_uri=supplier_parts.index("uri") %} +{% for supplier, partUris in order_parts.index("supplier,part").items() %} {{ ("From " + supplier) | subsection }} -{% for part in parts %} +{% for partUri in partUris %} +{%- set part=part_by_uri.get_single(partUri) %} {%- set pn=part|first_pn %} {%- set spn=part|first_spn %} {%- set title=pn.valueProp if pn else (spn.valueProp if spn else "???") %} -{%- set links=part.linksProp.link %} .. _part-{{title}}: {{ title|subsubsection }} -{#- +=========== === +{%- if part.description %} +Description {{ part.description }} +{%- endif %} +MPN {{ pn.value }} +SPN {{ spn.value }} +Used by: {% for op in order_part_uri_idx.get(part.uriProp) %}`{{ op.ref }} `_{{ ", " if not loop.last }}{% endfor %} +=========== === +{# Facts ..... {% for f in part.facts.fact %} f={{f}} {% endfor %} -#} -Media -..... +#} +Documentation +............. -{% for l in links %} +{% for l in part.get_links() %} {%- if l.relationProp == "http://purl.org/ee/link-relation#documentation" %} * `{{ l.title }} <{{ l.url }}>`__ {%- endif %} 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) diff --git a/src/ee/project/__init__.py b/src/ee/project/__init__.py index 6851256..bca9984 100644 --- a/src/ee/project/__init__.py +++ b/src/ee/project/__init__.py @@ -1,7 +1,9 @@ import configparser from pathlib import Path +from ee import EeException from ee.tools import mk_parents +from ee.xml.uris import DIGIKEY_URI def load_config(project_dir: Path) -> configparser.ConfigParser: @@ -17,6 +19,12 @@ def load_config(project_dir: Path) -> configparser.ConfigParser: return config +class SupplierDescriptor(object): + def __init__(self, key: str, uri: str): + self.key = key + self.uri = uri + + class Project(object): def __init__(self, project_dir: Path, cfg: configparser.ConfigParser): self.report_dir = project_dir / "ee" / "reports" @@ -24,6 +32,15 @@ class Project(object): self.project_dir = project_dir self._cfg = cfg + # TODO: read from config + self._suppliers = [SupplierDescriptor("digikey", DIGIKEY_URI)] + + def get_supplier_by_key(self, key) -> SupplierDescriptor: + sd = next((s for s in self._suppliers if s.key == key), None) + if sd: + return sd + raise EeException("No such supplier configured: {}".format(key)) + @property def cfg(self): return self._cfg diff --git a/src/ee/project/report.py b/src/ee/project/report.py index 80e590a..26cdaa3 100644 --- a/src/ee/project/report.py +++ b/src/ee/project/report.py @@ -3,8 +3,9 @@ from typing import Optional from jinja2 import Environment, PackageLoader, select_autoescape +from ee.part import Part from ee.tools import mk_parents -from ee.xml import types, bom_file_utils +from ee.xml import types class Message(object): @@ -29,12 +30,16 @@ def subsubsubsection_filter(s: str): return "{}\n{}".format(s, "." * len(s)) -def first_pn_filter(part: types.Part): - return next(iter(bom_file_utils.part_numbers(part)), None) +def first_ref_filter(part: Part) -> Optional[types.SchematicReference]: + return part.get_only_schematic_reference() -def first_spn_filter(part: types.Part): - return next(iter(bom_file_utils.supplier_part_numbers(part)), None) +def first_pn_filter(part: Part) -> Optional[types.PartNumber]: + return part.get_only_mpn() + + +def first_spn_filter(part: Part) -> Optional[types.SupplierPartNumber]: + return part.get_only_spn() def save_report(package: str, template_name: str, out_file: Path, **kwargs): @@ -45,6 +50,7 @@ def save_report(package: str, template_name: str, out_file: Path, **kwargs): env.filters["subsection"] = subsection_filter env.filters["subsubsection"] = subsubsection_filter env.filters["subsubsubsection"] = subsubsubsection_filter + env.filters["first_ref"] = first_ref_filter env.filters["first_pn"] = first_pn_filter env.filters["first_spn"] = first_spn_filter diff --git a/src/ee/supplier/seeed.py b/src/ee/supplier/seeed.py index 2df2068..45cff44 100644 --- a/src/ee/supplier/seeed.py +++ b/src/ee/supplier/seeed.py @@ -9,7 +9,7 @@ import requests from selenium import webdriver import ee._utils -from ee.part import PartDb, save_db +from ee.part import Part, PartDb, save_db from ee.xml import types _title_re = re.compile(r"^([^(]*)\( *[0-9]* *\) *") @@ -113,13 +113,10 @@ def download_opls(out_dir: Path, cache_dir: Path): ladder_price = p["ladder_price"] - part = types.Part(references=types.ReferencesList(), price_breaks=types.PriceBreakList()) - part_numbers = part.referencesProp.part_numberProp - supplier_part_numbers = part.referencesProp.supplier_part_numberProp - pbs = part.price_breaksProp.price_breakProp + xml = types.Part(references=types.ReferencesList(), price_breaks=types.PriceBreakList()) + xml.supplierProp = supplier_uri - if desc: - part.descriptionProp = desc + xml.descriptionProp = desc uri_params = { "opl": opls_type, @@ -128,17 +125,18 @@ def download_opls(out_dir: Path, cache_dir: Path): uri_params["sku"] = sku if mpn: uri_params["mpn"] = mpn - part.uriProp = "http://purl.org/ee/supplier/seeed?{}".format(urlencode(uri_params)) + xml.uriProp = "http://purl.org/ee/supplier/seeed?{}".format(urlencode(uri_params)) + + part = Part(xml) if mpn: - part_numbers.append(types.PartNumber(mpn)) + part.add_mpn(mpn) if sku: - supplier_part_numbers.append(types.SupplierPartNumber(value=mpn, supplier=supplier_uri)) + part.add_spn(sku) for item in ladder_price: price = money.parse(item["price"], currency="USD") - amount = types.Amount(value=price.amount, currency=price.currency) - pbs.append(types.PriceBreak(quantity=item["qty"], amount=amount)) + part.add_price_break(item["qty"], price) db.add_entry(part, True) diff --git a/src/ee/xml/bom_file_utils.py b/src/ee/xml/bom_file_utils.py deleted file mode 100644 index 97b609f..0000000 --- a/src/ee/xml/bom_file_utils.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing import List, Optional - -from ee.xml import types - -__all__ = [ - "facts", - "find_root_tag", - "schematic_references", - "part_numbers", - "supplier_part_numbers", -] - - -def find_root_tag(root): - return next((tag for tag, klass in types.GDSClassesMapping.items() if klass == type(root)), None) - - -def schematic_references(part: types.Part) -> List[types.SchematicReference]: - return [] if part.referencesProp is None or part.referencesProp.schematic_reference is None else \ - part.referencesProp.schematic_reference - - -def part_numbers(part: types.Part) -> List[types.PartNumber]: - return [] if part.referencesProp is None or part.referencesProp.part_number is None else \ - part.referencesProp.part_number - - -def supplier_part_numbers(part: types.Part) -> List[types.SupplierPartNumber]: - return [] if part.referencesProp is None or part.referencesProp.supplier_part_number is None else \ - part.referencesProp.supplier_part_number - - -def facts(part: types.Part, create=False) -> Optional[types.FactList]: - fs: types.FactList = part.factsProp - - if fs is not None: - return fs - - if not create: - return - - fs = types.FactList() - part.factsProp = fs - return fs - - -def find_fact(fs: types.FactList, key: str) -> Optional[types.Fact]: - l: List[types.Fact] = fs.factProp - return next((f for f in l if f.keyProp == key), None) diff --git a/src/ee/xml/types.py b/src/ee/xml/types.py index 60d659d..8e5e39f 100644 --- a/src/ee/xml/types.py +++ b/src/ee/xml/types.py @@ -807,10 +807,11 @@ class PartDb(GeneratedsSuper): class Part(GeneratedsSuper): subclass = None superclass = None - def __init__(self, uri=None, part_type=None, description=None, links=None, references=None, distributor_info=None, facts=None, price_breaks=None, **kwargs_): + def __init__(self, uri=None, supplier=None, part_type=None, description=None, links=None, references=None, distributor_info=None, facts=None, price_breaks=None, **kwargs_): self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') self.uri = _cast(None, uri) + self.supplier = supplier self.part_type = part_type self.description = description self.links = links @@ -829,6 +830,11 @@ class Part(GeneratedsSuper): else: return Part(*args_, **kwargs_) factory = staticmethod(factory) + def get_supplier(self): + return self.supplier + def set_supplier(self, supplier): + self.supplier = supplier + supplierProp = property(get_supplier, set_supplier) def get_part_type(self): return self.part_type def set_part_type(self, part_type): @@ -871,6 +877,7 @@ class Part(GeneratedsSuper): uriProp = property(get_uri, set_uri) def hasContent_(self): if ( + self.supplier is not None or self.part_type is not None or self.description is not None or self.links is not None or @@ -912,6 +919,9 @@ class Part(GeneratedsSuper): eol_ = '\n' else: eol_ = '' + if self.supplier is not None: + showIndent(outfile, level, pretty_print) + outfile.write('<%ssupplier>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.supplier), input_name='supplier')), namespaceprefix_ , eol_)) if self.part_type is not None: showIndent(outfile, level, pretty_print) outfile.write('<%spart-type>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.part_type), input_name='part-type')), namespaceprefix_ , eol_)) @@ -941,7 +951,11 @@ class Part(GeneratedsSuper): already_processed.add('uri') self.uri = value def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): - if nodeName_ == 'part-type': + if nodeName_ == 'supplier': + supplier_ = child_.text + supplier_ = self.gds_validate_string(supplier_, node, 'supplier') + self.supplier = supplier_ + elif nodeName_ == 'part-type': part_type_ = child_.text part_type_ = self.gds_validate_string(part_type_, node, 'part_type') self.part_type = part_type_ @@ -1066,38 +1080,38 @@ class PartList(GeneratedsSuper): # end class PartList -class PartUri(GeneratedsSuper): +class PartReference(GeneratedsSuper): subclass = None superclass = None - def __init__(self, value=None, **kwargs_): + def __init__(self, part_uri=None, **kwargs_): self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') - self.value = value + self.part_uri = part_uri def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( - CurrentSubclassModule_, PartUri) + CurrentSubclassModule_, PartReference) if subclass is not None: return subclass(*args_, **kwargs_) - if PartUri.subclass: - return PartUri.subclass(*args_, **kwargs_) + if PartReference.subclass: + return PartReference.subclass(*args_, **kwargs_) else: - return PartUri(*args_, **kwargs_) + return PartReference(*args_, **kwargs_) factory = staticmethod(factory) - def get_value(self): - return self.value - def set_value(self, value): - self.value = value - valueProp = property(get_value, set_value) + def get_part_uri(self): + return self.part_uri + def set_part_uri(self, part_uri): + self.part_uri = part_uri + part_uriProp = property(get_part_uri, set_part_uri) def hasContent_(self): if ( - self.value is not None + self.part_uri is not None ): return True else: return False - def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='PartUri', pretty_print=True): - imported_ns_def_ = GenerateDSNamespaceDefs_.get('PartUri') + def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='PartReference', pretty_print=True): + imported_ns_def_ = GenerateDSNamespaceDefs_.get('PartReference') if imported_ns_def_ is not None: namespacedef_ = imported_ns_def_ if pretty_print: @@ -1109,24 +1123,24 @@ class PartUri(GeneratedsSuper): showIndent(outfile, level, pretty_print) outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', )) already_processed = set() - self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='PartUri') + self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='PartReference') if self.hasContent_(): outfile.write('>%s' % (eol_, )) - self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='PartUri', pretty_print=pretty_print) + self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='PartReference', pretty_print=pretty_print) showIndent(outfile, level, pretty_print) outfile.write('%s' % (namespaceprefix_, name_, eol_)) else: outfile.write('/>%s' % (eol_, )) - def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='PartUri'): + def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='PartReference'): pass - def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='PartUri', fromsubclass_=False, pretty_print=True): + def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='PartReference', fromsubclass_=False, pretty_print=True): if pretty_print: eol_ = '\n' else: eol_ = '' - if self.value is not None: + if self.part_uri is not None: showIndent(outfile, level, pretty_print) - outfile.write('<%svalue>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.value), input_name='value')), namespaceprefix_ , eol_)) + outfile.write('<%spart-uri>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.part_uri), input_name='part-uri')), namespaceprefix_ , eol_)) def build(self, node): already_processed = set() self.buildAttributes(node, node.attrib, already_processed) @@ -1137,11 +1151,11 @@ class PartUri(GeneratedsSuper): def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): - if nodeName_ == 'value': - value_ = child_.text - value_ = self.gds_validate_string(value_, node, 'value') - self.value = value_ -# end class PartUri + if nodeName_ == 'part-uri': + part_uri_ = child_.text + part_uri_ = self.gds_validate_string(part_uri_, node, 'part_uri') + self.part_uri = part_uri_ +# end class PartReference class PartNumber(GeneratedsSuper): @@ -1225,11 +1239,10 @@ class PartNumber(GeneratedsSuper): class SupplierPartNumber(GeneratedsSuper): subclass = None superclass = None - def __init__(self, value=None, supplier=None, **kwargs_): + def __init__(self, value=None, **kwargs_): self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') self.value = value - self.supplier = supplier def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( @@ -1246,15 +1259,9 @@ class SupplierPartNumber(GeneratedsSuper): def set_value(self, value): self.value = value valueProp = property(get_value, set_value) - def get_supplier(self): - return self.supplier - def set_supplier(self, supplier): - self.supplier = supplier - supplierProp = property(get_supplier, set_supplier) def hasContent_(self): if ( - self.value is not None or - self.supplier is not None + self.value is not None ): return True else: @@ -1290,9 +1297,6 @@ class SupplierPartNumber(GeneratedsSuper): if self.value is not None: showIndent(outfile, level, pretty_print) outfile.write('<%svalue>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.value), input_name='value')), namespaceprefix_ , eol_)) - if self.supplier is not None: - showIndent(outfile, level, pretty_print) - outfile.write('<%ssupplier>%s%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.supplier), input_name='supplier')), namespaceprefix_ , eol_)) def build(self, node): already_processed = set() self.buildAttributes(node, node.attrib, already_processed) @@ -1307,10 +1311,6 @@ class SupplierPartNumber(GeneratedsSuper): value_ = child_.text value_ = self.gds_validate_string(value_, node, 'value') self.value = value_ - elif nodeName_ == 'supplier': - supplier_ = child_.text - supplier_ = self.gds_validate_string(supplier_, node, 'supplier') - self.supplier = supplier_ # end class SupplierPartNumber @@ -1395,17 +1395,17 @@ class SchematicReference(GeneratedsSuper): class ReferencesList(GeneratedsSuper): subclass = None superclass = None - def __init__(self, schematic_reference=None, part_uri=None, part_number=None, supplier_part_number=None, **kwargs_): + def __init__(self, part_reference=None, schematic_reference=None, part_number=None, supplier_part_number=None, **kwargs_): self.original_tagname_ = None self.parent_object_ = kwargs_.get('parent_object_') + if part_reference is None: + self.part_reference = [] + else: + self.part_reference = part_reference if schematic_reference is None: self.schematic_reference = [] else: self.schematic_reference = schematic_reference - if part_uri is None: - self.part_uri = [] - else: - self.part_uri = part_uri if part_number is None: self.part_number = [] else: @@ -1425,6 +1425,19 @@ class ReferencesList(GeneratedsSuper): else: return ReferencesList(*args_, **kwargs_) factory = staticmethod(factory) + def get_part_reference(self): + return self.part_reference + def set_part_reference(self, part_reference): + self.part_reference = part_reference + def add_part_reference(self, value): + self.part_reference.append(value) + def add_part_reference(self, value): + self.part_reference.append(value) + def insert_part_reference_at(self, index, value): + self.part_reference.insert(index, value) + def replace_part_reference_at(self, index, value): + self.part_reference[index] = value + part_referenceProp = property(get_part_reference, set_part_reference) def get_schematic_reference(self): return self.schematic_reference def set_schematic_reference(self, schematic_reference): @@ -1438,19 +1451,6 @@ class ReferencesList(GeneratedsSuper): def replace_schematic_reference_at(self, index, value): self.schematic_reference[index] = value schematic_referenceProp = property(get_schematic_reference, set_schematic_reference) - def get_part_uri(self): - return self.part_uri - def set_part_uri(self, part_uri): - self.part_uri = part_uri - def add_part_uri(self, value): - self.part_uri.append(value) - def add_part_uri(self, value): - self.part_uri.append(value) - def insert_part_uri_at(self, index, value): - self.part_uri.insert(index, value) - def replace_part_uri_at(self, index, value): - self.part_uri[index] = value - part_uriProp = property(get_part_uri, set_part_uri) def get_part_number(self): return self.part_number def set_part_number(self, part_number): @@ -1479,8 +1479,8 @@ class ReferencesList(GeneratedsSuper): supplier_part_numberProp = property(get_supplier_part_number, set_supplier_part_number) def hasContent_(self): if ( + self.part_reference or self.schematic_reference or - self.part_uri or self.part_number or self.supplier_part_number ): @@ -1515,10 +1515,10 @@ class ReferencesList(GeneratedsSuper): eol_ = '\n' else: eol_ = '' + for part_reference_ in self.part_reference: + part_reference_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='part-reference', pretty_print=pretty_print) for schematic_reference_ in self.schematic_reference: schematic_reference_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='schematic-reference', pretty_print=pretty_print) - for part_uri_ in self.part_uri: - part_uri_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='part-uri', pretty_print=pretty_print) for part_number_ in self.part_number: part_number_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='part-number', pretty_print=pretty_print) for supplier_part_number_ in self.supplier_part_number: @@ -1533,16 +1533,16 @@ class ReferencesList(GeneratedsSuper): def buildAttributes(self, node, attrs, already_processed): pass def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): - if nodeName_ == 'schematic-reference': + if nodeName_ == 'part-reference': + obj_ = PartReference.factory(parent_object_=self) + obj_.build(child_) + self.part_reference.append(obj_) + obj_.original_tagname_ = 'part-reference' + elif nodeName_ == 'schematic-reference': obj_ = SchematicReference.factory(parent_object_=self) obj_.build(child_) self.schematic_reference.append(obj_) obj_.original_tagname_ = 'schematic-reference' - elif nodeName_ == 'part-uri': - obj_ = PartUri.factory(parent_object_=self) - obj_.build(child_) - self.part_uri.append(obj_) - obj_.original_tagname_ = 'part-uri' elif nodeName_ == 'part-number': obj_ = PartNumber.factory(parent_object_=self) obj_.build(child_) @@ -2437,7 +2437,7 @@ __all__ = [ "PartDb", "PartList", "PartNumber", - "PartUri", + "PartReference", "PriceBreak", "PriceBreakList", "ReferencesList", -- cgit v1.2.3