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"\n

QE App Workflow (pk: {node.pk}) — {formula}

" + else: + title += "\n

QE 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"