Skip to content

Commit

Permalink
Feature/clean button (#838)
Browse files Browse the repository at this point in the history
This fixes #815

* Clean button added

also, modified a bit the logic of kill button (hidden by default,
as sometimes it is loaded - seen for a second - but not necessary)

* Added minimal test for kill and clean button diplay.

* Fixed hiding of the kill workchain button when process is_terminated

* Changing a line, better implementation

* Replacing double bool check with only one.

---------

Co-authored-by: AndresOrtegaGuerrero <34098967+AndresOrtegaGuerrero@users.noreply.github.com>
  • Loading branch information
mikibonacci and AndresOrtegaGuerrero authored Oct 4, 2024
1 parent 2b3963a commit 73f2534
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 6 deletions.
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

0 comments on commit 73f2534

Please sign in to comment.