Initial commit
This commit is contained in:
115
app/routes/checkin.py
Normal file
115
app/routes/checkin.py
Normal file
@@ -0,0 +1,115 @@
|
||||
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"))
|
||||
Reference in New Issue
Block a user