From a41ed13600e9e329d2526141ae96780c0fe29218 Mon Sep 17 00:00:00 2001 From: Arash Date: Tue, 17 Aug 2021 16:44:44 -0400 Subject: [PATCH 1/7] copy to Clipboard order --- .../src/SqlLab/components/ResultSet.tsx | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/ResultSet.tsx b/superset-frontend/src/SqlLab/components/ResultSet.tsx index b5a6e4faaa0a..d828e184e3e8 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet.tsx @@ -190,6 +190,7 @@ export default class ResultSet extends React.PureComponent< this, ); this.handleExploreBtnClick = this.handleExploreBtnClick.bind(this); + this.orderResultsData = this.orderResultsData.bind(this); } async componentDidMount() { @@ -442,13 +443,34 @@ export default class ResultSet extends React.PureComponent< } } + orderResultsData(data: Record[]) { + // orders the data from results based on the columns. + const { columns } = this.props.query.results; + const orderedData = []; + for (let i = 0; i < data.length; i += 1) { + const row = {}; + for (let j = 0; j < columns.length; j += 1) { + const key = columns[j].name; + if (data[i][key]) { + row[j] = data[i][key]; + } else { + row[j] = data[i][parseFloat(key)]; + } + } + orderedData.push(row); + } + return orderedData; + } + renderControls() { if (this.props.search || this.props.visualize || this.props.csv) { let { data } = this.props.query.results; if (this.props.cache && this.props.query.cached) { ({ data } = this.state); } - + // JavaScript does not mantain the order of a mixed set of keys (i.e integers and strings) + // the below function orders the keys based on the column names. + const orderedData = this.orderResultsData(data); // Added compute logic to stop user from being able to Save & Explore const { saveDatasetRadioBtnState, @@ -508,7 +530,7 @@ export default class ResultSet extends React.PureComponent< )} From 1b40fb39f4309704dbfcf6e70a12ab7116196e0b Mon Sep 17 00:00:00 2001 From: Arash Date: Wed, 18 Aug 2021 13:40:32 -0400 Subject: [PATCH 2/7] centralized copyToClipboard --- .../src/SqlLab/components/ResultSet.tsx | 26 ++----------------- .../src/components/TableView/TableView.tsx | 1 - .../components/DataTableControl/index.tsx | 4 ++- .../components/DataTablesPane/index.tsx | 3 ++- .../explore/components/ExploreChartPanel.jsx | 1 - superset-frontend/src/utils/common.js | 15 +++++++++-- superset-frontend/src/utils/common.test.jsx | 9 ++++--- 7 files changed, 26 insertions(+), 33 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/ResultSet.tsx b/superset-frontend/src/SqlLab/components/ResultSet.tsx index d828e184e3e8..4f1c788b1e16 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet.tsx @@ -190,7 +190,6 @@ export default class ResultSet extends React.PureComponent< this, ); this.handleExploreBtnClick = this.handleExploreBtnClick.bind(this); - this.orderResultsData = this.orderResultsData.bind(this); } async componentDidMount() { @@ -443,34 +442,13 @@ export default class ResultSet extends React.PureComponent< } } - orderResultsData(data: Record[]) { - // orders the data from results based on the columns. - const { columns } = this.props.query.results; - const orderedData = []; - for (let i = 0; i < data.length; i += 1) { - const row = {}; - for (let j = 0; j < columns.length; j += 1) { - const key = columns[j].name; - if (data[i][key]) { - row[j] = data[i][key]; - } else { - row[j] = data[i][parseFloat(key)]; - } - } - orderedData.push(row); - } - return orderedData; - } - renderControls() { if (this.props.search || this.props.visualize || this.props.csv) { let { data } = this.props.query.results; if (this.props.cache && this.props.query.cached) { ({ data } = this.state); } - // JavaScript does not mantain the order of a mixed set of keys (i.e integers and strings) - // the below function orders the keys based on the column names. - const orderedData = this.orderResultsData(data); + const { columns } = this.props.query.results; // Added compute logic to stop user from being able to Save & Explore const { saveDatasetRadioBtnState, @@ -530,7 +508,7 @@ export default class ResultSet extends React.PureComponent< )} diff --git a/superset-frontend/src/components/TableView/TableView.tsx b/superset-frontend/src/components/TableView/TableView.tsx index 139757970a89..a0eb635d985a 100644 --- a/superset-frontend/src/components/TableView/TableView.tsx +++ b/superset-frontend/src/components/TableView/TableView.tsx @@ -156,7 +156,6 @@ const TableView = ({ useSortBy, usePagination, ); - useEffect(() => { if (serverPagination && pageIndex !== initialState.pageIndex) { onServerPagination({ diff --git a/superset-frontend/src/explore/components/DataTableControl/index.tsx b/superset-frontend/src/explore/components/DataTableControl/index.tsx index 16a6c64bbad9..28710f250ab0 100644 --- a/superset-frontend/src/explore/components/DataTableControl/index.tsx +++ b/superset-frontend/src/explore/components/DataTableControl/index.tsx @@ -55,11 +55,13 @@ const CopyNode = ( export const CopyToClipboardButton = ({ data, + columns, }: { data?: Record; + columns?: string[]; }) => ( diff --git a/superset-frontend/src/explore/components/DataTablesPane/index.tsx b/superset-frontend/src/explore/components/DataTablesPane/index.tsx index 3d8308981198..2c2435737c28 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/index.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/index.tsx @@ -281,6 +281,7 @@ export const DataTablesPane = ({ [RESULT_TYPES.results]: useTableColumns(data[RESULT_TYPES.results]), [RESULT_TYPES.samples]: useTableColumns(data[RESULT_TYPES.samples]), }; + const { all_columns } = queryFormData; const renderDataTable = (type: string) => { if (isLoading[type]) { @@ -316,7 +317,7 @@ export const DataTablesPane = ({ const TableControls = ( - + ); diff --git a/superset-frontend/src/explore/components/ExploreChartPanel.jsx b/superset-frontend/src/explore/components/ExploreChartPanel.jsx index 82cc0b9fd864..e81bd7d3e12d 100644 --- a/superset-frontend/src/explore/components/ExploreChartPanel.jsx +++ b/superset-frontend/src/explore/components/ExploreChartPanel.jsx @@ -116,7 +116,6 @@ const ExploreChartPanel = props => { const theme = useTheme(); const gutterMargin = theme.gridUnit * GUTTER_SIZE_FACTOR; const gutterHeight = theme.gridUnit * GUTTER_SIZE_FACTOR; - const { height: hHeight, ref: headerRef } = useResizeDetector({ refreshMode: 'debounce', refreshRate: 300, diff --git a/superset-frontend/src/utils/common.js b/superset-frontend/src/utils/common.js index 974268c58e32..567ec3d968f4 100644 --- a/superset-frontend/src/utils/common.js +++ b/superset-frontend/src/utils/common.js @@ -87,10 +87,21 @@ export function optionFromValue(opt) { return { value: optionValue(opt), label: optionLabel(opt) }; } -export function prepareCopyToClipboardTabularData(data) { +export function prepareCopyToClipboardTabularData(data, columns) { let result = ''; for (let i = 0; i < data.length; i += 1) { - result += `${Object.values(data[i]).join('\t')}\n`; + const row = {}; + for (let j = 0; j < columns.length; j += 1) { + // JavaScript does not mantain the order of a mixed set of keys (i.e integers and strings) + // the below function orders the keys based on the column names. + const key = columns[j].name || columns[j]; + if (data[i][key]) { + row[j] = data[i][key]; + } else { + row[j] = data[i][parseFloat(key)]; + } + } + result += `${Object.values(row).join('\t')}\n`; } return result; } diff --git a/superset-frontend/src/utils/common.test.jsx b/superset-frontend/src/utils/common.test.jsx index b47f423f2ffd..5542648eb871 100644 --- a/superset-frontend/src/utils/common.test.jsx +++ b/superset-frontend/src/utils/common.test.jsx @@ -46,15 +46,18 @@ describe('utils/common', () => { describe('prepareCopyToClipboardTabularData', () => { it('converts empty array', () => { const array = []; - expect(prepareCopyToClipboardTabularData(array)).toEqual(''); + const column = []; + expect(prepareCopyToClipboardTabularData(array, column)).toEqual(''); }); it('converts non empty array', () => { const array = [ { column1: 'lorem', column2: 'ipsum' }, { column1: 'dolor', column2: 'sit', column3: 'amet' }, ]; - expect(prepareCopyToClipboardTabularData(array)).toEqual( - 'lorem\tipsum\ndolor\tsit\tamet\n', + const column = ['column1', 'column2', 'column3']; + console.log(prepareCopyToClipboardTabularData(array, column)); + expect(prepareCopyToClipboardTabularData(array, column)).toEqual( + 'lorem\tipsum\t\ndolor\tsit\tamet\n', ); }); }); From 0491d424bd37ac2f6c7cf3e5428b05153fbb94d5 Mon Sep 17 00:00:00 2001 From: Arash Date: Thu, 19 Aug 2021 11:39:06 -0400 Subject: [PATCH 3/7] fixed table order --- .../explore/components/DataTableControl/index.tsx | 3 ++- .../DataTableControl/useTableColumns.test.ts | 9 ++++++--- .../src/explore/components/DataTablesPane/index.tsx | 13 +++++++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/superset-frontend/src/explore/components/DataTableControl/index.tsx b/superset-frontend/src/explore/components/DataTableControl/index.tsx index 28710f250ab0..3002f7a80c5d 100644 --- a/superset-frontend/src/explore/components/DataTableControl/index.tsx +++ b/superset-frontend/src/explore/components/DataTableControl/index.tsx @@ -115,13 +115,14 @@ export const useFilteredTableData = ( }, [data, filterText]); export const useTableColumns = ( + all_columns: string[], data?: Record[], moreConfigs?: { [key: string]: Partial }, ) => useMemo( () => data?.length - ? Object.keys(data[0]).map( + ? all_columns.map( key => ({ accessor: row => row[key], diff --git a/superset-frontend/src/explore/components/DataTableControl/useTableColumns.test.ts b/superset-frontend/src/explore/components/DataTableControl/useTableColumns.test.ts index 952b817b9dab..fac2e3d5f7b2 100644 --- a/superset-frontend/src/explore/components/DataTableControl/useTableColumns.test.ts +++ b/superset-frontend/src/explore/components/DataTableControl/useTableColumns.test.ts @@ -30,6 +30,7 @@ for (let i = 32; i < 127; i += 1) { const asciiKey = asciiChars.join(''); const unicodeKey = '你好. 吃了吗?'; +const all_columns = ['col01', 'col02']; const data = [ { col01: true, col02: false, [asciiKey]: asciiKey, [unicodeKey]: unicodeKey }, { col01: true, col02: false, [asciiKey]: asciiKey, [unicodeKey]: unicodeKey }, @@ -44,7 +45,7 @@ const data = [ ]; test('useTableColumns with no options', () => { - const hook = renderHook(() => useTableColumns(data)); + const hook = renderHook(() => useTableColumns(all_columns, data)); expect(hook.result.current).toEqual([ { Cell: expect.any(Function), @@ -83,7 +84,7 @@ test('useTableColumns with no options', () => { test('use only the first record columns', () => { const newData = [data[3], data[0]]; - const hook = renderHook(() => useTableColumns(newData)); + const hook = renderHook(() => useTableColumns(all_columns, newData)); expect(hook.result.current).toEqual([ { Cell: expect.any(Function), @@ -134,7 +135,9 @@ test('use only the first record columns', () => { }); test('useTableColumns with options', () => { - const hook = renderHook(() => useTableColumns(data, { col01: { id: 'ID' } })); + const hook = renderHook(() => + useTableColumns(all_columns, data, { col01: { id: 'ID' } }), + ); expect(hook.result.current).toEqual([ { Cell: expect.any(Function), diff --git a/superset-frontend/src/explore/components/DataTablesPane/index.tsx b/superset-frontend/src/explore/components/DataTablesPane/index.tsx index 2c2435737c28..08696cf1ac6d 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/index.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/index.tsx @@ -276,12 +276,17 @@ export const DataTablesPane = ({ data[RESULT_TYPES.samples], ), }; - + const { all_columns } = queryFormData; const columns = { - [RESULT_TYPES.results]: useTableColumns(data[RESULT_TYPES.results]), - [RESULT_TYPES.samples]: useTableColumns(data[RESULT_TYPES.samples]), + [RESULT_TYPES.results]: useTableColumns( + all_columns, + data[RESULT_TYPES.results], + ), + [RESULT_TYPES.samples]: useTableColumns( + all_columns, + data[RESULT_TYPES.samples], + ), }; - const { all_columns } = queryFormData; const renderDataTable = (type: string) => { if (isLoading[type]) { From f4eb0817ab4a4aeb6227afd25382a923c151b72b Mon Sep 17 00:00:00 2001 From: Arash Date: Thu, 19 Aug 2021 12:59:23 -0400 Subject: [PATCH 4/7] fixed tests --- .../components/DataTableControl/index.tsx | 36 ++++++++++--------- .../DataTableControl/useTableColumns.test.ts | 2 +- .../components/DataTablesPane/index.tsx | 3 ++ 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/superset-frontend/src/explore/components/DataTableControl/index.tsx b/superset-frontend/src/explore/components/DataTableControl/index.tsx index 3002f7a80c5d..4f530fb16750 100644 --- a/superset-frontend/src/explore/components/DataTableControl/index.tsx +++ b/superset-frontend/src/explore/components/DataTableControl/index.tsx @@ -122,23 +122,25 @@ export const useTableColumns = ( useMemo( () => data?.length - ? all_columns.map( - key => - ({ - accessor: row => row[key], - Header: key, - Cell: ({ value }) => { - if (value === true) { - return BOOL_TRUE_DISPLAY; - } - if (value === false) { - return BOOL_FALSE_DISPLAY; - } - return String(value); - }, - ...moreConfigs?.[key], - } as Column), - ) + ? all_columns + .filter((column: string) => Object.keys(data[0]).includes(column)) + .map( + key => + ({ + accessor: row => row[key], + Header: key, + Cell: ({ value }) => { + if (value === true) { + return BOOL_TRUE_DISPLAY; + } + if (value === false) { + return BOOL_FALSE_DISPLAY; + } + return String(value); + }, + ...moreConfigs?.[key], + } as Column), + ) : [], [data, moreConfigs], ); diff --git a/superset-frontend/src/explore/components/DataTableControl/useTableColumns.test.ts b/superset-frontend/src/explore/components/DataTableControl/useTableColumns.test.ts index fac2e3d5f7b2..537f12bc0cb4 100644 --- a/superset-frontend/src/explore/components/DataTableControl/useTableColumns.test.ts +++ b/superset-frontend/src/explore/components/DataTableControl/useTableColumns.test.ts @@ -30,7 +30,6 @@ for (let i = 32; i < 127; i += 1) { const asciiKey = asciiChars.join(''); const unicodeKey = '你好. 吃了吗?'; -const all_columns = ['col01', 'col02']; const data = [ { col01: true, col02: false, [asciiKey]: asciiKey, [unicodeKey]: unicodeKey }, { col01: true, col02: false, [asciiKey]: asciiKey, [unicodeKey]: unicodeKey }, @@ -43,6 +42,7 @@ const data = [ [unicodeKey]: unicodeKey, }, ]; +const all_columns = ['col01', 'col02', 'col03', asciiKey, unicodeKey]; test('useTableColumns with no options', () => { const hook = renderHook(() => useTableColumns(all_columns, data)); diff --git a/superset-frontend/src/explore/components/DataTablesPane/index.tsx b/superset-frontend/src/explore/components/DataTablesPane/index.tsx index 08696cf1ac6d..e06c14d312d4 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/index.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/index.tsx @@ -276,7 +276,10 @@ export const DataTablesPane = ({ data[RESULT_TYPES.samples], ), }; + const { all_columns } = queryFormData; + // this is to preserve the order of the columns, even if there are integer values, + // while also only grabbing the first column's keys const columns = { [RESULT_TYPES.results]: useTableColumns( all_columns, From f128ba3ba2154805682fc5228b4aba5f6f139b71 Mon Sep 17 00:00:00 2001 From: Arash Date: Thu, 19 Aug 2021 18:45:53 -0400 Subject: [PATCH 5/7] added colnames to all viz types --- .../components/DataTableControl/index.tsx | 10 ++++++---- .../explore/components/DataTablesPane/index.tsx | 17 +++++++++++++---- .../explore/components/ExploreChartPanel.jsx | 3 +-- superset/viz.py | 5 +++-- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/superset-frontend/src/explore/components/DataTableControl/index.tsx b/superset-frontend/src/explore/components/DataTableControl/index.tsx index 4f530fb16750..dc29adc377b4 100644 --- a/superset-frontend/src/explore/components/DataTableControl/index.tsx +++ b/superset-frontend/src/explore/components/DataTableControl/index.tsx @@ -61,7 +61,9 @@ export const CopyToClipboardButton = ({ columns?: string[]; }) => ( @@ -115,14 +117,14 @@ export const useFilteredTableData = ( }, [data, filterText]); export const useTableColumns = ( - all_columns: string[], + colnames?: string[], data?: Record[], moreConfigs?: { [key: string]: Partial }, ) => useMemo( () => - data?.length - ? all_columns + colnames && data?.length + ? colnames .filter((column: string) => Object.keys(data[0]).includes(column)) .map( key => diff --git a/superset-frontend/src/explore/components/DataTablesPane/index.tsx b/superset-frontend/src/explore/components/DataTablesPane/index.tsx index e06c14d312d4..4a1172d0ffa8 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/index.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/index.tsx @@ -112,6 +112,7 @@ export const DataTablesPane = ({ chartStatus, ownState, errorMessage, + queriesResponse, }: { queryFormData: Record; tableSectionHeight: number; @@ -119,6 +120,7 @@ export const DataTablesPane = ({ ownState?: JsonObject; onCollapseChange: (openPanelName: string) => void; errorMessage?: JSX.Element; + queriesResponse: Record; }) => { const [data, setData] = useState<{ [RESULT_TYPES.results]?: Record[]; @@ -128,6 +130,7 @@ export const DataTablesPane = ({ [RESULT_TYPES.results]: true, [RESULT_TYPES.samples]: true, }); + const [columnNames, setColumnNames] = useState([]); const [error, setError] = useState(NULLISH_RESULTS_STATE); const [filterText, setFilterText] = useState(''); const [activeTabKey, setActiveTabKey] = useState( @@ -220,6 +223,13 @@ export const DataTablesPane = ({ })); }, [queryFormData.adhoc_filters, queryFormData.datasource]); + useEffect(() => { + if (queriesResponse) { + const { colnames } = queriesResponse[0]; + setColumnNames([...colnames]); + } + }, [queriesResponse]); + useEffect(() => { if (panelOpen && isRequestPending[RESULT_TYPES.results]) { if (errorMessage) { @@ -277,16 +287,15 @@ export const DataTablesPane = ({ ), }; - const { all_columns } = queryFormData; // this is to preserve the order of the columns, even if there are integer values, // while also only grabbing the first column's keys const columns = { [RESULT_TYPES.results]: useTableColumns( - all_columns, + columnNames, data[RESULT_TYPES.results], ), [RESULT_TYPES.samples]: useTableColumns( - all_columns, + columnNames, data[RESULT_TYPES.samples], ), }; @@ -325,7 +334,7 @@ export const DataTablesPane = ({ const TableControls = ( - + ); diff --git a/superset-frontend/src/explore/components/ExploreChartPanel.jsx b/superset-frontend/src/explore/components/ExploreChartPanel.jsx index e81bd7d3e12d..c22285f8aee6 100644 --- a/superset-frontend/src/explore/components/ExploreChartPanel.jsx +++ b/superset-frontend/src/explore/components/ExploreChartPanel.jsx @@ -127,7 +127,6 @@ const ExploreChartPanel = props => { const [splitSizes, setSplitSizes] = useState( getFromLocalStorage(STORAGE_KEYS.sizes, INITIAL_SIZES), ); - const { slice } = props; const updateQueryContext = useCallback( async function fetchChartData() { @@ -210,7 +209,6 @@ const ExploreChartPanel = props => { } setSplitSizes(splitSizes); }; - const renderChart = useCallback(() => { const { chart, vizType } = props; const newHeight = @@ -316,6 +314,7 @@ const ExploreChartPanel = props => { onCollapseChange={onCollapseChange} chartStatus={props.chart.chartStatus} errorMessage={props.errorMessage} + queriesResponse={props.chart.queriesResponse} /> )} diff --git a/superset/viz.py b/superset/viz.py index cd19807b11b1..8d0a60fe2054 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -449,7 +449,7 @@ def get_payload(self, query_obj: Optional[QueryObjectDict] = None) -> VizPayload payload = self.get_df_payload(query_obj) - df = payload.get("df") + df = pd.DataFrame() if self.status != utils.QueryStatus.FAILED: payload["data"] = self.get_data(df) @@ -481,7 +481,8 @@ def get_payload(self, query_obj: Optional[QueryObjectDict] = None) -> VizPayload for col in filter_columns if col not in columns and col not in filter_values_columns ] + rejected_time_columns - + if hasattr(df, "columns"): + payload["colnames"] = list(df.columns) return payload def get_df_payload( From 1dd71b62ff9b6c631dfd3739a13165dfa0f38060 Mon Sep 17 00:00:00 2001 From: Arash Date: Thu, 19 Aug 2021 18:46:36 -0400 Subject: [PATCH 6/7] added colnames to all viz types --- superset/viz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/viz.py b/superset/viz.py index 8d0a60fe2054..a58831644af3 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -449,7 +449,7 @@ def get_payload(self, query_obj: Optional[QueryObjectDict] = None) -> VizPayload payload = self.get_df_payload(query_obj) - df = pd.DataFrame() + df = payload.get("df") or pd.DataFrame() if self.status != utils.QueryStatus.FAILED: payload["data"] = self.get_data(df) From 078816e33a012bcb547e8aa9463552ec6df55397 Mon Sep 17 00:00:00 2001 From: Arash Date: Thu, 19 Aug 2021 18:50:03 -0400 Subject: [PATCH 7/7] added colnames to all viz types --- .../components/DataTablesPane/DataTablesPane.test.tsx | 5 +++++ superset/viz.py | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx index 7df51908ad7c..3af49e0edfe0 100644 --- a/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx +++ b/superset-frontend/src/explore/components/DataTablesPane/DataTablesPane.test.tsx @@ -58,6 +58,11 @@ const createProps = () => ({ tableSectionHeight: 156.9, chartStatus: 'rendered', onCollapseChange: jest.fn(), + queriesResponse: [ + { + colnames: [], + }, + ], }); afterAll(() => { diff --git a/superset/viz.py b/superset/viz.py index a58831644af3..ad299d47e6c5 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -449,7 +449,8 @@ def get_payload(self, query_obj: Optional[QueryObjectDict] = None) -> VizPayload payload = self.get_df_payload(query_obj) - df = payload.get("df") or pd.DataFrame() + # if payload does not have a df, we are raising an error here. + df = cast(Optional[pd.DataFrame], payload["df"]) if self.status != utils.QueryStatus.FAILED: payload["data"] = self.get_data(df) @@ -481,7 +482,7 @@ def get_payload(self, query_obj: Optional[QueryObjectDict] = None) -> VizPayload for col in filter_columns if col not in columns and col not in filter_values_columns ] + rejected_time_columns - if hasattr(df, "columns"): + if df is not None: payload["colnames"] = list(df.columns) return payload