Skip to content

Commit d39af95

Browse files
committed
webgui updates
1 parent b0671c0 commit d39af95

File tree

11 files changed

+141
-52
lines changed

11 files changed

+141
-52
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,5 @@ cython_debug/
161161

162162
# macOS
163163
.DS_store
164+
165+
.sesskey

doc/source/user/gui.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.. _gui:
2+
3+
===
4+
GUI
5+
===
6+
7+
A web based GUI is available for jobflow remote.
8+
Being based on the `FastHTML framework <https://docs.fastht.ml/>`_,
9+
requires the installation of the ``python-fasthtml`` package::
10+
11+
pip install python-fasthtml
12+
13+
.. warning::
14+
15+
The web GUI is an experimental feature and in addition does not
16+
cover all the options available through the CLI.
17+
18+
The GUI can be started from the CLI with the command::
19+
20+
jf gui
21+
22+
This will start a local server and the GUI can be reached with
23+
a browser at the address `http://localhost:5001 <http://localhost:5001>`_.
24+
The default port is 5001, but can be customized with the ``-p``
25+
option.
26+
27+
Some of the features available in the GUI are:
28+
29+
* Selecting the project
30+
* Starting and stopping the Runner
31+
* Query Jobs and Flows based on different criteria
32+
* Explore Jobs and Flows details
33+
* Apply some standard actions on Jobs and Flows

doc/source/user/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ details are found in :ref:`reference`.
2222
advancedoptions
2323
backup
2424
cli
25+
gui
2526

2627
.. toctree::
2728
:hidden:

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ docs = [
4949
"sphinxcontrib-mermaid",
5050
"sphinxcontrib-typer[html]",
5151
]
52+
gui = [
53+
"python-fasthtml>=0.12.0"
54+
]
5255

5356
[project.scripts]
5457
jf = "jobflow_remote.cli.jf:app"
@@ -61,6 +64,7 @@ changelog = "https://matgenix.github.io/jobflow-remote/changelog"
6164

6265
[tool.setuptools.package-data]
6366
jobflow_remote = ["py.typed"]
67+
"jobflow_remote.webgui" = ["*.png", "*.css"]
6468

6569
[build-system]
6670
requires = ["setuptools >= 42", "versioningit ~= 1.0", "wheel"]

src/jobflow_remote/cli/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import jobflow_remote.cli.batch
55
import jobflow_remote.cli.execution
66
import jobflow_remote.cli.flow
7+
import jobflow_remote.cli.gui
78
import jobflow_remote.cli.job
89
import jobflow_remote.cli.project
910
import jobflow_remote.cli.runner

src/jobflow_remote/cli/gui.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from typing import Annotated, Optional
2+
3+
import typer
4+
from jobflow_remote.cli.jf import app
5+
6+
7+
@app.command()
8+
def gui(
9+
port: Annotated[
10+
Optional[int],
11+
typer.Option(
12+
"--port",
13+
"-p",
14+
help="The port where the web server will be started. Defaults to 5001",
15+
),
16+
] = None,
17+
) -> None:
18+
"""
19+
Start the server for the GUI
20+
"""
21+
from jobflow_remote.webgui.webgui import start_gui
22+
start_gui(port=port)

src/jobflow_remote/jobs/jobcontroller.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@ def get_jobs_info(
462462
query. Follows pymongo conventions.
463463
limit
464464
Maximum number of entries to retrieve. 0 means no limit.
465+
skip
466+
The number of documents to omit (from the start of the result set).
465467
466468
Returns
467469
-------
@@ -2299,6 +2301,8 @@ def get_flows_info(
22992301
If True data is fetched from both the Flow collection and Job collection
23002302
with an aggregate. Otherwise, only the Job information in the Flow
23012303
document will be used.
2304+
skip
2305+
The number of documents to omit (from the start of the result set).
23022306
23032307
Returns
23042308
-------
10.7 KB
Binary file not shown.

src/jobflow_remote/webgui/style.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ table {
101101
width: 100%;
102102
border-collapse: collapse;
103103
table-layout: auto;
104-
max-width: 1200px;
105104
}
106105

107106
th, td {

src/jobflow_remote/webgui/webgui.py

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,63 @@
44
from datetime import datetime, timezone
55
from math import ceil
66
from zoneinfo import ZoneInfo
7+
from pathlib import Path
8+
9+
from monty.dev import requires
10+
11+
try:
12+
import fasthtml
13+
from fasthtml.common import (
14+
H1,
15+
H3,
16+
H4,
17+
A,
18+
Button,
19+
Card,
20+
CheckboxX,
21+
Dialog,
22+
Div,
23+
Favicon,
24+
Form,
25+
Group,
26+
Img,
27+
Input,
28+
Label,
29+
Li,
30+
Link,
31+
Main,
32+
Option,
33+
P,
34+
Script,
35+
Select,
36+
Span,
37+
Table,
38+
Td,
39+
Th,
40+
Title,
41+
Tr,
42+
Ul,
43+
fast_app,
44+
serve,
45+
)
46+
except ImportError:
47+
fasthtml = None
48+
49+
# fake rt decorator
50+
def rt_fake(*args, **kwargs):
51+
def wrapper(func):
52+
return func
53+
54+
return wrapper
55+
56+
def fast_app(*args, **kwargs):
57+
return lambda x: x, rt_fake
58+
59+
def fake_function(*args, **kwargs):
60+
return None
61+
62+
Title = Link = Script = Favicon = fake_function
763

8-
from fasthtml.common import (
9-
H1,
10-
H3,
11-
H4,
12-
A,
13-
Button,
14-
Card,
15-
CheckboxX,
16-
Dialog,
17-
Div,
18-
Favicon,
19-
Form,
20-
Group,
21-
Img,
22-
Input,
23-
Label,
24-
Li,
25-
Link,
26-
Main,
27-
Option,
28-
P,
29-
Script,
30-
Select,
31-
Span,
32-
Table,
33-
Td,
34-
Th,
35-
Title,
36-
Tr,
37-
Ul,
38-
fast_app,
39-
serve,
40-
)
4164

4265
from jobflow_remote import ConfigManager
4366
from jobflow_remote.jobs.daemon import DaemonManager, DaemonStatus
@@ -86,9 +109,10 @@ def error_handler(req, exc):
86109
hdrs=(
87110
Link(rel="stylesheet", href="./style.css", type="text/css"),
88111
Script(mermaid_js, type="module"),
89-
Favicon("logo_jfr.png", "logo_jfr.png"),
112+
Favicon("jfr_favicon.ico", "jfr_favicon.ico"),
90113
), # Add custom CSS as a header
91114
exception_handlers=exception_handlers,
115+
static_path=Path(__file__).parent,
92116
)
93117

94118

@@ -129,11 +153,6 @@ def error_handler(req, exc):
129153

130154
list_projects = list(cm.projects.keys())
131155

132-
# job_controllers: dict[str, JobController] = {}
133-
# daemon_managers: dict[str, DaemonManager] = {}
134-
# job_controller: JobController | None = None
135-
# job_controller_actions: dict[str, dict[str, Callable]] | None = None
136-
# daemon_manager: DaemonManager | None = None
137156
job_controllers = {}
138157
daemon_managers = {}
139158
job_controller = None
@@ -384,7 +403,7 @@ def close_dialog():
384403
return ""
385404

386405

387-
# Add these new routes to handle starting and stopping the runner
406+
# handle starting and stopping the runner
388407
@rt("/runner/{proj_name}/start", methods=["POST"])
389408
def start_runner_route(proj_name: str):
390409
dm = daemon_managers[proj_name]
@@ -429,7 +448,6 @@ def stop_runner_route(proj_name: str):
429448
)
430449

431450

432-
# Modify the existing route to update the runner status
433451
@rt("/runner/{proj_name}/status")
434452
def get_runner_status_update(proj_name: str):
435453
status, color = get_runner_status(proj_name)
@@ -458,7 +476,6 @@ def get_runner_status_update(proj_name: str):
458476

459477
def get_runner_status(proj_name: str):
460478
dm = daemon_managers[proj_name]
461-
current_status = None
462479
current_status = dm.check_status()
463480
color = status_colors[current_status]
464481

@@ -727,15 +744,6 @@ def get_info_job_flow(jf_id: str, what: str, proj_name: str):
727744
return P("Job not found")
728745

729746

730-
# @rt("/{proj_name}/flows/graph/{jf_id}")
731-
# def get_graph_job_flow(jf_id: str, proj_name: str):
732-
# flowinfo = job_controller.get_flows_info(limit=1,full=True)[0]
733-
# graph = get_mermaid(flowinfo)
734-
# # return Div(ScrollableArea(Pre(graph,cls="mermaid"), Script("await mermaid.run()")),cls="card")
735-
# # return Div(ScrollableArea(Pre(graph,cls="mermaid"), Script("await mermaid.init()")),cls="card")
736-
# return Div(ScrollableArea(Pre(graph,cls="mermaid"), Script("await mermaid.run({nodes: document.querySelectorAll('.mermaid'),})")),cls="card")
737-
738-
739747
@rt("/{proj_name}/flows/graph/{jf_id}")
740748
def get_graph_job_flow(jf_id: str, proj_name: str):
741749
flowinfo = job_controller.get_flows_info(limit=1, full=True)[0]
@@ -1160,5 +1168,10 @@ def post(
11601168
), Div(Script(mermaid_js, type="module"), id="dialog-container")
11611169

11621170

1171+
@requires(fasthtml is not None, "The 'python-fasthtml' package is required to run the gui.")
1172+
def start_gui(port: int | None = None):
1173+
serve(appname="jobflow_remote.webgui.webgui", port=port, reload_includes=[Path(__file__).parent],)
1174+
1175+
11631176
if __name__ == "__main__":
11641177
serve()

0 commit comments

Comments
 (0)