-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
307 lines (259 loc) · 13.2 KB
/
Copy pathmain.py
File metadata and controls
307 lines (259 loc) · 13.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
import os
import csv
import time
from datetime import date, timedelta
import pandas as pd
def validate_amount(str: str) -> float:
"""Checks if a string can be converted to valid $ amount (positive float with 2 or less decimals).
\nIf invalid, asks the user to enter a new string until it is valid.
"""
while True:
try:
float(str)
if float(str) < 0:
str = input("Please enter a positive $ amount. ")
continue
if ("." in str) and len(str.split(".")[1]) > 2:
str = input("Please enter a $ amount with 2 or less decimals. $ ")
continue
return float(str)
except ValueError:
str = input("Please enter a numerical $ amount. $")
def validate_date(date_str: str) -> date:
"""Checks that a string is a valid date. If invalid, asks for new input until it is valid.
Returns a date object."""
while True:
try:
user_date = date.fromisoformat(date_str)
return user_date
except ValueError:
date_str = input("Invalid date, please try again in format YYYY-MM-DD ")
def create_account():
"""Creates a new account with a unique name.\n
Appends [account_name, starting balance] to the balances.csv file.\n
Also creates a new account_name.csv file to store transactions.\n
Catches exceptions in the file handling process."""
account_name = input("Enter a name for your account: ").upper()
while (f"{account_name}.csv" in os.listdir("program_files")):
account_name = input("That account name is not available, please enter another name: ").upper()
starting_balance = validate_amount(input(f"What is the starting balance of your {account_name} account? $"))
try:
with open("program_files/BALANCES.csv", "a", newline="") as balance_csv:
balance_writer = csv.writer(balance_csv)
balance_writer.writerow([account_name, starting_balance])
with open(f"program_files/{account_name}.csv", "x", newline="") as account_csv:
pass
except Exception as e:
print(f"An error occurred. Error message '{e}'. Please try again.")
def make_transaction(account_name: str, amount: float, date: date, category: str, memo: str) -> None:
"""Appends the transaction to the individual account file in the format [date, category, amount, memo] and the all accounts file in the format [account name, date, category, amount, memo].
Updates the balance file.
Prints a summary of the transaction and the new account balance."""
try:
with open(f"program_files/{account_name}.csv", "a", newline="") as account_csv: # appends the transaction to the individual account file
transaction_writer = csv.writer(account_csv)
transaction_writer.writerow([date, category, -amount, memo])
with open("program_files/ALL ACCOUNTS.csv", "a", newline="") as all_csv: # appends the transaction to the all accounts file
all_writer = csv.writer(all_csv)
all_writer.writerow([account_name, date, category, -amount, memo])
current_balances = [] # updates the balance in the balances file
this_account_balance = 0
with open("program_files/BALANCES.csv", "r", newline="") as balances_csv:
balance_reader = csv.reader(balances_csv)
for row in balance_reader:
if row[0] == account_name:
this_account_balance = round(float(row[1])-amount,2)
current_balances.append([row[0], this_account_balance])
else:
current_balances.append(row)
with open("program_files/BALANCES.csv", "w", newline="") as balances_csv:
balance_writer = csv.writer(balances_csv)
balance_writer.writerows(current_balances)
if this_account_balance <= 0:
print(f"\nWarning: your {account_name} account now has a balance of ${float(this_account_balance):.2f} ( • ᴖ • 。)")
else:
print(f"\nYour {account_name} account now has a balance of ${float(this_account_balance):.2f} (˶ᵔ ᵕ ᵔ˶)")
print(f"\t${float(-amount):.2f} - {category} - {date}")
time.sleep(1)
except Exception as e:
print("An exception occurred. Transaction not recorded. Message: " + str(e))
def search_transaction(start_date: date, end_date: date, account: str) -> None:
"""Prints transactions in the file {account.csv} that have a date that is between {start_date} and {end_date}, inclusive.
Sorts them by date before printing. Each transaction is formatted [date, category, amount, memo]"""
print(f"Transactions from {start_date} to {end_date}:")
try:
transactions = pd.read_csv(f"program_files/{account}.csv", header=None, parse_dates=[0])
transactions.columns = ["DATE", "CATEGORY", "AMOUNT", "MEMO"]
transactions.sort_values(by="DATE", inplace=True)
in_range = transactions[(transactions["DATE"] >= start_date.isoformat()) & (transactions["DATE"] <= end_date.isoformat())]
if len(in_range) == 0:
print("\tNo transactions found.")
else:
print(in_range.to_string(index=False))
except Exception as e:
print(e)
def display_stats(account_name: str) -> None:
'''Print the total amount spent for each category of transactions in the 'account_name' account'''
try:
transactions = pd.read_csv(f"program_files/{account_name}.csv", header=None, parse_dates=[0])
transactions.columns = ["DATE", "CATEGORY", "AMOUNT", "MEMO"]
print(transactions.groupby("CATEGORY", as_index=False)["AMOUNT"].sum())
except Exception as e:
print("An exception occurred.")
print(e)
def menu1():
print("\n✦ Record a transaction ✦")
account_name = input("\tWhich account would you like to access? ").upper()
while (f"{account_name}.csv" not in os.listdir("program_files") or (account_name == "ALL ACCOUNTS") or account_name == "BALANCES"):
account_name = input("\tAccount not found. Please enter a valid account name, or type 'm' to return to the menu to create an account. ").upper()
if account_name == "M":
return
debit_credit = input("Would you like to record a debit or a credit? (d/c) ").lower()
while (debit_credit != "d" and debit_credit != "c"):
debit_credit = input("Not a valid choice, please enter 'd' or 'c'. ").lower()
transaction_amount = validate_amount(input("\tEnter the transaction amount. $"))
if debit_credit == "c":
transaction_amount *= -1
transaction_category = input("\tEnter a category for the transaction. ").upper()
transaction_memo = input("\tEnter a memo for the transaction (optional) ")
transaction_date = input("\tEnter the transaction date in the format YYYY-MM-DD (if blank, defaults to today) ")
if transaction_date == "":
transaction_date = date.today()
else:
transaction_date = validate_date(transaction_date)
make_transaction(account_name, transaction_amount, transaction_date, transaction_category, transaction_memo)
print("\nTransaction recorded ( • ᴗ - ) ~ ✧")
def menu2():
print("\n✦ Record a transfer ✦")
account_name = input("\tWhich account would you like to access? ").upper()
while (f"{account_name}.csv" not in os.listdir("program_files") or (account_name == "ALL ACCOUNTS") or account_name == "BALANCES"):
account_name = input("\tAccount not found. Please enter a valid account name, or type 'm' to return to the menu to create an account. ").upper()
if account_name == "M":
return
transfer_to_name = input("\tWhich account would you like to transfer to? ").upper()
while (f"{transfer_to_name}.csv" not in os.listdir("program_files") or (transfer_to_name == "ALL ACCOUNTS") or transfer_to_name == "BALANCES"):
transfer_to_name = input("\tAccount not found. Please enter a valid account name, or type 'm' to return to the menu to create an account. ").upper()
if transfer_to_name == "M":
return
transaction_amount = validate_amount(input("\tEnter the amount to be transferred. $"))
transaction_memo = input("\tEnter a memo for the transaction (optional) ")
transaction_date = input("\tEnter the transaction date in the format (if blank, defaults to today) ")
if transaction_date == "":
transaction_date = date.today()
else:
transaction_date = validate_date(transaction_date)
make_transaction(account_name, transaction_amount, transaction_date, "TRANSFER TO " + transfer_to_name, transaction_memo)
make_transaction(transfer_to_name, -transaction_amount, transaction_date, "TRANSFER FROM " + account_name, transaction_memo)
print("Transfer recorded ( • ᴗ - ) ~ ✧")
def menu3():
print("\n✦ View transactions ✦")
print("\t1. This current month")
print("\t2. The past week")
print("\t3. From a specific date range")
menu3_choice = input("Pick which transactions you would like to view (1/2/3) ")
valid_choices = ["1", "2", "3"]
while (menu3_choice not in valid_choices):
menu3_choice = input("Invalid option. Please try again. ")
account_name = input("\tWhich account would you like to view the transactions from? ").upper()
while (f"{account_name}.csv" not in os.listdir("program_files") or (account_name == "ALL ACCOUNTS") or account_name == "BALANCES"):
account_name = input("\tAccount not found. Please enter a valid account name, or type 'm' to return to the menu to create an account. ").upper()
if account_name == "M":
return
date1 = None
date2 = None
if menu3_choice == "1":
date1 = date(date.today().year, date.today().month, 1)
# sets date1 as the first day of the current month
date2 = date(date.today().year, date.today().month, 28) + timedelta(4)
date2 = date2 - timedelta(1)
# sets date2 as the last day of the current month
if menu3_choice == "2":
date2 = date.today()
date1 = date2 - timedelta(7)
if menu3_choice == "3":
date1 = input("\tEnter the starting date in the format YYYY-MM-DD (if blank, defaults to today) ")
if date1 == "":
date1 = date.today()
else:
date1 = validate_date(date1)
date2 = input("\tEnter the ending date in the format YYYY-MM-DD (if blank, defaults to today) ")
if date2 == "":
date2 = date.today()
else:
date2 = validate_date(date2)
search_transaction(date1, date2, account_name)
time.sleep(3)
def menu4():
print("\n✦ View account balances ✦")
try:
with open("program_files/BALANCES.csv", "r", newline="") as balances:
balance_reader = csv.reader(balances)
for row in balance_reader:
print(f"\t{row[0]} account has a balance of ${float(row[1]):.2f}")
except Exception as e:
print("Exception occurred.")
print(e)
time.sleep(1.5)
def menu5():
print("\n✦ Create new account ✦")
create_account()
print("Account created ( • ᴗ - ) ~ ✧")
def menu6():
print("\n✦ Display stats ✦")
account_name = input("\tWhich account would you like to view the stats for? ").upper()
while (f"{account_name}.csv" not in os.listdir("program_files") or (account_name == "ALL ACCOUNTS") or account_name == "BALANCES"):
account_name = input("\tAccount not found. Please enter a valid account name, or type 'm' to return to the menu to create an account. ").upper()
if account_name == "M":
return
print(f"Total spending stats for your {account_name} account:")
display_stats(account_name)
time.sleep(2)
#main loop
timeDelay = .4
print("\n⋆˙⟡ Welcome to the Finance Tracker ⟡˙⋆\n")
try:
with open("program_files/ALL ACCOUNTS.csv", "a", newline="") as all_csv:
pass
with open("program_files/BALANCES.csv", "a", newline="") as all_csv:
pass
except FileNotFoundError:
os.mkdir("program_files")
try:
with open("program_files/ALL ACCOUNTS.csv", "a", newline="") as all_csv:
pass
with open("program_files/BALANCES.csv", "a", newline="") as all_csv:
pass
except Exception as e:
print("There was an exception. Please check that there is a program_files folder.")
print(e)
menu_control = ""
while True:
time.sleep(timeDelay)
print("⋆ Main Menu ⋆")
print("\t1. Record transaction (debit/credit)")
print("\t2. Record transfer between accounts")
print("\t3. View transactions")
print("\t4. View account balances")
print("\t5. Create new account")
print("\t6. Display stats")
time.sleep(timeDelay)
menu_control = input("\nSelect a menu option (1, 2, 3, etc.) or type 'quit'\n")
if menu_control.lower() == "quit":
break
if menu_control == "1":
menu1()
elif menu_control == "2":
menu2()
elif menu_control == "3":
menu3()
elif menu_control == "4":
menu4()
elif menu_control == "5":
menu5()
elif menu_control == "6":
menu6()
else:
time.sleep(timeDelay)
print("\nInvalid input. Please enter a number from the menu.")
print("")
time.sleep(timeDelay)