fix: dashboard extra filters (#10692)

Co-authored-by: John Bodley <john.bodley@airbnb.com>
This commit is contained in:
John Bodley 2020-09-02 16:03:25 -07:00 committed by GitHub
parent 45f4c689a2
commit 1ee87cc4d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 255 deletions

View File

@ -153,7 +153,7 @@ def load_world_bank_health_n_pop( # pylint: disable=too-many-locals, too-many-s
"column": "region", "column": "region",
"key": "2s98dfu", "key": "2s98dfu",
"metric": "sum__SP_POP_TOTL", "metric": "sum__SP_POP_TOTL",
"multiple": True, "multiple": False,
}, },
{ {
"asc": False, "asc": False,

View File

@ -336,7 +336,7 @@ def get_dashboard_extra_filters(
return [] return []
def build_extra_filters( def build_extra_filters( # pylint: disable=too-many-locals,too-many-nested-blocks
layout: Dict[str, Dict[str, Any]], layout: Dict[str, Dict[str, Any]],
filter_scopes: Dict[str, Dict[str, Any]], filter_scopes: Dict[str, Dict[str, Any]],
default_filters: Dict[str, Dict[str, List[Any]]], default_filters: Dict[str, Dict[str, List[Any]]],
@ -344,11 +344,22 @@ def build_extra_filters(
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
extra_filters = [] extra_filters = []
# do not apply filters if chart is not in filter's scope or # do not apply filters if chart is not in filter's scope or chart is immune to the
# chart is immune to the filter # filter.
for filter_id, columns in default_filters.items(): for filter_id, columns in default_filters.items():
filter_slice = db.session.query(Slice).filter_by(id=filter_id).one_or_none()
filter_configs = (
json.loads(filter_slice.params or "{}").get("filter_configs") or []
if filter_slice
else []
)
scopes_by_filter_field = filter_scopes.get(filter_id, {}) scopes_by_filter_field = filter_scopes.get(filter_id, {})
for col, val in columns.items(): for col, val in columns.items():
if not val:
continue
current_field_scopes = scopes_by_filter_field.get(col, {}) current_field_scopes = scopes_by_filter_field.get(col, {})
scoped_container_ids = current_field_scopes.get("scope", ["ROOT_ID"]) scoped_container_ids = current_field_scopes.get("scope", ["ROOT_ID"])
immune_slice_ids = current_field_scopes.get("immune", []) immune_slice_ids = current_field_scopes.get("immune", [])
@ -357,7 +368,25 @@ def build_extra_filters(
if slice_id not in immune_slice_ids and is_slice_in_container( if slice_id not in immune_slice_ids and is_slice_in_container(
layout, container_id, slice_id layout, container_id, slice_id
): ):
extra_filters.append({"col": col, "op": "in", "val": val}) # Ensure that the filter value encoding adheres to the filter select
# type.
for filter_config in filter_configs:
if filter_config["column"] == col:
is_multiple = filter_config["multiple"]
if not is_multiple and isinstance(val, list):
val = val[0]
elif is_multiple and not isinstance(val, list):
val = [val]
break
extra_filters.append(
{
"col": col,
"op": "in" if isinstance(val, list) else "==",
"val": val,
}
)
return extra_filters return extra_filters

View File

@ -168,7 +168,7 @@ class TestCacheWarmUp(SupersetTestCase):
"slice_id": chart_id, "slice_id": chart_id,
"extra_filters": [ "extra_filters": [
{"col": "name", "op": "in", "val": ["Alice", "Bob"]}, {"col": "name", "op": "in", "val": ["Alice", "Bob"]},
{"col": "__time_range", "op": "in", "val": "100 years ago : today"}, {"col": "__time_range", "op": "==", "val": "100 years ago : today"},
], ],
} }
self.assertEqual(result, expected) self.assertEqual(result, expected)

View File

@ -35,6 +35,8 @@ import tests.test_app
from superset import app, db, security_manager from superset import app, db, security_manager
from superset.exceptions import CertificateException, SupersetException from superset.exceptions import CertificateException, SupersetException
from superset.models.core import Database, Log from superset.models.core import Database, Log
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.utils.cache_manager import CacheManager from superset.utils.cache_manager import CacheManager
from superset.utils.core import ( from superset.utils.core import (
base_json_conv, base_json_conv,
@ -965,268 +967,47 @@ class TestUtils(SupersetTestCase):
self.assertListEqual(get_iterable("foo"), ["foo"]) self.assertListEqual(get_iterable("foo"), ["foo"])
def test_build_extra_filters(self): def test_build_extra_filters(self):
layout = { world_health = db.session.query(Dashboard).filter_by(slug="world_health").one()
"CHART-2ee52f30": { layout = json.loads(world_health.position_json)
"children": [], filter_ = db.session.query(Slice).filter_by(slice_name="Region Filter").one()
"id": "CHART-2ee52f30", world = db.session.query(Slice).filter_by(slice_name="World's Population").one()
"meta": { box_plot = db.session.query(Slice).filter_by(slice_name="Box plot").one()
"chartId": 1020, treemap = db.session.query(Slice).filter_by(slice_name="Treemap").one()
"height": 38,
"sliceName": "Chart 927",
"width": 6,
},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-VrhTX2WUlO",
"TABS-N1zN4CIZP0",
"TAB-asWdJzKmTN",
"ROW-i_sG4ccXE",
],
"type": "CHART",
},
"CHART-36bfc934": {
"children": [],
"id": "CHART-36bfc934",
"meta": {
"chartId": 1018,
"height": 26,
"sliceName": "Region Filter",
"width": 2,
},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-W62P60D88",
"ROW-1e064e3c",
"COLUMN-fe3914b8",
],
"type": "CHART",
},
"CHART-E_y2cuNHTv": {
"children": [],
"id": "CHART-E_y2cuNHTv",
"meta": {"chartId": 998, "height": 55, "sliceName": "MAP", "width": 6},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-W62P60D88",
"ROW-1e064e3c",
],
"type": "CHART",
},
"CHART-JNxDOsAfEb": {
"children": [],
"id": "CHART-JNxDOsAfEb",
"meta": {
"chartId": 1015,
"height": 27,
"sliceName": "Population",
"width": 4,
},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-W62P60D88",
"ROW-1e064e3c",
"COLUMN-fe3914b8",
],
"type": "CHART",
},
"CHART-KoOwqalV80": {
"children": [],
"id": "CHART-KoOwqalV80",
"meta": {
"chartId": 927,
"height": 20,
"sliceName": "Chart 927",
"width": 4,
},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-VrhTX2WUlO",
"TABS-N1zN4CIZP0",
"TAB-cHNWcBZC9",
"ROW-9b9vrWKPY",
],
"type": "CHART",
},
"CHART-YCQAPVK7mQ": {
"children": [],
"id": "CHART-YCQAPVK7mQ",
"meta": {
"chartId": 1023,
"height": 38,
"sliceName": "World's Population",
"width": 4,
},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-VrhTX2WUlO",
"ROW-UfxFT36oV5",
],
"type": "CHART",
},
"COLUMN-fe3914b8": {
"children": ["CHART-36bfc934", "CHART-JNxDOsAfEb"],
"id": "COLUMN-fe3914b8",
"meta": {"background": "BACKGROUND_TRANSPARENT", "width": 6},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-W62P60D88",
"ROW-1e064e3c",
],
"type": "COLUMN",
},
"DASHBOARD_VERSION_KEY": "v2",
"GRID_ID": {
"children": [],
"id": "GRID_ID",
"parents": ["ROOT_ID"],
"type": "GRID",
},
"HEADER_ID": {
"id": "HEADER_ID",
"meta": {"text": "Test warmup 1023"},
"type": "HEADER",
},
"ROOT_ID": {
"children": ["TABS-Qq4sdkANSY"],
"id": "ROOT_ID",
"type": "ROOT",
},
"ROW-1e064e3c": {
"children": ["COLUMN-fe3914b8", "CHART-E_y2cuNHTv"],
"id": "ROW-1e064e3c",
"meta": {"background": "BACKGROUND_TRANSPARENT"},
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY", "TAB-W62P60D88"],
"type": "ROW",
},
"ROW-9b9vrWKPY": {
"children": ["CHART-KoOwqalV80"],
"id": "ROW-9b9vrWKPY",
"meta": {"background": "BACKGROUND_TRANSPARENT"},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-VrhTX2WUlO",
"TABS-N1zN4CIZP0",
"TAB-cHNWcBZC9",
],
"type": "ROW",
},
"ROW-UfxFT36oV5": {
"children": ["CHART-YCQAPVK7mQ"],
"id": "ROW-UfxFT36oV5",
"meta": {"background": "BACKGROUND_TRANSPARENT"},
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY", "TAB-VrhTX2WUlO"],
"type": "ROW",
},
"ROW-i_sG4ccXE": {
"children": ["CHART-2ee52f30"],
"id": "ROW-i_sG4ccXE",
"meta": {"background": "BACKGROUND_TRANSPARENT"},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-VrhTX2WUlO",
"TABS-N1zN4CIZP0",
"TAB-asWdJzKmTN",
],
"type": "ROW",
},
"TAB-VrhTX2WUlO": {
"children": ["ROW-UfxFT36oV5", "TABS-N1zN4CIZP0"],
"id": "TAB-VrhTX2WUlO",
"meta": {"text": "New Tab"},
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY"],
"type": "TAB",
},
"TAB-W62P60D88": {
"children": ["ROW-1e064e3c"],
"id": "TAB-W62P60D88",
"meta": {"text": "Tab 2"},
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY"],
"type": "TAB",
},
"TAB-asWdJzKmTN": {
"children": ["ROW-i_sG4ccXE"],
"id": "TAB-asWdJzKmTN",
"meta": {"text": "nested tab 1"},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-VrhTX2WUlO",
"TABS-N1zN4CIZP0",
],
"type": "TAB",
},
"TAB-cHNWcBZC9": {
"children": ["ROW-9b9vrWKPY"],
"id": "TAB-cHNWcBZC9",
"meta": {"text": "test2d tab 2"},
"parents": [
"ROOT_ID",
"TABS-Qq4sdkANSY",
"TAB-VrhTX2WUlO",
"TABS-N1zN4CIZP0",
],
"type": "TAB",
},
"TABS-N1zN4CIZP0": {
"children": ["TAB-asWdJzKmTN", "TAB-cHNWcBZC9"],
"id": "TABS-N1zN4CIZP0",
"meta": {},
"parents": ["ROOT_ID", "TABS-Qq4sdkANSY", "TAB-VrhTX2WUlO"],
"type": "TABS",
},
"TABS-Qq4sdkANSY": {
"children": ["TAB-VrhTX2WUlO", "TAB-W62P60D88"],
"id": "TABS-Qq4sdkANSY",
"meta": {},
"parents": ["ROOT_ID"],
"type": "TABS",
},
}
filter_scopes = { filter_scopes = {
"1018": { str(filter_.id): {
"region": {"scope": ["TAB-W62P60D88"], "immune": [998]}, "region": {"scope": ["ROOT_ID"], "immune": [treemap.id]},
"country_name": {"scope": ["ROOT_ID"], "immune": [927, 998]}, "country_name": {
"scope": ["ROOT_ID"],
"immune": [treemap.id, box_plot.id],
},
} }
} }
default_filters = { default_filters = {
"1018": {"region": ["North America"], "country_name": ["United States"]} str(filter_.id): {
"region": ["North America"],
"country_name": ["United States"],
}
} }
# immune to all filters # immune to all filters
slice_id = 998 assert (
extra_filters = build_extra_filters( build_extra_filters(layout, filter_scopes, default_filters, treemap.id)
layout, filter_scopes, default_filters, slice_id == []
) )
expected = []
self.assertEqual(extra_filters, expected)
# in scope # in scope
slice_id = 1015 assert build_extra_filters(
extra_filters = build_extra_filters( layout, filter_scopes, default_filters, world.id
layout, filter_scopes, default_filters, slice_id ) == [
) {"col": "region", "op": "==", "val": "North America"},
expected = [
{"col": "region", "op": "in", "val": ["North America"]},
{"col": "country_name", "op": "in", "val": ["United States"]}, {"col": "country_name", "op": "in", "val": ["United States"]},
] ]
self.assertEqual(extra_filters, expected)
# not in scope assert build_extra_filters(
slice_id = 927 layout, filter_scopes, default_filters, box_plot.id
extra_filters = build_extra_filters( ) == [{"col": "region", "op": "==", "val": "North America"}]
layout, filter_scopes, default_filters, slice_id
)
expected = []
self.assertEqual(extra_filters, expected)
def test_ssl_certificate_parse(self): def test_ssl_certificate_parse(self):
parsed_certificate = parse_ssl_cert(ssl_certificate) parsed_certificate = parse_ssl_cert(ssl_certificate)