Files
fn_registry/python/pyproject.toml
T
egutierrez 9cdde4a341 feat(eda): núcleo AutomaticEDA — documento por capítulos + renderers PDF/PPTX anti-corte
Introduce la capa intermedia entre el contenido de un EDA y su formato de
salida. Un documento es una lista de capítulos versionados; cada capítulo es
un conjunto ordenado de bloques (heading, markdown, kv_table, data_table,
figure, image, caption, note) independientes del formato.

Núcleo (paquete de soporte python/functions/datascience/automatic_eda/):
- model.py: dataclasses de bloques + Chapter, normalizadores defensivos
  (aceptan dataclass o dict, nunca lanzan), ENGINE_VERSION y el manifiesto
  por capítulo (automatic_eda_manifest.json).
- text_layout.py: medición/wrapping por rejilla de caracteres compartida.
- chapters_registry.py: CHAPTER_ORDER pre-declarado + build_document con
  auto-discovery de capítulos por convención (permite añadir capítulos en
  paralelo sin editar el registro).
- render_pdf_impl.py: paginador A5 retrato móvil que MIDE cada bloque y nunca
  corta: texto a líneas completas, tablas largas partidas por filas repitiendo
  cabecera, figuras/imágenes escaladas para caber enteras. Pie versionado por
  capítulo.
- render_pptx_impl.py: mismo principio sobre slides 16:9 (continúa en slide
  "(cont.)"; tablas repiten cabecera; figuras exportadas a PNG escaladas).
- chapters/portada.py y chapters/overview.py: capítulos de referencia. Portada
  con nombre, rótulo Automatic-EDA, fuente, almacenamiento (inferido de
  source), fecha europea, filas×cols, descripción, granularidad y calidad con
  criterios. Overview con df.head (placeholder honesto si falta head_rows),
  diccionario de columnas (tipo/nulos/ejemplos) y describe numérico.

Funciones públicas del registry (grupo eda, dict-no-throw):
- render_automatic_eda_pdf / render_automatic_eda_pptx: aceptan capítulos o un
  TableProfile (construyen los capítulos con build_document) y escriben el
  manifiesto. Aditivas — no reemplazan render_eda_pdf.

Tests self-contained (sin DuckDB) para ambos renderers: golden (portada +
overview), partición de tablas largas repitiendo cabecera, no-corte de celdas
y markdown largos, profile None/{} válido de 1 página/slide, y error path en
directorio no escribible. 23 tests verdes (incluye los previos de
render_eda_pdf, intactos).

Dependencia nueva python-pptx>=1.0.2 declarada en python/pyproject.toml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:30:31 +02:00

69 lines
1.8 KiB
TOML

[project]
name = "fn-registry-python"
version = "0.1.0"
description = "Funciones Python del fn-registry: Metabase API, ML, utilidades"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"chardet>=7.4.3",
"contextily>=1.7.0",
"cryptography>=46.0.6",
"duckdb>=1.5.2",
"fpdf2>=2.8.7",
"geopandas>=1.1.3",
"google-api-python-client>=2.197.0",
"google-auth>=2.49.1",
"google-cloud-bigquery>=3.25",
"google-cloud-bigquery-datatransfer>=3.22.0",
"google-cloud-bigquery-storage>=2.27",
"google-cloud-storage>=3.10.1",
"httpx",
"matplotlib>=3.10.9",
"opencv-contrib-python-headless>=4.13.0.92",
"openpyxl>=3.1.5",
"pillow>=12.2.0",
"polars>=1.40.1",
"pymeshlab>=2025.7.post1",
"pymssql>=2.3.13",
"pypdf>=6.10.0",
"pyproj>=3.7.2",
"python-docx>=1.2.0",
"python-pptx>=1.0.2",
"pyyaml>=6.0.3",
"qrcode[pil]>=8.2",
"rapidfuzz>=3.14.5",
"reportlab>=4.5.0",
"scikit-image>=0.26.0",
"scikit-learn>=1.8.0",
"scipy>=1.17.1",
"seaborn>=0.13.2",
"shapely>=2.1.2",
"statsmodels>=0.14.6",
"trimesh>=4.12.2",
"xlrd>=2.0.2",
]
[project.optional-dependencies]
nlp = [
"gliner>=0.2.13",
"glirel>=1.0.0",
]
jupyter = [
"jupyterlab>=4.0",
"jupyter-collaboration>=2.0",
"jupyter-mcp-server",
]
[dependency-groups]
dev = [
"pytest>=9.0.2",
]
[tool.pytest.ini_options]
# Las funciones del registry importan paquetes por su nombre raiz
# (p.ej. `from obsidian import format_obsidian_note`), por lo que el
# directorio `functions/` debe estar en sys.path al recolectar tests.
# `functions/obsidian` permite a los tests importar los modulos hoja por
# su nombre directo (p.ej. `from format_obsidian_note import ...`).
pythonpath = ["functions", "functions/obsidian"]