Skip to content
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
95cc989
Draft implementation of the AiiDA control page.
yakutovicha Jul 20, 2023
15a9d10
Move widgets to control module.
yakutovicha Jun 21, 2024
885537f
Merge branch 'main' into feature/aiida-control-page
yakutovicha Jun 21, 2024
abb5e98
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 21, 2024
dea251e
Fix pre-commit
yakutovicha Jun 21, 2024
ee45eaa
Merge status page into daemon page
yakutovicha Jun 21, 2024
461cc32
Merge branch 'main' into feature/aiida-control-page
danielhollas Oct 14, 2024
cb5b3e9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 14, 2024
8a0b24d
Merge branch 'main' into feature/aiida-control-page
danielhollas Oct 22, 2024
a2db55f
Merge branch 'main' into feature/aiida-control-page
yakutovicha Jul 9, 2026
693efd4
Fix control module
yakutovicha Jul 9, 2026
ebb09aa
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 9, 2026
5026834
Fix profile handling
yakutovicha Jul 9, 2026
be0e3dd
Make daemon start/stop/restart non-blocking with proper feedback
yakutovicha Jul 9, 2026
d8c731f
Replace subprocess-based status refresh with in-process probes
yakutovicha Jul 9, 2026
c8ef698
Control.ipynb: show an alert instead of a traceback when a control se…
yakutovicha Jul 9, 2026
2ed9a94
Re-enable daemon buttons even if the status refresh fails
yakutovicha Jul 9, 2026
5b3dcf9
Surface process list query errors instead of failing silently
yakutovicha Jul 9, 2026
78ee72c
Cleanup of the control notebook
yakutovicha Jul 9, 2026
9ed87eb
Status overview section
yakutovicha Jul 9, 2026
b257de5
Implement system resources panel
yakutovicha Jul 9, 2026
c77e687
Merge branch 'main' into feature/aiida-control-page
yakutovicha Jul 9, 2026
aa70df2
Add storage widget
yakutovicha Jul 9, 2026
7435e81
Implement Danger Zone
yakutovicha Jul 10, 2026
df0633b
Improve daemon handling
yakutovicha Jul 10, 2026
d2ba999
Improve process control widget
yakutovicha Jul 10, 2026
e0b5f4e
Remove group control widget
yakutovicha Jul 10, 2026
3fb6fd1
Use Tab instead of ToggleButtons
yakutovicha Jul 10, 2026
ec87a8a
Add shared ControlSectionWidget base for control page sections
yakutovicha Jul 10, 2026
d4d309a
Unify theme colors and iconography across control page widgets
yakutovicha Jul 10, 2026
7ac5398
Polish control page content: safe status rows, alignment, profile and…
yakutovicha Jul 10, 2026
df6a53c
Drop redundant h3 headings from control-page sections
yakutovicha Jul 10, 2026
6bbec55
Add branded header and cap content width on control page
yakutovicha Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions control.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"# AiiDA control"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"import ipywidgets as ipw\n",
"from aiida import load_profile\n",
"from IPython.display import clear_output, display\n",
"\n",
"load_profile()\n",
"from home.control import (\n",
" DaemonControlWidget,\n",
" GroupControlWidget,\n",
" ProcessControlWidget,\n",
" ProfileControlWidget,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2",
"metadata": {},
"outputs": [],
"source": [
"correspondance = {\n",
" \"Daemon\": DaemonControlWidget,\n",
" \"Group\": GroupControlWidget,\n",
" \"Process\": ProcessControlWidget,\n",
" \"Profile\": ProfileControlWidget,\n",
"}\n",
"\n",
"toc = ipw.ToggleButtons(\n",
" options=correspondance.keys(),\n",
" value=None,\n",
" orientation=\"vertical\",\n",
" layout=ipw.Layout(width=\"200px\"),\n",
")\n",
"\n",
"\n",
"output = ipw.Output()\n",
"\n",
"\n",
"def update_output(value={\"new\": \"Group\"}):\n",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've merged in main with updated ruff that now checks jupyter notebooks. It reports a lint violation here:


control.ipynb:cell 3:19:25: B006 Do not use mutable data structures for argument defaults
   |
19 | def update_output(value={"new": "Group"}):
   |                         ^^^^^^^^^^^^^^^^ B006
20 |     if value["new"] in correspondance:
21 |         with output:
   |
   = help: Replace with `None`; initialize within function

" if value[\"new\"] in correspondance:\n",
" with output:\n",
" clear_output()\n",
" display(correspondance[value[\"new\"]]())\n",
"\n",
"\n",
"toc.observe(update_output, names=\"value\")\n",
"\n",
"toc.value = \"Daemon\"\n",
"display(ipw.HBox([toc, output]))"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
138 changes: 138 additions & 0 deletions home/control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import subprocess

import aiidalab_widgets_base as awb
import ipywidgets as ipw
import plumpy
import traitlets as tr
from aiida import engine, manage
from IPython.display import clear_output


class DaemonControlWidget(ipw.VBox):
def __init__(self):
self._daemon = engine.daemon.get_daemon_client()
self._status = ipw.Output()

# Start daemon.
start_button = ipw.Button(description="Start daemon", button_style="info")
start_button.on_click(self._start_daemon)

# Stop daemon.
stop_button = ipw.Button(description="Stop daemon", button_style="danger")
stop_button.on_click(self._stop_daemon)

# Restart daemon.
restart_button = ipw.Button(
description="Restart daemon", button_style="warning"
)
restart_button.on_click(self._restart_daemon)

self.info = ipw.HTML()
self._update_status()
super().__init__(
children=[
self.info,
self._status,
ipw.HBox([start_button, stop_button, restart_button]),
]
)

def _restart_daemon(self, _=None):
self._clear_status()
self.info.value = "Restarting the daemon..."
response = self._daemon.restart_daemon()
self.info.value = ""
self._update_status()
return response

def _start_daemon(self, _=None):
self._clear_status()
self.info.value = "Starting the daemon..."
response = self._daemon.start_daemon()
self.info.value = ""
self._update_status()
return response

def _stop_daemon(self, _=None):
self._clear_status()
self.info.value = "Stopping the daemon..."
response = self._daemon.stop_daemon()
self.info.value = ""
self._update_status()
return response

def _update_status(self, _=None):
self._clear_status()
with self._status:
result_status = subprocess.run(
["verdi", "status"], capture_output=True, text=True, check=False
)
print(result_status.stdout, result_status.stderr)

result_daemon = subprocess.run(
["verdi", "daemon", "status"],
capture_output=True,
text=True,
check=False,
)
print(result_daemon.stdout, result_daemon.stderr)

def _clear_status(self):
with self._status:
clear_output()


class ProcessControlWidget(ipw.VBox):
def __init__(self):
process_list = awb.ProcessListWidget(path_to_root="../")
past_days_widget = ipw.IntText(value=7, description="Past days:")
tr.link((past_days_widget, "value"), (process_list, "past_days"))

all_days_checkbox = ipw.Checkbox(description="All days", value=True)
tr.dlink((all_days_checkbox, "value"), (past_days_widget, "disabled"))
tr.dlink(
(all_days_checkbox, "value"),
(process_list, "past_days"),
transform=lambda v: -1 if v else past_days_widget.value,
)

available_states = [state.value for state in plumpy.ProcessState]
process_state_widget = ipw.SelectMultiple(
options=available_states,
value=["running", "waiting"],
description="Process State:",
style={"description_width": "initial"},
disabled=False,
)
tr.dlink((process_state_widget, "value"), (process_list, "process_states"))
process_list.update()

super().__init__(
children=[
ipw.HBox([past_days_widget, all_days_checkbox]),
process_state_widget,
process_list,
]
)


class GroupControlWidget(ipw.VBox):
def __init__(self):
text = ipw.HTML("I am a Group Control Page")
super().__init__(children=[text])


class Profile(ipw.HBox):
def __init__(self, profile):
self.profile = profile
self.name = ipw.HTML(f"""<font size="3"> * {self.profile.name}</font>""")
self.make_default = ipw.Button(description="Make default", button_style="info")
self.delete = ipw.Button(description="Delele", button_style="danger")
super().__init__(children=[self.name, self.make_default, self.delete])


class ProfileControlWidget(ipw.VBox):
def __init__(self):
text = ipw.HTML(value="<h3> List of profiles </h3>")
children = [Profile(p) for p in manage.get_config().profiles]
super().__init__(children=[text, *children])
24 changes: 19 additions & 5 deletions start.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

def get_start_widget(appbase, jupbase):
# http://fontawesome.io/icons/

width = "70px"

template = """
<center>
<table>
Expand All @@ -15,7 +18,7 @@ def get_start_widget(appbase, jupbase):
File Manager
</td>

<td style="width:70px"></td>
<td style="width:{width}"></td>

<td style="text-align:center">
<a style="{style}" href="{appbase}/terminal.ipynb" title="Open Terminal" target="_blank">
Expand All @@ -24,7 +27,7 @@ def get_start_widget(appbase, jupbase):
Terminal
</td>

<td style="width:70px"></td>
<td style="width:{width}"></td>

<td style="text-align:center">
<a style="{style}" href="{jupbase}/tree/#running" title="Task Manager" target="_blank">
Expand All @@ -33,7 +36,7 @@ def get_start_widget(appbase, jupbase):
Tasks
</td>

<td style="width:70px"></td>
<td style="width:{width}"></td>

<td style="text-align:center">
<a style="{style}" href="{appbase}/appstore.ipynb" title="Install New Apps" target="_blank">
Expand All @@ -42,7 +45,16 @@ def get_start_widget(appbase, jupbase):
App Store
</td>

<td style="width:70px"></td>
<td style="width:{width}"></td>

<td style="text-align:center">
<a style="{style}" href="{appbase}/control.ipynb" title="Manage AiiDA and AiiDAlab" target="_blank">
<i class="fa fa-cogs fa-3x" aria-hidden="true"></i>
</a><br>
Control
</td>

<td style="width:{width}"></td>

<td style="text-align:center">
<a style="{style}" href="https://aiidalab.readthedocs.io" title="Learn about AiiDAlab" target="_blank">
Expand All @@ -55,5 +67,7 @@ def get_start_widget(appbase, jupbase):
</center>
"""

html = template.format(appbase=appbase, jupbase=jupbase, style="margin:20px")
html = template.format(
appbase=appbase, jupbase=jupbase, style="margin:20px", width=width
)
return ipw.HTML(html)