mirror of
https://github.com/StefBuwalda/cal_counter.git
synced 2025-10-30 11:19:59 +00:00
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:
@@ -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()
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -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"])
|
||||||
|
|||||||
@@ -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 %}
|
|
||||||
Reference in New Issue
Block a user