> Source URL: /users/john-p/week-5.path
---
week: 5
date: 2026-04-08
---

# 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.
```

3. Save it (**Cmd+S**)

- [ ] I have `TODO.md` with today's plan

---

## 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:

```python
print("Drake")
```

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

```python
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.

- [ ] I updated `favs.py` to use f-strings with `{variable}` references
- [ ] I ran `favs.py` (play button) and it prints correctly

**Optional:** `v0-prompt.md` and `warmup.py` templates are in your [Week 4 guide](./week-4.path.md) 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](../../resources/variables.resource.md) - how to store text and numbers
- [Print Statements](../../resources/print-statements.resource.md) - how to display output, use f-strings, and format cards

- [ ] I created `practice.py` and it runs without errors
- [ ] My output looks like a formatted expense report
- [ ] I committed and synced my work

---

## 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:

```bash
pip install flask
```

Use `pip3` if `pip` doesn't work.

- [ ] Flask installed

### Step 2: Create `app.py`

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

```python
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>`.

- [ ] I created `app.py` and pasted the code

### 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**.

- [ ] Home page loads and shows "Budget Buddy"
- [ ] Summary shows the example expenses with a total
- [ ] I can add a new expense and see it on the summary page

### Challenge: Add a budget limit

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

```python
budget_limit = 500
```

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

```python
<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.

- [ ] (Challenge) I added a budget limit and remaining balance to the summary page

---

## 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**

- [ ] I committed and synced my 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.

3. If something breaks, paste the error:

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

See the [Prompting Cheat Sheet](../../resources/prompting-cheat-sheet.guide.md) 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"

- [ ] I picked a feature and updated `TODO.md`
- [ ] I tried using the AI chat to start building it
- [ ] I committed and synced my work

---

## 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

- [Flask](../../resources/flask.resource.md) - how routes, forms, and templates work
- [Tailwind CSS](../../resources/tailwind.resource.md) - styling your app with utility classes
- [GitHub Codespaces Guide](../../resources/github-codespaces.guide.md) - how to open and use Codespaces
- [Cursor Guide](../../resources/cursor.resource.md) - how to open and use Cursor
- [GitHub Basics](../../resources/github-basics.guide.md) - how to commit and push your code
- [Prompting Cheat Sheet](../../resources/prompting-cheat-sheet.guide.md) - effective prompts for the AI agent


---

## Backlinks

The following sources link to this document:

- [Week 5: Build Your Budget Tracker](/students.path.llm.md)
- [Week 5: Build Your Budget Tracker](/users/john-p/index.path.llm.md)
