CreaRack-SL

Import Visio v2 — arquitectura del motor y la UI

Qué es

El sistema de importación de stencils desde ficheros Visio del Rack editor, reconstruido en jul-2026 (PR #314 motor v1.52.0, #315 UI v1.53.0, QA+modernización .vss v1.54.0, fix EMF v1.54.1). Convierte ficheros de fabricante (Cisco, Eaton, Dell…) en stencils de la librería del tenant.

Rutas por formato

FormatoRutaDónde corre
.vsdx, .vsd, .vsslibvisio-ng → SVG por página (+ paso EMF→PNG, abajo)Worker Huey (racks/tasks.py::convert_visio_async)
.vssx, .vdxVisioParser (iconos embebidos o placeholder SVG)Síncrono en el request de analyze
.svgDirecto (1 fichero = 1 stencil), sanitizado antes del previewSíncrono
  • Los .vss migran a libvisio-ng en el QA de PR 3: la cadena legacy vss2xhtml→EMF→LibreOffice→PNG dejaba los shapes de fabricante casi vacíos (solo taladros y logo con Eaton-Racks) y además hacía OOM con shapes altos (getdata() por píxel sobre PNGs de 2400 px). Toda esa cadena se retiró (convert_vss_masters, flujo vss_index diferido en confirm).
  • Los metadatos de un .vss (nombre real del master, u_height = pulgadas/1.75, half_width < 1.5 in) siguen saliendo de vss2raw (binario libvisio-tools, ~1 s) vía VisioParser.extract_vss_metadata(), y el task los casa con los SVG por número de página del nombre de fichero (orden natural, no lexicográfico — con ≥10 páginas sorted() descolocaba nombres, bug cazado en PR 3).

Masters solo-EMF → PNG (v1.54.1, el caso Cisco)

Cisco (y otros fabricantes) no dibujan sus masters con geometría Visio: incrustan el arte como EMF, y libvisio-ng lo envuelve tal cual en <image href="data:image/emf"> — que ningún navegador renderiza (cazado en PROD con Catalyst 9000: 165/165 masters solo-EMF → miniaturas y stencils rotos). El worker aplica ahora un post-paso (racks/utils/visio_parser.py::convert_emf_only_svgs):

  1. svg_is_emf_only() detecta SVGs con data:image/emf y sin primitivas de dibujo.
  2. Los EMF se extraen y se convierten todos en UNA invocación de LibreOffice (el arranque de LO es el coste; perfil propio vía -env:UserInstallation para evitar lock clashes) — EMF→SVG vectorial → rsvg-convert a 2400 px; fallback por lote EMF→PNG directo.
  3. Postproceso Pillow vectorizado (_postprocess_stencil_png): blanco→transparente (ImageChops), autocrop por canal alpha, ancho mínimo 800 px. Nunca getdata() por píxel (OOM del QA PR 3).
  4. El SVG solo-EMF se sustituye por el PNG (mismo base de nombre → el número de página sigue alineando metadatos); si la conversión falla, el master se descarta con warning (mejor ausente que roto).

Los SVG con geometría real (Eaton) no se tocan y conservan calidad vectorial.

Flujo async (sesiones)

  1. POST /api/racks/visio/analyze (gate racks:admin, límite 50 MB) guarda el upload en MEDIA_ROOT/temp/{uuid}/, escribe visio_session.json con status=processing y encola convert_visio_async.
  2. El task convierte, aplica el paso EMF→PNG, sanitiza cada SVG y deja status=done con los masters (o status=error). Sin modelo de BD: el estado vive en el JSON del temp de la sesión.
  3. El frontend (static/js/editor/import_export.js, vanilla) hace polling de GET /api/racks/visio/session/{id} cada 2,5 s (tope 4 min; aborta si se cierra el modal) y pinta el grid: miniatura + nombre editable inline + categoría por shape.
  4. POST /api/racks/visio/confirm (mismo gate) mueve las imágenes a uploads/stencils/, crea los Stencil y devuelve category por item (el frontend agrupa por carpeta sin recargar).

Seguridad (fixes s220)

  • Sanitizador SVG (racks/utils/svg_sanitizer.py, allowlist con defusedxml) en 3 puntos: SVG subido directo, SVGs de la conversión, y gate final en confirm.
  • confirm exige racks:admin · anti path-traversal en image_path (realpath + prefijo MEDIA_ROOT) · límite de subida 50 MB.

Perfil de memoria

libvisio_ng.convert pica ~515 MB de RSS con un .vss de fabricante de 40 MB (Catalyst 9000). En PROD el worker no fija límite; el compose de dev subió el web a 1024M en v1.54.1 (con 512M el kernel mataba el proceso — y ese techo ya mordía a mypy, AUDIT s215).

QA con ficheros reales (PR 3 + v1.54.1)

tests/qa/test_visio_real_vendor_files.py — NO corre en CI (skip si no existe media/temp/qa-visio-src/); fuentes de los ficheros en su docstring. Resultados 13-07-2026: Cisco UCS .vssx 47 MB ✅ · Dell .vssx ✅ · Eaton Racks/UPS .vss ✅ nombres reales y calidad vectorial · .vsdx reales (MikroTik) ✅ · Catalyst 9000 .vss (40 MB, 165 masters EMF) ✅ → PNG nítidos (guardia: skip si el container tiene <900 MB) · el .vss de Cisco UCS (87 MB) rebota limpio en el límite. Dependencias runtime: libvisio-ng (pip, GPL-3 — solo worker), libvisio-tools (apt, metadatos), libreoffice-draw + rsvg-convert (apt, conversión EMF).

Límites conocidos

  • Granularidad .vsdx/.vsd = página, no shape individual. Troceo fino = posible v2.
  • u_height estimado del tamaño del dibujo — el usuario lo revisa en el grid (editable).
  • get_page_info de libvisio-ng devuelve 0 páginas para .vss — por eso los nombres salen de vss2raw.
  • Stencils importados rotos ANTES de v1.54.1 no se auto-reparan: borrar y re-importar.