> Source URL: /users/emily-kate-s/week-4.path
---
week: 4
date: 2026-04-01
---

# Emily Kate: Week 4: Build Your Caffeine Calculator

Your v0 prototype shows what **CaffeineCheck** should feel like: https://v0.app/chat/caffeine-intake-calculator-vre4443Hsz3?ref=5KYOA7

v0 uses React/Next.js -- great for a demo, harder to change line by line as a beginner. This week you rebuild the **core flow** in **Flask**: home, calculator form, and a result page with real math.

**Important:** Your first Flask version may look different from v0. That is intentional. You own the Python. We improve the design week by week. This guide uses **Tailwind CSS** via CDN so you get a clean layout without pasting huge `<style>` blocks. See [Tailwind CSS](../../resources/tailwind.resource.md).

---

## Check In: What Are You Working On?

Open `TODO.md`. If it is vague ("fix the design"), that is OK for now -- add a rough line like "Build CaffeineCheck in Flask this week." You will replace it with a sharper goal at the end.

- [ ] I read `TODO.md` and it roughly matches today's focus

---

## Part 1: Essential Project Catch-up

### Fill in **Known Limitations** in `vibe-code-report.md`

Your reflection is already strong. Scroll to `## Known Limitations` and add 2-3 bullets -- for example:

- "Results are estimates, not medical advice"
- "Food logging is simplified compared to my v0 idea"
- "Data does not save between visits yet"

- [ ] I filled in Known Limitations

**Optional:** If you never added `warmup.py`, you can create one later for extra practice -- it is **not** required for this week's Flask path.

---

## Part 2: Build Your Flask App

Flask maps URLs to Python functions. The calculator uses **POST** so the browser sends form data to `/result`. Styling uses Tailwind. See [Flask](../../resources/flask.resource.md).

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

Create `app.py` and paste:

```python
from flask import Flask, request

app = Flask(__name__)


@app.route("/")
def home():
    return """
    <html>
    <head>
        <meta charset="utf-8">
        <title>CaffeineCheck</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="min-h-screen bg-emerald-50 text-slate-900">
        <div class="bg-gradient-to-br from-emerald-700 to-emerald-500 px-6 py-12 text-center text-white">
            <h1 class="text-3xl font-bold tracking-wide">CaffeineCheck</h1>
            <p class="mt-2 text-emerald-100">For Student Athletes</p>
        </div>
        <div class="mx-auto max-w-xl px-6 py-10">
            <div class="rounded-2xl bg-white p-6 shadow">
                <h2 class="text-xl font-semibold text-emerald-800">How much caffeine is safe for you?</h2>
                <p class="mt-3 text-slate-600">Enter your info and we will estimate a recommendation using your weight, caffeine already consumed, and how hard practice will be.</p>
                <a class="mt-5 inline-block rounded-xl bg-emerald-600 px-6 py-3 font-semibold text-white shadow hover:bg-emerald-500" href="/calculate">Start Calculator</a>
            </div>
            <div class="mt-6 rounded-2xl bg-white p-6 shadow">
                <h2 class="text-xl font-semibold text-emerald-800">How It Works</h2>
                <p class="mt-3 text-slate-600">We start from a simple daily estimate based on body weight, subtract what you already drank, then adjust for intensity.</p>
            </div>
            <p class="mt-8 text-center text-sm text-slate-500">Built by Emily Kate S.</p>
        </div>
    </body>
    </html>
    """


@app.route("/calculate")
def calculate():
    return """
    <html>
    <head>
        <meta charset="utf-8">
        <title>CaffeineCheck - Calculator</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="min-h-screen bg-emerald-50 text-slate-900">
        <div class="bg-gradient-to-br from-emerald-700 to-emerald-500 px-6 py-8 text-center text-white">
            <h1 class="text-2xl font-bold">CaffeineCheck Calculator</h1>
        </div>
        <div class="mx-auto max-w-xl px-6 py-10">
            <div class="rounded-2xl bg-white p-6 shadow">
                <form action="/result" method="POST" class="space-y-5">
                    <div>
                        <label class="block text-sm font-semibold text-emerald-800" for="sport">What sport / activity?</label>
                        <select class="mt-2 w-full rounded-lg border border-emerald-200 bg-emerald-50/50 px-3 py-2 text-slate-900 focus:border-emerald-500 focus:outline-none" name="sport" id="sport">
                            <option value="basketball">Basketball</option>
                            <option value="soccer">Soccer</option>
                            <option value="football">Football</option>
                            <option value="volleyball">Volleyball</option>
                            <option value="track">Track &amp; Field</option>
                            <option value="swimming">Swimming</option>
                            <option value="cheer">Cheer / Dance</option>
                            <option value="weightlifting">Weightlifting</option>
                            <option value="other">Other</option>
                        </select>
                    </div>
                    <div>
                        <label class="block text-sm font-semibold text-emerald-800" for="intensity">Intensity (1 = light, 10 = all-out)</label>
                        <div class="mt-2 flex items-center gap-3">
                            <input class="flex-1 accent-emerald-600" type="range" name="intensity" id="intensity" min="1" max="10" value="5">
                            <span class="min-w-[2rem] text-center text-lg font-bold text-emerald-700" id="intensity-val">5</span>
                        </div>
                    </div>
                    <div>
                        <label class="block text-sm font-semibold text-emerald-800" for="weight">Your body weight (lbs)</label>
                        <input class="mt-2 w-full rounded-lg border border-emerald-200 bg-emerald-50/50 px-3 py-2 focus:border-emerald-500 focus:outline-none" type="number" name="weight" id="weight" placeholder="e.g. 130" min="50" max="400" required>
                    </div>
                    <div>
                        <label class="block text-sm font-semibold text-emerald-800" for="caffeine">Caffeine already consumed today (mg)</label>
                        <input class="mt-2 w-full rounded-lg border border-emerald-200 bg-emerald-50/50 px-3 py-2 focus:border-emerald-500 focus:outline-none" type="number" name="caffeine" id="caffeine" placeholder="e.g. 100 (one coffee is about 95mg)" min="0" max="1000" required>
                    </div>
                    <button class="w-full rounded-xl bg-emerald-600 py-3 font-semibold text-white shadow hover:bg-emerald-500" type="submit">Get My Recommendation</button>
                </form>
            </div>
            <p class="mt-6 text-center"><a class="text-emerald-700 underline hover:text-emerald-900" href="/">&larr; Back to Home</a></p>
        </div>
        <script>
        document.getElementById('intensity').oninput = function() {
            document.getElementById('intensity-val').textContent = this.value;
        };
        </script>
    </body>
    </html>
    """


@app.route("/result", methods=["POST"])
def result():
    sport = request.form.get("sport", "your sport")
    intensity = int(request.form.get("intensity", 5))
    weight = float(request.form.get("weight", 130))
    caffeine_had = float(request.form.get("caffeine", 0))

    safe_daily_limit = weight * 2.7
    remaining = safe_daily_limit - caffeine_had

    if intensity >= 7:
        remaining = remaining * 0.7
        intensity_note = "Since your intensity is high, we reduced the recommendation to keep you safe."
    elif intensity >= 4:
        remaining = remaining * 0.85
        intensity_note = "Moderate intensity -- we adjusted your limit slightly."
    else:
        intensity_note = "Light activity -- caffeine should not be a big concern."

    remaining = max(0, round(remaining))

    if remaining <= 0:
        border = "border-l-red-500"
        amount_class = "text-red-600"
        message = "You may already be at or past a safe amount for today. Prioritize water and rest -- this is a simplified estimate, not medical advice."
        emoji = "Warning"
    elif remaining < 80:
        border = "border-l-orange-500"
        amount_class = "text-orange-600"
        message = (
            f"You might have room for a small amount -- about {remaining}mg. "
            "That is less than one typical cup of coffee (~95mg). Consider water or tea."
        )
        emoji = "Heads up"
    else:
        border = "border-l-emerald-500"
        amount_class = "text-emerald-600"
        message = (
            f"You might have up to about {remaining}mg more caffeine today by this estimate. "
            "A typical coffee is about 95mg."
        )
        emoji = "Estimate"

    return f"""
    <html>
    <head>
        <meta charset="utf-8">
        <title>CaffeineCheck - Result</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="min-h-screen bg-emerald-50 text-slate-900">
        <div class="bg-gradient-to-br from-emerald-700 to-emerald-500 px-6 py-8 text-center text-white">
            <h1 class="text-2xl font-bold">Your Result</h1>
        </div>
        <div class="mx-auto max-w-xl px-6 py-10">
            <div class="rounded-2xl border-l-4 {border} bg-white p-6 shadow">
                <h2 class="text-lg font-semibold text-slate-800">{emoji}: Caffeine Recommendation</h2>
                <p class="mt-4 text-4xl font-bold {amount_class}">{remaining}mg</p>
                <p class="mt-4 text-slate-600">{message}</p>
                <div class="mt-6 rounded-xl bg-emerald-50 p-4 text-sm text-slate-600">
                    <p><span class="font-semibold text-emerald-800">Sport:</span> {sport.replace('_', ' ').title()}</p>
                    <p><span class="font-semibold text-emerald-800">Intensity:</span> {intensity}/10</p>
                    <p><span class="font-semibold text-emerald-800">Body weight:</span> {weight} lbs</p>
                    <p><span class="font-semibold text-emerald-800">Caffeine already consumed:</span> {caffeine_had}mg</p>
                    <p><span class="font-semibold text-emerald-800">Estimated daily limit:</span> {round(safe_daily_limit)}mg</p>
                    <p class="mt-3 text-slate-500">{intensity_note}</p>
                </div>
                <a class="mt-6 inline-block rounded-xl bg-emerald-600 px-5 py-2 font-semibold text-white hover:bg-emerald-500" href="/calculate">Calculate Again</a>
            </div>
            <p class="mt-8 text-center"><a class="text-emerald-700 underline hover:text-emerald-900" href="/">&larr; Back to Home</a></p>
        </div>
    </body>
    </html>
    """


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

What matters:

- **`methods=["POST"]`** on `/result` matches the form `method="POST"`.
- **Tailwind** must be in each page's `<head>`.
- **Math** is a teaching estimate -- not medical advice.

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

### Step 2: Install Flask

```bash
pip install flask
```

- [ ] Flask is installed

### Step 3: Run your app

Save `app.py`, use the **play button**, open `http://localhost:5000` (Codespaces: **Ports**).

Try the calculator with different numbers.

**Stop:** **Ctrl+C** in the terminal.

- [ ] Home, calculator, and result all work

### Step 4: Customize

Change Tailwind colors (search for `emerald` and swap to `teal` or `sky` in a few places), edit sport options, or tweak the `safe_daily_limit` formula line if you want a stricter or looser estimate.

- [ ] I customized at least one thing

### Challenge: Add `/about`

Add a short page that explains that this is an educational estimate and points people to trusted health sources. Link it from the home page. Match the same Tailwind header style.

- [ ] (Optional) I added `/about`

---

## Part 3: Save to GitHub

Message: `add caffeinecheck flask app with tailwind`

- [ ] I committed and synced

---

## Set Your Goal for Next Week

Open `TODO.md` again and write **one specific** goal -- you know more now than at the start. Examples:

- "Polish colors and typography to match my v0 vision."
- "Add an /about page with sources and disclaimers."
- "Add a small table of common drinks and caffeine amounts."

- [ ] I updated `TODO.md` with one specific goal for next week
- [ ] I saved, committed, and synced

---

## Troubleshooting

### "No module named flask"

`pip install flask` or `pip3 install flask`.

### "Method Not Allowed" on `/result`

Keep `@app.route("/result", methods=["POST"])` and `method="POST"` on the form.

### Page looks unstyled

Check the Tailwind `<script>` tag in each `<head>`. Restart Flask.

### Slider number does not move

Make sure the small `<script>` at the bottom of `/calculate` is still there.

---

## Resources

- [Flask](../../resources/flask.resource.md)
- [Tailwind CSS](../../resources/tailwind.resource.md)
- [GitHub Codespaces Guide](../../resources/github-codespaces.guide.md)
- [Cursor Guide](../../resources/cursor.resource.md)
- [GitHub Basics](../../resources/github-basics.guide.md)
- [Prompting Cheat Sheet](../../resources/prompting-cheat-sheet.guide.md)
- [Project Folder Guide](../../resources/gh-project-folder.guide.md)

---

## Get Help From Your AI Agent

> I'm building **CaffeineCheck** in Flask for Demo Day. This week I'm working on: **[e.g. first run / POST form / Tailwind / result math]**.
>
> ```
> [paste app.py or one route]
> ```
>
> Problem: **[describe]. Paste terminal errors.**
>
> Coach me through checks and small steps. Do not rewrite my whole file.


---

## Backlinks

The following sources link to this document:

- [Week 4: Build Your Caffeine Calculator](/users/emily-kate-s/index.path.llm.md)
