From 311f226d448f7444629b8fcbe4c9e21620adbe06 Mon Sep 17 00:00:00 2001 From: Afonso Coutinho Date: Tue, 2 Jun 2026 17:03:58 +0100 Subject: [PATCH] fix: calendar check-in digest drops events 7-8 days out (#1249) * fix: close 1-day gap in calendar digest windows (events ~7-8 days out) * test: calendar digest windows are contiguous and cover 7-8 day events --- src/task_scheduler.py | 20 +++++++++++++++----- tests/test_digest_windows.py | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 tests/test_digest_windows.py diff --git a/src/task_scheduler.py b/src/task_scheduler.py index d5b7a87..dbf389b 100644 --- a/src/task_scheduler.py +++ b/src/task_scheduler.py @@ -203,6 +203,20 @@ RETIRED_HOUSEKEEPING_ACTIONS = frozenset({ }) +def _digest_windows(now): + """(label, start, end) buckets for the calendar check-in digest. + + The windows are contiguous so no event is dropped between buckets — an + earlier version started the 30-day window at now+8d while the week window + ended at now+7d, so events ~7-8 days out fell into no bucket. + """ + return [ + ("today_tomorrow", now, now + timedelta(days=2)), + ("this_week", now + timedelta(days=2), now + timedelta(days=7)), + ("next_30_days", now + timedelta(days=7), now + timedelta(days=30)), + ] + + class TaskScheduler: def __init__(self, session_manager): self._session_manager = session_manager @@ -1082,11 +1096,7 @@ class TaskScheduler: from core.database import SessionLocal as _SL, CalendarEvent as _CE _db = _SL() try: - for label, start, end in [ - ("today_tomorrow", now, now + timedelta(days=2)), - ("this_week", now + timedelta(days=2), now + timedelta(days=7)), - ("next_30_days", now + timedelta(days=8), now + timedelta(days=30)), - ]: + for label, start, end in _digest_windows(now): # Strip timezone for naive DB comparison _s = start.replace(tzinfo=None) if start.tzinfo else start _e = end.replace(tzinfo=None) if end.tzinfo else end diff --git a/tests/test_digest_windows.py b/tests/test_digest_windows.py new file mode 100644 index 0000000..143306b --- /dev/null +++ b/tests/test_digest_windows.py @@ -0,0 +1,22 @@ +"""Tests for the calendar check-in digest windows (src/task_scheduler.py).""" +from datetime import datetime, timedelta + +from src.task_scheduler import _digest_windows + + +def test_windows_are_contiguous_with_no_gap(): + now = datetime(2026, 6, 2, 9, 0, 0) + windows = _digest_windows(now) + # Each window starts exactly where the previous ended — no gap between + # buckets (the old code jumped from now+7d to now+8d, dropping events). + for (prev, cur) in zip(windows, windows[1:]): + assert cur[1] == prev[2] + assert windows[0][1] == now + assert windows[-1][2] == now + timedelta(days=30) + + +def test_event_seven_and_a_half_days_out_is_covered(): + now = datetime(2026, 6, 2, 9, 0, 0) + event = now + timedelta(days=7, hours=12) # fell in the old 7-8 day gap + buckets = [label for label, start, end in _digest_windows(now) if start <= event <= end] + assert buckets, "event ~7.5 days out should land in a digest window"