#!/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: # 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 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()