nextmv 1.0.0.dev7__py3-none-any.whl → 1.0.0.dev8__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 +0 -2
- nextmv/cli/cloud/run/create.py +9 -4
- nextmv/cli/cloud/shadow/stop.py +2 -14
- nextmv/cli/cloud/switchback/stop.py +2 -14
- nextmv/cli/community/clone.py +197 -11
- nextmv/cli/community/list.py +116 -46
- nextmv/cloud/__init__.py +0 -4
- nextmv/cloud/application/__init__.py +200 -1
- nextmv/cloud/application/_acceptance.py +8 -13
- nextmv/cloud/application/_input_set.py +6 -42
- nextmv/cloud/application/_run.py +8 -1
- nextmv/cloud/application/_shadow.py +3 -9
- nextmv/cloud/application/_switchback.py +2 -11
- nextmv/cloud/batch_experiment.py +1 -3
- nextmv/cloud/client.py +1 -1
- nextmv/cloud/integration.py +4 -7
- nextmv/cloud/shadow.py +0 -25
- nextmv/cloud/switchback.py +0 -2
- nextmv/default_app/main.py +4 -6
- nextmv/local/executor.py +83 -3
- nextmv/local/geojson_handler.py +1 -1
- nextmv/manifest.py +11 -7
- nextmv/model.py +2 -2
- nextmv/options.py +1 -1
- nextmv/output.py +57 -21
- nextmv/run.py +12 -3
- {nextmv-1.0.0.dev7.dist-info → nextmv-1.0.0.dev8.dist-info}/METADATA +1 -3
- {nextmv-1.0.0.dev7.dist-info → nextmv-1.0.0.dev8.dist-info}/RECORD +35 -37
- nextmv/cli/cloud/input_set/delete.py +0 -67
- nextmv/cloud/community.py +0 -441
- {nextmv-1.0.0.dev7.dist-info → nextmv-1.0.0.dev8.dist-info}/WHEEL +0 -0
- {nextmv-1.0.0.dev7.dist-info → nextmv-1.0.0.dev8.dist-info}/entry_points.txt +0 -0
- {nextmv-1.0.0.dev7.dist-info → nextmv-1.0.0.dev8.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,7 +6,6 @@ from datetime import datetime
|
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
from nextmv.cloud.input_set import InputSet, ManagedInput
|
|
9
|
-
from nextmv.safe import safe_id
|
|
10
9
|
|
|
11
10
|
if TYPE_CHECKING:
|
|
12
11
|
from . import Application
|
|
@@ -17,32 +16,6 @@ class ApplicationInputSetMixin:
|
|
|
17
16
|
Mixin class for managing app input sets within an application.
|
|
18
17
|
"""
|
|
19
18
|
|
|
20
|
-
def delete_input_set(self: "Application", input_set_id: str) -> None:
|
|
21
|
-
"""
|
|
22
|
-
Delete an input set.
|
|
23
|
-
|
|
24
|
-
Deletes an input set along with all the associated information.
|
|
25
|
-
|
|
26
|
-
Parameters
|
|
27
|
-
----------
|
|
28
|
-
input_set_id : str
|
|
29
|
-
ID of the input set to delete.
|
|
30
|
-
|
|
31
|
-
Raises
|
|
32
|
-
------
|
|
33
|
-
requests.HTTPError
|
|
34
|
-
If the response status code is not 2xx.
|
|
35
|
-
|
|
36
|
-
Examples
|
|
37
|
-
--------
|
|
38
|
-
>>> app.delete_input_set("input-set-123")
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
_ = self.client.request(
|
|
42
|
-
method="DELETE",
|
|
43
|
-
endpoint=f"{self.experiments_endpoint}/inputsets/{input_set_id}",
|
|
44
|
-
)
|
|
45
|
-
|
|
46
19
|
def input_set(self: "Application", input_set_id: str) -> InputSet:
|
|
47
20
|
"""
|
|
48
21
|
Get an input set.
|
|
@@ -108,8 +81,8 @@ class ApplicationInputSetMixin:
|
|
|
108
81
|
|
|
109
82
|
def new_input_set(
|
|
110
83
|
self: "Application",
|
|
111
|
-
id: str
|
|
112
|
-
name: str
|
|
84
|
+
id: str,
|
|
85
|
+
name: str,
|
|
113
86
|
description: str | None = None,
|
|
114
87
|
end_time: datetime | None = None,
|
|
115
88
|
instance_id: str | None = None,
|
|
@@ -134,11 +107,10 @@ class ApplicationInputSetMixin:
|
|
|
134
107
|
|
|
135
108
|
Parameters
|
|
136
109
|
----------
|
|
137
|
-
id: str
|
|
138
|
-
ID of the input set
|
|
139
|
-
name: str
|
|
140
|
-
Name of the input set.
|
|
141
|
-
the name.
|
|
110
|
+
id: str
|
|
111
|
+
ID of the input set
|
|
112
|
+
name: str
|
|
113
|
+
Name of the input set.
|
|
142
114
|
description: Optional[str]
|
|
143
115
|
Optional description of the input set.
|
|
144
116
|
end_time: Optional[datetime]
|
|
@@ -173,14 +145,6 @@ class ApplicationInputSetMixin:
|
|
|
173
145
|
If the response status code is not 2xx.
|
|
174
146
|
"""
|
|
175
147
|
|
|
176
|
-
# Generate ID if not provided
|
|
177
|
-
if id is None or id == "":
|
|
178
|
-
id = safe_id("input-set")
|
|
179
|
-
|
|
180
|
-
# Use ID as name if name not provided
|
|
181
|
-
if name is None or name == "":
|
|
182
|
-
name = id
|
|
183
|
-
|
|
184
148
|
payload = {
|
|
185
149
|
"id": id,
|
|
186
150
|
"name": name,
|
nextmv/cloud/application/_run.py
CHANGED
|
@@ -21,7 +21,14 @@ from nextmv.cloud.url import DownloadURL
|
|
|
21
21
|
from nextmv.input import Input, InputFormat
|
|
22
22
|
from nextmv.logger import log
|
|
23
23
|
from nextmv.options import Options
|
|
24
|
-
from nextmv.output import
|
|
24
|
+
from nextmv.output import (
|
|
25
|
+
ASSETS_KEY,
|
|
26
|
+
STATISTICS_KEY,
|
|
27
|
+
Asset,
|
|
28
|
+
Output,
|
|
29
|
+
OutputFormat,
|
|
30
|
+
Statistics,
|
|
31
|
+
)
|
|
25
32
|
from nextmv.polling import DEFAULT_POLLING_OPTIONS, PollingOptions, poll
|
|
26
33
|
from nextmv.run import (
|
|
27
34
|
ExternalRunResult,
|
|
@@ -4,7 +4,7 @@ Application mixin for managing shadow tests.
|
|
|
4
4
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
|
-
from nextmv.cloud.shadow import ShadowTest, ShadowTestMetadata, StartEvents,
|
|
7
|
+
from nextmv.cloud.shadow import ShadowTest, ShadowTestMetadata, StartEvents, TerminationEvents
|
|
8
8
|
from nextmv.run import Run
|
|
9
9
|
from nextmv.safe import safe_id
|
|
10
10
|
|
|
@@ -248,7 +248,7 @@ class ApplicationShadowMixin:
|
|
|
248
248
|
endpoint=f"{self.experiments_endpoint}/shadow/{shadow_test_id}/start",
|
|
249
249
|
)
|
|
250
250
|
|
|
251
|
-
def stop_shadow_test(self: "Application", shadow_test_id: str
|
|
251
|
+
def stop_shadow_test(self: "Application", shadow_test_id: str) -> None:
|
|
252
252
|
"""
|
|
253
253
|
Stop a shadow test. The test should already have started before using
|
|
254
254
|
this method.
|
|
@@ -257,22 +257,16 @@ class ApplicationShadowMixin:
|
|
|
257
257
|
----------
|
|
258
258
|
shadow_test_id : str
|
|
259
259
|
ID of the shadow test to stop.
|
|
260
|
-
|
|
261
|
-
Intent for stopping the shadow test.
|
|
260
|
+
|
|
262
261
|
Raises
|
|
263
262
|
------
|
|
264
263
|
requests.HTTPError
|
|
265
264
|
If the response status code is not 2xx.
|
|
266
265
|
"""
|
|
267
266
|
|
|
268
|
-
payload = {
|
|
269
|
-
"intent": intent.value,
|
|
270
|
-
}
|
|
271
|
-
|
|
272
267
|
_ = self.client.request(
|
|
273
268
|
method="PUT",
|
|
274
269
|
endpoint=f"{self.experiments_endpoint}/shadow/{shadow_test_id}/stop",
|
|
275
|
-
payload=payload,
|
|
276
270
|
)
|
|
277
271
|
|
|
278
272
|
def update_shadow_test(
|
|
@@ -5,7 +5,6 @@ Application mixin for managing switchback tests.
|
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
|
-
from nextmv.cloud.shadow import StopIntent
|
|
9
8
|
from nextmv.cloud.switchback import SwitchbackTest, SwitchbackTestMetadata, TestComparisonSingle
|
|
10
9
|
from nextmv.run import Run
|
|
11
10
|
from nextmv.safe import safe_id
|
|
@@ -217,7 +216,7 @@ class ApplicationSwitchbackMixin:
|
|
|
217
216
|
payload = {
|
|
218
217
|
"id": switchback_test_id,
|
|
219
218
|
"name": name,
|
|
220
|
-
"comparison": comparison
|
|
219
|
+
"comparison": comparison,
|
|
221
220
|
"generate_random_plan": {
|
|
222
221
|
"unit_duration_minutes": unit_duration_minutes,
|
|
223
222
|
"units": units,
|
|
@@ -258,7 +257,7 @@ class ApplicationSwitchbackMixin:
|
|
|
258
257
|
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/start",
|
|
259
258
|
)
|
|
260
259
|
|
|
261
|
-
def stop_switchback_test(self: "Application", switchback_test_id: str
|
|
260
|
+
def stop_switchback_test(self: "Application", switchback_test_id: str) -> None:
|
|
262
261
|
"""
|
|
263
262
|
Stop a switchback test. The test should already have started before using
|
|
264
263
|
this method.
|
|
@@ -268,23 +267,15 @@ class ApplicationSwitchbackMixin:
|
|
|
268
267
|
switchback_test_id : str
|
|
269
268
|
ID of the switchback test to stop.
|
|
270
269
|
|
|
271
|
-
intent : StopIntent
|
|
272
|
-
Intent for stopping the switchback test.
|
|
273
|
-
|
|
274
270
|
Raises
|
|
275
271
|
------
|
|
276
272
|
requests.HTTPError
|
|
277
273
|
If the response status code is not 2xx.
|
|
278
274
|
"""
|
|
279
275
|
|
|
280
|
-
payload = {
|
|
281
|
-
"intent": intent.value,
|
|
282
|
-
}
|
|
283
|
-
|
|
284
276
|
_ = self.client.request(
|
|
285
277
|
method="PUT",
|
|
286
278
|
endpoint=f"{self.experiments_endpoint}/switchback/{switchback_test_id}/stop",
|
|
287
|
-
payload=payload,
|
|
288
279
|
)
|
|
289
280
|
|
|
290
281
|
def update_switchback_test(
|
nextmv/cloud/batch_experiment.py
CHANGED
|
@@ -30,9 +30,7 @@ class ExperimentStatus(str, Enum):
|
|
|
30
30
|
|
|
31
31
|
You can import the `ExperimentStatus` class directly from `cloud`:
|
|
32
32
|
|
|
33
|
-
```python
|
|
34
|
-
from nextmv.cloud import ExperimentStatus
|
|
35
|
-
```
|
|
33
|
+
```python from nextmv.cloud import ExperimentStatus ```
|
|
36
34
|
|
|
37
35
|
This enum represents the comprehensive set of possible states for an
|
|
38
36
|
experiment in Nextmv Cloud.
|
nextmv/cloud/client.py
CHANGED
|
@@ -303,7 +303,7 @@ class Client:
|
|
|
303
303
|
if data is not None:
|
|
304
304
|
kwargs["data"] = data
|
|
305
305
|
if payload is not None:
|
|
306
|
-
if isinstance(payload,
|
|
306
|
+
if isinstance(payload, dict | list):
|
|
307
307
|
data = deflated_serialize_json(payload, json_configurations=json_configurations)
|
|
308
308
|
kwargs["data"] = data
|
|
309
309
|
else:
|
nextmv/cloud/integration.py
CHANGED
|
@@ -225,12 +225,12 @@ class Integration(BaseModel):
|
|
|
225
225
|
def new( # noqa: C901
|
|
226
226
|
cls,
|
|
227
227
|
client: Client,
|
|
228
|
+
name: str,
|
|
228
229
|
integration_type: IntegrationType | str,
|
|
229
230
|
exec_types: list[ManifestType | str],
|
|
230
231
|
provider: IntegrationProvider | str,
|
|
231
232
|
provider_config: dict[str, Any],
|
|
232
233
|
integration_id: str | None = None,
|
|
233
|
-
name: str | None = None,
|
|
234
234
|
description: str | None = None,
|
|
235
235
|
is_global: bool = False,
|
|
236
236
|
application_ids: list[str] | None = None,
|
|
@@ -243,6 +243,8 @@ class Integration(BaseModel):
|
|
|
243
243
|
----------
|
|
244
244
|
client : Client
|
|
245
245
|
Client to use for interacting with the Nextmv Cloud API.
|
|
246
|
+
name : str
|
|
247
|
+
The name of the integration.
|
|
246
248
|
integration_type : IntegrationType | str
|
|
247
249
|
The type of the integration. Please refer to the `IntegrationType`
|
|
248
250
|
enum for possible values.
|
|
@@ -257,9 +259,6 @@ class Integration(BaseModel):
|
|
|
257
259
|
integration_id : str, optional
|
|
258
260
|
The unique identifier of the integration. If not provided,
|
|
259
261
|
it will be generated automatically.
|
|
260
|
-
name : str | None, optional
|
|
261
|
-
The name of the integration. If not provided, the integration ID
|
|
262
|
-
will be used as the name.
|
|
263
262
|
description : str, optional
|
|
264
263
|
An optional description of the integration.
|
|
265
264
|
is_global : bool, optional, default=False
|
|
@@ -303,10 +302,8 @@ class Integration(BaseModel):
|
|
|
303
302
|
elif not is_global and application_ids is None:
|
|
304
303
|
raise ValueError("A non-global integration must have specific application IDs.")
|
|
305
304
|
|
|
306
|
-
if integration_id is None
|
|
305
|
+
if integration_id is None:
|
|
307
306
|
integration_id = safe_id("integration")
|
|
308
|
-
if name is None or name == "":
|
|
309
|
-
name = integration_id
|
|
310
307
|
|
|
311
308
|
if exist_ok:
|
|
312
309
|
try:
|
nextmv/cloud/shadow.py
CHANGED
|
@@ -19,7 +19,6 @@ ShadowTest
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
from datetime import datetime
|
|
22
|
-
from enum import Enum
|
|
23
22
|
from typing import Any
|
|
24
23
|
|
|
25
24
|
from pydantic import AliasChoices, Field
|
|
@@ -228,27 +227,3 @@ class ShadowTest(BaseModel):
|
|
|
228
227
|
"""Grouped distributional summaries of the shadow test."""
|
|
229
228
|
runs: list[Run] | None = None
|
|
230
229
|
"""List of runs in the shadow test."""
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
class StopIntent(str, Enum):
|
|
234
|
-
"""
|
|
235
|
-
Intent for stopping a shadow test.
|
|
236
|
-
|
|
237
|
-
You can import the `StopIntent` class directly from `cloud`:
|
|
238
|
-
|
|
239
|
-
```python
|
|
240
|
-
from nextmv.cloud import StopIntent
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
Attributes
|
|
244
|
-
----------
|
|
245
|
-
complete : str
|
|
246
|
-
The test is marked as complete.
|
|
247
|
-
cancel : str
|
|
248
|
-
The test is canceled.
|
|
249
|
-
"""
|
|
250
|
-
|
|
251
|
-
complete = "complete"
|
|
252
|
-
"""The test is marked as complete."""
|
|
253
|
-
cancel = "cancel"
|
|
254
|
-
"""The test is canceled."""
|
nextmv/cloud/switchback.py
CHANGED
|
@@ -45,8 +45,6 @@ class TestComparisonSingle(BaseModel):
|
|
|
45
45
|
ID of the candidate instance for comparison.
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
|
-
__test__ = False # Prevents pytest from collecting this class as a test case
|
|
49
|
-
|
|
50
48
|
baseline_instance_id: str
|
|
51
49
|
"""ID of the baseline instance for comparison."""
|
|
52
50
|
candidate_instance_id: str
|
nextmv/default_app/main.py
CHANGED
|
@@ -26,12 +26,10 @@ assets = create_visuals(name, input.data["radius"], input.data["distance"])
|
|
|
26
26
|
output = nextmv.Output(
|
|
27
27
|
options=options,
|
|
28
28
|
solution={"message": message},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
),
|
|
34
|
-
),
|
|
29
|
+
metrics={
|
|
30
|
+
"value": 1.23,
|
|
31
|
+
"custom": {"message": message},
|
|
32
|
+
},
|
|
35
33
|
assets=assets,
|
|
36
34
|
)
|
|
37
35
|
nextmv.write(output)
|
nextmv/local/executor.py
CHANGED
|
@@ -22,6 +22,8 @@ process_run_information
|
|
|
22
22
|
Function to update run metadata including duration and status.
|
|
23
23
|
process_run_logs
|
|
24
24
|
Function to process and save run logs.
|
|
25
|
+
process_run_metrics
|
|
26
|
+
Function to process and save run metrics.
|
|
25
27
|
process_run_statistics
|
|
26
28
|
Function to process and save run statistics.
|
|
27
29
|
process_run_assets
|
|
@@ -57,7 +59,16 @@ from nextmv.local.local import (
|
|
|
57
59
|
)
|
|
58
60
|
from nextmv.local.plotly_handler import handle_plotly_visual
|
|
59
61
|
from nextmv.manifest import Manifest, ManifestType
|
|
60
|
-
from nextmv.output import
|
|
62
|
+
from nextmv.output import (
|
|
63
|
+
ASSETS_KEY,
|
|
64
|
+
METRICS_KEY,
|
|
65
|
+
OUTPUTS_KEY,
|
|
66
|
+
SOLUTIONS_KEY,
|
|
67
|
+
STATISTICS_KEY,
|
|
68
|
+
Asset,
|
|
69
|
+
OutputFormat,
|
|
70
|
+
VisualSchema,
|
|
71
|
+
)
|
|
61
72
|
from nextmv.status import StatusV2
|
|
62
73
|
|
|
63
74
|
|
|
@@ -305,7 +316,7 @@ def process_run_output(
|
|
|
305
316
|
) -> None:
|
|
306
317
|
"""
|
|
307
318
|
Processes the result of the subprocess run. This function is in charge of
|
|
308
|
-
handling the run results, including solutions, statistics, logs, assets,
|
|
319
|
+
handling the run results, including solutions, statistics, metrics, logs, assets,
|
|
309
320
|
and visuals.
|
|
310
321
|
|
|
311
322
|
Parameters
|
|
@@ -347,6 +358,13 @@ def process_run_output(
|
|
|
347
358
|
result=result,
|
|
348
359
|
stdout_output=stdout_output,
|
|
349
360
|
)
|
|
361
|
+
process_run_metrics(
|
|
362
|
+
temp_run_outputs_dir=temp_run_outputs_dir,
|
|
363
|
+
outputs_dir=outputs_dir,
|
|
364
|
+
stdout_output=stdout_output,
|
|
365
|
+
temp_src=temp_src,
|
|
366
|
+
manifest=manifest,
|
|
367
|
+
)
|
|
350
368
|
process_run_statistics(
|
|
351
369
|
temp_run_outputs_dir=temp_run_outputs_dir,
|
|
352
370
|
outputs_dir=outputs_dir,
|
|
@@ -499,6 +517,65 @@ def process_run_logs(
|
|
|
499
517
|
|
|
500
518
|
f.write(std_err)
|
|
501
519
|
|
|
520
|
+
def process_run_metrics(
|
|
521
|
+
temp_run_outputs_dir: str,
|
|
522
|
+
outputs_dir: str,
|
|
523
|
+
stdout_output: str | dict[str, Any],
|
|
524
|
+
temp_src: str,
|
|
525
|
+
manifest: Manifest,
|
|
526
|
+
) -> None:
|
|
527
|
+
"""
|
|
528
|
+
Processes the metrics of the run. Checks for an outputs/metrics folder
|
|
529
|
+
or custom metrics file location from manifest. If found, copies to run
|
|
530
|
+
directory. Otherwise, attempts to extract metrics from stdout.
|
|
531
|
+
|
|
532
|
+
Parameters
|
|
533
|
+
----------
|
|
534
|
+
temp_run_outputs_dir : str
|
|
535
|
+
The path to the temporary outputs directory.
|
|
536
|
+
outputs_dir : str
|
|
537
|
+
The path to the outputs directory in the run directory.
|
|
538
|
+
stdout_output : Union[str, dict[str, Any]]
|
|
539
|
+
The stdout output of the run, either as raw string or parsed dictionary.
|
|
540
|
+
temp_src : str
|
|
541
|
+
The path to the temporary source directory.
|
|
542
|
+
manifest : Manifest
|
|
543
|
+
The application manifest containing configuration and custom paths.
|
|
544
|
+
"""
|
|
545
|
+
|
|
546
|
+
metrics_dst = os.path.join(outputs_dir, METRICS_KEY)
|
|
547
|
+
os.makedirs(metrics_dst, exist_ok=True)
|
|
548
|
+
metrics_file = f"{METRICS_KEY}.json"
|
|
549
|
+
|
|
550
|
+
# Check for custom location in manifest and override metrics_src if needed.
|
|
551
|
+
if (
|
|
552
|
+
manifest.configuration is not None
|
|
553
|
+
and manifest.configuration.content is not None
|
|
554
|
+
and manifest.configuration.content.format == OutputFormat.MULTI_FILE
|
|
555
|
+
and manifest.configuration.content.multi_file is not None
|
|
556
|
+
):
|
|
557
|
+
metrics_src_file = os.path.join(temp_src, manifest.configuration.content.multi_file.output.metrics)
|
|
558
|
+
|
|
559
|
+
# If the custom metrics file exists, copy it to the metrics destination
|
|
560
|
+
if os.path.exists(metrics_src_file) and os.path.isfile(metrics_src_file):
|
|
561
|
+
metrics_dst_file = os.path.join(metrics_dst, metrics_file)
|
|
562
|
+
shutil.copy2(metrics_src_file, metrics_dst_file)
|
|
563
|
+
return
|
|
564
|
+
|
|
565
|
+
metrics_src = os.path.join(temp_run_outputs_dir, METRICS_KEY)
|
|
566
|
+
if os.path.exists(metrics_src) and os.path.isdir(metrics_src):
|
|
567
|
+
shutil.copytree(metrics_src, metrics_dst, dirs_exist_ok=True)
|
|
568
|
+
return
|
|
569
|
+
|
|
570
|
+
if not isinstance(stdout_output, dict):
|
|
571
|
+
return
|
|
572
|
+
|
|
573
|
+
if METRICS_KEY not in stdout_output:
|
|
574
|
+
return
|
|
575
|
+
|
|
576
|
+
with open(os.path.join(metrics_dst, metrics_file), "w") as f:
|
|
577
|
+
metrics = {METRICS_KEY: stdout_output[METRICS_KEY]}
|
|
578
|
+
json.dump(metrics, f, indent=2)
|
|
502
579
|
|
|
503
580
|
def process_run_statistics(
|
|
504
581
|
temp_run_outputs_dir: str,
|
|
@@ -508,6 +585,9 @@ def process_run_statistics(
|
|
|
508
585
|
manifest: Manifest,
|
|
509
586
|
) -> None:
|
|
510
587
|
"""
|
|
588
|
+
!!! warning
|
|
589
|
+
`process_run_statistics` is deprecated, use `process_run_metrics` instead.
|
|
590
|
+
|
|
511
591
|
Processes the statistics of the run. Checks for an outputs/statistics folder
|
|
512
592
|
or custom statistics file location from manifest. If found, copies to run
|
|
513
593
|
directory. Otherwise, attempts to extract statistics from stdout.
|
|
@@ -848,7 +928,7 @@ def _copy_new_or_modified_files( # noqa: C901
|
|
|
848
928
|
This function identifies files that are either new (not present in the original
|
|
849
929
|
source) or have been modified (different content, checksum, or modification time)
|
|
850
930
|
compared to the original source. It excludes files that exist in specified
|
|
851
|
-
exclusion directories to avoid copying input data, statistics, or assets as
|
|
931
|
+
exclusion directories to avoid copying input data, statistics, metrics, or assets as
|
|
852
932
|
solution outputs.
|
|
853
933
|
|
|
854
934
|
Parameters
|
nextmv/local/geojson_handler.py
CHANGED
|
@@ -111,7 +111,7 @@ def extract_coordinates(coords, all_coords) -> None:
|
|
|
111
111
|
like Polygons and MultiPolygons
|
|
112
112
|
"""
|
|
113
113
|
if isinstance(coords, list):
|
|
114
|
-
if len(coords) == 2 and isinstance(coords[0],
|
|
114
|
+
if len(coords) == 2 and isinstance(coords[0], int | float) and isinstance(coords[1], int | float):
|
|
115
115
|
# This is a coordinate pair [lon, lat]
|
|
116
116
|
all_coords.append(coords)
|
|
117
117
|
else:
|
nextmv/manifest.py
CHANGED
|
@@ -829,7 +829,9 @@ class ManifestContentMultiFileOutput(BaseModel):
|
|
|
829
829
|
Parameters
|
|
830
830
|
----------
|
|
831
831
|
statistics : Optional[str], default=""
|
|
832
|
-
The path to the statistics file.
|
|
832
|
+
Deprecated: Use `metrics` instead. The path to the statistics file.
|
|
833
|
+
metrics : Optional[str], default=""
|
|
834
|
+
The path to the metrics file.
|
|
833
835
|
assets : Optional[str], default=""
|
|
834
836
|
The path to the assets file.
|
|
835
837
|
solutions : Optional[str], default=""
|
|
@@ -839,16 +841,18 @@ class ManifestContentMultiFileOutput(BaseModel):
|
|
|
839
841
|
--------
|
|
840
842
|
>>> from nextmv import ManifestContentMultiFileOutput
|
|
841
843
|
>>> output_config = ManifestContentMultiFileOutput(
|
|
842
|
-
...
|
|
844
|
+
... metrics="my-outputs/metrics.json",
|
|
843
845
|
... assets="my-outputs/assets.json",
|
|
844
846
|
... solutions="my-outputs/solutions/"
|
|
845
847
|
... )
|
|
846
|
-
>>> output_config.
|
|
847
|
-
'my-outputs/
|
|
848
|
+
>>> output_config.metrics
|
|
849
|
+
'my-outputs/metrics.json'
|
|
848
850
|
"""
|
|
849
851
|
|
|
850
852
|
statistics: str | None = ""
|
|
851
|
-
"""The path to the statistics file."""
|
|
853
|
+
"""Deprecated: Use `metrics` instead. The path to the statistics file."""
|
|
854
|
+
metrics: str | None = ""
|
|
855
|
+
"""The path to the metrics file."""
|
|
852
856
|
assets: str | None = ""
|
|
853
857
|
"""The path to the assets file."""
|
|
854
858
|
solutions: str | None = ""
|
|
@@ -878,7 +882,7 @@ class ManifestContentMultiFile(BaseModel):
|
|
|
878
882
|
>>> multi_file_config = ManifestContentMultiFile(
|
|
879
883
|
... input=ManifestContentMultiFileInput(path="data/input/"),
|
|
880
884
|
... output=ManifestContentMultiFileOutput(
|
|
881
|
-
...
|
|
885
|
+
... metrics="my-outputs/metrics.json",
|
|
882
886
|
... assets="my-outputs/assets.json",
|
|
883
887
|
... solutions="my-outputs/solutions/"
|
|
884
888
|
... )
|
|
@@ -919,7 +923,7 @@ class ManifestContent(BaseModel):
|
|
|
919
923
|
... multi_file=ManifestContentMultiFile(
|
|
920
924
|
... input=ManifestContentMultiFileInput(path="data/input/"),
|
|
921
925
|
... output=ManifestContentMultiFileOutput(
|
|
922
|
-
...
|
|
926
|
+
... metrics="my-outputs/metrics.json",
|
|
923
927
|
... assets="my-outputs/assets.json",
|
|
924
928
|
... solutions="my-outputs/solutions/"
|
|
925
929
|
... )
|
nextmv/model.py
CHANGED
|
@@ -195,7 +195,7 @@ class Model:
|
|
|
195
195
|
... return nextmv.Output(
|
|
196
196
|
... options=input.options,
|
|
197
197
|
... solution=nextroute_output.solutions[0].to_dict(),
|
|
198
|
-
...
|
|
198
|
+
... metrics=nextroute_output.metrics.to_dict(),
|
|
199
199
|
... )
|
|
200
200
|
"""
|
|
201
201
|
|
|
@@ -234,7 +234,7 @@ class Model:
|
|
|
234
234
|
... return Output(
|
|
235
235
|
... options=input.options,
|
|
236
236
|
... solution=result,
|
|
237
|
-
...
|
|
237
|
+
... metrics={"processing_time": 0.5}
|
|
238
238
|
... )
|
|
239
239
|
"""
|
|
240
240
|
|