aminghadersohi commented on code in PR #43770:
URL: https://github.com/apache/superset/pull/43770#discussion_r4224297347


##########
superset/mcp_service/chart/chart_utils.py:
##########
@@ -1715,6 +1758,847 @@ def map_histogram_config(config: 
"HistogramChartConfig") -> Dict[str, Any]:
     return form_data
 
 
+def _bullet_token_list(values: Sequence[str | int | float]) -> str:
+    """Serialize typed Bullet controls to the frontend's comma-separated 
form."""
+    tokens: list[str] = []
+    for value in values:
+        if isinstance(value, float):
+            token = repr(value)
+            # ``100`` parses back to the same binary float as ``100.0`` and
+            # preserves the frontend's established compact integer spelling.
+            if token.endswith(".0") and not (
+                value == 0.0 and math.copysign(1.0, value) < 0
+            ):
+                token = token[:-2]
+            tokens.append(token)
+        else:
+            tokens.append(str(value))
+    return ",".join(tokens)
+
+
+def map_bullet_config(config: BulletChartConfig) -> Dict[str, Any]:  # noqa: 
C901
+    """Map typed Bullet config to ``Bullet/buildQuery`` and transformProps.
+
+    The frontend buildQuery replaces the generic query fields with exactly one
+    metric and the groupby hierarchy. Presentation controls stay in native
+    snake_case form_data; the chart plugin camelizes them for transformProps.
+    """
+    if (
+        config.dimensions is None
+        and config._inherited_groupby is None
+        and config.order_by
+    ):
+        # An update resolves its saved hierarchy before mapping. Without one,
+        # creation must validate sort targets against an empty hierarchy.
+        BulletChartConfig.model_validate(
+            {**config.model_dump(exclude_unset=True), "dimensions": []}
+        )
+    metric = create_metric_object(config.metric)
+    form_data: Dict[str, Any] = {
+        "viz_type": "bullet",
+        "metric": metric,
+    }
+
+    # Optional semantic/query fields are emitted only when explicitly supplied.
+    # This lets update_chart and update_chart_preview preserve native saved 
state,
+    # while an explicit empty value still clears it through the generic merge 
path.
+    if "dimensions" in config.model_fields_set:
+        form_data["groupby"] = [dimension.name for dimension in 
config.dimensions or []]
+    if "row_limit" in config.model_fields_set:

Review Comment:
   Agreed. Fixed in 1a7455b21d4d6339beb650b6f093c4f7f55c5084: 
`map_bullet_config` now always writes `row_limit` (schema default 10000 when 
omitted), and the Bullet plugin update merge drops the mapper default when 
`row_limit` is not in `model_fields_set`, so `merge_bullet_form_data` restores 
the saved value. A saved chart with no stored limit falls back to the default, 
matching the shared merge path. Covered by 
`test_bullet_mapper_preserves_omission_and_honors_explicit_values` (omitted 
create gets 10000) and the new 
`test_bullet_update_merge_row_limit_omission_and_explicit_value` (omitted 
update keeps 15000, explicit 25 replaces it).



##########
superset/mcp_service/chart/preview_utils.py:
##########
@@ -1398,6 +2412,101 @@ def fallback_vega_lite_preview(
     return None
 
 
+def generate_xy_pivot_vega_lite_preview(
+    data: list[dict[str, Any]], form_data: dict[str, Any], *, mark: str
+) -> VegaLitePreview | None:
+    """Render flattened timeseries pivot columns without dropping grouped 
series.
+
+    Folding escaped field paths resolves literal output keys without splitting
+    category values that contain escaped commas. The legend retains each
+    complete metric/category label.
+    Long-form results continue through the generic renderer.
+    """
+    from superset.mcp_service.chart.chart_helpers import _as_list
+    from superset.utils.pandas_postprocessing.utils import (
+        escape_separator,
+        FLAT_COLUMN_SEPARATOR,
+    )
+
+    if not data:
+        return None
+    dimensions = [
+        label
+        for column in _as_list(form_data.get("groupby"))
+        if (label := _form_column_label(column))
+    ]
+    if not dimensions or any(label in data[0] for label in dimensions):
+        return None
+    x_axis = _form_column_label(form_data.get("x_axis")) or "__timestamp"
+    if x_axis not in data[0]:
+        return None
+    metric_labels = [
+        escape_separator(label)
+        for metric in _as_list(form_data.get("metrics"))
+        if (label := metric_result_label(metric))
+    ]
+    fields = [
+        field
+        for field in data[0]
+        if field != x_axis
+        and any(
+            field.startswith(label + FLAT_COLUMN_SEPARATOR)

Review Comment:
   Confirmed: with `truncate_metric: true` and one metric the rename step 
leaves `{event_date, East, West}` and field discovery found nothing. Fixed in 
f19daaf51a2da8b6d9a3a36f0e3f9f1e3e3ab0e6: when no metric-prefixed field 
matches, there is a single metric, and `truncate_metric` is set, every 
non-x-axis column is folded as a category series. New 
`test_xy_preview_renders_truncated_metric_series` runs the real post-processing 
chain and asserts the fold covers `East` and `West`; it fails without the fix.



##########
superset/mcp_service/chart/compile.py:
##########
@@ -116,14 +119,19 @@ def _compile_chart(
             row_limit=plugin.compile_row_limit(form_data) if plugin else 2,
             force=False,
         )
+        set_query_context_form_data(query_context, dataset_id, "table")
 
         command = ChartDataCommand(query_context)
         command.validate()
         result = command.run()
 
         warnings: List[str] = []
         row_count = 0
-        if query_failure := query_result_failure(result):
+        query_data, query_failure = query_result_data(

Review Comment:
   Confirmed: master only checked failure statuses there, and the new value 
validation rejected `inf` before the Gauge normalizer could skip the dial. 
Fixed in c537e1add4: compile, the unsaved preview, and the saved 
ASCII/table/Vega-Lite previews pass `preserve_nonfinite_floats` from the owning 
plugin (true only for Gauge), the redundant `query_result_failure` re-checks in 
the saved previews are removed, and the saved Vega-Lite preview renders the 
normalized rows. Other plugins keep the strict check. New 
`test_compile_chart_skips_nonfinite_grouped_gauge_dial` and 
`test_unsaved_gauge_preview_skips_nonfinite_dial` (A=Infinity, B=42) fail 
without the fix and pass with it.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to