Skip to content

Instantly share code, notes, and snippets.

@hlin117
Last active February 22, 2016 18:56
Show Gist options
  • Save hlin117/6a91406e56f0d56e1c6a to your computer and use it in GitHub Desktop.
Save hlin117/6a91406e56f0d56e1c6a to your computer and use it in GitHub Desktop.
Email notification script: Emails you when your job has finished running. Read the docstring for how to use.
import smtplib
from email.mime.text import MIMEText
import time
from time import strftime
import traceback
__author__ = "Henry Lin <hlin117@gmail.com>"
__version__ = "0.1"
class Notification(object):
"""A useful module for those poor researchers who struggle to find
a work life balance.
The module helps to notify you when a set of lines have finished
being run. By setting up a fake email account (whose password you don't
mind hardcoded), you could notify yourself when your code is finished
running. Furthermore, this class catches exceptions, and notifies you
when your code has failed.
Extra information
-----------------
The original idea was to have SMS messages get forwarded to your
phone instead of emails. But SMS automatic messaging services seem
to cost money. Though there is an API for OS X's "Chirp" app,
this doesn't work for Linux.
This code was haphazardly hacked together. Sorry if it is pretty
unmaintainable.
Parameters
----------
receiver : str
The email to notify upon completion.
sendername : str, default="My notification bot"
Refers to the name from whom you receive your notification from.
Examples
--------
>>> from notification import Notification
>>> import time
>>> notify = Notification("hlin117@gmail.com", sendername="Henry's bot")
>>> with notify:
>>> time.sleep(10)
>>> notify.send_email("The first 10 seconds finished without failing")
>>> time.sleep(5)
You would then receive three consecutive emails of when your code
has started running, and when it completed. These emails are sent from
linsanity1115@gmail.com, which is my own dummy email account.
In my case, these were my three emails:
1.
Sender name: "Henry's bot"
Content:
Code is starting: Feb 12, 2016 at 17:02:41
2.
Sender name: "Henry's bot"
Content:
The first 10 seconds finished without failing
3.
Sender name: "Henry's bot"
Content:
Success!
Took 0 minutes and 19 seconds.
Todo
----
* Set up options for different "dummy" email accounts
* Email server options (port numbers, email servers, etc)
* Allow for different email titles. Currently defaults to
"Notification number i", where i is the number of times you've used
this module. (Starting at 1.)
* Calculate the number of hours it takes code to run
* Ping the user every hour (or some time interval) a notification that
the code of interest is still running. This would be useful when
running extremely large jobs (over many hours). You want to know your
program is still alive after long periods of time.
Resources:
----------
http://effbot.org/zone/python-with-statement.htm
http://docs.quantifiedcode.com/python-code-patterns/correctness/exit_must_accept_three_arguments.html
http://stackoverflow.com/a/22417454/2014591
https://docs.python.org/2/library/email-examples.html
"""
# Note that this notification number resets to 1 every time you
# reload this module.
# This is to change the title of the notification email.
notification_number = 1
def __init__(self, receiver, sendername="My notification bot"):
# Email channeling setup
self.emailaddr = None
self.password = None
if not self.emailaddr:
raise ValueError("Make sure to set from which email you're notifying yourself with.")
self.receiver = receiver
self.sendername = sendername
self.serveraddr = 'smtp.gmail.com'
self.serverport = 587
# Email body setup
self.start = None
self.end = None
self.subject = "Notification number " + str(self.notification_number)
self.formatstring = "%h %d, %Y at %H:%M:%S"
def __enter__(self):
"""Callback taken when the code has started.
"""
self.start = time.time()
datetime = strftime(self.formatstring)
self.send_email("Code is starting: " + datetime)
Notification.notification_number += 1
return self
def __exit__(self, except_type, except_value, except_traceback):
"""Callback taken after calling the user's code. Notifies the user
whether or not the code has errored. Will raise an exception if the
user's code errored.
"""
self.end = time.time()
elapsed_time = int(self.end - self.start)
minutes = elapsed_time / 60
seconds = elapsed_time % 60
timestring = "Took {0} minutes and {1} seconds.".format(minutes, seconds)
if except_type is None:
message = "Success!\n\n" + timestring
else:
message = "Failed with the following message:\n\n"
traceback_str = traceback.format_exception(except_type, except_value, except_traceback)
traceback_str = "\n".join(traceback_str)
# Change indents from two space to four space.
traceback_str = traceback_str.replace(" ", " ")
message += traceback_str
message += "\n" + timestring
self.send_email(message)
return (except_type is None)
def send_email(self, content):
"""Sends an email to the notified recipiant
"""
msg = MIMEText(content)
msg['Subject'] = self.subject
msg['From'] = self.sendername
msg['To'] = self.receiver
# Send the message via an SMTP server
server = smtplib.SMTP(self.serveraddr + ":" + str(self.serverport))
server.starttls()
server.login(self.emailaddr, self.password)
server.sendmail(self.emailaddr, [self.receiver], msg.as_string())
server.quit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment