parent
7c46e612f4
commit
c53ace7eba
@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings" defaultProject="true" />
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
@ -0,0 +1,168 @@
|
|||||||
|
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 dieses Jahr"
|
||||||
|
message = (
|
||||||
|
f"Hallo liebe/r {giver['name']},\n\n"
|
||||||
|
f"Dein Match für das diesjährige Wichteln ist {receiver['name']}.\n\n"
|
||||||
|
"Mit freundlichen Grüßen,\n"
|
||||||
|
"Dein Wichtelsystem\n\n"
|
||||||
|
"--Bitte beachtet, dass ungefähr für 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()
|
||||||
Loading…
Reference in new issue