mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 00:27:01 +01:00
`requires-python` and the CI pin already said 3.14, but ruff, mypy and pylint were still grading `meta/` against 3.10 -- three tools judging an interpreter nothing runs any more, and ruff withholding the pyupgrade fixes that only become safe at the newer target. `pin_ci_python.py` rewrites these three knobs. What the stale target was hiding: `datetime.timezone.utc` where `datetime.UTC` is now correct (UP017), and ruff's stable formatter applying PEP 758 -- `except (A, B):` becomes `except A, B:`. That makes the source 3.14-only at the syntax level, which is what `requires-python = ">=3.14"` already promised; kuhy chose it deliberately over holding ruff a release back (2026-08-29). Also drops a comment in `wsg_grabber/models.py` that the bump makes false: it explained why the file used ``str, Enum`` instead of ``StrEnum`` under mypy at 3.10, sitting directly above a ``class Verdict(StrEnum)`` that had already stopped obeying it. ruff, ruff-format, mypy --strict and pylint all clean at the new target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
132 lines
4.4 KiB
Python
132 lines
4.4 KiB
Python
"""Turning a raw SNMP supply reading into a percentage, a bar and a verdict.
|
|
|
|
Split out of :mod:`python_pkg.brother_printer.display` to keep it under the
|
|
250-line cap. Only the arithmetic lives here. The functions that *print*
|
|
(``_display_supply_levels``, ``_display_supply_warnings``) stay in ``display``
|
|
because ``tests/test_display_part2.py`` patches them there, and a patched name
|
|
has to remain resolvable in the module the test names.
|
|
|
|
``display`` re-exports everything below, because the tests import these names
|
|
from there rather than from here.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from python_pkg.brother_printer.constants import (
|
|
BOLD,
|
|
GREEN,
|
|
PROGRESS_BAR_WIDTH,
|
|
RED,
|
|
RESET,
|
|
SNMP_LEVEL_LOW,
|
|
SNMP_LEVEL_OK,
|
|
SUPPLY_LOW_PCT,
|
|
SUPPLY_WARN_PCT,
|
|
YELLOW,
|
|
_out,
|
|
)
|
|
from python_pkg.brother_printer.data_classes import SupplyStatus
|
|
|
|
if TYPE_CHECKING:
|
|
from python_pkg.brother_printer.data_classes import NetworkResult
|
|
|
|
|
|
def _classify_percentage_level(desc: str, pct: int) -> tuple[int, str, str, str, bool]:
|
|
"""Classify a supply by its calculated percentage."""
|
|
if pct <= SUPPLY_LOW_PCT:
|
|
return pct, f"{pct}%", RED, f"{desc} at {pct}%.", True
|
|
if pct <= SUPPLY_WARN_PCT:
|
|
return pct, f"{pct}%", YELLOW, f"{desc} at {pct}% -- order soon.", False
|
|
return pct, f"{pct}%", GREEN, "", False
|
|
|
|
|
|
def _classify_supply_level(
|
|
desc: str, max_val: int, level: int
|
|
) -> tuple[int, str, str, str, bool]:
|
|
"""Classify a supply level. Returns (pct, status, color, warning, replace)."""
|
|
if level == SNMP_LEVEL_OK:
|
|
return -1, "OK", GREEN, "", False
|
|
if level == SNMP_LEVEL_LOW:
|
|
return -1, "LOW", RED, f"{desc} is LOW.", True
|
|
if level == 0:
|
|
return 0, "EMPTY", RED, f"{desc} is EMPTY -- replace now!", True
|
|
if max_val > 0:
|
|
pct = min(level * 100 // max_val, 100)
|
|
return _classify_percentage_level(desc, pct)
|
|
return -1, "", GREEN, "", False
|
|
|
|
|
|
def _format_supply_bar(pct: int) -> str:
|
|
"""Build a progress bar string for a supply percentage."""
|
|
if pct < 0:
|
|
return ""
|
|
filled = pct * PROGRESS_BAR_WIDTH // 100
|
|
empty = PROGRESS_BAR_WIDTH - filled
|
|
return f"[{'█' * filled}{'░' * empty}]"
|
|
|
|
|
|
def _process_supply_item(desc: str, max_val: int, level: int) -> SupplyStatus:
|
|
"""Process a single supply item into display info."""
|
|
pct, status_text, color, warning, needs_replacement = _classify_supply_level(
|
|
desc, max_val, level
|
|
)
|
|
bar_text = _format_supply_bar(pct)
|
|
return SupplyStatus(color, bar_text, status_text, warning, needs_replacement)
|
|
|
|
|
|
def _parse_supply_value(values: list[str], index: int) -> int:
|
|
"""Safely parse an integer from a supply value list."""
|
|
try:
|
|
return int(values[index])
|
|
except IndexError, ValueError:
|
|
return 0
|
|
|
|
|
|
def _collect_supply_items(
|
|
result: NetworkResult,
|
|
) -> tuple[list[SupplyStatus], list[str]]:
|
|
"""Parse and collect supply items with their descriptions."""
|
|
items: list[SupplyStatus] = []
|
|
descs: list[str] = []
|
|
for i, desc in enumerate(result.supplies.descriptions):
|
|
max_val = _parse_supply_value(result.supplies.max_values, i)
|
|
level = _parse_supply_value(result.supplies.levels, i)
|
|
items.append(_process_supply_item(desc, max_val, level))
|
|
descs.append(desc)
|
|
return items, descs
|
|
|
|
|
|
def render_life_bar(
|
|
label: str,
|
|
pct: int,
|
|
*,
|
|
exhausted: bool = False,
|
|
low: bool = False,
|
|
exhausted_note: str = "",
|
|
low_note: str = "",
|
|
) -> None:
|
|
"""Print one consumable's remaining-life bar.
|
|
|
|
The toner and drum bars in the page-count estimate were byte-for-byte the
|
|
same logic with different colours and notes, which is what this collapses.
|
|
|
|
Args:
|
|
label: Displayed name, already padded by the caller if needed.
|
|
pct: Percentage remaining, 0-100.
|
|
exhausted: Render red with ``exhausted_note``.
|
|
low: Render yellow with ``low_note``. Ignored when *exhausted*.
|
|
exhausted_note: Suffix shown when *exhausted*.
|
|
low_note: Suffix shown when *low*.
|
|
"""
|
|
filled = pct * PROGRESS_BAR_WIDTH // 100
|
|
meter = f"[{'█' * filled}{'░' * (PROGRESS_BAR_WIDTH - filled)}]"
|
|
if exhausted:
|
|
color, note = RED, exhausted_note
|
|
elif low:
|
|
color, note = YELLOW, low_note
|
|
else:
|
|
color, note = GREEN, ""
|
|
_out(f" {BOLD}{label}{RESET} {color}{meter} ~{pct}%{note}{RESET}")
|