Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Diff and Extract View Filtering Options #1749

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
84c5b74
Initial Search box
jetchirag Jul 11, 2023
384b41e
Created a base file_dialog class to reuse code in diff and extract di…
jetchirag Jul 17, 2023
45f3383
Added search syntax with name and path search along with a error signal
jetchirag Jul 21, 2023
1da2ecd
Use existing method to join path in searchpattern
jetchirag Jul 21, 2023
69649b0
Added search button
jetchirag Jul 21, 2023
b947858
Added size filter, improved string search
jetchirag Jul 25, 2023
f14a1c6
Added change type filter
jetchirag Jul 29, 2023
15b2fd4
Added search to extract view
jetchirag Jul 29, 2023
370a4fb
Added Balance and healthy filter
jetchirag Aug 8, 2023
4247e82
Added Last Modified filter
jetchirag Aug 12, 2023
a1cbdfc
Added Search Icon
jetchirag Aug 16, 2023
67e884f
Merge branch 'master' into search-file
jetchirag Aug 16, 2023
ba80f2e
Added search clear icon and minor fixes with borg outputs
jetchirag Aug 19, 2023
f965fb4
Merge master
jetchirag Aug 19, 2023
1e8bd75
Merge branch 'search-file' of github.com:jetchirag/vorta into search-…
jetchirag Aug 19, 2023
b91ada9
Added new flag 'exclude-parents' and added test to cover all flags an…
jetchirag Aug 19, 2023
2db3d24
Modular approach for Diff and Extract Tree Views with base class
jetchirag Aug 21, 2023
547d3b4
Fixed Tooltip border on error
jetchirag Aug 21, 2023
57309cc
Added docstrings description to BaseFileDialog
jetchirag Aug 21, 2023
1b0f0b6
Added docstring for test
jetchirag Aug 21, 2023
a56af3a
Fixed pytest exception with scheduler
jetchirag Aug 21, 2023
d45d6af
Merge branch 'master' into search-file
jetchirag Aug 21, 2023
43c2d91
Merge branch 'search-file' of github.com:jetchirag/vorta into search-…
jetchirag Aug 21, 2023
0706d87
Fix diff test
jetchirag Aug 21, 2023
cd77419
Added missing docstring in treemodel
jetchirag Aug 21, 2023
b1fdbf4
Merge branch 'master' into search-file
jetchirag Sep 8, 2023
7ebc36e
Common dest for healthy and unhealthy search syntax
jetchirag Sep 8, 2023
e2912f5
Added tests for both extract and diff filtering
jetchirag Sep 25, 2023
8950eb2
Merge branch 'master' into search-file
jetchirag Sep 25, 2023
06560d2
Resolve merge conflict
jetchirag Oct 28, 2023
9e43cca
Fixed test; Added help icon linking to doc
jetchirag Oct 28, 2023
f288952
Merge remote-tracking branch 'upstream/master' into search-file
jetchirag Feb 9, 2024
4f46c4a
Minor refactoring
jetchirag Feb 9, 2024
8c28bde
Merge branch 'master' into search-file
jetchirag Mar 29, 2024
63e231e
Merge remote-tracking branch 'upstream/master' into search-file
jetchirag Apr 8, 2024
53fef0b
Merge branch 'search-file' of github.com:jetchirag/vorta into search-…
jetchirag Apr 8, 2024
a6a0707
Remove condition to force --path with -m fm
jetchirag Apr 8, 2024
ab39467
Check for invalid regex pattern
jetchirag Apr 8, 2024
e53546a
Test now check whether test syntax emitted error or not
jetchirag Apr 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/vorta/assets/UI/diffresult.ui
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,54 @@
</item>
</layout>
</item>

<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>4</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>

<widget class="QLineEdit" name="searchWidget">
<property name="toolTip">
<string>Search Pattern</string>
</property>
<property name="placeholderText">
<string>Search Pattern</string>
</property>
</widget>

</item>

<item>
<widget class="QToolButton" name="bSearch">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Submit Search</string>
</property>
</widget>
</item>

<item>
<widget class="QToolButton" name="bHelp">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Help</string>
</property>
</widget>
</item>

</layout>
</item>


<item>
<widget class="QTreeView" name="treeView"/>
</item>
Expand Down
49 changes: 49 additions & 0 deletions src/vorta/assets/UI/extractdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,55 @@
</item>
</layout>
</item>

<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>4</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>

<widget class="QLineEdit" name="searchWidget">
<property name="toolTip">
<string>Search Pattern</string>
</property>
<property name="placeholderText">
<string>Search Pattern</string>
</property>
</widget>

</item>

<item>
<widget class="QToolButton" name="bSearch">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Submit Search</string>
</property>
</widget>
</item>

<item>
<widget class="QToolButton" name="bHelp">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Help</string>
</property>
</widget>
</item>


</layout>
</item>


<item>
<widget class="QTreeView" name="treeView">
<attribute name="headerMinimumSectionSize">
Expand Down
2 changes: 2 additions & 0 deletions src/vorta/assets/icons/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
167 changes: 60 additions & 107 deletions src/vorta/views/diff_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import logging
import re
import webbrowser
from dataclasses import dataclass
from pathlib import PurePath
from typing import List, Optional, Tuple
Expand All @@ -10,26 +11,24 @@
from PyQt6.QtCore import (
QDateTime,
QLocale,
QMimeData,
QModelIndex,
QPoint,
Qt,
QThread,
QUrl,
)
from PyQt6.QtGui import QColor, QKeySequence, QShortcut
from PyQt6.QtWidgets import QApplication, QHeaderView, QMenu, QTreeView
from PyQt6.QtGui import QColor
from PyQt6.QtWidgets import QHeaderView

from vorta.store.models import SettingsModel
from vorta.utils import get_asset, pretty_bytes, uses_dark_mode
from vorta.views.partials.file_dialog import BaseFileDialog
from vorta.views.partials.treemodel import (
FileSystemItem,
FileTreeModel,
FileTreeSortProxyModel,
FileTreeSortFilterProxyModel,
path_to_str,
relative_path,
)
from vorta.views.utils import get_colored_icon
from vorta.views.utils import compare_values_with_sign, get_colored_icon

uifile = get_asset('UI/diffresult.ui')
DiffResultUI, DiffResultBase = uic.loadUiType(uifile)
Expand Down Expand Up @@ -65,121 +64,40 @@ def run(self) -> None:
parse_diff_lines(lines, self.model)


class DiffResultDialog(DiffResultBase, DiffResultUI):
class DiffResultDialog(BaseFileDialog, DiffResultBase, DiffResultUI):
"""Display the results of `borg diff`."""

def __init__(self, archive_newer, archive_older, model: 'DiffTree'):
"""Init."""
super().__init__()
self.setupUi(self)

self.model = model
self.model.setParent(self)

self.treeView: QTreeView
self.treeView.setUniformRowHeights(True) # Allows for scrolling optimizations.
self.treeView.setAlternatingRowColors(True)
self.treeView.setTextElideMode(Qt.TextElideMode.ElideMiddle) # to better see name of paths

# custom context menu
self.treeView.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.treeView.customContextMenuRequested.connect(self.treeview_context_menu)

# shortcuts
shortcut_copy = QShortcut(QKeySequence.StandardKey.Copy, self.treeView)
shortcut_copy.activated.connect(self.diff_item_copy)
def __init__(self, archive_newer, archive_older, model):
super().__init__(model)

# add sort proxy model
self.sortproxy = DiffSortProxyModel(self)
self.sortproxy.setSourceModel(self.model)
self.treeView.setModel(self.sortproxy)
self.sortproxy.sorted.connect(self.slot_sorted)

self.treeView.setSortingEnabled(True)

# header
header = self.treeView.header()
header.setStretchLastSection(False) # stretch only first section
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)

# signals

self.archiveNameLabel_1.setText(f'{archive_newer.name}')
self.archiveNameLabel_2.setText(f'{archive_older.name}')

self.comboBoxDisplayMode.currentIndexChanged.connect(self.change_display_mode)
diff_result_display_mode = SettingsModel.get(key='diff_files_display_mode').str_value
self.comboBoxDisplayMode.setCurrentIndex(int(diff_result_display_mode))
self.bFoldersOnTop.toggled.connect(self.sortproxy.keepFoldersOnTop)
self.bCollapseAll.clicked.connect(self.treeView.collapseAll)
self.bHelp.clicked.connect(lambda: webbrowser.open('https://vorta.borgbase.com/usage/search/'))

self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
def get_sort_proxy_model(self):
"""Return the sort proxy model for the tree view."""
return DiffSortFilterProxyModel(self)

self.set_icons()

# Connect to palette change
QApplication.instance().paletteChanged.connect(lambda p: self.set_icons())
def get_diff_result_display_mode(self):
return SettingsModel.get(key='diff_files_display_mode').str_value

def set_icons(self):
"""Set or update the icons in the right color scheme."""
self.bCollapseAll.setIcon(get_colored_icon('angle-up-solid'))
self.bFoldersOnTop.setIcon(get_colored_icon('folder-on-top'))
self.bSearch.setIcon(get_colored_icon('search'))
self.bHelp.setIcon(get_colored_icon('help-about'))
self.comboBoxDisplayMode.setItemIcon(0, get_colored_icon("view-list-tree"))
self.comboBoxDisplayMode.setItemIcon(1, get_colored_icon("view-list-tree"))
self.comboBoxDisplayMode.setItemIcon(2, get_colored_icon("view-list-details"))

def treeview_context_menu(self, pos: QPoint):
"""Display a context menu for `treeView`."""
index = self.treeView.indexAt(pos)
if not index.isValid():
# popup only for items
return

menu = QMenu(self.treeView)

menu.addAction(
get_colored_icon('copy'),
self.tr("Copy"),
lambda: self.diff_item_copy(index),
)

if self.model.getMode() != self.model.DisplayMode.FLAT:
menu.addSeparator()
menu.addAction(
get_colored_icon('angle-down-solid'),
self.tr("Expand recursively"),
lambda: self.treeView.expandRecursively(index),
)

menu.popup(self.treeView.viewport().mapToGlobal(pos))

def diff_item_copy(self, index: QModelIndex = None):
"""
Copy a diff item path to the clipboard.

Copies the first selected item if no index is specified.
"""
if index is None or (not index.isValid()):
indexes = self.treeView.selectionModel().selectedRows()

if not indexes:
return

index = indexes[0]

index = self.sortproxy.mapToSource(index)
item: DiffItem = index.internalPointer()
path = PurePath('/', *item.path)

data = QMimeData()
data.setUrls([QUrl(path.as_uri())])
data.setText(str(path))

QApplication.clipboard().setMimeData(data)

def change_display_mode(self, selection: int):
"""
Change the display mode of the tree view
Expand All @@ -206,13 +124,6 @@ def change_display_mode(self, selection: int):

self.model.setMode(mode)

def slot_sorted(self, column, order):
"""React the tree view being sorted."""
# reveal selection
selectedRows = self.treeView.selectionModel().selectedRows()
if selectedRows:
self.treeView.scrollTo(selectedRows[0])


# ---- Output parsing --------------------------------------------------------

Expand Down Expand Up @@ -488,11 +399,53 @@ def size_to_byte(significand: str, unit: str) -> int:
# ---- Sorting ---------------------------------------------------------------


class DiffSortProxyModel(FileTreeSortProxyModel):
class DiffSortFilterProxyModel(FileTreeSortFilterProxyModel):
"""
Sort a DiffTree model.
"""

def __init__(self, parent=None) -> None:
super().__init__(parent)

def get_parser(self):
"""Add Diff view specific arguments to the parser."""
parser = super().get_parser()
parser.add_argument(
"-b", "--balance", type=FileTreeSortFilterProxyModel.valid_size, help="Match by balance size."
)
parser.add_argument("-c", "--change", choices=["A", "D", "M"], help="Only available in Diff View.")

return parser

def filterAcceptsRow(self, sourceRow: int, sourceParent: QModelIndex) -> bool:
"""
Return whether the row should be accepted.
"""

if not self.searchPattern:
return True

if not super().filterAcceptsRow(sourceRow, sourceParent):
return False

model = self.sourceModel()
item = model.index(sourceRow, 0, sourceParent).internalPointer()

if self.searchPattern.balance:
item_balance = item.data.size

for filter_balance in self.searchPattern.balance:
if not compare_values_with_sign(item_balance, filter_balance[1], filter_balance[0]):
return False

if self.searchPattern.change:
item_change = item.data.change_type.short()

if item_change != self.searchPattern.change:
return False

return True

def choose_data(self, index: QModelIndex):
"""Choose the data of index used for comparison."""
item: DiffItem = index.internalPointer()
Expand Down
Loading
Loading