munch-ease-frontend/src/components/shopping/shopping.js
2024-05-19 20:21:49 +10:00

121 lines
No EOL
3.7 KiB
JavaScript

import units from '@/units.js';
export function mealToShoppingListSources(meal) {
const sources = [];
for (const recipe of meal.recipes) {
for (const ingredient of recipe.ingredients) {
sources.push({ ingredient, meal, recipe, });
}
}
for (const ingredient of meal.extra_ingredients) {
sources.push({ ingredient, meal, });
}
return sources;
}
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;
}
export function removeMealFromShoppingListSources(sources, meal) {
return sources.filter(source => source.meal?.id !== meal.id);
}
export function groupingToIngredients(grouping) {
const product = grouping.product;
const totals = [];
for (const total of grouping.totals) {
totals.push({ id: 0, line: `${total.quantity} ${total.unit} of ${product.name}`, preparation: '', name: product.name, product, ...total });
}
return totals;
}
function calculateTotals(sources) {
const totals = {};
for (const { quantity, unit } of sources) {
const conversion = units.getConversionFactor(unit) ?? { unit, factor: 1 };
const quantityInBase = quantity / conversion.factor;
if (!totals[conversion.unit]) {
totals[conversion.unit] = quantityInBase;
}
else {
totals[conversion.unit] += quantityInBase;
}
}
return totals;
}
function groupBy(groups, keyFn) {
const grouped = {};
for (const group of groups) {
const key = keyFn(group);
if (!grouped[key]) {
grouped[key] = [];
}
grouped[key].push(group);
}
return grouped;
}
function getRemainingIngredients(requiredTotals, foundTotals) {
const remaining = {};
for (const unit in requiredTotals) {
const required = requiredTotals[unit];
const found = foundTotals[unit] ?? 0;
const remainingQuantity = required - found;
if (remainingQuantity > 0) {
remaining[unit] = remainingQuantity;
}
}
return remaining;
}
export function toProductsModel(shoppingList) {
const flattenedIngredients = requestsToSources(shoppingList.requests);
const foundIngredientsByProduct = groupBy(shoppingList.results.filter(f => f.found_date), found => found.product_id);
const requiredIngredientsByProduct = groupBy(flattenedIngredients, source => source.ingredient?.product?.id ?? "no-product");
const results = [];
for (const product_id in requiredIngredientsByProduct) {
const sources = requiredIngredientsByProduct[product_id];
const requiredIngredients = sources.map(source => source.ingredient);
const requiredTotals = calculateTotals(requiredIngredients);
const foundTotals = calculateTotals(foundIngredientsByProduct[product_id] ?? []);
let fullyFound = null, foundDate = null;
if (Object.keys(foundTotals).length === 0) {
foundDate = null;
fullyFound = false;
}
else {
foundDate = foundIngredientsByProduct[product_id][0].found_date;
fullyFound = Object.keys(getRemainingIngredients(requiredTotals, foundTotals)).length === 0;
}
results.push({
product: requiredIngredients[0].product,
totals: Object.entries(requiredTotals).map(([unit, quantity]) => ({ unit, quantity, })),
fullyFound, foundDate, sources,
});
}
return results;
}