Commit be056698 authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

add urls to projects list

parent 2cad28db
Loading
Loading
Loading
Loading
+86 −38
Original line number Diff line number Diff line
import subprocess
from pathlib import Path
from typing import Optional, Union, Iterator, List, Any
from typing import Optional, Union, Iterator, List
from typing_extensions import Annotated
import gettext
import tempfile
@@ -14,7 +14,7 @@ import shutil
from functools import cache
import socket
from .eggs import get_yolk
from .model import MaXProjectConfig, db, MaxInstall, DEFAULT_DOT_BASEX_FILE
from .model import MaXProjectConfig, db as climax_db, DEFAULT_DOT_BASEX_FILE
from .config import (
    WELCOME_PAGE,
    USER_MAX_DIR,
@@ -34,7 +34,7 @@ from .config import (
    CRASH_MESSAGE,
)

locales_dir = Path(__file__).parent / "locales"
locales_dir = Path(Path(__file__).parent, "locales")
gettext.bindtextdomain("messages", str(locales_dir))
gettext.textdomain("messages")
_ = gettext.gettext
@@ -46,20 +46,33 @@ from rich.progress import track # noqa: E402
from rich.console import Console  # noqa: E402
from rich.table import Table  # noqa: E402

climax_db = db()

def _ping_projects(max_install_directory: Path, meta: dict = None):
    if isinstance(max_install_directory, str):
        max_install_directory = Path(max_install_directory)
    db = climax_db()
    max_install = db.fetch_or_create_project_from_path(
        str(max_install_directory.resolve())
    )
    if meta is not None:
        max_install.meta = meta
    db.save(max_install)
    db.commit()


last_project_dir = None


def _track_last_dir(directory: Any) -> Any:
def _track_last_dir(directory: Path) -> Path:
    global last_project_dir
    last_project_dir = directory
    _ping_projects(directory)
    return directory


def _port_is_in_use(port: int) -> bool:
def _port_is_in_use(port: int, host: str = "localhost") -> bool:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        return s.connect_ex(("localhost", port)) == 0
        return s.connect_ex((host, port)) == 0


def _closer_free_port_to(start_port: int) -> int:
@@ -279,6 +292,7 @@ def _sync_bundles(root_directory: Path):
                dir_name, _ = os.path.splitext(os.path.basename(bundle["url"]))
                Path(bundles_dir, dir_name).rename(bundle_dir)
                bundle_archive_path.unlink()
    _ping_projects(root_directory)


def _copy_max_tree(
@@ -342,14 +356,12 @@ def _install_new_max_instance(root_directory: Path, init_values: dict = None):
    welcome_page = Path(root_directory, "content_html", "fr", "index.html")
    with open(welcome_page, "w", encoding="utf-8") as f:
        f.write(WELCOME_PAGE)
    climax_db.save(MaxInstall(None, str(root_directory.resolve())))
    climax_db.commit()
    rich_print(
        "[bold]"
        + _("La nouvelle instance de MaX est prête dans {}").format(root_directory)
        + "[/bold]"
    )
    print(
    rich_print(
        _(
            'Vous pouvez éventuellement installer un projet de démonstration avec "climax demo".'
        )
@@ -414,7 +426,9 @@ def new(
    ensure_java()
    root_directory, is_max = ensure_available_max_directory(directory)
    if is_max:
        print(_("Le dossier contient une instance de MaX. Utilisez la commande sync"))
        rich_print(
            _("Le dossier contient une instance de MaX. Utilisez la commande sync")
        )
        raise typer.Exit()
    init_values = None
    if interactive:
@@ -475,27 +489,14 @@ def demo(
        )


@app.command(
    help=_(
        "Démarre l'instance de MaX.\n\nSi les ports choisis ne sont pas disponibles, climax tentera de lancer le serveur sur d'autres ports."
    )
)
def start(
def _start_http_server(
    http_host: str = WEB_HOST,
    http_port: int = WEB_PORT,
    basex_port: int = BASEX_PORT,
    http_stop_port: int = STOP_PORT,
    service: Annotated[
        bool,
        typer.Option(
            help=_(
                'Démarrer MaX en tant que service. Utilisez "climax stop" pour arrêter le service.'
            )
        ),
    ] = False,
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
    service: bool = False,
    directory: str = os.getcwd(),
    ping_projects: bool = True,
):
    basex_port = _closer_free_port_to(basex_port)
    http_port = _closer_free_port_to(http_port)
@@ -523,7 +524,47 @@ def start(
        + _("Démarrage de MaX sur http://{}:{}").format(http_host, http_port)
        + "[/green]"
    )
    if ping_projects:
        _ping_projects(
            directory,
            {
                "basex_port": basex_port,
                "http_port": http_port,
                "http_host": http_host,
                "http_stop_port": http_stop_port,
            },
        )
    try:
        subprocess.run(process_args)
    except KeyboardInterrupt:
        _ping_projects(directory, {})


@app.command(
    help=_(
        "Démarre l'instance de MaX.\n\nSi les ports choisis ne sont pas disponibles, climax tentera de lancer le serveur sur d'autres ports."
    )
)
def start(
    http_host: str = WEB_HOST,
    http_port: int = WEB_PORT,
    basex_port: int = BASEX_PORT,
    http_stop_port: int = STOP_PORT,
    service: Annotated[
        bool,
        typer.Option(
            help=_(
                'Démarrer MaX en tant que service. Utilisez "climax stop" pour arrêter le service.'
            )
        ),
    ] = False,
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    _start_http_server(
        http_host, http_port, basex_port, http_stop_port, service, directory, True
    )


def _choose_between(values: List[str], label: str = "") -> str:
@@ -560,6 +601,7 @@ def stop(
        "stop",
    ]
    subprocess.run(process_args)
    _ping_projects(directory, {})


@app.command(help=_("Affiche la configuration de MaX"))
@@ -599,12 +641,13 @@ def freeze(
    port_number = _free_port()
    stop_port = port_number + 1
    stop_port = _closer_free_port_to(stop_port)
    start(
    _start_http_server(
        WEB_HOST,
        port_number,
        http_stop_port=stop_port,
        service=True,
        directory=directory,
        ping_projects=False,
    )
    # copy website
    try:
@@ -811,10 +854,8 @@ def sync(

    if is_max:
        _sync_max_instance(root_directory)
        climax_db.save(MaxInstall(None, str(root_directory.resolve())))
        climax_db.commit()
    else:
        print(
        rich_print(
            _(
                "Le dossier ne contient pas une instance de MaX. Utiliser la commande new"
            )
@@ -904,14 +945,21 @@ def static_list(
@app.command(help=_("Liste les projets gérés par climax"))
def projects():
    console = Console()
    table = Table(_("Projet"), _("Chemin"), show_lines=True)
    for max_install in climax_db.projects():
    table = Table(_("Projet"), _("Chemin"), _("URL"), show_lines=True)
    db = climax_db()
    for max_install in db.projects():
        if max_install.exists():
            config = max_install.config()
            table.add_row(config.title, max_install.path)
            url = ""
            if max_install.meta:
                if _port_is_in_use(
                    max_install.meta.get("http_port"), max_install.meta.get("http_host")
                ):
                    url = f"http://{max_install.meta.get('http_host')}:{max_install.meta.get('http_port')}/"
            table.add_row(config.title, max_install.path, url)
        else:
            climax_db.delete("maxinstall", "id = ?", max_install.id)
            climax_db.commit()
            db.delete("maxinstall", "id = ?", max_install.id)
            db.commit()
    console.print(table)


+27 −2
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ from bs4 import BeautifulSoup
from pathlib import Path
from typing import Union, Dict, Iterator
import os
import json
import oora
from dataclasses import dataclass
from .config import USER_MAX_DIR
@@ -260,6 +261,7 @@ class MaXProjectConfig:
class MaxInstall:
    id: int
    path: str
    _meta: str = None

    def exists(self):
        own_path = Path(self.path)
@@ -271,10 +273,32 @@ class MaxInstall:
        if self.exists():
            return MaXProjectConfig(Path(self.path, "config.xml"))

    @property
    def meta(self) -> dict:
        if self._meta is not None:
            return json.loads(self._meta)
        return {}

    @meta.setter
    def meta(self, data: dict = None):
        if data is not None:
            self._meta = json.dumps(data)
        else:
            self._meta = None


class ClimaxStore(oora.DB):
    def fetch_or_create_project_from_path(self, path) -> Union[MaxInstall, None]:
        row = self.execute(
            "select id, path, _meta from maxinstall where path = ?", [path]
        ).fetchone()
        if row:
            return self.hydrate(MaxInstall, row)
        else:
            return MaxInstall(None, path)

    def projects(self) -> Iterator[MaxInstall]:
        for row in self.execute("select * from maxinstall"):
        for row in self.execute("select id, path, _meta from maxinstall"):
            yield self.hydrate(MaxInstall, row)


@@ -285,6 +309,7 @@ def db() -> ClimaxStore:
            "climax.sqlite3",
        ),
        migrations={
            "0000": "CREATE TABLE IF NOT EXISTS maxinstall(id INTEGER PRIMARY KEY, path TEXT UNIQUE ON CONFLICT REPLACE NOT NULL);"
            "0000": "CREATE TABLE IF NOT EXISTS maxinstall(id INTEGER PRIMARY KEY, path TEXT UNIQUE ON CONFLICT REPLACE NOT NULL);",
            "0001": "ALTER TABLE maxinstall ADD COLUMN _meta TEXT;",
        },
    ).migrate()