Skip to content

Commit 129c290

Browse files
GitHKAndrei Neagu
andauthored
🐛 Add internal scheduler UI in dynamic-scheduler (#8410)
Co-authored-by: Andrei Neagu <[email protected]>
1 parent 67d0e3c commit 129c290

File tree

14 files changed

+133
-39
lines changed

14 files changed

+133
-39
lines changed

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/frontend/_setup.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
from fastapi import FastAPI
33

44
from ...core.settings import ApplicationSettings
5+
from . import routes_external_scheduler, routes_internal_scheduler
56
from ._utils import set_parent_app
6-
from .routes import router
77

88

99
def initialize_frontend(app: FastAPI) -> None:
1010
settings: ApplicationSettings = app.state.settings
1111

12-
nicegui.app.include_router(router)
12+
if settings.DYNAMIC_SCHEDULER_USE_INTERNAL_SCHEDULER:
13+
nicegui.app.include_router(routes_internal_scheduler.router)
14+
else:
15+
nicegui.app.include_router(routes_external_scheduler.router)
1316

1417
nicegui.ui.run_with(
1518
app,

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/frontend/routes/_index.py renamed to services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/frontend/routes_external_scheduler/_index.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import arrow
12
import httpx
23
from common_library.json_serialization import json_dumps, json_loads
34
from fastapi import FastAPI
@@ -10,7 +11,7 @@
1011
from ....services.service_tracker import TrackedServiceModel, get_all_tracked_services
1112
from ....services.service_tracker._models import SchedulerServiceState
1213
from .._utils import get_parent_app, get_settings
13-
from ._render_utils import base_page, get_iso_formatted_date
14+
from ._render_utils import base_page
1415

1516
router = APIRouter()
1617

@@ -21,7 +22,7 @@ def _render_service_details(node_id: NodeID, service: TrackedServiceModel) -> No
2122
"Display State": ("label", service.current_state),
2223
"Last State Change": (
2324
"label",
24-
get_iso_formatted_date(service.last_state_change),
25+
arrow.get(service.last_state_change).isoformat(),
2526
),
2627
"UserID": ("copy", f"{service.user_id}"),
2728
"ProjectID": ("copy", f"{service.project_id}"),

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/frontend/routes/_render_utils.py renamed to services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/frontend/routes_external_scheduler/_render_utils.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from collections.abc import Iterator
22
from contextlib import contextmanager
33

4-
import arrow
54
from nicegui import ui
65

76

@@ -17,7 +16,3 @@ def base_page(*, title: str | None = None) -> Iterator[None]:
1716
ui.label(display_title)
1817

1918
yield None
20-
21-
22-
def get_iso_formatted_date(timestamp: float) -> str:
23-
return arrow.get(timestamp).isoformat()

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/frontend/routes/_service.py renamed to services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/frontend/routes_external_scheduler/_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
stop_dynamic_service,
1010
)
1111
from settings_library.utils_service import DEFAULT_FASTAPI_PORT
12-
from simcore_service_dynamic_scheduler.services.rabbitmq import get_rabbitmq_rpc_client
1312

1413
from ....core.settings import ApplicationSettings
14+
from ....services.rabbitmq import get_rabbitmq_rpc_client
1515
from ....services.service_tracker import get_tracked_service, remove_tracked_service
1616
from .._utils import get_parent_app, get_settings
1717
from ._render_utils import base_page
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from nicegui import APIRouter
2+
3+
from . import _index
4+
5+
router = APIRouter()
6+
7+
router.include_router(_index.router)
8+
9+
__all__: tuple[str, ...] = ("router",)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from nicegui import APIRouter, ui
2+
3+
router = APIRouter()
4+
5+
6+
@router.page("/")
7+
async def index():
8+
ui.label("PLACEHOLDER for internal scheduler UI")
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import pytest
2+
3+
4+
@pytest.fixture
5+
def use_internal_scheduler() -> bool:
6+
return True
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# pylint:disable=redefined-outer-name
2+
# pylint:disable=unused-argument
3+
4+
from helpers import assert_contains_text
5+
from playwright.async_api import Page
6+
from simcore_service_dynamic_scheduler.api.frontend._utils import get_settings
7+
8+
pytest_simcore_core_services_selection = [
9+
"postgres",
10+
"rabbit",
11+
"redis",
12+
]
13+
14+
pytest_simcore_ops_services_selection = [
15+
"redis-commander",
16+
]
17+
18+
19+
async def test_placeholder_index(
20+
app_runner: None, async_page: Page, server_host_port: str
21+
):
22+
await async_page.goto(
23+
f"{server_host_port}{get_settings().DYNAMIC_SCHEDULER_UI_MOUNT_PATH}"
24+
)
25+
26+
await assert_contains_text(async_page, "PLACEHOLDER for internal scheduler UI")

services/dynamic-scheduler/tests/unit/api_frontend/conftest.py

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
import subprocess
66
from collections.abc import AsyncIterable
77
from contextlib import suppress
8-
from typing import Final
9-
from unittest.mock import AsyncMock
108

11-
import nicegui
129
import pytest
1310
import sqlalchemy as sa
1411
from fastapi import FastAPI, status
@@ -17,6 +14,7 @@
1714
from hypercorn.config import Config
1815
from playwright.async_api import Page, async_playwright
1916
from pytest_mock import MockerFixture
17+
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
2018
from pytest_simcore.helpers.postgres_tools import PostgresTestConfig
2119
from pytest_simcore.helpers.typing_env import EnvVarsDict
2220
from settings_library.rabbit import RabbitSettings
@@ -26,57 +24,76 @@
2624
from simcore_service_dynamic_scheduler.core.settings import ApplicationSettings
2725
from tenacity import AsyncRetrying, stop_after_delay, wait_fixed
2826

29-
_MODULE: Final["str"] = "simcore_service_dynamic_scheduler"
30-
3127

3228
@pytest.fixture
3329
def disable_status_monitor_background_task(mocker: MockerFixture) -> None:
3430
mocker.patch(
35-
f"{_MODULE}.services.status_monitor._monitor.Monitor._worker_check_services_require_status_update"
31+
"simcore_service_dynamic_scheduler.services.status_monitor._monitor.Monitor._worker_check_services_require_status_update"
3632
)
3733

3834

3935
@pytest.fixture
40-
def mock_stop_dynamic_service(mocker: MockerFixture) -> AsyncMock:
41-
async_mock = AsyncMock()
42-
mocker.patch(
43-
f"{_MODULE}.api.frontend.routes._service.stop_dynamic_service", async_mock
44-
)
45-
return async_mock
46-
47-
48-
@pytest.fixture
49-
def mock_remove_tracked_service(mocker: MockerFixture) -> AsyncMock:
50-
async_mock = AsyncMock()
51-
mocker.patch(
52-
f"{_MODULE}.api.frontend.routes._service.remove_tracked_service", async_mock
53-
)
54-
return async_mock
36+
def use_internal_scheduler() -> bool:
37+
pytest.fail("please define use_internal_scheduler fixture in your tests folder")
5538

5639

5740
@pytest.fixture
5841
def app_environment(
42+
monkeypatch: pytest.MonkeyPatch,
5943
app_environment: EnvVarsDict,
44+
use_internal_scheduler: bool,
6045
postgres_db: sa.engine.Engine,
6146
postgres_host_config: PostgresTestConfig,
6247
disable_status_monitor_background_task: None,
6348
rabbit_service: RabbitSettings,
6449
redis_service: RedisSettings,
6550
remove_redis_data: None,
6651
) -> EnvVarsDict:
67-
return app_environment
52+
to_set = {
53+
"DYNAMIC_SCHEDULER_USE_INTERNAL_SCHEDULER": f"{use_internal_scheduler}",
54+
}
55+
setenvs_from_dict(monkeypatch, to_set)
56+
return {**app_environment, **to_set}
6857

6958

7059
@pytest.fixture
7160
def server_host_port() -> str:
7261
return f"127.0.0.1:{DEFAULT_FASTAPI_PORT}"
7362

7463

64+
def _reset_nicegui_app() -> None:
65+
# forces rebuild of middleware stack on next test
66+
67+
# below is based on nicegui.testing.general_fixtures.nicegui_reset_globals
68+
69+
from nicegui import Client, app
70+
from starlette.routing import Route
71+
72+
for route in list(app.routes):
73+
if isinstance(route, Route) and route.path.startswith("/_nicegui/auto/static/"):
74+
app.remove_route(route.path)
75+
76+
all_page_routes = set(Client.page_routes.values())
77+
all_page_routes.add("/")
78+
for path in all_page_routes:
79+
app.remove_route(path)
80+
81+
for route in list(app.routes):
82+
if (
83+
isinstance(route, Route)
84+
and "{" in route.path
85+
and "}" in route.path
86+
and not route.path.startswith("/_nicegui/")
87+
):
88+
app.remove_route(route.path)
89+
90+
app.middleware_stack = None
91+
app.user_middleware.clear()
92+
93+
7594
@pytest.fixture
7695
def not_initialized_app(app_environment: EnvVarsDict) -> FastAPI:
77-
# forces rebuild of middleware stack on next test
78-
nicegui.app.user_middleware.clear()
79-
nicegui.app.middleware_stack = None
96+
_reset_nicegui_app()
8097
return create_app()
8198

8299

0 commit comments

Comments
 (0)