3
\$\begingroup\$

This is a personal project of mine. I made a simple seven day agenda program in Python that stores tasks as a string, saves it into a .dat file, and returns that task when you input a day of the week. This works on a terminal. I'm new to programming in general.

This is Python 3.8.2, 32 bit, and in a virtual environment.

# Pickle module allows you to save data after program closes
import pickle

week = {"Sunday": [], "Monday": [], "Tuesday": [],
        "Wednesday": [], "Thursday": [], "Friday": [], "Saturday": []}


def agenda():
    option = input(
        "What would you like to do?  (A - look at agenda, B - create task, C - clear list, or D - quit) ").upper()
    if option == "A":
        look_at_agenda()
    elif option == "B":
        create_task()
    elif option == "C":
        clear_list()
    elif option == "D":
        print("Have a nice day!")
    else:
        print("Invalid option.")
        agenda()

# global allows day variable in other functions to work even without declaring it


def valid_days():
    global day
    day = input("Which day? ").capitalize()
    week_list = [day_list for day_list in week.keys()]
    if day not in week_list:
        print("Invalid day.")
        valid_days()


# Loads whatever string from week.dat file
def look_at_agenda():
    see_all = input("See entire week? Y - yes, N - no. ").upper()
    if see_all == "Y":
        week = pickle.load(open("week.dat", "rb"))
        print(week)
    elif see_all == "N":
        valid_days()
        week = pickle.load(open("week.dat", "rb"))
        print(week[day])
    else:
        print("Invalid option.")
        look_at_agenda()
    agenda()


def create_task():
    valid_days()
    task = input("Describe your task. ")
    # Loads string from week.dat file; when program restarts, it allows you to create a task without deleting the saved ones
    week = pickle.load(open("week.dat", "rb"))
    week[day].append(task)
    # Saves string into week.dat file so it remembers when you open the program again
    pickle.dump(week, open("week.dat", "wb"))
    agenda()


def clear_list():
    clear_all = input("Clear all lists? Y - yes, N - no ").upper()
    if clear_all == "N":
        valid_days()
        week[day].clear()
        print(f"List cleared for {day}!")
        # saves the new list into a week.dat file
        pickle.dump(week, open("week.dat", "wb"))
    elif clear_all == "Y":
        [week[day].clear() for day in week]
        # saves the now empty list into a week.dat file
        pickle.dump(week, open("week.dat", "wb"))
    else:
        print("Invalid option.")
        clear_list()
    agenda()

agenda()
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Welcome to the code review community. The code looks clean, and follows the PEP8 style guide. Here are a few suggestions/modifications you can make though:

  1. Do not use global variables.

  2. Instead of calling agenda() at the end, put it inside the if __name__ block.

  3. Since you're working with python 3.8+, you may also add type hinting for function arguments.

  4. For user input/validation, you can make use of a while loop:

     from typing import List
    
     def read_user_input_from_options(message: str, valid_inputs: List = None):
         """
         Show `message` in the console while asking for user input.
    
         If `valid_inputs` is not provided, returns the value from user as is.
         """
         if valid_inputs:
             valid_inputs = map(lambda x: x.upper(), valid_inputs)
         while True:
             user_input = input(message)
             if not valid_inputs:
                 return user_input
             user_input = user_input.upper()
             if user_input in valid_inputs:
                 return user_input
             print("Invalid option chosen. Try again.")
    

    The above is an example suited for your case, where all the input option you need are capitalised alphabets. It can be extended to support other validators as per requirements.

  5. Use multiline strings. Makes it more readable. It is more a personal preference than rules to live by.

  6. You can map the option to a function call (see below):

     def quit_program():
         print("Have a nice day!")
         return
    
    
     def main():
         OPTION_TO_FUNCTION = {
             "A": look_at_agenda,
             "B": create_task,
             "C": clear_list,
             "D": quit_program,
         }
         option = read_user_input_from_options(
             """
             What would you like to do?
             A - look at agenda
             B - create task
             C - clear list
             D - quit
             """,
             valid_inputs=["a", "b", "C", "d"],
         )
         return OPTION_TO_FUNCTION[option]()
    

    where main is the function called from inside your if __name__ == "__main__" block.

  7. Make use of a class to keep the state of current execution. This would accompany my point about avoiding globals as well.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.