1+
2+ """
3+ 💰 Budget Tracker CLI Tool
4+ A command-line budget tracker to manage income, expenses, and view summaries by category.
5+ Data is saved persistently in a CSV file.
6+ """
7+
8+ import csv
9+ import os
10+ from datetime import datetime
11+ from collections import defaultdict
12+
13+ DATA_FILE = "budget_data.csv"
14+ FIELDNAMES = ["date" , "type" , "category" , "description" , "amount" ]
15+
16+ # ─────────────────────────────────────────────
17+ # FILE HELPERS
18+ # ─────────────────────────────────────────────
19+
20+ def initialize_file ():
21+ """Create the CSV file with headers if it doesn't exist."""
22+ if not os .path .exists (DATA_FILE ):
23+ with open (DATA_FILE , mode = "w" , newline = "" ) as f :
24+ writer = csv .DictWriter (f , fieldnames = FIELDNAMES )
25+ writer .writeheader ()
26+
27+
28+ def load_transactions ():
29+ """Load all transactions from CSV."""
30+ transactions = []
31+ with open (DATA_FILE , mode = "r" , newline = "" ) as f :
32+ reader = csv .DictReader (f )
33+ for row in reader :
34+ row ["amount" ] = float (row ["amount" ])
35+ transactions .append (row )
36+ return transactions
37+
38+
39+ def save_transaction (entry ):
40+ """Append a single transaction to the CSV."""
41+ with open (DATA_FILE , mode = "a" , newline = "" ) as f :
42+ writer = csv .DictWriter (f , fieldnames = FIELDNAMES )
43+ writer .writerow (entry )
44+
45+
46+ # ─────────────────────────────────────────────
47+ # CORE FEATURES
48+ # ─────────────────────────────────────────────
49+
50+ def add_transaction (trans_type ):
51+ """Add an income or expense entry."""
52+ print (f"\n ── Add { trans_type .capitalize ()} ──" )
53+
54+ category = input ("Category (e.g. Food, Rent, Salary): " ).strip ()
55+ if not category :
56+ print ("❌ Category cannot be empty." )
57+ return
58+
59+ description = input ("Description (optional): " ).strip ()
60+
61+ try :
62+ amount = float (input ("Amount (₹): " ).strip ())
63+ if amount <= 0 :
64+ print ("❌ Amount must be greater than 0." )
65+ return
66+ except ValueError :
67+ print ("❌ Invalid amount. Please enter a number." )
68+ return
69+
70+ entry = {
71+ "date" : datetime .now ().strftime ("%Y-%m-%d %H:%M" ),
72+ "type" : trans_type ,
73+ "category" : category .capitalize (),
74+ "description" : description ,
75+ "amount" : round (amount , 2 ),
76+ }
77+
78+ save_transaction (entry )
79+ print (f"✅ { trans_type .capitalize ()} of ₹{ amount :.2f} added under '{ category .capitalize ()} '." )
80+
81+
82+ def view_summary ():
83+ """Show total income, expenses, and current balance."""
84+ transactions = load_transactions ()
85+
86+ if not transactions :
87+ print ("\n 📭 No transactions found." )
88+ return
89+
90+ total_income = sum (t ["amount" ] for t in transactions if t ["type" ] == "income" )
91+ total_expense = sum (t ["amount" ] for t in transactions if t ["type" ] == "expense" )
92+ balance = total_income - total_expense
93+
94+ print ("\n " + "═" * 35 )
95+ print (" 💰 BUDGET SUMMARY" )
96+ print ("═" * 35 )
97+ print (f" Total Income : ₹{ total_income :>10.2f} " )
98+ print (f" Total Expenses: ₹{ total_expense :>10.2f} " )
99+ print ("─" * 35 )
100+ balance_label = "✅ Balance" if balance >= 0 else "⚠️ Deficit"
101+ print (f" { balance_label } : ₹{ abs (balance ):>10.2f} " )
102+ print ("═" * 35 )
103+
104+
105+ def view_category_summary ():
106+ """Show spending/income broken down by category."""
107+ transactions = load_transactions ()
108+
109+ if not transactions :
110+ print ("\n 📭 No transactions found." )
111+ return
112+
113+ income_by_cat = defaultdict (float )
114+ expense_by_cat = defaultdict (float )
115+
116+ for t in transactions :
117+ if t ["type" ] == "income" :
118+ income_by_cat [t ["category" ]] += t ["amount" ]
119+ else :
120+ expense_by_cat [t ["category" ]] += t ["amount" ]
121+
122+ print ("\n " + "═" * 35 )
123+ print (" 📊 CATEGORY-WISE BREAKDOWN" )
124+ print ("═" * 35 )
125+
126+ if income_by_cat :
127+ print ("\n 📈 Income:" )
128+ for cat , amt in sorted (income_by_cat .items ()):
129+ print (f" { cat :<20} ₹{ amt :.2f} " )
130+
131+ if expense_by_cat :
132+ print ("\n 📉 Expenses:" )
133+ for cat , amt in sorted (expense_by_cat .items ()):
134+ print (f" { cat :<20} ₹{ amt :.2f} " )
135+
136+ print ("═" * 35 )
137+
138+
139+ def view_all_transactions ():
140+ """Display all recorded transactions."""
141+ transactions = load_transactions ()
142+
143+ if not transactions :
144+ print ("\n 📭 No transactions found." )
145+ return
146+
147+ print ("\n " + "═" * 70 )
148+ print (f" { 'DATE' :<17} { 'TYPE' :<10} { 'CATEGORY' :<15} { 'DESCRIPTION' :<15} { 'AMOUNT' :>8} " )
149+ print ("─" * 70 )
150+
151+ for t in transactions :
152+ symbol = "+" if t ["type" ] == "income" else "-"
153+ print (
154+ f" { t ['date' ]:<17} { t ['type' ]:<10} { t ['category' ]:<15} "
155+ f"{ t ['description' ][:14 ]:<15} { symbol } ₹{ t ['amount' ]:>7.2f} "
156+ )
157+
158+ print ("═" * 70 )
159+
160+
161+ def delete_all_transactions ():
162+ """Clear all transaction data after confirmation."""
163+ confirm = input ("\n ⚠️ Are you sure you want to delete ALL data? (yes/no): " ).strip ().lower ()
164+ if confirm == "yes" :
165+ with open (DATA_FILE , mode = "w" , newline = "" ) as f :
166+ writer = csv .DictWriter (f , fieldnames = FIELDNAMES )
167+ writer .writeheader ()
168+ print ("🗑️ All transactions deleted." )
169+ else :
170+ print ("❌ Cancelled." )
171+
172+
173+ # ─────────────────────────────────────────────
174+ # MAIN MENU
175+ # ─────────────────────────────────────────────
176+
177+ def print_menu ():
178+ print ("\n " + "═" * 35 )
179+ print (" 💰 BUDGET TRACKER CLI" )
180+ print ("═" * 35 )
181+ print (" 1. ➕ Add Income" )
182+ print (" 2. ➖ Add Expense" )
183+ print (" 3. 📋 View All Transactions" )
184+ print (" 4. 📊 View Summary" )
185+ print (" 5. 🗂️ Category-wise Breakdown" )
186+ print (" 6. 🗑️ Clear All Data" )
187+ print (" 7. 🚪 Exit" )
188+ print ("═" * 35 )
189+
190+
191+ def main ():
192+ initialize_file ()
193+ print ("\n 👋 Welcome to Budget Tracker!" )
194+
195+ while True :
196+ print_menu ()
197+ choice = input (" Enter your choice (1-7): " ).strip ()
198+
199+ if choice == "1" :
200+ add_transaction ("income" )
201+ elif choice == "2" :
202+ add_transaction ("expense" )
203+ elif choice == "3" :
204+ view_all_transactions ()
205+ elif choice == "4" :
206+ view_summary ()
207+ elif choice == "5" :
208+ view_category_summary ()
209+ elif choice == "6" :
210+ delete_all_transactions ()
211+ elif choice == "7" :
212+ print ("\n 👋 Goodbye! Keep tracking your budget. 💸\n " )
213+ break
214+ else :
215+ print ("❌ Invalid choice. Please enter a number between 1 and 7." )
216+
217+
218+ if __name__ == "__main__" :
219+ main ()
0 commit comments