diff --git a/superset-frontend/src/features/alerts/AlertReportModal.tsx b/superset-frontend/src/features/alerts/AlertReportModal.tsx index e136675e35a49..665d1af8629e2 100644 --- a/superset-frontend/src/features/alerts/AlertReportModal.tsx +++ b/superset-frontend/src/features/alerts/AlertReportModal.tsx @@ -823,10 +823,31 @@ const AlertReportModal: FunctionComponent = ({ }) .then(response => { const { tab_tree: tabTree, all_tabs: allTabs } = response.json.result; + tabTree.push({ + title: 'All Tabs', + // select tree only works with string value + value: JSON.stringify(Object.keys(allTabs)), + }); setTabOptions(tabTree); + const anchor = currentAlert?.extra?.dashboard?.anchor; - if (anchor && !(anchor in allTabs)) { - updateAnchorState(undefined); + if (anchor) { + try { + const parsedAnchor = JSON.parse(anchor); + if (Array.isArray(parsedAnchor)) { + // Check if all elements in parsedAnchor list are in allTabs + const isValidSubset = parsedAnchor.every(tab => tab in allTabs); + if (!isValidSubset) { + updateAnchorState(undefined); + } + } else { + throw new Error('Parsed value is not an array'); + } + } catch (error) { + if (!(anchor in allTabs)) { + updateAnchorState(undefined); + } + } } }) .catch(() => { diff --git a/superset/commands/report/create.py b/superset/commands/report/create.py index 2a67f640022d2..ef7aae5bcc012 100644 --- a/superset/commands/report/create.py +++ b/superset/commands/report/create.py @@ -143,14 +143,21 @@ def _validate_report_extra(self, exceptions: list[ValidationError]) -> None: position_data = json.loads(dashboard.position_json or "{}") active_tabs = dashboard_state.get("activeTabs") or [] - anchor = dashboard_state.get("anchor") invalid_tab_ids = set(active_tabs) - set(position_data.keys()) - if anchor and anchor not in position_data: - invalid_tab_ids.add(anchor) + + if anchor := dashboard_state.get("anchor"): + try: + anchor_list: list[str] = json.loads(anchor) + if _invalid_tab_ids := set(anchor_list) - set(position_data.keys()): + invalid_tab_ids.update(_invalid_tab_ids) + except json.JSONDecodeError: + if anchor not in position_data: + invalid_tab_ids.add(anchor) + if invalid_tab_ids: exceptions.append( ValidationError( - _("Invalid tab ids: %s(tab_ids)", tab_ids=str(invalid_tab_ids)), + _("Invalid tab idsssss: %s(tab_ids)", tab_ids=str(invalid_tab_ids)), "extra", ) ) diff --git a/superset/commands/report/execute.py b/superset/commands/report/execute.py index afc488df5640f..cb126d27633d5 100644 --- a/superset/commands/report/execute.py +++ b/superset/commands/report/execute.py @@ -49,6 +49,7 @@ REPORT_SCHEDULE_ERROR_NOTIFICATION_MARKER, ReportScheduleDAO, ) +from superset.dashboards.permalink.types import DashboardPermalinkState from superset.errors import ErrorLevel, SupersetError, SupersetErrorType from superset.exceptions import SupersetErrorsException, SupersetException from superset.extensions import feature_flag_manager, machine_auth_provider_factory @@ -206,11 +207,8 @@ def _get_url( if ( dashboard_state := self._report_schedule.extra.get("dashboard") ) and feature_flag_manager.is_feature_enabled("ALERT_REPORT_TABS"): - 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) + return self._get_tab_url(dashboard_state) + dashboard = self._report_schedule.dashboard dashboard_id_or_slug = ( dashboard.uuid if dashboard and dashboard.uuid else dashboard.id @@ -223,12 +221,70 @@ def _get_url( **kwargs, ) + def get_dashboard_urls( + self, user_friendly: bool = False, **kwargs: Any + ) -> list[str]: + """ + Get the url for dashboard tabs or dashboard if there is no tab + :raises: ReportScheduleScreenshotFailedError + """ + 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 = json.loads(anchor) + return self._get_tabs_urls(anchor_list) + except json.JSONDecodeError: + return [self._get_tab_url(dashboard_state)] + + dashboard = self._report_schedule.dashboard + dashboard_id_or_slug = ( + dashboard.uuid if dashboard and dashboard.uuid else dashboard.id + ) + + return [ + get_url_path( + "Superset.dashboard", + user_friendly=user_friendly, + dashboard_id_or_slug=dashboard_id_or_slug, + force=force, + **kwargs, + ) + ] + + def _get_tab_url(self, dashboard_state: DashboardPermalinkState) -> str: + """ + Get one tab url + """ + 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) + + def _get_tabs_urls(self, tab_anchors: list[str]) -> list[str]: + """ + Get multple tabs urls + """ + return [ + self._get_tab_url( + { + "anchor": tab_anchor, + "dataMask": None, + "activeTabs": None, + "urlParams": None, + } + ) + for tab_anchor in tab_anchors + ] + def _get_screenshots(self) -> list[bytes]: """ Get chart or dashboard screenshots :raises: ReportScheduleScreenshotFailedError """ - url = self._get_url() _, username = get_executor( executor_types=app.config["ALERT_REPORTS_EXECUTE_AS"], model=self._report_schedule, @@ -236,31 +292,41 @@ def _get_screenshots(self) -> list[bytes]: user = security_manager.find_user(username) if self._report_schedule.chart: + url = self._get_url() window_width, window_height = app.config["WEBDRIVER_WINDOW"]["slice"] window_size = ( self._report_schedule.custom_width or window_width, self._report_schedule.custom_height or window_height, ) - screenshot: Union[ChartScreenshot, DashboardScreenshot] = ChartScreenshot( - url, - self._report_schedule.chart.digest, - window_size=window_size, - thumb_size=app.config["WEBDRIVER_WINDOW"]["slice"], - ) + screenshots: list[Union[ChartScreenshot, DashboardScreenshot]] = [ + ChartScreenshot( + url, + self._report_schedule.chart.digest, + window_size=window_size, + thumb_size=app.config["WEBDRIVER_WINDOW"]["slice"], + ) + ] else: + urls = self.get_dashboard_urls() window_width, window_height = app.config["WEBDRIVER_WINDOW"]["dashboard"] window_size = ( self._report_schedule.custom_width or window_width, self._report_schedule.custom_height or window_height, ) - screenshot = DashboardScreenshot( - url, - self._report_schedule.dashboard.digest, - window_size=window_size, - thumb_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], - ) + screenshots = [ + DashboardScreenshot( + url, + self._report_schedule.dashboard.digest, + window_size=window_size, + thumb_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], + ) + for url in urls + ] try: - image = screenshot.get_screenshot(user=user) + imges = [] + for screenshot in screenshots: + if imge := screenshot.get_screenshot(user=user): + imges.append(imge) except SoftTimeLimitExceeded as ex: logger.warning("A timeout occurred while taking a screenshot.") raise ReportScheduleScreenshotTimeout() from ex @@ -268,9 +334,9 @@ def _get_screenshots(self) -> list[bytes]: raise ReportScheduleScreenshotFailedError( f"Failed taking a screenshot {str(ex)}" ) from ex - if not image: + if not imges: raise ReportScheduleScreenshotFailedError() - return [image] + return imges def _get_pdf(self) -> bytes: """ @@ -412,6 +478,9 @@ def _get_notification_content(self) -> NotificationContent: error_text = "Unexpected missing screenshot" elif self._report_schedule.report_format == ReportDataFormat.PDF: pdf_data = self._get_pdf() + logger.info("Write pdf to disk +++++++ ") + with open("myjoylove.pdf", "wb") as f: + f.write(pdf_data) if not pdf_data: error_text = "Unexpected missing pdf" elif (