nextmv 0.18.0__py3-none-any.whl → 1.0.0.dev2__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 +8 -13
- nextmv/__init__.py +53 -0
- nextmv/_serialization.py +96 -0
- nextmv/base_model.py +54 -9
- nextmv/cli/CONTRIBUTING.md +511 -0
- nextmv/cli/__init__.py +0 -0
- nextmv/cli/cloud/__init__.py +47 -0
- nextmv/cli/cloud/acceptance/__init__.py +27 -0
- nextmv/cli/cloud/acceptance/create.py +393 -0
- nextmv/cli/cloud/acceptance/delete.py +68 -0
- nextmv/cli/cloud/acceptance/get.py +104 -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 +60 -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 +141 -0
- nextmv/cli/cloud/app/delete.py +58 -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 +137 -0
- nextmv/cli/cloud/app/update.py +124 -0
- nextmv/cli/cloud/batch/__init__.py +29 -0
- nextmv/cli/cloud/batch/create.py +454 -0
- nextmv/cli/cloud/batch/delete.py +68 -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 +31 -0
- nextmv/cli/cloud/ensemble/create.py +414 -0
- nextmv/cli/cloud/ensemble/delete.py +67 -0
- nextmv/cli/cloud/ensemble/get.py +65 -0
- nextmv/cli/cloud/ensemble/update.py +103 -0
- nextmv/cli/cloud/input_set/__init__.py +30 -0
- nextmv/cli/cloud/input_set/create.py +170 -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 +290 -0
- nextmv/cli/cloud/instance/delete.py +62 -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 +146 -0
- nextmv/cli/cloud/managed_input/delete.py +65 -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 +530 -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 +167 -0
- nextmv/cli/cloud/run/metadata.py +67 -0
- nextmv/cli/cloud/run/track.py +501 -0
- nextmv/cli/cloud/scenario/__init__.py +29 -0
- nextmv/cli/cloud/scenario/create.py +451 -0
- nextmv/cli/cloud/scenario/delete.py +65 -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 +67 -0
- nextmv/cli/cloud/secrets/get.py +66 -0
- nextmv/cli/cloud/secrets/list.py +60 -0
- nextmv/cli/cloud/secrets/update.py +147 -0
- nextmv/cli/cloud/shadow/__init__.py +33 -0
- nextmv/cli/cloud/shadow/create.py +184 -0
- nextmv/cli/cloud/shadow/delete.py +68 -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 +43 -0
- nextmv/cli/cloud/shadow/update.py +95 -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 +97 -0
- nextmv/cli/cloud/version/delete.py +62 -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 +270 -0
- nextmv/cli/community/list.py +265 -0
- nextmv/cli/configuration/__init__.py +23 -0
- nextmv/cli/configuration/config.py +195 -0
- nextmv/cli/configuration/create.py +94 -0
- nextmv/cli/configuration/delete.py +67 -0
- nextmv/cli/configuration/list.py +77 -0
- nextmv/cli/main.py +188 -0
- nextmv/cli/message.py +153 -0
- nextmv/cli/options.py +206 -0
- nextmv/cli/version.py +38 -0
- nextmv/cloud/__init__.py +71 -17
- nextmv/cloud/acceptance_test.py +757 -51
- nextmv/cloud/account.py +406 -17
- nextmv/cloud/application/__init__.py +957 -0
- nextmv/cloud/application/_acceptance.py +419 -0
- nextmv/cloud/application/_batch_scenario.py +860 -0
- nextmv/cloud/application/_ensemble.py +251 -0
- nextmv/cloud/application/_input_set.py +227 -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 +314 -0
- nextmv/cloud/application/_utils.py +54 -0
- nextmv/cloud/application/_version.py +303 -0
- nextmv/cloud/assets.py +48 -0
- nextmv/cloud/batch_experiment.py +294 -33
- nextmv/cloud/client.py +307 -66
- nextmv/cloud/ensemble.py +247 -0
- nextmv/cloud/input_set.py +120 -2
- nextmv/cloud/instance.py +133 -8
- nextmv/cloud/integration.py +533 -0
- nextmv/cloud/package.py +168 -53
- nextmv/cloud/scenario.py +410 -0
- nextmv/cloud/secrets.py +234 -0
- nextmv/cloud/shadow.py +190 -0
- nextmv/cloud/url.py +73 -0
- nextmv/cloud/version.py +132 -4
- nextmv/default_app/.gitignore +1 -0
- nextmv/default_app/README.md +32 -0
- nextmv/default_app/app.yaml +12 -0
- nextmv/default_app/input.json +5 -0
- nextmv/default_app/main.py +37 -0
- nextmv/default_app/requirements.txt +2 -0
- nextmv/default_app/src/__init__.py +0 -0
- nextmv/default_app/src/visuals.py +36 -0
- nextmv/deprecated.py +47 -0
- nextmv/input.py +861 -90
- nextmv/local/__init__.py +5 -0
- nextmv/local/application.py +1251 -0
- nextmv/local/executor.py +1042 -0
- nextmv/local/geojson_handler.py +323 -0
- nextmv/local/local.py +97 -0
- nextmv/local/plotly_handler.py +61 -0
- nextmv/local/runner.py +274 -0
- nextmv/logger.py +80 -9
- nextmv/manifest.py +1466 -0
- nextmv/model.py +241 -66
- nextmv/options.py +708 -115
- nextmv/output.py +1301 -274
- nextmv/polling.py +325 -0
- nextmv/run.py +1702 -0
- nextmv/safe.py +145 -0
- nextmv/status.py +122 -0
- nextmv-1.0.0.dev2.dist-info/METADATA +311 -0
- nextmv-1.0.0.dev2.dist-info/RECORD +170 -0
- {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/WHEEL +1 -1
- nextmv-1.0.0.dev2.dist-info/entry_points.txt +2 -0
- nextmv/cloud/application.py +0 -1405
- nextmv/cloud/manifest.py +0 -234
- nextmv/cloud/status.py +0 -29
- nextmv-0.18.0.dist-info/METADATA +0 -770
- nextmv-0.18.0.dist-info/RECORD +0 -25
- {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Application mixin for managing acceptance tests.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from nextmv.cloud.acceptance_test import AcceptanceTest, Metric
|
|
10
|
+
from nextmv.cloud.batch_experiment import BatchExperimentRun, ExperimentStatus
|
|
11
|
+
from nextmv.polling import DEFAULT_POLLING_OPTIONS, PollingOptions, poll
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from . import Application
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ApplicationAcceptanceMixin:
|
|
18
|
+
"""
|
|
19
|
+
Mixin class providing acceptance test methods for Application.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def acceptance_test(self: "Application", acceptance_test_id: str) -> AcceptanceTest:
|
|
23
|
+
"""
|
|
24
|
+
Retrieve details of an acceptance test.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
acceptance_test_id : str
|
|
29
|
+
ID of the acceptance test to retrieve.
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
AcceptanceTest
|
|
34
|
+
The requested acceptance test details.
|
|
35
|
+
|
|
36
|
+
Raises
|
|
37
|
+
------
|
|
38
|
+
requests.HTTPError
|
|
39
|
+
If the response status code is not 2xx.
|
|
40
|
+
|
|
41
|
+
Examples
|
|
42
|
+
--------
|
|
43
|
+
>>> test = app.acceptance_test("test-123")
|
|
44
|
+
>>> print(test.name)
|
|
45
|
+
'My Test'
|
|
46
|
+
"""
|
|
47
|
+
response = self.client.request(
|
|
48
|
+
method="GET",
|
|
49
|
+
endpoint=f"{self.experiments_endpoint}/acceptance/{acceptance_test_id}",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return AcceptanceTest.from_dict(response.json())
|
|
53
|
+
|
|
54
|
+
def acceptance_test_with_polling(
|
|
55
|
+
self: "Application",
|
|
56
|
+
acceptance_test_id: str,
|
|
57
|
+
polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
|
|
58
|
+
) -> AcceptanceTest:
|
|
59
|
+
"""
|
|
60
|
+
Retrieve details of an acceptance test using polling.
|
|
61
|
+
|
|
62
|
+
Retrieves the result of an acceptance test. This method polls for the
|
|
63
|
+
result until the test finishes executing or the polling strategy is
|
|
64
|
+
exhausted.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
acceptance_test_id : str
|
|
69
|
+
ID of the acceptance test to retrieve.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
AcceptanceTest
|
|
74
|
+
The requested acceptance test details.
|
|
75
|
+
|
|
76
|
+
Raises
|
|
77
|
+
------
|
|
78
|
+
requests.HTTPError
|
|
79
|
+
If the response status code is not 2xx.
|
|
80
|
+
|
|
81
|
+
Examples
|
|
82
|
+
--------
|
|
83
|
+
>>> test = app.acceptance_test_with_polling("test-123")
|
|
84
|
+
>>> print(test.name)
|
|
85
|
+
'My Test'
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def polling_func() -> tuple[Any, bool]:
|
|
89
|
+
acceptance_test_result = self.acceptance_test(acceptance_test_id=acceptance_test_id)
|
|
90
|
+
if acceptance_test_result.status in {
|
|
91
|
+
ExperimentStatus.COMPLETED,
|
|
92
|
+
ExperimentStatus.FAILED,
|
|
93
|
+
ExperimentStatus.DRAFT,
|
|
94
|
+
ExperimentStatus.CANCELED,
|
|
95
|
+
ExperimentStatus.DELETE_FAILED,
|
|
96
|
+
}:
|
|
97
|
+
return acceptance_test_result, True
|
|
98
|
+
|
|
99
|
+
return None, False
|
|
100
|
+
|
|
101
|
+
acceptance_test = poll(polling_options=polling_options, polling_func=polling_func)
|
|
102
|
+
|
|
103
|
+
return self.acceptance_test(acceptance_test_id=acceptance_test.id)
|
|
104
|
+
|
|
105
|
+
def delete_acceptance_test(self: "Application", acceptance_test_id: str) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Delete an acceptance test.
|
|
108
|
+
|
|
109
|
+
Deletes an acceptance test along with all the associated information
|
|
110
|
+
such as the underlying batch experiment.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
acceptance_test_id : str
|
|
115
|
+
ID of the acceptance test to delete.
|
|
116
|
+
|
|
117
|
+
Raises
|
|
118
|
+
------
|
|
119
|
+
requests.HTTPError
|
|
120
|
+
If the response status code is not 2xx.
|
|
121
|
+
|
|
122
|
+
Examples
|
|
123
|
+
--------
|
|
124
|
+
>>> app.delete_acceptance_test("test-123")
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
_ = self.client.request(
|
|
128
|
+
method="DELETE",
|
|
129
|
+
endpoint=f"{self.experiments_endpoint}/acceptance/{acceptance_test_id}",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def list_acceptance_tests(self: "Application") -> list[AcceptanceTest]:
|
|
133
|
+
"""
|
|
134
|
+
List all acceptance tests.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
list[AcceptanceTest]
|
|
139
|
+
List of all acceptance tests associated with this application.
|
|
140
|
+
|
|
141
|
+
Raises
|
|
142
|
+
------
|
|
143
|
+
requests.HTTPError
|
|
144
|
+
If the response status code is not 2xx.
|
|
145
|
+
|
|
146
|
+
Examples
|
|
147
|
+
--------
|
|
148
|
+
>>> tests = app.list_acceptance_tests()
|
|
149
|
+
>>> for test in tests:
|
|
150
|
+
... print(test.name)
|
|
151
|
+
'Test 1'
|
|
152
|
+
'Test 2'
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
response = self.client.request(
|
|
156
|
+
method="GET",
|
|
157
|
+
endpoint=f"{self.experiments_endpoint}/acceptance",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
return [AcceptanceTest.from_dict(acceptance_test) for acceptance_test in response.json()]
|
|
161
|
+
|
|
162
|
+
def new_acceptance_test( # noqa: C901
|
|
163
|
+
self: "Application",
|
|
164
|
+
candidate_instance_id: str,
|
|
165
|
+
baseline_instance_id: str,
|
|
166
|
+
id: str,
|
|
167
|
+
metrics: list[Metric | dict[str, Any]],
|
|
168
|
+
name: str | None = None,
|
|
169
|
+
input_set_id: str | None = None,
|
|
170
|
+
description: str | None = None,
|
|
171
|
+
) -> AcceptanceTest:
|
|
172
|
+
"""
|
|
173
|
+
Create a new acceptance test.
|
|
174
|
+
|
|
175
|
+
The acceptance test is based on a batch experiment. If you already
|
|
176
|
+
started a batch experiment, you don't need to provide the input_set_id
|
|
177
|
+
parameter. In that case, the ID of the acceptance test and the batch
|
|
178
|
+
experiment must be the same. If the batch experiment does not exist,
|
|
179
|
+
you can provide the input_set_id parameter and a new batch experiment
|
|
180
|
+
will be created for you.
|
|
181
|
+
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
candidate_instance_id : str
|
|
185
|
+
ID of the candidate instance.
|
|
186
|
+
baseline_instance_id : str
|
|
187
|
+
ID of the baseline instance.
|
|
188
|
+
id : str
|
|
189
|
+
ID of the acceptance test.
|
|
190
|
+
metrics : list[Union[Metric, dict[str, Any]]]
|
|
191
|
+
List of metrics to use for the acceptance test.
|
|
192
|
+
name : Optional[str], default=None
|
|
193
|
+
Name of the acceptance test. If not provided, the ID will be used as the name.
|
|
194
|
+
input_set_id : Optional[str], default=None
|
|
195
|
+
ID of the input set to use for the underlying batch experiment,
|
|
196
|
+
in case it hasn't been started.
|
|
197
|
+
description : Optional[str], default=None
|
|
198
|
+
Description of the acceptance test.
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
-------
|
|
202
|
+
AcceptanceTest
|
|
203
|
+
The created acceptance test.
|
|
204
|
+
|
|
205
|
+
Raises
|
|
206
|
+
------
|
|
207
|
+
requests.HTTPError
|
|
208
|
+
If the response status code is not 2xx.
|
|
209
|
+
ValueError
|
|
210
|
+
If the batch experiment ID does not match the acceptance test ID.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
if name is None or name == "":
|
|
214
|
+
name = id
|
|
215
|
+
|
|
216
|
+
if input_set_id is None:
|
|
217
|
+
try:
|
|
218
|
+
batch_experiment = self.batch_experiment(batch_id=id)
|
|
219
|
+
batch_experiment_id = batch_experiment.id
|
|
220
|
+
except requests.HTTPError as e:
|
|
221
|
+
if e.response.status_code != 404:
|
|
222
|
+
raise e
|
|
223
|
+
|
|
224
|
+
raise ValueError(
|
|
225
|
+
f"batch experiment {id} does not exist, input_set_id must be defined to create a new one"
|
|
226
|
+
) from e
|
|
227
|
+
else:
|
|
228
|
+
# Get all input IDs from the input set.
|
|
229
|
+
input_set = self.input_set(input_set_id=input_set_id)
|
|
230
|
+
if not input_set.input_ids:
|
|
231
|
+
raise ValueError(f"input set {input_set_id} does not contain any inputs")
|
|
232
|
+
runs = []
|
|
233
|
+
for input_id in input_set.input_ids:
|
|
234
|
+
runs.append(
|
|
235
|
+
BatchExperimentRun(
|
|
236
|
+
instance_id=candidate_instance_id,
|
|
237
|
+
input_set_id=input_set_id,
|
|
238
|
+
input_id=input_id,
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
runs.append(
|
|
242
|
+
BatchExperimentRun(
|
|
243
|
+
instance_id=baseline_instance_id,
|
|
244
|
+
input_set_id=input_set_id,
|
|
245
|
+
input_id=input_id,
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
batch_experiment_id = self.new_batch_experiment(
|
|
249
|
+
name=name,
|
|
250
|
+
description=description,
|
|
251
|
+
id=id,
|
|
252
|
+
runs=runs,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
if batch_experiment_id != id:
|
|
256
|
+
raise ValueError(f"batch experiment_id ({batch_experiment_id}) does not match acceptance test id ({id})")
|
|
257
|
+
|
|
258
|
+
payload_metrics = [{}] * len(metrics)
|
|
259
|
+
for i, metric in enumerate(metrics):
|
|
260
|
+
payload_metrics[i] = metric.to_dict() if isinstance(metric, Metric) else metric
|
|
261
|
+
|
|
262
|
+
payload = {
|
|
263
|
+
"candidate": {"instance_id": candidate_instance_id},
|
|
264
|
+
"control": {"instance_id": baseline_instance_id},
|
|
265
|
+
"metrics": payload_metrics,
|
|
266
|
+
"experiment_id": batch_experiment_id,
|
|
267
|
+
"name": name,
|
|
268
|
+
}
|
|
269
|
+
if description is not None:
|
|
270
|
+
payload["description"] = description
|
|
271
|
+
if id is not None:
|
|
272
|
+
payload["id"] = id
|
|
273
|
+
|
|
274
|
+
response = self.client.request(
|
|
275
|
+
method="POST",
|
|
276
|
+
endpoint=f"{self.experiments_endpoint}/acceptance",
|
|
277
|
+
payload=payload,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
return AcceptanceTest.from_dict(response.json())
|
|
281
|
+
|
|
282
|
+
def new_acceptance_test_with_result(
|
|
283
|
+
self: "Application",
|
|
284
|
+
candidate_instance_id: str,
|
|
285
|
+
baseline_instance_id: str,
|
|
286
|
+
id: str,
|
|
287
|
+
metrics: list[Metric | dict[str, Any]],
|
|
288
|
+
name: str | None = None,
|
|
289
|
+
input_set_id: str | None = None,
|
|
290
|
+
description: str | None = None,
|
|
291
|
+
polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
|
|
292
|
+
) -> AcceptanceTest:
|
|
293
|
+
"""
|
|
294
|
+
Create a new acceptance test and poll for the result.
|
|
295
|
+
|
|
296
|
+
This is a convenience method that combines the new_acceptance_test with polling
|
|
297
|
+
logic to check when the acceptance test is done.
|
|
298
|
+
|
|
299
|
+
Parameters
|
|
300
|
+
----------
|
|
301
|
+
candidate_instance_id : str
|
|
302
|
+
ID of the candidate instance.
|
|
303
|
+
baseline_instance_id : str
|
|
304
|
+
ID of the baseline instance.
|
|
305
|
+
id : str
|
|
306
|
+
ID of the acceptance test.
|
|
307
|
+
metrics : list[Union[Metric, dict[str, Any]]]
|
|
308
|
+
List of metrics to use for the acceptance test.
|
|
309
|
+
name : Optional[str], default=None
|
|
310
|
+
Name of the acceptance test. If not provided, the ID will be used as the name.
|
|
311
|
+
input_set_id : Optional[str], default=None
|
|
312
|
+
ID of the input set to use for the underlying batch experiment,
|
|
313
|
+
in case it hasn't been started.
|
|
314
|
+
description : Optional[str], default=None
|
|
315
|
+
Description of the acceptance test.
|
|
316
|
+
polling_options : PollingOptions, default=_DEFAULT_POLLING_OPTIONS
|
|
317
|
+
Options to use when polling for the acceptance test result.
|
|
318
|
+
|
|
319
|
+
Returns
|
|
320
|
+
-------
|
|
321
|
+
AcceptanceTest
|
|
322
|
+
The completed acceptance test with results.
|
|
323
|
+
|
|
324
|
+
Raises
|
|
325
|
+
------
|
|
326
|
+
requests.HTTPError
|
|
327
|
+
If the response status code is not 2xx.
|
|
328
|
+
TimeoutError
|
|
329
|
+
If the acceptance test does not succeed after the
|
|
330
|
+
polling strategy is exhausted based on time duration.
|
|
331
|
+
RuntimeError
|
|
332
|
+
If the acceptance test does not succeed after the
|
|
333
|
+
polling strategy is exhausted based on number of tries.
|
|
334
|
+
|
|
335
|
+
Examples
|
|
336
|
+
--------
|
|
337
|
+
>>> test = app.new_acceptance_test_with_result(
|
|
338
|
+
... candidate_instance_id="candidate-123",
|
|
339
|
+
... baseline_instance_id="baseline-456",
|
|
340
|
+
... id="test-789",
|
|
341
|
+
... metrics=[Metric(name="objective", type="numeric")],
|
|
342
|
+
... name="Performance Test",
|
|
343
|
+
... input_set_id="input-set-123"
|
|
344
|
+
... )
|
|
345
|
+
>>> print(test.status)
|
|
346
|
+
'completed'
|
|
347
|
+
"""
|
|
348
|
+
|
|
349
|
+
acceptance_test = self.new_acceptance_test(
|
|
350
|
+
candidate_instance_id=candidate_instance_id,
|
|
351
|
+
baseline_instance_id=baseline_instance_id,
|
|
352
|
+
id=id,
|
|
353
|
+
metrics=metrics,
|
|
354
|
+
name=name,
|
|
355
|
+
input_set_id=input_set_id,
|
|
356
|
+
description=description,
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return self.acceptance_test_with_polling(
|
|
360
|
+
acceptance_test_id=acceptance_test.id,
|
|
361
|
+
polling_options=polling_options,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
def update_acceptance_test(
|
|
365
|
+
self: "Application",
|
|
366
|
+
acceptance_test_id: str,
|
|
367
|
+
name: str | None = None,
|
|
368
|
+
description: str | None = None,
|
|
369
|
+
) -> AcceptanceTest:
|
|
370
|
+
"""
|
|
371
|
+
Update an acceptance test.
|
|
372
|
+
|
|
373
|
+
Parameters
|
|
374
|
+
----------
|
|
375
|
+
acceptance_test_id : str
|
|
376
|
+
ID of the acceptance test to update.
|
|
377
|
+
name : Optional[str], default=None
|
|
378
|
+
Optional name of the acceptance test.
|
|
379
|
+
description : Optional[str], default=None
|
|
380
|
+
Optional description of the acceptance test.
|
|
381
|
+
|
|
382
|
+
Returns
|
|
383
|
+
-------
|
|
384
|
+
AcceptanceTest
|
|
385
|
+
The updated acceptance test.
|
|
386
|
+
|
|
387
|
+
Raises
|
|
388
|
+
------
|
|
389
|
+
requests.HTTPError
|
|
390
|
+
If the response status code is not 2xx.
|
|
391
|
+
|
|
392
|
+
Examples
|
|
393
|
+
--------
|
|
394
|
+
>>> test = app.update_acceptance_test(
|
|
395
|
+
... acceptance_test_id="test-123",
|
|
396
|
+
... name="Updated Test Name",
|
|
397
|
+
... description="Updated description"
|
|
398
|
+
... )
|
|
399
|
+
>>> print(test.name)
|
|
400
|
+
'Updated Test Name'
|
|
401
|
+
"""
|
|
402
|
+
|
|
403
|
+
if (name is None or name == "") and (description is None or description == ""):
|
|
404
|
+
raise ValueError("at least one of name or description must be provided for update")
|
|
405
|
+
|
|
406
|
+
payload = {}
|
|
407
|
+
|
|
408
|
+
if name is not None:
|
|
409
|
+
payload["name"] = name
|
|
410
|
+
if description is not None:
|
|
411
|
+
payload["description"] = description
|
|
412
|
+
|
|
413
|
+
response = self.client.request(
|
|
414
|
+
method="PATCH",
|
|
415
|
+
endpoint=f"{self.experiments_endpoint}/acceptance/{acceptance_test_id}",
|
|
416
|
+
payload=payload,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
return AcceptanceTest.from_dict(response.json())
|