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 & 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="/">← 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="/">← 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/resultmatches the formmethod="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.
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.