Files
fn_registry/python/functions/datascience/render_paper_pdf.md
T
egutierrez 9c1b7dd0f3 feat(papers): render_paper_pdf (Markdown IMRaD → PDF) + agente paper-reviewer
Subsistema papers/: pieza de entrega + control de calidad.

- render_paper_pdf_py_datascience (Python, impure, dominio datascience, grupo
  `papers`): convierte papers/<slug>/paper.md (frontmatter YAML + cuerpo IMRaD)
  en papers/<slug>/out/paper.pdf. Reutiliza el motor de paginación de flujo del
  paquete automatic_eda (matplotlib PdfPages, el mismo PDF móvil A5 de los
  informes EDA) — no reimplementa paginación ni toca matplotlib, y no añade
  dependencias. Cada sección IMRaD (# H1) → un Chapter en página nueva; portada
  desde el frontmatter (title/authors/date europea/abstract); detecta las
  imágenes Markdown ![alt](src) que el motor no entiende y las parte en bloques
  Image resueltos contra base_dir y base_dir/figures/. dict-no-throw estricto.
  5 tests verdes (golden + edges: sin frontmatter, path inexistente, figura
  inexistente, ruta directa al .md).

- .claude/agents/paper-reviewer: revisor académico adversarial read-only (gate
  anti paper-mill). Puntúa novedad/rigor/reproducibilidad/validez (0-5), intenta
  refutar cada claim contra la evidencia citada, detecta HARKing contra el
  preregistration.md, exige limitaciones declaradas y claims ≤ evidencia, y
  emite veredicto estructurado JSON (accept|major_revision|reject) con default
  conservador. Tools: Read, Grep, Glob, Bash (sin Edit/Write: solo juzga).

Diseño completo: reports/0001-2026-06-30-papers-system-design.md (agente C).

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

5.6 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports params output tested tests test_file_path file_path
render_paper_pdf function py datascience 1.0.0 impure def render_paper_pdf(paper_dir: str) -> dict Convierte un paper académico IMRaD escrito en Markdown (papers/<slug>/paper.md, con frontmatter YAML opcional title/authors/date/abstract + cuerpo) en un PDF papers/<slug>/out/paper.pdf. REUTILIZA el paginador de flujo del paquete automatic_eda (el mismo motor del PDF móvil A5 de los informes EDA): no reimplementa paginación ni toca matplotlib. Cada sección IMRaD (encabezado de nivel 1, p.ej. # Introduction, # Methods) se mapea a un Chapter que empieza en página nueva; el motor parsea por sí mismo headings, listas, tablas pipe, párrafos y **negrita** dentro del texto. Como el motor NO entiende la sintaxis de imagen Markdown ![alt](src), esta función detecta esas líneas y las parte en bloques Image separados, resolviendo el src relativo a base_dir y base_dir/figures/. La portada (si hay título) lista autores y fecha (DD/MM/AAAA si parseable) más el abstract. dict-no-throw: nunca lanza, devuelve {status, pdf_path, n_pages, note}.
papers
pdf
academic
render
report
imrad
mobile
automatic-eda
markdown
no-cut
matplotlib
datascience
python
false error_go_core
os
re
datetime
yaml
datascience.automatic_eda
name desc
paper_dir ruta al directorio del paper (papers/<slug>/, del que se lee paper.md) O directamente la ruta a un archivo paper.md (cualquier ruta terminada en .md). El directorio base para resolver figuras y escribir el PDF es el dirname del paper.md. Si el paper.md no existe (incluida una ruta totalmente inexistente) devuelve status='error' sin crash.
dict (nunca lanza): {status: 'ok'|'error', pdf_path: str|None, n_pages: int, note: str}. En éxito status='ok', pdf_path es la ruta del PDF escrito (<base_dir>/out/paper.pdf) y n_pages el total de páginas. En error status='error', pdf_path=None, n_pages=0 y note explica la causa (paper.md no encontrado, fallo del motor, o excepción inesperada). true
test_golden_genera_pdf_con_portada_y_secciones
test_edge_sin_frontmatter_ni_figuras
test_edge_path_inexistente_no_revienta
test_edge_figura_inexistente_degrada
test_acepta_ruta_directa_al_md
python/functions/datascience/render_paper_pdf_test.py python/functions/datascience/render_paper_pdf.py

Ejemplo

from datascience import render_paper_pdf

# Estructura del paper:
#   papers/zz-demo/paper.md          (frontmatter YAML + cuerpo IMRaD)
#   papers/zz-demo/figures/fig1.png  (figuras referenciadas con ![alt](figures/fig1.png))
#
# paper.md:
#   ---
#   title: A Minimal IMRaD Paper
#   authors: [Ada Lovelace, Alan Turing]
#   date: 2026-06-30
#   abstract: Demostramos que el motor pagina un paper sin cortar nada.
#   ---
#   # Introduction
#   Texto con **negrita** y una lista:
#   - Punto uno.
#   ![Figura 1](figures/fig1.png)
#   # Methods
#   | Métrica | Valor |
#   | --- | --- |
#   | Precisión | 0.91 |

res = render_paper_pdf("papers/zz-demo")
print(res["status"], res["n_pages"], res["pdf_path"])
# -> ok 3 papers/zz-demo/out/paper.pdf

# También acepta la ruta directa al .md:
render_paper_pdf("papers/zz-demo/paper.md")

Cuando usarla

Cuando tengas un paper académico (o cualquier documento IMRaD) escrito en Markdown y quieras un PDF móvil A5 listo para leer, sin montar LaTeX ni configurar un pipeline de pandoc. Úsala después de redactar paper.md con su frontmatter (título, autores, fecha, abstract) y secciones de nivel 1; obtienes out/paper.pdf con portada, una página nueva por sección IMRaD, tablas que se parten repitiendo la cabecera y figuras escaladas para caber enteras — garantía de no-corte heredada del motor automatic_eda. Es la capa de presentación PDF del grupo papers.

Gotchas

  • Impura: escribe out/paper.pdf (y crea el directorio out/) junto al paper.md. Necesita matplotlib instalado en el venv (lo usa el motor automatic_eda.render_pdf con backend headless Agg; corre en agentes/CI sin display). pyyaml es opcional: si falta, el frontmatter se parsea con un parser line-based clave: valor degradado.
  • Reutiliza el motor automatic_eda.render_pdf: NO reimplementa paginación ni toca matplotlib. render_pdf no tiene ID propio en el registry (es parte del paquete de soporte automatic_eda), por eso uses_functions queda vacío; la dependencia real es ese motor del paquete.
  • Nunca lanza (dict-no-throw): paper.md inexistente → {status:"error", pdf_path:None, note:"paper.md no encontrado: ..."}; cualquier excepción inesperada → {status:"error", note:"fallo: ..."}. Frontmatter ausente o incompleto degrada limpio (sin portada, el cuerpo entero se pagina).
  • Figuras relativas a figures/: el src de ![alt](src) se resuelve probando <base_dir>/<src> y <base_dir>/figures/<basename>; usa el primero que exista. Si ninguno existe, el motor degrada dibujando "(imagen no encontrada: ...)" — el PDF se genera igual, no crashea. Las URLs http(s) se dejan como texto Markdown, no se descargan.
  • Solo imágenes en línea propia: el motor _place_markdown NO entiende ![alt](src); esta función solo convierte a Image las líneas cuyo único contenido es la imagen. Una imagen embebida a mitad de un párrafo se quedaría como texto crudo.
  • A5 portrait mobile-first: el formato (tamaño de página, tipografía, pie Capítulo · vX.Y.Z) lo fija el motor EDA y no es configurable desde aquí.