diff options
Diffstat (limited to 'src/ee/digikey')
-rw-r--r-- | src/ee/digikey/__init__.py | 59 | ||||
-rw-r--r-- | src/ee/digikey/search_parts.py | 8 |
2 files changed, 60 insertions, 7 deletions
diff --git a/src/ee/digikey/__init__.py b/src/ee/digikey/__init__.py index 6baae84..f67e6b5 100644 --- a/src/ee/digikey/__init__.py +++ b/src/ee/digikey/__init__.py @@ -13,32 +13,35 @@ from lxml import html from selenium import webdriver import ee._utils +from ee.money import Money, get_default_context from ee.tools import mk_parents +money = get_default_context() -def normalize_filename(part): + +def normalize_filename(part) -> str: return part.replace('/', '_').replace(' ', '_') -def _clean(s): +def _clean(s) -> Optional[str]: if s is None: return None s = s.strip() return None if len(s) == 0 else s -def _to_string(e): +def _to_string(e) -> str: s = "" for t in e.itertext(): s += t return s.strip() -def _parse_int(s): +def _parse_int(s) -> int: return int(s.replace(',', '').replace('.', '')) -def _to_int(s): +def _to_int(s) -> Optional[int]: try: return _parse_int(s) except ValueError: @@ -69,16 +72,24 @@ class Digikey(object): return a +class PriceBreak(object): + def __init__(self, quantity: int, per_piece_price: Money, per_quantity_price: Money): + self.quantity = quantity + self.per_piece_price = per_piece_price + self.per_quantity_price = per_quantity_price + + @total_ordering class DigikeyProduct(object): def __init__(self, part_number, mpn, url, attributes: List["DigikeyAttributeValue"] = None, categories=None): self.part_number = _clean(part_number) self.mpn = _clean(mpn) self.url = url - self.attributes = attributes or [] # type: List["DigikeyAttributeValue"] + self.attributes = attributes or [] # type: List["DigikeyAttributeValue"] self.categories = categories or [] self.quantity_available = None self.description = None + self.price_breaks: List[PriceBreak] = [] assert self.part_number assert self.mpn @@ -214,7 +225,7 @@ class DigikeyClient(object): def __init__(self, cache_dir: Path = None, on_download=None): self.on_download = on_download or self.__nop self.cache = ee._utils.maybe_cache(cache_dir) - self.driver: webdriver.Chrome = None + self.driver: Optional[webdriver.Chrome] = None def search(self, query: str, page_size=10) -> str: return self.product_search(query, page_size) @@ -291,6 +302,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) for n in tree.xpath("//*[@itemprop='description']"): p.description = _to_string(n) return p @@ -298,6 +310,39 @@ class DigikeyParser(object): return None @staticmethod + def _find_currency(tree: html) -> Optional[str]: + for e in tree.xpath("//*[@id='cur-dropdown']"): + s = _clean(e.text) + if s: + return s + + def _parse_price_breaks(self, tree: html) -> List[PriceBreak]: + currency = self._find_currency(tree) + + price_breaks = [] + + ok = True + for row in tree.xpath("//table[@class='product-dollars']//tr"): + tds = list(row.xpath("./td")) + + if len(tds) != 3: + continue + + tds = ["".join(td.xpath("./descendant-or-self::*/text()")) for td in tds] + + quantity = _to_int(tds[0]) + price = money.try_parse(tds[1], currency=currency) + + if quantity is None or price is None: + ok = False + break + + price_breaks.append(PriceBreak(quantity=quantity, per_piece_price=price, + per_quantity_price=price * quantity)) + + return price_breaks if ok else [] + + @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 9c8eb74..61c5c1b 100644 --- a/src/ee/digikey/search_parts.py +++ b/src/ee/digikey/search_parts.py @@ -27,6 +27,14 @@ def resolved(p: DigikeyProduct) -> bomFile.Part: key = make_digikey_fact_key(a.attribute_type.id) facts.append(bomFile.Fact(key=key, label=a.attribute_type.label, value=a.value)) + if len(p.price_breaks): + part.price_breaksProp = bomFile.PriceBreakList() + + price_breaks: List[bomFile.PriceBreak] = part.price_breaksProp.price_break + for pb in p.price_breaks: + amount = bomFile.Amount(value=str(pb.per_piece_price.amount), currency=pb.per_piece_price.currency) + price_breaks.append(bomFile.PriceBreak(pb.quantity, amount=amount)) + return part |