import argparse from itertools import * from functools import total_ordering import ee.digikey as dk from ee.digikey import SearchResponseTypes, DigikeyProduct from ee.tools import log @total_ordering class Query(object): def __init__(self, is_mpn, query): self.is_mpn = is_mpn self.query = query def __eq__(self, other): return (self.is_mpn, self.query) == (other.is_mpn, other.query) def __lt__(self, other): return (self.is_mpn, self.query) < (other.is_mpn, other.query) def __hash__(self): return hash((self.is_mpn, self.query)) parser = argparse.ArgumentParser(description="Download facts about parts from Digi-Key") parser.add_argument("--out", required=True, metavar="OUTPUT_DIRECTORY", dest="out", action="store", help="A directory to store fact files") parser.add_argument("--part", nargs="+", help="the parts to download fact for") parser.add_argument("--sch") parser.add_argument("--force", dest="force", action="store", help="Always download fact even if there is a local file") args = parser.parse_args() digikey = dk.Digikey() client = dk.DigikeyClient(digikey, on_download=log.debug) repo = dk.DigikeyRepository(digikey, args.out) def on_product(product: DigikeyProduct): repo.save(product) queries = [] if args.part: [queries.append(Query(True, p)) for p in args.part] if args.sch: from ee.kicad import read_schematics, to_bom sch = read_schematics(args.sch) for c in to_bom(sch): digikey = c.get_field("digikey") if digikey: queries.append(Query(False, digikey.value)) mpn = c.get_field("mpn") if mpn: queries.append(Query(True, mpn.value)) queries = sorted(set(queries)) for q in queries: p = q.query if repo.has_product(mpn=p if q.is_mpn else None, dpn=p if not q.is_mpn else None) and not args.force: log.info("Already have facts for {}".format(p)) continue log.info("Searching for {}".format(p)) response = client.search(p) todos = [] if response.response_type == SearchResponseTypes.SINGLE: p = response.products[0] log.info("Direct match mpn/dpn: {}/{}".format(p.mpn, p.part_number)) on_product(p) elif response.response_type == SearchResponseTypes.MANY: # A search for "FOO" might return products "FOO" and "FOOT" so we pick out the ones with the matching mpn/dpn. if q.is_mpn: viable_products = [p for p in response.products if p.mpn == q.query] else: viable_products = [p for p in response.products if p.part_number == q.query] if len(viable_products): # Pick the first one, should be as good as any part_number = sorted(viable_products, key=lambda p: p.part_number)[0].part_number log.info("Got many hits for term '{}', will use {} for downloading attributes.".format(q.query, part_number)) todos.append(part_number) else: log.warn("Got many results: {}".format(", ".join([p.part_number for p in response.products]))) elif response.response_type == SearchResponseTypes.TOO_MANY: log.warn("Too many results ({}), select a category first".format(response.count)) elif response.response_type == SearchResponseTypes.NO_MATCHES: log.warn("Part not found") for part_number in todos: response = client.search(part_number) if response.response_type == SearchResponseTypes.SINGLE: p = sorted(response.products, key=lambda p: p.part_number)[0] log.info("Downloaded {}".format(p.mpn)) on_product(p) else: log.warn("Got response of type {} when really expecting a single product.".format(response.response_type))