From b5543d879e170e88e0864e032885c9e014be6007 Mon Sep 17 00:00:00 2001 From: Rogan Date: Wed, 26 Jul 2017 13:35:52 +0800 Subject: [PATCH 01/12] [add] Save filters to dashboard --- .../assets/javascripts/dashboard/Dashboard.jsx | 11 +++++++++++ .../dashboard/components/SaveModal.jsx | 1 + superset/assets/visualizations/filter_box.jsx | 17 +++++++++++++++++ superset/models/core.py | 11 +++++++++++ superset/views/core.py | 1 + 5 files changed, 41 insertions(+) diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index 8b0a0e1c69114..ee0cf569e2e70 100644 --- a/superset/assets/javascripts/dashboard/Dashboard.jsx +++ b/superset/assets/javascripts/dashboard/Dashboard.jsx @@ -166,6 +166,10 @@ export function dashboardContainer(dashboard, datasources, userid) { } }, effectiveExtraFilters(sliceId) { + // Don't filter the filter_box itself by preselect_filters + if (this.getSlice(sliceId).formData.viz_type == 'filter_box'){ + return []; + } const f = []; const immuneSlices = this.metadata.filter_immune_slices || []; if (sliceId && immuneSlices.includes(sliceId)) { @@ -195,6 +199,13 @@ export function dashboardContainer(dashboard, datasources, userid) { return f; }, addFilter(sliceId, col, vals, merge = true, refresh = true) { + // If slice doesn't exist, remove the related parameters in preselect_filters. + if (!this.getSlice(sliceId)) { + return this.updateFilterParamsInUrl(); + } else if (col != '__from' && col != '__to' && (this.getSlice(sliceId).formData.groupby.indexOf(col) == -1) ){ + // If col doesn't exist, remove the related parameters in preselect_filters. + return this.updateFilterParamsInUrl(); + } if (!(sliceId in this.filters)) { this.filters[sliceId] = {}; } diff --git a/superset/assets/javascripts/dashboard/components/SaveModal.jsx b/superset/assets/javascripts/dashboard/components/SaveModal.jsx index a22f4ac7725e0..82b44df0f678a 100644 --- a/superset/assets/javascripts/dashboard/components/SaveModal.jsx +++ b/superset/assets/javascripts/dashboard/components/SaveModal.jsx @@ -80,6 +80,7 @@ class SaveModal extends React.PureComponent { css: this.state.css, expanded_slices: expandedSlices, dashboard_title: dashboard.dashboard_title, + default_filters: dashboard.readFilters(), }; let url = null; if (saveType === 'overwrite') { diff --git a/superset/assets/visualizations/filter_box.jsx b/superset/assets/visualizations/filter_box.jsx index a97ec3fe6f85b..d050d5886c3a6 100644 --- a/superset/assets/visualizations/filter_box.jsx +++ b/superset/assets/visualizations/filter_box.jsx @@ -74,6 +74,23 @@ class FilterBox extends React.Component { ); }); } + // Add created options to filtersChoices, even though it doesn't exist, + // or these options will exist in query sql but invisible to end user. + if (this.state.selectedValues){ + for (var filter in this.state.selectedValues){ + const exist_values = this.props.filtersChoices[filter].map(f => f.id) + for (let v of this.state.selectedValues[filter]){ + if (exist_values.indexOf(v) == -1){ + this.props.filtersChoices[filter].push({ + 'filter': filter, + 'id': v, + 'text': v, + 'metric': 0 + }) + } + } + } + } const filters = Object.keys(this.props.filtersChoices).map((filter) => { const data = this.props.filtersChoices[filter]; const maxes = {}; diff --git a/superset/models/core.py b/superset/models/core.py index 5527f11d45291..1352be847a990 100644 --- a/superset/models/core.py +++ b/superset/models/core.py @@ -322,6 +322,17 @@ def table_names(self): @property def url(self): + if self.json_metadata: + # add default_filters to the preselect_filters of dashboard + json_metadata = json.loads(self.json_metadata) + default_filters = json_metadata.get('default_filters') + if default_filters: + filters = '' + if isinstance(default_filters, str): + filters = urllib.quote(default_filters) + elif isinstance(default_filters, unicode): + filters = urllib.quote(default_filters.encode('utf8')) + return "/superset/dashboard/{}/?preselect_filters={}".format(self.slug or self.id, filters) return "/superset/dashboard/{}/".format(self.slug or self.id) @property diff --git a/superset/views/core.py b/superset/views/core.py index eded30904f4bf..4743f91dc51bd 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -1345,6 +1345,7 @@ def _set_dash_metadata(dashboard, data): if 'filter_immune_slice_fields' not in md: md['filter_immune_slice_fields'] = {} md['expanded_slices'] = data['expanded_slices'] + md['default_filters'] = data['default_filters'] dashboard.json_metadata = json.dumps(md, indent=4) @api From 581f610f741f22777266305e2120532c4f71b16f Mon Sep 17 00:00:00 2001 From: Rogan Date: Wed, 26 Jul 2017 15:12:30 +0800 Subject: [PATCH 02/12] format code --- .../assets/javascripts/dashboard/Dashboard.jsx | 4 ++-- superset/assets/visualizations/filter_box.jsx | 18 +++++++++--------- superset/models/core.py | 4 +++- superset/views/core.py | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index ee0cf569e2e70..bd48b1df5d01e 100644 --- a/superset/assets/javascripts/dashboard/Dashboard.jsx +++ b/superset/assets/javascripts/dashboard/Dashboard.jsx @@ -167,7 +167,7 @@ export function dashboardContainer(dashboard, datasources, userid) { }, effectiveExtraFilters(sliceId) { // Don't filter the filter_box itself by preselect_filters - if (this.getSlice(sliceId).formData.viz_type == 'filter_box'){ + if (this.getSlice(sliceId).formData.viz_type === 'filter_box') { return []; } const f = []; @@ -202,7 +202,7 @@ export function dashboardContainer(dashboard, datasources, userid) { // If slice doesn't exist, remove the related parameters in preselect_filters. if (!this.getSlice(sliceId)) { return this.updateFilterParamsInUrl(); - } else if (col != '__from' && col != '__to' && (this.getSlice(sliceId).formData.groupby.indexOf(col) == -1) ){ + } else if (col !== '__from' && col !== '__to' && (this.getSlice(sliceId).formData.groupby.indexOf(col) === -1)) { // If col doesn't exist, remove the related parameters in preselect_filters. return this.updateFilterParamsInUrl(); } diff --git a/superset/assets/visualizations/filter_box.jsx b/superset/assets/visualizations/filter_box.jsx index d050d5886c3a6..965bb22621fdd 100644 --- a/superset/assets/visualizations/filter_box.jsx +++ b/superset/assets/visualizations/filter_box.jsx @@ -76,17 +76,17 @@ class FilterBox extends React.Component { } // Add created options to filtersChoices, even though it doesn't exist, // or these options will exist in query sql but invisible to end user. - if (this.state.selectedValues){ - for (var filter in this.state.selectedValues){ - const exist_values = this.props.filtersChoices[filter].map(f => f.id) - for (let v of this.state.selectedValues[filter]){ - if (exist_values.indexOf(v) == -1){ - this.props.filtersChoices[filter].push({ - 'filter': filter, + if (this.state.selectedValues) { + for (let filterKey of this.state.selectedValues) { + const existValues = this.props.filtersChoices[filterKey].map(f => f.id); + for (const v of this.state.selectedValues[filterKey]) { + if (existValues.indexOf(v) === -1) { + this.props.filtersChoices[filterKey].push({ + 'filter': filterKey, 'id': v, 'text': v, - 'metric': 0 - }) + 'metric': 0, + }); } } } diff --git a/superset/models/core.py b/superset/models/core.py index 1352be847a990..94a070dbd4bdb 100644 --- a/superset/models/core.py +++ b/superset/models/core.py @@ -40,6 +40,7 @@ from superset.viz import viz_types from superset.models.helpers import AuditMixinNullable, ImportMixin, set_perm install_aliases() +import urllib from urllib import parse # noqa config = app.config @@ -332,7 +333,8 @@ def url(self): filters = urllib.quote(default_filters) elif isinstance(default_filters, unicode): filters = urllib.quote(default_filters.encode('utf8')) - return "/superset/dashboard/{}/?preselect_filters={}".format(self.slug or self.id, filters) + return "/superset/dashboard/{}/?preselect_filters={}".format( + self.slug or self.id, filters) return "/superset/dashboard/{}/".format(self.slug or self.id) @property diff --git a/superset/views/core.py b/superset/views/core.py index 4743f91dc51bd..9a6bae43f6f85 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -1345,7 +1345,7 @@ def _set_dash_metadata(dashboard, data): if 'filter_immune_slice_fields' not in md: md['filter_immune_slice_fields'] = {} md['expanded_slices'] = data['expanded_slices'] - md['default_filters'] = data['default_filters'] + md['default_filters'] = data.get('default_filters', '') dashboard.json_metadata = json.dumps(md, indent=4) @api From 10e366ed869d74aaeabbb428bd5899051451881f Mon Sep 17 00:00:00 2001 From: Rogan Date: Wed, 26 Jul 2017 17:14:38 +0800 Subject: [PATCH 03/12] fix CI error --- .../assets/javascripts/dashboard/Dashboard.jsx | 2 +- superset/assets/visualizations/filter_box.jsx | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index bd48b1df5d01e..f028a6a8729e2 100644 --- a/superset/assets/javascripts/dashboard/Dashboard.jsx +++ b/superset/assets/javascripts/dashboard/Dashboard.jsx @@ -222,7 +222,7 @@ export function dashboardContainer(dashboard, datasources, userid) { if (refresh) { this.refreshExcept(sliceId); } - this.updateFilterParamsInUrl(); + return this.updateFilterParamsInUrl(); }, readFilters() { // Returns a list of human readable active filters diff --git a/superset/assets/visualizations/filter_box.jsx b/superset/assets/visualizations/filter_box.jsx index 965bb22621fdd..15c95895e653b 100644 --- a/superset/assets/visualizations/filter_box.jsx +++ b/superset/assets/visualizations/filter_box.jsx @@ -77,16 +77,17 @@ class FilterBox extends React.Component { // Add created options to filtersChoices, even though it doesn't exist, // or these options will exist in query sql but invisible to end user. if (this.state.selectedValues) { - for (let filterKey of this.state.selectedValues) { + for (const filterKey of this.state.selectedValues) { const existValues = this.props.filtersChoices[filterKey].map(f => f.id); for (const v of this.state.selectedValues[filterKey]) { if (existValues.indexOf(v) === -1) { - this.props.filtersChoices[filterKey].push({ - 'filter': filterKey, - 'id': v, - 'text': v, - 'metric': 0, - }); + const addChoice = { + filter: filterKey, + id: v, + text: v, + metric: 0, + } + this.props.filtersChoices[filterKey].push(addChoice); } } } From 5df20881ffc50b94fb017a4589d9ae1b4f6f74d1 Mon Sep 17 00:00:00 2001 From: Rogan Date: Wed, 26 Jul 2017 19:41:08 +0800 Subject: [PATCH 04/12] add semicolon semi --- superset/assets/visualizations/filter_box.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/visualizations/filter_box.jsx b/superset/assets/visualizations/filter_box.jsx index 15c95895e653b..b70f1a34bb1fe 100644 --- a/superset/assets/visualizations/filter_box.jsx +++ b/superset/assets/visualizations/filter_box.jsx @@ -86,7 +86,7 @@ class FilterBox extends React.Component { id: v, text: v, metric: 0, - } + }; this.props.filtersChoices[filterKey].push(addChoice); } } From b043d7e61dc0c74ee0f6c7cb981f3101daf20fbb Mon Sep 17 00:00:00 2001 From: Rogan Date: Sun, 30 Jul 2017 23:36:58 +0800 Subject: [PATCH 05/12] fix none object --- superset/assets/visualizations/filter_box.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/visualizations/filter_box.jsx b/superset/assets/visualizations/filter_box.jsx index b70f1a34bb1fe..7e02dd59bbaeb 100644 --- a/superset/assets/visualizations/filter_box.jsx +++ b/superset/assets/visualizations/filter_box.jsx @@ -76,7 +76,7 @@ class FilterBox extends React.Component { } // Add created options to filtersChoices, even though it doesn't exist, // or these options will exist in query sql but invisible to end user. - if (this.state.selectedValues) { + if (this.state.selectedValues.hasOwnProperty()) { for (const filterKey of this.state.selectedValues) { const existValues = this.props.filtersChoices[filterKey].map(f => f.id); for (const v of this.state.selectedValues[filterKey]) { From b38bac4d28d1adf3b0447414f24b662bba196efe Mon Sep 17 00:00:00 2001 From: Rogan Date: Fri, 11 Aug 2017 01:28:15 +0800 Subject: [PATCH 06/12] add test data optimize the js code fix the compatibility issue --- .../javascripts/dashboard/Dashboard.jsx | 38 +++++++++---------- superset/data/__init__.py | 3 ++ superset/models/core.py | 6 +-- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index f028a6a8729e2..4ba20c2a4b8ef 100644 --- a/superset/assets/javascripts/dashboard/Dashboard.jsx +++ b/superset/assets/javascripts/dashboard/Dashboard.jsx @@ -199,28 +199,24 @@ export function dashboardContainer(dashboard, datasources, userid) { return f; }, addFilter(sliceId, col, vals, merge = true, refresh = true) { - // If slice doesn't exist, remove the related parameters in preselect_filters. - if (!this.getSlice(sliceId)) { - return this.updateFilterParamsInUrl(); - } else if (col !== '__from' && col !== '__to' && (this.getSlice(sliceId).formData.groupby.indexOf(col) === -1)) { - // If col doesn't exist, remove the related parameters in preselect_filters. - return this.updateFilterParamsInUrl(); - } - if (!(sliceId in this.filters)) { - this.filters[sliceId] = {}; - } - if (!(col in this.filters[sliceId]) || !merge) { - this.filters[sliceId][col] = vals; + if (this.getSlice(sliceId) && (col === '__from' || col === '__to' || + this.getSlice(sliceId).formData.groupby.indexOf(col) !== -1)){ + if (!(sliceId in this.filters)) { + this.filters[sliceId] = {}; + } + if (!(col in this.filters[sliceId]) || !merge) { + this.filters[sliceId][col] = vals; - // d3.merge pass in array of arrays while some value form filter components - // from and to filter box require string to be process and return - } else if (this.filters[sliceId][col] instanceof Array) { - this.filters[sliceId][col] = d3.merge([this.filters[sliceId][col], vals]); - } else { - this.filters[sliceId][col] = d3.merge([[this.filters[sliceId][col]], vals])[0] || ''; - } - if (refresh) { - this.refreshExcept(sliceId); + // d3.merge pass in array of arrays while some value form filter components + // from and to filter box require string to be process and return + } else if (this.filters[sliceId][col] instanceof Array) { + this.filters[sliceId][col] = d3.merge([this.filters[sliceId][col], vals]); + } else { + this.filters[sliceId][col] = d3.merge([[this.filters[sliceId][col]], vals])[0] || ''; + } + if (refresh) { + this.refreshExcept(sliceId); + } } return this.updateFilterParamsInUrl(); }, diff --git a/superset/data/__init__.py b/superset/data/__init__.py index c3428b3d2c354..d71f67f2bd77e 100644 --- a/superset/data/__init__.py +++ b/superset/data/__init__.py @@ -441,6 +441,9 @@ def load_world_bank_health_n_pop(): dash.position_json = json.dumps(l, indent=4) dash.slug = slug + filters = json.dumps({str(slices[0].id): {'region': ['North America']}}) + dash.json_metadata = json.dumps({'default_filters': filters}) + dash.slices = slices[:-1] db.session.merge(dash) db.session.commit() diff --git a/superset/models/core.py b/superset/models/core.py index 94a070dbd4bdb..637356c82f870 100644 --- a/superset/models/core.py +++ b/superset/models/core.py @@ -328,11 +328,7 @@ def url(self): json_metadata = json.loads(self.json_metadata) default_filters = json_metadata.get('default_filters') if default_filters: - filters = '' - if isinstance(default_filters, str): - filters = urllib.quote(default_filters) - elif isinstance(default_filters, unicode): - filters = urllib.quote(default_filters.encode('utf8')) + filters = urllib.quote(default_filters.encode('utf8')) return "/superset/dashboard/{}/?preselect_filters={}".format( self.slug or self.id, filters) return "/superset/dashboard/{}/".format(self.slug or self.id) From 5c11b9f77fa2307b10bbe970baad6f3d507a0f39 Mon Sep 17 00:00:00 2001 From: Rogan Date: Fri, 11 Aug 2017 02:15:26 +0800 Subject: [PATCH 07/12] fix urllib to urllib.parse --- superset/models/core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/superset/models/core.py b/superset/models/core.py index 637356c82f870..674a84f61e683 100644 --- a/superset/models/core.py +++ b/superset/models/core.py @@ -40,7 +40,6 @@ from superset.viz import viz_types from superset.models.helpers import AuditMixinNullable, ImportMixin, set_perm install_aliases() -import urllib from urllib import parse # noqa config = app.config @@ -328,7 +327,7 @@ def url(self): json_metadata = json.loads(self.json_metadata) default_filters = json_metadata.get('default_filters') if default_filters: - filters = urllib.quote(default_filters.encode('utf8')) + filters = parse.quote(default_filters.encode('utf8')) return "/superset/dashboard/{}/?preselect_filters={}".format( self.slug or self.id, filters) return "/superset/dashboard/{}/".format(self.slug or self.id) From 4bbe05d72b5989d569bb2d26cf8e5f48aef28c6c Mon Sep 17 00:00:00 2001 From: Rogan Date: Fri, 11 Aug 2017 02:17:26 +0800 Subject: [PATCH 08/12] add space --- superset/assets/javascripts/dashboard/Dashboard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index 4ba20c2a4b8ef..927969a97824c 100644 --- a/superset/assets/javascripts/dashboard/Dashboard.jsx +++ b/superset/assets/javascripts/dashboard/Dashboard.jsx @@ -200,7 +200,7 @@ export function dashboardContainer(dashboard, datasources, userid) { }, addFilter(sliceId, col, vals, merge = true, refresh = true) { if (this.getSlice(sliceId) && (col === '__from' || col === '__to' || - this.getSlice(sliceId).formData.groupby.indexOf(col) !== -1)){ + this.getSlice(sliceId).formData.groupby.indexOf(col) !== -1)) { if (!(sliceId in this.filters)) { this.filters[sliceId] = {}; } From 5d815b224790446c4360e6312a7ea2657122f2c6 Mon Sep 17 00:00:00 2001 From: Rogan Date: Fri, 11 Aug 2017 02:50:41 +0800 Subject: [PATCH 09/12] update test case --- superset/data/__init__.py | 3 --- tests/core_tests.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/superset/data/__init__.py b/superset/data/__init__.py index d71f67f2bd77e..c3428b3d2c354 100644 --- a/superset/data/__init__.py +++ b/superset/data/__init__.py @@ -441,9 +441,6 @@ def load_world_bank_health_n_pop(): dash.position_json = json.dumps(l, indent=4) dash.slug = slug - filters = json.dumps({str(slices[0].id): {'region': ['North America']}}) - dash.json_metadata = json.dumps({'default_filters': filters}) - dash.slices = slices[:-1] db.session.merge(dash) db.session.commit() diff --git a/tests/core_tests.py b/tests/core_tests.py index f3e98873a63fa..b8fdd86dbcff7 100644 --- a/tests/core_tests.py +++ b/tests/core_tests.py @@ -380,6 +380,34 @@ def test_save_dash(self, username='admin'): resp = self.get_resp(url, data=dict(data=json.dumps(data))) self.assertIn("SUCCESS", resp) + def test_save_dash_with_filter(self, username='admin'): + self.login(username=username) + dash = db.session.query(models.Dashboard).filter_by( + slug="world_health").first() + positions = [] + for i, slc in enumerate(dash.slices): + d = { + 'col': 0, + 'row': i * 4, + 'size_x': 4, + 'size_y': 4, + 'slice_id': '{}'.format(slc.id)} + positions.append(d) + + filters = {str(dash.slices[0].id): {'region': ['North America']}} + json_metadata = json.dumps({'default_filters': json.dumps(filters)}) + data = { + 'css': '', + 'expanded_slices': {}, + 'positions': positions, + 'dashboard_title': dash.dashboard_title, + 'json_metadata': json_metadata + } + + url = '/superset/save_dash/{}/'.format(dash.id) + resp = self.get_resp(url, data=dict(data=json.dumps(data))) + self.assertIn("SUCCESS", resp) + def test_save_dash_with_dashboard_title(self, username='admin'): self.login(username=username) dash = ( From adfd6941fc26c327f79714f59755a65e85dcd220 Mon Sep 17 00:00:00 2001 From: Rogan Date: Fri, 11 Aug 2017 06:36:32 +0800 Subject: [PATCH 10/12] remove 'return' --- superset/assets/javascripts/dashboard/Dashboard.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index 927969a97824c..5de74774f676f 100644 --- a/superset/assets/javascripts/dashboard/Dashboard.jsx +++ b/superset/assets/javascripts/dashboard/Dashboard.jsx @@ -217,8 +217,7 @@ export function dashboardContainer(dashboard, datasources, userid) { if (refresh) { this.refreshExcept(sliceId); } - } - return this.updateFilterParamsInUrl(); + this.updateFilterParamsInUrl(); }, readFilters() { // Returns a list of human readable active filters From 396e375d4e393ec28abf805ef14e82b4380228d0 Mon Sep 17 00:00:00 2001 From: Rogan Date: Fri, 11 Aug 2017 08:19:38 +0800 Subject: [PATCH 11/12] fix error --- superset/assets/javascripts/dashboard/Dashboard.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index 5de74774f676f..c6262f580ee8e 100644 --- a/superset/assets/javascripts/dashboard/Dashboard.jsx +++ b/superset/assets/javascripts/dashboard/Dashboard.jsx @@ -217,6 +217,7 @@ export function dashboardContainer(dashboard, datasources, userid) { if (refresh) { this.refreshExcept(sliceId); } + } this.updateFilterParamsInUrl(); }, readFilters() { From f46ea38698253b1e515d09c3faf695057c1c72a5 Mon Sep 17 00:00:00 2001 From: Rogan Date: Fri, 11 Aug 2017 09:29:37 +0800 Subject: [PATCH 12/12] update test case --- tests/core_tests.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/core_tests.py b/tests/core_tests.py index b8fdd86dbcff7..4a7efc4de6aff 100644 --- a/tests/core_tests.py +++ b/tests/core_tests.py @@ -393,21 +393,29 @@ def test_save_dash_with_filter(self, username='admin'): 'size_y': 4, 'slice_id': '{}'.format(slc.id)} positions.append(d) - + filters = {str(dash.slices[0].id): {'region': ['North America']}} - json_metadata = json.dumps({'default_filters': json.dumps(filters)}) + default_filters = json.dumps(filters) data = { 'css': '', 'expanded_slices': {}, 'positions': positions, 'dashboard_title': dash.dashboard_title, - 'json_metadata': json_metadata + 'default_filters': default_filters } url = '/superset/save_dash/{}/'.format(dash.id) resp = self.get_resp(url, data=dict(data=json.dumps(data))) self.assertIn("SUCCESS", resp) + updatedDash = db.session.query(models.Dashboard).filter_by( + slug="world_health").first() + new_url = updatedDash.url + self.assertIn("region", new_url) + + resp = self.get_resp(new_url) + self.assertIn("North America", resp) + def test_save_dash_with_dashboard_title(self, username='admin'): self.login(username=username) dash = (