#!/usr/bin/env python3 import re import sys from pathlib import Path import openpyxl import ezodf from openpyxl.utils import column_index_from_string def find_suffix_in_column(ws, column_letter, prefix): """Search column for a cell starting with prefix and return the suffix.""" col_idx = column_index_from_string(column_letter) for row in ws.iter_rows(min_col=col_idx, max_col=col_idx, values_only=False): cell = row[0] if isinstance(cell.value, str): m = re.search(rf'{re.escape(prefix)}(.+)', cell.value) if m: return m.group(1) return None def find_cell_containing(ws, substring): """Return the first cell whose value contains the given substring.""" for row in ws.iter_rows(values_only=False): for cell in row: if isinstance(cell.value, str) and substring in cell.value: return cell return None def address_to_indices(addr): """Convert an A1-style address to zero‑based (row, col) indices.""" match = re.match(r'([A-Z]+)(\\d+)', addr) col_letter, row_str = match.groups() col = column_index_from_string(col_letter) - 1 row = int(row_str) - 1 return row, col def fill_range(sheet, start_addr, end_addr, value): """Fill a rectangular range in the ODS sheet with the same value.""" start_row, start_col = address_to_indices(start_addr) end_row, end_col = address_to_indices(end_addr) for r in range(start_row, end_row + 1): for c in range(start_col, end_col + 1): sheet[r, c].set_value(value) def main(): # Paths are relative to this script's location source_path = Path(__file__).parent / "Production Order (mrp.production).xlsx" template_path = Path(__file__).parent / "A4004_v3.0-260617.ods" # Load source workbook wb = openpyxl.load_workbook(source_path, data_only=True) ws = wb.active # 1. Serial number suffix after 'FA-' suffix = find_suffix_in_column(ws, "B", "FA-") if not suffix: print("FA- suffix not found", file=sys.stderr) sys.exit(1) # 2. Component values components = { "All / Components / Barebone": "C10:D10", "All / Components / GPU": "C20:D23", "All / Components / RAM": "C12:D19", "All / Components / CPU": "C11:D11", "All / Components / SSD": "C24:D25", "All / Components / RAID": "C27:D27", } comp_values = {} for label, target_range in components.items(): cell = find_cell_containing(ws, label) if cell: right_cell = ws.cell(row=cell.row, column=cell.column + 1) comp_values[label] = right_cell.value else: comp_values[label] = None print(f"Warning: '{label}' not found", file=sys.stderr) # Load ODS template doc = ezodf.opendoc(str(template_path)) sheet = doc.sheets[0] # Fill serial suffix into B1:C2 fill_range(sheet, "B1", "C2", suffix) # Fill component ranges for label, target_range in components.items(): value = comp_values[label] if value is not None: start_addr, end_addr = target_range.split(":") fill_range(sheet, start_addr, end_addr, value) # Output file name from A2 a2_value = ws["A2"].value if not a2_value: print("Cell A2 is empty; cannot determine output filename.", file=sys.stderr) sys.exit(1) output_name = f"{a2_value}.ods" output_path = Path(__file__).parent / output_name doc.saveas(str(output_path))