Updates
This commit is contained in:
BIN
BACKUP/Axio4_Nexio_Conduent Subsystem Cost Analysis.xlsx
Normal file
BIN
BACKUP/Axio4_Nexio_Conduent Subsystem Cost Analysis.xlsx
Normal file
Binary file not shown.
Binary file not shown.
38
octo_fill.py
38
octo_fill.py
@@ -46,7 +46,7 @@ COST_HEADER = "Unit Cost EUR @1000"
|
|||||||
SKIP_MPNS = {
|
SKIP_MPNS = {
|
||||||
"", "0", "tbd", "n/a", "na", "-", "--", "---", "?", "none",
|
"", "0", "tbd", "n/a", "na", "-", "--", "---", "?", "none",
|
||||||
"null", "nan", "xxx", "x", "dnf", "dnp", "do not fit",
|
"null", "nan", "xxx", "x", "dnf", "dnp", "do not fit",
|
||||||
"do not populate",
|
"do not populate", "total",
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -199,7 +199,10 @@ def _find_tables(indexed_rows: list[tuple[int, tuple]]):
|
|||||||
continue
|
continue
|
||||||
empty_streak = 0
|
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
|
break
|
||||||
|
|
||||||
if mpn and mpn.lower() not in SKIP_MPNS:
|
if mpn and mpn.lower() not in SKIP_MPNS:
|
||||||
@@ -237,7 +240,10 @@ def fill_boms(
|
|||||||
for f in files:
|
for f in files:
|
||||||
log.info(f"Processing {f.name}")
|
log.info(f"Processing {f.name}")
|
||||||
try:
|
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:
|
except Exception as exc:
|
||||||
log.error(f" Cannot open {f.name}: {exc}")
|
log.error(f" Cannot open {f.name}: {exc}")
|
||||||
continue
|
continue
|
||||||
@@ -246,14 +252,19 @@ def fill_boms(
|
|||||||
ws = wb[sheet_name]
|
ws = wb[sheet_name]
|
||||||
indexed = [
|
indexed = [
|
||||||
(i, tuple(row))
|
(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):
|
for table in _find_tables(indexed):
|
||||||
header_row = table["header_row"]
|
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(
|
log.info(
|
||||||
f" Sheet '{sheet_name}' row {header_row}: "
|
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.
|
# Find or create the cost column.
|
||||||
@@ -272,8 +283,11 @@ def fill_boms(
|
|||||||
for c in range(table["start_col"], max_col + 1):
|
for c in range(table["start_col"], max_col + 1):
|
||||||
val = ws.cell(header_row, c).value
|
val = ws.cell(header_row, c).value
|
||||||
if val is not None:
|
if val is not None:
|
||||||
last_used = c
|
val_str = str(val).strip()
|
||||||
if str(val).strip().lower() in KNOWN_COST_HEADERS:
|
# 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
|
cost_col = c
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -288,7 +302,14 @@ def fill_boms(
|
|||||||
if isinstance(cell, MergedCell):
|
if isinstance(cell, MergedCell):
|
||||||
continue
|
continue
|
||||||
existing = cell.value
|
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
|
total_skipped += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -306,6 +327,7 @@ def fill_boms(
|
|||||||
total_missing += 1
|
total_missing += 1
|
||||||
log.info(f" No match in Octopart: [{mfr}] [{mpn}]")
|
log.info(f" No match in Octopart: [{mfr}] [{mpn}]")
|
||||||
|
|
||||||
|
wb_ro.close()
|
||||||
try:
|
try:
|
||||||
wb.save(f)
|
wb.save(f)
|
||||||
log.info(f" Saved {f.name}")
|
log.info(f" Saved {f.name}")
|
||||||
|
|||||||
Reference in New Issue
Block a user