134 lines
5.5 KiB
Python
134 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
populate_template.py
|
||
|
||
Script to copy data from a production order Excel file into an ODS template
|
||
and save the result as a new ODS file named after the value in cell A2.
|
||
|
||
Requirements:
|
||
pip install openpyxl odfpy
|
||
"""
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
from openpyxl import load_workbook
|
||
from odf.opendocument import load, OpenDocumentSpreadsheet
|
||
from odf.table import Table, TableRow, TableCell
|
||
from odf.text import P
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Configuration – adjust if your filenames differ
|
||
# ----------------------------------------------------------------------
|
||
SOURCE_XLSX = Path("Production Order (mrp.production).xlsx")
|
||
TEMPLATE_ODS = Path("A4004_v3.0-260617.ods")
|
||
OUTPUT_DIR = Path(".") # current directory; change if you prefer another location
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Helper functions for ODS manipulation
|
||
# ----------------------------------------------------------------------
|
||
def get_cell(table: Table, col_idx: int, row_idx: int) -> TableCell:
|
||
"""Return the TableCell at (col_idx, row_idx) – 0‑based indices."""
|
||
rows = table.getElementsByType(TableRow)
|
||
if row_idx >= len(rows):
|
||
raise IndexError(f"Row {row_idx} out of range")
|
||
cells = rows[row_idx].getElementsByType(TableCell)
|
||
if col_idx >= len(cells):
|
||
raise IndexError(f"Column {col_idx} out of range")
|
||
return cells[col_idx]
|
||
|
||
def set_cell_value(table: Table, col_idx: int, row_idx: int, value: str):
|
||
"""Write a string value into the specified cell."""
|
||
cell = get_cell(table, col_idx, row_idx)
|
||
# Clear existing content
|
||
for child in list(cell.childNodes):
|
||
cell.removeChild(child)
|
||
# Insert new text
|
||
cell.addElement(P(text=str(value)))
|
||
|
||
def col_letter_to_index(letter: str) -> int:
|
||
"""Convert Excel‑style column letter (A=0, B=1, …) to zero‑based index."""
|
||
return ord(letter.upper()) - ord('A')
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Main processing
|
||
# ----------------------------------------------------------------------
|
||
def main() -> None:
|
||
# 1. Load source workbook
|
||
wb = load_workbook(SOURCE_XLSX, data_only=True)
|
||
ws = wb.active
|
||
|
||
# Helper to find a cell containing a given substring (case‑sensitive)
|
||
def find_cell_containing(text: str):
|
||
for row in ws.iter_rows(values_only=False):
|
||
for cell in row:
|
||
if isinstance(cell.value, str) and text in cell.value:
|
||
return cell
|
||
return None
|
||
|
||
# ------------------------------------------------------------------
|
||
# Extract required values from the source file
|
||
# ------------------------------------------------------------------
|
||
# a) FA‑ suffix (column B)
|
||
fa_suffix = None
|
||
for cell in ws["B"]:
|
||
if isinstance(cell.value, str) and cell.value.startswith("FA-"):
|
||
fa_suffix = cell.value.split("FA-")[1].strip()
|
||
break
|
||
if fa_suffix is None:
|
||
sys.exit("Error: Could not find a cell in column B starting with 'FA-'")
|
||
|
||
# b) Adjacent values for the component strings
|
||
def get_right_adjacent(search_str: str) -> str:
|
||
match = find_cell_containing(search_str)
|
||
if not match:
|
||
sys.exit(f"Error: Could not find cell containing '{search_str}'")
|
||
right_cell = ws.cell(row=match.row, column=match.column + 1)
|
||
return right_cell.value if right_cell.value is not None else ""
|
||
|
||
barebone_val = get_right_adjacent("All / Components / Barebone")
|
||
gpu_val = get_right_adjacent("All / Components / GPU")
|
||
ram_val = get_right_adjacent("All / Components / RAM")
|
||
cpu_val = get_right_adjacent("All / Components / CPU")
|
||
ssd_val = get_right_adjacent("All / Components / SSD")
|
||
raid_val = get_right_adjacent("All / Components / RAID")
|
||
|
||
# c) File name from A2
|
||
file_name_cell = ws["A2"]
|
||
if file_name_cell.value is None:
|
||
sys.exit("Error: Cell A2 in source file is empty")
|
||
output_name = f"{file_name_cell.value}.ods"
|
||
|
||
# ------------------------------------------------------------------
|
||
# Load ODS template and write values
|
||
# ------------------------------------------------------------------
|
||
ods_doc: OpenDocumentSpreadsheet = load(str(TEMPLATE_ODS))
|
||
# Assuming the template has a single table; adjust index if needed
|
||
table: Table = ods_doc.spreadsheet.getElementsByType(Table)[0]
|
||
|
||
# Mapping of target ranges (top‑left cell only) → value
|
||
# (col_letter, row_number) are 1‑based as in Excel/ODS UI
|
||
targets = {
|
||
("B", 1): fa_suffix, # B1:C2 → write to B1
|
||
("C", 10): barebone_val, # C10:D10 → write to C10
|
||
("C", 20): gpu_val, # C20:D23 → write to C20
|
||
("C", 12): ram_val, # C12:D19 → write to C12
|
||
("C", 11): cpu_val, # C11:D11 → write to C11
|
||
("C", 24): ssd_val, # C24:D25 → write to C24
|
||
("C", 27): raid_val, # C27:D27 → write to C27
|
||
}
|
||
|
||
for (col_letter, row_number), value in targets.items():
|
||
col_idx = col_letter_to_index(col_letter)
|
||
row_idx = row_number - 1 # zero‑based for our helper
|
||
set_cell_value(table, col_idx, row_idx, value)
|
||
|
||
# ------------------------------------------------------------------
|
||
# Save the modified document
|
||
# ------------------------------------------------------------------
|
||
output_path = OUTPUT_DIR / output_name
|
||
ods_doc.save(str(output_path))
|
||
print(f"Successfully created: {output_path}")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|