109 lines
3.5 KiB
Python
109 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))
|