diff --git a/aiida_datatypes.ipynb b/aiida_datatypes.ipynb
new file mode 100644
index 000000000..2f5a0dcd5
--- /dev/null
+++ b/aiida_datatypes.ipynb
@@ -0,0 +1,206 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from os import path\n",
+ "from aiida import load_dbenv, is_dbenv_loaded\n",
+ "from aiida.backends import settings\n",
+ "if not is_dbenv_loaded():\n",
+ " load_dbenv(profile=settings.AIIDADB_PROFILE)\n",
+ "from aiida.orm import DataFactory"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from aiidalab_widgets_base import aiidalab_display"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## ParameterData"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# visualize ParameterData\n",
+ "ParameterData = DataFactory('parameter')\n",
+ "p = ParameterData(dict={\n",
+ " 'Parameter' :'super long string '*4,\n",
+ " 'parameter 2' :'value 2',\n",
+ " 'parameter 3' : 1,\n",
+ " 'parameter 4' : 2,\n",
+ "})\n",
+ "aiidalab_display(p.store(), downloadable=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# create molecule\n",
+ "from ase.build import molecule\n",
+ "m = molecule('H2O')\n",
+ "m.center(vacuum=2.0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## CifData"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# visualize CifData\n",
+ "CifData = DataFactory('cif')\n",
+ "s = CifData(ase=m)\n",
+ "aiidalab_display(s.store(), downloadable=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## StructureData"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# visualize StructureData\n",
+ "StructureData = DataFactory('structure')\n",
+ "s = StructureData(ase=m)\n",
+ "aiidalab_display(s.store(), downloadable=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## BandsData"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "BandsData = DataFactory('array.bands')\n",
+ "bs = BandsData()\n",
+ "kpoints = np.array([[0. , 0. , 0. ], # array shape is 12 * 3\n",
+ " [0.1 , 0. , 0.1 ],\n",
+ " [0.2 , 0. , 0.2 ],\n",
+ " [0.3 , 0. , 0.3 ],\n",
+ " [0.4 , 0. , 0.4 ],\n",
+ " [0.5 , 0. , 0.5 ],\n",
+ " [0.5 , 0. , 0.5 ],\n",
+ " [0.525 , 0.05 , 0.525 ],\n",
+ " [0.55 , 0.1 , 0.55 ],\n",
+ " [0.575 , 0.15 , 0.575 ],\n",
+ " [0.6 , 0.2 , 0.6 ],\n",
+ " [0.625 , 0.25 , 0.625 ]])\n",
+ "\n",
+ "bands = np.array([\n",
+ " [-5.64024889, 6.66929678, 6.66929678, 6.66929678, 8.91047649], # array shape is 12 * 5, where 12 is the size of the kpoints mesh\n",
+ " [-5.46976726, 5.76113772, 5.97844699, 5.97844699, 8.48186734], # and 5 is the number of states\n",
+ " [-4.93870761, 4.06179965, 4.97235487, 4.97235488, 7.68276008],\n",
+ " [-4.05318686, 2.21579935, 4.18048674, 4.18048675, 7.04145185],\n",
+ " [-2.83974972, 0.37738276, 3.69024464, 3.69024465, 6.75053465],\n",
+ " [-1.34041116, -1.34041115, 3.52500177, 3.52500178, 6.92381041],\n",
+ " [-1.34041116, -1.34041115, 3.52500177, 3.52500178, 6.92381041],\n",
+ " [-1.34599146, -1.31663872, 3.34867603, 3.54390139, 6.93928289],\n",
+ " [-1.36769345, -1.24523403, 2.94149041, 3.6004033 , 6.98809593],\n",
+ " [-1.42050683, -1.12604118, 2.48497007, 3.69389815, 7.07537154],\n",
+ " [-1.52788845, -0.95900776, 2.09104321, 3.82330632, 7.20537566],\n",
+ " [-1.71354964, -0.74425095, 1.82242466, 3.98697455, 7.37979746]])\n",
+ "bs.set_kpoints(kpoints)\n",
+ "bs.set_bands(bands)\n",
+ "labels = [(0, u'GAMMA'),\n",
+ " (5, u'X'),\n",
+ " (6, u'Z'),\n",
+ " (11, u'U')]\n",
+ "bs.labels = labels"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "aiidalab_display(bs.store()) # to visualize the bands"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## FolderData"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "FolderData = DataFactory('folder')\n",
+ "fd = FolderData()\n",
+ "with fd.folder.open(path.join('path','test1.txt'), 'w') as fobj:\n",
+ " fobj.write('content of test1 file')\n",
+ "with fd.folder.open(path.join('path','test2.txt'), 'w') as fobj:\n",
+ " fobj.write('content of test2\\nfile')\n",
+ "with fd.folder.open(path.join('path','test_long.txt'), 'w') as fobj:\n",
+ " fobj.write('content of test_long file'*1000)\n",
+ "aiidalab_display(fd.store(), downloadable=True)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 2",
+ "language": "python",
+ "name": "python2"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.15rc1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/aiidalab_widgets_base/__init__.py b/aiidalab_widgets_base/__init__.py
index e1dd90bcb..b8d09f6c3 100644
--- a/aiidalab_widgets_base/__init__.py
+++ b/aiidalab_widgets_base/__init__.py
@@ -1,6 +1,14 @@
# pylint: disable=unused-import
+from aiida import load_dbenv, is_dbenv_loaded
+from aiida.backends import settings
+if not is_dbenv_loaded():
+ load_dbenv(profile=settings.AIIDADB_PROFILE)
+
from .structures import StructureUploadWidget # noqa
from .structures_multi import MultiStructureUploadWidget # noqa
-from .codes import CodeDropdown # noqa
+from .codes import CodeDropdown, AiiDACodeSetup, extract_aiidacodesetup_arguments # noqa
+from .computers import SshComputerSetup, extract_sshcomputersetup_arguments # noqa
+from .computers import AiidaComputerSetup, extract_aiidacomputer_arguments # noqa
+from .display import aiidalab_display # noqa
-__version__ = "0.2.0a1"
+__version__ = "0.3.0b1"
diff --git a/aiidalab_widgets_base/aiida_visualizers.py b/aiidalab_widgets_base/aiida_visualizers.py
new file mode 100644
index 000000000..8283e52c7
--- /dev/null
+++ b/aiidalab_widgets_base/aiida_visualizers.py
@@ -0,0 +1,145 @@
+from __future__ import print_function
+import os
+
+import ipywidgets as ipw
+
+class ParameterDataVisualizer(ipw.HTML):
+ """Visualizer class for ParameterData object"""
+ def __init__(self, parameter, downloadable=True, **kwargs):
+ super(ParameterDataVisualizer, self).__init__(**kwargs)
+ import pandas as pd
+ # Here we are defining properties of 'df' class (specified while exporting pandas table into html).
+ # Since the exported object is nothing more than HTML table, all 'standard' HTML table settings
+ # can be applied to it as well.
+ # For more information on how to controle the table appearance please visit:
+ # https://css-tricks.com/complete-guide-table-element/
+ self.value = '''
+
+ '''
+ pd.set_option('max_colwidth', 40)
+ df = pd.DataFrame([(key, value) for key, value
+ in sorted(parameter.get_dict().items())
+ ], columns=['Key', 'Value'])
+ self.value += df.to_html(classes='df', index=False) # specify that exported table belongs to 'df' class
+ # this is used to setup table's appearance using CSS
+ if downloadable:
+ import base64
+ payload = base64.b64encode(df.to_csv(index=False).encode()).decode()
+ fname = '{}.csv'.format(parameter.pk)
+ to_add = """Download table in csv format: {title}"""
+ self.value += to_add.format(filename=fname, payload=payload,title=fname)
+
+class StructureDataVisualizer(ipw.VBox):
+ """Visualizer class for StructureData object"""
+ def __init__(self, structure, downloadable=True, **kwargs):
+ import nglview
+ self._structure = structure
+ viewer = nglview.NGLWidget()
+ viewer.add_component(nglview.ASEStructure(self._structure.get_ase())) # adds ball+stick
+ viewer.add_unitcell()
+ children = [viewer]
+ if downloadable:
+ self.file_format = ipw.Dropdown(
+ options=['xyz', 'cif'],
+ description="File format:",
+ )
+ self.download_btn = ipw.Button(description="Download")
+ self.download_btn.on_click(self.download)
+ children.append(ipw.HBox([self.file_format, self.download_btn]))
+ super(StructureDataVisualizer, self).__init__(children, **kwargs)
+
+ def download(self, b=None):
+ import base64
+ from tempfile import TemporaryFile
+ from IPython.display import Javascript
+ with TemporaryFile() as fobj:
+ self._structure.get_ase().write(fobj, format=self.file_format.value)
+ fobj.seek(0)
+ b64 = base64.b64encode(fobj.read())
+ payload = b64.decode()
+ js = Javascript(
+ """
+ var link = document.createElement('a');
+ link.href = "data:;base64,{payload}"
+ link.download = "{filename}"
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ """.format(payload=payload,filename=str(self._structure.id)+'.'+self.file_format.value)
+ )
+ display(js)
+
+class FolderDataVisualizer(ipw.VBox):
+ """Visualizer class for FolderData object"""
+ def __init__(self, folder, downloadable=True, **kwargs):
+ self._folder = folder
+ self.files = ipw.Dropdown(
+ options=self._folder.get_folder_list(),
+ description="Select file:",
+ )
+ self.text = ipw.Textarea(
+ value="",
+ description='File content:',
+ layout={'width':"900px", 'height':'300px'},
+ disabled=False
+ )
+ self.change_file_view()
+ self.files.observe(self.change_file_view, names='value')
+ children = [self.files, self.text]
+ if downloadable:
+ self.download_btn = ipw.Button(description="Download")
+ self.download_btn.on_click(self.download)
+ children.append(self.download_btn)
+ super(FolderDataVisualizer, self).__init__(children, **kwargs)
+
+ def change_file_view(self, b=None):
+ with open(self._folder.get_abs_path(self.files.value), "rb") as fobj:
+ self.text.value = fobj.read()
+
+ def download(self, b=None):
+ import base64
+ from IPython.display import Javascript
+ with open(self._folder.get_abs_path(self.files.value), "rb") as fobj:
+ b64 = base64.b64encode(fobj.read())
+ payload = b64.decode()
+ js = Javascript(
+ """
+ var link = document.createElement('a');
+ link.href = "data:;base64,{payload}"
+ link.download = "{filename}"
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ """.format(payload=payload,filename=self.files.value)
+ )
+ display(js)
+
+class BandsDataVisualizer(ipw.VBox):
+ """Visualizer class for BandsData object"""
+ def __init__(self, bands, **kwargs):
+ from bokeh.plotting import figure
+ from bokeh.io import show, output_notebook
+ output_notebook(hide_banner=True)
+ out = ipw.Output()
+ with out:
+ plot_info = bands._get_bandplot_data(cartesian=True, join_symbol="|")
+ y = plot_info['y'].transpose().tolist()
+ x = [plot_info['x'] for i in range(len(y))]
+ labels = plot_info['labels']
+ p = figure(y_axis_label='Dispersion ({})'.format(bands.units))
+ p.multi_line(x, y, line_width=2)
+ p.xaxis.ticker = [l[0] for l in labels]
+ p.xaxis.major_label_overrides = {int(l[0]) if l[0].is_integer() else l[0]:l[1] for l in labels}
+ # int(l[0]) if l[0].is_integer() else l[0]
+ # This trick was suggested here: https://github.com/bokeh/bokeh/issues/8166#issuecomment-426124290
+ show(p)
+ children = [out]
+ super(BandsDataVisualizer, self).__init__(children, **kwargs)
\ No newline at end of file
diff --git a/aiidalab_widgets_base/codes.py b/aiidalab_widgets_base/codes.py
index 115f95d15..46d7eef1f 100644
--- a/aiidalab_widgets_base/codes.py
+++ b/aiidalab_widgets_base/codes.py
@@ -2,8 +2,28 @@
from __future__ import absolute_import
import ipywidgets as ipw
+
+from subprocess import check_output
from IPython.display import clear_output
+from aiida.orm import Code
+
+VALID_AIIDA_CODE_SETUP_ARGUMETNS = {'label', 'selected_computer', 'plugin', 'description',
+ 'exec_path', 'prepend_text', 'append_text'}
+
+def valid_arguments(arguments, valid_arguments):
+ result = {}
+ for key, value in arguments.items():
+ if key in valid_arguments:
+ if type(value) is tuple or type(value) is list:
+ result[key] = '\n'.join(value)
+ else:
+ result[key] = value
+ return result
+
+def extract_aiidacodesetup_arguments(arguments):
+ return valid_arguments(arguments, VALID_AIIDA_CODE_SETUP_ARGUMETNS)
+
class CodeDropdown(ipw.VBox):
def __init__(self, input_plugin, text='Select code:', **kwargs):
@@ -18,25 +38,23 @@ def __init__(self, input_plugin, text='Select code:', **kwargs):
self.input_plugin = input_plugin
self.codes = {}
- self.label = ipw.Label(value=text)
- self.dropdown = ipw.Dropdown(options=[], disabled=True)
+ 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)
+ self._setup_another = ipw.HTML(value="""Setup new code""")
self.output = ipw.Output()
- children = [ipw.HBox([self.label, self.dropdown, self.output])]
+ children = [ipw.HBox([self.dropdown, self._btn_refresh, self._setup_another]),
+ self.output]
super(CodeDropdown, self).__init__(children=children, **kwargs)
- from aiida import load_dbenv, is_dbenv_loaded
- from aiida.backends import settings
- if not is_dbenv_loaded():
- load_dbenv(profile=settings.AIIDADB_PROFILE)
self.refresh()
def _get_codes(self, input_plugin):
from aiida.orm.querybuilder import QueryBuilder
- from aiida.orm import Code, Computer
from aiida.backends.utils import get_automatic_user
-
+ from aiida.orm import Computer
current_user = get_automatic_user()
qb = QueryBuilder()
@@ -62,7 +80,7 @@ def _get_codes(self, input_plugin):
codes = {"{}@{}".format(r[1].label, r[0].name): r[1] for r in results}
return codes
- def refresh(self):
+ def refresh(self, b=None):
with self.output:
clear_output()
self.codes = self._get_codes(self.input_plugin)
@@ -83,3 +101,158 @@ def selected_code(self):
return self.codes[self.dropdown.value]
except KeyError:
return None
+
+class AiiDACodeSetup(ipw.VBox):
+ """Class that allows to setup AiiDA code"""
+ def __init__(self, **kwargs):
+ from aiida.common.pluginloader import all_plugins
+ from aiidalab_widgets_base.computers import ComputerDropdown
+
+ style = {"description_width":"200px"}
+
+ # list of widgets to be displayed
+
+ self._inp_code_label = ipw.Text(description="AiiDA code label:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+
+ self._computer = ComputerDropdown(layout={'margin': '0px 0px 0px 125px'})
+
+ self._inp_code_description = ipw.Text(placeholder='No description (yet)',
+ description="Code description:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+
+ self._inp_code_plugin = ipw.Dropdown(options=sorted(all_plugins('calculations')),
+ description="Code plugin:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+
+ self._exec_path = ipw.Text(placeholder='/path/to/executable',
+ description="Absolute path to executable:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+
+ self._prepend_text = ipw.Textarea(placeholder='Text to prepend to each command execution',
+ description='Prepend text:',
+ layout=ipw.Layout(width="400px"))
+
+ self._append_text = ipw.Textarea(placeholder='Text to append to each command execution',
+ description='Append text:',
+ layout=ipw.Layout(width="400px"))
+
+ self._btn_setup_code = ipw.Button(description="Setup code")
+ self._btn_setup_code.on_click(self._setup_code)
+ self._setup_code_out = ipw.Output()
+ children = [ipw.HBox([ipw.VBox([self._inp_code_label,
+ self._computer,
+ self._inp_code_plugin,
+ self._inp_code_description,
+ self._exec_path]), ipw.VBox([self._prepend_text,
+ self._append_text])]),
+ self._btn_setup_code,
+ self._setup_code_out,
+ ]
+ # Check if some settings were already provided
+ self._predefine_settings(**kwargs)
+ super(AiiDACodeSetup, self).__init__(children, **kwargs)
+
+ def _predefine_settings(self, **kwargs):
+ for key, value in kwargs.items():
+ if hasattr(self, key):
+ setattr(self, key, value)
+ else:
+ raise AttributeError("'{}' object has no attribute '{}'".format(self, key))
+
+ def _setup_code(self, b=None):
+ with self._setup_code_out:
+ clear_output()
+ if self.label is None:
+ print("You did not specify code label")
+ return
+ if not self.exec_path:
+ print("You did not specify absolute path to the executable")
+ return
+ if self.exists():
+ print ("Code {}@{} already exists".format(self.label, self.selected_computer.name))
+ return
+ code = Code(remote_computer_exec=(self.selected_computer, self.exec_path))
+ code.label = self.label
+ code.description = self.description
+ code.set_input_plugin_name(self.plugin)
+ code.set_prepend_text(self.prepend_text)
+ code.set_append_text(self.append_text)
+ code.store()
+ code._reveal()
+ full_string = "{}@{}".format(self.label, self.selected_computer.name)
+ print(check_output(['verdi', 'code', 'show', full_string]))
+
+ def exists(self):
+ from aiida.common.exceptions import NotExistent, MultipleObjectsError
+ try:
+ Code.get_from_string("{}@{}".format(self.label, self.selected_computer.name))
+ return True
+ except MultipleObjectsError:
+ return True
+ except NotExistent:
+ return False
+
+ @property
+ def label(self):
+ if len(self._inp_code_label.value.strip()) == 0:
+ return None
+ else:
+ return self._inp_code_label.value
+
+ @label.setter
+ def label(self, label):
+ self._inp_code_label.value = label
+
+ @property
+ def description(self):
+ return self._inp_code_description.value
+
+ @description.setter
+ def description(self, description):
+ self._inp_code_description.value = description
+
+ @property
+ def plugin(self):
+ return self._inp_code_plugin.value
+
+ @plugin.setter
+ def plugin(self, plugin):
+ if plugin in self._inp_code_plugin.options:
+ self._inp_code_plugin.value = plugin
+
+ @property
+ def exec_path(self):
+ return self._exec_path.value
+
+ @exec_path.setter
+ def exec_path(self, exec_path):
+ self._exec_path.value = exec_path
+
+ @property
+ def prepend_text(self):
+ return self._prepend_text.value
+
+ @prepend_text.setter
+ def prepend_text(self, prepend_text):
+ self._prepend_text.value = prepend_text
+
+ @property
+ def append_text(self):
+ return self._append_text.value
+
+ @append_text.setter
+ def append_text(self, append_text):
+ self._append_text.value = append_text
+
+ @property
+ def selected_computer(self):
+ return self._computer.selected_computer
+
+ @selected_computer.setter
+ def selected_computer(self, selected_computer):
+ self._computer.selected_computer = selected_computer
diff --git a/aiidalab_widgets_base/computers.py b/aiidalab_widgets_base/computers.py
new file mode 100644
index 000000000..96cb55eb8
--- /dev/null
+++ b/aiidalab_widgets_base/computers.py
@@ -0,0 +1,717 @@
+from __future__ import print_function
+
+import pexpect
+import ipywidgets as ipw
+
+from os import path
+from copy import copy
+from IPython.display import clear_output
+from subprocess import check_output, call
+from traitlets import Int
+
+from aiida.orm import Computer
+from aiida.backends.utils import get_automatic_user, get_backend_type
+from aiida.common.exceptions import NotExistent
+from aiida.transport.plugins.ssh import parse_sshconfig
+
+if get_backend_type() == 'sqlalchemy':
+ from aiida.backends.sqlalchemy.models.authinfo import DbAuthInfo
+else:
+ from aiida.backends.djsite.db.models import DbAuthInfo
+
+VALID_SSH_COMPUTER_SETUP_ARGUMETNS = {'hostname', 'username', 'proxy_hostname', 'proxy_username'}
+VALID_AIIDA_COMPUTER_SETUP_ARGUMETNS = {'name', 'hostname', 'description', 'workdir', 'mpirun_cmd',
+ 'ncpus', 'transport_type', 'scheduler', 'prepend_text', 'append_text'}
+
+def valid_arguments(arguments, valid_arguments):
+ result = {}
+ for key, value in arguments.items():
+ if key in valid_arguments:
+ if type(value) is tuple or type(value) is list:
+ result[key] = '\n'.join(value)
+ else:
+ result[key] = value
+ return result
+
+def extract_sshcomputersetup_arguments(arguments):
+ return valid_arguments(arguments, VALID_SSH_COMPUTER_SETUP_ARGUMETNS)
+
+def extract_aiidacomputer_arguments(arguments):
+ return valid_arguments(arguments, VALID_AIIDA_COMPUTER_SETUP_ARGUMETNS)
+
+class SshComputerSetup(ipw.VBox):
+ setup_counter = Int(0) # Traitlet to inform other widgets about changes
+ def __init__(self, **kwargs):
+ style = {"description_width":"200px"}
+ computer_image = ipw.HTML('')
+ self._inp_username = ipw.Text(description="SSH username:",
+ layout=ipw.Layout(width="350px"),
+ style=style)
+ self._inp_password = ipw.Password(description="SSH password:",
+ layout=ipw.Layout(width="130px"),
+ style=style)
+ # Computer ssh settings
+ self._inp_computer_hostname = ipw.Text(description="Computer name:",
+ layout=ipw.Layout(width="350px"),
+ style=style)
+ self._use_proxy = ipw.Checkbox(value=False, description='Use proxy')
+ self._use_proxy.observe(self.on_use_proxy_change, names='value')
+
+
+ # Proxy ssh settings
+ self._inp_proxy_address = ipw.Text(description="Proxy server address:",
+ layout=ipw.Layout(width="350px"),
+ style=style)
+ self._use_diff_proxy_username = ipw.Checkbox(value=False,
+ description='Use different username and password',
+ layout={'width': 'initial'})
+ self._use_diff_proxy_username.observe(self.on_use_diff_proxy_username_change, names='value')
+
+ self._inp_proxy_username = ipw.Text(value='',
+ description="Proxy server username:",
+ layout=ipw.Layout(width="350px"), style=style)
+ self._inp_proxy_password = ipw.Password(value='',
+ description="Proxy server password:",
+ layout=ipw.Layout(width="138px"),
+ style=style)
+ self._btn_setup_ssh = ipw.Button(description="Setup ssh")
+ self._btn_setup_ssh.on_click(self.on_setup_ssh)
+ self._setup_ssh_out = ipw.Output()
+
+ # Check if some settings were already provided
+ self._predefine_settings(**kwargs)
+
+ # Defining widgets positions
+ computer_ssh_box = ipw.VBox([self._inp_computer_hostname,
+ self._inp_username,
+ self._inp_password,
+ self._use_proxy],
+ layout=ipw.Layout(width="400px"))
+
+ self._proxy_user_password_box = ipw.VBox([self._inp_proxy_username,
+ self._inp_proxy_password],
+ layout={'visibility':'hidden'})
+
+ self._proxy_ssh_box = ipw.VBox([self._inp_proxy_address,
+ self._use_diff_proxy_username,
+ self._proxy_user_password_box],
+ layout = {'visibility':'hidden','width':'400px'})
+
+ children = [ipw.HBox([computer_image, computer_ssh_box, self._proxy_ssh_box]),
+ self._btn_setup_ssh,
+ self._setup_ssh_out]
+
+ super(SshComputerSetup, self).__init__(children, **kwargs)
+
+ def _predefine_settings(self, **kwargs):
+ for key, value in kwargs.items():
+ if hasattr(self, key):
+ setattr(self, key, value)
+ else:
+ raise AttributeError("'{}' object has no attirubte '{}'".format(self, key))
+
+ def _ssh_keygen(self):
+ fn = path.expanduser("~/.ssh/id_rsa")
+ if not path.exists(fn):
+ print("Creating ssh key pair")
+ # returns non-0 if the key pair already exists
+ call(["ssh-keygen", "-f", fn, "-t", "rsa", "-N", ""])
+
+ def is_host_known(self, hostname=None):
+ if hostname is None:
+ hostname = self.hostname
+ fn = path.expanduser("~/.ssh/known_hosts")
+ if not path.exists(fn):
+ return False
+ return call(["ssh-keygen", "-F", hostname]) == 0
+
+ def _make_host_known(self, hostname, proxycmd=[]):
+ fn = path.expanduser("~/.ssh/known_hosts")
+ print("Adding keys from %s to %s"%(hostname, fn))
+ hashes = check_output(proxycmd+["ssh-keyscan", "-H", hostname])
+ with open(fn, "a") as f:
+ f.write(hashes)
+
+ def can_login(self, silent=False):
+ if self.username is None: # if I can't find the username - I must fail
+ return False
+ userhost = self.username+"@"+self.hostname
+ if not silent:
+ print("Trying ssh "+userhost+"... ", end='')
+ # With BatchMode on, no password prompt or other interaction is attempted,
+ # so a connect that requires a password will fail.
+ ret = call(["ssh", userhost, "-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "true"])
+ if not silent:
+ print("Ok" if ret==0 else "Failed")
+ return ret==0
+
+ def is_in_config(self):
+ fn = path.expanduser("~/.ssh/config")
+ if not path.exists(fn):
+ return False
+ cfglines = open(fn).read().split("\n")
+ return "Host "+self.hostname in cfglines
+
+ def _write_ssh_config(self, proxycmd=''):
+ fn = path.expanduser("~/.ssh/config")
+ print("Adding section to "+fn)
+ with open(fn, "a") as f:
+ f.write("Host "+self.hostname+"\n")
+ f.write("User "+self.username+"\n")
+ if proxycmd:
+ f.write("ProxyCommand ssh -q -Y "+proxycmd+" netcat %h %p\n")
+ f.write("ServerAliveInterval 5\n")
+
+ def _send_pubkey(self, hostname, username, password, proxycmd=''):
+ from pexpect import TIMEOUT
+ timeout = 10
+ print("Sending public key to {}... ".format(hostname),end='')
+ str_ssh = 'ssh-copy-id {}@{}'.format(username, hostname)
+ if proxycmd:
+ str_ssh += ' -o "ProxyCommand ssh -q -Y '+proxycmd+' netcat %h %p\n"'
+ child = pexpect.spawn(str_ssh)
+ try:
+ index = child.expect(['s password:', # 0
+ 'ERROR: No identities found', # 1
+ 'All keys were skipped because they already exist on the remote system', # 2
+ 'Could not resolve hostname', # 3
+ pexpect.EOF],timeout=timeout) # final
+ except TIMEOUT:
+ print ("Exceeded {} s timeout".format(timeout))
+ return False
+
+ if index == 0:
+ child.sendline(password)
+ try:
+ child.expect("Now try logging into",timeout=5)
+ except:
+ print("Failed")
+ print("Please check your username and/or password")
+ return False
+ print("Ok")
+ child.close()
+ return True
+ elif index == 1:
+ print("Failed")
+ print("Looks like the key pair is not present in ~/.ssh folder")
+ return False
+ elif index == 2:
+ print("Keys are already there")
+ return True
+ elif index == 3:
+ print("Failed")
+ print("Unknown hostname")
+ return False
+ else:
+ print ("Failed")
+ print ("Unknown problem")
+ print (child.before, child.after)
+ child.close()
+ return False
+
+ def _configure_proxy(self, password, proxy_password):
+ # if proxy IS required
+ if self._use_proxy.value:
+ # again some standard checks if proxy server parameters are provided
+ if self.proxy_hostname is None: # hostname
+ print("Please specify the proxy server hostname")
+ return False, ''
+ # if proxy username and password must be different from the main computer - they should be provided
+ if self._use_diff_proxy_username.value:
+ # check username
+ if not self.proxy_username is None:
+ proxy_username = self.proxy_username
+ else:
+ print("Please specify the proxy server username")
+ return False, ''
+ # check password
+ if len(proxy_password.strip()) == 0:
+ print("Please specify the proxy server password")
+ return False, ''
+ else: # if username and password are the same as for the main computer
+ proxy_username = self.username
+ proxy_password = password
+ # make proxy server known
+ if not self.is_host_known(self.proxy_hostname):
+ self._make_host_known(self.proxy_hostname)
+ # Finally trying to connect
+ if self._send_pubkey(self.proxy_hostname, proxy_username, proxy_password):
+ return True, proxy_username + '@' + self.proxy_hostname
+ else:
+ print ("Could not send public key to {} (proxy server).".format(self.proxy_hostname))
+ return False, ''
+ # if proxy is NOT required
+ else:
+ return True, ''
+
+ def on_setup_ssh(self, b):
+ """ATTENTION: modifying the order of operations in this function can lead to unexpected problems"""
+ with self._setup_ssh_out:
+ clear_output()
+ self._ssh_keygen()
+
+ #temporary passwords
+ password = self.__password
+ proxy_password = self.__proxy_password
+
+ # step 1: if hostname is not provided - do not do anything
+ if self.hostname is None: # check hostname
+ print("Please specify the computer hostname")
+ return
+
+ # step 2: check if password-free access was enabled earlier
+ if self.can_login():
+ print ("Password-free access is already enabled")
+ # it can still happen that password-free access is enabled
+ # but host is not present in the config file - fixing this
+ if not self.is_in_config():
+ self._write_ssh_config() # we do not use proxy here, because if computer
+ # can be accessed without any info in the config - proxy is not needed.
+ self.setup_counter += 1 # only if config file has changed - increase setup_counter
+ return
+
+ # step 3: if can't login already, chek whether all required information is provided
+ if self.username is None: # check username
+ print("Please enter your ssh username")
+ return
+ if len(password.strip()) == 0: # check password
+ print("Please enter your ssh password")
+ return
+
+ # step 4: get the right commands to access the proxy server (if provided)
+ success, proxycmd = self._configure_proxy(password, proxy_password)
+ if not success:
+ return
+
+ # step 5: make host known by ssh on the proxy server
+ if not self.is_host_known():
+ self._make_host_known(self.hostname,['ssh']+[proxycmd] if proxycmd else [])
+
+ # step 6: sending public key to the main host
+ if not self._send_pubkey(self.hostname, self.username, password, proxycmd):
+ print ("Could not send public key to {}".format(self.hostname))
+ return
+
+ # step 7: modify the ssh config file if necessary
+ if not self.is_in_config():
+ self._write_ssh_config(proxycmd=proxycmd)
+ # TODO: add a check if new config is different from the current one. If so
+ # infrom the user about it.
+
+ # step 8: final check
+ if self.can_login():
+ self.setup_counter += 1
+ print("Automatic ssh setup successful :-)")
+ return
+ else:
+ print("Automatic ssh setup failed, sorry :-(")
+ return
+
+ def on_use_proxy_change(self, b):
+ if self._use_proxy.value:
+ self._proxy_ssh_box.layout.visibility = 'visible'
+ else:
+ self._proxy_ssh_box.layout.visibility = 'hidden'
+ self._use_diff_proxy_username.value = False
+
+ def on_use_diff_proxy_username_change(self, b):
+ if self._use_diff_proxy_username.value:
+ self._proxy_user_password_box.layout.visibility = 'visible'
+ else:
+ self._proxy_user_password_box.layout.visibility = 'hidden'
+
+# Keep this function only because it might be used later.
+# What it does: looks inside .ssh/config file and loads computer setup from
+# there (if present)
+# def _get_from_config(self, b):
+# config = parse_sshconfig(self.hostname)
+# if 'user' in config:
+# self._inp_username.value = config['user']
+# else:
+# self._inp_username.value = ''
+# if 'proxycommand' in config:
+# self._use_proxy.value = True
+# proxy = ''.join([ s for s in config['proxycommand'].split() if '@' in s])
+# username, hostname = proxy.split('@')
+# self._inp_proxy_address.value = hostname
+# if username != self.username:
+# self._use_diff_proxy_username.value = True
+# self.proxy_username = username
+# else:
+# self._use_proxy.value = False
+
+ @property
+ def __password(self):
+ """Returning the password and immediately destroying it"""
+ passwd = copy(self._inp_password.value)
+ self._inp_password.value = ''
+ return passwd
+
+ @property
+ def __proxy_password(self):
+ """Returning the password and immediately destroying it"""
+ passwd = copy(self._inp_proxy_password.value)
+ self._inp_proxy_password.value = ''
+ return passwd
+
+ @property
+ def hostname(self):
+ if len(self._inp_computer_hostname.value.strip()) == 0: # check hostname
+ return None
+ else:
+ return self._inp_computer_hostname.value
+
+ @hostname.setter
+ def hostname(self, hostname):
+ self._inp_computer_hostname.value = hostname
+
+ @property
+ def username(self):
+ """Loking for username in user's input and config file"""
+ if len(self._inp_username.value.strip()) == 0: # if username provided by user
+ if not self.hostname is None:
+ config = parse_sshconfig(self.hostname)
+ if 'user' in config: # if username is present in the config file
+ return config['user']
+ else:
+ return None
+ else:
+ return self._inp_username.value
+
+ @username.setter
+ def username(self, username):
+ self._inp_username.value = username
+
+ @property
+ def proxy_hostname(self):
+ if len(self._inp_proxy_address.value.strip()) == 0:
+ return None
+ else:
+ return self._inp_proxy_address.value
+
+ @proxy_hostname.setter
+ def proxy_hostname(self, proxy_hostname):
+ self._use_proxy.value = True
+ self._inp_proxy_address.value = proxy_hostname
+
+ @property
+ def proxy_username(self):
+ if len(self._inp_proxy_username.value.strip()) == 0:
+ return None
+ else:
+ return self._inp_proxy_username.value
+
+ @proxy_username.setter
+ def proxy_username(self, proxy_username):
+ self._use_proxy.value = True
+ self._use_diff_proxy_username.value = True
+ self._inp_proxy_username.value = proxy_username
+
+class AiidaComputerSetup(ipw.VBox):
+ def __init__(self, **kwargs):
+ from aiida.transport import Transport
+ from aiida.scheduler import Scheduler
+ style = {"description_width":"200px"}
+
+ # list of widgets to be displayed
+ self._btn_setup_comp = ipw.Button(description="Setup computer")
+ self._btn_setup_comp.on_click(self._on_setup_computer)
+ self._inp_computer_name = ipw.Text(value='',
+ placeholder='Will only be used within AiiDA',
+ description="AiiDA computer name:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+ self._computer_hostname = ipw.Dropdown(description="Select among configured hosts:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+ self._inp_computer_description = ipw.Text(value='',
+ placeholder='No description (yet)',
+ description="Computer description:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+ self._computer_workdir = ipw.Text(value='/scratch/{username}/aiida_run',
+ description="Workdir:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+ self._computer_mpirun_cmd = ipw.Text(value='mpirun -n {tot_num_mpiprocs}',
+ description="Mpirun command:",
+ layout=ipw.Layout(width="500px"),
+ style=style)
+ self._computer_ncpus = ipw.IntText(value=12,
+ step=1,
+ description='Number of CPU(s) per node:',
+ layout=ipw.Layout(width="270px"),
+ style=style)
+ self._transport_type = ipw.Dropdown(value='ssh',
+ options=Transport.get_valid_transports(),
+ description="Transport type:",
+ style=style)
+ self._scheduler = ipw.Dropdown(value='slurm',
+ options=Scheduler.get_valid_schedulers(),
+ description="Scheduler:",
+ style=style)
+ self._prepend_text = ipw.Textarea(placeholder='Text to prepend to each command execution',
+ description='Prepend text:',
+ layout=ipw.Layout(width="400px")
+ )
+ self._append_text = ipw.Textarea(placeholder='Text to append to each command execution',
+ description='Append text:',
+ layout=ipw.Layout(width="400px")
+ )
+ self._btn_test = ipw.Button(description="Test computer")
+ self._btn_test.on_click(self.test)
+
+ self._setup_comp_out = ipw.Output(layout=ipw.Layout(width="500px"))
+ self._test_out = ipw.Output(layout=ipw.Layout(width="500px"))
+
+ # getting the list of available computers
+ self.get_available_computers()
+
+ # Check if some settings were already provided
+ self._predefine_settings(**kwargs)
+ children =[ipw.HBox([ipw.VBox([self._inp_computer_name,
+ self._computer_hostname,
+ self._inp_computer_description,
+ self._computer_workdir,
+ self._computer_mpirun_cmd,
+ self._computer_ncpus,
+ self._transport_type,
+ self._scheduler]),
+ ipw.VBox([self._prepend_text,
+ self._append_text])]),
+ ipw.HBox([self._btn_setup_comp, self._btn_test]),
+ ipw.HBox([self._setup_comp_out, self._test_out]),
+ ]
+ super(AiidaComputerSetup, self).__init__(children, **kwargs)
+
+ def _predefine_settings(self, **kwargs):
+ for key, value in kwargs.items():
+ if hasattr(self, key):
+ setattr(self, key, value)
+ else:
+ raise AttributeError("'{}' object has no attribute '{}'".format(self, key))
+
+ def get_available_computers(self, b=None):
+ fn = path.expanduser("~/.ssh/config")
+ if not path.exists(fn):
+ return []
+ cfglines = open(fn).readlines()
+ self._computer_hostname.options = [line.split()[1] for line in cfglines if 'Host' in line]
+
+ def _configure_computer(self):
+ """create DbAuthInfo"""
+ print("Configuring '{}'".format(self.name))
+ sshcfg = parse_sshconfig(self.hostname)
+ authparams = {
+ 'compress': True,
+ 'gss_auth': False,
+ 'gss_deleg_creds': False,
+ 'gss_host': self.hostname,
+ 'gss_kex': False,
+ 'key_policy': 'WarningPolicy',
+ 'load_system_host_keys': True,
+ 'port': 22,
+ 'timeout': 60,
+ }
+ if 'user' in sshcfg:
+ authparams['username'] = sshcfg['user']
+ else:
+ print ("SSH username is not provided, please run `verdi computer configure {}` "
+ "from the command line".format(self.name))
+ return
+ if 'proxycommand' in sshcfg:
+ authparams['proxy_command'] = sshcfg['proxycommand']
+ aiidauser = get_automatic_user()
+ authinfo = DbAuthInfo(dbcomputer=Computer.get(self.name).dbcomputer, aiidauser=aiidauser)
+ authinfo.set_auth_params(authparams)
+ authinfo.save()
+ print(check_output(['verdi', 'computer', 'show', self.name]))
+
+ def _on_setup_computer(self, b):
+ with self._setup_comp_out:
+ clear_output()
+ if self.name is None: # check hostname
+ print("Please specify the computer name (for AiiDA)")
+ return
+ try:
+ computer = Computer.get(self.name)
+ print("A computer called {} already exists.".format(self.name))
+ return
+ except NotExistent:
+ pass
+
+ print("Creating new computer with name '{}'".format(self.name))
+ computer = Computer(name=self.name)
+ computer.set_hostname(self.hostname)
+ computer.set_description(self.description)
+ computer.set_enabled_state(True)
+ computer.set_transport_type(self.transport_type)
+ computer.set_scheduler_type(self.scheduler)
+ computer.set_workdir(self.workdir)
+ computer.set_mpirun_command(self.mpirun_cmd.split())
+ computer.set_default_mpiprocs_per_machine(self.ncpus)
+ if self._prepend_text.value:
+ computer.set_prepend_text(self.prepend_text)
+ if self._append_text.value:
+ computer.set_append_text(self.append_text)
+ computer.store()
+ self._configure_computer()
+
+ def test(self, b=None):
+ with self._test_out:
+ clear_output()
+ print(check_output(['verdi', 'computer', 'test', '--traceback', self.name]))
+
+ @property
+ def name(self):
+ if len(self._inp_computer_name.value.strip()) == 0: # check hostname
+ return None
+ else:
+ return self._inp_computer_name.value
+
+ @name.setter
+ def name(self, name):
+ self._inp_computer_name.value = name
+
+ @property
+ def hostname(self):
+ if self._computer_hostname.value is None or len(self._computer_hostname.value.strip()) == 0: # check hostname
+ return None
+ else:
+ return self._computer_hostname.value
+
+ @hostname.setter
+ def hostname(self, hostname):
+ if hostname in self._computer_hostname.options:
+ self._computer_hostname.value = hostname
+
+ @property
+ def description(self):
+ return self._inp_computer_description.value
+
+ @description.setter
+ def description(self, description):
+ self._inp_computer_description.value = description
+
+ @property
+ def workdir(self):
+ return self._computer_workdir.value
+
+ @workdir.setter
+ def workdir(self, workdir):
+ self._computer_workdir.value = workdir
+
+ @property
+ def mpirun_cmd(self):
+ return self._computer_mpirun_cmd.value
+
+ @mpirun_cmd.setter
+ def mpirun_cmd(self, mpirun_cmd):
+ self._computer_mpirun_cmd.value = mpirun_cmd
+
+ @property
+ def ncpus(self):
+ return self._computer_ncpus.value
+
+ @ncpus.setter
+ def ncpus(self, ncpus):
+ self._computer_ncpus.value = int(ncpus)
+
+ @property
+ def transport_type(self):
+ return self._transport_type.value
+
+ @transport_type.setter
+ def transport_type(self, transport_type):
+ if transport_type in self._transport_type.options:
+ self._transport_type.value = transport_type
+ @property
+ def scheduler(self):
+ return self._scheduler.value
+
+ @scheduler.setter
+ def scheduler(self, scheduler):
+ if scheduler in self._scheduler.options:
+ self._scheduler.value = scheduler
+
+ @property
+ def prepend_text(self):
+ return self._prepend_text.value
+
+ @prepend_text.setter
+ def prepend_text(self, prepend_text):
+ self._prepend_text.value = prepend_text
+
+ @property
+ def append_text(self):
+ return self._append_text.value
+
+ @append_text.setter
+ def append_text(self, append_text):
+ self._append_text.value = append_text
+
+class ComputerDropdown(ipw.VBox):
+ def __init__(self, text='Select computer:', **kwargs):
+ """ Dropdown for Codes for one input plugin.
+
+ :param text: Text to display before dropdown
+ :type text: str
+ """
+
+ self._dropdown = ipw.Dropdown(options=[], description=text, style={'description_width': 'initial'}, disabled=True)
+ self._btn_refresh = ipw.Button(description="Refresh", layout=ipw.Layout(width="70px"))
+
+ self._setup_another = ipw.HTML(value="""Setup new computer""",
+ layout = {'margin': '0px 0px 0px 250px'})
+ 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)
+
+ self._refresh()
+
+ def _get_computers(self):
+ from aiida.orm.querybuilder import QueryBuilder
+ current_user = get_automatic_user()
+
+ qb = QueryBuilder()
+ qb.append(
+ Computer, filters={'enabled': True}, project=['*'], tag='computer')
+
+ results = qb.all()
+
+ # only computers configured for the current user
+ results = [r for r in results if r[0].is_user_configured(current_user)]
+
+ self._dropdown.options = {r[0].name:r[0] for r in results}
+
+ def _refresh(self, b=None):
+ with self.output:
+ clear_output()
+ 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
+
+ @property
+ def selected_computer(self):
+ try:
+ return self._dropdown.value
+ except KeyError:
+ return None
+
+ @selected_computer.setter
+ def selected_computer(self, selected_computer):
+ if selected_computer in self.computers:
+ self._dropdown.label = selected_computer
+
diff --git a/aiidalab_widgets_base/display.py b/aiidalab_widgets_base/display.py
new file mode 100644
index 000000000..ce4f0489d
--- /dev/null
+++ b/aiidalab_widgets_base/display.py
@@ -0,0 +1,26 @@
+from __future__ import print_function
+
+import importlib
+from IPython.display import display
+
+AIIDA_VISUALIZER_MAPPING = {
+ 'data.parameter.ParameterData.' : 'ParameterDataVisualizer',
+ 'data.structure.StructureData.' : 'StructureDataVisualizer',
+ 'data.cif.CifData.' : 'StructureDataVisualizer',
+ 'data.folder.FolderData.' : 'FolderDataVisualizer',
+ 'data.array.bands.BandsData.' : 'BandsDataVisualizer',
+}
+
+def aiidalab_display(obj, downloadable=True, **kwargs):
+ """Display AiiDA data types in Jupyter notebooks.
+
+ :param downloadable: If True, add link/button to download content of displayed AiiDA object.
+
+ Defers to IPython.display.display for any objects it does not recognize.
+ """
+ from aiidalab_widgets_base import aiida_visualizers
+ try:
+ visualizer = getattr(aiida_visualizers, AIIDA_VISUALIZER_MAPPING[obj.type])
+ display(visualizer(obj, downloadable=downloadable), **kwargs)
+ except KeyError:
+ display(obj, **kwargs)
\ No newline at end of file
diff --git a/aiidalab_widgets_base/search_qb.py b/aiidalab_widgets_base/search_qb.py
index 09a208f1e..94c70b57b 100644
--- a/aiidalab_widgets_base/search_qb.py
+++ b/aiidalab_widgets_base/search_qb.py
@@ -1,8 +1,3 @@
-from aiida import load_dbenv, is_dbenv_loaded
-from aiida.backends import settings
-if not is_dbenv_loaded():
- load_dbenv(profile=settings.AIIDADB_PROFILE)
-
from aiida.orm.querybuilder import QueryBuilder
from aiida.orm.data.structure import StructureData
diff --git a/aiidalab_widgets_base/structures.py b/aiidalab_widgets_base/structures.py
index 2dd905687..f327b6a91 100644
--- a/aiidalab_widgets_base/structures.py
+++ b/aiidalab_widgets_base/structures.py
@@ -1,6 +1,6 @@
from __future__ import print_function
-
from __future__ import absolute_import
+
import ase.io
import ipywidgets as ipw
from fileupload import FileUploadWidget
@@ -54,11 +54,6 @@ def __init__(self, text="Upload Structure", node_class=None, **kwargs):
self.file_upload.observe(self._on_file_upload, names='data')
self.btn_store.on_click(self._on_click_store)
- from aiida import load_dbenv, is_dbenv_loaded
- from aiida.backends import settings
- if not is_dbenv_loaded():
- load_dbenv(profile=settings.AIIDADB_PROFILE)
-
# pylint: disable=unused-argument
def _on_file_upload(self, change):
self.tmp_folder = tempfile.mkdtemp()
diff --git a/aiidalab_widgets_base/structures_multi.py b/aiidalab_widgets_base/structures_multi.py
index 699bd5aa8..e88de5a9e 100644
--- a/aiidalab_widgets_base/structures_multi.py
+++ b/aiidalab_widgets_base/structures_multi.py
@@ -68,12 +68,6 @@ def __init__(self, text="Upload Zip or Tar archive", node_class=None, **kwargs):
self.btn_store_all.on_click(self._on_click_store_all)
self.btn_store_selected.on_click(self._on_click_store_selected)
- # create aiida-related things
- from aiida import load_dbenv, is_dbenv_loaded
- from aiida.backends import settings
- if not is_dbenv_loaded():
- load_dbenv(profile=settings.AIIDADB_PROFILE)
-
# function to be called when selection_slider changes
def change_structure(self):
if self.selection_slider.value is None:
diff --git a/cod.ipynb b/cod.ipynb
index cf8b2b23e..ae8ec38d1 100644
--- a/cod.ipynb
+++ b/cod.ipynb
@@ -266,7 +266,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
- "version": "2.7.13"
+ "version": "2.7.15rc1"
}
},
"nbformat": 4,
diff --git a/codes.ipynb b/codes.ipynb
index d009889a0..a9fb7ea0f 100644
--- a/codes.ipynb
+++ b/codes.ipynb
@@ -40,7 +40,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
- "version": "2.7.15"
+ "version": "2.7.15rc1"
}
},
"nbformat": 4,
diff --git a/metadata.json b/metadata.json
index be3b3e17d..e7544ce63 100644
--- a/metadata.json
+++ b/metadata.json
@@ -2,7 +2,7 @@
"title": "AiiDA Lab Widgets",
"description": "Reusable widgets for applications in the AiiDA Lab.",
"authors": "AiiDA Team",
- "version": "0.2.0a2",
+ "version": "0.3.0b1",
"logo": "miscellaneous/logos/aiidalab.png",
"state": "stable"
}
diff --git a/miscellaneous/images/computer.png b/miscellaneous/images/computer.png
new file mode 100644
index 000000000..9e0465273
Binary files /dev/null and b/miscellaneous/images/computer.png differ
diff --git a/setup_code.ipynb b/setup_code.ipynb
new file mode 100644
index 000000000..284641a8d
--- /dev/null
+++ b/setup_code.ipynb
@@ -0,0 +1,62 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Setup code:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import urlparse\n",
+ "from aiidalab_widgets_base import AiiDACodeSetup, extract_aiidacodesetup_arguments"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "parsed_url = urlparse.parse_qs(urlparse.urlsplit(jupyter_notebook_url).query)\n",
+ "args = extract_aiidacodesetup_arguments(parsed_url)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "code = AiiDACodeSetup(**args)\n",
+ "display(code)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 2",
+ "language": "python",
+ "name": "python2"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.15rc1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/setup_computer.ipynb b/setup_computer.ipynb
new file mode 100644
index 000000000..5ec69f1e6
--- /dev/null
+++ b/setup_computer.ipynb
@@ -0,0 +1,91 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Setup a computer with AiiDA "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import urlparse\n",
+ "\n",
+ "from aiidalab_widgets_base import SshComputerSetup, extract_sshcomputersetup_arguments\n",
+ "from aiidalab_widgets_base import AiidaComputerSetup, extract_aiidacomputer_arguments"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "parsed_url = urlparse.parse_qs(urlparse.urlsplit(jupyter_notebook_url).query)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 1: Setup ssh\n",
+ "Note: The password is used only to set up the ssh connection and is never stored."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "args = extract_sshcomputersetup_arguments(parsed_url)\n",
+ "sshcomputer = SshComputerSetup(**args)\n",
+ "display(sshcomputer)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Setup & Test AiiDA Computer"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "args = extract_aiidacomputer_arguments(parsed_url)\n",
+ "aiidacomputer = AiidaComputerSetup(**args)\n",
+ "sshcomputer.observe(aiidacomputer.get_available_computers, names=['setup_counter'])\n",
+ "display(aiidacomputer)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 2",
+ "language": "python",
+ "name": "python2"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.15rc1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/start.py b/start.py
index ce84ab1a0..007a5b389 100644
--- a/start.py
+++ b/start.py
@@ -4,13 +4,20 @@
AiiDA Lab widgets | ++ | |
---|---|---|
+ |