From a734be5f948edd5cfbd0f1989ffc02535ec99f6e Mon Sep 17 00:00:00 2001 From: Asher Norland Date: Tue, 17 Mar 2026 11:26:43 -0700 Subject: [PATCH] Fix --- .../application/screen/action_card.py | 19 +++++++++++++ synodic_client/application/screen/install.py | 21 +++++++++------ synodic_client/operations/schema.py | 27 +++++++++++++------ 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/synodic_client/application/screen/action_card.py b/synodic_client/application/screen/action_card.py index 12996df..a538ec1 100644 --- a/synodic_client/application/screen/action_card.py +++ b/synodic_client/application/screen/action_card.py @@ -632,6 +632,7 @@ def __init__(self, parent: QWidget | None = None) -> None: self._cards: list[ActionCard] = [] self._action_map: dict[SetupAction, ActionCard] = {} + self._index_map: dict[int, ActionCard] = {} # ------------------------------------------------------------------ # Skeleton loading @@ -683,6 +684,14 @@ def populate( self._cards.append(card) self._action_map[act] = card + # Build original-index → card mapping so callers can look up by + # the action index porringer emits, which is independent of the + # display sort order. + for original_index, act in enumerate(actions): + card = self._action_map.get(act) + if card is not None: + self._index_map[original_index] = card + # ------------------------------------------------------------------ # Card lookup # ------------------------------------------------------------------ @@ -697,6 +706,15 @@ def card_count(self) -> int: """Return the number of cards (including skeletons).""" return len(self._cards) + def card_for_action_index(self, action_index: int) -> ActionCard | None: + """Return the card for the given original action index. + + The index corresponds to the action's position in the unsorted + list passed to :meth:`populate`, matching the indices emitted + by porringer's ``ActionCompletedEvent``. + """ + return self._index_map.get(action_index) + def get_card(self, action: SetupAction) -> ActionCard | None: """Look up the card for a given action. @@ -727,3 +745,4 @@ def clear(self) -> None: card.deleteLater() self._cards.clear() self._action_map.clear() + self._index_map.clear() diff --git a/synodic_client/application/screen/install.py b/synodic_client/application/screen/install.py index 2433936..5ada4cb 100644 --- a/synodic_client/application/screen/install.py +++ b/synodic_client/application/screen/install.py @@ -638,7 +638,7 @@ def _on_action_checked(self, row: int, result: SetupActionResult, status: str) - # Update the card widget if m.preview and 0 <= row < len(m.preview.actions): action = m.preview.actions[row] - card = self._card_list.get_card(action) + card = self._card_list.card_for_action_index(row) if card is not None: card.set_check_result(result, status) @@ -679,15 +679,20 @@ def _on_preview_finished(self) -> None: finalized, ) - # Compute summary + # Compute summary using the shared operations-layer classifier + from collections import Counter + + from synodic_client.operations.schema import classify_status + total = len(m.action_states) - needed = sum(1 for s in m.action_states if s.status == 'Needed') + counts = Counter(classify_status(s.status) for s in m.action_states) + needed = counts.get('needed', 0) + satisfied = counts.get('satisfied', 0) + pending = counts.get('pending', 0) + ready = counts.get('ready', 0) + unavailable = counts.get('unavailable', 0) + failed = counts.get('failed', 0) upgradable = len(m.upgradable_keys) - unavailable = sum(1 for s in m.action_states if s.status == 'Not installed') - failed = sum(1 for s in m.action_states if s.status == 'Failed') - pending = sum(1 for s in m.action_states if s.status == 'Pending') - ready = sum(1 for s in m.action_states if s.status == 'Ready') - satisfied = total - needed - upgradable - unavailable - failed - pending - ready parts: list[str] = [] _counts: list[tuple[int, str]] = [ diff --git a/synodic_client/operations/schema.py b/synodic_client/operations/schema.py index 1f682f5..f06e9b1 100644 --- a/synodic_client/operations/schema.py +++ b/synodic_client/operations/schema.py @@ -50,19 +50,30 @@ def resolve_action_status(result: SetupActionResult, action: SetupAction) -> str return 'Needed' +_STATUS_BUCKETS: dict[str, str] = { + 'Needed': 'needed', + 'Pending': 'pending', + 'Ready': 'ready', + 'Not installed': 'unavailable', + 'Failed': 'failed', + 'Already installed': 'satisfied', + 'Already latest': 'satisfied', +} + + def classify_status(status: str) -> str: """Classify a resolved status string into a summary bucket. - Returns one of ``'needed'``, ``'satisfied'``, ``'pending'``, or - ``'unknown'``. Upgradability is determined separately from skip - reason, so it is not included here. + Returns one of ``'needed'``, ``'satisfied'``, ``'pending'``, + ``'ready'``, ``'unavailable'``, ``'failed'``, or ``'unknown'``. + Upgradability is determined separately from skip reason, so it + is not included here. """ - if status == 'Needed': - return 'needed' - if '\u2713' in status or status in {'Already installed', 'Already latest'}: + bucket = _STATUS_BUCKETS.get(status) + if bucket is not None: + return bucket + if '\u2713' in status: return 'satisfied' - if status == 'Pending': - return 'pending' return 'unknown'