From 438d9db8dbee975a18e2d33aa0df42c9f6ad21b2 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 21 Sep 2026 14:44:13 +0200 Subject: [PATCH] fix: correct recurrence rule parsing for schedules and calendar events (#29262) * fix: correct recurrence rule parsing for schedules and calendar events An automation set to repeat a limited number of times, say ten or a hundred, was treated as a one-shot and reported no repeat interval, because any count whose digits began with a one matched a text check for the one-shot case. The scheduler already answers that question correctly by asking the rule for its next two occurrences, so the text check is gone and the count is read as the number it is. A recurrence rule that carries its start date on the same line as the repeat text kept that date when the automation was parsed, so the schedule ran from whatever date the rule happened to carry and ignored the start the user picked. The filter that drops the start date now splits the rule on any whitespace, the same way the rule parser itself does, so both agree on where one part of the rule ends and the next begins. The same mismatch on the calendar path anchored a recurring event to the date inside its rule, so occurrences showed up before the event had begun and at the wrong time of day. That filter splits the rule the same way now, and the series starts at the event's own start. All three come from one place, recurrence rules being matched and cut as text. Rules written across several lines, which is what the schedule and calendar editors produce, behave exactly as before. * refac: name rrule token vars parts to match calendar.py --- backend/open_webui/utils/automations.py | 10 ++++------ backend/open_webui/utils/calendar.py | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/backend/open_webui/utils/automations.py b/backend/open_webui/utils/automations.py index 7df4c399aa..557e84ce13 100644 --- a/backend/open_webui/utils/automations.py +++ b/backend/open_webui/utils/automations.py @@ -89,9 +89,9 @@ def _parse_rule(s: str, now: Optional[datetime] = None): rule = rules[0] start = rule._dtstart.replace(tzinfo=None) anchor = now or datetime.now() - lines = s.splitlines() - stripped = '\n'.join(line for line in lines if not line.upper().startswith('DTSTART')) or s - has_dtstart = any(line.upper().startswith('DTSTART') for line in lines) + parts = s.split() + stripped = '\n'.join(part for part in parts if not part.upper().startswith('DTSTART')) or s + has_dtstart = any(part.upper().startswith('DTSTART') for part in parts) step = { SECONDLY: timedelta(seconds=rule._interval), MINUTELY: timedelta(minutes=rule._interval), @@ -183,9 +183,7 @@ def rrule_interval_seconds(s: str) -> Optional[int]: Returns None for one-shot (COUNT=1) schedules or rules with fewer than two future occurrences. """ - if 'COUNT=1' in s: - return None - s = '\n'.join(line for line in s.splitlines() if not line.upper().startswith('DTSTART')) or s + s = '\n'.join(part for part in s.split() if not part.upper().startswith('DTSTART')) or s now = datetime.now() rule = _parse_rule(s, now) first = rule.after(now) diff --git a/backend/open_webui/utils/calendar.py b/backend/open_webui/utils/calendar.py index 1ce590203a..0406db38f6 100644 --- a/backend/open_webui/utils/calendar.py +++ b/backend/open_webui/utils/calendar.py @@ -44,7 +44,7 @@ def expand_recurring_event( original_start_ns = event_dict['start_at'] original_start = to_local_datetime(original_start_ns) - rule_str = '\n'.join(line for line in rrule_str.splitlines() if not line.upper().startswith('DTSTART')) or rrule_str + rule_str = '\n'.join(part for part in rrule_str.split() if not part.upper().startswith('DTSTART')) or rrule_str try: # Anchor to the event's real start so day-of-week / day-of-month are correct