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

Trigger the update of node view #844

Merged
merged 4 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 23 additions & 15 deletions src/aiidalab_qe/app/result/workchain_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@
@register_viewer_widget("process.workflow.workchain.WorkChainNode.")
class WorkChainViewer(ipw.VBox):
_results_shown = tl.Set()
process_uuid = tl.Unicode(allow_none=True)

def __init__(self, node, **kwargs):
if node.process_label != "QeAppWorkChain":
super().__init__()
return

self.node = node
self.process_uuid = node.uuid
# In the new version of the plugin, the ui_parameters are stored as a yaml string
# which is then converted to a dictionary
ui_parameters = node.base.extras.get("ui_parameters", {})
Expand All @@ -41,12 +42,12 @@ def __init__(self, node, **kwargs):
self.title = ipw.HTML(
f"""
<hr style="height:2px;background-color:#2097F3;">
<h4>QE App Workflow (pk: {self.node.pk}) &mdash;
{self.node.inputs.structure.get_formula()}
<h4>QE App Workflow (pk: {node.pk}) &mdash;
{node.inputs.structure.get_formula()}
</h4>
"""
)
self.workflows_summary = SummaryView(self.node)
self.workflows_summary = SummaryView(node)

self.summary_tab = ipw.VBox(children=[self.workflows_summary])
# Only the summary tab is shown by default
Expand All @@ -59,7 +60,7 @@ def __init__(self, node, **kwargs):
self.results = {}
entries = get_entry_items("aiidalab_qe.properties", "result")
for identifier, entry_point in entries.items():
result = entry_point(self.node)
result = entry_point(node)
self.results[identifier] = result
self.results[identifier].identifier = identifier

Expand All @@ -83,29 +84,36 @@ def toggle_camera():
toggle_camera()

self.result_tabs.observe(on_selected_index_change, "selected_index")
self._update_view()

super().__init__(
children=[self.title, self.result_tabs],
**kwargs,
)
self._process_monitor = ProcessMonitor(
process=self.node,
callbacks=[
self.process_monitor = ProcessMonitor(
timeout=1.0,
Copy link
Member

@AndresOrtegaGuerrero AndresOrtegaGuerrero Oct 8, 2024

Choose a reason for hiding this comment

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

How this timeout works ?, Could it lead to any memory issue for a workflow that takes long ?

Copy link
Member Author

Choose a reason for hiding this comment

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

For the moment, the ProcessMonitor will call the callback functions every timeout seconds. This, indeed, will be a problem if the callback function is computationally expensive.

In principle, we only need to run the callback function when the process is finished (or even better, when the process is modified). I have opened an issue in the aiidalab-widget-base.

We can wait for that issue to be solved before merging this PR.

Choose a reason for hiding this comment

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

or at least we should increase the time , 1 second is to often

Copy link
Member Author

Choose a reason for hiding this comment

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

We use a small value for the timeout, so that the results can be immediately shown when the process is finished.

As pointed out by @danielhollas , there is a on_sealed argument for the callbacks. I changed the callback to on_sealed, so that the _update_view method will only call when the process is finished. In this case, the timeout 1 s should be fine.

on_sealed=[
self._update_view,
],
)
ipw.dlink((self, "process_uuid"), (self.process_monitor, "value"))

@property
def node(self):
"""Load the workchain node using the process_uuid.
Because the workchain node is used in another thread inside the process monitor,
we need to load the node from the database, instead of passing the node object.
Otherwise, we will get a "Instance is not persistent" error.
"""
return orm.load_node(self.process_uuid)

def _update_view(self):
with self.hold_trait_notifications():
if self.node.is_finished:
node = self.node
if node.is_finished:
self._show_workflow_output()
# if the structure is present in the workchain,
# the structure tab will be added.
if (
"structure" not in self._results_shown
and "structure" in self.node.outputs
):
if "structure" not in self._results_shown and "structure" in node.outputs:
self._show_structure()
self.result_tabs.children += (self.structure_tab,)
# index of the last tab
Expand All @@ -119,7 +127,7 @@ def _update_view(self):
if result.identifier not in self._results_shown:
# check if the all required results are in the outputs
results_ready = [
label in self.node.outputs for label in result.workchain_labels
label in node.outputs for label in result.workchain_labels
]
if all(results_ready):
result._update_view()
Expand Down
5 changes: 5 additions & 0 deletions tests/test_plugins_electronic_structure.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
def test_electronic_structure(generate_qeapp_workchain):
"""Test the electronic structure tab."""
import time

import plotly.graph_objects as go

from aiida import engine
Expand All @@ -10,7 +12,10 @@ def test_electronic_structure(generate_qeapp_workchain):
wkchain = generate_qeapp_workchain()
wkchain.node.set_exit_status(0)
wkchain.node.set_process_state(engine.ProcessState.FINISHED)
wkchain.node.seal()
wcv = WorkChainViewer(wkchain.node)
# wait for the tabs to be updated by the process monitor
time.sleep(3)
# find the tab with the identifier "electronic_structure"
# the built-in summary and structure tabs is not a plugin panel,
# thus don't have identifiers
Expand Down
4 changes: 4 additions & 0 deletions tests/test_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ def test_kill_and_clean_buttons(app_to_submit, generate_qeapp_workchain):
@pytest.mark.usefixtures("sssp")
def test_workchainview(generate_qeapp_workchain):
"""Test the result tabs are properly updated"""
import time

from aiidalab_qe.app.result.workchain_viewer import WorkChainViewer

wkchain = generate_qeapp_workchain()
wkchain.node.seal()
wcv = WorkChainViewer(wkchain.node)
time.sleep(3)
assert len(wcv.result_tabs.children) == 5
assert wcv.result_tabs._titles["0"] == "Workflow Summary"
assert wcv.result_tabs._titles["1"] == "Final Geometry"
Expand Down
Loading