From 409c94f1622ea5794ae5e204e7a00f0d2f5e2b5c Mon Sep 17 00:00:00 2001 From: superstar54 Date: Sat, 23 Sep 2023 07:07:49 +0000 Subject: [PATCH 01/10] add code entry point --- src/aiidalab_qe/app/submission/__init__.py | 91 ++++++---------------- src/aiidalab_qe/common/panel.py | 17 ++++ src/aiidalab_qe/plugins/pdos/__init__.py | 15 +++- src/aiidalab_qe/plugins/pdos/workchain.py | 27 +++++++ 4 files changed, 80 insertions(+), 70 deletions(-) diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index 05963257a..ac3af3fe7 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -16,6 +16,7 @@ from IPython.display import display from aiidalab_qe.app.parameters import DEFAULT_PARAMETERS +from aiidalab_qe.app.utils import get_entry_items from aiidalab_qe.common.setup_codes import QESetupWidget from aiidalab_qe.workflows import QeAppWorkChain @@ -68,26 +69,29 @@ def __init__(self, qe_auto_setup=True, **kwargs): self.pw_code = ComputationalResourcesWidget( description="pw.x:", default_calc_job_plugin="quantumespresso.pw" ) - self.dos_code = ComputationalResourcesWidget( - description="dos.x:", - default_calc_job_plugin="quantumespresso.dos", - ) - self.projwfc_code = ComputationalResourcesWidget( - description="projwfc.x:", - default_calc_job_plugin="quantumespresso.projwfc", - ) self.resources_config = ResourceSelectionWidget() self.parallelization = ParallelizationSettings() - self.set_selected_codes(DEFAULT_PARAMETERS) self.set_resource_defaults() self.pw_code.observe(self._update_state, "value") self.pw_code.observe(self._update_resources, "value") - self.dos_code.observe(self._update_state, "value") - self.projwfc_code.observe(self._update_state, "value") + # add plugin's entry points + self.codes = {"pw": self.pw_code} + self.code_children = [ + self.codes_title, + self.codes_help, + self.pw_code, + ] + entries = get_entry_items("aiidalab_qe.properties", "code") + for _, entry_point in entries.items(): + for name, code in entry_point.items(): + self.codes[name] = code() + self.code_children.append(self.codes[name]) + self.set_selected_codes(DEFAULT_PARAMETERS["codes"]) + # s self.submit_button = ipw.Button( description="Submit", tooltip="Submit the calculation with the selected parameters.", @@ -121,11 +125,7 @@ def __init__(self, qe_auto_setup=True, **kwargs): super().__init__( children=[ - self.codes_title, - self.codes_help, - self.pw_code, - self.dos_code, - self.projwfc_code, + *self.code_children, self.resources_config, self.parallelization, self.message_area, @@ -156,43 +156,10 @@ def _identify_submission_blockers(self): if self.pw_code.value is None and not self.qe_setup_status.busy: yield ("No pw code selected") - # No code selected for pdos (this is ignored while the setup process is running). - if ( - "pdos" in self.input_parameters.get("workchain", {}).get("properties", []) - and (self.dos_code.value is None or self.projwfc_code.value is None) - and not self.qe_setup_status.busy - ): - yield "Calculating the PDOS requires both dos.x and projwfc.x to be set." - # SSSP library not installed if not self.sssp_installation_status.installed: yield "The SSSP library is not installed." - if ( - "pdos" in self.input_parameters.get("workchain", {}).get("properties", []) - and not any( - [ - self.pw_code.value is None, - self.dos_code.value is None, - self.projwfc_code.value is None, - ] - ) - and len( - set( - ( - orm.load_code(self.pw_code.value).computer.pk, - orm.load_code(self.dos_code.value).computer.pk, - orm.load_code(self.projwfc_code.value).computer.pk, - ) - ) - ) - != 1 - ): - yield ( - "All selected codes must be installed on the same computer. This is because the " - "PDOS calculations rely on large files that are not retrieved by AiiDA." - ) - def _update_state(self, _=None): # If the previous step has failed, this should fail as well. if self.previous_step_state is self.State.FAIL: @@ -342,7 +309,6 @@ def _observe_state(self, change): @tl.observe("previous_step_state") def _observe_input_structure(self, _): self._update_state() - self.set_pdos_status() @tl.observe("process") def _observe_process(self, change): @@ -352,7 +318,7 @@ def _observe_process(self, change): self.input_structure = process_node.inputs.structure ui_parameters = process_node.base.extras.get("ui_parameters", None) if ui_parameters is not None: - self.set_selected_codes(ui_parameters) + self.set_selected_codes(ui_parameters["codes"]) self._update_state() def _on_submit_button_clicked(self, _): @@ -364,15 +330,11 @@ def get_selected_codes(self): return: A dict with the code names as keys and the code UUIDs as values. """ - codes = { - "pw": self.pw_code.value, - "dos": self.dos_code.value, - "projwfc": self.projwfc_code.value, - } + codes = {key: code.value for key, code in self.codes.items()} return codes - def set_selected_codes(self, parameters): - """Set the inputs in the GUI based on a set of parameters.""" + def set_selected_codes(self, codes): + """Set the inputs in the GUI based on a set of codes.""" # Codes def _get_code_uuid(code): @@ -384,17 +346,8 @@ def _get_code_uuid(code): with self.hold_trait_notifications(): # Codes - self.pw_code.value = _get_code_uuid(parameters["codes"]["pw"]) - self.dos_code.value = _get_code_uuid(parameters["codes"]["dos"]) - self.projwfc_code.value = _get_code_uuid(parameters["codes"]["projwfc"]) - - def set_pdos_status(self): - if "pdos" in self.input_parameters.get("workchain", {}).get("properties", []): - self.dos_code.code_select_dropdown.disabled = False - self.projwfc_code.code_select_dropdown.disabled = False - else: - self.dos_code.code_select_dropdown.disabled = True - self.projwfc_code.code_select_dropdown.disabled = True + for key, value in codes.items(): + self.codes[key].value = _get_code_uuid(value) def submit(self, _=None): """Submit the work chain with the current inputs.""" diff --git a/src/aiidalab_qe/common/panel.py b/src/aiidalab_qe/common/panel.py index 5bd304495..54b24d1de 100644 --- a/src/aiidalab_qe/common/panel.py +++ b/src/aiidalab_qe/common/panel.py @@ -123,3 +123,20 @@ def _update_view(self): :param result: the result of the calculation. """ + + +class CodePanel(Panel): + title = "Code" + description = "pw.x" + default_calc_job_plugin = "quantumespresso.pw" + + def __init__(self, **kwargs): + from aiidalab_widgets_base import ComputationalResourcesWidget + + # Checkbox to see if the property should be calculated + self.code = ComputationalResourcesWidget( + description=self.description, + default_calc_job_plugin=self.default_calc_job_plugin, + ) + self.children = [self.code] + super().__init__(**kwargs) diff --git a/src/aiidalab_qe/plugins/pdos/__init__.py b/src/aiidalab_qe/plugins/pdos/__init__.py index 03c149309..0aedb80d0 100644 --- a/src/aiidalab_qe/plugins/pdos/__init__.py +++ b/src/aiidalab_qe/plugins/pdos/__init__.py @@ -1,4 +1,4 @@ -from aiidalab_qe.common.panel import OutlinePanel +from aiidalab_qe.common.panel import CodePanel, OutlinePanel from .result import Result from .workchain import workchain_and_builder @@ -9,8 +9,21 @@ class PdosOutline(OutlinePanel): help = """""" +class DosCodePanel(CodePanel): + title = "Code" + description = "dos.x" + default_calc_job_plugin = "quantumespresso.dos" + + +class ProjwfcCodePanel(CodePanel): + title = "Code" + description = "projwfc.x" + default_calc_job_plugin = "quantumespresso.projwfc" + + pdos = { "outline": PdosOutline, + "code": {"dos": DosCodePanel, "projwfc": ProjwfcCodePanel}, "result": Result, "workchain": workchain_and_builder, } diff --git a/src/aiidalab_qe/plugins/pdos/workchain.py b/src/aiidalab_qe/plugins/pdos/workchain.py index 992bca19f..857e24d64 100644 --- a/src/aiidalab_qe/plugins/pdos/workchain.py +++ b/src/aiidalab_qe/plugins/pdos/workchain.py @@ -4,12 +4,39 @@ PdosWorkChain = WorkflowFactory("quantumespresso.pdos") +def check_codes(pw_code, dos_code, projwfc_code): + if ( + not any( + [ + pw_code.value is None, + dos_code.value is None, + projwfc_code.value is None, + ] + ) + and len( + set( + ( + pw_code.computer.pk, + dos_code.computer.pk, + projwfc_code.computer.pk, + ) + ) + ) + != 1 + ): + raise ValueError( + "All selected codes must be installed on the same computer. This is because the " + "PDOS calculations rely on large files that are not retrieved by AiiDA." + ) + + def get_builder(codes, structure, parameters, **kwargs): from copy import deepcopy pw_code = codes.get("pw", None) dos_code = codes.get("dos", None) projwfc_code = codes.get("projwfc", None) + check_codes(pw_code, dos_code, projwfc_code) protocol = parameters["workchain"]["protocol"] # scf_overrides = deepcopy(parameters["advanced"]) From 86b7b9affbbec7da362f626f06b0b24e3aaaccfb Mon Sep 17 00:00:00 2001 From: superstar54 Date: Thu, 19 Oct 2023 09:01:05 +0000 Subject: [PATCH 02/10] add set_code_status and unittest --- src/aiidalab_qe/app/submission/__init__.py | 33 ++++++++++++++------- src/aiidalab_qe/common/panel.py | 17 ----------- src/aiidalab_qe/plugins/pdos/__init__.py | 23 ++++++++------- tests/conftest.py | 18 +++++------- tests/test_codes.py | 34 ++++++++++++++++++++++ tests/test_submit_qe_workchain.py | 24 --------------- 6 files changed, 76 insertions(+), 73 deletions(-) create mode 100644 tests/test_codes.py diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index 6793fdeb6..cf3639404 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -85,10 +85,10 @@ def __init__(self, qe_auto_setup=True, **kwargs): self.codes_help, self.pw_code, ] - entries = get_entry_items("aiidalab_qe.properties", "code") - for _, entry_point in entries.items(): + self.code_entries = get_entry_items("aiidalab_qe.properties", "code") + for _, entry_point in self.code_entries.items(): for name, code in entry_point.items(): - self.codes[name] = code() + self.codes[name] = code self.code_children.append(self.codes[name]) self.set_selected_codes(DEFAULT_PARAMETERS["codes"]) # s @@ -192,16 +192,11 @@ def _toggle_install_widgets(self, change): def _auto_select_code(self, change): if change["new"] and not change["old"]: - for code in [ - "pw", - "dos", - "projwfc", - ]: + for name, code_widget in self.codes.items(): try: - code_widget = getattr(self, f"{code}_code") code_widget.refresh() code_widget.value = orm.load_code( - DEFAULT_PARAMETERS["codes"][code] + DEFAULT_PARAMETERS["codes"][name] ).uuid except NotExistent: pass @@ -309,6 +304,7 @@ def _observe_state(self, change): @tl.observe("previous_step_state") def _observe_input_structure(self, _): self._update_state() + self.set_codes_status() @tl.observe("process") def _observe_process(self, change): @@ -342,7 +338,22 @@ def _get_code_uuid(code): return None with self.hold_trait_notifications(): - # Codes + for name, code in self.codes.items(): + code.value = _get_code_uuid(codes.get(name)) + + def set_codes_status(self): + """Disable code if no related property is selected.""" + # disable all codes except pw + for name, code in self.codes.items(): + if name == "pw": + continue + code.code_select_dropdown.disabled = True + # enable code if the related property is selected. + properties = self.input_parameters.get("workchain", {}).get("properties", []) + for identifer, codes in self.code_entries.items(): + if identifer in properties: + for name in codes: + self.codes[name].code_select_dropdown.disabled = False def submit(self, _=None): """Submit the work chain with the current inputs.""" diff --git a/src/aiidalab_qe/common/panel.py b/src/aiidalab_qe/common/panel.py index 54b24d1de..5bd304495 100644 --- a/src/aiidalab_qe/common/panel.py +++ b/src/aiidalab_qe/common/panel.py @@ -123,20 +123,3 @@ def _update_view(self): :param result: the result of the calculation. """ - - -class CodePanel(Panel): - title = "Code" - description = "pw.x" - default_calc_job_plugin = "quantumespresso.pw" - - def __init__(self, **kwargs): - from aiidalab_widgets_base import ComputationalResourcesWidget - - # Checkbox to see if the property should be calculated - self.code = ComputationalResourcesWidget( - description=self.description, - default_calc_job_plugin=self.default_calc_job_plugin, - ) - self.children = [self.code] - super().__init__(**kwargs) diff --git a/src/aiidalab_qe/plugins/pdos/__init__.py b/src/aiidalab_qe/plugins/pdos/__init__.py index a3ac57ccf..e84e4f31f 100644 --- a/src/aiidalab_qe/plugins/pdos/__init__.py +++ b/src/aiidalab_qe/plugins/pdos/__init__.py @@ -1,4 +1,6 @@ -from aiidalab_qe.common.panel import CodePanel, OutlinePanel +from aiidalab_widgets_base import ComputationalResourcesWidget + +from aiidalab_qe.common.panel import OutlinePanel from .result import Result from .setting import Setting @@ -10,21 +12,20 @@ class PdosOutline(OutlinePanel): help = """""" -class DosCodePanel(CodePanel): - title = "Code" - description = "dos.x" - default_calc_job_plugin = "quantumespresso.dos" - +dos_code = ComputationalResourcesWidget( + description="dos.x", + default_calc_job_plugin="quantumespresso.dos", +) -class ProjwfcCodePanel(CodePanel): - title = "Code" - description = "projwfc.x" - default_calc_job_plugin = "quantumespresso.projwfc" +projwfc_code = ComputationalResourcesWidget( + description="projwfc.x", + default_calc_job_plugin="quantumespresso.projwfc", +) pdos = { "outline": PdosOutline, - "code": {"dos": DosCodePanel, "projwfc": ProjwfcCodePanel} + "code": {"dos": dos_code, "projwfc": projwfc_code}, "setting": Setting, "result": Result, "workchain": workchain_and_builder, diff --git a/tests/conftest.py b/tests/conftest.py index 8c0cbcb8f..65e918bbe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -299,19 +299,17 @@ def app(pw_code, dos_code, projwfc_code): app = App(qe_auto_setup=False) # set up codes app.submit_step.pw_code.refresh() - app.submit_step.dos_code.refresh() - app.submit_step.projwfc_code.refresh() + app.submit_step.codes["dos"].refresh() + app.submit_step.codes["projwfc"].refresh() app.submit_step.pw_code.value = ( app.submit_step.pw_code.code_select_dropdown.options[pw_code.full_label] ) - app.submit_step.dos_code.value = ( - app.submit_step.dos_code.code_select_dropdown.options[dos_code.full_label] - ) - app.submit_step.projwfc_code.value = ( - app.submit_step.projwfc_code.code_select_dropdown.options[ - projwfc_code.full_label - ] - ) + app.submit_step.codes["dos"].value = app.submit_step.codes[ + "dos" + ].code_select_dropdown.options[dos_code.full_label] + app.submit_step.codes["projwfc"].value = app.submit_step.codes[ + "projwfc" + ].code_select_dropdown.options[projwfc_code.full_label] yield app diff --git a/tests/test_codes.py b/tests/test_codes.py new file mode 100644 index 000000000..5ac1e12a3 --- /dev/null +++ b/tests/test_codes.py @@ -0,0 +1,34 @@ +def test_code_not_selected(submit_app_generator): + """Test if there is an error when the code is not selected.""" + app = submit_app_generator() + app.submit_step.dos_code.value = None + app.submit_step._create_builder() + + +def test_reload_selected_code(submit_app_generator): + """Test set_selected_codes method.""" + from aiidalab_qe.app.submission import SubmitQeAppWorkChainStep + + app = submit_app_generator() + submit_step = app.submit_step + + submit_step._create_builder() + + new_submit_step = SubmitQeAppWorkChainStep(qe_auto_setup=False) + new_submit_step.set_selected_codes(submit_step.ui_parameters["codes"]) + + assert new_submit_step.get_selected_codes() == submit_step.get_selected_codes() + + +def test_set_code_status(): + """Test set_codes_status method. + If the workchain property is not selected, the related code should be disabled. + """ + from aiidalab_qe.app.submission import SubmitQeAppWorkChainStep + + submit = SubmitQeAppWorkChainStep(qe_auto_setup=False) + submit.set_codes_status() + assert submit.codes["dos"].code_select_dropdown.disabled is True + submit.input_parameters = {"workchain": {"properties": ["pdos"]}} + submit.set_codes_status() + assert submit.codes["dos"].code_select_dropdown.disabled is False diff --git a/tests/test_submit_qe_workchain.py b/tests/test_submit_qe_workchain.py index b56cc873a..40a8560ad 100644 --- a/tests/test_submit_qe_workchain.py +++ b/tests/test_submit_qe_workchain.py @@ -1,27 +1,3 @@ -def test_code_not_selected(submit_app_generator): - """Test if there is an error when the code is not selected.""" - app = submit_app_generator() - app.submit_step.dos_code.value = None - app.submit_step._create_builder() - - -def test_reload_selected_code(submit_app_generator): - """Test set_selected_codes method.""" - from aiidalab_qe.app.submission import SubmitQeAppWorkChainStep - - app = submit_app_generator() - submit_step = app.submit_step - - submit_step._create_builder() - - new_submit_step = SubmitQeAppWorkChainStep(qe_auto_setup=False) - new_submit_step.set_selected_codes(submit_step.ui_parameters["codes"]) - - assert new_submit_step.pw_code.value == submit_step.pw_code.value - assert new_submit_step.dos_code.value == submit_step.dos_code.value - assert new_submit_step.projwfc_code.value == submit_step.projwfc_code.value - - def test_create_builder_default( data_regression, submit_app_generator, From ffb1fa23f769c01ed53c1a6fe4f17e29dc420cad Mon Sep 17 00:00:00 2001 From: superstar54 Date: Thu, 19 Oct 2023 09:25:15 +0000 Subject: [PATCH 03/10] fix pdos --- src/aiidalab_qe/app/submission/__init__.py | 1 + src/aiidalab_qe/plugins/bands/workchain.py | 2 +- src/aiidalab_qe/plugins/pdos/workchain.py | 13 +++++++------ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index cf3639404..66421c442 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -439,6 +439,7 @@ def set_submission_parameters(self, parameters): self.set_selected_codes(parameters["codes"]) def get_submission_parameters(self): + """Get the parameters for the submission step.""" return { "codes": self.get_selected_codes(), "resources": self.get_resources(), diff --git a/src/aiidalab_qe/plugins/bands/workchain.py b/src/aiidalab_qe/plugins/bands/workchain.py index 78266a721..4574032af 100644 --- a/src/aiidalab_qe/plugins/bands/workchain.py +++ b/src/aiidalab_qe/plugins/bands/workchain.py @@ -174,7 +174,7 @@ def get_builder(codes, structure, parameters, **kwargs): """Get a builder for the PwBandsWorkChain.""" from copy import deepcopy - pw_code = codes.get("pw", {}) + pw_code = codes.get("pw") protocol = parameters["workchain"]["protocol"] scf_overrides = deepcopy(parameters["advanced"]) bands_overrides = deepcopy(parameters["advanced"]) diff --git a/src/aiidalab_qe/plugins/pdos/workchain.py b/src/aiidalab_qe/plugins/pdos/workchain.py index e1a962fc0..bd9915f2e 100644 --- a/src/aiidalab_qe/plugins/pdos/workchain.py +++ b/src/aiidalab_qe/plugins/pdos/workchain.py @@ -5,12 +5,13 @@ def check_codes(pw_code, dos_code, projwfc_code): + """Check that the codes are installed on the same computer.""" if ( not any( [ - pw_code.value is None, - dos_code.value is None, - projwfc_code.value is None, + pw_code is None, + dos_code is None, + projwfc_code is None, ] ) and len( @@ -33,9 +34,9 @@ def check_codes(pw_code, dos_code, projwfc_code): def get_builder(codes, structure, parameters, **kwargs): from copy import deepcopy - pw_code = codes.get("pw", None) - dos_code = codes.get("dos", None) - projwfc_code = codes.get("projwfc", None) + pw_code = codes.get("pw") + dos_code = codes.get("dos") + projwfc_code = codes.get("projwfc") check_codes(pw_code, dos_code, projwfc_code) protocol = parameters["workchain"]["protocol"] From 2b13b51a796fa04ebd997c3d669b89cf94c09870 Mon Sep 17 00:00:00 2001 From: superstar54 Date: Thu, 19 Oct 2023 09:33:01 +0000 Subject: [PATCH 04/10] update test --- tests/test_codes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_codes.py b/tests/test_codes.py index 5ac1e12a3..026a0d472 100644 --- a/tests/test_codes.py +++ b/tests/test_codes.py @@ -1,7 +1,7 @@ def test_code_not_selected(submit_app_generator): """Test if there is an error when the code is not selected.""" app = submit_app_generator() - app.submit_step.dos_code.value = None + app.submit_step.codes["dos"].value = None app.submit_step._create_builder() From 844ce641b89b3b2a943762a2fee11c10098c2557 Mon Sep 17 00:00:00 2001 From: superstar54 Date: Thu, 19 Oct 2023 10:37:51 +0000 Subject: [PATCH 05/10] udpate name --- tests/test_codes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_codes.py b/tests/test_codes.py index 026a0d472..14bf5f156 100644 --- a/tests/test_codes.py +++ b/tests/test_codes.py @@ -5,7 +5,7 @@ def test_code_not_selected(submit_app_generator): app.submit_step._create_builder() -def test_reload_selected_code(submit_app_generator): +def test_set_selected_codes(submit_app_generator): """Test set_selected_codes method.""" from aiidalab_qe.app.submission import SubmitQeAppWorkChainStep From 931f61b1a6a3043a60edd34e716e71990b4e6e20 Mon Sep 17 00:00:00 2001 From: superstar54 Date: Fri, 20 Oct 2023 11:23:47 +0000 Subject: [PATCH 06/10] add identify_submission_blockers, codes_visibility --- src/aiidalab_qe/app/submission/__init__.py | 31 +++++++++++++--------- tests/test_codes.py | 29 +++++++++++++++----- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index 66421c442..c90b057da 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -90,8 +90,9 @@ def __init__(self, qe_auto_setup=True, **kwargs): for name, code in entry_point.items(): self.codes[name] = code self.code_children.append(self.codes[name]) + # set default codes self.set_selected_codes(DEFAULT_PARAMETERS["codes"]) - # s + # self.submit_button = ipw.Button( description="Submit", tooltip="Submit the calculation with the selected parameters.", @@ -152,10 +153,15 @@ def _identify_submission_blockers(self): if self.qe_setup_status.busy or self.sssp_installation_status.busy: yield "Background setup processes must finish." - # No code selected (this is ignored while the setup process is running). + # No pw code selected (this is ignored while the setup process is running). if self.pw_code.value is None and not self.qe_setup_status.busy: yield ("No pw code selected") - + # code related to the selected property is not installed + properties = self.input_parameters.get("workchain", {}).get("properties", []) + for identifer in properties: + for name, code in self.code_entries.get(identifer, {}).items(): + if code.value is None: + yield f"Calculating the {identifer} property requires code {name} to be set." # SSSP library not installed if not self.sssp_installation_status.installed: yield "The SSSP library is not installed." @@ -304,7 +310,7 @@ def _observe_state(self, change): @tl.observe("previous_step_state") def _observe_input_structure(self, _): self._update_state() - self.set_codes_status() + self.udpate_codes_visibility() @tl.observe("process") def _observe_process(self, change): @@ -341,19 +347,18 @@ def _get_code_uuid(code): for name, code in self.codes.items(): code.value = _get_code_uuid(codes.get(name)) - def set_codes_status(self): - """Disable code if no related property is selected.""" - # disable all codes except pw + def udpate_codes_visibility(self): + """Hide code if no related property is selected.""" + # hide all codes except pw for name, code in self.codes.items(): if name == "pw": continue - code.code_select_dropdown.disabled = True - # enable code if the related property is selected. + code.layout.visibility = "hidden" properties = self.input_parameters.get("workchain", {}).get("properties", []) - for identifer, codes in self.code_entries.items(): - if identifer in properties: - for name in codes: - self.codes[name].code_select_dropdown.disabled = False + # show the code if the related property is selected. + for identifer in properties: + for code in self.code_entries.get(identifer, {}).values(): + code.layout.visibility = "visible" def submit(self, _=None): """Submit the work chain with the current inputs.""" diff --git a/tests/test_codes.py b/tests/test_codes.py index 14bf5f156..b4f388cd1 100644 --- a/tests/test_codes.py +++ b/tests/test_codes.py @@ -20,15 +20,30 @@ def test_set_selected_codes(submit_app_generator): assert new_submit_step.get_selected_codes() == submit_step.get_selected_codes() -def test_set_code_status(): - """Test set_codes_status method. - If the workchain property is not selected, the related code should be disabled. +def test_udpate_codes_visibility(): + """Test udpate_codes_visibility method. + If the workchain property is not selected, the related code should be hidden. """ from aiidalab_qe.app.submission import SubmitQeAppWorkChainStep submit = SubmitQeAppWorkChainStep(qe_auto_setup=False) - submit.set_codes_status() - assert submit.codes["dos"].code_select_dropdown.disabled is True + submit.udpate_codes_visibility() + assert submit.codes["dos"].layout.visibility == "hidden" submit.input_parameters = {"workchain": {"properties": ["pdos"]}} - submit.set_codes_status() - assert submit.codes["dos"].code_select_dropdown.disabled is False + submit.udpate_codes_visibility() + assert submit.codes["dos"].layout.visibility == "visible" + + +def test_identify_submission_blockers(app): + """Test identify_submission_blockers method.""" + submit = app.submit_step + blockers = list(submit._identify_submission_blockers()) + # there is one blocker: ['The SSSP library is not installed.'] + assert len(blockers) == 1 + submit.input_parameters = {"workchain": {"properties": ["pdos"]}} + blockers = list(submit._identify_submission_blockers()) + assert len(blockers) == 1 + # set dos code to None, will introduce another blocker + submit.codes["dos"].value = None + blockers = list(submit._identify_submission_blockers()) + assert len(blockers) == 2 From 880fa5e3354ff8eec5cdb859060129d11cf69c2c Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Wed, 25 Oct 2023 09:43:03 +0000 Subject: [PATCH 07/10] adding to the observation of the "input_parameters" in the _observe_input_structure. This is needed to allow a dynamic display of the codes in step 3, if something is changed in step 2. --- src/aiidalab_qe/app/submission/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index afa630137..96a0d4e19 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -308,7 +308,7 @@ def _observe_state(self, change): with self.hold_trait_notifications(): self.submit_button.disabled = change["new"] != self.State.CONFIGURED - @tl.observe("previous_step_state") + @tl.observe("previous_step_state","input_parameters") def _observe_input_structure(self, _): self._update_state() self.update_codes_display() From 0061f7d5f5b5cc4546c87e83fafc102c76ec7ba3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 09:48:50 +0000 Subject: [PATCH 08/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiidalab_qe/app/submission/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index 96a0d4e19..e3a8f9602 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -308,7 +308,7 @@ def _observe_state(self, change): with self.hold_trait_notifications(): self.submit_button.disabled = change["new"] != self.State.CONFIGURED - @tl.observe("previous_step_state","input_parameters") + @tl.observe("previous_step_state", "input_parameters") def _observe_input_structure(self, _): self._update_state() self.update_codes_display() From 5a1a7fa078b3f0e9ead717bc0be5abe9cc24eb21 Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Wed, 25 Oct 2023 09:55:30 +0000 Subject: [PATCH 09/10] deleting space in multiple trailtets observe params --- src/aiidalab_qe/app/submission/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index e3a8f9602..96a0d4e19 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -308,7 +308,7 @@ def _observe_state(self, change): with self.hold_trait_notifications(): self.submit_button.disabled = change["new"] != self.State.CONFIGURED - @tl.observe("previous_step_state", "input_parameters") + @tl.observe("previous_step_state","input_parameters") def _observe_input_structure(self, _): self._update_state() self.update_codes_display() From fb1de7c6d0be56afddd7f661df756fa7b4705e6f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 09:56:05 +0000 Subject: [PATCH 10/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiidalab_qe/app/submission/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index 96a0d4e19..e3a8f9602 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -308,7 +308,7 @@ def _observe_state(self, change): with self.hold_trait_notifications(): self.submit_button.disabled = change["new"] != self.State.CONFIGURED - @tl.observe("previous_step_state","input_parameters") + @tl.observe("previous_step_state", "input_parameters") def _observe_input_structure(self, _): self._update_state() self.update_codes_display()