nextmv 0.39.0.dev1__py3-none-any.whl → 1.0.0__py3-none-any.whl
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.
- nextmv/__about__.py +1 -1
- nextmv/__entrypoint__.py +1 -2
- nextmv/__init__.py +2 -4
- nextmv/cli/CONTRIBUTING.md +583 -0
- nextmv/cli/cloud/__init__.py +49 -0
- nextmv/cli/cloud/acceptance/__init__.py +27 -0
- nextmv/cli/cloud/acceptance/create.py +391 -0
- nextmv/cli/cloud/acceptance/delete.py +64 -0
- nextmv/cli/cloud/acceptance/get.py +103 -0
- nextmv/cli/cloud/acceptance/list.py +62 -0
- nextmv/cli/cloud/acceptance/update.py +95 -0
- nextmv/cli/cloud/account/__init__.py +28 -0
- nextmv/cli/cloud/account/create.py +83 -0
- nextmv/cli/cloud/account/delete.py +59 -0
- nextmv/cli/cloud/account/get.py +66 -0
- nextmv/cli/cloud/account/update.py +70 -0
- nextmv/cli/cloud/app/__init__.py +35 -0
- nextmv/cli/cloud/app/create.py +140 -0
- nextmv/cli/cloud/app/delete.py +57 -0
- nextmv/cli/cloud/app/exists.py +44 -0
- nextmv/cli/cloud/app/get.py +66 -0
- nextmv/cli/cloud/app/list.py +61 -0
- nextmv/cli/cloud/app/push.py +432 -0
- nextmv/cli/cloud/app/update.py +124 -0
- nextmv/cli/cloud/batch/__init__.py +29 -0
- nextmv/cli/cloud/batch/create.py +452 -0
- nextmv/cli/cloud/batch/delete.py +64 -0
- nextmv/cli/cloud/batch/get.py +104 -0
- nextmv/cli/cloud/batch/list.py +63 -0
- nextmv/cli/cloud/batch/metadata.py +66 -0
- nextmv/cli/cloud/batch/update.py +95 -0
- nextmv/cli/cloud/data/__init__.py +26 -0
- nextmv/cli/cloud/data/upload.py +162 -0
- nextmv/cli/cloud/ensemble/__init__.py +33 -0
- nextmv/cli/cloud/ensemble/create.py +413 -0
- nextmv/cli/cloud/ensemble/delete.py +63 -0
- nextmv/cli/cloud/ensemble/get.py +65 -0
- nextmv/cli/cloud/ensemble/list.py +63 -0
- nextmv/cli/cloud/ensemble/update.py +103 -0
- nextmv/cli/cloud/input_set/__init__.py +32 -0
- nextmv/cli/cloud/input_set/create.py +168 -0
- nextmv/cli/cloud/input_set/delete.py +64 -0
- nextmv/cli/cloud/input_set/get.py +63 -0
- nextmv/cli/cloud/input_set/list.py +63 -0
- nextmv/cli/cloud/input_set/update.py +123 -0
- nextmv/cli/cloud/instance/__init__.py +35 -0
- nextmv/cli/cloud/instance/create.py +289 -0
- nextmv/cli/cloud/instance/delete.py +61 -0
- nextmv/cli/cloud/instance/exists.py +39 -0
- nextmv/cli/cloud/instance/get.py +62 -0
- nextmv/cli/cloud/instance/list.py +60 -0
- nextmv/cli/cloud/instance/update.py +216 -0
- nextmv/cli/cloud/managed_input/__init__.py +31 -0
- nextmv/cli/cloud/managed_input/create.py +144 -0
- nextmv/cli/cloud/managed_input/delete.py +64 -0
- nextmv/cli/cloud/managed_input/get.py +63 -0
- nextmv/cli/cloud/managed_input/list.py +60 -0
- nextmv/cli/cloud/managed_input/update.py +97 -0
- nextmv/cli/cloud/run/__init__.py +37 -0
- nextmv/cli/cloud/run/cancel.py +37 -0
- nextmv/cli/cloud/run/create.py +524 -0
- nextmv/cli/cloud/run/get.py +199 -0
- nextmv/cli/cloud/run/input.py +86 -0
- nextmv/cli/cloud/run/list.py +80 -0
- nextmv/cli/cloud/run/logs.py +166 -0
- nextmv/cli/cloud/run/metadata.py +67 -0
- nextmv/cli/cloud/run/track.py +500 -0
- nextmv/cli/cloud/scenario/__init__.py +29 -0
- nextmv/cli/cloud/scenario/create.py +451 -0
- nextmv/cli/cloud/scenario/delete.py +61 -0
- nextmv/cli/cloud/scenario/get.py +102 -0
- nextmv/cli/cloud/scenario/list.py +63 -0
- nextmv/cli/cloud/scenario/metadata.py +67 -0
- nextmv/cli/cloud/scenario/update.py +93 -0
- nextmv/cli/cloud/secrets/__init__.py +33 -0
- nextmv/cli/cloud/secrets/create.py +206 -0
- nextmv/cli/cloud/secrets/delete.py +63 -0
- nextmv/cli/cloud/secrets/get.py +66 -0
- nextmv/cli/cloud/secrets/list.py +60 -0
- nextmv/cli/cloud/secrets/update.py +144 -0
- nextmv/cli/cloud/shadow/__init__.py +33 -0
- nextmv/cli/cloud/shadow/create.py +184 -0
- nextmv/cli/cloud/shadow/delete.py +64 -0
- nextmv/cli/cloud/shadow/get.py +61 -0
- nextmv/cli/cloud/shadow/list.py +63 -0
- nextmv/cli/cloud/shadow/metadata.py +66 -0
- nextmv/cli/cloud/shadow/start.py +43 -0
- nextmv/cli/cloud/shadow/stop.py +53 -0
- nextmv/cli/cloud/shadow/update.py +96 -0
- nextmv/cli/cloud/switchback/__init__.py +33 -0
- nextmv/cli/cloud/switchback/create.py +151 -0
- nextmv/cli/cloud/switchback/delete.py +64 -0
- nextmv/cli/cloud/switchback/get.py +62 -0
- nextmv/cli/cloud/switchback/list.py +63 -0
- nextmv/cli/cloud/switchback/metadata.py +68 -0
- nextmv/cli/cloud/switchback/start.py +43 -0
- nextmv/cli/cloud/switchback/stop.py +53 -0
- nextmv/cli/cloud/switchback/update.py +96 -0
- nextmv/cli/cloud/upload/__init__.py +22 -0
- nextmv/cli/cloud/upload/create.py +39 -0
- nextmv/cli/cloud/version/__init__.py +33 -0
- nextmv/cli/cloud/version/create.py +96 -0
- nextmv/cli/cloud/version/delete.py +61 -0
- nextmv/cli/cloud/version/exists.py +39 -0
- nextmv/cli/cloud/version/get.py +62 -0
- nextmv/cli/cloud/version/list.py +60 -0
- nextmv/cli/cloud/version/update.py +92 -0
- nextmv/cli/community/__init__.py +24 -0
- nextmv/cli/community/clone.py +86 -0
- nextmv/cli/community/list.py +200 -0
- nextmv/cli/configuration/__init__.py +23 -0
- nextmv/cli/configuration/config.py +228 -0
- nextmv/cli/configuration/create.py +94 -0
- nextmv/cli/configuration/delete.py +67 -0
- nextmv/cli/configuration/list.py +77 -0
- nextmv/cli/confirm.py +34 -0
- nextmv/cli/main.py +161 -3
- nextmv/cli/message.py +170 -0
- nextmv/cli/options.py +220 -0
- nextmv/cli/version.py +22 -2
- nextmv/cloud/__init__.py +17 -38
- nextmv/cloud/acceptance_test.py +20 -83
- nextmv/cloud/account.py +269 -30
- nextmv/cloud/application/__init__.py +898 -0
- nextmv/cloud/application/_acceptance.py +424 -0
- nextmv/cloud/application/_batch_scenario.py +845 -0
- nextmv/cloud/application/_ensemble.py +251 -0
- nextmv/cloud/application/_input_set.py +263 -0
- nextmv/cloud/application/_instance.py +289 -0
- nextmv/cloud/application/_managed_input.py +227 -0
- nextmv/cloud/application/_run.py +1393 -0
- nextmv/cloud/application/_secrets.py +294 -0
- nextmv/cloud/application/_shadow.py +320 -0
- nextmv/cloud/application/_switchback.py +332 -0
- nextmv/cloud/application/_utils.py +54 -0
- nextmv/cloud/application/_version.py +304 -0
- nextmv/cloud/batch_experiment.py +6 -2
- nextmv/cloud/community.py +446 -0
- nextmv/cloud/instance.py +11 -1
- nextmv/cloud/integration.py +8 -5
- nextmv/cloud/package.py +50 -9
- nextmv/cloud/shadow.py +254 -0
- nextmv/cloud/switchback.py +228 -0
- nextmv/deprecated.py +5 -3
- nextmv/input.py +20 -88
- nextmv/local/application.py +3 -15
- nextmv/local/runner.py +1 -1
- nextmv/model.py +50 -11
- nextmv/options.py +11 -256
- nextmv/output.py +0 -62
- nextmv/polling.py +54 -16
- nextmv/run.py +84 -37
- nextmv/status.py +1 -51
- {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/METADATA +37 -11
- nextmv-1.0.0.dist-info/RECORD +185 -0
- nextmv-1.0.0.dist-info/entry_points.txt +2 -0
- nextmv/cloud/application.py +0 -4204
- nextmv-0.39.0.dev1.dist-info/RECORD +0 -55
- nextmv-0.39.0.dev1.dist-info/entry_points.txt +0 -2
- {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/WHEEL +0 -0
- {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Application mixin for managing batch experiments.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from nextmv.cloud.batch_experiment import (
|
|
8
|
+
BatchExperiment,
|
|
9
|
+
BatchExperimentInformation,
|
|
10
|
+
BatchExperimentMetadata,
|
|
11
|
+
BatchExperimentRun,
|
|
12
|
+
ExperimentStatus,
|
|
13
|
+
)
|
|
14
|
+
from nextmv.cloud.input_set import InputSet, ManagedInput
|
|
15
|
+
from nextmv.cloud.scenario import Scenario, ScenarioInputType, _option_sets, _scenarios_by_id
|
|
16
|
+
from nextmv.polling import DEFAULT_POLLING_OPTIONS, PollingOptions, poll
|
|
17
|
+
from nextmv.run import Run
|
|
18
|
+
from nextmv.safe import safe_id, safe_name_and_id
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from . import Application
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ApplicationBatchMixin:
|
|
25
|
+
"""
|
|
26
|
+
Mixin class for managing batch experiments within an application.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def batch_experiment(self: "Application", batch_id: str) -> BatchExperiment:
|
|
30
|
+
"""
|
|
31
|
+
Get a batch experiment. This method also returns the runs of the batch
|
|
32
|
+
experiment under the `.runs` attribute.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
batch_id : str
|
|
37
|
+
ID of the batch experiment.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
BatchExperiment
|
|
42
|
+
The requested batch experiment details.
|
|
43
|
+
|
|
44
|
+
Raises
|
|
45
|
+
------
|
|
46
|
+
requests.HTTPError
|
|
47
|
+
If the response status code is not 2xx.
|
|
48
|
+
|
|
49
|
+
Examples
|
|
50
|
+
--------
|
|
51
|
+
>>> batch_exp = app.batch_experiment("batch-123")
|
|
52
|
+
>>> print(batch_exp.name)
|
|
53
|
+
'My Batch Experiment'
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
response = self.client.request(
|
|
57
|
+
method="GET",
|
|
58
|
+
endpoint=f"{self.experiments_endpoint}/batch/{batch_id}",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
exp = BatchExperiment.from_dict(response.json())
|
|
62
|
+
|
|
63
|
+
runs_response = self.client.request(
|
|
64
|
+
method="GET",
|
|
65
|
+
endpoint=f"{self.experiments_endpoint}/batch/{batch_id}/runs",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
runs = [Run.from_dict(run) for run in runs_response.json().get("runs", [])]
|
|
69
|
+
exp.runs = runs
|
|
70
|
+
|
|
71
|
+
return exp
|
|
72
|
+
|
|
73
|
+
def batch_experiment_metadata(self: "Application", batch_id: str) -> BatchExperimentMetadata:
|
|
74
|
+
"""
|
|
75
|
+
Get metadata for a batch experiment.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
batch_id : str
|
|
80
|
+
ID of the batch experiment.
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
BatchExperimentMetadata
|
|
85
|
+
The requested batch experiment metadata.
|
|
86
|
+
|
|
87
|
+
Raises
|
|
88
|
+
------
|
|
89
|
+
requests.HTTPError
|
|
90
|
+
If the response status code is not 2xx.
|
|
91
|
+
|
|
92
|
+
Examples
|
|
93
|
+
--------
|
|
94
|
+
>>> metadata = app.batch_experiment_metadata("batch-123")
|
|
95
|
+
>>> print(metadata.name)
|
|
96
|
+
'My Batch Experiment'
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
response = self.client.request(
|
|
100
|
+
method="GET",
|
|
101
|
+
endpoint=f"{self.experiments_endpoint}/batch/{batch_id}/metadata",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return BatchExperimentMetadata.from_dict(response.json())
|
|
105
|
+
|
|
106
|
+
def batch_experiment_with_polling(
|
|
107
|
+
self: "Application",
|
|
108
|
+
batch_id: str,
|
|
109
|
+
polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
|
|
110
|
+
) -> BatchExperiment:
|
|
111
|
+
"""
|
|
112
|
+
Get a batch experiment with polling.
|
|
113
|
+
|
|
114
|
+
Retrieves the result of an experiment. This method polls for the result
|
|
115
|
+
until the experiment finishes executing or the polling strategy is
|
|
116
|
+
exhausted.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
batch_id : str
|
|
121
|
+
ID of the batch experiment.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
BatchExperiment
|
|
126
|
+
The requested batch experiment details.
|
|
127
|
+
|
|
128
|
+
Raises
|
|
129
|
+
------
|
|
130
|
+
requests.HTTPError
|
|
131
|
+
If the response status code is not 2xx.
|
|
132
|
+
|
|
133
|
+
Examples
|
|
134
|
+
--------
|
|
135
|
+
>>> batch_exp = app.batch_experiment_with_polling("batch-123")
|
|
136
|
+
>>> print(batch_exp.name)
|
|
137
|
+
'My Batch Experiment'
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def polling_func() -> tuple[Any, bool]:
|
|
141
|
+
batch_metadata = self.batch_experiment_metadata(batch_id=batch_id)
|
|
142
|
+
if batch_metadata.status in {
|
|
143
|
+
ExperimentStatus.COMPLETED,
|
|
144
|
+
ExperimentStatus.FAILED,
|
|
145
|
+
ExperimentStatus.DRAFT,
|
|
146
|
+
ExperimentStatus.CANCELED,
|
|
147
|
+
ExperimentStatus.DELETE_FAILED,
|
|
148
|
+
}:
|
|
149
|
+
return batch_metadata, True
|
|
150
|
+
|
|
151
|
+
return None, False
|
|
152
|
+
|
|
153
|
+
batch_information = poll(polling_options=polling_options, polling_func=polling_func)
|
|
154
|
+
|
|
155
|
+
return self.batch_experiment(batch_id=batch_information.id)
|
|
156
|
+
|
|
157
|
+
def delete_batch_experiment(self: "Application", batch_id: str) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Delete a batch experiment.
|
|
160
|
+
|
|
161
|
+
Deletes a batch experiment along with all the associated information,
|
|
162
|
+
such as its runs.
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
batch_id : str
|
|
167
|
+
ID of the batch experiment to delete.
|
|
168
|
+
|
|
169
|
+
Raises
|
|
170
|
+
------
|
|
171
|
+
requests.HTTPError
|
|
172
|
+
If the response status code is not 2xx.
|
|
173
|
+
|
|
174
|
+
Examples
|
|
175
|
+
--------
|
|
176
|
+
>>> app.delete_batch_experiment("batch-123")
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
_ = self.client.request(
|
|
180
|
+
method="DELETE",
|
|
181
|
+
endpoint=f"{self.experiments_endpoint}/batch/{batch_id}",
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def delete_scenario_test(self: "Application", scenario_test_id: str) -> None:
|
|
185
|
+
"""
|
|
186
|
+
Delete a scenario test.
|
|
187
|
+
|
|
188
|
+
Deletes a scenario test. Scenario tests are based on the batch
|
|
189
|
+
experiments API, so this function summons `delete_batch_experiment`.
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
scenario_test_id : str
|
|
194
|
+
ID of the scenario test to delete.
|
|
195
|
+
|
|
196
|
+
Raises
|
|
197
|
+
------
|
|
198
|
+
requests.HTTPError
|
|
199
|
+
If the response status code is not 2xx.
|
|
200
|
+
|
|
201
|
+
Examples
|
|
202
|
+
--------
|
|
203
|
+
>>> app.delete_scenario_test("scenario-123")
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
self.delete_batch_experiment(batch_id=scenario_test_id)
|
|
207
|
+
|
|
208
|
+
def list_batch_experiments(self: "Application") -> list[BatchExperimentMetadata]:
|
|
209
|
+
"""
|
|
210
|
+
List all batch experiments.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
list[BatchExperimentMetadata]
|
|
215
|
+
List of batch experiments.
|
|
216
|
+
|
|
217
|
+
Raises
|
|
218
|
+
------
|
|
219
|
+
requests.HTTPError
|
|
220
|
+
If the response status code is not 2xx.
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
response = self.client.request(
|
|
224
|
+
method="GET",
|
|
225
|
+
endpoint=f"{self.experiments_endpoint}/batch",
|
|
226
|
+
query_params={"type": "batch"},
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return [BatchExperimentMetadata.from_dict(batch_experiment) for batch_experiment in response.json()]
|
|
230
|
+
|
|
231
|
+
def list_scenario_tests(self: "Application") -> list[BatchExperimentMetadata]:
|
|
232
|
+
"""
|
|
233
|
+
List all batch scenario tests. Scenario tests are based on the batch
|
|
234
|
+
experiments API, so this function returns the same information as
|
|
235
|
+
`list_batch_experiments`, albeit using a different query parameter.
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
list[BatchExperimentMetadata]
|
|
240
|
+
List of scenario tests.
|
|
241
|
+
|
|
242
|
+
Raises
|
|
243
|
+
------
|
|
244
|
+
requests.HTTPError
|
|
245
|
+
If the response status code is not 2xx.
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
response = self.client.request(
|
|
249
|
+
method="GET",
|
|
250
|
+
endpoint=f"{self.experiments_endpoint}/batch",
|
|
251
|
+
query_params={"type": "scenario"},
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return [BatchExperimentMetadata.from_dict(batch_experiment) for batch_experiment in response.json()]
|
|
255
|
+
|
|
256
|
+
def new_batch_experiment( # noqa: C901
|
|
257
|
+
self: "Application",
|
|
258
|
+
name: str | None = None,
|
|
259
|
+
input_set_id: str | None = None,
|
|
260
|
+
description: str | None = None,
|
|
261
|
+
id: str | None = None,
|
|
262
|
+
option_sets: dict[str, dict[str, str]] | None = None,
|
|
263
|
+
runs: list[BatchExperimentRun | dict[str, Any]] | None = None,
|
|
264
|
+
type: str | None = "batch",
|
|
265
|
+
) -> str:
|
|
266
|
+
"""
|
|
267
|
+
Create a new batch experiment.
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
name: Optional[str]
|
|
272
|
+
Name of the batch experiment. If not provided, the ID will be used as the name.
|
|
273
|
+
input_set_id: str | None
|
|
274
|
+
ID of the input set to use for the batch experiment.
|
|
275
|
+
description: Optional[str]
|
|
276
|
+
Optional description of the batch experiment.
|
|
277
|
+
id: Optional[str]
|
|
278
|
+
ID of the batch experiment. Will be generated if not provided.
|
|
279
|
+
option_sets: Optional[dict[str, dict[str, str]]]
|
|
280
|
+
Option sets to use for the batch experiment. This is a dictionary
|
|
281
|
+
where the keys are option set IDs and the values are dictionaries
|
|
282
|
+
with the actual options.
|
|
283
|
+
runs: Optional[list[BatchExperimentRun]]
|
|
284
|
+
List of runs to use for the batch experiment.
|
|
285
|
+
type: Optional[str]
|
|
286
|
+
Type of the batch experiment. This is used to determine the
|
|
287
|
+
experiment type. The default value is "batch". If you want to
|
|
288
|
+
create a scenario test, set this to "scenario".
|
|
289
|
+
|
|
290
|
+
Returns
|
|
291
|
+
-------
|
|
292
|
+
str
|
|
293
|
+
ID of the batch experiment.
|
|
294
|
+
|
|
295
|
+
Raises
|
|
296
|
+
------
|
|
297
|
+
requests.HTTPError
|
|
298
|
+
If the response status code is not 2xx.
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
# Generate ID if not provided
|
|
302
|
+
if id is None or id == "":
|
|
303
|
+
id = safe_id("batch")
|
|
304
|
+
|
|
305
|
+
# Use ID as name if name not provided
|
|
306
|
+
if name is None or name == "":
|
|
307
|
+
name = id
|
|
308
|
+
|
|
309
|
+
payload = {
|
|
310
|
+
"id": id,
|
|
311
|
+
"name": name,
|
|
312
|
+
}
|
|
313
|
+
if input_set_id is not None:
|
|
314
|
+
payload["input_set_id"] = input_set_id
|
|
315
|
+
if description is not None:
|
|
316
|
+
payload["description"] = description
|
|
317
|
+
if option_sets is not None:
|
|
318
|
+
payload["option_sets"] = option_sets
|
|
319
|
+
if runs is not None:
|
|
320
|
+
payload_runs = [{}] * len(runs)
|
|
321
|
+
for i, run in enumerate(runs):
|
|
322
|
+
payload_runs[i] = run.to_dict() if isinstance(run, BatchExperimentRun) else run
|
|
323
|
+
payload["runs"] = payload_runs
|
|
324
|
+
if type is not None:
|
|
325
|
+
payload["type"] = type
|
|
326
|
+
|
|
327
|
+
response = self.client.request(
|
|
328
|
+
method="POST",
|
|
329
|
+
endpoint=f"{self.experiments_endpoint}/batch",
|
|
330
|
+
payload=payload,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
return response.json()["id"]
|
|
334
|
+
|
|
335
|
+
def new_batch_experiment_with_result(
|
|
336
|
+
self: "Application",
|
|
337
|
+
name: str | None = None,
|
|
338
|
+
input_set_id: str | None = None,
|
|
339
|
+
description: str | None = None,
|
|
340
|
+
id: str | None = None,
|
|
341
|
+
option_sets: dict[str, dict[str, str]] | None = None,
|
|
342
|
+
runs: list[BatchExperimentRun | dict[str, Any]] | None = None,
|
|
343
|
+
type: str | None = "batch",
|
|
344
|
+
polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
|
|
345
|
+
) -> BatchExperiment:
|
|
346
|
+
"""
|
|
347
|
+
Convenience method to create a new batch experiment and poll for the
|
|
348
|
+
result.
|
|
349
|
+
|
|
350
|
+
This method combines the `new_batch_experiment` and
|
|
351
|
+
`batch_experiment_with_polling` methods, applying polling logic to
|
|
352
|
+
check when the experiment succeeded.
|
|
353
|
+
|
|
354
|
+
Parameters
|
|
355
|
+
----------
|
|
356
|
+
name: Optional[str]
|
|
357
|
+
Name of the batch experiment. If not provided, the ID will be used as the name.
|
|
358
|
+
input_set_id: str
|
|
359
|
+
ID of the input set to use for the batch experiment.
|
|
360
|
+
description: Optional[str]
|
|
361
|
+
Optional description of the batch experiment.
|
|
362
|
+
id: Optional[str]
|
|
363
|
+
ID of the batch experiment. Will be generated if not provided.
|
|
364
|
+
option_sets: Optional[dict[str, dict[str, str]]]
|
|
365
|
+
Option sets to use for the batch experiment. This is a dictionary
|
|
366
|
+
where the keys are option set IDs and the values are dictionaries
|
|
367
|
+
with the actual options.
|
|
368
|
+
runs: Optional[list[BatchExperimentRun]]
|
|
369
|
+
List of runs to use for the batch experiment.
|
|
370
|
+
type: Optional[str]
|
|
371
|
+
Type of the batch experiment. This is used to determine the
|
|
372
|
+
experiment type. The default value is "batch". If you want to
|
|
373
|
+
create a scenario test, set this to "scenario".
|
|
374
|
+
polling_options : PollingOptions, default=DEFAULT_POLLING_OPTIONS
|
|
375
|
+
Options to use when polling for the batch experiment result.
|
|
376
|
+
|
|
377
|
+
Returns
|
|
378
|
+
-------
|
|
379
|
+
BatchExperiment
|
|
380
|
+
The completed batch experiment with results.
|
|
381
|
+
|
|
382
|
+
Raises
|
|
383
|
+
------
|
|
384
|
+
requests.HTTPError
|
|
385
|
+
If the response status code is not 2xx.
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
batch_id = self.new_batch_experiment(
|
|
389
|
+
name=name,
|
|
390
|
+
input_set_id=input_set_id,
|
|
391
|
+
description=description,
|
|
392
|
+
id=id,
|
|
393
|
+
option_sets=option_sets,
|
|
394
|
+
runs=runs,
|
|
395
|
+
type=type,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
return self.batch_experiment_with_polling(batch_id=batch_id, polling_options=polling_options)
|
|
399
|
+
|
|
400
|
+
def new_scenario_test(
|
|
401
|
+
self: "Application",
|
|
402
|
+
scenarios: list[Scenario],
|
|
403
|
+
id: str | None = None,
|
|
404
|
+
name: str | None = None,
|
|
405
|
+
description: str | None = None,
|
|
406
|
+
repetitions: int | None = 0,
|
|
407
|
+
) -> str:
|
|
408
|
+
"""
|
|
409
|
+
Create a new scenario test. The test is based on `scenarios` and you
|
|
410
|
+
may specify `repetitions` to run the test multiple times. 0 repetitions
|
|
411
|
+
means that the tests will be executed once. 1 repetition means that the
|
|
412
|
+
test will be repeated once, i.e.: it will be executed twice. 2
|
|
413
|
+
repetitions equals 3 executions, so on, and so forth.
|
|
414
|
+
|
|
415
|
+
For each scenario, consider the `scenario_input` and `configuration`.
|
|
416
|
+
The `scenario_input.scenario_input_type` allows you to specify the data
|
|
417
|
+
that will be used for that scenario.
|
|
418
|
+
|
|
419
|
+
- `ScenarioInputType.INPUT_SET`: the data should be taken from an
|
|
420
|
+
existing input set.
|
|
421
|
+
- `ScenarioInputType.INPUT`: the data should be taken from a list of
|
|
422
|
+
existing inputs. When using this type, an input set will be created
|
|
423
|
+
from this set of managed inputs.
|
|
424
|
+
- `ScenarioInputType.New`: a new set of data will be uploaded as a set
|
|
425
|
+
of managed inputs. A new input set will be created from this set of
|
|
426
|
+
managed inputs.
|
|
427
|
+
|
|
428
|
+
On the other hand, the `configuration` allows you to specify multiple
|
|
429
|
+
option variations for the scenario. Please see the
|
|
430
|
+
`ScenarioConfiguration` class for more information.
|
|
431
|
+
|
|
432
|
+
The scenario tests uses the batch experiments API under the hood.
|
|
433
|
+
|
|
434
|
+
Parameters
|
|
435
|
+
----------
|
|
436
|
+
scenarios: list[Scenario]
|
|
437
|
+
List of scenarios to use for the scenario test. At least one
|
|
438
|
+
scenario should be provided.
|
|
439
|
+
id: Optional[str]
|
|
440
|
+
ID of the scenario test. Will be generated if not provided.
|
|
441
|
+
name: Optional[str]
|
|
442
|
+
Name of the scenario test. If not provided, the ID will be used as the name.
|
|
443
|
+
description: Optional[str]
|
|
444
|
+
Optional description of the scenario test.
|
|
445
|
+
repetitions: Optional[int]
|
|
446
|
+
Number of repetitions to use for the scenario test. 0
|
|
447
|
+
repetitions means that the tests will be executed once. 1
|
|
448
|
+
repetition means that the test will be repeated once, i.e.: it
|
|
449
|
+
will be executed twice. 2 repetitions equals 3 executions, so on,
|
|
450
|
+
and so forth.
|
|
451
|
+
|
|
452
|
+
Returns
|
|
453
|
+
-------
|
|
454
|
+
str
|
|
455
|
+
ID of the scenario test.
|
|
456
|
+
|
|
457
|
+
Raises
|
|
458
|
+
------
|
|
459
|
+
requests.HTTPError
|
|
460
|
+
If the response status code is not 2xx.
|
|
461
|
+
ValueError
|
|
462
|
+
If no scenarios are provided.
|
|
463
|
+
"""
|
|
464
|
+
|
|
465
|
+
if len(scenarios) < 1:
|
|
466
|
+
raise ValueError("At least one scenario must be provided")
|
|
467
|
+
|
|
468
|
+
# Generate ID if not provided
|
|
469
|
+
if id is None or id == "":
|
|
470
|
+
id = safe_id("scenario")
|
|
471
|
+
|
|
472
|
+
# Use ID as name if name not provided
|
|
473
|
+
if name is None or name == "":
|
|
474
|
+
name = id
|
|
475
|
+
|
|
476
|
+
scenarios_by_id = _scenarios_by_id(scenarios)
|
|
477
|
+
|
|
478
|
+
# Save all the information needed by scenario.
|
|
479
|
+
input_sets = {}
|
|
480
|
+
instances = {}
|
|
481
|
+
for scenario_id, scenario in scenarios_by_id.items():
|
|
482
|
+
instance = self.instance(instance_id=scenario.instance_id)
|
|
483
|
+
|
|
484
|
+
# Each scenario is associated to an input set, so we must either
|
|
485
|
+
# get it or create it.
|
|
486
|
+
input_set = self.__input_set_for_scenario(scenario, scenario_id)
|
|
487
|
+
|
|
488
|
+
instances[scenario_id] = instance
|
|
489
|
+
input_sets[scenario_id] = input_set
|
|
490
|
+
|
|
491
|
+
# Calculate the combinations of all the option sets across scenarios.
|
|
492
|
+
opt_sets_by_scenario = _option_sets(scenarios)
|
|
493
|
+
|
|
494
|
+
# The scenario tests results in multiple individual runs.
|
|
495
|
+
runs = []
|
|
496
|
+
run_counter = 0
|
|
497
|
+
opt_sets = {}
|
|
498
|
+
for scenario_id, scenario_opt_sets in opt_sets_by_scenario.items():
|
|
499
|
+
opt_sets = {**opt_sets, **scenario_opt_sets}
|
|
500
|
+
input_set = input_sets[scenario_id]
|
|
501
|
+
scenario = scenarios_by_id[scenario_id]
|
|
502
|
+
|
|
503
|
+
for set_key in scenario_opt_sets.keys():
|
|
504
|
+
inputs = input_set.input_ids if len(input_set.input_ids) > 0 else input_set.inputs
|
|
505
|
+
for input in inputs:
|
|
506
|
+
input_id = input.id if isinstance(input, ManagedInput) else input
|
|
507
|
+
for repetition in range(repetitions + 1):
|
|
508
|
+
run_counter += 1
|
|
509
|
+
run = BatchExperimentRun(
|
|
510
|
+
input_id=input_id,
|
|
511
|
+
input_set_id=input_set.id,
|
|
512
|
+
instance_id=scenario.instance_id,
|
|
513
|
+
option_set=set_key,
|
|
514
|
+
scenario_id=scenario_id,
|
|
515
|
+
repetition=repetition,
|
|
516
|
+
run_number=f"{run_counter}",
|
|
517
|
+
)
|
|
518
|
+
runs.append(run)
|
|
519
|
+
|
|
520
|
+
return self.new_batch_experiment(
|
|
521
|
+
id=id,
|
|
522
|
+
name=name,
|
|
523
|
+
description=description,
|
|
524
|
+
type="scenario",
|
|
525
|
+
option_sets=opt_sets,
|
|
526
|
+
runs=runs,
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
def new_scenario_test_with_result(
|
|
530
|
+
self: "Application",
|
|
531
|
+
scenarios: list[Scenario],
|
|
532
|
+
id: str | None = None,
|
|
533
|
+
name: str | None = None,
|
|
534
|
+
description: str | None = None,
|
|
535
|
+
repetitions: int | None = 0,
|
|
536
|
+
polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
|
|
537
|
+
) -> BatchExperiment:
|
|
538
|
+
"""
|
|
539
|
+
Convenience method to create a new scenario test and poll for the
|
|
540
|
+
result.
|
|
541
|
+
|
|
542
|
+
This method combines the `new_scenario_test` and
|
|
543
|
+
`scenario_test_with_polling` methods, applying polling logic to
|
|
544
|
+
check when the test succeeded.
|
|
545
|
+
|
|
546
|
+
The scenario tests uses the batch experiments API under the hood.
|
|
547
|
+
|
|
548
|
+
Parameters
|
|
549
|
+
----------
|
|
550
|
+
scenarios: list[Scenario]
|
|
551
|
+
List of scenarios to use for the scenario test. At least one
|
|
552
|
+
scenario should be provided.
|
|
553
|
+
id: Optional[str]
|
|
554
|
+
ID of the scenario test. Will be generated if not provided.
|
|
555
|
+
name: Optional[str]
|
|
556
|
+
Name of the scenario test. If not provided, the ID will be used as the name.
|
|
557
|
+
description: Optional[str]
|
|
558
|
+
Optional description of the scenario test.
|
|
559
|
+
repetitions: Optional[int]
|
|
560
|
+
Number of repetitions to use for the scenario test. 0
|
|
561
|
+
repetitions means that the tests will be executed once. 1
|
|
562
|
+
repetition means that the test will be repeated once, i.e.: it
|
|
563
|
+
will be executed twice. 2 repetitions equals 3 executions, so on,
|
|
564
|
+
and so forth.
|
|
565
|
+
polling_options : PollingOptions, default=DEFAULT_POLLING_OPTIONS
|
|
566
|
+
Options to use when polling for the scenario test result.
|
|
567
|
+
|
|
568
|
+
Returns
|
|
569
|
+
-------
|
|
570
|
+
BatchExperiment
|
|
571
|
+
The completed scenario test as a BatchExperiment.
|
|
572
|
+
|
|
573
|
+
Raises
|
|
574
|
+
------
|
|
575
|
+
requests.HTTPError
|
|
576
|
+
If the response status code is not 2xx.
|
|
577
|
+
ValueError
|
|
578
|
+
If no scenarios are provided.
|
|
579
|
+
"""
|
|
580
|
+
|
|
581
|
+
test_id = self.new_scenario_test(
|
|
582
|
+
scenarios=scenarios,
|
|
583
|
+
id=id,
|
|
584
|
+
name=name,
|
|
585
|
+
description=description,
|
|
586
|
+
repetitions=repetitions,
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
return self.scenario_test_with_polling(
|
|
590
|
+
scenario_test_id=test_id,
|
|
591
|
+
polling_options=polling_options,
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
def scenario_test(self: "Application", scenario_test_id: str) -> BatchExperiment:
|
|
595
|
+
"""
|
|
596
|
+
Get a scenario test.
|
|
597
|
+
|
|
598
|
+
Retrieves a scenario test by ID. Scenario tests are based on batch
|
|
599
|
+
experiments, so this function returns the corresponding batch
|
|
600
|
+
experiment associated with the scenario test.
|
|
601
|
+
|
|
602
|
+
Parameters
|
|
603
|
+
----------
|
|
604
|
+
scenario_test_id : str
|
|
605
|
+
ID of the scenario test to retrieve.
|
|
606
|
+
|
|
607
|
+
Returns
|
|
608
|
+
-------
|
|
609
|
+
BatchExperiment
|
|
610
|
+
The scenario test details as a batch experiment.
|
|
611
|
+
|
|
612
|
+
Raises
|
|
613
|
+
------
|
|
614
|
+
requests.HTTPError
|
|
615
|
+
If the response status code is not 2xx.
|
|
616
|
+
|
|
617
|
+
Examples
|
|
618
|
+
--------
|
|
619
|
+
>>> test = app.scenario_test("scenario-123")
|
|
620
|
+
>>> print(test.name)
|
|
621
|
+
'My Scenario Test'
|
|
622
|
+
>>> print(test.type)
|
|
623
|
+
'scenario'
|
|
624
|
+
"""
|
|
625
|
+
|
|
626
|
+
return self.batch_experiment(batch_id=scenario_test_id)
|
|
627
|
+
|
|
628
|
+
def scenario_test_metadata(self: "Application", scenario_test_id: str) -> BatchExperimentMetadata:
|
|
629
|
+
"""
|
|
630
|
+
Get the metadata for a scenario test, given its ID.
|
|
631
|
+
|
|
632
|
+
Scenario tests are based on batch experiments, so this function returns
|
|
633
|
+
the corresponding batch experiment metadata associated with the
|
|
634
|
+
scenario test.
|
|
635
|
+
|
|
636
|
+
Parameters
|
|
637
|
+
----------
|
|
638
|
+
scenario_test_id : str
|
|
639
|
+
ID of the scenario test to retrieve.
|
|
640
|
+
|
|
641
|
+
Returns
|
|
642
|
+
-------
|
|
643
|
+
BatchExperimentMetadata
|
|
644
|
+
The scenario test metadata as a batch experiment.
|
|
645
|
+
|
|
646
|
+
Raises
|
|
647
|
+
------
|
|
648
|
+
requests.HTTPError
|
|
649
|
+
If the response status code is not 2xx.
|
|
650
|
+
|
|
651
|
+
Examples
|
|
652
|
+
--------
|
|
653
|
+
>>> metadata = app.scenario_test_metadata("scenario-123")
|
|
654
|
+
>>> print(metadata.name)
|
|
655
|
+
'My Scenario Test'
|
|
656
|
+
>>> print(metadata.type)
|
|
657
|
+
'scenario'
|
|
658
|
+
"""
|
|
659
|
+
|
|
660
|
+
return self.batch_experiment_metadata(batch_id=scenario_test_id)
|
|
661
|
+
|
|
662
|
+
def scenario_test_with_polling(
|
|
663
|
+
self: "Application",
|
|
664
|
+
scenario_test_id: str,
|
|
665
|
+
polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
|
|
666
|
+
) -> BatchExperiment:
|
|
667
|
+
"""
|
|
668
|
+
Get a scenario test with polling.
|
|
669
|
+
|
|
670
|
+
Retrieves the result of a scenario test. This method polls for the
|
|
671
|
+
result until the test finishes executing or the polling strategy is
|
|
672
|
+
exhausted.
|
|
673
|
+
|
|
674
|
+
The scenario tests uses the batch experiments API under the hood.
|
|
675
|
+
|
|
676
|
+
Parameters
|
|
677
|
+
----------
|
|
678
|
+
scenario_test_id : str
|
|
679
|
+
ID of the scenario test to retrieve.
|
|
680
|
+
polling_options : PollingOptions, default=DEFAULT_POLLING_OPTIONS
|
|
681
|
+
Options to use when polling for the scenario test result.
|
|
682
|
+
|
|
683
|
+
Returns
|
|
684
|
+
-------
|
|
685
|
+
BatchExperiment
|
|
686
|
+
The scenario test details as a batch experiment.
|
|
687
|
+
|
|
688
|
+
Raises
|
|
689
|
+
------
|
|
690
|
+
requests.HTTPError
|
|
691
|
+
If the response status code is not 2xx.
|
|
692
|
+
|
|
693
|
+
Examples
|
|
694
|
+
--------
|
|
695
|
+
>>> test = app.scenario_test_with_polling("scenario-123")
|
|
696
|
+
>>> print(test.name)
|
|
697
|
+
'My Scenario Test'
|
|
698
|
+
>>> print(test.type)
|
|
699
|
+
'scenario'
|
|
700
|
+
"""
|
|
701
|
+
|
|
702
|
+
return self.batch_experiment_with_polling(batch_id=scenario_test_id, polling_options=polling_options)
|
|
703
|
+
|
|
704
|
+
def update_batch_experiment(
|
|
705
|
+
self: "Application",
|
|
706
|
+
batch_experiment_id: str,
|
|
707
|
+
name: str | None = None,
|
|
708
|
+
description: str | None = None,
|
|
709
|
+
) -> BatchExperimentInformation:
|
|
710
|
+
"""
|
|
711
|
+
Update a batch experiment.
|
|
712
|
+
|
|
713
|
+
Parameters
|
|
714
|
+
----------
|
|
715
|
+
batch_experiment_id : str
|
|
716
|
+
ID of the batch experiment to update.
|
|
717
|
+
name : Optional[str], default=None
|
|
718
|
+
Optional name of the batch experiment.
|
|
719
|
+
description : Optional[str], default=None
|
|
720
|
+
Optional description of the batch experiment.
|
|
721
|
+
|
|
722
|
+
Returns
|
|
723
|
+
-------
|
|
724
|
+
BatchExperimentInformation
|
|
725
|
+
The information with the updated batch experiment.
|
|
726
|
+
|
|
727
|
+
Raises
|
|
728
|
+
------
|
|
729
|
+
requests.HTTPError
|
|
730
|
+
If the response status code is not 2xx.
|
|
731
|
+
"""
|
|
732
|
+
|
|
733
|
+
payload = {}
|
|
734
|
+
|
|
735
|
+
if name is not None:
|
|
736
|
+
payload["name"] = name
|
|
737
|
+
if description is not None:
|
|
738
|
+
payload["description"] = description
|
|
739
|
+
|
|
740
|
+
response = self.client.request(
|
|
741
|
+
method="PATCH",
|
|
742
|
+
endpoint=f"{self.experiments_endpoint}/batch/{batch_experiment_id}",
|
|
743
|
+
payload=payload,
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
return BatchExperimentInformation.from_dict(response.json())
|
|
747
|
+
|
|
748
|
+
def update_scenario_test(
|
|
749
|
+
self: "Application",
|
|
750
|
+
scenario_test_id: str,
|
|
751
|
+
name: str | None = None,
|
|
752
|
+
description: str | None = None,
|
|
753
|
+
) -> BatchExperimentInformation:
|
|
754
|
+
"""
|
|
755
|
+
Update a scenario test.
|
|
756
|
+
|
|
757
|
+
Updates a scenario test with new name and description. Scenario tests
|
|
758
|
+
use the batch experiments API, so this method calls the
|
|
759
|
+
`update_batch_experiment` method, and thus the return type is the same.
|
|
760
|
+
|
|
761
|
+
Parameters
|
|
762
|
+
----------
|
|
763
|
+
scenario_test_id : str
|
|
764
|
+
ID of the scenario test to update.
|
|
765
|
+
name : Optional[str], default=None
|
|
766
|
+
Optional new name for the scenario test.
|
|
767
|
+
description : Optional[str], default=None
|
|
768
|
+
Optional new description for the scenario test.
|
|
769
|
+
|
|
770
|
+
Returns
|
|
771
|
+
-------
|
|
772
|
+
BatchExperimentInformation
|
|
773
|
+
The information about the updated scenario test.
|
|
774
|
+
|
|
775
|
+
Raises
|
|
776
|
+
------
|
|
777
|
+
requests.HTTPError
|
|
778
|
+
If the response status code is not 2xx.
|
|
779
|
+
|
|
780
|
+
Examples
|
|
781
|
+
--------
|
|
782
|
+
>>> info = app.update_scenario_test(
|
|
783
|
+
... scenario_test_id="scenario-123",
|
|
784
|
+
... name="Updated Test Name",
|
|
785
|
+
... description="Updated description for this test"
|
|
786
|
+
... )
|
|
787
|
+
>>> print(info.name)
|
|
788
|
+
'Updated Test Name'
|
|
789
|
+
"""
|
|
790
|
+
|
|
791
|
+
return self.update_batch_experiment(
|
|
792
|
+
batch_experiment_id=scenario_test_id,
|
|
793
|
+
name=name,
|
|
794
|
+
description=description,
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
def __input_set_for_scenario(self: "Application", scenario: Scenario, scenario_id: str) -> InputSet:
|
|
798
|
+
# If working with an input set, there is no need to create one.
|
|
799
|
+
if scenario.scenario_input.scenario_input_type == ScenarioInputType.INPUT_SET:
|
|
800
|
+
input_set = self.input_set(input_set_id=scenario.scenario_input.scenario_input_data)
|
|
801
|
+
return input_set
|
|
802
|
+
|
|
803
|
+
# If working with a list of managed inputs, we need to create an
|
|
804
|
+
# input set.
|
|
805
|
+
if scenario.scenario_input.scenario_input_type == ScenarioInputType.INPUT:
|
|
806
|
+
name, id = safe_name_and_id(prefix="inpset", entity_id=scenario_id)
|
|
807
|
+
input_set = self.new_input_set(
|
|
808
|
+
id=id,
|
|
809
|
+
name=name,
|
|
810
|
+
description=f"Automatically created from scenario test: {id}",
|
|
811
|
+
maximum_runs=20,
|
|
812
|
+
inputs=[
|
|
813
|
+
ManagedInput.from_dict(data={"id": input_id})
|
|
814
|
+
for input_id in scenario.scenario_input.scenario_input_data
|
|
815
|
+
],
|
|
816
|
+
)
|
|
817
|
+
return input_set
|
|
818
|
+
|
|
819
|
+
# If working with new data, we need to create managed inputs, and then,
|
|
820
|
+
# an input set.
|
|
821
|
+
if scenario.scenario_input.scenario_input_type == ScenarioInputType.NEW:
|
|
822
|
+
managed_inputs = []
|
|
823
|
+
for data in scenario.scenario_input.scenario_input_data:
|
|
824
|
+
upload_url = self.upload_url()
|
|
825
|
+
self.upload_data(data=data, upload_url=upload_url)
|
|
826
|
+
name, id = safe_name_and_id(prefix="man-input", entity_id=scenario_id)
|
|
827
|
+
managed_input = self.new_managed_input(
|
|
828
|
+
id=id,
|
|
829
|
+
name=name,
|
|
830
|
+
description=f"Automatically created from scenario test: {id}",
|
|
831
|
+
upload_id=upload_url.upload_id,
|
|
832
|
+
)
|
|
833
|
+
managed_inputs.append(managed_input)
|
|
834
|
+
|
|
835
|
+
name, id = safe_name_and_id(prefix="inpset", entity_id=scenario_id)
|
|
836
|
+
input_set = self.new_input_set(
|
|
837
|
+
id=id,
|
|
838
|
+
name=name,
|
|
839
|
+
description=f"Automatically created from scenario test: {id}",
|
|
840
|
+
maximum_runs=20,
|
|
841
|
+
inputs=managed_inputs,
|
|
842
|
+
)
|
|
843
|
+
return input_set
|
|
844
|
+
|
|
845
|
+
raise ValueError(f"Unknown scenario input type: {scenario.scenario_input.scenario_input_type}")
|