Share an HTML report from Prefect
Prefect makes the orchestration part pleasant: the flow is Python, the retries
are declarative, the run is observable. Then a task writes report.html inside
a container from an ephemeral work pool, and the last mile is the same one
every pipeline has.
- The container is temporary. The file is gone with the infrastructure that ran the flow, usually within minutes.
- Artifacts are summaries. Markdown, tables and links attached to a run — useful, and not the place for a charted document.
- The UI is behind a login. Prefect Cloud or a self-hosted server, either way scoped to the people who operate pipelines rather than the people who read their output.
- A bucket relocates the problem. Writing to S3 means the reader now needs bucket credentials to see a report.
Publish from the flow
import json, os, urllib.request
from prefect import flow
from prefect.artifacts import create_link_artifact
REPORT_ID = "rep_…" # created once; PATCHed from every run
@flow
def daily_quality_report():
html = build_report_html()
body = json.dumps({"title": "Daily data quality", "html": html}).encode()
req = urllib.request.Request(
f"https://commareports.com/api/v1/reports/{REPORT_ID}",
data=body,
method="PATCH",
headers={
"Authorization": f"Bearer {os.environ['COMMA_API_TOKEN']}",
"Content-Type": "application/json",
},
)
urllib.request.urlopen(req).read()
create_link_artifact(
key="quality-report",
link=f"https://commareports.com/p/{REPORT_ID}",
description="Rendered report",
)
The link artifact is the part that makes this discoverable: the run page in Prefect now points at the document, and the document is readable by people who have never seen a Prefect run page.
Three details that matter:
PATCHa saved id, don'tPOST. POSTing every run scatters orphan links; one id gives the flow a permanent URL and a revision per run.- Publish unconditionally. An
on_failurehook on the flow publishes the digest for the run that broke, which is the one somebody is asking about. - Scope the token to
reports:writeand keep it in a Secret block — see API tokens.
What the reader gets
- A URL and nothing to install. No Prefect account, no bucket credentials.
- The document rendered faithfully — a Plotly figure stays interactive, a pandas table stays a table. See sharing a Plotly chart and sharing a pandas DataFrame.
- Comments anchored to the failing row — see commenting on HTML.
- A revision history answering "when did this start drifting", without anyone keeping dated copies in a folder.
Worth knowing
- Subflows and mapped tasks. Publish once from the parent rather than once per mapped run; the rate limit is 60 requests/minute per token, and 300 separate links help nobody.
- HTML body: 5 MB. Aggregate inline, extract attached.
- Multi-file output needs the folder treatment — see sharing an HTML folder.
- Nothing re-executes on Comma. A published report is a snapshot; if you want a refresh without a scheduler at all, that is a routine.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.
Related
- Publish from CI — the general pipeline pattern
- Share an Airflow report · Share a Dagster asset report
- Share an EDA report · Scheduled HTML reports