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]