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.xml.uris import DIGIKEY_URI, make_digikey_fact_key __all__ = ["search_parts"] 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(), facts=types.FactList(), references=types.ReferencesList()) part.distributor_infoProp.stateProp = "resolved" supplier_part_numbers = part.referencesProp.supplier_part_numberProp supplier_part_numbers.append(types.SupplierPartNumber(value=p.part_number, supplier=DIGIKEY_URI)) part_numbers = part.referencesProp.part_numberProp if p.mpn: part_numbers.append(types.PartNumber(value=p.mpn)) facts: List[types.Fact] = part.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() price_breaks: List[types.PriceBreak] = part.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)) return part def search_parts(in_path: Path, out_path: Path, cache_dir: Path): in_db = load_db(in_path) out_parts = PartDb() 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) is_mpn = query = None if dpn is not None: query = dpn is_mpn = False elif mpn is not None: query = mpn is_mpn = True if query is None: # TODO: use schematic reference print("could not find pn or dpn: part.uri={}".format(part.uriProp)) continue out_id = query out_part = types.Part(uri=out_id, distributor_info=types.DistributorInfo(), references=part.referencesProp) di = out_part.distributor_infoProp text = client.search(query) response = parser.parse_string(text) if response.response_type == SearchResponseTypes.SINGLE: out_part = resolved(response.products[0]) elif response.response_type == SearchResponseTypes.MANY: # find those with an exact match. Digikey uses a prefix search so a query for "FOO" will return "FOO" # and "FOOT". def get_field(p): return p.mpn if is_mpn else p.part_number filtered_products = [p for p in response.products if get_field(p) == query] if len(filtered_products) == 0: di.stateProp = "not-found" else: dpn = sorted(filtered_products, key=lambda p: p.part_number)[0].part_number response = parser.parse_string(client.search(dpn)) if response.response_type == SearchResponseTypes.SINGLE: out_part = resolved(response.products[0]) else: di.stateProp = "many" elif response.response_type == SearchResponseTypes.TOO_MANY: di.stateProp = "too-many" elif response.response_type == SearchResponseTypes.NO_MATCHES: di.stateProp = "not-found" out_parts.add_entry(out_part, True) print("Saving {} work parts".format(out_parts.size())) save_db(out_path, out_parts, sort=True)