Compare commits

...

3 Commits

Author SHA1 Message Date
2f3f1d9a13 minor Updates 2024-10-25 10:45:38 +02:00
541b947609 Update function to load n years of history
Add a function to load participants data from the current year file
2024-10-25 10:45:22 +02:00
598bae34ba Update draw function to take n years of history in account 2024-10-25 10:44:39 +02:00
3 changed files with 101 additions and 47 deletions

View File

@ -1,41 +1,51 @@
from random import choice
def draw_names(previous_draw, draws_per_person):
def draw_names(current_participants, history_data, draws_per_person, max_attempts=5):
"""
Perform the Secret Santa draw considering past draws.
:param previous_draw: Last year's draw data (list of participants and recipients).
Perform the Secret Santa draw considering past years' draws.
:param current_participants: This year's participant list.
:param history_data: Historical draw data to avoid repeats.
:param draws_per_person: Number of people each participant should give gifts to.
:param max_attempts: Maximum number of retry attempts before loosening exclusions. by default : 5.
:return: The new draw results.
"""
participants = [a[0] for a in previous_draw] # Get participant names
participants = [p[0] for p in current_participants] # Get participant names
already_drawn = [] # Track who has been drawn
new_draw = [] # Store new draw results
for i in range(len(participants)):
giver = previous_draw[i][0]
email = previous_draw[i][1]
print(email)
last_year_r1 = previous_draw[i][2]
last_year_r2 = previous_draw[i][3]
for attempt in range(max_attempts):
new_draw.clear()
already_drawn.clear()
success = True # Track if draw is successful in this attempt
available_participants = participants.copy()
try:
available_participants.remove(giver)
available_participants.remove(last_year_r1)
available_participants.remove(last_year_r2)
except ValueError:
pass
for i, giver in enumerate(participants):
email = current_participants[i][1]
# Collect previous recipients to avoid drawing the same person again
previous_recipients = {recipient for record in history_data if record[0] == giver for recipient in record[2:]}
new_recipients = []
while len(new_recipients) < draws_per_person:
selected = choice(available_participants)
if already_drawn.count(selected) >= draws_per_person:
available_participants.remove(selected)
else:
new_recipients.append(selected)
already_drawn.append(selected)
available_participants.remove(selected)
# Create a set of available participants excluding the giver and previous recipients
available_participants = set(participants) - {giver} - previous_recipients
new_recipients = []
new_draw.append([giver, email] + new_recipients)
# Ensure there are enough available participants for the draw
if len(available_participants) < draws_per_person:
success = False
break
return new_draw
# Select recipients for the current giver
while len(new_recipients) < draws_per_person:
selected = choice(list(available_participants))
if already_drawn.count(selected) < draws_per_person:
new_recipients.append(selected)
already_drawn.append(selected)
available_participants.discard(selected)
new_draw.append([giver, email] + new_recipients)
# If the draw was successful, break out of attempts
if success:
return new_draw
print(f"Attempt {attempt + 1} failed, retrying...")
# If all attempts fail, raise an error
raise ValueError("Unable to complete a valid draw after maximum attempts.")

View File

@ -1,13 +1,57 @@
import csv
from datetime import date
from env import CSV_PREFIX, CSV_PATH
def open_csv(file_name):
"""Open the CSV file and return its contents as a list of rows"""
with open(file_name, "r", encoding='utf-8') as file:
reader = csv.reader(file)
return list(reader)
def load_history(years):
"""
Load participant data from multiple CSV files based on the specified history years.
:param years: Number of years of history to load.
:return: A list of all historical draw data.
"""
current_year = date.today().year
history_data = []
def save_csv(data, file_name):
"""Save the updated draw results to the CSV file"""
# Load data for each of the past specified years
for i in range(1, years + 1):
try:
year = current_year - i
file_name = f"{CSV_PATH}/{CSV_PREFIX}_{year}.csv"
with open(file_name, "r", encoding='utf-8') as file:
reader = csv.reader(file)
history_data.extend(list(reader)) # Add each year's data
except FileNotFoundError:
print(f"No historical file found for year {year}, skipping.")
continue
return history_data
def load_participants():
"""
Load participant data from the current year CSV file.
:return: A list of all historical draw data.
"""
current_year = date.today().year
participants_data = []
try:
file_name = f"{CSV_PATH}/{CSV_PREFIX}_{current_year}.csv"
print(file_name)
with open(file_name, "r", encoding='utf-8') as file:
reader = csv.reader(file)
participants_data.extend(list(reader)) # Add each year's data
except FileNotFoundError:
print(f"No file found for year {current_year}, skipping.")
return participants_data
def save_csv(data, year):
"""
Save the new draw results to a CSV file named with the current year.
:param data: New draw data.
:param year: The current year for naming the file.
"""
file_name = f"{CSV_PATH}/{CSV_PREFIX}_{year}.csv"
with open(file_name, 'w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerows(data)

View File

@ -1,5 +1,5 @@
from env import CSV_FILE_PATH, DRAW_PER_PERSON, EMAIL_SUBJECT, EMAIL_BODY
from file_io import open_csv, save_csv
from env import DRAW_PER_PERSON, HISTORY_YEARS, EMAIL_SUBJECT, EMAIL_BODY
from file_io import load_history, save_csv, load_participants
from draw import draw_names
from emailer import send_email
from utils import get_current_time
@ -11,29 +11,29 @@ def send_all_emails(new_draw):
for participant in new_draw:
name = participant[0]
receiver_email = participant[1]
draws = ", ".join(participant[2:]) # List of recipients
draws = ", ".join(participant[2:])
message = EMAIL_BODY.format(name=name, draws=draws)
subject = EMAIL_SUBJECT.format(year=current_year)
send_email(receiver_email, subject, message) # Send the email
send_email(receiver_email, subject, message)
print(f"Email sent to {name} ({receiver_email})")
if __name__ == "__main__":
try:
# Load previous draw data
old_draw = open_csv(CSV_FILE_PATH)
# Load data from historical CSVs and this year's participants
history_data = load_history(HISTORY_YEARS)
# Perform new draw
new_draw = draw_names(old_draw, DRAW_PER_PERSON)
current_year = date.today().year
current_participants = load_participants() # Load participants data
# Perform the draw
new_draw = draw_names(current_participants, history_data, DRAW_PER_PERSON)
# Send emails to participants
send_all_emails(new_draw)
# Save new draw results
save_csv(new_draw, CSV_FILE_PATH)
save_csv(new_draw, current_year)
# Output completion time
print(f"Process completed at {get_current_time()[1]} on {get_current_time()[0]}")
except Exception as e:
print(f"Error occurred: {e}")
# Retry or handle errors