Last active
November 18, 2019 20:32
-
-
Save herdingbats/095c21767e93aabb7daf9e86f50b11e2 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
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"collapsed": true | |
}, | |
"source": [ | |
"## Secret Santa drawing (or, TDD to the maxx)\n", | |
"\n", | |
"Like test-driven development? \n", | |
"\n", | |
"How about just having a test and the programming is just random until it passes? \n", | |
"\n", | |
"Here's the set-up. We have a family Christmas drawing (for the adults, kids are no-limit gift recipients!) that involves four couples. There are four conditions that the drawing algorithm needs to satisfy: \n", | |
"\n", | |
"1. Everyone draws someone (exactly one person).\n", | |
"1. Everyone gets drawn (exactly once).\n", | |
"1. No one draws her or himself.\n", | |
"1. No one draws his or her spouse. \n", | |
"\n", | |
"TBH I set up a grand scheme of randomizing names and assigning them and checking them one at a time (based on an analogy to an in-person draw) but that was overly fussy, so we're just going to set up a test for our four conditions, shuffle the list of names and see if the shuffled list zipped to the original list fails the test. " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Let's make our people into variables. " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"a = ('my mom', 'Mom', '[email protected]') \n", | |
" #'my mom', 'my dad', and 'me' (below)\n", | |
" #make sense in the syntax of the email template \n", | |
"b = ('my dad', 'Dad', '[email protected]')\n", | |
"c = ('Alice', 'Alice', '[email protected]')\n", | |
"d = ('Bob', 'Bob', '[email protected]')\n", | |
"e = ('Carol', 'Carol', '[email protected]')\n", | |
"f = ('Dan', 'Dan', '[email protected]')\n", | |
"g = ('Frank', 'Frank', '[email protected]')\n", | |
"h = ('me', 'Tim', '[email protected]') \n", | |
" #I send this from an email I don't usually use to my regular account.\n", | |
"\n", | |
"names = [a, b, c, d, e, f, g, h]" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"And we'll define the 'ineligible receivers' for each person, too. In one version of this, I defined a list of lists: \n", | |
"`couples = ((a, b), (c,d), (e,f), (g,h))`\n", | |
"which made this part of the thing simple but made the checking part a bit more complicated. We're going to set this up to be a bit simpler down the road by making it a dictionary:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"bad_recips = {\n", | |
" a : b,\n", | |
" b : a,\n", | |
" c : d,\n", | |
" d : c,\n", | |
" e : f, \n", | |
" f : e,\n", | |
" g : h, \n", | |
" h : g\n", | |
"}" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from random import shuffle" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"I'm going to forego writing a test for conditions 1 and 2 b/c I'm just going to shuffle the list, zip it to the original list, and then test conditions 3 and 4. But let's go to those tests." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# No one draws her or himself.\n", | |
"\n", | |
"def check_self(draw):\n", | |
" for pair in draw:\n", | |
" if pair[0] == pair [1]:\n", | |
" return False\n", | |
" return True\n", | |
" \n", | |
"\n", | |
"# No one draws his or her spouse.\n", | |
"def check_spouse(draw):\n", | |
" for pair in draw:\n", | |
" if bad_recips[pair[0]] == pair [1]:\n", | |
" return False\n", | |
" return True\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now, we'll get to doing a drawing, checking it, and returning it if it satisfies the conditions." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def draw(nameslist):\n", | |
" drawlist = nameslist.copy()\n", | |
" shuffle(drawlist)\n", | |
" return list(zip(nameslist, drawlist))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def run(nameslist):\n", | |
" candidate = draw(nameslist)\n", | |
" while not check_self(candidate) or not check_spouse(candidate):\n", | |
" candidate = draw(nameslist)\n", | |
" return candidate" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"pairs = run(names)\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 19, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"import smtplib\n", | |
"from email.mime.multipart import MIMEMultipart\n", | |
"from email.mime.text import MIMEText\n", | |
"\n", | |
"for pair in pairs:\n", | |
" msg = MIMEMultipart()\n", | |
" msg['From'] = '[email protected]'\n", | |
" msg['To'] = toaddrs\n", | |
" msg['subject'] = 'Secret Santa Drawing!'\n", | |
" body = ''' Hi, ''' + pair[0][1] + ''' \n", | |
" Here's this year's Christmas drawing (at, um, last)!\n", | |
" \n", | |
" You're going to be giving a gift to:\n", | |
" ''' + pair[1][0] + '''\n", | |
" \n", | |
" \n", | |
" It's a bit late, but you've got 23 days.\n", | |
" \n", | |
" Thanks! and love to all, \n", | |
" Tim\n", | |
" '''\n", | |
" \n", | |
" msg.attach(MIMEText(body, 'plain'))\n", | |
"\n", | |
"\n", | |
" # Credentials (if needed)\n", | |
" username = '[email protected]'\n", | |
" password = 'PASSWORD' #Note that if you have 2FA enabled in gmail, you can create an app-specific password for your script.\n", | |
"\n", | |
" # The actual mail send\n", | |
" server = smtplib.SMTP('smtp.gmail.com:587')\n", | |
" server.starttls()\n", | |
" server.login(username,password)\n", | |
" server.sendmail(fromaddr, toaddrs, msg.as_string())\n", | |
" server.quit()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment