Handle Weissman day and overnight times

This commit is contained in:
Fizzlepoof
2026-05-22 09:27:46 -05:00
parent 1631dcd1c1
commit 4b1e3d4061
3 changed files with 19 additions and 6 deletions

View File

@@ -296,9 +296,10 @@ def enrich_recipe_node_from_dom(recipe_node: dict[str, Any], html: str) -> dict[
DURATION_RE = re.compile(r'^P(?:T(?:(?P<hours>\d+)H)?(?:(?P<minutes>\d+)M)?(?:(?P<seconds>\d+)S)?)?$') DURATION_RE = re.compile(r'^P(?:T(?:(?P<hours>\d+)H)?(?:(?P<minutes>\d+)M)?(?:(?P<seconds>\d+)S)?)?$')
TEXT_DURATION_PART_RE = re.compile( TEXT_DURATION_PART_RE = re.compile(
r'(?P<value>\d+(?:\.\d+)?)\s*(?P<unit>hours?|hrs?|hr|minutes?|mins?|min|seconds?|secs?|sec|s)\b', r'(?P<value>\d+(?:\.\d+)?)\s*(?P<unit>days?|day|hours?|hrs?|hr|minutes?|mins?|min|seconds?|secs?|sec|s)\b',
re.IGNORECASE, re.IGNORECASE,
) )
OVERNIGHT_RE = re.compile(r'\bovernight\b', re.IGNORECASE)
def parse_duration_minutes(value: Any) -> int | None: def parse_duration_minutes(value: Any) -> int | None:
@@ -319,17 +320,21 @@ def parse_duration_minutes(value: Any) -> int | None:
total = hours * 60 + minutes + (1 if seconds >= 30 else 0) total = hours * 60 + minutes + (1 if seconds >= 30 else 0)
return total or None return total or None
textual_matches = list(TEXT_DURATION_PART_RE.finditer(value)) textual_matches = list(TEXT_DURATION_PART_RE.finditer(value))
if textual_matches: overnight_count = len(OVERNIGHT_RE.findall(value))
if textual_matches or overnight_count:
total_minutes = 0.0 total_minutes = 0.0
for part in textual_matches: for part in textual_matches:
amount = float(part.group('value')) amount = float(part.group('value'))
unit = part.group('unit').lower() unit = part.group('unit').lower()
if unit.startswith(('hour', 'hr')): if unit.startswith('day'):
total_minutes += amount * 24 * 60
elif unit.startswith(('hour', 'hr')):
total_minutes += amount * 60 total_minutes += amount * 60
elif unit.startswith(('minute', 'min')): elif unit.startswith(('minute', 'min')):
total_minutes += amount total_minutes += amount
else: else:
total_minutes += amount / 60 total_minutes += amount / 60
total_minutes += overnight_count * 12 * 60
rounded = int(total_minutes + 0.5) rounded = int(total_minutes + 0.5)
return rounded or None return rounded or None
num = re.search(r'(\d+)', value) num = re.search(r'(\d+)', value)

View File

@@ -21,9 +21,10 @@ STOPWORDS = {"fresh", "large", "small", "medium", "optional", "to", "taste", "fo
TAG_KEYS = ("recipeCategory", "recipeCuisine", "keywords") TAG_KEYS = ("recipeCategory", "recipeCuisine", "keywords")
DURATION_RE = re.compile(r"^P(?:T(?:(?P<hours>\d+)H)?(?:(?P<minutes>\d+)M)?(?:(?P<seconds>\d+)S)?)?$") DURATION_RE = re.compile(r"^P(?:T(?:(?P<hours>\d+)H)?(?:(?P<minutes>\d+)M)?(?:(?P<seconds>\d+)S)?)?$")
TEXT_DURATION_PART_RE = re.compile( TEXT_DURATION_PART_RE = re.compile(
r"(?P<value>\d+(?:\.\d+)?)\s*(?P<unit>hours?|hrs?|hr|minutes?|mins?|min|seconds?|secs?|sec|s)\b", r"(?P<value>\d+(?:\.\d+)?)\s*(?P<unit>days?|day|hours?|hrs?|hr|minutes?|mins?|min|seconds?|secs?|sec|s)\b",
re.IGNORECASE, re.IGNORECASE,
) )
OVERNIGHT_RE = re.compile(r"\bovernight\b", re.IGNORECASE)
class ScriptExtractor(HTMLParser): class ScriptExtractor(HTMLParser):
@@ -415,17 +416,21 @@ def parse_duration_minutes(value: Any) -> int | None:
total = hours * 60 + minutes + (1 if seconds >= 30 else 0) total = hours * 60 + minutes + (1 if seconds >= 30 else 0)
return total or None return total or None
textual_matches = list(TEXT_DURATION_PART_RE.finditer(text)) textual_matches = list(TEXT_DURATION_PART_RE.finditer(text))
if textual_matches: overnight_count = len(OVERNIGHT_RE.findall(text))
if textual_matches or overnight_count:
total_minutes = 0.0 total_minutes = 0.0
for part in textual_matches: for part in textual_matches:
amount = float(part.group("value")) amount = float(part.group("value"))
unit = part.group("unit").lower() unit = part.group("unit").lower()
if unit.startswith(("hour", "hr")): if unit.startswith("day"):
total_minutes += amount * 24 * 60
elif unit.startswith(("hour", "hr")):
total_minutes += amount * 60 total_minutes += amount * 60
elif unit.startswith(("minute", "min")): elif unit.startswith(("minute", "min")):
total_minutes += amount total_minutes += amount
else: else:
total_minutes += amount / 60 total_minutes += amount / 60
total_minutes += overnight_count * 12 * 60
rounded = int(total_minutes + 0.5) rounded = int(total_minutes + 0.5)
return rounded or None return rounded or None
number = re.search(r"(\d+)", text) number = re.search(r"(\d+)", text)

View File

@@ -13,6 +13,9 @@ class TaggingTests(unittest.TestCase):
def test_textual_durations_cover_weissman_style_time_strings(self): def test_textual_durations_cover_weissman_style_time_strings(self):
self.assertEqual(parse_duration_minutes('1 hr, 15 min'), 75) self.assertEqual(parse_duration_minutes('1 hr, 15 min'), 75)
self.assertEqual(parse_duration_minutes('45 min + 12 hrs'), 765) self.assertEqual(parse_duration_minutes('45 min + 12 hrs'), 765)
self.assertEqual(parse_duration_minutes('1 day + 30 min'), 1470)
self.assertEqual(parse_duration_minutes('1 hr + overnight'), 780)
self.assertEqual(parse_duration_minutes('1 hr 30 min + overnight'), 810)
self.assertEqual(parse_duration_minutes('30 minutes'), 30) self.assertEqual(parse_duration_minutes('30 minutes'), 30)
self.assertEqual(parse_duration_minutes('90 sec'), 2) self.assertEqual(parse_duration_minutes('90 sec'), 2)