diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 97842f9..bb87d3b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,3 +1,4 @@
+---
# Run basic tests for this app on the latest aiidalab-docker image.
name: continuous-integration
@@ -6,22 +7,22 @@ on: [push, pull_request]
jobs:
- pre-commit:
+ pre-commit:
# Adapted from: https://github.com/CasperWA/voila-optimade-client
- runs-on: ubuntu-latest
+ runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
+ steps:
+ - uses: actions/checkout@v2
- - name: Setup Python
- uses: actions/setup-python@v2
- with:
- python-version: 3.8
+ - name: Setup Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
- - name: Install dependencies
- run: |
- pip install .[pre_commit]
+ - name: Install dependencies
+ run: |
+ pip install .[pre_commit]
- - name: Run pre-commit
- run: pre-commit run --all-files || ( git status --short ; git diff ; exit 1 )
+ - name: Run pre-commit
+ run: pre-commit run --all-files || ( git status --short ; git diff ; exit 1 )
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5b4a7e3..227df0e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,18 +1,46 @@
+---
repos:
- - repo: https://github.com/psf/black
- rev: 20.8b1
- hooks:
- - id: black
- language_version: python3 # Should be a command that runs python3.6+
- - repo: https://gitlab.com/pycqa/flake8
- rev: '3.8.4'
- hooks:
- - id: flake8
- args: [--count, --show-source, --statistics]
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.3.0
+ hooks:
+ - id: check-json
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
+ exclude: miscellaneous/structures/SiO2.xyz
- - repo: https://github.com/pycqa/isort
- rev: 5.6.4
- hooks:
- - id: isort
- args: ["--profile", "black", "--filter-files"]
+ - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt
+ rev: 0.2.2
+ hooks:
+ - id: yamlfmt
+
+ - repo: https://github.com/psf/black
+ rev: 22.6.0
+ hooks:
+ - id: black
+ language_version: python3 # Should be a command that runs python3.6+
+
+ - repo: https://github.com/PyCQA/flake8
+ rev: 4.0.1
+ hooks:
+ - id: flake8
+ args: [--count, --show-source, --statistics]
+ additional_dependencies:
+ - flake8-bugbear==21.3.1
+
+ - repo: https://github.com/pycqa/isort
+ rev: 5.10.1
+ hooks:
+ - id: isort
+ args: [--profile, black, --filter-files]
+
+ - repo: https://github.com/asottile/setup-cfg-fmt
+ rev: v1.20.2
+ hooks:
+ - id: setup-cfg-fmt
+
+ - repo: https://github.com/sirosen/check-jsonschema
+ rev: 0.17.1
+ hooks:
+ - id: check-github-workflows
diff --git a/LICENSE.txt b/LICENSE.txt
index a6a7277..dacf60f 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
+SOFTWARE.
diff --git a/home/app_manager.py b/home/app_manager.py
index c0ffbbb..f4dd289 100644
--- a/home/app_manager.py
+++ b/home/app_manager.py
@@ -260,7 +260,11 @@ def _refresh_widget_state(self, _=None):
# Check app compatibility and show banner if not compatible.
self.compatibility_warning.layout.visibility = (
"visible"
- if (self.app.is_installed() and self.app.compatible is False)
+ if (
+ not busy
+ and self.app.is_installed()
+ and self.app.compatible is False
+ )
else "hidden"
)
@@ -374,6 +378,8 @@ def _refresh_widget_state(self, _=None):
self.issue_indicator.value = f' Unable to reach the registry server.'
elif not registered:
self.issue_indicator.value = f' The app is not registered.'
+ elif busy:
+ self.issue_indicator.value = ""
elif detached:
self.issue_indicator.value = (
f' The app has local modifications or was checked out '
@@ -388,7 +394,8 @@ def _refresh_widget_state(self, _=None):
)
if (
- any(self.app.compatibility_info.values())
+ not busy
+ and any(self.app.compatibility_info.values())
and self.app.compatible is False
):
self.compatibility_info.value = self.COMPATIBILITY_INFO.render(
@@ -429,6 +436,7 @@ def _install_version(self, _=None):
self._show_msg_success(
f"Installed app ({self._formatted_version(version)})."
)
+ self.dependencies_log.value = ""
def _update_app(self, _):
"""Attempt to update the app."""
@@ -439,6 +447,7 @@ def _update_app(self, _):
self._show_msg_failure(str(error))
else:
self._show_msg_success("Updated app.")
+ self.dependencies_log.value = ""
def _uninstall_app(self, _):
"""Attempt to uninstall the app."""
diff --git a/open_app.ipynb b/open_app.ipynb
new file mode 100644
index 0000000..8f23ee0
--- /dev/null
+++ b/open_app.ipynb
@@ -0,0 +1,132 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1e6a8c8c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import urllib.parse as urlparse\n",
+ "import ipywidgets as ipw"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7e7e205e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import sys\n",
+ "from aiidalab.__main__ import _find_version_to_install, _parse_requirement\n",
+ "from aiidalab.utils import PEP508CompliantUrl\n",
+ "\n",
+ "\n",
+ "def identify_app_and_version(app_query, prereleases, force): \n",
+ " try:\n",
+ " requirement = _parse_requirement(app_query)\n",
+ " except Exception as error:\n",
+ " raise RuntimeError(str(error)) from error\n",
+ " \n",
+ " try:\n",
+ " return _find_version_to_install(\n",
+ " requirement,\n",
+ " dependencies=\"install\",\n",
+ " force=force,\n",
+ " python_bin=sys.executable,\n",
+ " prereleases=prereleases)\n",
+ " except Exception as error:\n",
+ " raise RuntimeError(str(error)) from error"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b3e749de",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "url = urlparse.urlsplit(jupyter_notebook_url)\n",
+ "\n",
+ "query = urlparse.parse_qs(url.query)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c25a71a5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from home.widgets import LogOutputWidget, Spinner, StatusHTML\n",
+ "from IPython.display import Javascript\n",
+ "\n",
+ "header = ipw.HTML()\n",
+ "logs = LogOutputWidget(layout=ipw.Layout(min_height=\"250px\", max_height=\"100px\"))\n",
+ "spinner = Spinner()\n",
+ "\n",
+ "display(ipw.VBox([ipw.HBox([header, spinner]), logs]))\n",
+ "\n",
+ "try:\n",
+ " try:\n",
+ " app_query = query[\"app\"][0]\n",
+ " except KeyError:\n",
+ " raise RuntimeError(\"No app to install specified.\")\n",
+ " \n",
+ " force = query.get(\"force\", [False])[0]\n",
+ " prereleases = query.get(\"prereleases\", [False])[0]\n",
+ " \n",
+ " app, version = identify_app_and_version(query[\"app\"][0], force=force, prereleases=prereleases)\n",
+ "except Exception as error:\n",
+ " header.value = f\"Error: {error}\"\n",
+ "\n",
+ "try:\n",
+ " if version is None:\n",
+ " header.value = \"App already installed.\"\n",
+ " else:\n",
+ " header.value = f\"Installing app '{app.name}': {version}...\"\n",
+ "\n",
+ " spinner.enabled = True\n",
+ " app.install(\n",
+ " version=version,\n",
+ " install_dependencies=True,\n",
+ " python_bin=sys.executable,\n",
+ " stdout=logs,\n",
+ " )\n",
+ "except Exception as error:\n",
+ " header.value = f\"Error while trying to install the app: {error}\"\n",
+ "else:\n",
+ " redirect = query.get(\"redirect\", None)\n",
+ " if redirect:\n",
+ " redirect_url = urlparse.urlunsplit(url._replace(path=redirect[0], query=None, fragment=None))\n",
+ " header.value = f'Redirecting now to {redirect_url}...'\n",
+ " js = Javascript(f\"window.location = '{redirect_url}'\")\n",
+ " display(js)\n",
+ "finally:\n",
+ " spinner.enabled = False"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/setup.cfg b/setup.cfg
index 552f5c3..f3225b1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,38 +1,42 @@
[metadata]
-name = aiidalab-home
+name = aiidalab_home
version = attr: home.__version__
-author = The AiiDAlab Team
-author_email = aiidalab@materialscloud.org
description = Package for the AiiDAlab Home app.
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/aiidalab/aiidalab-home
-project_urls =
- Bug Tracker = https://github.com/aiidalab/aiidalab-home/issues
+author = The AiiDAlab Team
+author_email = aiidalab@materialscloud.org
+license = MIT
+license_file = LICENSE.txt
classifiers =
Development Status :: 4 - Beta
Framework :: AiiDA
License :: OSI Approved :: MIT License
- Programming Language :: Python :: 3.7
+ Programming Language :: Python :: 3
+ Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
+ Programming Language :: Python :: 3.10
+project_urls =
+ Bug Tracker = https://github.com/aiidalab/aiidalab-home/issues
[options]
packages = find:
-python_requires = >=3.7
install_requires =
- aiidalab>=v21.06.0
- ipython~=7.25
+ Jinja2>=2.11.3,<4
+ Markdown>=3.4
+ aiidalab>=v21.10.2
+ ipython~=7.0
ipywidgets~=7.6
- jinja2~=2.11
- traitlets~=4.3
+ traitlets~=5.0
+python_requires = >=3.8
[options.extras_require]
pre_commit =
pre-commit==2.15.0
[flake8]
-# Adapted from: https://github.com/CasperWA/voila-optimade-client
ignore =
E501 # Line length handled by black.
W503 # Line break before binary operator, preferred formatting for black.