---
name: metabase_dashboard_append_row
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def metabase_dashboard_append_row(client: MetabaseClient, *, dashboard_id: int, tab_id: int, card_ids: list[int], height: int = 4, donor_card_id: int = 0, grid_width: int = 24) -> dict"
description: "Añade N cards como fila horizontal al final de una tab de dashboard. Calcula la primera fila libre con metabase_dashboard_next_row, distribuye las cards con ancho uniforme (grid_width // N), copia parameter_mappings de un dashcard donante si se indica y llama a metabase_update_dashboard_safe. Util para añadir filas de KPIs con los filtros del dashboard ya conectados."
tags: [metabase, dashboard, dashcard, layout, row, append, parameter-mappings, api, python, pendiente-usar]
uses_functions:
  - metabase_dashboard_next_row_py_infra
  - metabase_copy_dashcard_mappings_py_infra
  - metabase_update_dashboard_safe_py_infra
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
  - name: client
    desc: "MetabaseClient autenticado con sesion activa"
  - name: dashboard_id
    desc: "ID del dashboard donde se añaden las cards"
  - name: tab_id
    desc: "ID de la tab donde se insertan las cards. Usar 0 para dashboards sin tabs o tab raiz"
  - name: card_ids
    desc: "Lista de IDs de cards a colocar como fila, de izquierda a derecha. No puede estar vacia"
  - name: height
    desc: "Altura en filas del grid para cada card. Default 4"
  - name: donor_card_id
    desc: "Si distinto de 0, copia los parameter_mappings de esa card a cada nueva card. Util para replicar los filtros de dashboard ya configurados. Default 0 (sin mappings)"
  - name: grid_width
    desc: "Anchura total del grid de Metabase. Default 24 (estandar). Cada card recibe grid_width // len(card_ids) columnas"
output: "Dict con el resumen de metabase_update_dashboard_safe: {'added': [negative_ids], 'updated': int, 'removed': int, 'response': dict_respuesta_PUT}"
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/metabase/metabase_dashboard_append_row.py"
---

## Por que existe

Añadir una fila de KPIs a un dashboard de Metabase que ya tiene 18 filtros
requiere: (1) calcular la fila libre, (2) construir cada dashcard con su
``parameter_mappings`` copiado, y (3) enviar el PUT sin activar los gotchas
conocidos (413, 500 FK). Esta funcion compone las tres operaciones atomicas
del registry en un solo paso.

## Ejemplo

```python
from metabase import MetabaseClient, metabase_dashboard_append_row

client = MetabaseClient("https://metabase.example.com", "token...")

# Añadir 4 KPIs al final de la tab 7, copiando filtros de card 42
result = metabase_dashboard_append_row(
    client,
    dashboard_id=10,
    tab_id=7,
    card_ids=[101, 102, 103, 104],
    height=4,
    donor_card_id=42,
)
print(result["added"])   # [-1, -2, -3, -4] IDs temporales asignados

# Añadir 2 cards sin filtros en dashboard sin tabs
result = metabase_dashboard_append_row(
    client,
    dashboard_id=15,
    tab_id=0,
    card_ids=[200, 201],
    height=6,
)

# Añadir 1 card de ancho completo (24 columnas)
result = metabase_dashboard_append_row(
    client,
    dashboard_id=10,
    tab_id=7,
    card_ids=[300],
    height=8,
    donor_card_id=42,
)
```

## Notas

- Realiza entre 2 y ``1 + 2*len(card_ids)`` requests HTTP: 1 GET para
  ``next_row`` + (si ``donor_card_id != 0``) 1 GET por card para los
  mappings (que comparten el GET del siguiente ``update_safe``) + 1 GET + 1
  PUT para ``update_dashboard_safe``. En practica ``metabase_get_dashboard``
  se llama 2-3 veces segun si hay donor.
- Si ``grid_width // len(card_ids)`` produce un cociente con resto, las
  columnas sobrantes (al lado derecho) quedan vacias en el grid. Para
  ajuste preciso, construir los dashcards manualmente y usar
  ``metabase_update_dashboard_safe`` directamente.
- ``donor_card_id`` debe ser el ``card_id`` de una card que ya existe en el
  dashboard (no el ID del dashcard). Lanza ``ValueError`` si no se
  encuentra.
- Si ``tab_id != 0``, ``metabase_update_dashboard_safe`` conserva las tabs
  del dashboard (evita 500 FK violation).
