Merge pull request 'Remind from due Week Ahead plans' (#1193) from timmy/1192-week-ahead-start-day-reminder into main
All checks were successful
CI / lint (push) Successful in 3m19s
CI / build-release (push) Successful in 8s
CI / browser-journey (push) Successful in 4m22s
CI / release-candidate (push) Successful in 7s

This commit is contained in:
rockachopa 2026-08-20 19:38:32 +00:00
commit 5c74051aab
4 changed files with 98 additions and 3 deletions

View File

@ -127,7 +127,7 @@ async def _start_day_plan_snapshot() -> dict:
login = user.get("login") if isinstance(user, dict) else None
if not isinstance(login, str) or not login:
return {}
return await asyncio.to_thread(_today_store().get_tomorrow, login)
return await asyncio.to_thread(_today_store().get_start_day_plan, login)
async def _push_poll_loop() -> None:

View File

@ -450,6 +450,32 @@ class TodayStore:
raise PrivateStateEncryptionError("private state could not be decrypted")
return {"revision": int(row[0]), **payload}
def get_start_day_plan(self, login: str) -> dict:
"""Return the next private planned day without changing either plan."""
login = self._normalize_login(login)
with self._connect() as connection:
week_row = connection.execute(
"SELECT revision, payload FROM week_plans WHERE login = ?", (login,)
).fetchone()
if week_row is not None:
week = self._week_snapshot(week_row, login)
local_date = datetime.fromtimestamp(
self.clock(), ZoneInfo(week["timezone"])
).date().isoformat()
for day in week["days"]:
if (
isinstance(day, dict)
and isinstance(day.get("ids"), list)
and day["ids"]
and day.get("plan_date", "") <= local_date
):
return {**day, "timezone": week.get("timezone")}
tomorrow_row = connection.execute(
"SELECT revision, payload FROM tomorrow_plans WHERE login = ?", (login,)
).fetchone()
tomorrow = self._tomorrow_snapshot(tomorrow_row, login)
return tomorrow if tomorrow.get("ids") and tomorrow.get("plan_date") else {}
def get_week(self, login: str) -> dict:
login = self._normalize_login(login)
with self._connect() as connection:

View File

@ -281,15 +281,18 @@ async def test_authenticated_device_can_enable_start_day_reminders(tmp_path, mon
@pytest.mark.anyio
async def test_worker_snapshot_reads_the_confirmed_accounts_tomorrow_plan(monkeypatch):
async def test_worker_snapshot_reads_the_confirmed_accounts_next_planned_day(monkeypatch):
async def current_user():
return {"login": "Timmy"}
class Store:
def get_tomorrow(self, login):
def get_start_day_plan(self, login):
assert login == "Timmy"
return {"ids": ["one"], "plan_date": "2026-08-20"}
def get_tomorrow(self, _login):
raise AssertionError("the worker must use Week Ahead discovery")
monkeypatch.setattr(main, "current_user", current_user)
monkeypatch.setattr(main, "_today_store", lambda: Store())

View File

@ -51,6 +51,72 @@ def test_week_plan_is_encrypted_account_scoped_revisioned_and_preserves_other_da
assert conflict.value.snapshot == updated
def test_start_day_plan_selects_earliest_nonempty_week_date_without_mutation(tmp_path):
store = TodayStore(
tmp_path / "today.sqlite3", encryption_key=b"r" * 32,
clock=lambda: datetime(2026, 8, 21, 13, tzinfo=timezone.utc).timestamp(),
)
days = [
{
"plan_date": "2026-08-20", "ids": [],
"capacity_minutes": 60, "estimates": {},
},
{
"plan_date": "2026-08-21", "ids": ["issue:private/repo:7:"],
"capacity_minutes": 90,
"estimates": {"issue:private/repo:7:": 45},
},
{
"plan_date": "2026-08-22", "ids": ["issue:private/repo:8:"],
"capacity_minutes": 120,
"estimates": {"issue:private/repo:8:": 30},
},
]
week = store.replace_week(
"timmy", base_revision=0, days=days, timezone="America/New_York"
)
assert store.get_start_day_plan("Timmy") == {
**days[1], "timezone": "America/New_York",
}
assert store.get_week("timmy") == week
def test_start_day_plan_falls_back_to_tomorrow_without_migrating_it(tmp_path):
store = TodayStore(tmp_path / "today.sqlite3", encryption_key=b"r" * 32)
tomorrow = store.replace_tomorrow(
"timmy", base_revision=0, ids=["issue:private/repo:9:"],
capacity_minutes=75, estimates={"issue:private/repo:9:": 45},
plan_date="2026-08-21", timezone="UTC",
)
assert store.get_start_day_plan("timmy") == tomorrow
assert store.get_tomorrow("timmy") == tomorrow
with store._connect() as connection:
assert connection.execute(
"SELECT COUNT(*) FROM week_plans WHERE login = ?", ("timmy",)
).fetchone()[0] == 0
def test_start_day_plan_uses_due_tomorrow_fallback_before_future_week_day(tmp_path):
store = TodayStore(
tmp_path / "today.sqlite3", encryption_key=b"r" * 32,
clock=lambda: datetime(2026, 8, 20, 13, tzinfo=timezone.utc).timestamp(),
)
future = [{
"plan_date": "2026-08-21", "ids": ["issue:private/repo:10:"],
"capacity_minutes": 90, "estimates": {"issue:private/repo:10:": 45},
}]
store.replace_week("timmy", base_revision=0, days=future, timezone="UTC")
tomorrow = store.replace_tomorrow(
"timmy", base_revision=0, ids=["issue:private/repo:9:"],
capacity_minutes=75, estimates={"issue:private/repo:9:": 45},
plan_date="2026-08-20", timezone="UTC",
)
assert store.get_start_day_plan("timmy") == tomorrow
def test_week_plan_rejects_the_same_work_on_multiple_dates_without_advancing_revision(tmp_path):
store = TodayStore(tmp_path / "today.sqlite3", encryption_key=b"w" * 32)
original = store.replace_week(