--- 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] 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).