Skip to content

Commit

Permalink
Release/1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
yakutovicha committed Oct 29, 2021
2 parents f25b855 + 56d88c4 commit 0ac4a94
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 59 deletions.
2 changes: 1 addition & 1 deletion aiidalab_widgets_base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@
"viewer",
]

__version__ = "1.0.0"
__version__ = "1.1.0"
54 changes: 39 additions & 15 deletions aiidalab_widgets_base/codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from subprocess import check_output

import ipywidgets as ipw
from aiida.orm import Code, Computer, QueryBuilder, User
from aiida.orm import Code, QueryBuilder, User
from aiida.plugins.entry_point import get_entry_point_names
from IPython.display import clear_output
from traitlets import Bool, Dict, Instance, Unicode, Union, dlink, link, validate
Expand Down Expand Up @@ -157,9 +157,8 @@ class AiiDACodeSetup(ipw.VBox):
"""Class that allows to setup AiiDA code"""

label = Unicode()
computer = Union([Unicode(), Instance(Computer)], allow_none=True)

input_plugin = Unicode()
computer = Unicode(allow_none=True)
input_plugin = Unicode(allow_none=True)
description = Unicode()
remote_abs_path = Unicode()
prepend_text = Unicode()
Expand All @@ -178,20 +177,19 @@ def __init__(self, path_to_root="../", **kwargs):
link((inp_label, "value"), (self, "label"))

# Computer on which the code is installed. Two dlinks are needed to make sure we get a Computer instance.
inp_computer = ComputerDropdown(
self.inp_computer = ComputerDropdown(
path_to_root=path_to_root, layout={"margin": "0px 0px 0px 125px"}
)
dlink((inp_computer, "selected_computer"), (self, "computer"))
dlink((self, "computer"), (inp_computer, "selected_computer"))
dlink((self, "computer"), (self.inp_computer, "selected_computer"))

# Code plugin.
inp_code_plugin = ipw.Dropdown(
self.inp_code_plugin = ipw.Dropdown(
options=sorted(get_entry_point_names("aiida.calculations")),
description="Code plugin:",
layout=ipw.Layout(width="500px"),
style=style,
)
link((inp_code_plugin, "value"), (self, "input_plugin"))
link((self.inp_code_plugin, "value"), (self, "input_plugin"))

# Code description.
inp_description = ipw.Text(
Expand Down Expand Up @@ -233,8 +231,8 @@ def __init__(self, path_to_root="../", **kwargs):
ipw.VBox(
[
inp_label,
inp_computer,
inp_code_plugin,
self.inp_computer,
self.inp_code_plugin,
inp_description,
inp_abs_path,
]
Expand All @@ -247,6 +245,11 @@ def __init__(self, path_to_root="../", **kwargs):
]
super(AiiDACodeSetup, self).__init__(children, **kwargs)

@validate("input_plugin")
def _validate_input_plugin(self, proposal):
plugin = proposal["value"]
return plugin if plugin in self.inp_code_plugin.options else None

def _setup_code(self, _=None):
"""Setup an AiiDA code."""
with self._setup_code_out:
Expand All @@ -257,26 +260,47 @@ def _setup_code(self, _=None):
if not self.remote_abs_path:
print("You did not specify absolute path to the executable.")
return
if not self.inp_computer.selected_computer:
print(
"Please specify a computer that is configured in your AiiDA profile."
)
return False
if not self.input_plugin:
print(
"Please specify an input plugin that is installed in your AiiDA environment."
)
return False
if self.exists():
print(f"Code {self.label}@{self.computer.label} already exists.")
print(
f"Code {self.label}@{self.inp_computer.selected_computer.label} already exists."
)
return
code = Code(remote_computer_exec=(self.computer, self.remote_abs_path))
code = Code(
remote_computer_exec=(
self.inp_computer.selected_computer,
self.remote_abs_path,
)
)
code.label = self.label
code.description = self.description
code.set_input_plugin_name(self.input_plugin)
code.set_prepend_text(self.prepend_text)
code.set_append_text(self.append_text)
code.store()
code.reveal()
full_string = f"{self.label}@{self.computer.label}"
full_string = f"{self.label}@{self.inp_computer.selected_computer.label}"
print(check_output(["verdi", "code", "show", full_string]).decode("utf-8"))

def exists(self):
"""Returns True if the code exists, returns False otherwise."""
from aiida.common import MultipleObjectsError, NotExistent

if not self.label:
return False
try:
Code.get_from_string(f"{self.label}@{self.computer.label}")
Code.get_from_string(
f"{self.label}@{self.inp_computer.selected_computer.label}"
)
return True
except MultipleObjectsError:
return True
Expand Down
9 changes: 5 additions & 4 deletions aiidalab_widgets_base/computers.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,12 +908,13 @@ def _validate_selected_computer(self, change):
if isinstance(computer, str):
if computer in self.computers:
return self.computers[computer]
self.output.value = f"""No computer named '<span style="color:red">{computer}</span>'
was found in your AiiDA database."""
self.output.value = f"""Computer instance '<span style="color:red">{computer}</span>'
is not configured in your AiiDA profile."""
return None

if isinstance(computer, Computer):
if computer.label in self.computers:
return computer
self.output.value = f"""The computer instance '<span style="color:red">{computer}</span>'
supplied was not found in your AiiDA database."""
self.output.value = f"""Computer instance '<span style="color:red">{computer.label}</span>'
is not configured in your AiiDA profile."""
return None
3 changes: 2 additions & 1 deletion aiidalab_widgets_base/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def __init__(
title: str = None,
**kwargs,
) -> None:
providers_header = ipw.HTML("<h4>Select a provider</h4>")
providers = OptimadeQueryProviderWidget(
embedded=embedded,
width_ratio=kwargs.pop("width_ratio", None),
Expand All @@ -207,7 +208,7 @@ def __init__(
layout = kwargs.pop("layout", {"width": "auto", "height": "auto"})

super().__init__(
children=(providers, filters),
children=(providers_header, providers, filters),
layout=layout,
**kwargs,
)
Expand Down
55 changes: 40 additions & 15 deletions aiidalab_widgets_base/elns.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,48 @@


def connect_to_eln(eln_instance=None, **kwargs):

# assuming that the connection can only be established to the ELNs
# with the stored configuration.
try:
with open(ELN_CONFIG, "r") as file:
config = json.load(file)
except (FileNotFoundError, json.JSONDecodeError, KeyError):
return None
return (
None,
f"Can't open '{ELN_CONFIG}' (ELN configuration file). Instance: {eln_instance}",
)

# If no ELN instance was specified, trying the default one.
if not eln_instance:
eln_instance = config.pop("default", None)

if eln_instance:
if eln_instance: # The ELN instance could be identified.
if eln_instance in config:
eln_config = config[eln_instance]
eln_type = eln_config.pop("eln_type", None)
else:
eln_type = None
else: # The selected instance is not present in the config.
return None, f"Didn't find configuration for the '{eln_instance}' instance."

# If the ELN type cannot be identified - aborting.
if not eln_type:
return None
eln = get_eln_connector(eln_type)(
eln_instance=eln_instance, **eln_config, **kwargs
)
return None, f"Can't identify the type of {eln_instance} ELN."

# Everything is alright, can populate the ELN connector
# with the required info.
try:
eln = get_eln_connector(eln_type)(
eln_instance=eln_instance, **eln_config, **kwargs
)
except NotImplementedError as err:
return None, str(err)
eln.connect()
return eln
return eln, None

return None
return (
None,
"No ELN instance was provided, the default ELN instance is not configured either. Set a default ELN or select an ELN instance.",
)


class ElnImportWidget(ipw.VBox):
Expand All @@ -52,11 +70,11 @@ def __init__(self, path_to_root="../", **kwargs):
error_message = ipw.HTML()
super().__init__(children=[error_message], **kwargs)

eln = connect_to_eln(**kwargs)
eln, msg = connect_to_eln(**kwargs)

if eln is None:
url = f"{path_to_root}aiidalab-widgets-base/notebooks/eln_configure.ipynb"
error_message.value = f"""Warning! The access to ELN {kwargs['eln_instance']} is not configured. Please follow <a href="{url}" target="_blank">the link</a> to configure it."""
error_message.value = f"""Warning! The access to ELN is not configured. Please follow <a href="{url}" target="_blank">the link</a> to configure it.</br> More details: {msg}"""
return

traitlets.dlink((eln, "node"), (self, "node"))
Expand Down Expand Up @@ -93,13 +111,13 @@ def __init__(self, path_to_root="../", **kwargs):
self._output,
self.message,
]
self.eln = connect_to_eln()
self.eln, msg = connect_to_eln()
if self.eln:
traitlets.dlink((self, "node"), (self.eln, "node"))
else:
self.modify_settings.disabled = True
send_button.disabled = True
self.message.value = f"""Warning! The access to an ELN is not configured. Please follow <a href="{self.path_to_root}/aiidalab-widgets-base/notebooks/eln_configure.ipynb" target="_blank">the link</a> to configure it."""
self.message.value = f"""Warning! The access to ELN is not configured. Please follow <a href="{self.path_to_root}/aiidalab-widgets-base/notebooks/eln_configure.ipynb" target="_blank">the link</a> to configure it.</br> </br> More details: {msg}"""

super().__init__(children=children, **kwargs)

Expand Down Expand Up @@ -261,7 +279,14 @@ def check_connection(self, _=None):

def display_eln_config(self, value=None):
"""Display ELN configuration specific to the selected type of ELN."""
eln_class = get_eln_connector(self.eln_types.value)
try:
eln_class = get_eln_connector(self.eln_types.value)
except NotImplementedError as err:
with self._output:
clear_output()
display(ipw.HTML("&#10060;" + str(err)))
return

self.eln = eln_class(
eln_instance=self.eln_instance.label if self.eln_instance.value else "",
**self.eln_instance.value,
Expand Down
12 changes: 7 additions & 5 deletions aiidalab_widgets_base/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import datetime
import io
import tempfile
from collections import OrderedDict

# ASE imports
Expand Down Expand Up @@ -61,7 +62,7 @@ def __init__(
editors=None,
storable=True,
node_class=None,
**kwargs
**kwargs,
):
"""
Arguments:
Expand Down Expand Up @@ -389,11 +390,12 @@ def _on_file_upload(self, change=None):
if frmt == "cif":
self.structure = CifData(file=io.BytesIO(item["content"]))
else:
self.structure = self._validate_and_fix_ase_cell(
get_ase_from_file(
io.StringIO(item["content"].decode()), format=frmt
with tempfile.NamedTemporaryFile(suffix=f".{frmt}") as temp_file:
temp_file.write(item["content"])
temp_file.flush()
self.structure = self._validate_and_fix_ase_cell(
get_ase_from_file(temp_file.name)
)
)
self.file_upload.value.clear()
break

Expand Down

0 comments on commit 0ac4a94

Please sign in to comment.