Commit fd20e820 authored by Roman Alifanov's avatar Roman Alifanov

init is a minimal dependency checking system

parent f5e6275f
......@@ -9,4 +9,6 @@ src/main.py
src/window.py
src/window.blp
src/settings/main.py
src/settings/deps.py
src/settings/widgets/deps_alert_dialog.blp
src/settings/widgets/service_dialog.py
......@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: tuneit\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-21 13:16+0300\n"
"PO-Revision-Date: 2025-01-21 13:18+0300\n"
"POT-Creation-Date: 2025-02-11 15:59+0300\n"
"PO-Revision-Date: 2025-02-11 16:01+0300\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: ru_RU\n"
......@@ -26,7 +26,7 @@ msgstr ""
msgid "GTK;"
msgstr ""
#: data/ru.ximperlinux.TuneIt.metainfo.xml.in:7 src/window.blp:92
#: data/ru.ximperlinux.TuneIt.metainfo.xml.in:7 src/window.blp:9
msgid "TuneIt"
msgstr ""
......@@ -52,30 +52,46 @@ msgstr "Показывать модули которым нужны root пра
msgid "translator-credits"
msgstr "ximper@etersoft.ru"
#: src/window.blp:26
#: src/window.blp:40 src/window.blp:66 src/window.blp:87
msgid "Main Menu"
msgstr ""
#: src/window.blp:72
#: src/window.blp:48
msgid "Settings"
msgstr "Настройки"
#: src/window.blp:79
#: src/window.blp:75
msgid "Sections"
msgstr ""
#: src/window.blp:113
msgid "Shop"
msgstr "Магазин"
#: src/window.blp:99
#: src/window.blp:131
msgid "_Preferences"
msgstr ""
#: src/window.blp:104
#: src/window.blp:136
msgid "_Keyboard Shortcuts"
msgstr ""
#: src/window.blp:109
#: src/window.blp:141
msgid "_About TuneIt"
msgstr "О программе"
#: src/settings/widgets/deps_alert_dialog.blp:5
msgid "The module has unmet dependencies"
msgstr "Модуль имеет неудовлетворенные зависимости"
#: src/settings/widgets/deps_alert_dialog.blp:8
msgid "Ignore"
msgstr "Игнорировать"
#: src/settings/widgets/deps_alert_dialog.blp:9
msgid "Skip module"
msgstr "Пропустить модуль"
#: src/settings/widgets/service_dialog.py:13
msgid "Dbus service is disabled or unresponsive."
msgstr "Dbus сервис отключен или не отвечает."
......
from .os import OSReleaseChecker
from .path import PathChecker
from .binary import BinaryChecker
class DependencyCheckerFactory:
def __init__(self):
self._checkers = {
'os': OSReleaseChecker,
'path': PathChecker,
'binary': BinaryChecker,
}
def create_checker(self, dependency_type):
checker_class = self._checkers.get(dependency_type)
if not checker_class:
raise ValueError(f"Unfinished type of dependence: {dependency_type}")
return checker_class()
class DependencyManager:
def __init__(self):
self.factory = DependencyCheckerFactory()
def _verify(self, items, check_type):
results = []
for item_type, expected_value in items.items():
try:
checker = self.factory.create_checker(item_type)
result = checker.check(expected_value, is_conflict=(check_type == "conflict"))
results.append({
'type': check_type,
'name': item_type,
'success': result['success'],
'actual': result['actual'],
'expected': result['expected'],
'error': result.get('error', '')
})
except Exception as e:
print(f"Error when checking {item_type}: {str(e)}")
results.append({
'type': check_type,
'name': item_type,
'success': False,
'error': str(e)
})
return results
def verify_deps(self, dependencies):
return self._verify(dependencies, "dependency")
def verify_conflicts(self, conflicts):
return self._verify(conflicts, "conflict")
def format_results(self, results):
message = ""
for result in results:
label = {
'dependency': f"{result['name']} {_("dependency")}",
'conflict': f"{result['name']} {_("conflict")}"
}[result['type']]
status = "✓" if result['success'] else "✕"
message += f"{label} {status}\n"
if 'actual' in result:
message += f" {_("Actual")} {result['actual']}\n"
message += f" {_("Expected")}: {result['expected']}\n"
if result['error']:
message += f" {_("Error")} {result['error']}\n"
return message
class DependencyChecker:
def check(self, expected_value, is_conflict=False):
raise NotImplementedError("check() method must be implemented in the subclass!")
from .base import DependencyChecker
class BinaryChecker(DependencyChecker):
def check(self, expected_value, is_conflict=False):
from shutil import which
binary_path = which(expected_value)
if isinstance(expected_value, (str, bytes)):
expected_values = (expected_value,)
else:
expected_values = tuple(expected_value)
if is_conflict:
success = binary_path is None
else:
success = binary_path is not None
return {
'success': success,
'actual': binary_path,
'expected': expected_values
}
from .base import DependencyChecker
class OSReleaseChecker(DependencyChecker):
def check(self, expected_value='Etersoft Ximper', is_conflict=False):
from ..backends.file import FileBackend
actual_name = FileBackend({'file_path': '/etc/os-release'}).get_value('NAME', 's')
if isinstance(expected_value, (str, bytes)):
expected_values = (expected_value,)
else:
expected_values = tuple(expected_value)
if is_conflict:
success = actual_name not in expected_values
else:
success = actual_name in expected_values
return {
'success': success,
'actual': actual_name,
'expected': expected_values
}
from .base import DependencyChecker
class PathChecker(DependencyChecker):
def check(self, expected_value, is_conflict=False):
from os import path
file_path = path.expanduser(expected_value)
file_path = path.expandvars(file_path)
file_exists = path.exists(file_path)
if isinstance(expected_value, (str, bytes)):
expected_values = (expected_value,)
else:
expected_values = tuple(expected_value)
if is_conflict:
success = not file_exists
else:
success = file_exists
return {
'success': success,
'actual': file_path,
'expected': expected_values,
'exists': file_exists
}
......@@ -3,7 +3,9 @@ from .page import Page
from .sections import SectionFactory
from .tools.yml_tools import load_modules
from .widgets.deps_alert_dialog import TuneItDepsAlertDialog
from .deps import DependencyManager
def init_settings_stack(stack, listbox, split_view):
yaml_data = load_modules()
......@@ -11,6 +13,8 @@ def init_settings_stack(stack, listbox, split_view):
modules_dict = {}
pages_dict = {}
dep_manager = DependencyManager()
if stack.get_pages():
print("Clear pages...")
listbox.remove_all()
......@@ -20,6 +24,31 @@ def init_settings_stack(stack, listbox, split_view):
print("First init...")
for module_data in yaml_data:
deps_results = dep_manager.verify_deps(module_data.get('deps', {}))
conflicts_results = dep_manager.verify_conflicts(module_data.get('conflicts', {}))
deps_message = dep_manager.format_results(deps_results)
conflicts_message = dep_manager.format_results(conflicts_results)
all_deps_ok = all(r['success'] for r in deps_results)
all_conflicts_ok = all(r['success'] for r in conflicts_results)
if all_deps_ok and all_conflicts_ok:
print("Deps: OK")
else:
dialog = TuneItDepsAlertDialog()
dialog.set_body(module_data['name'])
dialog.deps_message_textbuffer.set_text(
f"{deps_message}\n{conflicts_message}"
)
response = dialog.user_question(listbox.get_root())
print(f"RESPONSE: {response}")
if response == "skip":
break
module = Module(module_data)
modules_dict[module.name] = module
......
using Gtk 4.0;
using Adw 1;
template $TuneItDepsAlertDialog: Adw.AlertDialog {
heading: _("The module has unmet dependencies");
responses [
close: _("Ignore") destructive,
skip: _("Skip module") suggested,
]
close-response: "close";
extra-child: Gtk.TextView{
wrap-mode: word_char;
buffer: Gtk.TextBuffer deps_message_textbuffer {};
};
}
from gi.repository import GObject, Adw, Gtk
from time import sleep
@Gtk.Template(resource_path='/ru.ximperlinux.TuneIt/settings/widgets/deps_alert_dialog.ui')
class TuneItDepsAlertDialog(Adw.AlertDialog):
__gtype_name__ = "TuneItDepsAlertDialog"
deps_message_textbuffer = Gtk.Template.Child()
response = ""
def user_question(self, window):
self.present(window)
def on_response(dialog, response):
self.response = response
self.connect('response', on_response)
while True:
if self.response != "":
return self.response
else:
sleep(0.1)
continue
......@@ -3,6 +3,7 @@
<gresource prefix="/ru.ximperlinux.TuneIt">
<file preprocess="xml-stripblanks">window.ui</file>
<file preprocess="xml-stripblanks">settings/widgets/panel_row.ui</file>
<file preprocess="xml-stripblanks">settings/widgets/deps_alert_dialog.ui</file>
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
</gresource>
</gresources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment