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

michaelsmolina pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 464ddee4b4 fix: Time Column on Generic X-axis (#23021)
464ddee4b4 is described below

commit 464ddee4b4164460193027645d87cb25e7b2320e
Author: Michael S. Molina <[email protected]>
AuthorDate: Fri Feb 10 13:33:07 2023 -0500

    fix: Time Column on Generic X-axis (#23021)
---
 .../src/query/buildQueryContext.ts                 | 17 +---
 .../test/query/buildQueryContext.test.ts           | 37 ---------
 .../src/explore/components/SaveModal.tsx           |  3 +-
 superset/common/query_context_factory.py           | 96 +++++++++++++++++++++-
 4 files changed, 97 insertions(+), 56 deletions(-)

diff --git 
a/superset-frontend/packages/superset-ui-core/src/query/buildQueryContext.ts 
b/superset-frontend/packages/superset-ui-core/src/query/buildQueryContext.ts
index e66c1e201e..c1dc5bd89d 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/buildQueryContext.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/buildQueryContext.ts
@@ -20,16 +20,11 @@
 import buildQueryObject from './buildQueryObject';
 import DatasourceKey from './DatasourceKey';
 import { QueryFieldAliases, QueryFormData } from './types/QueryFormData';
-import {
-  BinaryQueryObjectFilterClause,
-  QueryContext,
-  QueryObject,
-} from './types/Query';
+import { QueryContext, QueryObject } from './types/Query';
 import { SetDataMaskHook } from '../chart';
 import { JsonObject } from '../connection';
 import { normalizeTimeColumn } from './normalizeTimeColumn';
-import { hasGenericChartAxes, isXAxisSet } from './getXAxis';
-import { ensureIsArray } from '../utils';
+import { isXAxisSet } from './getXAxis';
 
 const WRAP_IN_ARRAY = (baseQueryObject: QueryObject) => [baseQueryObject];
 
@@ -60,14 +55,6 @@ export default function buildQueryContext(
       // eslint-disable-next-line no-param-reassign
       query.post_processing = query.post_processing.filter(Boolean);
     }
-    if (hasGenericChartAxes && query.time_range) {
-      // eslint-disable-next-line no-param-reassign
-      query.filters = ensureIsArray(query.filters).map(flt =>
-        flt?.op === 'TEMPORAL_RANGE'
-          ? ({ ...flt, val: query.time_range } as 
BinaryQueryObjectFilterClause)
-          : flt,
-      );
-    }
   });
   if (isXAxisSet(formData)) {
     queries = queries.map(query => normalizeTimeColumn(formData, query));
diff --git 
a/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts
 
b/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts
index e14648a984..9d47361e8f 100644
--- 
a/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts
+++ 
b/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts
@@ -164,41 +164,4 @@ describe('buildQueryContext', () => {
     expect(spyNormalizeTimeColumn).not.toBeCalled();
     spyNormalizeTimeColumn.mockRestore();
   });
-  it('should orverride time filter if GENERIC_CHART_AXES is enabled', () => {
-    Object.defineProperty(getXAxisModule, 'hasGenericChartAxes', {
-      value: true,
-    });
-
-    const queryContext = buildQueryContext(
-      {
-        datasource: '5__table',
-        viz_type: 'table',
-      },
-      () => [
-        {
-          filters: [
-            {
-              col: 'col1',
-              op: 'TEMPORAL_RANGE',
-              val: '2001 : 2002',
-            },
-            {
-              col: 'col2',
-              op: 'IN',
-              val: ['a', 'b'],
-            },
-          ],
-          time_range: '1990 : 1991',
-        },
-      ],
-    );
-    expect(queryContext.queries[0].filters).toEqual([
-      { col: 'col1', op: 'TEMPORAL_RANGE', val: '1990 : 1991' },
-      {
-        col: 'col2',
-        op: 'IN',
-        val: ['a', 'b'],
-      },
-    ]);
-  });
 });
diff --git a/superset-frontend/src/explore/components/SaveModal.tsx 
b/superset-frontend/src/explore/components/SaveModal.tsx
index 8552eff7fa..d62cb4d898 100644
--- a/superset-frontend/src/explore/components/SaveModal.tsx
+++ b/superset-frontend/src/explore/components/SaveModal.tsx
@@ -198,7 +198,8 @@ class SaveModal extends React.Component<SaveModalProps, 
SaveModalState> {
         );
       }
 
-      const { url_params, ...formData } = this.props.form_data || {};
+      const formData = this.props.form_data || {};
+      delete formData.url_params;
 
       let dashboard: DashboardGetResponse | null = null;
       if (this.state.newDashboardName || this.state.saveToDashboardId) {
diff --git a/superset/common/query_context_factory.py 
b/superset/common/query_context_factory.py
index 360ee449a4..3a8380bdb0 100644
--- a/superset/common/query_context_factory.py
+++ b/superset/common/query_context_factory.py
@@ -22,6 +22,7 @@ from superset import app, db
 from superset.charts.dao import ChartDAO
 from superset.common.chart_data import ChartDataResultFormat, 
ChartDataResultType
 from superset.common.query_context import QueryContext
+from superset.common.query_object import QueryObject
 from superset.common.query_object_factory import QueryObjectFactory
 from superset.datasource.dao import DatasourceDAO
 from superset.models.slice import Slice
@@ -65,8 +66,12 @@ class QueryContextFactory:  # pylint: 
disable=too-few-public-methods
         result_type = result_type or ChartDataResultType.FULL
         result_format = result_format or ChartDataResultFormat.JSON
         queries_ = [
-            self._query_object_factory.create(
-                result_type, datasource=datasource, **query_obj
+            self._process_query_object(
+                datasource_model_instance,
+                form_data,
+                self._query_object_factory.create(
+                    result_type, datasource=datasource, **query_obj
+                ),
             )
             for query_obj in queries
         ]
@@ -90,7 +95,6 @@ class QueryContextFactory:  # pylint: 
disable=too-few-public-methods
 
     # pylint: disable=no-self-use
     def _convert_to_model(self, datasource: DatasourceDict) -> BaseDatasource:
-
         return DatasourceDAO.get_datasource(
             session=db.session,
             datasource_type=DatasourceType(datasource["type"]),
@@ -99,3 +103,89 @@ class QueryContextFactory:  # pylint: 
disable=too-few-public-methods
 
     def _get_slice(self, slice_id: Any) -> Optional[Slice]:
         return ChartDAO.find_by_id(slice_id)
+
+    def _process_query_object(
+        self,
+        datasource: BaseDatasource,
+        form_data: Optional[Dict[str, Any]],
+        query_object: QueryObject,
+    ) -> QueryObject:
+        self._apply_granularity(query_object, form_data, datasource)
+        self._apply_filters(query_object)
+        return query_object
+
+    def _apply_granularity(
+        self,
+        query_object: QueryObject,
+        form_data: Optional[Dict[str, Any]],
+        datasource: BaseDatasource,
+    ) -> None:
+        temporal_columns = {
+            column.column_name
+            for column in datasource.columns
+            if (column["is_dttm"] if isinstance(column, dict) else 
column.is_dttm)
+        }
+        granularity = query_object.granularity
+        x_axis = form_data and form_data.get("x_axis")
+
+        if granularity:
+            filter_to_remove = None
+            if x_axis and x_axis in temporal_columns:
+                filter_to_remove = x_axis
+                x_axis_column = next(
+                    (
+                        column
+                        for column in query_object.columns
+                        if column == x_axis
+                        or (
+                            isinstance(column, dict)
+                            and column["sqlExpression"] == x_axis
+                        )
+                    ),
+                    None,
+                )
+                # Replaces x-axis column values with granularity
+                if x_axis_column:
+                    if isinstance(x_axis_column, dict):
+                        x_axis_column["sqlExpression"] = granularity
+                        x_axis_column["label"] = granularity
+                    else:
+                        query_object.columns = [
+                            granularity if column == x_axis_column else column
+                            for column in query_object.columns
+                        ]
+                    for post_processing in query_object.post_processing:
+                        if post_processing.get("operation") == "pivot":
+                            post_processing["options"]["index"] = [granularity]
+
+            # If no temporal x-axis, then get the default temporal filter
+            if not filter_to_remove:
+                temporal_filters = [
+                    filter["col"]
+                    for filter in query_object.filter
+                    if filter["op"] == "TEMPORAL_RANGE"
+                ]
+                if len(temporal_filters) > 0:
+                    # Use granularity if it's already in the filters
+                    if granularity in temporal_filters:
+                        filter_to_remove = granularity
+                    else:
+                        # Use the first temporal filter
+                        filter_to_remove = temporal_filters[0]
+
+            # Removes the temporal filter which may be an x-axis or
+            # another temporal filter. A new filter based on the value of
+            # the granularity will be added later in the code.
+            # In practice, this is replacing the previous default temporal 
filter.
+            if filter_to_remove:
+                query_object.filter = [
+                    filter
+                    for filter in query_object.filter
+                    if filter["col"] != filter_to_remove
+                ]
+
+    def _apply_filters(self, query_object: QueryObject) -> None:
+        if query_object.time_range:
+            for filter_object in query_object.filter:
+                if filter_object["op"] == "TEMPORAL_RANGE":
+                    filter_object["val"] = query_object.time_range

Reply via email to