[view] improvement: sorting packages by closest match instead of considering installed first (search)

This commit is contained in:
Vinicius Moreira
2022-03-11 11:55:07 -03:00
parent b1a131950c
commit 508e9169c2
5 changed files with 46 additions and 71 deletions

View File

@@ -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) - cli/tray: removed some unneeded API calls when listing updates [#234](https://github.com/vinifmor/bauh/issues/234)
- UI - UI
- search: sorting packages by closest match instead of considering installed first
- upgrade summary dialog size - upgrade summary dialog size
- displaying tips for some custom actions (on mouse hover) - 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) - screenshots: displaying the current image index as a label in the button bar (e.g: 1/3)

View File

@@ -115,27 +115,6 @@ class GenericSoftwareManager(SoftwareManager):
type_=MessageType.ERROR) type_=MessageType.ERROR)
return False 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): def _can_work(self, man: SoftwareManager):
if self._available_cache is not None: if self._available_cache is not None:
available = False available = False
@@ -197,8 +176,8 @@ class GenericSoftwareManager(SoftwareManager):
disk_loader.stop_working() disk_loader.stop_working()
disk_loader.join() disk_loader.join()
res.installed = self._sort(res.installed, norm_word) # res.installed = self._sort(res.installed, norm_word)
res.new = self._sort(res.new, norm_word) # res.new = self._sort(res.new, norm_word)
else: else:
raise NoInternetException() raise NoInternetException()

View File

@@ -1,3 +1,6 @@
from typing import Iterable, List
from bauh.api.abstract.model import SoftwarePackage
from bauh.view.qt.view_model import PackageView 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() hidden = not filters['name'] in pkg.model.name.lower()
return hidden 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

View File

@@ -5,7 +5,7 @@ import traceback
from datetime import datetime, timedelta from datetime import datetime, timedelta
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
from typing import List, Type, Set, Tuple, Optional from typing import List, Type, Set, Tuple, Optional, Iterable
import requests import requests
from PyQt5.QtCore import QThread, pyqtSignal, QObject 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 import timeshift
from bauh.view.core.config import CoreConfigManager from bauh.view.core.config import CoreConfigManager
from bauh.view.qt import commons 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.qt.view_model import PackageView, PackageViewStatus
from bauh.view.util.translation import I18n from bauh.view.util.translation import I18n
@@ -704,8 +705,7 @@ class SearchPackages(AsyncAction):
if self.word: if self.word:
try: try:
res = self.manager.search(words=self.word, disk_loader=None, limit=-1, is_url=False) 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'] = sort_packages((*(res.installed or ()), *(res.new or ())), self.word)
search_res['pkgs_found'].extend(res.new)
except NoInternetException: except NoInternetException:
search_res['error'] = 'internet.required' search_res['error'] = 'internet.required'
finally: finally:
@@ -951,48 +951,6 @@ class ApplyFilters(AsyncAction):
def stop_waiting(self): def stop_waiting(self):
self.wait_table_update = False 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): def run(self):
if self.pkgs: if self.pkgs:
pkgs_info = commons.new_pkgs_info() pkgs_info = commons.new_pkgs_info()
@@ -1004,7 +962,7 @@ class ApplyFilters(AsyncAction):
commons.apply_filters(pkgv, self.filters, pkgs_info, limit=not name_filtering) commons.apply_filters(pkgv, self.filters, pkgs_info, limit=not name_filtering)
if name_filtering and pkgs_info['pkgs_displayed']: if name_filtering and pkgs_info['pkgs_displayed']:
pkgs_info['pkgs_displayed'] = self._sort_by_word(word=self.filters['name'], pkgs_info['pkgs_displayed'] = sort_packages(word=self.filters['name'],
pkgs=pkgs_info['pkgs_displayed'], pkgs=pkgs_info['pkgs_displayed'],
limit=self.filters['display_limit']) limit=self.filters['display_limit'])

View File

@@ -32,6 +32,10 @@ class PackageView:
self.update_checked = model.update self.update_checked = model.update
self.status = PackageViewStatus.LOADING if model.status == PackageStatus.LOADING_DATA else PackageViewStatus.READY 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): def __repr__(self):
return '{} ({})'.format(self.model.name, self.get_type_label()) return '{} ({})'.format(self.model.name, self.get_type_label())