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

Feature/clean button #838

Merged
merged 9 commits into from
Oct 4, 2024
73 changes: 67 additions & 6 deletions src/aiidalab_qe/app/result/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,30 @@ def __init__(self, **kwargs):
tooltip="Kill the below workchain.",
button_style="danger",
icon="window-close",
layout=ipw.Layout(width="120px", height="40px"),
layout=ipw.Layout(width="120px", height="40px", display="none"),
)
self.kill_button.on_click(self._on_click_kill_button)

self.clean_scratch_button = ipw.Button(
description="Clean remote data",
tooltip="Clean the remote folders of the workchain.",
button_style="danger",
icon="folder",
layout=ipw.Layout(width="150px", height="40px", display="none"),
)
self.clean_scratch_button.on_click(self._on_click_clean_scratch_button)

self.process_info = ipw.HTML()

super().__init__(
[
ipw.HBox(children=[self.kill_button, self.process_info]),
ipw.HBox(
children=[
self.kill_button,
self.process_info,
self.clean_scratch_button,
]
),
self.process_status,
],
**kwargs,
Expand Down Expand Up @@ -93,12 +109,14 @@ def _update_state(self):
or process.is_failed
):
self.state = self.State.FAIL
self.kill_button.layout.display = "none"
self.process_info.value = PROCESS_EXCEPTED
elif process.is_finished_ok:
self.state = self.State.SUCCESS
self.kill_button.layout.display = "none"
self.process_info.value = PROCESS_COMPLETED
# trigger the update of kill and clean button.
if self.state in [self.State.SUCCESS, self.State.FAIL]:
self._update_kill_button_layout()
self._update_clean_scratch_button_layout()

def _update_kill_button_layout(self):
"""Update the layout of the kill button."""
Expand All @@ -107,8 +125,8 @@ def _update_kill_button_layout(self):
self.kill_button.layout.display = "none"
else:
process = orm.load_node(self.process)
# If the process is finished or excepted, hide the button.
if process.is_finished or process.is_excepted:
# If the process is terminated, hide the button.
if process.is_terminated:
self.kill_button.layout.display = "none"
else:
self.kill_button.layout.display = "block"
Expand All @@ -120,6 +138,32 @@ def _update_kill_button_layout(self):
else:
self.kill_button.disabled = True

def _update_clean_scratch_button_layout(self):
"""Update the layout of the kill button."""
# The button is hidden by default, but if we load a new process, we hide again.
if not self.process:
self.clean_scratch_button.layout.display = "none"
else:
process = orm.load_node(self.process)
# If the process is terminated, show the button.
if process.is_terminated:
self.clean_scratch_button.layout.display = "block"
else:
self.clean_scratch_button.layout.display = "none"

# If the scratch is already empty, we should deactivate the button.
# not sure about the performance if descendants are several.
cleaned_bool = []
for called_descendant in process.called_descendants:
if isinstance(called_descendant, orm.CalcJobNode):
try:
cleaned_bool.append(
called_descendant.outputs.remote_folder.is_cleaned
)
except (OSError, KeyError):
pass
self.clean_scratch_button.disabled = all(cleaned_bool)

def _on_click_kill_button(self, _=None):
"""callback for the kill button.
First kill the process, then update the kill button layout.
Expand All @@ -130,10 +174,27 @@ def _on_click_kill_button(self, _=None):
# update the kill button layout
self._update_kill_button_layout()

def _on_click_clean_scratch_button(self, _=None):
"""callback for the clean scratch button.
First clean the remote folders, then update the clean button layout.
"""
process = orm.load_node(self.process)

for called_descendant in process.called_descendants:
if isinstance(called_descendant, orm.CalcJobNode):
try:
called_descendant.outputs.remote_folder._clean()
except (OSError, KeyError):
pass

# update the kill button layout
self._update_clean_scratch_button_layout()

@tl.observe("process")
def _observe_process(self, _):
"""Callback for when the process is changed."""
# The order of the following calls matters,
# as the self.state is updated in the _update_state method.
self._update_state()
self._update_kill_button_layout()
self._update_clean_scratch_button_layout()
22 changes: 22 additions & 0 deletions tests/test_result.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import pytest


@pytest.mark.usefixtures("sssp")
def test_result_step(app_to_submit, generate_qeapp_workchain):
"""Test the result step is properly updated when the process
is running."""
Expand All @@ -7,6 +11,21 @@ def test_result_step(app_to_submit, generate_qeapp_workchain):
assert step.state == step.State.ACTIVE


@pytest.mark.usefixtures("sssp")
def test_kill_and_clean_buttons(app_to_submit, generate_qeapp_workchain):
"""Test the kill and clean_scratch button are properly displayed when the process
is in different states."""

step = app_to_submit.results_step
step.process = generate_qeapp_workchain().node.uuid
step._update_state()
step._update_kill_button_layout()
step._update_clean_scratch_button_layout()
assert step.kill_button.layout.display == "block"
assert step.clean_scratch_button.layout.display == "none"


@pytest.mark.usefixtures("sssp")
def test_workchainview(generate_qeapp_workchain):
"""Test the result tabs are properly updated"""
from aiidalab_qe.app.result.workchain_viewer import WorkChainViewer
Expand All @@ -18,6 +37,7 @@ def test_workchainview(generate_qeapp_workchain):
assert wcv.result_tabs._titles["1"] == "Final Geometry"


@pytest.mark.usefixtures("sssp")
def test_summary_report(data_regression, generate_qeapp_workchain):
"""Test the summary report can be properly generated."""
from aiidalab_qe.app.result.summary_viewer import SummaryView
Expand All @@ -29,6 +49,7 @@ def test_summary_report(data_regression, generate_qeapp_workchain):
data_regression.check(report)


@pytest.mark.usefixtures("sssp")
def test_summary_report_advanced_settings(data_regression, generate_qeapp_workchain):
"""Test advanced settings are properly reported"""
from aiidalab_qe.app.result.summary_viewer import SummaryView
Expand All @@ -41,6 +62,7 @@ def test_summary_report_advanced_settings(data_regression, generate_qeapp_workch
assert report["initial_magnetic_moments"]["Si"] == 0.1


@pytest.mark.usefixtures("sssp")
def test_summary_view(generate_qeapp_workchain):
"""Test the report html can be properly generated."""
from bs4 import BeautifulSoup
Expand Down
Loading