Wire ArchiveTableModel into ArchiveTab (QTableView migration, Phase 4 pt 1)#2519
Conversation
|
These were intentionally carried forward from #2517 so that one could merge quickly as a clean spike — they belong here, where the model goes live:
|
2d61227 to
198c795
Compare
|
Thanks!, addressed . |
|
Reviewed locally — two bugs found. The first is in this PR's new code; the second is pre-existing but sits right in the rename flow this PR reworks, so flagging it here too. 1. Size sorting breaks for archives ≥ 2 GiB (introduced here)Sorting the Size column produces a wrong order for any archive of 2 GiB or larger — i.e. most real backups. Small archives sort fine, which is why it looks correct in light testing. Root cause: the model returns a raw Python # archive_table_model.py
if column == self.COL_SIZE:
return row.size or 0The sort runs through a plain Repro (model + proxy, ascending sort on the Size column):
Why CI stays green: the Suggested fix: compare in Python instead of letting C++ truncate. A proxy subclass whose class ArchiveSortProxyModel(QSortFilterProxyModel):
def lessThan(self, left, right):
lv, rv = left.data(ArchiveTableModel.SortRole), right.data(ArchiveTableModel.SortRole)
if lv is None:
return True
if rv is None:
return False
return lv < rvUse it in place of the bare Please also add a regression test with realistic > 2 GiB sizes — the current suite would remain green even with this bug present. 2. Start-Backup button stuck "in progress" after a rename (pre-existing, but in this flow)After a rename, the main-window Root cause: every Borg job that drives that spinner pairs two overrides —
Deterministic repro: emit Scope: Suggested fix: give def finished_event(self, result):
self.app.backup_finished_event.emit(result)
self.result.emit(result)This stops the spinner on both success and failure and removes the success path's accidental dependency on the info-refresh side effect. |
|
@m3nu Just wanted to share my roadmap for the next few days. The Source tab is already open. I'll get Phase 4 Part 2 opened on Monday, and the other tabs' refactoring (smaller ones) on Tuesday. If everything goes smoothly with the review cycles, I am hoping to fully complete the view migration mid next week. Then, for next week, I plan to set up a draft PR for the scheduler refactor so we can start discussing the implementation. Let me know if you have any feedback on this schedule |
57a00af to
510c89a
Compare
m3nu
left a comment
There was a problem hiding this comment.
🔍 Code Review
PR: #2519 — Wire ArchiveTableModel into ArchiveTab (Phase 4 pt 1 of #2361)
Changes: +256 / −259 across 14 files · CI green (lint, unit macOS+Ubuntu, integration)
✅ Prior review items — all 6 verified fixed, with tests
archive_at()dropped;ArchiveRoleis the only accessor.- P2 icon-cache invalidation now tested (
test_trigger_icon_cache_hits_and_rebuilds_on_theme_switch: cache-hit identity + rebuild on theme flip). setDatacontract documented (overwritesrow.name; view stashes the original at edit-start).Dict[str, QIcon]typing fixed.- ≥2 GiB size-sort overflow fixed via
ArchiveSortProxyModel.lessThancomparingSortRolein Python, with a >2 GiB regression test that runs through the proxy (500 MiB / 2 GiB / 10 GiB). - Rename spinner:
BorgRenameJob.finished_eventmatches thecreate.pypattern; baserun()calls it once, so no doubleresultemit.
✅ Independent checks — clean
- All read/destructive sites (extract, mount, check, delete, diff, copy, refresh,
selected_archive_name) route throughselected_archives()readingArchiveRoleoff proxy indices — sort-safe, covered bytest_selection_maps_through_sort_proxy. NoEditTriggers+ explicitedit()makecell_double_clickedthe only editor entry, so theis_editing/original-name stash can't be bypassed;on_name_editedclearsis_editingbefore_revert_name, so the revert'sdataChangedcan't re-enter.- Delete DB query now repo-scoped — fixes a pre-existing cross-repo delete bug.
- Zero leftover
QTableWidget-API callers (.item(,row_of_archive,cellDoubleClicked,editItem) in src/ or tests/. - #2361 decisions respected: model in
views/partials/, no DB reads in the model (use_fixed_unitsinjected), i18n contexts preserved (Formheaders,ArchiveTabtooltips).
🔍 Review Details
1 inline comment (🔵 info: dead import — fine to fix in Phase 4 pt 2).
Optional hardening, not blocking: ArchiveSortProxyModel.lessThan returns True both ways when both keys are None (violates strict weak ordering). Unreachable today since _sort_data never returns None for valid columns; if lv is None: return rv is not None would future-proof it.
📊 Verdict: APPROVE ✅
Mergeable as-is. Nice, thorough round — every item from the last review landed with a test attached.
🤖 Reviewed by Claude Code
| from vorta.views.dialogs.archive import diff_result | ||
| from vorta.views.dialogs.archive.diff_result import DiffResultDialog, DiffTree | ||
| from vorta.views.source_tab import SizeItem | ||
| from vorta.views.partials.archive_table_model import SIZE_DECIMAL_DIGITS, ArchiveSortProxyModel, ArchiveTableModel |
There was a problem hiding this comment.
🔵 [dead-import] SIZE_DECIMAL_DIGITS is imported but unused
The constant moved into archive_table_model.py, which uses it internally — nothing in this file references it anymore. Ruff doesn't flag it because F401 is in the ignore list in pyproject.toml.
Fix:
from vorta.views.partials.archive_table_model import ArchiveSortProxyModel, ArchiveTableModelFine to fold into Phase 4 pt 2 rather than re-spin CI here.
Description
Wires the
ArchiveTableModelfrom #2517 intoArchiveTab, migrating the Archive tab from item-basedQTableWidgettoQTableView+QSortFilterProxyModel.Part of #2361 Phase 4.3088310Wire ArchiveTableModel into ArchiveTab2d61227Scope delete to repo, preserve scroll on refresh, ungate mount-openOnce #2517 lands I'll rebase onto master and the spike commits drop out.
Key changes:
.ui:QTableWidget→QTableView; sort viaQSortFilterProxyModel+setSortRole(SortRole)so non-string columns order by raw value, not lexically.selected_archives()helper readingArchiveRole: proxy-safe, so actions hit the right archive after sorting. All read sites (incl.archive_extract/archive_mount) route through it.view.edit()+ modeldataChanged;NoEditTriggersmakes the explicit edit the only editor entry.populate_from_profile(preserve_view=True)(keeps scroll offset instead of jumping to top); delete query scoped to repo.Related Issue
#2361 (Phase 4). Builds on #2517.
Motivation and Context
The item-based
QTableWidgetmixed data, formatting, and sort keys into widget items, which made sorting fragile (durations >24h sorted lexically, e.g.'1 day, …') and selection-after-sort error-prone. Moving to aQAbstractTableModel+ sort proxy separates data from presentation, fixes the sort-key bugs, and makes selection robust under sorting via a custom data role.How Has This Been Tested?
make test-unit(Borg 1.2.4) green, plus new tests for selection-after-sort and rename-failure revert. Manually checked sort → delete/rename hit the right archive.Screenshots (if appropriate):
Types of changes
Checklist:
I provide my contribution under the terms of the license of this repository and I affirm the Developer Certificate of Origin.