nominal 1.100.0__py3-none-any.whl → 1.102.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.
- CHANGELOG.md +32 -0
- nominal/core/__init__.py +3 -1
- nominal/core/_event_types.py +100 -0
- nominal/core/_utils/query_tools.py +50 -47
- nominal/core/asset.py +174 -45
- nominal/core/client.py +127 -95
- nominal/core/dataset.py +284 -1
- nominal/core/datasource.py +6 -1
- nominal/core/event.py +73 -34
- nominal/core/filetype.py +2 -1
- nominal/core/run.py +85 -4
- nominal/experimental/migration/migration_utils.py +107 -8
- nominal/experimental/rust_streaming/rust_write_stream.py +1 -3
- {nominal-1.100.0.dist-info → nominal-1.102.0.dist-info}/METADATA +2 -2
- {nominal-1.100.0.dist-info → nominal-1.102.0.dist-info}/RECORD +18 -17
- {nominal-1.100.0.dist-info → nominal-1.102.0.dist-info}/WHEEL +0 -0
- {nominal-1.100.0.dist-info → nominal-1.102.0.dist-info}/entry_points.txt +0 -0
- {nominal-1.100.0.dist-info → nominal-1.102.0.dist-info}/licenses/LICENSE +0 -0
nominal/core/datasource.py
CHANGED
|
@@ -396,7 +396,12 @@ def _get_write_stream(
|
|
|
396
396
|
)
|
|
397
397
|
elif data_format == "rust_experimental":
|
|
398
398
|
# Delayed import intentionally in case of any issues with experimental and pre-compiled binaries
|
|
399
|
-
|
|
399
|
+
try:
|
|
400
|
+
from nominal.experimental.rust_streaming.rust_write_stream import RustWriteStream
|
|
401
|
+
except ImportError as ex:
|
|
402
|
+
raise ImportError(
|
|
403
|
+
"nominal-streaming is required to use get_write_stream with data_format='rust_experimental'"
|
|
404
|
+
) from ex
|
|
400
405
|
|
|
401
406
|
return RustWriteStream._from_datasource(
|
|
402
407
|
write_rid,
|
nominal/core/event.py
CHANGED
|
@@ -3,15 +3,18 @@ from __future__ import annotations
|
|
|
3
3
|
import warnings
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from datetime import datetime, timedelta
|
|
6
|
-
from enum import Enum
|
|
7
6
|
from typing import Iterable, Mapping, Protocol, Sequence
|
|
8
7
|
|
|
9
8
|
from nominal_api import event
|
|
10
9
|
from typing_extensions import Self
|
|
11
10
|
|
|
11
|
+
from nominal.core import asset as core_asset
|
|
12
12
|
from nominal.core._clientsbunch import HasScoutParams
|
|
13
|
+
from nominal.core._event_types import EventType as EventType # noqa: PLC0414
|
|
14
|
+
from nominal.core._event_types import SearchEventOriginType as SearchEventOriginType # noqa: PLC0414
|
|
13
15
|
from nominal.core._utils.api_tools import HasRid, RefreshableMixin, rid_from_instance_or_string
|
|
14
|
-
from nominal.core.
|
|
16
|
+
from nominal.core._utils.pagination_tools import search_events_paginated
|
|
17
|
+
from nominal.core._utils.query_tools import _create_search_events_query
|
|
15
18
|
from nominal.ts import IntegralNanosecondsDuration, IntegralNanosecondsUTC, _SecondsNanos, _to_api_duration
|
|
16
19
|
|
|
17
20
|
|
|
@@ -50,7 +53,7 @@ class Event(HasRid, RefreshableMixin[event.Event]):
|
|
|
50
53
|
*,
|
|
51
54
|
name: str | None = None,
|
|
52
55
|
description: str | None = None,
|
|
53
|
-
assets: Iterable[Asset | str] | None = None,
|
|
56
|
+
assets: Iterable[core_asset.Asset | str] | None = None,
|
|
54
57
|
start: datetime | IntegralNanosecondsUTC | None = None,
|
|
55
58
|
duration: timedelta | IntegralNanosecondsDuration | None = None,
|
|
56
59
|
properties: Mapping[str, str] | None = None,
|
|
@@ -123,34 +126,70 @@ class Event(HasRid, RefreshableMixin[event.Event]):
|
|
|
123
126
|
)
|
|
124
127
|
|
|
125
128
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
129
|
+
def _create_event(
|
|
130
|
+
clients: Event._Clients,
|
|
131
|
+
*,
|
|
132
|
+
name: str,
|
|
133
|
+
type: EventType,
|
|
134
|
+
start: datetime | IntegralNanosecondsUTC,
|
|
135
|
+
duration: timedelta | IntegralNanosecondsDuration,
|
|
136
|
+
assets: Iterable[core_asset.Asset | str] | None,
|
|
137
|
+
description: str | None,
|
|
138
|
+
properties: Mapping[str, str] | None,
|
|
139
|
+
labels: Iterable[str] | None,
|
|
140
|
+
) -> Event:
|
|
141
|
+
request = event.CreateEvent(
|
|
142
|
+
name=name,
|
|
143
|
+
description=description,
|
|
144
|
+
asset_rids=[rid_from_instance_or_string(asset) for asset in (assets or [])],
|
|
145
|
+
timestamp=_SecondsNanos.from_flexible(start).to_api(),
|
|
146
|
+
duration=_to_api_duration(duration),
|
|
147
|
+
origins=[],
|
|
148
|
+
properties=dict(properties or {}),
|
|
149
|
+
labels=list(labels or []),
|
|
150
|
+
type=type._to_api_event_type(),
|
|
151
|
+
)
|
|
152
|
+
response = clients.event.create_event(clients.auth_header, request)
|
|
153
|
+
return Event._from_conjure(clients, response)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _iter_search_events(clients: Event._Clients, query: event.SearchQuery) -> Iterable[Event]:
|
|
157
|
+
for e in search_events_paginated(clients.event, clients.auth_header, query):
|
|
158
|
+
yield Event._from_conjure(clients, e)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _search_events(
|
|
162
|
+
clients: Event._Clients,
|
|
163
|
+
*,
|
|
164
|
+
search_text: str | None = None,
|
|
165
|
+
after: str | datetime | IntegralNanosecondsUTC | None = None,
|
|
166
|
+
before: str | datetime | IntegralNanosecondsUTC | None = None,
|
|
167
|
+
asset_rids: Iterable[str] | None = None,
|
|
168
|
+
labels: Iterable[str] | None = None,
|
|
169
|
+
properties: Mapping[str, str] | None = None,
|
|
170
|
+
created_by_rid: str | None = None,
|
|
171
|
+
workbook_rid: str | None = None,
|
|
172
|
+
data_review_rid: str | None = None,
|
|
173
|
+
assignee_rid: str | None = None,
|
|
174
|
+
event_type: EventType | None = None,
|
|
175
|
+
origin_types: Iterable[SearchEventOriginType] | None = None,
|
|
176
|
+
workspace_rid: str | None = None,
|
|
177
|
+
) -> Sequence[Event]:
|
|
178
|
+
query = _create_search_events_query(
|
|
179
|
+
asset_rids=asset_rids,
|
|
180
|
+
search_text=search_text,
|
|
181
|
+
after=after,
|
|
182
|
+
before=before,
|
|
183
|
+
labels=labels,
|
|
184
|
+
properties=properties,
|
|
185
|
+
created_by_rid=created_by_rid,
|
|
186
|
+
workbook_rid=workbook_rid,
|
|
187
|
+
data_review_rid=data_review_rid,
|
|
188
|
+
assignee_rid=assignee_rid,
|
|
189
|
+
event_type=event_type._to_api_event_type() if event_type else None,
|
|
190
|
+
origin_types=[origin_type._to_api_search_event_origin_type() for origin_type in origin_types]
|
|
191
|
+
if origin_types
|
|
192
|
+
else None,
|
|
193
|
+
workspace_rid=workspace_rid,
|
|
194
|
+
)
|
|
195
|
+
return list(_iter_search_events(clients, query))
|
nominal/core/filetype.py
CHANGED
|
@@ -111,6 +111,7 @@ class FileType(NamedTuple):
|
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
class FileTypes:
|
|
114
|
+
AVI: FileType = FileType(".avi", "video/x-msvideo")
|
|
114
115
|
AVRO_STREAM: FileType = FileType(".avro", "application/avro")
|
|
115
116
|
BINARY: FileType = FileType("", "application/octet-stream")
|
|
116
117
|
CSV: FileType = FileType(".csv", "text/csv")
|
|
@@ -134,4 +135,4 @@ class FileTypes:
|
|
|
134
135
|
_PARQUET_FILE_TYPES = (PARQUET_GZ, PARQUET)
|
|
135
136
|
_PARQUET_ARCHIVE_TYPES = (PARQUET_TAR_GZ, PARQUET_TAR, PARQUET_ZIP)
|
|
136
137
|
_JOURNAL_TYPES = (JOURNAL_JSONL, JOURNAL_JSONL_GZ)
|
|
137
|
-
_VIDEO_TYPES = (MKV, MP4, TS)
|
|
138
|
+
_VIDEO_TYPES = (AVI, MKV, MP4, TS)
|
nominal/core/run.py
CHANGED
|
@@ -6,12 +6,14 @@ from types import MappingProxyType
|
|
|
6
6
|
from typing import Iterable, Mapping, Protocol, Sequence, cast
|
|
7
7
|
|
|
8
8
|
from nominal_api import (
|
|
9
|
+
scout_asset_api,
|
|
9
10
|
scout_run_api,
|
|
10
11
|
)
|
|
11
12
|
from typing_extensions import Self
|
|
12
13
|
|
|
13
14
|
from nominal.core import asset as core_asset
|
|
14
15
|
from nominal.core._clientsbunch import HasScoutParams
|
|
16
|
+
from nominal.core._event_types import EventType
|
|
15
17
|
from nominal.core._utils.api_tools import (
|
|
16
18
|
HasRid,
|
|
17
19
|
Link,
|
|
@@ -20,15 +22,17 @@ from nominal.core._utils.api_tools import (
|
|
|
20
22
|
create_links,
|
|
21
23
|
rid_from_instance_or_string,
|
|
22
24
|
)
|
|
25
|
+
from nominal.core.asset import _filter_scopes
|
|
23
26
|
from nominal.core.attachment import Attachment, _iter_get_attachments
|
|
24
27
|
from nominal.core.connection import Connection, _get_connections
|
|
25
|
-
from nominal.core.dataset import Dataset, _get_datasets
|
|
28
|
+
from nominal.core.dataset import Dataset, _DatasetWrapper, _get_datasets
|
|
29
|
+
from nominal.core.event import Event, _create_event
|
|
26
30
|
from nominal.core.video import Video, _get_video
|
|
27
31
|
from nominal.ts import IntegralNanosecondsDuration, IntegralNanosecondsUTC, _SecondsNanos, _to_api_duration
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
@dataclass(frozen=True)
|
|
31
|
-
class Run(HasRid, RefreshableMixin[scout_run_api.Run]):
|
|
35
|
+
class Run(HasRid, RefreshableMixin[scout_run_api.Run], _DatasetWrapper):
|
|
32
36
|
rid: str
|
|
33
37
|
name: str
|
|
34
38
|
description: str
|
|
@@ -96,6 +100,13 @@ class Run(HasRid, RefreshableMixin[scout_run_api.Run]):
|
|
|
96
100
|
updated_run = self._clients.run.update_run(self._clients.auth_header, request, self.rid)
|
|
97
101
|
return self._refresh_from_api(updated_run)
|
|
98
102
|
|
|
103
|
+
def _list_dataset_scopes(self) -> Sequence[scout_asset_api.DataScope]:
|
|
104
|
+
api_run = self._get_latest_api()
|
|
105
|
+
if len(api_run.assets) > 1:
|
|
106
|
+
raise RuntimeError("Can't retrieve dataset scopes on multi-asset runs")
|
|
107
|
+
|
|
108
|
+
return _filter_scopes(api_run.asset_data_scopes, "dataset")
|
|
109
|
+
|
|
99
110
|
def _list_datasource_rids(
|
|
100
111
|
self, datasource_type: str | None = None, property_name: str | None = None
|
|
101
112
|
) -> Mapping[str, str]:
|
|
@@ -148,6 +159,43 @@ class Run(HasRid, RefreshableMixin[scout_run_api.Run]):
|
|
|
148
159
|
)
|
|
149
160
|
self._refresh_from_api(updated_run)
|
|
150
161
|
|
|
162
|
+
def create_event(
|
|
163
|
+
self,
|
|
164
|
+
name: str,
|
|
165
|
+
type: EventType,
|
|
166
|
+
start: datetime | IntegralNanosecondsUTC,
|
|
167
|
+
duration: timedelta | IntegralNanosecondsDuration = 0,
|
|
168
|
+
*,
|
|
169
|
+
description: str | None = None,
|
|
170
|
+
properties: Mapping[str, str] | None = None,
|
|
171
|
+
labels: Iterable[str] = (),
|
|
172
|
+
) -> Event:
|
|
173
|
+
"""Create an event associated with all associated assets of this run at a given point in time.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
name: Name of the event
|
|
177
|
+
type: Verbosity level of the event.
|
|
178
|
+
start: Starting timestamp of the event
|
|
179
|
+
duration: Duration of the event, or 0 for an event without duration.
|
|
180
|
+
description: Optionally, a human readable description of the event to create
|
|
181
|
+
properties: Key-value pairs to use as properties on the created event
|
|
182
|
+
labels: Sequence of labels to use on the created event.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
The created event that is associated with all of the assets associated with this run..
|
|
186
|
+
"""
|
|
187
|
+
return _create_event(
|
|
188
|
+
self._clients,
|
|
189
|
+
name=name,
|
|
190
|
+
type=type,
|
|
191
|
+
start=start,
|
|
192
|
+
duration=duration,
|
|
193
|
+
description=description,
|
|
194
|
+
assets=self.assets,
|
|
195
|
+
properties=properties,
|
|
196
|
+
labels=labels,
|
|
197
|
+
)
|
|
198
|
+
|
|
151
199
|
def add_dataset(
|
|
152
200
|
self,
|
|
153
201
|
ref_name: str,
|
|
@@ -325,11 +373,13 @@ class Run(HasRid, RefreshableMixin[scout_run_api.Run]):
|
|
|
325
373
|
def archive(self) -> None:
|
|
326
374
|
"""Archive this run.
|
|
327
375
|
Archived runs are not deleted, but are hidden from the UI.
|
|
328
|
-
|
|
329
|
-
NOTE: currently, it is not possible (yet) to unarchive a run once archived.
|
|
330
376
|
"""
|
|
331
377
|
self._clients.run.archive_run(self._clients.auth_header, self.rid)
|
|
332
378
|
|
|
379
|
+
def unarchive(self) -> None:
|
|
380
|
+
"""Unarchive this run, allowing it to appear on the UI."""
|
|
381
|
+
self._clients.run.unarchive_run(self._clients.auth_header, self.rid)
|
|
382
|
+
|
|
333
383
|
@classmethod
|
|
334
384
|
def _from_conjure(cls, clients: _Clients, run: scout_run_api.Run) -> Self:
|
|
335
385
|
return cls(
|
|
@@ -349,3 +399,34 @@ class Run(HasRid, RefreshableMixin[scout_run_api.Run]):
|
|
|
349
399
|
created_at=_SecondsNanos.from_flexible(run.created_at).to_nanoseconds(),
|
|
350
400
|
_clients=clients,
|
|
351
401
|
)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _create_run(
|
|
405
|
+
clients: Run._Clients,
|
|
406
|
+
*,
|
|
407
|
+
name: str,
|
|
408
|
+
start: datetime | IntegralNanosecondsUTC,
|
|
409
|
+
end: datetime | IntegralNanosecondsUTC | None,
|
|
410
|
+
description: str | None,
|
|
411
|
+
properties: Mapping[str, str] | None,
|
|
412
|
+
labels: Sequence[str] | None,
|
|
413
|
+
links: Sequence[str | Link | LinkDict] | None,
|
|
414
|
+
attachments: Iterable[Attachment] | Iterable[str] | None,
|
|
415
|
+
asset_rids: Sequence[str] | None,
|
|
416
|
+
) -> Run:
|
|
417
|
+
"""Create a run."""
|
|
418
|
+
request = scout_run_api.CreateRunRequest(
|
|
419
|
+
attachments=[rid_from_instance_or_string(a) for a in attachments or ()],
|
|
420
|
+
data_sources={},
|
|
421
|
+
description=description or "",
|
|
422
|
+
labels=[] if labels is None else list(labels),
|
|
423
|
+
links=[] if links is None else create_links(links),
|
|
424
|
+
properties={} if properties is None else dict(properties),
|
|
425
|
+
start_time=_SecondsNanos.from_flexible(start).to_scout_run_api(),
|
|
426
|
+
title=name,
|
|
427
|
+
end_time=None if end is None else _SecondsNanos.from_flexible(end).to_scout_run_api(),
|
|
428
|
+
assets=[] if asset_rids is None else list(asset_rids),
|
|
429
|
+
workspace=clients.workspace_rid,
|
|
430
|
+
)
|
|
431
|
+
response = clients.run.create_run(clients.auth_header, request)
|
|
432
|
+
return Run._from_conjure(clients, response)
|
|
@@ -2,8 +2,9 @@ import json
|
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
4
|
import uuid
|
|
5
|
+
from datetime import datetime, timedelta
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import Any, BinaryIO, Mapping, Sequence, TypeVar, Union, cast, overload
|
|
7
|
+
from typing import Any, BinaryIO, Iterable, Mapping, Sequence, TypeVar, Union, cast, overload
|
|
7
8
|
|
|
8
9
|
import requests
|
|
9
10
|
from conjure_python_client import ConjureBeanType, ConjureEnumType, ConjureUnionType
|
|
@@ -11,7 +12,21 @@ from conjure_python_client._serde.decoder import ConjureDecoder
|
|
|
11
12
|
from conjure_python_client._serde.encoder import ConjureEncoder
|
|
12
13
|
from nominal_api import scout_layout_api, scout_template_api, scout_workbookcommon_api
|
|
13
14
|
|
|
14
|
-
from nominal.core import
|
|
15
|
+
from nominal.core import (
|
|
16
|
+
Asset,
|
|
17
|
+
Dataset,
|
|
18
|
+
DatasetFile,
|
|
19
|
+
Event,
|
|
20
|
+
FileType,
|
|
21
|
+
NominalClient,
|
|
22
|
+
Workbook,
|
|
23
|
+
WorkbookTemplate,
|
|
24
|
+
)
|
|
25
|
+
from nominal.core._event_types import EventType, SearchEventOriginType
|
|
26
|
+
from nominal.ts import (
|
|
27
|
+
IntegralNanosecondsDuration,
|
|
28
|
+
IntegralNanosecondsUTC,
|
|
29
|
+
)
|
|
15
30
|
|
|
16
31
|
logger = logging.getLogger(__name__)
|
|
17
32
|
|
|
@@ -278,7 +293,9 @@ def copy_workbook_template_from(
|
|
|
278
293
|
Returns:
|
|
279
294
|
The newly created WorkbookTemplate in the target workspace.
|
|
280
295
|
"""
|
|
281
|
-
log_extras = {
|
|
296
|
+
log_extras = {
|
|
297
|
+
"destination_client_workspace": destination_client.get_workspace(destination_client._clients.workspace_rid).rid
|
|
298
|
+
}
|
|
282
299
|
logger.debug(
|
|
283
300
|
"Cloning workbook template: %s (rid: %s)", source_template.title, source_template.rid, extra=log_extras
|
|
284
301
|
)
|
|
@@ -315,7 +332,7 @@ def copy_workbook_template_from(
|
|
|
315
332
|
layout=new_template_layout,
|
|
316
333
|
content=new_workbook_content,
|
|
317
334
|
commit_message="Cloned from template",
|
|
318
|
-
workspace_rid=destination_client.get_workspace().rid,
|
|
335
|
+
workspace_rid=destination_client.get_workspace(destination_client._clients.workspace_rid).rid,
|
|
319
336
|
)
|
|
320
337
|
logger.debug(
|
|
321
338
|
"New workbook template created %s from %s (rid: %s)",
|
|
@@ -418,7 +435,9 @@ def copy_dataset_from(
|
|
|
418
435
|
Returns:
|
|
419
436
|
The newly created Dataset in the destination client.
|
|
420
437
|
"""
|
|
421
|
-
log_extras = {
|
|
438
|
+
log_extras = {
|
|
439
|
+
"destination_client_workspace": destination_client.get_workspace(destination_client._clients.workspace_rid).rid
|
|
440
|
+
}
|
|
422
441
|
logger.debug(
|
|
423
442
|
"Copying dataset %s (rid: %s)",
|
|
424
443
|
source_dataset.name,
|
|
@@ -438,6 +457,72 @@ def copy_dataset_from(
|
|
|
438
457
|
return new_dataset
|
|
439
458
|
|
|
440
459
|
|
|
460
|
+
def clone_event(source_event: Event, destination_client: NominalClient) -> Event:
|
|
461
|
+
"""Clones an event, maintaining all properties and linked assets.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
source_event (Event): The event to copy from.
|
|
465
|
+
destination_client (NominalClient): The destination client.
|
|
466
|
+
|
|
467
|
+
Returns:
|
|
468
|
+
The cloned event.
|
|
469
|
+
"""
|
|
470
|
+
return copy_event_from(source_event=source_event, destination_client=destination_client)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def copy_event_from(
|
|
474
|
+
source_event: Event,
|
|
475
|
+
destination_client: NominalClient,
|
|
476
|
+
*,
|
|
477
|
+
new_name: str | None = None,
|
|
478
|
+
new_type: EventType | None = None,
|
|
479
|
+
new_start: datetime | IntegralNanosecondsUTC | None = None,
|
|
480
|
+
new_duration: timedelta | IntegralNanosecondsDuration = timedelta(),
|
|
481
|
+
new_description: str | None = None,
|
|
482
|
+
new_assets: Iterable[Asset | str] = (),
|
|
483
|
+
new_properties: Mapping[str, str] | None = None,
|
|
484
|
+
new_labels: Iterable[str] = (),
|
|
485
|
+
) -> Event:
|
|
486
|
+
"""Copy an event from the source to the destination client.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
source_event: The source Event to copy.
|
|
490
|
+
destination_client: The NominalClient to create the copied event in.
|
|
491
|
+
new_name: Optional new name for the copied event. If not provided, the original name is used.
|
|
492
|
+
new_type: Optional new type for the copied event. If not provided, the original type is used.
|
|
493
|
+
new_start: Optional new start time for the copied event. If not provided, the original start time is used.
|
|
494
|
+
new_duration: Optional new duration for the copied event. If not provided, the original duration is used.
|
|
495
|
+
new_description: Optional new description for the copied event. If not provided, the original description used.
|
|
496
|
+
new_assets: Optional new assets for the copied event. If not provided, the original assets are used.
|
|
497
|
+
new_properties: Optional new properties for the copied event. If not provided, the original properties are used.
|
|
498
|
+
new_labels: Optional new labels for the copied event. If not provided, the original labels are used.
|
|
499
|
+
|
|
500
|
+
Returns:
|
|
501
|
+
The newly created Event in the destination client.
|
|
502
|
+
"""
|
|
503
|
+
log_extras = {
|
|
504
|
+
"destination_client_workspace": destination_client.get_workspace(destination_client._clients.workspace_rid).rid
|
|
505
|
+
}
|
|
506
|
+
logger.debug(
|
|
507
|
+
"Copying event %s (rid: %s)",
|
|
508
|
+
source_event.name,
|
|
509
|
+
source_event.rid,
|
|
510
|
+
extra=log_extras,
|
|
511
|
+
)
|
|
512
|
+
new_event = destination_client.create_event(
|
|
513
|
+
name=new_name or source_event.name,
|
|
514
|
+
type=new_type or source_event.type,
|
|
515
|
+
start=new_start or source_event.start,
|
|
516
|
+
duration=new_duration or source_event.duration,
|
|
517
|
+
description=new_description or source_event.description,
|
|
518
|
+
assets=new_assets or source_event.asset_rids,
|
|
519
|
+
properties=new_properties or source_event.properties,
|
|
520
|
+
labels=new_labels or source_event.labels,
|
|
521
|
+
)
|
|
522
|
+
logger.debug("New event created: %s (rid: %s)", new_event.name, new_event.rid, extra=log_extras)
|
|
523
|
+
return new_event
|
|
524
|
+
|
|
525
|
+
|
|
441
526
|
def clone_asset(
|
|
442
527
|
source_asset: Asset,
|
|
443
528
|
destination_client: NominalClient,
|
|
@@ -451,7 +536,9 @@ def clone_asset(
|
|
|
451
536
|
Returns:
|
|
452
537
|
The newly created Asset in the target client.
|
|
453
538
|
"""
|
|
454
|
-
return copy_asset_from(
|
|
539
|
+
return copy_asset_from(
|
|
540
|
+
source_asset=source_asset, destination_client=destination_client, include_data=True, include_events=True
|
|
541
|
+
)
|
|
455
542
|
|
|
456
543
|
|
|
457
544
|
def copy_asset_from(
|
|
@@ -463,6 +550,7 @@ def copy_asset_from(
|
|
|
463
550
|
new_asset_properties: dict[str, Any] | None = None,
|
|
464
551
|
new_asset_labels: Sequence[str] | None = None,
|
|
465
552
|
include_data: bool = False,
|
|
553
|
+
include_events: bool = False,
|
|
466
554
|
) -> Asset:
|
|
467
555
|
"""Copy an asset from the source to the destination client.
|
|
468
556
|
|
|
@@ -474,11 +562,14 @@ def copy_asset_from(
|
|
|
474
562
|
new_asset_properties: Optional new properties for the copied asset. If not provided, original properties used.
|
|
475
563
|
new_asset_labels: Optional new labels for the copied asset. If not provided, the original labels are used.
|
|
476
564
|
include_data: Whether to include data in the copied asset.
|
|
565
|
+
include_events: Whether to include events in the copied dataset.
|
|
477
566
|
|
|
478
567
|
Returns:
|
|
479
568
|
The new asset created.
|
|
480
569
|
"""
|
|
481
|
-
log_extras = {
|
|
570
|
+
log_extras = {
|
|
571
|
+
"destination_client_workspace": destination_client.get_workspace(destination_client._clients.workspace_rid).rid
|
|
572
|
+
}
|
|
482
573
|
logger.debug("Copying asset %s (rid: %s)", source_asset.name, source_asset.rid, extra=log_extras)
|
|
483
574
|
new_asset = destination_client.create_asset(
|
|
484
575
|
name=new_asset_name if new_asset_name is not None else source_asset.name,
|
|
@@ -496,6 +587,14 @@ def copy_asset_from(
|
|
|
496
587
|
)
|
|
497
588
|
new_datasets.append(new_dataset)
|
|
498
589
|
new_asset.add_dataset(data_scope, new_dataset)
|
|
590
|
+
|
|
591
|
+
if include_events:
|
|
592
|
+
source_events = source_asset.search_events(origin_types=SearchEventOriginType.get_manual_origin_types())
|
|
593
|
+
new_events = []
|
|
594
|
+
for source_event in source_events:
|
|
595
|
+
new_event = copy_event_from(source_event, destination_client, new_assets=[new_asset])
|
|
596
|
+
new_events.append(new_event)
|
|
597
|
+
|
|
499
598
|
logger.debug("New asset created: %s (rid: %s)", new_asset, new_asset.rid, extra=log_extras)
|
|
500
599
|
return new_asset
|
|
501
600
|
|
|
@@ -518,7 +617,7 @@ def copy_resources_to_destination_client(
|
|
|
518
617
|
All of the created resources.
|
|
519
618
|
"""
|
|
520
619
|
log_extras = {
|
|
521
|
-
"destination_client_workspace": destination_client.get_workspace().rid,
|
|
620
|
+
"destination_client_workspace": destination_client.get_workspace(destination_client._clients.workspace_rid).rid,
|
|
522
621
|
}
|
|
523
622
|
|
|
524
623
|
if len(source_assets) != 1:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nominal
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.102.0
|
|
4
4
|
Summary: Automate Nominal workflows in Python
|
|
5
5
|
Project-URL: Homepage, https://nominal.io
|
|
6
6
|
Project-URL: Documentation, https://docs.nominal.io
|
|
@@ -21,7 +21,7 @@ Requires-Dist: click<9,>=8
|
|
|
21
21
|
Requires-Dist: conjure-python-client<4,>=3.1.0
|
|
22
22
|
Requires-Dist: ffmpeg-python>=0.2.0
|
|
23
23
|
Requires-Dist: nominal-api==0.1032.0
|
|
24
|
-
Requires-Dist: nominal-streaming==0.5.8
|
|
24
|
+
Requires-Dist: nominal-streaming==0.5.8; platform_python_implementation == 'CPython' and python_version >= '3.10' and ((sys_platform == 'win32' and platform_machine == 'AMD64') or (sys_platform == 'darwin' and platform_machine == 'arm64') or (sys_platform == 'linux' and (platform_machine == 'x86_64' or platform_machine == 'armv7l')))
|
|
25
25
|
Requires-Dist: openpyxl>=0.0.0
|
|
26
26
|
Requires-Dist: pandas>=0.0.0
|
|
27
27
|
Requires-Dist: polars>=0.0.0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
CHANGELOG.md,sha256=
|
|
1
|
+
CHANGELOG.md,sha256=VVSixbZ3ayFrWHceTSkabfHQYIkY1HU3GRSszsywUXQ,85986
|
|
2
2
|
LICENSE,sha256=zEGHG9mjDjaIS3I79O8mweQo-yiTbqx8jJvUPppVAwk,1067
|
|
3
3
|
README.md,sha256=KKe0dxh_pHXCtB7I9G4qWGQYvot_BZU8yW6MJyuyUHM,311
|
|
4
4
|
nominal/__init__.py,sha256=rbraORnXUrNn1hywLXM0XwSQCd9UmQt20PDYlsBalfE,2167
|
|
@@ -27,26 +27,27 @@ nominal/cli/util/global_decorators.py,sha256=SBxhz4KbMlWDcCV08feouftd3HLnBNR-JJt
|
|
|
27
27
|
nominal/cli/util/verify_connection.py,sha256=KU17ejaDfKBLmLiZ3MZSVLyfrqNE7c6mFBvskhqQLCo,1902
|
|
28
28
|
nominal/config/__init__.py,sha256=wV8cq8X3J4NTJ5H_uR5THaMT_NQpWQO5qCUGEb-rPnM,3157
|
|
29
29
|
nominal/config/_config.py,sha256=yKq_H1iYJDoxRfLz2iXLbbVdoL0MTEY0FS4eVL12w0g,2004
|
|
30
|
-
nominal/core/__init__.py,sha256=
|
|
30
|
+
nominal/core/__init__.py,sha256=1MiCC44cxHYFofP4hf2fz4EIkepK-OAhDzpPFIzHbWw,2422
|
|
31
31
|
nominal/core/_clientsbunch.py,sha256=YwciugX7rQ9AOPHyvKuavG7b9SlX1PURRquP37nvLqE,8458
|
|
32
32
|
nominal/core/_constants.py,sha256=SrxgaSqAEB1MvTSrorgGam3eO29iCmRr6VIdajxX3gI,56
|
|
33
|
-
nominal/core/
|
|
33
|
+
nominal/core/_event_types.py,sha256=Cq_8x-zv_5EDvRo9UTbaOpenAy92bTfQxlsEuHPOhtE,3706
|
|
34
|
+
nominal/core/asset.py,sha256=-hNMGXiU1dPWfrzmOngbab-Hf6vfq2Rm_j0FP-woJ-s,23120
|
|
34
35
|
nominal/core/attachment.py,sha256=iJaDyF6JXsKxxBLA03I0WMmQF8U0bA-wRwvXMEhfWLU,4284
|
|
35
36
|
nominal/core/bounds.py,sha256=742BWmGL3FBryRAjoiJRg2N6aVinjYkQLxN7kfnJ40Q,581
|
|
36
37
|
nominal/core/channel.py,sha256=dbe8wpfMiWqHu98x66w6GOmC9Ro33Wv9AhBVx2DvtVk,18970
|
|
37
38
|
nominal/core/checklist.py,sha256=rO1RPDYV3o2miPKF7DcCiYpj6bUN-sdtZNhJkXzkfYE,7110
|
|
38
|
-
nominal/core/client.py,sha256=
|
|
39
|
+
nominal/core/client.py,sha256=34AhkJmnftU1dumVg80jPv9fnP7W5mQrkgOR8zI0VoU,68291
|
|
39
40
|
nominal/core/connection.py,sha256=ySbPN_a2takVa8wIU9mK4fB6vYLyZnN-qSmXVkLUxAY,5157
|
|
40
41
|
nominal/core/containerized_extractors.py,sha256=fUz3-NHoNWYKqOCD15gLwGXDKVfdsW-x_kpXnkOI3BE,10224
|
|
41
42
|
nominal/core/data_review.py,sha256=bEnRsd8LI4x9YOBPcF2H3h5-e12A7Gh8gQfsNUAZmPQ,7922
|
|
42
|
-
nominal/core/dataset.py,sha256=
|
|
43
|
+
nominal/core/dataset.py,sha256=Rt20H2ekUbF0_YyF-OkJhs3KaRTqQzNNxyneRjIEOJk,46627
|
|
43
44
|
nominal/core/dataset_file.py,sha256=oENANJ17A4K63cZ8Fr7lUm_kVPyA4fL2rUsZ3oXXk2U,16396
|
|
44
|
-
nominal/core/datasource.py,sha256=
|
|
45
|
-
nominal/core/event.py,sha256=
|
|
45
|
+
nominal/core/datasource.py,sha256=k13B6u6uw5pd49SuVM3gXtATgqO_BUnqGUMGiiW6Moc,16920
|
|
46
|
+
nominal/core/event.py,sha256=8trZXyuAqRlKedgcqSgDIimXAAJBmEfDLyHkOOBwUC0,7762
|
|
46
47
|
nominal/core/exceptions.py,sha256=GUpwXRgdYamLl6684FE8ttCRHkBx6WEhOZ3NPE-ybD4,2671
|
|
47
|
-
nominal/core/filetype.py,sha256=
|
|
48
|
+
nominal/core/filetype.py,sha256=uzKe4iNHSv27mvz8-5EJEsvGOn3msEm_IhCj8OsCAPY,5526
|
|
48
49
|
nominal/core/log.py,sha256=z3hI3CIEyMwpUSWjwBsJ6a3JNGzBbsmrVusSU6uI7CY,3885
|
|
49
|
-
nominal/core/run.py,sha256=
|
|
50
|
+
nominal/core/run.py,sha256=IqXCP24UhdHKkss0LbXU_zAhx-7Pf2MIQI-lic_-quw,17987
|
|
50
51
|
nominal/core/secret.py,sha256=Ckq48m60i7rktxL9GY-nxHU5v8gHv9F1-JN7_MSf4bM,2863
|
|
51
52
|
nominal/core/unit.py,sha256=Wa-Bvu0hD-nzxVaQJSnn5YqAfnhUd2kWw2SswXnbMHY,3161
|
|
52
53
|
nominal/core/user.py,sha256=FV333TN4pQzcLh5b2CfxvBnnXyB1TrOP8Ppx1-XdaiE,481
|
|
@@ -67,7 +68,7 @@ nominal/core/_utils/multipart.py,sha256=0dA2XcTHuOQIyS0139O8WZiCjwePaD1sYDUmTgmW
|
|
|
67
68
|
nominal/core/_utils/multipart_downloader.py,sha256=16OJEPqxCwOnfjptYdrlwQVuSUQYoe9_iiW60ZSjWos,13859
|
|
68
69
|
nominal/core/_utils/networking.py,sha256=n9ZqYtnpwPCjz9C-4eixsTkrhFh-DW6lknBJlHckHhg,8200
|
|
69
70
|
nominal/core/_utils/pagination_tools.py,sha256=cEBY1WiA1d3cWJEM0myYF_pX8JdQ_e-5asngVXrUc_Y,12152
|
|
70
|
-
nominal/core/_utils/query_tools.py,sha256=
|
|
71
|
+
nominal/core/_utils/query_tools.py,sha256=0AuIZPtxR_BgrjBjRjc8fPjPKa9zicCe1xT9znVB_RA,16137
|
|
71
72
|
nominal/core/_utils/queueing.py,sha256=3qljc7dFI1UahlKjCaRVybM4poMCV5SayjyRPyXcPxg,3654
|
|
72
73
|
nominal/exceptions/__init__.py,sha256=W2r_GWJkZQQ6t3HooFjGRdhIgJq3fBvRV7Yn6gseoO0,415
|
|
73
74
|
nominal/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -85,9 +86,9 @@ nominal/experimental/logging/click_log_handler.py,sha256=ANLf4IGgmh95V0kJlr756wQ
|
|
|
85
86
|
nominal/experimental/logging/nominal_log_handler.py,sha256=hyTxyjsvFnE7vtyrDJpunAqADHmXekNWALwxXPIJGCk,5120
|
|
86
87
|
nominal/experimental/logging/rich_log_handler.py,sha256=8yz_VtxNgJg2oiesnXz2iXoBvQrUP5pAsYkxknOXgXA,1231
|
|
87
88
|
nominal/experimental/migration/__init__.py,sha256=E2IgWJLwJ5bN6jbl8k5nHECKFx5aT11jKAzVYcyXn3o,460
|
|
88
|
-
nominal/experimental/migration/migration_utils.py,sha256=
|
|
89
|
+
nominal/experimental/migration/migration_utils.py,sha256=Xuu9NilMxZQU_o8tqOn9WfM25Yz720HteiyN5L9Bbhs,26792
|
|
89
90
|
nominal/experimental/rust_streaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
|
-
nominal/experimental/rust_streaming/rust_write_stream.py,sha256=
|
|
91
|
+
nominal/experimental/rust_streaming/rust_write_stream.py,sha256=E-L5JtcwPWnCEm0o4_k-AVzw173sRSgElzKrgHoYwbs,1490
|
|
91
92
|
nominal/experimental/stream_v2/__init__.py,sha256=W39vK46pssx5sXvmsImMuJiEPs7iGtwrbYBI0bWnXCY,2313
|
|
92
93
|
nominal/experimental/stream_v2/_serializer.py,sha256=DcGimcY1LsXNeCzOWrel3SwuvoRV4XLdOFjqjM7MgPY,1035
|
|
93
94
|
nominal/experimental/stream_v2/_write_stream.py,sha256=-EncNPXUDYaL1YpFlJFEkuLgcxMdyKEXS5JJzP_2LlI,9981
|
|
@@ -104,8 +105,8 @@ nominal/thirdparty/polars/polars_export_handler.py,sha256=hGCSwXX9dC4MG01CmmjlTb
|
|
|
104
105
|
nominal/thirdparty/tdms/__init__.py,sha256=6n2ImFr2Wiil6JM1P5Q7Mpr0VzLcnDkmup_ftNpPq-s,142
|
|
105
106
|
nominal/thirdparty/tdms/_tdms.py,sha256=eiHFTUviyDPDClckNldjs_jTTSH_sdmboKDq0oIGChQ,8711
|
|
106
107
|
nominal/ts/__init__.py,sha256=hmd0ENvDhxRnzDKGLxIub6QG8LpcxCgcyAct029CaEs,21442
|
|
107
|
-
nominal-1.
|
|
108
|
-
nominal-1.
|
|
109
|
-
nominal-1.
|
|
110
|
-
nominal-1.
|
|
111
|
-
nominal-1.
|
|
108
|
+
nominal-1.102.0.dist-info/METADATA,sha256=vO-itznAaNKlS3Awvzo8S81LTu7iPKIyLrxEi2y5ZFs,2277
|
|
109
|
+
nominal-1.102.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
110
|
+
nominal-1.102.0.dist-info/entry_points.txt,sha256=-mCLhxgg9R_lm5efT7vW9wuBH12izvY322R0a3TYxbE,66
|
|
111
|
+
nominal-1.102.0.dist-info/licenses/LICENSE,sha256=zEGHG9mjDjaIS3I79O8mweQo-yiTbqx8jJvUPppVAwk,1067
|
|
112
|
+
nominal-1.102.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|