I made a contactless Secret Santa algorithm with Python

Monique Cheng
Analytics Vidhya
Published in
6 min readNov 3, 2020
Photo by Roman Samborskyi on Dreamstime.com

This year, my friends tasked me with the role: organize this year’s Secret Santa. Normally it isn’t my role. But the person who usually organizes it has left us for England to drink tea and meet the Queen. Now, you may be asking, why not just write names on pieces of paper, crumple them up and shove them in a cap? Why don’t you do it how normal people would? However, you may or may not have noticed, but we are currently in a pandemic. There is no normal way of doing something, unless you mean the new normal. So I needed to make it contactless, and easy for me to carry out — since I am VERY lazy.

We could stick names in a hat and pick names one by one. However, a) we have more than 5 people in our group and this would be illegal and b) it takes a while and if I pick out my own name and we have to restart I would probably cry in pain. I could just allocate names, but that means I get left out. No presents for me, no figuring out who got who with my super detective powers. I couldn’t do that. No, I knew I had to create an algorithm for this using Python!

So I went to the drawing board and made a list of the functions of the program:

  • Get all the participants’ names and emails and store them.
  • Assign every participant a Secret Santa, ensuring everyone has a name that is not their own.
  • Send everyone their allocations via email for a truly contactless experience!

First, I needed to find the Python libraries necessary for this to work. There were quite a few:

import re
import
random
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

The random library was so I could use the randint function to randomize the Secret Santa allocations. The rest of the libraries were necessary for the emailing section of my program. The re library was so I could validate the email!

I also initialised a few variables:

sender_address = 'email'
sender_pass = 'password'
names = []
emails = []
recipient = []
budget = 50
count = 0

Next, I needed to decide how I would get the names and emails into the program. I decided to have two data entry methods:

  • Via text (.txt) file: The user sets up a text file with a specific format of name, email. Every new line has a new participant and the program will process the text file and store all the participants in lists.
  • Manual entry: The user manually types in the name and email of each participant, and will be prompted by the program (‘Enter participant 1’s name: ‘, ‘Enter participant 1’s email: ‘, etc.).

Here’s how the data entry code looks like (including validation!):

# asks the for data entry method
print("""Welcome to the secret santa decision-maker! How would you like to enter the information?
1. give a text (.txt) file with format of: name, email address
2. manually enter information""")
x = 0
while(x == 0):
try:
option = int(input("Info entry method (1 or 2): "))
if(option > 2 or option < 1):
print("ERROR: You can only input 1 or 2!")
print("""How would you like to enter the information?
1. give a text (.txt) file
2. manually enter information""") else:
x = 1
except ValueError:
print("ERROR: Please input 1 or 2!")
print("""How would you like to enter the information?
1. give a text (.txt) file
2. manually enter information""")
# gets the number of participants
x = 0
while(x == 0):
try:
count = int(input("Enter number of participants: "))
if(count < 2):
print("ERROR: Number of participants must be 2 or more!")
else:
x = 1
except ValueError:
print("ERROR: Please input a valid integer number!")
# option 1: read the file (assumes file format is correct)
if(option == 1):
x = 0
while(x == 0):
filename = str(input("Name of text file (must end in .txt): "))
if (filename[-4:] == '.txt'):
x = 1
else:
print("ERROR: Please input a file name which ends with .txt")
text = open(filename, "r")
for i in range(0, count):
info = text.readline().split(', ')
names.append(info[0])
emails.append(info[1])
# option 2: manually get info (assumes email is correct)
elif(option == 2):

# for validating an Email
regex = '^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$'
print("Ok! It's time for you to input the participants' information!")
for i in range(1, count + 1):
name = str(input(f'Enter participant the name of participant {i}: '))
names.append(name)
x = 0
while(x == 0):
email = str(input(f'Enter the email of participant {i}: '))
if(re.search(regex, email)):
emails.append(email)
x = 1
else:
print("ERROR: invalid email!")

Now it’s time for the main area of the program: the Secret Santa allocation. First I duplicated the names list — this list would be the one I would select the allocations from and delete the name once it has been allocated. Then, I used a for loop to go through each name in the names list and allocate who they would be the Secret Santa of. To decide this, I would use the randint function from the built-in Python random library. This random number represents the index of the recipient. I then append the name of the recipient to the recipient list. However, there were a few cases I had to check for:

  • If the name of the recipient was the same as the name of the Secret Santa. This isn’t possible, as you cannot be your own secret santa! Therefore, I set an if, else argument. If the names are the same, the program will run the randint function again to choose another index.
  • If the only recipient name left is the same as the Secret Santa’s name. If I keep using the randint function I will be stuck in an infinite loop since there is no other option! Therefore, I have another if statement, checking if the length of the possible_santa list is 1 (meaning the program must be run again). This sets the redo boolean to True, allowing the program to run again.
possible_santa = names.copy()cont = 0while(cont == 0):

redo = False

possible_santa = names.copy()

for i in range(0, len(names)):
recip = random.randint(0, len(possible_santa) - 1)
x = 0
while(x == 0):
if(names[i] == possible_santa[recip]):
if(len(possible_santa) == 1):
redo = True
x = 1
else:
recip = random.randint(0, len(possible_santa) - 1)
else:
x = 1
if(redo != True):
recipient.append(possible_santa[recip])
possible_santa.pop(recip)
cont = 1
else:
cont = 0

Now that the allocations have been set, I needed to email them to the participants! To do this I used SMTP — Python has a built in library for email sending, smtplib. I also used email.mime.multipart and email.mime.text — more Python libraries. I used this website: https://www.freecodecamp.org/news/send-emails-using-code-4fcea9df63f/ for reference, and it was really helpful!

Here is the code snippet, with some comments which I hope explain everything!

# this code must run for each name
for i in range(0, count):
# the message which will be sent in the email
mail_content = f'''Hello {names[i]},

You are the secret santa of {recipient[i]}!

Remember the budget is ${budget}
'''

# sets the email address the email will be sent to
receiver_address = emails[i]

# sets up the MIME
message = MIMEMultipart()
message['From'] = sender_address # your email address
message['To'] = receiver_address # Secret Santa's email address
message['Subject'] = 'Secret Santa' # subject of the

# sets the body of the mail
message.attach(MIMEText(mail_content, 'plain'))

# creates the SMTP session for sending the mail
session = smtplib.SMTP('smtp.gmail.com', 587)
session.connect("smtp.gmail.com", 587)
session.ehlo()
session.starttls()
session.login(sender_address, sender_pass)
text = message.as_string()
session.sendmail(sender_address, receiver_address, text)
session.quit()

I also wanted to create a text file which has the allocations saved in case something goes wrong! This ensures I can double check everything and we have a backup list.

For this all I did was create a new text file, using the open function. Then I used a for loop to go through each Secret Santa and the recipient and write each allocation into a new line in the file! Make sure you also close the text file!

allocations = open("SantaAllocations.txt", "w+")for i in range(0, len(names)):
allocations.write(f'{names[i]} is the secret santa of {recipient[i]}\n')

allocations.close()

This is not the only way to create this generator, and if you can think of any other ways, let me know!

I can’t wait to use this program and get all the presents, and I hope you enjoy coding it and using it as well! Have a very Merry Christmas, and if you liked this, check out my blog https://itsliterallymonique.wordpress.com/ for more like this!

If you want to download this code, it is in this github repository: https://github.com/moniquethemuffin/Secret-Santa-Generator

--

--

Monique Cheng
Analytics Vidhya

A Filipino high-schooler studing in Singapore who likes to code. I literally just code whatever I want. Sometimes I also post random opinions :)