= ({
)}
+ {isReport &&
+ contentType === ContentType.Chart &&
+ reportFormat === 'CSV' && (
+
+
+ {t('Remove index column')}
+
+
+ )}
bytes:
def _get_csv_data(self) -> bytes:
url = self._get_url(result_format=ChartDataResultFormat.CSV)
+ if self._report_schedule.remove_index:
+ url = remove_post_processed(url)
_, username = get_executor(
executors=app.config["ALERT_REPORTS_EXECUTORS"],
model=self._report_schedule,
diff --git a/superset/commands/report/utils.py b/superset/commands/report/utils.py
new file mode 100644
index 0000000000000..779ac8efb8297
--- /dev/null
+++ b/superset/commands/report/utils.py
@@ -0,0 +1,32 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+def remove_post_processed(url: str) -> str:
+ """Remove the type=post_processed parameter from the URL query string.
+ Args:
+ url (str): The URL to process.
+ Returns:
+ str: The URL with the type=post_processed parameter removed."""
+ if "?" not in url:
+ return url
+ base_url, query_string = url.split("?", 1)
+ params = query_string.split("&")
+ filtered_params = [param for param in params if param != "type=post_processed"]
+ filtered_query_string = "&".join(filtered_params)
+ filtered_url = f"{base_url}?{filtered_query_string}"
+ return filtered_url
diff --git a/superset/migrations/versions/2025-02-24_17-52_3b86f563edbc_add_remove_index_to_report_schedule.py b/superset/migrations/versions/2025-02-24_17-52_3b86f563edbc_add_remove_index_to_report_schedule.py
new file mode 100644
index 0000000000000..270716ab42a32
--- /dev/null
+++ b/superset/migrations/versions/2025-02-24_17-52_3b86f563edbc_add_remove_index_to_report_schedule.py
@@ -0,0 +1,41 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""add_remove_index_to_report_schedule
+
+Revision ID: 3b86f563edbc
+Revises: 74ad1125881c
+Create Date: 2025-02-24 17:52:02.369467
+
+"""
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = "3b86f563edbc"
+down_revision = "74ad1125881c"
+
+
+def upgrade():
+ op.add_column(
+ "report_schedule",
+ sa.Column("remove_index", sa.Boolean(), default=False),
+ )
+
+
+def downgrade():
+ op.drop_column("report_schedule", "remove_index")
diff --git a/superset/reports/api.py b/superset/reports/api.py
index 320eb97c05133..1cdee1e5986f3 100644
--- a/superset/reports/api.py
+++ b/superset/reports/api.py
@@ -116,6 +116,7 @@ def ensure_alert_reports_enabled(self) -> Optional[Response]:
"recipients.id",
"recipients.recipient_config_json",
"recipients.type",
+ "remove_index",
"report_format",
"sql",
"timezone",
@@ -175,6 +176,7 @@ def ensure_alert_reports_enabled(self) -> Optional[Response]:
"owners",
"recipients",
"report_format",
+ "remove_index",
"sql",
"timezone",
"type",
diff --git a/superset/reports/models.py b/superset/reports/models.py
index e4cdd7c9b4d0e..cb51fc9a74e15 100644
--- a/superset/reports/models.py
+++ b/superset/reports/models.py
@@ -169,6 +169,9 @@ class ReportSchedule(AuditMixinNullable, ExtraJSONMixin, Model):
# (Reports) When generating a screenshot, bypass the cache?
force_screenshot = Column(Boolean, default=False)
+ # (Reports) Remove index column in report
+ remove_index = Column(Boolean, default=False)
+
custom_width = Column(Integer, nullable=True)
custom_height = Column(Integer, nullable=True)
diff --git a/superset/reports/schemas.py b/superset/reports/schemas.py
index 7078970b3815b..574fe5bd1c1d4 100644
--- a/superset/reports/schemas.py
+++ b/superset/reports/schemas.py
@@ -233,6 +233,13 @@ class ReportSchedulePostSchema(Schema):
dump_default=None,
)
force_screenshot = fields.Boolean(dump_default=False)
+ remove_index = fields.Boolean(
+ metadata={
+ "description": _("Remove index column in report"),
+ "example": False,
+ },
+ dump_default=False,
+ )
custom_width = fields.Integer(
metadata={
"description": _("Custom width of the screenshot in pixels"),
@@ -370,7 +377,14 @@ class ReportSchedulePutSchema(Schema):
)
extra = fields.Dict(dump_default=None)
force_screenshot = fields.Boolean(dump_default=False)
-
+ remove_index = fields.Boolean(
+ metadata={
+ "description": _("Remove index column in report"),
+ "example": False,
+ },
+ dump_default=False,
+ required=False,
+ )
custom_width = fields.Integer(
metadata={
"description": _("Custom width of the screenshot in pixels"),
diff --git a/tests/unit_tests/commands/report/test_report_utils.py b/tests/unit_tests/commands/report/test_report_utils.py
new file mode 100644
index 0000000000000..1313af047ca77
--- /dev/null
+++ b/tests/unit_tests/commands/report/test_report_utils.py
@@ -0,0 +1,48 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from superset.commands.report.utils import remove_post_processed
+
+
+def test_remove_post_processed():
+ url = "https://superset.com/?param1=value1&type=post_processed¶m2=value2"
+ expected = "https://superset.com/?param1=value1¶m2=value2"
+ assert remove_post_processed(url) == expected
+
+
+def test_retain_other_parameters():
+ url = "https://superset.com/?param1=value1¶m2=value2"
+ expected = "https://superset.com/?param1=value1¶m2=value2"
+ assert remove_post_processed(url) == expected
+
+
+def test_no_post_processed_present():
+ url = "https://superset.com/?param1=value1¶m2=value2"
+ expected = "https://superset.com/?param1=value1¶m2=value2"
+ assert remove_post_processed(url) == expected
+
+
+def test_empty_query_string():
+ url = "https://superset.com/?"
+ expected = "https://superset.com/?"
+ assert remove_post_processed(url) == expected
+
+
+def test_no_query_string():
+ url = "https://superset.com"
+ expected = "https://superset.com"
+ assert remove_post_processed(url) == expected