# -*- coding: utf-8 -*- """ F4: System-Pakete Uebersicht — Tabelle mit Status. """ from __future__ import annotations import shutil import subprocess from textual.widgets import DataTable, Label, Static from .base import InstallerView # Kanonische Paketnamen mit Beschreibung und Typ _RUNTIME_PACKAGES = [ ("python3", "Python Interpreter", "Runtime"), ("python3-venv", "Python virtuelle Umgebungen", "Runtime"), ("python3-pip", "Python Paketmanager", "Runtime"), ("git", "Versionskontrolle", "Runtime"), ("curl", "Datentransfer", "Runtime"), ("wget", "Datei-Download", "Runtime"), ("alsa-utils", "ALSA Audio-Werkzeuge", "Runtime"), ("ffmpeg", "Audio/Video Konvertierung", "Runtime"), ] _BUILD_PACKAGES = [ ("python3-dev", "Python C-Extension Header", "Build"), ("portaudio-dev", "Audio-I/O (sounddevice)", "Build"), ("alsa-dev", "ALSA Header (pyalsaaudio)", "Build"), ("openblas-dev", "BLAS (numpy/scipy)", "Build"), ("lapack-dev", "LAPACK (scipy)", "Build"), ("gfortran", "Fortran Compiler (scipy)", "Build"), ("ffi-dev", "FFI (cryptography)", "Build"), ("ssl-dev", "SSL/TLS (cryptography/aiohttp)", "Build"), ] _WIFI_PACKAGES = [ ("hostapd", "WiFi Access Point", "WiFi"), ("dnsmasq", "DHCP/DNS Server", "WiFi"), ("iptables", "Firewall/NAT", "WiFi"), ] def _detect_pm() -> tuple[str, list[str]]: """Erkennt den Paketmanager und gibt (name, check_cmd) zurueck.""" managers = [ ("apt", ["dpkg", "-s"]), ("dnf", ["rpm", "-q"]), ("pacman", ["pacman", "-Q"]), ("zypper", ["rpm", "-q"]), ] for name, check in managers: # Pruefe ob der Install-Befehl existiert install_cmd = {"apt": "apt-get", "dnf": "dnf", "pacman": "pacman", "zypper": "zypper"} if shutil.which(install_cmd.get(name, name)): return name, check return "", [] def _resolve_name(canonical: str, pm: str) -> str: """Loest kanonischen Namen fuer den Paketmanager auf.""" # Importiere die Map aus dem Runner from ..runner import InstallRunner mapping = InstallRunner._PKG_MAP.get(canonical, {}) return mapping.get(pm, canonical) def _check_installed(pkg_name: str, check_cmd: list[str]) -> str: """Prueft ob ein Paket installiert ist.""" # Zuerst: Kommandozeilen-Tool direkt pruefen simple = pkg_name.split("-")[0].replace("lib", "").replace("19", "") if shutil.which(pkg_name) or shutil.which(simple): return "Installiert" # python3 spezial if pkg_name in ("python3", "python3-venv", "python3-pip", "python"): return "Installiert" if shutil.which("python3") else "Fehlt" # Paketmanager fragen if check_cmd: try: result = subprocess.run( [*check_cmd, pkg_name], capture_output=True, timeout=3, ) return "Installiert" if result.returncode == 0 else "Fehlt" except Exception: pass return "?" class RequirementsView(InstallerView): """System-Pakete Uebersicht (F4).""" TITLE = "Pakete" VIEW_ID = "requirements" MODES = ["server", "client", "standalone", "config"] def compose(self): yield Static("── System-Pakete ──", classes="section-header") yield Label("", id="pkg-pm-info") yield DataTable(id="pkg-table") async def on_activate(self) -> None: """Fuellt die Tabelle beim Aktivieren.""" pm_name, check_cmd = _detect_pm() # Paketmanager-Info info = self.query_one("#pkg-pm-info", Label) if pm_name: info.update(f"Paketmanager: {pm_name}\n") else: info.update("Kein unterstuetzter Paketmanager erkannt\n") table = self.query_one("#pkg-table", DataTable) table.clear(columns=True) table.add_columns("Typ", "Paket", "Zweck", "Status") # Runtime for canonical, desc, typ in _RUNTIME_PACKAGES: resolved = _resolve_name(canonical, pm_name) if pm_name else canonical status = _check_installed(resolved, check_cmd) table.add_row(typ, resolved, desc, status) # Build (nicht fuer config) if self._config.mode != "config": for canonical, desc, typ in _BUILD_PACKAGES: resolved = _resolve_name(canonical, pm_name) if pm_name else canonical status = _check_installed(resolved, check_cmd) table.add_row(typ, resolved, desc, status) # WiFi if self._config.mode == "server" and self._config.wifi_enabled: for canonical, desc, typ in _WIFI_PACKAGES: resolved = _resolve_name(canonical, pm_name) if pm_name else canonical status = _check_installed(resolved, check_cmd) table.add_row(typ, resolved, desc, status)