From 862c7ddbb3f70ca6095490e4961f94015c5315f4 Mon Sep 17 00:00:00 2001 From: Aliaksandr Yakutovich Date: Fri, 23 Jun 2023 09:38:19 +0000 Subject: [PATCH 1/6] Migrate the app to AiiDA 2.x --- empa_molecules/steps.py | 55 +++++++++++++++++++++++---------------- empa_molecules/widgets.py | 15 ++++++----- search.ipynb | 2 +- setup.cfg | 8 +++--- spin_calculation.ipynb | 26 ++++++++++-------- 5 files changed, 60 insertions(+), 46 deletions(-) diff --git a/empa_molecules/steps.py b/empa_molecules/steps.py index 1b59556..97aabce 100644 --- a/empa_molecules/steps.py +++ b/empa_molecules/steps.py @@ -1,6 +1,6 @@ import aiidalab_widgets_base as awb import ipywidgets as ipw -import traitlets +import traitlets as tr from aiida import engine, orm, plugins from .widgets import NodeViewWidget @@ -12,7 +12,7 @@ class StructureSelectionStep(ipw.VBox, awb.WizardAppWidgetStep): """Integrated widget for the selection of structures from different sources.""" - confirmed_structure = traitlets.Instance(StructureData, allow_none=True) + confirmed_structure = tr.Instance(StructureData, allow_none=True) def __init__(self, description=None, **kwargs): self.manager = awb.StructureManagerWidget( @@ -57,7 +57,7 @@ def __init__(self, description=None, **kwargs): **kwargs ) - @traitlets.default("state") + @tr.default("state") def _default_state(self): return self.State.READY @@ -73,12 +73,12 @@ def _update_state(self, _=None): else: self.state = self.State.SUCCESS - @traitlets.observe("confirmed_structure") + @tr.observe("confirmed_structure") def _observe_confirmed_structure(self, _): with self.hold_trait_notifications(): self._update_state() - @traitlets.observe("state") + @tr.observe("state") def _observe_state(self, change): with self.hold_trait_notifications(): state = change["new"] @@ -100,8 +100,8 @@ def reset(self): # unconfirm class ConfigureGaussianCalculationStep(ipw.VBox, awb.WizardAppWidgetStep): """Widget to prepare gaussian inputs.""" - inputs = traitlets.Dict() - input_structure = traitlets.Instance(StructureData, allow_none=True) + inputs = tr.Dict() + input_structure = tr.Instance(StructureData, allow_none=True) def __init__(self, **kwargs): self.dft_functional = ipw.Dropdown( @@ -200,7 +200,7 @@ def confirm(self, _=None): } self.state = self.State.SUCCESS - @traitlets.default("state") + @tr.default("state") def _default_state(self): return self.State.INIT @@ -210,23 +210,23 @@ class SubmitGaussianCalculationStep(ipw.VBox, awb.WizardAppWidgetStep): # We use traitlets to connect the different steps. # Note that we can use dlinked transformations, they do not need to be of the same type. - inputs = traitlets.Dict() - process = traitlets.Instance(orm.ProcessNode, allow_none=True) + inputs = tr.Dict() + value = tr.Unicode(allow_none=True) def __init__(self, **kwargs): self.configuration_label = ipw.HTML("Specify computational resources.") # Codes. self.gaussian_code_dropdown = awb.ComputationalResourcesWidget( - description="Gaussian", input_plugin="gaussian" + description="Gaussian", default_calc_job_plugin="gaussian" ) self.gaussian_code_dropdown.observe(self._update_state, ["value"]) self.cubegen_code_dropdown = awb.ComputationalResourcesWidget( - description="Cubegen", input_plugin="gaussian.cubegen" + description="Cubegen", default_calc_job_plugin="gaussian.cubegen" ) self.cubegen_code_dropdown.observe(self._update_state, ["value"]) self.formchk_code_dropdown = awb.ComputationalResourcesWidget( - description="Formchk", input_plugin="gaussian.formchk" + description="Formchk", default_calc_job_plugin="gaussian.formchk" ) self.formchk_code_dropdown.observe(self._update_state, ["value"]) @@ -255,10 +255,14 @@ def __init__(self, **kwargs): self.observe(self._update_state, ["inputs"]) self.btn_submit_mol_opt = awb.SubmitButtonWidget( - GaussianSpinWorkChain, input_dictionary_function=self.prepare_spin_calc + GaussianSpinWorkChain, inputs_generator=self.prepare_spin_calc ) self.btn_submit_mol_opt.btn_submit.disabled = True - traitlets.dlink((self.btn_submit_mol_opt, "process"), (self, "process")) + tr.dlink( + (self.btn_submit_mol_opt, "process"), + (self, "value"), + transform=lambda x: x.uuid if x else None, + ) super().__init__( [ @@ -316,9 +320,9 @@ def prepare_spin_calc(self): builder[key] = value # Codes. - builder.gaussian_code = self.gaussian_code_dropdown.value - builder.formchk_code = self.formchk_code_dropdown.value - builder.cubegen_code = self.cubegen_code_dropdown.value + builder.gaussian_code = orm.load_code(self.gaussian_code_dropdown.value) + builder.formchk_code = orm.load_code(self.formchk_code_dropdown.value) + builder.cubegen_code = orm.load_code(self.cubegen_code_dropdown.value) # Resources. builder.options = orm.Dict( @@ -338,11 +342,12 @@ def prepare_spin_calc(self): class ViewGaussianWorkChainStatusAndResultsStep(ipw.VBox, awb.WizardAppWidgetStep): - process = traitlets.Instance(orm.ProcessNode, allow_none=True) + value = tr.Unicode(allow_none=True) def __init__(self, **kwargs): + self.process = None self.process_tree = awb.ProcessNodesTreeWidget() - ipw.dlink((self, "process"), (self.process_tree, "process")) + ipw.dlink((self, "value"), (self.process_tree, "value")) self.node_view = NodeViewWidget(layout={"width": "auto", "height": "auto"}) ipw.dlink( @@ -360,7 +365,7 @@ def __init__(self, **kwargs): self._update_state, ], ) - ipw.dlink((self, "process"), (self.process_monitor, "process")) + ipw.dlink((self, "value"), (self.process_monitor, "value")) super().__init__([self.process_status], **kwargs) @@ -369,7 +374,7 @@ def can_reset(self): return self.state is not self.State.ACTIVE def reset(self): - self.process = None + self.value = None def _update_state(self): if self.process is None: @@ -390,6 +395,10 @@ def _update_state(self): elif process_state is engine.ProcessState.FINISHED: self.state = self.State.SUCCESS - @traitlets.observe("process") + @tr.observe("value") def _observe_process(self, change): + if self.value is None: + self.process = None + else: + self.process = orm.load_node(self.value) self._update_state() diff --git a/empa_molecules/widgets.py b/empa_molecules/widgets.py index 6128c6e..6c7c872 100644 --- a/empa_molecules/widgets.py +++ b/empa_molecules/widgets.py @@ -34,7 +34,7 @@ def _observe_node(self, change): class WorkChainSelectorWidget(ipw.HBox): # The PK of a 'aiida.workflows:quantumespresso.pw.bands' WorkChainNode. - value = traitlets.Int(allow_none=True) + value = traitlets.Unicode(allow_none=True) # When this trait is set to a positive value, the work chains are automatically # refreshed every `auto_refresh_interval` seconds. @@ -59,7 +59,7 @@ def __init__(self, **kwargs): ipw.dlink( (self.work_chains_selector, "value"), (self, "value"), - transform=lambda pk: None if pk is self._NO_PROCESS else pk, + transform=lambda uuid: None if uuid is self._NO_PROCESS else uuid, ) self.refresh_work_chains_button = ipw.Button(description="Refresh") @@ -82,6 +82,7 @@ def __init__(self, **kwargs): @dataclass class WorkChainData: pk: int + uuid: str ctime: str state: str formula: str @@ -97,12 +98,12 @@ def find_work_chains(cls): order_by={"ctime": "desc"}, ) projected = builder.get_projected( - query_set, projections=["pk", "ctime", "state"] + query_set, projections=["pk", "uuid", "ctime", "state"] ) for process in projected[1:]: - pk = process[0] - formula = orm.load_node(pk).inputs.structure.get_formula() + uuid = process[0] + formula = orm.load_node(uuid).inputs.structure.get_formula() yield cls.WorkChainData(formula=formula, *process) @traitlets.default("busy") @@ -127,7 +128,7 @@ def refresh_work_chains(self, _=None): self.work_chains_selector.options = [ ("New calculation...", self._NO_PROCESS) ] + [ - (self.FMT_WORKCHAIN.format(wc=wc), wc.pk) + (self.FMT_WORKCHAIN.format(wc=wc), wc.uuid) for wc in self.find_work_chains() ] @@ -170,7 +171,7 @@ def _observe_value(self, change): new = self._NO_PROCESS if change["new"] is None else change["new"] - if new not in {pk for _, pk in self.work_chains_selector.options}: + if new not in {uuid for _, uuid in self.work_chains_selector.options}: self.refresh_work_chains() self.work_chains_selector.value = new diff --git a/search.ipynb b/search.ipynb index 1c746cf..15b7443 100644 --- a/search.ipynb +++ b/search.ipynb @@ -48,7 +48,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.9" + "version": "3.9.13" } }, "nbformat": 4, diff --git a/setup.cfg b/setup.cfg index 9fec209..57a31cf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,10 +22,10 @@ project_urls = [options] packages = find: install_requires = - aiida-gaussian>=1.4.3 - aiida-nanotech-empa>=0.5.3 - aiidalab>=21.11.0 - aiidalab-widgets-base~=1.3 + aiida-core~=2.2 + aiida-gaussian~=2.0 + aiida-nanotech-empa>=1.0.0b3 + aiidalab-widgets-base~=2.0 python_requires = >=3.7 [options.extras_require] diff --git a/spin_calculation.ipynb b/spin_calculation.ipynb index da0cbe9..646d824 100644 --- a/spin_calculation.ipynb +++ b/spin_calculation.ipynb @@ -18,8 +18,10 @@ "metadata": {}, "outputs": [], "source": [ + "%load_ext aiida\n", "%aiida\n", "import ipywidgets as ipw\n", + "import traitlets as tr\n", "import aiidalab_widgets_base as awb\n", "import urllib.parse as urlparse\n", "from aiidalab_widgets_base.bug_report import install_create_github_issue_exception_handler\n", @@ -36,12 +38,12 @@ "def _observe_process_selection(change):\n", " if change['old'] == change['new']:\n", " return\n", - " pk = change['new']\n", - " if pk is None:\n", + " uuid = change['new']\n", + " if uuid is None:\n", " app.reset()\n", " app.selected_index = 0\n", " else:\n", - " process = load_node(pk)\n", + " process = load_node(uuid)\n", "\n", " # Structure:\n", " with select_structure_step.manager.hold_sync():\n", @@ -58,9 +60,12 @@ " configure_calculation_step.confirm_button.disabled = True\n", "\n", " # Codes & resources\n", - " submit_calculation_step.gaussian_code_dropdown.value = process.inputs.gaussian_code\n", - " submit_calculation_step.formchk_code_dropdown.value = process.inputs.formchk_code\n", - " submit_calculation_step.cubegen_code_dropdown.value = process.inputs.cubegen_code\n", + " try:\n", + " submit_calculation_step.gaussian_code_dropdown.value = process.inputs.gaussian_code.uuid\n", + " submit_calculation_step.formchk_code_dropdown.value = process.inputs.formchk_code.uuid\n", + " submit_calculation_step.cubegen_code_dropdown.value = process.inputs.cubegen_code.uuid\n", + " except tr.TraitError:\n", + " pass\n", "\n", " if \"options\" in process.inputs:\n", " submit_calculation_step.n_mpi_tasks_widget.value = process.inputs.options[\"resources\"][\"tot_num_mpiprocs\"]\n", @@ -69,7 +74,7 @@ " submit_calculation_step.state = WizardAppWidgetStep.State.SUCCESS\n", "\n", " # Running process.\n", - " submit_calculation_step.process = process\n" + " submit_calculation_step.value = process.uuid\n" ] }, { @@ -94,9 +99,8 @@ "\n", "\n", "results = steps.ViewGaussianWorkChainStatusAndResultsStep(auto_advance=True)\n", - "ipw.dlink((submit_calculation_step, 'process'), (results, 'process')) \n", - "ipw.dlink((submit_calculation_step, 'process'), (work_chain_selector, 'value'),\n", - " transform=lambda node: None if node is None else node.pk)\n", + "ipw.dlink((submit_calculation_step, 'value'), (results, 'value')) \n", + "ipw.dlink((submit_calculation_step, 'value'), (work_chain_selector, 'value'))\n", "\n", "app = awb.WizardAppWidget(\n", " steps=[\n", @@ -150,7 +154,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.9" + "version": "3.9.13" } }, "nbformat": 4, From cd0f1c181f625219b0871e0e4e221594b1d005e8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Yakutovich Date: Fri, 23 Jun 2023 09:53:57 +0000 Subject: [PATCH 2/6] Fix search notebook. --- empa_molecules/templates/search.j2 | 2 +- empa_molecules/widgets.py | 9 ++++----- spin_calculation.ipynb | 13 ++++++------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/empa_molecules/templates/search.j2 b/empa_molecules/templates/search.j2 index 69e10e7..a7b9642 100644 --- a/empa_molecules/templates/search.j2 +++ b/empa_molecules/templates/search.j2 @@ -14,7 +14,7 @@ {% for row in rows %} - {{ row['pk'] }} + {{ row['pk'] }} {{ row['ctime'] }} {{ row['formula'] }} {{ row['description'] }} diff --git a/empa_molecules/widgets.py b/empa_molecules/widgets.py index 6c7c872..9909287 100644 --- a/empa_molecules/widgets.py +++ b/empa_molecules/widgets.py @@ -380,8 +380,7 @@ def observe_inp_pks(_=None): style=style, ) - # --------- - # date selection + # Date selection dt_now = datetime.datetime.now() dt_from = dt_now - datetime.timedelta(days=20) self.date_start = ipw.Text( @@ -399,7 +398,6 @@ def observe_inp_pks(_=None): ) self.date_text = ipw.HTML(value="

Select the date range:

", width="150px") - # --------- search_crit = [ ipw.HBox([inp_pks, pks_wrong_syntax]), @@ -432,6 +430,7 @@ def search(self): # html table header column_names = { "pk": "PK", + "uuid": "UUID", "ctime": "Creation Time", "formula": "Formula", "description": "Descrition", @@ -439,8 +438,7 @@ def search(self): "thumbnail": "Thumbnail", } - # query AiiDA database - + # Query AiiDA database qb = orm.QueryBuilder() qb.append(self.workchain_class, filters=self.prepare_query_filters()) qb.order_by({self.workchain_class: {"ctime": "desc"}}) @@ -454,6 +452,7 @@ def search(self): row = { "pk": node.pk, + "uuid": node.uuid, "ctime": node.ctime.strftime("%Y-%m-%d %H:%M"), "formula": node.outputs.gs_structure.get_formula(), "description": node.description, diff --git a/spin_calculation.ipynb b/spin_calculation.ipynb index 646d824..b899e4b 100644 --- a/spin_calculation.ipynb +++ b/spin_calculation.ipynb @@ -23,9 +23,8 @@ "import ipywidgets as ipw\n", "import traitlets as tr\n", "import aiidalab_widgets_base as awb\n", + "import aiidalab_widgets_base.bug_report as br\n", "import urllib.parse as urlparse\n", - "from aiidalab_widgets_base.bug_report import install_create_github_issue_exception_handler\n", - "from aiidalab_widgets_base import WizardAppWidgetStep\n", "from empa_molecules import steps, widgets" ] }, @@ -56,7 +55,7 @@ " configure_calculation_step.basis_set_opt.value = process.inputs.basis_set_opt\n", " configure_calculation_step.basis_set_scf.value = process.inputs.basis_set_scf\n", " configure_calculation_step.multiplicity_list.value = \" \".join(map(str, process.inputs.multiplicity_list))\n", - " configure_calculation_step.state = WizardAppWidgetStep.State.SUCCESS\n", + " configure_calculation_step.state = awb.WizardAppWidgetStep.State.SUCCESS\n", " configure_calculation_step.confirm_button.disabled = True\n", "\n", " # Codes & resources\n", @@ -71,7 +70,7 @@ " submit_calculation_step.n_mpi_tasks_widget.value = process.inputs.options[\"resources\"][\"tot_num_mpiprocs\"]\n", " submit_calculation_step.memory_widget.value = process.inputs.options[\"max_memory_kb\"] / 1024 / 1.25\n", " submit_calculation_step.run_time_widget.value = process.inputs.options[\"max_wallclock_seconds\"] / 60\n", - " submit_calculation_step.state = WizardAppWidgetStep.State.SUCCESS\n", + " submit_calculation_step.state = awb.WizardAppWidgetStep.State.SUCCESS\n", "\n", " # Running process.\n", " submit_calculation_step.value = process.uuid\n" @@ -114,7 +113,7 @@ "app_with_work_chain_selector = ipw.VBox(children=[work_chain_selector, app])\n", "\n", "output = ipw.Output()\n", - "install_create_github_issue_exception_handler(\n", + "br.install_create_github_issue_exception_handler(\n", " output,\n", " url='https://github.com/nanotech-empa/aiidalab-empa-molecules/issues/new',\n", " labels=('bug', 'automated-report'))\n", @@ -133,8 +132,8 @@ "source": [ "url = urlparse.urlsplit(jupyter_notebook_url)\n", "parsed_url = urlparse.parse_qs(url.query)\n", - "if 'pk' in parsed_url:\n", - " work_chain_selector.value = int(parsed_url['pk'][0])" + "if 'uuid' in parsed_url:\n", + " work_chain_selector.value = parsed_url['uuid'][0]" ] } ], From 70051a00ab56c0dafc413f924d5800f34c95280a Mon Sep 17 00:00:00 2001 From: Aliaksandr Yakutovich Date: Fri, 23 Jun 2023 09:57:06 +0000 Subject: [PATCH 3/6] Small change. --- empa_molecules/version.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/empa_molecules/version.py b/empa_molecules/version.py index e7e7209..dc6e2c8 100644 --- a/empa_molecules/version.py +++ b/empa_molecules/version.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - """This module contains project version information.""" __version__ = "v22.03.0a0" From 1537eab9ace726cf61a2fde0e53417eecda8d2e8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Yakutovich Date: Fri, 23 Jun 2023 11:06:42 +0000 Subject: [PATCH 4/6] setup.cfg: update version specification. --- setup.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 57a31cf..556e4a9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,13 +1,13 @@ [metadata] name = aiidalab_empa_molecules -version = empa_molecules.version.__version__ +version = attr: empa_molecules.version.__version__ description = App to compute molecular properties. long_description = file: README.md long_description_content_type = text/markdown url = https://github.com/nanotech-empa/aiidalab-empa-molecules author = nanotech-empa license = MIT -license_file = LICENSE +license_files = LICENSE classifiers = Development Status :: 2 - Pre-Alpha License :: OSI Approved :: MIT License From fb47dc0feccde1c61dfa439b8242566554a56c99 Mon Sep 17 00:00:00 2001 From: Aliaksandr Yakutovich Date: Fri, 23 Jun 2023 11:12:47 +0000 Subject: [PATCH 5/6] Change aiidalab image to aiidalab/full-stack --- .github/workflows/di.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/di.yml b/.github/workflows/di.yml index 70a8728..0eee6cf 100644 --- a/.github/workflows/di.yml +++ b/.github/workflows/di.yml @@ -26,7 +26,7 @@ jobs: - name: Test app uses: aiidalab/aiidalab-test-app-action@v2 with: - image: aiidalab/aiidalab-docker-stack:${{ matrix.tag }} + image: aiidalab/full-stack:${{ matrix.tag }} browser: ${{ matrix.browser }} name: empa-molecules From 647fdf40a5f591bc20d35db6c2656567fbc7d910 Mon Sep 17 00:00:00 2001 From: Aliaksandr Yakutovich Date: Fri, 23 Jun 2023 11:16:12 +0000 Subject: [PATCH 6/6] Swap stable tag with edge. --- .github/workflows/di.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/di.yml b/.github/workflows/di.yml index 0eee6cf..7860e91 100644 --- a/.github/workflows/di.yml +++ b/.github/workflows/di.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - tag: [stable, latest] + tag: [edge, latest] browser: [chrome, firefox] fail-fast: false