From 588d0928586e4849c1d3978b5e4b5743495fa939 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Mon, 15 Jun 2026 01:35:42 +0200 Subject: [PATCH] docs(infra): add .md metadata for write_xlsx_sheets function The function code and its registry metadata were created together but the .md was left untracked by the auto-commit. Add it so the indexed function has its companion metadata versioned. Co-Authored-By: Claude Opus 4.8 (1M context) --- python/functions/infra/write_xlsx_sheets.md | 101 ++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 python/functions/infra/write_xlsx_sheets.md diff --git a/python/functions/infra/write_xlsx_sheets.md b/python/functions/infra/write_xlsx_sheets.md new file mode 100644 index 00000000..14b1b2f1 --- /dev/null +++ b/python/functions/infra/write_xlsx_sheets.md @@ -0,0 +1,101 @@ +--- +name: write_xlsx_sheets +kind: function +lang: py +domain: infra +version: "1.0.0" +purity: impure +signature: "def write_xlsx_sheets(out_path: str, sheets: dict, header_bold: bool = True, autofit: bool = True, freeze_header: bool = True) -> str" +description: "Escribe un archivo Excel (.xlsx) multi-hoja desde datos en memoria con openpyxl. Crea (o sobrescribe) un libro completo a partir de un dict {nombre_hoja: datos}, una hoja por key en orden de insercion. Cada hoja acepta list[list] (primera fila = headers) o {\"headers\": [...], \"rows\": [[...]]}. Cabecera en negrita opcional, auto-ancho de columnas aproximado (cap 60), congelado de cabecera (freeze_panes A2), y manejo de celdas None/numeros/strings/bool. Crea los directorios padre si faltan. Devuelve la ruta absoluta del archivo escrito." +tags: [xlsx, excel, spreadsheet, openpyxl, office, onlyoffice, io, infra] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [openpyxl] +tested: false +tests: [] +test_file_path: "" +file_path: "python/functions/infra/write_xlsx_sheets.py" +params: + - name: out_path + desc: "Ruta del archivo .xlsx a escribir. Se crean los directorios padre si faltan. Si el archivo ya existe se SOBRESCRIBE por completo (no preserva hojas previas). Vacio lanza ValueError." + - name: sheets + desc: "Dict {nombre_hoja: datos}, una hoja por key en orden de insercion. Cada valor admite dos formas: (1) list[list] donde la primera fila son los headers y el resto filas de datos; (2) dict {\"headers\": [...], \"rows\": [[...], ...]} separando cabeceras de filas. Dict vacio lanza ValueError. El nombre de hoja se trunca a 31 chars (limite de Excel)." + - name: header_bold + desc: "Si True (default) la primera fila (cabecera) de cada hoja se escribe en negrita. Solo aplica cuando la hoja tiene headers." + - name: autofit + desc: "Si True (default) ajusta el ancho de cada columna a la longitud maxima del contenido de esa columna (incluida la cabecera), con un cap de 60 caracteres y +2 de holgura. Es aproximado (cuenta caracteres, no pixeles reales de la fuente)." + - name: freeze_header + desc: "Si True (default) congela la fila de cabecera con freeze_panes='A2' en cada hoja que tenga cabecera, dejandola fija al hacer scroll." +output: "str — la ruta ABSOLUTA del archivo .xlsx escrito (os.path.abspath de out_path)." +--- + +## Ejemplo + +```python +import sys, os +sys.path.insert(0, os.path.join("python", "functions")) +from infra.write_xlsx_sheets import write_xlsx_sheets + +path = write_xlsx_sheets( + "/tmp/reporte_ventas.xlsx", + { + # Forma list[list]: primera fila = headers + "Ventas": [ + ["Producto", "Unidades", "Precio", "Activo"], + ["Teclado", 12, 29.99, True], + ["Raton", 30, 14.5, False], + ["Monitor", None, 199.0, True], # None -> celda vacia + ], + # Forma explicita: headers + rows + "Resumen": { + "headers": ["Metrica", "Valor"], + "rows": [ + ["Total productos", 3], + ["Ingreso estimado", 6359.99], + ], + }, + }, +) +print(path) # /tmp/reporte_ventas.xlsx + +# Leer de vuelta para confirmar +from openpyxl import load_workbook +wb = load_workbook(path) +print(wb.sheetnames) # ['Ventas', 'Resumen'] (orden preservado) +print(wb["Ventas"]["B2"].value) # 12 (int conservado) +``` + +## Cuando usarla + +Usala cuando tengas datos en memoria (resultado de un scraping, una query, un +report) y quieras volcarlos a un archivo Excel limpio de una sola pasada, con +una o varias hojas. Es la pieza de escritura para exportar datasets a `.xlsx` +sin formulas ni preservacion de estado previo: tu generas el dict +`{hoja: filas}` y la funcion produce el libro entero. Si necesitas actualizar +UNA hoja de un libro que ya existe sin destruir las demas (preservando trabajo +manual, formulas, key matching), usa `upsert_xlsx_sheet_py_infra` en su lugar. + +## Gotchas + +- **Impura — escribe en disco.** Devuelve la ruta absoluta del archivo escrito. +- **SOBRESCRIBE el archivo si ya existe.** No hace merge ni backup: el libro + anterior se reemplaza por completo. Para escritura no destructiva sobre un + libro existente, usa `upsert_xlsx_sheet`. +- **Requiere openpyxl** (ya instalado en `python/.venv`, version 3.1.5). Si no + esta disponible lanza `ImportError` con la instruccion de instalacion + (`cd python && uv add openpyxl`). +- **Nombre de hoja truncado a 31 caracteres** (limite de Excel). openpyxl + tambien prohibe ciertos caracteres en el nombre (`\ / ? * [ ] :`); pasarlos + puede lanzar. +- **autofit es aproximado**: mide longitud de caracteres del contenido, no el + ancho real en pixeles de la fuente. Cap a 60 chars para columnas con texto + largo. +- **Tipos de celda**: None, int, float, str y bool se escriben nativos + (preservan su tipo en Excel). Cualquier otro tipo (date, Decimal, objetos) se + serializa a `str` — convierte tu mismo antes si quieres tipos nativos de Excel. +- **sheets vacio o out_path vacio** lanzan `ValueError`. +- **Encoding**: openpyxl maneja Unicode (UTF-8) de forma transparente en los + valores de celda; no hay que codificar nada a mano.