2024-09-28 04:57:32 +00:00
|
|
|
function scaleFactor(mealRecipe) {
|
|
|
|
|
const numRecipe = mealRecipe.recipe.serves;
|
|
|
|
|
const numRequested = mealRecipe.servings;
|
2024-05-19 11:31:58 +00:00
|
|
|
|
2024-09-28 04:57:32 +00:00
|
|
|
return numRequested / numRecipe;
|
2024-05-19 11:31:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-05-12 03:32:18 +00:00
|
|
|
export function mealToShoppingListSources(meal) {
|
|
|
|
|
const sources = [];
|
2024-09-28 04:57:32 +00:00
|
|
|
for (const mealRecipe of meal.recipes) {
|
|
|
|
|
const scale = scaleFactor(mealRecipe);
|
|
|
|
|
for (const ingredient of mealRecipe.recipe.ingredients) {
|
2024-05-19 11:31:58 +00:00
|
|
|
const quantity = ingredient.quantity * scale;
|
2024-09-28 04:57:32 +00:00
|
|
|
sources.push({ meal, mealRecipe, recipe: mealRecipe.recipe, ingredient: { ...ingredient, quantity, }, });
|
2024-05-12 03:32:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const ingredient of meal.extra_ingredients) {
|
|
|
|
|
sources.push({ ingredient, meal, });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sources;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-18 07:05:28 +00:00
|
|
|
export function requestsToSources(requests) {
|
|
|
|
|
const sources = [];
|
|
|
|
|
for (const request of requests) {
|
|
|
|
|
if (request.ingredient) {
|
|
|
|
|
sources.push(request);
|
|
|
|
|
}
|
|
|
|
|
else if (request.meal) {
|
|
|
|
|
sources.push(...mealToShoppingListSources(request.meal));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sources;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
function groupBy(groups, keyFn) {
|
|
|
|
|
const grouped = {};
|
|
|
|
|
for (const group of groups) {
|
|
|
|
|
const key = keyFn(group);
|
|
|
|
|
if (!grouped[key]) {
|
|
|
|
|
grouped[key] = [];
|
|
|
|
|
}
|
2024-05-12 03:32:18 +00:00
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
grouped[key].push(group);
|
2024-05-19 03:43:59 +00:00
|
|
|
}
|
2024-10-13 08:19:36 +00:00
|
|
|
|
|
|
|
|
return grouped;
|
2024-05-19 03:43:59 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
function resultsToSources(shop) {
|
|
|
|
|
return shop.results.map(result => ({ shop, ingredient: { product: result.product, unit: result.unit, quantity: result.quantity, }, }));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getOutstandingGroupedByMeal(currentShoppingList) {
|
|
|
|
|
// { meal_id: { purchased: [ sources, ], required: [ sources, ], }, }
|
|
|
|
|
const requestedMeals = groupBy(requestsToSources(currentShoppingList.requests), source => source.meal?.id);
|
|
|
|
|
for (const meal_id in requestedMeals) {
|
|
|
|
|
requestedMeals[meal_id] = { id: parseInt(meal_id), purchased: [], required: requestedMeals[meal_id], };
|
2024-05-19 11:57:28 +00:00
|
|
|
}
|
2024-05-19 03:43:59 +00:00
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
for (const shop of currentShoppingList.overlapping_previous_shops) {
|
|
|
|
|
const shopResults = groupBy(resultsToSources(shop), source => source.ingredient.product.id);
|
|
|
|
|
const purchasedSources = requestsToSources(shop.requests)
|
|
|
|
|
.filter(source => source.meal && requestedMeals[source.meal.id])
|
|
|
|
|
.filter(source => source.ingredient.product.id in shopResults);
|
2024-05-19 11:57:28 +00:00
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
for (const source of purchasedSources) {
|
2024-10-14 05:11:00 +00:00
|
|
|
requestedMeals[source.meal.id].purchased.push({ shop, ...source });
|
2024-10-13 08:19:36 +00:00
|
|
|
requestedMeals[source.meal.id].required = requestedMeals[source.meal.id].required.filter(required => required.ingredient.product.id !== source.ingredient.product.id);
|
2024-05-19 03:43:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
return requestedMeals;
|
2024-05-19 03:43:59 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
export function getCompletedRequests(currentShoppingList, results) {
|
|
|
|
|
const newShoppingList = { requests: currentShoppingList.requests, results };
|
|
|
|
|
const fakeCurrentShoppingList = { requests: currentShoppingList.requests, overlapping_previous_shops: [ newShoppingList, ...currentShoppingList.overlapping_previous_shops ], };
|
|
|
|
|
const requestedMealResults = Object.values(getOutstandingGroupedByMeal(fakeCurrentShoppingList));
|
|
|
|
|
|
|
|
|
|
const completedMeals = new Set(requestedMealResults.filter(mealGroup => mealGroup.required.length === 0).map(mealGroup => mealGroup.id));
|
|
|
|
|
const purchasedProducts = new Set(results.map(result => result.product.id));
|
|
|
|
|
const completedSources = currentShoppingList.requests.filter(request => {
|
|
|
|
|
if (request.meal) {
|
|
|
|
|
return completedMeals.has(request.meal.id);
|
2024-05-12 03:32:18 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
return purchasedProducts.has(request.ingredient.product.id);
|
|
|
|
|
});
|
2024-05-12 03:32:18 +00:00
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
return sourcesToRequests(completedSources);
|
2024-05-19 10:21:49 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
export function sourcesToRequests(sources) {
|
|
|
|
|
const uniqueMeals = sources.filter(source => source.meal)
|
|
|
|
|
.map(source => source.meal)
|
|
|
|
|
.reduce((acc, meal) => {
|
|
|
|
|
acc[meal.id] = { meal, meal_id: meal.id, };
|
|
|
|
|
return acc;
|
|
|
|
|
}, {});
|
2024-05-19 12:04:20 +00:00
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
const directIngredients = sources.filter(source => !source.meal);
|
|
|
|
|
return [ ...Object.values(uniqueMeals), ...directIngredients, ];
|
2024-05-19 12:04:20 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
export function toProductsModel(currentShoppingList) {
|
|
|
|
|
const requestedMealResults = getOutstandingGroupedByMeal(currentShoppingList);
|
2024-05-19 12:04:20 +00:00
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
// Transform into { product_id: { product, requested: [ sources, ], purchased: [ sources, ], }, }
|
|
|
|
|
const grouped = {};
|
|
|
|
|
for (const meal_id in requestedMealResults) {
|
|
|
|
|
for (const source of requestedMealResults[meal_id].required) {
|
|
|
|
|
if (!grouped[source.ingredient.product.id]) {
|
|
|
|
|
grouped[source.ingredient.product.id] = { product: source.ingredient.product, requested: [], purchased: [], };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
grouped[source.ingredient.product.id].requested.push(source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const source of requestedMealResults[meal_id].purchased) {
|
|
|
|
|
if (!grouped[source.ingredient.product.id]) {
|
|
|
|
|
grouped[source.ingredient.product.id] = { product: source.ingredient.product, requested: [], purchased: [], };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
grouped[source.ingredient.product.id].purchased.push(source);
|
2024-05-19 10:21:49 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
// Only return values, array
|
|
|
|
|
return grouped;
|
2024-05-19 10:21:49 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
export function purchasedToProductModel(shoppingList) {
|
|
|
|
|
const purchasedProductIds = new Set(shoppingList.results.map(r => r.product.id));
|
|
|
|
|
const purchasedSources = requestsToSources(shoppingList.requests).filter(r => purchasedProductIds.has(r.ingredient.product.id));
|
|
|
|
|
|
|
|
|
|
const grouped = {};
|
|
|
|
|
for (const source of purchasedSources) {
|
|
|
|
|
const product = source.ingredient.product;
|
|
|
|
|
if (!grouped[product.id]) {
|
|
|
|
|
grouped[product.id] = { product, requested: [], purchased: [], };
|
2024-05-19 10:21:49 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
grouped[product.id].requested.push(source);
|
2024-05-19 03:43:59 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 08:19:36 +00:00
|
|
|
return grouped;
|
2024-05-12 03:32:18 +00:00
|
|
|
}
|