Loana Ibañez
Data Lab001

¿Cómo podemos construir documentación en Power BI que acompañe la evolución de nuestra solución analítica?

Tu dashboard cambió.¿Tu documentación también?

Una guía para diseñar documentación que vive dentro del modelo semántico de Power BI y se consulta desde el tablero, con buenas prácticas y prompts listos para copiar y pegar.

Power BIData Saturday LATAM

Ilustración de Loana Ibañez
Documentación Escalabilidad
Etapa 1 de 4

Exploramos

Primero entendemos el problema antes de construir una solución.

01

La documentación envejece más rápido que el dashboard

  • sumar una nueva medida
  • ajustar una regla de negocio
  • renombrar una agrupación

El dashboard está al día. El documento que explica cómo funciona sigue describiendo la versión anterior. Seis meses después, alguien —quizás vos— abre el modelo y necesita responder:

Analista frente a su laptop pensando en el modelo de datos
  • ¿Qué mide esto?
  • ¿Por qué está calculado así?
  • ¿De qué tablas y relaciones depende?
  • ¿Puedo cambiarlo sin romper otra cosa?
02

La documentación también tiene que evolucionar

El modelo cambia a medida que evoluciona el tablero. Por eso, la documentación no debería tratarse como un entregable cerrado, sino como un activo que acompaña esa evolución: una parte puede generarse y actualizarse desde el propio modelo y otra necesita el criterio del equipo.

Si documentamos fuera del modeloSi documentamos en el modelo
Hay que acordarse de actualizarlaLos metadatos se actualizan con cada refresh
Puede describir una versión anteriorRefleja el modelo semántico tal como está
Hay que ir a buscarlaSe puede consultar desde el tablero
Todo depende del mantenimiento manualAlgunas partes pueden sistematizarse o automatizarse

Documentar dentro de Power BI no significa que todo se genere automáticamente. Significa diseñar un sistema para no volver a escribir a mano lo que el modelo ya sabe de sí mismo.

Se genera solo

Lo que el modelo sabe de sí mismo.

  • Tablas y columnas, con sus tipos
  • Relaciones
  • Medidas y carpetas

Lo escribimos una vez y vive adentro

Lo que aportamos junto con cada cambio, antes de llevarlo a producción.

  • Por qué existe una tabla
  • Qué decisión soporta una métrica
  • Cómo interpretar un campo
  • Las excepciones de negocio

Vive afuera y está bien que así sea

Lo que conviene mantener fuera del modelo.

  • Por qué elegimos esta arquitectura
  • El historial de decisiones del proyecto
  • Los acuerdos con áreas, clientes o responsables del proyecto
03

Qué necesitamos

Hasta acá entendimos el problema y distinguimos qué parte de la documentación puede acompañar al modelo y qué parte sigue necesitando criterio. Antes de pensar cómo resolverlo, bajemos eso a necesidades concretas.

Necesidad 01

Acompañar la evolución del modelo

La documentación debería mantenerse alineada con los cambios del modelo para no describir una versión que ya no existe.

¿Cómo podría verse esto dentro de Power BI?

Con estas necesidades en mente, podemos empezar a imaginar una página de documentación que viva dentro de la propia solución analítica.

Documentación

El objetivo de esta documentación es que puedas entender cómo está organizado el modelo, qué representa cada elemento y qué reglas necesitás conocer para interpretarlo correctamente.

5Tablas
34Columnas
21Medidas
6Relaciones
2. Estructuras de datos
2.1 fct_salesHechos
ColumnaTipoDescripción
UnitsInt64Unidades vendidas por línea de transacción.
RevenueDecimal—
ProductIDInt64Clave hacia dim_product.
4. Diccionario de medidas

Prototipo simplificado de la página de documentación. Los vacíos permanecen visibles cuando todavía falta contexto por completar.

Con una idea acerca de nuestro destino, el siguiente paso es probar cuánto de esa página podemos construir directamente a partir del modelo.

Etapa 2 de 4

Probamos

Llevamos la idea al modelo y vemos qué pasa.

Pasos: Chequeamos el modelo (04), implementamos (05), completamos las definiciones (06), detectamos lo que falta (07) y armamos la página completa (08).

04

Chequeá el modelo

Cinco cosas antes de documentar. No son del contenido de la documentación: son del modelo. Se arreglan una vez y quedan.

Documentar un modelo desordenado te da documentación de un desorden. Y buena parte de por qué la página del ejemplo sale prolija es que ese modelo ya estaba ordenado. Este paso pone a todos en la misma posición de partida.

1 · Fecha y hora automáticas desactivadas

Cómo lo ves: en la vista de modelo, si aparecen tablas LocalDateTable_ con un GUID, están activas.

Cómo lo arreglás: Archivo › Opciones y configuración › Opciones, sección Inteligencia de tiempo. Antes de desactivarla, revisá que ningún visual use esas jerarquías de fecha, porque se rompen.

Por qué importa: vienen ocultas, así que no ensucian la documentación. Pero inflan el archivo y llenan la vista de relaciones de líneas que no son del negocio.

2 · Una tabla de fechas marcada como tal

Cómo lo ves: clic derecho en tu tabla de calendario. Si dice “Marcar como tabla de fechas”, no está marcada.

Cómo lo arreglás: marcala. Necesita una columna de fecha continua, sin huecos ni repetidos.

Por qué importa: sin eso, la inteligencia de tiempo puede devolver resultados incorrectos sin avisar.

3 · Las medidas viven en tablas propias

Cómo lo ves: ¿tenés una o más tablas que solo contienen medidas, o están desparramadas dentro de las tablas de datos?

Cómo lo arreglás: creá una tabla vacía con Entrada de datos, borrale la columna y mové las medidas ahí.

Lo que recomiendo: dos tablas. Una para la lógica del modelo semántico y otra para las medidas que resuelven visualización o interfaz: colores, textos dinámicos, HTML, SVG. Son dos cosas distintas y conviene leerlas separadas. La documentación agrupa las medidas por la tabla que las contiene, así que con una tabla ves un bloque y con dos la separación aparece sola.

4 · Toda medida visible tiene carpeta

Cómo lo ves: en el panel Datos, ¿hay medidas colgando sueltas fuera de toda carpeta?

Cómo lo arreglás: asigná una carpeta por familia de métricas. En lote se hace desde la vista TMDL con la propiedad displayFolder.

El criterio: un único lugar por medida y ninguna sin clasificar. Power BI permite que una medida aparezca en varias carpetas separándolas con punto y coma, pero la documentación agrupa por el texto de DisplayFolder y una medida en dos carpetas aparece como una categoría combinada.

5 · Ninguna medida con alerta de error

Cómo lo ves: una medida con error muestra un triángulo en el panel Datos, o devuelve error al usarla en un visual.

Cómo lo arreglás: revisá la expresión. Suele ser una referencia a una columna o tabla que se renombró o se borró.

Por qué importa: documentar una medida que no evalúa deja la documentación describiendo algo que no funciona.

05

Implementá

Cuatro pasos hasta ver la primera página. Esta versión no decide nada: muestra lo que el modelo ya sabe de sí mismo.

Paso 1 · Crear las tablas de metadatos

Creá una tabla calculada por cada función INFO.VIEW, con el prefijo DOC_.

Documentación de tablas
DOC_tablas = INFO.VIEW.TABLES()
Documentación de columnas
DOC_columnas = INFO.VIEW.COLUMNS()
Documentación de medidas
DOC_medidas = INFO.VIEW.MEASURES()
Documentación de relaciones
DOC_relaciones = INFO.VIEW.RELATIONSHIPS()

Agrupalas en una zona reconocible de la vista de modelo, por ejemplo a la izquierda: así son lo primero que encuentra otra persona del equipo.

Vista de modelo de Power BI. Un rectángulo marca la segunda columna, donde están juntas las cuatro tablas de metadatos: doc_tables, doc_columns, doc_measures y doc_relationships. Alrededor quedan las tablas de medidas, las de parámetros y el esquema en estrella con sus dimensiones y hechos.

Paso 2 · Instalar HTML Content (lite)

En Obtener más objetos visuales, buscá “html content” y elegí HTML Content (lite) de Daniel Marsh-Patrick, el que tiene la insignia azul.

La versión lite sanea lo que recibe. Conviene saberlo antes de editar la medida, porque lo que no admite no da error: lo elimina y sigue.

✓ Admite

  • HTML simple: div, p, span, títulos, tablas, listas
  • SVG, salvo use, script y foreignObject
  • Estilos inline, class y bloques <style>
  • Imágenes como data URL

✕ No admite

  • JavaScript ni atributos on*
  • iframe, link, form
  • Recursos externos, @import, @font-face
  • Comentarios // dentro de <style>

Fuente: HTML Content · Sanitization y Accepted Tags.

Buscador de objetos visuales de Power BI con tres resultados para html content. El primero, HTML Content (lite) de Daniel Marsh-Patrick con insignia azul, está resaltado.

Paso 3 · Crear la medida

Va en tu tabla de medidas de visualización. Esta primera versión hace algo deliberadamente simple: toma los metadatos que acabamos de exponer y los renderiza sin intentar decidir qué función cumple cada tabla. Si falta una descripción, el hueco queda visible.

Medida HTML de la documentación
HTML Documentación = 
// Para reutilizar esta lógica con otras tablas de introspección, ajustá UNICAMENTE este bloque del principio.

// Vamos a estandarizar las tablas de introspección a una estructura interna. Desde este punto, el resto de la medida trabaja únicamente con estos nombres claros.

VAR DocumentacionTablas =
//Reemplaza todos los DOC_tablas de esta variable por tu tabla de documentación de tablas.Te la toma cuando es azul, sino es gris.
    SELECTCOLUMNS (
        DOC_tablas,
        "NombreTabla",       DOC_tablas[Name],
        "DescripcionTabla",  COALESCE ( DOC_tablas[Description], "" ),
        "TablaOculta",       DOC_tablas[IsHidden]
    )

VAR DocumentacionColumnas =
//Reemplaza todos los DOC_columnas de esta variable por tu tabla de documentación de columnas.Te la toma cuando es azul, sino es gris.
    SELECTCOLUMNS (
        DOC_columnas,
        "NombreTabla",          DOC_columnas[Table],
        "NombreColumna",        DOC_columnas[Name],
        "TipoDato",             DOC_columnas[DataType],
        "DescripcionColumna",   COALESCE ( DOC_columnas[Description], "" )
    )

VAR DocumentacionMedidas =
//Reemplaza todos los DOC_medidas de esta variable por tu tabla de documentación de medidas.
    SELECTCOLUMNS (
        DOC_medidas,
        "NombreTabla",         DOC_medidas[Table],
        "NombreMedida",        DOC_medidas[Name],
        "Carpeta",             COALESCE ( DOC_medidas[DisplayFolder], "" ),
        "DescripcionMedida",   COALESCE ( DOC_medidas[Description], "" ),
        "MedidaOculta",        DOC_medidas[IsHidden]
    )

VAR DocumentacionRelaciones =
//Reemplaza todos los DOC_relaciones de esta variable por tu tabla de documentación de relaciones.
    SELECTCOLUMNS (
        DOC_relaciones,
        "TablaOrigen",           DOC_relaciones[FromTable],
        "ColumnaOrigen",         DOC_relaciones[FromColumn],
        "CardinalidadOrigen",    DOC_relaciones[FromCardinality],
        "TablaDestino",          DOC_relaciones[ToTable],
        "ColumnaDestino",        DOC_relaciones[ToColumn],
        "CardinalidadDestino",   DOC_relaciones[ToCardinality],
        "DireccionFiltro",       DOC_relaciones[CrossFilteringBehavior],
        "RelacionActiva",        DOC_relaciones[IsActive]
    )


// Listo. Acá empiezan los bloques que levantan la documentación de manera automática. Espero que te sirva! Loa 💛

VAR TablasADocumentar =
    FILTER (
        DocumentacionTablas,
        NOT [TablaOculta]
            && LEFT ( [NombreTabla], 4 ) <> "doc_"
    )

VAR NombresTablas =
    SELECTCOLUMNS (
        TablasADocumentar,
        "NombreTabla", [NombreTabla]
    )

VAR ColumnasADocumentar =
    FILTER (
        DocumentacionColumnas,
        [NombreTabla] IN NombresTablas
    )

VAR MedidasADocumentar =
    FILTER (
        DocumentacionMedidas,
        NOT [MedidaOculta]
            && [NombreTabla] IN NombresTablas
    )

VAR RelacionesADocumentar =
    FILTER (
        DocumentacionRelaciones,
        [TablaOrigen] IN NombresTablas
            && [TablaDestino] IN NombresTablas
    )

VAR Fuente =
    "Segoe UI Variable Text,Segoe UI,system-ui,Arial,sans-serif"

VAR FuenteMono =
    "Consolas,Menlo,monospace"

VAR EstiloReset =
    "<style>
        html,body{
            margin:0;
            padding:0;
            height:100%;
            width:100%;
            overflow:hidden;
        }
        *{
            box-sizing:border-box;
        }
        p{
            margin:0;
        }
        table{
            table-layout:fixed;
        }
    </style>"

VAR EstiloContenedor =
    "position:fixed;
     inset:0;
     overflow:hidden;
     background:transparent;"

VAR EstiloTarjeta =
    "background:#FFFFFF;
     border:0.5px solid #E4E4E4;
     border-radius:12px;
     padding:20px 3%;
     width:100%;
     height:100%;
     overflow-y:auto;
     overflow-x:hidden;
     font-family:" & Fuente & ";"

VAR EstiloTitulo =
    "font-size:19px;
     font-weight:600;
     color:#1A1A1A;
     margin:0 0 6px;"

VAR EstiloSubtitulo =
    "font-size:15px;
     font-weight:600;
     color:#1A1A1A;
     margin:26px 0 8px;"

VAR EstiloParrafo =
    "font-size:12.5px;
     line-height:1.55;
     color:#6B6B6B;
     margin:0 0 10px;"

VAR EstiloTabla =
    "width:100%;
     border-collapse:collapse;
     margin:6px 0 4px;"

VAR EstiloEncabezado =
    "text-align:left;
     font-size:11px;
     font-weight:600;
     color:#6B6B6B;
     text-transform:uppercase;
     letter-spacing:0.4px;
     padding:7px 10px;
     background:#FAFBFF;
     border-bottom:1px solid #E4E4E4;
     overflow-wrap:anywhere;"

VAR EstiloNombre =
    "font-family:" & FuenteMono & ";
     font-size:11.5px;
     color:#4752CE;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloMetadata =
    "font-size:11px;
     color:#9B9B9B;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloDescripcion =
    "font-size:12px;
     color:#6B6B6B;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     line-height:1.45;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloFalta =
    "font-size:11.5px;
     color:#B9B9C6;
     font-style:italic;"

VAR EstiloEstado =
    "display:inline-block;
     background:#F8EFDD;
     color:#8A5A12;
     border-radius:4px;
     padding:1px 5px;
     font-size:10px;
     font-weight:600;"

VAR FaltaDescripcion =
    "<span style='" &
        EstiloFalta &
    "'>Falta la descripción</span>"

VAR SinTablas =
    "<p style='" &
        EstiloParrafo &
    "'>No hay tablas visibles para documentar.</p>"

VAR SinColumnas =
    "<p style='" &
        EstiloParrafo &
    "'>No hay columnas para mostrar en las tablas documentadas.</p>"

VAR SinRelaciones =
    "<p style='" &
        EstiloParrafo &
    "'>No hay relaciones entre las tablas documentadas.</p>"

VAR SinMedidas =
    "<p style='" &
        EstiloParrafo &
    "'>No hay medidas visibles para documentar.</p>"

VAR HTMLTablas =
    CONCATENATEX (
        TablasADocumentar,

        VAR NombreTabla =
            [NombreTabla]

        VAR DescripcionTabla =
            [DescripcionTabla]

        VAR NombreTablaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreTabla,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DescripcionTablaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        DescripcionTabla,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        RETURN
            "<tr>" &

                "<td style='" &
                    EstiloNombre &
                "'>" &
                    NombreTablaSeguro &
                "</td>" &

                "<td style='" &
                    EstiloDescripcion &
                "'>" &
                    IF (
                        DescripcionTabla = "",
                        FaltaDescripcion,
                        DescripcionTablaSegura
                    ) &
                "</td>" &

            "</tr>",

        "",
        [NombreTabla], ASC
    )

VAR HTMLColumnas =
    CONCATENATEX (
        ColumnasADocumentar,

        VAR NombreTabla =
            [NombreTabla]

        VAR NombreColumna =
            [NombreColumna]

        VAR TipoDato =
            [TipoDato]

        VAR DescripcionColumna =
            [DescripcionColumna]

        VAR NombreTablaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreTabla,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR NombreColumnaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreColumna,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR TipoDatoSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        TipoDato,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DescripcionColumnaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        DescripcionColumna,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        RETURN
            "<tr>" &

                "<td style='" &
                    EstiloMetadata &
                "'>" &
                    NombreTablaSeguro &
                "</td>" &

                "<td style='" &
                    EstiloNombre &
                "'>" &
                    NombreColumnaSeguro &
                "</td>" &

                "<td style='" &
                    EstiloMetadata &
                "'>" &
                    TipoDatoSeguro &
                "</td>" &

                "<td style='" &
                    EstiloDescripcion &
                "'>" &
                    IF (
                        DescripcionColumna = "",
                        FaltaDescripcion,
                        DescripcionColumnaSegura
                    ) &
                "</td>" &

            "</tr>",

        "",
        [NombreTabla] & [NombreColumna],
        ASC
    )

VAR HTMLRelaciones =
    CONCATENATEX (
        RelacionesADocumentar,

        VAR TablaOrigen =
            [TablaOrigen]

        VAR ColumnaOrigen =
            [ColumnaOrigen]

        VAR CardinalidadOrigen =
            [CardinalidadOrigen]

        VAR TablaDestino =
            [TablaDestino]

        VAR ColumnaDestino =
            [ColumnaDestino]

        VAR CardinalidadDestino =
            [CardinalidadDestino]

        VAR DireccionFiltro =
            [DireccionFiltro]

        VAR RelacionActiva =
            [RelacionActiva]

        VAR OrigenSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        TablaOrigen & "[" & ColumnaOrigen & "]",
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DestinoSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        TablaDestino & "[" & ColumnaDestino & "]",
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        RETURN
            "<tr>" &

                "<td style='" &
                    EstiloNombre &
                "'>" &
                    OrigenSeguro &
                "</td>" &

                "<td style='" &
                    EstiloMetadata &
                "'>" &
                    CardinalidadOrigen &
                    " &rarr; " &
                    CardinalidadDestino &
                "</td>" &

                "<td style='" &
                    EstiloNombre &
                "'>" &
                    DestinoSeguro &
                "</td>" &

                "<td style='" &
                    EstiloMetadata &
                "'>" &
                    DireccionFiltro &
                "</td>" &

                "<td style='" &
                    EstiloMetadata &
                "'>" &
                    IF (
                        RelacionActiva,
                        "Activa",
                        "<span style='" &
                            EstiloEstado &
                        "'>INACTIVA</span>"
                    ) &
                "</td>" &

            "</tr>",

        "",
        [TablaOrigen] &
        [ColumnaOrigen] &
        [TablaDestino] &
        [ColumnaDestino],
        ASC
    )

VAR HTMLMedidas =
    CONCATENATEX (
        MedidasADocumentar,

        VAR NombreTabla =
            [NombreTabla]

        VAR NombreMedida =
            [NombreMedida]

        VAR Carpeta =
            [Carpeta]

        VAR DescripcionMedida =
            [DescripcionMedida]

        VAR NombreTablaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreTabla,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR NombreMedidaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreMedida,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR CarpetaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        Carpeta,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DescripcionMedidaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        DescripcionMedida,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        RETURN
            "<tr>" &

                "<td style='" &
                    EstiloMetadata &
                "'>" &
                    NombreTablaSeguro &
                "</td>" &

                "<td style='" &
                    EstiloMetadata &
                "'>" &
                    IF (
                        Carpeta = "",
                        "<span style='" &
                            EstiloFalta &
                        "'>Sin carpeta</span>",
                        CarpetaSegura
                    ) &
                "</td>" &

                "<td style='" &
                    EstiloNombre &
                "'>" &
                    NombreMedidaSeguro &
                "</td>" &

                "<td style='" &
                    EstiloDescripcion &
                "'>" &
                    IF (
                        DescripcionMedida = "",
                        FaltaDescripcion,
                        DescripcionMedidaSegura
                    ) &
                "</td>" &

            "</tr>",

        "",
        [NombreTabla] &
        [Carpeta] &
        [NombreMedida],
        ASC
    )

RETURN

    EstiloReset &

    "<div style='" &
        EstiloContenedor &
    "'>" &

        "<div style='" &
            EstiloTarjeta &
        "'>" &

            "<p style='" &
                EstiloTitulo &
            "'>Documentación del modelo</p>" &

            "<p style='" &
                EstiloParrafo &
            "'>" &
                "Esta primera versión lee los metadatos disponibles y los muestra sin interpretar la arquitectura del modelo." &
            "</p>" &

            "<p style='" &
                EstiloSubtitulo &
            "'>1. Tablas</p>" &

            IF (
                HTMLTablas = "",

                SinTablas,

                "<table style='" &
                    EstiloTabla &
                "'>" &

                    "<tr>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Tabla</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Descripción</th>" &

                    "</tr>" &

                    HTMLTablas &

                "</table>"
            ) &

            "<p style='" &
                EstiloSubtitulo &
            "'>2. Columnas</p>" &

            IF (
                HTMLColumnas = "",

                SinColumnas,

                "<table style='" &
                    EstiloTabla &
                "'>" &

                    "<tr>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Tabla</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Columna</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Tipo</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Descripción</th>" &

                    "</tr>" &

                    HTMLColumnas &

                "</table>"
            ) &

            "<p style='" &
                EstiloSubtitulo &
            "'>3. Relaciones</p>" &

            IF (
                HTMLRelaciones = "",

                SinRelaciones,

                "<table style='" &
                    EstiloTabla &
                "'>" &

                    "<tr>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Desde</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Cardinalidad</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Hacia</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Filtro cruzado</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Estado</th>" &

                    "</tr>" &

                    HTMLRelaciones &

                "</table>"
            ) &

            "<p style='" &
                EstiloSubtitulo &
            "'>4. Medidas</p>" &

            IF (
                HTMLMedidas = "",

                SinMedidas,

                "<table style='" &
                    EstiloTabla &
                "'>" &

                    "<tr>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Tabla</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Carpeta</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Medida</th>" &

                        "<th style='" &
                            EstiloEncabezado &
                        "'>Descripción</th>" &

                    "</tr>" &

                    HTMLMedidas &

                "</table>"
            ) &


        "</div>" &

    "</div>"

Paso 4 · Mostrarla

Página nueva, agregás el visual HTML Content (lite) y arrastrás la medida al campo del visual.

Si no se ve bien

Revisá el mensaje de error de la medida si tira error, te indica dónde hay que corregir.

Mensaje de error de Power BI: Failed to resolve name 'DOC_columnas'. It is not a valid table, variable, or function name.
Acordate de actualizar el modelo. Las tablas DOC_ son calculadas, así que se vuelven a evaluar cuando actualizás el modelo completo (esquema y datos). Si agregaste o cambiaste descripciones, actualizá antes de revisar la página. Conviene refrescar las cuatro juntas para que tablas, columnas, medidas y relaciones representen el mismo estado del modelo.

¿Sabías que las funciones definidas por el usuario también pueden ser documentadas?

Las últimas actualizaciones de Power BI permiten consultar metadata de las UDF del modelo semántico. En este caso no se crea una tabla calculada como las anteriores: la consulta se ejecuta desde la vista DAX cuando necesitás revisar esa información.

Documentación de UDF
EVALUATE
INFO.USERDEFINEDFUNCTIONS()

Resultado: una tabla que podés exportar con información sobre cada una de tus UDF. Si alguna aparece con la descripción vacía, podés completarla desde la vista de modelo, entrando al árbol del modelo y al nodo Funciones. El criterio es el mismo que para una medida.

06

Completá las definiciones

Hasta acá la página muestra todo lo que el modelo sabe de sí mismo, y nombra cada hueco. Ahora hay que llenarlos.

Todo vive en propiedades que Power BI ya tiene. No hay tabla nueva ni texto adentro de la medida. Se completan a mano en el panel Datos con la propiedad Descripción, o con /// arriba de cada objeto en la vista TMDL.

Una buena descripción agrega el contexto que el nombre no puede contar. No hace falta escribir un párrafo distinto desde cero para cada objeto: podemos usar una estructura estable según estemos documentando una tabla, una columna o una medida.

Tabla

Estructura
Qué representa → qué rol cumple → qué representa una fila.

Ejemplo
fct_ventas
Descripción: Registra las ventas confirmadas del negocio. Funciona como tabla de hechos del proceso comercial. Una fila representa una línea de producto dentro de una operación.

Preguntas para escribirla

  • ¿Qué información reúne?
  • ¿Para qué existe dentro del modelo?
  • ¿Cuál es su granularidad?

Columna

Estructura
Qué representa → cómo interpretar sus valores → definición estable que haga falta conocer.

Ejemplo
Estado Pedido
Descripción: Estado operativo asignado al pedido. Permite identificar en qué instancia del proceso se encuentra. Sus valores distinguen pedidos pendientes, completados o anulados.

Preguntas para escribirla

  • ¿Qué atributo representa?
  • ¿Hay valores, códigos o unidades que necesitan interpretación?
  • ¿Existe alguna definición estable que no sea evidente por el nombre?

Medida

En una medida, cada propiedad aporta una parte distinta de la documentación: el nombre identifica qué resultado devuelve, la Description explica su significado y la Display Folder indica dónde encontrarla. En este patrón, cada medida tiene una única carpeta y ninguna queda sin clasificar.

No buscamos traducir el DAX línea por línea. El código conserva la implementación; la descripción conserva el significado, la forma de leer el resultado y la regla que debería seguir siendo válida aunque cambie la expresión.

Estructura
Qué representa → cómo se interpreta → regla de negocio general.

Ejemplo
Average Revenue per Unit
Descripción: Revenue promedio generado por cada unidad vendida en el contexto de filtros actual. Permite comparar cuánto revenue aporta, en promedio, cada unidad entre períodos, productos o regiones. Se calcula dividiendo Revenue por Units Sold.

Preguntas para escribirla

  • ¿Qué resultado devuelve?
  • ¿Cómo debería interpretarse?
  • ¿Cuál es la regla general que lo define?

¿Y las relaciones?

Power BI no permite cargar una descripción propia en una relación. Su legibilidad depende de la arquitectura y de cómo estén nombradas las tablas. Los nombres consistentes ayudan a leer el modelo, pero la página no usa esos prefijos para clasificar las tablas: esa función la vamos a declarar explícitamente en 08.

Tip · Separá cómo medís de cómo lo mostrás

En un proyecto PBIP, Power BI separa el modelo semántico del reporte: una capa organiza y define los datos; la otra construye la experiencia para explorarlos. Son partes de una misma solución, pero cumplen funciones distintas.

Podemos aplicar ese mismo criterio dentro del modelo semántico. En este Data Lab usamos una tabla Measures para la lógica analítica y de negocio, y una tabla Visualizations para las medidas que resuelven necesidades de presentación o interacción en el reporte.

Measures

Revenue, unidades vendidas, promedios, porcentajes, inteligencia temporal y otras métricas que definen cómo medimos el negocio.

Descripción: Reúne las medidas que definen la lógica analítica y de negocio del modelo. Contiene los cálculos utilizados para medir e interpretar sus principales indicadores.

Visualizations

HTML, SVG, títulos dinámicos, estado de filtros, switches de KPI, documentación y otros helpers que definen cómo mostramos o hacemos interactuar esa información.

Descripción: Reúne las medidas creadas para resolver necesidades de presentación e interacción en el reporte. Incluye recursos como HTML, SVG, títulos dinámicos, estados de filtros, switches de KPI y otros helpers visuales.

Separarlas no cambia cómo Power BI ejecuta las medidas. Hace explícita su función y evita mezclar la lógica que calcula el dato con la lógica que determina cómo verlo.

Tené en cuenta que la descripción debería sobrevivir al próximo cambio.

Documentá lo estable en la descripción. Excepciones temporales, cambios de criterio, fechas de vigencia o decisiones que pueden modificarse deberían vivir en las reglas de negocio o en el registro de cambios, no quedar enterradas en la descripción del objeto.
Con este criterio

Describir no es repetir lo que el modelo ya dice. Es hacer explícito el significado que necesita conservar.

  • Lo que el modelo ya sabe, lo reutilizamos.
  • Lo que necesita contexto de negocio, lo aportamos nosotros.
  • Y si todavía falta una respuesta, no la inventamos: dejamos el hueco visible.
07

IA como copiloto

Ya sabemos qué queremos documentar. Ahora necesitamos detectar qué todavía falta explicar.

Cuando trabajamos hace tiempo sobre un modelo, muchas cosas se vuelven obvias para nosotros: sabemos qué representa una tabla, qué significa un código o por qué una medida existe. El problema aparece cuando ese conocimiento no quedó en el modelo.

El TMDL nos permite mostrarle a la IA la estructura que existe. En este caso, el trabajo de la IA no va a ser completar los huecos con una interpretación plausible, sino encontrarlos y preguntarnos por ellos.

Una segunda mirada, no una segunda fuente de verdad

La IA puede señalar qué falta explicar. El significado sigue viniendo de las personas que conocen el modelo.

La IA puede observar

  • objetos sin Description
  • descripciones a las que les falta alguna parte del criterio que definimos
  • medidas sin Display Folder
  • diferencias observables respecto de nuestras convenciones de nombres y carpetas
  • metadata técnica de las relaciones
  • lugares donde necesita contexto humano para seguir

Vos aportás

  • qué representa realmente una tabla, columna o medida
  • la granularidad de una tabla
  • cómo se interpreta una métrica
  • qué significan códigos, categorías o unidades
  • las reglas de negocio
  • la intención con la que fue diseñado un objeto

No le pedimos que deduzca esas respuestas desde el DAX, los nombres o las relaciones. Si el modelo no contiene el significado que necesita, queremos que lo deje visible y formule una pregunta.

Pasale el modelo que querés revisar

Copiá desde la vista TMDL los objetos que quieras revisar. Podés pasar el modelo completo o trabajar por partes, si preferís resolver una tabla a la vez.

El prompt lleva incorporado el mismo criterio de la sección 06. Así la IA no evalúa el modelo contra sus propias preferencias: lo compara contra las convenciones que definimos nosotros.

Usalo cuandoya definiste el criterio y el alcance, y querés saber qué documentación todavía está incompleta.
Qué obtenésun diagnóstico de la metadata existente y una lista de preguntas concretas para completar lo que el modelo no puede explicar solo.
Detectemos qué falta documentar
Te paso el TMDL de un modelo semántico de Power BI.

Quiero que actúes como copiloto de documentación.

Tu tarea es revisar la metadata existente, detectar qué información falta
para documentar el modelo con el criterio que te doy y hacerme las
preguntas necesarias para completarla.

IMPORTANTE

Este es un ejercicio de revisión y documentación.

No modifiques el TMDL.
No generes una versión modificada del TMDL.
No escribas código TMDL nuevo.
No propongas cambios directamente sobre el modelo.
No redactes metadata para pegar automáticamente.

Tu salida debe ser únicamente un diagnóstico documental y las preguntas
que necesito responder.

ALCANCE

Revisá únicamente los objetos que forman parte de este alcance de
documentación:

[PEGÁ ACÁ LAS TABLAS U OBJETOS QUE QUERÉS REVISAR]

No incluyas otras tablas aunque aparezcan en el TMDL.

En este modelo, las tablas DOC_ pertenecen a la infraestructura de esta
documentación y quedan fuera del alcance. No infieras exclusiones a
partir de otros prefijos.

CRITERIO DE DOCUMENTACIÓN

Usamos estas convenciones.

1. NOMBRES

TABLAS

El nombre debería permitir reconocer qué conjunto de información
contiene y, cuando corresponda, qué rol técnico cumple.

En este modelo usamos:
- hechos_ para hechos
- dim_ para dimensiones
- subdim_ para intermedias
- param_ para parámetros

No asumas el rol de una tabla solamente por su contenido.

COLUMNAS

El nombre debería identificar con precisión el atributo dentro de su
tabla.

Revisá especialmente nombres genéricos que puedan necesitar contexto,
por ejemplo Estado, Tipo o Valor.

No asumas qué representan. Si el significado no está explícito, señalá
que necesita contexto.

MEDIDAS

El nombre debería permitir reconocer qué resultado devuelve sin
intentar explicar toda la lógica del cálculo.

Usamos este patrón:

[Métrica] + [contexto necesario] + [modificador temporal]

Los modificadores temporales se ubican como sufijo.

No señales un nombre solamente porque vos usarías otra convención.
Comparalo únicamente contra la convención que te proporcioné.

2. CARPETAS DE MEDIDAS

Usamos Display Folders para agrupar las medidas por familia de
métricas.

En este modelo seguimos un criterio MECE:
- cada medida pertenece a una sola carpeta;
- ninguna medida queda sin clasificar.

Además distinguimos dos capas:

Measures
Lógica analítica y de negocio del modelo semántico.

Visualizations
Medidas creadas para resolver necesidades de presentación o
interacción en el reporte, por ejemplo documentación, estado de
filtros, HTML, SVG, switches de KPI o helpers visuales.

Regla conceptual:

Tabla = capa.
Display Folder = categoría dentro de esa capa.

No decidas por tu cuenta a qué capa pertenece una medida si esa
intención no está explícitamente documentada.

3. DESCRIPCIONES

Una descripción debería agregar significado que no se obtiene solamente
leyendo la metadata técnica.

Para una TABLA buscamos:

qué representa → qué rol cumple → qué representa una fila.

Debería permitir responder:
- ¿Qué información reúne?
- ¿Para qué existe dentro del modelo?
- ¿Cuál es su granularidad?

Para una COLUMNA buscamos:

qué representa → cómo interpretar sus valores → definición estable que
haga falta conocer.

Debería permitir responder:
- ¿Qué atributo representa?
- ¿Hay valores, códigos o unidades que necesitan interpretación?
- ¿Existe alguna definición estable que no sea evidente por el nombre?

Para una MEDIDA buscamos:

qué representa → cómo se interpreta → regla de negocio general.

Debería permitir responder:
- ¿Qué resultado devuelve?
- ¿Cómo debería interpretarse?
- ¿Cuál es la regla general que lo define?

REGLA PRINCIPAL: NO INFERIR CONTEXTO DE NEGOCIO

Trabajá únicamente con información explícita en el TMDL y con el
contexto que yo te proporcione.

No infieras contexto de negocio a partir de:
- expresiones DAX;
- nombres de objetos;
- relaciones;
- filtros;
- condiciones;
- nombres de columnas o tablas;
- patrones que reconozcas por experiencia.

No asumas:
- qué significa una métrica;
- cómo debería interpretarse;
- la granularidad real de una tabla;
- qué significan códigos o categorías;
- por qué existe una regla;
- por qué existe una relación;
- si una condición es permanente, temporal o excepcional;
- si una definición cambió históricamente;
- la intención con la que fue diseñado un objeto.

Tampoco decidas por tu cuenta qué función semántica cumple una tabla.

Si necesitás cualquiera de esas respuestas para completar la
documentación, no la deduzcas.

Marcá el objeto como NECESITA CONTEXTO y haceme una pregunta concreta.

DESCRIPCIONES EXISTENTES

No evalúes si una afirmación de negocio es verdadera o falsa.

Solo verificá si la descripción existente contiene las partes que
definimos en nuestro criterio.

Por ejemplo:
- si una tabla explica qué representa pero no informa granularidad,
  señalá que falta granularidad;
- si una medida explica qué representa pero no cómo se interpreta,
  señalá qué parte falta;
- si una columna no tiene Description, señalalo.

No completes la información faltante.

RELACIONES

Las relaciones no tienen una propiedad Description propia en Power BI.

No inventes descripciones para ellas.

Solo registrá la metadata técnica que esté explícitamente disponible:
- tabla y columna de origen;
- tabla y columna de destino;
- cardinalidad;
- dirección de filtro;
- estado activa/inactiva.

No infieras por qué existe una relación.
No evalúes si está bien o mal diseñada.
No propongas modificarla.

QUÉ TENÉS QUE REVISAR

Para cada objeto dentro del alcance, verificá:

TABLAS
- si tiene Description;
- si esa Description cubre qué representa, rol y granularidad;
- si su nombre respeta la convención definida.

COLUMNAS
- si tiene Description;
- si esa Description permite entender qué representa y cómo interpretar
  sus valores cuando hace falta;
- si su nombre necesita contexto adicional para ser entendido dentro de
  su tabla.

MEDIDAS
- si tiene Description;
- si esa Description cubre qué representa, cómo se interpreta y regla
  general;
- si tiene Display Folder;
- si está clasificada en una única carpeta;
- si su nombre respeta la convención definida;
- si su capa actual puede determinarse con información explícita.

RELACIONES
- registrá solamente la metadata técnica disponible.

SALIDA

Devolvé una tabla con estas columnas:

1. Objeto
2. Tipo de objeto
3. Tabla
4. Metadata actual
5. Qué falta completar
6. Estado
7. Pregunta para mí

Usá solamente estos estados:

OK
La metadata observable ya cubre el criterio que te proporcioné.

FALTA METADATA
Falta una propiedad técnica observable, por ejemplo Description o
Display Folder.

NECESITA CONTEXTO
Hace falta información humana para completar la documentación sin
inventar.

REVISAR CONVENCIÓN
Existe una diferencia observable respecto de la convención que te
proporcioné.

REGLAS DE RESPUESTA

- No modifiques el TMDL.
- No generes TMDL nuevo.
- No devuelvas una versión corregida del TMDL.
- No redactes código para aplicar cambios.
- No propongas nuevas descripciones todavía.
- No reescribas las descripciones existentes.
- No propongas reglas de negocio.
- No interpretes el DAX como contexto de negocio.
- No deduzcas granularidad.
- No deduzcas significado de códigos.
- No deduzcas intención de diseño.
- No propongas cambios en relaciones.
- No completes silenciosamente información faltante.
- No compares el modelo contra prácticas generales que yo no haya
  definido.
- Compará únicamente contra el criterio que te proporcioné.
- Cuando falte contexto, hacé una pregunta breve, concreta y
  respondible.
- Si no encontrás un problema observable, no inventes uno.

Al final agregá:

PREGUNTAS PENDIENTES

Agrupá las preguntas que necesito responder para completar la
documentación.

RESUMEN

Indicá:
- cantidad de objetos OK;
- cantidad con FALTA METADATA;
- cantidad que NECESITA CONTEXTO;
- cantidad para REVISAR CONVENCIÓN.

TMDL:

[PEGÁ ACÁ EL TMDL]

Tres cosas del prompt son tuyas y conviene revisarlas antes de mandarlo: los prefijos de tablas, el patrón de nombres de medidas y los nombres de las dos capas son los que usa tu equipo. Si los cambiás ahí, todo el diagnóstico se mide contra tu convención y no contra la mía.

Antes de compartir el TMDL: el TMDL puede contener información interna. Revisá las políticas de tu organización y qué información estás autorizada o autorizado a enviar: puede incluir lógica de cálculo, nombres de fuentes y otros metadatos del modelo. Usá herramientas aprobadas y compartí solamente el contexto necesario para la revisión.

El diagnóstico te muestra qué falta

La IA no completa la documentación ni se convierte en una nueva fuente de verdad. Solo señala qué metadata falta y qué preguntas necesitamos responder.

Usá esa lista para recuperar el contexto, completar las propiedades correspondientes en Power BI y volver a pasar el TMDL si querés comprobar que los huecos quedaron resueltos.

Cuando un objeto aparece como NECESITA CONTEXTO, no significa que la IA deba intentar otra vez. Significa que ese conocimiento todavía no quedó registrado en el modelo.

Un hueco visible es mejor que una definición inventada.

¿Y si quiero que la IA modifique el TMDL?

En otros contextos, la IA también puede generar o modificar TMDL. Ese flujo puede ser útil, pero necesita guardrails adicionales para controlar qué cambia y validar el resultado.

No vamos a cubrir ese flujo en este Data Lab. Acá usamos el TMDL como entrada para revisar y preguntar; los cambios sobre el modelo quedan bajo nuestro control. Estos dos prompts son el punto de partida si igual querés probarlo: devuelven TMDL con descripciones agregadas y nada más.

Pedirle a la IA que sugiera descripciones
Te paso el TMDL de las tablas de datos de un modelo de Power BI.
Contexto del dominio: [dos o tres líneas de qué se trata el modelo].

Tarea: agregar una descripción con /// arriba de cada declaración.
- Tabla: empezá con la granularidad en la forma "Una fila por…", y seguí
  con qué representa y cuál es su propósito en el modelo.
- Columna: qué representa el campo, cómo se interpreta y, si es clave
  primaria o foránea, hacia qué tabla.

Reglas estrictas:
- No cambies nombres, tipos, expresiones, sourceColumn, lineageTag ni
  ninguna otra propiedad existente.
- No agregues, borres ni reordenes tablas ni columnas.
- Lo único que podés agregar son líneas ///.
- Sin excepciones ni condiciones que puedan cambiar con el tiempo.
- Si no podés inferir qué representa algo a partir del nombre, el tipo
  y las relaciones, escribí [COMPLETAR]. No supongas.
- Devolvé el TMDL completo, listo para pegar en la vista TMDL.
- Al final, listá lo que quedó en [COMPLETAR].
Delegarle a la IA sumar las descripciones
Te paso el TMDL de mi tabla de medidas.

Tarea: agregar a cada medida una descripción con /// justo arriba de su
declaración: qué representa, cómo se interpreta y cuál es la regla de
negocio general que la define.

Reglas estrictas:
- No cambies ninguna expresión DAX.
- No cambies nombres, formatString, lineageTag ni ninguna otra propiedad.
- No agregues, borres ni reordenes medidas.
- Lo único que podés agregar son líneas ///.
- Sin excepciones ni condiciones que puedan cambiar con el tiempo: si
  detectás alguna, listala al final.
- Si no podés entender qué hace una medida, escribí [COMPLETAR].
- Devolvé el TMDL completo, listo para pegar.
- Al final, listá las medidas que quedaron en [COMPLETAR].

Antes de aplicar: pegá la respuesta en una pestaña nueva de la vista TMDL y usá Vista previa para confirmar que solo cambiaron descripciones. Así cada cambio pasa por tu aprobación (human-in-the-loop).

El marcador [COMPLETAR] es a propósito. Un hueco marcado es honesto; una descripción inventada es peor que ninguna. Cuando la IA no puede inferir algo, que lo diga.

Primero hacemos explícito el significado. Después hacemos que la documentación lo reutilice.

  • Ahora sabemos qué podía contarnos el modelo y qué tuvimos que aportar nosotros.
  • Una vez que esa metadata está completa, deja de ser información dispersa dentro de Power BI: podemos reutilizarla. En la sección 08 hacemos justamente eso.
08

La página completa

La medida que instalamos en 05 hace algo a propósito: lee el modelo sin interpretarlo. Tablas, columnas, relaciones y medidas aparecen aunque todavía no hemos decidido cómo organizarlas.

Ahora tenemos más información. Definimos el criterio de documentación, preparamos el modelo, completamos metadata y usamos IA como segunda mirada donde hacía falta. Ya podemos volver a esa medida y hacerla crecer.

La diferencia es pequeña en concepto, aunque el resultado sea bastante más completo: seguimos automatizando lo que el modelo puede decirnos solo, y declaramos lo que necesita criterio.

De una lista de objetos a una página que orienta

La medida de 05 podía detectar que una tabla existía. Lo que no podía saber era qué lugar ocupa esa tabla dentro de nuestro modelo.

No queremos deducirlo por el nombre ni por la dirección de una relación. Una tabla que empieza con hechos_ puede haberse nombrado mal; una relación tampoco alcanza para decidir por sí sola qué función semántica cumple cada objeto. Como pistas, sin embargo, valen mucho: el prefijo y el lugar que ocupa la tabla entre las relaciones son lo primero que miramos cuando escribimos las descripciones. Sirven para orientar el criterio, no para reemplazarlo.

Por eso, en esta versión vamos a agregar una pequeña capa de configuración. Cada tabla recibe una etiqueta, y cada etiqueta declara cómo quiere comportarse.

Cómo está organizada la medida

La versión completa tiene cuatro bloques de configuración arriba y, debajo, el motor que arma la página. Los cuatro bloques son lo único que se toca.

1 · FUENTES DE METADATA        de dónde sale la metadata
2 · ÚLTIMA ACTUALIZACIÓN       cuándo se actualizó por última vez
3 · CONFIGURACIÓN DE TABLAS    qué etiqueta le ponemos a cada tabla
4 · SEMÁNTICA DE ETIQUETAS     qué mostramos según la etiqueta
───────────────────────────────────────────────────────────
de acá para abajo, la página se genera sola

Vamos a armarla en ese orden. Volvé a HTML Documentación, la medida que creamos en 05, borrá su expresión y pegá los bloques uno atrás del otro: cuando termines, la medida está completa.

Si preferís trabajarla de una sola vez —en VS Code, en Tabular Editor o pegándola directo en Power BI— al final de la sección está la medida entera en un solo bloque.

Bloque 1 · De dónde sale la metadata

Son las mismas cuatro tablas de introspección que creamos en 05. Acá les damos nombres de columna en castellano, para que el resto de la medida se lea sin traducir.

Fuentes de metadata
HTML Documentación = 

// PARA REUTILIZAR ESTA MEDIDA:
// 1. Indicá tus tablas de introspección en FUENTES DE METADATA.
// 2. Asigná una etiqueta a cada tabla en CONFIGURACIÓN DE TABLAS.
// 3. Definí el comportamiento y el orden de esas etiquetas en SEMÁNTICA DE ETIQUETAS.
//
// Si sumaste una nueva tabla y no está configurada:
// → no desaparece;
// → no se clasifica automáticamente;
// → aparece como "Sin clasificar" al final.

// ------------------------------------------------------------------------------------------------------
// 1. FUENTES DE METADATA

VAR DocumentacionTablas =
    // Si tu tabla de documentación de tablas se llama distinto, cambiá DOC_tablas acá.
    SELECTCOLUMNS (
        DOC_tablas,
        "NombreTabla",      DOC_tablas[Name],
        "DescripcionTabla", COALESCE ( DOC_tablas[Description], "" ),
        "TablaOculta",      DOC_tablas[IsHidden]
    )

VAR DocumentacionColumnas =
    // Si tu tabla de documentación de columnas se llama distinto, cambiá DOC_columnas acá.
    SELECTCOLUMNS (
        DOC_columnas,
        "NombreTabla",         DOC_columnas[Table],
        "NombreColumna",       DOC_columnas[Name],
        "TipoDato",            DOC_columnas[DataType],
        "DescripcionColumna",  COALESCE ( DOC_columnas[Description], "" )
    )

VAR DocumentacionMedidas =
    // Si tu tabla de documentación de medidas se llama distinto, cambiá DOC_medidas acá.
    SELECTCOLUMNS (
        DOC_medidas,
        "NombreTabla",        DOC_medidas[Table],
        "NombreMedida",       DOC_medidas[Name],
        "Carpeta",            COALESCE ( DOC_medidas[DisplayFolder], "" ),
        "DescripcionMedida",  COALESCE ( DOC_medidas[Description], "" ),
        "MedidaOculta",       DOC_medidas[IsHidden],
        "EstadoMedida",       DOC_medidas[State]
    )

VAR DocumentacionRelaciones =
    // Si tu tabla de documentación de relaciones se llama distinto, cambiá DOC_relaciones acá.
    SELECTCOLUMNS (
        DOC_relaciones,
        "TablaOrigen",          DOC_relaciones[FromTable],
        "ColumnaOrigen",        DOC_relaciones[FromColumn],
        "CardinalidadOrigen",   DOC_relaciones[FromCardinality],
        "TablaDestino",         DOC_relaciones[ToTable],
        "ColumnaDestino",       DOC_relaciones[ToColumn],
        "CardinalidadDestino",  DOC_relaciones[ToCardinality],
        "DireccionFiltro",      DOC_relaciones[CrossFilteringBehavior],
        "RelacionActiva",       DOC_relaciones[IsActive]
    )

El bloque ya viene con los nombres de 05: DOC_tablas, DOC_columnas, DOC_medidas y DOC_relaciones. Si seguiste esa convención no tenés que tocar nada acá. Si tus tablas de introspección se llaman de otra manera, reemplazá esos cuatro nombres por los tuyos donde lo indican los comentarios: es lo único que hay que ajustar en este bloque.

Las tablas que empiezan con DOC_ quedan fuera de la documentación automáticamente, porque pertenecen a la infraestructura de esta misma página.

Bloque 2 · Cuándo fue actualizada por última vez la documentación

Este bloque viene preparado para funcionar sin fecha: si no lo tocás, la página anda igual y el pie no muestra ninguna.

Última actualización
// ------------------------------------------------------------------------------------------------------
// 2. ÚLTIMA ACTUALIZACIÓN · FUENTE AUXILIAR OPCIONAL
//
// Si sumas la hora de actualización vas a poder tener registro de cuándo fue la última vez en actualizarse los datos. Esto es clave cuando sumaste o cambiaste descripciones y te encontras con las definiciones previas. Scrolleando al final del visual renderizado chequeas la fecha y hora. Esto es UX en acción.

// Descomentá este bloque e indicá el nombre de tu medida con su tabla correspondiente. 
// VAR DocumentacionActualizacion =
//     'Nombre tabla'[Nombre medida]

// Si armaste la medida, una vez que descomentaste la anterior, comentas esta! Si no vas a poner hora de última actualización, dejá este bloque como está así no se rompe la medida.
VAR DocumentacionActualizacion =
        BLANK ()

Para que aparezca hacen falta dos cosas. Primero, una consulta en Power Query que guarde el momento del refresco. Creala en blanco, pegá esto en el Editor avanzado y llamala aux_last_refresh:

Marca de tiempo
let
    Source =
        #table(
            type table [FechaActualizacion = datetimezone],
            {
                {
                    DateTimeZone.SwitchZone(
                        DateTimeZone.FixedUtcNow(),
                        -3
                    )
                }
            }
        )
in
    Source

Tiene que resolverse durante el refresco, no al abrir el informe: así el valor queda congelado en el momento en que los datos entraron al modelo, que es justo lo que querés decir. Por eso DateTimeZone.FixedUtcNow y no DateTime.LocalNow: el primero devuelve UTC y queda fijo durante toda la evaluación de la consulta; el segundo toma la hora de la máquina que ejecuta el refresco, que no es la misma en tu Desktop que en el Servicio. El -3 de SwitchZone es la diferencia horaria de Buenos Aires: cambiala por la tuya.

Segundo, una medida que lea esa tabla, que es lo que el bloque 2 espera:

Medida de la fecha
Fecha Última Actualización =
MAX ( 'aux_last_refresh'[FechaActualizacion] )

Como aux_last_refresh no empieza con DOC_, no queda excluida sola de la documentación: acordate de declararla en el bloque 3, con la etiqueta que uses para las tablas auxiliares.

Con la medida creada, en el bloque 2 descomentá las dos líneas de arriba, poné ahí su nombre con la tabla donde vive, y comentá el BLANK ().

Bloque 3 · Qué etiqueta le ponemos a cada tabla

Acá va la primera de las dos decisiones que no se pueden automatizar: qué lugar ocupa cada tabla dentro del modelo. Reemplazá el ejemplo por tus tablas.

Configuración de tablas
// ------------------------------------------------------------------------------------------------------
// 3. CONFIGURACIÓN DE TABLAS
//
// Declaramos qué etiqueta describe a cada tabla.
//
// No repetís una tabla.
// Si una tabla no aparece acá, recibe la etiqueta marcada como fallback en el bloque 4.

VAR ConfiguracionTablas =
//Acá reemplazá con tus tablas, yo puse un ejemplo
    DATATABLE (
        "TablaConfig", STRING,
        "EtiquetaConfig", STRING,
        {
            { "fct_sales",          "Hechos" },
            { "fct_sentiment",      "Hechos" },
            { "dim_calendar",       "Dimensión" },
            { "dim_geography",      "Dimensión" },
            { "dim_product",        "Dimensión" },
            { "Visualizations",     "Medidas" },
            { "⚙️ Measures",         "Medidas" },
            { "param_key_insigths", "Parámetro" },
            { "param_userguide",    "Parámetro" },
            { "Documentación Tablas", "Excluir" },
            { "aux_last_refresh",   "Auxiliar" }
        }
    )

Las tablas que empiezan con DOC_ se excluyen automáticamente. Si alguna tabla que sostiene la documentación usa otro nombre, conviene declararla acá con la etiqueta Excluir; por ejemplo: { "Documentación Tablas", "Excluir" }.

Las etiquetas del ejemplo no son una taxonomía universal: son una forma de describir un modelo. Otro modelo podría necesitar categorías como Tabla puente, Configuración, Snapshot o cualquier otra que ayude a entender su estructura. Lo importante es que la clasificación queda declarada, no inferida.

Una tabla nueva no desaparece porque te olvidaste de configurarla. Si la introspección la encuentra pero no existe en ConfiguracionTablas, la medida le asigna Sin clasificar y la manda al final. Eso deja una señal visible de que el modelo cambió y todavía falta tomar una decisión sobre esa tabla.

Bloque 4 · Qué vamos a mostrar de cada tabla en base a su etiqueta

La segunda decisión. Cada tabla ya recibió una etiqueta; acá declaramos qué significa esa etiqueta para la página. Así podés llamarla Medidas, KPIs o como tenga sentido en tu equipo: la lógica no depende de un texto fijo.

Semántica de etiquetas
// ------------------------------------------------------------------------------------------------------
// 4. SEMÁNTICA DE ETIQUETAS
//
// Acá definís qué significa cada etiqueta para la página.
//
// DocumentarEstructura:
//     1 = muestra sus columnas en el diccionario de estructuras
//     0 = no las muestra (por ejemplo, una tabla que solo contiene medidas)
//
// Incluir:
//     1 = forma parte de la documentación
//     0 = queda completamente afuera
//
// EsFallback:
//     marcá una sola etiqueta con 1. Esa recibe cualquier tabla nueva no configurada.
//
// Podés renombrar "Medidas" o cualquier otra etiqueta.
// La lógica de render no depende del texto: reutiliza esta configuración.

VAR ConfiguracionEtiquetas =
    DATATABLE (
        "EtiquetaConfig", STRING,
        "Orden", INTEGER,
        "DocumentarEstructura", INTEGER,
        "Incluir", INTEGER,
        "EsFallback", INTEGER,
        {
            // Etiqueta              Orden  Estructura  Incluir  Fallback
            { "Hechos",                 1,          1,       1,        0 },
            { "Dimensión",              2,          1,       1,        0 },
            { "Dimensión auxiliar",     3,          1,       1,        0 },
            { "Auxiliar",               4,          1,       1,        0 },
            { "Parámetro",              5,          1,       1,        0 },
            { "Medidas",                6,          0,       1,        0 },
            { "Sin clasificar",       999,          1,       1,        1 },
            { "Excluir",             1000,          0,       0,        0 }
        }
    )
Orden

Posición de presentación.

DocumentarEstructura

En 0, esa etiqueta no entra al diccionario de estructuras.

Incluir

En 0, las tablas con esa etiqueta no se documentan.

EsFallback

En 1, es la etiqueta que reciben las tablas nuevas que todavía no fueron configuradas.

En este ejemplo, Medidas se muestra como categoría pero no entra al diccionario de estructuras. Excluir queda fuera de la documentación y Sin clasificar funciona como fallback para las tablas que todavía no tienen una decisión explícita.

Y de acá para abajo, se genera sola

Debajo del bloque 4 empieza el motor: el que cuenta objetos, calcula la cobertura, ordena las tablas por su etiqueta y arma el HTML. No hay nada que configurar ahí. Pegalo tal cual, al final de los cuatro bloques anteriores, y la medida queda lista.

Y de acá para abajo, se genera sola
// Listo. No necesitamos que toques más la medida.
// Desde este punto la documentación se genera automáticamente. Espero que te sea de utilidad, Loa 💛

VAR EtiquetaFallback =
    MAXX (
        FILTER (
            ConfiguracionEtiquetas,
            [EsFallback] = 1
        ),
        [EtiquetaConfig]
    )

VAR TablasBase =
    FILTER (
        DocumentacionTablas,
        NOT [TablaOculta]
            && LEFT (
                LOWER ( [NombreTabla] ),
                4
            ) <> "doc_"
    )

VAR TablasConEtiqueta =
    ADDCOLUMNS (
        TablasBase,

        "EtiquetaTabla",

            VAR NombreTablaActual =
                [NombreTabla]

            VAR EtiquetaEncontrada =
                MAXX (
                    FILTER (
                        ConfiguracionTablas,
                        [TablaConfig] = NombreTablaActual
                    ),
                    [EtiquetaConfig]
                )

            RETURN
                COALESCE (
                    EtiquetaEncontrada,
                    EtiquetaFallback
                )
    )

VAR TablasConSemantica =
    ADDCOLUMNS (
        TablasConEtiqueta,

        "OrdenEtiqueta",
            VAR EtiquetaActual = [EtiquetaTabla]
            RETURN
                COALESCE (
                    MAXX (
                        FILTER (
                            ConfiguracionEtiquetas,
                            [EtiquetaConfig] = EtiquetaActual
                        ),
                        [Orden]
                    ),
                    998
                ),

        "DocumentarEstructuraTabla",
            VAR EtiquetaActual = [EtiquetaTabla]
            RETURN
                COALESCE (
                    MAXX (
                        FILTER (
                            ConfiguracionEtiquetas,
                            [EtiquetaConfig] = EtiquetaActual
                        ),
                        [DocumentarEstructura]
                    ),
                    1
                ),

        "IncluirTabla",
            VAR EtiquetaActual = [EtiquetaTabla]
            RETURN
                COALESCE (
                    MAXX (
                        FILTER (
                            ConfiguracionEtiquetas,
                            [EtiquetaConfig] = EtiquetaActual
                        ),
                        [Incluir]
                    ),
                    1
                ),

        "EsFallbackTabla",
            VAR EtiquetaActual = [EtiquetaTabla]
            RETURN
                COALESCE (
                    MAXX (
                        FILTER (
                            ConfiguracionEtiquetas,
                            [EtiquetaConfig] = EtiquetaActual
                        ),
                        [EsFallback]
                    ),
                    0
                )
    )

VAR TablasADocumentar =
    FILTER (
        TablasConSemantica,
        [IncluirTabla] = 1
    )

VAR NombresTablas =
    SELECTCOLUMNS (
        TablasADocumentar,
        "NombreTabla", [NombreTabla]
    )


VAR TablasConColumnasDocumentables =
    FILTER (
        TablasADocumentar,
        [DocumentarEstructuraTabla] = 1
    )

VAR NombresTablasConColumnasDocumentables =
    SELECTCOLUMNS (
        TablasConColumnasDocumentables,
        "NombreTabla", [NombreTabla]
    )

VAR ColumnasADocumentar =
    FILTER (
        DocumentacionColumnas,

        [NombreTabla]
            IN NombresTablasConColumnasDocumentables

            && NOT CONTAINSSTRING (
                [NombreColumna],
                "RowNumber-"
            )
    )

VAR MedidasADocumentar =
    FILTER (
        DocumentacionMedidas,

        NOT [MedidaOculta]
            && [NombreTabla] IN NombresTablas
    )

VAR RelacionesADocumentar =
    FILTER (
        DocumentacionRelaciones,

        [TablaOrigen] IN NombresTablas
            && [TablaDestino] IN NombresTablas
    )

VAR TablasConColumnas =
    DISTINCT (
        SELECTCOLUMNS (
            ColumnasADocumentar,
            "NombreTabla", [NombreTabla]
        )
    )

VAR TablasEstructuras =
    FILTER (
        TablasADocumentar,

        [DocumentarEstructuraTabla] = 1
            && [NombreTabla] IN TablasConColumnas
    )

VAR CantidadTablas =
    COUNTROWS ( TablasADocumentar ) + 0

VAR CantidadColumnas =
    COUNTROWS ( ColumnasADocumentar ) + 0

VAR CantidadMedidas =
    COUNTROWS ( MedidasADocumentar ) + 0

VAR CantidadRelaciones =
    COUNTROWS ( RelacionesADocumentar ) + 0

VAR CantidadSinClasificar =
    COUNTROWS (
        FILTER (
            TablasADocumentar,
            [EsFallbackTabla] = 1
        )
    ) + 0

VAR CantidadTablasConDescripcion =
    COUNTROWS (
        FILTER (
            TablasADocumentar,
            [DescripcionTabla] <> ""
        )
    )

VAR CantidadColumnasConDescripcion =
    COUNTROWS (
        FILTER (
            ColumnasADocumentar,
            [DescripcionColumna] <> ""
        )
    )

VAR CantidadMedidasConDescripcion =
    COUNTROWS (
        FILTER (
            MedidasADocumentar,
            [DescripcionMedida] <> ""
        )
    )

VAR CantidadObjetos =
        CantidadTablas
        + CantidadColumnas
        + CantidadMedidas

VAR CantidadObjetosDocumentados =
        CantidadTablasConDescripcion
        + CantidadColumnasConDescripcion
        + CantidadMedidasConDescripcion

VAR CantidadHuecos =
    CantidadObjetos
        - CantidadObjetosDocumentados

VAR CoberturaDocumentacion =
    DIVIDE (
        CantidadObjetosDocumentados,
        CantidadObjetos,
        0
    )

VAR CoberturaTexto =
    FORMAT (
        COALESCE (
            CoberturaDocumentacion,
            0
        ),
        "0%"
    )

VAR EtiquetasPresentes =
    DISTINCT (
        SELECTCOLUMNS (
            TablasADocumentar,
            "EtiquetaResumen", [EtiquetaTabla]
        )
    )

VAR EtiquetasConResumen =
    ADDCOLUMNS (
        EtiquetasPresentes,

        "OrdenResumen",

            VAR EtiquetaActual =
                [EtiquetaResumen]

            VAR OrdenActual =
                MAXX (
                    FILTER (
                        ConfiguracionEtiquetas,
                        [EtiquetaConfig] = EtiquetaActual
                    ),
                    [Orden]
                )

            RETURN
                COALESCE (
                    OrdenActual,
                    998
                ),

        "CantidadResumen",

            VAR EtiquetaActual =
                [EtiquetaResumen]

            RETURN
                COUNTROWS (
                    FILTER (
                        TablasADocumentar,
                        [EtiquetaTabla] = EtiquetaActual
                    )
                )
    )

VAR ResumenEtiquetas =
    CONCATENATEX (
        EtiquetasConResumen,

        [EtiquetaResumen]
            & ": "
            & [CantidadResumen],

        " · ",

        FORMAT (
            [OrdenResumen],
            "000"
        )
            & "|"
            & [EtiquetaResumen],

        ASC
    )

VAR ResumenEstructura =
    "El alcance documentado contiene "
        & CantidadTablas
        & IF (
            CantidadTablas = 1,
            " tabla. ",
            " tablas. "
        )
        & ResumenEtiquetas
        & ". Entre las tablas incluidas hay "
        & CantidadRelaciones
        & IF (
            CantidadRelaciones = 1,
            " relación.",
            " relaciones."
        )

VAR Fuente =
    "Segoe UI Variable Text,Segoe UI,system-ui,Arial,sans-serif"

VAR FuenteMono =
    "Consolas,Menlo,monospace"

VAR EstiloReset =
    "<style>
        html,body{
            margin:0;
            padding:0;
            height:100%;
            width:100%;
            overflow:hidden;
        }

        *{
            box-sizing:border-box;
        }

        p{
            margin:0;
        }

        table{
            table-layout:fixed;
        }
    </style>"

VAR EstiloContenedor =
    "position:fixed;
     inset:0;
     overflow:hidden;
     background:transparent;"

VAR EstiloTarjeta =
    "background:#FFFFFF;
     border:0.5px solid #E4E4E4;
     border-radius:12px;
     padding:20px 3%;
     width:100%;
     height:100%;
     overflow-y:auto;
     overflow-x:hidden;
     font-family:" & Fuente & ";"

VAR EstiloTitulo =
    "font-size:19px;
     font-weight:600;
     color:#1A1A1A;
     margin:0 0 6px;"

VAR EstiloSubtitulo =
    "font-size:15px;
     font-weight:600;
     color:#1A1A1A;
     margin:26px 0 8px;"

VAR EstiloNombreSeccion =
    "font-size:13px;
     font-weight:600;
     color:#1A1A1A;
     margin:20px 0 4px;"

VAR EstiloParrafo =
    "font-size:12.5px;
     line-height:1.55;
     color:#6B6B6B;
     margin:0 0 10px;"

VAR EstiloTabla =
    "width:100%;
     border-collapse:collapse;
     margin:6px 0 4px;"

VAR EstiloEncabezado =
    "text-align:left;
     font-size:11px;
     font-weight:600;
     color:#6B6B6B;
     text-transform:uppercase;
     letter-spacing:0.4px;
     padding:7px 10px;
     background:#FAFBFF;
     border-bottom:1px solid #E4E4E4;
     overflow-wrap:anywhere;"

VAR EstiloNombre =
    "font-family:" & FuenteMono & ";
     font-size:11.5px;
     color:#4752CE;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloMetadata =
    "font-size:11px;
     color:#9B9B9B;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloDescripcion =
    "font-size:12px;
     color:#6B6B6B;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     line-height:1.45;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloRol =
    "font-size:11px;
     color:#9B9B9B;
     font-weight:400;
     padding-left:8px;"

VAR EstiloFalta =
    "font-size:11.5px;
     color:#B9B9C6;
     font-style:italic;"

VAR EstiloAviso =
    "font-size:11.5px;
     color:#8A5A12;
     background:#F8EFDD;
     border:1px solid #F0DCB4;
     border-radius:6px;
     padding:7px 11px;
     margin:10px 0 0;
     line-height:1.5;"

VAR EstiloEtiqueta =
    "display:inline-block;
     background:#F8EFDD;
     color:#8A5A12;
     border-radius:4px;
     padding:1px 5px;
     font-size:10px;
     font-weight:600;
     margin-left:5px;
     white-space:nowrap;"

VAR EstiloError =
    "display:inline-block;
     background:#FAECE7;
     color:#993C1D;
     border-radius:4px;
     padding:1px 5px;
     font-size:10px;
     font-weight:600;
     margin-left:5px;
     white-space:nowrap;"

VAR EstiloContadores =
    "display:flex;
     flex-wrap:wrap;
     gap:12px;
     margin:14px 0 4px;"

VAR EstiloContador =
    "flex:1 1 110px;
     min-width:0;
     background:#FAFBFF;
     border:1px solid #EAEDFF;
     border-radius:10px;
     padding:12px 14px;
     text-align:center;"

VAR EstiloNumero =
    "font-size:22px;
     font-weight:600;
     color:#1A1A1A;
     margin:0;"

VAR EstiloEtiquetaContador =
    "font-size:10.5px;
     color:#9B9B9B;
     text-transform:uppercase;
     letter-spacing:0.4px;
     margin:2px 0 0;"

VAR EstiloPie =
    "font-size:11px;
     color:#9B9B9B;
     border-top:1px solid #F0F0F0;
     margin:24px 0 0;
     padding-top:10px;"

VAR FaltaDescripcionTabla =
    "<span style='"
        & EstiloFalta
        & "'>Falta el propósito y la granularidad de esta tabla</span>"

VAR FaltaDescripcionColumna =
    "<span style='"
        & EstiloFalta
        & "'>Falta la descripción</span>"

VAR FaltaDescripcionMedida =
    "<span style='"
        & EstiloFalta
        & "'>Falta qué representa y su regla de negocio</span>"

VAR FaltaDescripcionCapa =
    "<span style='"
        & EstiloFalta
        & "'>Falta la descripción de esta capa de medidas</span>"

VAR HTMLEstructuras =
    CONCATENATEX (
        TablasEstructuras,

        VAR NombreTablaActual =
            [NombreTabla]

        VAR EtiquetaTablaActual =
            [EtiquetaTabla]

        VAR DescripcionTablaActual =
            [DescripcionTabla]

        VAR NombreTablaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreTablaActual,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR EtiquetaTablaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        EtiquetaTablaActual,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DescripcionTablaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        DescripcionTablaActual,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR FilasColumnas =
            CONCATENATEX (
                FILTER (
                    ColumnasADocumentar,
                    [NombreTabla] = NombreTablaActual
                ),

                VAR NombreColumnaActual =
                    [NombreColumna]

                VAR TipoDatoActual =
                    [TipoDato]

                VAR DescripcionColumnaActual =
                    [DescripcionColumna]

                VAR NombreColumnaSeguro =
                    SUBSTITUTE (
                        SUBSTITUTE (
                            SUBSTITUTE (
                                NombreColumnaActual,
                                "&", "&amp;"
                            ),
                            "<", "&lt;"
                        ),
                        ">", "&gt;"
                    )

                VAR TipoDatoSeguro =
                    SUBSTITUTE (
                        SUBSTITUTE (
                            SUBSTITUTE (
                                TipoDatoActual,
                                "&", "&amp;"
                            ),
                            "<", "&lt;"
                        ),
                        ">", "&gt;"
                    )

                VAR DescripcionColumnaSegura =
                    SUBSTITUTE (
                        SUBSTITUTE (
                            SUBSTITUTE (
                                DescripcionColumnaActual,
                                "&", "&amp;"
                            ),
                            "<", "&lt;"
                        ),
                        ">", "&gt;"
                    )

                RETURN

                    "<tr>"

                        & "<td style='"
                        & EstiloNombre
                        & "'>"
                        & NombreColumnaSeguro
                        & "</td>"

                        & "<td style='"
                        & EstiloMetadata
                        & "'>"
                        & TipoDatoSeguro
                        & "</td>"

                        & "<td style='"
                        & EstiloDescripcion
                        & "'>"
                        & IF (
                            DescripcionColumnaActual = "",
                            FaltaDescripcionColumna,
                            DescripcionColumnaSegura
                        )
                        & "</td>"

                    & "</tr>",

                "",

                [NombreColumna],

                ASC
            )

        RETURN

            "<p style='"
                & EstiloNombreSeccion
                & "'>"

                & NombreTablaSeguro

                & "<span style='"
                & EstiloRol
                & "'>"
                & EtiquetaTablaSegura
                & "</span>"

            & "</p>"

            & "<p style='"
                & EstiloParrafo
                & "'>"

                & IF (
                    DescripcionTablaActual = "",
                    FaltaDescripcionTabla,
                    DescripcionTablaSegura
                )

            & "</p>"

            & IF (
                FilasColumnas = "",

                "<p style='"
                    & EstiloParrafo
                    & "'>No hay columnas para mostrar.</p>",

                "<table style='"
                    & EstiloTabla
                    & "'>"

                    & "<tr>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Columna</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Tipo</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Descripción</th>"

                    & "</tr>"

                    & FilasColumnas

                & "</table>"
            ),

        "",

        FORMAT (
            [OrdenEtiqueta],
            "000"
        )
            & "|"
            & [NombreTabla],

        ASC
    )

VAR HTMLRelaciones =
    CONCATENATEX (
        RelacionesADocumentar,

        VAR TablaOrigenActual =
            [TablaOrigen]

        VAR ColumnaOrigenActual =
            [ColumnaOrigen]

        VAR CardinalidadOrigenActual =
            [CardinalidadOrigen]

        VAR TablaDestinoActual =
            [TablaDestino]

        VAR ColumnaDestinoActual =
            [ColumnaDestino]

        VAR CardinalidadDestinoActual =
            [CardinalidadDestino]

        VAR DireccionFiltroActual =
            [DireccionFiltro]

        VAR RelacionActivaActual =
            [RelacionActiva]

        VAR OrigenSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        TablaOrigenActual
                            & "["
                            & ColumnaOrigenActual
                            & "]",
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DestinoSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        TablaDestinoActual
                            & "["
                            & ColumnaDestinoActual
                            & "]",
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        RETURN

            "<tr>"

                & "<td style='"
                & EstiloNombre
                & "'>"
                & OrigenSeguro
                & "</td>"

                & "<td style='"
                & EstiloMetadata
                & "'>"
                & CardinalidadOrigenActual
                & " &rarr; "
                & CardinalidadDestinoActual
                & "</td>"

                & "<td style='"
                & EstiloNombre
                & "'>"
                & DestinoSeguro
                & "</td>"

                & "<td style='"
                & EstiloMetadata
                & "'>"
                & DireccionFiltroActual

                & IF (
                    RelacionActivaActual,
                    "",
                    "<span style='"
                        & EstiloEtiqueta
                        & "'>INACTIVA</span>"
                )

                & "</td>"

            & "</tr>",

        "",

        [TablaOrigen]
            & [ColumnaOrigen]
            & [TablaDestino]
            & [ColumnaDestino],

        ASC
    )

VAR TablasConMedidas =
    FILTER (
        SELECTCOLUMNS (
            TablasADocumentar,

            "NombreTabla",
                [NombreTabla],

            "EtiquetaTabla",
                [EtiquetaTabla],

            "OrdenEtiqueta",
                [OrdenEtiqueta]
        ),

        VAR NombreTablaActual =
            [NombreTabla]

        RETURN
            COUNTROWS (
                FILTER (
                    MedidasADocumentar,
                    [NombreTabla] = NombreTablaActual
                )
            ) > 0
    )

VAR HTMLMedidas =
    CONCATENATEX (
        TablasConMedidas,

        VAR NombreTablaMedidas =
            [NombreTabla]

        VAR EtiquetaTablaMedidas =
            [EtiquetaTabla]

        VAR DescripcionCapa =
            MAXX (
                FILTER (
                    TablasADocumentar,
                    [NombreTabla] = NombreTablaMedidas
                ),
                [DescripcionTabla]
            )

        VAR NombreTablaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreTablaMedidas,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR EtiquetaTablaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        EtiquetaTablaMedidas,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DescripcionCapaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        COALESCE (
                            DescripcionCapa,
                            ""
                        ),
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR MedidasDeTabla =
            FILTER (
                MedidasADocumentar,
                [NombreTabla] = NombreTablaMedidas
            )

        VAR Carpetas =
            DISTINCT (
                SELECTCOLUMNS (
                    MedidasDeTabla,
                    "Carpeta", [Carpeta]
                )
            )

        VAR HTMLCarpetas =
            CONCATENATEX (
                Carpetas,

                VAR CarpetaActual =
                    [Carpeta]

                VAR CarpetaSegura =
                    SUBSTITUTE (
                        SUBSTITUTE (
                            SUBSTITUTE (
                                CarpetaActual,
                                "&", "&amp;"
                            ),
                            "<", "&lt;"
                        ),
                        ">", "&gt;"
                    )

                VAR FilasMedidas =
                    CONCATENATEX (
                        FILTER (
                            MedidasDeTabla,
                            [Carpeta] = CarpetaActual
                        ),

                        VAR NombreMedidaActual =
                            [NombreMedida]

                        VAR DescripcionMedidaActual =
                            [DescripcionMedida]

                        VAR EstadoMedidaActual =
                            [EstadoMedida]

                        VAR NombreMedidaSeguro =
                            SUBSTITUTE (
                                SUBSTITUTE (
                                    SUBSTITUTE (
                                        NombreMedidaActual,
                                        "&", "&amp;"
                                    ),
                                    "<", "&lt;"
                                ),
                                ">", "&gt;"
                            )

                        VAR DescripcionMedidaSegura =
                            SUBSTITUTE (
                                SUBSTITUTE (
                                    SUBSTITUTE (
                                        DescripcionMedidaActual,
                                        "&", "&amp;"
                                    ),
                                    "<", "&lt;"
                                ),
                                ">", "&gt;"
                            )

                        RETURN

                            "<tr>"

                                & "<td style='"
                                & EstiloNombre
                                & "'>"

                                & NombreMedidaSeguro

                                & IF (
                                    EstadoMedidaActual
                                        IN {
                                            "Valid",
                                            "Ready"
                                        },
                                    "",
                                    "<span style='"
                                        & EstiloError
                                        & "'>ERROR</span>"
                                )

                                & "</td>"

                                & "<td style='"
                                & EstiloDescripcion
                                & "'>"

                                & IF (
                                    DescripcionMedidaActual = "",
                                    FaltaDescripcionMedida,
                                    DescripcionMedidaSegura
                                )

                                & "</td>"

                            & "</tr>",

                        "",

                        [NombreMedida],

                        ASC
                    )

                RETURN

                    "<p style='"
                        & EstiloNombreSeccion
                        & "'>"

                        & IF (
                            CarpetaActual = "",
                            "<span style='"
                                & EstiloFalta
                                & "'>Sin carpeta</span>",
                            CarpetaSegura
                        )

                    & "</p>"

                    & "<table style='"
                        & EstiloTabla
                        & "'>"

                        & "<tr>"

                            & "<th style='"
                            & EstiloEncabezado
                            & "'>Medida</th>"

                            & "<th style='"
                            & EstiloEncabezado
                            & "'>Descripción</th>"

                        & "</tr>"

                        & FilasMedidas

                    & "</table>",

                "",

                IF (
                    [Carpeta] = "",
                    "zzzz",
                    [Carpeta]
                ),

                ASC
            )

        RETURN

            "<p style='"
                & EstiloSubtitulo
                & "'>"

                & NombreTablaSeguro

                & "<span style='"
                & EstiloRol
                & "'>"
                & EtiquetaTablaSegura
                & "</span>"

            & "</p>"

            & "<p style='"
                & EstiloParrafo
                & "'>"

                & IF (
                    COALESCE (
                        DescripcionCapa,
                        ""
                    ) = "",
                    FaltaDescripcionCapa,
                    DescripcionCapaSegura
                )

            & "</p>"

            & HTMLCarpetas,

        "",

        FORMAT (
            [OrdenEtiqueta],
            "000"
        )
            & "|"
            & [NombreTabla],

        ASC
    )

RETURN

    EstiloReset

    & "<div style='"
        & EstiloContenedor
        & "'>"

        & "<div style='"
            & EstiloTarjeta
            & "'>"

            & "<p style='"
                & EstiloTitulo
                & "'>Documentación del modelo</p>"

            & "<p style='"
                & EstiloParrafo
                & "'>"
                & "La información brindada se genera a partir de los metadatos del modelo semántico."
                & "Las etiquetas de las tablas se toman de la configuración declarada en la medida."
            & "</p>"

            & "<div style='"
                & EstiloContadores
                & "'>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CantidadTablas
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Tablas</p>"

                & "</div>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CantidadColumnas
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Columnas</p>"

                & "</div>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CantidadMedidas
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Medidas</p>"

                & "</div>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CantidadRelaciones
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Relaciones</p>"

                & "</div>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CoberturaTexto
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Documentado</p>"

                & "</div>"


            & "</div>"

            & IF (
                CantidadHuecos = 0,

                "",

                "<p style='"
                    & EstiloAviso
                    & "'>"

                    & CantidadHuecos
                    & " de "
                    & CantidadObjetos
                    & " objetos todavía no tienen descripción. "
                    & "Los huecos quedan visibles para saber qué contexto falta."

                & "</p>"
            )

            & IF (
                CantidadSinClasificar = 0,

                "",

                "<p style='"
                    & EstiloAviso
                    & "'>"

                    & CantidadSinClasificar

                    & IF (
                        CantidadSinClasificar = 1,
                        " tabla todavía no tiene una etiqueta asignada. ",
                        " tablas todavía no tienen una etiqueta asignada. "
                    )

                    & "Revisá el bloque CONFIGURACIÓN DE TABLAS."

                & "</p>"
            )

            & "<p style='"
                & EstiloSubtitulo
                & "'>1. Estructura del modelo</p>"

            & "<p style='"
                & EstiloParrafo
                & "'>"
                & ResumenEstructura
            & "</p>"

            & "<p style='"
                & EstiloSubtitulo
                & "'>2. Estructuras de datos</p>"

            & IF (
                HTMLEstructuras = "",

                "<p style='"
                    & EstiloParrafo
                    & "'>No hay estructuras de datos para mostrar.</p>",

                HTMLEstructuras
            )

            & "<p style='"
                & EstiloSubtitulo
                & "'>3. Relaciones</p>"

            & IF (
                HTMLRelaciones = "",

                "<p style='"
                    & EstiloParrafo
                    & "'>No hay relaciones entre las tablas documentadas.</p>",

                "<table style='"
                    & EstiloTabla
                    & "'>"

                    & "<tr>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Desde</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Cardinalidad</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Hacia</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Filtro cruzado</th>"

                    & "</tr>"

                    & HTMLRelaciones

                & "</table>"
            )

            & "<p style='"
                & EstiloSubtitulo
                & "'>4. Medidas</p>"

            & IF (
                HTMLMedidas = "",

                "<p style='"
                    & EstiloParrafo
                    & "'>No hay medidas visibles para documentar.</p>",

                HTMLMedidas
            )

            & "<p style='"
                & EstiloPie
                & "'>"

                & "Diseñado por Loana Ibañez 💛 datavizargentina.com.ar"

                & IF (
                    ISBLANK ( DocumentacionActualizacion ),

                    "",

                    " · Última actualización: "
                        & FORMAT (
                            DocumentacionActualizacion,
                            "dd/mm/yyyy HH:mm"
                        )
                )

            & "</p>"


        & "</div>"

    & "</div>"
Copiar la medida completa en un solo bloque

La misma medida, con los cuatro bloques y el motor juntos, para ajustarla en VS Code o en Tabular Editor y pegarla de una sola vez.

HTML Documentación (versión completa)
HTML Documentación = 

// PARA REUTILIZAR ESTA MEDIDA:
// 1. Indicá tus tablas de introspección en FUENTES DE METADATA.
// 2. Asigná una etiqueta a cada tabla en CONFIGURACIÓN DE TABLAS.
// 3. Definí el comportamiento y el orden de esas etiquetas en SEMÁNTICA DE ETIQUETAS.
//
// Si sumaste una nueva tabla y no está configurada:
// → no desaparece;
// → no se clasifica automáticamente;
// → aparece como "Sin clasificar" al final.

// ------------------------------------------------------------------------------------------------------
// 1. FUENTES DE METADATA

VAR DocumentacionTablas =
    // Si tu tabla de documentación de tablas se llama distinto, cambiá DOC_tablas acá.
    SELECTCOLUMNS (
        DOC_tablas,
        "NombreTabla",      DOC_tablas[Name],
        "DescripcionTabla", COALESCE ( DOC_tablas[Description], "" ),
        "TablaOculta",      DOC_tablas[IsHidden]
    )

VAR DocumentacionColumnas =
    // Si tu tabla de documentación de columnas se llama distinto, cambiá DOC_columnas acá.
    SELECTCOLUMNS (
        DOC_columnas,
        "NombreTabla",         DOC_columnas[Table],
        "NombreColumna",       DOC_columnas[Name],
        "TipoDato",            DOC_columnas[DataType],
        "DescripcionColumna",  COALESCE ( DOC_columnas[Description], "" )
    )

VAR DocumentacionMedidas =
    // Si tu tabla de documentación de medidas se llama distinto, cambiá DOC_medidas acá.
    SELECTCOLUMNS (
        DOC_medidas,
        "NombreTabla",        DOC_medidas[Table],
        "NombreMedida",       DOC_medidas[Name],
        "Carpeta",            COALESCE ( DOC_medidas[DisplayFolder], "" ),
        "DescripcionMedida",  COALESCE ( DOC_medidas[Description], "" ),
        "MedidaOculta",       DOC_medidas[IsHidden],
        "EstadoMedida",       DOC_medidas[State]
    )

VAR DocumentacionRelaciones =
    // Si tu tabla de documentación de relaciones se llama distinto, cambiá DOC_relaciones acá.
    SELECTCOLUMNS (
        DOC_relaciones,
        "TablaOrigen",          DOC_relaciones[FromTable],
        "ColumnaOrigen",        DOC_relaciones[FromColumn],
        "CardinalidadOrigen",   DOC_relaciones[FromCardinality],
        "TablaDestino",         DOC_relaciones[ToTable],
        "ColumnaDestino",       DOC_relaciones[ToColumn],
        "CardinalidadDestino",  DOC_relaciones[ToCardinality],
        "DireccionFiltro",      DOC_relaciones[CrossFilteringBehavior],
        "RelacionActiva",       DOC_relaciones[IsActive]
    )


// ------------------------------------------------------------------------------------------------------
// 2. ÚLTIMA ACTUALIZACIÓN · FUENTE AUXILIAR OPCIONAL
//
// Si sumas la hora de actualización vas a poder tener registro de cuándo fue la última vez en actualizarse los datos. Esto es clave cuando sumaste o cambiaste descripciones y te encontras con las definiciones previas. Scrolleando al final del visual renderizado chequeas la fecha y hora. Esto es UX en acción.

// Descomentá este bloque e indicá el nombre de tu medida con su tabla correspondiente. 
// VAR DocumentacionActualizacion =
//     'Nombre tabla'[Nombre medida]

// Si armaste la medida, una vez que descomentaste la anterior, comentas esta! Si no vas a poner hora de última actualización, dejá este bloque como está así no se rompe la medida.
VAR DocumentacionActualizacion =
        BLANK ()

// ------------------------------------------------------------------------------------------------------
// 3. CONFIGURACIÓN DE TABLAS
//
// Declaramos qué etiqueta describe a cada tabla.
//
// No repetís una tabla.
// Si una tabla no aparece acá, recibe la etiqueta marcada como fallback en el bloque 4.

VAR ConfiguracionTablas =
//Acá reemplazá con tus tablas, yo puse un ejemplo
    DATATABLE (
        "TablaConfig", STRING,
        "EtiquetaConfig", STRING,
        {
            { "fct_sales",          "Hechos" },
            { "fct_sentiment",      "Hechos" },
            { "dim_calendar",       "Dimensión" },
            { "dim_geography",      "Dimensión" },
            { "dim_product",        "Dimensión" },
            { "Visualizations",     "Medidas" },
            { "⚙️ Measures",         "Medidas" },
            { "param_key_insigths", "Parámetro" },
            { "param_userguide",    "Parámetro" },
            { "Documentación Tablas", "Excluir" },
            { "aux_last_refresh",   "Auxiliar" }
        }
    )


// ------------------------------------------------------------------------------------------------------
// 4. SEMÁNTICA DE ETIQUETAS
//
// Acá definís qué significa cada etiqueta para la página.
//
// DocumentarEstructura:
//     1 = muestra sus columnas en el diccionario de estructuras
//     0 = no las muestra (por ejemplo, una tabla que solo contiene medidas)
//
// Incluir:
//     1 = forma parte de la documentación
//     0 = queda completamente afuera
//
// EsFallback:
//     marcá una sola etiqueta con 1. Esa recibe cualquier tabla nueva no configurada.
//
// Podés renombrar "Medidas" o cualquier otra etiqueta.
// La lógica de render no depende del texto: reutiliza esta configuración.

VAR ConfiguracionEtiquetas =
    DATATABLE (
        "EtiquetaConfig", STRING,
        "Orden", INTEGER,
        "DocumentarEstructura", INTEGER,
        "Incluir", INTEGER,
        "EsFallback", INTEGER,
        {
            // Etiqueta              Orden  Estructura  Incluir  Fallback
            { "Hechos",                 1,          1,       1,        0 },
            { "Dimensión",              2,          1,       1,        0 },
            { "Dimensión auxiliar",     3,          1,       1,        0 },
            { "Auxiliar",               4,          1,       1,        0 },
            { "Parámetro",              5,          1,       1,        0 },
            { "Medidas",                6,          0,       1,        0 },
            { "Sin clasificar",       999,          1,       1,        1 },
            { "Excluir",             1000,          0,       0,        0 }
        }
    )

// Listo. No necesitamos que toques más la medida.
// Desde este punto la documentación se genera automáticamente. Espero que te sea de utilidad, Loa 💛

VAR EtiquetaFallback =
    MAXX (
        FILTER (
            ConfiguracionEtiquetas,
            [EsFallback] = 1
        ),
        [EtiquetaConfig]
    )

VAR TablasBase =
    FILTER (
        DocumentacionTablas,
        NOT [TablaOculta]
            && LEFT (
                LOWER ( [NombreTabla] ),
                4
            ) <> "doc_"
    )

VAR TablasConEtiqueta =
    ADDCOLUMNS (
        TablasBase,

        "EtiquetaTabla",

            VAR NombreTablaActual =
                [NombreTabla]

            VAR EtiquetaEncontrada =
                MAXX (
                    FILTER (
                        ConfiguracionTablas,
                        [TablaConfig] = NombreTablaActual
                    ),
                    [EtiquetaConfig]
                )

            RETURN
                COALESCE (
                    EtiquetaEncontrada,
                    EtiquetaFallback
                )
    )

VAR TablasConSemantica =
    ADDCOLUMNS (
        TablasConEtiqueta,

        "OrdenEtiqueta",
            VAR EtiquetaActual = [EtiquetaTabla]
            RETURN
                COALESCE (
                    MAXX (
                        FILTER (
                            ConfiguracionEtiquetas,
                            [EtiquetaConfig] = EtiquetaActual
                        ),
                        [Orden]
                    ),
                    998
                ),

        "DocumentarEstructuraTabla",
            VAR EtiquetaActual = [EtiquetaTabla]
            RETURN
                COALESCE (
                    MAXX (
                        FILTER (
                            ConfiguracionEtiquetas,
                            [EtiquetaConfig] = EtiquetaActual
                        ),
                        [DocumentarEstructura]
                    ),
                    1
                ),

        "IncluirTabla",
            VAR EtiquetaActual = [EtiquetaTabla]
            RETURN
                COALESCE (
                    MAXX (
                        FILTER (
                            ConfiguracionEtiquetas,
                            [EtiquetaConfig] = EtiquetaActual
                        ),
                        [Incluir]
                    ),
                    1
                ),

        "EsFallbackTabla",
            VAR EtiquetaActual = [EtiquetaTabla]
            RETURN
                COALESCE (
                    MAXX (
                        FILTER (
                            ConfiguracionEtiquetas,
                            [EtiquetaConfig] = EtiquetaActual
                        ),
                        [EsFallback]
                    ),
                    0
                )
    )

VAR TablasADocumentar =
    FILTER (
        TablasConSemantica,
        [IncluirTabla] = 1
    )

VAR NombresTablas =
    SELECTCOLUMNS (
        TablasADocumentar,
        "NombreTabla", [NombreTabla]
    )


VAR TablasConColumnasDocumentables =
    FILTER (
        TablasADocumentar,
        [DocumentarEstructuraTabla] = 1
    )

VAR NombresTablasConColumnasDocumentables =
    SELECTCOLUMNS (
        TablasConColumnasDocumentables,
        "NombreTabla", [NombreTabla]
    )

VAR ColumnasADocumentar =
    FILTER (
        DocumentacionColumnas,

        [NombreTabla]
            IN NombresTablasConColumnasDocumentables

            && NOT CONTAINSSTRING (
                [NombreColumna],
                "RowNumber-"
            )
    )

VAR MedidasADocumentar =
    FILTER (
        DocumentacionMedidas,

        NOT [MedidaOculta]
            && [NombreTabla] IN NombresTablas
    )

VAR RelacionesADocumentar =
    FILTER (
        DocumentacionRelaciones,

        [TablaOrigen] IN NombresTablas
            && [TablaDestino] IN NombresTablas
    )

VAR TablasConColumnas =
    DISTINCT (
        SELECTCOLUMNS (
            ColumnasADocumentar,
            "NombreTabla", [NombreTabla]
        )
    )

VAR TablasEstructuras =
    FILTER (
        TablasADocumentar,

        [DocumentarEstructuraTabla] = 1
            && [NombreTabla] IN TablasConColumnas
    )

VAR CantidadTablas =
    COUNTROWS ( TablasADocumentar ) + 0

VAR CantidadColumnas =
    COUNTROWS ( ColumnasADocumentar ) + 0

VAR CantidadMedidas =
    COUNTROWS ( MedidasADocumentar ) + 0

VAR CantidadRelaciones =
    COUNTROWS ( RelacionesADocumentar ) + 0

VAR CantidadSinClasificar =
    COUNTROWS (
        FILTER (
            TablasADocumentar,
            [EsFallbackTabla] = 1
        )
    ) + 0

VAR CantidadTablasConDescripcion =
    COUNTROWS (
        FILTER (
            TablasADocumentar,
            [DescripcionTabla] <> ""
        )
    )

VAR CantidadColumnasConDescripcion =
    COUNTROWS (
        FILTER (
            ColumnasADocumentar,
            [DescripcionColumna] <> ""
        )
    )

VAR CantidadMedidasConDescripcion =
    COUNTROWS (
        FILTER (
            MedidasADocumentar,
            [DescripcionMedida] <> ""
        )
    )

VAR CantidadObjetos =
        CantidadTablas
        + CantidadColumnas
        + CantidadMedidas

VAR CantidadObjetosDocumentados =
        CantidadTablasConDescripcion
        + CantidadColumnasConDescripcion
        + CantidadMedidasConDescripcion

VAR CantidadHuecos =
    CantidadObjetos
        - CantidadObjetosDocumentados

VAR CoberturaDocumentacion =
    DIVIDE (
        CantidadObjetosDocumentados,
        CantidadObjetos,
        0
    )

VAR CoberturaTexto =
    FORMAT (
        COALESCE (
            CoberturaDocumentacion,
            0
        ),
        "0%"
    )

VAR EtiquetasPresentes =
    DISTINCT (
        SELECTCOLUMNS (
            TablasADocumentar,
            "EtiquetaResumen", [EtiquetaTabla]
        )
    )

VAR EtiquetasConResumen =
    ADDCOLUMNS (
        EtiquetasPresentes,

        "OrdenResumen",

            VAR EtiquetaActual =
                [EtiquetaResumen]

            VAR OrdenActual =
                MAXX (
                    FILTER (
                        ConfiguracionEtiquetas,
                        [EtiquetaConfig] = EtiquetaActual
                    ),
                    [Orden]
                )

            RETURN
                COALESCE (
                    OrdenActual,
                    998
                ),

        "CantidadResumen",

            VAR EtiquetaActual =
                [EtiquetaResumen]

            RETURN
                COUNTROWS (
                    FILTER (
                        TablasADocumentar,
                        [EtiquetaTabla] = EtiquetaActual
                    )
                )
    )

VAR ResumenEtiquetas =
    CONCATENATEX (
        EtiquetasConResumen,

        [EtiquetaResumen]
            & ": "
            & [CantidadResumen],

        " · ",

        FORMAT (
            [OrdenResumen],
            "000"
        )
            & "|"
            & [EtiquetaResumen],

        ASC
    )

VAR ResumenEstructura =
    "El alcance documentado contiene "
        & CantidadTablas
        & IF (
            CantidadTablas = 1,
            " tabla. ",
            " tablas. "
        )
        & ResumenEtiquetas
        & ". Entre las tablas incluidas hay "
        & CantidadRelaciones
        & IF (
            CantidadRelaciones = 1,
            " relación.",
            " relaciones."
        )

VAR Fuente =
    "Segoe UI Variable Text,Segoe UI,system-ui,Arial,sans-serif"

VAR FuenteMono =
    "Consolas,Menlo,monospace"

VAR EstiloReset =
    "<style>
        html,body{
            margin:0;
            padding:0;
            height:100%;
            width:100%;
            overflow:hidden;
        }

        *{
            box-sizing:border-box;
        }

        p{
            margin:0;
        }

        table{
            table-layout:fixed;
        }
    </style>"

VAR EstiloContenedor =
    "position:fixed;
     inset:0;
     overflow:hidden;
     background:transparent;"

VAR EstiloTarjeta =
    "background:#FFFFFF;
     border:0.5px solid #E4E4E4;
     border-radius:12px;
     padding:20px 3%;
     width:100%;
     height:100%;
     overflow-y:auto;
     overflow-x:hidden;
     font-family:" & Fuente & ";"

VAR EstiloTitulo =
    "font-size:19px;
     font-weight:600;
     color:#1A1A1A;
     margin:0 0 6px;"

VAR EstiloSubtitulo =
    "font-size:15px;
     font-weight:600;
     color:#1A1A1A;
     margin:26px 0 8px;"

VAR EstiloNombreSeccion =
    "font-size:13px;
     font-weight:600;
     color:#1A1A1A;
     margin:20px 0 4px;"

VAR EstiloParrafo =
    "font-size:12.5px;
     line-height:1.55;
     color:#6B6B6B;
     margin:0 0 10px;"

VAR EstiloTabla =
    "width:100%;
     border-collapse:collapse;
     margin:6px 0 4px;"

VAR EstiloEncabezado =
    "text-align:left;
     font-size:11px;
     font-weight:600;
     color:#6B6B6B;
     text-transform:uppercase;
     letter-spacing:0.4px;
     padding:7px 10px;
     background:#FAFBFF;
     border-bottom:1px solid #E4E4E4;
     overflow-wrap:anywhere;"

VAR EstiloNombre =
    "font-family:" & FuenteMono & ";
     font-size:11.5px;
     color:#4752CE;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloMetadata =
    "font-size:11px;
     color:#9B9B9B;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloDescripcion =
    "font-size:12px;
     color:#6B6B6B;
     padding:7px 10px;
     border-bottom:1px solid #F0F0F0;
     vertical-align:top;
     line-height:1.45;
     word-break:break-word;
     overflow-wrap:anywhere;"

VAR EstiloRol =
    "font-size:11px;
     color:#9B9B9B;
     font-weight:400;
     padding-left:8px;"

VAR EstiloFalta =
    "font-size:11.5px;
     color:#B9B9C6;
     font-style:italic;"

VAR EstiloAviso =
    "font-size:11.5px;
     color:#8A5A12;
     background:#F8EFDD;
     border:1px solid #F0DCB4;
     border-radius:6px;
     padding:7px 11px;
     margin:10px 0 0;
     line-height:1.5;"

VAR EstiloEtiqueta =
    "display:inline-block;
     background:#F8EFDD;
     color:#8A5A12;
     border-radius:4px;
     padding:1px 5px;
     font-size:10px;
     font-weight:600;
     margin-left:5px;
     white-space:nowrap;"

VAR EstiloError =
    "display:inline-block;
     background:#FAECE7;
     color:#993C1D;
     border-radius:4px;
     padding:1px 5px;
     font-size:10px;
     font-weight:600;
     margin-left:5px;
     white-space:nowrap;"

VAR EstiloContadores =
    "display:flex;
     flex-wrap:wrap;
     gap:12px;
     margin:14px 0 4px;"

VAR EstiloContador =
    "flex:1 1 110px;
     min-width:0;
     background:#FAFBFF;
     border:1px solid #EAEDFF;
     border-radius:10px;
     padding:12px 14px;
     text-align:center;"

VAR EstiloNumero =
    "font-size:22px;
     font-weight:600;
     color:#1A1A1A;
     margin:0;"

VAR EstiloEtiquetaContador =
    "font-size:10.5px;
     color:#9B9B9B;
     text-transform:uppercase;
     letter-spacing:0.4px;
     margin:2px 0 0;"

VAR EstiloPie =
    "font-size:11px;
     color:#9B9B9B;
     border-top:1px solid #F0F0F0;
     margin:24px 0 0;
     padding-top:10px;"

VAR FaltaDescripcionTabla =
    "<span style='"
        & EstiloFalta
        & "'>Falta el propósito y la granularidad de esta tabla</span>"

VAR FaltaDescripcionColumna =
    "<span style='"
        & EstiloFalta
        & "'>Falta la descripción</span>"

VAR FaltaDescripcionMedida =
    "<span style='"
        & EstiloFalta
        & "'>Falta qué representa y su regla de negocio</span>"

VAR FaltaDescripcionCapa =
    "<span style='"
        & EstiloFalta
        & "'>Falta la descripción de esta capa de medidas</span>"

VAR HTMLEstructuras =
    CONCATENATEX (
        TablasEstructuras,

        VAR NombreTablaActual =
            [NombreTabla]

        VAR EtiquetaTablaActual =
            [EtiquetaTabla]

        VAR DescripcionTablaActual =
            [DescripcionTabla]

        VAR NombreTablaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreTablaActual,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR EtiquetaTablaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        EtiquetaTablaActual,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DescripcionTablaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        DescripcionTablaActual,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR FilasColumnas =
            CONCATENATEX (
                FILTER (
                    ColumnasADocumentar,
                    [NombreTabla] = NombreTablaActual
                ),

                VAR NombreColumnaActual =
                    [NombreColumna]

                VAR TipoDatoActual =
                    [TipoDato]

                VAR DescripcionColumnaActual =
                    [DescripcionColumna]

                VAR NombreColumnaSeguro =
                    SUBSTITUTE (
                        SUBSTITUTE (
                            SUBSTITUTE (
                                NombreColumnaActual,
                                "&", "&amp;"
                            ),
                            "<", "&lt;"
                        ),
                        ">", "&gt;"
                    )

                VAR TipoDatoSeguro =
                    SUBSTITUTE (
                        SUBSTITUTE (
                            SUBSTITUTE (
                                TipoDatoActual,
                                "&", "&amp;"
                            ),
                            "<", "&lt;"
                        ),
                        ">", "&gt;"
                    )

                VAR DescripcionColumnaSegura =
                    SUBSTITUTE (
                        SUBSTITUTE (
                            SUBSTITUTE (
                                DescripcionColumnaActual,
                                "&", "&amp;"
                            ),
                            "<", "&lt;"
                        ),
                        ">", "&gt;"
                    )

                RETURN

                    "<tr>"

                        & "<td style='"
                        & EstiloNombre
                        & "'>"
                        & NombreColumnaSeguro
                        & "</td>"

                        & "<td style='"
                        & EstiloMetadata
                        & "'>"
                        & TipoDatoSeguro
                        & "</td>"

                        & "<td style='"
                        & EstiloDescripcion
                        & "'>"
                        & IF (
                            DescripcionColumnaActual = "",
                            FaltaDescripcionColumna,
                            DescripcionColumnaSegura
                        )
                        & "</td>"

                    & "</tr>",

                "",

                [NombreColumna],

                ASC
            )

        RETURN

            "<p style='"
                & EstiloNombreSeccion
                & "'>"

                & NombreTablaSeguro

                & "<span style='"
                & EstiloRol
                & "'>"
                & EtiquetaTablaSegura
                & "</span>"

            & "</p>"

            & "<p style='"
                & EstiloParrafo
                & "'>"

                & IF (
                    DescripcionTablaActual = "",
                    FaltaDescripcionTabla,
                    DescripcionTablaSegura
                )

            & "</p>"

            & IF (
                FilasColumnas = "",

                "<p style='"
                    & EstiloParrafo
                    & "'>No hay columnas para mostrar.</p>",

                "<table style='"
                    & EstiloTabla
                    & "'>"

                    & "<tr>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Columna</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Tipo</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Descripción</th>"

                    & "</tr>"

                    & FilasColumnas

                & "</table>"
            ),

        "",

        FORMAT (
            [OrdenEtiqueta],
            "000"
        )
            & "|"
            & [NombreTabla],

        ASC
    )

VAR HTMLRelaciones =
    CONCATENATEX (
        RelacionesADocumentar,

        VAR TablaOrigenActual =
            [TablaOrigen]

        VAR ColumnaOrigenActual =
            [ColumnaOrigen]

        VAR CardinalidadOrigenActual =
            [CardinalidadOrigen]

        VAR TablaDestinoActual =
            [TablaDestino]

        VAR ColumnaDestinoActual =
            [ColumnaDestino]

        VAR CardinalidadDestinoActual =
            [CardinalidadDestino]

        VAR DireccionFiltroActual =
            [DireccionFiltro]

        VAR RelacionActivaActual =
            [RelacionActiva]

        VAR OrigenSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        TablaOrigenActual
                            & "["
                            & ColumnaOrigenActual
                            & "]",
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DestinoSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        TablaDestinoActual
                            & "["
                            & ColumnaDestinoActual
                            & "]",
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        RETURN

            "<tr>"

                & "<td style='"
                & EstiloNombre
                & "'>"
                & OrigenSeguro
                & "</td>"

                & "<td style='"
                & EstiloMetadata
                & "'>"
                & CardinalidadOrigenActual
                & " &rarr; "
                & CardinalidadDestinoActual
                & "</td>"

                & "<td style='"
                & EstiloNombre
                & "'>"
                & DestinoSeguro
                & "</td>"

                & "<td style='"
                & EstiloMetadata
                & "'>"
                & DireccionFiltroActual

                & IF (
                    RelacionActivaActual,
                    "",
                    "<span style='"
                        & EstiloEtiqueta
                        & "'>INACTIVA</span>"
                )

                & "</td>"

            & "</tr>",

        "",

        [TablaOrigen]
            & [ColumnaOrigen]
            & [TablaDestino]
            & [ColumnaDestino],

        ASC
    )

VAR TablasConMedidas =
    FILTER (
        SELECTCOLUMNS (
            TablasADocumentar,

            "NombreTabla",
                [NombreTabla],

            "EtiquetaTabla",
                [EtiquetaTabla],

            "OrdenEtiqueta",
                [OrdenEtiqueta]
        ),

        VAR NombreTablaActual =
            [NombreTabla]

        RETURN
            COUNTROWS (
                FILTER (
                    MedidasADocumentar,
                    [NombreTabla] = NombreTablaActual
                )
            ) > 0
    )

VAR HTMLMedidas =
    CONCATENATEX (
        TablasConMedidas,

        VAR NombreTablaMedidas =
            [NombreTabla]

        VAR EtiquetaTablaMedidas =
            [EtiquetaTabla]

        VAR DescripcionCapa =
            MAXX (
                FILTER (
                    TablasADocumentar,
                    [NombreTabla] = NombreTablaMedidas
                ),
                [DescripcionTabla]
            )

        VAR NombreTablaSeguro =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        NombreTablaMedidas,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR EtiquetaTablaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        EtiquetaTablaMedidas,
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR DescripcionCapaSegura =
            SUBSTITUTE (
                SUBSTITUTE (
                    SUBSTITUTE (
                        COALESCE (
                            DescripcionCapa,
                            ""
                        ),
                        "&", "&amp;"
                    ),
                    "<", "&lt;"
                ),
                ">", "&gt;"
            )

        VAR MedidasDeTabla =
            FILTER (
                MedidasADocumentar,
                [NombreTabla] = NombreTablaMedidas
            )

        VAR Carpetas =
            DISTINCT (
                SELECTCOLUMNS (
                    MedidasDeTabla,
                    "Carpeta", [Carpeta]
                )
            )

        VAR HTMLCarpetas =
            CONCATENATEX (
                Carpetas,

                VAR CarpetaActual =
                    [Carpeta]

                VAR CarpetaSegura =
                    SUBSTITUTE (
                        SUBSTITUTE (
                            SUBSTITUTE (
                                CarpetaActual,
                                "&", "&amp;"
                            ),
                            "<", "&lt;"
                        ),
                        ">", "&gt;"
                    )

                VAR FilasMedidas =
                    CONCATENATEX (
                        FILTER (
                            MedidasDeTabla,
                            [Carpeta] = CarpetaActual
                        ),

                        VAR NombreMedidaActual =
                            [NombreMedida]

                        VAR DescripcionMedidaActual =
                            [DescripcionMedida]

                        VAR EstadoMedidaActual =
                            [EstadoMedida]

                        VAR NombreMedidaSeguro =
                            SUBSTITUTE (
                                SUBSTITUTE (
                                    SUBSTITUTE (
                                        NombreMedidaActual,
                                        "&", "&amp;"
                                    ),
                                    "<", "&lt;"
                                ),
                                ">", "&gt;"
                            )

                        VAR DescripcionMedidaSegura =
                            SUBSTITUTE (
                                SUBSTITUTE (
                                    SUBSTITUTE (
                                        DescripcionMedidaActual,
                                        "&", "&amp;"
                                    ),
                                    "<", "&lt;"
                                ),
                                ">", "&gt;"
                            )

                        RETURN

                            "<tr>"

                                & "<td style='"
                                & EstiloNombre
                                & "'>"

                                & NombreMedidaSeguro

                                & IF (
                                    EstadoMedidaActual
                                        IN {
                                            "Valid",
                                            "Ready"
                                        },
                                    "",
                                    "<span style='"
                                        & EstiloError
                                        & "'>ERROR</span>"
                                )

                                & "</td>"

                                & "<td style='"
                                & EstiloDescripcion
                                & "'>"

                                & IF (
                                    DescripcionMedidaActual = "",
                                    FaltaDescripcionMedida,
                                    DescripcionMedidaSegura
                                )

                                & "</td>"

                            & "</tr>",

                        "",

                        [NombreMedida],

                        ASC
                    )

                RETURN

                    "<p style='"
                        & EstiloNombreSeccion
                        & "'>"

                        & IF (
                            CarpetaActual = "",
                            "<span style='"
                                & EstiloFalta
                                & "'>Sin carpeta</span>",
                            CarpetaSegura
                        )

                    & "</p>"

                    & "<table style='"
                        & EstiloTabla
                        & "'>"

                        & "<tr>"

                            & "<th style='"
                            & EstiloEncabezado
                            & "'>Medida</th>"

                            & "<th style='"
                            & EstiloEncabezado
                            & "'>Descripción</th>"

                        & "</tr>"

                        & FilasMedidas

                    & "</table>",

                "",

                IF (
                    [Carpeta] = "",
                    "zzzz",
                    [Carpeta]
                ),

                ASC
            )

        RETURN

            "<p style='"
                & EstiloSubtitulo
                & "'>"

                & NombreTablaSeguro

                & "<span style='"
                & EstiloRol
                & "'>"
                & EtiquetaTablaSegura
                & "</span>"

            & "</p>"

            & "<p style='"
                & EstiloParrafo
                & "'>"

                & IF (
                    COALESCE (
                        DescripcionCapa,
                        ""
                    ) = "",
                    FaltaDescripcionCapa,
                    DescripcionCapaSegura
                )

            & "</p>"

            & HTMLCarpetas,

        "",

        FORMAT (
            [OrdenEtiqueta],
            "000"
        )
            & "|"
            & [NombreTabla],

        ASC
    )

RETURN

    EstiloReset

    & "<div style='"
        & EstiloContenedor
        & "'>"

        & "<div style='"
            & EstiloTarjeta
            & "'>"

            & "<p style='"
                & EstiloTitulo
                & "'>Documentación del modelo</p>"

            & "<p style='"
                & EstiloParrafo
                & "'>"
                & "La información brindada se genera a partir de los metadatos del modelo semántico."
                & "Las etiquetas de las tablas se toman de la configuración declarada en la medida."
            & "</p>"

            & "<div style='"
                & EstiloContadores
                & "'>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CantidadTablas
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Tablas</p>"

                & "</div>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CantidadColumnas
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Columnas</p>"

                & "</div>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CantidadMedidas
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Medidas</p>"

                & "</div>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CantidadRelaciones
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Relaciones</p>"

                & "</div>"


                & "<div style='"
                    & EstiloContador
                    & "'>"

                    & "<p style='"
                        & EstiloNumero
                        & "'>"
                        & CoberturaTexto
                    & "</p>"

                    & "<p style='"
                        & EstiloEtiquetaContador
                        & "'>Documentado</p>"

                & "</div>"


            & "</div>"

            & IF (
                CantidadHuecos = 0,

                "",

                "<p style='"
                    & EstiloAviso
                    & "'>"

                    & CantidadHuecos
                    & " de "
                    & CantidadObjetos
                    & " objetos todavía no tienen descripción. "
                    & "Los huecos quedan visibles para saber qué contexto falta."

                & "</p>"
            )

            & IF (
                CantidadSinClasificar = 0,

                "",

                "<p style='"
                    & EstiloAviso
                    & "'>"

                    & CantidadSinClasificar

                    & IF (
                        CantidadSinClasificar = 1,
                        " tabla todavía no tiene una etiqueta asignada. ",
                        " tablas todavía no tienen una etiqueta asignada. "
                    )

                    & "Revisá el bloque CONFIGURACIÓN DE TABLAS."

                & "</p>"
            )

            & "<p style='"
                & EstiloSubtitulo
                & "'>1. Estructura del modelo</p>"

            & "<p style='"
                & EstiloParrafo
                & "'>"
                & ResumenEstructura
            & "</p>"

            & "<p style='"
                & EstiloSubtitulo
                & "'>2. Estructuras de datos</p>"

            & IF (
                HTMLEstructuras = "",

                "<p style='"
                    & EstiloParrafo
                    & "'>No hay estructuras de datos para mostrar.</p>",

                HTMLEstructuras
            )

            & "<p style='"
                & EstiloSubtitulo
                & "'>3. Relaciones</p>"

            & IF (
                HTMLRelaciones = "",

                "<p style='"
                    & EstiloParrafo
                    & "'>No hay relaciones entre las tablas documentadas.</p>",

                "<table style='"
                    & EstiloTabla
                    & "'>"

                    & "<tr>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Desde</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Cardinalidad</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Hacia</th>"

                        & "<th style='"
                        & EstiloEncabezado
                        & "'>Filtro cruzado</th>"

                    & "</tr>"

                    & HTMLRelaciones

                & "</table>"
            )

            & "<p style='"
                & EstiloSubtitulo
                & "'>4. Medidas</p>"

            & IF (
                HTMLMedidas = "",

                "<p style='"
                    & EstiloParrafo
                    & "'>No hay medidas visibles para documentar.</p>",

                HTMLMedidas
            )

            & "<p style='"
                & EstiloPie
                & "'>"

                & "Diseñado por Loana Ibañez 💛 datavizargentina.com.ar"

                & IF (
                    ISBLANK ( DocumentacionActualizacion ),

                    "",

                    " · Última actualización: "
                        & FORMAT (
                            DocumentacionActualizacion,
                            "dd/mm/yyyy HH:mm"
                        )
                )

            & "</p>"


        & "</div>"

    & "</div>"

Lo que cambia respecto de la versión de 05

En 05 la pregunta era: ¿podemos leer el modelo sin importar cómo esté armado? Ahora la pregunta cambia: ¿podemos usar las decisiones que ya tomamos para organizar esa información sin volver a escribirla a mano?

La página completa agrega orientación antes del detalle: cuenta qué contiene el modelo, hace visibles los huecos, ordena las tablas según su función, muestra sus descripciones y columnas, explicita las relaciones y organiza las medidas por sus capas y carpetas.

Pero la fuente sigue siendo la misma.

Una descripción cambia en el modelo  →  cambia acá
Aparece una medida                   →  aparece acá
Se agrega una relación               →  aparece acá
Se suma una tabla nueva              →  aparece acá, aunque quede Sin clasificar
Página de documentación renderizada dentro de Power BI: contadores de tablas, columnas, medidas, relaciones y porcentaje documentado, avisos de huecos y de tablas sin clasificar, y el diccionario de estructuras con las descripciones de cada columna.

La medida de 05 leía el modelo. Esta versión usa el contexto que fuimos construyendo para explicarlo.

  • Automatizamos todo lo que el modelo puede decir de sí mismo.
  • Declaramos las dos cosas que necesitan criterio: qué significa cada tabla y en qué orden queremos leerlas.
Etapa 3 de 4

Evaluamos

Comprobamos si el cambio realmente hizo más fácil trabajar con el modelo.

09

¿Cómo sabemos si esto mejoró algo?

¿Alguien que desarrolla soluciones de BI y no construyó este modelo puede modificarlo sin hablar con quien lo creó?

Probalo. Dale el modelo a alguien que no lo construyó y pedile que:

  1. Explique qué mide una métrica.
  2. Encuentre la regla de negocio que la define.
  3. Diga qué tablas intervienen.
  4. Detecte una relación inactiva.
  5. Indique dónde agregaría una medida nueva.

No hace falta montar una prueba formal: una sola persona y una de estas tareas ya pueden darte una primera señal. Si después querés comparar con más casos, repetí el mismo ejercicio.

Por cada tarea, registrá tres cosas:

ResultadoCorrecto, parcial o incorrecto
TiempoMinutos hasta la respuesta
Ayuda requeridaNinguna, una pista o una explicación

Repetí la prueba antes y después de documentar. Si el resultado mejora y bajan el tiempo y la ayuda requerida, tenés evidencia, no solo una sensación.

10

El ritual de los 5 minutos

Antes de cerrar cada entrega, una pasada rápida:

Etapa 4 de 4

Compartimos

Ponemos esta solución en contexto y abrimos la conversación.

Esta es la solución que me sirvió a mí

Con esta página cubro lo que necesito documentar dentro del tablero. Eso no agota todas las posibilidades: otros equipos la acompañan con una bitácora de cambios, el historial de commits o herramientas de gobierno como Microsoft Purview.

¿CÓMO LO ESTÁN RESOLVIENDO USTEDES?

¿Dónde vive hoy la documentación de tus tableros?

Esta es una forma de hacerlo que podemos usar como punto de partida y adaptar. Si en tu equipo lo resuelven de otra manera, me interesa conocerla.

💛 Loana Ibañez · Data Lab 001