From 2d03a9b557057c06015351fb57e71e4795dac1d9 Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 12 Mar 2026 14:28:02 +0100 Subject: [PATCH] Display OpenMetadata catalog enrichment in table profile overview - API endpoint /api/catalog/profile/ enriches response with catalog metadata (tier, owners, tags, url) - renderOverview() template function displays 'Data Catalog' section with tier, owners, tags, and catalog link - Graceful degradation: section only shown if catalog enrichment available --- webapp/app.py | 50 ++++++++++++++++++++++++++++++++++- webapp/templates/catalog.html | 21 +++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/webapp/app.py b/webapp/app.py index 4bbc2e2..f0fe6a1 100644 --- a/webapp/app.py +++ b/webapp/app.py @@ -673,7 +673,7 @@ def register_routes(app: Flask) -> None: @app.route("/api/catalog/profile/") @login_required def catalog_profile(table_name): - """Return profiler data for a single table.""" + """Return profiler data for a single table with OpenMetadata catalog enrichment.""" profiles_path = _resolve_metadata_path("profiles.json") try: if not profiles_path.exists(): @@ -686,6 +686,54 @@ def register_routes(app: Flask) -> None: if not table_profile: return jsonify({"error": f"No profile for table '{table_name}'"}), 404 + # Enrich with OpenMetadata catalog data if available + if _catalog_enricher and _catalog_enricher.enabled: + try: + # Find table config from data_description.md + from src.config import TableConfig + from config.loader import load_instance_config + + # Load data_description.md to find table config by name + instance_config = load_instance_config() + desc_path = Path(os.path.dirname(__file__)) / ".." / "docs" / "data_description.md" + if desc_path.exists(): + with open(desc_path) as f: + content = f.read() + + import re + yaml_match = re.search(r'```yaml\s*\n(.*?)```', content, re.DOTALL) + if yaml_match: + import yaml + yaml_data = yaml.safe_load(yaml_match.group(1)) + if yaml_data and "tables" in yaml_data: + # Find table by name + for table_def in yaml_data["tables"]: + if table_def.get("name") == table_name: + table_config = TableConfig( + id=table_def.get("id", ""), + name=table_def.get("name", ""), + description=table_def.get("description", ""), + primary_key=table_def.get("primary_key", "id"), + sync_strategy=table_def.get("sync_strategy", "full_refresh"), + catalog_fqn=table_def.get("catalog_fqn"), + ) + catalog_data = _catalog_enricher.enrich_table(table_config) + if catalog_data: + # Add catalog enrichment to profile + table_profile["catalog"] = { + "description": catalog_data.description, + "tags": catalog_data.tags, + "tier": catalog_data.tier, + "owners": catalog_data.owners, + "url": catalog_data.catalog_url, + } + # Override description with catalog version + if catalog_data.description: + table_profile["description"] = catalog_data.description + break + except Exception as e: + logger.warning(f"Error enriching profile for {table_name}: {e}") + return jsonify(table_profile) except Exception as e: logger.error(f"Error loading profile for {table_name}: {e}") diff --git a/webapp/templates/catalog.html b/webapp/templates/catalog.html index cb2a570..7ff4696 100644 --- a/webapp/templates/catalog.html +++ b/webapp/templates/catalog.html @@ -1723,6 +1723,26 @@ function renderOverview(data) { const dr = data.date_range; const dateRow = dr ? `Date Coverage${dr.earliest} to ${dr.latest}` : ''; + // Catalog enrichment section + let catalogHtml = ''; + if (data.catalog) { + const cat = data.catalog; + const tagsHtml = (cat.tags || []).length > 0 + ? `
Tags: ${cat.tags.map(t => `${t}`).join('')}
` + : ''; + const ownersHtml = (cat.owners || []).length > 0 + ? `
Owners: ${cat.owners.join(', ')}
` + : ''; + const tierHtml = cat.tier + ? `
Tier: ${cat.tier}
` + : ''; + const urlHtml = cat.url + ? `
View in Data Catalog →
` + : ''; + + catalogHtml = `
Data Catalog
${tierHtml}${ownersHtml}${tagsHtml}${urlHtml}
`; + } + document.getElementById('sectionOverview').innerHTML = `
@@ -1743,6 +1763,7 @@ function renderOverview(data) { ${vtHtml}
${data.description ? `
Description

${data.description}

` : ''} ${(data.used_by_metrics || []).length > 0 ? `
Used by Metrics
${data.used_by_metrics.map(m => m.file ? `${m.name}` : `${typeof m === 'string' ? m : m.name}`).join('')}
` : ''} + ${catalogHtml}
`; }