Draw and email your Secret Santas using Python and SendGrid
Project on GitHub here.
It's that time of the year, AGAIN! It's been a fast one, but I do feel like I learned a lot, on my holy quest to become a rock-star programmer. That say, I would like to finish the year on a fun simple project. So when my mom asked me to draw our Secret Santas this year, I had it.
If you are not familiar with the concept, it's a nice way to celebrate xmas amongst a large group of friends or family members, withoug having to spend a salary on gifs. Every member of the group is assigned one recipient, and as his/her Secret Santa (since these random assignments are kept secret), you need to buy a gift for this person only. This way everyone will get a nice present instead of dozens of cheap ones.
In the past years, I used a basic random name generator for that, but with the downside that I knew all the assignments and who my Secret Santa was. So this year, let's be clever and use a python script to do the draw and contact Santas per email.
Get list of Santas from a CSV file
I leaned the Pandas library this year, which is great for extracting information from a CSV file. It's a user-friendly way to input data, easier than writing in the script directly. Here is our CSV file, with our six friends and the information we need to contact them.
The Black List
Black list, quid est? I like to spice things up and I thought it would be handy to add a black list feature, i.e. a list of names that one Santa should not get for any reason. In our case for instance:
- Ross and Rachel should not get each other, cause they had a big fight and they are 'on a break'
- Monica and Chandler should not get each other, as they are married and they wanna give each others more expensive presents
- Joey is broke, he bought a cheap present but it's for a guy so he cannot be one of the girl's Secret Santa
- Phoebe likes everyone so her black list is empty
Create the SecretSanta class
I learned programming using C++ and I have the CLASS word inprinted in caps inside my brain. So why not organise our script a bit by adding a SecretSanta class? Here are the class members and methods we need for the draw:
def __init__(self, name, email, black_list = None):
""" initialize class variables """
self.name = name
self.email = email
self.recipient = None # will be allocated later
self.black_list = list()
self.black_list.append(self.name) # adding own name
self.black_list += black_list.split('|')
""" override print method (optional) """
# return string to print
""" contact recipient """
# use SendGrid example here
Load settings from a json file
Again, I find it more friendly to use external files to load variables instead of editing the code directly. So I usually add a settings.json file to all my projects. Here are the variables that we need to define there:
- Path to our CSV file containing Secret Santas information
- Path to the email template (.txt or .html)
- Max attempts that should be done while trying to assign recipients to secret santas. Due to the black lists, an assignment may not be possible and therefore this variable will break the while loop and raise an error.
- Email of your SendGrid account
- Personal SendGrid API key (private, don't share it with ANYONE)
The json library helps you import and store these parameters in a SETTINGS dictionnary, that you can use further in your script.
with open("data/settings.json", 'r') as json_file:
SETTINGS = json.load(json_file)
# output: 'data/secret_santas_list.csv'
Main - Create Santas
We now have everything we need to write our main procedure.
To extract the CSV infos into a Pandas dataframe and create our SecretSanta instances is a child game.
from pandas import read_csv
df = read_csv(csv_file_path).fillna('')
secret_santas = 
for i in range(len(df)):
new_name = str(df['Name'][i])
new_email = str(df['Email'][i])
new_black_list = str(df['Black List'][i])
new_santa = SecretSanta(name=new_name, email=new_email, black_list=new_black_list)
Main - Draw
Finally we can draw our Secret Santas!
Since we introduced the black-list option, we face the issue that a draw might not be possible due to too many constraints (for instance, some poor guy who would be on everyone's black list!). I'm sure we could come up with a clever algorithm that would analyse all black-lists first and determine if a draw is possible or not. But let's keep it simple and take the easy road of trying, until we succeed or decide it's not gonna happen!
We will therefore wrap everything inside a while loop, that will break if the draw is successful (everyone has been assigned as Secret Santa) or after a maximum numner of attempts (defined in our settings file).
Here are the main steps I am following, check the GitHub project for the (well-commented!) code:
- Initiate the while loop with our two conditions (success or reaching max attempts)
- Shuffle our list of names and delete any previous assignment
- Go through our list of SecretSanta instances and look for a recipient, which is not on the black-list. If we find one, we assign it and we jump to the next SecretSanta. If not, the draw fails and we start a new one (the while loop restart, we are back at step 2)
- If we assign a recipient to all Secret Santas, the draw is successful and the while loop will break. If it never happens, the loop will break once reaching the max attemps.
- Process the result: A succesful draw will trigger contacting the Secret Santas, while a failed one will raise an error and ask the user to review the input parameters.
This is our last step. We are going to use Twilio SendGrid for this, which provides a cloud-based service that assists businesses with email delivery. This is a very easy step thanks to their crystal-clear tutorials. Here are the steps you need to follow to use SendGrid services.
Create a SendGrid account
You need to register for an account first. The free offer allows you to send up to 100 emails a day, which is good enough for our little project.
Create a Single Sender Verification
Your recipients will receive emails from a specific address. Terefore this e-mail needs to be existing and verified as your own.
Create a personal API key
This is an important step. The API key is personal and should never be shared. It will serve as a badass password (seriously, just look at it!) that will need to be passed everytime e-mails are sent from your account. For simplicity, you can add it to your environment variables.
Install the SendGrid python library
Finally you can install the python library. The documentation gives you some simple examples on how to send your first emails. I did not need to dig much further, those are great. This project uses the "Without Mail Helper Class" example, that I implemented in my contact_secret_santa() class method.
Personalising the email content
This is an optional but nice little step. I found it more joyful and christmassy to write the email contents as from the hand of Santa himself, asking people to help him spread the joy. Sendgrid let you use a text or html format for the contents of the email. So why not draft an html file with some pictures, colors, a handwriting font, etc? You can also put tags like [NAME] and [RECIPIENT] that will be replaced with your class attributes to address the person directly. Put some CSS to make it look more personal and believable. I don't know much about web dev but feel free to check my html template.
Once all these steps are done and if your draw was successful, you can now call the contact_secret_santa() method of all your instances. They will be notified within a few minutes and receive an email based on your template. Hey, you got one too!
I hope this little project taught you something new, whether you are playing with CSV and json files, trying to send emails or simply improving your python skills. It was a fun last projet to wrap up this year, which has been for me rich in learning new stuff and hopefully a few more steps towards my goals.
I wish you all a wonderful Christmas. Have a great Secret Santa sharing time with the ones you love (and the ones from your black list too!). Take care and read you next year :)
Friends pictures © Warner Bros. Entertainment Inc
Cover picture from Rachel Middleton