#! /usr/bin/python3
#
# Draws matches for the gifting game 'secret santa'
# and notifies all participants by mail
#
# Expects as input a json file in the current working
# directory of the following form:
# {
#      "sender"  : "...",
#      "subject" : "...",
#      "message" : ["...", "...", ...]
#      "santas"  : {
#          "Aaron" : "aaron_mail",
#          "Bryce" : "bryce_mail"
#      }
# }
#
# To send the mails a prompt asks for username
# and password as login for the smtp server. The
# server name is constructed from the email domain.
# 
# Disclaimer: This script was written to suit my
#   needs, to be useful some alterations might
#   be necessary.

import sys
import json
import random as elf
import getpass as gp
import smtplib
from email.mime.text import MIMEText
from email.utils import parseaddr

with open('santas.json', 'r') as f:
    config = json.load(f)
    message = "\n".join(config["message"])
    santas  = config["santas"]

imps = list(santas.keys())

elf.seed()
for _ in range(100):
    elf.shuffle(imps)

# draw a match for each santa
secret_santa = dict()
for santa, mail in santas.items():
    imp = elf.choice(imps)
    while imp == santa:
        imp = elf.choice(imps)

    secret_santa[santa] = imp
    imps.remove(imp)
    elf.shuffle(imps)

# prompt for username and check for correct format
user = input('Username: ')
if '@' in parseaddr(user)[1]:
    server = user.split('@')[1]
else:
    sys.exit('Not a valid email address, try again!')

# prompt for the password
pwd  = gp.getpass()

# send a mail to each participant
for santa, imp in secret_santa.items():
    msg = MIMEText(message.format(santa, imp))
    msg['Subject'] = config["subject"]
    msg['From']    = config["sender"]
    msg['To']      = santas[santa]

    s = smtplib.SMTP(server, 587)
    s.starttls()
    s.login(user, pwd)
    s.send_message(msg)
    s.quit()

@@ -292,16 +292,17 @@
 .%22, ...%5D
+,
 %0A#      
@@ -745,16 +745,48 @@
 smtplib%0A
+%0Afrom collections import deque%0A%0A
 from ema
@@ -964,24 +964,29 @@
   santas  = 
+list(
 config%5B%22sant
@@ -993,335 +993,337 @@
 as%22%5D
-%0A%0Aimps = list(santas.keys())%0A%0Aelf.seed()%0Afor _ in range(100):%0A    elf.shuffle(imps)%0A%0A# draw a match for each santa%0Asecret_santa = dict()%0Afor santa, mail in santas.items():%0A    imp = elf.choice(imps)%0A    while imp == santa:%0A        imp = elf.choice(imps)%0A%0A    secret_santa%5Bsanta%5D = imp%0A    imps.remove(imp)%0A    elf.shuffle(
+.keys())%0A%0A# shuffle the santas and create the pairing%0A# by rotating the shuffled list by one.%0A# That way there is a perfect match for%0A# everyone and the relations form a loop,%0A# see: http://stackoverflow.com/a/303476/839079%0Aelf.seed()%0Aelf.shuffle(santas)%0Aimps = deque(santas)%0Aimps.rotate()%0Asecret_santas = dict(zip(santas, 
 imps)
+)
 %0A%0A# 
@@ -1632,16 +1632,17 @@
 et_santa
+s
 .items()
@@ -1788,22 +1788,32 @@
       = 
+config%5B%22
 santas
+%22%5D
 %5Bsanta%5D%0A

Fix a bug where the pairing might get stuck