mirror of
https://github.com/sstent/foodplanner.git
synced 2025-12-05 23:51:46 +00:00
fixing quantities in meals page
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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']
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user