Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement AiiDA control page #156

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
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
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)
Loading