[view] feature: adding a new 'verified' filter for the packages table

This commit is contained in:
Vinicius Moreira
2023-11-30 17:17:32 -03:00
parent 4c91973028
commit a4c24c8aa6
32 changed files with 254 additions and 38 deletions

View File

@@ -11,6 +11,7 @@ from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QToolButton, QWidge
from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.model import PackageStatus, CustomSoftwareAction
from bauh.api.abstract.view import MessageType
from bauh.commons.html import strip_html, bold
from bauh.view.qt.components import IconButton, QCustomMenuAction, QCustomToolbar
from bauh.view.qt.dialog import ConfirmationDialog
@@ -206,15 +207,17 @@ class PackagesTable(QTableWidget):
body = self.i18n['manage_window.apps_table.row.actions.install.popup.body'].format(self._bold(str(pkgv)))
warning = self.i18n.get('gem.{}.install.warning'.format(pkgv.model.get_type().lower()))
if warning:
confirm_icon = MessageType.INFO
if not pkgv.model.is_trustable():
warning = self.i18n["action.install.unverified.warning"]
confirm_icon = MessageType.WARNING
body += '<br/><br/> {}'.format(
'<br/>'.join(('{}.'.format(phrase) for phrase in warning.split('.') if phrase)))
if ConfirmationDialog(title=self.i18n['manage_window.apps_table.row.actions.install.popup.title'],
body=self._parag(body),
i18n=self.i18n).ask():
i18n=self.i18n,
confirmation_icon_type=confirm_icon).ask():
self.window.install(pkgv)
def _update_pkg_icon(self, url_: str, content: Optional[bytes], table_idx: int):

View File

@@ -15,13 +15,14 @@ class PackageFilters(NamedTuple):
only_apps: bool
only_installed: bool
only_updates: bool
only_verified: bool
search: Optional[str] # initial search term
type: str
@property
def anything(self) -> bool:
return not self.only_installed and not self.only_updates and not self.only_apps \
and not self.name and self.type == "any" and self.category == "any"
and not self.only_verified and not self.name and self.type == "any" and self.category == "any"
def new_pkgs_info() -> Dict[str, Any]:
@@ -35,6 +36,7 @@ def new_pkgs_info() -> Dict[str, Any]:
'not_installed': 0,
'installed': 0,
'categories': set(),
'verified': 0,
'pkgs': []} # total packages
@@ -46,6 +48,9 @@ def update_info(pkgv: PackageView, pkgs_info: Dict[str, Any]):
else:
pkgs_info['napps_count'] += 1
if pkgv.model.is_trustable():
pkgs_info['verified'] += 1
if pkgv.model.update and not pkgv.model.is_update_ignored():
if pkgv.model.is_application():
pkgs_info['app_updates'] += 1
@@ -94,6 +99,9 @@ def is_package_hidden(pkg: PackageView, filters: PackageFilters) -> bool:
if not hidden and filters.only_updates:
hidden = not pkg.model.update or pkg.model.is_update_ignored()
if not hidden and filters.only_verified:
hidden = not pkg.model.is_trustable()
if not hidden and filters.type is not None and filters.type != "any":
hidden = pkg.model.get_type() != filters.type

View File

@@ -35,7 +35,8 @@ class ConfirmationDialog(QDialog):
widgets: Optional[List[QWidget]] = None, confirmation_button: bool = True, deny_button: bool = True,
window_cancel: bool = False, confirmation_label: Optional[str] = None, deny_label: Optional[str] = None,
confirmation_icon: bool = True, min_width: Optional[int] = None,
min_height: Optional[int] = None, max_width: Optional[int] = None):
min_height: Optional[int] = None, max_width: Optional[int] = None,
confirmation_icon_type: MessageType = MessageType.INFO):
super(ConfirmationDialog, self).__init__()
if not window_cancel:
@@ -79,6 +80,7 @@ class ConfirmationDialog(QDialog):
if confirmation_icon:
lb_icon = QLabel()
lb_icon.setObjectName("confirm_dialog_icon")
lb_icon.setProperty("type", confirmation_icon_type.name.lower())
container_body.layout().addWidget(lb_icon)
if body:

View File

@@ -17,15 +17,19 @@ def new_type_index() -> Dict[str, Dict[str, Dict[str, List[PackageView]]]]:
return defaultdict(new_category_idx)
def new_update_index() -> Dict[int, Dict[str, Dict[str, Dict[str, List[PackageView]]]]]:
def new_verified_index() -> Dict[int, Dict[str, Dict[str, Dict[str, List[PackageView]]]]]:
return defaultdict(new_type_index)
def new_app_index() -> Dict[int, Dict[int, Dict[str, Dict[str, Dict[str, List[PackageView]]]]]]:
def new_update_index() -> Dict[int, Dict[int, Dict[str, Dict[str, Dict[str, List[PackageView]]]]]]:
return defaultdict(new_verified_index)
def new_app_index() -> Dict[int, Dict[int, Dict[int, Dict[str, Dict[str, Dict[str, List[PackageView]]]]]]]:
return defaultdict(new_update_index)
def new_package_index() -> Dict[int, Dict[int, Dict[int, Dict[str, Dict[str, Dict[str, List[PackageView]]]]]]]:
def new_package_index() -> Dict[int, Dict[int, Dict[int, Dict[int, Dict[str, Dict[str, Dict[str, List[PackageView]]]]]]]]:
return defaultdict(new_app_index)
@@ -42,6 +46,9 @@ def add_to_index(pkgv: PackageView, index: dict) -> None:
else:
update_lvl = app_lvl[0]
# verified keys: (1) verified | (0) unverified
verified_lvl = update_lvl[1 if pkgv.model.is_trustable() else 0]
norm_name = pkgv.name.strip().lower()
starts_with_chars = tuple(norm_name[0:i] for i in range(1, len(norm_name) + 1))
@@ -49,13 +56,13 @@ def add_to_index(pkgv: PackageView, index: dict) -> None:
category = cat.lower().strip()
# any type > specific category > any character (None)
update_lvl["any"][category][None].append(pkgv)
verified_lvl["any"][category][None].append(pkgv)
# any type > specific category > characters (start
for chars in starts_with_chars:
update_lvl["any"][category][chars].append(pkgv)
verified_lvl["any"][category][chars].append(pkgv)
type_lvl = update_lvl[pkgv.model.get_type()]
type_lvl = verified_lvl[pkgv.model.get_type()]
# specific type > specific category > any character (None)
type_lvl[category][None].append(pkgv)
@@ -65,7 +72,7 @@ def add_to_index(pkgv: PackageView, index: dict) -> None:
type_lvl[category][chars].append(pkgv)
def generate_queries(filters: PackageFilters) -> Generator[Tuple[Optional[Union[int, str]]], None, None]:
def generate_queries(filters: PackageFilters) -> Generator[Tuple[Optional[Union[int, str]], ...], None, None]:
chars_query = None
if filters.name:
@@ -74,11 +81,13 @@ def generate_queries(filters: PackageFilters) -> Generator[Tuple[Optional[Union[
installed_queries = (1,) if filters.only_installed else (1, 0)
apps_queries = (1,) if filters.only_apps else (1, 0)
updates_queries = (1,) if filters.only_updates else (1, 0)
verified_queries = (1,) if filters.only_verified else (1, 0)
for installed in installed_queries:
for app in apps_queries:
for update in updates_queries:
yield installed, app, update, filters.type, filters.category, chars_query
for verified in verified_queries:
yield installed, app, update, verified, filters.type, filters.category, chars_query
def query_packages(index: dict, filters: PackageFilters) -> Generator[PackageView, None, None]:
@@ -90,7 +99,7 @@ def query_packages(index: dict, filters: PackageFilters) -> Generator[PackageVie
yielded_pkgs = defaultdict(set)
for query in queries:
packages = index[query[0]][query[1]][query[2]][query[3]][query[4]][query[5]]
packages = index[query[0]][query[1]][query[2]][query[3]][query[4]][query[5]][query[6]]
for pkgv in packages:
yield pkgv
@@ -111,7 +120,7 @@ def query_packages(index: dict, filters: PackageFilters) -> Generator[PackageVie
if 0 < yield_limit <= yield_count:
break
packages = index[query[0]][query[1]][query[2]][query[3]][query[4]][None]
packages = index[query[0]][query[1]][query[2]][query[3]][query[4]][query[5]][None]
for pkgv in packages:
# checking if the package has already been yielded

View File

@@ -75,13 +75,14 @@ CHECK_INSTALLED = 6
CHECK_UPDATES = 7
CHECK_APPS = 8
COMBO_TYPES = 9
COMBO_CATEGORIES = 10
INP_NAME = 11
CHECK_DETAILS = 12
BT_SETTINGS = 13
BT_CUSTOM_ACTIONS = 14
BT_ABOUT = 15
BT_THEMES = 16
CHECK_VERIFIED = 10
COMBO_CATEGORIES = 11
INP_NAME = 12
CHECK_DETAILS = 13
BT_SETTINGS = 14
BT_CUSTOM_ACTIONS = 15
BT_ABOUT = 16
BT_THEMES = 17
# component groups ids
GROUP_FILTERS = 1
@@ -177,6 +178,16 @@ class ManageWindow(QWidget):
self.toolbar_filters.layout().addWidget(self.check_apps)
self.comp_manager.register_component(CHECK_APPS, self.check_apps)
self.check_verified = QCheckBox()
self.check_verified.setObjectName('check_verified')
self.check_verified.setCursor(QCursor(Qt.PointingHandCursor))
self.check_verified.setText(self.i18n['manage_window.checkbox.only_verified'])
self.check_verified.setChecked(False)
self.check_verified.stateChanged.connect(self._handle_filter_only_verified)
self.check_verified.sizePolicy().setRetainSizeWhenHidden(True)
self.toolbar_filters.layout().addWidget(self.check_verified)
self.comp_manager.register_component(CHECK_VERIFIED, self.check_verified)
self.any_type_filter = 'any'
self.cache_type_filter_icons = {}
self.combo_filter_type = QComboBox()
@@ -434,6 +445,7 @@ class ManageWindow(QWidget):
self.layout.addWidget(self.container_progress)
self.filter_only_apps = True
self.filter_only_verified = False
self.type_filter = self.any_type_filter
self.category_filter = self.any_category_filter
self.filter_updates = False
@@ -462,22 +474,23 @@ class ManageWindow(QWidget):
qt_utils.centralize(self)
def _register_groups(self):
common_filters = (CHECK_APPS, CHECK_UPDATES, COMBO_CATEGORIES, COMBO_TYPES, INP_NAME)
common_filters = (CHECK_APPS, CHECK_VERIFIED, CHECK_UPDATES, COMBO_CATEGORIES, COMBO_TYPES, INP_NAME)
self.comp_manager.register_group(GROUP_FILTERS, False, CHECK_INSTALLED, *common_filters)
self.comp_manager.register_group(GROUP_VIEW_SEARCH, False,
COMBO_CATEGORIES, COMBO_TYPES, INP_NAME, # filters
BT_INSTALLED, BT_SUGGESTIONS, CHECK_INSTALLED) # buttons
CHECK_INSTALLED, CHECK_VERIFIED, COMBO_CATEGORIES, COMBO_TYPES, INP_NAME,
BT_INSTALLED, BT_SUGGESTIONS)
self.comp_manager.register_group(GROUP_VIEW_INSTALLED, False,
BT_REFRESH, BT_UPGRADE, # buttons
*common_filters)
self.comp_manager.register_group(GROUP_UPPER_BAR, False,
CHECK_APPS, CHECK_UPDATES, CHECK_INSTALLED, COMBO_CATEGORIES, COMBO_TYPES, INP_NAME,
BT_INSTALLED, BT_SUGGESTIONS, BT_REFRESH, BT_UPGRADE)
CHECK_APPS, CHECK_VERIFIED, CHECK_UPDATES, CHECK_INSTALLED, COMBO_CATEGORIES,
COMBO_TYPES, INP_NAME, BT_INSTALLED, BT_SUGGESTIONS, BT_REFRESH, BT_UPGRADE)
self.comp_manager.register_group(GROUP_LOWER_BTS, False, BT_SUGGESTIONS, BT_THEMES, BT_CUSTOM_ACTIONS, BT_SETTINGS, BT_ABOUT)
self.comp_manager.register_group(GROUP_LOWER_BTS, False, BT_SUGGESTIONS, BT_THEMES, BT_CUSTOM_ACTIONS,
BT_SETTINGS, BT_ABOUT)
def update_custom_actions(self):
self.custom_actions = [a for a in self.manager.gen_custom_actions()]
@@ -653,6 +666,10 @@ class ManageWindow(QWidget):
self.filter_only_apps = status == 2
self.begin_apply_filters()
def _handle_filter_only_verified(self, status: int):
self.filter_only_verified = status == 2
self.begin_apply_filters()
def _handle_filter_only_installed(self, status: int):
self.filter_installed = status == 2
self.begin_apply_filters()
@@ -974,6 +991,7 @@ class ManageWindow(QWidget):
display_limit=0 if self.filter_updates else self.display_limit,
name=self.input_name.text().strip(),
only_apps=False if self.search_performed else self.filter_only_apps,
only_verified=self.filter_only_verified,
only_updates=False if ignore_updates else self.filter_updates,
only_installed=self.filter_installed,
search=self.searched_term,
@@ -1029,6 +1047,7 @@ class ManageWindow(QWidget):
self.check_apps.setCheckable(True)
self._change_checkbox(self.check_apps, True, 'filter_only_apps', trigger=False)
self._update_verified_filter(verified_available=pkgs_info['verified'] > 0, keep_state=keep_filters)
self.change_update_state(pkgs_info=pkgs_info, trigger_filters=False, keep_selected=keep_filters and bool(pkgs_info['pkgs_displayed']))
self._update_categories(pkgs_info['categories'], keep_selected=keep_filters and bool(pkgs_info['pkgs_displayed']))
self._update_type_filters(pkgs_info['available_types'], keep_selected=keep_filters and bool(pkgs_info['pkgs_displayed']))
@@ -1081,6 +1100,19 @@ class ManageWindow(QWidget):
else:
self.comp_manager.set_component_visible(CHECK_INSTALLED, has_installed)
def _update_verified_filter(self, keep_state: bool = True, verified_available: Optional[bool] = None):
if verified_available is not None:
has_verified = verified_available
else:
has_verified = False
if self.pkgs_available:
has_verified = next((True for p in self.pkgs_available if p.model.is_trustable()), False)
if not keep_state or not has_verified:
self._change_checkbox(self.check_verified, False, 'filter_only_verified', trigger=False)
self.comp_manager.set_component_visible(CHECK_VERIFIED, has_verified)
def _apply_filters(self, pkgs_info: dict, ignore_updates: bool):
pkgs_info['pkgs_displayed'] = []
filters = self._gen_filters(ignore_updates=ignore_updates)