feat: add date format to the email subject (#31413)

Co-authored-by: Steven Liu <steven.l@covergenius.com>
This commit is contained in:
Steven Liu 2025-01-29 04:33:41 +11:00 committed by GitHub
parent 962fd4cca3
commit 6478bb7eab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 72 additions and 4 deletions

View File

@ -45,6 +45,7 @@ These features are **finished** but currently being tested. They are usable, but
- CACHE_IMPERSONATION - CACHE_IMPERSONATION
- CONFIRM_DASHBOARD_DIFF - CONFIRM_DASHBOARD_DIFF
- DYNAMIC_PLUGINS - DYNAMIC_PLUGINS
- DATE_FORMAT_IN_EMAIL_SUBJECT: [(docs)](https://superset.apache.org/docs/configuration/alerts-reports#commons)
- ENABLE_SUPERSET_META_DB: [(docs)](https://superset.apache.org/docs/configuration/databases/#querying-across-databases) - ENABLE_SUPERSET_META_DB: [(docs)](https://superset.apache.org/docs/configuration/databases/#querying-across-databases)
- ESTIMATE_QUERY_COST - ESTIMATE_QUERY_COST
- GLOBAL_ASYNC_QUERIES [(docs)](https://github.com/apache/superset/blob/master/CONTRIBUTING.md#async-chart-queries) - GLOBAL_ASYNC_QUERIES [(docs)](https://github.com/apache/superset/blob/master/CONTRIBUTING.md#async-chart-queries)

View File

@ -37,6 +37,7 @@ assists people when migrating to a new version.
- [30099](https://github.com/apache/superset/pull/30099) Translations are no longer included in the default docker image builds. If your environment requires translations, you'll want to set the docker build arg `BUILD_TRANSACTION=true`. - [30099](https://github.com/apache/superset/pull/30099) Translations are no longer included in the default docker image builds. If your environment requires translations, you'll want to set the docker build arg `BUILD_TRANSACTION=true`.
- [31262](https://github.com/apache/superset/pull/31262) NOTE: deprecated `pylint` in favor of `ruff` as our only python linter. Only affect development workflows positively (not the release itself). It should cover most important rules, be much faster, but some things linting rules that were enforced before may not be enforce in the exact same way as before. - [31262](https://github.com/apache/superset/pull/31262) NOTE: deprecated `pylint` in favor of `ruff` as our only python linter. Only affect development workflows positively (not the release itself). It should cover most important rules, be much faster, but some things linting rules that were enforced before may not be enforce in the exact same way as before.
- [31173](https://github.com/apache/superset/pull/31173) Modified `fetch_csrf_token` to align with HTTP standards, particularly regarding how cookies are handled. If you encounter any issues related to CSRF functionality, please report them as a new issue and reference this PR for context. - [31173](https://github.com/apache/superset/pull/31173) Modified `fetch_csrf_token` to align with HTTP standards, particularly regarding how cookies are handled. If you encounter any issues related to CSRF functionality, please report them as a new issue and reference this PR for context.
- [31413](https://github.com/apache/superset/pull/31413) Enable the DATE_FORMAT_IN_EMAIL_SUBJECT feature flag to allow users to specify a date format for the email subject, which will then be replaced with the actual date.
- [31385](https://github.com/apache/superset/pull/31385) Significant docker refactor, reducing access levels for the `superset` user, streamlining layer building, ... - [31385](https://github.com/apache/superset/pull/31385) Significant docker refactor, reducing access levels for the `superset` user, streamlining layer building, ...
- [31503](https://github.com/apache/superset/pull/31503) Deprecating python 3.9.x support, 3.11 is now the recommended version and 3.10 is still supported over the Superset 5.0 lifecycle. - [31503](https://github.com/apache/superset/pull/31503) Deprecating python 3.9.x support, 3.11 is now the recommended version and 3.10 is still supported over the Superset 5.0 lifecycle.
- [29121](https://github.com/apache/superset/pull/29121) Removed the `css`, `position_json`, and `json_metadata` from the payload of the dashboard list endpoint (`GET api/v1/dashboard`) for performance reasons. - [29121](https://github.com/apache/superset/pull/29121) Removed the `css`, `position_json`, and `json_metadata` from the payload of the dashboard list endpoint (`GET api/v1/dashboard`) for performance reasons.

View File

@ -25,6 +25,9 @@ Alerts and reports are disabled by default. To turn them on, you need to do some
- At least one of those must be configured, depending on what you want to use: - At least one of those must be configured, depending on what you want to use:
- emails: `SMTP_*` settings - emails: `SMTP_*` settings
- Slack messages: `SLACK_API_TOKEN` - Slack messages: `SLACK_API_TOKEN`
- Users can customize the email subject by including date code placeholders, which will automatically be replaced with the corresponding UTC date when the email is sent. To enable this functionality, activate the `"DATE_FORMAT_IN_EMAIL_SUBJECT"` [feature flag](/docs/configuration/configuring-superset#feature-flags). This enables date formatting in email subjects, preventing all reporting emails from being grouped into the same thread (optional for the reporting feature).
- Use date codes from [strftime.org](https://strftime.org/) to create the email subject.
- If no date code is provided, the original string will be used as the email subject.
##### Disable dry-run mode ##### Disable dry-run mode

View File

@ -559,6 +559,8 @@ DEFAULT_FEATURE_FLAGS: dict[str, bool] = {
# If on, you'll want to add "https://avatars.slack-edge.com" to the list of allowed # If on, you'll want to add "https://avatars.slack-edge.com" to the list of allowed
# domains in your TALISMAN_CONFIG # domains in your TALISMAN_CONFIG
"SLACK_ENABLE_AVATARS": False, "SLACK_ENABLE_AVATARS": False,
# Allow users to optionally specify date formats in email subjects, which will be parsed if enabled. # noqa: E501
"DATE_FORMAT_IN_EMAIL_SUBJECT": False,
} }
# ------------------------------ # ------------------------------

View File

@ -17,13 +17,15 @@
import logging import logging
import textwrap import textwrap
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime
from email.utils import make_msgid, parseaddr from email.utils import make_msgid, parseaddr
from typing import Any, Optional from typing import Any, Optional
import nh3 import nh3
from flask_babel import gettext as __ from flask_babel import gettext as __
from pytz import timezone
from superset import app from superset import app, is_feature_enabled
from superset.exceptions import SupersetErrorsException from superset.exceptions import SupersetErrorsException
from superset.reports.models import ReportRecipientType from superset.reports.models import ReportRecipientType
from superset.reports.notifications.base import BaseNotification from superset.reports.notifications.base import BaseNotification
@ -79,6 +81,16 @@ class EmailNotification(BaseNotification): # pylint: disable=too-few-public-met
""" """
type = ReportRecipientType.EMAIL type = ReportRecipientType.EMAIL
now = datetime.now(timezone("UTC"))
@property
def _name(self) -> str:
"""Include date format in the name if feature flag is enabled"""
return (
self._parse_name(self._content.name)
if is_feature_enabled("DATE_FORMAT_IN_EMAIL_SUBJECT")
else self._content.name
)
@staticmethod @staticmethod
def _get_smtp_domain() -> str: def _get_smtp_domain() -> str:
@ -173,11 +185,11 @@ class EmailNotification(BaseNotification): # pylint: disable=too-few-public-met
) )
csv_data = None csv_data = None
if self._content.csv: if self._content.csv:
csv_data = {__("%(name)s.csv", name=self._content.name): self._content.csv} csv_data = {__("%(name)s.csv", name=self._name): self._content.csv}
pdf_data = None pdf_data = None
if self._content.pdf: if self._content.pdf:
pdf_data = {__("%(name)s.pdf", name=self._content.name): self._content.pdf} pdf_data = {__("%(name)s.pdf", name=self._name): self._content.pdf}
return EmailContent( return EmailContent(
body=body, body=body,
@ -191,9 +203,16 @@ class EmailNotification(BaseNotification): # pylint: disable=too-few-public-met
return __( return __(
"%(prefix)s %(title)s", "%(prefix)s %(title)s",
prefix=app.config["EMAIL_REPORTS_SUBJECT_PREFIX"], prefix=app.config["EMAIL_REPORTS_SUBJECT_PREFIX"],
title=self._content.name, title=self._name,
) )
def _parse_name(self, name: str) -> str:
"""If user add a date format to the subject, parse it to the real date
This feature is hidden behind a feature flag `DATE_FORMAT_IN_EMAIL_SUBJECT`
by default it is disabled
"""
return self.now.strftime(name)
def _get_call_to_action(self) -> str: def _get_call_to_action(self) -> str:
return __(app.config["EMAIL_REPORTS_CTA"]) return __(app.config["EMAIL_REPORTS_CTA"])

View File

@ -14,7 +14,12 @@
# KIND, either express or implied. See the License for the # KIND, either express or implied. See the License for the
# specific language governing permissions and limitations # specific language governing permissions and limitations
# under the License. # under the License.
from datetime import datetime
import pandas as pd import pandas as pd
from pytz import timezone
from tests.unit_tests.conftest import with_feature_flags
def test_render_description_with_html() -> None: def test_render_description_with_html() -> None:
@ -56,3 +61,40 @@ def test_render_description_with_html() -> None:
in email_body in email_body
) )
assert '<td>&lt;a href="http://www.example.com"&gt;333&lt;/a&gt;</td>' in email_body assert '<td>&lt;a href="http://www.example.com"&gt;333&lt;/a&gt;</td>' in email_body
@with_feature_flags(DATE_FORMAT_IN_EMAIL_SUBJECT=True)
def test_email_subject_with_datetime() -> None:
# `superset.models.helpers`, a dependency of following imports,
# requires app context
from superset.reports.models import ReportRecipients, ReportRecipientType
from superset.reports.notifications.base import NotificationContent
from superset.reports.notifications.email import EmailNotification
now = datetime.now(timezone("UTC"))
datetime_pattern = "%Y-%m-%d"
content = NotificationContent(
name=f"test alert {datetime_pattern}",
embedded_data=pd.DataFrame(
{
"A": [1, 2, 3],
}
),
description='<p>This is <a href="#">a test</a> alert</p><br />',
header_data={
"notification_format": "PNG",
"notification_type": "Alert",
"owners": [1],
"notification_source": None,
"chart_id": None,
"dashboard_id": None,
"slack_channels": None,
},
)
subject = EmailNotification(
recipient=ReportRecipients(type=ReportRecipientType.EMAIL), content=content
)._get_subject()
assert datetime_pattern not in subject
assert now.strftime(datetime_pattern) in subject