diff options
-rw-r--r-- | src/ee/db.py | 143 | ||||
-rw-r--r-- | src/ee/digikey/__init__.py | 41 | ||||
-rw-r--r-- | src/ee/digikey/search_parts.py | 13 | ||||
-rw-r--r-- | src/ee/order/__init__.py | 119 | ||||
-rw-r--r-- | src/ee/order/templates/order.rst.j2 | 60 | ||||
-rw-r--r-- | src/ee/part/__init__.py | 5 | ||||
-rw-r--r-- | src/ee/project/report.py | 25 | ||||
-rw-r--r-- | src/ee/xml/types.py | 221 | ||||
-rw-r--r-- | xsd/ee.xsd | 14 |
9 files changed, 573 insertions, 68 deletions
diff --git a/src/ee/db.py b/src/ee/db.py new file mode 100644 index 0000000..e471278 --- /dev/null +++ b/src/ee/db.py @@ -0,0 +1,143 @@ +from typing import TypeVar, Generic, Callable, MutableMapping, List, Iterable, Union, Any + +K = TypeVar('K') +V = TypeVar('V') + +KeyCallable = Callable[[V], Union[K, Iterable[K]]] + +__all__ = [ + "Index", + "ListIndex", + "UniqueIndex", + "ObjDb", +] + + +class Index(Generic[K, V]): + def add(self, value: V): + pass + + def get(self, key: K) -> Iterable[V]: + pass + + def clear(self): + pass + + def __iter__(self): + pass + + def items(self): + pass + + def values(self): + pass + + +class ListIndex(Index[K, V]): + def __init__(self, key_callable: KeyCallable): + self.idx: MutableMapping[K, V] = {} + self.key_callable = key_callable + + def add(self, value: V): + keys = self.key_callable(value) + + if keys is None: + return + + for key in keys: + if key is None: + continue + + values = self.idx.get(key, None) + if values is not None: + values.append(value) + else: + self.idx[key] = [value] + + def get(self, key: K) -> Iterable[V]: + return [self.idx[key]] + + 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 UniqueIndex(Index[K, V]): + def __init__(self, key_callable: KeyCallable): + self.idx: MutableMapping[K, V] = {} + self.key_callable = key_callable + + def get(self, key: K) -> Iterable[V]: + return [self.idx[key]] + + def get_single(self, key: K) -> V: + return self.idx[key] + + def add(self, value: V): + keys = self.key_callable(value) + + if not isinstance(keys, Iterable): + 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)) + + self.idx[key] = True + + 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 ObjDb(Generic[V]): + def __init__(self): + self.values: List[V] = [] + self._indexes: MutableMapping[str, Index[Any, V]] = {} + + def add(self, value: V): + for idx in self._indexes.values(): + idx.add(value) + + self.values.append(value) + + def __iter__(self): + return self.values.__iter__() + + 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 + + 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] diff --git a/src/ee/digikey/__init__.py b/src/ee/digikey/__init__.py index d0405f1..2fe7443 100644 --- a/src/ee/digikey/__init__.py +++ b/src/ee/digikey/__init__.py @@ -79,6 +79,13 @@ class PriceBreak(object): self.per_quantity_price = per_quantity_price +class Document(object): + def __init__(self, kind: str, title: str, url: str): + self.kind = kind + self.title = title + self.url = url + + @total_ordering class DigikeyProduct(object): def __init__(self, part_number, mpn, url, attributes: List["DigikeyAttributeValue"] = None, categories=None): @@ -90,6 +97,7 @@ class DigikeyProduct(object): self.quantity_available = None self.description = None self.price_breaks: List[PriceBreak] = [] + self.documents: List[Document] = [] assert self.part_number assert self.mpn @@ -303,6 +311,7 @@ class DigikeyParser(object): if part_number and mpn: p = DigikeyProduct(part_number, mpn, url, attributes, categories) p.price_breaks = self._parse_price_breaks(tree) + p.documents = self._parse_documents(tree) for n in tree.xpath("//*[@itemprop='description']"): p.description = _to_string(n) return p @@ -348,6 +357,38 @@ class DigikeyParser(object): return price_breaks if ok else [] @staticmethod + def _parse_documents(tree: html) -> List[Document]: + docs = [] + + for row in tree.xpath("//*[@class='product-details-documents-media product-details-section']//tr"): + # print("row={}".format(row)) + kind: str = _first(row.xpath(".//th/text()")) + + if not kind: + continue + + kind = kind.strip() + + for a in row.xpath(".//td//a[not(contains(@class, '-expander-toggle'))]"): + # print("a={}".format(a)) + title = a.text + if not title: + continue + title = title.strip() + + href = a.get("href") + if not href: + continue + + href = href.strip() + if href.startswith("//"): + href = "https:" + href + + docs.append(Document(kind, title, href)) + + return docs + + @staticmethod def _handle_product_table(tree: html, res: DigikeySearchResponse): products = tree.xpath("//*[@itemtype='http://schema.org/Product']") diff --git a/src/ee/digikey/search_parts.py b/src/ee/digikey/search_parts.py index 3d271d5..358266a 100644 --- a/src/ee/digikey/search_parts.py +++ b/src/ee/digikey/search_parts.py @@ -13,9 +13,19 @@ def resolved(p: DigikeyProduct) -> types.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 + + if p.url: + 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)) supplier_part_numbers = part.referencesProp.supplier_part_numberProp supplier_part_numbers.append(types.SupplierPartNumber(value=p.part_number, supplier=DIGIKEY_URI)) @@ -48,7 +58,8 @@ def search_parts(in_path: Path, out_path: Path, cache_dir: Path): 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) + 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) is_mpn = query = None diff --git a/src/ee/order/__init__.py b/src/ee/order/__init__.py index 1652f24..a384cb7 100644 --- a/src/ee/order/__init__.py +++ b/src/ee/order/__init__.py @@ -1,9 +1,9 @@ -from functools import total_ordering -from itertools import groupby +import os.path from pathlib import Path 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 @@ -11,96 +11,105 @@ from ee.xml import types, bom_file_utils __all__ = ["create_order"] -@total_ordering -class PartInfo(object): +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.pn = next((p.valueProp for p in bom_file_utils.part_numbers(part)), None) self.available_from: List[Tuple[str, types.Part]] = [] + self.selected_supplier_and_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 __lt__(self, other: "PartInfo"): - return self.ref == other.ref + +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() + + kwargs = { + "order_parts": order_parts, + "parts_by_supplier": parts_by_supplier, + "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], fail_on_missing_parts: bool): - messages = [] - sch_db = load_db(schematic_path) dbs = [(path.parent.name, load_db(path)) for path in part_db_dirs] - out_parts = PartDb() + 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]) - infos = [PartInfo(sch) for sch in sch_db.iterparts()] + def supplier_part_index(op: OrderPart): + return set([s_part.uriProp for s, s_part in op.available_from]) - for info in infos: - sch_part_numbers = info.part.referencesProp.part_number - sch_supplier_part_numbers: List[types.SupplierPartNumber] = info.part.referencesProp.supplier_part_number + def pn_index(op: OrderPart): + return [pn.value for pn in bom_file_utils.part_numbers(op.part)] + + order_parts: ObjDb[OrderPart] = ObjDb[OrderPart]() + 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 for supplier, db in dbs: for supplier_part in db.iterparts(sort=True): - found = None for sch_pn in sch_part_numbers: for pn in bom_file_utils.part_numbers(supplier_part): if sch_pn.valueProp == pn.valueProp: - found = supplier_part - break - if found: - break - - if found: - info.available_from.append((supplier, found)) - break + order_part.available_from.append((supplier, supplier_part)) - found = None 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: - found = supplier_part - break - if found: - break - - if found: - info.available_from.append((supplier, found)) - break - - for info in infos: - if len(info.available_from): - strings = ["{} from {}".format(part.uri, s) for s, part in info.available_from] - text = "Resolved {} to {}".format(info.ref, ", ".join(strings)) - messages.append(report.Message(object=info.ref, text=text)) - - has_missing_parts = False - for info in infos: - if len(info.available_from) == 0: - has_missing_parts = True - text = "Could not find {} in any database, part numbers: {}". \ - format(info.ref, ", ".join([p.value for p in bom_file_utils.part_numbers(info.part)])) - messages.append(report.Message(object=info.ref, text=text)) - - if has_missing_parts and fail_on_missing_parts: + order_part.available_from.append((supplier, 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] + else: + raise EeException("unimplemented: part available from multiple suppliers") + + if has_unresolved_parts and fail_on_missing_parts: raise EeException("The order has parts that can't be found from any supplier") - for info in infos: - if len(info.available_from) == 0: + 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) + + out_parts = PartDb() + for order_part in order_parts: + if not order_part.selected_supplier_and_part: continue - supplier, supplier_part = info.available_from[0] + supplier, supplier_part = order_part.selected_supplier_and_part - references = types.ReferencesList(schematic_reference=info.part.referencesProp.schematic_reference) + references = types.ReferencesList(schematic_reference=order_part.part.referencesProp.schematic_reference) part = types.Part(uri=supplier_part.uri, references=references) out_parts.add_entry(part, True) save_db(out_path, out_parts) - # messages - kwargs = {"messages_by_object": groupby(messages, lambda m: m.object)} - report.save_report("ee.order", "order.rst.j2", project.report_dir / "order.rst", **kwargs) diff --git a/src/ee/order/templates/order.rst.j2 b/src/ee/order/templates/order.rst.j2 index ea1dbdf..6616167 100644 --- a/src/ee/order/templates/order.rst.j2 +++ b/src/ee/order/templates/order.rst.j2 @@ -1,9 +1,55 @@ -Messages -======== -{% for o, messages in messages_by_object %} -{{ o | subsection }} -{% for m in messages %} -* {{ m.text }} -{% endfor %} +Order +===== + +Has unresolved parts: {{ "yes" if has_unresolved_parts else "no" }}. + +Parts for Order +=============== +{% for op in order_parts %} +{{ op.ref | subsection }} +{% if op.available_from|length == 0 %} +Part not resolved. +{% elif op.available_from|length == 1 %} +{%- set from=op.available_from[0] %} +{%- set part=from[1] %} +{%- set pn=part|first_pn %} +{%- set spn=part|first_spn %} +Selected supplier: {{ from[0] }}{{ (", pn: " + pn.valueProp) if pn else "" }}{{ (", spn: " + spn.valueProp) if spn else "" }}. +Part: part-{{pn.valueProp}}_ +{% else %} +MANY +{% endif %} +{%- endfor %} + +Part details +============ +{% for supplier, parts in parts_by_supplier.items() %} +{{ ("From " + supplier) | subsection }} +{% for part in parts %} +{%- 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 }} +{#- + +"Facts" +....... + +{% for f in part.facts.fact %} +f={{f}} {% endfor %} +#} + +"Media" +....... +{% for l in links %} +{%- if l.relationProp == "http://purl.org/ee/link-relation#documentation" %} +* `{{ l.title }} <{{ l.url }}>`__ +{%- endif %} +{%- endfor %} +{% endfor %} +{% endfor %} diff --git a/src/ee/part/__init__.py b/src/ee/part/__init__.py index 36a3d3c..4ed6edb 100644 --- a/src/ee/part/__init__.py +++ b/src/ee/part/__init__.py @@ -23,9 +23,8 @@ class Entry(object): class PartDb(object): def __init__(self): - self.parts = [] # type: List[Entry] - self.pn_index = {} # type: MutableMapping[str, Entry] - self.dpn_indexes = {} # type: MutableMapping[str, MutableMapping[str, Entry]] + self.parts: List[Entry] = [] + self.pn_index: MutableMapping[str, Entry] = {} self.new_entries = 0 def add_entry(self, part: types.Part, new: bool): diff --git a/src/ee/project/report.py b/src/ee/project/report.py index 5370f6c..80e590a 100644 --- a/src/ee/project/report.py +++ b/src/ee/project/report.py @@ -4,6 +4,7 @@ from typing import Optional from jinja2 import Environment, PackageLoader, select_autoescape from ee.tools import mk_parents +from ee.xml import types, bom_file_utils class Message(object): @@ -12,16 +13,40 @@ class Message(object): self.text = text +def section_filter(s: str): + return "{}\n{}".format(s, "=" * len(s)) + + def subsection_filter(s: str): + return "{}\n{}".format(s, "-" * len(s)) + + +def subsubsection_filter(s: str): return "{}\n{}".format(s, "~" * len(s)) +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_spn_filter(part: types.Part): + return next(iter(bom_file_utils.supplier_part_numbers(part)), None) + + def save_report(package: str, template_name: str, out_file: Path, **kwargs): env = Environment( loader=PackageLoader(package, "templates"), autoescape=select_autoescape(["html", "xml"]), ) env.filters["subsection"] = subsection_filter + env.filters["subsubsection"] = subsubsection_filter + env.filters["subsubsubsection"] = subsubsubsection_filter + env.filters["first_pn"] = first_pn_filter + env.filters["first_spn"] = first_spn_filter mk_parents(out_file) with out_file.open("w") as f: diff --git a/src/ee/xml/types.py b/src/ee/xml/types.py index a1ab7ec..60d659d 100644 --- a/src/ee/xml/types.py +++ b/src/ee/xml/types.py @@ -3,7 +3,7 @@ # # Generated by generateDS.py. -# Python 3.7.2+ (default, Feb 27 2019, 15:41:59) [GCC 8.2.0] +# Python 3.7.2+ (default, Feb 2 2019, 14:31:48) [GCC 8.2.0] # # Command line options: # ('-f', '') @@ -807,12 +807,13 @@ class PartDb(GeneratedsSuper): class Part(GeneratedsSuper): subclass = None superclass = None - def __init__(self, uri=None, part_type=None, description=None, references=None, distributor_info=None, facts=None, price_breaks=None, **kwargs_): + def __init__(self, uri=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.part_type = part_type self.description = description + self.links = links self.references = references self.distributor_info = distributor_info self.facts = facts @@ -838,6 +839,11 @@ class Part(GeneratedsSuper): def set_description(self, description): self.description = description descriptionProp = property(get_description, set_description) + def get_links(self): + return self.links + def set_links(self, links): + self.links = links + linksProp = property(get_links, set_links) def get_references(self): return self.references def set_references(self, references): @@ -867,6 +873,7 @@ class Part(GeneratedsSuper): if ( self.part_type is not None or self.description is not None or + self.links is not None or self.references is not None or self.distributor_info is not None or self.facts is not None or @@ -911,6 +918,8 @@ class Part(GeneratedsSuper): if self.description is not None: showIndent(outfile, level, pretty_print) outfile.write('<%sdescription>%s</%sdescription>%s' % (namespaceprefix_ , self.gds_encode(self.gds_format_string(quote_xml(self.description), input_name='description')), namespaceprefix_ , eol_)) + if self.links is not None: + self.links.export(outfile, level, namespaceprefix_, namespacedef_='', name_='links', pretty_print=pretty_print) if self.references is not None: self.references.export(outfile, level, namespaceprefix_, namespacedef_='', name_='references', pretty_print=pretty_print) if self.distributor_info is not None: @@ -940,6 +949,11 @@ class Part(GeneratedsSuper): description_ = child_.text description_ = self.gds_validate_string(description_, node, 'description') self.description = description_ + elif nodeName_ == 'links': + obj_ = LinkList.factory(parent_object_=self) + obj_.build(child_) + self.links = obj_ + obj_.original_tagname_ = 'links' elif nodeName_ == 'references': obj_ = ReferencesList.factory(parent_object_=self) obj_.build(child_) @@ -2082,6 +2096,207 @@ class PriceBreakList(GeneratedsSuper): # end class PriceBreakList +class Link(GeneratedsSuper): + subclass = None + superclass = None + def __init__(self, url=None, relation=None, media_type=None, title=None, **kwargs_): + self.original_tagname_ = None + self.parent_object_ = kwargs_.get('parent_object_') + self.url = _cast(None, url) + self.relation = _cast(None, relation) + self.media_type = _cast(None, media_type) + self.title = _cast(None, title) + def factory(*args_, **kwargs_): + if CurrentSubclassModule_ is not None: + subclass = getSubclassFromModule_( + CurrentSubclassModule_, Link) + if subclass is not None: + return subclass(*args_, **kwargs_) + if Link.subclass: + return Link.subclass(*args_, **kwargs_) + else: + return Link(*args_, **kwargs_) + factory = staticmethod(factory) + def get_url(self): + return self.url + def set_url(self, url): + self.url = url + urlProp = property(get_url, set_url) + def get_relation(self): + return self.relation + def set_relation(self, relation): + self.relation = relation + relationProp = property(get_relation, set_relation) + def get_media_type(self): + return self.media_type + def set_media_type(self, media_type): + self.media_type = media_type + media_typeProp = property(get_media_type, set_media_type) + def get_title(self): + return self.title + def set_title(self, title): + self.title = title + titleProp = property(get_title, set_title) + def hasContent_(self): + if ( + + ): + return True + else: + return False + def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='Link', pretty_print=True): + imported_ns_def_ = GenerateDSNamespaceDefs_.get('Link') + if imported_ns_def_ is not None: + namespacedef_ = imported_ns_def_ + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + if self.original_tagname_ is not None: + name_ = self.original_tagname_ + 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_='Link') + if self.hasContent_(): + outfile.write('>%s' % (eol_, )) + self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='Link', pretty_print=pretty_print) + outfile.write('</%s%s>%s' % (namespaceprefix_, name_, eol_)) + else: + outfile.write('/>%s' % (eol_, )) + def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='Link'): + if self.url is not None and 'url' not in already_processed: + already_processed.add('url') + outfile.write(' url=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.url), input_name='url')), )) + if self.relation is not None and 'relation' not in already_processed: + already_processed.add('relation') + outfile.write(' relation=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.relation), input_name='relation')), )) + if self.media_type is not None and 'media_type' not in already_processed: + already_processed.add('media_type') + outfile.write(' media-type=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.media_type), input_name='media-type')), )) + if self.title is not None and 'title' not in already_processed: + already_processed.add('title') + outfile.write(' title=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.title), input_name='title')), )) + def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='Link', fromsubclass_=False, pretty_print=True): + pass + def build(self, node): + already_processed = set() + self.buildAttributes(node, node.attrib, already_processed) + for child in node: + nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] + self.buildChildren(child, node, nodeName_) + return self + def buildAttributes(self, node, attrs, already_processed): + value = find_attr_value_('url', node) + if value is not None and 'url' not in already_processed: + already_processed.add('url') + self.url = value + value = find_attr_value_('relation', node) + if value is not None and 'relation' not in already_processed: + already_processed.add('relation') + self.relation = value + value = find_attr_value_('media-type', node) + if value is not None and 'media-type' not in already_processed: + already_processed.add('media-type') + self.media_type = value + value = find_attr_value_('title', node) + if value is not None and 'title' not in already_processed: + already_processed.add('title') + self.title = value + def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): + pass +# end class Link + + +class LinkList(GeneratedsSuper): + subclass = None + superclass = None + def __init__(self, link=None, **kwargs_): + self.original_tagname_ = None + self.parent_object_ = kwargs_.get('parent_object_') + if link is None: + self.link = [] + else: + self.link = link + def factory(*args_, **kwargs_): + if CurrentSubclassModule_ is not None: + subclass = getSubclassFromModule_( + CurrentSubclassModule_, LinkList) + if subclass is not None: + return subclass(*args_, **kwargs_) + if LinkList.subclass: + return LinkList.subclass(*args_, **kwargs_) + else: + return LinkList(*args_, **kwargs_) + factory = staticmethod(factory) + def get_link(self): + return self.link + def set_link(self, link): + self.link = link + def add_link(self, value): + self.link.append(value) + def add_link(self, value): + self.link.append(value) + def insert_link_at(self, index, value): + self.link.insert(index, value) + def replace_link_at(self, index, value): + self.link[index] = value + linkProp = property(get_link, set_link) + def hasContent_(self): + if ( + self.link + ): + return True + else: + return False + def export(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='LinkList', pretty_print=True): + imported_ns_def_ = GenerateDSNamespaceDefs_.get('LinkList') + if imported_ns_def_ is not None: + namespacedef_ = imported_ns_def_ + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + if self.original_tagname_ is not None: + name_ = self.original_tagname_ + 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_='LinkList') + if self.hasContent_(): + outfile.write('>%s' % (eol_, )) + self.exportChildren(outfile, level + 1, namespaceprefix_, namespacedef_, name_='LinkList', pretty_print=pretty_print) + showIndent(outfile, level, pretty_print) + outfile.write('</%s%s>%s' % (namespaceprefix_, name_, eol_)) + else: + outfile.write('/>%s' % (eol_, )) + def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='LinkList'): + pass + def exportChildren(self, outfile, level, namespaceprefix_='', namespacedef_='', name_='LinkList', fromsubclass_=False, pretty_print=True): + if pretty_print: + eol_ = '\n' + else: + eol_ = '' + for link_ in self.link: + link_.export(outfile, level, namespaceprefix_, namespacedef_='', name_='link', pretty_print=pretty_print) + def build(self, node): + already_processed = set() + self.buildAttributes(node, node.attrib, already_processed) + for child in node: + nodeName_ = Tag_pattern_.match(child.tag).groups()[-1] + self.buildChildren(child, node, nodeName_) + return self + def buildAttributes(self, node, attrs, already_processed): + pass + def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): + if nodeName_ == 'link': + obj_ = Link.factory(parent_object_=self) + obj_.build(child_) + self.link.append(obj_) + obj_.original_tagname_ = 'link' +# end class LinkList + + GDSClassesMapping = { 'part': Part, 'part-db': PartDb, @@ -2216,6 +2431,8 @@ __all__ = [ "DistributorInfo", "Fact", "FactList", + "Link", + "LinkList", "Part", "PartDb", "PartList", @@ -47,6 +47,7 @@ TODO: rename 'id' to 'url'. <!-- TODO: this should be a fact --> <xs:element name="part-type" type="xs:anyURI"/> <xs:element name="description" type="xs:string"/> + <xs:element name="links" type="LinkList"/> <xs:element name="references" type="ReferencesList"/> <xs:element name="distributor-info" type="DistributorInfo"/> <xs:element name="facts" type="FactList"/> @@ -135,4 +136,17 @@ TODO: rename 'id' to 'url'. </xs:sequence> </xs:complexType> + <xs:complexType name="Link"> + <xs:attribute name="url" type="xs:anyURI"/> + <xs:attribute name="relation" type="xs:string"/> + <xs:attribute name="media-type" type="xs:string"/> + <xs:attribute name="title" type="xs:string"/> + </xs:complexType> + + <xs:complexType name="LinkList"> + <xs:sequence> + <xs:element name="link" type="Link" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:schema> |