pin earned-time-v0.3.0 (registers the automation earner)
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Has been cancelled
Tests / test (push) Has been cancelled
Tests / lock-popup-safety (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (8) (push) Has been cancelled
Tests / flutter test (shard ${{ matrix.shard }}) (9) (push) Has been cancelled
Tests / web-coverage (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
Tests / flutter-coverage (push) Has been cancelled

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-10-05 15:20:10 +02:00
parent 218d327145
commit ec7f4805f7
6 changed files with 36 additions and 25 deletions

View File

@ -134,7 +134,7 @@ repos:
# than merely an old one.
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.1#subdirectory=crdt-sync
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.2.0#subdirectory=earned_time
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.3.0#subdirectory=earned_time
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.0#subdirectory=freedays
- mcp>=2,<3
- pydantic
@ -155,7 +155,7 @@ repos:
# than merely an old one.
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.1#subdirectory=crdt-sync
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.2.0#subdirectory=earned_time
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.3.0#subdirectory=earned_time
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.0#subdirectory=freedays
- mcp>=2,<3
- pydantic

View File

@ -10,7 +10,7 @@ dependencies = [
"gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock",
"crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.1#subdirectory=crdt-sync",
"freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.0#subdirectory=freedays",
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.2.0#subdirectory=earned_time",
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.3.0#subdirectory=earned_time",
]
[project.scripts]

View File

@ -4,7 +4,7 @@ bandit==1.9.4
codespell==2.4.3
coverage==7.16.2
crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.1#subdirectory=crdt-sync
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.2.0#subdirectory=earned_time
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.3.0#subdirectory=earned_time
freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.0#subdirectory=freedays
gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
mcp==2.3.0

View File

@ -101,11 +101,9 @@ class TestLedgerFile:
class TestFlatEarners:
def test_registry_order_without_the_counted_workout(self) -> None:
assert flat_earners() == (
earned_time.LEETCODE,
earned_time.READING,
earned_time.ANKI,
)
flat = tuple(e for e in earned_time.EARNERS if e.kind == "flat")
assert flat_earners() == flat
assert flat[:3] == (earned_time.LEETCODE, earned_time.READING, earned_time.ANKI)
def test_a_newly_registered_earner_is_picked_up(
self, monkeypatch: pytest.MonkeyPatch
@ -215,7 +213,8 @@ class TestCannotCheck:
class TestFlatAnswers:
def test_every_flat_earner_answers_by_name(self, key: Path) -> None:
write_ledger(earned_time.LEETCODE, [credit(earned_time.LEETCODE)])
assert flat_answers() == {"leetcode": True, "reading": False, "anki": False}
nos = dict.fromkeys((e.name for e in flat_earners()), False)
assert flat_answers() == {**nos, "leetcode": True}
def test_an_unknown_answer_is_none_and_warned(
self,
@ -225,19 +224,16 @@ class TestFlatAnswers:
) -> None:
monkeypatch.setattr(_earned, "HMAC_KEY_FILE", tmp_path / "no.key")
with caplog.at_level(logging.WARNING):
assert flat_answers() == {"leetcode": None, "reading": None, "anki": None}
assert "LeetCode state could not be checked" in caplog.text
assert "reading state could not be checked" in caplog.text
assert "Anki state could not be checked" in caplog.text
assert flat_answers() == dict.fromkeys(e.name for e in flat_earners())
for earner in flat_earners():
assert f"{earner.label} state could not be checked" in caplog.text
def test_a_no_is_not_warned(
self, key: Path, caplog: pytest.LogCaptureFixture
) -> None:
write_ledger(earned_time.LEETCODE, [])
with caplog.at_level(logging.WARNING):
assert flat_answers() == {
"leetcode": False,
"reading": False,
"anki": False,
}
assert flat_answers() == dict.fromkeys(
(e.name for e in flat_earners()), False
)
assert "could not be checked" not in caplog.text

View File

@ -180,8 +180,11 @@ class TestProjectionFollowsTheCut:
def test_next_week_preview_includes_a_cut_dated_after_today(
self, tmp_path: Path
) -> None:
"""On Mon 2026-10-05, Anki's cut from Tue already shows; next week has it."""
"""On Mon 2026-10-05, the cuts from Tue already show; next week has them."""
shutdown = self._shutdown(tmp_path, datetime(2026, 10, 5, 12, 0, tzinfo=UTC))
lowered = 19 * 60 - earned_time.ANKI.shutdown_minutes
tuesday = date(2026, 10, 6)
lowered = 19 * 60 - sum(
e.shutdown_minutes for e in earned_time.EARNERS if e.penalty_from == tuesday
)
assert [d.minutes for d in shutdown.rest_of_week] == [19 * 60] + [lowered] * 6
assert [d.minutes for d in shutdown.next_week_preview] == [lowered] * 7

View File

@ -18,7 +18,7 @@ import pytest
from screen_locker import _shutdown_base
from screen_locker._day import today_str
from screen_locker._earned import hhmm
from screen_locker._earned import flat_earners, hhmm
from screen_locker._shutdown_base import (
_apply_flat_bonus,
apply_flat_bonuses_if_new,
@ -31,6 +31,9 @@ if TYPE_CHECKING:
from collections.abc import Iterator
from pathlib import Path
# Terms the reset log spells out by hand; every later earner follows them.
_NAMED = frozenset({"leetcode", "reading"})
def _mixin(*, adjust_ok: bool = True) -> MagicMock:
mixin = MagicMock()
@ -62,8 +65,14 @@ class TestBaseHour:
assert base_minutes(date(2026, 10, 1)) == 19 * 60
def test_long_after_the_cut_stays_lowered(self) -> None:
"""No drift back to 20:00; Anki's cut (from 2026-10-06) holds too."""
later_cuts = earned_time.ANKI.shutdown_minutes
"""No drift back to 20:00; every later earner's cut holds too."""
cut = earned_time.READING.penalty_from
assert cut is not None
later_cuts = sum(
e.shutdown_minutes
for e in earned_time.EARNERS
if e.penalty_from is not None and e.penalty_from > cut
)
assert base_minutes(date(2030, 1, 1)) == 19 * 60 - later_cuts
def test_default_is_the_local_today(self) -> None:
@ -148,9 +157,12 @@ class TestResetIncludesReading:
with caplog.at_level("INFO"):
reset_to_base_if_new_day(tmp_path / "state.json", _mixin())
base = base_minutes()
later = "".join(
f" + 0h {e.label}" for e in flat_earners() if e.name not in _NAMED
)
assert (
f"Daily base reset: {hhmm(base + 60)} (base {hhmm(base)} + 0h workout + "
"0h LeetCode + 1h reading + 0h Anki already earned today)."
f"0h LeetCode + 1h reading{later} already earned today)."
) in caplog.text