Files
WeightTracker/app/routes/checkin.py
Peter Stockings ccdb3d8dc7 Initial commit
2026-02-22 22:53:22 +11:00

116 lines
3.6 KiB
Python

import math
from flask import Blueprint, render_template, request, redirect, url_for, flash
from app.auth import login_required, get_current_user
from app.db import query, query_one, execute, execute_returning
bp = Blueprint("checkin", __name__)
def calculate_bmi(weight_kg, height_cm):
"""Calculate BMI from weight (kg) and height (cm)."""
if not weight_kg or not height_cm or float(height_cm) == 0:
return None
h_m = float(height_cm) / 100.0
return round(float(weight_kg) / (h_m * h_m), 1)
def check_milestones(user_id, user):
"""Check and award any new milestones after a check-in."""
checkins = query(
"SELECT weight_kg, checked_in_at FROM checkins WHERE user_id = %s ORDER BY checked_in_at ASC",
(user_id,),
)
if not checkins:
return
starting = float(user.get("starting_weight_kg") or checkins[0]["weight_kg"])
current = float(checkins[-1]["weight_kg"])
total_lost = starting - current
count = len(checkins)
milestone_checks = [
("first_checkin", count >= 1),
("5_checkins", count >= 5),
("10_checkins", count >= 10),
("25_checkins", count >= 25),
("lost_1kg", total_lost >= 1),
("lost_2kg", total_lost >= 2),
("lost_5kg", total_lost >= 5),
("lost_10kg", total_lost >= 10),
("lost_15kg", total_lost >= 15),
("lost_20kg", total_lost >= 20),
]
for key, achieved in milestone_checks:
if achieved:
try:
execute(
"INSERT INTO milestones (user_id, milestone_key) VALUES (%s, %s) ON CONFLICT DO NOTHING",
(user_id, key),
)
except Exception:
pass
@bp.route("/checkin", methods=["GET"])
@login_required
def index():
user = get_current_user()
checkins = query(
"SELECT * FROM checkins WHERE user_id = %s ORDER BY checked_in_at DESC",
(user["id"],),
)
return render_template("checkin.html", user=user, checkins=checkins)
@bp.route("/checkin", methods=["POST"])
@login_required
def create():
user = get_current_user()
weight_kg = request.form.get("weight_kg")
notes = request.form.get("notes", "").strip()
if not weight_kg:
flash("Weight is required.", "error")
return redirect(url_for("checkin.index"))
try:
weight_kg = float(weight_kg)
except ValueError:
flash("Invalid weight value.", "error")
return redirect(url_for("checkin.index"))
bmi = calculate_bmi(weight_kg, user.get("height_cm"))
checkin = execute_returning(
"""INSERT INTO checkins (user_id, weight_kg, bmi, notes)
VALUES (%s, %s, %s, %s) RETURNING *""",
(user["id"], weight_kg, bmi, notes or None),
)
# Check milestones
check_milestones(user["id"], user)
# If HTMX request, return just the new row
if request.headers.get("HX-Request"):
return render_template("partials/checkin_row.html", c=checkin, user=user)
flash("Check-in recorded!", "success")
return redirect(url_for("checkin.index"))
@bp.route("/checkin/<int:checkin_id>", methods=["DELETE"])
@login_required
def delete(checkin_id):
user = get_current_user()
execute(
"DELETE FROM checkins WHERE id = %s AND user_id = %s",
(checkin_id, user["id"]),
)
if request.headers.get("HX-Request"):
return ""
flash("Check-in deleted.", "info")
return redirect(url_for("checkin.index"))