You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ma...@apache.org on 2026/02/11 02:35:35 UTC

(superset) branch fix-12066-timeseries-groupby created (now 72687640179)

This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a change to branch fix-12066-timeseries-groupby
in repository https://gitbox.apache.org/repos/asf/superset.git


      at 72687640179 fix(viz): flatten MultiIndex columns in Time-Series Table for multiple Group By (#12066)

This branch includes the following new commits:

     new 72687640179 fix(viz): flatten MultiIndex columns in Time-Series Table for multiple Group By (#12066)

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(superset) 01/01: fix(viz): flatten MultiIndex columns in Time-Series Table for multiple Group By (#12066)

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a commit to branch fix-12066-timeseries-groupby
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 72687640179693dd74e0242c619c2b9632cd35a3
Author: Maxime Beauchemin <ma...@gmail.com>
AuthorDate: Wed Feb 11 02:34:09 2026 +0000

    fix(viz): flatten MultiIndex columns in Time-Series Table for multiple Group By (#12066)
    
    When using multiple "Group By" columns in a Time-Series Table, pivot_table()
    produces a MultiIndex on columns. Calling to_dict(orient="index") on such a
    DataFrame fails with "TypeError: keys must be str, int, float, bool or None,
    not tuple" because tuple keys cannot be JSON-serialized.
    
    Flatten MultiIndex columns to comma-separated strings before serialization
    so the result is valid JSON and compatible with the frontend.
    
    Co-Authored-By: Claude Opus 4 <no...@anthropic.com>
---
 superset/viz.py                      |  2 ++
 tests/integration_tests/viz_tests.py | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/superset/viz.py b/superset/viz.py
index c6c7cf699b7..1ff7ca9ddd0 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -747,6 +747,8 @@ class TimeTableViz(BaseViz):
         pt = df.pivot_table(index=DTTM_ALIAS, columns=columns, values=values)
         pt.index = pt.index.map(str)
         pt = pt.sort_index()
+        if isinstance(pt.columns, pd.MultiIndex):
+            pt.columns = [", ".join(str(s) for s in col) for col in pt.columns]
         return {
             "records": pt.to_dict(orient="index"),
             "columns": list(pt.columns),
diff --git a/tests/integration_tests/viz_tests.py b/tests/integration_tests/viz_tests.py
index 7ebcf1197fc..a3dcca8c0ba 100644
--- a/tests/integration_tests/viz_tests.py
+++ b/tests/integration_tests/viz_tests.py
@@ -626,6 +626,39 @@ class TestTimeSeriesTableViz(SupersetTestCase):
         }
         assert expected == data["records"]
 
+    def test_get_data_multiple_group_by(self):
+        form_data = {"metrics": ["sum__A"], "groupby": ["groupby1", "groupby2"]}
+        datasource = self.get_datasource_mock()
+        raw = {}
+        t1 = pd.Timestamp("2000")
+        t2 = pd.Timestamp("2002")
+        raw[DTTM_ALIAS] = [t1, t1, t1, t1, t2, t2, t2, t2]
+        raw["sum__A"] = [15, 20, 25, 30, 35, 40, 45, 50]
+        raw["groupby1"] = ["a1", "a2", "a1", "a2", "a1", "a2", "a1", "a2"]
+        raw["groupby2"] = ["b1", "b1", "b2", "b2", "b1", "b1", "b2", "b2"]
+        df = pd.DataFrame(raw)
+        test_viz = viz.TimeTableViz(datasource, form_data)
+        data = test_viz.get_data(df)
+        # Columns should be flattened strings, not tuples
+        assert {"a1, b1", "a1, b2", "a2, b1", "a2, b2"} == set(data["columns"])
+        time_format = "%Y-%m-%d %H:%M:%S"
+        expected = {
+            t1.strftime(time_format): {
+                "a1, b1": 15,
+                "a1, b2": 25,
+                "a2, b1": 20,
+                "a2, b2": 30,
+            },
+            t2.strftime(time_format): {
+                "a1, b1": 35,
+                "a1, b2": 45,
+                "a2, b1": 40,
+                "a2, b2": 50,
+            },
+        }
+        assert expected == data["records"]
+        assert data["is_group_by"] is True
+
     @patch("superset.viz.BaseViz.query_obj")
     def test_query_obj_throws_metrics_and_groupby(self, super_query_obj):
         datasource = self.get_datasource_mock()