alc-aiidalab-widgets 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.
- alc_aiidalab_widgets-0.0.1/LICENSE +28 -0
- alc_aiidalab_widgets-0.0.1/PKG-INFO +156 -0
- alc_aiidalab_widgets-0.0.1/README.md +100 -0
- alc_aiidalab_widgets-0.0.1/pyproject.toml +77 -0
- alc_aiidalab_widgets-0.0.1/src/alc_aiidalab_widgets/__init__.py +5 -0
- alc_aiidalab_widgets-0.0.1/src/alc_aiidalab_widgets/widgets/__init__.py +1 -0
- alc_aiidalab_widgets-0.0.1/src/alc_aiidalab_widgets/widgets/code_setup.py +54 -0
- alc_aiidalab_widgets-0.0.1/src/alc_aiidalab_widgets/widgets/database.py +185 -0
- alc_aiidalab_widgets-0.0.1/src/alc_aiidalab_widgets/widgets/file_handling.py +97 -0
- alc_aiidalab_widgets-0.0.1/src/alc_aiidalab_widgets/widgets/loading.py +26 -0
- alc_aiidalab_widgets-0.0.1/src/alc_aiidalab_widgets/widgets/structure.py +80 -0
- alc_aiidalab_widgets-0.0.1/src/alc_aiidalab_widgets/widgets/tables.py +60 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, 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,156 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: alc-aiidalab-widgets
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Commonly used components for ALC managed AiiDAlab plugins
|
|
5
|
+
Keywords: aiida,workflows,ALC
|
|
6
|
+
Author: Dr. Benjamin T. Speake
|
|
7
|
+
Author-email: Dr. Benjamin T. Speake <benjamin.speake@stfc.ac.uk>
|
|
8
|
+
License: BSD 3-Clause License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026, UKRI Science and Technology Facilities Council
|
|
11
|
+
|
|
12
|
+
Redistribution and use in source and binary forms, with or without
|
|
13
|
+
modification, are permitted provided that the following conditions are met:
|
|
14
|
+
|
|
15
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
16
|
+
list of conditions and the following disclaimer.
|
|
17
|
+
|
|
18
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
19
|
+
this list of conditions and the following disclaimer in the documentation
|
|
20
|
+
and/or other materials provided with the distribution.
|
|
21
|
+
|
|
22
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
23
|
+
contributors may be used to endorse or promote products derived from
|
|
24
|
+
this software without specific prior written permission.
|
|
25
|
+
|
|
26
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
27
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
28
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
29
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
30
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
31
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
32
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
33
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
34
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
35
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
36
|
+
Classifier: Intended Audience :: Science/Research
|
|
37
|
+
Classifier: Programming Language :: Python :: 3
|
|
38
|
+
Classifier: Topic :: Scientific/Engineering
|
|
39
|
+
Classifier: Framework :: AiiDA
|
|
40
|
+
Classifier: Development Status :: 1 - Planning
|
|
41
|
+
Requires-Dist: aiidalab-widgets-base
|
|
42
|
+
Requires-Dist: ipywidgets
|
|
43
|
+
Requires-Dist: traitlets
|
|
44
|
+
Requires-Dist: aiida-core>=2.6
|
|
45
|
+
Requires-Dist: weas-widget
|
|
46
|
+
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest-cov>=6.0 ; extra == 'dev'
|
|
48
|
+
Requires-Dist: ruff>=0.11.0 ; extra == 'dev'
|
|
49
|
+
Requires-Dist: pre-commit ; extra == 'dev'
|
|
50
|
+
Requires-Dist: sphinx>=8.0.2 ; extra == 'docs'
|
|
51
|
+
Requires-Python: >=3.10
|
|
52
|
+
Project-URL: Source, https://github.com/stfc/alc-aiidalab-widgets
|
|
53
|
+
Provides-Extra: dev
|
|
54
|
+
Provides-Extra: docs
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
|
|
57
|
+
# alc-aiidalab-widgets
|
|
58
|
+
|
|
59
|
+
[](https://github.com/stfc/alc-aiidalab-widgets/releases)
|
|
60
|
+
[](https://pypi.org/project/alc-aiidalab-widgets/)
|
|
61
|
+
|
|
62
|
+
[](https://github.com/stfc/alc-aiidalab-widgets/actions)
|
|
63
|
+
[](https://stfc.github.io/alc-aiidalab-widgets/)
|
|
64
|
+
<!-- [](https://coveralls.io/github/stfc/alc-aiidalab-widgets?branch=main) -->
|
|
65
|
+
|
|
66
|
+
<!-- [](https://doi.org/10.5281/zenodo.19188755) -->
|
|
67
|
+
|
|
68
|
+
This is a collection of commonly used widgets used across AiiDAlab plugins developed by
|
|
69
|
+
the Ada Lovelace Centre (STFC). It provides re-usable components and consistent styling
|
|
70
|
+
which can be deployed to any new or developing AiiDAlab plugin. It can be installed via
|
|
71
|
+
pip,
|
|
72
|
+
|
|
73
|
+
``` sh
|
|
74
|
+
pip install alc-aiidalab-widgets
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
and then imported within any python project as,
|
|
78
|
+
|
|
79
|
+
``` python
|
|
80
|
+
import alc_aiidalab_widgets
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## For Developers
|
|
84
|
+
|
|
85
|
+
### Style Checking
|
|
86
|
+
|
|
87
|
+
This package uses pre-commit hooks to check for style consistency, to use these the ``pre-commit`` tool is required.
|
|
88
|
+
This can be installed alongside the base package by running,
|
|
89
|
+
|
|
90
|
+
``` sh
|
|
91
|
+
pip install .[dev]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
or separately via,
|
|
95
|
+
|
|
96
|
+
``` sh
|
|
97
|
+
pip install pre-commit
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Once installed run,
|
|
101
|
+
|
|
102
|
+
``` sh
|
|
103
|
+
pre-commit install
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
in the base repository to enable the pre-commit hooks.
|
|
107
|
+
This will now run style and formatting checks on every commit.
|
|
108
|
+
|
|
109
|
+
### Testing
|
|
110
|
+
|
|
111
|
+
This package uses [pytest](https://docs.pytest.org/en/stable/)
|
|
112
|
+
to run all unit tests which is included in the ```[dev]``` optional
|
|
113
|
+
package dependencies. Once installed it can be run from the project root directory.
|
|
114
|
+
The CI workflows are configured to ensure all tests pass
|
|
115
|
+
before a pull request can be accepted into the main repository.
|
|
116
|
+
It is important that any new additions to the code base are accompanied
|
|
117
|
+
by appropriate testing, maintaining a high code coverage. The coverage
|
|
118
|
+
can be checked via,
|
|
119
|
+
|
|
120
|
+
``` sh
|
|
121
|
+
pytest --cov=aiidalab_alc
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Documentation
|
|
125
|
+
|
|
126
|
+
The documentation, including a User Guide, Developer Guide and an API reference,
|
|
127
|
+
is built using [sphinx](https://www.sphinx-doc.org/). The source
|
|
128
|
+
for which is contained in the ```docs/``` directory. At present
|
|
129
|
+
only the html generator has been fully tested. All required packages can
|
|
130
|
+
be installed alongside the core package via,
|
|
131
|
+
|
|
132
|
+
``` sh
|
|
133
|
+
pip install .[docs]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
and then the documentation can be built using sphinx-build,
|
|
137
|
+
|
|
138
|
+
``` sh
|
|
139
|
+
sphinx-build -b html docs/src/ docs/build/html
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
from the root directory.
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
[BSD 3-Clause License](LICENSE)
|
|
147
|
+
|
|
148
|
+
## Funding
|
|
149
|
+
|
|
150
|
+
Contributors to this project were funded by
|
|
151
|
+
|
|
152
|
+
<div align="center">
|
|
153
|
+
<a href="https://adalovelacecentre.ac.uk/">
|
|
154
|
+
<img src="images/alc.svg" alt="ALC Logo" style="width: 30%">
|
|
155
|
+
</a>
|
|
156
|
+
</div>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# alc-aiidalab-widgets
|
|
2
|
+
|
|
3
|
+
[](https://github.com/stfc/alc-aiidalab-widgets/releases)
|
|
4
|
+
[](https://pypi.org/project/alc-aiidalab-widgets/)
|
|
5
|
+
|
|
6
|
+
[](https://github.com/stfc/alc-aiidalab-widgets/actions)
|
|
7
|
+
[](https://stfc.github.io/alc-aiidalab-widgets/)
|
|
8
|
+
<!-- [](https://coveralls.io/github/stfc/alc-aiidalab-widgets?branch=main) -->
|
|
9
|
+
|
|
10
|
+
<!-- [](https://doi.org/10.5281/zenodo.19188755) -->
|
|
11
|
+
|
|
12
|
+
This is a collection of commonly used widgets used across AiiDAlab plugins developed by
|
|
13
|
+
the Ada Lovelace Centre (STFC). It provides re-usable components and consistent styling
|
|
14
|
+
which can be deployed to any new or developing AiiDAlab plugin. It can be installed via
|
|
15
|
+
pip,
|
|
16
|
+
|
|
17
|
+
``` sh
|
|
18
|
+
pip install alc-aiidalab-widgets
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
and then imported within any python project as,
|
|
22
|
+
|
|
23
|
+
``` python
|
|
24
|
+
import alc_aiidalab_widgets
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## For Developers
|
|
28
|
+
|
|
29
|
+
### Style Checking
|
|
30
|
+
|
|
31
|
+
This package uses pre-commit hooks to check for style consistency, to use these the ``pre-commit`` tool is required.
|
|
32
|
+
This can be installed alongside the base package by running,
|
|
33
|
+
|
|
34
|
+
``` sh
|
|
35
|
+
pip install .[dev]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
or separately via,
|
|
39
|
+
|
|
40
|
+
``` sh
|
|
41
|
+
pip install pre-commit
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Once installed run,
|
|
45
|
+
|
|
46
|
+
``` sh
|
|
47
|
+
pre-commit install
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
in the base repository to enable the pre-commit hooks.
|
|
51
|
+
This will now run style and formatting checks on every commit.
|
|
52
|
+
|
|
53
|
+
### Testing
|
|
54
|
+
|
|
55
|
+
This package uses [pytest](https://docs.pytest.org/en/stable/)
|
|
56
|
+
to run all unit tests which is included in the ```[dev]``` optional
|
|
57
|
+
package dependencies. Once installed it can be run from the project root directory.
|
|
58
|
+
The CI workflows are configured to ensure all tests pass
|
|
59
|
+
before a pull request can be accepted into the main repository.
|
|
60
|
+
It is important that any new additions to the code base are accompanied
|
|
61
|
+
by appropriate testing, maintaining a high code coverage. The coverage
|
|
62
|
+
can be checked via,
|
|
63
|
+
|
|
64
|
+
``` sh
|
|
65
|
+
pytest --cov=aiidalab_alc
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Documentation
|
|
69
|
+
|
|
70
|
+
The documentation, including a User Guide, Developer Guide and an API reference,
|
|
71
|
+
is built using [sphinx](https://www.sphinx-doc.org/). The source
|
|
72
|
+
for which is contained in the ```docs/``` directory. At present
|
|
73
|
+
only the html generator has been fully tested. All required packages can
|
|
74
|
+
be installed alongside the core package via,
|
|
75
|
+
|
|
76
|
+
``` sh
|
|
77
|
+
pip install .[docs]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
and then the documentation can be built using sphinx-build,
|
|
81
|
+
|
|
82
|
+
``` sh
|
|
83
|
+
sphinx-build -b html docs/src/ docs/build/html
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
from the root directory.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
[BSD 3-Clause License](LICENSE)
|
|
91
|
+
|
|
92
|
+
## Funding
|
|
93
|
+
|
|
94
|
+
Contributors to this project were funded by
|
|
95
|
+
|
|
96
|
+
<div align="center">
|
|
97
|
+
<a href="https://adalovelacecentre.ac.uk/">
|
|
98
|
+
<img src="images/alc.svg" alt="ALC Logo" style="width: 30%">
|
|
99
|
+
</a>
|
|
100
|
+
</div>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv-build"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
name = "alc-aiidalab-widgets"
|
|
9
|
+
description = "Commonly used components for ALC managed AiiDAlab plugins"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Dr. Benjamin T. Speake", email = "benjamin.speake@stfc.ac.uk" },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
license = { file = "LICENSE" }
|
|
16
|
+
keywords = ["aiida", "workflows", "ALC"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Scientific/Engineering",
|
|
21
|
+
"Framework :: AiiDA",
|
|
22
|
+
"Development Status :: 1 - Planning",
|
|
23
|
+
]
|
|
24
|
+
requires-python = ">=3.10"
|
|
25
|
+
dependencies = [
|
|
26
|
+
"aiidalab-widgets-base",
|
|
27
|
+
"ipywidgets",
|
|
28
|
+
"traitlets",
|
|
29
|
+
"aiida-core>=2.6",
|
|
30
|
+
"weas-widget",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Source = "https://github.com/stfc/alc-aiidalab-widgets"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = ["pytest>=8.0", "pytest-cov>=6.0", "ruff>=0.11.0", "pre-commit"]
|
|
39
|
+
docs = ["sphinx>=8.0.2"]
|
|
40
|
+
|
|
41
|
+
[tool.ruff]
|
|
42
|
+
target-version = "py312"
|
|
43
|
+
line-length = 88
|
|
44
|
+
indent-width = 4
|
|
45
|
+
exclude = ["docs/src/conf.py"]
|
|
46
|
+
|
|
47
|
+
[tool.ruff.lint.per-file-ignores]
|
|
48
|
+
|
|
49
|
+
[tool.ruff.lint]
|
|
50
|
+
select = [
|
|
51
|
+
# flake8-bugbear
|
|
52
|
+
"B",
|
|
53
|
+
# pylint
|
|
54
|
+
"C",
|
|
55
|
+
"R",
|
|
56
|
+
# pydocstyle
|
|
57
|
+
"D",
|
|
58
|
+
# pycodestyle
|
|
59
|
+
"E",
|
|
60
|
+
"W",
|
|
61
|
+
# Pyflakes
|
|
62
|
+
"F",
|
|
63
|
+
"FA",
|
|
64
|
+
# isort
|
|
65
|
+
"I",
|
|
66
|
+
# pep8-naming
|
|
67
|
+
"N",
|
|
68
|
+
# pyupgrade
|
|
69
|
+
"UP",
|
|
70
|
+
]
|
|
71
|
+
ignore = ["C901"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint.pydocstyle]
|
|
74
|
+
convention = "numpy"
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint.pylint]
|
|
77
|
+
max-args = 10
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Module providing reusable and extensible widget implementations."""
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Defines a resource setup widget based on foundations from aiidalab-widgets-base."""
|
|
2
|
+
|
|
3
|
+
from aiidalab_widgets_base.computational_resources import ResourceSetupBaseWidget
|
|
4
|
+
from aiidalab_widgets_base.utils import StatusHTML
|
|
5
|
+
from ipywidgets import HTML, Text, VBox, dlink
|
|
6
|
+
from traitlets import HasTraits, Unicode, observe
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CodeSetupWidget(VBox, HasTraits):
|
|
10
|
+
"""Widget to setup a new code instance.
|
|
11
|
+
|
|
12
|
+
Extends the existing ResourceSetupBaseWidget from aiidalab-widgets-base with the
|
|
13
|
+
ability to alter the database source link. By default using this widget will
|
|
14
|
+
point to the provided ALC resources database contained within the
|
|
15
|
+
https://github.com/stfc/alc-ux repository.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
_database_source = Unicode(
|
|
19
|
+
"",
|
|
20
|
+
allow_none=False,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def __init__(self, **kwargs):
|
|
24
|
+
"""CodeSetupWidget constructor."""
|
|
25
|
+
self.source = Text(
|
|
26
|
+
value=self._database_source, description="Source: ", layout={"width": "80%"}
|
|
27
|
+
)
|
|
28
|
+
dlink((self.source, "value"), (self, "_database_source"))
|
|
29
|
+
self.resource_widget = ResourceSetupBaseWidget()
|
|
30
|
+
self.setup_message = StatusHTML(clear_after=15)
|
|
31
|
+
dlink(
|
|
32
|
+
(self.resource_widget, "message"),
|
|
33
|
+
(self.setup_message, "message"),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
self.source.value = "https://raw.githubusercontent.com/stfc/alc-ux/refs/heads/main/resources/remotes.json"
|
|
37
|
+
|
|
38
|
+
children = [
|
|
39
|
+
HTML("<hr>"),
|
|
40
|
+
self.source,
|
|
41
|
+
HTML("<hr>"),
|
|
42
|
+
self.resource_widget,
|
|
43
|
+
self.setup_message,
|
|
44
|
+
]
|
|
45
|
+
super().__init__(children=children, **kwargs)
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
@observe("_database_source")
|
|
49
|
+
def _update_database_source(self, _):
|
|
50
|
+
"""Update the database source in the ResourceSetupBaseWidget."""
|
|
51
|
+
self.resource_widget.comp_resources_database.database_source = (
|
|
52
|
+
self._database_source
|
|
53
|
+
)
|
|
54
|
+
return
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Module for components relating to AiiDA database management."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
import ipywidgets as ipw
|
|
6
|
+
from aiida.orm import (
|
|
7
|
+
CalcFunctionNode,
|
|
8
|
+
CalcJobNode,
|
|
9
|
+
Node,
|
|
10
|
+
QueryBuilder,
|
|
11
|
+
WorkChainNode,
|
|
12
|
+
)
|
|
13
|
+
from traitlets import HasTraits, Instance
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AiiDADatabaseQueryWidget(ipw.VBox, HasTraits):
|
|
17
|
+
"""Widget for AiiDA database searching."""
|
|
18
|
+
|
|
19
|
+
data_object = Instance(Node, allow_none=True)
|
|
20
|
+
|
|
21
|
+
def __init__(self, title: str = "", query: list | None = None):
|
|
22
|
+
"""AiiDADatabaseQueryWidget constructor.
|
|
23
|
+
|
|
24
|
+
Creates a widgets for searching specific nodes within the AiiDA database,
|
|
25
|
+
based on an input query list of node types. It also provides further options
|
|
26
|
+
for tailoring the search.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
title: str
|
|
31
|
+
The widget's title
|
|
32
|
+
query: List | None
|
|
33
|
+
A list of AiiDA node types to search for in the database, if None will
|
|
34
|
+
default to search for CalcJobNodes and WorkChainNodes.
|
|
35
|
+
"""
|
|
36
|
+
if query is None:
|
|
37
|
+
query = []
|
|
38
|
+
self.title = title
|
|
39
|
+
self.query_type = tuple(query)
|
|
40
|
+
|
|
41
|
+
qbuilder = QueryBuilder().append((CalcJobNode, WorkChainNode), project="label")
|
|
42
|
+
|
|
43
|
+
self.drop_down = ipw.Dropdown(
|
|
44
|
+
options=sorted({"All"}.union({i[0] for i in qbuilder.iterall() if i[0]})),
|
|
45
|
+
value="All",
|
|
46
|
+
description="Process Label",
|
|
47
|
+
disabled=True,
|
|
48
|
+
style={"description_width": "120px"},
|
|
49
|
+
layout={"width": "50%"},
|
|
50
|
+
)
|
|
51
|
+
self.drop_down.observe(self.search, names="value")
|
|
52
|
+
|
|
53
|
+
# Disable process labels selection if we are not looking for the calculated
|
|
54
|
+
# structures.
|
|
55
|
+
def disable_drop_down(change):
|
|
56
|
+
self.drop_down.disabled = not change["new"] == "calculated"
|
|
57
|
+
|
|
58
|
+
# Select structures kind.
|
|
59
|
+
self.mode = ipw.RadioButtons(
|
|
60
|
+
options=["all", "uploaded", "calculated"], layout={"width": "25%"}
|
|
61
|
+
)
|
|
62
|
+
self.mode.observe(self.search, names="value")
|
|
63
|
+
self.mode.observe(disable_drop_down, names="value")
|
|
64
|
+
|
|
65
|
+
# Date range.
|
|
66
|
+
# Note: there is Date picker widget, but it currently does not work in Safari:
|
|
67
|
+
# https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html#Date-picker
|
|
68
|
+
date_text = ipw.HTML(value="<p>Select the date range:</p>")
|
|
69
|
+
self.start_date_widget = ipw.Text(
|
|
70
|
+
value="", description="From: ", style={"description_width": "120px"}
|
|
71
|
+
)
|
|
72
|
+
self.end_date_widget = ipw.Text(value="", description="To: ")
|
|
73
|
+
|
|
74
|
+
# Search button.
|
|
75
|
+
btn_search = ipw.Button(
|
|
76
|
+
description="Search",
|
|
77
|
+
button_style="info",
|
|
78
|
+
layout={"width": "initial", "margin": "2px 0 0 2em"},
|
|
79
|
+
)
|
|
80
|
+
btn_search.on_click(self.search)
|
|
81
|
+
|
|
82
|
+
age_selection = ipw.VBox(
|
|
83
|
+
[
|
|
84
|
+
date_text,
|
|
85
|
+
ipw.HBox([self.start_date_widget, self.end_date_widget, btn_search]),
|
|
86
|
+
],
|
|
87
|
+
layout={"border": "1px solid #fafafa", "padding": "1em"},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
h_line = ipw.HTML("<hr>")
|
|
91
|
+
box = ipw.VBox([age_selection, h_line, ipw.HBox([self.mode, self.drop_down])])
|
|
92
|
+
|
|
93
|
+
self.results = ipw.Dropdown(layout={"width": "900px"})
|
|
94
|
+
self.results.observe(self._on_select_node, names="value")
|
|
95
|
+
self.search()
|
|
96
|
+
super().__init__([box, h_line, self.results])
|
|
97
|
+
|
|
98
|
+
def search(self, _=None) -> None:
|
|
99
|
+
"""Search structures in the AiiDA database."""
|
|
100
|
+
qbuild = QueryBuilder()
|
|
101
|
+
|
|
102
|
+
# If the date range is valid, use it for the search
|
|
103
|
+
try:
|
|
104
|
+
start_date = datetime.datetime.strptime(
|
|
105
|
+
self.start_date_widget.value, "%Y-%m-%d"
|
|
106
|
+
)
|
|
107
|
+
end_date = datetime.datetime.strptime(
|
|
108
|
+
self.end_date_widget.value, "%Y-%m-%d"
|
|
109
|
+
) + datetime.timedelta(hours=24)
|
|
110
|
+
|
|
111
|
+
# Otherwise revert to the standard (i.e. last 7 days)
|
|
112
|
+
except ValueError:
|
|
113
|
+
start_date = datetime.datetime.now() - datetime.timedelta(days=7)
|
|
114
|
+
end_date = datetime.datetime.now() + datetime.timedelta(hours=24)
|
|
115
|
+
|
|
116
|
+
self.start_date_widget.value = start_date.strftime("%Y-%m-%d")
|
|
117
|
+
self.end_date_widget.value = end_date.strftime("%Y-%m-%d")
|
|
118
|
+
|
|
119
|
+
filters = {}
|
|
120
|
+
filters["ctime"] = {"and": [{">": start_date}, {"<=": end_date}]}
|
|
121
|
+
|
|
122
|
+
if self.mode.value == "uploaded":
|
|
123
|
+
qbuild2 = (
|
|
124
|
+
QueryBuilder()
|
|
125
|
+
.append(self.query_type, project=["id"], tag="structures")
|
|
126
|
+
.append(Node, with_outgoing="structures")
|
|
127
|
+
)
|
|
128
|
+
processed_nodes = [n[0] for n in qbuild2.all()]
|
|
129
|
+
if processed_nodes:
|
|
130
|
+
filters["id"] = {"!in": processed_nodes}
|
|
131
|
+
qbuild.append(self.query_type, filters=filters)
|
|
132
|
+
|
|
133
|
+
elif self.mode.value == "calculated":
|
|
134
|
+
if self.drop_down.value == "All":
|
|
135
|
+
qbuild.append((CalcJobNode, WorkChainNode), tag="calcjobworkchain")
|
|
136
|
+
else:
|
|
137
|
+
qbuild.append(
|
|
138
|
+
(CalcJobNode, WorkChainNode),
|
|
139
|
+
filters={"label": self.drop_down.value},
|
|
140
|
+
tag="calcjobworkchain",
|
|
141
|
+
)
|
|
142
|
+
qbuild.append(
|
|
143
|
+
self.query_type,
|
|
144
|
+
with_incoming="calcjobworkchain",
|
|
145
|
+
filters=filters,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
elif self.mode.value == "edited":
|
|
149
|
+
qbuild.append(CalcFunctionNode)
|
|
150
|
+
qbuild.append(
|
|
151
|
+
self.query_type,
|
|
152
|
+
with_incoming=CalcFunctionNode,
|
|
153
|
+
filters=filters,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
elif self.mode.value == "all":
|
|
157
|
+
qbuild.append(self.query_type, filters=filters)
|
|
158
|
+
|
|
159
|
+
qbuild.order_by({self.query_type: {"ctime": "desc"}})
|
|
160
|
+
matches = {n[0] for n in qbuild.iterall()}
|
|
161
|
+
matches = sorted(matches, reverse=True, key=lambda n: n.ctime)
|
|
162
|
+
|
|
163
|
+
options = [(f"Select a Node ({len(matches)} found)", False)]
|
|
164
|
+
for mch in matches:
|
|
165
|
+
label = f"PK: {mch.pk}"
|
|
166
|
+
label += " | " + mch.ctime.strftime("%Y-%m-%d %H:%M")
|
|
167
|
+
label += " | " + mch.base.extras.get("formula", "")
|
|
168
|
+
label += " | " + mch.node_type.split(".")[-2]
|
|
169
|
+
label += " | " + mch.label
|
|
170
|
+
label += " | " + mch.description
|
|
171
|
+
options.append((label, mch))
|
|
172
|
+
|
|
173
|
+
self.results.options = options
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
def _on_select_node(self, _) -> None:
|
|
177
|
+
"""Update the selected node."""
|
|
178
|
+
self.data_object = self.results.value or None
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
def disable(self, val: bool) -> None:
|
|
182
|
+
"""Disable the widget."""
|
|
183
|
+
self.results.disabled = True
|
|
184
|
+
# self.
|
|
185
|
+
return
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Module for providing widgets 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
|
+
try:
|
|
55
|
+
self.file_dict = self.file_upload.value[
|
|
56
|
+
list(self.file_upload.value.keys())[0]
|
|
57
|
+
]
|
|
58
|
+
self.file_handle.value = self.file_dict["metadata"]["name"]
|
|
59
|
+
except AttributeError:
|
|
60
|
+
self.file_dict = self.file_upload.value[0]
|
|
61
|
+
self.file_handle.value = self.file_dict["name"]
|
|
62
|
+
finally:
|
|
63
|
+
self.file = self.get_aiida_file_object()
|
|
64
|
+
else:
|
|
65
|
+
self.file_handle.value = ""
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
def get_file_contents(self) -> BytesIO | None:
|
|
69
|
+
"""Get the contents of the uploaded file as a BytesIO object."""
|
|
70
|
+
if self.file_dict is not None:
|
|
71
|
+
return BytesIO(self.file_dict["content"])
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
def filename(self) -> str:
|
|
75
|
+
"""Get the name of the uploaded file."""
|
|
76
|
+
if self.file_dict is not None:
|
|
77
|
+
try:
|
|
78
|
+
return self.file_dict["metadata"]["name"]
|
|
79
|
+
except KeyError:
|
|
80
|
+
return self.file_dict["name"]
|
|
81
|
+
return ""
|
|
82
|
+
|
|
83
|
+
def get_aiida_file_object(self):
|
|
84
|
+
"""Get the uploaded file as an AiiDA SinglefileData object."""
|
|
85
|
+
if self.file_dict is not None:
|
|
86
|
+
return SinglefileData(
|
|
87
|
+
file=self.get_file_contents(),
|
|
88
|
+
filename=self.filename(),
|
|
89
|
+
label=self.filename(),
|
|
90
|
+
description=self.file_handle.description,
|
|
91
|
+
)
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
def disable(self, val: bool) -> None:
|
|
95
|
+
"""Disable the file upload widget."""
|
|
96
|
+
self.file_upload.disabled = val
|
|
97
|
+
return
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Temporary widgets used to fill wait times."""
|
|
2
|
+
|
|
3
|
+
from ipywidgets import HTML, HBox, Label, Layout
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LoadingWidget(HBox):
|
|
7
|
+
"""Widget for displaying a loading spinner."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message="Loading", **kwargs):
|
|
10
|
+
"""LoadingWidget constructor."""
|
|
11
|
+
super().__init__(
|
|
12
|
+
children=[
|
|
13
|
+
Label(message),
|
|
14
|
+
HTML(
|
|
15
|
+
value="<i class='fa fa-spinner fa-spin fa-2x fa-fw'/>",
|
|
16
|
+
layout=Layout(margin="12px 0 6px"),
|
|
17
|
+
),
|
|
18
|
+
],
|
|
19
|
+
layout=Layout(
|
|
20
|
+
justify_content="center",
|
|
21
|
+
align_items="center",
|
|
22
|
+
**kwargs.pop("layout", {}),
|
|
23
|
+
),
|
|
24
|
+
**kwargs,
|
|
25
|
+
)
|
|
26
|
+
self.add_class("loading")
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Defines a widget for visualisation of chemical structures."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from tempfile import NamedTemporaryFile
|
|
5
|
+
|
|
6
|
+
from aiida.orm import StructureData
|
|
7
|
+
from ase import Atoms
|
|
8
|
+
from ase import io as ase_io
|
|
9
|
+
from ipywidgets import HTML, VBox
|
|
10
|
+
from weas_widget import WeasWidget
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class StructureViewWidget(VBox):
|
|
14
|
+
"""Visualise atom structure using weas_widget."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, **kwargs):
|
|
17
|
+
"""StructureViewWidget constructor."""
|
|
18
|
+
super().__init__(**kwargs)
|
|
19
|
+
self.message = HTML("<p>No Structure Currently Loaded</p>")
|
|
20
|
+
self.viewer = None
|
|
21
|
+
self.children = [
|
|
22
|
+
self.message,
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
def assign_structure_from_file(self, fname: str, content: bytes) -> None:
|
|
26
|
+
"""Visualise the given structure from a file.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
fname: str
|
|
31
|
+
The structure file path.
|
|
32
|
+
content: bytes
|
|
33
|
+
The content of the file.
|
|
34
|
+
"""
|
|
35
|
+
suffix = "".join(Path(fname).suffixes)
|
|
36
|
+
with NamedTemporaryFile(suffix=suffix) as tmpf:
|
|
37
|
+
tmpf.write(content)
|
|
38
|
+
tmpf.flush()
|
|
39
|
+
try:
|
|
40
|
+
structure = ase_io.read(tmpf.name, index=":")[0]
|
|
41
|
+
except (KeyError, ase_io.formats.UnknownFileTypeError):
|
|
42
|
+
self.message = HTML("<p>Could not visualise structure...</p>")
|
|
43
|
+
self.children = [
|
|
44
|
+
self.message,
|
|
45
|
+
]
|
|
46
|
+
except Exception as e:
|
|
47
|
+
raise e
|
|
48
|
+
else:
|
|
49
|
+
self.viewer = WeasWidget()
|
|
50
|
+
self.viewer.from_ase(structure)
|
|
51
|
+
self.children = [
|
|
52
|
+
self.viewer,
|
|
53
|
+
]
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
def assign_structure_from_ase(self, structure: Atoms) -> None:
|
|
57
|
+
"""Visualise the given ASE structure.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
structure: Atoms
|
|
62
|
+
The ASE atoms structure object.
|
|
63
|
+
"""
|
|
64
|
+
self.viewer = WeasWidget()
|
|
65
|
+
self.viewer.from_ase(structure)
|
|
66
|
+
self.children = [
|
|
67
|
+
self.viewer,
|
|
68
|
+
]
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
def assign_structure_from_structuredata(self, structure: StructureData) -> None:
|
|
72
|
+
"""Visualise the given AiiDA StructureData object.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
structure: StructureData
|
|
77
|
+
The AiiDA StructureData object.
|
|
78
|
+
"""
|
|
79
|
+
self.assign_structure_from_ase(structure._get_object_ase())
|
|
80
|
+
return
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Widgets used for displaying tabular information."""
|
|
2
|
+
|
|
3
|
+
from aiida.orm import ArrayData
|
|
4
|
+
from ipywidgets import HTML, Dropdown, VBox
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class XYZArrayDataTableWidget(VBox):
|
|
8
|
+
"""
|
|
9
|
+
Custom widget to display array data produced from ChemShell jobs.
|
|
10
|
+
|
|
11
|
+
Create a table based widget for displaying different arrays within
|
|
12
|
+
an ArrayData object assuming that all the data is XYZ based i.e
|
|
13
|
+
atomic positions of forces.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, array: ArrayData, **kwargs):
|
|
17
|
+
"""AiidaArrayDataViewWidget Constructor.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
array : ArrayData
|
|
22
|
+
The AiiDA ArrayData object to display.
|
|
23
|
+
"""
|
|
24
|
+
super().__init__(**kwargs)
|
|
25
|
+
self.array = array
|
|
26
|
+
self.array_names = array.get_arraynames()
|
|
27
|
+
|
|
28
|
+
self.array_selector = Dropdown(
|
|
29
|
+
options=self.array_names,
|
|
30
|
+
description="Array Label:",
|
|
31
|
+
disabled=False,
|
|
32
|
+
layout={"width": "30%"},
|
|
33
|
+
)
|
|
34
|
+
self._render_array({"new": self.array_selector.index, "old": -1})
|
|
35
|
+
self.array_selector.observe(self._render_array, "index")
|
|
36
|
+
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
def _render_array(self, change) -> None:
|
|
40
|
+
"""Create a HTML table based on the currently selected array."""
|
|
41
|
+
index = change["new"]
|
|
42
|
+
if index == change["old"]:
|
|
43
|
+
return
|
|
44
|
+
values = self.array.get_array(self.array_names[index])
|
|
45
|
+
# Construct HTML Table
|
|
46
|
+
html = "<table style='width:100%; border: 1px solid #ddd; text-align: left; "
|
|
47
|
+
html += "border-collapse: collapse;'>"
|
|
48
|
+
html += "<tr style='background-color: #2196F3; color: white;'>"
|
|
49
|
+
html += "<th> </th><th>X</th><th>Y</th><th>Z</th></tr>"
|
|
50
|
+
|
|
51
|
+
for idx, row in enumerate(values):
|
|
52
|
+
bg_color = "#f9f9f9" if idx % 2 == 0 else "#ffffff"
|
|
53
|
+
html += f"<tr style='background-color: {bg_color};'>"
|
|
54
|
+
html += f"<td><b>{idx}</b></td><td>{row[0]:.6f}</td><td>{row[1]:.6f}</td>"
|
|
55
|
+
html += f"<td>{row[2]:.6f}</td>"
|
|
56
|
+
html += "</tr>"
|
|
57
|
+
html += "</table>"
|
|
58
|
+
|
|
59
|
+
self.children = [self.array_selector, HTML(html)]
|
|
60
|
+
return
|