mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-09 05:14:16 +02:00
appimage history | history panel can now be maximized
This commit is contained in:
@@ -11,10 +11,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
### Improvements
|
### Improvements
|
||||||
- AUR:
|
- AUR:
|
||||||
- Optional dependencies are not checked by default in their installation popup.
|
- Optional dependencies are not checked by default in their installation popup.
|
||||||
|
- History panel can now me maximized, minimized and allows to copy column content.
|
||||||
- Minor UI improvements
|
- Minor UI improvements
|
||||||
|
|
||||||
### AppImage support
|
### AppImage support
|
||||||
- Search, install, uninstall applications available in [AppImageHub](https://appimage.github.io)
|
- Search, install, uninstall and retrieve history of applications available in [AppImageHub](https://appimage.github.io)
|
||||||
- Adds desktop entries (menu shortcuts) for the installed applications
|
- Adds desktop entries (menu shortcuts) for the installed applications
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, r
|
|||||||
from bauh.gems.appimage import query, INSTALLATION_PATH
|
from bauh.gems.appimage import query, INSTALLATION_PATH
|
||||||
from bauh.gems.appimage.model import AppImage
|
from bauh.gems.appimage.model import AppImage
|
||||||
|
|
||||||
DB_PATH = '{}/{}'.format(HOME_PATH, '.local/share/bauh/appimage/appimage.db')
|
DB_APPS_PATH = '{}/{}'.format(HOME_PATH, '.local/share/bauh/appimage/apps.db')
|
||||||
|
DB_RELEASES_PATH = '{}/{}'.format(HOME_PATH, '.local/share/bauh/appimage/releases.db')
|
||||||
|
|
||||||
DESKTOP_ENTRIES_PATH = '{}/.local/share/applications'.format(HOME_PATH)
|
DESKTOP_ENTRIES_PATH = '{}/.local/share/applications'.format(HOME_PATH)
|
||||||
|
|
||||||
RE_DESKTOP_EXEC = re.compile(r'Exec\s*=\s*.+\n')
|
RE_DESKTOP_EXEC = re.compile(r'Exec\s*=\s*.+\n')
|
||||||
@@ -39,18 +41,18 @@ class AppImageManager(SoftwareManager):
|
|||||||
self.logger = context.logger
|
self.logger = context.logger
|
||||||
self.file_downloader = context.file_downloader
|
self.file_downloader = context.file_downloader
|
||||||
|
|
||||||
def _get_db_connection(self) -> sqlite3.Connection:
|
def _get_db_connection(self, db_path: str) -> sqlite3.Connection:
|
||||||
if os.path.exists(DB_PATH):
|
if os.path.exists(db_path):
|
||||||
return sqlite3.connect(DB_PATH)
|
return sqlite3.connect(db_path)
|
||||||
|
|
||||||
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1) -> SearchResult:
|
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1) -> SearchResult:
|
||||||
res = SearchResult([], [], 0)
|
res = SearchResult([], [], 0)
|
||||||
connection = self._get_db_connection()
|
connection = self._get_db_connection(DB_APPS_PATH)
|
||||||
|
|
||||||
if connection:
|
if connection:
|
||||||
try:
|
try:
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute(query.SEARCH_BY_NAME_OR_DESCRIPTION.format(words, words))
|
cursor.execute(query.SEARCH_APPS_BY_NAME_OR_DESCRIPTION.format(words, words))
|
||||||
|
|
||||||
for l in cursor.fetchall():
|
for l in cursor.fetchall():
|
||||||
app = AppImage(*l)
|
app = AppImage(*l)
|
||||||
@@ -58,7 +60,7 @@ class AppImageManager(SoftwareManager):
|
|||||||
finally:
|
finally:
|
||||||
connection.close()
|
connection.close()
|
||||||
else:
|
else:
|
||||||
self.logger.warning('Could not get a connection from the local database at {}'.format(DB_PATH))
|
self.logger.warning('Could not get a connection from the local database at {}'.format(DB_APPS_PATH))
|
||||||
|
|
||||||
res.total = len(res.installed) + len(res.new)
|
res.total = len(res.installed) + len(res.new)
|
||||||
return res
|
return res
|
||||||
@@ -111,8 +113,35 @@ class AppImageManager(SoftwareManager):
|
|||||||
return pkg.get_data_to_cache()
|
return pkg.get_data_to_cache()
|
||||||
|
|
||||||
def get_history(self, pkg: AppImage) -> PackageHistory:
|
def get_history(self, pkg: AppImage) -> PackageHistory:
|
||||||
# TODO
|
history = []
|
||||||
pass
|
res = PackageHistory(pkg, history, -1)
|
||||||
|
|
||||||
|
connection = self._get_db_connection(DB_APPS_PATH)
|
||||||
|
|
||||||
|
if connection:
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
cursor.execute(query.FIND_APP_ID_BY_NAME_AND_GITHUB.format(pkg.name.lower(), pkg.github.lower()))
|
||||||
|
app_tuple = cursor.fetchone()
|
||||||
|
|
||||||
|
if not app_tuple:
|
||||||
|
raise Exception("Could not retrieve {} from the database {}".format(pkg, DB_APPS_PATH))
|
||||||
|
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
connection = self._get_db_connection(DB_RELEASES_PATH)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
releases = cursor.execute(query.FIND_RELEASES_BY_APP_ID.format(app_tuple[0]))
|
||||||
|
|
||||||
|
if releases:
|
||||||
|
for idx, tup in enumerate(releases):
|
||||||
|
history.append({'0_version': tup[0], '1_published_at': tup[2], '2_url_download': tup[1]})
|
||||||
|
|
||||||
|
if res.pkg_status_idx == -1 and pkg.version == tup[0]:
|
||||||
|
res.pkg_status_idx = idx
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
def _find_desktop_file(self, folder: str) -> str:
|
def _find_desktop_file(self, folder: str) -> str:
|
||||||
for r, d, files in os.walk(folder):
|
for r, d, files in os.walk(folder):
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from bauh.api.abstract.model import SoftwarePackage
|
|||||||
from bauh.commons import resource
|
from bauh.commons import resource
|
||||||
from bauh.gems.appimage import ROOT_DIR, INSTALLATION_PATH
|
from bauh.gems.appimage import ROOT_DIR, INSTALLATION_PATH
|
||||||
|
|
||||||
CACHED_ATTRS = {'name', 'description', 'version', 'url_download', 'author', 'license', 'source', 'icon_path'}
|
CACHED_ATTRS = {'name', 'description', 'version', 'url_download', 'author', 'license', 'source', 'icon_path', 'github'}
|
||||||
|
|
||||||
|
|
||||||
class AppImage(SoftwarePackage):
|
class AppImage(SoftwarePackage):
|
||||||
@@ -22,12 +22,14 @@ class AppImage(SoftwarePackage):
|
|||||||
self.icon_path = icon_path
|
self.icon_path = icon_path
|
||||||
self.author = author
|
self.author = author
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "{} (name={}, github={})".format(self.__class__.__name__, self.name, self.github)
|
||||||
|
|
||||||
def can_be_installed(self):
|
def can_be_installed(self):
|
||||||
return not self.installed and self.url_download
|
return not self.installed and self.url_download
|
||||||
|
|
||||||
def has_history(self):
|
def has_history(self):
|
||||||
# TODO
|
return self.installed
|
||||||
return False
|
|
||||||
|
|
||||||
def has_info(self):
|
def has_info(self):
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
ATTRS = ('name', 'description', 'github', 'source', 'version', 'url_download', 'url_icon', 'license', 'author')
|
APP_ATTRS = ('name', 'description', 'github', 'source', 'version', 'url_download', 'url_icon', 'license', 'author')
|
||||||
|
RELEASE_ATTRS = ('version', 'url_download', 'published_at')
|
||||||
|
|
||||||
_SELECT_BASE = "SELECT {} FROM apps".format(','.join(ATTRS))
|
|
||||||
|
|
||||||
SEARCH_BY_NAME_OR_DESCRIPTION = _SELECT_BASE + " WHERE lower(name) LIKE '%{}%' or lower(description) LIKE '%{}%'"
|
SEARCH_APPS_BY_NAME_OR_DESCRIPTION = "SELECT {} FROM apps".format(','.join(APP_ATTRS)) + " WHERE lower(name) LIKE '%{}%' or lower(description) LIKE '%{}%'"
|
||||||
|
FIND_APP_ID_BY_NAME_AND_GITHUB = "SELECT id FROM apps WHERE lower(name) = '{}' and lower(github) = '{}'"
|
||||||
|
FIND_RELEASES_BY_APP_ID = "SELECT {} FROM releases".format(','.join(RELEASE_ATTRS)) + " WHERE app_id = {} ORDER BY version desc"
|
||||||
|
|||||||
@@ -4,4 +4,7 @@ appimage.install.extract=Extracting the content from {}
|
|||||||
appimage.install.desktop_entry=Generating a menu shortcut
|
appimage.install.desktop_entry=Generating a menu shortcut
|
||||||
appimage.uninstall.error.remove_folder=Could not remove the application installation directory {}
|
appimage.uninstall.error.remove_folder=Could not remove the application installation directory {}
|
||||||
appimage.info.url_download=File URL
|
appimage.info.url_download=File URL
|
||||||
appimage.info.icon_path=icon
|
appimage.info.icon_path=icon
|
||||||
|
appimage.history.0_version=version
|
||||||
|
appimage.history.1_published_at=date
|
||||||
|
appimage.history.2_url_download=File URL
|
||||||
@@ -4,4 +4,7 @@ appimage.install.extract=Extrayendo el contenido de {}
|
|||||||
appimage.install.desktop_entry=Creando un atajo en el menú
|
appimage.install.desktop_entry=Creando un atajo en el menú
|
||||||
appimage.uninstall.error.remove_folder=No se pudo eliminar el directorio de instalación de la aplicación {}
|
appimage.uninstall.error.remove_folder=No se pudo eliminar el directorio de instalación de la aplicación {}
|
||||||
appimage.info.url_download=URL del archivo
|
appimage.info.url_download=URL del archivo
|
||||||
appimage.info.icon_path=icono
|
appimage.info.icon_path=icono
|
||||||
|
appimage.history.0_version=versión
|
||||||
|
appimage.history.1_published_at=fecha
|
||||||
|
appimage.history.2_url_download=URL del archivo
|
||||||
@@ -4,4 +4,7 @@ appimage.install.extract=Extraindo o conteúdo de {}
|
|||||||
appimage.install.desktop_entry=Criando um atalho no menu
|
appimage.install.desktop_entry=Criando um atalho no menu
|
||||||
appimage.uninstall.error.remove_folder=Não foi possível remover o diretóri ode instalação do aplicativo {}
|
appimage.uninstall.error.remove_folder=Não foi possível remover o diretóri ode instalação do aplicativo {}
|
||||||
appimage.info.url_download=URL do arquivo
|
appimage.info.url_download=URL do arquivo
|
||||||
appimage.info.icon_path=ícone
|
appimage.info.icon_path=ícone
|
||||||
|
appimage.history.0_version=versão
|
||||||
|
appimage.history.1_published_at=data
|
||||||
|
appimage.history.2_url_download=URL do arquivo
|
||||||
@@ -4,16 +4,18 @@ from functools import reduce
|
|||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QColor
|
from PyQt5.QtGui import QColor
|
||||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView
|
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView
|
||||||
|
|
||||||
from bauh.api.abstract.cache import MemoryCache
|
from bauh.api.abstract.cache import MemoryCache
|
||||||
from bauh.api.abstract.model import PackageHistory
|
from bauh.api.abstract.model import PackageHistory
|
||||||
|
|
||||||
|
|
||||||
class HistoryDialog(QDialog):
|
class HistoryDialog(QDialog):
|
||||||
|
|
||||||
def __init__(self, history: PackageHistory, icon_cache: MemoryCache, locale_keys: dict):
|
def __init__(self, history: PackageHistory, icon_cache: MemoryCache, i18n: dict):
|
||||||
super(HistoryDialog, self).__init__()
|
super(HistoryDialog, self).__init__()
|
||||||
|
self.setWindowFlags(self.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint)
|
||||||
|
|
||||||
self.setWindowTitle('{} - {} ({})'.format(locale_keys['popup.history.title'], history.pkg.name, history.pkg.get_type()))
|
self.setWindowTitle('{} - {} ({})'.format(i18n['popup.history.title'], history.pkg.name, history.pkg.get_type()))
|
||||||
|
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
@@ -26,20 +28,19 @@ class HistoryDialog(QDialog):
|
|||||||
|
|
||||||
table_history.setColumnCount(len(history.history[0]))
|
table_history.setColumnCount(len(history.history[0]))
|
||||||
table_history.setRowCount(len(history.history))
|
table_history.setRowCount(len(history.history))
|
||||||
table_history.setHorizontalHeaderLabels([locale_keys.get(history.pkg.get_type() + '.history.' + key, key).capitalize() for key in sorted(history.history[0].keys())])
|
table_history.setHorizontalHeaderLabels([i18n.get(history.pkg.get_type().lower() + '.history.' + key, i18n.get(key, key)).capitalize() for key in sorted(history.history[0].keys())])
|
||||||
|
|
||||||
for row, commit in enumerate(history.history):
|
for row, data in enumerate(history.history):
|
||||||
|
|
||||||
current_status = history.pkg_status_idx == row
|
current_status = history.pkg_status_idx == row
|
||||||
|
|
||||||
for col, key in enumerate(sorted(commit.keys())):
|
for col, key in enumerate(sorted(data.keys())):
|
||||||
item = QTableWidgetItem()
|
item = QTableWidgetItem()
|
||||||
item.setText(str(commit[key]))
|
item.setText(str(data[key]))
|
||||||
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
|
|
||||||
|
|
||||||
if current_status:
|
if current_status:
|
||||||
item.setBackground(QColor('#ffbf00' if row != 0 else '#32CD32'))
|
item.setBackground(QColor('#ffbf00' if row != 0 else '#32CD32'))
|
||||||
tip = '{}. {}.'.format(locale_keys['popup.history.selected.tooltip'], locale_keys['version.{}'.format('updated'if row == 0 else 'outdated')].capitalize())
|
tip = '{}. {}.'.format(i18n['popup.history.selected.tooltip'], i18n['version.{}'.format('updated'if row == 0 else 'outdated')].capitalize())
|
||||||
|
|
||||||
item.setToolTip(tip)
|
item.setToolTip(tip)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user