Merge pull request 'Remind from due Week Ahead plans' (#1193) from timmy/1192-week-ahead-start-day-reminder into main
This commit is contained in:
commit
5c74051aab
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user