aiidalab-chemshell 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aiidalab_chemshell-0.0.1/LICENSE +28 -0
- aiidalab_chemshell-0.0.1/PKG-INFO +32 -0
- aiidalab_chemshell-0.0.1/README.md +124 -0
- aiidalab_chemshell-0.0.1/pyproject.toml +31 -0
- aiidalab_chemshell-0.0.1/setup.cfg +55 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/__init__.py +5 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/common/__init__.py +1 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/common/chemshell.py +53 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/common/database.py +170 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/common/file_handling.py +89 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/common/navigation.py +81 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/common/node_viewers.py +166 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/history.py +124 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/main.py +70 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/models/__init__.py +1 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/models/process.py +38 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/models/resources.py +35 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/models/results.py +11 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/models/structure.py +34 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/models/workflow.py +31 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/process.py +141 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/utils.py +106 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/__init__.py +1 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/main_app.py +70 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/resources.py +173 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/results.py +84 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/structure.py +138 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/workflows/__init__.py +5 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/workflows/geometry_optimisation.py +153 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/workflows/main_view.py +127 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell/wizards/workflows/neb.py +1 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell.egg-info/PKG-INFO +32 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell.egg-info/SOURCES.txt +36 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell.egg-info/dependency_links.txt +1 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell.egg-info/requires.txt +15 -0
- aiidalab_chemshell-0.0.1/src/aiidalab_chemshell.egg-info/top_level.txt +1 -0
- aiidalab_chemshell-0.0.1/tests/test_base.py +8 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, UKRI Science and Technology Facilities Council
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aiidalab-chemshell
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: AiiDAlab application for ChemShell based workflows
|
|
5
|
+
Home-page: https://github.com/stfc/aiidalab-chemshell
|
|
6
|
+
Author: Dr. Benjamin T. Speake
|
|
7
|
+
Author-email: benjamin.speake@stfc.ac.uk
|
|
8
|
+
Project-URL: Logo, https://raw.githubusercontent.com/stfc/aiidalab-chemshell/refs/heads/main/images/alc-100.webp
|
|
9
|
+
Project-URL: Source, https://github.com/stfc/aiidalab-chemshell
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/stfc/aiidalab-chemshell/issues
|
|
11
|
+
Project-URL: Documentation, https://stfc.github.io/aiidalab-chemshell
|
|
12
|
+
Classifier: Framework :: AiiDA
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: aiida-core
|
|
20
|
+
Requires-Dist: aiidalab-widgets-base
|
|
21
|
+
Requires-Dist: aiida-chemshell>=0.1.5
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-cov>=6.0; extra == "dev"
|
|
25
|
+
Requires-Dist: ruff>=0.11.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
27
|
+
Provides-Extra: docs
|
|
28
|
+
Requires-Dist: sphinx>=8.0.2; extra == "docs"
|
|
29
|
+
Requires-Dist: piccolo-theme>=0.14.0; extra == "docs"
|
|
30
|
+
Requires-Dist: sphinx-toolbox; extra == "docs"
|
|
31
|
+
Requires-Dist: numpydoc; extra == "docs"
|
|
32
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# AiiDAlab ChemShell Plugin
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[](https://pypi.org/)
|
|
5
|
+
|
|
6
|
+
[](https://github.com/stfc/aiidalab-chemshell/actions)
|
|
7
|
+
[](https://stfc.github.io/aiidalab-chemshell/)
|
|
8
|
+
<!-- [](https://coveralls.io/github/stfc/aiidalab-chemshell?branch=main) -->
|
|
9
|
+
|
|
10
|
+
[](https://zenodo.org/)
|
|
11
|
+
|
|
12
|
+
This is an AiiDAlab application plugin for ChemShell based scientific workflows, maintained by the [Ada Lovelace Center](https://adalovelacecentre.ac.uk/) (ALC).
|
|
13
|
+
The app is still in early development stage and any input/contributions are welcome.
|
|
14
|
+
Full documentation can be found here: [https://stfc.github.io/aiidalab-chemshell/](https://stfc.github.io/aiidalab-chemshell/).
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
This plugin is hosted on the AiiDAlab plugin registry and therefore can be installed via the AiiDAlab plugin
|
|
19
|
+
management UI page from within the AiiDAlab application interface. Instructions for how to run AiiDAlab
|
|
20
|
+
itself can be found in its [documentation](https://aiidalab.readthedocs.io/en/latest/usage/access/index.html)
|
|
21
|
+
and are also included in the documentation associated with this project
|
|
22
|
+
[https://stfc.github.io/aiidalab-chemshell/](https://stfc.github.io/aiidalab-chemshell/).
|
|
23
|
+
It is generally recommended to run AiiDAlab through a container engine such as Docker or Apptainer, both of
|
|
24
|
+
which are discussed in more detail in the documentation provided. In general the core docker image applicable
|
|
25
|
+
to most use cases is [aiidalab/full-stack:latest](https://hub.docker.com/r/aiidalab/full-stack) however, many
|
|
26
|
+
other options exist for more tailored startup environments.
|
|
27
|
+
|
|
28
|
+
## ChemShell Containers
|
|
29
|
+
|
|
30
|
+
Multiple docker images are provided with this repository which build on the core foundation of the images
|
|
31
|
+
provided by AiiDAlab bundling various components of the ChemShell workflows on-top of the core AiiDAlab
|
|
32
|
+
application. At present two images are available; [base](https://github.com/stfc/aiidalab-chemshell/pkgs/container/aiidalab-chemshell%2Fbase) includes the AiiDAlab ChemShell plugin pre-installed including all
|
|
33
|
+
required dependencies,
|
|
34
|
+
[full](https://github.com/stfc/aiidalab-chemshell/pkgs/container/aiidalab-chemshell%2Ffull) builds upon
|
|
35
|
+
the base package including a working installation of ChemShell (version 25) configured with DL_POLY, NWChem
|
|
36
|
+
and PySCF as available backends.
|
|
37
|
+
|
|
38
|
+
To run one of the provided containers, first install and setup your desired container engine, then run the
|
|
39
|
+
image as follows,
|
|
40
|
+
|
|
41
|
+
``` sh
|
|
42
|
+
docker run -it --rm -p 8888:8888 -v $HOME:/home/jovyan ghcr.io/stfc/aiidalab-chemshell/base:latest
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The additional run parameters will run the container interactively (``-it``), delete it when it is finished
|
|
46
|
+
(``--rm``), expose the required port for the jupyter notebook instance (``-p 8888:8888``) and the final flag
|
|
47
|
+
(``-v``) binds the home directory into the containers home directory so data can be made available and will
|
|
48
|
+
persist beyond the container instance. For more information on how to configure containers for AiiDAlab
|
|
49
|
+
see [https://stfc.github.io/alc-ux](https://stfc.github.io/alc-ux).
|
|
50
|
+
|
|
51
|
+
## For Developers
|
|
52
|
+
|
|
53
|
+
### Style Checking
|
|
54
|
+
|
|
55
|
+
This package uses pre-commit hooks to check for style consistency, to use these the ``pre-commit`` tool is required.
|
|
56
|
+
This can be installed alongside the base package by running,
|
|
57
|
+
|
|
58
|
+
``` sh
|
|
59
|
+
pip install .[dev]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
or separately via,
|
|
63
|
+
|
|
64
|
+
``` sh
|
|
65
|
+
pip install pre-commit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Once installed run,
|
|
69
|
+
|
|
70
|
+
``` sh
|
|
71
|
+
pre-commit install
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
in the base repository to enable the pre-commit hooks.
|
|
75
|
+
This will now run style and formatting checks on every commit.
|
|
76
|
+
|
|
77
|
+
### Testing
|
|
78
|
+
|
|
79
|
+
This package uses [pytest](https://docs.pytest.org/en/stable/)
|
|
80
|
+
to run all unit tests which is included in the ```[dev]``` optional
|
|
81
|
+
package dependencies. Once installed it can be run from the project root directory.
|
|
82
|
+
The CI workflows are configured to ensure all tests pass
|
|
83
|
+
before a pull request can be accepted into the main repository.
|
|
84
|
+
It is important that any new additions to the code base are accompanied
|
|
85
|
+
by appropriate testing, maintaining a high code coverage. The coverage
|
|
86
|
+
can be checked via,
|
|
87
|
+
|
|
88
|
+
``` sh
|
|
89
|
+
pytest --cov=aiidalab_alc
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Documentation
|
|
93
|
+
|
|
94
|
+
The documentation, including a User Guide, Developer Guide and an API reference,
|
|
95
|
+
is built using [sphinx](https://www.sphinx-doc.org/). The source
|
|
96
|
+
for which is contained in the ```docs/``` directory. At present
|
|
97
|
+
only the html generator has been fully tested. All required packages can
|
|
98
|
+
be installed alongside the core package via,
|
|
99
|
+
|
|
100
|
+
``` sh
|
|
101
|
+
pip install .[docs]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
and then the documentation can be built using sphinx-build,
|
|
105
|
+
|
|
106
|
+
``` sh
|
|
107
|
+
sphinx-build -b html docs/source/ docs/build/html
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
from the root directory.
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
[BSD 3-Clause License](LICENSE)
|
|
115
|
+
|
|
116
|
+
## Funding
|
|
117
|
+
|
|
118
|
+
Contributors to this project were funded by
|
|
119
|
+
|
|
120
|
+
<div align="center">
|
|
121
|
+
<a href="https://adalovelacecentre.ac.uk/">
|
|
122
|
+
<img src="images/alc.svg" alt="ALC Logo" style="width: 30%">
|
|
123
|
+
</a>
|
|
124
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=62.6"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[tool.ruff]
|
|
7
|
+
target-version = "py310"
|
|
8
|
+
exclude = ["conf.py",]
|
|
9
|
+
|
|
10
|
+
[tool.ruff.lint]
|
|
11
|
+
select = [
|
|
12
|
+
# flake8-bugbear
|
|
13
|
+
"B",
|
|
14
|
+
# pylint
|
|
15
|
+
"C", "R",
|
|
16
|
+
# pydocstyle
|
|
17
|
+
"D",
|
|
18
|
+
# pycodestyle
|
|
19
|
+
"E", "W",
|
|
20
|
+
# Pyflakes
|
|
21
|
+
"F", "FA",
|
|
22
|
+
# isort
|
|
23
|
+
"I",
|
|
24
|
+
# pep8-naming
|
|
25
|
+
"N",
|
|
26
|
+
# pyupgrade
|
|
27
|
+
"UP",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.ruff.lint.pydocstyle]
|
|
31
|
+
convention = "numpy"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[aiidalab]
|
|
2
|
+
title = AiiDAlab ChemShell
|
|
3
|
+
categories =
|
|
4
|
+
Classical
|
|
5
|
+
Quantum
|
|
6
|
+
|
|
7
|
+
[metadata]
|
|
8
|
+
name = aiidalab-chemshell
|
|
9
|
+
version = 0.0.1
|
|
10
|
+
author = Dr. Benjamin T. Speake
|
|
11
|
+
author_email = benjamin.speake@stfc.ac.uk
|
|
12
|
+
description = AiiDAlab application for ChemShell based workflows
|
|
13
|
+
long_description = file: README
|
|
14
|
+
long_description_content_type = text/markdown
|
|
15
|
+
url = https://github.com/stfc/aiidalab-chemshell
|
|
16
|
+
project_urls =
|
|
17
|
+
Logo = https://raw.githubusercontent.com/stfc/aiidalab-chemshell/refs/heads/main/images/alc-100.webp
|
|
18
|
+
Source = https://github.com/stfc/aiidalab-chemshell
|
|
19
|
+
Bug Tracker = https://github.com/stfc/aiidalab-chemshell/issues
|
|
20
|
+
Documentation = https://stfc.github.io/aiidalab-chemshell
|
|
21
|
+
classifiers =
|
|
22
|
+
Framework :: AiiDA
|
|
23
|
+
Operating System :: OS Independent
|
|
24
|
+
Programming Language :: Python :: 3
|
|
25
|
+
Development Status :: 4 - Beta
|
|
26
|
+
|
|
27
|
+
[options]
|
|
28
|
+
package_dir =
|
|
29
|
+
= src
|
|
30
|
+
packages = find:
|
|
31
|
+
install_requires =
|
|
32
|
+
aiida-core
|
|
33
|
+
aiidalab-widgets-base
|
|
34
|
+
aiida-chemshell>=0.1.5
|
|
35
|
+
python_requires = >=3.9
|
|
36
|
+
|
|
37
|
+
[options.packages.find]
|
|
38
|
+
where = src
|
|
39
|
+
|
|
40
|
+
[options.extras_require]
|
|
41
|
+
dev =
|
|
42
|
+
pytest>=8.0
|
|
43
|
+
pytest-cov>=6.0
|
|
44
|
+
ruff>=0.11.0
|
|
45
|
+
pre-commit
|
|
46
|
+
docs =
|
|
47
|
+
sphinx>=8.0.2
|
|
48
|
+
piccolo-theme>=0.14.0
|
|
49
|
+
sphinx-toolbox
|
|
50
|
+
numpydoc
|
|
51
|
+
|
|
52
|
+
[egg_info]
|
|
53
|
+
tag_build =
|
|
54
|
+
tag_date = 0
|
|
55
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package for general common components."""
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Module containing common settings for configuring ChemShell."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum, auto
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BasisSetOptions(Enum):
|
|
7
|
+
"""Pre-defined basis set levels for simplified ChemShell inputs."""
|
|
8
|
+
|
|
9
|
+
FAST = 0
|
|
10
|
+
BALANCED = auto()
|
|
11
|
+
QUALITY = auto()
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def label(self) -> str:
|
|
15
|
+
"""Convert enum value to a string representation for ChemShell input."""
|
|
16
|
+
match self:
|
|
17
|
+
case BasisSetOptions.FAST:
|
|
18
|
+
return "3-21G"
|
|
19
|
+
case BasisSetOptions.BALANCED:
|
|
20
|
+
return "cc-pvdz"
|
|
21
|
+
case BasisSetOptions.QUALITY:
|
|
22
|
+
return "aug-cc-pvtz"
|
|
23
|
+
case "":
|
|
24
|
+
return ""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class WorkflowOptions(Enum):
|
|
28
|
+
"""Enum defining the available ChemShell based AiiDA workflows."""
|
|
29
|
+
|
|
30
|
+
GEOMETRY = 0
|
|
31
|
+
NEB = auto()
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def label(self) -> str:
|
|
35
|
+
"""Convert enum value into a more human readable string."""
|
|
36
|
+
match self:
|
|
37
|
+
case WorkflowOptions.GEOMETRY:
|
|
38
|
+
return "Geometry Optimisation"
|
|
39
|
+
case WorkflowOptions.NEB:
|
|
40
|
+
return "Nudged Elastic Band"
|
|
41
|
+
case _:
|
|
42
|
+
return ""
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def tab_label(self) -> str:
|
|
46
|
+
"""Create a tab title for the given enum option."""
|
|
47
|
+
match self:
|
|
48
|
+
case WorkflowOptions.GEOMETRY:
|
|
49
|
+
return "Optimisation"
|
|
50
|
+
case WorkflowOptions.NEB:
|
|
51
|
+
return "NEB"
|
|
52
|
+
case _:
|
|
53
|
+
return "ChemShell"
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Module for components relating to AiiDA database management."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
import ipywidgets as ipw
|
|
6
|
+
import traitlets as tl
|
|
7
|
+
from aiida.orm import (
|
|
8
|
+
CalcFunctionNode,
|
|
9
|
+
CalcJobNode,
|
|
10
|
+
Node,
|
|
11
|
+
QueryBuilder,
|
|
12
|
+
WorkChainNode,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AiiDADatabaseWidget(ipw.VBox, tl.HasTraits):
|
|
17
|
+
"""Widget for AiiDA database querying."""
|
|
18
|
+
|
|
19
|
+
data_object = tl.Instance(Node, allow_none=True)
|
|
20
|
+
|
|
21
|
+
def __init__(self, title: str = "", query: list | None = None):
|
|
22
|
+
if query is None:
|
|
23
|
+
query = []
|
|
24
|
+
self.title = title
|
|
25
|
+
self.query_type = tuple(query)
|
|
26
|
+
|
|
27
|
+
qbuilder = QueryBuilder().append((CalcJobNode, WorkChainNode), project="label")
|
|
28
|
+
|
|
29
|
+
self.drop_down = ipw.Dropdown(
|
|
30
|
+
options=sorted({"All"}.union({i[0] for i in qbuilder.iterall() if i[0]})),
|
|
31
|
+
value="All",
|
|
32
|
+
description="Process Label",
|
|
33
|
+
disabled=True,
|
|
34
|
+
style={"description_width": "120px"},
|
|
35
|
+
layout={"width": "50%"},
|
|
36
|
+
)
|
|
37
|
+
self.drop_down.observe(self.search, names="value")
|
|
38
|
+
|
|
39
|
+
# Disable process labels selection if we are not looking for the calculated
|
|
40
|
+
# structures.
|
|
41
|
+
def disable_drop_down(change):
|
|
42
|
+
self.drop_down.disabled = not change["new"] == "calculated"
|
|
43
|
+
|
|
44
|
+
# Select structures kind.
|
|
45
|
+
self.mode = ipw.RadioButtons(
|
|
46
|
+
options=["all", "uploaded", "calculated"], layout={"width": "25%"}
|
|
47
|
+
)
|
|
48
|
+
self.mode.observe(self.search, names="value")
|
|
49
|
+
self.mode.observe(disable_drop_down, names="value")
|
|
50
|
+
|
|
51
|
+
# Date range.
|
|
52
|
+
# Note: there is Date picker widget, but it currently does not work in Safari:
|
|
53
|
+
# https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html#Date-picker
|
|
54
|
+
date_text = ipw.HTML(value="<p>Select the date range:</p>")
|
|
55
|
+
self.start_date_widget = ipw.Text(
|
|
56
|
+
value="", description="From: ", style={"description_width": "120px"}
|
|
57
|
+
)
|
|
58
|
+
self.end_date_widget = ipw.Text(value="", description="To: ")
|
|
59
|
+
|
|
60
|
+
# Search button.
|
|
61
|
+
btn_search = ipw.Button(
|
|
62
|
+
description="Search",
|
|
63
|
+
button_style="info",
|
|
64
|
+
layout={"width": "initial", "margin": "2px 0 0 2em"},
|
|
65
|
+
)
|
|
66
|
+
btn_search.on_click(self.search)
|
|
67
|
+
|
|
68
|
+
age_selection = ipw.VBox(
|
|
69
|
+
[
|
|
70
|
+
date_text,
|
|
71
|
+
ipw.HBox([self.start_date_widget, self.end_date_widget, btn_search]),
|
|
72
|
+
],
|
|
73
|
+
layout={"border": "1px solid #fafafa", "padding": "1em"},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
h_line = ipw.HTML("<hr>")
|
|
77
|
+
box = ipw.VBox([age_selection, h_line, ipw.HBox([self.mode, self.drop_down])])
|
|
78
|
+
|
|
79
|
+
self.results = ipw.Dropdown(layout={"width": "900px"})
|
|
80
|
+
self.results.observe(self._on_select_structure, names="value")
|
|
81
|
+
self.search()
|
|
82
|
+
super().__init__([box, h_line, self.results])
|
|
83
|
+
|
|
84
|
+
def search(self, _=None) -> None:
|
|
85
|
+
"""Search structures in the AiiDA database."""
|
|
86
|
+
qbuild = QueryBuilder()
|
|
87
|
+
|
|
88
|
+
# If the date range is valid, use it for the search
|
|
89
|
+
try:
|
|
90
|
+
start_date = datetime.datetime.strptime(
|
|
91
|
+
self.start_date_widget.value, "%Y-%m-%d"
|
|
92
|
+
)
|
|
93
|
+
end_date = datetime.datetime.strptime(
|
|
94
|
+
self.end_date_widget.value, "%Y-%m-%d"
|
|
95
|
+
) + datetime.timedelta(hours=24)
|
|
96
|
+
|
|
97
|
+
# Otherwise revert to the standard (i.e. last 7 days)
|
|
98
|
+
except ValueError:
|
|
99
|
+
start_date = datetime.datetime.now() - datetime.timedelta(days=7)
|
|
100
|
+
end_date = datetime.datetime.now() + datetime.timedelta(hours=24)
|
|
101
|
+
|
|
102
|
+
self.start_date_widget.value = start_date.strftime("%Y-%m-%d")
|
|
103
|
+
self.end_date_widget.value = end_date.strftime("%Y-%m-%d")
|
|
104
|
+
|
|
105
|
+
filters = {}
|
|
106
|
+
filters["ctime"] = {"and": [{">": start_date}, {"<=": end_date}]}
|
|
107
|
+
|
|
108
|
+
if self.mode.value == "uploaded":
|
|
109
|
+
qbuild2 = (
|
|
110
|
+
QueryBuilder()
|
|
111
|
+
.append(self.query_type, project=["id"], tag="structures")
|
|
112
|
+
.append(Node, with_outgoing="structures")
|
|
113
|
+
)
|
|
114
|
+
processed_nodes = [n[0] for n in qbuild2.all()]
|
|
115
|
+
if processed_nodes:
|
|
116
|
+
filters["id"] = {"!in": processed_nodes}
|
|
117
|
+
qbuild.append(self.query_type, filters=filters)
|
|
118
|
+
|
|
119
|
+
elif self.mode.value == "calculated":
|
|
120
|
+
if self.drop_down.value == "All":
|
|
121
|
+
qbuild.append((CalcJobNode, WorkChainNode), tag="calcjobworkchain")
|
|
122
|
+
else:
|
|
123
|
+
qbuild.append(
|
|
124
|
+
(CalcJobNode, WorkChainNode),
|
|
125
|
+
filters={"label": self.drop_down.value},
|
|
126
|
+
tag="calcjobworkchain",
|
|
127
|
+
)
|
|
128
|
+
qbuild.append(
|
|
129
|
+
self.query_type,
|
|
130
|
+
with_incoming="calcjobworkchain",
|
|
131
|
+
filters=filters,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
elif self.mode.value == "edited":
|
|
135
|
+
qbuild.append(CalcFunctionNode)
|
|
136
|
+
qbuild.append(
|
|
137
|
+
self.query_type,
|
|
138
|
+
with_incoming=CalcFunctionNode,
|
|
139
|
+
filters=filters,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
elif self.mode.value == "all":
|
|
143
|
+
qbuild.append(self.query_type, filters=filters)
|
|
144
|
+
|
|
145
|
+
qbuild.order_by({self.query_type: {"ctime": "desc"}})
|
|
146
|
+
matches = {n[0] for n in qbuild.iterall()}
|
|
147
|
+
matches = sorted(matches, reverse=True, key=lambda n: n.ctime)
|
|
148
|
+
|
|
149
|
+
options = [(f"Select a Node ({len(matches)} found)", False)]
|
|
150
|
+
for mch in matches:
|
|
151
|
+
label = f"PK: {mch.pk}"
|
|
152
|
+
label += " | " + mch.ctime.strftime("%Y-%m-%d %H:%M")
|
|
153
|
+
label += " | " + mch.base.extras.get("formula", "")
|
|
154
|
+
label += " | " + mch.node_type.split(".")[-2]
|
|
155
|
+
label += " | " + mch.label
|
|
156
|
+
label += " | " + mch.description
|
|
157
|
+
options.append((label, mch))
|
|
158
|
+
|
|
159
|
+
self.results.options = options
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
def _on_select_structure(self, _) -> None:
|
|
163
|
+
self.data_object = self.results.value or None
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
def disable(self, val: bool) -> None:
|
|
167
|
+
"""Disable the widget."""
|
|
168
|
+
self.results.disabled = True
|
|
169
|
+
# self.
|
|
170
|
+
return
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Module for providing functionality to deal with files."""
|
|
2
|
+
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
|
|
5
|
+
import traitlets as tl
|
|
6
|
+
from aiida.orm import SinglefileData
|
|
7
|
+
from ipywidgets import FileUpload, HBox, Text
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FileUploadWidget(HBox, tl.HasTraits):
|
|
11
|
+
"""A widget for uploading files."""
|
|
12
|
+
|
|
13
|
+
file = tl.Instance(SinglefileData, allow_none=True)
|
|
14
|
+
|
|
15
|
+
def __init__(self, description: str = "File: ", **kwargs):
|
|
16
|
+
"""
|
|
17
|
+
FileUploadWidget constructor.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
**kwargs :
|
|
22
|
+
Keyword arguments passed to the parent class's constructor.
|
|
23
|
+
"""
|
|
24
|
+
super().__init__(**kwargs)
|
|
25
|
+
self.file_dict = None
|
|
26
|
+
|
|
27
|
+
self.file_upload = FileUpload(
|
|
28
|
+
accept="",
|
|
29
|
+
multiple=False,
|
|
30
|
+
description="Upload",
|
|
31
|
+
layout={"width": "20%"},
|
|
32
|
+
)
|
|
33
|
+
self.file_handle = Text(
|
|
34
|
+
value="",
|
|
35
|
+
placeholder="",
|
|
36
|
+
description=description,
|
|
37
|
+
disabled=True,
|
|
38
|
+
layout={"width": "70%"},
|
|
39
|
+
)
|
|
40
|
+
self.children = [self.file_handle, self.file_upload]
|
|
41
|
+
|
|
42
|
+
self.file_upload.observe(self._on_file_upload, names="value")
|
|
43
|
+
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def has_file(self) -> bool:
|
|
48
|
+
"""True if a file has been uploaded."""
|
|
49
|
+
return self.file is not None
|
|
50
|
+
|
|
51
|
+
def _on_file_upload(self, _):
|
|
52
|
+
"""Handle file upload events."""
|
|
53
|
+
if self.file_upload.value:
|
|
54
|
+
self.file_dict = self.file_upload.value[
|
|
55
|
+
list(self.file_upload.value.keys())[0]
|
|
56
|
+
]
|
|
57
|
+
self.file_handle.value = self.file_dict["metadata"]["name"]
|
|
58
|
+
self.file = self.get_aiida_file_object()
|
|
59
|
+
else:
|
|
60
|
+
self.file_handle.value = ""
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
def get_file_contents(self) -> BytesIO | None:
|
|
64
|
+
"""Get the contents of the uploaded file as a BytesIO object."""
|
|
65
|
+
if self.file_dict is not None:
|
|
66
|
+
return BytesIO(self.file_dict["content"])
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
def filename(self) -> str:
|
|
70
|
+
"""Get the name of the uploaded file."""
|
|
71
|
+
if self.file_dict is not None:
|
|
72
|
+
return self.file_dict["metadata"]["name"]
|
|
73
|
+
return ""
|
|
74
|
+
|
|
75
|
+
def get_aiida_file_object(self):
|
|
76
|
+
"""Get the uploaded file as an AiiDA SinglefileData object."""
|
|
77
|
+
if self.file_dict is not None:
|
|
78
|
+
return SinglefileData(
|
|
79
|
+
file=self.get_file_contents(),
|
|
80
|
+
filename=self.filename(),
|
|
81
|
+
label=self.filename(),
|
|
82
|
+
description=self.file_handle.description,
|
|
83
|
+
)
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
def disable(self, val: bool) -> None:
|
|
87
|
+
"""Disable the file upload widget."""
|
|
88
|
+
self.file_upload.disabled = val
|
|
89
|
+
return
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Module handling navigation controls within the app."""
|
|
2
|
+
|
|
3
|
+
from functools import partial
|
|
4
|
+
|
|
5
|
+
import ipywidgets as ipw
|
|
6
|
+
|
|
7
|
+
from aiidalab_chemshell.utils import open_link_in_new_tab
|
|
8
|
+
|
|
9
|
+
_APPS_DIRECTORY = "/apps/apps/"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class QuickAccessButtons(ipw.HBox):
|
|
13
|
+
"""Quick access buttons present in the apps header and start banner."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, **kwargs):
|
|
16
|
+
"""
|
|
17
|
+
QuickAccessButtons constructor.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
**kwargs :
|
|
22
|
+
Keyword arguments passed to the `ipywidgets.HBox.__init__()`.
|
|
23
|
+
"""
|
|
24
|
+
self.new_calc_link = ipw.Button(
|
|
25
|
+
description="New Calculation",
|
|
26
|
+
disabled=False,
|
|
27
|
+
button_style="success",
|
|
28
|
+
tooltip="Start a new calculation",
|
|
29
|
+
icon="plus",
|
|
30
|
+
)
|
|
31
|
+
self.new_calc_link.on_click(
|
|
32
|
+
partial(
|
|
33
|
+
open_link_in_new_tab,
|
|
34
|
+
_APPS_DIRECTORY + "aiidalab-chemshell/notebooks/main.ipynb",
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
self.history_link = ipw.Button(
|
|
39
|
+
description="History",
|
|
40
|
+
disabled=False,
|
|
41
|
+
button_style="primary",
|
|
42
|
+
tooltip="View Calculation History",
|
|
43
|
+
icon="history",
|
|
44
|
+
)
|
|
45
|
+
self.history_link.on_click(
|
|
46
|
+
partial(
|
|
47
|
+
open_link_in_new_tab,
|
|
48
|
+
_APPS_DIRECTORY + "aiidalab-chemshell/notebooks/history.ipynb",
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
self.resource_setup_link = ipw.Button(
|
|
53
|
+
description="Setup Resources",
|
|
54
|
+
disabled=False,
|
|
55
|
+
button_style="primary",
|
|
56
|
+
tooltip="Configure Computational Resources",
|
|
57
|
+
icon="cogs",
|
|
58
|
+
)
|
|
59
|
+
self.resource_setup_link.on_click(
|
|
60
|
+
partial(open_link_in_new_tab, _APPS_DIRECTORY + "home/code_setup.ipynb")
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
self.docs_link = ipw.Button(
|
|
64
|
+
description="Documentation",
|
|
65
|
+
disabled=False,
|
|
66
|
+
button_style="info",
|
|
67
|
+
tooltip="Open Documentation",
|
|
68
|
+
icon="book",
|
|
69
|
+
)
|
|
70
|
+
self.docs_link.on_click(
|
|
71
|
+
partial(open_link_in_new_tab, "https://github.com/stfc/aiidalab-chemshell")
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
children = [
|
|
75
|
+
self.new_calc_link,
|
|
76
|
+
self.history_link,
|
|
77
|
+
self.resource_setup_link,
|
|
78
|
+
self.docs_link,
|
|
79
|
+
]
|
|
80
|
+
super().__init__(children=children, layout={"margin": "auto"}, **kwargs)
|
|
81
|
+
return
|