-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
443: Capstone: Building a Data-Backed Personal Finance Tracker
We've spent a lot of time learning the individual components of Python—lists, dictionaries, file handling, and classes. Now, it's time to stop treating them like isolated tools and start treating them like a toolkit. For this capstone, we're building a Personal Finance Tracker. Instead of trying to build a full-blown app with a GUI, we're going to focus on the engine: the logic that takes raw spending data and turns it into something meaningful.
Defining the data source
Most banks let you export your transactions as a CSV file. It's a messy format, but it's the industry standard for a reason. I've created a simple dummy file called transactions.csv with columns for the date, the description, and the amount. To keep this manageable, our first goal is to get that data into Python and calculate our total spending.
import csv
def calculate_total_spend(filename):
total = 0
with open(filename, mode='r') as file:
reader = csv.reader(file)
next(reader) # Skip the header row
for row in reader:
total += float(row[2])
return total
# Assuming we have a file with: Date, Description, Amount
print(f"Total spent: ${calculate_total_spend('transactions.csv')}")
The "Index Trap" and a better approach
Now, here is where I usually trip up when I'm rushing a prototype. In the code above, I used row[2] to get the amount. That works fine until you realize that if the bank adds a "Category" column in the middle of the CSV, row[2] is suddenly a string like "Groceries" and the whole program crashes with a ValueError. It's fragile code.
I'm going to pivot. Instead of using the csv.reader, I'll use csv.DictReader. This treats every row as a dictionary where the keys are the header names. It makes the code slightly slower, but it's infinitely more readable and robust. Let's rewrite the ingestion logic to use a list of dictionaries.
import csv
def load_transactions(filename):
transactions = []
with open(filename, mode='r') as file:
# DictReader uses the first row as keys for every subsequent row
reader = csv.DictReader(file)
for row in reader:
# Convert amount to float immediately so we don't have to do it later
row['Amount'] = float(row['Amount'])
transactions.append(row)
return transactions
data = load_transactions('transactions.csv')
# Now I can access data[0]['Amount'] instead of data[0][2]
Building a keyword-based categorizer
Raw bank descriptions are useless. "SQ *COFFEE SHOP 123" should just be "Dining". To handle this, I'll create a mapping of keywords to categories. I'll use a dictionary where the keys are categories and the values are lists of keywords. This allows me to easily add new keywords to an existing category without changing the logic.
CATEGORIES = {
"Dining": ["starbucks", "mcdonalds", "restaurant", "cafe"],
"Utilities": ["electric", "water", "internet", "verizon"],
"Shopping": ["amazon", "walmart", "target"],
}
def categorize_transaction(description):
description = description.lower()
for category, keywords in CATEGORIES.items():
for word in keywords:
if word in description:
return category
return "Other" # Default if no keyword matches
# Let's apply this to our data
for t in data:
t['Category'] = categorize_transaction(t['Description'])
Aggregating the final report
The final piece is the aggregation. I want to know exactly how much I spent per category. A dictionary is perfect here; the key will be the category name, and the value will be the running total. I'll iterate through our categorized list and sum the amounts.
def generate_report(transactions):
report = {}
for t in transactions:
cat = t['Category']
amount = t['Amount']
report[cat] = report.get(cat, 0) + amount
return report
final_report = generate_report(data)
for cat, total in final_report.items():
print(f"{cat}: ${total:.2f}")
By breaking this down—ingestion, cleaning, categorization, and aggregation—we've built a data pipeline. We moved from a fragile, index-based approach to a flexible, dictionary-based system that can handle changing CSV formats and expanding category lists without breaking.
📋 Practical Task
Implement a Monthly Budget Alert System
Your task is to extend the Finance Tracker by adding a budget alert system. Create a new dictionary called BUDGETS where the keys are categories (e.g., "Dining", "Shopping") and the values are the maximum dollar amount you are willing to spend in that category.
Write a function called check_budget_alerts(report, budgets) that takes the final spending report and the budget dictionary. The function should print a warning message for every category where the actual spending exceeded the budget, including the amount by which you went over.
Requirements:
- Define a
BUDGETSdictionary with at least three categories. - The output should look like:
"ALERT: Dining exceeded budget by $15.50!" - Ensure the function handles categories that exist in the report but are not defined in your budget dictionary (these should be ignored).
There are no comments for now.