feat: include custom meals in round-off combinatorial food pool
Custom meals are now resolved to per100g nutrition data and added to the base food pool, allowing them to appear in 1-3 food combo suggestions alongside pantry items and favorites.
This commit is contained in:
@@ -5,6 +5,7 @@ import { dbConnect } from '$utils/db';
|
|||||||
import { BLS_DB } from '$lib/data/blsDb';
|
import { BLS_DB } from '$lib/data/blsDb';
|
||||||
import { getBlsEntryByCode, getNutritionEntryByFdcId } from '$lib/server/nutritionMatcher';
|
import { getBlsEntryByCode, getNutritionEntryByFdcId } from '$lib/server/nutritionMatcher';
|
||||||
import { Recipe } from '$models/Recipe';
|
import { Recipe } from '$models/Recipe';
|
||||||
|
import { CustomMeal } from '$models/CustomMeal';
|
||||||
import { FavoriteIngredient } from '$models/FavoriteIngredient';
|
import { FavoriteIngredient } from '$models/FavoriteIngredient';
|
||||||
import { FoodLogEntry } from '$models/FoodLogEntry';
|
import { FoodLogEntry } from '$models/FoodLogEntry';
|
||||||
import { OpenFoodFact } from '$models/OpenFoodFact';
|
import { OpenFoodFact } from '$models/OpenFoodFact';
|
||||||
@@ -188,10 +189,43 @@ export const GET: RequestHandler = async ({ url, locals }) => {
|
|||||||
allFoods.push(f);
|
allFoods.push(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Find best combos (1-3 foods)
|
// 3. Resolve custom meals and add to food pool
|
||||||
|
const customMeals = await CustomMeal.find({ createdBy: user.nickname }).lean();
|
||||||
|
for (const meal of customMeals as any[]) {
|
||||||
|
if (!meal.ingredients?.length) continue;
|
||||||
|
let totalGrams = 0;
|
||||||
|
let totalCal = 0, totalP = 0, totalF = 0, totalC = 0;
|
||||||
|
for (const ing of meal.ingredients) {
|
||||||
|
const g = ing.amountGrams ?? 100;
|
||||||
|
totalGrams += g;
|
||||||
|
const f = g / 100;
|
||||||
|
totalCal += (ing.per100g?.calories ?? 0) * f;
|
||||||
|
totalP += (ing.per100g?.protein ?? 0) * f;
|
||||||
|
totalF += (ing.per100g?.fat ?? 0) * f;
|
||||||
|
totalC += (ing.per100g?.carbs ?? 0) * f;
|
||||||
|
}
|
||||||
|
if (totalGrams <= 0 || totalCal <= 5) continue;
|
||||||
|
const key = `custom:${meal._id}`;
|
||||||
|
if (allFoodsSeen.has(key)) continue;
|
||||||
|
allFoodsSeen.add(key);
|
||||||
|
allFoods.push({
|
||||||
|
source: 'custom',
|
||||||
|
id: String(meal._id),
|
||||||
|
name: meal.name,
|
||||||
|
nameEn: meal.name,
|
||||||
|
per100g: {
|
||||||
|
calories: totalCal / totalGrams * 100,
|
||||||
|
protein: totalP / totalGrams * 100,
|
||||||
|
fat: totalF / totalGrams * 100,
|
||||||
|
carbs: totalC / totalGrams * 100,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Find best combos (1-3 foods)
|
||||||
const foodCombos = findBestCombos(allFoods, remaining, 'pantry', limit * 2);
|
const foodCombos = findBestCombos(allFoods, remaining, 'pantry', limit * 2);
|
||||||
|
|
||||||
// 4. Find best recipes (single items only, no combos)
|
// 5. Find best recipes (single items only, no combos)
|
||||||
const recipes = await Recipe.find(
|
const recipes = await Recipe.find(
|
||||||
{ cachedPer100g: { $exists: true, $ne: null } },
|
{ cachedPer100g: { $exists: true, $ne: null } },
|
||||||
{ name: 1, short_name: 1, cachedPer100g: 1, cachedTotalGrams: 1, portions: 1 }
|
{ name: 1, short_name: 1, cachedPer100g: 1, cachedTotalGrams: 1, portions: 1 }
|
||||||
@@ -217,12 +251,12 @@ export const GET: RequestHandler = async ({ url, locals }) => {
|
|||||||
|
|
||||||
const recipeCombos = findBestCombos(resolvedRecipes, remaining, 'recipe', limit, 1);
|
const recipeCombos = findBestCombos(resolvedRecipes, remaining, 'recipe', limit, 1);
|
||||||
|
|
||||||
// 5. Merge and sort by score (lower = better)
|
// 6. Merge and sort by score (lower = better)
|
||||||
const all: ComboSuggestion[] = [...foodCombos, ...recipeCombos];
|
const all: ComboSuggestion[] = [...foodCombos, ...recipeCombos];
|
||||||
all.sort((a, b) => a.score - b.score);
|
all.sort((a, b) => a.score - b.score);
|
||||||
const suggestions = all.slice(0, limit);
|
const suggestions = all.slice(0, limit);
|
||||||
|
|
||||||
// 6. Store in cache
|
// 7. Store in cache
|
||||||
await RoundOffCache.findOneAndUpdate(
|
await RoundOffCache.findOneAndUpdate(
|
||||||
{ createdBy: user.nickname, date: today },
|
{ createdBy: user.nickname, date: today },
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user