From e667fbe56c39e6149e58242a4ed88b28a8278c15 Mon Sep 17 00:00:00 2001 From: David Rice Date: Thu, 9 Apr 2026 15:27:23 +0100 Subject: [PATCH] Updates --- nexarxclaude.py | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/nexarxclaude.py b/nexarxclaude.py index 6f1828b..326e542 100644 --- a/nexarxclaude.py +++ b/nexarxclaude.py @@ -100,9 +100,11 @@ query GetSheetComponents($projectId: ID!, $cursor: String) { component { name description - manufacturerParts { - partNumber - companyName + details { + parameters { + name + value + } } } } @@ -256,6 +258,19 @@ def _prefix_match(a: str, b: str) -> bool: return bool(na and nb and (na == nb or na.startswith(nb) or nb.startswith(na))) +def _mpn_match(a: str, b: str) -> bool: + """True if MPNs match, accounting for packaging suffix (last character) differences.""" + na, nb = normalise(a), normalise(b) + if not na or not nb: + return False + if na == nb or na.startswith(nb) or nb.startswith(na): + return True + # Strip last character (e.g. R=reel, T=tape) and retry + if len(na) > 1 and len(nb) > 1 and na[:-1] == nb[:-1]: + return True + return False + + def load_bom_data(bom_path: str) -> list | None: """Load BOM xlsx and return a list of entry dicts. Returns None if no 'bom' tab is found.""" @@ -297,7 +312,7 @@ def _find_bom_match(bom_data: list, mfr_name: str, mpn: str) -> dict | None: """Return the highest unit cost BOM entry that prefix-matches both manufacturer and MPN.""" candidates = [ e for e in bom_data - if _prefix_match(e["manufacturer"], mfr_name) and _prefix_match(e["mpn"], mpn) + if _prefix_match(e["manufacturer"], mfr_name) and _mpn_match(e["mpn"], mpn) ] return max(candidates, key=lambda e: e["unit_cost"]) if candidates else None @@ -331,18 +346,19 @@ def export_to_xlsx(project_name: str, variant_name: str, schematics: list, bom_d nodes = (sheet.get("designItems") or {}).get("nodes") or [] for node in sorted(nodes, key=lambda n: n.get("designator") or ""): comp = node.get("component") or {} - mfr_parts = comp.get("manufacturerParts") or [] + params = {p["name"]: (p.get("value") or "").strip() + for p in (comp.get("details") or {}).get("parameters") or [] + if p.get("name")} - # Collect up to MAX_MFR_PARTS manufacturer/MPN pairs + # Collect up to MAX_MFR_PARTS manufacturer/MPN pairs from named parameters pairs = [] - for mp in mfr_parts[:MAX_MFR_PARTS]: - mfr_name = mp.get("companyName") or "-" - mpn = mp.get("partNumber") or "-" - pairs.append((mfr_name, mpn)) - # Pad with '-' if fewer than MAX_MFR_PARTS - while len(pairs) < MAX_MFR_PARTS: - pairs.append(("-", "-")) - + for n in range(1, MAX_MFR_PARTS + 1): + mfr_name = params.get(f"Manufacturer {n}", "").strip() + mpn = params.get(f"Manufacturer Part Number {n}", "").strip() + if not mfr_name or not mpn or mfr_name == "-" or mpn == "-": + pairs.append(("-", "-")) + else: + pairs.append((mfr_name, mpn)) flat_pairs = [v for pair in pairs for v in pair] row = [node.get("designator") or "", comp.get("name") or "", comp.get("description") or ""] + flat_pairs