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.

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.

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"

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.

Step 1: Create app.py

Create app.py and paste:

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.

Step 2: Install Flask

pip install flask

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.

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.

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.

Part 3: Save to GitHub

Message: add caffeinecheck flask app with tailwind

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."

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.

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.