shellsmith 0.1.0__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.
- shellsmith-0.1.0/LICENSE +21 -0
- shellsmith-0.1.0/PKG-INFO +215 -0
- shellsmith-0.1.0/README.md +170 -0
- shellsmith-0.1.0/pyproject.toml +37 -0
- shellsmith-0.1.0/setup.cfg +4 -0
- shellsmith-0.1.0/src/shellsmith/__init__.py +18 -0
- shellsmith-0.1.0/src/shellsmith/__main__.py +4 -0
- shellsmith-0.1.0/src/shellsmith/cli/__init__.py +0 -0
- shellsmith-0.1.0/src/shellsmith/cli/commands/__init__.py +5 -0
- shellsmith-0.1.0/src/shellsmith/cli/commands/info.py +47 -0
- shellsmith-0.1.0/src/shellsmith/cli/commands/nuke.py +9 -0
- shellsmith-0.1.0/src/shellsmith/cli/commands/shell.py +17 -0
- shellsmith-0.1.0/src/shellsmith/cli/commands/submodel.py +16 -0
- shellsmith-0.1.0/src/shellsmith/cli/commands/upload.py +14 -0
- shellsmith-0.1.0/src/shellsmith/cli/main.py +44 -0
- shellsmith-0.1.0/src/shellsmith/cli/parser.py +63 -0
- shellsmith-0.1.0/src/shellsmith/config.py +15 -0
- shellsmith-0.1.0/src/shellsmith/crud/__init__.py +0 -0
- shellsmith-0.1.0/src/shellsmith/crud/shells.py +61 -0
- shellsmith-0.1.0/src/shellsmith/crud/submodels.py +126 -0
- shellsmith-0.1.0/src/shellsmith/services.py +131 -0
- shellsmith-0.1.0/src/shellsmith/upload.py +40 -0
- shellsmith-0.1.0/src/shellsmith/utils.py +35 -0
- shellsmith-0.1.0/src/shellsmith.egg-info/PKG-INFO +215 -0
- shellsmith-0.1.0/src/shellsmith.egg-info/SOURCES.txt +32 -0
- shellsmith-0.1.0/src/shellsmith.egg-info/dependency_links.txt +1 -0
- shellsmith-0.1.0/src/shellsmith.egg-info/entry_points.txt +2 -0
- shellsmith-0.1.0/src/shellsmith.egg-info/requires.txt +10 -0
- shellsmith-0.1.0/src/shellsmith.egg-info/top_level.txt +1 -0
- shellsmith-0.1.0/tests/test_cli.py +110 -0
- shellsmith-0.1.0/tests/test_cli_integration.py +59 -0
- shellsmith-0.1.0/tests/test_crud.py +123 -0
- shellsmith-0.1.0/tests/test_upload.py +12 -0
- shellsmith-0.1.0/tests/test_utils.py +27 -0
shellsmith-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Peter Stein
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shellsmith
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python toolkit and CLI for managing Asset Administration Shells
|
|
5
|
+
Author-email: Peter Stein <peterstein@dfki.de>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Peter Stein
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/ptrstn/shellsmith
|
|
29
|
+
Project-URL: Issues, https://github.com/ptrstn/shellsmith/issues
|
|
30
|
+
Classifier: Programming Language :: Python :: 3
|
|
31
|
+
Classifier: Operating System :: OS Independent
|
|
32
|
+
Requires-Python: >=3.10
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
License-File: LICENSE
|
|
35
|
+
Requires-Dist: pydantic-settings
|
|
36
|
+
Requires-Dist: requests
|
|
37
|
+
Provides-Extra: test
|
|
38
|
+
Requires-Dist: black; extra == "test"
|
|
39
|
+
Requires-Dist: flake8; extra == "test"
|
|
40
|
+
Requires-Dist: isort; extra == "test"
|
|
41
|
+
Requires-Dist: pytest; extra == "test"
|
|
42
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
43
|
+
Requires-Dist: pytest-dotenv; extra == "test"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
[](https://github.com/ptrstn/shellsmith/actions/workflows/python-package.yaml)
|
|
47
|
+
[](https://codecov.io/gh/ptrstn/shellsmith)
|
|
48
|
+
[](https://badge.fury.io/py/shellsmith)
|
|
49
|
+
[](https://github.com/psf/black)
|
|
50
|
+
|
|
51
|
+
# Shellsmith
|
|
52
|
+
|
|
53
|
+
Shellsmith is a Python toolkit and CLI for managing Asset Administration Shells (AAS), Submodels, and related resources.
|
|
54
|
+
It is designed to interact with [Eclipse BaSyx](https://www.eclipse.org/basyx/), a middleware platform for AAS that follows the [Industry 4.0 standard](https://industrialdigitaltwin.org/en/content-hub/aasspecifications).
|
|
55
|
+
|
|
56
|
+
### Features
|
|
57
|
+
|
|
58
|
+
- Python API for CRUD operations on shells, submodels, and submodel elements
|
|
59
|
+
- CLI interface for quick scripting
|
|
60
|
+
- `.env`-based configuration
|
|
61
|
+
|
|
62
|
+
## 🚀 Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install shellsmith
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Requires**: Python 3.10+
|
|
69
|
+
|
|
70
|
+
## 🔧 Configuration
|
|
71
|
+
|
|
72
|
+
The default AAS environment host is:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
http://localhost:8081
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You can override it by setting the `SHELLSMITH_BASYX_ENV_HOST` environment variable, or by creating a `.env` file in your project root with:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
SHELLSMITH_BASYX_ENV_HOST=http://your-host:1234
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 🛠️ Usage
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
aas --help
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Common commands:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
aas info # Show all shells and submodels
|
|
94
|
+
aas upload <file|folder> # Upload AAS file or folder
|
|
95
|
+
aas shell delete <id> # Delete a shell
|
|
96
|
+
aas submodel delete <id> # Delete a submodel
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use `--cascade` or `--unlink` to control deletion behavior:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
aas shell delete <id> --cascade # Also delete referenced submodels
|
|
103
|
+
aas submodel delete <id> --unlink # Remove references from shells
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 📡 API Usage
|
|
107
|
+
|
|
108
|
+
You can also use `shellsmith` as a Python package:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
import shellsmith
|
|
112
|
+
|
|
113
|
+
# Get all available shells
|
|
114
|
+
shells = shellsmith.get_shells()
|
|
115
|
+
|
|
116
|
+
# Get a specific shell by ID (automatically base64-encoded)
|
|
117
|
+
shell = shellsmith.get_shell("example_aas_id")
|
|
118
|
+
|
|
119
|
+
# Disable base64 encoding if your ID is already encoded
|
|
120
|
+
submodel = shellsmith.get_submodel("ZXhhbXBsZV9hYXNfaWQ=", encode=False)
|
|
121
|
+
|
|
122
|
+
# Use a custom AAS environment host
|
|
123
|
+
submodel_refs = shellsmith.get_submodel_refs("example_aas_id", host="http://localhost:8081")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
> ℹ️ `shell_id` and `submodel_id` are automatically base64-encoded unless you set `encode=False`. This is required by the BaSyx API for identifier-based URLs.
|
|
127
|
+
|
|
128
|
+
The tables below show the mapping between BaSyx AAS REST API endpoints and the implemented client functions.
|
|
129
|
+
|
|
130
|
+
> 📚 See [Plattform_i40 API reference](https://app.swaggerhub.com/apis/Plattform_i40/Entire-API-Collection) for endpoint details.
|
|
131
|
+
|
|
132
|
+
### Shells
|
|
133
|
+
|
|
134
|
+
| Method | BaSyx Endpoint | Shellsmith Function |
|
|
135
|
+
|--------|--------------------------------------------------------------|-----------------------|
|
|
136
|
+
| GET | `/shells` | `get_shells` |
|
|
137
|
+
| POST | `/shells` | ❌ |
|
|
138
|
+
| GET | `/shells/{aasIdentifier}` | `get_shell` |
|
|
139
|
+
| PUT | `/shells/{aasIdentifier}` | ❌ |
|
|
140
|
+
| DELETE | `/shells/{aasIdentifier}` | `delete_shell` |
|
|
141
|
+
| GET | `/shells/{aasIdentifier}/submodel-refs` | `get_submodel_refs` |
|
|
142
|
+
| POST | `/shells/{aasIdentifier}/submodel-refs` | ❌ |
|
|
143
|
+
| DELETE | `/shells/{aasIdentifier}/submodel-refs/{submodelIdentifier}` | `delete_submodel_ref` |
|
|
144
|
+
|
|
145
|
+
### Submodels
|
|
146
|
+
|
|
147
|
+
| Method | BaSyx Endpoint | Shellsmith Function |
|
|
148
|
+
|--------|---------------------------------------------|---------------------|
|
|
149
|
+
| GET | `/submodels` | `get_submodels` |
|
|
150
|
+
| POST | `/submodels` | ❌ |
|
|
151
|
+
| GET | `/submodels/{submodelIdentifier}` | `get_submodel` |
|
|
152
|
+
| PUT | `/submodels/{submodelIdentifier}` | ❌ |
|
|
153
|
+
| DELETE | `/submodels/{submodelIdentifier}` | `delete_submodel` |
|
|
154
|
+
| GET | `/submodels/{submodelIdentifier}/$value` | ❌ |
|
|
155
|
+
| PATCH | `/submodels/{submodelIdentifier}/$value` | ❌ |
|
|
156
|
+
| GET | `/submodels/{submodelIdentifier}/$metadata` | ❌ |
|
|
157
|
+
|
|
158
|
+
### Submodel Elements
|
|
159
|
+
|
|
160
|
+
| Method | BaSyx Endpoint | Shellsmith Function |
|
|
161
|
+
|--------|--------------------------------------------------------------------------|--------------------------------|
|
|
162
|
+
| GET | `/submodels/{submodelIdentifier}/submodel-elements` | `get_submodel_elements` |
|
|
163
|
+
| POST | `/submodels/{submodelIdentifier}/submodel-elements` | ❌ |
|
|
164
|
+
| GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `get_submodel_element` |
|
|
165
|
+
| PUT | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | ❌ |
|
|
166
|
+
| POST | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | ❌ |
|
|
167
|
+
| DELETE | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `delete_submodel_element` |
|
|
168
|
+
| GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | ❌ |
|
|
169
|
+
| PATCH | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | `patch_submodel_element_value` |
|
|
170
|
+
|
|
171
|
+
### Upload
|
|
172
|
+
|
|
173
|
+
| Method | BaSyx Endpoint | Shellsmith Function |
|
|
174
|
+
|--------|----------------|-----------------------------------------------------|
|
|
175
|
+
| POST | `/upload` | `upload.upload_aas` <br> `upload.upload_aas_folder` |
|
|
176
|
+
|
|
177
|
+
> ℹ️ Upload functions are available under the `shellsmith.upload` submodule.
|
|
178
|
+
|
|
179
|
+
## ⚙️ Development
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
git clone https://github.com/ptrstn/shellsmith
|
|
183
|
+
cd shellsmith
|
|
184
|
+
python -m venv .venv
|
|
185
|
+
source .venv/bin/activate # or .venv\Scripts\activate on Windows
|
|
186
|
+
pip install -e .[test]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### ✅ Testing
|
|
190
|
+
|
|
191
|
+
Before running the tests, make sure the BaSyx stack is up and running:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
docker compose up -d
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Then run the test suite with coverage:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
pytest --cov
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
To view a detailed, visual coverage report:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
pytest --cov --cov-report=html
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Then open `htmlcov/index.html` in your web browser to explore which lines are covered and which are missing.
|
|
210
|
+
|
|
211
|
+
## Resources
|
|
212
|
+
|
|
213
|
+
- https://github.com/eclipse-basyx/basyx-java-server-sdk
|
|
214
|
+
- https://github.com/admin-shell-io/aas-specs-api
|
|
215
|
+
- https://app.swaggerhub.com/apis/Plattform_i40/Entire-API-Collection
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
[](https://github.com/ptrstn/shellsmith/actions/workflows/python-package.yaml)
|
|
2
|
+
[](https://codecov.io/gh/ptrstn/shellsmith)
|
|
3
|
+
[](https://badge.fury.io/py/shellsmith)
|
|
4
|
+
[](https://github.com/psf/black)
|
|
5
|
+
|
|
6
|
+
# Shellsmith
|
|
7
|
+
|
|
8
|
+
Shellsmith is a Python toolkit and CLI for managing Asset Administration Shells (AAS), Submodels, and related resources.
|
|
9
|
+
It is designed to interact with [Eclipse BaSyx](https://www.eclipse.org/basyx/), a middleware platform for AAS that follows the [Industry 4.0 standard](https://industrialdigitaltwin.org/en/content-hub/aasspecifications).
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- Python API for CRUD operations on shells, submodels, and submodel elements
|
|
14
|
+
- CLI interface for quick scripting
|
|
15
|
+
- `.env`-based configuration
|
|
16
|
+
|
|
17
|
+
## 🚀 Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install shellsmith
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Requires**: Python 3.10+
|
|
24
|
+
|
|
25
|
+
## 🔧 Configuration
|
|
26
|
+
|
|
27
|
+
The default AAS environment host is:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
http://localhost:8081
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
You can override it by setting the `SHELLSMITH_BASYX_ENV_HOST` environment variable, or by creating a `.env` file in your project root with:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
SHELLSMITH_BASYX_ENV_HOST=http://your-host:1234
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 🛠️ Usage
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
aas --help
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Common commands:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
aas info # Show all shells and submodels
|
|
49
|
+
aas upload <file|folder> # Upload AAS file or folder
|
|
50
|
+
aas shell delete <id> # Delete a shell
|
|
51
|
+
aas submodel delete <id> # Delete a submodel
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use `--cascade` or `--unlink` to control deletion behavior:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
aas shell delete <id> --cascade # Also delete referenced submodels
|
|
58
|
+
aas submodel delete <id> --unlink # Remove references from shells
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 📡 API Usage
|
|
62
|
+
|
|
63
|
+
You can also use `shellsmith` as a Python package:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import shellsmith
|
|
67
|
+
|
|
68
|
+
# Get all available shells
|
|
69
|
+
shells = shellsmith.get_shells()
|
|
70
|
+
|
|
71
|
+
# Get a specific shell by ID (automatically base64-encoded)
|
|
72
|
+
shell = shellsmith.get_shell("example_aas_id")
|
|
73
|
+
|
|
74
|
+
# Disable base64 encoding if your ID is already encoded
|
|
75
|
+
submodel = shellsmith.get_submodel("ZXhhbXBsZV9hYXNfaWQ=", encode=False)
|
|
76
|
+
|
|
77
|
+
# Use a custom AAS environment host
|
|
78
|
+
submodel_refs = shellsmith.get_submodel_refs("example_aas_id", host="http://localhost:8081")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> ℹ️ `shell_id` and `submodel_id` are automatically base64-encoded unless you set `encode=False`. This is required by the BaSyx API for identifier-based URLs.
|
|
82
|
+
|
|
83
|
+
The tables below show the mapping between BaSyx AAS REST API endpoints and the implemented client functions.
|
|
84
|
+
|
|
85
|
+
> 📚 See [Plattform_i40 API reference](https://app.swaggerhub.com/apis/Plattform_i40/Entire-API-Collection) for endpoint details.
|
|
86
|
+
|
|
87
|
+
### Shells
|
|
88
|
+
|
|
89
|
+
| Method | BaSyx Endpoint | Shellsmith Function |
|
|
90
|
+
|--------|--------------------------------------------------------------|-----------------------|
|
|
91
|
+
| GET | `/shells` | `get_shells` |
|
|
92
|
+
| POST | `/shells` | ❌ |
|
|
93
|
+
| GET | `/shells/{aasIdentifier}` | `get_shell` |
|
|
94
|
+
| PUT | `/shells/{aasIdentifier}` | ❌ |
|
|
95
|
+
| DELETE | `/shells/{aasIdentifier}` | `delete_shell` |
|
|
96
|
+
| GET | `/shells/{aasIdentifier}/submodel-refs` | `get_submodel_refs` |
|
|
97
|
+
| POST | `/shells/{aasIdentifier}/submodel-refs` | ❌ |
|
|
98
|
+
| DELETE | `/shells/{aasIdentifier}/submodel-refs/{submodelIdentifier}` | `delete_submodel_ref` |
|
|
99
|
+
|
|
100
|
+
### Submodels
|
|
101
|
+
|
|
102
|
+
| Method | BaSyx Endpoint | Shellsmith Function |
|
|
103
|
+
|--------|---------------------------------------------|---------------------|
|
|
104
|
+
| GET | `/submodels` | `get_submodels` |
|
|
105
|
+
| POST | `/submodels` | ❌ |
|
|
106
|
+
| GET | `/submodels/{submodelIdentifier}` | `get_submodel` |
|
|
107
|
+
| PUT | `/submodels/{submodelIdentifier}` | ❌ |
|
|
108
|
+
| DELETE | `/submodels/{submodelIdentifier}` | `delete_submodel` |
|
|
109
|
+
| GET | `/submodels/{submodelIdentifier}/$value` | ❌ |
|
|
110
|
+
| PATCH | `/submodels/{submodelIdentifier}/$value` | ❌ |
|
|
111
|
+
| GET | `/submodels/{submodelIdentifier}/$metadata` | ❌ |
|
|
112
|
+
|
|
113
|
+
### Submodel Elements
|
|
114
|
+
|
|
115
|
+
| Method | BaSyx Endpoint | Shellsmith Function |
|
|
116
|
+
|--------|--------------------------------------------------------------------------|--------------------------------|
|
|
117
|
+
| GET | `/submodels/{submodelIdentifier}/submodel-elements` | `get_submodel_elements` |
|
|
118
|
+
| POST | `/submodels/{submodelIdentifier}/submodel-elements` | ❌ |
|
|
119
|
+
| GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `get_submodel_element` |
|
|
120
|
+
| PUT | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | ❌ |
|
|
121
|
+
| POST | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | ❌ |
|
|
122
|
+
| DELETE | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}` | `delete_submodel_element` |
|
|
123
|
+
| GET | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | ❌ |
|
|
124
|
+
| PATCH | `/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/$value` | `patch_submodel_element_value` |
|
|
125
|
+
|
|
126
|
+
### Upload
|
|
127
|
+
|
|
128
|
+
| Method | BaSyx Endpoint | Shellsmith Function |
|
|
129
|
+
|--------|----------------|-----------------------------------------------------|
|
|
130
|
+
| POST | `/upload` | `upload.upload_aas` <br> `upload.upload_aas_folder` |
|
|
131
|
+
|
|
132
|
+
> ℹ️ Upload functions are available under the `shellsmith.upload` submodule.
|
|
133
|
+
|
|
134
|
+
## ⚙️ Development
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
git clone https://github.com/ptrstn/shellsmith
|
|
138
|
+
cd shellsmith
|
|
139
|
+
python -m venv .venv
|
|
140
|
+
source .venv/bin/activate # or .venv\Scripts\activate on Windows
|
|
141
|
+
pip install -e .[test]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### ✅ Testing
|
|
145
|
+
|
|
146
|
+
Before running the tests, make sure the BaSyx stack is up and running:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
docker compose up -d
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Then run the test suite with coverage:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
pytest --cov
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
To view a detailed, visual coverage report:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
pytest --cov --cov-report=html
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Then open `htmlcov/index.html` in your web browser to explore which lines are covered and which are missing.
|
|
165
|
+
|
|
166
|
+
## Resources
|
|
167
|
+
|
|
168
|
+
- https://github.com/eclipse-basyx/basyx-java-server-sdk
|
|
169
|
+
- https://github.com/admin-shell-io/aas-specs-api
|
|
170
|
+
- https://app.swaggerhub.com/apis/Plattform_i40/Entire-API-Collection
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "shellsmith"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A Python toolkit and CLI for managing Asset Administration Shells"
|
|
9
|
+
authors = [{ name = "Peter Stein", email = "peterstein@dfki.de" }]
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"pydantic-settings",
|
|
19
|
+
"requests",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
test = [
|
|
24
|
+
"black",
|
|
25
|
+
"flake8",
|
|
26
|
+
"isort",
|
|
27
|
+
"pytest",
|
|
28
|
+
"pytest-cov",
|
|
29
|
+
"pytest-dotenv",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
aas = "shellsmith.cli.main:main"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/ptrstn/shellsmith"
|
|
37
|
+
Issues = "https://github.com/ptrstn/shellsmith/issues"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
2
|
+
|
|
3
|
+
from .crud.shells import ( # noqa: F401
|
|
4
|
+
delete_shell,
|
|
5
|
+
delete_submodel_ref,
|
|
6
|
+
get_shell,
|
|
7
|
+
get_shells,
|
|
8
|
+
get_submodel_refs,
|
|
9
|
+
)
|
|
10
|
+
from .crud.submodels import ( # noqa: F401
|
|
11
|
+
delete_submodel,
|
|
12
|
+
delete_submodel_element,
|
|
13
|
+
get_submodel,
|
|
14
|
+
get_submodel_element,
|
|
15
|
+
get_submodel_elements,
|
|
16
|
+
get_submodels,
|
|
17
|
+
patch_submodel_element_value,
|
|
18
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import shellsmith
|
|
2
|
+
from shellsmith import services
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def info():
|
|
6
|
+
print("ℹ️ Showing all shells")
|
|
7
|
+
print_shells_tree()
|
|
8
|
+
print_unreferenced_submodels()
|
|
9
|
+
print_dangling_submodel_refs()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def print_unreferenced_submodels():
|
|
13
|
+
submodel_ids = services.find_unreferenced_submodels()
|
|
14
|
+
|
|
15
|
+
if submodel_ids:
|
|
16
|
+
print()
|
|
17
|
+
print("⚠️ Unreferenced Submodels:")
|
|
18
|
+
for submodel_id in submodel_ids:
|
|
19
|
+
submodel = shellsmith.get_submodel(submodel_id)
|
|
20
|
+
id_short = submodel["idShort"]
|
|
21
|
+
print(f"- {id_short} ({submodel_id})")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def print_shells_tree():
|
|
25
|
+
shells = shellsmith.get_shells()
|
|
26
|
+
for shell in shells:
|
|
27
|
+
print(f"{shell['idShort']}: {shell['id']}")
|
|
28
|
+
|
|
29
|
+
submodels = services.get_shell_submodels(shell["id"])
|
|
30
|
+
|
|
31
|
+
for i, submodel in enumerate(submodels):
|
|
32
|
+
is_last = i == len(submodels) - 1
|
|
33
|
+
prefix = "└──" if is_last else "├──"
|
|
34
|
+
print(f"{prefix} {submodel['idShort']}: {submodel['id']}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def print_dangling_submodel_refs():
|
|
38
|
+
dangling = services.find_dangling_submodel_refs()
|
|
39
|
+
|
|
40
|
+
if dangling:
|
|
41
|
+
print()
|
|
42
|
+
print("⚠️ Dangling Submodel References:")
|
|
43
|
+
for shell_id, submodel_ids in dangling.items():
|
|
44
|
+
shell = shellsmith.get_shell(shell_id)
|
|
45
|
+
print(f"- {shell['idShort']}: {shell_id}")
|
|
46
|
+
for submodel_id in submodel_ids:
|
|
47
|
+
print(f" └── {submodel_id}")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import requests.exceptions
|
|
2
|
+
|
|
3
|
+
import shellsmith
|
|
4
|
+
from shellsmith import services
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def shell_delete(shell_id: str, cascade: bool = False):
|
|
8
|
+
print(f"🗑️ Deleting Shell: {shell_id}")
|
|
9
|
+
try:
|
|
10
|
+
if cascade:
|
|
11
|
+
services.delete_shell_cascading(shell_id)
|
|
12
|
+
print(f"✅ Shell '{shell_id}' and its submodels deleted.")
|
|
13
|
+
else:
|
|
14
|
+
shellsmith.delete_shell(shell_id)
|
|
15
|
+
print(f"✅ Shell '{shell_id}' deleted.")
|
|
16
|
+
except requests.exceptions.HTTPError as e:
|
|
17
|
+
print(f"❌ Failed to delete Shell '{shell_id}': {e}")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
import shellsmith
|
|
4
|
+
from shellsmith import services
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def submodel_delete(submodel_id: str, unlink: bool = False):
|
|
8
|
+
print(f"🗑️ Deleting Submodel: {submodel_id}")
|
|
9
|
+
try:
|
|
10
|
+
shellsmith.delete_submodel(submodel_id)
|
|
11
|
+
print(f"✅ Submodel '{submodel_id}' deleted.")
|
|
12
|
+
except requests.exceptions.HTTPError:
|
|
13
|
+
print(f"❌ Submodel '{submodel_id}' doesn't exist.")
|
|
14
|
+
if unlink:
|
|
15
|
+
services.remove_submodel_references(submodel_id)
|
|
16
|
+
print(f"✅ Removed Shell references to Submodel '{submodel_id}'.")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from shellsmith.upload import upload_aas, upload_aas_folder
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def upload(path: Path):
|
|
7
|
+
if path.is_file():
|
|
8
|
+
print(f"ℹ️ Uploading file: {path}")
|
|
9
|
+
upload_aas(path)
|
|
10
|
+
elif path.is_dir():
|
|
11
|
+
print(f"ℹ️ Uploading all AAS files in folder: {path}")
|
|
12
|
+
upload_aas_folder(path)
|
|
13
|
+
else:
|
|
14
|
+
print(f"❌ Path '{path}' does not exist or is invalid.")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from shellsmith import __version__, services
|
|
4
|
+
from shellsmith.config import config
|
|
5
|
+
|
|
6
|
+
from .commands import info, nuke, shell_delete, submodel_delete, upload
|
|
7
|
+
from .parser import build_parser
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def print_header():
|
|
11
|
+
print("===============================================")
|
|
12
|
+
print(f"Shellsmith - AAS Toolkit v{__version__}")
|
|
13
|
+
print(f"Host: {config.host} ({services.health()})")
|
|
14
|
+
print("===============================================")
|
|
15
|
+
print()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main():
|
|
19
|
+
print_header()
|
|
20
|
+
parser = build_parser()
|
|
21
|
+
args = parser.parse_args()
|
|
22
|
+
|
|
23
|
+
commands = {
|
|
24
|
+
"upload": lambda _args: upload(args.path),
|
|
25
|
+
"info": lambda _args: info(),
|
|
26
|
+
"nuke": lambda _args: nuke(),
|
|
27
|
+
"shell.delete": lambda _args: shell_delete(args.id, cascade=args.cascade),
|
|
28
|
+
"submodel.delete": lambda _args: submodel_delete(args.id, unlink=args.unlink),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
key = args.command
|
|
33
|
+
if key == "shell" and args.shell_command:
|
|
34
|
+
key += f".{args.shell_command}"
|
|
35
|
+
elif key == "submodel" and args.submodel_command:
|
|
36
|
+
key += f".{args.submodel_command}"
|
|
37
|
+
|
|
38
|
+
handler = commands.get(key)
|
|
39
|
+
if handler:
|
|
40
|
+
handler(args)
|
|
41
|
+
else:
|
|
42
|
+
parser.print_help()
|
|
43
|
+
except requests.exceptions.ConnectionError as e:
|
|
44
|
+
print(f"😩 Cannot reach {config.host}: {e}")
|