Email Results of a Flow
Question
How can I make an email alert from a step in my Metaflow flow?
Solution
You can format a human-readable report as a Metaflow Card. You can then send the card, which is a simple HTML file, as an email attachment.
1Get Postmark API Token
There is nothing specific to Postmark in this guide. Sendgrid, or any other provider, can readily be swapped as an alternative.
Before using the code in this example to send emails from your Python code, you need to set up a Postmark server. You can set this as an environment variable:
export PM_SERVER_API_TOKEN=<YOUR TOKEN>
2Use Postmark to Send Email
The following code snippet will be called from the flow. It uses Postmark to send an email with HTML attached. The HTML will contain the contents of a Metaflow card, a quick way to visualize flow artifacts in HTML.
You can replace this send_email
function with another one that uses an email provider of your choice.
import os
import requests
import base64
def send_email(from_email, to_email, subject, html):
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"X-Postmark-Server-Token": os.environ.get('PM_SERVER_API_TOKEN')
}
payload = {
"From": from_email,
"To": to_email,
"Subject": subject,
"HtmlBody": html,
"Attachments": [
{
"Name": "card.html",
"Content": base64.b64encode(html.encode('utf-8')).decode('utf-8'),
"ContentType": "text/html"
}
]
}
response = requests.post("https://api.postmarkapp.com/email", json=payload, headers=headers)
if response.status_code == 200:
print("Email sent successfully!")
else:
print("Failed to send email:", response.json())
3Run Flow
This flow shows how to:
- Pull an image with an internet request.
- Store the image in a Metaflow card.
- Access the card and attach it to an email.
- Pull an image with an HTTP request to show how you can include images in the card. The
@card
decorator packages them in the HTML file, so the recipient of the email will be able to see them too.
import os
from metaflow import FlowSpec, step, current, card, Flow
from metaflow.cards import Markdown, Image, get_cards
CAT = 'https://upload.wikimedia.org' + \
'/wikipedia/commons/b/b9/CyprusShorthair.jpg'
class EmailCardFlow(FlowSpec):
@card(type='blank')
@step
def start(self):
import requests
resp = requests.get(CAT, headers = {'user-agent': 'metaflow-example'})
current.card.append(Markdown("# Meow mail 🐈"))
current.card.append(Image(resp.content))
self.next(self.end)
@step
def end(self):
from emailer import send_email
send_email(
'eddie@outerbounds.co', # put your email
'eddie@outerbounds.co', # put receiver's email
f'Card from {current.flow_name}/{current.run_id}', # message body
get_cards(
Flow(current.flow_name)[current.run_id]['start'].task
)[0].get()
)
if __name__ == '__main__':
EmailCardFlow()
python send_email_from_flow.py run
2Visualize Artifact
You can use the following command to look at your card and verify the email sent the same in an HTML attachment.
python send_email_from_flow.py card view start