diff --git a/web/src/padelnomics/admin/pipeline_routes.py b/web/src/padelnomics/admin/pipeline_routes.py index cea4070..3ce3ac8 100644 --- a/web/src/padelnomics/admin/pipeline_routes.py +++ b/web/src/padelnomics/admin/pipeline_routes.py @@ -767,6 +767,12 @@ async def pipeline_trigger_extract(): # ── Lineage tab ─────────────────────────────────────────────────────────────── +# Compute downstream map once at import time (DAG is static). +_DOWNSTREAM: dict[str, list[str]] = {n: [] for n in _DAG} +for _name, _deps in _DAG.items(): + for _dep in _deps: + _DOWNSTREAM.setdefault(_dep, []).append(_name) + @bp.route("/lineage") @role_required("admin") @@ -780,6 +786,67 @@ async def pipeline_lineage(): ) +@bp.route("/lineage/schema/") +@role_required("admin") +async def pipeline_lineage_schema(model: str): + """JSON: schema details for a lineage node. + + Returns columns + types from information_schema (serving models only — + staging/foundation live in lakehouse.duckdb which the web app cannot open). + Row count is included for serving models when the table exists. + """ + from quart import jsonify + + from ..analytics import fetch_analytics + + if model not in _DAG: + return jsonify({"error": "unknown model"}), 404 + + layer = _classify_layer(model) + upstream = _DAG[model] + downstream = _DOWNSTREAM.get(model, []) + + row_count = None + columns: list[dict] = [] + + if layer == "serving": + col_rows = await fetch_analytics( + """ + SELECT column_name, data_type, is_nullable + FROM information_schema.columns + WHERE table_schema = 'serving' AND table_name = ? + ORDER BY ordinal_position + """, + [model], + ) + columns = [ + { + "name": r["column_name"], + "type": r["data_type"], + "nullable": r["is_nullable"] == "YES", + } + for r in col_rows + ] + if columns: + # model is validated against _DAG keys — safe to interpolate + count_rows = await fetch_analytics( + f"SELECT count(*) AS n FROM serving.{model}" + ) + if count_rows: + row_count = count_rows[0]["n"] + + return jsonify( + { + "model": model, + "layer": layer, + "upstream": upstream, + "downstream": downstream, + "row_count": row_count, + "columns": columns, + } + ) + + # ── Catalog tab ─────────────────────────────────────────────────────────────── diff --git a/web/src/padelnomics/admin/templates/admin/partials/pipeline_lineage.html b/web/src/padelnomics/admin/templates/admin/partials/pipeline_lineage.html index 825ed08..d112924 100644 --- a/web/src/padelnomics/admin/templates/admin/partials/pipeline_lineage.html +++ b/web/src/padelnomics/admin/templates/admin/partials/pipeline_lineage.html @@ -5,45 +5,296 @@ {{ node_count }} models — staging → foundation → serving + + hover to preview · click to inspect +

{{ lineage_svg | safe }}
+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+