108 lines
3.5 KiB
Python
108 lines
3.5 KiB
Python
#!/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))
|