Created
October 23, 2017 12:19
-
-
Save tomwhoiscontrary/ef888a2875fe2d2dc2002d0c2c8b7713 to your computer and use it in GitHub Desktop.
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
#! /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 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 <addresses@like.this.again> | |
INCLUDING YOURSELF IF YOU ARE IN THE GROUP <and@your.address> | |
NORMALLY ONE PER LINE <and@one.address> | |
BUT IF THERE ARE PEOPLE IN A RELATIONSHIP <who@shouldnt.buy> | PUT THEM ON ONE LINE SEPARATED BY A PIPE <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 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 "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