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
This commit is contained in:
parent
a7faf70cb3
commit
2d03a9b557
2 changed files with 70 additions and 1 deletions
|
|
@ -673,7 +673,7 @@ def register_routes(app: Flask) -> None:
|
|||
@app.route("/api/catalog/profile/<table_name>")
|
||||
@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}")
|
||||
|
|
|
|||
|
|
@ -1723,6 +1723,26 @@ function renderOverview(data) {
|
|||
const dr = data.date_range;
|
||||
const dateRow = dr ? `<tr><td>Date Coverage</td><td>${dr.earliest} to ${dr.latest}</td></tr>` : '';
|
||||
|
||||
// Catalog enrichment section
|
||||
let catalogHtml = '';
|
||||
if (data.catalog) {
|
||||
const cat = data.catalog;
|
||||
const tagsHtml = (cat.tags || []).length > 0
|
||||
? `<div style="margin-top:8px;"><span style="font-size:11px;color:var(--text-secondary);font-weight:500;">Tags:</span> ${cat.tags.map(t => `<span class="metric-badge" style="display:inline-block;margin:2px 4px 2px 0;">${t}</span>`).join('')}</div>`
|
||||
: '';
|
||||
const ownersHtml = (cat.owners || []).length > 0
|
||||
? `<div style="margin-top:8px;"><span style="font-size:11px;color:var(--text-secondary);font-weight:500;">Owners:</span> ${cat.owners.join(', ')}</div>`
|
||||
: '';
|
||||
const tierHtml = cat.tier
|
||||
? `<div style="margin-top:8px;"><span style="font-size:11px;color:var(--text-secondary);font-weight:500;">Tier:</span> <strong>${cat.tier}</strong></div>`
|
||||
: '';
|
||||
const urlHtml = cat.url
|
||||
? `<div style="margin-top:8px;"><a href="${cat.url}" target="_blank" style="font-size:11px;color:var(--primary);text-decoration:none;">View in Data Catalog →</a></div>`
|
||||
: '';
|
||||
|
||||
catalogHtml = `<div style="margin-top:16px;border-top:1px solid var(--border);padding-top:16px;"><div class="overview-title">Data Catalog</div>${tierHtml}${ownersHtml}${tagsHtml}${urlHtml}</div>`;
|
||||
}
|
||||
|
||||
document.getElementById('sectionOverview').innerHTML = `
|
||||
<div class="overview-grid">
|
||||
<div>
|
||||
|
|
@ -1743,6 +1763,7 @@ function renderOverview(data) {
|
|||
<table class="overview-stats-table">${vtHtml}</table>
|
||||
${data.description ? `<div style="margin-top:16px;"><div class="overview-title">Description</div><p style="font-size:13px;color:var(--text-secondary);">${data.description}</p></div>` : ''}
|
||||
${(data.used_by_metrics || []).length > 0 ? `<div style="margin-top:16px;"><div class="overview-title">Used by Metrics</div><div>${data.used_by_metrics.map(m => m.file ? `<a class="metric-badge" style="cursor:pointer;text-decoration:none;" onclick="openMetricModal('${m.file}')">${m.name}</a>` : `<span class="metric-badge">${typeof m === 'string' ? m : m.name}</span>`).join('')}</div></div>` : ''}
|
||||
${catalogHtml}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue