Skip to content

Commit

Permalink
Merge pull request #59 from aiidalab/release_1.0.0b2
Browse files Browse the repository at this point in the history
Release 1.0.0b2
  • Loading branch information
yakutovicha authored Feb 24, 2020
2 parents 32fdfe1 + 9033814 commit 36f9eaf
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 135 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ before_install:
- pip install -U wheel setuptools coveralls

install:
- pip install -e .[testing,pre-commit]
- pip install .[testing,pre-commit]
- reentry scan -r aiida

script:
Expand Down
2 changes: 1 addition & 1 deletion aiidalab_widgets_base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
from .structures_multi import MultiStructureUploadWidget # noqa
from .viewers import viewer # noqa

__version__ = "1.0.0b1"
__version__ = "1.0.0b2"
106 changes: 70 additions & 36 deletions aiidalab_widgets_base/codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

import ipywidgets as ipw
from IPython.display import clear_output
from traitlets import Dict, Instance, Unicode, Union, link, validate

from aiida.orm import Code
from aiida.orm import Code, QueryBuilder, User

from aiidalab_widgets_base.utils import predefine_settings, valid_arguments

Expand All @@ -19,10 +20,22 @@ def valid_aiidacode_args(arguments):


class CodeDropdown(ipw.VBox):
"""Code selection widget."""
"""Code selection widget.
Attributes:
selected_code(Unicode or Code): Trait that points to the selected Code instance.
It can be set either to an AiiDA Code instance or to a code label (will
automatically be replaced by the corresponding Code instance).
It is linked to the 'value' trait of the `self.dropdown` widget.
codes(Dict): Trait that contains a dictionary (label => Code instance) for all
codes found in the AiiDA database for the selected plugin. It is linked
to the 'options' trait of the `self.dropdown` widget.
"""
selected_code = Union([Unicode(), Instance(Code)], allow_none=True)
codes = Dict(allow_none=True)

def __init__(self, input_plugin, text='Select code:', path_to_root='../', **kwargs):
""" Dropdown for Codes for one input plugin.
"""Dropdown for Codes for one input plugin.
:param input_plugin: Input plugin of codes to show
:type input_plugin: str
Expand All @@ -31,75 +44,96 @@ def __init__(self, input_plugin, text='Select code:', path_to_root='../', **kwar
"""

self.input_plugin = input_plugin
self.codes = {}
self.output = ipw.Output()

self.dropdown = ipw.Dropdown(optionsdescription=text, disabled=True, value=None)
link((self, 'codes'), (self.dropdown, 'options'))
link((self.dropdown, 'value'), (self, 'selected_code'))

self.dropdown = ipw.Dropdown(description=text, disabled=True)
self._btn_refresh = ipw.Button(description="Refresh", layout=ipw.Layout(width="70px"))
self._btn_refresh.on_click(self.refresh)

# FOR LATER: use base_url here, when it will be implemented in the appmode.
self._setup_another = ipw.HTML(value="""<a href={path_to_root}aiidalab-widgets-base/setup_code.ipynb?
label={label}&plugin={plugin} target="_blank">Setup new code</a>""".format(
path_to_root=path_to_root, label=input_plugin, plugin=input_plugin))
self.output = ipw.Output()

children = [ipw.HBox([self.dropdown, self._btn_refresh, self._setup_another]), self.output]

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

self.refresh()

@staticmethod
def _get_codes(input_plugin):
def _get_codes(self):
"""Query the list of available codes."""
from aiida.orm.querybuilder import QueryBuilder
from aiida.orm import User
from aiida.orm import Computer
current_user = User.objects.get_default()

querybuild = QueryBuilder()
querybuild.append(Computer, project=['*'], tag='computer')
querybuild.append(Code,
filters={
'attributes.input_plugin': {
'==': input_plugin
'==': self.input_plugin
},
'extras.hidden': {
"~==": True
}
},
project=['*'],
with_computer='computer')
results = querybuild.all()
project=['*'])

# only codes on computers configured for the current user
results = [r for r in results if r[0].is_user_configured(current_user)]
# Only codes on computers configured for the current user.
return {
self._full_code_label(c[0]): c[0]
for c in querybuild.all()
if c[0].computer.is_user_configured(User.objects.get_default())
}

codes = {"{}@{}".format(r[1].label, r[0].name): r[1] for r in results}
return codes
@staticmethod
def _full_code_label(code):
return "{}@{}".format(code.label, code.computer.name)

def refresh(self, change=None): # pylint: disable=unused-argument
"""Refresh available codes."""
with self.output:
clear_output()
self.codes = self._get_codes(self.input_plugin)
options = list(self.codes.keys())
def refresh(self, _=None):
"""Refresh available codes.
self.dropdown.options = options
The job of this function is to look in AiiDA database, find available codes and
put them in the dropdown attribute."""

if not options:
with self.output:
clear_output()
with self.hold_trait_notifications():
self.dropdown.options = self._get_codes()
if not self.dropdown.options:
print("No codes found for input plugin '{}'.".format(self.input_plugin))
self.dropdown.disabled = True
else:
self.dropdown.disabled = False
self.dropdown.value = None

@property
def selected_code(self):
"""Returns a selected code."""
try:
return self.codes[self.dropdown.value]
except KeyError:
@validate('selected_code')
def _validate_selected_code(self, change):
"""If code is provided, set it as it is. If code's name is provided,
select the code and set it."""
code = change['value']

# If code None, set value to None
if code is None:
return None

# Check code by name.
if isinstance(code, str):
if code in self.codes:
return self.codes[code]
raise KeyError("No code named '{}' found in the AiiDA database.".format(code))

# Check code by value.
if isinstance(code, Code):
label = self._full_code_label(code)
if label in self.codes:
return code
raise ValueError(
"The code instance '{}' supplied was not found in the AiiDA database. Consider reloading.".format(code))

# This place will never be reached, because the trait's type is checked.
return None


class AiiDACodeSetup(ipw.VBox):
"""Class that allows to setup AiiDA code"""
Expand Down
103 changes: 60 additions & 43 deletions aiidalab_widgets_base/computers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
import pexpect
import ipywidgets as ipw
from IPython.display import clear_output
from traitlets import Int
from traitlets import Dict, Instance, Int, Unicode, Union, validate, link

from aiida.orm import Computer
from aiida.orm import User
from aiida.common import NotExistent
from aiida.orm import Computer, QueryBuilder, User

from aiida.transports.plugins.ssh import parse_sshconfig

from aiidalab_widgets_base.utils import predefine_settings, valid_arguments
Expand Down Expand Up @@ -652,72 +652,89 @@ def append_text(self, append_text):


class ComputerDropdown(ipw.VBox):
"""Widget to select a configured computer."""
"""Widget to select a configured computer.
Attributes:
selected_computer(Unicode or Computer): Trait that points to the selected Computer instance.
It can be set either to an AiiDA Computer instance or to a computer label (will
automatically be replaced by the corresponding Computer instance). It is linked to the
'value' trait of `self._dropdown` widget.
computers(Dict): Trait that contains a dictionary (label => Computer instance) for all
computers found in the AiiDA database. It is linked to the 'options' trait of
`self._dropdown` widget.
"""

def __init__(self, text='Select computer:', **kwargs):
""" Dropdown for Codes for one input plugin.
selected_computer = Union([Unicode(), Instance(Computer)], allow_none=True)
computers = Dict(allow_none=True)

def __init__(self, text='Select computer:', path_to_root='../', **kwargs):
"""Dropdown for configured AiiDA Computers.
:param text: Text to display before dropdown
:type text: str
"""
:type text: str"""
self.output = ipw.Output()

self._dropdown = ipw.Dropdown(options=[],
self._dropdown = ipw.Dropdown(options={},
value=None,
description=text,
style={'description_width': 'initial'},
disabled=True)
self._btn_refresh = ipw.Button(description="Refresh", layout=ipw.Layout(width="70px"))
link((self, 'computers'), (self._dropdown, 'options'))
link((self._dropdown, 'value'), (self, 'selected_computer'))

self._btn_refresh = ipw.Button(description="Refresh", layout=ipw.Layout(width="70px"))
self._setup_another = ipw.HTML(
value="""<a href=./setup_computer.ipynb target="_blank">Setup new computer</a>""",
layout={'margin': '0px 0px 0px 250px'})
value="""<a href={path_to_root}aiidalab-widgets-base/setup_computer.ipynb target="_blank">
Setup new computer</a>""".format(path_to_root=path_to_root))
self._btn_refresh.on_click(self._refresh)
self.output = ipw.Output()

children = [ipw.HBox([self._dropdown, self._btn_refresh]), self._setup_another, self.output]

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

children = [ipw.HBox([self._dropdown, self._btn_refresh, self._setup_another]), self.output]
self._refresh()
super().__init__(children=children, **kwargs)

def _get_computers(self):
@staticmethod
def _get_computers():
"""Get the list of available computers."""
from aiida.orm.querybuilder import QueryBuilder
current_user = User.objects.get_default()

query_b = QueryBuilder()
query_b.append(Computer, project=['*'], tag='computer')

results = query_b.all()

# only computers configured for the current user
results = [r for r in results if r[0].is_user_configured(current_user)]
query_b.append(Computer, tag='computer')

self._dropdown.options = {r[0].name: r[0] for r in results}
# Only computers configured for the current user.
return {c[0].name: c[0] for c in query_b.all() if c[0].is_user_configured(User.objects.get_default())}

def _refresh(self, change=None): # pylint: disable=unused-argument
def _refresh(self, _=None):
"""Refresh the list of configured computers."""

with self.output:
clear_output()
self._get_computers()
with self.hold_trait_notifications():
self._dropdown.options = self._get_computers()
if not self.computers:
print("No computers found.")
self._dropdown.disabled = True
else:
self._dropdown.disabled = False

@property
def computers(self):
return self._dropdown.options
self._dropdown.value = None

@property
def selected_computer(self):
"""Return selected computer."""
try:
return self._dropdown.value
except KeyError:
@validate('selected_computer')
def _validate_selected_computer(self, change):
"""Select computer either by name or by class instance."""
computer = change['value']

if computer is None:
return None

@selected_computer.setter
def selected_computer(self, selected_computer):
if selected_computer in self.computers:
self._dropdown.label = selected_computer
if isinstance(computer, str):
if computer in self.computers:
return self.computers[computer]
raise KeyError("No computer named '{}' was found in AiiDA database.".format(computer))

if isinstance(computer, Computer):
if computer.name in self.computers:
return computer
raise ValueError("The computer instance '{}' supplied was not found in the AiiDA database. "
"Consider reloading".format(computer))

# This place will never be reached, because the trait's type is checked before validation.
return None
Loading

0 comments on commit 36f9eaf

Please sign in to comment.