mirror of
https://github.com/apache/superset.git
synced 2026-04-19 16:14:52 +00:00
feat(alert/reports): adding logic to handle downstream reports when tab is deleted from dashboard (#29333)
This commit is contained in:
@@ -15,13 +15,14 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import logging
|
||||
import textwrap
|
||||
from functools import partial
|
||||
from typing import Any, Optional
|
||||
|
||||
from flask_appbuilder.models.sqla import Model
|
||||
from marshmallow import ValidationError
|
||||
|
||||
from superset import security_manager
|
||||
from superset import app, security_manager
|
||||
from superset.commands.base import BaseCommand, UpdateMixin
|
||||
from superset.commands.dashboard.exceptions import (
|
||||
DashboardForbiddenError,
|
||||
@@ -32,10 +33,13 @@ from superset.commands.dashboard.exceptions import (
|
||||
)
|
||||
from superset.commands.utils import populate_roles, update_tags, validate_tags
|
||||
from superset.daos.dashboard import DashboardDAO
|
||||
from superset.daos.report import ReportScheduleDAO
|
||||
from superset.exceptions import SupersetSecurityException
|
||||
from superset.models.dashboard import Dashboard
|
||||
from superset.reports.models import ReportSchedule
|
||||
from superset.tags.models import ObjectType
|
||||
from superset.utils import json
|
||||
from superset.utils.core import send_email_smtp
|
||||
from superset.utils.decorators import on_error, transaction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -50,7 +54,8 @@ class UpdateDashboardCommand(UpdateMixin, BaseCommand):
|
||||
@transaction(on_error=partial(on_error, reraise=DashboardUpdateFailedError))
|
||||
def run(self) -> Model:
|
||||
self.validate()
|
||||
assert self._model
|
||||
assert self._model is not None
|
||||
self.process_tab_diff()
|
||||
|
||||
# Update tags
|
||||
if (tags := self._properties.pop("tags", None)) is not None:
|
||||
@@ -112,3 +117,73 @@ class UpdateDashboardCommand(UpdateMixin, BaseCommand):
|
||||
exceptions.append(ex)
|
||||
if exceptions:
|
||||
raise DashboardInvalidError(exceptions=exceptions)
|
||||
|
||||
def process_tab_diff(self) -> None:
|
||||
def find_deleted_tabs() -> list[str]:
|
||||
position_json = self._properties.get("position_json", "")
|
||||
current_tabs = self._model.tabs # type: ignore
|
||||
if position_json and current_tabs:
|
||||
position = json.loads(position_json)
|
||||
deleted_tabs = [
|
||||
tab for tab in current_tabs["all_tabs"] if tab not in position
|
||||
]
|
||||
return deleted_tabs
|
||||
return []
|
||||
|
||||
def find_reports_containing_tabs(tabs: list[str]) -> list[ReportSchedule]:
|
||||
alert_reports_list = []
|
||||
for tab in tabs:
|
||||
for report in ReportScheduleDAO.find_by_extra_metadata(tab):
|
||||
alert_reports_list.append(report)
|
||||
return alert_reports_list
|
||||
|
||||
def send_deactivated_email_warning(report: ReportSchedule) -> None:
|
||||
description = textwrap.dedent(
|
||||
"""
|
||||
The dashboard tab used in this report has been deleted and your report has been deactivated.
|
||||
Please update your report settings to remove or change the tab used.
|
||||
"""
|
||||
)
|
||||
|
||||
html_content = textwrap.dedent(
|
||||
f"""
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
table, th, td {{
|
||||
border-collapse: collapse;
|
||||
border-color: rgb(200, 212, 227);
|
||||
color: rgb(42, 63, 95);
|
||||
padding: 4px 8px;
|
||||
}}
|
||||
.image{{
|
||||
margin-bottom: 18px;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>{description}</div>
|
||||
<br>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
)
|
||||
for report_owner in report.owners:
|
||||
if email := report_owner.email:
|
||||
send_email_smtp(
|
||||
to=email,
|
||||
subject=f"[Report: {report.name}] Deactivated",
|
||||
html_content=html_content,
|
||||
config=app.config,
|
||||
)
|
||||
|
||||
def deactivate_reports(reports_list: list[ReportSchedule]) -> None:
|
||||
for report in reports_list:
|
||||
# deactivate
|
||||
ReportScheduleDAO.update(report, {"active": False})
|
||||
# send email to report owner
|
||||
send_deactivated_email_warning(report)
|
||||
|
||||
deleted_tabs = find_deleted_tabs()
|
||||
reports = find_reports_containing_tabs(deleted_tabs)
|
||||
deactivate_reports(reports)
|
||||
|
||||
Reference in New Issue
Block a user