mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-11 13:47:39 +01:00
deps: earned_time 0.6.1 (tutor cutover; new-gate rungs spare the floor)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3a2ad0245c
commit
2140ca5180
@ -134,7 +134,7 @@ repos:
|
|||||||
# than merely an old one.
|
# than merely an old one.
|
||||||
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
|
- 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.2#subdirectory=crdt-sync
|
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
|
||||||
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.0#subdirectory=earned_time
|
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.1#subdirectory=earned_time
|
||||||
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
||||||
- mcp>=2,<3
|
- mcp>=2,<3
|
||||||
- pydantic
|
- pydantic
|
||||||
@ -155,7 +155,7 @@ repos:
|
|||||||
# than merely an old one.
|
# than merely an old one.
|
||||||
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
|
- 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.2#subdirectory=crdt-sync
|
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
|
||||||
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.0#subdirectory=earned_time
|
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.1#subdirectory=earned_time
|
||||||
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
||||||
- mcp>=2,<3
|
- mcp>=2,<3
|
||||||
- pydantic
|
- pydantic
|
||||||
|
|||||||
@ -10,7 +10,7 @@ dependencies = [
|
|||||||
"gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock",
|
"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.2#subdirectory=crdt-sync",
|
"crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync",
|
||||||
"freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays",
|
"freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays",
|
||||||
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.0#subdirectory=earned_time",
|
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.1#subdirectory=earned_time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
@ -4,7 +4,7 @@ bandit==1.9.4
|
|||||||
codespell==2.4.3
|
codespell==2.4.3
|
||||||
coverage==7.16.2
|
coverage==7.16.2
|
||||||
crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
|
crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
|
||||||
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.0#subdirectory=earned_time
|
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.1#subdirectory=earned_time
|
||||||
freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
||||||
gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
|
gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
|
||||||
mcp==2.3.0
|
mcp==2.3.0
|
||||||
|
|||||||
@ -199,8 +199,14 @@ class TestProjectionFollowsTheCut:
|
|||||||
def base(day: date) -> int:
|
def base(day: date) -> int:
|
||||||
# From the sleep ladder (Sat 2026-10-10) the registry prices the
|
# From the sleep ladder (Sat 2026-10-10) the registry prices the
|
||||||
# day itself; before it, the cut from Tuesday holds.
|
# day itself; before it, the cut from Tuesday holds.
|
||||||
# The registry in force that day (the tutor cutover switches it).
|
# The registry in force that day (the tutor cutover switches it),
|
||||||
return earned_time.base_for(day, _earned.registry(day)).shutdown_minutes
|
# and from today on its first credits: a never-paid tutor spares
|
||||||
|
# the floor (earned_time 0.6.1).
|
||||||
|
earners = _earned.registry(day)
|
||||||
|
starts = _earned.first_credits(earners, day)
|
||||||
|
return earned_time.base_for(
|
||||||
|
day, earners, first_credits=starts
|
||||||
|
).shutdown_minutes
|
||||||
|
|
||||||
week = [date(2026, 10, 5 + i) for i in range(7)]
|
week = [date(2026, 10, 5 + i) for i in range(7)]
|
||||||
assert [base(d) for d in week[1:4]] == [lowered] * 3
|
assert [base(d) for d in week[1:4]] == [lowered] * 3
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import earned_time
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from screen_locker._day import today_str
|
from screen_locker._day import today_str
|
||||||
from screen_locker._earned import flat_earners, hhmm, registry
|
from screen_locker._earned import first_credits, gate_earners, hhmm, registry
|
||||||
from screen_locker._shutdown_base import (
|
from screen_locker._shutdown_base import (
|
||||||
_apply_flat_bonus,
|
_apply_flat_bonus,
|
||||||
apply_flat_bonuses_if_new,
|
apply_flat_bonuses_if_new,
|
||||||
@ -70,12 +70,14 @@ class TestBaseHour:
|
|||||||
minus every earner's first unit, not 20:00 minus the cuts, so the day
|
minus every earner's first unit, not 20:00 minus the cuts, so the day
|
||||||
is priced by the registry and only the "stays lowered" part is ours.
|
is priced by the registry and only the "stays lowered" part is ours.
|
||||||
The Anki waiver (``ANKI_WAIVED_FROM``) raises that floor to 19:50, so
|
The Anki waiver (``ANKI_WAIVED_FROM``) raises that floor to 19:50, so
|
||||||
"lowered" means below the uncut 20:00, not at or below 19:00.
|
"lowered" means below the uncut 20:00, not at or below 19:00; a gate
|
||||||
|
that never paid out (the tutor) spares it (earned_time 0.6.1).
|
||||||
"""
|
"""
|
||||||
later = date(2030, 1, 1)
|
later = date(2030, 1, 1)
|
||||||
expected = earned_time.base_for(later, registry(later)).shutdown_minutes
|
starts = first_credits(registry(later), later)
|
||||||
assert base_minutes(later) == expected
|
base = earned_time.base_for(later, registry(later), first_credits=starts)
|
||||||
assert expected < base_minutes(date(2026, 9, 30))
|
assert base_minutes(later) == base.shutdown_minutes
|
||||||
|
assert base.shutdown_minutes < base_minutes(date(2026, 9, 30))
|
||||||
|
|
||||||
def test_default_is_the_local_today(self) -> None:
|
def test_default_is_the_local_today(self) -> None:
|
||||||
assert base_minutes() == base_minutes(datetime.now().astimezone().date())
|
assert base_minutes() == base_minutes(datetime.now().astimezone().date())
|
||||||
@ -163,7 +165,7 @@ class TestResetIncludesReading:
|
|||||||
reset_to_base_if_new_day(tmp_path / "state.json", _mixin())
|
reset_to_base_if_new_day(tmp_path / "state.json", _mixin())
|
||||||
base = base_minutes()
|
base = base_minutes()
|
||||||
later = "".join(
|
later = "".join(
|
||||||
f" + 0h {e.label}" for e in flat_earners() if e.name not in _NAMED
|
f" + 0h {e.label}" for e in gate_earners() if e.name not in _NAMED
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
f"Daily base reset: {hhmm(base + 60)} (base {hhmm(base)} + 0h workout + "
|
f"Daily base reset: {hhmm(base + 60)} (base {hhmm(base)} + 0h workout + "
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
"""The Automation tutor on a simulated cutover: penalty start and grace floor.
|
"""The Automation tutor on a simulated cutover: penalty start and grace floor.
|
||||||
|
|
||||||
``earned_time.TUTOR_FROM`` is a far-future sentinel, so the cutover is
|
``earned_time.TUTOR_FROM`` is 2026-10-10 since earned_time 0.6.1; the
|
||||||
simulated on a fixed day: the registry's cutover (bound by value in
|
fixture still pins the registry's cutover (bound by value in ``_registry`` and
|
||||||
``_registry`` and ``_ladder``) and the tutor's own ``penalty_from`` move to
|
``_ladder``) and the tutor's own ``penalty_from`` to :data:`_CUTOVER`, so the
|
||||||
:data:`_CUTOVER`. Nothing is written outside the suite's redirected home.
|
suite does not move if a later release moves the cutover. Nothing is written
|
||||||
|
outside the suite's redirected home.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@ -92,13 +93,25 @@ class TestNeverCredited:
|
|||||||
assert wired.resolution.base.gaming_minutes == (
|
assert wired.resolution.base.gaming_minutes == (
|
||||||
unwired.resolution.base.gaming_minutes + cut
|
unwired.resolution.base.gaming_minutes + cut
|
||||||
)
|
)
|
||||||
# KNOWN GAP (earned_time 0.6.0): on the sleep ladder the shutdown
|
|
||||||
# floor is the ceiling minus every registered earner's full pay and
|
def test_no_tutor_rung_off_the_shutdown_floor(self) -> None:
|
||||||
# never asks whether a penalty is in force, so a never-credited tutor
|
"""earned_time 0.6.1: a gate whose penalty has not started spares the floor.
|
||||||
# still costs its shutdown minutes. A fix there flips this assertion.
|
|
||||||
|
Unwired, the tutor's penalty starts at ``penalty_from`` and its full
|
||||||
|
rung (13+13+12+12) comes off the ladder floor; wired, the never-paid
|
||||||
|
tutor costs no shutdown either, so the floor stays at 19:50.
|
||||||
|
"""
|
||||||
|
answers = {e.name: 0 for e in registry(_CUTOVER)}
|
||||||
|
starts = first_credits(registry(_CUTOVER), _CUTOVER)
|
||||||
|
wired = derive(DayInputs(_CUTOVER, answers, first_credits=starts))
|
||||||
|
unwired = derive(DayInputs(_CUTOVER, answers))
|
||||||
|
tutor = earned_time.AUTOMATION_TUTOR
|
||||||
|
rung = tutor.shutdown_for(tutor.max_units or 1, _CUTOVER)
|
||||||
|
assert rung == 50
|
||||||
assert wired.resolution.base.shutdown_minutes == (
|
assert wired.resolution.base.shutdown_minutes == (
|
||||||
unwired.resolution.base.shutdown_minutes
|
unwired.resolution.base.shutdown_minutes + rung
|
||||||
)
|
)
|
||||||
|
assert wired.resolution.base.shutdown_minutes == 19 * 60 + 50
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("tutor")
|
@pytest.mark.usefixtures("tutor")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user