screen-locker/screen_locker/_glyphs.py
Krzysztof kuhy Rudnicki e5bfdd9a9a Make the Tk locker screens text-free
Every locker screen, the status window and the credit toast now show
glyphs (screen_locker/_glyphs.py) and numbers, coloured by state.
Sentences that carried a reason (verifier message, sync diagnosis,
sick refusal, form rejection) go to the log at warning instead.
New pre-commit hook text-free runs scripts/verify_text_free.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 01:20:23 +01:00

99 lines
3.4 KiB
Python

"""The locker's pictograms: one place, so a glyph means one thing everywhere.
textgate (kuhy, 2026-11-08): nothing he sees carries words. Every screen is
built from these plus numbers, each paired with a palette colour as the
second channel (success / warning / danger / muted). Plain geometric
characters only: a colour-emoji fallback font can crash Tk 8.6, so no
U+1Fxxx and nothing with an emoji presentation by default.
"""
from __future__ import annotations
from typing import Final
LIFT: Final = "●━━●" # a barbell: the StrongLifts workout / "go work out"
RUN: Final = "⇝" # a RunnerUp run
PHONE: Final = "▯" # the phone (adb)
WAIT: Final = "⋯" # checking / unlocking in progress
OK: Final = "✓"
FAIL: Final = "✗"
RETRY: Final = "↻"
CLOSE: Final = "✕"
BACK: Final = "↩"
GO: Final = "▶"
SKIP: Final = "↷"
SYNC: Final = "⇅" # a sync backend (Firebase)
CLOCK: Final = "◷" # time, the system clock
SHORT: Final = "◔" # a session shorter than the minimum
FORM: Final = "≡" # the manual-workout evidence form
SICK: Final = "✚" # a sick day
SHUTDOWN: Final = "⏻" # the PC's shutdown time
STREAK: Final = "▰" # one week of the streak
WEEK_DONE: Final = "●" # a counted day
WEEK_SICK: Final = "✚" # a sick day in the week row
WEEK_EMPTY: Final = "○" # a day with nothing counted
HEAT: Final = "▲" # the outdoor temperature
LOCK: Final = "■" # the lock fired
UNLOCK: Final = "□" # the lock stood down
OPTIONAL: Final = "◇" # Tue-Thu: a workout is optional today
GIFT: Final = "+" # an earned reward, always followed by its amount
# Startup phone-check status -> the glyph that replaces its sentence.
STATUS_GLYPH: Final[dict[str, str]] = {
"too_short": SHORT,
"clock_tampered": f"{CLOCK}{FAIL}",
"stale": f"{CLOCK}{FAIL}",
"no_exercises": f"{LIFT} 0",
"not_verified": FAIL,
"no_phone": f"{PHONE}{FAIL}",
"error": f"{PHONE}{FAIL}",
}
# log.json entry ``type`` -> glyph, for the status window's day cells.
ENTRY_GLYPH: Final[dict[str, str]] = {
"phone_verified": LIFT,
"strength": LIFT,
"pc_workout_verified": LIFT,
"runnerup_verified": RUN,
"running": RUN,
"manual_workout": FORM,
"heat_skip": HEAT,
"relaxed_day_skip": SKIP,
"early_bird": OK,
}
# Lock-decision stage (``_compliance_state._STEPS`` names) -> glyph. A stage
# not listed shows only the lock/unlock square and its colour.
STAGE_GLYPH: Final[dict[str, str]] = {
"scheduled_skip": SKIP,
"early_bird_pending_expired": CLOCK,
"early_bird_window_open": CLOCK,
"sick_day": SICK,
"already_logged": OK,
"wake_alarm_skip": CLOCK,
"test_hold_active": WAIT,
"relaxed_day_already_skipped": f"{OPTIONAL}{SKIP}",
"relaxed_day": OPTIONAL,
"weekly_minimum_met": WEEK_DONE,
"evening_lockdown_today": SHUTDOWN,
"would_lock": LIFT,
}
def entry_glyphs(types: tuple[str, ...]) -> str:
"""Glyphs for a day's entry types; an unknown type shows as ``OK``."""
return " ".join(ENTRY_GLYPH.get(t, OK) for t in types)
def dots(done: int, total: int) -> str:
"""``●●○○ 2/4``: progress as filled dots plus the numbers (two channels)."""
filled = max(0, min(done, total))
return f"{WEEK_DONE * filled}{WEEK_EMPTY * (total - filled)} {done}/{total}"
def status_glyph(status: str) -> str:
"""Return the glyph for a phone-check *status* (``FAIL`` if unknown)."""
return STATUS_GLYPH.get(status, FAIL)