1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-19 13:19:41 +02:00
mealie/mealie/services/parser_services/parser_utils/string_utils.py
Michael Genson 327da02fc8
feat: Structured Yields (#4489)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-11-20 08:46:27 -06:00

111 lines
3.5 KiB
Python

import re
from fractions import Fraction
compiled_match = re.compile(r"(.){1,6}\s\((.[^\(\)])+\)\s")
compiled_search = re.compile(r"\((.[^\(])+\)")
def move_parens_to_end(ing_str) -> str:
"""
Moves all parentheses in the string to the end of the string using Regex.
If no parentheses are found, the string is returned unchanged.
"""
if re.match(compiled_match, ing_str):
if match := re.search(compiled_search, ing_str):
start = match.start()
end = match.end()
ing_str = ing_str[:start] + ing_str[end:] + " " + ing_str[start:end]
return ing_str
def check_char(char, *eql) -> bool:
"""Helper method to check if a characters matches any of the additional provided arguments"""
return any(char == eql_char for eql_char in eql)
def convert_vulgar_fractions_to_regular_fractions(text: str) -> str:
vulgar_fractions = {
"¼": "1/4",
"½": "1/2",
"¾": "3/4",
"": "1/7",
"": "1/9",
"": "1/10",
"": "1/3",
"": "2/3",
"": "1/5",
"": "2/5",
"": "3/5",
"": "4/5",
"": "1/6",
"": "5/6",
"": "1/8",
"": "3/8",
"": "5/8",
"": "7/8",
}
for vulgar_fraction, regular_fraction in vulgar_fractions.items():
# if we don't add a space in front of the fraction, mixed fractions will be broken
# e.g. "1½" -> "11/2"
text = text.replace(vulgar_fraction, f" {regular_fraction}").strip()
return text
def extract_quantity_from_string(source_str: str) -> tuple[float, str]:
"""
Extracts a quantity from a string. The quantity can be a fraction, decimal, or integer.
Returns the quantity and the remaining string. If no quantity is found, returns the quantity as 0.
"""
source_str = source_str.strip()
if not source_str:
return 0, ""
source_str = convert_vulgar_fractions_to_regular_fractions(source_str)
mixed_fraction_pattern = re.compile(r"(\d+)\s+(\d+)/(\d+)")
fraction_pattern = re.compile(r"(\d+)/(\d+)")
number_pattern = re.compile(r"\d+(\.\d+)?")
try:
# Check for a mixed fraction (e.g. "1 1/2")
match = mixed_fraction_pattern.search(source_str)
if match:
whole_number = int(match.group(1))
numerator = int(match.group(2))
denominator = int(match.group(3))
quantity = whole_number + float(Fraction(numerator, denominator))
remaining_str = source_str[: match.start()] + source_str[match.end() :]
remaining_str = remaining_str.strip()
return quantity, remaining_str
# Check for a fraction (e.g. "1/2")
match = fraction_pattern.search(source_str)
if match:
numerator = int(match.group(1))
denominator = int(match.group(2))
quantity = float(Fraction(numerator, denominator))
remaining_str = source_str[: match.start()] + source_str[match.end() :]
remaining_str = remaining_str.strip()
return quantity, remaining_str
# Check for a number (integer or float)
match = number_pattern.search(source_str)
if match:
quantity = float(match.group())
remaining_str = source_str[: match.start()] + source_str[match.end() :]
remaining_str = remaining_str.strip()
return quantity, remaining_str
except ZeroDivisionError:
pass
# If no match, return 0 and the original string
return 0, source_str