From 25390841690f40874154f56c99d6e3420327591c Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sat, 17 Oct 2026 14:17:05 +0200 Subject: [PATCH] Lock screen "I'm sick": one tap clears an already-sick day's kept lockers The one-tap button is ALL-OFF. On a day already sick that keeps lockers it now clears the keep set so every locker stands down; if clearing fails the refusal is returned and the lock stays up. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PAhbNszs5JKdtnD4uBi3xp --- screen_locker/_sick.py | 29 ++++++++++++++++++++----- screen_locker/tests/test_sick_button.py | 19 ++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/screen_locker/_sick.py b/screen_locker/_sick.py index f9fc126..8280899 100644 --- a/screen_locker/_sick.py +++ b/screen_locker/_sick.py @@ -97,18 +97,37 @@ class MarkResult: def mark_today_sick() -> MarkResult: """Mark today sick in the shared pool; every gate stands down for it. - Already sick counts as success. Any other refusal (a free day, an - unreadable pool, freedays not importable) is returned with sickdays' own - explanation, so the lock screen can show it instead of unlocking. + The one tap is ALL-OFF. Already sick counts as success; if that sick day + keeps some lockers, the keep set is cleared so every locker stands down, + and a failure to clear it is a refusal (the lock stays up). Any other + refusal (a free day, an unreadable pool, freedays not importable) is + returned with sickdays' own explanation, so the lock screen can show it + instead of unlocking. """ today = sickdays.today() try: sickdays.add_sick_day(today) except sickdays.AlreadySickError: - _logger.warning("%s was already a sick day; unlocking", today) - return MarkResult(ok=True, message="Today is already a sick day.") + if not sickdays.kept_lockers(today): + _logger.warning("%s was already a sick day; unlocking", today) + return MarkResult(ok=True, message="Today is already a sick day.") + return _clear_kept_lockers(today) except sickdays.SickDayError as exc: _logger.warning("Could not mark %s as a sick day: %s", today, exc) return MarkResult(ok=False, message=str(exc)) _logger.warning("Marked %s as a sick day from the lock screen", today) return MarkResult(ok=True, message="Today is a sick day. Get well soon.") + + +def _clear_kept_lockers(today: date) -> MarkResult: + """Clear the keep set of an already-sick ``today`` so every locker stands down.""" + try: + sickdays.set_kept_lockers(today, ()) + except sickdays.SickDayError as exc: + _logger.warning("Could not clear the kept lockers of %s: %s", today, exc) + return MarkResult(ok=False, message=str(exc)) + _logger.warning("%s was already a sick day; cleared its kept lockers", today) + return MarkResult( + ok=True, + message="Today was already a sick day; every locker now stands down.", + ) diff --git a/screen_locker/tests/test_sick_button.py b/screen_locker/tests/test_sick_button.py index fd2dd36..b0a9f6a 100644 --- a/screen_locker/tests/test_sick_button.py +++ b/screen_locker/tests/test_sick_button.py @@ -97,6 +97,25 @@ class TestSeam: assert result.ok is True assert "already" in result.message + def test_one_tap_on_a_day_that_keeps_lockers_clears_the_keep_set(self) -> None: + """One tap is ALL-OFF even on a sick day that already keeps lockers.""" + mark_sick(keep=(sickdays.LOCKER_WORKOUT, sickdays.LOCKER_LEETCODE)) + result = _sick.mark_today_sick() + assert result.ok is True + assert sickdays.kept_lockers() == frozenset() + assert _sick.stands_down_workout() is True + + def test_one_tap_that_cannot_clear_the_keep_set_stays_locked(self) -> None: + mark_sick(keep=(sickdays.LOCKER_WORKOUT,)) + with patch.object( + sickdays, + "set_kept_lockers", + side_effect=sickdays.UnreadablePoolError("pool unreadable"), + ): + result = _sick.mark_today_sick() + assert result.ok is False + assert "pool unreadable" in result.message + def test_a_free_day_is_refused_with_the_reason(self) -> None: freedays.mark(date.today(), reason="test") result = _sick.mark_today_sick()