John: Week 5: Build Your Budget Tracker

Demo Day is three weeks out (April 29). You have the ambition and the ideas - now you need running code. Your budget tracker concept is strong, but today we're scoping to something you can build and demo in one session: add expenses and see a summary. Forecasting, trends, and security are real features - they come after you have a working app to build on top of.

Today's goal: walk out of class with a running Flask app - a home page, an expense form, and a summary page with totals. Everything else is a feature you stack on later.

Check In: What Are You Working On?

  1. Create a file called TODO.md in your project folder (right-click in the file explorer > New File)
  2. Write one sentence about your plan for today. Example:
Build a working Flask app with an expense form and summary page.
  1. Save it (Cmd+S)

Part 1: Essential Catch-up

Fix favs.py - use your variables

Your favs.py defines variables but prints hardcoded strings instead of referencing them. Quick fix - this is the exact same f-string pattern you'll use in your Flask app.

Open favs.py and find lines like:

print("Drake")

Change them to use f-strings that reference your variables:

artist = "Drake"
print(f"My favorite artist is {artist}")

The {artist} part pulls the value from the variable instead of repeating the text. Do the same for any other hardcoded prints in the file.

Optional: v0-prompt.md and warmup.py templates are in your Week 4 guide if you want them. Not required today - the Flask app is the priority.

Part 2: Python Practice

Create a new file called practice.py.

Challenge: Build an expense report that stores expenses as variables, calculates totals, and prints a formatted summary showing how much budget you have left.

Your program should:

  • Store a budget amount (like 500) as a variable
  • Store 3-4 expenses as separate variables with descriptive names (e.g. rent, groceries, gas, subscriptions)
  • Calculate the total spent by adding all the expense variables together
  • Calculate the remaining budget (budget minus total spent)
  • Print a formatted expense report with borders, each expense listed with its dollar amount, the total, and the remaining balance

Example output (use your own numbers):

╔══════════════════════════════════════╗
  Expense Report - John's Budget
──────────────────────────────────────
  Budget:         $500.00
──────────────────────────────────────
  Rent:           $200.00
  Groceries:      $85.50
  Gas:            $45.00
  Subscriptions:  $15.99
──────────────────────────────────────
  Total Spent:    $346.49
  Remaining:      $153.51
╚══════════════════════════════════════╝

Hints:

  • Define each expense with a clear name: rent = 200.00
  • Add them up: total = rent + groceries + gas + subscriptions
  • Use f-strings with formatting for dollar amounts: f"${total:.2f}" gives you two decimal places
  • Build borders with print("═" * 38)

Resources:

  • Variables - how to store text and numbers

  • Print Statements - how to display output, use f-strings, and format cards

Part 3: Build Your Budget Tracker

Time to build the real thing. You're creating a Flask app with three pages:

  • Home - landing page for Budget Buddy
  • Add Expense - form with description, amount, and category
  • Summary - table of all expenses with a calculated total

Step 1: Install Flask

Open the terminal (View > Terminal) and run:

pip install flask

Use pip3 if pip doesn't work.

Step 2: Create app.py

Create a new file called app.py and paste this entire block:

from flask import Flask, request

app = Flask(__name__)

expenses = [
    {"description": "Chipotle", "amount": 12.50, "category": "food"},
    {"description": "Gas", "amount": 45.00, "category": "transport"},
    {"description": "Spotify", "amount": 11.99, "category": "entertainment"},
]


@app.route("/")
def home():
    return """
    <html>
    <head>
        <meta charset="utf-8">
        <title>Budget Buddy</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="min-h-screen bg-slate-950 text-slate-100">
        <div class="mx-auto max-w-2xl px-6 py-16">
            <h1 class="text-4xl font-bold text-emerald-400">Budget Buddy</h1>
            <p class="mt-4 text-lg text-slate-300">Track your spending. Know where your money goes.</p>
            <div class="mt-10 flex flex-wrap gap-4">
                <a class="rounded-lg bg-emerald-500 px-6 py-3 font-semibold text-slate-950 hover:bg-emerald-400" href="/add">Add Expense</a>
                <a class="rounded-lg border border-emerald-500 px-6 py-3 font-semibold text-emerald-300 hover:bg-emerald-500/10" href="/summary">View Summary</a>
            </div>
        </div>
    </body>
    </html>
    """


@app.route("/add", methods=["GET", "POST"])
def add():
    message = ""
    if request.method == "POST":
        description = request.form["description"]
        amount = float(request.form["amount"])
        category = request.form["category"]
        expenses.append({
            "description": description,
            "amount": amount,
            "category": category,
        })
        message = f'<p class="mb-4 rounded-lg bg-emerald-500/20 px-4 py-2 font-semibold text-emerald-300">Added: {description} (${amount:.2f})</p>'

    return f"""
    <html>
    <head>
        <meta charset="utf-8">
        <title>Add Expense - Budget Buddy</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="min-h-screen bg-slate-950 text-slate-100">
        <div class="mx-auto max-w-2xl px-6 py-12">
            <h1 class="text-2xl font-bold text-emerald-400">Add Expense</h1>
            {message}
            <form method="POST" class="mt-6 space-y-4 rounded-xl border border-slate-700 bg-slate-900/80 p-6">
                <div>
                    <label class="block text-sm font-medium text-sky-300" for="description">What did you spend on?</label>
                    <input class="mt-1 w-full rounded-lg border border-slate-600 bg-slate-900 px-3 py-2 text-white placeholder-slate-500 focus:border-emerald-500 focus:outline-none" type="text" id="description" name="description" placeholder="e.g. Chipotle, gas, Netflix" required>
                </div>
                <div>
                    <label class="block text-sm font-medium text-sky-300" for="amount">How much? ($)</label>
                    <input class="mt-1 w-full rounded-lg border border-slate-600 bg-slate-900 px-3 py-2 text-white focus:border-emerald-500 focus:outline-none" type="number" id="amount" name="amount" step="0.01" min="0" placeholder="0.00" required>
                </div>
                <div>
                    <label class="block text-sm font-medium text-sky-300" for="category">Category</label>
                    <select class="mt-1 w-full rounded-lg border border-slate-600 bg-slate-900 px-3 py-2 text-white focus:border-emerald-500 focus:outline-none" id="category" name="category">
                        <option value="food">Food</option>
                        <option value="transport">Transport</option>
                        <option value="entertainment">Entertainment</option>
                        <option value="bills">Bills</option>
                        <option value="other">Other</option>
                    </select>
                </div>
                <button class="w-full rounded-lg bg-emerald-500 py-3 font-semibold text-slate-950 hover:bg-emerald-400" type="submit">Add Expense</button>
            </form>
            <p class="mt-8 text-sky-300"><a class="underline hover:text-white" href="/">Home</a> &middot; <a class="underline hover:text-white" href="/summary">View Summary</a></p>
        </div>
    </body>
    </html>
    """


@app.route("/summary")
def summary():
    total = 0
    rows = ""
    for expense in expenses:
        total = total + expense["amount"]
        rows += f"""
            <tr class="border-b border-slate-700">
                <td class="px-4 py-3">{expense["description"]}</td>
                <td class="px-4 py-3">${expense["amount"]:.2f}</td>
                <td class="px-4 py-3 capitalize">{expense["category"]}</td>
            </tr>
        """

    if len(expenses) == 0:
        rows = '<tr><td colspan="3" class="px-4 py-6 text-center text-slate-400">No expenses yet. <a class="text-sky-400 underline" href="/add">Add one.</a></td></tr>'

    return f"""
    <html>
    <head>
        <meta charset="utf-8">
        <title>Summary - Budget Buddy</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="min-h-screen bg-slate-950 text-slate-100">
        <div class="mx-auto max-w-3xl px-6 py-12">
            <h1 class="text-2xl font-bold text-emerald-400">Spending Summary</h1>
            <div class="mt-6 overflow-hidden rounded-xl border border-slate-700">
                <table class="w-full border-collapse text-left text-sm">
                    <thead class="bg-slate-900 text-sky-300">
                        <tr>
                            <th class="px-4 py-3">Description</th>
                            <th class="px-4 py-3">Amount</th>
                            <th class="px-4 py-3">Category</th>
                        </tr>
                    </thead>
                    <tbody class="divide-y divide-slate-700">
                        {rows}
                    </tbody>
                </table>
            </div>
            <p class="mt-6 text-xl font-bold text-emerald-400">Total: ${total:.2f}</p>
            <p class="mt-8 text-sky-300"><a class="underline hover:text-white" href="/add">Add Expense</a> &middot; <a class="underline hover:text-white" href="/">Home</a></p>
        </div>
    </body>
    </html>
    """


if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=5000)

Here's what's going on:

  • expenses starts with 3 example items so your summary page has data right away. Each expense is a dictionary - a set of key-value pairs like "description": "Chipotle". The whole list of dictionaries is your app's database for now (data resets when the server restarts, which is fine).

  • / is the home page with links to Add and Summary.

  • /add shows a form. When you submit it (POST), the code grabs the form data and appends a new dictionary to the expenses list.

  • /summary loops through every expense, builds a table row for each one, and adds up the total.

  • Tailwind CSS loads from the CDN <script> tag in each page's <head>.

Step 3: Run and test

  1. Save app.py (Cmd+S)
  2. Click the play button (top-right triangle)
  3. Open http://localhost:5000 in your browser (Codespaces: check the Ports tab)
  4. Click View Summary - you should see the 3 example expenses and a total of $69.49
  5. Click Add Expense, fill in the form, submit it
  6. Go back to Summary - your new expense should appear with an updated total

To stop the server: click inside the terminal and press Ctrl+C.

Challenge: Add a budget limit

Near the top of app.py, right below expenses = [...], add a variable:

budget_limit = 500

Then on the summary page, right after the total line, add a line showing remaining balance:

<p class="mt-2 text-lg text-sky-300">Budget: ${budget_limit:.2f} | Remaining: ${budget_limit - total:.2f}</p>

If spending exceeds the budget, the remaining goes negative. That tells the user they're over budget.

Part 4: Save to GitHub

  1. Save all files (Cmd+S)
  2. Click the Source Control icon on the left sidebar
  3. Type a commit message: add budget buddy flask app
  4. Click Commit, then Sync Changes

Set Your Goal: Add a New Feature

Your app works. Now pick one feature to build before next week:

  • Make expenses persist - save the list to a JSON file so data survives a server restart
  • Category filter - add a dropdown on the summary page to filter expenses by category
  • Trends page - create a /trends route that shows which category you spend the most in

Use the AI chat to help you build it

  1. Open the AI chat panel:

    • Codespaces: Click the chat icon in the left sidebar, or press Ctrl+I
    • Cursor: Press Cmd+L
  2. Use this prompt template:

I'm building a budget tracker Flask app. This week I built the basic app with add/summary pages. Here's my current code: [paste your app.py]. I want to add: [describe the feature you picked]. Can you help me understand what I need to change and guide me through it? Don't just give me the answer. Help me understand the steps.

  1. If something breaks, paste the error:

I got this error: [paste error]. What's wrong?

See the Prompting Cheat Sheet for more.

Update TODO.md

Open TODO.md and replace your sentence with the feature you picked. Be specific:

  • Good: "Save expenses to a JSON file so they persist between server restarts"

  • Not good: "work on my app"

Troubleshooting

"No module named flask"

Run pip install flask or pip3 install flask in the terminal.

Play button missing or doesn't run

Make sure app.py is the active file (click on its tab). Save it first (Cmd+S).

"Address already in use"

Click inside the terminal, press Ctrl+C to stop the old process, then run again.

Blank page or browser error

  • File not saved - press Cmd+S
  • Missing triple quotes (""") around the HTML strings
  • Missing @app.route decorator above a function

Route shows "Not Found"

  • Check the URL matches your @app.route("/...") exactly
  • Make sure the route function is above the if __name__ block
  • Restart the app after making changes

Tailwind styles not showing

  • Make sure <script src="https://cdn.tailwindcss.com"></script> is in every page's <head>
  • Hard refresh the browser (Cmd+Shift+R)

Source Control issues

  • Save all files first
  • If there's no upstream branch, click "Publish Branch" when prompted

Still stuck

Ask the AI agent:

I'm building a budget tracker in Flask and I'm stuck on: [describe the problem]. Here's the error: [paste it]. What should I check?

Or ask your instructor.

Resources