From 404c346a78eb04be386b401c7294b66d912e3b1d Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Fri, 17 Feb 2023 05:08:13 +0100 Subject: [PATCH 01/13] automatic archive unit detection --- src/vorta/utils.py | 29 ++++++++++++++++++++++++----- src/vorta/views/archive_tab.py | 14 ++++++++++++-- src/vorta/views/source_tab.py | 4 ++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 7cf105776..24feca312 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -240,7 +240,22 @@ def sort_sizes(size_list): return final_list -def pretty_bytes(size, metric=True, sign=False, precision=1): +def find_best_size_formatting(size_list, metric=True): + 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']) + ) + min_size = min(size_list) + # we tolerate a maximum precision of 1 + n = 0 + while abs(min_size / power**n > 0.1) and n + 1 < len(units): + n += 1 + n = max(n - 1, 0) + return 1, units[n] + + +def pretty_bytes(size, metric=True, sign=False, precision=1, fixed_unit=None): if not isinstance(size, int): return '' prefix = '+' if sign and size > 0 else '' @@ -249,10 +264,14 @@ def pretty_bytes(size, metric=True, sign=False, precision=1): if metric else (2**10, ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']) ) - n = 0 - while abs(round(size, precision)) >= power and n + 1 < len(units): - size /= power - n += 1 + if fixed_unit is None: + n = 0 + while abs(round(size, precision)) >= power and n + 1 < len(units): + size /= power + n += 1 + else: + n = units.index(fixed_unit) + size /= power**n try: unit = units[n] return f'{prefix}{round(size, precision)} {unit}B' diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index 622fed5e1..ec9b03d6f 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -32,7 +32,14 @@ from vorta.borg.umount import BorgUmountJob from vorta.i18n import translate from vorta.store.models import ArchiveModel, BackupProfileMixin -from vorta.utils import choose_file_dialog, format_archive_name, get_asset, get_mount_points, pretty_bytes +from vorta.utils import ( + choose_file_dialog, + format_archive_name, + get_asset, + get_mount_points, + pretty_bytes, + find_best_size_formatting, +) from vorta.views import diff_result, extract_dialog from vorta.views.diff_result import DiffResultDialog, DiffTree from vorta.views.extract_dialog import ExtractDialog, ExtractTree @@ -243,12 +250,15 @@ def populate_from_profile(self): sorting = self.archiveTable.isSortingEnabled() self.archiveTable.setSortingEnabled(False) + best_precision, best_unit = find_best_size_formatting(a.size for a in archives) for row, archive in enumerate(archives): self.archiveTable.insertRow(row) 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))) + self.archiveTable.setItem( + row, 1, SizeItem(pretty_bytes(archive.size, fixed_unit=best_unit, precision=best_precision)) + ) if archive.duration is not None: formatted_duration = str(timedelta(seconds=round(archive.duration))) else: diff --git a/src/vorta/views/source_tab.py b/src/vorta/views/source_tab.py index abac41eaa..7d0731708 100644 --- a/src/vorta/views/source_tab.py +++ b/src/vorta/views/source_tab.py @@ -21,6 +21,10 @@ class SourceColumn: class SizeItem(QTableWidgetItem): + def __init__(self, s): + super().__init__(s) + self.setTextAlignment(int('0x82', 16)) + def __lt__(self, other): if other.text() == '': return False From a2becc2b85ba34ad32f0de88388cb187448357ca Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Fri, 17 Feb 2023 13:48:27 +0100 Subject: [PATCH 02/13] check size type in find_best_size_formatting --- src/vorta/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 24feca312..cba6c3b0f 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -246,7 +246,7 @@ def find_best_size_formatting(size_list, metric=True): if metric else (2**10, ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']) ) - min_size = min(size_list) + min_size = min(s for s in size_list if isinstance(s, int)) # we tolerate a maximum precision of 1 n = 0 while abs(min_size / power**n > 0.1) and n + 1 < len(units): From 587e572b894a282c572876edea5ce35850887011 Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Fri, 17 Feb 2023 15:48:14 +0100 Subject: [PATCH 03/13] fix import order --- src/vorta/views/archive_tab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index ec9b03d6f..82e5266f4 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -34,11 +34,11 @@ from vorta.store.models import ArchiveModel, BackupProfileMixin from vorta.utils import ( choose_file_dialog, + find_best_size_formatting, format_archive_name, get_asset, get_mount_points, pretty_bytes, - find_best_size_formatting, ) from vorta.views import diff_result, extract_dialog from vorta.views.diff_result import DiffResultDialog, DiffTree From 958d721c2ba02d18a480268e083bbd957ec3cf68 Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Sat, 18 Feb 2023 14:24:31 +0100 Subject: [PATCH 04/13] fix/rename/test find_best_size; use Qt.Alignment; types & docstrings --- src/vorta/utils.py | 34 +++++++++++++++------------ src/vorta/views/archive_tab.py | 7 +++--- src/vorta/views/source_tab.py | 2 +- tests/test_utils.py | 43 ++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index cba6c3b0f..dd1f1fa34 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -2,6 +2,7 @@ import errno import fnmatch import getpass +import math import os import platform import re @@ -240,22 +241,25 @@ def sort_sizes(size_list): return final_list -def find_best_size_formatting(size_list, metric=True): - 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']) - ) - min_size = min(s for s in size_list if isinstance(s, int)) - # we tolerate a maximum precision of 1 - n = 0 - while abs(min_size / power**n > 0.1) and n + 1 < len(units): - n += 1 - n = max(n - 1, 0) - return 1, units[n] +def find_best_size_unit(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 + representing the smallest size in the sizes iterable. + """ + power = 10**3 if metric else 2**10 + min_size = min((s for s in sizes if isinstance(s, int)), default=None) + if min_size is None: + return 0 + n = math.floor(math.log(min_size * 10**precision, power)) + return n -def pretty_bytes(size, metric=True, sign=False, precision=1, fixed_unit=None): +def pretty_bytes(size: int, metric: bool = True, sign: bool = False, precision: int = 1, fixed_unit: int = None) -> str: + """ + Formats the size with the requested unit and precision. The find_best_size_unit function + can be used to find the correct unit for a list of sizes. If no fixed_unit is passed it will + find the biggest unit to represent the size + """ if not isinstance(size, int): return '' prefix = '+' if sign and size > 0 else '' @@ -270,7 +274,7 @@ def pretty_bytes(size, metric=True, sign=False, precision=1, fixed_unit=None): size /= power n += 1 else: - n = units.index(fixed_unit) + n = fixed_unit size /= power**n try: unit = units[n] diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index 82e5266f4..5691b1c9a 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -34,7 +34,7 @@ from vorta.store.models import ArchiveModel, BackupProfileMixin from vorta.utils import ( choose_file_dialog, - find_best_size_formatting, + find_best_size_unit, format_archive_name, get_asset, get_mount_points, @@ -250,14 +250,15 @@ def populate_from_profile(self): sorting = self.archiveTable.isSortingEnabled() self.archiveTable.setSortingEnabled(False) - best_precision, best_unit = find_best_size_formatting(a.size for a in archives) + precision=1 #could be a setting + best_unit = find_best_size_unit((a.size for a in archives), precision=precision) for row, archive in enumerate(archives): self.archiveTable.insertRow(row) 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=best_precision)) + row, 1, SizeItem(pretty_bytes(archive.size, fixed_unit=best_unit, precision=precision)) ) if archive.duration is not None: formatted_duration = str(timedelta(seconds=round(archive.duration))) diff --git a/src/vorta/views/source_tab.py b/src/vorta/views/source_tab.py index 7d0731708..11db6075c 100644 --- a/src/vorta/views/source_tab.py +++ b/src/vorta/views/source_tab.py @@ -23,7 +23,7 @@ class SourceColumn: class SizeItem(QTableWidgetItem): def __init__(self, s): super().__init__(s) - self.setTextAlignment(int('0x82', 16)) + self.setTextAlignment(Qt.AlignVCenter + Qt.AlignRight) def __lt__(self, other): if other.text() == '': diff --git a/tests/test_utils.py b/tests/test_utils.py index a04084c65..ae2b6640b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,6 @@ import uuid from vorta.keyring.abc import VortaKeyring +from vorta.utils import find_best_size_unit def test_keyring(): @@ -9,3 +10,45 @@ def test_keyring(): keyring = VortaKeyring.get_keyring() keyring.set_password('vorta-repo', REPO, UNICODE_PW) assert keyring.get_password("vorta-repo", REPO) == UNICODE_PW + + +def test_best_size_unit_precision0(): + MB = 1000000 + sizes = [int(0.1 * MB), 100 * MB, 2000 * MB] + unit = find_best_size_unit(sizes, metric=True, precision=0) + assert unit == 1 # KB, min=100KB + + +def test_best_size_unit_precision1(): + MB = 1000000 + sizes = [int(0.1 * MB), 100 * MB, 2000 * MB] + unit = find_best_size_unit(sizes, metric=True, precision=1) + assert unit == 2 # MB, min=0.1MB + + +def test_best_size_unit_empty(): + MB = 1000000 + sizes = [] + unit = find_best_size_unit(sizes, metric=True, precision=1) + assert unit == 0 # bytes + + +def test_best_size_unit_precision3(): + MB = 1000000 + sizes = [1 * MB, 100 * MB, 2000 * MB] + unit = find_best_size_unit(sizes, metric=True, precision=3) + assert unit == 3 # GB, min=0.001 GB + + +def test_best_size_unit_nonmetric1(): + KB = 1024 + sizes = [102] + unit = find_best_size_unit(sizes, metric=False, precision=1) + assert unit == 0 # 102 < 0.1KB + + +def test_best_size_unit_nonmetric2(): + KB = 1024 + sizes = [103] + unit = find_best_size_unit(sizes, metric=False, precision=1) + assert unit == 1 # 103bytes == 0.1KB From c8ff5a4bb3171682f7eded363f4e8e8ccb2cacd9 Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Sat, 18 Feb 2023 14:48:25 +0100 Subject: [PATCH 05/13] fix formatting --- src/vorta/views/archive_tab.py | 2 +- tests/test_utils.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index 5691b1c9a..21e6bd855 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -250,7 +250,7 @@ def populate_from_profile(self): sorting = self.archiveTable.isSortingEnabled() self.archiveTable.setSortingEnabled(False) - precision=1 #could be a setting + precision = 1 # could be a setting best_unit = find_best_size_unit((a.size for a in archives), precision=precision) for row, archive in enumerate(archives): self.archiveTable.insertRow(row) diff --git a/tests/test_utils.py b/tests/test_utils.py index ae2b6640b..cf2ebbdcf 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -27,7 +27,6 @@ def test_best_size_unit_precision1(): def test_best_size_unit_empty(): - MB = 1000000 sizes = [] unit = find_best_size_unit(sizes, metric=True, precision=1) assert unit == 0 # bytes @@ -41,14 +40,12 @@ def test_best_size_unit_precision3(): def test_best_size_unit_nonmetric1(): - KB = 1024 sizes = [102] unit = find_best_size_unit(sizes, metric=False, precision=1) assert unit == 0 # 102 < 0.1KB def test_best_size_unit_nonmetric2(): - KB = 1024 sizes = [103] unit = find_best_size_unit(sizes, metric=False, precision=1) assert unit == 1 # 103bytes == 0.1KB From 3988e7980cc04773e653f6398a1baffd8a5692ba Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Sat, 18 Feb 2023 18:50:52 +0100 Subject: [PATCH 06/13] allow precision=0; rename/refactor --- src/vorta/utils.py | 41 +++++++++++++++++++++++----------- src/vorta/views/archive_tab.py | 10 +++++---- tests/test_utils.py | 39 ++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index dd1f1fa34..654b6bfd0 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -10,7 +10,7 @@ import unicodedata from datetime import datetime as dt from functools import reduce -from typing import Any, Callable, Iterable, Tuple +from typing import Any, Callable, Iterable, Tuple, TypeVar import psutil from paramiko import SSHException from paramiko.ecdsakey import ECDSAKey @@ -241,16 +241,32 @@ def sort_sizes(size_list): return final_list -def find_best_size_unit(sizes: Iterable[int], metric: bool = True, precision: int = 1) -> int: +T = TypeVar("T") + + +def clamp(n: T, min_: T, max_: T) -> T: + """Restrict the number n inside a range""" + return min(max_, max(n, min_)) + + +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 representing the smallest size in the sizes iterable. """ - power = 10**3 if metric else 2**10 min_size = min((s for s in sizes if isinstance(s, int)), default=None) - if min_size is None: + return find_best_unit_for_size(min_size, metric=metric, precision=precision) + + +def find_best_unit_for_size(size: 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 + representing the passed size. + """ + if not isinstance(size, int) or size == 0: # this will also take care of the None case return 0 - n = math.floor(math.log(min_size * 10**precision, power)) + power = 10**3 if metric else 2**10 + n = math.floor(math.log(size * 10**precision, power)) return n @@ -269,18 +285,17 @@ def pretty_bytes(size: int, metric: bool = True, sign: bool = False, precision: else (2**10, ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']) ) if fixed_unit is None: - n = 0 - while abs(round(size, precision)) >= power and n + 1 < len(units): - size /= power - n += 1 + n = find_best_unit_for_size(size, metric=metric, precision=precision) else: n = fixed_unit - size /= power**n + n = clamp(n, 0, len(units)) + size /= power**n try: unit = units[n] - return f'{prefix}{round(size, precision)} {unit}B' - except KeyError as e: - logger.error(e) + digits = f'%.{precision}f' % (round(size, precision)) + return f'{prefix}{digits} {unit}B' + except KeyError as error: + logger.error(error) return "NaN" diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index 21e6bd855..f512b5599 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -34,7 +34,7 @@ from vorta.store.models import ArchiveModel, BackupProfileMixin from vorta.utils import ( choose_file_dialog, - find_best_size_unit, + find_best_unit_for_sizes, format_archive_name, get_asset, get_mount_points, @@ -51,6 +51,8 @@ logger = logging.getLogger(__name__) +PRECISION = 1 + class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): prune_intervals = ['hour', 'day', 'week', 'month', 'year'] @@ -238,6 +240,7 @@ def _toggle_all_buttons(self, enabled=True): def populate_from_profile(self): """Populate archive list and prune settings from profile.""" + global PRECISION profile = self.profile() if profile.repo is not None: # get mount points @@ -250,15 +253,14 @@ def populate_from_profile(self): sorting = self.archiveTable.isSortingEnabled() self.archiveTable.setSortingEnabled(False) - precision = 1 # could be a setting - best_unit = find_best_size_unit((a.size for a in archives), precision=precision) + best_unit = find_best_unit_for_sizes((a.size for a in archives), precision=PRECISION) for row, archive in enumerate(archives): self.archiveTable.insertRow(row) 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=precision)) + row, 1, SizeItem(pretty_bytes(archive.size, fixed_unit=best_unit, precision=PRECISION)) ) if archive.duration is not None: formatted_duration = str(timedelta(seconds=round(archive.duration))) diff --git a/tests/test_utils.py b/tests/test_utils.py index cf2ebbdcf..9b4c723d8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ import uuid from vorta.keyring.abc import VortaKeyring -from vorta.utils import find_best_size_unit +from vorta.utils import find_best_unit_for_sizes, pretty_bytes def test_keyring(): @@ -15,37 +15,62 @@ def test_keyring(): def test_best_size_unit_precision0(): MB = 1000000 sizes = [int(0.1 * MB), 100 * MB, 2000 * MB] - unit = find_best_size_unit(sizes, metric=True, precision=0) + unit = find_best_unit_for_sizes(sizes, metric=True, precision=0) assert unit == 1 # KB, min=100KB def test_best_size_unit_precision1(): MB = 1000000 sizes = [int(0.1 * MB), 100 * MB, 2000 * MB] - unit = find_best_size_unit(sizes, metric=True, precision=1) + unit = find_best_unit_for_sizes(sizes, metric=True, precision=1) assert unit == 2 # MB, min=0.1MB def test_best_size_unit_empty(): sizes = [] - unit = find_best_size_unit(sizes, metric=True, precision=1) + unit = find_best_unit_for_sizes(sizes, metric=True, precision=1) assert unit == 0 # bytes def test_best_size_unit_precision3(): MB = 1000000 sizes = [1 * MB, 100 * MB, 2000 * MB] - unit = find_best_size_unit(sizes, metric=True, precision=3) + 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_size_unit(sizes, metric=False, precision=1) + 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_size_unit(sizes, metric=False, precision=1) + 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, fixed_unit=2) + assert s == "1.0 MB" From 4f5585562a2ae1a78a77f402db58ad343960c594 Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Sat, 18 Feb 2023 19:17:28 +0100 Subject: [PATCH 07/13] test & fix large sizes case --- src/vorta/utils.py | 4 ++-- src/vorta/views/archive_tab.py | 1 - tests/test_utils.py | 7 ++++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 654b6bfd0..dc8dc0485 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -227,7 +227,7 @@ def get_private_keys(): def sort_sizes(size_list): """Sorts sizes with extensions. Assumes that size is already in largest unit possible""" final_list = [] - for suffix in [" B", " KB", " MB", " GB", " TB"]: + for suffix in [" B", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"]: sub_list = [ float(size[: -len(suffix)]) for size in size_list @@ -288,7 +288,7 @@ def pretty_bytes(size: int, metric: bool = True, sign: bool = False, precision: n = find_best_unit_for_size(size, metric=metric, precision=precision) else: n = fixed_unit - n = clamp(n, 0, len(units)) + n = clamp(n, 0, len(units) - 1) size /= power**n try: unit = units[n] diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index f512b5599..1c0530921 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -240,7 +240,6 @@ def _toggle_all_buttons(self, enabled=True): def populate_from_profile(self): """Populate archive list and prune settings from profile.""" - global PRECISION profile = self.profile() if profile.repo is not None: # get mount points diff --git a/tests/test_utils.py b/tests/test_utils.py index 9b4c723d8..07585e3f9 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -72,5 +72,10 @@ def test_pretty_bytes_nonmetric_fixed1(): def test_pretty_bytes_metric_nonfixed2(): - s = pretty_bytes(1000000, metric=True, precision=1, fixed_unit=2) + 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" From 64aa23bcd84b441aea5ef0551f098b77fbf3ef3c Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Sun, 19 Feb 2023 01:04:03 +0100 Subject: [PATCH 08/13] better naming for precision constant --- src/vorta/views/archive_tab.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index 1c0530921..7c476ccff 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -51,7 +51,8 @@ logger = logging.getLogger(__name__) -PRECISION = 1 + +SIZE_DECIMAL_DIGITS = 1 class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): @@ -252,14 +253,14 @@ def populate_from_profile(self): sorting = self.archiveTable.isSortingEnabled() self.archiveTable.setSortingEnabled(False) - best_unit = find_best_unit_for_sizes((a.size for a in archives), precision=PRECISION) + best_unit = find_best_unit_for_sizes((a.size for a in archives), precision=SIZE_DECIMAL_DIGITS) for row, archive in enumerate(archives): self.archiveTable.insertRow(row) 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=PRECISION)) + row, 1, SizeItem(pretty_bytes(archive.size, fixed_unit=best_unit, precision=SIZE_DECIMAL_DIGITS)) ) if archive.duration is not None: formatted_duration = str(timedelta(seconds=round(archive.duration))) From 9d4afca2e8a625ef22e26e1b0812eca12f3516d5 Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Sun, 19 Feb 2023 01:08:14 +0100 Subject: [PATCH 09/13] Update src/vorta/utils.py Co-authored-by: yfprojects <62463991+real-yfprojects@users.noreply.github.com> --- src/vorta/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index dc8dc0485..bbe3ff4b4 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -270,7 +270,7 @@ def find_best_unit_for_size(size: int, metric: bool = True, precision: int = 1) return n -def pretty_bytes(size: int, metric: bool = True, sign: bool = False, precision: int = 1, fixed_unit: int = None) -> str: +def pretty_bytes(size: int, metric: bool = True, sign: bool = False, precision: int = 1, fixed_unit: Optional[int] = None) -> str: """ Formats the size with the requested unit and precision. The find_best_size_unit function can be used to find the correct unit for a list of sizes. If no fixed_unit is passed it will From b94c8d7511bdc484fc69883f9c08583cca9b42c2 Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Sun, 19 Feb 2023 01:13:43 +0100 Subject: [PATCH 10/13] fix typing import --- src/vorta/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index bbe3ff4b4..065da28d6 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -10,7 +10,7 @@ import unicodedata from datetime import datetime as dt from functools import reduce -from typing import Any, Callable, Iterable, Tuple, TypeVar +from typing import Any, Callable, Iterable, Optional, Tuple, TypeVar import psutil from paramiko import SSHException from paramiko.ecdsakey import ECDSAKey @@ -270,7 +270,9 @@ def find_best_unit_for_size(size: int, metric: bool = True, precision: int = 1) return n -def pretty_bytes(size: int, metric: bool = True, sign: bool = False, precision: int = 1, fixed_unit: Optional[int] = None) -> str: +def pretty_bytes( + size: int, metric: bool = True, sign: bool = False, precision: int = 1, fixed_unit: Optional[int] = None +) -> str: """ Formats the size with the requested unit and precision. The find_best_size_unit function can be used to find the correct unit for a list of sizes. If no fixed_unit is passed it will From 6cbdcaadc2b5224ff8936042d97b45c1dfef18bc Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Mon, 20 Feb 2023 19:00:16 +0100 Subject: [PATCH 11/13] docstring for constant --- src/vorta/views/archive_tab.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index 7c476ccff..db991f7bc 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -52,6 +52,7 @@ logger = logging.getLogger(__name__) +#: The number of decimal digits to show in the size column SIZE_DECIMAL_DIGITS = 1 From 641f1528adb5444777a80b90fa93ddfbe1074815 Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Tue, 21 Feb 2023 01:24:03 +0100 Subject: [PATCH 12/13] Update src/vorta/utils.py Co-authored-by: yfprojects <62463991+real-yfprojects@users.noreply.github.com> --- src/vorta/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 065da28d6..5b9a075f0 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -258,7 +258,7 @@ def find_best_unit_for_sizes(sizes: Iterable[int], metric: bool = True, precisio return find_best_unit_for_size(min_size, metric=metric, precision=precision) -def find_best_unit_for_size(size: int, metric: bool = True, precision: int = 1) -> int: +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 representing the passed size. From c5016b265a868af2fcc16a4467ef09c0b1be2b05 Mon Sep 17 00:00:00 2001 From: Roberto Previdi Date: Tue, 21 Feb 2023 01:44:59 +0100 Subject: [PATCH 13/13] improve typing --- src/vorta/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 5b9a075f0..6217fb518 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -241,10 +241,10 @@ def sort_sizes(size_list): return final_list -T = TypeVar("T") +Number = TypeVar("Number", int, float) -def clamp(n: T, min_: T, max_: T) -> T: +def clamp(n: Number, min_: Number, max_: Number) -> Number: """Restrict the number n inside a range""" return min(max_, max(n, min_))