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

Setting for number format in archive tab #1719

Merged
merged 17 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
11 changes: 7 additions & 4 deletions src/vorta/borg/borg_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from vorta.keyring.abc import VortaKeyring
from vorta.keyring.db import VortaDBKeyring
from vorta.store.models import BackupProfileMixin, EventLogModel
from vorta.utils import borg_compat, pretty_bytes
from vorta.utils import borg_compat, pretty_bytes_fixed_units

keyring_lock = Lock()
db_lock = Lock()
Expand Down Expand Up @@ -292,9 +292,12 @@ def read_async(fd):
elif parsed['type'] == 'archive_progress' and not parsed.get('finished', False):
msg = (
f"{translate('BorgJob','Files')}: {parsed['nfiles']}, "
f"{translate('BorgJob','Original')}: {pretty_bytes(parsed['original_size'])}, "
# f"{translate('BorgJob','Compressed')}: {pretty_bytes(parsed['compressed_size'])}, "
f"{translate('BorgJob','Deduplicated')}: {pretty_bytes(parsed['deduplicated_size'])}" # noqa: E501
f"{translate('BorgJob','Original')}: "
f"{pretty_bytes_fixed_units(parsed['original_size'])}, "
# f"{translate('BorgJob','Compressed')}:
# f"{pretty_bytes_fixed_units(parsed['compressed_size'])}, "
f"{translate('BorgJob','Deduplicated')}: "
f"{pretty_bytes_fixed_units(parsed['deduplicated_size'])}" # noqa: E501
)
self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {msg}")
except json.decoder.JSONDecodeError:
Expand Down
8 changes: 8 additions & 0 deletions src/vorta/store/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ 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', 'Display all archive sizes in a consistent unit of measurement'),
'tooltip': trans_late('settings', 'Enable to replace dynamic units of measurement based on archive size'),
},
{
'key': 'use_system_keyring',
'value': True,
Expand Down
33 changes: 21 additions & 12 deletions src/vorta/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -249,7 +247,7 @@ def clamp(n: Number, min_: Number, max_: Number) -> Number:

def find_best_unit_for_sizes(sizes: Iterable[int], metric: bool = True, precision: int = 1) -> int:
"""
Selects the index of the biggest unit (see the lists in the pretty_bytes function) capable of
Selects the index of the biggest unit (see the lists in the pretty_bytes_fixed_units function) capable of
representing the smallest size in the sizes iterable.
"""
min_size = min((s for s in sizes if isinstance(s, int)), default=None)
Expand All @@ -258,7 +256,7 @@ def find_best_unit_for_sizes(sizes: Iterable[int], metric: bool = True, precisio

def find_best_unit_for_size(size: Optional[int], metric: bool = True, precision: int = 1) -> int:
"""
Selects the index of the biggest unit (see the lists in the pretty_bytes function) capable of
Selects the index of the biggest unit (see the lists in the pretty_bytes_fixed_units function) capable of
representing the passed size.
"""
if not isinstance(size, int) or size == 0: # this will also take care of the None case
Expand All @@ -268,7 +266,22 @@ def find_best_unit_for_size(size: Optional[int], metric: bool = True, precision:
return n


def pretty_bytes(
def pretty_bytes_dynamic_units(size, metric=True, sign=False, precision=1):
if not isinstance(size, int):
return ''
prefix = '+' if sign and size > 0 else ''
power, units = (10**3, METRIC_UNITS) if metric else (2**10, NONMETRIC_UNITS)
n = find_best_unit_for_size(size, metric=metric, precision=precision)
size /= power**n
try:
unit = units[n]
return f'{prefix}{round(size, precision)} {unit}B'
except KeyError as e:
logger.error(e)
return "NaN"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you changed this method to use find_best_unit_for_size it does exactly the same as pretty_bytest_fixed_units when passing fixed_unit=None. Consequently we only need the latter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just combined these two into one pretty_bytes() function, and rewrote the tests to ensure functionality.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, went through the files affected by previous (and now reverted) pretty_bytes name change and set them back to their original state.


def pretty_bytes_fixed_units(
size: int, metric: bool = True, sign: bool = False, precision: int = 1, fixed_unit: Optional[int] = None
) -> str:
"""
Expand All @@ -279,11 +292,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:
Expand Down
17 changes: 12 additions & 5 deletions src/vorta/views/archive_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@
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,
find_best_unit_for_sizes,
format_archive_name,
get_asset,
get_mount_points,
pretty_bytes,
pretty_bytes_dynamic_units,
pretty_bytes_fixed_units,
)
from vorta.views import diff_result, extract_dialog
from vorta.views.diff_result import DiffResultDialog, DiffTree
Expand Down Expand Up @@ -291,9 +292,15 @@ 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
if SettingsModel.get(key='enable_fixed_units').value is True:
size = pretty_bytes_fixed_units(archive.size, fixed_unit=best_unit, precision=SIZE_DECIMAL_DIGITS)
else:
size = pretty_bytes_dynamic_units(archive.size, 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:
Expand Down
10 changes: 5 additions & 5 deletions src/vorta/views/diff_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from PyQt6.QtWidgets import QApplication, QHeaderView, QMenu, QTreeView

from vorta.store.models import SettingsModel
from vorta.utils import get_asset, pretty_bytes, uses_dark_mode
from vorta.utils import get_asset, pretty_bytes_fixed_units, uses_dark_mode
from vorta.views.partials.treemodel import (
FileSystemItem,
FileTreeModel,
Expand Down Expand Up @@ -827,10 +827,10 @@ def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole):
# change type
return item.data.change_type.short()
elif column == 2:
return pretty_bytes(item.data.changed_size)
return pretty_bytes_fixed_units(item.data.changed_size)
else:
# size
return pretty_bytes(item.data.size)
return pretty_bytes_fixed_units(item.data.size)

if role == Qt.ItemDataRole.ForegroundRole:
# colour
Expand Down Expand Up @@ -884,8 +884,8 @@ def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole):
if item.data.modified:
tooltip += '\n'
tooltip += modified_template.format(
pretty_bytes(item.data.modified[0]),
pretty_bytes(item.data.modified[1]),
pretty_bytes_fixed_units(item.data.modified[0]),
pretty_bytes_fixed_units(item.data.modified[1]),
)

if item.data.mode_change:
Expand Down
4 changes: 2 additions & 2 deletions src/vorta/views/extract_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
)

from vorta.store.models import SettingsModel
from vorta.utils import borg_compat, get_asset, pretty_bytes, uses_dark_mode
from vorta.utils import borg_compat, get_asset, pretty_bytes_fixed_units, uses_dark_mode
from vorta.views.utils import get_colored_icon

from .partials.treemodel import (
Expand Down Expand Up @@ -483,7 +483,7 @@ def data(self, index: QModelIndex, role: Union[int, Qt.ItemDataRole] = Qt.ItemDa
return QLocale.system().toString(item.data.last_modified, QLocale.FormatType.ShortFormat)
elif column == 2:
# size
return pretty_bytes(item.data.size)
return pretty_bytes_fixed_units(item.data.size)
else:
# health
return
Expand Down
1 change: 1 addition & 0 deletions src/vorta/views/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion src/vorta/views/misc_tab.py
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -28,6 +28,8 @@


class MiscTab(MiscTabBase, MiscTabUI, BackupProfileMixin):
refresh_archive = QtCore.pyqtSignal()

def __init__(self, parent=None):
"""Init."""
super().__init__(parent)
Expand Down Expand Up @@ -101,6 +103,7 @@ 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))
cb.stateChanged.connect(lambda v, key=setting.key: self.emit_archive_refresh(key))

tb = ToolTipButton()
tb.setToolTip(setting.tooltip)
Expand All @@ -125,6 +128,10 @@ def set_icons(self):
for button in self.tooltip_buttons:
button.setIcon(get_colored_icon('help-about'))

def emit_archive_refresh(self, key):
if key == 'enable_fixed_units':
self.refresh_archive.emit()

def save_setting(self, key, new_value):
setting = SettingsModel.get(key=key)
setting.value = bool(new_value)
Expand Down
13 changes: 9 additions & 4 deletions src/vorta/views/repo_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
from PyQt6.QtWidgets import QApplication, QLayout, QMenu, QMessageBox

from vorta.store.models import ArchiveModel, BackupProfileMixin, RepoModel
from vorta.utils import borg_compat, get_asset, get_private_keys, pretty_bytes
from vorta.utils import (
borg_compat,
get_asset,
get_private_keys,
pretty_bytes_fixed_units,
)

from .repo_add_dialog import AddRepoWindow, ExistingRepoWindow
from .ssh_dialog import SSHAddWindow
Expand Down Expand Up @@ -131,21 +136,21 @@ def init_repo_stats(self):

# update stats
if repo.unique_csize is not None:
self.sizeCompressed.setText(pretty_bytes(repo.unique_csize))
self.sizeCompressed.setText(pretty_bytes_fixed_units(repo.unique_csize))
self.sizeCompressed.setToolTip('')
else:
self.sizeCompressed.setText(na)
self.sizeCompressed.setToolTip(refresh)

if repo.unique_size is not None:
self.sizeDeduplicated.setText(pretty_bytes(repo.unique_size))
self.sizeDeduplicated.setText(pretty_bytes_fixed_units(repo.unique_size))
self.sizeDeduplicated.setToolTip('')
else:
self.sizeDeduplicated.setText(na)
self.sizeDeduplicated.setToolTip(refresh)

if repo.total_size is not None:
self.sizeOriginal.setText(pretty_bytes(repo.total_size))
self.sizeOriginal.setText(pretty_bytes_fixed_units(repo.total_size))
self.sizeOriginal.setToolTip('')
else:
self.sizeOriginal.setText(na)
Expand Down
8 changes: 5 additions & 3 deletions src/vorta/views/source_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
FilePathInfoAsync,
choose_file_dialog,
get_asset,
pretty_bytes,
pretty_bytes_fixed_units,
sort_sizes,
)
from vorta.views.utils import get_colored_icon
Expand Down Expand Up @@ -171,7 +171,7 @@ def set_path_info(self, path, data_size, files_count):
db_item.path_isdir = False
self.sourceFilesWidget.item(item.row(), SourceColumn.Path).setIcon(get_colored_icon('file'))

self.sourceFilesWidget.item(item.row(), SourceColumn.Size).setText(pretty_bytes(data_size))
self.sourceFilesWidget.item(item.row(), SourceColumn.Size).setText(pretty_bytes_fixed_units(data_size))

db_item.dir_size = data_size
db_item.dir_files_count = files_count
Expand Down Expand Up @@ -236,7 +236,9 @@ def add_source_to_table(self, source, update_data=None):

else: # Use cached data from DB
if source.dir_size > -1:
self.sourceFilesWidget.item(index_row, SourceColumn.Size).setText(pretty_bytes(source.dir_size))
self.sourceFilesWidget.item(index_row, SourceColumn.Size).setText(
pretty_bytes_fixed_units(source.dir_size)
)

if source.path_isdir:
self.sourceFilesWidget.item(index_row, SourceColumn.FilesCount).setText(
Expand Down
Loading