mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 20:07:16 +01:00
_runnerup_verification.py (312) -> _runnerup_tcx.py pull + parse TCX
_runnerup_backfill.py fill the log for a
date or a week
_ui_widgets.py (287) -> _ui_form_fields.py labelled fields,
focus, disable_paste
_sick_dialog.py (278) -> _sick_commitment.py next-day commitment
_ui_form_fields also takes disable_paste, since the fields are what use it;
_ui_widgets re-exports it for _manual_workout_widgets and _sick_dialog.
Two things had to move with the code rather than being left behind:
- conftest's _TK_MODULES gains _ui_form_fields, or its tkinter calls reach a
real display instead of the mock.
- pyproject's S314 waiver follows the ET.parse call it covers into
_runnerup_tcx.py. Same approved suppression, new home -- not a new one.
Test count 977 -> 978: _ui_form_fields is a new lock-surface module, so
test_no_popup_widgets safety-checks it too. 100% branch coverage held; pylint
back at 8.70 after fixing a genuinely undefined WidgetGroup annotation the
split exposed. `screen_lock --status` unchanged. Gate: 41 -> 28 files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EveB813BQ8abAP68VSqZ5Y
91 lines
3.3 KiB
Python
91 lines
3.3 KiB
Python
"""The next-day commitment prompt shown after a sick day.
|
|
|
|
Split out of :mod:`screen_locker._sick_dialog` to keep every file under the
|
|
250-line cap. Composed back into ``SickDialogMixin`` there, so callers see no
|
|
change.
|
|
|
|
Taking a sick day asks for a commitment to train tomorrow; breaking it is
|
|
recorded and costs budget, which is what stops sick days being free.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from screen_locker import _sick_tracker
|
|
from screen_locker._constants import COMMITMENT_PROMPT_TIMEOUT_SECONDS
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Callable
|
|
|
|
|
|
class SickCommitmentMixin:
|
|
"""Asks for, times out, and records the next-day training commitment."""
|
|
|
|
def _show_commitment_prompt(self, *, on_done: Callable[[], None]) -> None:
|
|
"""Ask the user to commit to working out tomorrow.
|
|
|
|
Calls ``on_done()`` once the user answers or the timeout elapses.
|
|
"""
|
|
self.clear_container()
|
|
self._label(
|
|
"Commit to working out tomorrow?",
|
|
role="display",
|
|
color=self._colors.warning,
|
|
pad="md",
|
|
)
|
|
# ~80 chars at font_size 16 would render as one unbroken line past the
|
|
# ~70-char readable range (rule 21) -- wrap it explicitly.
|
|
prompt = self._text(
|
|
"If you say YES and skip via 'I'm sick' tomorrow, "
|
|
"the sick day costs 2x normal.",
|
|
role="body",
|
|
)
|
|
prompt.config(wraplength=560, justify="center")
|
|
self._commitment_done_fn = on_done
|
|
self._commitment_remaining = COMMITMENT_PROMPT_TIMEOUT_SECONDS
|
|
self._commitment_timer_label = self._text(
|
|
f"Auto-skipping in {COMMITMENT_PROMPT_TIMEOUT_SECONDS}s",
|
|
color=self._colors.muted,
|
|
)
|
|
row = self._button_row()
|
|
self._button(
|
|
row,
|
|
"YES",
|
|
bg=self._colors.success,
|
|
command=lambda: self._answer_commitment(commit=True),
|
|
width=12,
|
|
).pack(side="left", padx=self._colors.space("sm"))
|
|
self._button(
|
|
row,
|
|
"NO",
|
|
bg=self._colors.field_bg,
|
|
command=lambda: self._answer_commitment(commit=False),
|
|
width=12,
|
|
).pack(side="left", padx=self._colors.space("sm"))
|
|
self._tick_commitment_timeout()
|
|
|
|
def _tick_commitment_timeout(self) -> None:
|
|
"""Advance commitment auto-skip timer; default to NO when it expires."""
|
|
if self._commitment_remaining <= 0:
|
|
self._answer_commitment(commit=False)
|
|
return
|
|
self._commitment_timer_label.config(
|
|
text=f"Auto-skipping in {self._commitment_remaining}s",
|
|
)
|
|
self._commitment_remaining -= 1
|
|
self.root.after(1000, self._tick_commitment_timeout)
|
|
|
|
def _answer_commitment(self, *, commit: bool) -> None:
|
|
"""Persist the commitment answer and call the completion callback."""
|
|
# Disable timer re-entry by zeroing remaining.
|
|
self._commitment_remaining = -1
|
|
if commit:
|
|
history = _sick_tracker.load_history()
|
|
_sick_tracker.record_commitment_for_tomorrow(history)
|
|
_sick_tracker.save_history(history)
|
|
done = getattr(self, "_commitment_done_fn", None)
|
|
if done is not None:
|
|
self._commitment_done_fn = None
|
|
done()
|