Created
September 25, 2022 15:33
-
-
Save ajoh504/d69a8c7fc1344bf22e21e9c430c46eb1 to your computer and use it in GitHub Desktop.
Random Chore Assignment Emailer - Automate the Boring Stuff With Python - CH 18
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!python3 | |
# emailchores.py - Scan a list of chores and a list of email addresses then | |
# randomly assign chores to people via email. Track recent | |
# chores so that the same chore is not assigned the following | |
# week. | |
# | |
# USAGE: Run script with --help or -h to view help information. | |
# Using Python 3.6 and EZGmail 2022.2.24 | |
# | |
# SOURCE: https://automatetheboringstuff.com/2e/chapter18/ | |
import random | |
import json | |
import argparse | |
import ezgmail | |
import time | |
from pathlib import Path | |
parser = argparse.ArgumentParser( | |
description=""" | |
Input emails and chores for weekly scheduling. Arguments are | |
required for first time setup, but optional otherwise. Emails | |
and chores will be stored in a text file, and new arguments will | |
be appended to the previous emails or chores files.\n | |
example: py emailchores.py -e \"email1\" \"email2\" -c \"chore1\" \"chore2\"""", | |
formatter_class=argparse.RawDescriptionHelpFormatter, | |
) | |
parser.add_argument("-e", "--emails", nargs="*", help="Input emails to send chores to") | |
parser.add_argument("-c", "--chores", nargs="*", help="Input chores to send via email") | |
cli_args = parser.parse_args() | |
def get_json_data() -> dict: | |
""":return: the contents of chores.json""" | |
with open(Path.home() / "chores.json", "r", encoding="utf-8") as f: | |
return json.load(f) | |
def update_chores_or_emails(dict_key: str, args: list) -> dict: | |
""" | |
Retrieve the current data from chores.json. Store the data temporarily and | |
update it with the command line arguments for new emails or new chores. | |
If argument is already in json_dict, then do not add it. | |
:return: JSON dict updated with command line arguments | |
""" | |
json_dict = get_json_data() | |
new_list = json_dict[dict_key] + [i for i in args if i not in json_dict[dict_key]] | |
json_dict[dict_key] = new_list | |
return json_dict | |
def get_emails_to_assign() -> list: | |
"""Create a list of dictionaries with all emails as keys, and empty strings as values.""" | |
emails = get_json_data()["emails"] | |
return [{i: ""} for i in emails] | |
def write_chores_to_file() -> None: | |
"""Overwrite JSON file with an updated dict if emails are supplied as command line arguments.""" | |
if cli_args.chores is not None: | |
data = update_chores_or_emails("chores", cli_args.chores) | |
with open(Path.home() / "chores.json", "w", encoding="utf-8") as f: | |
json.dump(data, f, sort_keys=True, indent=4) | |
def write_emails_to_file() -> None: | |
""" | |
Overwrite JSON file with an updated dict if emails are supplied as command line arguments. | |
Also update "previous_assignments" from the JSON file to contain a new list of available | |
assignments. | |
""" | |
if cli_args.emails is not None: | |
data = update_chores_or_emails("emails", cli_args.emails) | |
with open(Path.home() / "chores.json", "w", encoding="utf-8") as f: | |
json.dump(data, f, sort_keys=True, indent=4) | |
data = update_chores_or_emails("emails", cli_args.emails) | |
data["previous_assignments"] = get_emails_to_assign() | |
with open(Path.home() / "chores.json", "w", encoding="utf-8") as f: | |
json.dump(data, f, sort_keys=True, indent=4) | |
def get_new_assignments() -> list: | |
""" | |
Loop through all emails and compare their assignments to a randomly | |
selected chore. If the chore was previously assigned, skip it. Else, | |
assign the random chore. | |
:return: A list of new chore assignments. | |
""" | |
assignments = get_json_data()["previous_assignments"] | |
new_assignments = [] | |
for dictionary in assignments: | |
email, chore = dictionary.popitem() | |
while True: | |
random_chore = random.choice(get_json_data()["chores"]) | |
if chore == random_chore: | |
continue | |
else: | |
new_assignments.append({email: random_chore}) | |
break | |
return new_assignments | |
def save_weekly_chores(chores: list) -> None: | |
"""Get updated list of chore assignments and write the list to chores.json.""" | |
weekly_chores = chores | |
json_dict = get_json_data() | |
json_dict["previous_assignments"] = weekly_chores | |
with open(Path.home() / "chores.json", "w", encoding="utf-8") as f: | |
json.dump( | |
json_dict, | |
f, | |
sort_keys=True, | |
indent=4, | |
) | |
def email_chores() -> None: | |
for dictionary in get_json_data()["previous_assignments"]: | |
email, chore = dictionary.popitem() | |
ezgmail.send(email, "Your chore for this week", f"{chore}\nDon't forget to wash your hands!") | |
time.sleep(10) # pause ten seconds between emails | |
def main(): | |
""" | |
Create JSON file if it does not exist, and write the default data fields. | |
Run the main program execution. | |
""" | |
if not Path(Path.home() / "chores.json").exists(): | |
with open(Path.home() / "chores.json", "a", encoding="utf-8") as f: | |
json.dump( | |
{"chores": [], "emails": [], "previous_assignments": []}, | |
f, | |
sort_keys=True, | |
indent=4, | |
) | |
write_chores_to_file() | |
write_emails_to_file() | |
new_assignments = get_new_assignments() | |
save_weekly_chores(new_assignments) | |
email_chores() | |
print("P.S., check your junk mail") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment