Skip to content

Commit

Permalink
Add system information panel
Browse files Browse the repository at this point in the history
  • Loading branch information
jeckel committed Nov 5, 2024
1 parent b029459 commit 3dab87b
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 42 deletions.
6 changes: 3 additions & 3 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import typer

from service_locator import ServiceContainer
from service_locator import ServiceLocator
from models import Project
from presentation import MainApp

Expand All @@ -9,8 +9,8 @@

@app.command()
def tui(project_path: str) -> None:
ServiceContainer()
ServiceContainer.context().project = Project.from_json(json_path=project_path)
ServiceLocator()
ServiceLocator.context().project = Project.from_json(json_path=project_path)
tui_app = MainApp()
tui_app.run()

Expand Down
6 changes: 3 additions & 3 deletions src/presentation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from textual.css.query import NoMatches

from models import Project
from service_locator import ServiceContainer
from service_locator import ServiceLocator
from .component.message import TerminalCommandRequested

from .composer import ComposerCommandRequested
Expand Down Expand Up @@ -32,7 +32,7 @@ class MainApp(App[None]):

def __init__(self):
super().__init__()
self._project = ServiceContainer.context().current_project
self._project = ServiceLocator.context().current_project
self.title = f"DX Companion - {self._project.name}"

def on_mount(self) -> None:
Expand All @@ -48,7 +48,7 @@ def action_toggle_sidebar(self) -> None:
def action_composer_script(self, event: ComposerCommandRequested) -> None:
def refresh_composer(result: bool | None):
if event.refresh_composer_on_success and result:
ServiceContainer.composer_client().reset_updatable_packages()
ServiceLocator.composer_client().reset_updatable_packages()

self.query_one(Sidebar).add_class("-hidden")
self.app.push_screen(
Expand Down
6 changes: 3 additions & 3 deletions src/presentation/component/sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from presentation.component.action_option_list import ActionOptionList
from presentation.composer.composer_script_option_list import ComposerScriptOptionList
from service_locator import ServiceContainer
from service_locator import ServiceLocator


class Sidebar(Container):
Expand All @@ -22,13 +22,13 @@ class Sidebar(Container):
"""

def __init__(self, **kwargs):
self._project = ServiceContainer.context().current_project
self._project = ServiceLocator.context().current_project
super().__init__(**kwargs)
self.add_class("-hidden")

def compose(self) -> ComposeResult:

if len(ServiceContainer.composer_client().scripts(self._project)) > 0:
if len(ServiceLocator.composer_client().scripts(self._project)) > 0:
yield ComposerScriptOptionList(self._project)
if self._project.actions is None:
return
Expand Down
8 changes: 4 additions & 4 deletions src/presentation/composer/composer_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from textual.worker import Worker, WorkerState

from models.composer import Composer
from service_locator import ServiceContainer
from service_locator import ServiceLocator

from .composer_packages_table import ComposerPackagesTable
from .composer_script_button import ComposerScriptButton
Expand All @@ -29,7 +29,7 @@ class ComposerContainer(Container):
"""

def __init__(self, **kwargs):
self._project = ServiceContainer.context().current_project
self._project = ServiceLocator.context().current_project
self.composer = Composer.from_json(self._project.path)
super().__init__(**kwargs)

Expand Down Expand Up @@ -62,15 +62,15 @@ async def on_mount(self):

@work(exclusive=True, thread=True)
async def _load_composer(self) -> dict[str, str]:
return ServiceContainer.composer_client().updatable_packages()
return ServiceLocator.composer_client().updatable_packages()

@on(Worker.StateChanged)
async def refresh_packages(self, event: Worker.StateChanged) -> None:
"""Called when the worker state changes."""
if event.state != WorkerState.SUCCESS:
return
packages_updatable = event.worker.result
composer = ServiceContainer.composer_client().composer_json(self._project)
composer = ServiceLocator.composer_client().composer_json(self._project)
package_table: ComposerPackagesTable = self.query_one(
"#composer-packages-table"
)
Expand Down
4 changes: 2 additions & 2 deletions src/presentation/composer/composer_script_option_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from textual.widgets.option_list import Option

from models import Project
from service_locator import ServiceContainer
from service_locator import ServiceLocator
from .composer_message import ComposerCommandRequested


Expand All @@ -15,7 +15,7 @@ def __init__(self, project: Project, **kwargs):
super().__init__(
*(
Option(script)
for script in ServiceContainer.composer_client().scripts(self.project)
for script in ServiceLocator.composer_client().scripts(self.project)
),
**kwargs
)
Expand Down
4 changes: 2 additions & 2 deletions src/presentation/docker/container_log_widget.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from textual.widgets import RichLog
from textual import work

from service_locator import ServiceContainer
from service_locator import ServiceLocator


class ContainerLogWidget(RichLog):
Expand All @@ -13,6 +13,6 @@ def stream_logs(self, container_id: str):
self.clear()
self.border_title = f"Logs for container {container_id}"

for log in ServiceContainer.docker_client().get_container_logs(container_id):
for log in ServiceLocator.docker_client().get_container_logs(container_id):
# Convert bytes to string and update the logs widget
self.write(log.decode("utf-8").strip())
6 changes: 3 additions & 3 deletions src/presentation/docker/container_select.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from textual.widgets import Select

from service_locator import ServiceContainer
from service_locator import ServiceLocator


class ContainerSelect(Select):
def __init__(self, **kargs):
super().__init__(
(
(docker_container.name, docker_container.id)
for docker_container in ServiceContainer.docker_client().get_running_containers()
for docker_container in ServiceLocator.docker_client().get_running_containers()
),
**kargs
)
Expand All @@ -17,5 +17,5 @@ def refresh_container_list(self):
self.clear()
self.set_options(
(docker_container.name, docker_container.id)
for docker_container in ServiceContainer.docker_client().get_running_containers()
for docker_container in ServiceLocator.docker_client().get_running_containers()
)
8 changes: 4 additions & 4 deletions src/presentation/summary/composer_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from models.composer import Composer
from presentation.composer.composer_screen import ComposerScreen
from service_locator import ServiceContainer
from service_locator import ServiceLocator


class ComposerCard(Container):
Expand All @@ -34,15 +34,15 @@ class ComposerCard(Container):

def __init__(self, **kwargs):
super().__init__(**kwargs)
self._project = ServiceContainer.context().current_project
self._project = ServiceLocator.context().current_project
self._composer_panel = Static(id="composer_panel")

def compose(self) -> ComposeResult:
yield self._composer_panel
yield Button("[underline]Manage packages", id="toggle_composer_tab")

def on_mount(self) -> None:
self._composer_config = ServiceContainer.composer_client().composer_json(
self._composer_config = ServiceLocator.composer_client().composer_json(
self._project
)
self._composer_panel.update(self.get_composer_panel())
Expand Down Expand Up @@ -97,7 +97,7 @@ def get_composer_panel(self) -> Table:

@work(exclusive=True, thread=True)
async def _load_composer(self, no_cache: bool = False) -> dict[str, str]:
return ServiceContainer.composer_client().updatable_packages()
return ServiceLocator.composer_client().updatable_packages()

@on(Worker.StateChanged)
async def refresh_listview(self, event: Worker.StateChanged) -> None:
Expand Down
6 changes: 4 additions & 2 deletions src/presentation/summary/summary_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from textual.widgets import Markdown

from .composer_card import ComposerCard
from service_locator import ServiceContainer
from service_locator import ServiceLocator
from .system_card import SystemCard


class ProjectSummaryContainer(Container):
Expand All @@ -20,7 +21,7 @@ class ProjectSummaryContainer(Container):

def __init__(self, **kwargs):
super().__init__(**kwargs)
self._project = ServiceContainer.context().current_project
self._project = ServiceLocator.context().current_project

def compose(self):
yield Markdown(
Expand All @@ -29,6 +30,7 @@ def compose(self):
"""
)
yield ComposerCard()
yield SystemCard()

def refresh_composer(self):
self.query_one(ComposerCard).on_mount()
4 changes: 2 additions & 2 deletions src/presentation/summary/summary_screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from presentation import Sidebar, DockerContainer
from .summary_container import ProjectSummaryContainer
from service_locator import ServiceContainer
from service_locator import ServiceLocator


class SummaryScreen(Screen):
Expand All @@ -26,7 +26,7 @@ def compose(self) -> ComposeResult:
yield ProjectSummaryContainer()
with TabPane(title="Docker", id="docker-pan"):
yield DockerContainer(
project=ServiceContainer.context().current_project
project=ServiceLocator.context().current_project
)
yield Footer()

Expand Down
50 changes: 50 additions & 0 deletions src/presentation/summary/system_card.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from rich.style import Style
from rich.table import Table
from textual.app import ComposeResult
from textual.containers import Container
from textual.widgets import Static

from service_locator import ServiceLocator


class SystemCard(Container):
DEFAULT_CSS = """
SystemCard {
height: auto;
width: 45;
border: $primary-background round;
}
"""

def __init__(self, **kwargs):
super().__init__(**kwargs)
self._system_panel = Static(id="system_panel")

def compose(self) -> ComposeResult:
yield self._system_panel

def on_mount(self) -> None:
table = Table(
show_header=False,
box=None,
title="System status",
title_style=Style(color="#bbc8e8", bold=True),
)
table.add_column()
table.add_column(min_width=25, max_width=27)
system_status = ServiceLocator.system_status()
self._add_system_row(table, "Php", system_status.php_version())
self._add_system_row(table, "Composer", system_status.composer_version())
self._add_system_row(table, "Symfony-Cli", system_status.symfony_version())
self._add_system_row(table, "Castor", system_status.castor_version())
self._add_system_row(table, "Docker", system_status.docker_version())
self._add_system_row(table, "Ansible", system_status.ansible_version())

self._system_panel.update(table)

@staticmethod
def _add_system_row(table: Table, label: str, version: str|None) -> None:
table.add_row(
f"[label]{label}:",
f"[blue]{version}" if version is not None else "[orange1]N/A",
)
17 changes: 3 additions & 14 deletions src/service_locator.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
from dependency_injector import containers, providers

from models.app_context import AppContext
from services import DockerClient, ComposerClient
from services import DockerClient, ComposerClient, SystemStatus


class ServiceContainer(containers.DeclarativeContainer):
class ServiceLocator(containers.DeclarativeContainer):
config = providers.Configuration()
docker_client = providers.Singleton(DockerClient)
context = providers.Singleton(AppContext)
composer_client = providers.Singleton(ComposerClient, context=context)
# project = providers.Factory(Project)

# api_client = providers.Singleton(
# ApiClient,
# api_key=config.api_key,
# timeout=config.timeout,
# )
#
# service = providers.Factory(
# Service,
# api_client=api_client,
# )
system_status = providers.Singleton(SystemStatus)
1 change: 1 addition & 0 deletions src/services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .docker_client import DockerClient
from .composer_client import ComposerClient
from .system_status import SystemStatus
63 changes: 63 additions & 0 deletions src/services/system_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import subprocess

from .base_service import BaseService

class SystemStatus(BaseService):
def _capture_output(self, command: list[str]) -> str|None:
try:
result = subprocess.run(command, capture_output=True, text=True, check=True)
return result.stdout
except (subprocess.CalledProcessError, FileNotFoundError):
return None

def php_version(self) -> str|None:
output = self._capture_output(['php', '-v'])
if output is None:
return None
version_line = output.splitlines()[0]
version = version_line.split()[1]
return version
def composer_version(self) -> str|None:
output = self._capture_output(['composer', '--version'])
if output is None:
return None

version_line = output.splitlines()[0]
version = version_line.split()[2]
return version

def castor_version(self) -> str|None:
output = self._capture_output(['castor', '--version'])
if output is None:
return None

version_line = output.splitlines()[0]
version = version_line.split()[1]
return version[1:]

def symfony_version(self) -> str|None:
output = self._capture_output(['symfony', 'version', '--no-ansi'])
if output is None:
return None

version_line = output.splitlines()[0]
version = version_line.split()[3]
return version

def docker_version(self) -> str|None:
output = self._capture_output(['docker', '-v'])
if output is None:
return None

version_line = output.splitlines()[0]
version = version_line.split()[2]
return version[:-1]

def ansible_version(self) -> str|None:
output = self._capture_output(['ansible', '--version'])
if output is None:
return None

version_line = output.splitlines()[0]
version = version_line.split()[2]
return version[:-1]

0 comments on commit 3dab87b

Please sign in to comment.