Hello community, here is the log from the commit of package vorta for openSUSE:Factory checked in at 2020-05-28 09:13:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/vorta (Old) and /work/SRC/openSUSE:Factory/.vorta.new.3606 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "vorta" Thu May 28 09:13:42 2020 rev:8 rq:809083 version:0.6.26 Changes: -------- --- /work/SRC/openSUSE:Factory/vorta/vorta.changes 2020-05-19 14:47:13.991909952 +0200 +++ /work/SRC/openSUSE:Factory/.vorta.new.3606/vorta.changes 2020-05-28 09:14:07.948681217 +0200 @@ -1,0 +2,9 @@ +Tue May 26 07:09:27 UTC 2020 - Antonio Larrosa <[email protected]> + +- Update to version 0.6.26 + * Fixes some issues when no known Wifis are available on macOS. + * Starts with the correct window size for high-res displays on Linux. + * Test- and linting fixes. +- Version 0.6.25 wasn't released by upstream. + +------------------------------------------------------------------- Old: ---- v0.6.24.tar.gz New: ---- v0.6.26.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ vorta.spec ++++++ --- /var/tmp/diff_new_pack.Zgmgc0/_old 2020-05-28 09:14:10.408681885 +0200 +++ /var/tmp/diff_new_pack.Zgmgc0/_new 2020-05-28 09:14:10.412681887 +0200 @@ -17,7 +17,7 @@ Name: vorta -Version: 0.6.24 +Version: 0.6.26 Release: 0 Summary: Desktop Backup Client based on BorgBackup License: GPL-3.0-only ++++++ v0.6.24.tar.gz -> v0.6.26.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/.bumpversion.cfg new/vorta-0.6.26/.bumpversion.cfg --- old/vorta-0.6.24/.bumpversion.cfg 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/.bumpversion.cfg 2020-05-26 05:30:06.000000000 +0200 @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.6.24 +current_version = 0.6.26 tag = True [bumpversion:file:Makefile] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/Makefile new/vorta-0.6.26/Makefile --- old/vorta-0.6.24/Makefile 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/Makefile 2020-05-26 05:30:06.000000000 +0200 @@ -29,8 +29,8 @@ sh package/macos-package-app.sh github-release: dist/Vorta.dmg - cp dist/Vorta.dmg dist/dist/vorta-0.6.24.dmg - hub release create --attach=dist/vorta-0.6.24.dmg v0.6.24 + cp dist/Vorta.dmg dist/dist/vorta-0.6.26.dmg + hub release create --attach=dist/vorta-0.6.26.dmg v0.6.26 git checkout gh-pages git commit -m 'rebuild pages' --allow-empty git push upstream gh-pages @@ -38,7 +38,7 @@ pypi-release: translations-to-qm python setup.py sdist - twine upload dist/vorta-0.6.24.tar.gz + twine upload dist/vorta-0.6.26.tar.gz bump-version: ## Add new version tag and push to upstream repo. bumpversion patch diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/setup.cfg new/vorta-0.6.26/setup.cfg --- old/vorta-0.6.24/setup.cfg 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/setup.cfg 2020-05-26 05:30:06.000000000 +0200 @@ -2,7 +2,7 @@ name = vorta author = Manuel Riel description = A GUI for BorgBackup -version = 0.6.24 +version = 0.6.26 url = https://github.com/borgbase/vorta keywords = backup diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/_version.py new/vorta-0.6.26/src/vorta/_version.py --- old/vorta-0.6.24/src/vorta/_version.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/_version.py 2020-05-26 05:30:06.000000000 +0200 @@ -1 +1 @@ -__version__ = '0.6.24' +__version__ = '0.6.26' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/assets/icons/scalable/com.borgbase.Vorta.svg new/vorta-0.6.26/src/vorta/assets/icons/scalable/com.borgbase.Vorta.svg --- old/vorta-0.6.24/src/vorta/assets/icons/scalable/com.borgbase.Vorta.svg 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/assets/icons/scalable/com.borgbase.Vorta.svg 2020-05-26 05:30:06.000000000 +0200 @@ -1 +1,77 @@ -<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 384"><defs><style>.cls-1{fill:#515151;}.cls-2{fill:#e65100;}.cls-3{fill:#ff9800;}</style></defs><title>vorta</title><path class="cls-1" d="M586.52,335.52v96a48,48,0,0,1-48,48h-480a48,48,0,0,1-48-48v-96a48,48,0,0,1,48-48h480A48,48,0,0,1,586.52,335.52Zm-48-80a79.47,79.47,0,0,1,30.77,6.16L472.77,116.89a48,48,0,0,0-39.94-21.37H164.2a48,48,0,0,0-39.93,21.37L27.74,261.68a79.52,79.52,0,0,1,30.78-6.16Zm-48,96a32,32,0,1,0,32,32A32,32,0,0,0,490.52,351.52Zm-96,0a32,32,0,1,0,32,32A32,32,0,0,0,394.52,351.52Z" transform="translate(-10.52 -95.52)"/><circle class="cls-2" cx="384.5" cy="288" r="32"/><circle class="cls-3" cx="480" cy="288" r="32"/></svg> \ No newline at end of file +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + id="Layer_1" + data-name="Layer 1" + viewBox="0 0 576 576" + version="1.1" + sodipodi:docname="com.borgbase.Vorta.svg" + width="576" + height="576" + inkscape:version="0.92.4 5da689c313, 2019-01-14"> + <metadata + id="metadata17"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>vorta</dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1600" + inkscape:window-height="836" + id="namedview15" + showgrid="false" + inkscape:zoom="0.61458333" + inkscape:cx="291.25424" + inkscape:cy="192" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="Layer_1" /> + <defs + id="defs4"> + <style + id="style2">.cls-1{fill:#515151;}.cls-2{fill:#e65100;}.cls-3{fill:#ff9800;}</style> + </defs> + <title + id="title6">vorta</title> + <path + class="cls-1" + d="m 576,342 v 96 a 48,48 0 0 1 -48,48 H 48 A 48,48 0 0 1 0,438 v -96 a 48,48 0 0 1 48,-48 h 480 a 48,48 0 0 1 48,48 z m -48,-80 a 79.47,79.47 0 0 1 30.77,6.16 L 462.25,123.37 A 48,48 0 0 0 422.31,102 H 153.68 a 48,48 0 0 0 -39.93,21.37 L 17.22,268.16 A 79.52,79.52 0 0 1 48,262 Z m -48,96 a 32,32 0 1 0 32,32 32,32 0 0 0 -32,-32 z m -96,0 a 32,32 0 1 0 32,32 32,32 0 0 0 -32,-32 z" + id="path8" + inkscape:connector-curvature="0" + style="fill:#515151" /> + <circle + class="cls-2" + cx="384.5" + cy="390" + r="32" + id="circle10" + style="fill:#e65100" /> + <circle + class="cls-3" + cx="480" + cy="390" + r="32" + id="circle12" + style="fill:#ff9800" /> +</svg> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/assets/metadata/com.borgbase.Vorta.appdata.xml new/vorta-0.6.26/src/vorta/assets/metadata/com.borgbase.Vorta.appdata.xml --- old/vorta-0.6.24/src/vorta/assets/metadata/com.borgbase.Vorta.appdata.xml 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/assets/metadata/com.borgbase.Vorta.appdata.xml 2020-05-26 05:30:06.000000000 +0200 @@ -4,7 +4,9 @@ <name>Vorta</name> <project_license>GPL-3.0</project_license> <metadata_license>CC0-1.0</metadata_license> - <content_rating type="oars-1.1"/> + <content_rating type="oars-1.1"> + <content_attribute id="money-purchasing">mild</content_attribute> + </content_rating> <summary>Backup client</summary> <description> <p> @@ -38,7 +40,7 @@ </screenshot> </screenshots> <releases> - <release version="v0.6.24" date="2020-03-03"> + <release version="v0.6.26" date="2020-05-26"> </release> </releases> <url type="bugtracker">https://github.com/borgbase/vorta/issues</url> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/assets/metadata/com.borgbase.Vorta.desktop new/vorta-0.6.26/src/vorta/assets/metadata/com.borgbase.Vorta.desktop --- old/vorta-0.6.24/src/vorta/assets/metadata/com.borgbase.Vorta.desktop 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/assets/metadata/com.borgbase.Vorta.desktop 2020-05-26 05:30:06.000000000 +0200 @@ -5,3 +5,4 @@ Type=Application Icon=com.borgbase.Vorta Categories=Utility +StartupWMClass=vorta diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/borg/borg_thread.py new/vorta-0.6.26/src/vorta/borg/borg_thread.py --- old/vorta-0.6.24/src/vorta/borg/borg_thread.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/borg/borg_thread.py 2020-05-26 05:30:06.000000000 +0200 @@ -234,7 +234,7 @@ def cancel(self): if self.isRunning(): mutex.unlock() - os.killpg(os.getpgid(self.process.pid), signal.SIGTERM) + self.process.send_signal(signal.SIGINT) self.terminate() def process_result(self, result): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/utils.py new/vorta-0.6.26/src/vorta/utils.py --- old/vorta-0.6.24/src/vorta/utils.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/utils.py 2020-05-26 05:30:06.000000000 +0200 @@ -27,6 +27,9 @@ from vorta.keyring.abc import VortaKeyring from vorta.log import logger +QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) # enable highdpi scaling +QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) # use highdpi icons + keyring = VortaKeyring.get_keyring() logger.info('Using %s Keyring implementation.', keyring.__class__.__name__) @@ -102,7 +105,12 @@ while size >= power: size /= power n += 1 - return f'{round(size, 1)} {Dic_powerN[n]}B' + try: + unit = Dic_powerN[n] + return f'{round(size, 1)} {unit}B' + except KeyError as e: + logger.error(e) + return "NaN" def get_asset(path): @@ -128,12 +136,16 @@ wifis = plistlib.load(plist_file).get('KnownNetworks') except xml.parsers.expat.ExpatError: logger.error('Unable to parse list of Wifi networks.') - return + return [] if wifis is not None: for wifi in wifis.values(): timestamp = wifi.get('LastConnected', None) - ssid = wifi['SSIDString'] + ssid = wifi.get('SSIDString', None) + + if ssid is None: + continue + db_wifi, created = WifiSettingModel.get_or_create( ssid=ssid, profile=profile.id, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/views/archive_tab.py new/vorta-0.6.26/src/vorta/views/archive_tab.py --- old/vorta-0.6.24/src/vorta/views/archive_tab.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/views/archive_tab.py 2020-05-26 05:30:06.000000000 +0200 @@ -76,6 +76,8 @@ self.populate_from_profile() + self.selected_archives = None + def _set_status(self, text): self.mountErrors.setText(text) self.mountErrors.repaint() @@ -429,9 +431,10 @@ window.show() if window.exec_(): - selected_archives = window.selected_archives - archive_cell_newer = self.archiveTable.item(selected_archives[0], 4) - archive_cell_older = self.archiveTable.item(selected_archives[1], 4) + if window.selected_archives: + self.selected_archives = window.selected_archives + archive_cell_newer = self.archiveTable.item(self.selected_archives[0], 4) + archive_cell_older = self.archiveTable.item(self.selected_archives[1], 4) if archive_cell_older and archive_cell_newer: archive_name_newer = archive_cell_newer.text() archive_name_older = archive_cell_older.text() @@ -457,5 +460,5 @@ window = DiffResult(result['data'], archive_newer, archive_older) self._toggle_all_buttons(True) window.setParent(self, QtCore.Qt.Sheet) - self._window = window # for testing + self._resultwindow = window # for testing window.show() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/views/diff_dialog.py new/vorta-0.6.26/src/vorta/views/diff_dialog.py --- old/vorta-0.6.24/src/vorta/views/diff_dialog.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/views/diff_dialog.py 2020-05-26 05:30:06.000000000 +0200 @@ -3,12 +3,11 @@ from vorta.utils import get_asset -uifile = get_asset('UI/diffdialog.ui') +uifile = get_asset("UI/diffdialog.ui") DiffDialogUI, DiffDialogBase = uic.loadUiType(uifile) class DiffDialog(DiffDialogBase, DiffDialogUI): - def __init__(self, archiveTable): super().__init__() self.setupUi(self) @@ -38,7 +37,7 @@ text = archiveTable.item(row, column).text() self.archiveTable.setItem(row, column, QTableWidgetItem(text)) except AttributeError: - self.archiveTable.setItem(row, column, QTableWidgetItem('')) + self.archiveTable.setItem(row, column, QTableWidgetItem("")) self.diffButton.setEnabled(False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/views/diff_result.py new/vorta-0.6.26/src/vorta/views/diff_result.py --- old/vorta-0.6.24/src/vorta/views/diff_result.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/views/diff_result.py 2020-05-26 05:30:06.000000000 +0200 @@ -1,34 +1,28 @@ import os +import re from PyQt5 import uic -from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt, QVariant +from PyQt5.QtCore import Qt, QVariant from PyQt5.QtGui import QColor from PyQt5.QtWidgets import QHeaderView -from vorta.utils import (get_asset, get_dict_from_list, nested_dict, - pretty_bytes) +from vorta.utils import (get_asset, get_dict_from_list, nested_dict) + +from vorta.views.tree_view import TreeModel uifile = get_asset('UI/diffresult.ui') DiffResultUI, DiffResultBase = uic.loadUiType(uifile) -files_with_attributes = None -nested_file_list = None -selected_files_folders = None - class DiffResult(DiffResultBase, DiffResultUI): def __init__(self, fs_data, archive_newer, archive_older): super().__init__() self.setupUi(self) - global files_with_attributes, nested_file_list, selected_files_folders - # Clear global file lists files_with_attributes = [] nested_file_list = nested_dict() - selected_files_folders = set() def parse_line(line): - if line: line_split = line.split() else: @@ -36,59 +30,48 @@ if line_split[0] == 'added' or line_split[0] == 'removed': change_type = line_split[0] - size = line_split[1] - unit = line_split[2] + if line_split[1] in ['directory', 'link']: + size = 0 + full_path = re.search(r'^\w+ \w+ +(.*)', line).group(1) + else: + significand = line_split[1] + unit = line_split[2] + size = calc_size(significand, unit) + full_path = re.search(r'^\w+ +\S+ \w?B (.*)', line).group(1) else: - change_type = "modified" - size = line_split[0] - unit = line_split[1] - # If present remove '+' or '-' sign at the front - if size[0] in ('+', '-'): - size = size[1:] - - if line_split[0].startswith("["): - size = 0 - change_type = line[:line.find(line_split[3])] - full_path = line[line.find(line_split[3]):] - dir, name = os.path.split(full_path) - # add to nested dict of folders to find nested dirs. - d = get_dict_from_list(nested_file_list, full_path.split('/')) - elif line_split[1] not in ['directory', 'link']: - if unit == 'B': - size = int(size) - elif unit == 'kB': - size = int(float(size) * 10**3) - elif unit == 'MB': - size = int(float(size) * 10**6) - elif unit == 'GB': - size = int(float(size) * 10**9) - elif unit == 'TB': - size = int(float(size) * 10**12) - - if change_type == 'added' or change_type == 'removed': - full_path = line[line.find(line_split[3]):] - elif change_type == "modified": - full_path = line[line.find(line_split[4]):] - - dir, name = os.path.split(full_path) - # add to nested dict of folders to find nested dirs. - d = get_dict_from_list(nested_file_list, dir.split('/')) - if name not in d: - d[name] = {} - else: - size = 0 - full_path = line[line.find(line_split[2]):] - - dir, name = os.path.split(full_path) - # add to nested dict of folders to find nested dirs. - d = get_dict_from_list(nested_file_list, full_path.split('/')) + size_change = re.search(r' *[\+-]?(\d+\.*\d*) (\w?B) +[\+-]?.+\w?B ', line) + if size_change: + significand = size_change.group(1) + unit = size_change.group(2) + size = calc_size(significand, unit) + full_path_index = size_change.end(0) + else: + size = 0 + + permission_change = re.search(r' *(\[.{24}\]) ', line) + if permission_change: + change_type = permission_change.group(1) + full_path_index = permission_change.end(0) + else: + change_type = "modified" + + if size_change and permission_change: + full_path_index = max(size_change.end(0), permission_change.end(0)) + full_path = line[full_path_index:] + + dir, name = os.path.split(full_path) + + # add to nested dict of folders to find nested dirs. + d = get_dict_from_list(nested_file_list, dir.split('/')) + if name not in d: + d[name] = {} return size, change_type, name, dir - for l in fs_data.split('\n'): - files_with_attributes.append(parse_line(l)) + for line in fs_data.split('\n'): + files_with_attributes.append(parse_line(line)) - model = TreeModel() + model = DiffTree(files_with_attributes, nested_file_list) view = self.treeView view.setAlternatingRowColors(True) @@ -103,113 +86,31 @@ self.archiveNameLabel_1.setText(f'{archive_newer.name}') self.archiveNameLabel_2.setText(f'{archive_older.name}') self.okButton.clicked.connect(self.accept) - self.selected = selected_files_folders - - -class FolderItem: - def __init__(self, path, name, modified, parent=None): - self.parentItem = parent - self.path = path - self.itemData = [name, modified] - self.childItems = [] - - # Pre-filter children - self._filtered_children = [] - search_path = os.path.join(self.path, name) - if parent is None: # Find path for root folder - for root_folder in nested_file_list.keys(): - self._filtered_children.append((0, '', root_folder, '', )) - else: - - # This adds direct children. - self._filtered_children = [f for f in files_with_attributes if search_path == f[3]] - - # Add nested folders. - for immediate_child in get_dict_from_list(nested_file_list, search_path.split('/')).keys(): - if not [True for child in self._filtered_children if child[2] == immediate_child]: - self._filtered_children.append((0, '', immediate_child, search_path)) - - self.is_loaded = False - - def load_children(self): - for child_item in self._filtered_children: - if child_item[0] > 0: # This is a file - self.childItems.append(FileItem( - name=child_item[2], - modified=child_item[1], - size=child_item[0], - parent=self)) - else: # Folder - self.childItems.append( - FolderItem( - path=child_item[3], - name=child_item[2], - modified=child_item[1], - parent=self)) - self.is_loaded = True - - def child(self, row): - return self.childItems[row] - - def childCount(self): - return len(self._filtered_children) - - def columnCount(self): - return 3 - - def data(self, column): - if column <= 1: - return self.itemData[column] - else: - return None - - def parent(self): - return self.parentItem - - def row(self): - if self.parentItem: - return self.parentItem.childItems.index(self) - - return 0 - - -class FileItem(FolderItem): - def __init__(self, name, modified, size, parent=None): - self.parentItem = parent - self.itemData = [name, modified, size] - - def childCount(self): - return 0 - - def columnCount(self): - return 3 - - def data(self, column): - if column == 1: - return self.itemData[column] - elif column == 2: - return pretty_bytes(self.itemData[column]) - elif column == 0: - return self.itemData[column] - - def parent(self): - return self.parentItem - - def row(self): - return self.parentItem.childItems.index(self) -class TreeModel(QAbstractItemModel): - column_names = ['Name', 'Modified', 'Size'] - - def __init__(self, parent=None): - super(TreeModel, self).__init__(parent) - - self.rootItem = FolderItem(path='', name='', modified=None) - self.rootItem.load_children() - - def columnCount(self, parent): - return 3 +def calc_size(significand, unit): + if unit == 'B': + return int(significand) + elif unit == 'kB': + return int(float(significand) * 10**3) + elif unit == 'MB': + return int(float(significand) * 10**6) + elif unit == 'GB': + return int(float(significand) * 10**9) + elif unit == 'TB': + return int(float(significand) * 10**12) + + +class DiffTree(TreeModel): + def __init__( + self, + files_with_attributes, + nested_file_list, + parent=None, + ): + super().__init__( + files_with_attributes, nested_file_list, parent=parent + ) def data(self, index, role): if not index.isValid(): @@ -230,61 +131,7 @@ else: return None - def canFetchMore(self, index): - if not index.isValid(): - return False - item = index.internalPointer() - return not item.is_loaded - - def fetchMore(self, index): - item = index.internalPointer() - item.load_children() - def flags(self, index): if not index.isValid(): return Qt.NoItemFlags return Qt.ItemIsEnabled - - def headerData(self, section, orientation, role): - if orientation == Qt.Horizontal and role == Qt.DisplayRole: - return self.column_names[section] - - return None - - def index(self, row, column, parent): - if not self.hasIndex(row, column, parent): - return QModelIndex() - - if not parent.isValid(): - parentItem = self.rootItem - else: - parentItem = parent.internalPointer() - - childItem = parentItem.child(row) - if childItem: - return self.createIndex(row, column, childItem) - else: - return QModelIndex() - - def parent(self, index): - if not index.isValid(): - return QModelIndex() - - childItem = index.internalPointer() - parentItem = childItem.parent() - - if parentItem == self.rootItem: - return QModelIndex() - - return self.createIndex(parentItem.row(), 0, parentItem) - - def rowCount(self, parent): - if parent.column() > 0: - return 0 - - if not parent.isValid(): - parentItem = self.rootItem - else: - parentItem = parent.internalPointer() - - return parentItem.childCount() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/views/extract_dialog.py new/vorta-0.6.26/src/vorta/views/extract_dialog.py --- old/vorta-0.6.24/src/vorta/views/extract_dialog.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/views/extract_dialog.py 2020-05-26 05:30:06.000000000 +0200 @@ -4,50 +4,45 @@ from collections import namedtuple from PyQt5 import uic -from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt +from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QApplication, QHeaderView -from vorta.utils import get_asset, pretty_bytes, get_dict_from_list, nested_dict +from vorta.utils import get_asset, get_dict_from_list, nested_dict +from vorta.views.tree_view import TreeModel -uifile = get_asset('UI/extractdialog.ui') +uifile = get_asset("UI/extractdialog.ui") ExtractDialogUI, ExtractDialogBase = uic.loadUiType(uifile) -ISO_FORMAT = '%Y-%m-%dT%H:%M:%S.%f' - -files_with_attributes = None -nested_file_list = None -selected_files_folders = None +ISO_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" class ExtractDialog(ExtractDialogBase, ExtractDialogUI): def __init__(self, fs_data, archive): super().__init__() self.setupUi(self) - global files_with_attributes, nested_file_list, selected_files_folders - # Clear global file lists files_with_attributes = [] nested_file_list = nested_dict() - selected_files_folders = set() + self.selected = set() def parse_line(line): - size, modified, full_path = line.split('\t') + size, modified, full_path = line.split("\t") size = int(size) dir, name = os.path.split(full_path) # add to nested dict of folders to find nested dirs. - d = get_dict_from_list(nested_file_list, dir.split('/')) + d = get_dict_from_list(nested_file_list, dir.split("/")) if name not in d: d[name] = {} return size, modified, name, dir - for l in fs_data.split('\n'): + for line in fs_data.split("\n"): try: - files_with_attributes.append(parse_line(l)) + files_with_attributes.append(parse_line(line)) except ValueError: pass - model = TreeModel() + model = ExtractTree(files_with_attributes, nested_file_list, self.selected) view = self.treeView view.setAlternatingRowColors(True) @@ -59,141 +54,22 @@ header.setSectionResizeMode(2, QHeaderView.ResizeToContents) header.setSectionResizeMode(0, QHeaderView.Stretch) - self.archiveNameLabel.setText(f'{archive.name}, {archive.time}') + self.archiveNameLabel.setText(f"{archive.name}, {archive.time}") self.cancelButton.clicked.connect(self.close) self.extractButton.clicked.connect(self.accept) - self.selected = selected_files_folders - - -class FolderItem: - def __init__(self, path, name, modified, parent=None): - self.parentItem = parent - self.path = path - self.itemData = [name, modified] - self.childItems = [] - self.checkedState = False - - # Pre-filter children - self._filtered_children = [] - search_path = os.path.join(self.path, name) - if parent is None: # Find path for root folder - for root_folder in nested_file_list.keys(): - self._filtered_children.append((0, '', root_folder, '', )) - else: - self.checkedState = parent.checkedState # If there is a parent, use its checked-status. - - # This adds direct children. - self._filtered_children = [f for f in files_with_attributes if search_path == f[3]] - - # Add nested folders. - for immediate_child in get_dict_from_list(nested_file_list, search_path.split('/')).keys(): - if not [True for child in self._filtered_children if child[2] == immediate_child]: - self._filtered_children.append((0, '', immediate_child, search_path)) - - self.is_loaded = False - - def load_children(self): - for child_item in self._filtered_children: - if child_item[0] > 0: # This is a file - self.childItems.append(FileItem( - name=child_item[2], - modified=child_item[1], - size=child_item[0], - parent=self)) - else: # Folder - self.childItems.append( - FolderItem( - path=child_item[3], - name=child_item[2], - modified=child_item[1], - parent=self)) - - self.is_loaded = True - - def setCheckedState(self, value): - if value == 2: - self.checkedState = True - selected_files_folders.add( - os.path.join(self.parentItem.path, self.parentItem.data(0), self.itemData[0])) - else: - self.checkedState = False - path_to_remove = os.path.join(self.parentItem.path, self.parentItem.data(0), self.itemData[0]) - if path_to_remove in selected_files_folders: - selected_files_folders.remove(path_to_remove) - - if hasattr(self, 'childItems'): - for child in self.childItems: - child.setCheckedState(value) - - def getCheckedState(self): - if self.checkedState: - return Qt.Checked - else: - return Qt.Unchecked - - def child(self, row): - return self.childItems[row] - - def childCount(self): - return len(self._filtered_children) - - def columnCount(self): - return 3 - - def data(self, column): - if column <= 1: - return self.itemData[column] - else: - return None - - def parent(self): - return self.parentItem - - def row(self): - if self.parentItem: - return self.parentItem.childItems.index(self) - - return 0 - - -class FileItem(FolderItem): - def __init__(self, name, modified, size, parent=None): - self.parentItem = parent - self.itemData = [name, modified, size] # dt.strptime(modified, ISO_FORMAT) - self.checkedState = parent.checkedState - - def childCount(self): - return 0 - - def columnCount(self): - return 3 - - def data(self, column): - if column == 1: - return self.itemData[column] # .strftime('%Y-%m-%dT%H:%M') - elif column == 2: - return pretty_bytes(self.itemData[column]) - elif column == 0: - return self.itemData[column] - - def parent(self): - return self.parentItem - - def row(self): - return self.parentItem.childItems.index(self) -class TreeModel(QAbstractItemModel): - column_names = ['Name', 'Modified', 'Size'] - - def __init__(self, parent=None): - super(TreeModel, self).__init__(parent) - - self.rootItem = FolderItem(path='', name='', modified=None) - self.rootItem.load_children() - - def columnCount(self, parent): - return 3 +class ExtractTree(TreeModel): + def __init__( + self, + files_with_attributes, + nested_file_list, + selected_files_folders, + parent=None, + ): + super().__init__( + files_with_attributes, nested_file_list, selected_files_folders, parent + ) def data(self, index, role): if not index.isValid(): @@ -208,86 +84,24 @@ else: return None - def setData(self, index, value, role=Qt.EditRole): - if role == Qt.CheckStateRole: - item = index.internalPointer() - item.setCheckedState(value) - self.dataChanged.emit(QModelIndex(), QModelIndex(), []) - - return True - - def canFetchMore(self, index): - if not index.isValid(): - return False - item = index.internalPointer() - return not item.is_loaded - - def fetchMore(self, index): - item = index.internalPointer() - item.load_children() - def flags(self, index): if not index.isValid(): return Qt.NoItemFlags return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable - def headerData(self, section, orientation, role): - if orientation == Qt.Horizontal and role == Qt.DisplayRole: - return self.column_names[section] - - return None - - def index(self, row, column, parent): - if not self.hasIndex(row, column, parent): - return QModelIndex() - - if not parent.isValid(): - parentItem = self.rootItem - else: - parentItem = parent.internalPointer() - - childItem = parentItem.child(row) - if childItem: - return self.createIndex(row, column, childItem) - else: - return QModelIndex() - - def parent(self, index): - if not index.isValid(): - return QModelIndex() - - childItem = index.internalPointer() - parentItem = childItem.parent() - - if parentItem == self.rootItem: - return QModelIndex() - - return self.createIndex(parentItem.row(), 0, parentItem) - - def rowCount(self, parent): - if parent.column() > 0: - return 0 - - if not parent.isValid(): - parentItem = self.rootItem - else: - parentItem = parent.internalPointer() - - return parentItem.childCount() - -if __name__ == '__main__': +if __name__ == "__main__": """ For local testing: borg list --progress --info --log-json --format="{size:8d}{TAB}{mtime}{TAB}{path}{NL}" """ - FakeArchive = namedtuple('Archive', ['name', 'time']) + FakeArchive = namedtuple("Archive", ["name", "time"]) app = QApplication(sys.argv) - test_list = open('/Users/manu/Downloads/nyx2-list.txt').read() + test_list = open("/Users/manu/Downloads/nyx2-list.txt").read() - archive = FakeArchive('test-archive', datetime.datetime.now()) + archive = FakeArchive("test-archive", datetime.datetime.now()) view = ExtractDialog(test_list, archive) view.show() sys.exit(app.exec_()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/views/main_window.py new/vorta-0.6.26/src/vorta/views/main_window.py --- old/vorta-0.6.24/src/vorta/views/main_window.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/views/main_window.py 2020-05-26 05:30:06.000000000 +0200 @@ -1,12 +1,12 @@ import sys from PyQt5 import QtCore, uic +from PyQt5.QtWidgets import QShortcut, QMessageBox from PyQt5.QtGui import QKeySequence -from PyQt5.QtWidgets import QMessageBox, QShortcut from vorta.borg.borg_thread import BorgThread from vorta.i18n import trans_late -from vorta.models import BackupProfileModel +from vorta.models import BackupProfileModel, SettingsModel from vorta.utils import borg_compat, get_asset, is_system_tray_available from vorta.views.utils import get_theme_class @@ -31,6 +31,20 @@ self.current_profile = BackupProfileModel.select().order_by('id').first() self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint) + # Temporary fix for QT Darkstyle dropdown issue. + # See https://github.com/ColinDuquesnoy/QDarkStyleSheet/issues/200 + if SettingsModel.get(key='use_dark_theme').value: + self.setStyleSheet(""" + QComboBox::item:checked { + height: 12px; + border: 1px solid #32414B; + margin-top: 0px; + margin-bottom: 0px; + padding: 4px; + padding-left: 0px; + } + """) + # Load tab models self.repoTab = RepoTab(self.repoTabSlot) self.sourceTab = SourceTab(self.sourceTabSlot) @@ -114,12 +128,17 @@ # Remove pending background jobs to_delete_id = str(to_delete.id) - if self.app.scheduler.get_job(to_delete_id): - self.app.scheduler.remove_job(to_delete_id) - - to_delete.delete_instance(recursive=True) - self.profileSelector.removeItem(self.profileSelector.currentIndex()) - self.profile_select_action(0) + msg = self.tr("Are you sure you want to delete profile '{}'?".format(to_delete.name)) + reply = QMessageBox.question(self, self.tr("Confirm deletion"), + msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) + + if reply == QMessageBox.Yes: + if self.app.scheduler.get_job(to_delete_id): + self.app.scheduler.remove_job(to_delete_id) + + to_delete.delete_instance(recursive=True) + self.profileSelector.removeItem(self.profileSelector.currentIndex()) + self.profile_select_action(0) def profile_add_action(self): window = AddProfileWindow() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/src/vorta/views/tree_view.py new/vorta-0.6.26/src/vorta/views/tree_view.py --- old/vorta-0.6.24/src/vorta/views/tree_view.py 1970-01-01 01:00:00.000000000 +0100 +++ new/vorta-0.6.26/src/vorta/views/tree_view.py 2020-05-26 05:30:06.000000000 +0200 @@ -0,0 +1,273 @@ +from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt + +import os +import abc + +from vorta.utils import get_dict_from_list, pretty_bytes + + +class FolderItem: + def __init__( + self, + path, + name, + modified, + files_with_attributes, + nested_file_list, + selected_files_folders=None, + parent=None, + ): + self.parentItem = parent + self.path = path + self.itemData = [name, modified] + self.childItems = [] + self.checkedState = False + self.files_with_attributes = files_with_attributes + self.nested_file_list = nested_file_list + self.selected_files_folders = selected_files_folders + + # Pre-filter children + self._filtered_children = [] + search_path = os.path.join(self.path, name) + if parent is None: # Find path for root folder + for root_folder in nested_file_list.keys(): + self._filtered_children.append((0, "", root_folder, "",)) + else: + self.checkedState = ( + parent.checkedState + ) # If there is a parent, use its checked-status. + + # This adds direct children. + self._filtered_children = [ + f for f in files_with_attributes if search_path == f[3] + ] + + # Add nested folders. + for immediate_child in get_dict_from_list( + nested_file_list, search_path.split("/") + ).keys(): + if not [ + True + for child in self._filtered_children + if child[2] == immediate_child + ]: + self._filtered_children.append( + (0, "", immediate_child, search_path) + ) + + self.is_loaded = False + + def load_children(self): + for child_item in self._filtered_children: + if child_item[0] > 0: # This is a file + self.childItems.append( + FileItem( + name=child_item[2], + modified=child_item[1], + size=child_item[0], + files_with_attributes=self.files_with_attributes, + nested_file_list=self.nested_file_list, + selected_files_folders=self.selected_files_folders, + parent=self, + ) + ) + else: # Folder + self.childItems.append( + FolderItem( + path=child_item[3], + name=child_item[2], + modified=child_item[1], + files_with_attributes=self.files_with_attributes, + nested_file_list=self.nested_file_list, + selected_files_folders=self.selected_files_folders, + parent=self, + ) + ) + self.is_loaded = True + + def setCheckedState(self, value): + if value == 2: + self.checkedState = True + self.selected_files_folders.add( + os.path.join( + self.parentItem.path, self.parentItem.data(0), self.itemData[0] + ) + ) + else: + self.checkedState = False + path_to_remove = os.path.join( + self.parentItem.path, self.parentItem.data(0), self.itemData[0] + ) + if path_to_remove in self.selected_files_folders: + self.selected_files_folders.remove(path_to_remove) + + if hasattr(self, "childItems"): + for child in self.childItems: + child.setCheckedState(value) + + def getCheckedState(self): + if self.checkedState: + return Qt.Checked + else: + return Qt.Unchecked + + def child(self, row): + return self.childItems[row] + + def childCount(self): + return len(self._filtered_children) + + def columnCount(self): + return 3 + + def data(self, column): + if column <= 1: + return self.itemData[column] + else: + return None + + def parent(self): + return self.parentItem + + def row(self): + if self.parentItem: + return self.parentItem.childItems.index(self) + + return 0 + + +class FileItem(FolderItem): + def __init__( + self, + name, + modified, + size, + files_with_attributes, + nested_file_list, + selected_files_folders=None, + parent=None, + ): + self.parentItem = parent + self.itemData = [name, modified, size] + self.checkedState = parent.checkedState + self.files_with_attributes = files_with_attributes + self.nested_file_list = nested_file_list + self.selected_files_folders = selected_files_folders + + def childCount(self): + return 0 + + def columnCount(self): + return 3 + + def data(self, column): + if column == 1: + return self.itemData[column] + elif column == 2: + return pretty_bytes(self.itemData[column]) + elif column == 0: + return self.itemData[column] + + def parent(self): + return self.parentItem + + def row(self): + return self.parentItem.childItems.index(self) + + +class TreeModel(QAbstractItemModel): + __metaclass__ = abc.ABCMeta + + column_names = ["Name", "Modified", "Size"] + + def __init__( + self, + files_with_attributes, + nested_file_list, + selected_files_folders=None, + parent=None, + ): + super(TreeModel, self).__init__(parent) + + self.rootItem = FolderItem( + path="", + name="", + files_with_attributes=files_with_attributes, + nested_file_list=nested_file_list, + selected_files_folders=selected_files_folders, + modified=None, + ) + self.rootItem.load_children() + + def columnCount(self, parent): + return 3 + + @abc.abstractmethod + def data(self, index, role): + return + + def setData(self, index, value, role=Qt.EditRole): + if role == Qt.CheckStateRole: + item = index.internalPointer() + item.setCheckedState(value) + self.dataChanged.emit(QModelIndex(), QModelIndex(), []) + + return True + + def canFetchMore(self, index): + if not index.isValid(): + return False + item = index.internalPointer() + return not item.is_loaded + + def fetchMore(self, index): + item = index.internalPointer() + item.load_children() + + @abc.abstractmethod + def flags(self, index): + return + + def headerData(self, section, orientation, role): + if orientation == Qt.Horizontal and role == Qt.DisplayRole: + return self.column_names[section] + + return None + + def index(self, row, column, parent): + if not self.hasIndex(row, column, parent): + return QModelIndex() + + if not parent.isValid(): + parentItem = self.rootItem + else: + parentItem = parent.internalPointer() + + childItem = parentItem.child(row) + if childItem: + return self.createIndex(row, column, childItem) + else: + return QModelIndex() + + def parent(self, index): + if not index.isValid(): + return QModelIndex() + + childItem = index.internalPointer() + parentItem = childItem.parent() + + if parentItem == self.rootItem: + return QModelIndex() + + return self.createIndex(parentItem.row(), 0, parentItem) + + def rowCount(self, parent): + if parent.column() > 0: + return 0 + + if not parent.isValid(): + parentItem = self.rootItem + else: + parentItem = parent.internalPointer() + + return parentItem.childCount() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/tests/borg_json_output/diff_archives_stdout.json new/vorta-0.6.26/tests/borg_json_output/diff_archives_stdout.json --- old/vorta-0.6.24/tests/borg_json_output/diff_archives_stdout.json 1970-01-01 01:00:00.000000000 +0100 +++ new/vorta-0.6.26/tests/borg_json_output/diff_archives_stdout.json 2020-05-26 05:30:06.000000000 +0200 @@ -0,0 +1,2 @@ + +7 B 0 B [-rw-rw-r-- -> -rw-rw-rw-] test/hallo +added 0 B test/tschüss diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/tests/conftest.py new/vorta-0.6.26/tests/conftest.py --- old/vorta-0.6.24/tests/conftest.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/tests/conftest.py 2020-05-26 05:30:06.000000000 +0200 @@ -32,6 +32,9 @@ test_archive = ArchiveModel(snapshot_id='99999', name='test-archive', time=dt(2000, 1, 1, 0, 0), repo=1) test_archive.save() + test_archive1 = ArchiveModel(snapshot_id='99998', name='test-archive1', time=dt(2000, 1, 1, 0, 0), repo=1) + test_archive1.save() + source_dir = SourceFileModel(dir='/tmp/another', repo=new_repo) source_dir.save() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/tests/test_archives.py new/vorta-0.6.26/tests/test_archives.py --- old/vorta-0.6.24/tests/test_archives.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/tests/test_archives.py 2020-05-26 05:30:06.000000000 +0200 @@ -101,12 +101,10 @@ ) qtbot.mouseClick(tab.mountButton, QtCore.Qt.LeftButton) - qtbot.waitUntil(lambda: tab.mountErrors.text().startswith('Mounted'), timeout=5000) + qtbot.waitUntil(lambda: tab.mountErrors.text().startswith('Mounted'), timeout=10000) qtbot.mouseClick(tab.mountButton, QtCore.Qt.LeftButton) - # qtbot.waitUntil(lambda: tab.mountErrors.text() == 'No active Borg mounts found.') - - qtbot.waitUntil(lambda: tab.mountErrors.text().startswith('Un-mounted successfully.'), timeout=5000) + qtbot.waitUntil(lambda: tab.mountErrors.text().startswith('Un-mounted successfully.'), timeout=10000) def test_archive_extract(qapp, qtbot, mocker, borg_json_output, monkeypatch): @@ -115,7 +113,7 @@ main.tabWidget.setCurrentIndex(3) tab.populate_from_profile() - qtbot.waitUntil(lambda: tab.archiveTable.rowCount() == 1) + qtbot.waitUntil(lambda: tab.archiveTable.rowCount() == 2) monkeypatch.setattr( vorta.views.extract_dialog.ExtractDialog, "exec_", lambda *args: True @@ -127,10 +125,43 @@ mocker.patch.object(vorta.borg.borg_thread, 'Popen', return_value=popen_result) qtbot.mouseClick(tab.extractButton, QtCore.Qt.LeftButton) - qtbot.waitUntil(lambda: hasattr(tab, '_window'), timeout=5000) + qtbot.waitUntil(lambda: hasattr(tab, '_window'), timeout=10000) assert tab._window.treeView.model().rootItem.childItems[0].data(0) == 'Users' tab._window.treeView.model().rootItem.childItems[0].load_children() - assert tab._window.archiveNameLabel.text().startswith('test-archive, 2000') - tab._window.accept() + + +def test_archive_diff(qapp, qtbot, mocker, borg_json_output, monkeypatch): + main = qapp.main_window + tab = main.archiveTab + main.tabWidget.setCurrentIndex(3) + + tab.populate_from_profile() + qtbot.waitUntil(lambda: tab.archiveTable.rowCount() == 2) + + monkeypatch.setattr( + vorta.views.diff_dialog.DiffDialog, "exec_", lambda *args: True + ) + + monkeypatch.setattr( + tab, "selected_archives", (0, 1) + ) + + stdout, stderr = borg_json_output('diff_archives') + popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0) + mocker.patch.object(vorta.borg.borg_thread, 'Popen', return_value=popen_result) + + qtbot.mouseClick(tab.diffButton, QtCore.Qt.LeftButton) + qtbot.waitUntil(lambda: hasattr(tab, '_window'), timeout=5000) + + monkeypatch.setattr( + vorta.views.diff_result.DiffResult, "exec_", lambda *args: True + ) + qtbot.waitUntil(lambda: hasattr(tab, '_resultwindow'), timeout=5000) + + assert tab._resultwindow.treeView.model().rootItem.childItems[0].data(0) == 'test' + tab._resultwindow.treeView.model().rootItem.childItems[0].load_children() + + assert tab._resultwindow.archiveNameLabel_1.text() == 'test-archive' + tab._resultwindow.accept() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vorta-0.6.24/tests/test_repo.py new/vorta-0.6.26/tests/test_repo.py --- old/vorta-0.6.24/tests/test_repo.py 2020-03-03 06:29:20.000000000 +0100 +++ new/vorta-0.6.26/tests/test_repo.py 2020-05-26 05:30:06.000000000 +0200 @@ -101,8 +101,8 @@ qtbot.waitUntil(lambda: main.createProgressText.text().startswith('Backup finished.'), timeout=3000) qtbot.waitUntil(lambda: main.createStartBtn.isEnabled(), timeout=3000) assert EventLogModel.select().count() == 1 - assert ArchiveModel.select().count() == 2 + assert ArchiveModel.select().count() == 3 assert RepoModel.get(id=1).unique_size == 15520474 assert main.createStartBtn.isEnabled() - assert main.archiveTab.archiveTable.rowCount() == 2 + assert main.archiveTab.archiveTable.rowCount() == 3 assert main.scheduleTab.logTableWidget.rowCount() == 1
