This commit is contained in:
Vinícius Moreira
2020-04-13 11:49:28 -03:00
parent 83aab1ff55
commit 01a60ea686
165 changed files with 12778 additions and 6171 deletions

View File

@@ -1,6 +1,3 @@
import logging
import os
import traceback
from pathlib import Path
import yaml
@@ -21,17 +18,12 @@ def read_config(update_file: bool = False) -> dict:
},
'locale': None,
'updates': {
'check_interval': 30,
'sort_packages': True,
"pre_dependency_checking": True
'check_interval': 30
},
'system': {
'notifications': True,
'single_dependency_checking': False
},
'disk_cache': {
'enabled': True
},
'suggestions': {
'enabled': True,
'by_type': 10
@@ -52,7 +44,20 @@ def read_config(update_file: bool = False) -> dict:
'download': {
'multithreaded': True,
'icons': True
},
'store_root_password': True,
'disk': {
'trim_after_update': False
},
'backup': {
'enabled': True,
'install': None,
'uninstall': None,
'downgrade': None,
'upgrade': None,
'mode': 'incremental'
}
}
return read(FILE_PATH, default, update_file=update_file, update_async=True)
@@ -62,14 +67,3 @@ def save(config: dict):
with open(FILE_PATH, 'w+') as f:
f.write(yaml.safe_dump(config))
def remove_old_config(logger: logging.Logger):
old_file = FILE_PATH.replace('.yml', '.json')
if os.path.exists(old_file):
try:
os.remove(old_file)
logger.info('Old configuration file {} deleted'.format(old_file))
except:
logger.error('Could not delete the old configuration file {}'.format(old_file))
traceback.print_exc()

View File

@@ -1,21 +1,38 @@
import os
import re
import time
import traceback
from pathlib import Path
from threading import Thread
from typing import List, Set, Type, Tuple
from typing import List, Set, Type, Tuple, Dict
from bauh.api.abstract.controller import SoftwareManager, SearchResult, ApplicationContext
from bauh import __app_name__, __version__
from bauh.api.abstract.controller import SoftwareManager, SearchResult, ApplicationContext, UpgradeRequirements, \
UpgradeRequirement
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHistory, PackageSuggestion, PackageAction
from bauh.api.abstract.handler import ProcessWatcher, TaskManager
from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHistory, PackageSuggestion, \
CustomSoftwareAction
from bauh.api.abstract.view import ViewComponent, TabGroupComponent
from bauh.api.constants import CACHE_PATH
from bauh.api.exception import NoInternetException
from bauh.commons import internet
from bauh.commons.html import bold, link
from bauh.view.core.settings import GenericSettingsManager
RE_IS_URL = re.compile(r'^https?://.+')
class GenericUpgradeRequirements(UpgradeRequirements):
def __init__(self, to_install: List[UpgradeRequirement], to_remove: List[UpgradeRequirement],
to_upgrade: List[UpgradeRequirement], cannot_upgrade: List[SoftwarePackage],
sub_requirements: Dict[SoftwareManager, UpgradeRequirements]):
super(GenericUpgradeRequirements, self).__init__(to_install=to_install, to_upgrade=to_upgrade,
to_remove=to_remove, cannot_upgrade=cannot_upgrade)
self.sub_requirements = sub_requirements
class GenericSoftwareManager(SoftwareManager):
def __init__(self, managers: List[SoftwareManager], context: ApplicationContext, config: dict,
@@ -32,6 +49,7 @@ class GenericSoftwareManager(SoftwareManager):
self.working_managers = []
self.config = config
self.settings_manager = settings_manager
self.http_client = context.http_client
def reset_cache(self):
if self._available_cache is not None:
@@ -100,7 +118,7 @@ class GenericSoftwareManager(SoftwareManager):
res = SearchResult([], [], 0)
if internet.is_available(self.context.http_client, self.context.logger):
if internet.is_available():
norm_word = word.strip().lower()
url_words = RE_IS_URL.match(norm_word)
@@ -142,26 +160,15 @@ class GenericSoftwareManager(SoftwareManager):
def can_work(self) -> bool:
return True
def _is_internet_available(self, res: dict):
res['available'] = internet.is_available(self.context.http_client, self.context.logger)
def _get_internet_check(self, res: dict) -> Thread:
t = Thread(target=self._is_internet_available, args=(res,))
t.start()
return t
def read_installed(self, disk_loader: DiskCacheLoader = None, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, net_check: bool = None) -> SearchResult:
def read_installed(self, disk_loader: DiskCacheLoader = None, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None) -> SearchResult:
ti = time.time()
self._wait_to_be_ready()
net_check = {}
thread_internet_check = self._get_internet_check(net_check)
res = SearchResult([], None, 0)
disk_loader = None
internet_available = None
net_available = internet.is_available()
if not pkg_types: # any type
for man in self.managers:
if self._can_work(man):
@@ -169,12 +176,8 @@ class GenericSoftwareManager(SoftwareManager):
disk_loader = self.disk_loader_factory.new()
disk_loader.start()
if internet_available is None:
thread_internet_check.join()
internet_available = net_check.get('available', True)
mti = time.time()
man_res = man.read_installed(disk_loader=disk_loader, pkg_types=None, internet_available=internet_available)
man_res = man.read_installed(disk_loader=disk_loader, pkg_types=None, internet_available=net_available)
mtf = time.time()
self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(mtf - mti))
@@ -191,12 +194,8 @@ class GenericSoftwareManager(SoftwareManager):
disk_loader = self.disk_loader_factory.new()
disk_loader.start()
if internet_available is None:
thread_internet_check.join()
internet_available = net_check.get('available', True)
mti = time.time()
man_res = man.read_installed(disk_loader=disk_loader, pkg_types=None, internet_available=internet_available)
man_res = man.read_installed(disk_loader=disk_loader, pkg_types=None, internet_available=net_available)
mtf = time.time()
self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(mtf - mti))
@@ -229,11 +228,14 @@ class GenericSoftwareManager(SoftwareManager):
if man:
return man.clean_cache_for(app)
def update(self, app: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool:
man = self._get_manager_for(app)
def upgrade(self, requirements: GenericUpgradeRequirements, root_password: str, handler: ProcessWatcher) -> bool:
for man, man_reqs in requirements.sub_requirements.items():
res = man.upgrade(man_reqs, root_password, handler)
if man:
return man.update(app, root_password, handler)
if not res:
return False
return True
def uninstall(self, app: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool:
man = self._get_manager_for(app)
@@ -283,56 +285,100 @@ class GenericSoftwareManager(SoftwareManager):
return man if man and self._can_work(man) else None
def cache_to_disk(self, pkg: SoftwarePackage, icon_bytes: bytes, only_icon: bool):
if self.context.disk_cache and pkg.supports_disk_cache():
if pkg.supports_disk_cache():
man = self._get_manager_for(pkg)
if man:
return man.cache_to_disk(pkg, icon_bytes=icon_bytes, only_icon=only_icon)
def requires_root(self, action: str, app: SoftwarePackage):
man = self._get_manager_for(app)
def requires_root(self, action: str, app: SoftwarePackage) -> bool:
if app is None:
if self.managers:
for man in self.managers:
if self._can_work(man):
if man.requires_root(action, app):
return True
return False
else:
man = self._get_manager_for(app)
if man:
return man.requires_root(action, app)
if man:
return man.requires_root(action, app)
def _prepare(self):
def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool):
if self.managers:
internet_on = internet.is_available()
for man in self.managers:
if man not in self._already_prepared and self._can_work(man):
man.prepare()
if task_manager:
man.prepare(task_manager, root_password, internet_on)
self._already_prepared.append(man)
def prepare(self):
self.thread_prepare = Thread(target=self._prepare)
self.thread_prepare.start()
def list_updates(self, net_check: bool = None) -> List[PackageUpdate]:
def list_updates(self, internet_available: bool = None) -> List[PackageUpdate]:
self._wait_to_be_ready()
updates = []
if self.managers:
net_check = {}
thread_internet_check = self._get_internet_check(net_check)
net_available = internet.is_available()
for man in self.managers:
if self._can_work(man):
if thread_internet_check.is_alive():
thread_internet_check.join()
man_updates = man.list_updates(internet_available=net_check['available'])
man_updates = man.list_updates(internet_available=net_available)
if man_updates:
updates.extend(man_updates)
return updates
def _check_for_updates(self) -> str:
self.logger.info("Checking for updates")
try:
releases = self.http_client.get_json('https://api.github.com/repos/vinifmor/bauh/releases')
if releases:
latest = None
for r in releases:
if not r['draft']:
latest = r
break
if latest and latest.get('tag_name'):
notifications_dir = '{}/updates'.format(CACHE_PATH)
release_file = '{}/{}'.format(notifications_dir, latest['tag_name'])
if os.path.exists(release_file):
self.logger.info("Release {} already notified".format(latest['tag_name']))
elif latest['tag_name'] > __version__:
try:
Path(notifications_dir).mkdir(parents=True, exist_ok=True)
with open(release_file, 'w+') as f:
f.write('')
except:
self.logger.error("An error occurred while trying to create the update notification file: {}".format(release_file))
return self.i18n['warning.update_available'].format(bold(__app_name__), bold(latest['tag_name']), link(latest.get('html_url', '?')))
else:
self.logger.info("No updates available")
else:
self.logger.warning("No official release found")
else:
self.logger.warning("No releases returned from the GitHub API")
except:
self.logger.error("An error occurred while trying to retrieve the current releases")
def list_warnings(self, internet_available: bool = None) -> List[str]:
warnings = []
if self.managers:
int_available = internet.is_available(self.context.http_client, self.context.logger)
int_available = internet.is_available()
if int_available:
updates_msg = self._check_for_updates()
if updates_msg:
warnings.append(updates_msg)
if self.managers:
for man in self.managers:
if man.is_enabled():
man_warnings = man.list_warnings(internet_available=int_available)
@@ -360,7 +406,7 @@ class GenericSoftwareManager(SoftwareManager):
def list_suggestions(self, limit: int, filter_installed: bool) -> List[PackageSuggestion]:
if bool(self.config['suggestions']['enabled']):
if self.managers and internet.is_available(self.context.http_client, self.context.logger):
if self.managers and internet.is_available():
suggestions, threads = [], []
for man in self.managers:
t = Thread(target=self._fill_suggestions, args=(suggestions, man, int(self.config['suggestions']['by_type']), filter_installed))
@@ -376,11 +422,11 @@ class GenericSoftwareManager(SoftwareManager):
return suggestions
return []
def execute_custom_action(self, action: PackageAction, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher):
man = self._get_manager_for(pkg)
def execute_custom_action(self, action: CustomSoftwareAction, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher):
man = action.manager if action.manager else self._get_manager_for(pkg)
if man:
return exec('man.{}(pkg=pkg, root_password=root_password, watcher=watcher)'.format(action.manager_method))
return eval('man.{}({}root_password=root_password, watcher=watcher)'.format(action.manager_method, 'pkg=pkg, ' if pkg else ''))
def is_default_enabled(self) -> bool:
return True
@@ -418,9 +464,12 @@ class GenericSoftwareManager(SoftwareManager):
def save_settings(self, component: TabGroupComponent) -> Tuple[bool, List[str]]:
return self.settings_manager.save_settings(component)
def sort_update_order(self, pkgs: List[SoftwarePackage]) -> List[SoftwarePackage]:
def _map_pkgs_by_manager(self, pkgs: List[SoftwarePackage], pkg_filters: list = None) -> Dict[SoftwareManager, List[SoftwarePackage]]:
by_manager = {}
for pkg in pkgs:
if pkg_filters and not all((1 for f in pkg_filters if f(pkg))):
continue
man = self._get_manager_for(pkg)
if man:
@@ -432,42 +481,75 @@ class GenericSoftwareManager(SoftwareManager):
man_pkgs.append(pkg)
sorted_list = []
return by_manager
if by_manager:
for man, pkgs in by_manager.items():
if len(pkgs) > 1:
ti = time.time()
sorted_list.extend(man.sort_update_order(pkgs))
tf = time.time()
self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(tf - ti))
else:
self.logger.info("Only one package to sort for {}. Ignoring sorting.".format(man.__class__.__name__))
sorted_list.extend(pkgs)
return sorted_list
def get_update_requirements(self, pkgs: List[SoftwarePackage], watcher: ProcessWatcher) -> List[SoftwarePackage]:
by_manager = {}
for pkg in pkgs:
man = self._get_manager_for(pkg)
if man:
man_pkgs = by_manager.get(man)
if man_pkgs is None:
man_pkgs = []
by_manager[man] = man_pkgs
man_pkgs.append(pkg)
required = []
def get_upgrade_requirements(self, pkgs: List[SoftwarePackage], root_password: str, watcher: ProcessWatcher) -> UpgradeRequirements:
by_manager = self._map_pkgs_by_manager(pkgs)
res = GenericUpgradeRequirements([], [], [], [], {})
if by_manager:
for man, pkgs in by_manager.items():
ti = time.time()
required.extend(man.get_update_requirements(pkgs, watcher))
man_reqs = man.get_upgrade_requirements(pkgs, root_password, watcher)
tf = time.time()
self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(tf - ti))
return required
if not man_reqs:
return # it means the process should be stopped
if man_reqs:
res.sub_requirements[man] = man_reqs
if man_reqs.to_install:
res.to_install.extend(man_reqs.to_install)
if man_reqs.to_remove:
res.to_remove.extend(man_reqs.to_remove)
if man_reqs.to_upgrade:
res.to_upgrade.extend(man_reqs.to_upgrade)
if man_reqs.cannot_upgrade:
res.cannot_upgrade.extend(man_reqs.cannot_upgrade)
return res
def get_custom_actions(self) -> List[CustomSoftwareAction]:
if self.managers:
actions = []
working_managers = []
for man in self.managers:
if self._can_work(man):
working_managers.append(man)
if working_managers:
working_managers.sort(key=lambda m: m.__class__.__name__)
for man in working_managers:
man_actions = man.get_custom_actions()
if man_actions:
actions.extend(man_actions)
return actions
def _fill_sizes(self, man: SoftwareManager, pkgs: List[SoftwarePackage]):
ti = time.time()
man.fill_sizes(pkgs)
tf = time.time()
self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(tf - ti))
def fill_sizes(self, pkgs: List[SoftwarePackage]):
by_manager = self._map_pkgs_by_manager(pkgs, pkg_filters=[lambda p: p.size is None])
if by_manager:
threads = []
for man, man_pkgs in by_manager.items():
if man_pkgs:
t = Thread(target=self._fill_sizes, args=(man, man_pkgs), daemon=True)
t.start()
threads.append(t)
for t in threads:
t.join()

View File

@@ -11,7 +11,7 @@ from bauh.api.abstract.controller import SoftwareManager
from bauh.api.abstract.view import ViewComponent, TabComponent, InputOption, TextComponent, MultipleSelectComponent, \
PanelComponent, FormComponent, TabGroupComponent, SingleSelectComponent, SelectViewType, TextInputComponent, \
FileChooserComponent
from bauh.view.core import config
from bauh.view.core import config, timeshift
from bauh.view.core.config import read_config
from bauh.view.util import translation
from bauh.view.util.translation import I18n
@@ -70,6 +70,11 @@ class GenericSettingsManager:
tabs.append(self._gen_tray_settings(core_config, screen_width, screen_height))
tabs.append(self._gen_adv_settings(core_config, screen_width, screen_height))
bkp_settings = self._gen_backup_settings(core_config, screen_width, screen_height)
if bkp_settings:
tabs.append(bkp_settings)
for tab in gem_tabs:
tabs.append(tab)
@@ -78,12 +83,6 @@ class GenericSettingsManager:
def _gen_adv_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent:
default_width = floor(0.11 * screen_width)
select_dcache = self._gen_bool_component(label=self.i18n['core.config.disk_cache'],
tooltip=self.i18n['core.config.disk_cache.tip'],
value=core_config['disk_cache']['enabled'],
max_width=default_width,
id_='dcache')
input_data_exp = TextInputComponent(label=self.i18n['core.config.mem_cache.data_exp'],
tooltip=self.i18n['core.config.mem_cache.data_exp.tip'],
value=str(core_config['memory_cache']['data_expiration']),
@@ -98,17 +97,11 @@ class GenericSettingsManager:
max_width=default_width,
id_="icon_exp")
select_update_sort = self._gen_bool_component(label=self.i18n['core.config.updates.sort_pkgs'],
tooltip=self.i18n['core.config.updates.sort_pkgs.tip'],
value=bool(core_config['updates']['sort_packages']),
max_width=default_width,
id_="up_sort")
select_update_check = self._gen_bool_component(label=self.i18n['core.config.updates.dep_check'],
tooltip=self.i18n['core.config.updates.dep_check.tip'],
value=bool(core_config['updates']['pre_dependency_checking']),
max_width=default_width,
id_="up_dep_check")
select_trim_up = self._gen_bool_component(label=self.i18n['core.config.trim_after_update'],
tooltip=self.i18n['core.config.trim_after_update.tip'],
value=bool(core_config['disk']['trim_after_update']),
max_width=default_width,
id_='trim_after_update')
select_dep_check = self._gen_bool_component(label=self.i18n['core.config.system.dep_checking'],
tooltip=self.i18n['core.config.system.dep_checking.tip'],
@@ -122,7 +115,7 @@ class GenericSettingsManager:
max_width=default_width,
value=core_config['download']['multithreaded'])
sub_comps = [FormComponent([select_dcache, select_dmthread, select_update_check, select_update_sort, select_dep_check, input_data_exp, input_icon_exp], spaces=False)]
sub_comps = [FormComponent([select_dmthread, select_trim_up, select_dep_check, input_data_exp, input_icon_exp], spaces=False)]
return TabComponent(self.i18n['core.config.tab.advanced'].capitalize(), PanelComponent(sub_comps), None, 'core.adv')
def _gen_tray_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent:
@@ -219,6 +212,12 @@ class GenericSettingsManager:
max_width=default_width,
id_='locale')
select_store_pwd = self._gen_bool_component(label=self.i18n['core.config.store_password'].capitalize(),
tooltip=self.i18n['core.config.store_password.tip'].capitalize(),
id_="store_pwd",
max_width=default_width,
value=bool(core_config['store_root_password']))
select_sysnotify = self._gen_bool_component(label=self.i18n['core.config.system.notifications'].capitalize(),
tooltip=self.i18n['core.config.system.notifications.tip'].capitalize(),
value=bool(core_config['system']['notifications']),
@@ -238,7 +237,7 @@ class GenericSettingsManager:
max_width=default_width,
id_="sugs_by_type")
sub_comps = [FormComponent([select_locale, select_sysnotify, select_sugs, inp_sugs], spaces=False)]
sub_comps = [FormComponent([select_locale, select_store_pwd, select_sysnotify, select_sugs, inp_sugs], spaces=False)]
return TabComponent(self.i18n['core.config.tab.general'].capitalize(), PanelComponent(sub_comps), None, 'core.gen')
def _gen_bool_component(self, label: str, tooltip: str, value: bool, id_: str, max_width: int = 200) -> SingleSelectComponent:
@@ -256,6 +255,7 @@ class GenericSettingsManager:
def _save_settings(self, general: PanelComponent,
advanced: PanelComponent,
backup: PanelComponent,
ui: PanelComponent,
tray: PanelComponent,
gems_panel: PanelComponent) -> Tuple[bool, List[str]]:
@@ -271,20 +271,17 @@ class GenericSettingsManager:
core_config['system']['notifications'] = general_form.get_component('sys_notify').get_selected()
core_config['suggestions']['enabled'] = general_form.get_component('sugs_enabled').get_selected()
core_config['store_root_password'] = general_form.get_component('store_pwd').get_selected()
sugs_by_type = general_form.get_component('sugs_by_type').get_int_value()
core_config['suggestions']['by_type'] = sugs_by_type
# advanced
adv_form = advanced.components[0]
core_config['disk_cache']['enabled'] = adv_form.get_component('dcache').get_selected()
download_mthreaded = adv_form.get_component('down_mthread').get_selected()
core_config['download']['multithreaded'] = download_mthreaded
core_config['updates']['pre_dependency_checking'] = adv_form.get_component('up_dep_check').get_selected()
core_config['updates']['sort_packages'] = adv_form.get_component('up_sort').get_selected()
single_dep_check = adv_form.get_component('dep_check').get_selected()
core_config['system']['single_dependency_checking'] = single_dep_check
@@ -294,6 +291,19 @@ class GenericSettingsManager:
icon_exp = adv_form.get_component('icon_exp').get_int_value()
core_config['memory_cache']['icon_expiration'] = icon_exp
core_config['disk']['trim_after_update'] = adv_form.get_component('trim_after_update').get_selected()
# backup
if backup:
bkp_form = backup.components[0]
core_config['backup']['enabled'] = bkp_form.get_component('enabled').get_selected()
core_config['backup']['mode'] = bkp_form.get_component('mode').get_selected()
core_config['backup']['install'] = bkp_form.get_component('install').get_selected()
core_config['backup']['uninstall'] = bkp_form.get_component('uninstall').get_selected()
core_config['backup']['upgrade'] = bkp_form.get_component('upgrade').get_selected()
core_config['backup']['downgrade'] = bkp_form.get_component('downgrade').get_selected()
# tray
tray_form = tray.components[0]
core_config['updates']['check_interval'] = tray_form.get_component('updates_interval').get_int_value()
@@ -346,9 +356,11 @@ class GenericSettingsManager:
saved, warnings = True, []
bkp = component.get_tab('core.bkp')
success, errors = self._save_settings(general=component.get_tab('core.gen').content,
advanced=component.get_tab('core.adv').content,
tray=component.get_tab('core.tray').content,
backup=bkp.content if bkp else None,
ui=component.get_tab('core.ui').content,
gems_panel=component.get_tab('core.types').content)
@@ -378,3 +390,71 @@ class GenericSettingsManager:
warnings.extend(errors)
return saved, warnings
def _gen_backup_settings(self, core_config: dict, screen_width: int, screen_height: int) -> TabComponent:
if timeshift.is_available():
default_width = floor(0.22 * screen_width)
enabled_opt = self._gen_bool_component(label=self.i18n['core.config.backup'],
tooltip=None,
value=bool(core_config['backup']['enabled']),
id_='enabled',
max_width=default_width)
ops_opts = [(self.i18n['yes'].capitalize(), True, None),
(self.i18n['no'].capitalize(), False, None),
(self.i18n['ask'].capitalize(), None, None)]
install_mode = self._gen_select(label=self.i18n['core.config.backup.install'],
tip=None,
value=core_config['backup']['install'],
opts=ops_opts,
max_width=default_width,
id_='install')
uninstall_mode = self._gen_select(label=self.i18n['core.config.backup.uninstall'],
tip=None,
value=core_config['backup']['uninstall'],
opts=ops_opts,
max_width=default_width,
id_='uninstall')
upgrade_mode = self._gen_select(label=self.i18n['core.config.backup.upgrade'],
tip=None,
value=core_config['backup']['upgrade'],
opts=ops_opts,
max_width=default_width,
id_='upgrade')
downgrade_mode = self._gen_select(label=self.i18n['core.config.backup.downgrade'],
tip=None,
value=core_config['backup']['downgrade'],
opts=ops_opts,
max_width=default_width,
id_='downgrade')
mode = self._gen_select(label=self.i18n['core.config.backup.mode'],
tip=None,
value=core_config['backup']['mode'],
opts=[
(self.i18n['core.config.backup.mode.incremental'], 'incremental', self.i18n['core.config.backup.mode.incremental.tip']),
(self.i18n['core.config.backup.mode.only_one'], 'only_one', self.i18n['core.config.backup.mode.only_one.tip'])
],
max_width=default_width,
id_='mode')
sub_comps = [FormComponent([enabled_opt, mode, install_mode, uninstall_mode, upgrade_mode, downgrade_mode], spaces=False)]
return TabComponent(self.i18n['core.config.tab.backup'].capitalize(), PanelComponent(sub_comps), None, 'core.bkp')
def _gen_select(self, label: str, tip: str, id_: str, opts: List[tuple], value: str, max_width: int, type_: SelectViewType = SelectViewType.RADIO):
inp_opts = [InputOption(label=o[0].capitalize(), value=o[1], tooltip=o[2]) for o in opts]
def_opt = [o for o in inp_opts if o.value == value]
return SingleSelectComponent(label=label,
tooltip=tip,
options=inp_opts,
default_option=def_opt[0] if def_opt else inp_opts[0],
max_per_line=len(inp_opts),
max_width=max_width,
type_=type_,
id_=id_)

View File

@@ -0,0 +1,13 @@
from bauh.commons.system import run_cmd, SimpleProcess
def is_available() -> bool:
return bool(run_cmd('which timeshift', print_error=False))
def delete_all_snapshots(root_password: str) -> SimpleProcess:
return SimpleProcess(['timeshift', '--delete-all', '--scripted'], root_password=root_password)
def create_snapshot(root_password: str) -> SimpleProcess:
return SimpleProcess(['timeshift', '--create', '--scripted'], root_password=root_password)

View File

@@ -0,0 +1,12 @@
from pathlib import Path
from bauh.api.constants import CACHE_PATH
TRAY_CHECK_FILE = '{}/notify_tray'.format(CACHE_PATH) # it is a file that signals to the tray icon it should recheck for updates
def notify_tray():
Path(CACHE_PATH).mkdir(exist_ok=True, parents=True)
with open(TRAY_CHECK_FILE, 'w+') as f:
f.write('')