testsAndMisc/python_pkg/wsg_grabber/_undo.py
Krzysztof kuhy Rudnicki 62ad1484a6 Split wsg_grabber/review.py under the 250-line cap
review.py held three separable concerns. Two move out:

  _render.py  emptiness, status_line, filename_line, title_line, render --
              pure state-to-text, no toolkit and no transitions
  _undo.py    can_undo, on_undo, forget_last -- correcting the tally and
              putting the on-screen video back at the front of the queue

review.py keeps the state transitions and re-exports all eight names through
__all__, so ui.py and test_review.py reach them at review.* exactly as before;
verified by resolving every name in __all__.

Both new modules reference SessionState under TYPE_CHECKING only, so there is
no runtime cycle. Also inlined _worker_message, a single-caller passthrough
that just returned event.message.

review.py 330 -> 196, _render.py 107, _undo.py 74.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 15:42:10 +02:00

75 lines
2.2 KiB
Python

"""Taking verdicts back.
Split out of :mod:`python_pkg.wsg_grabber.review` to keep that module under the
250-line cap. Undo is its own concern: it has to correct the running tally,
put the video that was on screen back at the front of the queue, and cope with
a file that has since moved -- none of which the forward path cares about.
"""
from __future__ import annotations
from dataclasses import replace
from typing import TYPE_CHECKING
from python_pkg.wsg_grabber.models import Verdict
if TYPE_CHECKING:
from python_pkg.wsg_grabber.models import ReviewedItem
from python_pkg.wsg_grabber.review import SessionState
def can_undo(state: SessionState) -> bool:
"""Report whether there is a verdict to take back.
Args:
state: Current state.
Returns:
bool: True when the index holds at least one reversible verdict.
"""
return state.undoable > 0
def on_undo(state: SessionState, action: ReviewedItem) -> SessionState:
"""Show a restored video again after its verdict was reversed.
The video currently on screen goes to the front of the queue, so changing
your mind never skips anything.
Args:
state: Current state.
action: The verdict that was just reversed on disk and in the index.
Returns:
SessionState: Updated state showing the restored video.
"""
queue = list(state.pending)
if state.current is not None:
queue.insert(0, state.current)
corrected = (
replace(state, kept=max(0, state.kept - 1))
if action.choice is Verdict.KEEP
else replace(state, passed=max(0, state.passed - 1))
)
return replace(
corrected,
undoable=max(0, state.undoable - 1),
pending=tuple(queue),
current=action.item,
)
def forget_last(state: SessionState) -> SessionState:
"""Drop one verdict from the undo count without reversing it.
Used when the file is no longer where the verdict left it, so a second
attempt does not keep failing on the same entry.
Args:
state: Current state.
Returns:
SessionState: Updated state.
"""
return replace(state, undoable=max(0, state.undoable - 1))