310b409ae0
- push_all(): pushea todos los YAMLs de un proyecto (cards primero,
dashboards despues), solo CREATE/UPDATE, resiliente a fallos por item
- explore.py: comandos describe (schema de DB) y sql (query ad-hoc con
limite, cap 5MB, bloqueo de escrituras destructivas)
- payload.py: auto-inyecta id:-N, visualization_settings:{} y
parameter_mappings:[] en dashcards nuevas para evitar 500 en push
- test_local: 11 cards + 3 dashboards sobre Sample Database de Metabase
- registry.db regenerado con auto_metabase_py_analytics indexada
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
123 lines
4.1 KiB
Python
123 lines
4.1 KiB
Python
"""Crea database + cards + dashboard de prueba en Metabase para validar pull/push.
|
|
|
|
Usa la propia Postgres interna de Metabase (auto_metabase_test-postgres) como
|
|
database de prueba, ya que es accesible desde el container metabase via la
|
|
red docker compartida.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
APP_DIR = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(APP_DIR.parent.parent / "python" / "functions"))
|
|
sys.path.insert(0, str(APP_DIR))
|
|
|
|
from main import get_client # noqa: E402
|
|
from metabase.databases import metabase_add_database, metabase_list_databases # noqa: E402
|
|
from metabase.cards import metabase_create_card, metabase_list_cards # noqa: E402
|
|
from metabase.dashboards import ( # noqa: E402
|
|
metabase_create_dashboard,
|
|
metabase_list_dashboards,
|
|
metabase_update_dashboard,
|
|
)
|
|
|
|
|
|
def find_or_create_database(client) -> int:
|
|
dbs = metabase_list_databases(client)
|
|
# list_databases puede retornar un dict con 'data' o una lista directa
|
|
items = dbs["data"] if isinstance(dbs, dict) and "data" in dbs else dbs
|
|
for db in items:
|
|
if db.get("name") == "metabase_internal_pg":
|
|
print(f" database existente id={db['id']}")
|
|
return db["id"]
|
|
db = metabase_add_database(
|
|
client,
|
|
name="metabase_internal_pg",
|
|
engine="postgres",
|
|
details={
|
|
"host": "auto_metabase_test-postgres",
|
|
"port": 5432,
|
|
"dbname": "metabase",
|
|
"user": "metabase",
|
|
"password": "metabase",
|
|
"ssl": False,
|
|
},
|
|
)
|
|
print(f" database creada id={db['id']}")
|
|
return db["id"]
|
|
|
|
|
|
def find_or_create_card(client, name: str, db_id: int, sql: str, display: str = "table") -> int:
|
|
cards = metabase_list_cards(client)
|
|
for c in cards:
|
|
if c.get("name") == name:
|
|
print(f" card '{name}' existente id={c['id']}")
|
|
return c["id"]
|
|
card = metabase_create_card(
|
|
client,
|
|
name=name,
|
|
dataset_query={
|
|
"type": "native",
|
|
"native": {"query": sql},
|
|
"database": db_id,
|
|
},
|
|
display=display,
|
|
)
|
|
print(f" card '{name}' creada id={card['id']}")
|
|
return card["id"]
|
|
|
|
|
|
def find_or_create_dashboard(client, name: str) -> int:
|
|
dashes = metabase_list_dashboards(client)
|
|
for d in dashes:
|
|
if d.get("name") == name:
|
|
print(f" dashboard '{name}' existente id={d['id']}")
|
|
return d["id"]
|
|
d = metabase_create_dashboard(client, name=name, description="Dashboard de prueba para auto_metabase")
|
|
print(f" dashboard '{name}' creado id={d['id']}")
|
|
return d["id"]
|
|
|
|
|
|
def main():
|
|
client = get_client()
|
|
print("Seeding test data en Metabase...")
|
|
|
|
print("\n[1] Database")
|
|
db_id = find_or_create_database(client)
|
|
|
|
print("\n[2] Cards")
|
|
c1 = find_or_create_card(
|
|
client, "test_count_users", db_id,
|
|
"SELECT COUNT(*) AS users FROM core_user", "scalar",
|
|
)
|
|
c2 = find_or_create_card(
|
|
client, "test_users_by_locale", db_id,
|
|
"SELECT COALESCE(locale, 'unknown') AS locale, COUNT(*) AS n FROM core_user GROUP BY locale ORDER BY n DESC",
|
|
"bar",
|
|
)
|
|
|
|
print("\n[3] Dashboard con cards")
|
|
dash_id = find_or_create_dashboard(client, "auto_metabase test dashboard")
|
|
|
|
# Re-fetch dashboard para ver estado actual
|
|
from metabase.dashboards import metabase_get_dashboard
|
|
dash = metabase_get_dashboard(client, dash_id)
|
|
existing_card_ids = {dc.get("card_id") for dc in dash.get("dashcards", [])}
|
|
|
|
if c1 in existing_card_ids and c2 in existing_card_ids:
|
|
print(f" dashboard ya tiene las {len(dash.get('dashcards', []))} dashcards esperadas")
|
|
else:
|
|
# Construir dashcards: id negativo => nueva
|
|
new_dashcards = [
|
|
{"id": -1, "card_id": c1, "row": 0, "col": 0, "size_x": 6, "size_y": 4},
|
|
{"id": -2, "card_id": c2, "row": 0, "col": 6, "size_x": 6, "size_y": 4},
|
|
]
|
|
metabase_update_dashboard(client, dash_id, dashcards=new_dashcards)
|
|
print(f" dashcards añadidas: {len(new_dashcards)}")
|
|
|
|
print(f"\nListo. Abre http://localhost:3000/dashboard/{dash_id}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|