You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
6.7 KiB

import csv
import smtplib
from email.mime.text import MIMEText
from random import shuffle
# 1. Load participants from CSV, now including id and last_match_id
def load_participants(filename):
participants = []
with open(filename, 'r') as file:
reader = csv.DictReader(file)
for row in reader:
participants.append({
'id': int(row['id']),
'name': row['name'],
'household': row['household'],
'email': row['email'],
'last_match_id': int(row['last_match_id']) if row['last_match_id'] else None
})
return participants
# 2. Backtracking algorithm to find valid matches
def find_matches(givers, receivers, matches, used_receivers):
if len(matches) == len(givers):
return True # Found a complete set of matches
giver = givers[len(matches)] # Process the next giver
for receiver in receivers:
# Check constraints: different person, different household, no last-year match, no reciprocal match
if receiver['id'] not in used_receivers and \
giver['id'] != receiver['id'] and \
giver['household'] != receiver['household'] and \
giver['last_match_id'] != receiver['id'] and \
matches.get(receiver['id']) != giver['id']:
# Assign receiver to giver
matches[giver['id']] = receiver
used_receivers.add(receiver['id'])
# Recurse with the next giver
if find_matches(givers, receivers, matches, used_receivers):
return True # Solution found
# Backtrack: remove the match and try another receiver
del matches[giver['id']]
used_receivers.remove(receiver['id'])
return False # No valid match found, backtrack
# 3. Wrapper function to initialize and run the backtracking
def generate_complete_matches(participants):
givers = participants.copy()
receivers = participants.copy()
shuffle(givers) # Shuffle for variety
shuffle(receivers) # Shuffle for variety
matches = {}
used_receivers = set()
# Start backtracking
if find_matches(givers, receivers, matches, used_receivers):
return matches
else:
print("Could not find a complete set of matches.")
return None
# 4. Confirm matches interactively
def confirm_matches(participants, matches):
print("Proposed Matches:")
for giver_id, receiver in matches.items():
giver = next(p for p in participants if p['id'] == giver_id)
print(f"{giver['name']} ({giver['email']}) will give a gift to {receiver['name']} ({receiver['email']})")
response = input("Approve this match? (yes/no): ").strip().lower()
if response != 'yes':
print("Match not approved. Restarting the matching process...\n")
return False # Signal to restart if any match is disapproved
return True
# 5. Final confirmation to save matches
def final_confirmation(participants, matches):
print("\nFinal Match List:")
for giver_id, receiver in matches.items():
giver = next(p for p in participants if p['id'] == giver_id)
print(f"{giver['name']} will give a gift to {receiver['name']}")
# Final save prompt
response = input("\nDo you want to save this match list? (yes to save, no to restart): ").strip().lower()
return response == 'yes'
# 6. Update last matches in the CSV file
def update_last_match_in_file(participants, matches, filename):
for giver_id, receiver in matches.items():
for participant in participants:
if participant['id'] == giver_id:
participant['last_match_id'] = receiver['id']
with open(filename, 'w', newline='') as file:
fieldnames = ['id', 'name', 'household', 'email', 'last_match_id']
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for participant in participants:
writer.writerow({
'id': participant['id'],
'name': participant['name'],
'household': participant['household'],
'email': participant['email'],
'last_match_id': participant['last_match_id']
})
print("Matches saved to 'participants.csv'.")
# 7. Email sending function
# 7. Email sending function with improved formatting
def send_emails(matches, participants, sender_email, smtp_server, smtp_port, login, password):
for giver_id, receiver in matches.items():
giver = next(p for p in participants if p['id'] == giver_id)
# Compose email
subject = "Dein Wichtel-Match für Weihnachten 2025"
message = (
f"Hallo liebe/r {giver['name']},\n\n"
f"Dein Match für das diesjährige Wichteln ist {receiver['name']}.\n\n"
"Grüße,\n"
"Das Wichtelsystem\n\n"
"--Bitte beachtet, dass für ungefähr 25€ gewichtelt wird--"
)
# Prepare email
msg = MIMEText(message, 'plain')
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = giver['email']
# Send email
try:
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls() # Secure the connection
server.login(login, password)
server.sendmail(sender_email, giver['email'], msg.as_string())
print(f"Email sent to {giver['name']} at {giver['email']}")
except Exception as e:
print(f"Error sending email to {giver['name']} ({giver['email']}): {e}")
# 8. Main function to run the match process
def main():
participants = load_participants('participants.csv')
while True:
matches = generate_complete_matches(participants)
if matches and confirm_matches(participants, matches):
if final_confirmation(participants, matches):
update_last_match_in_file(participants, matches, 'participants.csv')
# Send emails after saving the matches
sender_email = "weihnachtswichteln0@gmail.com" # Update to your email
smtp_server = "smtp.gmail.com" # Update to your SMTP server
smtp_port = 587 # Typically 587 for TLS
login = "weihnachtswichteln0@gmail.com" # SMTP login
password = "" # SMTP password
send_emails(matches, participants, sender_email, smtp_server, smtp_port, login, password)
break
else:
print("\nRestarting the matching process...\n")
else:
print("No complete match found, restarting...\n")
if __name__ == '__main__':
main()