validibot-shared 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.
- validibot_shared-0.1.0/.gitignore +89 -0
- validibot_shared-0.1.0/LICENSE +21 -0
- validibot_shared-0.1.0/NOTICE +23 -0
- validibot_shared-0.1.0/PKG-INFO +168 -0
- validibot_shared-0.1.0/README.md +136 -0
- validibot_shared-0.1.0/pyproject.toml +82 -0
- validibot_shared-0.1.0/tests/__init__.py +0 -0
- validibot_shared-0.1.0/tests/test_energyplus_envelopes.py +146 -0
- validibot_shared-0.1.0/tests/test_energyplus_models.py +55 -0
- validibot_shared-0.1.0/tests/test_fmi_envelopes.py +107 -0
- validibot_shared-0.1.0/tests/test_fmi_models.py +29 -0
- validibot_shared-0.1.0/tests/test_package_init.py +31 -0
- validibot_shared-0.1.0/tests/test_validations_envelopes.py +144 -0
- validibot_shared-0.1.0/validibot_shared/__init__.py +16 -0
- validibot_shared-0.1.0/validibot_shared/energyplus/__init__.py +25 -0
- validibot_shared-0.1.0/validibot_shared/energyplus/envelopes.py +300 -0
- validibot_shared-0.1.0/validibot_shared/energyplus/models.py +140 -0
- validibot_shared-0.1.0/validibot_shared/fmi/__init__.py +6 -0
- validibot_shared-0.1.0/validibot_shared/fmi/envelopes.py +190 -0
- validibot_shared-0.1.0/validibot_shared/fmi/models.py +70 -0
- validibot_shared-0.1.0/validibot_shared/py.typed +0 -0
- validibot_shared-0.1.0/validibot_shared/validations/__init__.py +0 -0
- validibot_shared-0.1.0/validibot_shared/validations/envelopes.py +607 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
**/__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
*.manifest
|
|
29
|
+
*.spec
|
|
30
|
+
|
|
31
|
+
# Installer logs
|
|
32
|
+
pip-log.txt
|
|
33
|
+
pip-delete-this-directory.txt
|
|
34
|
+
|
|
35
|
+
# Unit test / coverage reports
|
|
36
|
+
htmlcov/
|
|
37
|
+
.tox/
|
|
38
|
+
.nox/
|
|
39
|
+
.coverage
|
|
40
|
+
.coverage.*
|
|
41
|
+
.cache
|
|
42
|
+
nosetests.xml
|
|
43
|
+
coverage.xml
|
|
44
|
+
*.cover
|
|
45
|
+
*.py,cover
|
|
46
|
+
.hypothesis/
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
|
|
49
|
+
# Translations
|
|
50
|
+
*.mo
|
|
51
|
+
*.pot
|
|
52
|
+
|
|
53
|
+
# Environments
|
|
54
|
+
.env
|
|
55
|
+
.venv
|
|
56
|
+
env/
|
|
57
|
+
venv/
|
|
58
|
+
ENV/
|
|
59
|
+
env.bak/
|
|
60
|
+
venv.bak/
|
|
61
|
+
|
|
62
|
+
# IDE
|
|
63
|
+
.idea/
|
|
64
|
+
.vscode/
|
|
65
|
+
*.swp
|
|
66
|
+
*.swo
|
|
67
|
+
*~
|
|
68
|
+
|
|
69
|
+
# mypy
|
|
70
|
+
.mypy_cache/
|
|
71
|
+
.dmypy.json
|
|
72
|
+
dmypy.json
|
|
73
|
+
|
|
74
|
+
# ruff
|
|
75
|
+
.ruff_cache/
|
|
76
|
+
|
|
77
|
+
# OS
|
|
78
|
+
.DS_Store
|
|
79
|
+
Thumbs.db
|
|
80
|
+
Desktop.ini
|
|
81
|
+
|
|
82
|
+
# Temporary files
|
|
83
|
+
tmp/
|
|
84
|
+
|
|
85
|
+
# Local config that may contain secrets
|
|
86
|
+
.envs/
|
|
87
|
+
|
|
88
|
+
# AI generated files
|
|
89
|
+
.claude
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 McQuillen Interactive Pty. Ltd.
|
|
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,23 @@
|
|
|
1
|
+
Validibot Shared Library
|
|
2
|
+
Copyright (c) 2024-2026 McQuillen Interactive Pty. Ltd.
|
|
3
|
+
|
|
4
|
+
This product is licensed under the MIT License.
|
|
5
|
+
See the LICENSE file for the full license text.
|
|
6
|
+
|
|
7
|
+
TRADEMARKS
|
|
8
|
+
|
|
9
|
+
"Validibot", the Validibot logo, and the Validibot robot character are
|
|
10
|
+
trademarks of McQuillen Interactive Pty. Ltd. The MIT license grants rights
|
|
11
|
+
to the source code only, not to the trademarks.
|
|
12
|
+
|
|
13
|
+
You may use the Validibot name to accurately describe that your software
|
|
14
|
+
uses or integrates with Validibot, but not in a way that suggests official
|
|
15
|
+
endorsement or affiliation beyond that relationship.
|
|
16
|
+
|
|
17
|
+
THIRD-PARTY COMPONENTS
|
|
18
|
+
|
|
19
|
+
This software includes the following third-party components:
|
|
20
|
+
|
|
21
|
+
- Pydantic (MIT License) - https://docs.pydantic.dev/
|
|
22
|
+
|
|
23
|
+
Each component is subject to its own license terms.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: validibot-shared
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Shared library for data interchange between Validibot and validator containers
|
|
5
|
+
Project-URL: Homepage, https://validibot.com
|
|
6
|
+
Project-URL: Documentation, https://docs.validibot.com
|
|
7
|
+
Project-URL: Repository, https://github.com/danielmcquillen/validibot-shared
|
|
8
|
+
Project-URL: Issues, https://github.com/danielmcquillen/validibot-shared/issues
|
|
9
|
+
Author-email: Daniel McQuillen <daniel@validibot.com>
|
|
10
|
+
Maintainer-email: Daniel McQuillen <daniel@validibot.com>
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
License-File: NOTICE
|
|
14
|
+
Keywords: data-interchange,energyplus,fmu,simulation,validation,validibot
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: pydantic>=2.8.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# validibot-shared
|
|
34
|
+
|
|
35
|
+
Shared Pydantic models for [Validibot](https://validibot.com) validator containers.
|
|
36
|
+
|
|
37
|
+
This library defines the data interchange types used between Validibot and its advanced validator services, ensuring type safety and contract consistency.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install validibot-shared
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or with uv:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv add validibot-shared
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Package Structure
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
validibot_shared/
|
|
55
|
+
├── validations/ # Base validation envelope schemas
|
|
56
|
+
│ └── envelopes.py # Input/output envelopes for all validators
|
|
57
|
+
├── energyplus/ # EnergyPlus-specific models and envelopes
|
|
58
|
+
│ ├── models.py # Simulation output models
|
|
59
|
+
│ └── envelopes.py # Typed envelope subclasses
|
|
60
|
+
└── fmi/ # FMI/FMU-specific models
|
|
61
|
+
├── models.py # Probe result models
|
|
62
|
+
└── envelopes.py # FMI envelope subclasses
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Core Concepts
|
|
66
|
+
|
|
67
|
+
### Validation Envelopes
|
|
68
|
+
|
|
69
|
+
The library provides base envelope classes for validator communication:
|
|
70
|
+
|
|
71
|
+
- `ValidationInputEnvelope` - Standard input format for validation jobs
|
|
72
|
+
- `ValidationOutputEnvelope` - Standard output format with results
|
|
73
|
+
- `ValidationCallback` - Callback payload for async job completion
|
|
74
|
+
|
|
75
|
+
Supporting classes include `InputFileItem`, `ValidatorInfo`, `ExecutionContext`, `ValidationMessage`, `ValidationMetric`, and `ValidationArtifact`.
|
|
76
|
+
|
|
77
|
+
### Typed Subclassing Pattern
|
|
78
|
+
|
|
79
|
+
Domain-specific validators extend the base envelopes with typed fields:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from validibot_shared.energyplus import EnergyPlusInputEnvelope, EnergyPlusInputs
|
|
83
|
+
|
|
84
|
+
# The envelope has typed inputs instead of dict[str, Any]
|
|
85
|
+
envelope = EnergyPlusInputEnvelope(
|
|
86
|
+
run_id="abc-123",
|
|
87
|
+
inputs=EnergyPlusInputs(timestep_per_hour=4),
|
|
88
|
+
# ... other fields
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# IDE autocomplete and type checking work
|
|
92
|
+
timestep = envelope.inputs.timestep_per_hour
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This provides:
|
|
96
|
+
|
|
97
|
+
- **Type safety** - mypy/pyright catch errors at compile time
|
|
98
|
+
- **Runtime validation** - Pydantic validates all data
|
|
99
|
+
- **IDE support** - Full autocomplete for domain-specific fields
|
|
100
|
+
|
|
101
|
+
## Usage Examples
|
|
102
|
+
|
|
103
|
+
### Creating an Input Envelope
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from validibot_shared.energyplus import EnergyPlusInputEnvelope, EnergyPlusInputs
|
|
107
|
+
from validibot_shared.validations.envelopes import (
|
|
108
|
+
InputFileItem,
|
|
109
|
+
ValidatorInfo,
|
|
110
|
+
ExecutionContext,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
envelope = EnergyPlusInputEnvelope(
|
|
114
|
+
run_id="run-123",
|
|
115
|
+
validator=ValidatorInfo(id="v1", type="energyplus", version="24.2.0"),
|
|
116
|
+
input_files=[
|
|
117
|
+
InputFileItem(
|
|
118
|
+
name="model.idf",
|
|
119
|
+
mime_type="application/vnd.energyplus.idf",
|
|
120
|
+
role="primary-model",
|
|
121
|
+
uri="gs://bucket/model.idf",
|
|
122
|
+
),
|
|
123
|
+
],
|
|
124
|
+
inputs=EnergyPlusInputs(timestep_per_hour=4),
|
|
125
|
+
context=ExecutionContext(
|
|
126
|
+
callback_url="https://api.example.com/callback",
|
|
127
|
+
execution_bundle_uri="gs://bucket/run-123/",
|
|
128
|
+
),
|
|
129
|
+
)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Deserializing Results
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from validibot_shared.energyplus import EnergyPlusOutputEnvelope
|
|
136
|
+
|
|
137
|
+
# Parse JSON response from validator
|
|
138
|
+
envelope = EnergyPlusOutputEnvelope.model_validate_json(response_json)
|
|
139
|
+
|
|
140
|
+
# Access typed outputs
|
|
141
|
+
if envelope.outputs:
|
|
142
|
+
print(f"EUI: {envelope.outputs.metrics.eui_kbtu_per_sqft}")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### FMI Probe Results
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from validibot_shared.fmi.models import FMIProbeResult, FMIVariableMeta
|
|
149
|
+
|
|
150
|
+
# Create a successful probe result
|
|
151
|
+
result = FMIProbeResult.success(
|
|
152
|
+
variables=[
|
|
153
|
+
FMIVariableMeta(name="temperature", causality="output", value_type="Real"),
|
|
154
|
+
],
|
|
155
|
+
execution_seconds=0.5,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Create a failure result
|
|
159
|
+
result = FMIProbeResult.failure(errors=["Invalid FMU: missing modelDescription.xml"])
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Dependencies
|
|
163
|
+
|
|
164
|
+
- `pydantic>=2.8.0`
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# validibot-shared
|
|
2
|
+
|
|
3
|
+
Shared Pydantic models for [Validibot](https://validibot.com) validator containers.
|
|
4
|
+
|
|
5
|
+
This library defines the data interchange types used between Validibot and its advanced validator services, ensuring type safety and contract consistency.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install validibot-shared
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or with uv:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv add validibot-shared
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Package Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
validibot_shared/
|
|
23
|
+
├── validations/ # Base validation envelope schemas
|
|
24
|
+
│ └── envelopes.py # Input/output envelopes for all validators
|
|
25
|
+
├── energyplus/ # EnergyPlus-specific models and envelopes
|
|
26
|
+
│ ├── models.py # Simulation output models
|
|
27
|
+
│ └── envelopes.py # Typed envelope subclasses
|
|
28
|
+
└── fmi/ # FMI/FMU-specific models
|
|
29
|
+
├── models.py # Probe result models
|
|
30
|
+
└── envelopes.py # FMI envelope subclasses
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Core Concepts
|
|
34
|
+
|
|
35
|
+
### Validation Envelopes
|
|
36
|
+
|
|
37
|
+
The library provides base envelope classes for validator communication:
|
|
38
|
+
|
|
39
|
+
- `ValidationInputEnvelope` - Standard input format for validation jobs
|
|
40
|
+
- `ValidationOutputEnvelope` - Standard output format with results
|
|
41
|
+
- `ValidationCallback` - Callback payload for async job completion
|
|
42
|
+
|
|
43
|
+
Supporting classes include `InputFileItem`, `ValidatorInfo`, `ExecutionContext`, `ValidationMessage`, `ValidationMetric`, and `ValidationArtifact`.
|
|
44
|
+
|
|
45
|
+
### Typed Subclassing Pattern
|
|
46
|
+
|
|
47
|
+
Domain-specific validators extend the base envelopes with typed fields:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from validibot_shared.energyplus import EnergyPlusInputEnvelope, EnergyPlusInputs
|
|
51
|
+
|
|
52
|
+
# The envelope has typed inputs instead of dict[str, Any]
|
|
53
|
+
envelope = EnergyPlusInputEnvelope(
|
|
54
|
+
run_id="abc-123",
|
|
55
|
+
inputs=EnergyPlusInputs(timestep_per_hour=4),
|
|
56
|
+
# ... other fields
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# IDE autocomplete and type checking work
|
|
60
|
+
timestep = envelope.inputs.timestep_per_hour
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This provides:
|
|
64
|
+
|
|
65
|
+
- **Type safety** - mypy/pyright catch errors at compile time
|
|
66
|
+
- **Runtime validation** - Pydantic validates all data
|
|
67
|
+
- **IDE support** - Full autocomplete for domain-specific fields
|
|
68
|
+
|
|
69
|
+
## Usage Examples
|
|
70
|
+
|
|
71
|
+
### Creating an Input Envelope
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from validibot_shared.energyplus import EnergyPlusInputEnvelope, EnergyPlusInputs
|
|
75
|
+
from validibot_shared.validations.envelopes import (
|
|
76
|
+
InputFileItem,
|
|
77
|
+
ValidatorInfo,
|
|
78
|
+
ExecutionContext,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
envelope = EnergyPlusInputEnvelope(
|
|
82
|
+
run_id="run-123",
|
|
83
|
+
validator=ValidatorInfo(id="v1", type="energyplus", version="24.2.0"),
|
|
84
|
+
input_files=[
|
|
85
|
+
InputFileItem(
|
|
86
|
+
name="model.idf",
|
|
87
|
+
mime_type="application/vnd.energyplus.idf",
|
|
88
|
+
role="primary-model",
|
|
89
|
+
uri="gs://bucket/model.idf",
|
|
90
|
+
),
|
|
91
|
+
],
|
|
92
|
+
inputs=EnergyPlusInputs(timestep_per_hour=4),
|
|
93
|
+
context=ExecutionContext(
|
|
94
|
+
callback_url="https://api.example.com/callback",
|
|
95
|
+
execution_bundle_uri="gs://bucket/run-123/",
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Deserializing Results
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from validibot_shared.energyplus import EnergyPlusOutputEnvelope
|
|
104
|
+
|
|
105
|
+
# Parse JSON response from validator
|
|
106
|
+
envelope = EnergyPlusOutputEnvelope.model_validate_json(response_json)
|
|
107
|
+
|
|
108
|
+
# Access typed outputs
|
|
109
|
+
if envelope.outputs:
|
|
110
|
+
print(f"EUI: {envelope.outputs.metrics.eui_kbtu_per_sqft}")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### FMI Probe Results
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from validibot_shared.fmi.models import FMIProbeResult, FMIVariableMeta
|
|
117
|
+
|
|
118
|
+
# Create a successful probe result
|
|
119
|
+
result = FMIProbeResult.success(
|
|
120
|
+
variables=[
|
|
121
|
+
FMIVariableMeta(name="temperature", causality="output", value_type="Real"),
|
|
122
|
+
],
|
|
123
|
+
execution_seconds=0.5,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Create a failure result
|
|
127
|
+
result = FMIProbeResult.failure(errors=["Invalid FMU: missing modelDescription.xml"])
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Dependencies
|
|
131
|
+
|
|
132
|
+
- `pydantic>=2.8.0`
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "validibot-shared"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Shared library for data interchange between Validibot and validator containers"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Daniel McQuillen", email = "daniel@validibot.com" }]
|
|
12
|
+
maintainers = [{ name = "Daniel McQuillen", email = "daniel@validibot.com" }]
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
keywords = [
|
|
15
|
+
"validibot",
|
|
16
|
+
"validation",
|
|
17
|
+
"energyplus",
|
|
18
|
+
"fmu",
|
|
19
|
+
"simulation",
|
|
20
|
+
"data-interchange",
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 4 - Beta",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.10",
|
|
29
|
+
"Programming Language :: Python :: 3.11",
|
|
30
|
+
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
33
|
+
"Topic :: Scientific/Engineering",
|
|
34
|
+
]
|
|
35
|
+
dependencies = [
|
|
36
|
+
"pydantic>=2.8.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=8.3.0",
|
|
42
|
+
"ruff>=0.8.0",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://validibot.com"
|
|
47
|
+
Documentation = "https://docs.validibot.com"
|
|
48
|
+
Repository = "https://github.com/danielmcquillen/validibot-shared"
|
|
49
|
+
Issues = "https://github.com/danielmcquillen/validibot-shared/issues"
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.wheel]
|
|
52
|
+
packages = ["validibot_shared"]
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.targets.sdist]
|
|
55
|
+
include = [
|
|
56
|
+
"/validibot_shared",
|
|
57
|
+
"/tests",
|
|
58
|
+
"/README.md",
|
|
59
|
+
"/LICENSE",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 88
|
|
64
|
+
target-version = "py310"
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint]
|
|
67
|
+
select = [
|
|
68
|
+
"E", # pycodestyle errors
|
|
69
|
+
"W", # pycodestyle warnings
|
|
70
|
+
"F", # Pyflakes
|
|
71
|
+
"I", # isort
|
|
72
|
+
"B", # flake8-bugbear
|
|
73
|
+
"C4", # flake8-comprehensions
|
|
74
|
+
"UP", # pyupgrade
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
[tool.ruff.lint.isort]
|
|
78
|
+
known-first-party = ["validibot_shared"]
|
|
79
|
+
|
|
80
|
+
[tool.pytest.ini_options]
|
|
81
|
+
addopts = "-ra -q"
|
|
82
|
+
testpaths = ["tests"]
|
|
File without changes
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from pydantic import ValidationError
|
|
5
|
+
|
|
6
|
+
from validibot_shared.energyplus.envelopes import (
|
|
7
|
+
EnergyPlusInputEnvelope,
|
|
8
|
+
EnergyPlusInputs,
|
|
9
|
+
EnergyPlusOutputEnvelope,
|
|
10
|
+
EnergyPlusOutputs,
|
|
11
|
+
)
|
|
12
|
+
from validibot_shared.validations.envelopes import (
|
|
13
|
+
ExecutionContext,
|
|
14
|
+
InputFileItem,
|
|
15
|
+
SupportedMimeType,
|
|
16
|
+
ValidationStatus,
|
|
17
|
+
ValidatorType,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Test constants to avoid magic values
|
|
21
|
+
DEFAULT_TIMESTEP_PER_HOUR = 4
|
|
22
|
+
TEST_TIMESTEP_PER_HOUR = 6
|
|
23
|
+
TEST_ELECTRICITY_KWH = 123.4
|
|
24
|
+
TEST_EXECUTION_SECONDS = 12.5
|
|
25
|
+
TEST_EUI_KWH_M2 = 10.5
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _base_input_envelope_kwargs():
|
|
29
|
+
return {
|
|
30
|
+
"run_id": "run-1",
|
|
31
|
+
"validator": {
|
|
32
|
+
"id": "val-1",
|
|
33
|
+
"type": ValidatorType.ENERGYPLUS,
|
|
34
|
+
"version": "1.0",
|
|
35
|
+
},
|
|
36
|
+
"org": {"id": "org-1", "name": "ValidiBot"},
|
|
37
|
+
"workflow": {"id": "wf-1", "step_id": "step-1", "step_name": "EnergyPlus"},
|
|
38
|
+
"input_files": [
|
|
39
|
+
InputFileItem(
|
|
40
|
+
name="model.idf",
|
|
41
|
+
mime_type=SupportedMimeType.ENERGYPLUS_IDF,
|
|
42
|
+
role="primary-model",
|
|
43
|
+
uri="gs://bucket/model.idf",
|
|
44
|
+
)
|
|
45
|
+
],
|
|
46
|
+
"context": ExecutionContext(
|
|
47
|
+
callback_url="https://example.com/callback",
|
|
48
|
+
execution_bundle_uri="gs://bucket/run-1/",
|
|
49
|
+
timeout_seconds=600,
|
|
50
|
+
tags=["smoke"],
|
|
51
|
+
),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_energyplus_inputs_defaults():
|
|
56
|
+
"""EnergyPlusInputs should have sensible defaults."""
|
|
57
|
+
inputs = EnergyPlusInputs()
|
|
58
|
+
|
|
59
|
+
assert inputs.timestep_per_hour == DEFAULT_TIMESTEP_PER_HOUR
|
|
60
|
+
assert inputs.run_period_days is None
|
|
61
|
+
assert inputs.invocation_mode == "cli"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_energyplus_inputs_validation():
|
|
65
|
+
"""EnergyPlusInputs should reject invalid values."""
|
|
66
|
+
with pytest.raises(ValidationError):
|
|
67
|
+
EnergyPlusInputs(timestep_per_hour=0)
|
|
68
|
+
|
|
69
|
+
with pytest.raises(ValidationError):
|
|
70
|
+
EnergyPlusInputs(invocation_mode="invalid-mode")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_energyplus_input_envelope_uses_typed_inputs():
|
|
74
|
+
"""EnergyPlusInputEnvelope should parse inputs into EnergyPlusInputs."""
|
|
75
|
+
data = _base_input_envelope_kwargs()
|
|
76
|
+
envelope = EnergyPlusInputEnvelope(
|
|
77
|
+
**data,
|
|
78
|
+
inputs={"timestep_per_hour": TEST_TIMESTEP_PER_HOUR},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
assert isinstance(envelope.inputs, EnergyPlusInputs)
|
|
82
|
+
assert envelope.inputs.timestep_per_hour == TEST_TIMESTEP_PER_HOUR
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_energyplus_input_envelope_rejects_invalid_inputs():
|
|
86
|
+
"""EnergyPlusInputEnvelope should reject invalid input configurations."""
|
|
87
|
+
data = _base_input_envelope_kwargs()
|
|
88
|
+
|
|
89
|
+
with pytest.raises(ValidationError):
|
|
90
|
+
EnergyPlusInputEnvelope(
|
|
91
|
+
**data,
|
|
92
|
+
inputs={"timestep_per_hour": 1, "invocation_mode": "bad"},
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_energyplus_outputs_compose_nested_models():
|
|
97
|
+
"""EnergyPlusOutputs should compose nested models correctly."""
|
|
98
|
+
outputs = EnergyPlusOutputs(
|
|
99
|
+
outputs={"eplusout_sql": "outputs/eplusout.sql", "eplusout_err": None},
|
|
100
|
+
metrics={"site_electricity_kwh": TEST_ELECTRICITY_KWH},
|
|
101
|
+
logs={"stdout_tail": "log tail"},
|
|
102
|
+
energyplus_returncode=0,
|
|
103
|
+
execution_seconds=TEST_EXECUTION_SECONDS,
|
|
104
|
+
invocation_mode="python_api",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
assert isinstance(outputs.outputs.eplusout_sql, Path)
|
|
108
|
+
assert outputs.metrics.site_electricity_kwh == TEST_ELECTRICITY_KWH
|
|
109
|
+
assert outputs.logs.stdout_tail == "log tail"
|
|
110
|
+
assert outputs.execution_seconds == TEST_EXECUTION_SECONDS
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_energyplus_outputs_forbid_extra_fields():
|
|
114
|
+
"""EnergyPlusOutputs should forbid extra fields."""
|
|
115
|
+
with pytest.raises(ValidationError):
|
|
116
|
+
EnergyPlusOutputs(
|
|
117
|
+
energyplus_returncode=0,
|
|
118
|
+
execution_seconds=1.0,
|
|
119
|
+
invocation_mode="cli",
|
|
120
|
+
unknown="nope",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_energyplus_output_envelope_accepts_typed_outputs():
|
|
125
|
+
"""EnergyPlusOutputEnvelope should accept typed outputs."""
|
|
126
|
+
envelope = EnergyPlusOutputEnvelope(
|
|
127
|
+
run_id="run-2",
|
|
128
|
+
validator={
|
|
129
|
+
"id": "val-1",
|
|
130
|
+
"type": ValidatorType.ENERGYPLUS,
|
|
131
|
+
"version": "1.0",
|
|
132
|
+
},
|
|
133
|
+
status=ValidationStatus.SUCCESS,
|
|
134
|
+
timing={"queued_at": None, "started_at": None, "finished_at": None},
|
|
135
|
+
outputs={
|
|
136
|
+
"outputs": {"eplusout_sql": "outputs/eplusout.sql"},
|
|
137
|
+
"metrics": {"site_eui_kwh_m2": TEST_EUI_KWH_M2},
|
|
138
|
+
"energyplus_returncode": 0,
|
|
139
|
+
"execution_seconds": 3.2,
|
|
140
|
+
"invocation_mode": "cli",
|
|
141
|
+
},
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
assert isinstance(envelope.outputs, EnergyPlusOutputs)
|
|
145
|
+
assert envelope.outputs.outputs.eplusout_sql.name == "eplusout.sql"
|
|
146
|
+
assert envelope.outputs.metrics.site_eui_kwh_m2 == TEST_EUI_KWH_M2
|