nominal 1.105.0__tar.gz → 1.107.0__tar.gz

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.
Files changed (115) hide show
  1. {nominal-1.105.0 → nominal-1.107.0}/CHANGELOG.md +19 -0
  2. {nominal-1.105.0 → nominal-1.107.0}/PKG-INFO +2 -2
  3. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/dataset.py +4 -0
  4. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/_buckets.py +35 -4
  5. nominal-1.107.0/nominal/experimental/dataset_utils/__init__.py +5 -0
  6. nominal-1.107.0/nominal/experimental/dataset_utils/_dataset_utils.py +52 -0
  7. nominal-1.107.0/nominal/experimental/migration/migration_data_config.py +11 -0
  8. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/migration/migration_utils.py +51 -14
  9. {nominal-1.105.0 → nominal-1.107.0}/pyproject.toml +2 -2
  10. {nominal-1.105.0 → nominal-1.107.0}/.gitignore +0 -0
  11. {nominal-1.105.0 → nominal-1.107.0}/LICENSE +0 -0
  12. {nominal-1.105.0 → nominal-1.107.0}/README.md +0 -0
  13. {nominal-1.105.0 → nominal-1.107.0}/nominal/__init__.py +0 -0
  14. {nominal-1.105.0 → nominal-1.107.0}/nominal/__main__.py +0 -0
  15. {nominal-1.105.0 → nominal-1.107.0}/nominal/_utils/README.md +0 -0
  16. {nominal-1.105.0 → nominal-1.107.0}/nominal/_utils/__init__.py +0 -0
  17. {nominal-1.105.0 → nominal-1.107.0}/nominal/_utils/dataclass_tools.py +0 -0
  18. {nominal-1.105.0 → nominal-1.107.0}/nominal/_utils/deprecation_tools.py +0 -0
  19. {nominal-1.105.0 → nominal-1.107.0}/nominal/_utils/iterator_tools.py +0 -0
  20. {nominal-1.105.0 → nominal-1.107.0}/nominal/_utils/streaming_tools.py +0 -0
  21. {nominal-1.105.0 → nominal-1.107.0}/nominal/_utils/timing_tools.py +0 -0
  22. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/__init__.py +0 -0
  23. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/__main__.py +0 -0
  24. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/attachment.py +0 -0
  25. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/auth.py +0 -0
  26. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/config.py +0 -0
  27. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/dataset.py +0 -0
  28. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/download.py +0 -0
  29. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/mis.py +0 -0
  30. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/run.py +0 -0
  31. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/util/__init__.py +0 -0
  32. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/util/click_log_handler.py +0 -0
  33. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/util/global_decorators.py +0 -0
  34. {nominal-1.105.0 → nominal-1.107.0}/nominal/cli/util/verify_connection.py +0 -0
  35. {nominal-1.105.0 → nominal-1.107.0}/nominal/config/__init__.py +0 -0
  36. {nominal-1.105.0 → nominal-1.107.0}/nominal/config/_config.py +0 -0
  37. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/__init__.py +0 -0
  38. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_clientsbunch.py +0 -0
  39. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_constants.py +0 -0
  40. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_event_types.py +0 -0
  41. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_stream/__init__.py +0 -0
  42. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_stream/batch_processor.py +0 -0
  43. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_stream/batch_processor_proto.py +0 -0
  44. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_stream/write_stream.py +0 -0
  45. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_stream/write_stream_base.py +0 -0
  46. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_types.py +0 -0
  47. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/README.md +0 -0
  48. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/__init__.py +0 -0
  49. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/api_tools.py +0 -0
  50. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/multipart.py +0 -0
  51. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/multipart_downloader.py +0 -0
  52. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/networking.py +0 -0
  53. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/pagination_tools.py +0 -0
  54. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/query_tools.py +0 -0
  55. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/_utils/queueing.py +0 -0
  56. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/asset.py +0 -0
  57. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/attachment.py +0 -0
  58. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/bounds.py +0 -0
  59. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/channel.py +0 -0
  60. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/checklist.py +0 -0
  61. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/client.py +0 -0
  62. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/connection.py +0 -0
  63. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/containerized_extractors.py +0 -0
  64. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/data_review.py +0 -0
  65. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/dataset_file.py +0 -0
  66. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/datasource.py +0 -0
  67. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/event.py +0 -0
  68. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/exceptions.py +0 -0
  69. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/filetype.py +0 -0
  70. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/log.py +0 -0
  71. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/run.py +0 -0
  72. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/secret.py +0 -0
  73. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/unit.py +0 -0
  74. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/user.py +0 -0
  75. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/video.py +0 -0
  76. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/video_file.py +0 -0
  77. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/workbook.py +0 -0
  78. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/workbook_template.py +0 -0
  79. {nominal-1.105.0 → nominal-1.107.0}/nominal/core/workspace.py +0 -0
  80. {nominal-1.105.0 → nominal-1.107.0}/nominal/exceptions/__init__.py +0 -0
  81. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/__init__.py +0 -0
  82. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/README.md +0 -0
  83. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/__init__.py +0 -0
  84. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/dsl/__init__.py +0 -0
  85. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/dsl/_enum_expr_impls.py +0 -0
  86. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/dsl/_numeric_expr_impls.py +0 -0
  87. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/dsl/_range_expr_impls.py +0 -0
  88. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/dsl/exprs.py +0 -0
  89. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/compute/dsl/params.py +0 -0
  90. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/logging/__init__.py +0 -0
  91. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/logging/click_log_handler.py +0 -0
  92. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/logging/nominal_log_handler.py +0 -0
  93. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/logging/rich_log_handler.py +0 -0
  94. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/migration/__init__.py +0 -0
  95. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/migration/migration_resources.py +0 -0
  96. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/rust_streaming/__init__.py +0 -0
  97. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/rust_streaming/rust_write_stream.py +0 -0
  98. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/stream_v2/__init__.py +0 -0
  99. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/stream_v2/_serializer.py +0 -0
  100. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/stream_v2/_write_stream.py +0 -0
  101. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/video_processing/__init__.py +0 -0
  102. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/video_processing/resolution.py +0 -0
  103. {nominal-1.105.0 → nominal-1.107.0}/nominal/experimental/video_processing/video_conversion.py +0 -0
  104. {nominal-1.105.0 → nominal-1.107.0}/nominal/nominal.py +0 -0
  105. {nominal-1.105.0 → nominal-1.107.0}/nominal/py.typed +0 -0
  106. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/__init__.py +0 -0
  107. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/matlab/__init__.py +0 -0
  108. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/matlab/_matlab.py +0 -0
  109. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/pandas/__init__.py +0 -0
  110. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/pandas/_pandas.py +0 -0
  111. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/polars/__init__.py +0 -0
  112. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/polars/polars_export_handler.py +0 -0
  113. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/tdms/__init__.py +0 -0
  114. {nominal-1.105.0 → nominal-1.107.0}/nominal/thirdparty/tdms/_tdms.py +0 -0
  115. {nominal-1.105.0 → nominal-1.107.0}/nominal/ts/__init__.py +0 -0
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.107.0](https://github.com/nominal-io/nominal-client/compare/v1.106.0...v1.107.0) (2026-01-22)
4
+
5
+
6
+ ### Features
7
+
8
+ * make migration preserve uuid and dataset include options configurable ([#584](https://github.com/nominal-io/nominal-client/issues/584)) ([95ae64d](https://github.com/nominal-io/nominal-client/commit/95ae64de267ad957d15641378b6b418e8c234faa))
9
+
10
+ ## [1.106.0](https://github.com/nominal-io/nominal-client/compare/v1.105.0...v1.106.0) (2026-01-21)
11
+
12
+
13
+ ### Features
14
+
15
+ * create dataset with uuid endpoint for mgiration ([#580](https://github.com/nominal-io/nominal-client/issues/580)) ([f3a3b38](https://github.com/nominal-io/nominal-client/commit/f3a3b383d62e8466af617179479918ef0d1ee534))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * handle case where compute returns a non-bucketed response for a bucketed query ([#579](https://github.com/nominal-io/nominal-client/issues/579)) ([58b84f5](https://github.com/nominal-io/nominal-client/commit/58b84f55d282a84c8eb6703816b16920e3b8ecf3))
21
+
3
22
  ## [1.105.0](https://github.com/nominal-io/nominal-client/compare/v1.104.3...v1.105.0) (2026-01-21)
4
23
 
5
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nominal
3
- Version: 1.105.0
3
+ Version: 1.107.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
@@ -20,7 +20,7 @@ Requires-Dist: cachetools>=6.1.0
20
20
  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
- Requires-Dist: nominal-api==0.1032.0
23
+ Requires-Dist: nominal-api==0.1075.0
24
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
@@ -1093,6 +1093,7 @@ def _construct_new_ingest_options(
1093
1093
  tag_columns=tag_columns,
1094
1094
  is_archive=file_type.is_parquet_archive(),
1095
1095
  additional_file_tags={**tags} if tags else None,
1096
+ exclude_columns=[],
1096
1097
  )
1097
1098
  )
1098
1099
  else:
@@ -1107,6 +1108,7 @@ def _construct_new_ingest_options(
1107
1108
  channel_prefix=channel_prefix,
1108
1109
  tag_columns=tag_columns,
1109
1110
  additional_file_tags={**tags} if tags else None,
1111
+ exclude_columns=[],
1110
1112
  )
1111
1113
  )
1112
1114
 
@@ -1139,6 +1141,7 @@ def _construct_existing_ingest_options(
1139
1141
  tag_columns=tag_columns,
1140
1142
  is_archive=file_type.is_parquet_archive(),
1141
1143
  additional_file_tags={**tags} if tags else None,
1144
+ exclude_columns=[],
1142
1145
  )
1143
1146
  )
1144
1147
  else:
@@ -1152,5 +1155,6 @@ def _construct_existing_ingest_options(
1152
1155
  timestamp_metadata=timestamp_metadata,
1153
1156
  tag_columns=tag_columns,
1154
1157
  additional_file_tags={**tags} if tags else None,
1158
+ exclude_columns=[],
1155
1159
  )
1156
1160
  )
@@ -317,10 +317,41 @@ def _compute_buckets(
317
317
  def _numeric_buckets_from_compute_response(
318
318
  response: scout_compute_api.ComputeNodeResponse,
319
319
  ) -> Iterable[tuple[api.Timestamp, scout_compute_api.NumericBucket]]:
320
- if response.type != "bucketedNumeric" or response.bucketed_numeric is None:
321
- return
322
-
323
- yield from zip(response.bucketed_numeric.timestamps, response.bucketed_numeric.buckets)
320
+ if response.numeric_point is not None:
321
+ # single point would be returned-- create a synthetic bucket
322
+ val = response.numeric_point.value
323
+ yield (
324
+ response.numeric_point.timestamp,
325
+ scout_compute_api.NumericBucket(
326
+ count=1,
327
+ first_point=response.numeric_point,
328
+ max=val,
329
+ mean=val,
330
+ min=val,
331
+ variance=0,
332
+ last_point=response.numeric_point,
333
+ ),
334
+ )
335
+ elif response.numeric is not None:
336
+ # Not enough points to reach the number of requested bucket count, so
337
+ # gets returned as all of the raw data.
338
+ for timestamp, value in zip(response.numeric.timestamps, response.numeric.values):
339
+ point = scout_compute_api.NumericPoint(timestamp, value)
340
+ yield (
341
+ timestamp,
342
+ scout_compute_api.NumericBucket(
343
+ count=1,
344
+ first_point=point,
345
+ max=value,
346
+ min=value,
347
+ mean=value,
348
+ variance=0,
349
+ last_point=point,
350
+ ),
351
+ )
352
+ elif response.bucketed_numeric is not None:
353
+ # Actually bucketed data
354
+ yield from zip(response.bucketed_numeric.timestamps, response.bucketed_numeric.buckets)
324
355
 
325
356
 
326
357
  def _timestamp_from_conjure(timestamp: api.Timestamp) -> params.NanosecondsUTC:
@@ -0,0 +1,5 @@
1
+ from nominal.experimental.dataset_utils._dataset_utils import create_dataset_with_uuid
2
+
3
+ __all__ = [
4
+ "create_dataset_with_uuid",
5
+ ]
@@ -0,0 +1,52 @@
1
+ from collections.abc import Mapping, Sequence
2
+
3
+ from nominal_api import scout_catalog
4
+
5
+ from nominal.core import Dataset, NominalClient
6
+
7
+
8
+ def create_dataset_with_uuid(
9
+ client: NominalClient,
10
+ dataset_uuid: str,
11
+ name: str,
12
+ *,
13
+ description: str | None = None,
14
+ labels: Sequence[str] = (),
15
+ properties: Mapping[str, str] | None = None,
16
+ ) -> Dataset:
17
+ """Create a dataset with a specific UUID.
18
+
19
+ This is useful for migrations where the dataset UUID must be controlled by the caller.
20
+ Throws a conflict error if a dataset with the specified UUID already exists.
21
+
22
+ This endpoint is not intended for general use. Use `NominalClient.create_dataset` instead
23
+ to create a new dataset with an auto-generated UUID.
24
+
25
+ Args:
26
+ client: The NominalClient to use for creating the dataset.
27
+ dataset_uuid: The UUID to assign to the new dataset.
28
+ name: Name of the dataset to create.
29
+ description: Human readable description of the dataset.
30
+ labels: Text labels to apply to the created dataset.
31
+ properties: Key-value properties to apply to the created dataset.
32
+
33
+ Returns:
34
+ Reference to the created dataset in Nominal.
35
+ """
36
+ create_dataset_request = scout_catalog.CreateDataset(
37
+ name=name,
38
+ description=description,
39
+ labels=list(labels),
40
+ properties={} if properties is None else dict(properties),
41
+ is_v2_dataset=True,
42
+ metadata={},
43
+ origin_metadata=scout_catalog.DatasetOriginMetadata(),
44
+ workspace=client._clients.workspace_rid,
45
+ marking_rids=[],
46
+ )
47
+ request = scout_catalog.CreateDatasetWithUuidRequest(
48
+ create_dataset=create_dataset_request,
49
+ uuid=dataset_uuid,
50
+ )
51
+ response = client._clients.catalog.create_dataset_with_uuid(client._clients.auth_header, request)
52
+ return Dataset._from_conjure(client._clients, response)
@@ -0,0 +1,11 @@
1
+ class MigrationDatasetConfig:
2
+ preserve_dataset_uuid: bool
3
+ include_dataset_files: bool
4
+
5
+ def __init__(self, preserve_dataset_uuid: bool, include_dataset_files: bool) -> None:
6
+ """Args:
7
+ preserve_dataset_uuid (bool): If true, preserves the original dataset UUIDs during migration.
8
+ include_dataset_files (bool): If true, includes dataset files in the migration.
9
+ """
10
+ self.preserve_dataset_uuid = preserve_dataset_uuid
11
+ self.include_dataset_files = include_dataset_files
@@ -26,6 +26,8 @@ from nominal.core._event_types import EventType, SearchEventOriginType
26
26
  from nominal.core._utils.api_tools import Link, LinkDict
27
27
  from nominal.core.attachment import Attachment
28
28
  from nominal.core.run import Run
29
+ from nominal.experimental.dataset_utils import create_dataset_with_uuid
30
+ from nominal.experimental.migration.migration_data_config import MigrationDatasetConfig
29
31
  from nominal.experimental.migration.migration_resources import MigrationResources
30
32
  from nominal.ts import (
31
33
  IntegralNanosecondsDuration,
@@ -422,6 +424,7 @@ def copy_dataset_from(
422
424
  new_dataset_properties: dict[str, Any] | None = None,
423
425
  new_dataset_labels: Sequence[str] | None = None,
424
426
  include_files: bool = False,
427
+ preserve_uuid: bool = False,
425
428
  ) -> Dataset:
426
429
  """Copy a dataset from the source to the destination client.
427
430
 
@@ -435,6 +438,9 @@ def copy_dataset_from(
435
438
  properties are used.
436
439
  new_dataset_labels: Optional new labels for the copied dataset. If not provided, the original labels are used.
437
440
  include_files: Whether to include files in the copied dataset.
441
+ preserve_uuid: If True, create the dataset with the same UUID as the source dataset.
442
+ This is useful for migrations where references to datasets must be preserved.
443
+ Throws a conflict error if a dataset with the UUID already exists.
438
444
 
439
445
  Returns:
440
446
  The newly created Dataset in the destination client.
@@ -448,12 +454,34 @@ def copy_dataset_from(
448
454
  source_dataset.rid,
449
455
  extra=log_extras,
450
456
  )
451
- new_dataset = destination_client.create_dataset(
452
- name=new_dataset_name if new_dataset_name is not None else source_dataset.name,
453
- description=new_dataset_description if new_dataset_description is not None else source_dataset.description,
454
- properties=new_dataset_properties if new_dataset_properties is not None else source_dataset.properties,
455
- labels=new_dataset_labels if new_dataset_labels is not None else source_dataset.labels,
456
- )
457
+
458
+ dataset_name = new_dataset_name if new_dataset_name is not None else source_dataset.name
459
+ dataset_description = new_dataset_description if new_dataset_description is not None else source_dataset.description
460
+ dataset_properties = new_dataset_properties if new_dataset_properties is not None else source_dataset.properties
461
+ dataset_labels = new_dataset_labels if new_dataset_labels is not None else source_dataset.labels
462
+
463
+ if preserve_uuid:
464
+ # Extract the UUID from the source dataset's rid
465
+ match = UUID_PATTERN.search(source_dataset.rid)
466
+ if not match:
467
+ raise ValueError(f"Could not extract UUID from dataset rid: {source_dataset.rid}")
468
+ source_uuid = match.group(2)
469
+ new_dataset = create_dataset_with_uuid(
470
+ client=destination_client,
471
+ dataset_uuid=source_uuid,
472
+ name=dataset_name,
473
+ description=dataset_description,
474
+ labels=dataset_labels,
475
+ properties=dataset_properties,
476
+ )
477
+ else:
478
+ new_dataset = destination_client.create_dataset(
479
+ name=dataset_name,
480
+ description=dataset_description,
481
+ properties=dataset_properties,
482
+ labels=dataset_labels,
483
+ )
484
+
457
485
  if include_files:
458
486
  for source_file in source_dataset.list_files():
459
487
  copy_file_to_dataset(source_file, new_dataset)
@@ -612,7 +640,7 @@ def clone_asset(
612
640
  return copy_asset_from(
613
641
  source_asset=source_asset,
614
642
  destination_client=destination_client,
615
- include_data=True,
643
+ dataset_config=MigrationDatasetConfig(preserve_dataset_uuid=True, include_dataset_files=True),
616
644
  include_events=True,
617
645
  include_runs=True,
618
646
  )
@@ -626,7 +654,7 @@ def copy_asset_from(
626
654
  new_asset_description: str | None = None,
627
655
  new_asset_properties: dict[str, Any] | None = None,
628
656
  new_asset_labels: Sequence[str] | None = None,
629
- include_data: bool = False,
657
+ dataset_config: MigrationDatasetConfig | None = None,
630
658
  include_events: bool = False,
631
659
  include_runs: bool = False,
632
660
  ) -> Asset:
@@ -639,7 +667,7 @@ def copy_asset_from(
639
667
  new_asset_description: Optional new description for the copied asset. If not provided, original description used
640
668
  new_asset_properties: Optional new properties for the copied asset. If not provided, original properties used.
641
669
  new_asset_labels: Optional new labels for the copied asset. If not provided, the original labels are used.
642
- include_data: Whether to include data in the copied asset.
670
+ dataset_config: Configuration for dataset migration.
643
671
  include_events: Whether to include events in the copied dataset.
644
672
  include_runs: Whether to include runs in the copied asset.
645
673
 
@@ -656,15 +684,16 @@ def copy_asset_from(
656
684
  properties=new_asset_properties if new_asset_properties is not None else source_asset.properties,
657
685
  labels=new_asset_labels if new_asset_labels is not None else source_asset.labels,
658
686
  )
659
- if include_data:
687
+ if dataset_config is not None:
660
688
  source_datasets = source_asset.list_datasets()
661
689
  for data_scope, source_dataset in source_datasets:
662
- new_dataset = clone_dataset(
690
+ new_dataset = copy_dataset_from(
663
691
  source_dataset=source_dataset,
664
692
  destination_client=destination_client,
693
+ preserve_uuid=dataset_config.preserve_dataset_uuid,
694
+ include_files=dataset_config.include_dataset_files,
665
695
  )
666
696
  new_asset.add_dataset(data_scope, new_dataset)
667
- source_asset._list_dataset_scopes
668
697
 
669
698
  if include_events:
670
699
  source_events = source_asset.search_events(origin_types=SearchEventOriginType.get_manual_origin_types())
@@ -683,6 +712,7 @@ def copy_asset_from(
683
712
  def copy_resources_to_destination_client(
684
713
  destination_client: NominalClient,
685
714
  migration_resources: MigrationResources,
715
+ dataset_config: MigrationDatasetConfig | None = None,
686
716
  ) -> tuple[Sequence[tuple[str, Dataset]], Sequence[Asset], Sequence[WorkbookTemplate], Sequence[Workbook]]:
687
717
  """Based on a list of assets and workbook templates, copy resources to destination client, creating
688
718
  new datasets, datafiles, and workbooks along the way.
@@ -690,6 +720,7 @@ def copy_resources_to_destination_client(
690
720
  Args:
691
721
  destination_client (NominalClient): client of the tenant/workspace to copy resources to.
692
722
  migration_resources (MigrationResources): resources to copy.
723
+ dataset_config (MigrationDataConfig | None): Configuration for dataset migration.
693
724
 
694
725
  Returns:
695
726
  All of the created resources.
@@ -704,7 +735,13 @@ def copy_resources_to_destination_client(
704
735
 
705
736
  new_data_scopes_and_datasets: list[tuple[str, Dataset]] = []
706
737
  for source_asset in migration_resources.source_assets:
707
- new_asset = clone_asset(source_asset.asset, destination_client)
738
+ new_asset = copy_asset_from(
739
+ source_asset.asset,
740
+ destination_client,
741
+ dataset_config=dataset_config,
742
+ include_events=True,
743
+ include_runs=True,
744
+ )
708
745
  new_assets.append(new_asset)
709
746
  new_data_scopes_and_datasets.extend(new_asset.list_datasets())
710
747
 
@@ -712,7 +749,7 @@ def copy_resources_to_destination_client(
712
749
  new_template = clone_workbook_template(source_workbook_template, destination_client)
713
750
  new_templates.append(new_template)
714
751
  new_workbook = new_template.create_workbook(
715
- title=new_template.title, description=new_template.description, asset=new_assets[0]
752
+ title=new_template.title, description=new_template.description, asset=new_asset
716
753
  )
717
754
  logger.debug(
718
755
  "Created new workbook %s (rid: %s) from template %s (rid: %s)",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nominal"
3
- version = "1.105.0"
3
+ version = "1.107.0"
4
4
  description = "Automate Nominal workflows in Python"
5
5
  authors = [
6
6
  { name = "Alexander Reynolds", email = "alex.reynolds@nominal.io" },
@@ -28,7 +28,7 @@ dependencies = [
28
28
  "click>=8,<9",
29
29
  "conjure-python-client>=3.1.0,<4",
30
30
  "ffmpeg-python>=0.2.0",
31
- "nominal-api==0.1032.0",
31
+ "nominal-api==0.1075.0",
32
32
  "rich>=14.1.0",
33
33
  "tabulate>=0.9.0,<0.10",
34
34
  "truststore>=0.10.4",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes