| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- # -*- 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)
|