feat(metabase): smartscalar KPI builders (sql + payload + dimension tag)

3 helpers puros para construir KPIs con display=smartscalar y comparacion
vs n-1 sin que Metabase v0.59 pida breakout temporal. Replican el patron
del dashboard Informe Lean (UNION ALL de 2 filas periodo/valor) y rellenan
la firma exacta de template-tags que el frontend MBQL5 acepta.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 18:29:26 +02:00
parent dabc945eda
commit b4db4e4ef5
5 changed files with 531 additions and 0 deletions
@@ -0,0 +1,77 @@
---
name: metabase_smartscalar_kpi_sql
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: pure
signature: "def metabase_smartscalar_kpi_sql(*, act_expr: str, n1_expr: str, body_sql: str, date_expr: str = 'MIN(fecha)') -> str"
description: "Envuelve agregaciones actual+n-1 en el patron de 2 filas (periodo, valor) que el display smartscalar de Metabase v0.59 requiere para mostrar comparacion vs ano anterior sin pedir breakout temporal. Genera SQL nativo BigQuery con UNION ALL d_min/d_min-52w."
tags: [metabase, smartscalar, kpi, sql, bigquery, pure, builder]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
- name: act_expr
desc: "Expresion SQL de agregado para el periodo actual, ej. 'ROUND(SUM(v.venta_n), 2)' o 'SAFE_DIVIDE(SUM(a), NULLIF(SUM(b),0))'"
- name: n1_expr
desc: "Expresion SQL para el mismo agregado del ano anterior, ej. 'ROUND(SUM(v.venta_n1), 2)'"
- name: body_sql
desc: "Cuerpo SQL desde 'FROM' que define las tablas/CTEs y filtros con template-tags Metabase ([[AND {{tag}}]])"
- name: date_expr
desc: "Expresion para extraer la fecha minima del periodo; usada como periodo en la fila actual y ancla para DATE_SUB de la fila n-1. Default 'MIN(fecha)'"
output: "string con SQL nativo BigQuery que devuelve 2 filas con columnas periodo (DATE) y valor (numero); fila 1 = periodo n-1, fila 2 = periodo actual"
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/metabase/smartscalar.py"
---
## Por que existe
El display ``smartscalar`` de Metabase v0.59.4 con ``previousValue`` requiere
una serie temporal para mostrar comparacion. Si el query devuelve un solo
numero el frontend muestra el error "Agrupa solo por un campo de tiempo para
ver como ha cambiado con el tiempo".
El truco probado en el dashboard ``Informe Lean`` (cards 9340-9373) es
producir 2 filas sinteticas: una para el periodo actual (``d_min``, ``act``)
y otra para n-1 (``d_min - 52 weeks``, ``n1``). Smartscalar lo interpreta como
serie de 2 puntos y compara naturalmente.
Esta funcion automatiza ese patron: el caller solo provee el cuerpo SQL con
sus filtros y las dos expresiones de agregado.
## Ejemplo
```python
body = """
FROM `proj.ds.base_margenes_aa` `proj.ds.base_margenes_aa` v
LEFT JOIN `proj.ds.Objeto_productos` p ON p.nav_id = v.prod_nav_id
WHERE 1=1
[[AND {{fecha}}]]
[[AND {{categoria}}]]
[[AND {{tipo}}]]
"""
sql = metabase_smartscalar_kpi_sql(
act_expr="ROUND(SUM(v.venta_n), 2)",
n1_expr="ROUND(SUM(v.venta_n1), 2)",
body_sql=body,
date_expr="MIN(v.fecha)",
)
# sql tiene UNION ALL con periodo y valor
```
## Notas
- Por convencion los template-tags estan envueltos en ``[[ ]]`` para que sean
opcionales: si el dashboard no aplica el filtro la clausula desaparece.
- Para que los field-filters de Metabase resuelvan correctamente las tablas
dentro de CTEs, alias cada tabla con la cadena ``schema.table`` exacta que
Metabase usa al expandir el template-tag (ej. ``\`proj.ds.tabla\` \`ds.tabla\``).
- El SQL generado usa ``DATE_SUB(d_min, INTERVAL 52 WEEK)`` (BigQuery dialect).
Para Postgres adaptar a ``d_min - INTERVAL '52 weeks'``.