Resolves betodealmeida's review comments on PR #41133:
- i18n: wrap all user-facing email strings in gettext with named
placeholders (build_subject / errored section / success + failure bodies).
- Human-readable link expiry via a pluralized, translatable helper, fixing
the sub-hour "0 hours" underreporting.
- Group charts that could not be exported by reason
(no-query-context / timeout / general-exception) instead of a flat
"skipped" list; render one labelled section per reason in the email.
- Replace deprecated datetime.utcnow() with datetime.now(tz=timezone.utc).
- Throttle concurrent exports per user+dashboard via a cache lock: return
202 "already in progress" if one is in flight, clear the lock in the
task's finally block (TTL is the backstop).
- Tests: switch the export API tests to the @with_config decorator; add
unit coverage for grouped errors and the throttle-lock cleanup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the async task that exports a dashboard's chart data to a multi-sheet xlsx.
Running as the requesting user, it walks charts in layout order, re-runs each
saved query context with the live filter state applied, streams results into a
constant-memory workbook, uploads to S3, and emails a pre-signed link. Charts
with no/invalid query context or a failing query are skipped and listed in the
email; all-skipped dashboards get a summary sheet. On failure (incl. soft
timeout) it emails the user and always cleans up the temp file. Registers the
task in CeleryConfig.imports. Adds unit tests for happy path, skips, summary,
upload failure, and timeout.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>