Skip to content

Instantly share code, notes, and snippets.

@herdingbats
Last active November 18, 2019 20:32
Show Gist options
  • Save herdingbats/095c21767e93aabb7daf9e86f50b11e2 to your computer and use it in GitHub Desktop.
Save herdingbats/095c21767e93aabb7daf9e86f50b11e2 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"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