From 9be22fdc82bd366496596647fc31477beca1e915 Mon Sep 17 00:00:00 2001 From: Petr Date: Mon, 16 Mar 2026 22:11:58 +0100 Subject: [PATCH] Fix metric display: use displayName in list, render HTML in modal List view: - Show display_name ("M1 + VFM Operational") instead of name ("M1PlusVFMOperational") - Strip HTML and truncate description for clean list excerpts Modal detail: - Render original HTML from catalog instead of stripped plain text - Add .om-description CSS class for structured HTML (bold labels, lists, code) - Pass description_html alongside plain text description for backwards compat --- connectors/openmetadata/transformer.py | 9 +++++- webapp/app.py | 2 +- webapp/static/css/metric_modal.css | 40 ++++++++++++++++++++++++++ webapp/static/js/metric_modal.js | 7 ++++- webapp/templates/catalog.html | 2 +- 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/connectors/openmetadata/transformer.py b/connectors/openmetadata/transformer.py index 6452478..4100e46 100644 --- a/connectors/openmetadata/transformer.py +++ b/connectors/openmetadata/transformer.py @@ -319,6 +319,7 @@ def metric_to_display_dict(raw_metric: Dict[str, Any]) -> Dict[str, Any]: Parse raw OpenMetadata metric for metric list display in webapp. Returns a lightweight dict for listing metrics (not full detail). + Description is stripped of HTML and truncated for list view. Args: raw_metric: Raw metric dict from OpenMetadata API @@ -332,10 +333,15 @@ def metric_to_display_dict(raw_metric: Dict[str, Any]) -> Dict[str, Any]: description = raw_metric.get("description", "") or "" tags = raw_metric.get("tags", []) + # Strip HTML and truncate for list excerpt + clean_desc = strip_html(description) + if len(clean_desc) > 150: + clean_desc = clean_desc[:147] + "..." + return { "name": name, "display_name": display_name, - "description": description, + "description": clean_desc, "grain": extract_grain(raw_metric), "category": extract_category(tags), "path": f"catalog:{fqn}", @@ -376,6 +382,7 @@ def metric_to_detail_dict(raw_metric: Dict[str, Any], category_colors: Optional[ }, "overview": { "description": strip_html(description), + "description_html": description, "key_insights": [], }, "validation": None, diff --git a/webapp/app.py b/webapp/app.py index 14b5d79..cbce3cd 100644 --- a/webapp/app.py +++ b/webapp/app.py @@ -829,7 +829,7 @@ def _build_om_metric_detail(raw_metric: dict) -> dict: "category": category, "category_color": category_colors.get(category, "#6B7280"), "metadata": {"type": metric_type, "unit": unit, "grain": grain, "time_column": ""}, - "overview": {"description": description.strip(), "key_insights": []}, + "overview": {"description": description.strip(), "description_html": description.strip(), "key_insights": []}, "validation": None, "dimensions": dimensions, "notes": {"all": [], "key_insights": []}, diff --git a/webapp/static/css/metric_modal.css b/webapp/static/css/metric_modal.css index dad9e98..175385a 100644 --- a/webapp/static/css/metric_modal.css +++ b/webapp/static/css/metric_modal.css @@ -330,6 +330,46 @@ font-weight: 500; } +/* OpenMetadata HTML description (rendered from catalog) */ +.om-description { + font-size: 15px; + line-height: 1.7; + color: #374151; +} + +.om-description p { + margin: 0 0 12px 0; +} + +.om-description p:last-child { + margin-bottom: 0; +} + +.om-description strong { + color: #111827; + font-weight: 600; +} + +.om-description ul, +.om-description ol { + margin: 8px 0 12px 0; + padding-left: 24px; +} + +.om-description li { + margin-bottom: 4px; + line-height: 1.6; +} + +.om-description code { + background: #F3F4F6; + color: #0073D1; + padding: 2px 6px; + border-radius: 3px; + font-family: 'Monaco', 'Menlo', monospace; + font-size: 13px; +} + /* Dimension Pills */ .metric-dimensions { display: flex; diff --git a/webapp/static/js/metric_modal.js b/webapp/static/js/metric_modal.js index 9e56f81..d6cc0b3 100644 --- a/webapp/static/js/metric_modal.js +++ b/webapp/static/js/metric_modal.js @@ -148,12 +148,17 @@ function renderMetricModal(data) { function renderOverviewTab(data) { const keyInsights = data.notes.key_insights || data.notes.all.slice(0, 5); + // Prefer rendered HTML from catalog over stripped plain text + const descriptionContent = data.overview.description_html + ? `
${data.overview.description_html}
` + : `

${escapeHtml(data.overview.description)}

`; + return `

What it measures

-

${data.overview.description}

+ ${descriptionContent}
diff --git a/webapp/templates/catalog.html b/webapp/templates/catalog.html index 62ff716..713bc96 100644 --- a/webapp/templates/catalog.html +++ b/webapp/templates/catalog.html @@ -1415,7 +1415,7 @@ {% for metric in category.metrics %}
-
{{ metric.name }}
+
{{ metric.display_name }}
{{ metric.description }}