diff --git a/src/components/shopping/FullShoppingListPage.vue b/src/components/shopping/FullShoppingListPage.vue index 2aa2110..77b5b6a 100644 --- a/src/components/shopping/FullShoppingListPage.vue +++ b/src/components/shopping/FullShoppingListPage.vue @@ -5,8 +5,8 @@ @@ -31,7 +31,7 @@ \ No newline at end of file diff --git a/src/components/shopping/shopping.js b/src/components/shopping/shopping.js index 1ecc848..4488666 100644 --- a/src/components/shopping/shopping.js +++ b/src/components/shopping/shopping.js @@ -44,50 +44,78 @@ export function groupingToIngredients(grouping) { function calculateTotals(sources) { const totals = {}; - for (const source of sources) { - const { unit, factor } = units.getConversionFactor(source.ingredient.unit) ?? { unit: source.ingredient.unit, factor: 1 }; + for (const { quantity, unit } of sources) { + const conversion = units.getConversionFactor(unit) ?? { unit, factor: 1 }; - const quantity = source.ingredient.quantity / factor; - if (!totals[unit]) { - totals[unit] = quantity; + const quantityInBase = quantity / conversion.factor; + if (!totals[conversion.unit]) { + totals[conversion.unit] = quantityInBase; } else { - totals[unit] += quantity; + totals[conversion.unit] += quantityInBase; } } - const result = []; - for (const unit in totals) { - result.push({ unit, quantity: totals[unit] }); - } - - return result; + return totals; } -export function groupByProduct(sources) { - const noProductKey = "no-product"; - const noProduct = { name: "No product", }; - +function groupBy(groups, keyFn) { const grouped = {}; - for (const source of sources) { - const key = source.ingredient?.product?.id ?? noProductKey; - if (!key) { - continue; - } - + for (const group of groups) { + const key = keyFn(group); if (!grouped[key]) { - grouped[key] = { - product: source.ingredient.product ?? noProduct, - sources: [], - }; + grouped[key] = []; } - grouped[key].sources.push(source); + grouped[key].push(group); } - for (const key in grouped) { - grouped[key].totals = calculateTotals(grouped[key].sources); + 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 Object.values(grouped); + 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; } \ No newline at end of file diff --git a/src/data.js b/src/data.js index cf40b51..873f1d7 100644 --- a/src/data.js +++ b/src/data.js @@ -1,11 +1,16 @@ const BASE_URL = window.location.href.replace(/^(http:\/\/[^/:]+).*/, "$1:8000") -function fixMealDates(meals) { - for (const meal of meals) { - meal.meal_date = new Date(meal.meal_date); +function fixDates(obj, ...fields) { + for (const field of fields) { + if (obj[field]) { + obj[field] = new Date(obj[field]); + } } } +const fixMealDates = meals => meals.forEach(meal => fixDates(meal, "meal_date")); +const fixResultDates = results => results.forEach(result => fixDates(result, "found_date")); + let user = null; export default { async getMeals(from, to) { @@ -164,6 +169,9 @@ export default { const response = await fetch(BASE_URL + "/shopping/current"); const lst = await response.json(); fixMealDates(lst.requests.map(request => request.meal).filter(meal => meal)); + fixResultDates(lst.results); + fixDates(lst, "created_date"); + return lst; }, async requestMeal(meal_id) { @@ -198,7 +206,10 @@ export default { body: JSON.stringify(ingredients), }); - return await response.json(); + const results = await response.json(); + fixResultDates(results.created); + fixResultDates(results.removed); + return results; }, async markNotFound(product_id) { const response = await fetch(BASE_URL + `/shopping/current/found/${product_id}`, { @@ -206,6 +217,8 @@ export default { credentials: "include", }); - return await response.json(); + const results = await response.json(); + fixResultDates(results); + return results; } } \ No newline at end of file diff --git a/src/units.js b/src/units.js index 73bdf77..cc979ad 100644 --- a/src/units.js +++ b/src/units.js @@ -96,19 +96,31 @@ function getBaseUnit(unit) { export default { getConversionFactor(unit) { - let baseUnit = getBaseUnit(unit); - if (!baseUnit) { - unit = unit.toLowerCase(); - baseUnit = getBaseUnit(unit); + if (unit in equivalentUnits) { + return { unit, factor: 1 }; } - if (!baseUnit) { - return null; + const unitLower = unit.toLowerCase(); + if (unitLower in equivalentUnits) { + return { unit: unitLower, factor: 1 }; + } + + const baseUnit = getBaseUnit(unit); + if (baseUnit) { + return { + unit: baseUnit, + factor: equivalentUnits[baseUnit][unit], + }; } - return { - unit: baseUnit, - factor: equivalentUnits[baseUnit][unit], - }; + const baseUnitLower = getBaseUnit(unitLower); + if (baseUnitLower) { + return { + unit: baseUnitLower, + factor: equivalentUnits[baseUnitLower][unitLower], + }; + } + + return null; }, } \ No newline at end of file