fixing quantities in meals page

This commit is contained in:
2025-10-01 06:25:48 -07:00
parent c79b42867b
commit 3371d7fa8e
4 changed files with 35 additions and 55 deletions

View File

@@ -6,7 +6,7 @@ import logging
from typing import List, Optional from typing import List, Optional
# Import from the database module # Import from the database module
from app.database import get_db, Food, Meal, MealFood, convert_grams_to_quantity from app.database import get_db, Food, Meal, MealFood
from main import templates from main import templates
router = APIRouter() router = APIRouter()
@@ -47,7 +47,7 @@ async def bulk_upload_meals(file: UploadFile = File(...), db: Session = Depends(
food_name = row[i].strip() food_name = row[i].strip()
grams = float(row[i+1].strip()) grams = float(row[i+1].strip())
quantity = convert_grams_to_quantity(food.id, grams, db) quantity = grams
# Try multiple matching strategies for food names # Try multiple matching strategies for food names
food = None food = None
@@ -183,8 +183,7 @@ async def add_food_to_meal(meal_id: int, food_id: int = Form(...),
grams: float = Form(..., alias="quantity"), db: Session = Depends(get_db)): grams: float = Form(..., alias="quantity"), db: Session = Depends(get_db)):
try: try:
quantity = convert_grams_to_quantity(food_id, grams, db) meal_food = MealFood(meal_id=meal_id, food_id=food_id, quantity=grams)
meal_food = MealFood(meal_id=meal_id, food_id=food_id, quantity=quantity)
db.add(meal_food) db.add(meal_food)
db.commit() db.commit()
return {"status": "success"} return {"status": "success"}
@@ -218,8 +217,7 @@ async def update_meal_food_quantity(meal_food_id: int = Form(...), grams: float
if not meal_food: if not meal_food:
return {"status": "error", "message": "Meal food not found"} return {"status": "error", "message": "Meal food not found"}
quantity = convert_grams_to_quantity(meal_food.food_id, grams, db) meal_food.quantity = grams
meal_food.quantity = quantity
db.commit() db.commit()
return {"status": "success"} return {"status": "success"}
except ValueError as ve: except ValueError as ve:

View File

@@ -6,6 +6,7 @@ from sqlalchemy import or_
from sqlalchemy.orm import sessionmaker, Session, relationship, declarative_base from sqlalchemy.orm import sessionmaker, Session, relationship, declarative_base
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
from typing import List, Optional from typing import List, Optional
from datetime import date, datetime from datetime import date, datetime
import os import os
@@ -309,42 +310,10 @@ def get_db():
db.close() db.close()
# Utility functions # Utility functions
def convert_grams_to_quantity(food_id: int, grams: float, db: Session) -> float:
"""
Converts a given gram value to the quantity multiplier based on the food's serving size.
Args:
food_id: The ID of the food item.
grams: The desired weight in grams.
db: The database session.
Returns:
The quantity multiplier.
Raises:
ValueError: If the food is not found or its serving size is invalid.
"""
food = db.query(Food).filter(Food.id == food_id).first()
if not food:
raise ValueError(f"Food with ID {food_id} not found.")
try:
# Assuming serving_size is stored in grams for simplicity as per the plan
# If serving_size can be other units, more complex conversion is needed.
serving_size_value = float(food.serving_size)
except ValueError:
raise ValueError(f"Invalid serving_size '{food.serving_size}' for food ID {food_id}. Expected a numeric value.")
if serving_size_value == 0:
raise ValueError(f"Serving size for food ID {food_id} cannot be zero.")
return grams / serving_size_value
def calculate_meal_nutrition(meal, db: Session): def calculate_meal_nutrition(meal, db: Session):
""" """
Calculate total nutrition for a meal. Calculate total nutrition for a meal.
Quantities in MealFood are now multipliers based on the Food's serving_size, Quantities in MealFood are now directly in grams.
where serving_size is assumed to be in grams.
""" """
totals = { totals = {
'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0, 'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0,
@@ -353,16 +322,27 @@ def calculate_meal_nutrition(meal, db: Session):
for meal_food in meal.meal_foods: for meal_food in meal.meal_foods:
food = meal_food.food food = meal_food.food
quantity = meal_food.quantity grams = meal_food.quantity # quantity is now grams
totals['calories'] += food.calories * quantity # Convert grams to a multiplier of serving size for nutrition calculation
totals['protein'] += food.protein * quantity try:
totals['carbs'] += food.carbs * quantity serving_size_value = float(food.serving_size)
totals['fat'] += food.fat * quantity except ValueError:
totals['fiber'] += (food.fiber or 0) * quantity serving_size_value = 1 # Fallback if serving_size is not a number
totals['sugar'] += (food.sugar or 0) * quantity
totals['sodium'] += (food.sodium or 0) * quantity if serving_size_value == 0:
totals['calcium'] += (food.calcium or 0) * quantity multiplier = 0 # Avoid division by zero
else:
multiplier = grams / serving_size_value
totals['calories'] += food.calories * multiplier
totals['protein'] += food.protein * multiplier
totals['carbs'] += food.carbs * multiplier
totals['fat'] += food.fat * multiplier
totals['fiber'] += (food.fiber or 0) * multiplier
totals['sugar'] += (food.sugar or 0) * multiplier
totals['sodium'] += (food.sodium or 0) * multiplier
totals['calcium'] += (food.calcium or 0) * multiplier
# Calculate percentages # Calculate percentages
total_cals = totals['calories'] total_cals = totals['calories']

View File

@@ -32,11 +32,12 @@ job "foodplanner" {
sidecar = false sidecar = false
} }
config { config {
image = "litestream/litestream:latest" # image = "litestream/litestream:latest"
image = "litestream/litestream:0.3"
args = [ args = [
"restore", "restore",
"-if-replica-exists", # "-if-replica-exists",
"-if-db-not-exists", #"-if-db-not-exists",
"-o", "/alloc/tmp/meal_planner.db", "-o", "/alloc/tmp/meal_planner.db",
"sftp://root:odroid@192.168.4.63/mnt/Shares/litestream/foodplanner.db" "sftp://root:odroid@192.168.4.63/mnt/Shares/litestream/foodplanner.db"
] ]
@@ -85,7 +86,8 @@ job "foodplanner" {
sidecar = true sidecar = true
} }
config { config {
image = "litestream/litestream:latest" # image = "litestream/litestream:0.5.0-test.10"
image = "litestream/litestream:0.3"
args = [ args = [
"replicate", "replicate",
"/alloc/tmp/meal_planner.db", "/alloc/tmp/meal_planner.db",

View File

@@ -26,7 +26,7 @@
{% if meal.meal_foods %} {% if meal.meal_foods %}
<ul class="list-unstyled"> <ul class="list-unstyled">
{% for meal_food in meal.meal_foods %} {% for meal_food in meal.meal_foods %}
<li>{{ meal_food.quantity }} × {{ meal_food.food.name }}</li> <li>{{ "%.2f" | format(meal_food.quantity) }}g × {{ meal_food.food.name }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% else %} {% else %}
@@ -122,8 +122,8 @@ async function loadCurrentMealFoods(mealId) {
container.innerHTML = foods.map(mf => ` container.innerHTML = foods.map(mf => `
<div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded"> <div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded">
<div class="input-group"> <div class="input-group">
<input type="number" step="0.01" class="form-control" value="${mf.quantity}" data-meal-food-id="${mf.id}" onchange="updateFoodQuantity(this)"> <input type="number" step="1" class="form-control" value="${mf.quantity}" data-meal-food-id="${mf.id}" onchange="updateFoodQuantity(this)">
<span class="input-group-text">${mf.food_name}</span> <span class="input-group-text">${mf.food_name} (g)</span>
<button class="btn btn-outline-danger" onclick="removeFoodFromMeal(${mf.id})"> <button class="btn btn-outline-danger" onclick="removeFoodFromMeal(${mf.id})">
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
</button> </button>