mirror of
https://github.com/borgbase/vorta
synced 2024-12-21 23:33:13 +00:00
Setting for number format in archive tab. By @bigtedde (#1719)
This commit is contained in:
parent
b015368fee
commit
b58ffb6aed
7 changed files with 126 additions and 95 deletions
|
@ -59,6 +59,18 @@ def get_misc_settings() -> List[Dict[str, str]]:
|
|||
'label': trans_late('settings', 'Get statistics of file/folder when added'),
|
||||
'tooltip': trans_late('settings', 'When adding a new source, calculate its size and the number of files.'),
|
||||
},
|
||||
{
|
||||
'key': 'enable_fixed_units',
|
||||
'value': False,
|
||||
'type': 'checkbox',
|
||||
'group': information,
|
||||
'label': trans_late('settings', 'Use the same unit of measurement for archive sizes'),
|
||||
'tooltip': trans_late(
|
||||
'settings',
|
||||
'When enabled, all archive sizes will use the same unit of measurement, '
|
||||
'such as KB or MB. This can make archive sizes easier to compare.',
|
||||
),
|
||||
},
|
||||
{
|
||||
'key': 'use_system_keyring',
|
||||
'value': True,
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
# Used to store whether a user wanted to override the
|
||||
# default directory for the --development flag
|
||||
DEFAULT_DIR_FLAG = object()
|
||||
METRIC_UNITS = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
|
||||
NONMETRIC_UNITS = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']
|
||||
|
||||
borg_compat = BorgCompatibility()
|
||||
_network_status_monitor = None
|
||||
|
@ -140,14 +142,10 @@ def get_network_status_monitor():
|
|||
|
||||
def get_path_datasize(path, exclude_patterns):
|
||||
file_info = QFileInfo(path)
|
||||
data_size = 0
|
||||
|
||||
if file_info.isDir():
|
||||
data_size, files_count = get_directory_size(file_info.absoluteFilePath(), exclude_patterns)
|
||||
# logger.info("path (folder) %s %u elements size now=%u (%s)",
|
||||
# file_info.absoluteFilePath(), files_count, data_size, pretty_bytes(data_size))
|
||||
else:
|
||||
# logger.info("path (file) %s size=%u", file_info.path(), file_info.size())
|
||||
data_size = file_info.size()
|
||||
files_count = 1
|
||||
|
||||
|
@ -279,11 +277,7 @@ def pretty_bytes(
|
|||
if not isinstance(size, int):
|
||||
return ''
|
||||
prefix = '+' if sign and size > 0 else ''
|
||||
power, units = (
|
||||
(10**3, ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'])
|
||||
if metric
|
||||
else (2**10, ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'])
|
||||
)
|
||||
power, units = (10**3, METRIC_UNITS) if metric else (2**10, NONMETRIC_UNITS)
|
||||
if fixed_unit is None:
|
||||
n = find_best_unit_for_size(size, metric=metric, precision=precision)
|
||||
else:
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
from vorta.borg.rename import BorgRenameJob
|
||||
from vorta.borg.umount import BorgUmountJob
|
||||
from vorta.i18n import translate
|
||||
from vorta.store.models import ArchiveModel, BackupProfileMixin
|
||||
from vorta.store.models import ArchiveModel, BackupProfileMixin, SettingsModel
|
||||
from vorta.utils import (
|
||||
borg_compat,
|
||||
choose_file_dialog,
|
||||
|
@ -291,9 +291,12 @@ def populate_from_profile(self):
|
|||
|
||||
formatted_time = archive.time.strftime('%Y-%m-%d %H:%M')
|
||||
self.archiveTable.setItem(row, 0, QTableWidgetItem(formatted_time))
|
||||
self.archiveTable.setItem(
|
||||
row, 1, SizeItem(pretty_bytes(archive.size, fixed_unit=best_unit, precision=SIZE_DECIMAL_DIGITS))
|
||||
)
|
||||
|
||||
# format units based on user settings for 'dynamic' or 'fixed' units
|
||||
fixed_unit = best_unit if SettingsModel.get(key='enable_fixed_units').value else None
|
||||
size = pretty_bytes(archive.size, fixed_unit=fixed_unit, precision=SIZE_DECIMAL_DIGITS)
|
||||
self.archiveTable.setItem(row, 1, SizeItem(size))
|
||||
|
||||
if archive.duration is not None:
|
||||
formatted_duration = str(timedelta(seconds=round(archive.duration)))
|
||||
else:
|
||||
|
|
|
@ -78,6 +78,7 @@ def __init__(self, parent=None):
|
|||
self.repoTab.repo_changed.connect(self.archiveTab.populate_from_profile)
|
||||
self.repoTab.repo_changed.connect(self.scheduleTab.populate_from_profile)
|
||||
self.repoTab.repo_added.connect(self.archiveTab.refresh_archive_list)
|
||||
self.miscTab.refresh_archive.connect(self.archiveTab.populate_from_profile)
|
||||
|
||||
self.createStartBtn.clicked.connect(self.app.create_backup_action)
|
||||
self.cancelButton.clicked.connect(self.app.backup_cancelled_event.emit)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import logging
|
||||
|
||||
from PyQt6 import uic
|
||||
from PyQt6 import QtCore, uic
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtWidgets import (
|
||||
QApplication,
|
||||
|
@ -28,6 +28,8 @@
|
|||
|
||||
|
||||
class MiscTab(MiscTabBase, MiscTabUI, BackupProfileMixin):
|
||||
refresh_archive = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""Init."""
|
||||
super().__init__(parent)
|
||||
|
@ -101,6 +103,8 @@ def populate(self):
|
|||
cb.setCheckState(Qt.CheckState(setting.value))
|
||||
cb.setTristate(False)
|
||||
cb.stateChanged.connect(lambda v, key=setting.key: self.save_setting(key, v))
|
||||
if setting.key == 'enable_fixed_units':
|
||||
cb.stateChanged.connect(self.refresh_archive.emit)
|
||||
|
||||
tb = ToolTipButton()
|
||||
tb.setToolTip(setting.tooltip)
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
def test_autostart(qapp, qtbot):
|
||||
"""Check if file exists only on Linux, otherwise just check it doesn't crash"""
|
||||
|
||||
setting = "Automatically start Vorta at login"
|
||||
|
||||
_click_toggle_setting(setting, qapp, qtbot)
|
||||
|
@ -34,10 +33,41 @@ def test_autostart(qapp, qtbot):
|
|||
assert not os.path.exists(autostart_path)
|
||||
|
||||
|
||||
def test_enable_fixed_units(qapp, qtbot, mocker):
|
||||
"""Tests the 'enable fixed units' setting to ensure the archive tab sizes are displayed correctly."""
|
||||
tab = qapp.main_window.archiveTab
|
||||
setting = "Use the same unit of measurement for archive sizes"
|
||||
|
||||
# set mocks
|
||||
mock_setting = mocker.patch.object(vorta.views.archive_tab.SettingsModel, "get", return_value=Mock(value=True))
|
||||
mock_pretty_bytes = mocker.patch.object(vorta.views.archive_tab, "pretty_bytes")
|
||||
|
||||
# with setting enabled, fixed units should be determined and passed to pretty_bytes as an 'int'
|
||||
tab.populate_from_profile()
|
||||
mock_pretty_bytes.assert_called()
|
||||
kwargs_list = mock_pretty_bytes.call_args_list[0].kwargs
|
||||
assert 'fixed_unit' in kwargs_list
|
||||
assert isinstance(kwargs_list['fixed_unit'], int)
|
||||
|
||||
# disable setting and reset mock
|
||||
mock_setting.return_value = Mock(value=False)
|
||||
mock_pretty_bytes.reset_mock()
|
||||
|
||||
# with setting disabled, pretty_bytes should be called with fixed units set to 'None'
|
||||
tab.populate_from_profile()
|
||||
mock_pretty_bytes.assert_called()
|
||||
kwargs_list = mock_pretty_bytes.call_args_list[0].kwargs
|
||||
assert 'fixed_unit' in kwargs_list
|
||||
assert kwargs_list['fixed_unit'] is None
|
||||
|
||||
# use the qt bot to click the setting and see that the refresh_archive emit works as intended.
|
||||
with qtbot.waitSignal(qapp.main_window.miscTab.refresh_archive, timeout=5000):
|
||||
_click_toggle_setting(setting, qapp, qtbot)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform != 'darwin', reason="Full Disk Access check only on Darwin")
|
||||
def test_check_full_disk_access(qapp, qtbot, mocker):
|
||||
"""Enables/disables 'Check for Full Disk Access on startup' setting and ensures functionality"""
|
||||
|
||||
setting = "Check for Full Disk Access on startup"
|
||||
|
||||
# Set mocks for setting enabled
|
||||
|
@ -64,23 +94,16 @@ def test_check_full_disk_access(qapp, qtbot, mocker):
|
|||
|
||||
|
||||
def _click_toggle_setting(setting, qapp, qtbot):
|
||||
"""Click toggle setting in the misc tab"""
|
||||
"""Toggle setting checkbox in the misc tab"""
|
||||
miscTab = qapp.main_window.miscTab
|
||||
|
||||
main = qapp.main_window
|
||||
main.tabWidget.setCurrentIndex(4)
|
||||
tab = main.miscTab
|
||||
|
||||
for x in range(0, tab.checkboxLayout.count()):
|
||||
item = tab.checkboxLayout.itemAt(x, QFormLayout.ItemRole.FieldRole)
|
||||
if not item:
|
||||
continue
|
||||
checkbox = item.itemAt(0).widget()
|
||||
checkbox.__class__ = QCheckBox
|
||||
|
||||
if checkbox.text() == setting:
|
||||
# Have to use pos to click checkbox correctly
|
||||
# https://stackoverflow.com/questions/19418125/pysides-qtest-not-checking-box/24070484#24070484
|
||||
qtbot.mouseClick(
|
||||
checkbox, QtCore.Qt.MouseButton.LeftButton, pos=QtCore.QPoint(2, int(checkbox.height() / 2))
|
||||
)
|
||||
break
|
||||
for x in range(miscTab.checkboxLayout.count()):
|
||||
item = miscTab.checkboxLayout.itemAt(x, QFormLayout.ItemRole.FieldRole)
|
||||
if item is not None:
|
||||
checkbox = item.itemAt(0).widget()
|
||||
if checkbox.text() == setting and isinstance(checkbox, QCheckBox):
|
||||
# Have to use pos to click checkbox correctly
|
||||
# https://stackoverflow.com/questions/19418125/pysides-qtest-not-checking-box/24070484#24070484
|
||||
pos = QtCore.QPoint(2, int(checkbox.height() / 2))
|
||||
qtbot.mouseClick(checkbox, QtCore.Qt.MouseButton.LeftButton, pos=pos)
|
||||
break
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import uuid
|
||||
|
||||
import pytest
|
||||
from vorta.keyring.abc import VortaKeyring
|
||||
from vorta.utils import find_best_unit_for_sizes, pretty_bytes
|
||||
from vorta.utils import (
|
||||
find_best_unit_for_sizes,
|
||||
pretty_bytes,
|
||||
)
|
||||
|
||||
|
||||
def test_keyring():
|
||||
|
@ -13,70 +17,60 @@ def test_keyring():
|
|||
assert keyring.get_password("vorta-repo", REPO) == UNICODE_PW
|
||||
|
||||
|
||||
def test_best_size_unit_precision0():
|
||||
@pytest.mark.parametrize(
|
||||
"precision, expected_unit",
|
||||
[
|
||||
(0, 1), # return units as "1" (represents KB), min=100KB
|
||||
(1, 2), # return units as "2" (represents MB), min=0.1MB
|
||||
(2, 2), # still returns KB, since 0.1MB < min=0.001 GB to allow for GB to be best_unit
|
||||
],
|
||||
)
|
||||
def test_best_unit_for_sizes_precision(precision, expected_unit):
|
||||
MB = 1000000
|
||||
sizes = [int(0.1 * MB), 100 * MB, 2000 * MB]
|
||||
unit = find_best_unit_for_sizes(sizes, metric=True, precision=0)
|
||||
assert unit == 1 # KB, min=100KB
|
||||
best_unit = find_best_unit_for_sizes(sizes, metric=True, precision=precision)
|
||||
assert best_unit == expected_unit
|
||||
|
||||
|
||||
def test_best_size_unit_precision1():
|
||||
MB = 1000000
|
||||
sizes = [int(0.1 * MB), 100 * MB, 2000 * MB]
|
||||
unit = find_best_unit_for_sizes(sizes, metric=True, precision=1)
|
||||
assert unit == 2 # MB, min=0.1MB
|
||||
@pytest.mark.parametrize(
|
||||
"sizes, expected_unit",
|
||||
[
|
||||
([], 0), # no sizes given but should still return "0" (represents bytes) as best representation
|
||||
([102], 0), # non-metric size 102 < 0.1KB (102 < 0.1 * 1024), so it will return 0 instead of 1
|
||||
([103], 1), # non-metric size 103 > 0.1KB (103 < 0.1 * 1024), so it will return 1
|
||||
],
|
||||
)
|
||||
def test_best_unit_for_sizes_nonmetric(sizes, expected_unit):
|
||||
best_unit = find_best_unit_for_sizes(sizes, metric=False, precision=1)
|
||||
assert best_unit == expected_unit
|
||||
|
||||
|
||||
def test_best_size_unit_empty():
|
||||
sizes = []
|
||||
unit = find_best_unit_for_sizes(sizes, metric=True, precision=1)
|
||||
assert unit == 0 # bytes
|
||||
@pytest.mark.parametrize(
|
||||
"size, metric, precision, fixed_unit, expected_output",
|
||||
[
|
||||
(10**5, True, 1, 2, "0.1 MB"), # 100KB, metric, precision 1, fixed unit "2" (MB)
|
||||
(10**6, True, 0, 2, "1 MB"), # 1MB, metric, precision 0, fixed unit "2" (MB)
|
||||
(10**6, True, 1, 2, "1.0 MB"), # 1MB, metric, precision 1, fixed unit "2" (MB)
|
||||
(1024 * 1024, False, 1, 2, "1.0 MiB"), # 1MiB, nonmetric, precision 1, fixed unit "2" (MiB)
|
||||
],
|
||||
)
|
||||
def test_pretty_bytes_fixed_units(size, metric, precision, fixed_unit, expected_output):
|
||||
# test pretty bytes when specifying a fixed unit of measurement
|
||||
output = pretty_bytes(size, metric=metric, precision=precision, fixed_unit=fixed_unit)
|
||||
assert output == expected_output
|
||||
|
||||
|
||||
def test_best_size_unit_precision3():
|
||||
MB = 1000000
|
||||
sizes = [1 * MB, 100 * MB, 2000 * MB]
|
||||
unit = find_best_unit_for_sizes(sizes, metric=True, precision=3)
|
||||
assert unit == 3 # GB, min=0.001 GB
|
||||
|
||||
|
||||
def test_best_size_unit_nonmetric1():
|
||||
sizes = [102]
|
||||
unit = find_best_unit_for_sizes(sizes, metric=False, precision=1)
|
||||
assert unit == 0 # 102 < 0.1KB
|
||||
|
||||
|
||||
def test_best_size_unit_nonmetric2():
|
||||
sizes = [103]
|
||||
unit = find_best_unit_for_sizes(sizes, metric=False, precision=1)
|
||||
assert unit == 1 # 103bytes == 0.1KB
|
||||
|
||||
|
||||
def test_pretty_bytes_metric_fixed1():
|
||||
s = pretty_bytes(1000000, metric=True, precision=0, fixed_unit=2)
|
||||
assert s == "1 MB"
|
||||
|
||||
|
||||
def test_pretty_bytes_metric_fixed2():
|
||||
s = pretty_bytes(1000000, metric=True, precision=1, fixed_unit=2)
|
||||
assert s == "1.0 MB"
|
||||
|
||||
|
||||
def test_pretty_bytes_metric_fixed3():
|
||||
s = pretty_bytes(100000, metric=True, precision=1, fixed_unit=2)
|
||||
assert s == "0.1 MB"
|
||||
|
||||
|
||||
def test_pretty_bytes_nonmetric_fixed1():
|
||||
s = pretty_bytes(1024 * 1024, metric=False, precision=1, fixed_unit=2)
|
||||
assert s == "1.0 MiB"
|
||||
|
||||
|
||||
def test_pretty_bytes_metric_nonfixed2():
|
||||
s = pretty_bytes(1000000, metric=True, precision=1)
|
||||
assert s == "1.0 MB"
|
||||
|
||||
|
||||
def test_pretty_bytes_metric_large():
|
||||
s = pretty_bytes(10**30, metric=True, precision=1)
|
||||
assert s == "1000000.0 YB"
|
||||
@pytest.mark.parametrize(
|
||||
"size, metric, expected_output",
|
||||
[
|
||||
(10**6, True, "1.0 MB"), # 1MB, metric
|
||||
(10**24, True, "1.0 YB"), # 1YB, metric
|
||||
(10**30, True, "1000000.0 YB"), # test huge number, metric
|
||||
(1024 * 1024, False, "1.0 MiB"), # 1MiB, nonmetric
|
||||
(2**40 * 2**40, False, "1.0 YiB"), # 1YiB, nonmetric
|
||||
],
|
||||
)
|
||||
def test_pretty_bytes_nonfixed_units(size, metric, expected_output):
|
||||
# test pretty bytes when NOT specifying a fixed unit of measurement
|
||||
output = pretty_bytes(size, metric=metric, precision=1)
|
||||
assert output == expected_output
|
||||
|
|
Loading…
Reference in a new issue