Homework 4

Due: Friday, February 28 at 11:59 pm

Submission: On Courseworks

Instructions

Please read the instructions carefully. An incorrectly formatted submission may not be counted.

There is one question in this assignment. This assignment starts from skeleton code called finances.py. Please include comments in your code detailing your thought process where appropriate. Put finances.py in a folder called uni-hw4 (so for me this would be tkp2108-hw4). Then compress this folder into a .zip file. For most operating systems, you should be able to right click and have a “compress” option in the context menu. This should create a file called tkp2108-hw4.zip (with your uni). Submit this on courseworks.

Personal Finances App

In this homework, we will build a real application using lists, dictionaries, and files.

The skeleton code below provides a simple version of the application. This application does the following:

There is a lot of code to unpack, with comments inline to provide context. We will walkthrough this code in class, but you do not need to modify any of the existing functions.

Your task is to do the following.

Calculators

Add at least 2 of the commands from the following list. Do not choose the same calculation twice (e.g. sum credits and sum debits).

Listing

Add at least 1 of the commands from the following list:

Cleaning

Add at least 1 of the commands from the following list:

Other

Add at least 1 other functionality, use your imagination! If you can’t think of anything, you can do one from Listing or Cleaning above.

Skeleton Code

import os
from csv import DictReader, DictWriter


# This is some example data, so you can see the format
# and the data structures (list of dicts)
EXAMPLE_FINANCES = [
    {"date": "2025-02-17", "amount": "500", "category": "Work Study"},
    {"date": "2025-02-17", "amount": "-200", "category": "Senior Gala"},
    {"date": "2025-02-18", "amount": "-15.5", "category": "JJ's"},
    {"date": "2025-02-18", "amount": "50", "category": "Dining Dollars"},
    {"date": "2025-02-19", "amount": "-5", "category": "Joe Coffee"},
    {"date": "2025-02-20", "amount": "-50.0", "category": "Music Hum Performance"},
    {"date": "2025-02-21", "amount": "-5", "category": "Joe Coffee"},
]


#####################
# UTILITY FUNCTIONS #
#####################
def write_finances_csv(file_path, data):
    """This is a helper function to write the list
    of dictionaries in the format above to a file"""
    file = open(file_path, 'w')
    csvwriter = DictWriter(file, fieldnames=["date", "amount", "category"])
    for row in data:
        csvwriter.writerow(row)
    file.close()


def read_finances_csv(file_path):
    """This is a helper function to read data in the format
    above from a file"""
    if not os.path.exists(file_path):
        return EXAMPLE_FINANCES
    file = open(file_path, 'r')
    data = []
    for row in DictReader(file, fieldnames=["date", "amount", "category"]):
        data.append(row)
    file.close()
    return data

#####################
# COMMAND FUNCTIONS #
#####################
def print_all_entries(data):
    """This function takes a list of dictionaries and prints out
    all of the entries with some nice spacing and formatting"""

    print("\nAll entries:")

    for i, entry in enumerate(data):
        date = entry["date"]
        amount = entry["amount"]
        category = entry["category"]
        print(f"\t{i}: {date}: ${amount}\t{category}")

    # return data without changes
    return data

def reset_to_examples(data):
    """This function ignores `data` and instead 
    returns the example data from above"""
    return EXAMPLE_FINANCES

def clear_entries(data):
    """This data removes all entries from data. We can 'cheat'
    and just ignore `data` and return an empty list instead"""
    return []

def add_entry(data):
    """This function will prompt the user for date, amount, and category,
    and append the new dictionary entry to the end of the list"""
    date = input("Enter the date: ")
    amount = input("Enter the amount: ")
    category = input("Enter the category: ")
    data.append({"date": date, "amount": amount, "category": category})
    return data

def delete_entry(data: list):
    """This function will show the entries to the user and then 
    prompt them for the index of the entry they want to delete"""
    """delete an entry"""
    # First print all the entries to remind the user
    print_all_entries(data)
    
    # Now ask for an entry
    index = int(input("Enter the index of the entry to delete, or -1 to cancel: "))
    
    if index >= 0:
        # remove it from the list
        data.pop(index)
    return data


#################
# MAIN FUNCTION #
#################
def main():
    """This is our main function. It is the primary entry point into our program
    and it does a couple of things. See the inline comments for details"""
    

    # Our list of commands.
    # This is a dictionary mapping a command (in our case an integer)
    # to the description of the command and the function. All of our
    # commands take the the form:
    #
    #     new_data = function(current_data)
    #
    # So they will take the current list of expenses and return a (maybe different)
    # list of expenses.
    #
    # You will add new commands here as you implement them
    commands = {
        "0": ["Save and quit", None],
        "1": ["Print all entries", print_all_entries],
        "2": ["Reset to examples", reset_to_examples],
        "3": ["Clear all entries", clear_entries],
        "4": ["Add new entry", add_entry],
        "5": ["Delete an entry", delete_entry],
        # YOUR NEW COMMANDS HERE
    }

    # This is the file we will work from
    finances_csv_filename = "finances.csv"
    
    # Load up the data
    data = read_finances_csv(finances_csv_filename)
    
    # Print a welcome message
    print("Welcome to the finances app!")
    
    # Enter an infinite loop until the user
    # chooses to quit
    while True:
        # Print the available commands
        print("\nPick a command to continue:")
        for command_number in commands:
            description, function = commands[command_number]
            print(f"\t{command_number}.  {description}")
    
        # Get the user's choice
        choice = input("Enter a command number: ")
        
        # If the choice is not valid, print an error message
        if choice not in commands:
            print("Invalid choice. Please try again.")
            continue
        
        # Handle saving and quitting
        if choice == "0":
            break
        
        # Otherwise, call the function associated with the choice
        # Pass in the data and return the new/modified data
        data = commands[choice][1](data)
        
    # Finally, save the changes
    write_finances_csv(finances_csv_filename, data)
    
   
    
if __name__ == "__main__":
    main()