mirror of
https://github.com/sstent/foodplanner.git
synced 2026-01-25 11:11:36 +00:00
unit consistency changes
This commit is contained in:
@@ -47,7 +47,6 @@ async def bulk_upload_meals(file: UploadFile = File(...), db: Session = Depends(
|
||||
|
||||
food_name = row[i].strip()
|
||||
grams = float(row[i+1].strip())
|
||||
quantity = grams
|
||||
|
||||
# Try multiple matching strategies for food names
|
||||
food = None
|
||||
@@ -72,7 +71,7 @@ async def bulk_upload_meals(file: UploadFile = File(...), db: Session = Depends(
|
||||
food_names = [f[0] for f in all_foods]
|
||||
raise ValueError(f"Food '{food_name}' not found. Available foods include: {', '.join(food_names[:5])}...")
|
||||
logging.info(f"Found food '{food_name}' with id {food.id}")
|
||||
ingredients.append((food.id, quantity))
|
||||
ingredients.append((food.id, grams))
|
||||
|
||||
# Create/update meal
|
||||
existing = db.query(Meal).filter(Meal.name == meal_name).first()
|
||||
@@ -89,11 +88,11 @@ async def bulk_upload_meals(file: UploadFile = File(...), db: Session = Depends(
|
||||
db.flush() # Get meal ID
|
||||
|
||||
# Add new ingredients
|
||||
for food_id, quantity in ingredients:
|
||||
for food_id, grams in ingredients:
|
||||
meal_food = MealFood(
|
||||
meal_id=existing.id,
|
||||
food_id=food_id,
|
||||
quantity=quantity
|
||||
quantity=grams
|
||||
)
|
||||
db.add(meal_food)
|
||||
|
||||
@@ -180,7 +179,7 @@ async def get_meal_foods(meal_id: int, db: Session = Depends(get_db)):
|
||||
|
||||
@router.post("/meals/{meal_id}/add_food")
|
||||
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(...), db: Session = Depends(get_db)):
|
||||
|
||||
try:
|
||||
meal_food = MealFood(meal_id=meal_id, food_id=food_id, quantity=grams)
|
||||
@@ -210,7 +209,7 @@ async def remove_food_from_meal(meal_food_id: int, db: Session = Depends(get_db)
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
@router.post("/meals/update_food_quantity")
|
||||
async def update_meal_food_quantity(meal_food_id: int = Form(...), grams: float = Form(..., alias="quantity"), db: Session = Depends(get_db)):
|
||||
async def update_meal_food_quantity(meal_food_id: int = Form(...), grams: float = Form(...), db: Session = Depends(get_db)):
|
||||
"""Update the quantity of a food in a meal"""
|
||||
try:
|
||||
meal_food = db.query(MealFood).filter(MealFood.id == meal_food_id).first()
|
||||
|
||||
@@ -280,10 +280,10 @@ async def update_tracked_food(request: Request, data: dict = Body(...), db: Sess
|
||||
"""Update quantity of a custom food in a tracked meal"""
|
||||
try:
|
||||
tracked_food_id = data.get("tracked_food_id")
|
||||
quantity = float(data.get("quantity", 1.0))
|
||||
grams = float(data.get("grams", 1.0))
|
||||
is_custom = data.get("is_custom", False)
|
||||
|
||||
logging.info(f"DEBUG: Updating tracked food {tracked_food_id} quantity to {quantity}")
|
||||
logging.info(f"DEBUG: Updating tracked food {tracked_food_id} grams to {grams}")
|
||||
|
||||
if is_custom:
|
||||
tracked_food = db.query(TrackedMealFood).filter(TrackedMealFood.id == tracked_food_id).first()
|
||||
@@ -300,7 +300,7 @@ async def update_tracked_food(request: Request, data: dict = Body(...), db: Sess
|
||||
tracked_food = TrackedMealFood(
|
||||
tracked_meal_id=tracked_meal.id,
|
||||
food_id=meal_food.food_id,
|
||||
quantity=quantity
|
||||
quantity=grams
|
||||
)
|
||||
db.add(tracked_food)
|
||||
|
||||
@@ -311,7 +311,7 @@ async def update_tracked_food(request: Request, data: dict = Body(...), db: Sess
|
||||
return {"status": "error", "message": "Tracked food not found"}
|
||||
|
||||
# Update quantity
|
||||
tracked_food.quantity = quantity
|
||||
tracked_food.quantity = grams
|
||||
|
||||
# Mark the tracked day as modified
|
||||
tracked_day = tracked_food.tracked_meal.tracked_day
|
||||
@@ -319,7 +319,7 @@ async def update_tracked_food(request: Request, data: dict = Body(...), db: Sess
|
||||
|
||||
db.commit()
|
||||
|
||||
logging.info(f"DEBUG: Successfully updated tracked food quantity")
|
||||
logging.info(f"DEBUG: Successfully updated tracked food grams")
|
||||
return {"status": "success"}
|
||||
|
||||
except Exception as e:
|
||||
@@ -440,7 +440,7 @@ async def add_food_to_tracked_meal(data: dict = Body(...), db: Session = Depends
|
||||
try:
|
||||
tracked_meal_id = data.get("tracked_meal_id")
|
||||
food_id = data.get("food_id")
|
||||
quantity = float(data.get("quantity", 1.0))
|
||||
grams = float(data.get("grams", 1.0))
|
||||
|
||||
tracked_meal = db.query(TrackedMeal).filter(TrackedMeal.id == tracked_meal_id).first()
|
||||
if not tracked_meal:
|
||||
@@ -454,7 +454,7 @@ async def add_food_to_tracked_meal(data: dict = Body(...), db: Session = Depends
|
||||
meal_food = MealFood(
|
||||
meal_id=tracked_meal.meal_id,
|
||||
food_id=food_id,
|
||||
quantity=quantity
|
||||
quantity=grams
|
||||
)
|
||||
db.add(meal_food)
|
||||
|
||||
@@ -489,27 +489,25 @@ async def update_tracked_meal_foods(data: dict = Body(...), db: Session = Depend
|
||||
|
||||
for food_data in foods_data:
|
||||
food_id = food_data.get("food_id")
|
||||
grams = float(food_data.get("quantity", 1.0)) # Assuming quantity is now grams
|
||||
grams = float(food_data.get("grams", 1.0))
|
||||
is_custom = food_data.get("is_custom", False)
|
||||
item_id = food_data.get("id") # This could be MealFood.id or TrackedMealFood.id
|
||||
logging.info(f"DEBUG: Processing food_id: {food_id}, quantity: {grams}, is_custom: {is_custom}, item_id: {item_id}")
|
||||
|
||||
quantity = grams
|
||||
logging.info(f"DEBUG: Processing food_id: {food_id}, grams: {grams}, is_custom: {is_custom}, item_id: {item_id}")
|
||||
|
||||
if is_custom:
|
||||
tracked_food = db.query(TrackedMealFood).filter(TrackedMealFood.id == item_id).first()
|
||||
if tracked_food:
|
||||
tracked_food.quantity = quantity
|
||||
logging.info(f"DEBUG: Updated existing custom tracked food {item_id} to quantity {quantity}")
|
||||
tracked_food.quantity = grams
|
||||
logging.info(f"DEBUG: Updated existing custom tracked food {item_id} to grams {grams}")
|
||||
else:
|
||||
# If it's a new custom food being added
|
||||
new_tracked_food = TrackedMealFood(
|
||||
tracked_meal_id=tracked_meal.id,
|
||||
food_id=food_id,
|
||||
quantity=quantity
|
||||
quantity=grams
|
||||
)
|
||||
db.add(new_tracked_food)
|
||||
logging.info(f"DEBUG: Added new custom tracked food for food_id {food_id} with quantity {quantity}")
|
||||
logging.info(f"DEBUG: Added new custom tracked food for food_id {food_id} with grams {grams}")
|
||||
else:
|
||||
# This is a food from the original meal definition
|
||||
# We need to check if it's already a TrackedMealFood (meaning it was overridden)
|
||||
@@ -521,8 +519,8 @@ async def update_tracked_meal_foods(data: dict = Body(...), db: Session = Depend
|
||||
logging.info(f"DEBUG: Checking for existing TrackedMealFood for food_id {food_id}: {existing_tracked_food.id if existing_tracked_food else 'None'}")
|
||||
|
||||
if existing_tracked_food:
|
||||
existing_tracked_food.quantity = quantity
|
||||
logging.info(f"DEBUG: Updated existing TrackedMealFood {existing_tracked_food.id} (override) to quantity {quantity}")
|
||||
existing_tracked_food.quantity = float(grams)
|
||||
logging.info(f"DEBUG: Updated existing TrackedMealFood {existing_tracked_food.id} (override) to grams {grams}")
|
||||
else:
|
||||
# If it's not a TrackedMealFood, it must be a MealFood
|
||||
meal_food = db.query(MealFood).filter(
|
||||
@@ -531,32 +529,29 @@ async def update_tracked_meal_foods(data: dict = Body(...), db: Session = Depend
|
||||
).first()
|
||||
logging.info(f"DEBUG: Checking for existing MealFood for food_id {food_id}: {meal_food.id if meal_food else 'None'}")
|
||||
if meal_food:
|
||||
# If quantity changed, convert to TrackedMealFood
|
||||
# NOTE: meal_food.quantity is already a multiplier,
|
||||
# but the incoming 'quantity' is a multiplier derived from grams.
|
||||
# So, we compare the incoming multiplier with the existing multiplier.
|
||||
if meal_food.quantity != quantity:
|
||||
# If grams changed, convert to TrackedMealFood
|
||||
if meal_food.quantity != grams:
|
||||
new_tracked_food = TrackedMealFood(
|
||||
tracked_meal_id=tracked_meal.id,
|
||||
food_id=food_id,
|
||||
quantity=quantity,
|
||||
quantity=float(grams),
|
||||
is_override=True
|
||||
)
|
||||
db.add(new_tracked_food)
|
||||
db.delete(meal_food) # Remove original MealFood
|
||||
logging.info(f"DEBUG: Converted MealFood {meal_food.id} to new TrackedMealFood for food_id {food_id} with quantity {quantity} and deleted original MealFood.")
|
||||
logging.info(f"DEBUG: Converted MealFood {meal_food.id} to new TrackedMealFood for food_id {food_id} with grams {grams} and deleted original MealFood.")
|
||||
else:
|
||||
logging.info(f"DEBUG: MealFood {meal_food.id} quantity unchanged, no override needed.")
|
||||
logging.info(f"DEBUG: MealFood {meal_food.id} grams unchanged, no override needed.")
|
||||
else:
|
||||
# This case should ideally not happen if data is consistent,
|
||||
# but as a fallback, add as a new TrackedMealFood
|
||||
new_tracked_food = TrackedMealFood(
|
||||
tracked_meal_id=tracked_meal.id,
|
||||
food_id=food_id,
|
||||
quantity=quantity
|
||||
quantity=float(grams)
|
||||
)
|
||||
db.add(new_tracked_food)
|
||||
logging.warning(f"DEBUG: Fallback: Added new TrackedMealFood for food_id {food_id} with quantity {quantity}. Original MealFood not found.")
|
||||
logging.warning(f"DEBUG: Fallback: Added new TrackedMealFood for food_id {food_id} with grams {grams}. Original MealFood not found.")
|
||||
|
||||
# Mark the tracked day as modified
|
||||
tracked_meal.tracked_day.is_modified = True
|
||||
@@ -656,7 +651,7 @@ async def save_as_new_meal(data: dict = Body(...), db: Session = Depends(get_db)
|
||||
meal_food = MealFood(
|
||||
meal_id=new_meal.id,
|
||||
food_id=food_data["food_id"],
|
||||
quantity=food_data["quantity"]
|
||||
quantity=food_data["grams"]
|
||||
)
|
||||
db.add(meal_food)
|
||||
|
||||
@@ -692,7 +687,7 @@ async def tracker_add_food(data: dict = Body(...), db: Session = Depends(get_db)
|
||||
person = data.get("person")
|
||||
date_str = data.get("date")
|
||||
food_id = data.get("food_id")
|
||||
grams = float(data.get("quantity", 1.0)) # Assuming quantity is now grams
|
||||
grams = float(data.get("grams", 1.0))
|
||||
meal_time = data.get("meal_time")
|
||||
|
||||
logging.info(f"DEBUG: Adding single food to tracker - person={person}, date={date_str}, food_id={food_id}, grams={grams}, meal_time={meal_time}")
|
||||
@@ -713,15 +708,12 @@ async def tracker_add_food(data: dict = Body(...), db: Session = Depends(get_db)
|
||||
db.commit()
|
||||
db.refresh(tracked_day)
|
||||
|
||||
# Convert grams to a quantity multiplier based on serving size
|
||||
food_item = db.query(Food).filter(Food.id == food_id).first()
|
||||
if not food_item:
|
||||
return {"status": "error", "message": "Food not found"}
|
||||
|
||||
if food_item.serving_size > 0:
|
||||
quantity = grams / food_item.serving_size
|
||||
else:
|
||||
quantity = 1.0 # Default to 1 serving if serving size is not set
|
||||
# Store grams directly
|
||||
quantity = grams
|
||||
|
||||
# Create a new Meal for this single food entry
|
||||
# This allows it to be treated like any other meal in the tracker view
|
||||
@@ -730,7 +722,7 @@ async def tracker_add_food(data: dict = Body(...), db: Session = Depends(get_db)
|
||||
db.flush() # Flush to get the new meal ID
|
||||
|
||||
# Link the food to the new meal
|
||||
meal_food = MealFood(meal_id=new_meal.id, food_id=food_id, quantity=quantity)
|
||||
meal_food = MealFood(meal_id=new_meal.id, food_id=food_id, quantity=grams)
|
||||
db.add(meal_food)
|
||||
|
||||
# Create tracked meal entry
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
"""
|
||||
Database models and session management for the meal planner app
|
||||
"""
|
||||
"""
|
||||
QUANTITY CONVENTION:
|
||||
All quantity fields in this application represent GRAMS.
|
||||
|
||||
- Food.serving_size: base serving size in grams (e.g., 100.0)
|
||||
- Food nutrition values: per serving_size grams
|
||||
- MealFood.quantity: grams of this food in the meal (e.g., 150.0)
|
||||
- TrackedMealFood.quantity: grams of this food as tracked (e.g., 200.0)
|
||||
|
||||
To calculate nutrition: multiplier = quantity / serving_size
|
||||
"""
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, ForeignKey, Text, Date, Boolean
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm import sessionmaker, Session, relationship, declarative_base
|
||||
@@ -313,7 +324,7 @@ def get_db():
|
||||
def calculate_meal_nutrition(meal, db: Session):
|
||||
"""
|
||||
Calculate total nutrition for a meal.
|
||||
Quantities in MealFood are now directly in grams.
|
||||
MealFood.quantity is in GRAMS. Multiplier = quantity / food.serving_size (serving_size in grams).
|
||||
"""
|
||||
totals = {
|
||||
'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0,
|
||||
@@ -322,12 +333,16 @@ def calculate_meal_nutrition(meal, db: Session):
|
||||
|
||||
for meal_food in meal.meal_foods:
|
||||
food = meal_food.food
|
||||
multiplier = meal_food.quantity
|
||||
try:
|
||||
serving_size = float(food.serving_size)
|
||||
multiplier = meal_food.quantity / serving_size if serving_size > 0 else 0
|
||||
except (ValueError, TypeError):
|
||||
multiplier = 0
|
||||
|
||||
totals['calories'] += food.calories * multiplier
|
||||
totals['protein'] += food.protein * multiplier
|
||||
totals['carbs'] += food.carbs * multiplier
|
||||
totals['fat'] += food.fat * multiplier
|
||||
totals['calories'] += (food.calories or 0) * multiplier
|
||||
totals['protein'] += (food.protein or 0) * multiplier
|
||||
totals['carbs'] += (food.carbs or 0) * multiplier
|
||||
totals['fat'] += (food.fat or 0) * multiplier
|
||||
totals['fiber'] += (food.fiber or 0) * multiplier
|
||||
totals['sugar'] += (food.sugar or 0) * multiplier
|
||||
totals['sodium'] += (food.sodium or 0) * multiplier
|
||||
@@ -377,7 +392,11 @@ def calculate_day_nutrition(plans, db: Session):
|
||||
return day_totals
|
||||
|
||||
def calculate_tracked_meal_nutrition(tracked_meal, db: Session):
|
||||
"""Calculate nutrition for a tracked meal, including custom foods"""
|
||||
"""
|
||||
Calculate nutrition for a tracked meal, including custom foods.
|
||||
TrackedMealFood.quantity is in GRAMS. Multiplier = quantity / food.serving_size (serving_size in grams).
|
||||
Base meal uses calculate_meal_nutrition which handles grams correctly.
|
||||
"""
|
||||
totals = {
|
||||
'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0,
|
||||
'fiber': 0, 'sugar': 0, 'sodium': 0, 'calcium': 0
|
||||
@@ -392,15 +411,15 @@ def calculate_tracked_meal_nutrition(tracked_meal, db: Session):
|
||||
# Add custom tracked foods
|
||||
for tracked_food in tracked_meal.tracked_foods:
|
||||
food = tracked_food.food
|
||||
food_quantity = tracked_food.quantity
|
||||
totals['calories'] += food.calories * food_quantity
|
||||
totals['protein'] += food.protein * food_quantity
|
||||
totals['carbs'] += food.carbs * food_quantity
|
||||
totals['fat'] += food.fat * food_quantity
|
||||
totals['fiber'] += (food.fiber or 0) * food_quantity
|
||||
totals['sugar'] += (food.sugar or 0) * food_quantity
|
||||
totals['sodium'] += (food.sodium or 0) * food_quantity
|
||||
totals['calcium'] += (food.calcium or 0) * food_quantity
|
||||
multiplier = tracked_food.quantity / food.serving_size if food.serving_size and food.serving_size != 0 else 0
|
||||
totals['calories'] += (food.calories or 0) * multiplier
|
||||
totals['protein'] += (food.protein or 0) * multiplier
|
||||
totals['carbs'] += (food.carbs or 0) * multiplier
|
||||
totals['fat'] += (food.fat or 0) * 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
|
||||
total_cals = totals['calories']
|
||||
@@ -447,10 +466,11 @@ def calculate_day_nutrition_tracked(tracked_meals, db: Session):
|
||||
return day_totals
|
||||
|
||||
|
||||
def convert_grams_to_quantity(food_id: int, grams: float, db: Session) -> float:
|
||||
def calculate_multiplier_from_grams(food_id: int, grams: float, db: Session) -> float:
|
||||
"""
|
||||
Converts a given amount in grams to the corresponding quantity multiplier
|
||||
based on the food's serving size.
|
||||
Calculate the multiplier from grams based on the food's serving size.
|
||||
Multiplier = grams / serving_size (both in grams).
|
||||
Used for nutrition calculations when quantity is provided in grams.
|
||||
"""
|
||||
food = db.query(Food).filter(Food.id == food_id).first()
|
||||
if not food:
|
||||
|
||||
Reference in New Issue
Block a user