diff --git a/src/aiidalab_qe/app/result/structure/model.py b/src/aiidalab_qe/app/result/structure/model.py index 847868a8..6ffb47ea 100644 --- a/src/aiidalab_qe/app/result/structure/model.py +++ b/src/aiidalab_qe/app/result/structure/model.py @@ -4,5 +4,8 @@ class StructureResultsModel(ResultsModel): identifier = "structure" - include = True _this_process_label = "PwRelaxWorkChain" + + @property + def include(self): + return "relax" in self.properties diff --git a/src/aiidalab_qe/app/result/summary/model.py b/src/aiidalab_qe/app/result/summary/model.py index ec5d58a2..881a3339 100644 --- a/src/aiidalab_qe/app/result/summary/model.py +++ b/src/aiidalab_qe/app/result/summary/model.py @@ -38,7 +38,9 @@ class WorkChainSummaryModel(ResultsModel): identifier = "summary" - include = True + @property + def include(self): + return True def generate_report_html(self): """Read from the bulider parameters and generate a html for reporting diff --git a/src/aiidalab_qe/app/result/viewer/viewer.py b/src/aiidalab_qe/app/result/viewer/viewer.py index 7c391f9b..403b2aef 100644 --- a/src/aiidalab_qe/app/result/viewer/viewer.py +++ b/src/aiidalab_qe/app/result/viewer/viewer.py @@ -49,7 +49,7 @@ def __init__(self, node: orm.Node, model: WorkChainViewerModel, **kwargs): self._fetch_plugin_results() - # HACK + # HACK should be called from result step - fix! self.render() def render(self): @@ -114,9 +114,6 @@ def _add_structure_panel(self): def _fetch_plugin_results(self): entries = get_entry_items("aiidalab_qe.properties", "result") - needs_electronic_structure = all( - identifier in self._model.properties for identifier in ("bands", "pdos") - ) for identifier, entry in entries.items(): for key in ("panel", "model"): if key not in entry: @@ -126,10 +123,6 @@ def _fetch_plugin_results(self): panel = entry["panel"] model = entry["model"]() self._model.add_model(identifier, model) - if identifier == "electronic_structure" and needs_electronic_structure: - model.include = True - else: - model.include = identifier in self._model.properties self.results[identifier] = panel( identifier=identifier, model=model, diff --git a/src/aiidalab_qe/common/bands_pdos/bandpdoswidget.py b/src/aiidalab_qe/common/bands_pdos/bandpdoswidget.py index b67e22fb..527300bd 100644 --- a/src/aiidalab_qe/common/bands_pdos/bandpdoswidget.py +++ b/src/aiidalab_qe/common/bands_pdos/bandpdoswidget.py @@ -108,7 +108,6 @@ def render(self): description="Apply selection", icon="pencil", button_style="primary", - disabled=False, ) self.update_plot_button.on_click(self._update_plot) @@ -116,7 +115,6 @@ def render(self): description="Download Data", icon="download", button_style="primary", - disabled=False, layout=ipw.Layout(visibility="hidden"), ) self.download_button.on_click(self._model.download_data) diff --git a/src/aiidalab_qe/common/panel.py b/src/aiidalab_qe/common/panel.py index 9bc60553..4763d817 100644 --- a/src/aiidalab_qe/common/panel.py +++ b/src/aiidalab_qe/common/panel.py @@ -188,7 +188,6 @@ class ResultsModel(Model, HasProcess): this_process_uuid = tl.Unicode(allow_none=True) process_state_notification = tl.Unicode("") - include = False _this_process_label = "" CSS_MAP = { @@ -201,6 +200,10 @@ class ResultsModel(Model, HasProcess): "created": "info", } + @property + def include(self): + return self.identifier in self.properties + @property def has_results(self): return self.identifier in self.outputs @@ -217,34 +220,6 @@ def update_process_state_notification(self): """ - @tl.observe("process_uuid") - def _on_process_uuid_change(self, _): - super()._on_process_uuid_change(_) - if self.identifier != "summary": - self._set_this_process_uuid() - - def _set_this_process_uuid(self): - if not self.process_uuid: - return - try: - self.this_process_uuid = ( - orm.QueryBuilder() - .append( - orm.WorkChainNode, - filters={"uuid": self.process_node.uuid}, - tag="root_process", - ) - .append( - orm.WorkChainNode, - filters={"attributes.process_label": self._this_process_label}, - project="uuid", - with_incoming="root_process", - ) - .first(flat=True) - ) - except Exception: - self.this_process_uuid = None - def _fetch_this_process_node(self): return ( orm.QueryBuilder() @@ -282,7 +257,7 @@ class ResultsPanel(Panel, t.Generic[RM]): def __init__(self, model: RM, **kwargs): from aiidalab_qe.common.widgets import LoadingWidget - self.loading_message = LoadingWidget(f"Loading {self.identifier} results") + self.loading_message = LoadingWidget(f"Loading {self.title.lower()} results") super().__init__( children=[self.loading_message], @@ -338,8 +313,6 @@ def render(self): self.rendered = True - self._model.update_process_state_notification() - def _on_load_results_click(self, _): self.children = [self.loading_message] self._update_view() diff --git a/src/aiidalab_qe/plugins/bands/result/model.py b/src/aiidalab_qe/plugins/bands/result/model.py index b0ad1a25..7bb4d4aa 100644 --- a/src/aiidalab_qe/plugins/bands/result/model.py +++ b/src/aiidalab_qe/plugins/bands/result/model.py @@ -10,25 +10,17 @@ def get_bands_node(self): # Check for 'bands' or 'bands_projwfc' attributes within 'bands' output if self._has_bands: return self.outputs.bands.bands - elif self._has_bands_projwfc: + elif self._has_band_projections: return self.outputs.bands.bands_projwfc - elif self._has_bands_output: + elif self.has_results: # If neither 'bands' nor 'bands_projwfc' exist, use 'bands_output' itself # This is the case for compatibility with older versions of the plugin return self.outputs.bands - @property - def _has_bands_output(self): - return hasattr(self.outputs, "bands") - @property def _has_bands(self): - if not self._has_bands_output: - return False - return hasattr(self.outputs.bands, "bands") + return self.has_results and hasattr(self.outputs.bands, "bands") @property - def _has_bands_projwfc(self): - if not self._has_bands_output: - return False - return hasattr(self.outputs.bands, "bands_projwfc") + def _has_band_projections(self): + return self.has_results and hasattr(self.outputs.bands, "bands_projwfc") diff --git a/src/aiidalab_qe/plugins/electronic_structure/__init__.py b/src/aiidalab_qe/plugins/electronic_structure/__init__.py index fb50ec92..9fb9631a 100644 --- a/src/aiidalab_qe/plugins/electronic_structure/__init__.py +++ b/src/aiidalab_qe/plugins/electronic_structure/__init__.py @@ -1,8 +1,8 @@ -from .result import ElectronicStructureResult, ElectronicStructureResultModel +from .result import ElectronicStructureResults, ElectronicStructureResultsModel electronic_structure = { "result": { - "panel": ElectronicStructureResult, - "model": ElectronicStructureResultModel, + "panel": ElectronicStructureResults, + "model": ElectronicStructureResultsModel, }, } diff --git a/src/aiidalab_qe/plugins/electronic_structure/result/__init__.py b/src/aiidalab_qe/plugins/electronic_structure/result/__init__.py index 309e5c78..b78cb362 100644 --- a/src/aiidalab_qe/plugins/electronic_structure/result/__init__.py +++ b/src/aiidalab_qe/plugins/electronic_structure/result/__init__.py @@ -1,7 +1,7 @@ -from .model import ElectronicStructureResultModel -from .result import ElectronicStructureResult +from .model import ElectronicStructureResultsModel +from .result import ElectronicStructureResults __all__ = [ - "ElectronicStructureResultModel", - "ElectronicStructureResult", + "ElectronicStructureResultsModel", + "ElectronicStructureResults", ] diff --git a/src/aiidalab_qe/plugins/electronic_structure/result/model.py b/src/aiidalab_qe/plugins/electronic_structure/result/model.py index 99792320..4534a6f5 100644 --- a/src/aiidalab_qe/plugins/electronic_structure/result/model.py +++ b/src/aiidalab_qe/plugins/electronic_structure/result/model.py @@ -1,7 +1,12 @@ +from aiida import orm from aiidalab_qe.common.panel import ResultsModel -class ElectronicStructureResultModel(ResultsModel): +# TODO if combined, this model should extend `HasModels`, and effectively +# TODO reduce to a container of Bands and PDOS, similar to its results panel +class ElectronicStructureResultsModel(ResultsModel): + identifier = "electronic_structure" + def get_pdos_node(self): try: return self.outputs.pdos @@ -12,25 +17,86 @@ def get_bands_node(self): # Check for 'bands' or 'bands_projwfc' attributes within 'bands' output if self._has_bands: return self.outputs.bands.bands - elif self._has_bands_projwfc: + elif self._has_band_projections: return self.outputs.bands.bands_projwfc elif self._has_bands_output: # If neither 'bands' nor 'bands_projwfc' exist, use 'bands_output' itself # This is the case for compatibility with older versions of the plugin return self.outputs.bands + @property + def include(self): + # TODO `and` should be `or` if combined + return all(identifier in self.properties for identifier in ("bands", "pdos")) + + @property + def has_results(self): + # TODO first `and` should be `or` if combined + return self._has_bands and (self._has_dos and self._has_dos_projections) + + @property + def process_states(self): + nodes = self._fetch_electronic_structure_process_nodes() + return [ + node.process_state.value if node and node.process_state else "queued" + for node in nodes + ] + + def update_process_state_notification(self): + processes = ("Bands", "PDOS") + states = self.process_states + self.process_state_notification = "\n".join( + f""" +