Skip to content

Instantly share code, notes, and snippets.

@jlipps
Created December 6, 2012 20:08
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 jlipps/4227872 to your computer and use it in GitHub Desktop.
Save jlipps/4227872 to your computer and use it in GitHub Desktop.
Randomized "Secret Santa" gift exchange generator with advanced behaviors, enables complete anonymity, allowing organizer to participate
from collections import OrderedDict
from random import randint
import smtplib
FROM = "Santa Claus <santa.claus@gmail.com>"
PARTICIPANTS = OrderedDict([
('Dasher', 'dasher@gmail.com'),
('Dancer', 'dancer@gmail.com'),
('Prancer', 'prancer@gmail.com'),
('Vixen', 'vixen@gmail.com'),
('Comet', 'comet@gmail.com'),
('Cupid', 'cupid@gmail.com'),
])
# if you want certain people to avoid giving gifts to others, say to prevent
# significant others from getting each other
AVOIDS = [
('Prancer', 'Vixen'),
('Dasher', 'Dancer'),
('Comet', 'Cupid')
]
def get_participant_by_index(i):
j = 0
for (name, email) in PARTICIPANTS.iteritems():
if j == i:
return (name, email)
j += 1
raise IndexError("Couldn't find participant at index %s" % i)
def get_participant_index_by_name(name):
i = 0
for (s_name, email) in PARTICIPANTS.iteritems():
if name == s_name:
return i
i += 1
raise KeyError("Couldn't find participant with name %s" % name)
def build_avoid_pairs():
pairs = []
for (a1, a2) in AVOIDS:
pairs.append((get_participant_index_by_name(a1),
get_participant_index_by_name(a2)))
return pairs
def get_index_map():
num_parts = len(PARTICIPANTS)
found_map = False
avoids = build_avoid_pairs()
# This is pretty ugly, and not guaranteed to find a solution in every case.
# Could run forever. Seems to work well in cases I've tried.
while not found_map:
print "Looking for giving map"
avail_froms = [x for x in xrange(0, num_parts)]
avail_tos = [x for x in xrange(0, num_parts)]
pairs = []
while len(avail_froms):
rand_from_index = randint(0, len(avail_froms) - 1)
rand_from = avail_froms[rand_from_index]
del avail_froms[rand_from_index]
found_match = False
while not found_match:
rand_to_index = randint(0, len(avail_tos) - 1)
rand_to = avail_tos[rand_to_index]
if rand_to != rand_from or len(avail_tos) == 1:
pairs.append((rand_from, rand_to))
del avail_tos[rand_to_index]
found_match = True
found_map = True
for pair in pairs:
if pair[1] == pair[0]:
print "\tFound self-giving, retrying..."
found_map = False
break
else:
rev_pair = (pair[1], pair[0])
for other_pair in [p for p in pairs if p != pair]:
if other_pair == rev_pair:
found_map = False
print "\tFound mutual giving, retrying..."
break
if not found_map:
break
for avoid_pair in avoids:
if pair[0] in avoid_pair and pair[1] in avoid_pair:
found_map = False
print "\tFound giving we should avoid, retrying..."
break
print "Found good map!"
return pairs
def email(pairs):
print "Emailing participants..."
subject = "Your Secret Santa gift exchange recipient"
text = ("Hi, %s!\n\nThe person you have for the gift exchange is: %s\n\n"
"Best Regards,\nSanta Claus")
server = smtplib.SMTP('localhost')
for pair in pairs:
(giver_name, email) = get_participant_by_index(pair[0])
(givee_name, _) = get_participant_by_index(pair[1])
to_email = "%s <%s>" % (giver_name, email)
body = "\r\n".join((
"From: %s" % FROM,
"To: %s" % to_email,
"Subject: %s" % subject,
"",
text % (giver_name, givee_name)
))
server.sendmail(FROM, [to_email], body)
print "\tE-mailed %s" % to_email
server.quit()
def print_map(pairs):
print "Giving map is:"
for pair in pairs:
(giver_name, _) = get_participant_by_index(pair[0])
(givee_name, _) = get_participant_by_index(pair[1])
print "\t%s -> %s" % (giver_name, givee_name)
def ensure_uniqueness():
names = [p for p in PARTICIPANTS.keys()]
if len(names) != len(set(names)):
raise Exception("There is a duplicate name in the participants, this "
"causes things to break")
if __name__ == "__main__":
ensure_uniqueness()
giving_pairs = get_index_map()
prompt = "Do you want to see the giving map [y/N]? "
if raw_input(prompt).strip() in ['y', 'Y']:
print_map(giving_pairs)
prompt = "Do you want to e-mail the participants [Y/n]? "
if raw_input(prompt).strip() in ['y', 'Y', '']:
email(giving_pairs)
print "Done. Happy holidays!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment