Updates
This commit is contained in:
@@ -252,9 +252,40 @@ def normalise(s: str) -> str:
|
||||
return re.sub(r"[^a-z0-9]", "", s.lower())
|
||||
|
||||
|
||||
def _prefix_match(a: str, b: str) -> bool:
|
||||
"""True if normalised strings match or one is a prefix of the other."""
|
||||
na, nb = normalise(a), normalise(b)
|
||||
|
||||
_MFR_ALIASES = {
|
||||
"ti": "texasinstruments",
|
||||
"texas": "texasinstruments",
|
||||
"texasinstruments": "texasinstruments",
|
||||
"onsemi": "onsemi",
|
||||
"onsemiconductor": "onsemi",
|
||||
"onsemin": "onsemi",
|
||||
"nxp": "nxp",
|
||||
"nxpsemiconductors": "nxp",
|
||||
"st": "stmicro",
|
||||
"stmicro": "stmicro",
|
||||
"stmicroelectronics": "stmicro",
|
||||
"abracon": "abracon",
|
||||
"abraconcorporation": "abracon",
|
||||
"ck": "ck",
|
||||
"ckcomponents": "ck",
|
||||
"murata": "murata",
|
||||
"muratamfg": "murata",
|
||||
"muratamanufacturing": "murata",
|
||||
"kingbright": "kingbright",
|
||||
"kingbight": "kingbright", # known Nexar typo
|
||||
}
|
||||
|
||||
|
||||
def _norm_mfr(s: str) -> str:
|
||||
"""Normalise a manufacturer name, resolving known abbreviations and aliases."""
|
||||
n = normalise(s)
|
||||
return _MFR_ALIASES.get(n, n)
|
||||
|
||||
|
||||
def _mfr_match(a: str, b: str) -> bool:
|
||||
"""True if two manufacturer names refer to the same company (alias-aware, prefix-tolerant)."""
|
||||
na, nb = _norm_mfr(a), _norm_mfr(b)
|
||||
return bool(na and nb and (na == nb or na.startswith(nb) or nb.startswith(na)))
|
||||
|
||||
|
||||
@@ -290,6 +321,8 @@ def _elec_match(a: str, b: str) -> bool:
|
||||
return na == nb or nb.startswith(na) or na.startswith(nb)
|
||||
|
||||
|
||||
|
||||
|
||||
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."""
|
||||
@@ -350,27 +383,29 @@ 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 _mpn_match(e["mpn"], mpn)
|
||||
if _mfr_match(e["manufacturer"], mfr_name) and _mpn_match(e["mpn"], mpn)
|
||||
]
|
||||
return max(candidates, key=lambda e: e["unit_cost"]) if candidates else None
|
||||
|
||||
|
||||
def _extract_r_vals(s: str) -> tuple:
|
||||
"""Extract (resistance_norm, footprint) from a description string using word boundaries."""
|
||||
s = re.sub(r'(\d),(\d)', r'\1.\2', s) # normalise European decimal comma
|
||||
rm = re.search(r'\b(\d+\.?\d*\s*(?:kohms?|mohms?|k[\s\-]?ohms?|ohms?|k|m|r))\b', s, re.IGNORECASE)
|
||||
resistance = _norm_elec(rm.group(1)) if rm else ""
|
||||
fm = re.search(r'\b(0\d{3})\b', s)
|
||||
fm = re.search(r'\b(\d{4})\b', s)
|
||||
footprint = fm.group(1) if fm else ""
|
||||
return resistance, footprint
|
||||
|
||||
|
||||
def _extract_c_vals(s: str) -> tuple:
|
||||
"""Extract (capacitance_norm, voltage_norm, case) from a description string."""
|
||||
s = re.sub(r'(\d),(\d)', r'\1.\2', s) # normalise European decimal comma
|
||||
cm = re.search(r'\b(\d+\.?\d*\s*(?:p|n|u|µ|μ|m)\s*f)\b', s, re.IGNORECASE)
|
||||
capacitance = _norm_elec(cm.group(1)) if cm else ""
|
||||
vm = re.search(r'\b(\d+\.?\d*\s*v)\b', s, re.IGNORECASE)
|
||||
voltage = _norm_elec(vm.group(1)) if vm else ""
|
||||
fm = re.search(r'\b(0\d{3})\b', s)
|
||||
fm = re.search(r'\b(\d{4})\b', s)
|
||||
case = fm.group(1) if fm else ""
|
||||
return capacitance, voltage, case
|
||||
|
||||
@@ -435,13 +470,11 @@ def _find_passive_bom_match(bom_data: list, designator: str, nexar_params: dict,
|
||||
desc = e.get("description", "")
|
||||
if not desc or "cap" not in desc.lower():
|
||||
continue
|
||||
bom_c, bom_v, bom_case = _extract_c_vals(desc)
|
||||
bom_c, _, bom_case = _extract_c_vals(desc)
|
||||
if not bom_c or not _elec_match(bom_c, capacitance):
|
||||
continue
|
||||
if not bom_case or bom_case.lower() != case.lower().strip():
|
||||
continue
|
||||
if voltage and bom_v and not _elec_match(bom_v, voltage):
|
||||
continue
|
||||
candidates.append(e)
|
||||
|
||||
return max(candidates, key=lambda e: e["unit_cost"]) if candidates else None
|
||||
@@ -528,22 +561,10 @@ def export_to_xlsx(project_name: str, variant_name: str, schematics: list, bom_d
|
||||
f"(BOM: '{mpn_in_bom['manufacturer']}' vs Nexar: '{valid_pairs[0][0]}')"
|
||||
)
|
||||
else:
|
||||
# Check if BOM has the manufacturer but with a different MPN
|
||||
mfr_in_bom = next(
|
||||
(e for e in bom_data for mfr, _ in valid_pairs if _prefix_match(e["manufacturer"], mfr)),
|
||||
None
|
||||
print(
|
||||
f" NO MATCH [{designator}] {ref}: "
|
||||
f"Not on Manufacturer's BoM"
|
||||
)
|
||||
if mfr_in_bom:
|
||||
print(
|
||||
f" NO MATCH [{designator}] {ref}: "
|
||||
f"manufacturer '{mfr_in_bom['manufacturer']}' found in BOM "
|
||||
f"but MPN differs (BOM: '{mfr_in_bom['mpn']}' vs Nexar: {tried_mpns})"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f" NO MATCH [{designator}] {ref}: "
|
||||
f"Nexar MPN(s) {tried_mpns} not found in BOM at all"
|
||||
)
|
||||
|
||||
if match:
|
||||
row += [match["manufacturer"], match["mpn"], match["unit_cost"]]
|
||||
|
||||
Reference in New Issue
Block a user