nextmv 1.0.0.dev5__py3-none-any.whl → 1.0.0.dev7__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/_serialization.py +1 -1
- nextmv/cli/cloud/acceptance/create.py +12 -12
- nextmv/cli/cloud/app/push.py +15 -15
- nextmv/cli/cloud/input_set/__init__.py +2 -0
- nextmv/cli/cloud/input_set/delete.py +67 -0
- nextmv/cli/cloud/run/create.py +4 -9
- nextmv/cli/cloud/shadow/stop.py +14 -2
- nextmv/cli/cloud/switchback/stop.py +14 -2
- nextmv/cli/community/clone.py +11 -197
- nextmv/cli/community/list.py +46 -116
- nextmv/cloud/__init__.py +4 -0
- nextmv/cloud/application/__init__.py +1 -200
- nextmv/cloud/application/_acceptance.py +13 -8
- nextmv/cloud/application/_input_set.py +42 -6
- nextmv/cloud/application/_run.py +1 -8
- nextmv/cloud/application/_shadow.py +9 -3
- nextmv/cloud/application/_switchback.py +11 -2
- nextmv/cloud/batch_experiment.py +3 -1
- nextmv/cloud/client.py +1 -1
- nextmv/cloud/community.py +441 -0
- nextmv/cloud/integration.py +7 -4
- nextmv/cloud/shadow.py +25 -0
- nextmv/cloud/switchback.py +2 -0
- nextmv/default_app/main.py +6 -4
- nextmv/local/executor.py +3 -83
- nextmv/local/geojson_handler.py +1 -1
- nextmv/manifest.py +7 -11
- nextmv/model.py +2 -2
- nextmv/options.py +1 -1
- nextmv/output.py +21 -57
- nextmv/run.py +3 -12
- {nextmv-1.0.0.dev5.dist-info → nextmv-1.0.0.dev7.dist-info}/METADATA +3 -1
- {nextmv-1.0.0.dev5.dist-info → nextmv-1.0.0.dev7.dist-info}/RECORD +37 -35
- {nextmv-1.0.0.dev5.dist-info → nextmv-1.0.0.dev7.dist-info}/WHEEL +0 -0
- {nextmv-1.0.0.dev5.dist-info → nextmv-1.0.0.dev7.dist-info}/entry_points.txt +0 -0
- {nextmv-1.0.0.dev5.dist-info → nextmv-1.0.0.dev7.dist-info}/licenses/LICENSE +0 -0
nextmv/cli/community/list.py
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
This module defines the community list command for the Nextmv CLI.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Annotated
|
|
5
|
+
from typing import Annotated
|
|
6
6
|
|
|
7
|
-
import requests
|
|
8
7
|
import rich
|
|
9
8
|
import typer
|
|
10
|
-
import yaml
|
|
11
9
|
from rich.console import Console
|
|
12
10
|
from rich.table import Table
|
|
13
11
|
|
|
14
12
|
from nextmv.cli.configuration.config import build_client
|
|
15
13
|
from nextmv.cli.message import error
|
|
16
14
|
from nextmv.cli.options import ProfileOption
|
|
15
|
+
from nextmv.cloud.client import Client
|
|
16
|
+
from nextmv.cloud.community import CommunityApp, list_community_apps
|
|
17
17
|
|
|
18
18
|
# Set up subcommand application.
|
|
19
19
|
app = typer.Typer()
|
|
@@ -62,96 +62,73 @@ def list(
|
|
|
62
62
|
if app is not None and app == "":
|
|
63
63
|
error("The --app flag cannot be an empty string.")
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
client = build_client(profile)
|
|
66
66
|
if flat and app is None:
|
|
67
|
-
|
|
67
|
+
_apps_list(client)
|
|
68
68
|
raise typer.Exit()
|
|
69
69
|
elif not flat and app is None:
|
|
70
|
-
|
|
70
|
+
_apps_table(client)
|
|
71
71
|
raise typer.Exit()
|
|
72
72
|
elif flat and app is not None and app != "":
|
|
73
|
-
|
|
73
|
+
_versions_list(client, app)
|
|
74
74
|
raise typer.Exit()
|
|
75
75
|
elif not flat and app is not None and app != "":
|
|
76
|
-
|
|
76
|
+
_versions_table(client, app)
|
|
77
77
|
raise typer.Exit()
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
def
|
|
80
|
+
def _apps_table(client: Client) -> None:
|
|
81
81
|
"""
|
|
82
|
-
|
|
82
|
+
This function prints a table of community apps.
|
|
83
83
|
|
|
84
84
|
Parameters
|
|
85
85
|
----------
|
|
86
|
-
|
|
87
|
-
The
|
|
88
|
-
|
|
89
|
-
Returns
|
|
90
|
-
-------
|
|
91
|
-
dict
|
|
92
|
-
The community apps manifest as a dictionary.
|
|
93
|
-
|
|
94
|
-
Raises
|
|
95
|
-
requests.HTTPError
|
|
96
|
-
If the response status code is not 2xx.
|
|
97
|
-
"""
|
|
98
|
-
|
|
99
|
-
response = download_file(directory="community-apps", file="manifest.yml", profile=profile)
|
|
100
|
-
manifest = yaml.safe_load(response.text)
|
|
101
|
-
|
|
102
|
-
return manifest
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def apps_table(manifest: dict[str, Any]) -> None:
|
|
106
|
-
"""
|
|
107
|
-
This function prints a table of community apps from the manifest.
|
|
108
|
-
|
|
109
|
-
Parameters
|
|
110
|
-
----------
|
|
111
|
-
manifest : dict[str, Any]
|
|
112
|
-
The community apps manifest.
|
|
86
|
+
client : Client
|
|
87
|
+
The Nextmv Cloud client to use for the request.
|
|
113
88
|
"""
|
|
114
89
|
|
|
90
|
+
apps = list_community_apps(client)
|
|
115
91
|
table = Table("Name", "Type", "Latest", "Description", border_style="cyan", header_style="cyan")
|
|
116
|
-
for app in
|
|
92
|
+
for app in apps:
|
|
117
93
|
table.add_row(
|
|
118
|
-
app.
|
|
119
|
-
app.
|
|
120
|
-
app.
|
|
121
|
-
app.
|
|
94
|
+
app.name,
|
|
95
|
+
app.app_type,
|
|
96
|
+
app.latest_app_version if app.latest_app_version is not None else "",
|
|
97
|
+
app.description,
|
|
122
98
|
)
|
|
123
99
|
|
|
124
100
|
console.print(table)
|
|
125
101
|
|
|
126
102
|
|
|
127
|
-
def
|
|
103
|
+
def _apps_list(client: Client) -> None:
|
|
128
104
|
"""
|
|
129
|
-
This function prints a flat list of community app names
|
|
105
|
+
This function prints a flat list of community app names.
|
|
130
106
|
|
|
131
107
|
Parameters
|
|
132
108
|
----------
|
|
133
|
-
|
|
134
|
-
The
|
|
109
|
+
client : Client
|
|
110
|
+
The Nextmv Cloud client to use for the request.
|
|
135
111
|
"""
|
|
136
112
|
|
|
137
|
-
|
|
113
|
+
apps = list_community_apps(client)
|
|
114
|
+
names = [app.name for app in apps]
|
|
138
115
|
print("\n".join(names))
|
|
139
116
|
|
|
140
117
|
|
|
141
|
-
def
|
|
118
|
+
def _versions_table(client: Client, app: str) -> None:
|
|
142
119
|
"""
|
|
143
120
|
This function prints a table of versions for a specific community app.
|
|
144
121
|
|
|
145
122
|
Parameters
|
|
146
123
|
----------
|
|
147
|
-
|
|
148
|
-
The
|
|
124
|
+
client : Client
|
|
125
|
+
The Nextmv Cloud client to use for the request.
|
|
149
126
|
app : str
|
|
150
127
|
The name of the community app.
|
|
151
128
|
"""
|
|
152
129
|
|
|
153
|
-
|
|
154
|
-
latest_version =
|
|
130
|
+
comm_app = _find_app(client, app)
|
|
131
|
+
latest_version = comm_app.latest_app_version if comm_app.latest_app_version is not None else ""
|
|
155
132
|
|
|
156
133
|
# Add the latest version with indicator
|
|
157
134
|
table = Table("Version", "Latest?", border_style="cyan", header_style="cyan")
|
|
@@ -159,7 +136,7 @@ def versions_table(manifest: dict[str, Any], app: str) -> None:
|
|
|
159
136
|
table.add_row("", "") # Empty row to separate latest from others.
|
|
160
137
|
|
|
161
138
|
# Add all other versions (excluding the latest)
|
|
162
|
-
versions =
|
|
139
|
+
versions = comm_app.app_versions if comm_app.app_versions is not None else []
|
|
163
140
|
for version in versions:
|
|
164
141
|
if version != latest_version:
|
|
165
142
|
table.add_row(version, "")
|
|
@@ -167,99 +144,52 @@ def versions_table(manifest: dict[str, Any], app: str) -> None:
|
|
|
167
144
|
console.print(table)
|
|
168
145
|
|
|
169
146
|
|
|
170
|
-
def
|
|
147
|
+
def _versions_list(client: Client, app: str) -> None:
|
|
171
148
|
"""
|
|
172
149
|
This function prints a flat list of versions for a specific community app.
|
|
173
150
|
|
|
174
151
|
Parameters
|
|
175
152
|
----------
|
|
176
|
-
|
|
177
|
-
The
|
|
153
|
+
client : Client
|
|
154
|
+
The Nextmv Cloud client to use for the request.
|
|
178
155
|
app : str
|
|
179
156
|
The name of the community app.
|
|
180
157
|
"""
|
|
181
158
|
|
|
182
|
-
|
|
183
|
-
versions =
|
|
159
|
+
comm_app = _find_app(client, app)
|
|
160
|
+
versions = comm_app.app_versions if comm_app.app_versions is not None else []
|
|
184
161
|
|
|
185
162
|
versions_output = ""
|
|
186
163
|
for version in versions:
|
|
187
164
|
versions_output += f"{version}\n"
|
|
188
165
|
|
|
189
|
-
print("\n".join(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def download_file(
|
|
193
|
-
directory: str,
|
|
194
|
-
file: str,
|
|
195
|
-
profile: str | None = None,
|
|
196
|
-
) -> requests.Response:
|
|
197
|
-
"""
|
|
198
|
-
Gets a file from an internal bucket and return it.
|
|
199
|
-
|
|
200
|
-
Parameters
|
|
201
|
-
----------
|
|
202
|
-
directory : str
|
|
203
|
-
The directory in the bucket where the file is located.
|
|
204
|
-
file : str
|
|
205
|
-
The name of the file to download.
|
|
206
|
-
profile : str | None
|
|
207
|
-
The profile name to use. If None, the default profile is used.
|
|
208
|
-
|
|
209
|
-
Returns
|
|
210
|
-
-------
|
|
211
|
-
requests.Response
|
|
212
|
-
The response object containing the file data.
|
|
213
|
-
|
|
214
|
-
Raises
|
|
215
|
-
requests.HTTPError
|
|
216
|
-
If the response status code is not 2xx.
|
|
217
|
-
"""
|
|
218
|
-
|
|
219
|
-
client = build_client(profile)
|
|
220
|
-
|
|
221
|
-
# Request the download URL for the file.
|
|
222
|
-
response = client.request(
|
|
223
|
-
method="GET",
|
|
224
|
-
endpoint="v0/internal/tools",
|
|
225
|
-
headers=client.headers | {"request-source": "cli"}, # Pass `client.headers` to preserve auth.
|
|
226
|
-
query_params={"file": f"{directory}/{file}"},
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
# Use the URL obtained to download the file.
|
|
230
|
-
body = response.json()
|
|
231
|
-
download_response = client.request(
|
|
232
|
-
method="GET",
|
|
233
|
-
endpoint=body.get("url"),
|
|
234
|
-
headers={"Content-Type": "application/json"},
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
return download_response
|
|
166
|
+
print("\n".join(versions_output))
|
|
238
167
|
|
|
239
168
|
|
|
240
|
-
def
|
|
169
|
+
def _find_app(client: Client, app: str) -> CommunityApp | None:
|
|
241
170
|
"""
|
|
242
171
|
Finds and returns a community app from the manifest by its name.
|
|
243
172
|
|
|
244
173
|
Parameters
|
|
245
174
|
----------
|
|
246
|
-
|
|
247
|
-
The
|
|
175
|
+
client : Client
|
|
176
|
+
The Nextmv Cloud client to use for the request.
|
|
248
177
|
app : str
|
|
249
178
|
The name of the community app to find.
|
|
250
179
|
|
|
251
180
|
Returns
|
|
252
181
|
-------
|
|
253
|
-
|
|
254
|
-
The community app
|
|
182
|
+
CommunityApp | None
|
|
183
|
+
The community app if found, otherwise None.
|
|
255
184
|
"""
|
|
256
185
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
186
|
+
comm_apps = list_community_apps(client)
|
|
187
|
+
for comm_app in comm_apps:
|
|
188
|
+
if comm_app.name == app:
|
|
189
|
+
return comm_app
|
|
260
190
|
|
|
261
191
|
# We don't use error() here to allow printing something before exiting.
|
|
262
192
|
rich.print(f"[red]Error:[/red] Community app [magenta]{app}[/magenta] was not found. Here are the available apps:")
|
|
263
|
-
|
|
193
|
+
_apps_table(client)
|
|
264
194
|
|
|
265
195
|
raise typer.Exit(code=1)
|
nextmv/cloud/__init__.py
CHANGED
|
@@ -30,6 +30,9 @@ from .batch_experiment import BatchExperimentRun as BatchExperimentRun
|
|
|
30
30
|
from .batch_experiment import ExperimentStatus as ExperimentStatus
|
|
31
31
|
from .client import Client as Client
|
|
32
32
|
from .client import get_size as get_size
|
|
33
|
+
from .community import CommunityApp as CommunityApp
|
|
34
|
+
from .community import clone_community_app as clone_community_app
|
|
35
|
+
from .community import list_community_apps as list_community_apps
|
|
33
36
|
from .ensemble import EnsembleDefinition as EnsembleDefinition
|
|
34
37
|
from .ensemble import EvaluationRule as EvaluationRule
|
|
35
38
|
from .ensemble import RuleObjective as RuleObjective
|
|
@@ -55,6 +58,7 @@ from .secrets import SecretType as SecretType
|
|
|
55
58
|
from .shadow import ShadowTest as ShadowTest
|
|
56
59
|
from .shadow import ShadowTestMetadata as ShadowTestMetadata
|
|
57
60
|
from .shadow import StartEvents as StartEvents
|
|
61
|
+
from .shadow import StopIntent as StopIntent
|
|
58
62
|
from .shadow import TerminationEvents as TerminationEvents
|
|
59
63
|
from .shadow import TestComparison as TestComparison
|
|
60
64
|
from .switchback import SwitchbackPlan as SwitchbackPlan
|
|
@@ -21,7 +21,7 @@ list_application
|
|
|
21
21
|
import json
|
|
22
22
|
import shutil
|
|
23
23
|
import sys
|
|
24
|
-
from datetime import datetime
|
|
24
|
+
from datetime import datetime
|
|
25
25
|
from enum import Enum
|
|
26
26
|
from typing import Any
|
|
27
27
|
|
|
@@ -46,9 +46,7 @@ from nextmv.cloud.application._switchback import ApplicationSwitchbackMixin
|
|
|
46
46
|
from nextmv.cloud.application._utils import _is_not_exist_error
|
|
47
47
|
from nextmv.cloud.application._version import ApplicationVersionMixin
|
|
48
48
|
from nextmv.cloud.client import Client
|
|
49
|
-
from nextmv.cloud.instance import Instance
|
|
50
49
|
from nextmv.cloud.url import UploadURL
|
|
51
|
-
from nextmv.cloud.version import Version
|
|
52
50
|
from nextmv.logger import log
|
|
53
51
|
from nextmv.manifest import Manifest
|
|
54
52
|
from nextmv.model import Model, ModelConfiguration
|
|
@@ -407,14 +405,6 @@ class Application(
|
|
|
407
405
|
model: Model | None = None,
|
|
408
406
|
model_configuration: ModelConfiguration | None = None,
|
|
409
407
|
rich_print: bool = False,
|
|
410
|
-
auto_create: bool = False,
|
|
411
|
-
version_id: str | None = None,
|
|
412
|
-
version_name: str | None = None,
|
|
413
|
-
version_description: str | None = None,
|
|
414
|
-
instance_id: str | None = None,
|
|
415
|
-
instance_name: str | None = None,
|
|
416
|
-
instance_description: str | None = None,
|
|
417
|
-
update_default_instance: bool = False,
|
|
418
408
|
) -> None:
|
|
419
409
|
"""
|
|
420
410
|
Push an app to Nextmv Cloud.
|
|
@@ -432,17 +422,6 @@ class Application(
|
|
|
432
422
|
`nextmv.Model`. The model is encoded, some dependencies and
|
|
433
423
|
accompanying files are packaged, and the app is pushed to Nextmv Cloud.
|
|
434
424
|
|
|
435
|
-
By default, this function only pushes the app. If you want to
|
|
436
|
-
automatically create a new version and instance after pushing, set
|
|
437
|
-
`auto_create=True`. The `version_id`, `version_name`, and
|
|
438
|
-
`version_description` arguments allow you to customize the new version
|
|
439
|
-
that is created. If not specified, defaults will be generated (random
|
|
440
|
-
ID, timestamped name/description). Similarly, `instance_id`,
|
|
441
|
-
`instance_name`, and `instance_description` can be used to customize
|
|
442
|
-
the new instance. If `update_default_instance` is True, the
|
|
443
|
-
application's default instance will be updated to the newly created
|
|
444
|
-
instance.
|
|
445
|
-
|
|
446
425
|
Parameters
|
|
447
426
|
----------
|
|
448
427
|
manifest : Optional[Manifest], default=None
|
|
@@ -461,30 +440,6 @@ class Application(
|
|
|
461
440
|
with `model`.
|
|
462
441
|
rich_print : bool, default=False
|
|
463
442
|
Whether to use rich printing when verbose output is enabled.
|
|
464
|
-
auto_create : bool, default=False
|
|
465
|
-
If True, automatically create a new version and instance after
|
|
466
|
-
pushing the app.
|
|
467
|
-
version_id : Optional[str], default=None
|
|
468
|
-
ID of the version to create after pushing the app. If None, a unique
|
|
469
|
-
ID will be generated.
|
|
470
|
-
version_name : Optional[str], default=None
|
|
471
|
-
Name of the version to create after pushing the app. If None, a
|
|
472
|
-
name will be generated.
|
|
473
|
-
version_description : Optional[str], default=None
|
|
474
|
-
Description of the version to create after pushing the app. If None, a
|
|
475
|
-
generic description with a timestamp will be generated.
|
|
476
|
-
instance_id : Optional[str], default=None
|
|
477
|
-
ID of the instance to create after pushing the app. If None, a unique
|
|
478
|
-
ID will be generated.
|
|
479
|
-
instance_name : Optional[str], default=None
|
|
480
|
-
Name of the instance to create after pushing the app. If None, a
|
|
481
|
-
name will be generated.
|
|
482
|
-
instance_description : Optional[str], default=None
|
|
483
|
-
Description of the instance to create after pushing the app. If None,
|
|
484
|
-
a generic description with a timestamp will be generated.
|
|
485
|
-
update_default_instance : bool, default=False
|
|
486
|
-
If True, update the application's default instance to the newly
|
|
487
|
-
created instance.
|
|
488
443
|
|
|
489
444
|
Raises
|
|
490
445
|
------
|
|
@@ -588,44 +543,6 @@ class Application(
|
|
|
588
543
|
except OSError as e:
|
|
589
544
|
raise Exception(f"error deleting output directory: {e}") from e
|
|
590
545
|
|
|
591
|
-
if not auto_create:
|
|
592
|
-
return
|
|
593
|
-
|
|
594
|
-
if verbose:
|
|
595
|
-
if rich_print:
|
|
596
|
-
rich.print(
|
|
597
|
-
f":hourglass_flowing_sand: Push completed for Nextmv application [magenta]{self.id}[/magenta], "
|
|
598
|
-
"creating a new version and instance...",
|
|
599
|
-
file=sys.stderr,
|
|
600
|
-
)
|
|
601
|
-
else:
|
|
602
|
-
log("⌛️ Push completed for the Nextmv application, creating a new version and instance...")
|
|
603
|
-
|
|
604
|
-
now = datetime.now(timezone.utc)
|
|
605
|
-
version = self.__version_on_push(
|
|
606
|
-
now=now,
|
|
607
|
-
version_id=version_id,
|
|
608
|
-
version_name=version_name,
|
|
609
|
-
version_description=version_description,
|
|
610
|
-
verbose=verbose,
|
|
611
|
-
rich_print=rich_print,
|
|
612
|
-
)
|
|
613
|
-
instance = self.__instance_on_push(
|
|
614
|
-
now=now,
|
|
615
|
-
version_id=version.id,
|
|
616
|
-
instance_id=instance_id,
|
|
617
|
-
instance_name=instance_name,
|
|
618
|
-
instance_description=instance_description,
|
|
619
|
-
verbose=verbose,
|
|
620
|
-
rich_print=rich_print,
|
|
621
|
-
)
|
|
622
|
-
self.__update_on_push(
|
|
623
|
-
instance=instance,
|
|
624
|
-
update_default_instance=update_default_instance,
|
|
625
|
-
verbose=verbose,
|
|
626
|
-
rich_print=rich_print,
|
|
627
|
-
)
|
|
628
|
-
|
|
629
546
|
def update(
|
|
630
547
|
self,
|
|
631
548
|
name: str | None = None,
|
|
@@ -867,8 +784,6 @@ class Application(
|
|
|
867
784
|
output_config = multi_config["output_configuration"] = {}
|
|
868
785
|
if content.multi_file.output.statistics:
|
|
869
786
|
output_config["statistics_path"] = content.multi_file.output.statistics
|
|
870
|
-
if content.multi_file.output.metrics:
|
|
871
|
-
output_config["metrics_path"] = content.multi_file.output.metrics
|
|
872
787
|
if content.multi_file.output.assets:
|
|
873
788
|
output_config["assets_path"] = content.multi_file.output.assets
|
|
874
789
|
if content.multi_file.output.solutions:
|
|
@@ -943,120 +858,6 @@ class Application(
|
|
|
943
858
|
log(f'💥️ Successfully pushed to application: "{self.id}".')
|
|
944
859
|
log(json.dumps(data, indent=2))
|
|
945
860
|
|
|
946
|
-
def __version_on_push(
|
|
947
|
-
self,
|
|
948
|
-
now: datetime,
|
|
949
|
-
version_id: str | None = None,
|
|
950
|
-
version_name: str | None = None,
|
|
951
|
-
version_description: str | None = None,
|
|
952
|
-
verbose: bool = False,
|
|
953
|
-
rich_print: bool = False,
|
|
954
|
-
) -> Version:
|
|
955
|
-
if version_description is None or version_description == "":
|
|
956
|
-
version_description = f"Version created automatically from push at {now.strftime('%Y-%m-%dT%H:%M:%SZ')}"
|
|
957
|
-
|
|
958
|
-
version = self.new_version(
|
|
959
|
-
id=version_id,
|
|
960
|
-
name=version_name,
|
|
961
|
-
description=version_description,
|
|
962
|
-
)
|
|
963
|
-
version_dict = version.to_dict()
|
|
964
|
-
|
|
965
|
-
if not verbose:
|
|
966
|
-
return version
|
|
967
|
-
|
|
968
|
-
if rich_print:
|
|
969
|
-
rich.print(
|
|
970
|
-
f":white_check_mark: Automatically created new version [magenta]{version.id}[/magenta].",
|
|
971
|
-
file=sys.stderr,
|
|
972
|
-
)
|
|
973
|
-
rich.print_json(data=version_dict)
|
|
974
|
-
|
|
975
|
-
return version
|
|
976
|
-
|
|
977
|
-
log(f'✅ Automatically created new version "{version.id}".')
|
|
978
|
-
log(json.dumps(version_dict, indent=2))
|
|
979
|
-
|
|
980
|
-
return version
|
|
981
|
-
|
|
982
|
-
def __instance_on_push(
|
|
983
|
-
self,
|
|
984
|
-
now: datetime,
|
|
985
|
-
version_id: str,
|
|
986
|
-
instance_id: str | None = None,
|
|
987
|
-
instance_name: str | None = None,
|
|
988
|
-
instance_description: str | None = None,
|
|
989
|
-
verbose: bool = False,
|
|
990
|
-
rich_print: bool = False,
|
|
991
|
-
) -> Instance:
|
|
992
|
-
if instance_description is None or instance_description == "":
|
|
993
|
-
instance_description = f"Instance created automatically from push at {now.strftime('%Y-%m-%dT%H:%M:%SZ')}"
|
|
994
|
-
|
|
995
|
-
instance = self.new_instance(
|
|
996
|
-
version_id=version_id,
|
|
997
|
-
id=instance_id,
|
|
998
|
-
name=instance_name,
|
|
999
|
-
description=instance_description,
|
|
1000
|
-
)
|
|
1001
|
-
instance_dict = instance.to_dict()
|
|
1002
|
-
|
|
1003
|
-
if not verbose:
|
|
1004
|
-
return instance
|
|
1005
|
-
|
|
1006
|
-
if rich_print:
|
|
1007
|
-
rich.print(
|
|
1008
|
-
f":white_check_mark: Automatically created new instance [magenta]{instance.id}[/magenta] "
|
|
1009
|
-
f"using version [magenta]{version_id}[/magenta].",
|
|
1010
|
-
file=sys.stderr,
|
|
1011
|
-
)
|
|
1012
|
-
rich.print_json(data=instance_dict)
|
|
1013
|
-
|
|
1014
|
-
return instance
|
|
1015
|
-
|
|
1016
|
-
log(f'✅ Automatically created new instance "{instance.id}" using version "{version_id}".')
|
|
1017
|
-
log(json.dumps(instance_dict, indent=2))
|
|
1018
|
-
|
|
1019
|
-
return instance
|
|
1020
|
-
|
|
1021
|
-
def __update_on_push(
|
|
1022
|
-
self,
|
|
1023
|
-
instance: Instance,
|
|
1024
|
-
update_default_instance: bool = False,
|
|
1025
|
-
verbose: bool = False,
|
|
1026
|
-
rich_print: bool = False,
|
|
1027
|
-
) -> None:
|
|
1028
|
-
if not update_default_instance:
|
|
1029
|
-
return
|
|
1030
|
-
|
|
1031
|
-
if verbose:
|
|
1032
|
-
if rich_print:
|
|
1033
|
-
rich.print(
|
|
1034
|
-
f":hourglass_flowing_sand: Updating default instance for app [magenta]{self.id}[/magenta]...",
|
|
1035
|
-
file=sys.stderr,
|
|
1036
|
-
)
|
|
1037
|
-
else:
|
|
1038
|
-
log("⌛️ Updating default instance for the Nextmv application...")
|
|
1039
|
-
|
|
1040
|
-
updated_app = self.update(default_instance_id=instance.id)
|
|
1041
|
-
if not verbose:
|
|
1042
|
-
return
|
|
1043
|
-
|
|
1044
|
-
if rich_print:
|
|
1045
|
-
rich.print(
|
|
1046
|
-
f":white_check_mark: Updated default instance to "
|
|
1047
|
-
f"[magenta]{updated_app.default_instance_id}[/magenta] for application "
|
|
1048
|
-
f"[magenta]{self.id}[/magenta].",
|
|
1049
|
-
file=sys.stderr,
|
|
1050
|
-
)
|
|
1051
|
-
rich.print_json(data=updated_app.to_dict())
|
|
1052
|
-
|
|
1053
|
-
return
|
|
1054
|
-
|
|
1055
|
-
log(
|
|
1056
|
-
f'✅ Updated default instance to "{updated_app.default_instance_id}" for application "{self.id}".',
|
|
1057
|
-
)
|
|
1058
|
-
log(json.dumps(updated_app.to_dict(), indent=2))
|
|
1059
|
-
|
|
1060
861
|
|
|
1061
862
|
def list_applications(client: Client) -> list[Application]:
|
|
1062
863
|
"""
|
|
@@ -9,6 +9,7 @@ import requests
|
|
|
9
9
|
from nextmv.cloud.acceptance_test import AcceptanceTest, Metric
|
|
10
10
|
from nextmv.cloud.batch_experiment import BatchExperimentRun, ExperimentStatus
|
|
11
11
|
from nextmv.polling import DEFAULT_POLLING_OPTIONS, PollingOptions, poll
|
|
12
|
+
from nextmv.safe import safe_id
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
14
15
|
from . import Application
|
|
@@ -163,8 +164,8 @@ class ApplicationAcceptanceMixin:
|
|
|
163
164
|
self: "Application",
|
|
164
165
|
candidate_instance_id: str,
|
|
165
166
|
baseline_instance_id: str,
|
|
166
|
-
id: str,
|
|
167
167
|
metrics: list[Metric | dict[str, Any]],
|
|
168
|
+
id: str | None = None,
|
|
168
169
|
name: str | None = None,
|
|
169
170
|
input_set_id: str | None = None,
|
|
170
171
|
description: str | None = None,
|
|
@@ -185,8 +186,8 @@ class ApplicationAcceptanceMixin:
|
|
|
185
186
|
ID of the candidate instance.
|
|
186
187
|
baseline_instance_id : str
|
|
187
188
|
ID of the baseline instance.
|
|
188
|
-
id : str
|
|
189
|
-
ID of the acceptance test.
|
|
189
|
+
id : str | None, default=None
|
|
190
|
+
ID of the acceptance test. Will be generated if not provided.
|
|
190
191
|
metrics : list[Union[Metric, dict[str, Any]]]
|
|
191
192
|
List of metrics to use for the acceptance test.
|
|
192
193
|
name : Optional[str], default=None
|
|
@@ -210,6 +211,11 @@ class ApplicationAcceptanceMixin:
|
|
|
210
211
|
If the batch experiment ID does not match the acceptance test ID.
|
|
211
212
|
"""
|
|
212
213
|
|
|
214
|
+
# Generate ID if not provided
|
|
215
|
+
if id is None or id == "":
|
|
216
|
+
id = safe_id("acceptance")
|
|
217
|
+
|
|
218
|
+
# Use ID as name if name not provided
|
|
213
219
|
if name is None or name == "":
|
|
214
220
|
name = id
|
|
215
221
|
|
|
@@ -265,11 +271,10 @@ class ApplicationAcceptanceMixin:
|
|
|
265
271
|
"metrics": payload_metrics,
|
|
266
272
|
"experiment_id": batch_experiment_id,
|
|
267
273
|
"name": name,
|
|
274
|
+
"id": id,
|
|
268
275
|
}
|
|
269
276
|
if description is not None:
|
|
270
277
|
payload["description"] = description
|
|
271
|
-
if id is not None:
|
|
272
|
-
payload["id"] = id
|
|
273
278
|
|
|
274
279
|
response = self.client.request(
|
|
275
280
|
method="POST",
|
|
@@ -283,8 +288,8 @@ class ApplicationAcceptanceMixin:
|
|
|
283
288
|
self: "Application",
|
|
284
289
|
candidate_instance_id: str,
|
|
285
290
|
baseline_instance_id: str,
|
|
286
|
-
id: str,
|
|
287
291
|
metrics: list[Metric | dict[str, Any]],
|
|
292
|
+
id: str | None = None,
|
|
288
293
|
name: str | None = None,
|
|
289
294
|
input_set_id: str | None = None,
|
|
290
295
|
description: str | None = None,
|
|
@@ -302,8 +307,8 @@ class ApplicationAcceptanceMixin:
|
|
|
302
307
|
ID of the candidate instance.
|
|
303
308
|
baseline_instance_id : str
|
|
304
309
|
ID of the baseline instance.
|
|
305
|
-
id : str
|
|
306
|
-
ID of the acceptance test.
|
|
310
|
+
id : str | None, default=None
|
|
311
|
+
ID of the acceptance test. Will be generated if not provided.
|
|
307
312
|
metrics : list[Union[Metric, dict[str, Any]]]
|
|
308
313
|
List of metrics to use for the acceptance test.
|
|
309
314
|
name : Optional[str], default=None
|