Updates
This commit is contained in:
BIN
OCTO/octo_1.xlsx
Normal file
BIN
OCTO/octo_1.xlsx
Normal file
Binary file not shown.
BIN
OCTO/octo_2.xlsx
Normal file
BIN
OCTO/octo_2.xlsx
Normal file
Binary file not shown.
BIN
OCTO/octo_3.xlsx
Normal file
BIN
OCTO/octo_3.xlsx
Normal file
Binary file not shown.
BIN
OUTPUT/bom_parts_1_of_3.xlsx
Normal file
BIN
OUTPUT/bom_parts_1_of_3.xlsx
Normal file
Binary file not shown.
BIN
OUTPUT/bom_parts_2_of_3.xlsx
Normal file
BIN
OUTPUT/bom_parts_2_of_3.xlsx
Normal file
Binary file not shown.
BIN
OUTPUT/bom_parts_3_of_3.xlsx
Normal file
BIN
OUTPUT/bom_parts_3_of_3.xlsx
Normal file
Binary file not shown.
45
octo_fill.py
45
octo_fill.py
@@ -40,7 +40,7 @@ _SFP.__init__ = _sfp_patched
|
|||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
BOM_DIR = Path("BoM")
|
BOM_DIR = Path("BoM")
|
||||||
OCTO_FILE = Path("OCTO/octo.xlsx")
|
OCTO_DIR = Path("OCTO")
|
||||||
COST_HEADER = "Unit Cost EUR @1000"
|
COST_HEADER = "Unit Cost EUR @1000"
|
||||||
|
|
||||||
SKIP_MPNS = {
|
SKIP_MPNS = {
|
||||||
@@ -59,26 +59,18 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# ── Load Octopart data ─────────────────────────────────────────────────────────
|
# ── Load Octopart data ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def load_octo(path: Path) -> tuple[dict[tuple[str,str], float], dict[str, float]]:
|
def _load_single(path: Path, exact_map: dict, mpn_map: dict) -> int:
|
||||||
"""
|
"""Load one Octopart xlsx into the shared maps. Returns number of entries added."""
|
||||||
Returns:
|
|
||||||
exact_map – (manufacturer_lower, mpn_lower) → lowest unit price
|
|
||||||
mpn_map – mpn_lower → lowest unit price (fallback)
|
|
||||||
"""
|
|
||||||
log.info(f"Reading Octopart data from {path}")
|
|
||||||
wb = openpyxl.load_workbook(path, data_only=True, read_only=True)
|
wb = openpyxl.load_workbook(path, data_only=True, read_only=True)
|
||||||
|
added = 0
|
||||||
exact_map: dict[tuple[str, str], float] = {}
|
|
||||||
mpn_map: dict[str, float] = {}
|
|
||||||
|
|
||||||
for sheet_name in wb.sheetnames:
|
for sheet_name in wb.sheetnames:
|
||||||
ws = wb[sheet_name]
|
ws = wb[sheet_name]
|
||||||
headers: Optional[dict[str, int]] = None # col_name → 0-based index
|
headers: Optional[dict[str, int]] = None
|
||||||
|
|
||||||
for row in ws.iter_rows(values_only=True):
|
for row in ws.iter_rows(values_only=True):
|
||||||
row = list(row)
|
row = list(row)
|
||||||
if headers is None:
|
if headers is None:
|
||||||
# Find header row
|
|
||||||
row_lower = [str(v).strip().lower() if v is not None else "" for v in row]
|
row_lower = [str(v).strip().lower() if v is not None else "" for v in row]
|
||||||
if "original part" in row_lower and "original manufacturer" in row_lower:
|
if "original part" in row_lower and "original manufacturer" in row_lower:
|
||||||
headers = {str(row[i]).strip(): i for i in range(len(row)) if row[i] is not None}
|
headers = {str(row[i]).strip(): i for i in range(len(row)) if row[i] is not None}
|
||||||
@@ -112,14 +104,35 @@ def load_octo(path: Path) -> tuple[dict[tuple[str,str], float], dict[str, float]
|
|||||||
key = (mfr.lower(), mpn.lower())
|
key = (mfr.lower(), mpn.lower())
|
||||||
if key not in exact_map or price < exact_map[key]:
|
if key not in exact_map or price < exact_map[key]:
|
||||||
exact_map[key] = price
|
exact_map[key] = price
|
||||||
|
added += 1
|
||||||
|
|
||||||
mpn_k = mpn.lower()
|
mpn_k = mpn.lower()
|
||||||
if mpn_k not in mpn_map or price < mpn_map[mpn_k]:
|
if mpn_k not in mpn_map or price < mpn_map[mpn_k]:
|
||||||
mpn_map[mpn_k] = price
|
mpn_map[mpn_k] = price
|
||||||
|
|
||||||
wb.close()
|
wb.close()
|
||||||
|
return added
|
||||||
|
|
||||||
log.info(f" Loaded {len(exact_map)} unique (manufacturer, part) entries from Octopart")
|
|
||||||
|
def load_octo(octo_dir: Path) -> tuple[dict[tuple[str, str], float], dict[str, float]]:
|
||||||
|
"""
|
||||||
|
Reads every .xlsx file in octo_dir into shared lookup maps.
|
||||||
|
exact_map – (manufacturer_lower, mpn_lower) → lowest unit price
|
||||||
|
mpn_map – mpn_lower → lowest unit price (fallback)
|
||||||
|
"""
|
||||||
|
files = sorted(octo_dir.glob("*.xlsx"))
|
||||||
|
if not files:
|
||||||
|
log.error(f"No .xlsx files found in {octo_dir}/")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
exact_map: dict[tuple[str, str], float] = {}
|
||||||
|
mpn_map: dict[str, float] = {}
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
added = _load_single(f, exact_map, mpn_map)
|
||||||
|
log.info(f" {f.name}: {added} entries loaded")
|
||||||
|
|
||||||
|
log.info(f"Octopart total: {len(exact_map)} unique (manufacturer, part) entries")
|
||||||
return exact_map, mpn_map
|
return exact_map, mpn_map
|
||||||
|
|
||||||
|
|
||||||
@@ -311,10 +324,10 @@ def fill_boms(
|
|||||||
# ── Main ───────────────────────────────────────────────────────────────────────
|
# ── Main ───────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
for p in (BOM_DIR, OCTO_FILE):
|
for p in (BOM_DIR, OCTO_DIR):
|
||||||
if not p.exists():
|
if not p.exists():
|
||||||
log.error(f"Not found: {p}")
|
log.error(f"Not found: {p}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
exact_map, mpn_map = load_octo(OCTO_FILE)
|
exact_map, mpn_map = load_octo(OCTO_DIR)
|
||||||
fill_boms(BOM_DIR, exact_map, mpn_map)
|
fill_boms(BOM_DIR, exact_map, mpn_map)
|
||||||
|
|||||||
Reference in New Issue
Block a user