diff --git a/src/aiidalab_qe/app/result/__init__.py b/src/aiidalab_qe/app/result/__init__.py
index 81b0d250a..ce8d2be85 100644
--- a/src/aiidalab_qe/app/result/__init__.py
+++ b/src/aiidalab_qe/app/result/__init__.py
@@ -209,7 +209,7 @@ def _update_kill_button_layout(self):
return
process_node = self._model.fetch_process_node()
if (
- not self._model.has_process
+ not process_node
or process_node.is_finished
or process_node.is_excepted
or self.state
@@ -236,7 +236,7 @@ def _update_status(self):
def _update_state(self):
process_node = self._model.fetch_process_node()
- if not self._model.has_process:
+ if not process_node:
self.state = self.State.INIT
elif process_node.process_state in (
ProcessState.CREATED,
diff --git a/src/aiidalab_qe/app/result/model.py b/src/aiidalab_qe/app/result/model.py
index 022dc5254..71b62da2f 100644
--- a/src/aiidalab_qe/app/result/model.py
+++ b/src/aiidalab_qe/app/result/model.py
@@ -18,13 +18,13 @@ def update(self):
self._update_process_remote_folder_state()
def kill_process(self):
- if self.has_process:
- control.kill_processes([self.process_node])
+ if process_node := self.fetch_process_node():
+ control.kill_processes([process_node])
def clean_remote_data(self):
- if not self.has_process:
+ if not (process_node := self.fetch_process_node()):
return
- for called_descendant in self.process_node.called_descendants:
+ for called_descendant in process_node.called_descendants:
if isinstance(called_descendant, orm.CalcJobNode):
with contextlib.suppress(Exception):
called_descendant.outputs.remote_folder._clean()
@@ -35,10 +35,10 @@ def reset(self):
self.process_info = ""
def _update_process_remote_folder_state(self):
- if not self.has_process:
+ if not (process_node := self.fetch_process_node()):
return
cleaned = []
- for called_descendant in self.process_node.called_descendants:
+ for called_descendant in process_node.called_descendants:
if isinstance(called_descendant, orm.CalcJobNode):
with contextlib.suppress(Exception):
cleaned.append(called_descendant.outputs.remote_folder.is_empty)
diff --git a/src/aiidalab_qe/app/result/summary/model.py b/src/aiidalab_qe/app/result/summary/model.py
index 881a3339c..d44f027b4 100644
--- a/src/aiidalab_qe/app/result/summary/model.py
+++ b/src/aiidalab_qe/app/result/summary/model.py
@@ -125,7 +125,8 @@ def _generate_report_parameters(self):
"""
from aiida.orm.utils.serialize import deserialize_unsafe
- qeapp_wc = self.process_node
+ if not (qeapp_wc := self.fetch_process_node()):
+ return {"error": "WorkChain not found."}
ui_parameters = qeapp_wc.base.extras.get("ui_parameters", {})
if isinstance(ui_parameters, str):
diff --git a/src/aiidalab_qe/app/result/viewer/viewer.py b/src/aiidalab_qe/app/result/viewer/viewer.py
index 3245ca777..0ab7e1138 100644
--- a/src/aiidalab_qe/app/result/viewer/viewer.py
+++ b/src/aiidalab_qe/app/result/viewer/viewer.py
@@ -49,14 +49,18 @@ def render(self):
if self.rendered:
return
- node = self._model.process_node
+ node = self._model.fetch_process_node()
- self.title = ipw.HTML(f"""
-
-
- QE App Workflow (pk: {node.pk}) — {node.inputs.structure.get_formula()}
-
- """)
+ self.title = ipw.HTML()
+
+ title = "
"
+ if node:
+ formula = node.inputs.structure.get_formula()
+ title += f"\nQE App Workflow (pk: {node.pk}) — {formula}
"
+ else:
+ title += "\nQE App Workflow
"
+
+ self.title.value = title
self.tabs = ipw.Tab(selected_index=None)
@@ -69,7 +73,7 @@ def render(self):
self._update_tabs()
- if node.is_finished:
+ if node and node.is_finished:
self._add_workflow_output_widget()
def _update_tabs(self):
@@ -87,7 +91,8 @@ def _update_tabs(self):
self.summary.render()
def _add_workflow_output_widget(self):
- self.summary.children += (WorkChainOutputs(self._model.process_node),)
+ process_node = self._model.fetch_process_node()
+ self.summary.children += (WorkChainOutputs(node=process_node),)
def _add_structure_panel(self):
structure_model = StructureResultsModel()
diff --git a/src/aiidalab_qe/common/mixins.py b/src/aiidalab_qe/common/mixins.py
index bad2b2285..21421c233 100644
--- a/src/aiidalab_qe/common/mixins.py
+++ b/src/aiidalab_qe/common/mixins.py
@@ -49,25 +49,22 @@ def _link_model(self, model: T):
class HasProcess(tl.HasTraits):
process_uuid = tl.Unicode(allow_none=True)
- monitor_counter = tl.Int(0)
-
- process_node = None
-
- @property
- def has_process(self):
- return self.process_node is not None
+ monitor_counter = tl.Int(0) # used for continuous updates
@property
def inputs(self):
- return self.process_node.inputs if self.has_process else []
+ process_node = self.fetch_process_node()
+ return process_node.inputs if process_node else []
@property
def properties(self):
- return self.process_node.inputs.properties if self.has_process else []
+ process_node = self.fetch_process_node()
+ return process_node.inputs.properties if process_node else []
@property
def outputs(self):
- return self.process_node.outputs if self.has_process else []
+ process_node = self.fetch_process_node()
+ return process_node.outputs if process_node else []
def fetch_process_node(self):
try:
@@ -75,10 +72,6 @@ def fetch_process_node(self):
except NotExistent:
return None
- @tl.observe("process_uuid")
- def _on_process_uuid_change(self, _):
- self.process_node = self.fetch_process_node()
-
class Confirmable(tl.HasTraits):
confirmed = tl.Bool(False)
diff --git a/src/aiidalab_qe/common/panel.py b/src/aiidalab_qe/common/panel.py
index 3104379d2..c2cf8b256 100644
--- a/src/aiidalab_qe/common/panel.py
+++ b/src/aiidalab_qe/common/panel.py
@@ -231,7 +231,7 @@ def _get_child_outputs(self, child="this"):
return AttributeDict({key: getattr(node.outputs, key) for key in node.outputs})
def _fetch_child_process_node(self, child="this") -> orm.ProcessNode | None:
- if not self.process_node:
+ if not self.process_uuid:
return
uuid = getattr(self, f"_{child}_process_uuid")
label = getattr(self, f"_{child}_process_label")
@@ -240,7 +240,7 @@ def _fetch_child_process_node(self, child="this") -> orm.ProcessNode | None:
orm.QueryBuilder()
.append(
orm.WorkChainNode,
- filters={"uuid": self.process_node.uuid},
+ filters={"uuid": self.process_uuid},
tag="root_process",
)
.append(
diff --git a/src/aiidalab_qe/plugins/xas/result/model.py b/src/aiidalab_qe/plugins/xas/result/model.py
index 92acc303c..ff607db2e 100644
--- a/src/aiidalab_qe/plugins/xas/result/model.py
+++ b/src/aiidalab_qe/plugins/xas/result/model.py
@@ -29,6 +29,8 @@ class XasResultsModel(ResultsModel):
_this_process_label = "XspectraCrystalWorkChain"
def update_spectrum_options(self):
+ if not (process_node := self.fetch_process_node()):
+ return
outputs = self._get_child_outputs()
(
self.final_spectra,
@@ -36,7 +38,7 @@ def update_spectrum_options(self):
) = export_xas_data(outputs)
xas_workchain = next(
node
- for node in self.process_node.called
+ for node in process_node.called
if node.process_label == self._this_process_label
)
core_workchains = {
diff --git a/tests/test_plugins_bands.py b/tests/test_plugins_bands.py
index 98efdbbdc..15e038e65 100644
--- a/tests/test_plugins_bands.py
+++ b/tests/test_plugins_bands.py
@@ -6,7 +6,7 @@ def test_result(generate_qeapp_workchain):
workchain = generate_qeapp_workchain()
model = BandsResultsModel()
- model.process_node = workchain.node
+ model.process_uuid = workchain.node.uuid
result = BandsResults(model=model)
result.render()
diff --git a/tests/test_plugins_electronic_structure.py b/tests/test_plugins_electronic_structure.py
index 81536f98d..307edcb43 100644
--- a/tests/test_plugins_electronic_structure.py
+++ b/tests/test_plugins_electronic_structure.py
@@ -9,7 +9,7 @@ def test_electronic_structure(generate_qeapp_workchain):
workchain = generate_qeapp_workchain()
model = ElectronicStructureResultsModel()
- model.process_node = workchain.node
+ model.process_uuid = workchain.node.uuid
result = ElectronicStructureResults(model=model)
result.render()
diff --git a/tests/test_plugins_pdos.py b/tests/test_plugins_pdos.py
index ba2d27042..574c4a591 100644
--- a/tests/test_plugins_pdos.py
+++ b/tests/test_plugins_pdos.py
@@ -6,7 +6,7 @@ def test_result(generate_qeapp_workchain):
workchain = generate_qeapp_workchain()
model = PdosResultsModel()
- model.process_node = workchain.node
+ model.process_uuid = workchain.node.uuid
result = PdosResults(model=model)
result.render()
diff --git a/tests/test_result.py b/tests/test_result.py
index ae6dd5bbc..0fd4c6796 100644
--- a/tests/test_result.py
+++ b/tests/test_result.py
@@ -43,7 +43,7 @@ def test_summary_report(data_regression, generate_qeapp_workchain):
"""Test the summary report can be properly generated."""
workchain = generate_qeapp_workchain()
model = WorkChainSummaryModel()
- model.process_node = workchain.node
+ model.process_uuid = workchain.node.uuid
report_parameters = model._generate_report_parameters()
data_regression.check(report_parameters)
@@ -54,7 +54,7 @@ def test_summary_report_advanced_settings(data_regression, generate_qeapp_workch
spin_type="collinear", electronic_type="metal", initial_magnetic_moments=0.1
)
model = WorkChainSummaryModel()
- model.process_node = workchain.node
+ model.process_uuid = workchain.node.uuid
report_parameters = model._generate_report_parameters()
assert report_parameters["initial_magnetic_moments"]["Si"] == 0.1
@@ -63,7 +63,7 @@ def test_summary_view(generate_qeapp_workchain):
"""Test the report html can be properly generated."""
workchain = generate_qeapp_workchain()
model = WorkChainSummaryModel()
- model.process_node = workchain.node
+ model.process_uuid = workchain.node.uuid
report_html = model.generate_report_html()
parsed = BeautifulSoup(report_html, "html.parser")
# find the td with the text "Initial Magnetic Moments"