From 8c942f785bd91f8e338e7ef7c3867eca571ca08f Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sat, 28 Dec 2024 14:06:45 +0000 Subject: [PATCH 01/15] Discard redundant workflow status from outputs --- src/aiidalab_qe/app/result/components/summary/outputs.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/aiidalab_qe/app/result/components/summary/outputs.py b/src/aiidalab_qe/app/result/components/summary/outputs.py index 3cedb8c13..c03d8650b 100644 --- a/src/aiidalab_qe/app/result/components/summary/outputs.py +++ b/src/aiidalab_qe/app/result/components/summary/outputs.py @@ -41,9 +41,6 @@ def __init__(self, node, export_dir=None, **kwargs): self._download_button_widget = DownloadDataWidget(workchain_node=self.node) if node.exit_status != 0: - title = ipw.HTML( - f"

Workflow failed with exit status [{ node.exit_status }]

" - ) final_calcjob = self._get_final_calcjob(node) env = Environment() template = files(templates).joinpath("workflow_failure.jinja").read_text() @@ -56,14 +53,13 @@ def __init__(self, node, export_dir=None, **kwargs): ) ) else: - title = ipw.HTML("

Workflow completed successfully!

") output = ipw.HTML() super().__init__( children=[ ipw.VBox( - children=[self._download_button_widget, title], - layout=ipw.Layout(justify_content="space-between", margin="10px"), + children=[self._download_button_widget], + layout=ipw.Layout(justify_content="space-between"), ), output, ], From 0ff890d5c87f8f7babcc3058c92b96be9c71fb7b Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sat, 28 Dec 2024 14:08:14 +0000 Subject: [PATCH 02/15] Disable both download buttons on download --- .../components/summary/download_data.py | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/aiidalab_qe/app/result/components/summary/download_data.py b/src/aiidalab_qe/app/result/components/summary/download_data.py index 7478ee35c..968b6c32f 100644 --- a/src/aiidalab_qe/app/result/components/summary/download_data.py +++ b/src/aiidalab_qe/app/result/components/summary/download_data.py @@ -15,7 +15,7 @@ def __init__(self, workchain_node): button_style="primary", disabled=False, tooltip="Download the AiiDA archive of the simulation, ready to be shared or imported into another AiiDA profile", - layout=ipw.Layout(width="auto"), + layout=ipw.Layout(width="100%"), ) self.download_archive_button.on_click(self._download_data_thread) @@ -25,7 +25,7 @@ def __init__(self, workchain_node): button_style="primary", disabled=False, tooltip="Download the raw data of the simulation, organized in intuitive directory paths.", - layout=ipw.Layout(width="auto"), + layout=ipw.Layout(width="100%"), ) try: # check that we can import the ProcessDumper (not implemented in old AiiDA versions) @@ -41,30 +41,30 @@ def __init__(self, workchain_node): self.node = workchain_node - super().__init__( - children=[ - ipw.HTML( - "

Download the data

" - "It is possible to download raw data (i.e. input and output files) and/or the AiiDA archive (ready to be shared or imported into another AiiDA profile)" - ), - ipw.HBox( - children=[self.download_raw_button], - layout=ipw.Layout(width="700px"), # Set the desired width here - ), - ipw.HBox( - children=[self.download_archive_button], - layout=ipw.Layout(width="700px"), # Set the desired width here - ), - ], - ) + self._downloading_message = ipw.HTML() + + children = [] if not dumper_is_available: - self.children[1].children += ( - ipw.HTML( - "

The raw data download is not available because the AiiDA version is too old.

" - ), + children.append( + ipw.HTML(""" +

The raw data download is not available because + the AiiDA version is too old.

+ """), ) + children.extend( + [ + ipw.HBox(children=[self.download_raw_button]), + ipw.HBox(children=[self.download_archive_button]), + self._downloading_message, + ] + ) + + super().__init__( + children=children, + ) + def _download_data_thread(self, button_instance): thread = Thread(target=lambda: self._download_data(button_instance)) thread.start() @@ -81,23 +81,34 @@ def _download_data(self, button_instance): Args: button_instance (ipywidgets.Button): The button instance that was clicked. """ - button_instance.disabled = True + self._disable_buttons() if "archive" in button_instance.description: what = "archive" filename = f"export_qeapp_calculation_pk_{self.node.pk}.aiida" - box = self.children[2] else: what = "raw" filename = f"export_{self.node.pk}_raw.zip" - box = self.children[1] - - box.children += (ipw.HTML("Downloading data..."),) + self._show_downloading_message(what) data = self.produce_bitestream(self.node, what=what) self._download(payload=data, filename=filename) del data - box.children = box.children[:1] - button_instance.disabled = False + self._hide_downloading_message() + self._enable_buttons() + + def _show_downloading_message(self, what): + self._downloading_message.value = f"Downloading {what} data..." + + def _hide_downloading_message(self): + self._downloading_message.value = "" + + def _disable_buttons(self): + self.download_raw_button.disabled = True + self.download_archive_button.disabled = True + + def _enable_buttons(self): + self.download_raw_button.disabled = False + self.download_archive_button.disabled = False @staticmethod def _download(payload, filename): From c588b5593d652a5c6e29f509e78a3f36c29beff0 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 15:16:24 +0000 Subject: [PATCH 03/15] Redesign summary view --- .../app/result/components/summary/model.py | 4 +- .../app/result/components/summary/summary.py | 38 ++++++- src/aiidalab_qe/app/static/styles/style.css | 5 +- src/aiidalab_qe/app/static/styles/summary.css | 44 ++++++++ .../templates/workflow_list_summary.jinja | 100 ++++++++++++++++++ ...ary.jinja => workflow_table_summary.jinja} | 0 tests/test_result.py | 6 +- 7 files changed, 185 insertions(+), 12 deletions(-) create mode 100644 src/aiidalab_qe/app/static/styles/summary.css create mode 100644 src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja rename src/aiidalab_qe/app/static/templates/{workflow_summary.jinja => workflow_table_summary.jinja} (100%) diff --git a/src/aiidalab_qe/app/result/components/summary/model.py b/src/aiidalab_qe/app/result/components/summary/model.py index 74b1b635a..e6ec6cf77 100644 --- a/src/aiidalab_qe/app/result/components/summary/model.py +++ b/src/aiidalab_qe/app/result/components/summary/model.py @@ -53,7 +53,7 @@ def generate_report_html(self): from aiidalab_qe.app.static import styles, templates def _fmt_yes_no(truthy): - return "Yes" if truthy else "No" + return "yes" if truthy else "no" env = Environment() env.filters.update( @@ -61,7 +61,7 @@ def _fmt_yes_no(truthy): "fmt_yes_no": _fmt_yes_no, } ) - template = files(templates).joinpath("workflow_summary.jinja").read_text() + template = files(templates).joinpath("workflow_list_summary.jinja").read_text() style = files(styles).joinpath("style.css").read_text() parameters = self._generate_report_parameters() report = {key: value for key, value in parameters.items() if value is not None} diff --git a/src/aiidalab_qe/app/result/components/summary/summary.py b/src/aiidalab_qe/app/result/components/summary/summary.py index aab7390da..5c9568390 100644 --- a/src/aiidalab_qe/app/result/components/summary/summary.py +++ b/src/aiidalab_qe/app/result/components/summary/summary.py @@ -26,12 +26,44 @@ def _render(self): def _render_summary(self): if not self._model.has_process: return - report = self._model.generate_report_html() - self.children = [ipw.HTML(report)] + + settings_summary = ipw.HTML( + value=self._model.generate_report_html(), + ) + settings_summary.add_class("summary-panel") + + self.output_download_container = ipw.VBox( + children=[ + ipw.HTML(""" +
+

Download the data

+ Once the workflow is finished, you can download raw data + (i.e. input and output files) and/or the AiiDA archive + (ready to be shared or imported into another AiiDA profile). +
+ """), + ipw.HTML("Download buttons will appear here when available."), + ], + ) + self.output_download_container.add_class("summary-panel") + + self.container = ipw.HBox( + children=[ + settings_summary, + self.output_download_container, + ], + ) + self.container.add_class("workflow-summary-container") + self.children = [self.container] self.has_report = True def _render_output(self): process_node = self._model.fetch_process_node() if process_node and process_node.is_terminated: - self.children += (WorkChainOutputs(node=process_node),) + output_download_widget = WorkChainOutputs(node=process_node) + output_download_widget.layout.width = "100%" + self.output_download_container.children = [ + self.output_download_container.children[0], # type: ignore + output_download_widget, + ] self.has_output = True diff --git a/src/aiidalab_qe/app/static/styles/style.css b/src/aiidalab_qe/app/static/styles/style.css index bd57b07c1..969a5a5bb 100644 --- a/src/aiidalab_qe/app/static/styles/style.css +++ b/src/aiidalab_qe/app/static/styles/style.css @@ -17,9 +17,8 @@ table { } td, tr { - border-left: 2px solid var(--lab-blue); text-align: left; - padding: 8px; + padding: 2px 8px; } tr { @@ -49,7 +48,7 @@ td:nth-child(even) { /* Error details */ .error-container { - margin: 10px; + margin: 10px 0; } .error { diff --git a/src/aiidalab_qe/app/static/styles/summary.css b/src/aiidalab_qe/app/static/styles/summary.css new file mode 100644 index 000000000..6e6d8bd00 --- /dev/null +++ b/src/aiidalab_qe/app/static/styles/summary.css @@ -0,0 +1,44 @@ +.workflow-summary-container { + width: 75%; + margin: 0 auto; + gap: 10px; +} + +@media (max-width: 1199px) { + .workflow-summary-container { + width: 92%; + } +} + +@media (max-width: 991px) { + .workflow-summary-container { + width: 100%; + } +} + +.summary-panel { + width: 100%; + border: 1px solid #9e9e9e; + padding: 0 20px 10px; + margin: 0; +} + +.settings-summary { + margin: 2px; + font-family: Arial, sans-serif; + line-height: 1.6; +} + +.settings-summary ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +.settings-summary ul ul { + margin-left: 20px; +} + +.settings-summary a { + text-decoration: none; +} diff --git a/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja new file mode 100644 index 000000000..704644cec --- /dev/null +++ b/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja @@ -0,0 +1,100 @@ + + +
+

Main settings

+
    +
  • + Structure geometry optimized: {{ relaxed | fmt_yes_no }} +
  • + {% if relaxed %} +
  • + Optimization method: {{ relax_method }} +
  • + {% endif %} +
  • + Protocol: {{ protocol }} +
  • +
  • + Magnetism: {{ material_magnetic }} +
  • +
  • + Electronic type: {{ electronic_type }} +
  • +
  • + Periodicity: {{ periodicity }} +
  • +
+ +

Advanced settings

+
    +
  • + Functional: + {{ functional }} +
  • +
  • + Pseudopotential library: + {{ pseudo_library }} {{ pseudo_protocol }} v{{ pseudo_version }} +
  • +
  • + Energy cutoff (wave functions): {{ energy_cutoff_wfc }} Ry +
  • +
  • + Energy cutoff (charge density): {{ energy_cutoff_rho }} Ry +
  • +
  • + Occupation type (SCF): {{ occupation_type }} +
  • + {% if occupation_type == "smearing" %} +
      +
    • + Smearing width (degauss): {{ degauss }} Ry +
    • +
    • + Smearing type: {{ smearing }} +
    • +
    + {% endif %} +
  • + K-point mesh distance (SCF): {{ scf_kpoints_distance }} Å-1 +
  • + {% if bands_computed %} +
  • + K-point line distance (Bands): {{ bands_kpoints_distance }} Å-1 +
  • + {% endif %} + {% if pdos_computed %} +
  • + K-point mesh distance (NSCF): {{ nscf_kpoints_distance }} Å-1 +
  • + {% endif %} +
  • + Total charge: {{ tot_charge }} +
  • +
  • + Van der Waals correction: {{ vdw_corr }} +
  • + {% if material_magnetic == "collinear" %} + {% if tot_magnetization %} +
  • + Total magnetization: {{ tot_magnetization }} +
  • + {% else %} +
  • + Initial magnetic moments: {{ initial_magnetic_moments }} +
  • + {% endif %} + {% endif %} + {% if hubbard_u %} +
  • + DFT+U: {{ hubbard_u }} +
  • + {% endif %} + {% if spin_orbit %} +
  • + Spin-orbit coupling: {{ spin_orbit }} +
  • + {% endif %} +
+
+ + diff --git a/src/aiidalab_qe/app/static/templates/workflow_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja similarity index 100% rename from src/aiidalab_qe/app/static/templates/workflow_summary.jinja rename to src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja diff --git a/tests/test_result.py b/tests/test_result.py index 3bc2e7f84..3a86c176d 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -71,15 +71,13 @@ def test_summary_view(generate_qeapp_workchain): 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" parameters = { "Energy cutoff (wave functions)": "30.0 Ry", "Total charge": "0.0", - "Initial magnetic moments": "", } for key, value in parameters.items(): - td = parsed.find("td", text=key).find_next_sibling("td") - assert td.text == value + li = parsed.find_all(lambda tag, key=key: tag.name == "li" and key in tag.text) + assert value in li[0].text def test_structure_results_panel(generate_qeapp_workchain): From 32d7dbef7f07ee3070dd07dee70798d015f62d34 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 16:00:06 +0000 Subject: [PATCH 04/15] Move failure report to buttom of summary view --- .../app/result/components/summary/model.py | 57 +++++++++++++++++-- .../app/result/components/summary/outputs.py | 44 +------------- .../app/result/components/summary/summary.py | 36 +++++++----- src/aiidalab_qe/app/static/styles/style.css | 28 --------- src/aiidalab_qe/app/static/styles/summary.css | 34 +++++++++++ .../static/templates/workflow_failure.jinja | 43 +++++++------- 6 files changed, 130 insertions(+), 112 deletions(-) diff --git a/src/aiidalab_qe/app/result/components/summary/model.py b/src/aiidalab_qe/app/result/components/summary/model.py index e6ec6cf77..f9456f79a 100644 --- a/src/aiidalab_qe/app/result/components/summary/model.py +++ b/src/aiidalab_qe/app/result/components/summary/model.py @@ -1,5 +1,14 @@ +from __future__ import annotations + +import traitlets as tl +from importlib_resources import files +from jinja2 import Environment + +from aiida import orm +from aiida.cmdline.utils.common import get_workchain_report from aiida_quantumespresso.workflows.pw.bands import PwBandsWorkChain from aiidalab_qe.app.result.components import ResultsComponentModel +from aiidalab_qe.app.static import styles, templates FUNCTIONAL_LINK_MAP = { "PBE": "https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.3865", @@ -38,6 +47,10 @@ class WorkChainSummaryModel(ResultsComponentModel): identifier = "workflow summary" + failed_calculation_report = tl.Unicode("") + + has_failure_report = False + @property def include(self): return True @@ -46,11 +59,6 @@ def generate_report_html(self): """Read from the builder parameters and generate a html for reporting the inputs for the `QeAppWorkChain`. """ - from importlib.resources import files - - from jinja2 import Environment - - from aiidalab_qe.app.static import styles, templates def _fmt_yes_no(truthy): return "yes" if truthy else "no" @@ -112,6 +120,21 @@ def generate_report_text(self, report_dict): return report_string + def generate_failure_report(self): + """Generate a html for reporting the failure of the `QeAppWorkChain`.""" + if not (process_node := self.fetch_process_node()): + return + final_calcjob = self._get_final_calcjob(process_node) + env = Environment() + template = files(templates).joinpath("workflow_failure.jinja").read_text() + style = files(styles).joinpath("style.css").read_text() + self.failed_calculation_report = env.from_string(template).render( + style=style, + process_report=get_workchain_report(process_node, "REPORT"), + calcjob_exit_message=final_calcjob.exit_message, + ) + self.has_failure_report = True + def _generate_report_parameters(self): """Generate the report parameters from the ui parameters and workchain's input. @@ -229,3 +252,27 @@ def _generate_report_parameters(self): qeapp_wc.inputs.pdos.nscf.kpoints_distance.base.attributes.get("value") ) return report + + @staticmethod + def _get_final_calcjob(node: orm.WorkChainNode) -> orm.CalcJobNode | None: + """Get the final calculation job node called by a workchain node. + + Parameters + ---------- + `node`: `orm.WorkChainNode` + The work chain node to get the final calculation job node from. + + Returns + ------- + `orm.CalcJobNode` | `None` + The final calculation job node called by the workchain node if available. + """ + try: + final_calcjob = [ + process + for process in node.called_descendants + if isinstance(process, orm.CalcJobNode) and process.is_finished + ][-1] + except IndexError: + final_calcjob = None + return final_calcjob diff --git a/src/aiidalab_qe/app/result/components/summary/outputs.py b/src/aiidalab_qe/app/result/components/summary/outputs.py index c03d8650b..9992e9824 100644 --- a/src/aiidalab_qe/app/result/components/summary/outputs.py +++ b/src/aiidalab_qe/app/result/components/summary/outputs.py @@ -1,7 +1,6 @@ from __future__ import annotations import shutil -from importlib.resources import files from pathlib import Path from tempfile import TemporaryDirectory @@ -9,12 +8,9 @@ import traitlets as tl from filelock import FileLock, Timeout from IPython.display import HTML, display -from jinja2 import Environment from aiida import orm -from aiida.cmdline.utils.common import get_workchain_report from aiida.common import LinkType -from aiidalab_qe.app.static import styles, templates from .download_data import DownloadDataWidget @@ -40,29 +36,8 @@ def __init__(self, node, export_dir=None, **kwargs): ) self._download_button_widget = DownloadDataWidget(workchain_node=self.node) - if node.exit_status != 0: - final_calcjob = self._get_final_calcjob(node) - env = Environment() - template = files(templates).joinpath("workflow_failure.jinja").read_text() - style = files(styles).joinpath("style.css").read_text() - output = ipw.HTML( - env.from_string(template).render( - style=style, - process_report=get_workchain_report(node, "REPORT"), - calcjob_exit_message=final_calcjob.exit_message, - ) - ) - else: - output = ipw.HTML() - super().__init__( - children=[ - ipw.VBox( - children=[self._download_button_widget], - layout=ipw.Layout(justify_content="space-between"), - ), - output, - ], + children=[self._download_button_widget], **kwargs, ) @@ -148,23 +123,6 @@ def _prepare_calcjob_io(cls, node: orm.WorkChainNode, root_folder: Path): counter += 1 - @staticmethod - def _get_final_calcjob(node: orm.WorkChainNode) -> orm.CalcJobNode | None: - """Get the final calculation job node called by a work chain node. - - :param node: Work chain node. - """ - try: - final_calcjob = [ - process - for process in node.called_descendants - if isinstance(process, orm.CalcJobNode) and process.is_finished - ][-1] - except IndexError: - final_calcjob = None - - return final_calcjob - @staticmethod def _write_calcjob_io(calcjob: orm.CalcJobNode, folder: Path) -> None: """Write the ``calcjob`` in and output files to ``folder``. diff --git a/src/aiidalab_qe/app/result/components/summary/summary.py b/src/aiidalab_qe/app/result/components/summary/summary.py index 5c9568390..712c7606d 100644 --- a/src/aiidalab_qe/app/result/components/summary/summary.py +++ b/src/aiidalab_qe/app/result/components/summary/summary.py @@ -7,18 +7,18 @@ class WorkChainSummary(ResultsComponent[WorkChainSummaryModel]): - def __init__(self, model: WorkChainSummaryModel, **kwargs): - super().__init__(model=model, **kwargs) - self.has_report = False - self.has_output = False + has_settings_report = False + has_download_widget = False def _on_process_change(self, _): - if not self.has_report: + if not self.has_settings_report: self._render_summary() def _on_monitor_counter_change(self, _): - if not self.has_output: - self._render_output() + if not self.has_download_widget: + self._render_download_widget() + if not self._model.has_failure_report: + self._model.generate_failure_report() def _render(self): self._render_summary() @@ -47,17 +47,27 @@ def _render_summary(self): ) self.output_download_container.add_class("summary-panel") - self.container = ipw.HBox( + container = ipw.HBox( children=[ settings_summary, self.output_download_container, ], ) - self.container.add_class("workflow-summary-container") - self.children = [self.container] - self.has_report = True + container.add_class("workflow-summary-container") - def _render_output(self): + self.failed_calculation_report = ipw.HTML() + ipw.dlink( + (self._model, "failed_calculation_report"), + (self.failed_calculation_report, "value"), + ) + + self.children = [ + container, + self.failed_calculation_report, + ] + self.has_settings_report = True + + def _render_download_widget(self): process_node = self._model.fetch_process_node() if process_node and process_node.is_terminated: output_download_widget = WorkChainOutputs(node=process_node) @@ -66,4 +76,4 @@ def _render_output(self): self.output_download_container.children[0], # type: ignore output_download_widget, ] - self.has_output = True + self.has_download_widget = True diff --git a/src/aiidalab_qe/app/static/styles/style.css b/src/aiidalab_qe/app/static/styles/style.css index 969a5a5bb..038de67a7 100644 --- a/src/aiidalab_qe/app/static/styles/style.css +++ b/src/aiidalab_qe/app/static/styles/style.css @@ -44,31 +44,3 @@ td:nth-child(even) { width: 50%; padding: 10px; } - -/* Error details */ - -.error-container { - margin: 10px 0; -} - -.error { - border: 1px solid #aaa; - border-radius: 4px; - padding: .5em .5em 0; - width: 750px; -} - -.summary { - font-weight: bold; - margin: -.5em -.5em 0; - padding: .5em -} - -.code-holder { - overflow-x: scroll; -} - -.code-holder code { - background: inherit; - white-space: pre; -} diff --git a/src/aiidalab_qe/app/static/styles/summary.css b/src/aiidalab_qe/app/static/styles/summary.css index 6e6d8bd00..44bf836ea 100644 --- a/src/aiidalab_qe/app/static/styles/summary.css +++ b/src/aiidalab_qe/app/static/styles/summary.css @@ -42,3 +42,37 @@ .settings-summary a { text-decoration: none; } + +/* Error details */ + +.error-container { + margin: 20px 0 0; +} + +.error-container p { + line-height: 1.4; +} + +.error-message { + margin-bottom: 10px; +} + +.error-message pre { + display: inline; +} + +.error { + border: 1px solid #aaa; + padding: 0.5em 0.5em 0; +} + +.summary { + font-weight: bold; + margin: -0.5em -0.5em 0; + padding: 0.5em; +} + +.code-holder code { + background: inherit; + white-space: pre; +} diff --git a/src/aiidalab_qe/app/static/templates/workflow_failure.jinja b/src/aiidalab_qe/app/static/templates/workflow_failure.jinja index b7adb3a8a..3146f972b 100644 --- a/src/aiidalab_qe/app/static/templates/workflow_failure.jinja +++ b/src/aiidalab_qe/app/static/templates/workflow_failure.jinja @@ -1,29 +1,26 @@ -
+
+

+ Oh no... It seems the workflow did not complete successfully. + Unfortunately, DFT calculations are not guaranteed to run to completion in all cases. + You can either consult the workflow report below or download the calculation files to help understand what + went wrong. +

+
+ The returned error message of the final failed calculation is: +
{{ calcjob_exit_message }}
+
+ +
+ + View the full workflow report + +
+ {{ process_report }} +
+
-

-Oh no... It seems the workflow did not complete successfully. -Unfortunately, DFT calculations are not guaranteed to run to completion in all cases. -You can either consult the workflow report below or download the calculation files to help understand what went wrong. -

- -

-The returned error message of the final failed calculation is: - -

{{ calcjob_exit_message }}
- -

- -
- - View the full workflow report - -
- {{ process_report }}
-
- -
From 07ce47969994779f65ecf3ee6676552c9cbd3985 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 16:02:14 +0000 Subject: [PATCH 05/15] Fix line height in old version notification --- .../app/result/components/summary/download_data.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/aiidalab_qe/app/result/components/summary/download_data.py b/src/aiidalab_qe/app/result/components/summary/download_data.py index 968b6c32f..0eb5b37b1 100644 --- a/src/aiidalab_qe/app/result/components/summary/download_data.py +++ b/src/aiidalab_qe/app/result/components/summary/download_data.py @@ -48,8 +48,10 @@ def __init__(self, workchain_node): if not dumper_is_available: children.append( ipw.HTML(""" -

The raw data download is not available because - the AiiDA version is too old.

+

+ The raw data download is not available because the AiiDA + version is too old. +

"""), ) From baaa988834123bd873e47df564309aadd51c591f Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 16:26:00 +0000 Subject: [PATCH 06/15] Fix download raw data button enable/disable logic --- .../app/result/components/summary/download_data.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/aiidalab_qe/app/result/components/summary/download_data.py b/src/aiidalab_qe/app/result/components/summary/download_data.py index 0eb5b37b1..d04886895 100644 --- a/src/aiidalab_qe/app/result/components/summary/download_data.py +++ b/src/aiidalab_qe/app/result/components/summary/download_data.py @@ -33,11 +33,11 @@ def __init__(self, workchain_node): from aiida.tools.dumping.processes import ProcessDumper # noqa: F401 self.download_raw_button.on_click(self._download_data_thread) - dumper_is_available = True + self.dumper_is_available = True except Exception: - dumper_is_available = False + self.dumper_is_available = False - self.download_raw_button.disabled = not dumper_is_available + self.download_raw_button.disabled = not self.dumper_is_available self.node = workchain_node @@ -45,7 +45,7 @@ def __init__(self, workchain_node): children = [] - if not dumper_is_available: + if not self.dumper_is_available: children.append( ipw.HTML("""

@@ -109,7 +109,8 @@ def _disable_buttons(self): self.download_archive_button.disabled = True def _enable_buttons(self): - self.download_raw_button.disabled = False + if self.dumper_is_available: + self.download_raw_button.disabled = False self.download_archive_button.disabled = False @staticmethod From f74fbead2cfdcd7a0ae4ba9afe20424b1f788dfd Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 16:26:36 +0000 Subject: [PATCH 07/15] Clean up table summary view --- src/aiidalab_qe/app/static/styles/style.css | 44 +++++++++---------- src/aiidalab_qe/app/static/styles/summary.css | 2 +- .../templates/workflow_table_summary.jinja | 16 +++---- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/aiidalab_qe/app/static/styles/style.css b/src/aiidalab_qe/app/static/styles/style.css index 038de67a7..55bc6080c 100644 --- a/src/aiidalab_qe/app/static/styles/style.css +++ b/src/aiidalab_qe/app/static/styles/style.css @@ -1,46 +1,46 @@ -:root { - --lab-blue: #2097F3; - --lab-background: #d3ecff; -} - h3 { - margin-top: 0px; + margin-top: 0px; } /* Tables */ table { - font-family: arial, sans-serif; - border-collapse: collapse; - border: 1px solid #bbbbbb; - width: 100%; + font-family: arial, sans-serif; + border-collapse: collapse; + border: 1px solid #bbbbbb; + width: 100%; } -td, tr { - text-align: left; - padding: 2px 8px; +td, +tr { + text-align: left; + padding: 2px 8px; } tr { - background-color: var(--lab-background); + background-color: var(--color-init); } tr:nth-child(even) { - background-color: #ffffff; + background-color: #ffffff; +} + +td:first-child { + width: 200px; } td:nth-child(even) { - border-left: 0px; + border-left: 0px; } .row:after { - content: ""; - display: table; - clear: both; + content: ""; + display: table; + clear: both; } .column { - float: left; - width: 50%; - padding: 10px; + float: left; + width: 50%; + padding: 10px; } diff --git a/src/aiidalab_qe/app/static/styles/summary.css b/src/aiidalab_qe/app/static/styles/summary.css index 44bf836ea..eba6fbf81 100644 --- a/src/aiidalab_qe/app/static/styles/summary.css +++ b/src/aiidalab_qe/app/static/styles/summary.css @@ -19,7 +19,7 @@ .summary-panel { width: 100%; border: 1px solid #9e9e9e; - padding: 0 20px 10px; + padding: 0 20px 20px; margin: 0; } diff --git a/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja index 7d96c9d24..88c8fbb41 100644 --- a/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja +++ b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja @@ -1,8 +1,7 @@ -

-
-

Standard settings

+
+

Main settings

@@ -33,16 +32,16 @@
Structure geometry optimized
-
-

Advanced settings

+
+

Advanced settings

- + @@ -99,6 +98,7 @@ + {% if material_magnetic == "collinear" %} {% if tot_magnetization %} @@ -110,6 +110,7 @@ {% endif %} + {% endif %} {% if hubbard_u %} @@ -125,6 +126,5 @@
Functional + {{ functional }} -
Pseudopotential library{{ vdw_corr }}
Total magnetization{{ initial_magnetic_moments }}
DFT+U
-
From 9209f7f121cf401e17f024aed04d39007daf49e5 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 16:27:11 +0000 Subject: [PATCH 08/15] Implement user-specified configuration for summary view format --- src/aiidalab_qe/app/parameters/qeapp.yaml | 2 ++ .../app/result/components/summary/model.py | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/aiidalab_qe/app/parameters/qeapp.yaml b/src/aiidalab_qe/app/parameters/qeapp.yaml index ca6798487..952d45b1d 100644 --- a/src/aiidalab_qe/app/parameters/qeapp.yaml +++ b/src/aiidalab_qe/app/parameters/qeapp.yaml @@ -40,3 +40,5 @@ codes: code: xspectra-7.2@localhost hp: code: hp-7.2@localhost + +summary_format: list diff --git a/src/aiidalab_qe/app/result/components/summary/model.py b/src/aiidalab_qe/app/result/components/summary/model.py index f9456f79a..d74d256d3 100644 --- a/src/aiidalab_qe/app/result/components/summary/model.py +++ b/src/aiidalab_qe/app/result/components/summary/model.py @@ -7,9 +7,13 @@ from aiida import orm from aiida.cmdline.utils.common import get_workchain_report from aiida_quantumespresso.workflows.pw.bands import PwBandsWorkChain +from aiidalab_qe.app.parameters import DEFAULT_PARAMETERS from aiidalab_qe.app.result.components import ResultsComponentModel from aiidalab_qe.app.static import styles, templates +DEFAULT: dict = DEFAULT_PARAMETERS # type: ignore + + FUNCTIONAL_LINK_MAP = { "PBE": "https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.77.3865", "PBEsol": "https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.100.136406", @@ -69,7 +73,11 @@ def _fmt_yes_no(truthy): "fmt_yes_no": _fmt_yes_no, } ) - template = files(templates).joinpath("workflow_list_summary.jinja").read_text() + template = ( + files(templates) + .joinpath(f"workflow_{DEFAULT['summary_format']}_summary.jinja") + .read_text() + ) style = files(styles).joinpath("style.css").read_text() parameters = self._generate_report_parameters() report = {key: value for key, value in parameters.items() if value is not None} @@ -231,7 +239,7 @@ def _generate_report_parameters(self): qeapp_wc.inputs.structure.pbc, "xyz" ) - # Spin-Oribit coupling + # Spin-Orbit coupling report["spin_orbit"] = pw_parameters["SYSTEM"].get("lspinorb", False) if hubbard_dict := ui_parameters["advanced"].pop("hubbard_parameters", None): From bd4fda5aa215619fde9149d2562d4c521085fd66 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 17:30:31 +0000 Subject: [PATCH 09/15] Only show failure report for failed jobs --- src/aiidalab_qe/app/result/components/summary/model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/aiidalab_qe/app/result/components/summary/model.py b/src/aiidalab_qe/app/result/components/summary/model.py index d74d256d3..c3bbafa99 100644 --- a/src/aiidalab_qe/app/result/components/summary/model.py +++ b/src/aiidalab_qe/app/result/components/summary/model.py @@ -132,6 +132,8 @@ def generate_failure_report(self): """Generate a html for reporting the failure of the `QeAppWorkChain`.""" if not (process_node := self.fetch_process_node()): return + if process_node.exit_status == 0: + return final_calcjob = self._get_final_calcjob(process_node) env = Environment() template = files(templates).joinpath("workflow_failure.jinja").read_text() From 6e7edfc7325bfc91bedf5aa35204df825b56e959 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 17:35:03 +0000 Subject: [PATCH 10/15] Reduce `td` vertical padding --- src/aiidalab_qe/app/static/styles/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiidalab_qe/app/static/styles/style.css b/src/aiidalab_qe/app/static/styles/style.css index 55bc6080c..c79cdcd5e 100644 --- a/src/aiidalab_qe/app/static/styles/style.css +++ b/src/aiidalab_qe/app/static/styles/style.css @@ -14,7 +14,7 @@ table { td, tr { text-align: left; - padding: 2px 8px; + padding: 0 8px; } tr { From 3dca720801b350e6ad7eacd02898174c2d20faf7 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 29 Dec 2024 17:35:20 +0000 Subject: [PATCH 11/15] Change "Main settings" to "Basic settings" for consistency --- .../app/static/templates/workflow_list_summary.jinja | 2 +- .../app/static/templates/workflow_table_summary.jinja | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja index 704644cec..21ab87a86 100644 --- a/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja +++ b/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja @@ -1,7 +1,7 @@
-

Main settings

+

Basic settings

  • Structure geometry optimized: {{ relaxed | fmt_yes_no }} diff --git a/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja index 88c8fbb41..eed7a2ef3 100644 --- a/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja +++ b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja @@ -1,7 +1,7 @@
    -

    Main settings

    +

    Basic settings

    From bcec2d491a85591560d698d8c8e7023f8e7374c4 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Mon, 30 Dec 2024 08:11:28 +0000 Subject: [PATCH 12/15] Add workflow properties section to summary --- .../app/result/components/summary/model.py | 9 + .../app/result/components/summary/summary.py | 4 + src/aiidalab_qe/app/static/styles/style.css | 6 +- src/aiidalab_qe/app/static/styles/summary.css | 27 ++- .../templates/workflow_list_summary.jinja | 203 ++++++++++-------- .../templates/workflow_table_summary.jinja | 33 ++- src/aiidalab_qe/common/time.py | 25 +++ 7 files changed, 206 insertions(+), 101 deletions(-) create mode 100644 src/aiidalab_qe/common/time.py diff --git a/src/aiidalab_qe/app/result/components/summary/model.py b/src/aiidalab_qe/app/result/components/summary/model.py index c3bbafa99..a588fa7c1 100644 --- a/src/aiidalab_qe/app/result/components/summary/model.py +++ b/src/aiidalab_qe/app/result/components/summary/model.py @@ -10,6 +10,7 @@ from aiidalab_qe.app.parameters import DEFAULT_PARAMETERS from aiidalab_qe.app.result.components import ResultsComponentModel from aiidalab_qe.app.static import styles, templates +from aiidalab_qe.common.time import format_time, relative_time DEFAULT: dict = DEFAULT_PARAMETERS # type: ignore @@ -168,6 +169,14 @@ def _generate_report_parameters(self): if "workchain" not in ui_parameters: return {} report = { + "pk": qeapp_wc.pk, + "uuid": str(qeapp_wc.uuid), + "label": qeapp_wc.label, + "description": qeapp_wc.description, + "creation_time": format_time(qeapp_wc.ctime), + "creation_time_relative": relative_time(qeapp_wc.ctime), + "modification_time": format_time(qeapp_wc.mtime), + "modification_time_relative": relative_time(qeapp_wc.mtime), "relaxed": None if ui_parameters["workchain"]["relax_type"] == "none" else ui_parameters["workchain"]["relax_type"], diff --git a/src/aiidalab_qe/app/result/components/summary/summary.py b/src/aiidalab_qe/app/result/components/summary/summary.py index 712c7606d..81712c343 100644 --- a/src/aiidalab_qe/app/result/components/summary/summary.py +++ b/src/aiidalab_qe/app/result/components/summary/summary.py @@ -1,10 +1,13 @@ import ipywidgets as ipw +from aiidalab_qe.app.parameters import DEFAULT_PARAMETERS from aiidalab_qe.app.result.components import ResultsComponent from .model import WorkChainSummaryModel from .outputs import WorkChainOutputs +DEFAULT: dict = DEFAULT_PARAMETERS # type: ignore + class WorkChainSummary(ResultsComponent[WorkChainSummaryModel]): has_settings_report = False @@ -54,6 +57,7 @@ def _render_summary(self): ], ) container.add_class("workflow-summary-container") + container.add_class(DEFAULT["summary_format"]) self.failed_calculation_report = ipw.HTML() ipw.dlink( diff --git a/src/aiidalab_qe/app/static/styles/style.css b/src/aiidalab_qe/app/static/styles/style.css index c79cdcd5e..2fad9be5e 100644 --- a/src/aiidalab_qe/app/static/styles/style.css +++ b/src/aiidalab_qe/app/static/styles/style.css @@ -14,7 +14,7 @@ table { td, tr { text-align: left; - padding: 0 8px; + padding: 4px 8px; } tr { @@ -25,10 +25,6 @@ tr:nth-child(even) { background-color: #ffffff; } -td:first-child { - width: 200px; -} - td:nth-child(even) { border-left: 0px; } diff --git a/src/aiidalab_qe/app/static/styles/summary.css b/src/aiidalab_qe/app/static/styles/summary.css index eba6fbf81..c15882a13 100644 --- a/src/aiidalab_qe/app/static/styles/summary.css +++ b/src/aiidalab_qe/app/static/styles/summary.css @@ -1,28 +1,43 @@ .workflow-summary-container { - width: 75%; - margin: 0 auto; gap: 10px; } +.workflow-summary-container.list { + width: 70%; + margin: 0 auto; +} + @media (max-width: 1199px) { - .workflow-summary-container { - width: 92%; + .workflow-summary-container.list { + width: 85%; } } @media (max-width: 991px) { - .workflow-summary-container { + .workflow-summary-container.list { width: 100%; } } .summary-panel { - width: 100%; border: 1px solid #9e9e9e; padding: 0 20px 20px; margin: 0; } +.summary-panel:first-child { + width: 200%; +} + +.summary-panel:first-child tr, +.summary-panel:first-child td { + padding: 0 8px; +} + +.summary-panel:first-child td:first-child { + width: 250px; +} + .settings-summary { margin: 2px; font-family: Arial, sans-serif; diff --git a/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja index 21ab87a86..421cb35a8 100644 --- a/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja +++ b/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja @@ -1,100 +1,127 @@
    -

    Basic settings

    -
      -
    • - Structure geometry optimized: {{ relaxed | fmt_yes_no }} -
    • - {% if relaxed %} -
    • - Optimization method: {{ relax_method }} -
    • - {% endif %} -
    • - Protocol: {{ protocol }} -
    • -
    • - Magnetism: {{ material_magnetic }} -
    • -
    • - Electronic type: {{ electronic_type }} -
    • -
    • - Periodicity: {{ periodicity }} -
    • -
    - -

    Advanced settings

    -
      -
    • - Functional: - {{ functional }} -
    • -
    • - Pseudopotential library: - {{ pseudo_library }} {{ pseudo_protocol }} v{{ pseudo_version }} -
    • -
    • - Energy cutoff (wave functions): {{ energy_cutoff_wfc }} Ry -
    • -
    • - Energy cutoff (charge density): {{ energy_cutoff_rho }} Ry -
    • -
    • - Occupation type (SCF): {{ occupation_type }} -
    • - {% if occupation_type == "smearing" %} +
      +

      Workflow properties

      • - Smearing width (degauss): {{ degauss }} Ry + PK: {{ pk }} +
      • +
      • + UUID: {{ uuid }} +
      • +
      • + Label: {{ label }} +
      • +
      • + Description: {{ description }} +
      • +
      • + Creation time: {{ creation_time }} ({{ creation_time_relative }}) +
      • +
      • + Modification time: {{ modification_time }} ({{ modification_time_relative }}) +
      • +
      +
      +
      +

      Basic settings

      +
        +
      • + Structure geometry optimized: {{ relaxed | fmt_yes_no }} +
      • + {% if relaxed %} +
      • + Optimization method: {{ relax_method }} +
      • + {% endif %} +
      • + Protocol: {{ protocol }} +
      • +
      • + Magnetism: {{ material_magnetic }} +
      • +
      • + Electronic type: {{ electronic_type }} +
      • +
      • + Periodicity: {{ periodicity }} +
      • +
      +
      +
      +

      Advanced settings

      +
        +
      • + Functional: + {{ functional }} +
      • +
      • + Pseudopotential library: + {{ pseudo_library }} {{ pseudo_protocol }} v{{ pseudo_version + }} +
      • +
      • + Energy cutoff (wave functions): {{ energy_cutoff_wfc }} Ry +
      • +
      • + Energy cutoff (charge density): {{ energy_cutoff_rho }} Ry +
      • +
      • + Occupation type (SCF): {{ occupation_type }} +
      • + {% if occupation_type == "smearing" %} +
          +
        • + Smearing width (degauss): {{ degauss }} Ry +
        • +
        • + Smearing type: {{ smearing }} +
        • +
        + {% endif %} +
      • + K-point mesh distance (SCF): {{ scf_kpoints_distance }} Å-1 +
      • + {% if bands_computed %} +
      • + K-point line distance (Bands): {{ bands_kpoints_distance }} Å-1 +
      • + {% endif %} + {% if pdos_computed %} +
      • + K-point mesh distance (NSCF): {{ nscf_kpoints_distance }} Å-1 +
      • + {% endif %} +
      • + Total charge: {{ tot_charge }} +
      • +
      • + Van der Waals correction: {{ vdw_corr }} +
      • + {% if material_magnetic == "collinear" %} + {% if tot_magnetization %} +
      • + Total magnetization: {{ tot_magnetization }} +
      • + {% else %} +
      • + Initial magnetic moments: {{ initial_magnetic_moments }} +
      • + {% endif %} + {% endif %} + {% if hubbard_u %} +
      • + DFT+U: {{ hubbard_u }}
      • + {% endif %} + {% if spin_orbit %}
      • - Smearing type: {{ smearing }} + Spin-orbit coupling: {{ spin_orbit }}
      • + {% endif %}
      - {% endif %} -
    • - K-point mesh distance (SCF): {{ scf_kpoints_distance }} Å-1 -
    • - {% if bands_computed %} -
    • - K-point line distance (Bands): {{ bands_kpoints_distance }} Å-1 -
    • - {% endif %} - {% if pdos_computed %} -
    • - K-point mesh distance (NSCF): {{ nscf_kpoints_distance }} Å-1 -
    • - {% endif %} -
    • - Total charge: {{ tot_charge }} -
    • -
    • - Van der Waals correction: {{ vdw_corr }} -
    • - {% if material_magnetic == "collinear" %} - {% if tot_magnetization %} -
    • - Total magnetization: {{ tot_magnetization }} -
    • - {% else %} -
    • - Initial magnetic moments: {{ initial_magnetic_moments }} -
    • - {% endif %} - {% endif %} - {% if hubbard_u %} -
    • - DFT+U: {{ hubbard_u }} -
    • - {% endif %} - {% if spin_orbit %} -
    • - Spin-orbit coupling: {{ spin_orbit }} -
    • - {% endif %} -
    +
    diff --git a/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja index eed7a2ef3..2a17b07b2 100644 --- a/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja +++ b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja @@ -1,6 +1,35 @@ -
    +
    +

    Workflow properties

    +
    Structure geometry optimized
    + + + + + + + + + + + + + + + + + + + + + + + + +
    PK{{ pk }}
    UUID{{ uuid }}
    Label{{ label }}
    Description{{ description }}
    Creation time{{ creation_time }} ({{ creation_time_relative }})
    Modification time{{ modification_time }} ({{ modification_time_relative }})
    +
    +

    Basic settings

    @@ -32,7 +61,7 @@
    -
    +

    Advanced settings

    diff --git a/src/aiidalab_qe/common/time.py b/src/aiidalab_qe/common/time.py new file mode 100644 index 000000000..1e1382e9a --- /dev/null +++ b/src/aiidalab_qe/common/time.py @@ -0,0 +1,25 @@ +from datetime import datetime + +from dateutil.relativedelta import relativedelta + + +def format_time(time: datetime): + return time.strftime("%Y-%m-%d %H:%M:%S") + + +def relative_time(time: datetime): + # TODO consider using humanize or arrow libraries for this + now = datetime.now(time.tzinfo) + delta = relativedelta(now, time) + if delta.years > 0: + return f"{delta.years} year{'s' if delta.years > 1 else ''} ago" + elif delta.months > 0: + return f"{delta.months} month{'s' if delta.months > 1 else ''} ago" + elif delta.days > 0: + return f"{delta.days} day{'s' if delta.days > 1 else ''} ago" + elif delta.hours > 0: + return f"{delta.hours} hour{'s' if delta.hours > 1 else ''} ago" + elif delta.minutes > 0: + return f"{delta.minutes} minute{'s' if delta.minutes > 1 else ''} ago" + else: + return f"{delta.seconds} second{'s' if delta.seconds > 1 else ''} ago" From 03c0a90a8bc16d585d0a52d062da165f8a386e35 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Mon, 30 Dec 2024 08:21:02 +0000 Subject: [PATCH 13/15] Discard redundant title --- .../app/result/components/viewer/viewer.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/aiidalab_qe/app/result/components/viewer/viewer.py b/src/aiidalab_qe/app/result/components/viewer/viewer.py index 3357aa32d..288671e51 100644 --- a/src/aiidalab_qe/app/result/components/viewer/viewer.py +++ b/src/aiidalab_qe/app/result/components/viewer/viewer.py @@ -29,24 +29,12 @@ def _on_tab_change(self, change): tab.render() def _render(self): - if node := self._model.fetch_process_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 = ipw.HTML(title) - self.tabs = ipw.Tab(selected_index=None) self.tabs.observe( self._on_tab_change, "selected_index", ) - - self.children = [ - self.title, - self.tabs, - ] + self.children = [self.tabs] def _post_render(self): self._set_tabs() From 4006186b8110220bd824170a872aed9be1133132 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Mon, 30 Dec 2024 09:33:51 +0000 Subject: [PATCH 14/15] Add structure properties to summary --- .../app/result/components/summary/model.py | 12 ++++++++ .../templates/workflow_list_summary.jinja | 22 +++++++++++++- .../templates/workflow_table_summary.jinja | 30 ++++++++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/aiidalab_qe/app/result/components/summary/model.py b/src/aiidalab_qe/app/result/components/summary/model.py index a588fa7c1..c81e75c4d 100644 --- a/src/aiidalab_qe/app/result/components/summary/model.py +++ b/src/aiidalab_qe/app/result/components/summary/model.py @@ -168,6 +168,7 @@ def _generate_report_parameters(self): # drop support for old ui parameters if "workchain" not in ui_parameters: return {} + initial_structure = qeapp_wc.inputs.structure report = { "pk": qeapp_wc.pk, "uuid": str(qeapp_wc.uuid), @@ -177,6 +178,17 @@ def _generate_report_parameters(self): "creation_time_relative": relative_time(qeapp_wc.ctime), "modification_time": format_time(qeapp_wc.mtime), "modification_time_relative": relative_time(qeapp_wc.mtime), + "formula": initial_structure.get_formula(), + "num_atoms": len(initial_structure.sites), + "space_group": "{} ({})".format( + *initial_structure.get_pymatgen().get_space_group_info() + ), + "cell_lengths": "{:.3f} {:.3f} {:.3f}".format( + *initial_structure.cell_lengths + ), + "cell_angles": "{:.0f} {:.0f} {:.0f}".format( + *initial_structure.cell_angles + ), "relaxed": None if ui_parameters["workchain"]["relax_type"] == "none" else ui_parameters["workchain"]["relax_type"], diff --git a/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja index 421cb35a8..4588d070c 100644 --- a/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja +++ b/src/aiidalab_qe/app/static/templates/workflow_list_summary.jinja @@ -24,11 +24,31 @@ +
    +

    Structure properties

    +
      +
    • + Chemical formula: {{ formula }} +
    • +
    • + Number of atoms: {{ num_atoms }} +
    • +
    • + Space group: {{ space_group }} +
    • +
    • + Cell lengths in Å: {{ cell_lengths }} +
    • +
    • + Cell angles in °: {{ cell_angles }} +
    • +
    +

    Basic settings

    • - Structure geometry optimized: {{ relaxed | fmt_yes_no }} + Structure geometry optimization: {{ relaxed | fmt_yes_no }}
    • {% if relaxed %}
    • diff --git a/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja index 2a17b07b2..7706dd014 100644 --- a/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja +++ b/src/aiidalab_qe/app/static/templates/workflow_table_summary.jinja @@ -29,11 +29,36 @@
    +
    +

    Structure properties

    + + + + + + + + + + + + + + + + + + + + + +
    Chemical formula{{ formula }}
    Number of atoms{{ num_atoms }}
    Space group{{ space_group }}
    Cell lengths in Å{{ cell_lengths }}
    Cell angles in °{{ cell_angles }}
    +

    Basic settings

    - + {% if relaxed %} @@ -58,7 +83,6 @@ -
    Structure geometry optimizedStructure geometry optimization {{ relaxed | fmt_yes_no }}
    Periodicity {{ periodicity }}
    @@ -126,7 +150,6 @@ Van der Waals correction {{ vdw_corr }} - {% if material_magnetic == "collinear" %} {% if tot_magnetization %} @@ -152,7 +175,6 @@ {{ spin_orbit }} {% endif %} -
    From c6e172532e74a8fe05052cd813d1034a6a43f6d7 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Mon, 30 Dec 2024 09:59:59 +0000 Subject: [PATCH 15/15] Update tests --- tests/test_result.py | 10 ++++++++++ tests/test_result/test_summary_report.yml | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/tests/test_result.py b/tests/test_result.py index 3a86c176d..db7de7038 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -50,6 +50,16 @@ def test_summary_report(data_regression, generate_qeapp_workchain): model = WorkChainSummaryModel() model.process_uuid = workchain.node.uuid report_parameters = model._generate_report_parameters() + # Discard variable parameters + for key in ( + "pk", + "uuid", + "creation_time", + "creation_time_relative", + "modification_time", + "modification_time_relative", + ): + report_parameters.pop(key) data_regression.check(report_parameters) diff --git a/tests/test_result/test_summary_report.yml b/tests/test_result/test_summary_report.yml index b1ebaa504..583bbbc82 100644 --- a/tests/test_result/test_summary_report.yml +++ b/tests/test_result/test_summary_report.yml @@ -1,14 +1,20 @@ bands_computed: true bands_kpoints_distance: 0.1 +cell_angles: 60 60 60 +cell_lengths: 3.847 3.847 3.847 degauss: 0.01 +description: '' electronic_type: metal energy_cutoff_rho: 240.0 energy_cutoff_wfc: 30.0 +formula: Si2 functional: PBEsol functional_link: https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.100.136406 initial_magnetic_moments: null +label: '' material_magnetic: none nscf_kpoints_distance: 0.5 +num_atoms: 2 occupation_type: smearing pdos_computed: true periodicity: xyz @@ -26,6 +32,7 @@ relax_method: positions_cell relaxed: positions_cell scf_kpoints_distance: 0.5 smearing: cold +space_group: Fd-3m (227) spin_orbit: false tot_charge: 0.0 tot_magnetization: false