В этом году друзья поручили мне организовать Тайного Санты в этом году. Обычно это не моя роль. Но человек, который обычно его организовывает, уехал в Англию, чтобы выпить чаю и встретиться с королевой. Вы можете спросить, а почему бы просто не написать имена на листах бумаги, скомкать их и засунуть в шапку? Почему ты не делаешь это так, как поступили бы нормальные люди? Однако вы могли заметить или не заметить, но в настоящее время мы находимся в пандемии. Нет нормального способа что-то сделать, если только вы не имеете в виду новую нормальность. Поэтому мне нужно было сделать его бесконтактным и простым в выполнении - я ОЧЕНЬ ленивый.

Мы могли бы наклеить имена в шляпу и выбирать имена одно за другим. Однако а) в нашей группе более 5 человек, и это было бы незаконно, и б) это требует времени, и если я выберу свое имя, и нам придется перезапустить, я, вероятно, заплачу от боли. Я мог бы просто присваивать имена, но это означает, что меня не интересуют. Ни подарков для меня, ни выяснения того, кто кого достал с моими супер-детективными способностями. Я не мог этого сделать. Нет, я знал, что мне нужно создать алгоритм для этого на Python!

Итак, я подошел к чертежной доске и составил список функций программы:

  • Получите имена и адреса электронной почты всех участников и сохраните их.
  • Назначьте каждому участнику Тайного Санты, убедившись, что у каждого есть чужое имя.
  • Отправляйте всем их распределения по электронной почте для поистине бесконтактного опыта!

Во-первых, мне нужно было найти библиотеки Python, необходимые для работы. Их было довольно много:

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

Библиотека random была предназначена для того, чтобы я мог использовать функцию randint для рандомизации распределения секретного Санты. Остальные библиотеки были необходимы для раздела электронной почты моей программы. Библиотека re была так, чтобы я мог проверить электронную почту!

Я также инициализировал несколько переменных:

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

Затем мне нужно было решить, как мне добавить имена и адреса электронной почты в программу. Я решил использовать два метода ввода данных:

  • Через текстовый файл (.txt): пользователь создает текстовый файл с определенным форматом имени, электронной почты. В каждой новой строке появляется новый участник, и программа обработает текстовый файл и сохранит всех участников в списках.
  • Ввод вручную: пользователь вручную вводит имя и адрес электронной почты каждого участника, и программа будет предлагать ему («Введите имя участника 1:», «Введите адрес электронной почты участника 1:» и т. д.) .

Вот как выглядит код ввода данных (включая проверку!):

# 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!")

Пришло время для основного направления программы: раздачи подарков Тайному Санту. Сначала я продублировал список имен - из этого списка я бы выбрал распределения и удалил имя после того, как оно было выделено. Затем я использовал цикл for, чтобы просмотреть каждое имя в списке имена и определить, кого они могли бы назвать Тайным Санта-Клаусом. Чтобы решить это, я бы использовал функцию randint из встроенной библиотеки Python random. Это случайное число представляет индекс получателя. Затем я добавляю имя получателя в список получателей. Однако было несколько случаев, на которые мне пришлось обратить внимание:

  • Если имя получателя совпадает с именем Тайного Санты. Это невозможно, потому что ты не можешь быть самим собой секретный Санта! Поэтому я устанавливаю аргумент if, else. Если имена совпадают, программа снова запустит функцию randint, чтобы выбрать другой индекс.
  • Если единственное оставшееся имя получателя совпадает с именем Тайного Санты. Если я продолжу использовать функцию randint, я застряну в бесконечном цикле, поскольку другого выхода нет! Поэтому у меня есть еще один оператор if, проверяющий, равна ли длина списка possible_santa 1 (что означает, что программа должна быть запущена снова). Это устанавливает для логического значения повтора значение Истина, позволяя программе запускаться снова.
possible_santa = names.copy()
cont = 0
while(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

Теперь, когда распределения были установлены, мне нужно было отправить их участникам по электронной почте! Для этого я использовал SMTP - в Python есть встроенная библиотека для отправки электронной почты smtplib. Я также использовал email.mime.multipart и email.mime.text - другие библиотеки Python. Я использовал этот веб-сайт: https://www.freecodecamp.org/news/send-emails-using-code-4fcea9df63f/ для справки, и это было действительно полезно!

Вот фрагмент кода с некоторыми комментариями, которые, я надеюсь, все объяснят!

# 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()

Я также хотел создать текстовый файл, в котором будут сохранены распределения на случай, если что-то пойдет не так! Это гарантирует, что я могу все перепроверить и у нас есть резервный список.

Для этого все, что я сделал, - это создал новый текстовый файл, используя функцию открытия. Затем я использовал цикл for, чтобы перебрать каждого Secret Santa и получателя и записать каждое выделение в новую строку файла! Убедитесь, что вы также закрыли текстовый файл!

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()

Это не единственный способ создать этот генератор, и если вы можете придумать другие способы, дайте мне знать!

Мне не терпится использовать эту программу и получить все подарки, и я надеюсь, что вам понравится ее кодирование и использование! Счастливого Рождества, и если вам это понравилось, загляните в мой блог https://itsliterallymonique.wordpress.com/, чтобы узнать больше об этом!

Если вы хотите загрузить этот код, он находится в этом репозитории github: https://github.com/moniquethemuffin/Secret-Santa-Generator