Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix resource setup widget bugs #661

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions aiidalab_widgets_base/computational_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def __init__(
children.append(self._setup_new_code_output)

# Computer/code setup
self.resource_setup = _ResourceSetupBaseWidget(
self.resource_setup = ResourceSetupBaseWidget(
default_calc_job_plugin=self.default_calc_job_plugin,
enable_quick_setup=enable_quick_setup,
enable_detailed_setup=enable_detailed_setup,
Expand Down Expand Up @@ -1193,6 +1193,13 @@ def on_setup_code(self, _=None):
with self.setup_code_out:
clear_output()

if not self.label.value:
self.message = wrap_message(
"Please provide a code label.",
MessageLevel.WARNING,
)
return False

if not self.computer.value:
self.message = wrap_message(
"Please select an existing computer.",
Expand Down Expand Up @@ -1229,7 +1236,7 @@ def on_setup_code(self, _=None):
qb = orm.QueryBuilder()
qb.append(orm.Computer, filters={"uuid": computer.uuid}, tag="computer")
qb.append(
orm.AbstractCode,
orm.Code,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this change needed? orm.Code is deprecated so would be nice to avoid if possible.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, Code worked where AbstractCode did not. But I think maybe InstalledCode should also work. Question is, do we support the other Code flavors?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, AbstractCode does not work, allowing codes of existing full labels to go through!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why doesn't it work though? Is this a limitation of QueryBuilder? Or a bug in aiida-core?

In your testing, are the preexisting codes of Code type or InstalledCode type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect they are all InstalledCode instances, but not sure. Will doubt check in the morning. It could be a QB issue. In any case, if Code works and covers all use cases and no resolution is found by tomorrow, I'll proceed with it for now, as this PR is holding up the release of the QE app. We can always revisit when we know more. Unless you think the use of Code is present breaking?

Copy link
Member Author

@edan-bainglass edan-bainglass Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, seems like a QB limitation. When using AbstractCode, the node_type field of the QB dictionary is {'like': 'data.code.abstract.%'}, whereas for Code, it is {'like': 'data.core.code.%'}.

All my instances are of InstalledCode, which have a node_type of data.core.code.installed.InstalledCode. I don't believe 'data.code.abstract' is actually a node_type of any of the Code flavors. All code entry points are data.core.code....

For now (due to time constraints), I will proceed with Code. Will open an issue+PR in aiida-core to address this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with_computer="computer",
filters={"label": kwargs["label"]},
)
Expand Down Expand Up @@ -1612,7 +1619,7 @@ def _on_template_variable_filled(self, _):
self.fill()


class _ResourceSetupBaseWidget(ipw.VBox):
class ResourceSetupBaseWidget(ipw.VBox):
"""The widget that allows to setup a computer and code.
This is the building block of the `ComputationalResourcesDatabaseWidget` which
will be directly used by the user.
Expand Down Expand Up @@ -1659,6 +1666,11 @@ def __init__(
default_calc_job_plugin=default_calc_job_plugin,
show_reset_button=False,
)
ipw.dlink(
(self.comp_resources_database, "configured"),
(self.quick_setup_button, "disabled"),
lambda configured: not configured,
)

# All templates
self.template_computer_setup = TemplateVariablesWidget()
Expand Down Expand Up @@ -1877,14 +1889,22 @@ def _on_quick_setup(self, _=None):
)
return

# Raise error if the code is not selected.
if not self.comp_resources_database.code_selector.value:
self.message = wrap_message(
"Please select a code from the database.",
MessageLevel.ERROR,
)
return

# Check if all the template variables are filled.
# If not raise a warning and return (skip the setup).
if (
unfilled_variables := self.template_computer_setup.unfilled_variables
+ self.template_computer_configure.unfilled_variables
+ self.template_code.unfilled_variables
):
var_warn_message = ", ".join([f"<b>{v}</b>" for v in unfilled_variables])
var_warn_message = ", ".join({f"<b>{v}</b>" for v in unfilled_variables})
self.message = wrap_message(
f"Please fill the template variables: {var_warn_message}",
MessageLevel.WARNING,
Expand Down
10 changes: 10 additions & 0 deletions aiidalab_widgets_base/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ class ComputationalResourcesDatabaseWidget(ipw.VBox):
computer_configure = tl.Dict()
code_setup = tl.Dict()

configured = tl.Bool(False)

STYLE = {"description_width": "180px"}
LAYOUT = {"width": "400px"}

Expand Down Expand Up @@ -413,6 +415,8 @@ def _computer_changed(self, change=None):
self.computer_setup = computer_setup
self.computer_configure = computer_configure

self._set_configured()

def _code_changed(self, change=None):
"""Update code settings."""
if change["new"] is None:
Expand All @@ -430,3 +434,9 @@ def _code_changed(self, change=None):
.get("codes", {})
.get(selected_code, {})
)

self._set_configured()

def _set_configured(self):
"""Update state of the widget."""
self.configured = all((self.computer_setup, self.code_setup))
2 changes: 1 addition & 1 deletion aiidalab_widgets_base/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def wrap_message(message, level=MessageLevel.INFO):
# The message is wrapped into a div with the class "alert" and the icon of the given level
return f"""
<div class="alert alert-{level.value}" role="alert" style="margin-bottom: 0px; padding: 6px 12px;">
<i class="fa fa-{mapping[level]}"></i>{message}
<i class="fa fa-{mapping[level]}"></i> {message}
</div>
"""

Expand Down
12 changes: 6 additions & 6 deletions tests/test_computational_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from aiidalab_widgets_base import computational_resources
from aiidalab_widgets_base.computational_resources import (
ComputationalResourcesWidget,
_ResourceSetupBaseWidget,
ResourceSetupBaseWidget,
)

HTML_TAG_CLEANER = re.compile(r"<[^>]*>")
Expand Down Expand Up @@ -496,11 +496,11 @@ def test_template_variables_widget_help_text_disappear_if_no_template_str():
def test_resource_setup_widget_default():
"""Test the _ResourceSetupBaseWidget."""
with pytest.raises(ValueError):
w = _ResourceSetupBaseWidget(
w = ResourceSetupBaseWidget(
enable_detailed_setup=False, enable_quick_setup=False
)

w = _ResourceSetupBaseWidget()
w = ResourceSetupBaseWidget()

# Test message is update correctly. By click setup button without filling in any information.
w._on_quick_setup()
Expand Down Expand Up @@ -587,7 +587,7 @@ def test_resource_setup_widget_for_password_configure(monkeypatch, tmp_path):
# monkeypatch home so the ssh key is generated in the temporary directory
monkeypatch.setenv("HOME", str(tmp_path))

w = _ResourceSetupBaseWidget()
w = ResourceSetupBaseWidget()

# Test select a new resource setup will update the output interface (e.g. ssh_config, computer_setup, code_setup)
# and the computer/code setup widget will be updated accordingly.
Expand Down Expand Up @@ -654,7 +654,7 @@ def test_resource_setup_widget_for_password_configure(monkeypatch, tmp_path):
@pytest.mark.usefixtures("aiida_profile_clean")
def test_resource_setup_widget_computer_change_code_reset():
"""Test the _ResourceSetupBaseWidget that when computer template changed, the code selector widget is reset."""
w = _ResourceSetupBaseWidget()
w = ResourceSetupBaseWidget()

# Test select a new resource setup will update the output interface (e.g. ssh_config, computer_setup, code_setup)
# and the computer/code setup widget will be updated accordingly.
Expand All @@ -673,7 +673,7 @@ def test_resource_setup_widget_computer_change_code_reset():

def test_resource_setup_widget_detailed_setup():
"""Detail branch test of the resource setup widget"""
w = _ResourceSetupBaseWidget()
w = ResourceSetupBaseWidget()

w.comp_resources_database.domain_selector.value = "daint.cscs.ch"
w.comp_resources_database.computer_selector.value = "mc"
Expand Down
Loading