FME Python Shutdown Email Script

Here is a Python script that is used in FME Desktop as a shutdown script to send an email on success or failure of the workbench. This is currently running in FME 2015.1.0.2 on Windows Server 2012 and uses Microsoft Exchange to send mail.

You need to set up a mail config file first with your email details:

# Email credentials
emailServer = "mail.example.com"
emailPort = 25
emailFrom = "FME <geoprocessing@example.com>"
emailUser = "username" # if your mail server needs
emailPass = "password" # authentication credentials

Save this in a file - email_config.py - somewhere on your server. This is loaded in the next script and means you can reuse the details in all your workbenches and not expose your email credentials in the workbench.

The script below gets the status - SUCCESS or FAILED - of the FME process and composes an appropriate email message based on the results. A successful process sends a simple email to the specified recipients. If the process fails then the script adjusts the email subject and message content. It then parses the log file looking for the lines with ERROR and STATS and writes them to an error log which is then attached to the email and sent to the specified recipients.

# Import the stuff we need for sending email, getting FME bits and using regular expressions
import smtplib, fme, fmeobjects
from smtplib import SMTP
from email.MIMEMultipart import MIMEMultipart 
from email.MIMEBase import MIMEBase 
from email.MIMEText import MIMEText 
from email.mime.application import MIMEApplication
from email.Utils import COMMASPACE, formatdate 
from email import Encoders
import string
import re
import os

# Get some stats and the number of features FME processed
FeaturesWritten = str(fme.featuresWritten)
FeaturesRead = str(fme.featuresRead)
ElapsedRunTime = str(round(fme.elapsedRunTime))
LogFile = str(fme.logFileName)

 # get Angus's standard email variables
execfile('path/to/your/email_config.py')

# Set the recipients
emailTo = "someone@example.com"
emailCC = "reiver@cattlerustling.org"

# Set the name(s) of the source datasets
# workbench specific - adjust as required
# might be worth setting User parameters to get the name rather than the path
sourceDataset = fme.macroValues['SourceDataset_ESRIASCIIGRID'].replace('"','')
destDataset1 = fme.macroValues['DestDataset_SHAPE'].replace('"','')
destDataset2 = fme.macroValues['DestDataset_GEOTIFF'].replace('"','')
#destDatasetName = fme.macroValues['destDatasetName'].replace('"','')
#destMDBDatasetName = fme.macroValues['destMDBDatasetName'].replace('"','')

# Set the default subject line
emailSubject = "SUCCESSFULLY wrote " + destDataset1 + " and " + destDataset2

# If FME Fails then change the subject
# fme.status tells us if FME was successful or not
# emailMessage can be tweaked per workbench if we want
status = fme.status
if status == 0:
    emailSubject = "FAILED to write " + sourceDataset
    emailMessage = 'Workspace FAILED\n\n' + fme.failureMessage + '\n\n'
    emailMessage = emailMessage + 'Log file: ' + LogFile
else:
    emailMessage = 'Workspace SUCCESSFUL\n\n'
    emailMessage = emailMessage + 'Shapefile Dataset: ' + destDataset1 + '\n\n'
    emailMessage = emailMessage + 'GeoTIFF Dataset: ' + destDataset2 + '\n\n'
    emailMessage = emailMessage + 'Log file: ' + LogFile + '\n\n'
    emailMessage = emailMessage + 'Time: ' + ElapsedRunTime + ' seconds\n\n'
    emailMessage = emailMessage + 'The following features were read:\n' + FeaturesRead + '\n\n'
    emailMessage = emailMessage + 'The following features were written:\n' + FeaturesWritten

# Let's build the email message
msg = MIMEMultipart()
msg['Subject'] = emailSubject
msg['From'] = emailFrom
msg['Reply-to'] = emailTo
msg['To'] = emailTo
msg['CC'] = emailCC

# That is what you see if dont have an email reader:
msg.preamble = 'This is a multipart message.\n'

# This is the textual part:
part = MIMEText(emailMessage)
msg.attach(part)

# only attach the log file if the process fails
if status == 0:
    # Set the attachment
    attachment = fme.logFileName

    # This is the binary part (the attachment):
    with open(attachment,'rb') as f:
         with open("errorlog.txt",'wb') as g:
             for line in f:
                 if re.match("(.*)ERROR (.*)",line) or re.match("(.*)STATS (.*)",line):
                    g.write(line)
    part = MIMEApplication(open("errorlog.txt","rb").read())
    part.add_header('Content-Disposition', 'errorlog', filename="log.txt")
    msg.attach(part)

# Create an instance in SMTP server
smtpserver = smtplib.SMTP(emailServer,emailPort)

# Start the server:
smtpserver.ehlo()
smtpserver.login(emailUser,emailPass)

# Send the email
smtpserver.sendmail(emailFrom, [emailTo, emailCC], msg.as_string())
smtpserver.close()

You can also do all the usual awesome stuff with FME like write out the features giving you grief to a spreadsheet and attach that. And even more could be done with FME User Parameters to make this script more flexible.

Note: if you copy and paste this code then make sure you get the Python indentation correct. And change all the relevant bits to suit your environment.

References

Thanks to Duncan Beaton for the starter script
1. http://fmepedia.safe.com/articles/How_To/Python-and-FME-Basics
2. http://docs.safe.com/fme/html/FME_Workbench/FME_Workbench.htm#Workbench/Startup_and_Shutdown_Python_Scripts.htm
3. http://docs.safe.com/fme/html/FME_Workbench/FME_Workbench.htm#Configuration/FME_END_PYTHON.htm
4. https://codeadict.wordpress.com/2010/02/11/send-e-mails-with-attachment-in-python/
5. http://stackoverflow.com/questions/8856117/how-to-send-email-to-multiple-recipients-using-python-smtplib