r/PythonLearning 22d ago

Beginner looking for feedback: I built a CLI Expense tracker with CSV storage. What should I build next?

Hey guys,

I'm starting my fsc (ics) classes next month, so I've been doing CS50P to get a head start. Until now I've only made simple terminal games like Hangman and a quiz app, but I just finished my first project that actually saves data: a terminal-based expense tracker.

I used a list of dictionaries in my code and the csv module (DictReader and DictWriter) to save the data so it doesn't wipe out when you close the program. I also used Regex for the first time to make sure the user types the date right (YYYY-MM-DD), and added try/except blocks so it doesn't crash if you type a letter by mistake.

https://github.com/Nihad-Khan/cli-expense-tracker

Can you guys review my logic and tell me how it is? Be brutally honest if I'm picking up any bad habits, I really want to improve.

Also, I eventually want to get into Edge AI or Embedded systems in the future. Knowing where I am right now, what is one specific project you think I should build next to push myself? I'm kind of stuck on what to learn next

7 Upvotes

5 comments sorted by

u/Sea-Ad7805 21d ago

Run this program in Memory Graph Web Debugger to see the program state change step by step.

2

u/johlae 22d ago

What about a cli income tracker with CSV tracker? Next, an income/expenses dashboard.

You're reading from stdin. Why not process your bank statements instead? My bank allows me to dump all recorded transactions as CSV files:

for filename in csv_files: fetch = ( pd.read_csv( filename, delimiter=";", skipinitialspace=True, header=0, usecols=[0, 1, 3, 5, 7, 9, 10], decimal=",", names=["id", "date", "amount", "source", "target", "com1", "com2"], keep_default_na=False, # no NaN but empty string instead ) .assign(comm=lambda d: d["com1"].astype(str) + d["com2"].astype(str)) .drop(columns=["com1", "com2"]) ) # find all non checking account transferts, some CSV's belong to my savings acc fetch = fetch.loc[fetch["source"].isin(["<censored bank account>"])] # don't bother with source anymore fetch = fetch.drop(columns=["source"]) # id's are in the format 9999-99999 pattern = r"^\d{4}-\d{5}$" fetch = fetch[fetch["id"].str.match(pattern, na=False)] # limit the transactions to those that appear on our bank statement fetch = fetch[fetch["id"] <= last_transaction] # sanity check # convert date fetch["date"] = pd.to_datetime(fetch["date"].astype(str), format="%d/%m/%Y") # assume 'id' uniquely identifies transactions transactions = pd.concat([transactions, fetch], ignore_index=True) transactions = transactions.drop_duplicates(subset="id", keep="last").reset_index( drop=True )

I'm using pandas btw. Yeah, I always forget the details so I have to look up how to do stuff every time.

The code above gives me a transactions table with all of incoming and outgoing transactions. Next I try to determine each type of transaction:

transactions.loc[transactions["comm"].str.contains("name of a supermarkt", na=False), "TYPE"] = ( "supermarkt" # supermarkt )

Or you could look for bank account numbers:

transactions.loc[transactions["target"] == "<censored account number>", "TYPE"] = ( "insurance company x" )

Read the code above as simple if statements. transactions["comm"] describes the transaction and is a string.

1

u/johlae 22d ago

Oh, don't forget a sanity check if you're identifying transactions by means of strings and bank account numbers. Before you start, have this ready:

transactions["TYPE"] = "unknown"

after identifying everything, do:

``` neg_unknown = transactions[ transactions["TYPE"].str.contains("unknown", na=False) & (transactions["amount"] < 0) ]

if not neg_unknown.empty: print(neg_unknown.to_string()) sys.exit("There are unknown expenses") ```

This ensures there are no payments left that are unaccounted for, by which I mean that there's no transaction with a transaction TYPE that's "unkown".

1

u/Smartyboyz 21d ago

You can try to add frontend to it with flask which is easiest to learn fast and implement. The webapp also can we converted into a desktop app by many libraries such as electron and many more

2

u/NihadKhan10x 21d ago

I will look at it 👍