From a2e5c9140985639b083138c571816771a2ffa498 Mon Sep 17 00:00:00 2001 From: sstent Date: Wed, 1 Oct 2025 07:46:32 -0700 Subject: [PATCH] fixing tracker food edits --- app/api/routes/tracker.py | 44 +++++++++++++++++++++------------ tests/test_edit_tracked_meal.py | 41 ++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/app/api/routes/tracker.py b/app/api/routes/tracker.py index 79556f3..dab08d2 100644 --- a/app/api/routes/tracker.py +++ b/app/api/routes/tracker.py @@ -368,28 +368,40 @@ async def tracker_reset_to_plan(request: Request, db: Session = Depends(get_db)) async def get_tracked_meal_foods(tracked_meal_id: int, db: Session = Depends(get_db)): """Get foods associated with a tracked meal""" try: - tracked_meal = db.query(TrackedMeal).options( - joinedload(TrackedMeal.meal).joinedload(Meal.meal_foods).joinedload(MealFood.food), - joinedload(TrackedMeal.tracked_foods).joinedload(TrackedMealFood.food) - ).filter(TrackedMeal.id == tracked_meal_id).first() + 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") - # Combine foods from the base meal and custom tracked foods + # Load the associated Meal and its foods + meal = db.query(Meal).options(joinedload(Meal.meal_foods).joinedload(MealFood.food)).filter(Meal.id == tracked_meal.meal_id).first() + if not meal: + raise HTTPException(status_code=404, detail="Associated meal not found") + + # Load custom tracked foods for this tracked meal + tracked_foods = db.query(TrackedMealFood).options(joinedload(TrackedMealFood.food)).filter(TrackedMealFood.tracked_meal_id == tracked_meal_id).all() + + # Combine foods from the base meal and custom tracked foods, handling overrides meal_foods_data = [] - for meal_food in tracked_meal.meal.meal_foods: - meal_foods_data.append({ - "id": meal_food.id, - "food_id": meal_food.food.id, - "food_name": meal_food.food.name, - "quantity": meal_food.quantity, - "serving_unit": meal_food.food.serving_unit, - "serving_size": meal_food.food.serving_size, - "is_custom": False - }) - for tracked_food in tracked_meal.tracked_foods: + # Keep track of food_ids that have been overridden by TrackedMealFood entries + # These should not be added from the base meal definition + overridden_food_ids = {tf.food_id for tf in tracked_foods} + + for meal_food in meal.meal_foods: + # Only add meal_food if it hasn't been overridden by a TrackedMealFood + if meal_food.food_id not in overridden_food_ids: + meal_foods_data.append({ + "id": meal_food.id, + "food_id": meal_food.food.id, + "food_name": meal_food.food.name, + "quantity": meal_food.quantity, + "serving_unit": meal_food.food.serving_unit, + "serving_size": meal_food.food.serving_size, + "is_custom": False + }) + + for tracked_food in tracked_foods: meal_foods_data.append({ "id": tracked_food.id, "food_id": tracked_food.food.id, diff --git a/tests/test_edit_tracked_meal.py b/tests/test_edit_tracked_meal.py index 04c9d6d..5f8e726 100644 --- a/tests/test_edit_tracked_meal.py +++ b/tests/test_edit_tracked_meal.py @@ -87,6 +87,46 @@ 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_get_tracked_meal_foods_after_override(client: TestClient, session: TestingSessionLocal): + """ + Test retrieving foods for a tracked meal after one of its MealFoods has been overridden + and converted to a TrackedMealFood. This simulates the bug where the original MealFood + might be deleted, causing issues when fetching foods. + """ + food1, food2, meal1, tracked_day, tracked_meal = create_test_data(session) + + # Manually create a TrackedMealFood to override food1 + tracked_meal_food1 = TrackedMealFood(tracked_meal_id=tracked_meal.id, food_id=food1.id, quantity=175.0, is_override=True) + session.add(tracked_meal_food1) + session.commit() + session.refresh(tracked_meal_food1) + + # Manually delete the original MealFood for food1 + meal_food_to_delete = session.query(MealFood).filter( + MealFood.meal_id == meal1.id, + MealFood.food_id == food1.id + ).first() + if meal_food_to_delete: + session.delete(meal_food_to_delete) + session.commit() + session.expire_all() # Ensure a fresh load from the database + + # Now, try to get the tracked meal foods + response_get = client.get(f"/tracker/get_tracked_meal_foods/{tracked_meal.id}") + assert response_get.status_code == 200 + data_get = response_get.json() + assert data_get["status"] == "success" + assert len(data_get["meal_foods"]) == 2 + + # Verify the updated quantity for food1 and the existence of food2 + for food_data in data_get["meal_foods"]: + if food_data["food_name"] == "Apple": + assert food_data["quantity"] == 175.0 + assert food_data["is_custom"] == True # It should now be a custom tracked food + elif food_data["food_name"] == "Banana": + assert food_data["quantity"] == 100.0 + assert food_data["is_custom"] == False # This one should still be from the original meal definition + 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) @@ -113,6 +153,7 @@ def test_update_tracked_meal_foods_endpoint(client: TestClient, session: Testing assert response.status_code == 200 data = response.json() assert data["status"] == "success" + session.expire_all() # Expire all objects in the session to ensure a fresh load # Verify updates in the database updated_meal_foods = session.query(TrackedMealFood).filter(TrackedMealFood.tracked_meal_id == tracked_meal.id).all()