Last active
February 22, 2016 18:56
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import smtplib | |
from email.mime.text import MIMEText | |
import time | |
from time import strftime | |
import traceback | |
__author__ = "Henry Lin <[email protected]>" | |
__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("[email protected]", 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 | |
[email protected], 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