Skip to content

Instantly share code, notes, and snippets.

@tomwhoiscontrary
Created October 23, 2017 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomwhoiscontrary/ef888a2875fe2d2dc2002d0c2c8b7713 to your computer and use it in GitHub Desktop.
Save tomwhoiscontrary/ef888a2875fe2d2dc2002d0c2c8b7713 to your computer and use it in GitHub Desktop.
#! /usr/bin/env python
import sys
import os
import cgi
import cgimail
import random
import traceback
CTYPE_TEXT = "Content-Type: text/plain; charset=iso-8859-1"
CTYPE_HTML = "Content-Type: text/html; charset=iso-8859-1"
DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
AUTOSANTA_URL = os.environ['SCRIPT_NAME']
AUTOSANTA_EMAIL = "AutoSanta <twic-autosanta@urchin.earth.li>"
def handle_get():
print CTYPE_HTML
print
print DOCTYPE
print """<html>
<head>
<title>AutoSanta</title>
</head>
<body>
<h1>AutoSanta</h1>
<p>HO HO HO HUMAN. THIS IS THE AUTOSANTA.</p>
"""
print "<form method=\"POST\" action=\"%s\">" % AUTOSANTA_URL
print """
<input type="text" name="group" size="80"
value="ENTER THE NAME OF YOUR GROUP HERE">
<br>
<input type="text" name="organiser" size="80"
value="ENTER YOUR NAME HERE <and.email.address@like.this>">
<br>
<textarea name="members" rows=20 cols="80">
ENTER THE NAMES OF EVERYONE IN THE GROUP HERE &lt;addresses@like.this.again>
INCLUDING YOURSELF IF YOU ARE IN THE GROUP &lt;and@your.address>
NORMALLY ONE PER LINE &lt;and@one.address>
BUT IF THERE ARE PEOPLE IN A RELATIONSHIP &lt;who@shouldnt.buy> | PUT THEM ON ONE LINE SEPARATED BY A PIPE &lt;each@other.presents>
</textarea>
<br>
<input type="submit" value="ACTIVATE THE AUTOSANTA">
<input type="reset" value="RESET THE AUTOSANTA">
</form>
</body>
</html>
"""
def handle_post():
# get this done first, so any crashes are visible
print CTYPE_HTML
print
print DOCTYPE
print """<html>
<head>
<title>AutoSanta: Results</title>
</head>
<body>
<h1>AutoSanta: Results</h1>
"""
try:
form = cgi.FieldStorage()
group = form.getfirst("group")
organiser = form.getfirst("organiser")
# members is a dict<member, relationship>
members = splitlines(form.getfirst("members"))
print "<p>GROUP:", cgi.escape(group), "</p>"
print "<p>ORGANISER:", cgi.escape(organiser), "</p>"
print "<p>MEMBERS:</p>"
print "<ul>"
for member in sorted(members.keys()):
print "<li>", cgi.escape(member), "</li>"
print "</ul>"
except:
print "<p>ERROR! AUTOSANTA WAS UNABLE TO COMPUTE GROUP.</p>"
print "<pre>", cgi.escape(format_exc(3)), "</pre>"
return
try:
autosanta(group, organiser, members)
except:
print "<p>ERROR! AUTOSANTA WAS UNABLE TO NOTIFY GROUP.</p>"
print "<pre>", cgi.escape(format_exc(3)), "</pre>"
return
print "<p>AUTOSANTA HAS NOTIFIED THE GROUP OF THE ARRANGEMENTS.</p>"
print "<p>HO HO HO.</p>"
print """
</body>
</html>
"""
def handle_badmethod(method):
print "Status: 405 Method '%s' not allowed" % method
print CTYPE_TEXT
print
print "Method '%s' not allowed. Try GET or POST." % method
def autosanta(group, organiser, members):
cgimail.validate_header_body(group)
cgimail.validate_address(organiser)
for member in members:
cgimail.validate_address(member)
shuffledMembers = shuffle(members)
for santa, recipient in zip(shuffledMembers, shift(shuffledMembers, 1)):
notify(santa, recipient, group, organiser)
def shuffle(members):
shuffledMembers = members.keys()
for n in xrange(100):
random.shuffle(shuffledMembers)
if (approve(members, shuffledMembers)):
return shuffledMembers
raise Exception, "could not arrange group members satisfactorily despite trying really hard"
def approve(members, shuffledMembers):
for santa, recipient in zip(shuffledMembers, shift(shuffledMembers, 1)):
if (members[santa] == members[recipient]):
return False
return True
NOTIFY_SUBJECT = "[AutoSanta] Notification for santa group '%(group)s'"
NOTIFY_MSG = """HO HO HO,
ATTENTION HUMAN. THIS IS A MESSAGE FROM THE AUTOSANTA.
%(organiser)s HAS DESIGNATED YOU PART OF SANTA GROUP '%(group)s'.
YOUR DESIGNATED RECIPIENT IS:
%(recipient)s
MERRY CHRISTMAS,
THE AUTOSANTA
"""
def notify(santa, recipient, group, organiser):
params = {
"santa": santa,
"recipient": recipient,
"group": group,
"organiser": organiser,
}
subject = NOTIFY_SUBJECT % params
msg = NOTIFY_MSG % params
cgimail.mail(santa, msg, subject, AUTOSANTA_EMAIL, Reply_To=organiser)
if False:
def mail(to, msg, subject=None, from_=None, **headers):
print "<pre>*** mail:"
print "from:", cgi.escape(from_)
print "to:", cgi.escape(to)
print "subject:", cgi.escape(subject)
print "headers:", cgi.escape(str(headers))
print "msg:", cgi.escape(msg)
print "</pre>"
def splitlines(s):
lines = filter(bool, map(str.strip, s.replace("\r", "\n").splitlines()))
members = {}
for line in lines:
addresses = map(str.strip, line.split("|"))
for address in addresses:
members[address] = line
return members
def exc_type(): # todo: bin this
return sys.exc_info()[0]
def format_exc(limit=None):
if (hasattr(traceback, "format_exc")):
return traceback.format_exc(limit)
else:
import StringIO
buf = StringIO.StringIO()
traceback.print_exc(limit, buf)
return buf.getvalue()
def shift(seq, off):
l = len(seq)
return [seq[((i + off) % l)] for i in xrange(l)]
# the actual main code
method = os.environ["REQUEST_METHOD"].upper()
if (method == "GET"):
handle_get()
elif (method == "POST"):
handle_post()
else:
handle_badmethod(method)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment