[ui][update] displaying requirements and upgrade order

This commit is contained in:
Vinicius Moreira
2020-02-10 12:42:53 -03:00
parent 684ff621d0
commit 3e956a5413
11 changed files with 188 additions and 56 deletions

View File

@@ -59,7 +59,7 @@ class ArchManager(SoftwareManager):
self.aur_cache = context.cache_factory.new()
# context.disk_loader_factory.map(ArchPackage, self.aur_cache) TODO
self.mapper = ArchDataMapper(http_client=context.http_client)
self.mapper = ArchDataMapper(http_client=context.http_client, i18n=context.i18n)
self.i18n = context.i18n
self.aur_client = AURClient(context.http_client, context.logger)
self.dcache_updater = ArchDiskCacheUpdater(context.logger, context.disk_cache)
@@ -157,7 +157,7 @@ class ArchManager(SoftwareManager):
for name, data in not_signed.items():
pkg = ArchPackage(name=name, version=data.get('version'),
latest_version=data.get('version'), description=data.get('description'),
installed=True, mirror='aur')
installed=True, mirror='aur', i18n=self.i18n)
pkg.categories = self.categories.get(pkg.name)
pkg.downgrade_enabled = downgrade_enabled
@@ -171,7 +171,7 @@ class ArchManager(SoftwareManager):
def _fill_mirror_pkgs(self, mirrors: dict, apps: list):
# TODO
for name, data in mirrors.items():
app = ArchPackage(name=name, version=data.get('version'), latest_version=data.get('version'), description=data.get('description'))
app = ArchPackage(name=name, version=data.get('version'), latest_version=data.get('version'), description=data.get('description'), i18n=self.i18n)
app.installed = True
app.mirror = '' # TODO
app.update = False # TODO
@@ -304,7 +304,7 @@ class ArchManager(SoftwareManager):
def get_info(self, pkg: ArchPackage) -> dict:
if pkg.installed:
t = Thread(target=self.mapper.fill_package_build, args=(pkg,))
t = Thread(target=self.mapper.fill_package_build, args=(pkg,), daemon=True)
t.start()
info = pacman.get_info_dict(pkg.name)
@@ -407,7 +407,7 @@ class ArchManager(SoftwareManager):
pkgbase = self.aur_client.get_src_info(dep[0])['pkgbase']
installed = self._install_from_aur(pkgname=dep[0], pkgbase=pkgbase, maintainer=None, root_password=root_password, handler=handler, dependency=True, change_progress=False)
else:
installed = self._install(pkgname=dep[0], maintainer=None, root_password=root_password, handler=handler, install_file=None, mirror=dep[1], change_progress=False)
installed = self._install(pkgname=dep[0], maintainer=dep[1], root_password=root_password, handler=handler, install_file=None, mirror=dep[1], change_progress=False)
if not installed:
return dep[0]
@@ -737,6 +737,8 @@ class ArchManager(SoftwareManager):
return False
def _install_from_aur(self, pkgname: str, pkgbase: str, maintainer: str, root_password: str, handler: ProcessHandler, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
self._optimize_makepkg(watcher=handler.watcher)
app_build_dir = '{}/build_{}'.format(BUILD_DIR, int(time.time()))
try:
@@ -831,9 +833,11 @@ class ArchManager(SoftwareManager):
handler = ProcessHandler(watcher)
self._sync_databases(root_password=root_password, handler=handler)
self._optimize_makepkg(watcher=watcher)
res = self._install_from_aur(pkg.name, pkg.package_base, pkg.maintainer, root_password, handler, dependency=False, skip_optdeps=skip_optdeps)
if pkg.mirror == 'aur':
res = self._install_from_aur(pkg.name, pkg.package_base, pkg.maintainer, root_password, handler, dependency=False, skip_optdeps=skip_optdeps)
else:
res = self._install(pkgname=pkg.name, maintainer=pkg.mirror, root_password=root_password, handler=handler, install_file=None, mirror=pkg.mirror, change_progress=False)
if res:
if os.path.exists(pkg.get_disk_data_path()):
@@ -1017,7 +1021,7 @@ class ArchManager(SoftwareManager):
threads = []
for pkg in pkgs:
t = Thread(target=_add_info, args=(pkg, ))
t = Thread(target=_add_info, args=(pkg, ), daemon=True)
t.start()
threads.append(t)
@@ -1082,38 +1086,45 @@ class ArchManager(SoftwareManager):
return sorted_names[pkg.name]
def _map_and_add_package(self, pkg_data: Tuple[str, str], idx: int, output: dict):
version = None
if pkg_data[1] == 'aur':
try:
info = self.aur_client.get_src_info(pkg_data[0])
if info:
version = info.get('pkgver')
if not version:
self.logger.warning("No version declared in SRCINFO of '{}'".format(pkg_data[0]))
else:
self.logger.warning("Could not retrieve the SRCINFO for '{}'".format(pkg_data[0]))
except:
self.logger.warning("Could not retrieve the SRCINFO for '{}'".format(pkg_data[0]))
else:
version = pacman.get_version_for_not_installed(pkg_data[0])
output[idx] = ArchPackage(name=pkg_data[0], version=version, latest_version=version, mirror=pkg_data[1], i18n=self.i18n)
def get_update_requirements(self, pkgs: List[ArchPackage], watcher: ProcessWatcher) -> List[ArchPackage]:
deps = self._map_known_missing_deps({p.get_base_name(): 'aur' for p in pkgs}, watcher)
if deps: # filtering selected packages
selected_names = {p.name for p in pkgs}
deps = [dep for dep in deps if dep[0] not in selected_names]
if deps:
pkg_names = {p.name for p in pkgs}
return [ArchPackage(name=pkg[0]) for pkg in deps if pkg[0] not in pkg_names]
map_threads, sorted_pkgs = [], {}
for idx, dep in enumerate(deps):
t = Thread(target=self._map_and_add_package, args=(dep, idx, sorted_pkgs), daemon=True)
t.start()
map_threads.append(t)
for t in map_threads:
t.join()
return [sorted_pkgs[idx] for idx in sorted(sorted_pkgs)]
else:
return []
# def add_srcinfo(pkg: ArchPackage):
# try:
# srcinfo = self.aur_client.get_src_info(pkg.get_base_name())
# pkg.src_info = srcinfo
# pkg.dependencies = self.aur_client.extract_required_dependencies(pkg.src_info)
# except:
# self.logger.warning("Could not retrieve the SRCINFO for package {}".format(pkg.name))
#
# threads = []
#
# for p in pkgs:
# t = Thread(target=add_srcinfo, args=(p,))
# t.start()
# threads.append(t)
#
# for t in threads:
# t.join()
#
# for p in pkgs:
# if p.src_info and p.dependencies:
# self.inst
# else:
# self.logger.warning("Not checking update requirements for package {}".format(pkg.name))

View File

@@ -4,6 +4,7 @@ from datetime import datetime
from bauh.api.abstract.model import PackageStatus
from bauh.api.http import HttpClient
from bauh.gems.arch.model import ArchPackage
from bauh.view.util.translation import I18n
URL_PKG_DOWNLOAD = 'https://aur.archlinux.org/{}'
RE_LETTERS = re.compile(r'\.([a-zA-Z]+)-\d+$')
@@ -22,8 +23,9 @@ V_SUFFIX_MAP = {s: {'c': sfxs[0], 'p': idx} for idx, sfxs in enumerate([RE_SFX,
class ArchDataMapper:
def __init__(self, http_client: HttpClient):
def __init__(self, http_client: HttpClient, i18n: I18n):
self.http_client = http_client
self.i18n = i18n
def fill_api_data(self, pkg: ArchPackage, package: dict, fill_version: bool = True):
@@ -115,7 +117,7 @@ class ArchDataMapper:
def map_api_data(self, apidata: dict, installed: dict, categories: dict) -> ArchPackage:
data = installed.get(apidata.get('Name'))
app = ArchPackage(name=apidata.get('Name'), installed=bool(data), mirror='aur')
app = ArchPackage(name=apidata.get('Name'), installed=bool(data), mirror='aur', i18n=self.i18n)
app.status = PackageStatus.LOADING_DATA
if categories:

View File

@@ -4,6 +4,7 @@ from typing import List, Set
from bauh.api.abstract.model import SoftwarePackage
from bauh.commons import resource
from bauh.gems.arch import ROOT_DIR, ARCH_CACHE_PATH
from bauh.view.util.translation import I18n
CACHED_ATTRS = {'command', 'icon_path', 'mirror', 'maintainer', 'desktop_entry', 'categories'}
@@ -14,7 +15,8 @@ class ArchPackage(SoftwarePackage):
package_base: str = None, votes: int = None, popularity: float = None,
first_submitted: datetime.datetime = None, last_modified: datetime.datetime = None,
maintainer: str = None, url_download: str = None, pkgbuild: str = None, mirror: str = None,
desktop_entry: str = None, installed: bool = False, srcinfo: dict = None, dependencies: Set[str] = None):
desktop_entry: str = None, installed: bool = False, srcinfo: dict = None, dependencies: Set[str] = None,
i18n: I18n = None):
super(ArchPackage, self).__init__(name=name, version=version, latest_version=latest_version, description=description, installed=installed)
self.package_base = package_base
@@ -32,6 +34,7 @@ class ArchPackage(SoftwarePackage):
self.desktop_entry = desktop_entry
self.src_info = srcinfo
self.dependencies = dependencies
self.i18n = i18n
@staticmethod
def disk_cache_path(pkgname: str, mirror: str):
@@ -63,7 +66,7 @@ class ArchPackage(SoftwarePackage):
return self.icon_path
def get_type_icon_path(self):
return resource.get_path('img/arch.svg', ROOT_DIR) # TODO change icon when from mirrors
return resource.get_path('img/{}.svg'.format('arch' if self.mirror == 'aur' else 'mirror'), ROOT_DIR)
def is_application(self):
return self.can_be_run()
@@ -121,6 +124,9 @@ class ArchPackage(SoftwarePackage):
def has_screenshots(self) -> bool:
return False
def get_name_tooltip(self) -> str:
return '{}: {}'.format(self.i18n['repository'], self.mirror)
def __str__(self):
return self.__repr__()

View File

@@ -359,3 +359,10 @@ def read_dependencies(name: str) -> Set[str]:
def sync_databases(root_password: str, force: bool = False) -> SimpleProcess:
return SimpleProcess(cmd=['pacman', '-Sy{}'.format('y' if force else '')],
root_password=root_password)
def get_version_for_not_installed(pkgname: str) -> str:
output = run_cmd('pacman -Ss {}'.format(pkgname), print_error=False)
if output:
return output.split('\n')[0].split(' ')[1].strip()

View File

@@ -1,3 +1,4 @@
import os
import re
import time
from datetime import datetime, timedelta
@@ -10,7 +11,7 @@ from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.controller import SoftwareManager
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import PackageStatus, SoftwarePackage, PackageAction
from bauh.api.abstract.view import InputViewComponent, MessageType
from bauh.api.abstract.view import InputViewComponent, MessageType, MultipleSelectComponent, InputOption
from bauh.api.exception import NoInternetException
from bauh.view.core import config
from bauh.view.qt import commons
@@ -98,6 +99,69 @@ class UpdateSelectedApps(AsyncAction):
self.root_password = None
self.i18n = i18n
def _pkg_as_option(self, pkg: SoftwarePackage, tooltip: bool = True) -> InputOption:
if pkg.installed:
icon_path = pkg.get_disk_icon_path()
if not os.path.isfile(icon_path):
icon_path = pkg.get_type_icon_path()
else:
icon_path = pkg.get_type_icon_path()
return InputOption(label='{}{}'.format(pkg.name, ' ( {} )'.format(pkg.latest_version) if pkg.version else ''),
value=None,
tooltip=pkg.get_name_tooltip() if tooltip else None,
read_only=True,
icon_path=icon_path)
def _handle_update_requirements(self, pkgs: List[SoftwarePackage]) -> bool:
self.change_substatus(self.i18n['action.update.requirements.status'])
required_pkgs = self.manager.get_update_requirements(pkgs, self)
if required_pkgs:
opts = [self._pkg_as_option(p) for p in required_pkgs]
if not self.request_confirmation(self.i18n['action.update.requirements.title'],
self.i18n['action.update.requirements.body'],
[MultipleSelectComponent(label='', options=opts, default_options=set(opts))],
confirmation_label=self.i18n['continue'].capitalize(),
deny_label=self.i18n['cancel'].capitalize()):
self.notify_finished({'success': False, 'updated': 0, 'types': set()})
self.pkgs = None
return False
else:
for pkg in required_pkgs:
if not self.manager.install(pkg, self.root_password, self):
self.notify_finished({'success': False, 'updated': 0, 'types': set()})
self.pkgs = None
label = '{}{}'.format(pkg.name, ' ( {} )'.format(pkg.version) if pkg.version else '')
self.show_message(title=self.i18n['action.update.install_req.fail.title'],
body=self.i18n['action.update.install_req.fail.body'].format(label),
type_=MessageType.ERROR)
return False
return True
def _sort_packages(self, pkgs: List[SoftwarePackage], app_config: dict) -> Tuple[bool, List[SoftwarePackage]]:
if bool(app_config['updates']['sort_packages']):
self.change_substatus(self.i18n['action.update.status.sorting'])
sorted_pkgs = self.manager.sort_update_order([view.model for view in self.pkgs])
else:
sorted_pkgs = pkgs
if len(sorted_pkgs) > 1:
opts = [self._pkg_as_option(p, tooltip=False) for p in sorted_pkgs]
proceed = self.request_confirmation(title=self.i18n['action.update.order.title'],
body=self.i18n['action.update.order.body'] + ':',
components=[MultipleSelectComponent(label='', options=opts, default_options=set(opts))],
confirmation_label=self.i18n['continue'].capitalize(),
deny_label=self.i18n['cancel'].capitalize())
else:
proceed = True
return proceed, sorted_pkgs
def run(self):
success = False
@@ -108,16 +172,16 @@ class UpdateSelectedApps(AsyncAction):
app_config = config.read_config()
models = [view.model for view in self.pkgs]
required_pkgs = self.manager.get_update_requirements(models, self)
# TODO stopped here: show required packages
if bool(app_config['updates']['sort_packages']):
self.change_substatus(self.i18n['action.update.status.sorting'])
sorted_pkgs = self.manager.sort_update_order()
else:
sorted_pkgs = models
if not self._handle_update_requirements(models):
return
# TODO show sort order
proceed, sorted_pkgs = self._sort_packages(models, app_config)
if not proceed:
self.notify_finished({'success': success, 'updated': updated, 'types': updated_types})
self.pkgs = None
return
for pkg in sorted_pkgs:
self.change_substatus('')

View File

@@ -268,4 +268,11 @@ interval=interval
installation=instal·lació
download=download
clean=netejar
action.update.status.sorting=Determinant el millor ordre dactualització
action.update.status.sorting=Determinant el millor ordre dactualització
action.update.requirements.title=Requisits dactualització
action.update.requirements.body=Abans dactualitzar cal instal·lar les següents aplicacions / paquets
action.update.install_req.fail.title=Ha fallat la instal·lació
action.update.install_req.fail.body=No s'ha pogut instal·lar {}
action.update.requirements.status=Verificant els requisits
action.update.order.title=Ordre d'actualització
action.update.order.body=L'actualització es realitzarà en l'ordre següent

View File

@@ -223,4 +223,11 @@ interval=intervall
installation=Installation
download=download
clean=reinigen
action.update.status.sorting=Determining the best update order
action.update.status.sorting=Determining the best update order
action.update.requirements.title=Upgrade requirements
action.update.requirements.body=The following applications / packages must be installed before upgrading
action.update.install_req.fail.title=Installation failed
action.update.install_req.fail.body=It was not possible to install {}
action.update.requirements.status=Checking requirements
action.update.order.title=Upgrade order
action.update.order.body=The upgrade will be performed in the following order

View File

@@ -230,4 +230,11 @@ interval=interval
installation=installation
download=download
clean=clean
action.update.status.sorting=Determining the best update order
action.update.status.sorting=Determining the best update order
action.update.requirements.title=Upgrade requirements
action.update.requirements.body=The following applications / packages must be installed before upgrading
action.update.install_req.fail.title=Installation failed
action.update.install_req.fail.body=It was not possible to install {}
action.update.requirements.status=Checking requirements
action.update.order.title=Upgrade order
action.update.order.body=The upgrade will be performed in the following order

View File

@@ -271,4 +271,11 @@ interval=intervalo
installation=instalación
download=descarga
clean=limpiar
action.update.status.sorting=Determinando el mejor orden de actualización
action.update.status.sorting=Determinando el mejor orden de actualización
action.update.requirements.title=Requisitos de actualización
action.update.requirements.body=Las siguientes aplicaciones / paquetes deben estar instalados antes de actualizar
action.update.install_req.fail.title=Falló la instalación
action.update.install_req.fail.body=No fue posible instalar {}
action.update.requirements.status=Verificando los requisitos
action.update.order.title=Orden de actualización
action.update.order.body=La actualización se realizará en el siguiente orden

View File

@@ -223,4 +223,11 @@ interval=intervallo
installation=installazione
download=download
clean=pulire
action.update.status.sorting=Sto determinando il miglior ordine di aggiornamento
action.update.status.sorting=Sto determinando il miglior ordine di aggiornamento
action.update.requirements.title=Requisiti di aggiornamento
action.update.requirements.body=Le seguenti applicazioni / pacchetti devono essere installati prima dell'aggiornamento
action.update.install_req.fail.title=Installazione non riuscita
action.update.install_req.fail.body=Non è stato possibile installare {}
action.update.requirements.status=Verificando i requisiti
action.update.order.title=Ordine di aggiornamento
action.update.order.body=L'aggiornamento verrà eseguito nel seguente ordine

View File

@@ -274,4 +274,11 @@ interval=intervalo
installation=instalação
download=download
clean=limpar
action.update.status.sorting=Determinando a melhor ordem de atualização
action.update.status.sorting=Determinando a melhor ordem de atualização
action.update.requirements.title=Requisitos de atualização
action.update.requirements.body=Os seguintes aplicativos / pacotes precisam ser instalados antes de atualizar
action.update.install_req.fail.title=Instalação falhou
action.update.install_req.fail.body=Não foi possível instalar {}
action.update.requirements.status=Verificando requisitos
action.update.order.title=Ordem de atualização
action.update.order.body=A atualização será realizada na seguinte ordem