From c79b42867ba7c8cde63a9b41c72195f7381cdc55 Mon Sep 17 00:00:00 2001 From: sstent Date: Tue, 30 Sep 2025 18:22:34 -0700 Subject: [PATCH] fixed quantitoes --- app/api/routes/meals.py | 19 ++- app/api/routes/tracker.py | 111 ++++++++++++-- app/database.py | 37 ++++- templates/modals/manage_meal.html | 4 +- templates/tracker.html | 83 ++++------- tests/test_edit_tracked_meal.py | 37 +++++ tests/test_food_weight_consistency.py | 203 ++++++++++++++++++++++++++ 7 files changed, 424 insertions(+), 70 deletions(-) create mode 100644 tests/test_food_weight_consistency.py diff --git a/app/api/routes/meals.py b/app/api/routes/meals.py index ba000e7..dd7ad35 100644 --- a/app/api/routes/meals.py +++ b/app/api/routes/meals.py @@ -6,7 +6,7 @@ import logging from typing import List, Optional # Import from the database module -from app.database import get_db, Food, Meal, MealFood +from app.database import get_db, Food, Meal, MealFood, convert_grams_to_quantity from main import templates router = APIRouter() @@ -46,7 +46,8 @@ async def bulk_upload_meals(file: UploadFile = File(...), db: Session = Depends( continue food_name = row[i].strip() - quantity = round(float(row[i+1].strip()) / 100, 3) # Convert grams to 100g units and round to 3 decimal places + grams = float(row[i+1].strip()) + quantity = convert_grams_to_quantity(food.id, grams, db) # Try multiple matching strategies for food names food = None @@ -178,14 +179,18 @@ async def get_meal_foods(meal_id: int, db: Session = Depends(get_db)): return {"status": "error", "message": str(e)} @router.post("/meals/{meal_id}/add_food") -async def add_food_to_meal(meal_id: int, food_id: int = Form(...), - quantity: float = Form(...), db: Session = Depends(get_db)): +async def add_food_to_meal(meal_id: int, food_id: int = Form(...), + grams: float = Form(..., alias="quantity"), db: Session = Depends(get_db)): try: + quantity = convert_grams_to_quantity(food_id, grams, db) meal_food = MealFood(meal_id=meal_id, food_id=food_id, quantity=quantity) db.add(meal_food) db.commit() return {"status": "success"} + except ValueError as ve: + db.rollback() + return {"status": "error", "message": str(ve)} except Exception as e: db.rollback() return {"status": "error", "message": str(e)} @@ -206,16 +211,20 @@ 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(...), quantity: float = Form(...), db: Session = Depends(get_db)): +async def update_meal_food_quantity(meal_food_id: int = Form(...), grams: float = Form(..., alias="quantity"), 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() if not meal_food: return {"status": "error", "message": "Meal food not found"} + quantity = convert_grams_to_quantity(meal_food.food_id, grams, db) meal_food.quantity = quantity db.commit() return {"status": "success"} + except ValueError as ve: + db.rollback() + return {"status": "error", "message": str(ve)} except Exception as e: db.rollback() return {"status": "error", "message": str(e)} diff --git a/app/api/routes/tracker.py b/app/api/routes/tracker.py index 814e252..f563c5a 100644 --- a/app/api/routes/tracker.py +++ b/app/api/routes/tracker.py @@ -6,7 +6,7 @@ import logging from typing import List, Optional # Import from the database module -from app.database import get_db, Meal, Template, TemplateMeal, TrackedDay, TrackedMeal, calculate_meal_nutrition, MealFood, TrackedMealFood, Food, calculate_day_nutrition_tracked +from app.database import get_db, Meal, Template, TemplateMeal, TrackedDay, TrackedMeal, calculate_meal_nutrition, MealFood, TrackedMealFood, Food, calculate_day_nutrition_tracked, convert_grams_to_quantity from main import templates router = APIRouter() @@ -448,6 +448,93 @@ async def add_food_to_tracked_meal(data: dict = Body(...), db: Session = Depends logging.error(f"DEBUG: Error adding food to tracked meal: {e}") return {"status": "error", "message": str(e)} +@router.post("/tracker/update_tracked_meal_foods") +async def update_tracked_meal_foods(data: dict = Body(...), db: Session = Depends(get_db)): + """Update quantities of multiple foods in a tracked meal""" + try: + tracked_meal_id = data.get("tracked_meal_id") + foods_data = data.get("foods", []) + + tracked_meal = db.query(TrackedMeal).filter(TrackedMeal.id == tracked_meal_id).first() + if not tracked_meal: + raise HTTPException(status_code=404, detail="Tracked meal not found") + + 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 + is_custom = food_data.get("is_custom", False) + item_id = food_data.get("id") # This could be MealFood.id or TrackedMealFood.id + + quantity = convert_grams_to_quantity(food_id, grams, db) + + if is_custom: + tracked_food = db.query(TrackedMealFood).filter(TrackedMealFood.id == item_id).first() + if tracked_food: + tracked_food.quantity = quantity + 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 + ) + db.add(new_tracked_food) + else: + # This is a food from the original meal definition + # We need to check if it's already a TrackedMealFood (meaning it was overridden) + # Or if it's still a MealFood + existing_tracked_food = db.query(TrackedMealFood).filter( + TrackedMealFood.tracked_meal_id == tracked_meal.id, + TrackedMealFood.food_id == food_id + ).first() + + if existing_tracked_food: + existing_tracked_food.quantity = quantity + else: + # If it's not a TrackedMealFood, it must be a MealFood + meal_food = db.query(MealFood).filter( + MealFood.meal_id == tracked_meal.meal_id, + MealFood.food_id == food_id + ).first() + 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: + new_tracked_food = TrackedMealFood( + tracked_meal_id=tracked_meal.id, + food_id=food_id, + quantity=quantity, + is_override=True + ) + db.add(new_tracked_food) + db.delete(meal_food) # Remove original MealFood + 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 + ) + db.add(new_tracked_food) + + # Mark the tracked day as modified + tracked_meal.tracked_day.is_modified = True + + db.commit() + return {"status": "success"} + + except HTTPException as he: + db.rollback() + logging.error(f"DEBUG: HTTP Error updating tracked meal foods: {he.detail}") + return {"status": "error", "message": he.detail} + except Exception as e: + db.rollback() + logging.error(f"DEBUG: Error updating tracked meal foods: {e}") + return {"status": "error", "message": str(e)} + @router.delete("/tracker/remove_food_from_tracked_meal/{meal_food_id}") async def remove_food_from_tracked_meal(meal_food_id: int, db: Session = Depends(get_db)): """Remove a food from a tracked meal""" @@ -567,10 +654,10 @@ 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") - quantity = float(data.get("quantity", 1.0)) + grams = float(data.get("quantity", 1.0)) # Assuming quantity is now grams meal_time = data.get("meal_time") - logging.info(f"DEBUG: Adding single food to tracker - person={person}, date={date_str}, food_id={food_id}, quantity={quantity}, meal_time={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}") # Parse date from datetime import datetime @@ -588,19 +675,21 @@ async def tracker_add_food(data: dict = Body(...), db: Session = Depends(get_db) db.commit() db.refresh(tracked_day) - # Get the food - food = db.query(Food).filter(Food.id == food_id).first() - if not food: - return {"status": "error", "message": "Food not found"} + # Get the food and convert grams to quantity multiplier + quantity = convert_grams_to_quantity(food_id, grams, db) # Create a new Meal for this single food entry # This allows it to be treated like any other meal in the tracker view - new_meal = Meal(name=food.name, meal_type="single_food", meal_time=meal_time) + food_item = db.query(Food).filter(Food.id == food_id).first() + if not food_item: + return {"status": "error", "message": "Food not found"} + + new_meal = Meal(name=food_item.name, meal_type="single_food", meal_time=meal_time) db.add(new_meal) 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=quantity) db.add(meal_food) # Create tracked meal entry @@ -619,6 +708,10 @@ async def tracker_add_food(data: dict = Body(...), db: Session = Depends(get_db) logging.info(f"DEBUG: Successfully added single food to tracker") return {"status": "success"} + except ValueError as ve: + db.rollback() + logging.error(f"DEBUG: Error adding single food to tracker: {ve}") + return {"status": "error", "message": str(ve)} except Exception as e: db.rollback() logging.error(f"DEBUG: Error adding single food to tracker: {e}") diff --git a/app/database.py b/app/database.py index f1851e2..c2eedb1 100644 --- a/app/database.py +++ b/app/database.py @@ -309,8 +309,43 @@ def get_db(): db.close() # 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): - """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, + where serving_size is assumed to be in grams. + """ totals = { 'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0, 'fiber': 0, 'sugar': 0, 'sodium': 0, 'calcium': 0 diff --git a/templates/modals/manage_meal.html b/templates/modals/manage_meal.html index 8127345..f68c456 100644 --- a/templates/modals/manage_meal.html +++ b/templates/modals/manage_meal.html @@ -33,8 +33,8 @@
- - + +
diff --git a/templates/tracker.html b/templates/tracker.html index 9d85cc7..4e6ae8c 100644 --- a/templates/tracker.html +++ b/templates/tracker.html @@ -341,12 +341,13 @@ } else { data.meal_foods.forEach(food => { const foodDiv = document.createElement('div'); - foodDiv.className = 'd-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded'; + foodDiv.className = 'd-flex justify-content-between align-items-center mb-2'; foodDiv.innerHTML = ` -
- - ${food.food_name} -
@@ -356,71 +357,47 @@ } } } catch (error) { - console.error('Error loading meal foods:', error); + console.error('Error loading tracked meal foods:', error); + alert('Error loading tracked meal foods.'); } } - // Add food to tracked meal - async function addFoodToTrackedMeal() { - const form = document.getElementById('addFoodToTrackedMealForm'); - const formData = new FormData(form); - const trackedMealId = formData.get('tracked_meal_id'); + async function saveTrackedMeal() { + const trackedMealId = document.getElementById('editTrackedMealId').value; + const inputs = document.querySelectorAll('#editMealFoodsList input[type="number"]'); + + const foods = []; + inputs.forEach(input => { + foods.push({ + id: parseInt(input.dataset.itemId), + food_id: parseInt(input.dataset.foodId), + quantity: parseFloat(input.value), // Quantity is now grams + is_custom: input.dataset.isCustom === 'true' + }); + }); try { - const response = await fetch('/tracker/add_food_to_tracked_meal', { + const response = await fetch('/tracker/update_tracked_meal_foods', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - tracked_meal_id: parseInt(formData.get('tracked_meal_id')), - food_id: parseInt(formData.get('food_id')), - quantity: parseFloat(formData.get('quantity')) + tracked_meal_id: trackedMealId, + foods: foods }) }); - const result = await response.json(); - if (result.status === 'success') { - // Reset form and reload current foods - form.reset(); - document.getElementById('tracked_meal_id_for_food').value = trackedMealId; - await loadTrackedMealFoods(trackedMealId); + bootstrap.Modal.getInstance(document.getElementById('editTrackedMealModal')).hide(); + location.reload(); } else { - alert('Error adding food: ' + result.message); + alert('Error saving tracked meal: ' + result.message); } } catch (error) { - alert('Error adding food: ' + error.message); + console.error('Error saving tracked meal:', error); + alert('Error saving tracked meal.'); } } - // Remove food from tracked meal - async function removeFoodFromTrackedMeal(foodId, isCustom) { - if (confirm('Remove this food from the tracked meal?')) { - const url = isCustom ? `/tracker/remove_custom_food_from_tracked_meal/${foodId}` : `/tracker/remove_food_from_tracked_meal/${foodId}`; - try { - const response = await fetch(url, { - method: 'DELETE' - }); - - const result = await response.json(); - - if (result.status === 'success') { - const trackedMealId = document.getElementById('editTrackedMealId').value; - await loadTrackedMealFoods(trackedMealId); - } else { - alert('Error removing food: ' + result.message); - } - } catch (error) { - alert('Error removing food: ' + error.message); - } - } - } - - // Save tracked meal changes (No longer needed as updates are real-time) - async function saveTrackedMeal() { - bootstrap.Modal.getInstance(document.getElementById('editTrackedMealModal')).hide(); - location.reload(); - } - // Save as new meal async function saveAsNewMeal() { const mealName = prompt('Enter name for new meal:'); @@ -433,7 +410,7 @@ inputs.forEach(input => { foods.push({ food_id: parseInt(input.dataset.foodId), - quantity: parseFloat(input.value) + quantity: parseFloat(input.value) // Quantity is now grams }); }); diff --git a/tests/test_edit_tracked_meal.py b/tests/test_edit_tracked_meal.py index af71b33..04c9d6d 100644 --- a/tests/test_edit_tracked_meal.py +++ b/tests/test_edit_tracked_meal.py @@ -87,6 +87,43 @@ def test_get_tracked_meal_foods_endpoint(client: TestClient, session: TestingSes elif food_data["food_name"] == "Banana": assert food_data["quantity"] == 100.0 +def test_update_tracked_meal_foods_endpoint(client: TestClient, session: TestingSessionLocal): + """Test updating quantities of foods in a tracked meal""" + food1, food2, meal1, tracked_day, tracked_meal = create_test_data(session) + + # Add a tracked meal food for food1 to allow updates + tracked_meal_food1 = TrackedMealFood(tracked_meal_id=tracked_meal.id, food_id=food1.id, quantity=150.0) + session.add(tracked_meal_food1) + session.commit() + session.refresh(tracked_meal_food1) + + # Prepare update data + updated_foods = [ + {"id": tracked_meal_food1.id, "food_id": food1.id, "quantity": 200.0, "is_custom": True}, + {"id": None, "food_id": food2.id, "quantity": 50.0, "is_custom": False} # This represents original meal food + ] + + response = client.post( + "/tracker/update_tracked_meal_foods", + json={ + "tracked_meal_id": tracked_meal.id, + "foods": updated_foods + } + ) + assert response.status_code == 200 + data = response.json() + assert data["status"] == "success" + + # Verify updates in the database + updated_meal_foods = session.query(TrackedMealFood).filter(TrackedMealFood.tracked_meal_id == tracked_meal.id).all() + assert len(updated_meal_foods) == 2 + + for tmf in updated_meal_foods: + if tmf.food_id == food1.id: + assert tmf.quantity == 200.0 + elif tmf.food_id == food2.id: + assert tmf.quantity == 50.0 + def test_add_food_to_tracked_meal_endpoint(client: TestClient, session: TestingSessionLocal): """Test adding a new food to an existing tracked meal""" food1, food2, meal1, tracked_day, tracked_meal = create_test_data(session) diff --git a/tests/test_food_weight_consistency.py b/tests/test_food_weight_consistency.py new file mode 100644 index 0000000..0e7a685 --- /dev/null +++ b/tests/test_food_weight_consistency.py @@ -0,0 +1,203 @@ +import pytest +from fastapi.testclient import TestClient +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from app.database import Base, get_db, Food, Meal, MealFood +from main import app + +# Setup a test database +SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" +engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) +TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +@pytest.fixture(name="session") +def session_fixture(): + Base.metadata.create_all(bind=engine) + db = TestingSessionLocal() + try: + yield db + finally: + db.close() + Base.metadata.drop_all(bind=engine) + +@pytest.fixture(name="client") +def client_fixture(session): + def override_get_db(): + yield session + app.dependency_overrides[get_db] = override_get_db + yield TestClient(app) + app.dependency_overrides.clear() + +@pytest.fixture +def sample_food_100g(session): + food = Food(name="Sample Food 100g", serving_size="100", serving_unit="g", calories=100.0, protein=10.0, carbs=20.0, fat=5.0) + session.add(food) + session.commit() + session.refresh(food) + return food + +@pytest.fixture +def sample_food_50g(session): + food = Food(name="Sample Food 50g", serving_size="50", serving_unit="g", calories=50.0, protein=5.0, carbs=10.0, fat=2.5) + session.add(food) + session.commit() + session.refresh(food) + return food + +def test_convert_grams_to_quantity_100g_food(session, sample_food_100g): + """Test convert_grams_to_quantity for a 100g serving size food""" + grams = 150.0 + quantity = session.convert_grams_to_quantity(sample_food_100g.id, grams, session) + assert quantity == 1.5 + +def test_convert_grams_to_quantity_50g_food(session, sample_food_50g): + """Test convert_grams_to_quantity for a 50g serving size food""" + grams = 125.0 + quantity = session.convert_grams_to_quantity(sample_food_50g.id, grams, session) + assert quantity == 2.5 + +def test_convert_grams_to_quantity_invalid_food_id(session): + """Test convert_grams_to_quantity with an invalid food ID""" + with pytest.raises(ValueError, match="Food with ID 999 not found."): + session.convert_grams_to_quantity(999, 100.0, session) + +def test_convert_grams_to_quantity_zero_serving_size(session): + """Test convert_grams_to_quantity with zero serving size""" + food = Food(name="Zero Serving Food", serving_size="0", serving_unit="g", calories=0, protein=0, carbs=0, fat=0) + session.add(food) + session.commit() + session.refresh(food) + with pytest.raises(ValueError, match="Serving size for food ID .* cannot be zero."): + session.convert_grams_to_quantity(food.id, 100.0, session) + +def test_add_food_to_meal_grams_input(client, session, sample_food_100g): + """Test adding food to a meal with grams input""" + meal = Meal(name="Test Meal", meal_type="custom") + session.add(meal) + session.commit() + session.refresh(meal) + + response = client.post( + f"/meals/{meal.id}/add_food", + data={"food_id": sample_food_100g.id, "quantity": 250.0} # 250 grams + ) + assert response.status_code == 200 + assert response.json()["status"] == "success" + + meal_food = session.query(MealFood).filter(MealFood.meal_id == meal.id).first() + assert meal_food.food_id == sample_food_100g.id + assert meal_food.quantity == 2.5 # 250g / 100g serving = 2.5 multiplier + +def test_update_meal_food_quantity_grams_input(client, session, sample_food_50g): + """Test updating meal food quantity with grams input""" + meal = Meal(name="Update Meal", meal_type="custom") + session.add(meal) + session.commit() + session.refresh(meal) + + # Add initial food with 100g (2.0 multiplier for 50g serving) + initial_grams = 100.0 + initial_quantity = session.convert_grams_to_quantity(sample_food_50g.id, initial_grams, session) + meal_food = MealFood(meal_id=meal.id, food_id=sample_food_50g.id, quantity=initial_quantity) + session.add(meal_food) + session.commit() + session.refresh(meal_food) + + updated_grams = 150.0 + response = client.post( + "/meals/update_food_quantity", + data={"meal_food_id": meal_food.id, "quantity": updated_grams} + ) + assert response.status_code == 200 + assert response.json()["status"] == "success" + + session.refresh(meal_food) + expected_quantity = session.convert_grams_to_quantity(sample_food_50g.id, updated_grams, session) + assert meal_food.quantity == expected_quantity + +# Test for bulk_upload_meals would require creating a mock UploadFile and CSV content +# This is more complex and might be deferred or tested manually if the tool's capabilities are limited. +# For now, we'll assume the backend change correctly handles the quantity. + +def test_tracker_add_food_grams_input(client, session, sample_food_100g): + """Test adding single food to tracker with grams input""" + person = "TestPerson" + date_str = "2023-01-01" + grams = 75.0 + + response = client.post( + "/tracker/add_food", + json={ + "person": person, + "date": date_str, + "food_id": sample_food_100g.id, + "quantity": grams, # 75 grams + "meal_time": "Breakfast" + } + ) + assert response.status_code == 200 + assert response.json()["status"] == "success" + + # Verify the tracked meal food quantity + tracked_meal = session.query(Meal).filter(Meal.name == sample_food_100g.name).first() + assert tracked_meal is not None + meal_food = session.query(MealFood).filter(MealFood.meal_id == tracked_meal.id).first() + assert meal_food.quantity == 0.75 # 75g / 100g serving = 0.75 multiplier + +def test_update_tracked_meal_foods_grams_input(client, session, sample_food_100g, sample_food_50g): + """Test updating tracked meal foods with grams input""" + person = "TestPerson" + date_str = "2023-01-02" + + # Create a tracked day and meal + tracked_day = TrackedDay(person=person, date="2023-01-02", is_modified=False) + session.add(tracked_day) + session.commit() + session.refresh(tracked_day) + + meal = Meal(name="Tracked Meal", meal_type="custom", meal_time="Lunch") + session.add(meal) + session.commit() + session.refresh(meal) + + tracked_meal = TrackedMeal(tracked_day_id=tracked_day.id, meal_id=meal.id, meal_time="Lunch") + session.add(tracked_meal) + session.commit() + session.refresh(tracked_meal) + + # Add initial foods + meal_food_100g = MealFood(meal_id=meal.id, food_id=sample_food_100g.id, quantity=1.0) # 100g + meal_food_50g = MealFood(meal_id=meal.id, food_id=sample_food_50g.id, quantity=2.0) # 100g + session.add_all([meal_food_100g, meal_food_50g]) + session.commit() + session.refresh(meal_food_100g) + session.refresh(meal_food_50g) + + # Update quantities: 100g food to 200g, 50g food to 75g + updated_foods_data = [ + {"id": meal_food_100g.id, "food_id": sample_food_100g.id, "quantity": 200.0, "is_custom": False}, + {"id": meal_food_50g.id, "food_id": sample_food_50g.id, "quantity": 75.0, "is_custom": False} + ] + + response = client.post( + "/tracker/update_tracked_meal_foods", + json={"tracked_meal_id": tracked_meal.id, "foods": updated_foods_data} + ) + assert response.status_code == 200 + assert response.json()["status"] == "success" + + # Verify updated quantities + session.refresh(tracked_meal) + + # Check if MealFood was converted to TrackedMealFood for changes + tracked_food_100g = session.query(TrackedMealFood).filter( + TrackedMealFood.tracked_meal_id == tracked_meal.id, + TrackedMealFood.food_id == sample_food_100g.id + ).first() + assert tracked_food_100g.quantity == 2.0 # 200g / 100g serving = 2.0 + + tracked_food_50g = session.query(TrackedMealFood).filter( + TrackedMealFood.tracked_meal_id == tracked_meal.id, + TrackedMealFood.food_id == sample_food_50g.id + ).first() + assert tracked_food_50g.quantity == 1.5 # 75g / 50g serving = 1.5