From 508e9169c291d5c3b29b9dde46fe3aaba08cbacf Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 11 Mar 2022 11:55:07 -0300 Subject: [PATCH] [view] improvement: sorting packages by closest match instead of considering installed first (search) --- CHANGELOG.md | 1 + bauh/view/core/controller.py | 25 ++--------------- bauh/view/qt/commons.py | 33 ++++++++++++++++++++++ bauh/view/qt/thread.py | 54 ++++-------------------------------- bauh/view/qt/view_model.py | 4 +++ 5 files changed, 46 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d62aac4d..a9e5ed15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - cli/tray: removed some unneeded API calls when listing updates [#234](https://github.com/vinifmor/bauh/issues/234) - UI + - search: sorting packages by closest match instead of considering installed first - upgrade summary dialog size - displaying tips for some custom actions (on mouse hover) - screenshots: displaying the current image index as a label in the button bar (e.g: 1/3) diff --git a/bauh/view/core/controller.py b/bauh/view/core/controller.py index 6c88db27..4e6be7d2 100755 --- a/bauh/view/core/controller.py +++ b/bauh/view/core/controller.py @@ -115,27 +115,6 @@ class GenericSoftwareManager(SoftwareManager): type_=MessageType.ERROR) return False - def _sort(self, apps: List[SoftwarePackage], word: str) -> List[SoftwarePackage]: - - exact_name_matches, contains_name_matches, others = [], [], [] - - for app in apps: - lower_name = app.name.lower() - - if word == lower_name: - exact_name_matches.append(app) - elif word in lower_name: - contains_name_matches.append(app) - else: - others.append(app) - - res = [] - for app_list in (exact_name_matches, contains_name_matches, others): - app_list.sort(key=lambda a: a.name.lower()) - res.extend(app_list) - - return res - def _can_work(self, man: SoftwareManager): if self._available_cache is not None: available = False @@ -197,8 +176,8 @@ class GenericSoftwareManager(SoftwareManager): disk_loader.stop_working() disk_loader.join() - res.installed = self._sort(res.installed, norm_word) - res.new = self._sort(res.new, norm_word) + # res.installed = self._sort(res.installed, norm_word) + # res.new = self._sort(res.new, norm_word) else: raise NoInternetException() diff --git a/bauh/view/qt/commons.py b/bauh/view/qt/commons.py index 08135568..69662db2 100644 --- a/bauh/view/qt/commons.py +++ b/bauh/view/qt/commons.py @@ -1,3 +1,6 @@ +from typing import Iterable, List + +from bauh.api.abstract.model import SoftwarePackage from bauh.view.qt.view_model import PackageView @@ -73,3 +76,33 @@ def is_package_hidden(pkg: PackageView, filters: dict) -> bool: hidden = not filters['name'] in pkg.model.name.lower() return hidden + + +def sort_packages(pkgs: Iterable[SoftwarePackage], word: str, limit: int = 0) -> List[SoftwarePackage]: + exact, starts_with, contains, others = [], [], [], [] + + for p in pkgs: + lower_name = p.name.lower() + + if word == lower_name: + exact.append(p) + elif lower_name.startswith(word): + starts_with.append(p) + elif word in lower_name: + contains.append(p) + else: + others.append(p) + + res = [] + for app_list in (exact, starts_with, contains, others): + if app_list: + last = limit - len(res) if limit > 0 else None + + if last is not None and last <= 0: + break + + to_add = app_list[0:last] + to_add.sort(key=lambda a: a.name.lower()) + res.extend(to_add) + + return res diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index b510b6b4..870f379c 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -5,7 +5,7 @@ import traceback from datetime import datetime, timedelta from io import StringIO from pathlib import Path -from typing import List, Type, Set, Tuple, Optional +from typing import List, Type, Set, Tuple, Optional, Iterable import requests from PyQt5.QtCore import QThread, pyqtSignal, QObject @@ -27,6 +27,7 @@ from bauh.commons.system import get_human_size_str, ProcessHandler, SimpleProces from bauh.view.core import timeshift from bauh.view.core.config import CoreConfigManager from bauh.view.qt import commons +from bauh.view.qt.commons import sort_packages from bauh.view.qt.view_model import PackageView, PackageViewStatus from bauh.view.util.translation import I18n @@ -704,8 +705,7 @@ class SearchPackages(AsyncAction): if self.word: try: res = self.manager.search(words=self.word, disk_loader=None, limit=-1, is_url=False) - search_res['pkgs_found'].extend(res.installed) - search_res['pkgs_found'].extend(res.new) + search_res['pkgs_found'] = sort_packages((*(res.installed or ()), *(res.new or ())), self.word) except NoInternetException: search_res['error'] = 'internet.required' finally: @@ -951,48 +951,6 @@ class ApplyFilters(AsyncAction): def stop_waiting(self): self.wait_table_update = False - def _sort_by_word(self, word: str, pkgs: List[PackageView], limit: int) -> List[PackageView]: - norm_word = word.strip().lower() - exact_installed, exact_not_installed = [], [] - starts_installed, starts_not_installed = [], [] - contains_installed, contains_not_installed = [], [] - - for p in pkgs: - lower_name = p.model.name.lower() - - if norm_word == lower_name: - if p.model.installed: - exact_installed.append(p) - else: - exact_not_installed.append(p) - elif lower_name.startswith(norm_word): - if p.model.installed: - starts_installed.append(p) - else: - starts_not_installed.append(p) - else: - if p.model.installed: - contains_installed.append(p) - else: - contains_not_installed.append(p) - - res = [] - for matches in (exact_installed, exact_not_installed, - starts_installed, starts_not_installed, - contains_installed, contains_not_installed): - if matches: - matches.sort(key=lambda p: p.model.name.lower()) - - if limit: - res.extend(matches[0:limit - len(res)]) - - if len(res) == limit: - break - else: - res.extend(matches) - - return res - def run(self): if self.pkgs: pkgs_info = commons.new_pkgs_info() @@ -1004,9 +962,9 @@ class ApplyFilters(AsyncAction): commons.apply_filters(pkgv, self.filters, pkgs_info, limit=not name_filtering) if name_filtering and pkgs_info['pkgs_displayed']: - pkgs_info['pkgs_displayed'] = self._sort_by_word(word=self.filters['name'], - pkgs=pkgs_info['pkgs_displayed'], - limit=self.filters['display_limit']) + pkgs_info['pkgs_displayed'] = sort_packages(word=self.filters['name'], + pkgs=pkgs_info['pkgs_displayed'], + limit=self.filters['display_limit']) self.wait_table_update = True self.signal_table.emit(pkgs_info) diff --git a/bauh/view/qt/view_model.py b/bauh/view/qt/view_model.py index b3dd646c..cab67179 100644 --- a/bauh/view/qt/view_model.py +++ b/bauh/view/qt/view_model.py @@ -32,6 +32,10 @@ class PackageView: self.update_checked = model.update self.status = PackageViewStatus.LOADING if model.status == PackageStatus.LOADING_DATA else PackageViewStatus.READY + @property + def name(self) -> str: + return self.model.name + def __repr__(self): return '{} ({})'.format(self.model.name, self.get_type_label())