From 24dbc8649c6830d1f5702b5dd26f5cc4eca09a4d Mon Sep 17 00:00:00 2001 From: Evan Rusackas Date: Thu, 2 Jul 2026 21:50:57 -0700 Subject: [PATCH] =?UTF-8?q?chore(viz):=20phase=200=20=E2=80=94=20remove=20?= =?UTF-8?q?orphaned=20viz.py=20classes=20and=20nvd3=20BoxPlot=20leftovers?= =?UTF-8?q?=20(#41715)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claude Code --- .gitignore | 1 + SCOPE.md | 10 +- .../BoxPlot/stories/NVD3BoxPlot.stories.tsx | 81 ------- .../src/BoxPlot/stories/data.ts | 78 ------- superset/viz.py | 204 ------------------ 5 files changed, 8 insertions(+), 366 deletions(-) delete mode 100644 superset-frontend/plugins/legacy-preset-chart-nvd3/src/BoxPlot/stories/NVD3BoxPlot.stories.tsx delete mode 100644 superset-frontend/plugins/legacy-preset-chart-nvd3/src/BoxPlot/stories/data.ts diff --git a/.gitignore b/.gitignore index 6f1b2261d8a..18f614dbcc2 100644 --- a/.gitignore +++ b/.gitignore @@ -137,6 +137,7 @@ PROJECT.md .aider* .claude_rc* .claude/settings.local.json +.claude/worktrees/ .env.local oxc-custom-build/ *.code-workspace diff --git a/SCOPE.md b/SCOPE.md index 0aef0d10e68..434f133b006 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -62,9 +62,9 @@ unchanged. "Retarget" = migrate saved charts to an existing modern chart via a ## Phases / sub-PRs (each targets this feature branch) ### Phase 0 — Dead code deletion -- [ ] Delete `superset-frontend/plugins/legacy-preset-chart-deckgl/` (empty husk: only a stale `node_modules/`, unreferenced, absent from `package.json`) -- [ ] Delete `legacy-preset-chart-nvd3/src/BoxPlot/` leftovers (unregistered; superseded by ECharts BoxPlot) -- [ ] Delete orphaned `viz.py` classes with no registered frontend: `MapboxViz`, `MapLibreViz`, `EventFlowViz` (+ their tests) +- [x] `superset-frontend/plugins/legacy-preset-chart-deckgl/` turned out to have **zero tracked files** — it was a stale local `node_modules/` husk, removed locally, nothing to commit +- [x] Delete `legacy-preset-chart-nvd3/src/BoxPlot/` leftovers (unregistered; superseded by ECharts BoxPlot) +- [x] Delete orphaned `viz.py` classes with no registered frontend: `MapboxViz`, `MapLibreViz`, `EventFlowViz` (no tests referenced them; `mapbox` saved charts were already migrated to `point_cluster_map` by revision `ce6bd21901ab`; the frontend `point_cluster_map` plugin has its own modern `buildQuery`, so removing `MapLibreViz` also fixes cache warm-up, which routed any `viz_type in viz_types` through the legacy path) ### Phase 1 — Chart migrations (tiers 1→4, easiest first; order above) Per-chart checklist: @@ -92,6 +92,10 @@ Per-chart checklist: (append entries as sub-PRs merge: date, sub-PR #, summary) +- 2026-07-02 — Phase 0: removed orphaned `viz.py` classes (`MapboxViz`, `MapLibreViz`, + `EventFlowViz`, −204 lines) and the unregistered nvd3 BoxPlot story leftovers. + Verified: `ruff` clean, `tests/unit_tests/test_viz_*` pass. + ## Decisions - **compare** → migrating to `echarts_timeseries_line`; nvd3's interactive % rebasing is not preserved (flagged in UPDATES.md). diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/BoxPlot/stories/NVD3BoxPlot.stories.tsx b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/BoxPlot/stories/NVD3BoxPlot.stories.tsx deleted file mode 100644 index acf4317cca8..00000000000 --- a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/BoxPlot/stories/NVD3BoxPlot.stories.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { SuperChart, VizType } from '@superset-ui/core'; -import { EchartsBoxPlotChartPlugin } from '@superset-ui/plugin-chart-echarts'; -import { dummyDatasource, withResizableChartDemo } from '@storybook-shared'; -import data from './data'; - -new EchartsBoxPlotChartPlugin().configure({ key: 'box-plot' }).register(); - -export default { - title: 'Legacy Chart Plugins/legacy-preset-chart-nvd3/BoxPlot', - decorators: [withResizableChartDemo], - args: { - colorScheme: 'd3Category10', - whiskerOptions: 'Min/max (no outliers)', - }, - argTypes: { - colorScheme: { - control: 'select', - options: [ - 'supersetColors', - 'd3Category10', - 'bnbColors', - 'googleCategory20c', - ], - }, - whiskerOptions: { - control: 'select', - options: [ - 'Tukey', - 'Min/max (no outliers)', - '2/98 percentiles', - '9/91 percentiles', - ], - }, - }, -}; - -export const Basic = ({ - colorScheme, - whiskerOptions, - width, - height, -}: { - colorScheme: string; - whiskerOptions: string; - width: number; - height: number; -}) => ( - -); diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/BoxPlot/stories/data.ts b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/BoxPlot/stories/data.ts deleted file mode 100644 index 02837a59c3e..00000000000 --- a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/BoxPlot/stories/data.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* eslint-disable sort-keys, no-magic-numbers */ -// Data format for ECharts BoxPlot - requires metric__min, metric__q1, metric__median, etc. -export default [ - { - region: 'East Asia & Pacific', - sum__SP_POP_TOTL__min: 1031863394.0, - sum__SP_POP_TOTL__q1: 1384725172.5, - sum__SP_POP_TOTL__median: 1717904169.0, - sum__SP_POP_TOTL__q3: 2032724922.5, - sum__SP_POP_TOTL__max: 2240687901.0, - sum__SP_POP_TOTL__mean: 1681000000.0, - sum__SP_POP_TOTL__count: 50, - sum__SP_POP_TOTL__outliers: [], - }, - { - region: 'Europe & Central Asia', - sum__SP_POP_TOTL__min: 660881033.0, - sum__SP_POP_TOTL__q1: 751386460.5, - sum__SP_POP_TOTL__median: 820716895.0, - sum__SP_POP_TOTL__q3: 862814192.5, - sum__SP_POP_TOTL__max: 903095786.0, - sum__SP_POP_TOTL__mean: 799778873.0, - sum__SP_POP_TOTL__count: 50, - sum__SP_POP_TOTL__outliers: [], - }, - { - region: 'Latin America & Caribbean', - sum__SP_POP_TOTL__min: 220564224.0, - sum__SP_POP_TOTL__q1: 313690832.5, - sum__SP_POP_TOTL__median: 421490233.0, - sum__SP_POP_TOTL__q3: 529668114.5, - sum__SP_POP_TOTL__max: 626270167.0, - sum__SP_POP_TOTL__mean: 422336714.0, - sum__SP_POP_TOTL__count: 50, - sum__SP_POP_TOTL__outliers: [], - }, - { - region: 'South Asia', - sum__SP_POP_TOTL__min: 572036107.0, - sum__SP_POP_TOTL__q1: 772373036.5, - sum__SP_POP_TOTL__median: 1059570231.0, - sum__SP_POP_TOTL__q3: 1398841234.0, - sum__SP_POP_TOTL__max: 1720976995.0, - sum__SP_POP_TOTL__mean: 1104759521.0, - sum__SP_POP_TOTL__count: 50, - sum__SP_POP_TOTL__outliers: [], - }, - { - region: 'Sub-Saharan Africa', - sum__SP_POP_TOTL__min: 228268752.0, - sum__SP_POP_TOTL__q1: 320037758.0, - sum__SP_POP_TOTL__median: 467337821.0, - sum__SP_POP_TOTL__q3: 676768689.0, - sum__SP_POP_TOTL__max: 974315323.0, - sum__SP_POP_TOTL__mean: 533345669.0, - sum__SP_POP_TOTL__count: 50, - sum__SP_POP_TOTL__outliers: [1100000000, 1200000000], - }, -]; diff --git a/superset/viz.py b/superset/viz.py index 412436854f7..d04286f0359 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -1446,177 +1446,6 @@ class HorizonViz(NVD3TimeSeriesViz): ) -class MapboxViz(BaseViz): - """Rich maps made with Mapbox""" - - viz_type = "mapbox" - verbose_name = _("Mapbox") - is_timeseries = False - credits = "Mapbox GL JS" - - @deprecated(deprecated_in="3.0") - def query_obj(self) -> QueryObjectDict: - query_obj = super().query_obj() - label_col = self.form_data.get("mapbox_label") - - if not self.form_data.get("groupby"): - if ( - self.form_data.get("all_columns_x") is None - or self.form_data.get("all_columns_y") is None - ): - raise QueryObjectValidationError( - _("[Longitude] and [Latitude] must be set") - ) - query_obj["columns"] = [ - self.form_data.get("all_columns_x"), - self.form_data.get("all_columns_y"), - ] - - if label_col and len(label_col) >= 1: - if label_col[0] == "count": - raise QueryObjectValidationError( - _( - "Must have a [Group By] column to have 'count' as the " - + "[Label]" - ) - ) - query_obj["columns"].append(label_col[0]) - - if self.form_data.get("point_radius") != "Auto": - query_obj["columns"].append(self.form_data.get("point_radius")) - - # Ensure this value is sorted so that it does not - # cause the cache key generation (which hashes the - # query object) to generate different keys for values - # that should be considered the same. - query_obj["columns"] = sorted(set(query_obj["columns"])) - else: - # Ensuring columns chosen are all in group by - if ( - label_col - and len(label_col) >= 1 - and label_col[0] != "count" - and label_col[0] not in self.form_data["groupby"] - ): - raise QueryObjectValidationError( - _("Choice of [Label] must be present in [Group By]") - ) - - if ( - self.form_data.get("point_radius") != "Auto" - and self.form_data.get("point_radius") not in self.form_data["groupby"] - ): - raise QueryObjectValidationError( - _("Choice of [Point Radius] must be present in [Group By]") - ) - - if ( - self.form_data.get("all_columns_x") not in self.form_data["groupby"] - or self.form_data.get("all_columns_y") not in self.form_data["groupby"] - ): - raise QueryObjectValidationError( - _( - "[Longitude] and [Latitude] columns must be present in " - + "[Group By]" - ) - ) - return query_obj - - @deprecated(deprecated_in="3.0") - def get_data(self, df: pd.DataFrame) -> VizData: - if df.empty: - return None - - label_col = self.form_data.get("mapbox_label") - has_custom_metric = label_col is not None and len(label_col) > 0 - metric_col = [None] * len(df.index) - if has_custom_metric: - if label_col[0] == self.form_data.get("all_columns_x"): # type: ignore - metric_col = df[self.form_data.get("all_columns_x")] - elif label_col[0] == self.form_data.get("all_columns_y"): # type: ignore - metric_col = df[self.form_data.get("all_columns_y")] - else: - metric_col = df[label_col[0]] # type: ignore - point_radius_col = ( - [None] * len(df.index) - if self.form_data.get("point_radius") == "Auto" - else df[self.form_data.get("point_radius")] - ) - - # limiting geo precision as long decimal values trigger issues - # around json-bignumber in Mapbox - geo_precision = 10 - # using geoJSON formatting - geo_json = { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": {"metric": metric, "radius": point_radius}, - "geometry": { - "type": "Point", - "coordinates": [ - round(lon, geo_precision), - round(lat, geo_precision), - ], - }, - } - for lon, lat, metric, point_radius in zip( - df[self.form_data.get("all_columns_x")], - df[self.form_data.get("all_columns_y")], - metric_col, - point_radius_col, - strict=False, - ) - ], - } - - x_series, y_series = ( - df[self.form_data.get("all_columns_x")], - df[self.form_data.get("all_columns_y")], - ) - south_west = [x_series.min(), y_series.min()] - north_east = [x_series.max(), y_series.max()] - - return { - "geoJSON": geo_json, - "hasCustomMetric": has_custom_metric, - "mapboxApiKey": current_app.config["MAPBOX_API_KEY"], - "mapStyle": self.form_data.get("mapbox_style"), - "aggregatorName": self.form_data.get("pandas_aggfunc"), - "clusteringRadius": self.form_data.get("clustering_radius"), - "pointRadiusUnit": self.form_data.get("point_radius_unit"), - "globalOpacity": self.form_data.get("global_opacity"), - "bounds": [south_west, north_east], - "renderWhileDragging": self.form_data.get("render_while_dragging"), - "tooltip": self.form_data.get("rich_tooltip"), - "color": self.form_data.get("mapbox_color"), - } - - -class MapLibreViz(MapboxViz): - """Rich maps made with MapLibre""" - - viz_type = "point_cluster_map" - verbose_name = _("Point Cluster Map") - credits = 'MapLibre GL JS' - - @deprecated(deprecated_in="3.0") - def query_obj(self) -> QueryObjectDict: - self.form_data["mapbox_label"] = self.form_data.get("map_label") - return super().query_obj() - - @deprecated(deprecated_in="3.0") - def get_data(self, df: pd.DataFrame) -> VizData: - self.form_data["mapbox_label"] = self.form_data.get("map_label") - self.form_data["mapbox_style"] = self.form_data.get("map_style") - self.form_data["mapbox_color"] = self.form_data.get("map_color") - data = super().get_data(df) - if data: - data.pop("mapboxApiKey", None) - return data - - class DeckGLMultiLayer(BaseViz): """Pile on multiple DeckGL layers""" @@ -2657,39 +2486,6 @@ class DeckArc(BaseDeckGLViz): } -class EventFlowViz(BaseViz): - """A visualization to explore patterns in event sequences""" - - viz_type = "event_flow" - verbose_name = _("Event flow") - credits = 'from @data-ui' - is_timeseries = True - - @deprecated(deprecated_in="3.0") - def query_obj(self) -> QueryObjectDict: - query = super().query_obj() - form_data = self.form_data - - event_key = form_data["all_columns_x"] - entity_key = form_data["entity"] - meta_keys = [ - col - for col in form_data["all_columns"] or [] - if col not in (event_key, entity_key) - ] - - query["columns"] = [event_key, entity_key] + meta_keys - - if form_data["order_by_entity"]: - query["orderby"] = [(entity_key, True)] - - return query - - @deprecated(deprecated_in="3.0") - def get_data(self, df: pd.DataFrame) -> VizData: - return df.to_dict(orient="records") - - class PairedTTestViz(BaseViz): """A table displaying paired t-test values"""