You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by hu...@apache.org on 2025/02/09 12:50:02 UTC
(superset) 01/02: save
This is an automated email from the ASF dual-hosted git repository.
hugh pushed a commit to branch hm/ar-filters
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 16751c09c3328eab4970d7c6e8f5e8d1870dc6d8
Author: Hugh Miles <hu...@gmail.com>
AuthorDate: Fri Feb 7 00:15:42 2025 +0000
save
---
.../src/features/alerts/AlertReportModal.tsx | 25 ++++++-----
superset-frontend/src/features/alerts/types.ts | 7 +++
superset/commands/dashboard/permalink/create.py | 19 +++++++-
superset/commands/explore/get.py | 1 +
superset/commands/report/execute.py | 31 +++++++++++--
superset/reports/models.py | 52 +++++++++++-----------
superset/views/core.py | 12 ++++-
7 files changed, 101 insertions(+), 46 deletions(-)
diff --git a/superset-frontend/src/features/alerts/AlertReportModal.tsx b/superset-frontend/src/features/alerts/AlertReportModal.tsx
index 5152160b4f..d971847637 100644
--- a/superset-frontend/src/features/alerts/AlertReportModal.tsx
+++ b/superset-frontend/src/features/alerts/AlertReportModal.tsx
@@ -463,6 +463,8 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
const [nativeFilter, setSelectedNativeFilter] = useState<object>({});
const [nativeFilters, setGlobalNativeFilters] = useState<object>({});
+ console.log('okkkkk', nativeFilter)
+
// Validation
const [validationStatus, setValidationStatus] = useState<ValidationObject>({
[Sections.General]: {
@@ -676,7 +678,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
// todo(hughhh): refactor to handle multiple native filters
- currentAlert.extra.nativeFilter = nativeFilter
+ currentAlert.extra.dashboard.nativeFilters = [nativeFilter]
const data: any = {
...currentAlert,
@@ -1186,14 +1188,14 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
setForceScreenshot(event.target.checked);
};
- const onChangeDashboardFilter = (value: any) => {
- console.log('dashboardFilter', value);
+ const onChangeDashboardFilter = (nativeFilterId: string) => {
+ console.log('dashboardFilter', nativeFilterId);
// set dashboardFilter
const anchor = currentAlert?.extra?.dashboard?.anchor
const inScopeFilters = nativeFilters[anchor];
- const filter = inScopeFilters.filter((f: any) => f.id === value)[0]
+ const filter = inScopeFilters.filter((f: any) => f.id === nativeFilterId)[0]
console.log(filter) // use filter to grab values from API
- console.log('about to get filter values', value);
+ console.log('about to get filter values', nativeFilterId);
const datasetId = filter.targets[0].datasetId;
const columnName = filter.targets[0].column.name;
@@ -1227,19 +1229,18 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
force: false,
ownState: {}
}
-
+ setSelectedNativeFilter({...nativeFilter, columnName, nativeFilterId})
getChartDataRequest(filterValues).then(response => {
setNativeFilterValues(response.json.result[0].data.map((item: any) => ({value: item[columnName], label: item[columnName]})))
});
-
}
- const onChangeDashboardFilterValue = (value: any) => {
- console.log('dashboardValue', value);
- // set dashboardValue
- setSelectedNativeFilter({...nativeFilter, nativeFilterValue: value});
+ const onChangeDashboardFilterValue = (filterValue: any) => {
+ console.log('dashboardValue', filterValue);
-
+ // todo(hughhh): refactor to handle multiple native filters
+ // once you have multiselect
+ setSelectedNativeFilter({...nativeFilter, filterValues: [filterValue]});
}
// Make sure notification settings has the required info
diff --git a/superset-frontend/src/features/alerts/types.ts b/superset-frontend/src/features/alerts/types.ts
index 06449697c3..3b00067a48 100644
--- a/superset-frontend/src/features/alerts/types.ts
+++ b/superset-frontend/src/features/alerts/types.ts
@@ -94,8 +94,15 @@ export type DashboardState = {
anchor?: string;
};
+export type ExtraNativeFilter = {
+ columnName?: string;
+ filterValues?: Array<any>;
+ // filterOp?: string; // assuming all operators are 'IN' for now
+};
+
export type Extra = {
dashboard?: DashboardState;
+ nativeFilters?: Array<ExtraNativeFilter>;
};
export type Operator = '<' | '>' | '<=' | '>=' | '==' | '!=' | 'not null';
diff --git a/superset/commands/dashboard/permalink/create.py b/superset/commands/dashboard/permalink/create.py
index 20bc5118f5..7bd91f8640 100644
--- a/superset/commands/dashboard/permalink/create.py
+++ b/superset/commands/dashboard/permalink/create.py
@@ -66,11 +66,21 @@ class CreateDashboardPermalinkCommand(BaseDashboardPermalinkCommand):
def run(self) -> str:
self.validate()
dashboard = DashboardDAO.get_by_id_or_slug(self.dashboard_id)
+ print("creating permalink...")
+
value = {
"dashboardId": str(dashboard.uuid),
- "state": self.state,
+ "state": {**self.state, "urlParams": [['native_filter', '(NATIVE_FILTER-8jS1fx4hl:(extraFormData:(filters:!((col:country_name,op:IN,val:!(Brazil)))),filterState:(label:country_name,validateStatus:!f,value:!(Brazil)),id:NATIVE_FILTER-8jS1fx4hl,ownState:()))']]},
}
user_id = get_user_id()
+
+ print('.' * 10)
+ print(self.resource)
+ print(user_id)
+ print(value)
+ print(self.codec)
+ print('.' * 10)
+
entry = KeyValueDAO.upsert_entry(
resource=self.resource,
key=get_deterministic_uuid(self.salt, (user_id, value)),
@@ -79,7 +89,12 @@ class CreateDashboardPermalinkCommand(BaseDashboardPermalinkCommand):
)
db.session.flush()
assert entry.id # for type checks
- return encode_permalink_key(key=entry.id, salt=self.salt)
+
+ hash = encode_permalink_key(key=entry.id, salt=self.salt)
+ print("permalink created")
+ print(hash)
+
+ return hash
def validate(self) -> None:
pass
diff --git a/superset/commands/explore/get.py b/superset/commands/explore/get.py
index dc600bbf97..d32b910137 100644
--- a/superset/commands/explore/get.py
+++ b/superset/commands/explore/get.py
@@ -69,6 +69,7 @@ class GetExploreCommand(BaseCommand, ABC):
state = permalink_value["state"]
initial_form_data = state["formData"]
url_params = state.get("urlParams")
+ print('perm_url_params', url_params)
if url_params:
initial_form_data["url_params"] = dict(url_params)
elif self._form_data_key:
diff --git a/superset/commands/report/execute.py b/superset/commands/report/execute.py
index 4345a7160b..493d0a64f1 100644
--- a/superset/commands/report/execute.py
+++ b/superset/commands/report/execute.py
@@ -228,16 +228,27 @@ class BaseReportState:
Retrieve the URL for the dashboard tabs, or return the dashboard URL if no tabs are available.
""" # noqa: E501
force = "true" if self._report_schedule.force_screenshot else "false"
+
+
if (
dashboard_state := self._report_schedule.extra.get("dashboard")
) and feature_flag_manager.is_feature_enabled("ALERT_REPORT_TABS"):
if anchor := dashboard_state.get("anchor"):
try:
anchor_list: list[str] = json.loads(anchor)
- return self._get_tabs_urls(anchor_list, user_friendly=user_friendly)
+ urls = self._get_tabs_urls(anchor_list, user_friendly=user_friendly)
+ return urls
except json.JSONDecodeError:
logger.debug("Anchor value is not a list, Fall back to single tab")
- return [self._get_tab_url(dashboard_state)]
+
+ print('returning single tab url....')
+ native_filter_params = self._report_schedule.get_native_filters_params()
+ return [self._get_tab_url({
+ 'anchor': anchor,
+ 'urlParams': {'native_filters': native_filter_params},
+ 'dataMask': None,
+ 'activeTabs': None},
+ user_friendly=user_friendly)]
dashboard = self._report_schedule.dashboard
dashboard_id_or_slug = (
@@ -260,10 +271,13 @@ class BaseReportState:
"""
Get one tab url
"""
+ print('in single tab....')
+ print(dashboard_state)
permalink_key = CreateDashboardPermalinkCommand(
dashboard_id=str(self._report_schedule.dashboard.uuid),
state=dashboard_state,
).run()
+
return get_url_path(
"Superset.dashboard_permalink",
key=permalink_key,
@@ -271,18 +285,21 @@ class BaseReportState:
)
def _get_tabs_urls(
- self, tab_anchors: list[str], user_friendly: bool = False
+ self,
+ tab_anchors: list[str],
+ user_friendly: bool = False
) -> list[str]:
"""
Get multple tabs urls
"""
+ print('hello5')
return [
self._get_tab_url(
{
"anchor": tab_anchor,
"dataMask": None,
"activeTabs": None,
- "urlParams": None, # could potentially back the native_filters here?
+ "urlParams": url_params,
},
user_friendly=user_friendly,
)
@@ -320,6 +337,7 @@ class BaseReportState:
]
else:
urls = self.get_dashboard_urls()
+ print('urls', urls)
window_width, window_height = app.config["WEBDRIVER_WINDOW"]["dashboard"]
width = min(max_width, self._report_schedule.custom_width or window_width)
@@ -481,6 +499,11 @@ class BaseReportState:
error_text = None
header_data = self._get_log_data()
url = self._get_url(user_friendly=True)
+
+ print("*"*100)
+ print(url)
+ print("*"*100)
+
if (
feature_flag_manager.is_feature_enabled("ALERTS_ATTACH_REPORTS")
or self._report_schedule.type == ReportScheduleType.REPORT
diff --git a/superset/reports/models.py b/superset/reports/models.py
index 932c4d6077..063e8d6b6c 100644
--- a/superset/reports/models.py
+++ b/superset/reports/models.py
@@ -186,36 +186,36 @@ class ReportSchedule(AuditMixinNullable, ExtraJSONMixin, Model):
def crontab_humanized(self) -> str:
return get_description(self.crontab)
- def get_native_filters_params(self) -> Optional[str]:
+ def get_native_filters_params(self) -> Optional[dict[str, any]]:
params = None
- if self.extra and self.extra.get("native_filters"):
- filter = self.extra.get("native_filters", {})
- params = self._generate_native_filter(filter.get("id"), filter.get("column"), filter.get("value"))
- return prison.dumps(params)
+ dashboard = self.extra.get('dashboard')
+ if dashboard and dashboard.get("nativeFilters"):
+ for filter in dashboard.get("nativeFilters", []):
+ # todo(hugh): handle multiple nativeFilters + multi values
+ params = self._generate_native_filter(filter.get("nativeFilterId"), filter.get("columnName"), filter.get("filterValues")[0])
+ return params
- def _generate_native_filter(native_filter_id: str, column: str, value: str) -> dict:
+ def _generate_native_filter(self, native_filter_id: str, column_name: str, value: str) -> dict:
return {
- "native_filters": {
- native_filter_id: {
- "id": native_filter_id,
- "extraFormData": {
- "filters": [
- {
- "col": column,
- "op": "IN",
- "val": [value]
- }
- ]
- },
- "filterState": {
- "label": column, # pretty name but still works without this value
- "validateStatus": False,
- "value": [value]
- },
- "ownState": {}
- }
+ native_filter_id: {
+ "id": native_filter_id,
+ "extraFormData": {
+ "filters": [
+ {
+ "col": column_name,
+ "op": "IN",
+ "val": [value]
+ }
+ ]
+ },
+ "filterState": {
+ "label": column_name, # pretty name but still works without this value
+ "validateStatus": False,
+ "value": [value]
+ },
+ "ownState": {}
}
- }
+ }
class ReportRecipients(Model, AuditMixinNullable):
diff --git a/superset/views/core.py b/superset/views/core.py
index 8a56befe0b..f31bba4a2b 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -19,6 +19,7 @@ from __future__ import annotations
import contextlib
import logging
+import prison
from datetime import datetime
from typing import Any, Callable, cast
from urllib import parse
@@ -846,15 +847,22 @@ class Superset(BaseSupersetView):
return redirect("/dashboard/list/")
if not value:
return json_error_response(_("permalink state not found"), status=404)
+
+ print('value___', value)
dashboard_id, state = value["dashboardId"], value.get("state", {})
url = f"/superset/dashboard/{dashboard_id}?permalink_key={key}"
if url_params := state.get("urlParams"):
- params = parse.urlencode(url_params)
- url = f"{url}&{params}"
+ for param_key, param_val in url_params:
+ if param_key == "native_filter":
+ url = f"{url}&native_filters={param_val}"
+ else:
+ params = parse.urlencode([param_key, param_val])
+ url = f"{url}&{params}"
if original_params := request.query_string.decode():
url = f"{url}&{original_params}"
if hash_ := state.get("anchor", state.get("hash")):
url = f"{url}#{hash_}"
+
return redirect(url)
@api