def request_install_missing_deps(pkgname: str, deps: List[Tuple[str, str]], watcher: ProcessWatcher, i18n: I18n) -> bool: msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format( name=bold(pkgname) if pkgname else '', deps=bold(str(len(deps))))) opts = [] sorted_deps = [*deps] sorted_deps.sort(key=lambda e: e[0]) for dep in sorted_deps: op = InputOption( '{} ( {}: {} )'.format(dep[0], i18n['repository'], dep[1].upper()), dep[0]) op.read_only = True op.icon_path = _get_mirror_icon(dep[1]) opts.append(op) comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts)) return watcher.request_confirmation( i18n['arch.missing_deps.title'], msg, [comp], confirmation_label=i18n['continue'].capitalize(), deny_label=i18n['cancel'].capitalize())
def show_dep_not_installed(watcher: ProcessWatcher, pkgname: str, depname: str, i18n: I18n): watcher.show_message( title=i18n['error'], body=i18n['arch.install.dependency.install.error'].format( bold(depname), bold(pkgname)), type_=MessageType.ERROR)
def show_deps_not_installed(watcher: ProcessWatcher, pkgname: str, depnames: Iterable[str], i18n: I18n): deps = ', '.join((bold(d) for d in depnames)) watcher.show_message( title=i18n['error'].capitalize(), body=i18n['arch.install.dependency.install.error'].format( deps, bold(pkgname)), type_=MessageType.ERROR)
def list_warnings(self, internet_available: bool) -> List[str]: if flatpak.is_installed(): if not flatpak.has_remotes_set(): return [ self.i18n['flatpak.notification.no_remotes'], self.i18n['flatpak.notification.disable'].format( bold('Flatpak'), bold(self.i18n['manage_window.settings.gems'])) ]
def install(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool: res, output = ProcessHandler(watcher).handle_simple( snap.install_and_stream(pkg.name, pkg.confinement, root_password)) if 'error:' in output: res = False if 'not available on stable' in output: channels = RE_AVAILABLE_CHANNELS.findall(output) if channels: opts = [ InputOption(label=c[0], value=c[1]) for c in channels ] channel_select = SingleSelectComponent( type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0]) body = '<p>{}.</p>'.format( self.i18n['snap.install.available_channels.message']. format(bold(self.i18n['stable']), bold(pkg.name))) body += '<p>{}:</p>'.format( self.i18n['snap.install.available_channels.help']) if watcher.request_confirmation( title=self. i18n['snap.install.available_channels.title'], body=body, components=[channel_select], confirmation_label=self.i18n['continue'], deny_label=self.i18n['cancel']): self.logger.info( "Installing '{}' with the custom command '{}'". format(pkg.name, channel_select.value)) res = ProcessHandler(watcher).handle( SystemProcess( new_root_subprocess( channel_select.value.value.split(' '), root_password=root_password))) if res: pkg.has_apps_field = snap.has_apps_field( pkg.name, self.ubuntu_distro) return res else: self.logger.error( "Could not find available channels in the installation output: {}" .format(output)) else: pkg.has_apps_field = snap.has_apps_field(pkg.name, self.ubuntu_distro) return res
def download(self, file_url: str, watcher: ProcessWatcher, output_path: str, cwd: str) -> bool: self.logger.info('Downloading {}'.format(file_url)) handler = ProcessHandler(watcher) file_name = file_url.split('/')[-1] final_cwd = cwd if cwd else '.' success = False ti = time.time() try: if output_path and os.path.exists(output_path): self.logger.info( 'Removing old file found before downloading: {}'.format( output_path)) os.remove(output_path) self.logger.info("Old file {} removed".format(output_path)) if self.is_multithreaded(): ti = time.time() process = self._get_aria2c_process(file_url, output_path, final_cwd) downloader = 'aria2c' else: ti = time.time() process = self._get_wget_process(file_url, output_path, final_cwd) downloader = 'wget' file_size = self.http_client.get_content_length(file_url) msg = bold('[{}] ').format( downloader) + self.i18n['downloading'] + ' ' + bold( file_url.split('/')[-1]) + (' ( {} )'.format(file_size) if file_size else '') watcher.change_substatus(msg) if isinstance(process, SimpleProcess): success = handler.handle_simple(process) else: success = handler.handle(process) except: traceback.print_exc() self._rm_bad_file(file_name, output_path, final_cwd) tf = time.time() self.logger.info(file_name + ' download took {0:.2f} minutes'.format((tf - ti) / 60)) if not success: self.logger.error("Could not download '{}'".format(file_name)) self._rm_bad_file(file_name, output_path, final_cwd) return success
def confirm_transaction(self, to_install: Optional[Collection[DebianPackage]], removal: Optional[Collection[DebianPackage]], watcher: ProcessWatcher) -> bool: components = [] to_remove_data = self._map_to_remove(removal) text_width, select_width = 672, 595 if to_remove_data: to_remove_data[0].sort(key=attrgetter('label')) lb_rem = self._i18n['debian.transaction.to_remove'].format( no=bold(str(len(to_remove_data[0]))), fspace=bold(to_remove_data[1])) components.append(TextComponent(html=lb_rem, min_width=text_width)) components.append( MultipleSelectComponent(id_='rem', options=to_remove_data[0], label=None, default_options={*to_remove_data[0]}, max_width=select_width)) to_install_data = self._map_to_install(to_install) if to_install_data: to_install_data[0].sort(key=attrgetter('label')) lb_deps = self._i18n['debian.transaction.to_install'].format( no=bold(str(len(to_install_data[0]))), dsize=bold(to_install_data[2]), isize=bold(to_install_data[1])) components.append( TextComponent(html=f'<br/>{lb_deps}', min_width=text_width)) components.append( MultipleSelectComponent(id_='inst', label='', options=to_install_data[0], default_options={*to_install_data[0]}, max_width=select_width)) return watcher.request_confirmation( title=self._i18n['debian.transaction.title'], components=components, confirmation_label=self._i18n['popup.button.continue'], deny_label=self._i18n['popup.button.cancel'], body=None, min_width=text_width, min_height=54)
def confirm_purge(self, pkg_name: str, watcher: ProcessWatcher) -> bool: msg = self._i18n['debian.action.purge.confirmation'].format( pkg=bold(pkg_name)) return watcher.request_confirmation( title=self._i18n['debian.action.purge'], body=msg, confirmation_label=self._i18n['popup.button.continue'])
def run(self): if self.create_config: self.taskman.update_progress( self.task_id, 0, self.i18n['task.waiting_task'].format( bold(self.create_config.task_name))) self.create_config.join() self.config = self.create_config.config ti = time.time() self.taskman.update_progress(self.task_id, 1, None) self.logger.info("Checking if suggestions should be downloaded") should_download = self.should_download(self.config) self.taskman.update_progress(self.task_id, 30, None) try: if should_download: suggestions_timestamp = datetime.utcnow().timestamp() suggestions_str = self.download() self.taskman.update_progress(self.task_id, 70, None) if suggestions_str: self.cache_suggestions(suggestions_str, suggestions_timestamp) else: self.logger.info("Cached suggestions are up-to-date") except: self.logger.error("An unexpected exception happened") traceback.print_exc() self.taskman.update_progress(self.task_id, 100, None) self.taskman.finish_task(self.task_id) tf = time.time() self.logger.info( "Took {0:.9f} seconds to download suggestions".format(tf - ti))
def run(self): ti = time.time() self._log.info("Begin: Debian applications indexation") self._taskman.update_progress( self._id, 1, self._i18n['task.waiting_task'].format( bold(self._i18n['debian.task.map_apps.status']))) self._mapping_apps.join() finish_msg = None if self._mapping_apps.cached: finish_msg = self._i18n['task.canceled'] else: status = self._i18n['debian.task.update_apps_idx.status'] self._taskman.update_progress(self._id, 50, status) self._change_substatus(status) try: self._indexer.update_index(self._mapping_apps.apps) except: finish_msg = self._i18n['error'] self._taskman.update_progress(self._id, 100, finish_msg) self._taskman.finish_task(self._id) tf = time.time() self._log.info( f"Finish: Debian applications indexation ({tf - ti:.4f} seconds)")
def request_providers(providers_map: Dict[str, Set[str]], repo_map: Dict[str, str], watcher: ProcessWatcher, i18n: I18n) -> Set[str]: msg = "<p>{}.</p><p>{}.</p>".format(i18n['arch.dialog.providers.line1'], i18n['arch.dialog.providers.line2']) repo_icon_path = get_repo_icon_path() aur_icon_path = get_icon_path() form = FormComponent([], label='') for dep, providers in providers_map.items(): opts = [] providers_list = [*providers] providers_list.sort() for p in providers_list: repo = repo_map.get(p, 'aur') opts.append(InputOption(label=p, value=p, icon_path=aur_icon_path if repo == 'aur' else repo_icon_path, tooltip='{}: {}'.format(i18n['repository'].capitalize(), repo))) form.components.append(SingleSelectComponent(label=bold(dep.lower()), options=opts, default_option=opts[0], type_=SelectViewType.COMBO, max_per_line=1)) if watcher.request_confirmation(title=i18n['arch.providers'].capitalize(), body=msg, components=[form], confirmation_label=i18n['proceed'].capitalize(), deny_label=i18n['cancel'].capitalize()): return {s.get_selected() for s in form.components}
def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: I18n) -> Set[str]: opts = [] for p, d in pkg_mirrors.items(): op = InputOption( '{}{} ( {}: {} )'.format(p, ': ' + d['desc'] if d['desc'] else '', i18n['repository'], d['mirror'].upper()), p) op.icon_path = _get_mirror_icon(d['mirror']) opts.append(op) view_opts = MultipleSelectComponent(label='', options=opts, default_options=set(opts)) install = watcher.request_confirmation( title=i18n['arch.install.optdeps.request.title'], body='<p>{}.</p><p>{}:</p>'.format( i18n['arch.install.optdeps.request.body'].format(bold(pkgname)), i18n['arch.install.optdeps.request.help']), components=[view_opts], confirmation_label=i18n['install'].capitalize(), deny_label=i18n['do_not.install'].capitalize()) if install: return {o.value for o in view_opts.values}
def confirm_missing_deps(deps: Collection[Tuple[str, str]], watcher: ProcessWatcher, i18n: I18n) -> bool: opts = [] total_isize, total_dsize = None, None pkgs_data = pacman.map_updates_data(pkgs=tuple(d[0] for d in deps if d[1] != 'aur'), description=True) or dict() for dep in deps: ver, desc, isize, dsize = None, None, None, None data = pkgs_data.get(dep[0]) if data: desc, isize, dsize = (data.get(f) for f in ('des', 's', 'ds')) if isize is not None: if total_isize is None: total_isize = 0 total_isize += isize if dsize is not None: if total_dsize is None: total_dsize = 0 total_dsize += dsize label = f"{dep[0]} | " \ f"{i18n['size'].capitalize()}: {get_human_size_str(isize) if isize is not None else '?'}" \ f"{' ({}: {})'.format(i18n['download'].capitalize(), get_human_size_str(dsize)) if dsize else ''}" op = InputOption(label=label, value=dep[0], tooltip=desc) op.read_only = True op.icon_path = _get_repo_icon(dep[1]) opts.append(op) comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts)) body = StringIO() body.write('<p>') body.write(i18n['arch.missing_deps.body'].format(deps=bold(str(len(deps))))) if total_isize is not None or total_dsize is not None: body.write(' (') if total_isize is not None: body.write(f"{i18n['size'].capitalize()}: {bold(get_human_size_str(total_isize))} | ") if total_dsize is not None: body.write(f"{i18n['download'].capitalize()}: {bold(get_human_size_str(total_dsize))}") body.write(')') body.write(':</p>') return watcher.request_confirmation(title=i18n['arch.missing_deps.title'], body=body.getvalue(), components=[comp], confirmation_label=i18n['continue'].capitalize(), deny_label=i18n['cancel'].capitalize(), min_width=625)
def show_dep_not_found(depname: str, i18n: I18n, watcher: ProcessWatcher): body = '<p>{}</p><p>{}</p><p></p><p>{}</p>'.format( i18n['arch.install.dep_not_found.body.l1'].format(bold(depname)), i18n['arch.install.dep_not_found.body.l2'], i18n['arch.install.dep_not_found.body.l3']) watcher.show_message( title=i18n['arch.install.dep_not_found.title'].capitalize(), body=body, type_=MessageType.ERROR)
def custom_action(): if action.i18n_confirm_key: body = self.i18n[action.i18n_confirm_key].format( bold(pkg.model.name)) else: body = '{} ?'.format(self.i18n[action.i18n_label_key]) if dialog.ask_confirmation(title=self.i18n[action.i18n_label_key], body=self._parag(body), i18n=self.i18n): self.window.begin_execute_custom_action(pkg, action)
def custom_action(): if action.i18n_confirm_key: body = self.i18n[action.i18n_confirm_key].format(bold(pkg.model.name)) else: body = '{} ?'.format(self.i18n[action.i18n_label_key]) if ConfirmationDialog(icon=QIcon(pkg.model.get_type_icon_path()), title=self.i18n[action.i18n_label_key], body=self._parag(body), i18n=self.i18n).ask(): self.window.begin_execute_custom_action(pkg, action)
def confirm_removal(self, source_pkg: str, dependencies: Collection[DebianPackage], watcher: ProcessWatcher) -> bool: dep_views = [] freed_space = 0 for p in sorted(dependencies, key=attrgetter('name')): if p.transaction_size is not None: size = p.transaction_size * (-1 if p.transaction_size < 0 else 1) freed_space += size size_str = get_human_size_str(size) else: size_str = '?' dep_views.append( InputOption(label=f"{p.name}: -{size_str}", value=p.name, read_only=True, icon_path=DEBIAN_ICON_PATH, tooltip=p.description)) deps_container = MultipleSelectComponent(id_='deps', label='', options=dep_views, default_options={*dep_views}, max_width=537) freed_space_str = bold('-' + get_human_size_str(freed_space)) body_text = TextComponent(html=self._i18n['debian.remove_deps'].format( no=bold(str(len(dependencies))), pkg=bold(source_pkg), fspace=freed_space_str), min_width=653) return watcher.request_confirmation( title=self._i18n['debian.transaction.title'], components=[body_text, deps_container], confirmation_label=self._i18n['popup.button.continue'], deny_label=self._i18n['popup.button.cancel'], min_height=200, body=None)
def list_warnings(self, internet_available: bool) -> List[str]: dbfiles = glob.glob('{}/*.db'.format(APPIMAGE_CACHE_PATH)) if not dbfiles or len({ f for f in (DATABASE_APPS_FILE, DATABASE_RELEASES_FILE) if f in dbfiles }) != 2: return [ self.i18n['appimage.warning.missing_db_files'].format( appimage=bold('AppImage')) ]
def _generate_backup(self, app_config: dict, i18n: I18n, root_password: str) -> bool: if timeshift.is_available(): if app_config['backup']['mode'] not in ('only_one', 'incremental'): self.show_message(title=self.i18n['error'].capitalize(), body='{}: {}'.format( self.i18n['action.backup.invalid_mode'], bold(app_config['backup']['mode'])), type_=MessageType.ERROR) return False if not user.is_root() and not root_password: root_pwd, valid = self.request_root_password() else: root_pwd, valid = root_password, True if not valid: return False handler = ProcessHandler(self) if app_config['backup']['mode'] == 'only_one': self.change_substatus('[{}] {}'.format( i18n['core.config.tab.backup'].lower(), i18n['action.backup.substatus.delete'])) deleted, _ = handler.handle_simple( timeshift.delete_all_snapshots(root_pwd)) if not deleted and not self.request_confirmation( title=i18n['core.config.tab.backup'], body='{}. {}'.format( i18n['action.backup.error.delete'], i18n['action.backup.error.proceed']), confirmation_label=i18n['yes'].capitalize(), deny_label=i18n['no'].capitalize()): return False self.change_substatus('[{}] {}'.format( i18n['core.config.tab.backup'].lower(), i18n['action.backup.substatus.create'])) created, _ = handler.handle_simple( timeshift.create_snapshot(root_pwd, app_config['backup']['type'])) if not created and not self.request_confirmation( title=i18n['core.config.tab.backup'], body='{}. {}'.format(i18n['action.backup.error.create'], i18n['action.backup.error.proceed']), confirmation_label=i18n['yes'].capitalize(), deny_label=i18n['no'].capitalize()): return False return True