From e47a988e624e75ab3c0bac32041668647ab2a0f1 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Tue, 2 Apr 2019 06:43:53 +0200 Subject: o Renaming create-order to create-bom. --- src/ee/bom/__init__.py | 124 ++++++++++++++++++++++++++++++++++ src/ee/bom/templates/bom.rst.j2 | 89 ++++++++++++++++++++++++ src/ee/order/__init__.py | 124 ---------------------------------- src/ee/order/templates/order.rst.j2 | 89 ------------------------ src/ee/project/__init__.py | 4 +- src/ee/tools/create_bom.py | 31 +++++++++ src/ee/tools/create_order.py | 31 --------- src/ee/tools/init.py | 8 +-- src/ee/tools/templates/build.ninja.j2 | 20 +++--- 9 files changed, 260 insertions(+), 260 deletions(-) create mode 100644 src/ee/bom/__init__.py create mode 100644 src/ee/bom/templates/bom.rst.j2 delete mode 100644 src/ee/order/__init__.py delete mode 100644 src/ee/order/templates/order.rst.j2 create mode 100644 src/ee/tools/create_bom.py delete mode 100644 src/ee/tools/create_order.py (limited to 'src') diff --git a/src/ee/bom/__init__.py b/src/ee/bom/__init__.py new file mode 100644 index 0000000..11c2fce --- /dev/null +++ b/src/ee/bom/__init__.py @@ -0,0 +1,124 @@ +import os.path +import pydoc +from pathlib import Path +from typing import List, MutableMapping, Optional + +from ee import EeException +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 + +__all__ = ["create_bom"] + + +class BomPart(object): + def __init__(self, part: types.Part): + self.part = 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 + + +def make_report(out_file, unresolved_parts, bom_parts: ObjDb[BomPart], supplier_parts: ObjDb[Path]): + kwargs = { + "bom_parts": bom_parts, + "supplier_parts": supplier_parts, + "unresolved_parts": unresolved_parts, + } + report.save_report("ee.bom", "bom.rst.j2", out_file, **kwargs) + + +def default_strategy(x): + return x + + +def create_bom(project: Project, schematic_path: Path, out_path: Path, part_dbs: List[Path], + fail_on_missing_parts: bool, strategy_name: Optional[str]): + strategy = default_strategy + if strategy_name: + strategy = pydoc.locate(strategy_name) + if not callable(strategy): + raise EeException("Not a callable: {}, is a {}".format(strategy_name, type(strategy))) + + 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) + bom_parts: ObjDb[BomPart] = ObjDb[BomPart]() + bom_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) + + for sch_part in sch_db.iterparts(): + part = BomPart(sch_part) + part.part = strategy(part.part) + if part.part is None: + continue + + bom_parts.add(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()] + + for supplier in suppliers: + 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 + 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 + + unresolved_parts = [] + for bom_part in bom_parts: + af = bom_part.available_from + if len(af) == 0: + unresolved_parts.append(bom_part) + elif len(af) == 1: + bom_part.selected_part = next(iter(af.values())) + else: + raise EeException("unimplemented: part ({}) available from multiple suppliers: {}". + format(bom_part.ref, ",".join(af.keys()))) + + bom_parts.add_index("uri", lambda op: op.selected_part.uri if op.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) + + if len(unresolved_parts) and fail_on_missing_parts: + raise EeException("The bom has parts that can't be found from any supplier") + + out_file = project.report_dir / (os.path.splitext(out_path.name)[0] + ".rst") + make_report(out_file, unresolved_parts, bom_parts, supplier_parts) + + out_parts = PartDb() + for bom_part in bom_parts: + if not bom_part.selected_part: + continue + + supplier_part = bom_part.selected_part + + part = Part(types.Part(supplier=supplier_part.supplier)) + part.add_schematic_reference(bom_part.part.get_exactly_one_schematic_reference().referenceProp) + part.add_part_reference(supplier_part.uri) + + out_parts.add_entry(part, True) + + save_db(out_path, out_parts) diff --git a/src/ee/bom/templates/bom.rst.j2 b/src/ee/bom/templates/bom.rst.j2 new file mode 100644 index 0000000..3463d90 --- /dev/null +++ b/src/ee/bom/templates/bom.rst.j2 @@ -0,0 +1,89 @@ +{% set bom_part_uri_idx = bom_parts.index("uri") -%} +BOM +=== + +{% if unresolved_parts %} +Unresolved parts: +{% for op in unresolved_parts %} +* `{{ op.ref }} `_ +{%- endfor %} +{%- endif %} + +Parts for BOM +=============== +{% for op in bom_parts %} +.. _ref-{{ op.ref }}: + +{{ op.ref | subsection }} +{% if op.available_from|length == 0 %} +Could not find part. + +{% if op.part.get_mpns()|length == 1 -%} +MPN: {{ op.part.get_mpns()[0].valueProp }} +{% elif op.part.get_mpns()|length > 1 -%} +{%- for mpn in op.part.get_mpns() %} +MPNs: +* {{ mpn.valueProp }} +{%- endfor %} +{%- endif -%} +{% if op.part.get_spns()|length == 1 -%} +SPN: {{ op.part.get_spns()[0].valueProp }} +{% elif op.part.get_spns()|length > 1 -%} +{%- for spn in op.part.get_spns() %} +SPNs: +* {{ spn.valueProp }} +{%- endfor %} +{%- endif -%} + +{% elif op.available_from|length == 1 %} +{%- set part=op.available_from.values()|first %} +{%- set pn=part|first_pn %} +{%- set spn=part|first_spn %} +Selected supplier: {{ part.supplier }}{{ (", pn: " + pn.valueProp) if pn else "" }}{{ (", spn: " + spn.valueProp) if spn else "" }}. +Part: `{{pn.valueProp}} `_ +{% else %} +MANY +{% endif %} +{%- endfor %} + +Part details +============ +{%- set part_by_uri=supplier_parts.index("uri") %} +{% for supplier, partUris in bom_parts.index("supplier,part").items() %} +{{ ("From " + supplier) | subsection }} +{% 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 "???") %} +.. _part-{{title}}: + +{{ title|subsubsection }} + +=========== === +{%- if part.description %} +Description {{ part.description }} +{%- endif %} +MPN {{ pn.value }} +SPN {{ spn.value }} +Used by: {% for op in bom_part_uri_idx.get(part.uriProp) %}`{{ op.ref }} `_{{ ", " if not loop.last }}{% endfor %} +=========== === +{# +Facts +..... + +{% for f in part.facts.fact %} +f={{f}} +{% endfor %} + +#} +Documentation +............. + +{% for l in part.get_links() %} +{%- if l.relationProp == "http://purl.org/ee/link-relation#documentation" %} +* `{{ l.title }} <{{ l.url }}>`__ +{%- endif %} +{%- endfor %} +{% endfor %} +{% endfor %} diff --git a/src/ee/order/__init__.py b/src/ee/order/__init__.py deleted file mode 100644 index 1624870..0000000 --- a/src/ee/order/__init__.py +++ /dev/null @@ -1,124 +0,0 @@ -import os.path -import pydoc -from pathlib import Path -from typing import List, MutableMapping, Optional - -from ee import EeException -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 - -__all__ = ["create_order"] - - -class OrderPart(object): - def __init__(self, part: types.Part): - self.part = 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 - - -def make_report(out_file, unresolved_parts, order_parts: ObjDb[OrderPart], supplier_parts: ObjDb[Path]): - kwargs = { - "order_parts": order_parts, - "supplier_parts": supplier_parts, - "unresolved_parts": unresolved_parts, - } - report.save_report("ee.order", "order.rst.j2", out_file, **kwargs) - - -def default_strategy(x): - return x - - -def create_order(project: Project, schematic_path: Path, out_path: Path, part_dbs: List[Path], - fail_on_missing_parts: bool, strategy_name: Optional[str]): - strategy = default_strategy - if strategy_name: - strategy = pydoc.locate(strategy_name) - if not callable(strategy): - raise EeException("Not a callable: {}, is a {}".format(strategy_name, type(strategy))) - - 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) - - for sch_part in sch_db.iterparts(): - part = OrderPart(sch_part) - part.part = strategy(part.part) - if part.part is None: - continue - - order_parts.add(part) - - for order_part in order_parts: - 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 in suppliers: - 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, []): - order_part.available_from[supplier_part.uri] = supplier_part - for sch_spn in sch_supplier_part_numbers: - for supplier_part in spns.get(sch_spn, []): - order_part.available_from[supplier_part.uri] = supplier_part - - unresolved_parts = [] - for order_part in order_parts: - af = order_part.available_from - if len(af) == 0: - unresolved_parts.append(order_part) - elif len(af) == 1: - order_part.selected_part = next(iter(af.values())) - else: - raise EeException("unimplemented: part ({}) available from multiple suppliers: {}". - format(order_part.ref, ",".join(af.keys()))) - - order_parts.add_index("uri", lambda op: op.selected_part.uri if op.selected_part else None) - order_parts.add_multi_index("supplier,part", lambda op: ( - op.selected_part.supplier, op.selected_part.uri) if op.selected_part else None) - - if len(unresolved_parts) and fail_on_missing_parts: - raise EeException("The order has parts that can't be found from any supplier") - - out_file = project.report_dir / (os.path.splitext(out_path.name)[0] + ".rst") - make_report(out_file, unresolved_parts, order_parts, supplier_parts) - - out_parts = PartDb() - for order_part in order_parts: - if not order_part.selected_part: - continue - - supplier_part = order_part.selected_part - - 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) - - save_db(out_path, out_parts) diff --git a/src/ee/order/templates/order.rst.j2 b/src/ee/order/templates/order.rst.j2 deleted file mode 100644 index b46600c..0000000 --- a/src/ee/order/templates/order.rst.j2 +++ /dev/null @@ -1,89 +0,0 @@ -{% set order_part_uri_idx = order_parts.index("uri") -%} -Order -===== - -{% if unresolved_parts %} -Unresolved parts: -{% for op in unresolved_parts %} -* `{{ op.ref }} `_ -{%- endfor %} -{%- endif %} - -Parts for Order -=============== -{% for op in order_parts %} -.. _ref-{{ op.ref }}: - -{{ op.ref | subsection }} -{% if op.available_from|length == 0 %} -Could not find part. - -{% if op.part.get_mpns()|length == 1 -%} -MPN: {{ op.part.get_mpns()[0].valueProp }} -{% elif op.part.get_mpns()|length > 1 -%} -{%- for mpn in op.part.get_mpns() %} -MPNs: -* {{ mpn.valueProp }} -{%- endfor %} -{%- endif -%} -{% if op.part.get_spns()|length == 1 -%} -SPN: {{ op.part.get_spns()[0].valueProp }} -{% elif op.part.get_spns()|length > 1 -%} -{%- for spn in op.part.get_spns() %} -SPNs: -* {{ spn.valueProp }} -{%- endfor %} -{%- endif -%} - -{% elif op.available_from|length == 1 %} -{%- set part=op.available_from.values()|first %} -{%- set pn=part|first_pn %} -{%- set spn=part|first_spn %} -Selected supplier: {{ part.supplier }}{{ (", pn: " + pn.valueProp) if pn else "" }}{{ (", spn: " + spn.valueProp) if spn else "" }}. -Part: `{{pn.valueProp}} `_ -{% else %} -MANY -{% endif %} -{%- endfor %} - -Part details -============ -{%- set part_by_uri=supplier_parts.index("uri") %} -{% for supplier, partUris in order_parts.index("supplier,part").items() %} -{{ ("From " + supplier) | subsection }} -{% 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 "???") %} -.. _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 %} - -#} -Documentation -............. - -{% for l in part.get_links() %} -{%- if l.relationProp == "http://purl.org/ee/link-relation#documentation" %} -* `{{ l.title }} <{{ l.url }}>`__ -{%- endif %} -{%- endfor %} -{% endfor %} -{% endfor %} diff --git a/src/ee/project/__init__.py b/src/ee/project/__init__.py index 1adbbde..24635ee 100644 --- a/src/ee/project/__init__.py +++ b/src/ee/project/__init__.py @@ -54,7 +54,7 @@ class Project(object): return Project(project_dir, cfg) def save(self): - path = self.project_dir / ".ee" / "config" + path = self.project_dir / "eeconfig" mk_parents(path) - with (path).open("w") as f: + with path.open("w") as f: self._cfg.write(f) diff --git a/src/ee/tools/create_bom.py b/src/ee/tools/create_bom.py new file mode 100644 index 0000000..19521ce --- /dev/null +++ b/src/ee/tools/create_bom.py @@ -0,0 +1,31 @@ +import argparse +from pathlib import Path + +from ee.bom import create_bom +from ee.project import Project + +parser = argparse.ArgumentParser() + +parser.add_argument("--schematic", + required=True, + metavar="PART DB") + +parser.add_argument("--out", + required=True, + metavar="PART DB") + +parser.add_argument("--part-db", + nargs="*", + required=True, + metavar="PART DB") + +parser.add_argument("--strategy", + metavar="FUNC") + +args = parser.parse_args() + +project = Project.load() +part_db_dirs = [Path(part_db) for part_db in args.part_db] +fail_on_missing_parts = False + +create_bom(project, Path(args.schematic), Path(args.out), part_db_dirs, fail_on_missing_parts, args.strategy) diff --git a/src/ee/tools/create_order.py b/src/ee/tools/create_order.py deleted file mode 100644 index d740ff2..0000000 --- a/src/ee/tools/create_order.py +++ /dev/null @@ -1,31 +0,0 @@ -import argparse -from pathlib import Path - -from ee.order import create_order -from ee.project import Project - -parser = argparse.ArgumentParser() - -parser.add_argument("--schematic", - required=True, - metavar="PART DB") - -parser.add_argument("--out", - required=True, - metavar="PART DB") - -parser.add_argument("--part-db", - nargs="*", - required=True, - metavar="PART DB") - -parser.add_argument("--strategy", - metavar="FUNC") - -args = parser.parse_args() - -project = Project.load() -part_db_dirs = [Path(part_db) for part_db in args.part_db] -fail_on_missing_parts = False - -create_order(project, Path(args.schematic), Path(args.out), part_db_dirs, fail_on_missing_parts, args.strategy) diff --git a/src/ee/tools/init.py b/src/ee/tools/init.py index 74a67d1..c3d7e41 100644 --- a/src/ee/tools/init.py +++ b/src/ee/tools/init.py @@ -44,9 +44,9 @@ def init(project_dir: Path, basedir: Path, args): init_kicad_project(basedir, cfg, args) init_seeed_opl(project) - if args.create_order_strategy: - project.cfg.add_section("create-order") - project.cfg["create-order"]["strategy"] = args.create_order_strategy + if args.create_bom_strategy: + project.cfg.add_section("create-bom") + project.cfg["create-bom"]["strategy"] = args.create_bom_strategy print("Saving project. Now run 'ee ninja' to generate Ninja build file") project.save() @@ -61,7 +61,7 @@ parser.add_argument("--kicad-bom-strategy", required=False, metavar="PY CALLABLE") -parser.add_argument("--create-order-strategy", +parser.add_argument("--create-bom-strategy", required=False, metavar="PY CALLABLE") diff --git a/src/ee/tools/templates/build.ninja.j2 b/src/ee/tools/templates/build.ninja.j2 index 2a56bdc..23bef37 100644 --- a/src/ee/tools/templates/build.ninja.j2 +++ b/src/ee/tools/templates/build.ninja.j2 @@ -41,9 +41,9 @@ rule element14-normalize-facts description = element14-normalize-facts command = $ee element14-normalize-facts --in $in --out $out -rule create-order - description = create-order - command = $ee create-order --schematic $schematic --part-db $part_dbs --out $out $strategy +rule create-bom + description = create-bom + command = $ee create-bom --schematic $schematic --part-db $part_dbs --out $out $strategy rule import-parts-yaml description = import-parts-yaml $in @@ -79,21 +79,21 @@ build {{ out }}: import-parts-yaml {{ f }} # default {{ out }} {% endfor %} -build ee/order.xml | $report_dir/order.rst: create-order ee/sch.xml {%- for p in part_dbs %} {{ p }}.xml{% endfor %} +build ee/bom.xml | $report_dir/bom.rst: create-bom ee/sch.xml {%- for p in part_dbs %} {{ p }}.xml{% endfor %} schematic = ee/sch.xml part_dbs ={%- for p in part_dbs %} {{ p }}.xml{% endfor %} -{%- if project.cfg["create-order"] and project.cfg["create-order"]["strategy"] %} - strategy = --strategy {{ project.cfg["create-order"]["strategy"] }} +{%- if project.cfg["create-bom"] and project.cfg["create-bom"]["strategy"] %} + strategy = --strategy {{ project.cfg["create-bom"]["strategy"] }} {%- endif %} -default ee/order.xml +default ee/bom.xml rule seeed-download-opl description = seeed-download-opl $opl command = $ee seeed-download-opl --out $out --opl $opl -{%- if project.cfg.has_section("seeed-opl") -%} -{% set opls=project.cfg["seeed-opl"]["opls"].split(",") -%} +{% if project.cfg.has_section("seeed-opl") %} +{%- set opls=project.cfg["seeed-opl"]["opls"].split(",") -%} build seeed-download-opls: phony{%- for opl in opls %} ee/seeed/opl/{{ opl.strip() }}.xml{% endfor %} {%- for opl in opls %} {% set opl=opl.strip() -%} @@ -101,4 +101,4 @@ build seeed-download-opls: phony{%- for opl in opls %} ee/seeed/opl/{{ opl.strip build ee/seeed/opl/{{ opl }}.xml: seeed-download-opl opl = {{ opl }} {% endfor -%} -{% endif %} +{% endif -%} -- cgit v1.2.3