diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index 96c9fff..041efb9 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -3861,13 +3861,16 @@ class Flomni( print(f"Deleted tomo queue job #{index} ({job['label']}).") def tomo_queue_move(self, index: int, new_index: int) -> None: - """Move a pending/incomplete tomo queue job to a new position. + """Move a pending tomo queue job to a new position. - Only ``pending``/``incomplete`` jobs can be reordered -- a - ``running`` or ``done`` job stays put. A move is also refused if it - would place the job at or above the position of the job currently - ``running`` (that job is a hard floor: nothing may be reordered - ahead of the thing already in progress). Takes effect at the next + Only ``pending`` jobs can be reordered -- a ``running``, + ``incomplete``, or ``done`` job stays put. ``incomplete`` is a floor + exactly like ``running``: tomo_queue_execute()'s pick-next loop + always resumes whichever of the two it finds first, regardless of + list order, so a pending job moved "ahead" of one in the list would + visually suggest a run order that isn't real. A move is also + refused if it would place the job at or above the position of any + currently ``running``/``incomplete`` job. Takes effect at the next job boundary if the queue is executing (see tomo_queue_execute()'s pick-next model). @@ -3882,16 +3885,19 @@ class Flomni( if not 0 <= new_index < len(jobs): raise ValueError(f"tomo_queue_move: new_index {new_index} out of range.") job = jobs[index] - if job.get("status", "pending") not in ("pending", "incomplete"): + if job.get("status", "pending") != "pending": raise ValueError( f"tomo_queue_move: job #{index} is '{job.get('status')}' -- only " - "pending/incomplete jobs can be reordered." + "pending jobs can be reordered." ) - running_idx = next((i for i, j in enumerate(jobs) if j.get("status") == "running"), None) - if running_idx is not None and new_index <= running_idx: + floor_indices = [ + i for i, j in enumerate(jobs) if j.get("status") in ("running", "incomplete") + ] + if floor_indices and new_index <= max(floor_indices): raise ValueError( f"tomo_queue_move: can't move to position {new_index} -- job " - f"#{running_idx} is currently running and is a hard floor for reordering." + f"#{max(floor_indices)} is running/incomplete and is a hard floor " + "for reordering." ) self._tomo_queue_proxy.move(index, new_index) print(f"Moved tomo queue job #{index} -> #{new_index} ({job['label']}).") diff --git a/csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py b/csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py index 5e00676..58ccfb8 100644 --- a/csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py +++ b/csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py @@ -1043,10 +1043,14 @@ class TomoQueueDialog(QDialog): _POLL_MS = 2000 # Statuses a job must have to be reorderable in sort mode. "running" and - # "done" are excluded: running is a hard floor (plan section 6.4), done - # is pinned for readability (plan section 6.3) even though moving it - # would be harmless. - _MOVABLE_STATUSES = ("pending", "incomplete") + # "incomplete" are both hard floors: tomo_queue_execute()'s pick-next + # loop always resumes whichever of them it finds first, regardless of + # list position (see TOMO_QUEUE_TESTING.md's resume-before-fresh + # rationale) -- so a pending job dragged "ahead" of one in the list + # would visually suggest a run order that isn't real. "done" is pinned + # for readability (plan section 6.3) even though moving it would be + # harmless. + _MOVABLE_STATUSES = ("pending",) def __init__(self, client, parent=None): super().__init__(parent) @@ -1125,7 +1129,7 @@ class TomoQueueDialog(QDialog): self._btn_sort = QPushButton("Sort queue…") self._btn_sort.setCheckable(True) self._btn_sort.setToolTip( - "Reorder pending/incomplete jobs. The running job (if any) and " + "Reorder pending jobs. The running/incomplete job (if any) and " "done jobs stay put." ) self._btn_move_up = QPushButton("▲ Move up") @@ -1279,10 +1283,10 @@ class TomoQueueDialog(QDialog): def _move_selected(self, direction: int) -> None: """Swap the selected row with the adjacent one. Only ever swaps two - movable (pending/incomplete) rows -- a running or done neighbour - blocks the swap, which is what keeps the running job as a hard - floor (plan section 6.4) and done jobs pinned (section 6.3) without - needing separate index-range bookkeeping. + movable (pending) rows -- a running, incomplete, or done neighbour + blocks the swap, which is what keeps the running/incomplete job as + a hard floor (plan section 6.4) and done jobs pinned (section 6.3) + without needing separate index-range bookkeeping. """ row = self._selected_single_row() jobs = self._load_queue()