feat: app script_navegador y dashboard Metabase
App Go para ejecutar scripts de navegación automatizada usando las funciones CDP del registry. Incluye script de creación de dashboard en Metabase para monitoreo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
"""Crea un dashboard en Metabase para monitorear operations de script_navegador."""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "python", "functions"))
|
||||
|
||||
from metabase.client import metabase_auth
|
||||
from metabase import (
|
||||
metabase_list_databases,
|
||||
metabase_create_card,
|
||||
metabase_create_dashboard,
|
||||
metabase_update_dashboard,
|
||||
metabase_list_dashboards,
|
||||
)
|
||||
from metabase.databases import metabase_add_database
|
||||
|
||||
# --- Config ---
|
||||
METABASE_URL = "http://localhost:3000"
|
||||
EMAIL = "admin@fnregistry.local"
|
||||
PASSWORD = "FnRegistry2024!"
|
||||
|
||||
# Path de operations.db dentro del contenedor Docker
|
||||
# Copiar con: docker exec metabase mkdir -p /data/ops-script-navegador && docker cp apps/script_navegador/operations.db metabase:/data/ops-script-navegador/operations.db
|
||||
OPS_DB_PATH = "/data/ops-script-navegador/operations.db"
|
||||
DB_NAME = "ops-script-navegador"
|
||||
|
||||
CARDS = [
|
||||
# ---- Fila 0: KPIs (h=5) ----
|
||||
{
|
||||
"name": "Total Ejecuciones",
|
||||
"display": "scalar",
|
||||
"sql": "SELECT COUNT(*) AS total FROM executions;",
|
||||
"size_x": 6, "size_y": 5, "col": 0, "row": 0,
|
||||
},
|
||||
{
|
||||
"name": "Ejecuciones Exitosas",
|
||||
"display": "scalar",
|
||||
"sql": "SELECT COUNT(*) AS exitosas FROM executions WHERE status = 'success';",
|
||||
"size_x": 6, "size_y": 5, "col": 6, "row": 0,
|
||||
},
|
||||
{
|
||||
"name": "Ejecuciones Fallidas",
|
||||
"display": "scalar",
|
||||
"sql": "SELECT COUNT(*) AS fallidas FROM executions WHERE status = 'failure';",
|
||||
"size_x": 6, "size_y": 5, "col": 12, "row": 0,
|
||||
},
|
||||
{
|
||||
"name": "Duracion Promedio (ms)",
|
||||
"display": "scalar",
|
||||
"sql": "SELECT ROUND(AVG(duration_ms)) AS avg_ms FROM executions WHERE status = 'success';",
|
||||
"size_x": 6, "size_y": 5, "col": 18, "row": 0,
|
||||
},
|
||||
# ---- Fila 5: Tendencias (h=8) ----
|
||||
{
|
||||
"name": "Ejecuciones por Estado",
|
||||
"display": "pie",
|
||||
"sql": "SELECT status, COUNT(*) AS cantidad FROM executions GROUP BY status;",
|
||||
"size_x": 8, "size_y": 8, "col": 0, "row": 5,
|
||||
},
|
||||
{
|
||||
"name": "Duracion por Ejecucion (timeline)",
|
||||
"display": "line",
|
||||
"sql": """
|
||||
SELECT
|
||||
started_at,
|
||||
duration_ms,
|
||||
status
|
||||
FROM executions
|
||||
ORDER BY started_at;
|
||||
""",
|
||||
"size_x": 16, "size_y": 8, "col": 8, "row": 5,
|
||||
},
|
||||
# ---- Fila 13: Detalle de pasos (h=9) ----
|
||||
{
|
||||
"name": "Pasos por Script (metricas)",
|
||||
"display": "table",
|
||||
"sql": """
|
||||
SELECT
|
||||
id,
|
||||
status,
|
||||
records_in AS pasos_total,
|
||||
records_out AS pasos_exitosos,
|
||||
duration_ms,
|
||||
CASE WHEN error = '' THEN '-' ELSE error END AS error,
|
||||
json_extract(metrics, '$.script_name') AS script,
|
||||
started_at
|
||||
FROM executions
|
||||
ORDER BY started_at DESC
|
||||
LIMIT 20;
|
||||
""",
|
||||
"size_x": 24, "size_y": 9, "col": 0, "row": 13,
|
||||
},
|
||||
# ---- Fila 22: Logs (h=9) ----
|
||||
{
|
||||
"name": "Logs Recientes",
|
||||
"display": "table",
|
||||
"sql": """
|
||||
SELECT
|
||||
level,
|
||||
source,
|
||||
message,
|
||||
json_extract(metadata, '$.action') AS action,
|
||||
json_extract(metadata, '$.elapsed_ms') AS elapsed_ms,
|
||||
created_at
|
||||
FROM logs
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 50;
|
||||
""",
|
||||
"size_x": 24, "size_y": 9, "col": 0, "row": 22,
|
||||
},
|
||||
]
|
||||
|
||||
DASHBOARD_NAME = "script_navegador Operations"
|
||||
|
||||
|
||||
def main():
|
||||
print("Autenticando en Metabase...")
|
||||
client = metabase_auth(METABASE_URL, EMAIL, PASSWORD)
|
||||
|
||||
# Buscar si ya existe la database
|
||||
dbs = metabase_list_databases(client)
|
||||
ops_db_id = None
|
||||
for db in dbs:
|
||||
if db.get("name") == DB_NAME:
|
||||
ops_db_id = db["id"]
|
||||
print(f" Database ya existe: {DB_NAME} (id={ops_db_id})")
|
||||
break
|
||||
|
||||
if not ops_db_id:
|
||||
print(f"Registrando {DB_NAME} como datasource SQLite ({OPS_DB_PATH})...")
|
||||
new_db = metabase_add_database(
|
||||
client=client,
|
||||
name=DB_NAME,
|
||||
engine="sqlite",
|
||||
details={"db": OPS_DB_PATH},
|
||||
)
|
||||
ops_db_id = new_db["id"]
|
||||
print(f" Database registrada: id={ops_db_id}")
|
||||
|
||||
# Eliminar dashboard existente si lo hay
|
||||
existing = metabase_list_dashboards(client)
|
||||
for d in existing:
|
||||
if d.get("name") == DASHBOARD_NAME:
|
||||
print(f" Dashboard ya existe (id={d['id']}), recreando...")
|
||||
from metabase import metabase_delete_dashboard
|
||||
metabase_delete_dashboard(client, d["id"])
|
||||
|
||||
# Crear cards
|
||||
print("Creando cards...")
|
||||
created_cards = []
|
||||
for i, card_def in enumerate(CARDS):
|
||||
card = metabase_create_card(
|
||||
client,
|
||||
name=card_def["name"],
|
||||
dataset_query={
|
||||
"database": ops_db_id,
|
||||
"type": "native",
|
||||
"native": {"query": card_def["sql"]},
|
||||
},
|
||||
display=card_def["display"],
|
||||
description=f"script_navegador: {card_def['name']}",
|
||||
)
|
||||
created_cards.append((card, card_def))
|
||||
print(f" [{i+1}/{len(CARDS)}] {card_def['name']} (id={card['id']})")
|
||||
|
||||
# Crear dashboard
|
||||
print("Creando dashboard...")
|
||||
dashboard = metabase_create_dashboard(
|
||||
client,
|
||||
name=DASHBOARD_NAME,
|
||||
description="Monitoreo de ejecuciones de script_navegador: KPIs, tendencias, detalle de pasos y logs.",
|
||||
)
|
||||
dash_id = dashboard["id"]
|
||||
print(f" Dashboard creado: id={dash_id}")
|
||||
|
||||
# Agregar cards al dashboard
|
||||
dashcards = []
|
||||
for idx, (card, card_def) in enumerate(created_cards):
|
||||
dashcards.append({
|
||||
"id": -(idx + 1),
|
||||
"card_id": card["id"],
|
||||
"size_x": card_def["size_x"],
|
||||
"size_y": card_def["size_y"],
|
||||
"col": card_def["col"],
|
||||
"row": card_def["row"],
|
||||
})
|
||||
|
||||
metabase_update_dashboard(client, dash_id, dashcards=dashcards)
|
||||
print(f"\nDashboard listo: {METABASE_URL}/dashboard/{dash_id}")
|
||||
client.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user