Refactor meal addition flow and improve date handling

Refactored add_meal_v2 routes to simplify and clarify the meal addition process, including renaming endpoints and templates, and introducing decorators for date and item selection. Updated daily_log2 to use user's timezone and display the selected date. Adjusted templates and barcode scan logic to match new routes and improved user experience.
This commit is contained in:
2025-08-14 06:59:29 +02:00
parent d78f48710e
commit 4f1b5a5667
4 changed files with 68 additions and 78 deletions

View File

@@ -6,14 +6,13 @@ from flask import (
session, session,
request, request,
jsonify, jsonify,
abort,
) )
from flask_login import current_user from flask_login import current_user
from forms import FoodItemForm, FoodLogForm from forms import FoodItemForm, FoodLogForm
from application import db from application import db
from models import FoodItem, FoodLog from models import FoodItem, FoodLog
from sqlalchemy import and_, or_ from sqlalchemy import and_, or_
from datetime import datetime, timedelta, timezone from datetime import datetime
from sqlalchemy.sql.elements import BinaryExpression from sqlalchemy.sql.elements import BinaryExpression
from typing import cast from typing import cast
@@ -25,31 +24,34 @@ bp = Blueprint(
) )
def date_present(func):
def check_date():
if "selected_date" not in session:
return redirect(url_for("user.daily_log2"))
def item_selected(func):
def check_item():
if check_item():
if "item_id" not in session:
return redirect(url_for("add_meal_v2.get_barcode"))
@bp.before_request @bp.before_request
def login_required(): def login_required():
if not current_user.is_authenticated: if not current_user.is_authenticated:
return redirect(url_for("auth.login")) return redirect(url_for("auth.login"))
@bp.route("/select_meal/<int:meal_type>", methods=["GET"]) @date_present
def step1(meal_type: int):
assert type(meal_type) is int
assert 0 <= meal_type <= 3
session["meal_type"] = meal_type
return redirect(url_for("add_meal.step2"))
@bp.route("/get_barcode", methods=["GET"]) @bp.route("/get_barcode", methods=["GET"])
def step2(): def get_barcode():
return render_template("scan_barcode.html") return render_template("scan_barcode_v2.html")
@bp.route("/step3/<string:input>", methods=["GET"]) @date_present
def step3(input: str): @bp.route("/add_existing/<string:input>", methods=["GET"])
# check if meal_type cookie is set def add_existing(input: str):
if "meal_type" not in session:
return redirect("/")
# Check if input is a barcode # Check if input is a barcode
if input.isdigit(): if input.isdigit():
item = current_user.food_items.filter_by(barcode=input).first() item = current_user.food_items.filter_by(barcode=input).first()
@@ -59,15 +61,16 @@ def step3(input: str):
if item is None: if item is None:
# Does not exist, add item # Does not exist, add item
return redirect(url_for("add_meal.step3_alt1", input=input)) return redirect(url_for("add_meal_v2.add_new", input=input))
# Track item to add and continue to next step # Track item to add and continue to next step
session["item_id"] = item.id session["item_id"] = item.id
return redirect(url_for("add_meal.step4")) return redirect(url_for("add_meal_v2.step4"))
@bp.route("/step3_alt1/<string:input>", methods=["GET"]) @date_present
def step3_alt1(input: str): @bp.route("/add_new/<string:input>", methods=["GET"])
def add_new(input: str):
form = FoodItemForm() form = FoodItemForm()
if input.isdigit(): if input.isdigit():
@@ -77,8 +80,9 @@ def step3_alt1(input: str):
return render_template("add_item.html", form=form) return render_template("add_item.html", form=form)
@bp.route("/step3_alt1/<string:input>", methods=["POST"]) @date_present
def step3_alt1_post(input: str): @bp.route("/add_new/<string:input>", methods=["POST"])
def add_new_post(input: str):
form = FoodItemForm() form = FoodItemForm()
if form.validate_on_submit(): if form.validate_on_submit():
@@ -122,25 +126,20 @@ def step3_alt1_post(input: str):
print(f"Item exists: {item.barcode} {item.name}") print(f"Item exists: {item.barcode} {item.name}")
# Item added or already present, return to step 3. # Item added or already present, return to step 3.
return redirect(url_for("add_meal.step3", input=input)) return redirect(url_for("add_meal_v2.step3", input=input))
else: else:
print("[DEBUG] Form Invalid") print("[DEBUG] Form Invalid")
return redirect(url_for("add_meal.step3_alt1", input=input)) return redirect(url_for("add_meal_v2.step3_alt1", input=input))
@date_present
@item_selected
@bp.route("/step4", methods=["GET", "POST"]) @bp.route("/step4", methods=["GET", "POST"])
def step4(): def step4():
if "item_id" not in session:
return redirect(url_for("add_meal.step2"))
form = FoodLogForm() form = FoodLogForm()
item = db.session.get(FoodItem, session["item_id"]) item = db.session.get(FoodItem, session["item_id"])
if not item:
offset = session["offset"] return redirect(url_for("add_meal_v2.get_barcode"))
if offset is None or item is None:
abort(404)
today = datetime.now(timezone.utc).date()
day = today + timedelta(days=offset)
if form.validate_on_submit(): if form.validate_on_submit():
assert form.amount.data assert form.amount.data
@@ -149,29 +148,21 @@ def step4():
food_item_id=item.id, food_item_id=item.id,
user_id=current_user.id, user_id=current_user.id,
amount=form.amount.data, amount=form.amount.data,
part_of_day=session["meal_type"], part_of_day=0,
date_=day, date_=datetime.strptime(
session["selected_date"], "%Y-%m-%d"
).date(),
) )
) )
db.session.commit() db.session.commit()
session.pop("meal_type")
session.pop("item_id") session.pop("item_id")
return redirect(url_for("user.daily_log", offset=offset)) session.pop("selected_date")
return redirect(url_for("user.daily_log2"))
match session["meal_type"]: return render_template("step4.html", tod="idk", item=item, form=form)
case 0:
tod = "Breakfast"
case 1:
tod = "Lunch"
case 2:
tod = "Dinner"
case 3:
tod = "Snack"
case _:
tod = "Unknown"
return render_template("step4.html", tod=tod, item=item, form=form)
@date_present
@bp.route("/query", methods=["GET"]) @bp.route("/query", methods=["GET"])
def query(): def query():
q = request.args.get("q", "").strip().lower() q = request.args.get("q", "").strip().lower()

View File

@@ -85,7 +85,7 @@
goButton.addEventListener('click', () => { goButton.addEventListener('click', () => {
const value = searchBox.value.trim(); const value = searchBox.value.trim();
if (value) { if (value) {
const baseURL = "{{url_for('add_meal.step3', input='!')}}"; const baseURL = "{{url_for('add_meal_v2.add_existing', input='!')}}";
window.location.href = baseURL.replace("!", encodeURIComponent(value)); window.location.href = baseURL.replace("!", encodeURIComponent(value));
} }
}); });
@@ -121,7 +121,7 @@
if (result) { if (result) {
// Result found, this should post the barcode // Result found, this should post the barcode
const codeText = result.getText(); const codeText = result.getText();
const baseURL = "{{url_for('add_meal.step3', input='!')}}"; const baseURL = "{{url_for('add_meal_v2.add_existing', input='!')}}";
window.location.href = baseURL.replace("!", encodeURIComponent(codeText)); window.location.href = baseURL.replace("!", encodeURIComponent(codeText));
} }
}) })

View File

@@ -13,8 +13,8 @@ from forms import FoodItemForm
from models import FoodItem, FoodLog from models import FoodItem, FoodLog
from datetime import datetime, timezone, timedelta from datetime import datetime, timezone, timedelta
from application.utils import login_required from application.utils import login_required
from typing import cast
from numpy import array from numpy import array
from zoneinfo import ZoneInfo
user_bp = Blueprint( user_bp = Blueprint(
"user", "user",
@@ -155,14 +155,30 @@ def daily_log(offset: int = 0):
@user_bp.route("/daily_log2", methods=["GET"]) @user_bp.route("/daily_log2", methods=["GET"])
def daily_log2(): def daily_log2():
today = datetime.now(timezone.utc).date() # Get today's date according to user's timezone
logs_today = current_user.food_logs.filter_by(date_=today).all() today = datetime.now(ZoneInfo(current_user.timezone)).date()
# Save date in session
session["selected_date"] = today.isoformat()
# Get logs from today
logs_today = current_user.food_logs.filter_by(
date_=today.isoformat()
).all()
# calculate macros
macros = array((0.0, 0.0, 0.0, 0.0)) macros = array((0.0, 0.0, 0.0, 0.0))
for log in logs_today: for log in logs_today:
item = cast(FoodItem, log.food_item) macros += array(log.food_item.macros()) / 100 * log.amount
macros += array(item.macros()) / 100 * log.amount
macros = macro_arr_to_json(macros.tolist()) macros = macro_arr_to_json(macros.tolist())
return render_template("daily_log2.html", macros=macros, logs=logs_today)
# Render HTML
return render_template(
"daily_log2.html",
macros=macros,
logs=logs_today,
today=today.strftime("%d/%m/%Y"),
)
@user_bp.route("/remove_log/<int:id>", methods=["POST"]) @user_bp.route("/remove_log/<int:id>", methods=["POST"])

View File

@@ -6,7 +6,7 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/macros.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/macros.css') }}">
<div class="container my-4"> <div class="container my-4">
<h2 class="mb-3">Daily Calorie Dashboard</h2> <h2 class="mb-3">Daily Calorie Dashboard ({{ today }})</h2>
<!-- Macro Summary --> <!-- Macro Summary -->
<div class="card p-3 mb-3"> <div class="card p-3 mb-3">
@@ -52,7 +52,7 @@
</a> </a>
<!-- Center Button (highlighted) --> <!-- Center Button (highlighted) -->
<a id="set_link_date" href="{{ url_for('add_meal.step1', meal_type=0) }}" <a id="set_link_date" href="{{ url_for('add_meal_v2.get_barcode', meal_type=0) }}"
class="btn btn-success flex-fill mx-2 fw-bold rounded-pill"> class="btn btn-success flex-fill mx-2 fw-bold rounded-pill">
Add Item Add Item
</a> </a>
@@ -65,20 +65,3 @@
</div> </div>
{% endblock %} {% endblock %}
{% block scripts %}
<script>
function formatToday() {
const today = new Date();
const yyyy = today.getFullYear();
const mm = String(today.getMonth() + 1).padStart(2, '0');
const dd = String(today.getDate()).padStart(2, '0');
return `${yyyy}-${mm}-${dd}`;
}
const link = document.getElementById('set_link_date');
const todayString = formatToday();
// Set href on page load
link.href = `/select_date?date=${todayString}`;
</script>
{% endblock %}