Skip to content
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
[#1602](https://github.com/nextcloud/cookbook/pull/1602) @j0hannesr0th
- Enhance recipe recalculation algorithm
[#1723](https://github.com/nextcloud/cookbook/pull/1723) @j0hannesr0th
- Enhance recipe recalculation algorithm
[#1723](https://github.com/nextcloud/cookbook/pull/1743) @j0hannesr0th

### Fixed
- Fix translation string to not contain quotes
Expand Down
8 changes: 7 additions & 1 deletion src/components/RecipeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
v-model="recipeYield"
type="number"
min="0"
style="width: 65px"
/>
<button @click="changeRecipeYield">
<span class="icon-view-next" />
Expand Down Expand Up @@ -147,14 +148,19 @@
:key="'ingr' + idx"
:ingredient="ingredient"
:ingredient-has-correct-syntax="
/* yieldCalculator.isValidIngredientSyntax(ingredient) */
ingredientsWithValidSyntax[idx]
"
:recipe-ingredients-have-subgroups="
recipeIngredientsHaveSubgroups
"
:style="{
'font-style': ingredientsWithValidSyntax[idx]
? 'normal'
: 'italic',
}"
/>
</ul>

<div
v-if="!ingredientsSyntaxCorrect"
class="ingredient-parsing-error"
Expand Down
75 changes: 46 additions & 29 deletions src/js/yieldCalculator.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
/*
The ingredientFractionRegExp is used to identify fractions in the string.
This is used to exclude strings that contain fractions from being valid.
*/
const fractionRegExp = /^((\d+\s+)?(\d+)\s*\/\s*(\d+)).*/

function isValidIngredientSyntax(ingredient) {
/*
*** Outdated!!! ***
Explanation of ingredientSyntaxRegExp:
^: Start of string
(?:\d+(?:\.\d+)?|\.\d+): Non-capturing group that matches either a positive float value or a positive integer value. The first alternative matches one or more digits, followed by an optional decimal part consisting of a dot and one or more digits. The second alternative matches a decimal point followed by one or more digits.
(?:\s.+$|\s\S+$): Non-capturing group that matches a whitespace character followed by any character with unlimited length or any special character with unlimited length. The first alternative matches a whitespace character followed by any character(s) until the end of the string. The second alternative matches a whitespace character followed by any non-whitespace character(s) until the end of the string.
$: End of string
The ingredientSyntaxRegExp checks whether the ingredient string starts with a number,
possibly followed by a fractional part or a fraction. Then there should be a space
and then any sequence of characters.
*/
const ingredientSyntaxRegExp = /^(?:\d+(?:\.\d+)?(?:\/\d+)?)\s?.*$/
// Regular expression to match all possible fractions within a string
const ingredientFractionRegExp = /\b\d+\/\d+\b/g

/*
Explanation of ingredientMultipleSeperatorsRegExp:
/^ - Start of the string
-? - Matches an optional minus sign
\d+ - Matches one or more digits
(?:[.,]\d+){2,} - Non-capturing group that matches a separator (.,) followed by one or more digits.
The {2,} quantifier ensures that there are at least two occurrences of this pattern.
.* - Matches any characters (except newline) zero or more times.
The ingredientMultipleSeperatorsRegExp is used to check whether the string contains
more than one separators (.,) after a number. This is used to exclude strings that
contain more than one separator from being valid.
*/
const ingredientMultipleSeperatorsRegExp = /^-?\d+(?:[.,]\d+){2,}.*/

return (
ingredientSyntaxRegExp.test(ingredient) &&
!ingredientFractionRegExp.test(ingredient) &&
!ingredientMultipleSeperatorsRegExp.test(ingredient)
fractionRegExp.test(ingredient) ||
(ingredientSyntaxRegExp.test(ingredient) &&
!ingredientMultipleSeperatorsRegExp.test(ingredient))
)
}

Expand All @@ -33,28 +31,47 @@ function isIngredientsArrayValid(ingredients) {
}

function recalculateIngredients(ingredients, currentYield, originalYield) {
return ingredients.map((ingredient, index) => {
return ingredients.map((ingredient) => {
const matches = ingredient.match(fractionRegExp)

if (matches) {
const [
,
fractionMatch,
wholeNumberPartRaw,
numeratorRaw,
denominatorRaw,
] = matches
const wholeNumberPart = wholeNumberPartRaw
? parseInt(wholeNumberPartRaw, 10)
: 0
const numerator = parseInt(numeratorRaw, 10)
const denominator = parseInt(denominatorRaw, 10)

const decimalAmount = wholeNumberPart + numerator / denominator
let newAmount = (decimalAmount / originalYield) * currentYield
newAmount = newAmount.toFixed(2).replace(/[.]00$/, "")

const newIngredient = ingredient.replace(fractionMatch, newAmount)
return newIngredient
}

if (isValidIngredientSyntax(ingredient)) {
// For some cases, where the unit is not separated from the amount: 100g cheese
const possibleUnit = ingredient
.split(" ")[0]
.replace(/[^a-zA-Z]/g, "")
const amount = parseFloat(ingredients[index].split(" ")[0])
const amount = parseFloat(
ingredient.split(" ")[0].replace(",", "."),
)
const unitAndIngredient = ingredient.split(" ").slice(1).join(" ")

let newAmount = (amount / originalYield) * currentYield
newAmount = newAmount.toFixed(2).replace(/[.]00$/, "")

return `${newAmount}${possibleUnit} ${unitAndIngredient}`
}

const factor = currentYield / originalYield
const prefix = ((f) => {
if (f === 1) {
return ""
}
return `${f.toFixed(2)}x `
})(factor)
return `${prefix}${ingredient}`
return ingredient
})
}

Expand Down