This commit is contained in:
Vinícius Moreira
2019-12-09 19:30:27 -03:00
parent 6bd27b565e
commit b9ed2cf4c1
16 changed files with 215 additions and 12 deletions

View File

@@ -1,4 +1,4 @@
__version__ = '0.7.4' __version__ = '0.8.0'
__app_name__ = 'bauh' __app_name__ = 'bauh'
import os import os

View File

@@ -37,11 +37,12 @@ class SoftwareManager(ABC):
self.context = context self.context = context
@abstractmethod @abstractmethod
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int) -> SearchResult: def search(self, words: str, disk_loader: DiskCacheLoader, limit: int, is_url: bool) -> SearchResult:
""" """
:param words: the words typed by the user :param words: the words typed by the user
:param disk_loader: a running disk loader thread that loads package data from the disk asynchronously :param disk_loader: a running disk loader thread that loads package data from the disk asynchronously
:param limit: the max number of packages to be retrieved. <= 1 should retrieve everything :param limit: the max number of packages to be retrieved. <= 1 should retrieve everything
:param is_url: if "words" is a URL
:return: :return:
""" """
pass pass

View File

@@ -61,7 +61,10 @@ class AppImageManager(SoftwareManager):
def _gen_app_key(self, app: AppImage): def _gen_app_key(self, app: AppImage):
return '{}{}'.format(app.name.lower(), app.github.lower() if app.github else '') return '{}{}'.format(app.name.lower(), app.github.lower() if app.github else '')
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1) -> SearchResult: def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
if is_url:
return SearchResult([], [], 0)
res = SearchResult([], [], 0) res = SearchResult([], [], 0)
connection = self._get_db_connection(DB_APPS_PATH) connection = self._get_db_connection(DB_APPS_PATH)

View File

@@ -79,7 +79,10 @@ class ArchManager(SoftwareManager):
Thread(target=self.mapper.fill_package_build, args=(app,), daemon=True).start() Thread(target=self.mapper.fill_package_build, args=(app,), daemon=True).start()
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1) -> SearchResult: def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
if is_url:
return SearchResult([], [], 0)
self.comp_optimizer.join() self.comp_optimizer.join()
downgrade_enabled = git.is_enabled() downgrade_enabled = git.is_enabled()

View File

@@ -56,7 +56,9 @@ class FlatpakManager(SoftwareManager):
return app return app
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1) -> SearchResult: def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
if is_url:
return SearchResult([], [], 0)
res = SearchResult([], [], 0) res = SearchResult([], [], 0)
apps_found = flatpak.search(flatpak.get_version(), words) apps_found = flatpak.search(flatpak.get_version(), words)

View File

@@ -84,7 +84,10 @@ class SnapManager(SoftwareManager):
return app return app
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1) -> SearchResult: def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
if is_url:
return SearchResult([], [], 0)
if snap.is_snapd_running(): if snap.is_snapd_running():
installed = self.read_installed(disk_loader).installed installed = self.read_installed(disk_loader).installed

View File

@@ -13,7 +13,7 @@ SNAPD_RUNNING_STATUS = {'listening', 'running'}
def is_installed(): def is_installed():
res = run_cmd('which snap') res = run_cmd('which snap', print_error=False)
return res and not res.strip().startswith('which ') return res and not res.strip().startswith('which ')

View File

@@ -0,0 +1,3 @@
import os
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))

117
bauh/gems/web/controller.py Normal file
View File

@@ -0,0 +1,117 @@
from typing import List, Type, Set
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager, SearchResult
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import SoftwarePackage, PackageAction, PackageSuggestion, PackageUpdate, PackageHistory
from bauh.gems.web import npm
from bauh.gems.web.model import WebApplication
try:
from bs4 import BeautifulSoup, SoupStrainer
BS4_AVAILABLE = True
except:
BS4_AVAILABLE = False
try:
import lxml
LXML_AVAILABLE = True
except:
LXML_AVAILABLE = False
class WebApplicationManager(SoftwareManager):
def __init__(self, context: ApplicationContext):
super(WebApplicationManager, self).__init__(context=context)
self.http_client = context.http_client
self.enabled = True
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
res = SearchResult([], [], 0)
if is_url:
url_res = self.http_client.get(words)
if url_res:
soup = BeautifulSoup(url_res.text, 'lxml', parse_only=SoupStrainer('head'))
name_tag = soup.head.find('meta', attrs={'name': 'application-name'})
name = name_tag.get('content') if name_tag else words.split('.')[0]
desc_tag = soup.head.find('meta', attrs={'name': 'description'})
desc = desc_tag.get('content') if desc_tag else words
icon_tag = soup.head.find('link', attrs={"rel": "icon"})
icon_url = icon_tag.get('href') if icon_tag else None
res.new = [WebApplication(url=words, name=name, description=desc, icon_url=icon_url)]
res.total = 1
else:
# TODO
pass
return res
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = True) -> SearchResult:
# TODO
return SearchResult([], [], 0)
def downgrade(self, pkg: SoftwarePackage, root_password: str, handler: ProcessWatcher) -> bool:
pass
def update(self, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher) -> bool:
pass
def uninstall(self, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher) -> bool:
pass
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
return {WebApplication}
def get_info(self, pkg: SoftwarePackage) -> dict:
pass
def get_history(self, pkg: SoftwarePackage) -> PackageHistory:
pass
def install(self, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher) -> bool:
pass
def is_enabled(self) -> bool:
return self.enabled
def set_enabled(self, enabled: bool):
self.enabled = enabled
def can_work(self) -> bool:
return BS4_AVAILABLE and LXML_AVAILABLE and npm.is_available()
def requires_root(self, action: str, pkg: SoftwarePackage):
return False
def prepare(self):
pass
def list_updates(self, internet_available: bool) -> List[PackageUpdate]:
pass
def list_warnings(self, internet_available: bool) -> List[str]:
pass
def list_suggestions(self, limit: int) -> List[PackageSuggestion]:
pass
def execute_custom_action(self, action: PackageAction, pkg: SoftwarePackage, root_password: str, watcher: ProcessWatcher) -> bool:
pass
def is_default_enabled(self) -> bool:
return True
def launch(self, pkg: SoftwarePackage):
pass
def get_screenshots(self, pkg: SoftwarePackage) -> List[str]:
pass

59
bauh/gems/web/model.py Normal file
View File

@@ -0,0 +1,59 @@
from bauh.api.abstract.model import SoftwarePackage
from bauh.api.constants import CACHE_PATH
from bauh.commons import resource
from bauh.gems.web import ROOT_DIR
class WebApplication(SoftwarePackage):
def __init__(self, url: str, name: str, description: str, icon_url: str):
super(WebApplication, self).__init__(id=url, name=name, description=description, icon_url=icon_url)
self.url = url
def has_history(self):
return False
def has_info(self):
return False
def can_be_downgraded(self):
return False
def get_type(self):
return 'web'
def get_type_icon_path(self) -> str:
return self.get_default_icon_path()
def get_default_icon_path(self) -> str:
return resource.get_path('img/web.png', ROOT_DIR)
def is_application(self):
return True
def supports_disk_cache(self):
return self.installed and self.is_application()
def get_disk_cache_path(self):
return CACHE_PATH + '/' + self.get_type()
def get_data_to_cache(self) -> dict:
"""
:return: the application data that should be cached in disk / memory for quick access
"""
pass
def fill_cached_data(self, data: dict):
pass
def can_be_run(self) -> bool:
return self.installed
def is_trustable(self) -> bool:
return False
def get_publisher(self) -> str:
pass
def has_screenshots(self) -> bool:
return False

6
bauh/gems/web/npm.py Normal file
View File

@@ -0,0 +1,6 @@
from bauh.commons.system import run_cmd
def is_available() -> bool:
res = run_cmd('which npm', print_error=False)
return res and not res.strip().startswith('which ')

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
gem.web.info=It allows to install Web applications on your system

View File

@@ -0,0 +1 @@
gem.web.info=Permite instalar aplicações Web no seu sistema

View File

@@ -1,3 +1,4 @@
import re
import time import time
import traceback import traceback
from argparse import Namespace from argparse import Namespace
@@ -11,6 +12,8 @@ from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHisto
from bauh.api.exception import NoInternetException from bauh.api.exception import NoInternetException
from bauh.commons import internet from bauh.commons import internet
RE_IS_URL = re.compile(r'^https?://.+')
SUGGESTIONS_LIMIT = 5 SUGGESTIONS_LIMIT = 5
@@ -79,17 +82,17 @@ class GenericSoftwareManager(SoftwareManager):
return available return available
def _search(self, word: str, man: SoftwareManager, disk_loader, res: SearchResult): def _search(self, word: str, is_url: bool, man: SoftwareManager, disk_loader, res: SearchResult):
if self._can_work(man): if self._can_work(man):
mti = time.time() mti = time.time()
apps_found = man.search(words=word, disk_loader=disk_loader) apps_found = man.search(words=word, disk_loader=disk_loader, is_url=is_url)
mtf = time.time() mtf = time.time()
self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(mtf - mti)) self.logger.info(man.__class__.__name__ + " took {0:.2f} seconds".format(mtf - mti))
res.installed.extend(apps_found.installed) res.installed.extend(apps_found.installed)
res.new.extend(apps_found.new) res.new.extend(apps_found.new)
def search(self, word: str, disk_loader: DiskCacheLoader = None, limit: int = -1) -> SearchResult: def search(self, word: str, disk_loader: DiskCacheLoader = None, limit: int = -1, is_url: bool = False) -> SearchResult:
ti = time.time() ti = time.time()
self._wait_to_be_ready() self._wait_to_be_ready()
@@ -97,13 +100,15 @@ class GenericSoftwareManager(SoftwareManager):
if internet.is_available(self.context.http_client, self.context.logger): if internet.is_available(self.context.http_client, self.context.logger):
norm_word = word.strip().lower() norm_word = word.strip().lower()
url_words = RE_IS_URL.match(norm_word)
disk_loader = self.disk_loader_factory.new() disk_loader = self.disk_loader_factory.new()
disk_loader.start() disk_loader.start()
threads = [] threads = []
for man in self.managers: for man in self.managers:
t = Thread(target=self._search, args=(norm_word, man, disk_loader, res)) t = Thread(target=self._search, args=(norm_word, url_words, man, disk_loader, res))
t.start() t.start()
threads.append(t) threads.append(t)

View File

@@ -106,7 +106,6 @@ class ManageWindow(QWidget):
self.toolbar_search.addWidget(label_pre_search) self.toolbar_search.addWidget(label_pre_search)
self.input_search = QLineEdit() self.input_search = QLineEdit()
self.input_search.setMaxLength(20)
self.input_search.setFrame(False) self.input_search.setFrame(False)
self.input_search.setPlaceholderText(self.i18n['window_manage.input_search.placeholder'] + "...") self.input_search.setPlaceholderText(self.i18n['window_manage.input_search.placeholder'] + "...")
self.input_search.setToolTip(self.i18n['window_manage.input_search.tooltip']) self.input_search.setToolTip(self.i18n['window_manage.input_search.tooltip'])