This commit is contained in:
David Rice
2026-04-30 14:40:06 +01:00
parent e14d79a0a3
commit fcdad21344
3 changed files with 30 additions and 8 deletions

View File

@@ -46,7 +46,7 @@ COST_HEADER = "Unit Cost EUR @1000"
SKIP_MPNS = {
"", "0", "tbd", "n/a", "na", "-", "--", "---", "?", "none",
"null", "nan", "xxx", "x", "dnf", "dnp", "do not fit",
"do not populate",
"do not populate", "total",
}
logging.basicConfig(
@@ -199,7 +199,10 @@ def _find_tables(indexed_rows: list[tuple[int, tuple]]):
continue
empty_streak = 0
if mfr.lower() == "manufacturer" and mpn.lower() == "mpn":
# Detect a new table header anywhere in the row (handles sub-tables
# at different column positions than the current table)
dr_str_lower = [_cell(v).lower() for v in dr]
if "manufacturer" in dr_str_lower and "mpn" in dr_str_lower:
break
if mpn and mpn.lower() not in SKIP_MPNS:
@@ -237,7 +240,10 @@ def fill_boms(
for f in files:
log.info(f"Processing {f.name}")
try:
wb = openpyxl.load_workbook(f)
# data_only gives us resolved cell values (not formula strings) for
# table/part detection; the writable wb is used for reading/writing prices.
wb_ro = openpyxl.load_workbook(f, data_only=True, read_only=True)
wb = openpyxl.load_workbook(f)
except Exception as exc:
log.error(f" Cannot open {f.name}: {exc}")
continue
@@ -246,14 +252,19 @@ def fill_boms(
ws = wb[sheet_name]
indexed = [
(i, tuple(row))
for i, row in enumerate(ws.iter_rows(values_only=True), start=1)
for i, row in enumerate(wb_ro[sheet_name].iter_rows(values_only=True), start=1)
]
for table in _find_tables(indexed):
header_row = table["header_row"]
data_rows = [r for r, _, _ in table["data"]]
row_range = (
f" (Excel rows {data_rows[0]}{data_rows[-1]})"
if data_rows else " (no data rows detected)"
)
log.info(
f" Sheet '{sheet_name}' row {header_row}: "
f"table at col {table['start_col']}, {len(table['data'])} parts"
f"table at col {table['start_col']}, {len(table['data'])} parts{row_range}"
)
# Find or create the cost column.
@@ -272,8 +283,11 @@ def fill_boms(
for c in range(table["start_col"], max_col + 1):
val = ws.cell(header_row, c).value
if val is not None:
last_used = c
if str(val).strip().lower() in KNOWN_COST_HEADERS:
val_str = str(val).strip()
# Don't count formula placeholders as "used" columns
if not val_str.startswith("="):
last_used = c
if val_str.lower() in KNOWN_COST_HEADERS:
cost_col = c
break
@@ -288,7 +302,14 @@ def fill_boms(
if isinstance(cell, MergedCell):
continue
existing = cell.value
if existing is not None and str(existing).strip() not in ("", "0") and existing != 0:
is_formula = isinstance(existing, str) and existing.startswith("=")
is_empty = (
existing is None
or (isinstance(existing, str) and existing.strip() in ("", "0"))
or (isinstance(existing, (int, float)) and existing == 0)
)
if not is_empty and not is_formula:
log.info(f" Skip row {row_num} [{mpn}]: cell already has {repr(existing)}")
total_skipped += 1
continue
@@ -306,6 +327,7 @@ def fill_boms(
total_missing += 1
log.info(f" No match in Octopart: [{mfr}] [{mpn}]")
wb_ro.close()
try:
wb.save(f)
log.info(f" Saved {f.name}")