mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-11 21:34:04 +01:00
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
earned_time pre-commit / pre-commit (push) Has been cancelled
earned_time tests / test (push) Has been cancelled
file length / file-length (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
palette drift / palette (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
shared gate tests / gate unit tests (push) Has been cancelled
resolve()/base_for(leisure=) is now tri-state: None (default) decides by the switch file on `day`, True forces leisure pricing (the shadow budget), False forces the old base and cap. A consumer billing on the 06:00 gaming day passes leisure_active(gaming_day), so 00:00-05:59 on the go-live date no longer prices the eve with the +2 h leisure base/cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
169 lines
4.3 KiB
Python
169 lines
4.3 KiB
Python
# Copyright (c) 2026 Krzysztof Rudnicki
|
|
"""Penalty-then-reward for gate apps: one registry, one sum, two consumers.
|
|
|
|
A gate (leetcode-guard, book-guard, ...) never touches gaming time or the
|
|
shutdown schedule. It publishes a fact -- HMAC-signed ``credit`` rows in its
|
|
ledger -- and registers one :class:`Earner` here. The two consumers read the
|
|
registry and apply the result:
|
|
|
|
* steam-backlog-enforcer enforces ``Resolution.gaming_minutes``;
|
|
* screen-locker writes ``Resolution.shutdown_minutes`` to the schedule.
|
|
|
|
A consumer's whole integration::
|
|
|
|
import earned_time
|
|
|
|
answers = {
|
|
e.name: earned_time.done_today(e, home / e.ledger, KEY_FILE)
|
|
for e in earned_time.EARNERS
|
|
if e.ledger is not None
|
|
}
|
|
answers["workout"] = my_workout_count()
|
|
day = earned_time.resolve(answers, sick=sickdays.is_sick_day())
|
|
apply(day.gaming_minutes)
|
|
|
|
``sick`` (0.11.0) only raises shutdown to at least 20:00; gaming is unchanged.
|
|
``leisure`` (0.16.0): ``None`` follows the switch file on ``day``, ``True``
|
|
prices gaming with the leisure base and cap (the shadow budget), ``False``
|
|
with the old ones. The first real leisure day is :func:`leisure_from`
|
|
(0.15.0, the switch file); ask :func:`leisure_active` for the gaming day.
|
|
|
|
How far to trust a gate is computed, not listed: :func:`maturity` (and
|
|
``python -m earned_time maturity``).
|
|
|
|
Adding a gate is one :class:`Earner` in :mod:`earned_time._policy`; with
|
|
``penalty_from`` set, the base drops by what it pays back from that day on.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from earned_time._credits import (
|
|
credit_units,
|
|
day_window,
|
|
first_credit_at,
|
|
)
|
|
from earned_time._evidence import real_credit
|
|
from earned_time._ladder import (
|
|
ANKI_WAIVED_FROM,
|
|
LADDER,
|
|
LADDER_FROM,
|
|
SHUTDOWN_CEILING_MINUTES,
|
|
TUTOR_FROM,
|
|
TUTOR_LADDER,
|
|
UNIFIED_FROM,
|
|
UNIFIED_LADDER,
|
|
WAKE_MINUTES,
|
|
Rung,
|
|
extra_shutdown_minutes_for,
|
|
ladder_for,
|
|
on_ladder,
|
|
shutdown_ceiling_for,
|
|
shutdown_minutes_for,
|
|
)
|
|
from earned_time._ledger import done_today, entry_signature, today_window, verified
|
|
from earned_time._leisure import (
|
|
LEISURE_BASE_MINUTES,
|
|
LEISURE_CEILING_MINUTES,
|
|
LEISURE_FROM,
|
|
LEISURE_FROM_PATH,
|
|
gaming_base_minutes_for,
|
|
gaming_ceiling_minutes_for,
|
|
leisure_active,
|
|
leisure_from,
|
|
)
|
|
from earned_time._maturity import (
|
|
MATURE_MIN_AGE_DAYS,
|
|
MATURE_MIN_CREDIT_DAYS,
|
|
Level,
|
|
Maturity,
|
|
confirmed_start,
|
|
maturity,
|
|
penalty_start,
|
|
)
|
|
from earned_time._policy import (
|
|
ANKI,
|
|
AUTOMATION,
|
|
EARNERS,
|
|
GAMING_BASE_MINUTES,
|
|
GAMING_CEILING_MINUTES,
|
|
LEETCODE,
|
|
READING,
|
|
SHUTDOWN_BASE_MINUTES,
|
|
TUTOR,
|
|
TUTOR_EARNERS,
|
|
WORKOUT,
|
|
Earner,
|
|
earner,
|
|
)
|
|
from earned_time._quota import TUTOR_QUOTA_FROM, TUTOR_QUOTA_MINUTES, tutor_quota_for
|
|
from earned_time._registry import all_earners, earners_for, registries
|
|
from earned_time._resolve import Base, Resolution, Term, base_for, resolve
|
|
from earned_time._sick import SICK_SHUTDOWN_FLOOR_MINUTES, sick_shutdown_from
|
|
|
|
__all__ = [
|
|
"ANKI",
|
|
"ANKI_WAIVED_FROM",
|
|
"AUTOMATION",
|
|
"EARNERS",
|
|
"GAMING_BASE_MINUTES",
|
|
"GAMING_CEILING_MINUTES",
|
|
"LADDER",
|
|
"LADDER_FROM",
|
|
"LEETCODE",
|
|
"LEISURE_BASE_MINUTES",
|
|
"LEISURE_CEILING_MINUTES",
|
|
"LEISURE_FROM",
|
|
"LEISURE_FROM_PATH",
|
|
"MATURE_MIN_AGE_DAYS",
|
|
"MATURE_MIN_CREDIT_DAYS",
|
|
"READING",
|
|
"SHUTDOWN_BASE_MINUTES",
|
|
"SHUTDOWN_CEILING_MINUTES",
|
|
"SICK_SHUTDOWN_FLOOR_MINUTES",
|
|
"TUTOR",
|
|
"TUTOR_EARNERS",
|
|
"TUTOR_FROM",
|
|
"TUTOR_LADDER",
|
|
"TUTOR_QUOTA_FROM",
|
|
"TUTOR_QUOTA_MINUTES",
|
|
"UNIFIED_FROM",
|
|
"UNIFIED_LADDER",
|
|
"WAKE_MINUTES",
|
|
"WORKOUT",
|
|
"Base",
|
|
"Earner",
|
|
"Level",
|
|
"Maturity",
|
|
"Resolution",
|
|
"Rung",
|
|
"Term",
|
|
"all_earners",
|
|
"base_for",
|
|
"confirmed_start",
|
|
"credit_units",
|
|
"day_window",
|
|
"done_today",
|
|
"earner",
|
|
"earners_for",
|
|
"entry_signature",
|
|
"extra_shutdown_minutes_for",
|
|
"first_credit_at",
|
|
"gaming_base_minutes_for",
|
|
"gaming_ceiling_minutes_for",
|
|
"ladder_for",
|
|
"leisure_active",
|
|
"leisure_from",
|
|
"maturity",
|
|
"on_ladder",
|
|
"penalty_start",
|
|
"real_credit",
|
|
"registries",
|
|
"resolve",
|
|
"shutdown_ceiling_for",
|
|
"shutdown_minutes_for",
|
|
"sick_shutdown_from",
|
|
"today_window",
|
|
"tutor_quota_for",
|
|
"verified",
|
|
]
|