nominal 1.106.0__py3-none-any.whl → 1.108.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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.108.0](https://github.com/nominal-io/nominal-client/compare/v1.107.0...v1.108.0) (2026-01-22)
4
+
5
+
6
+ ### Features
7
+
8
+ * add channels to datasets ([#583](https://github.com/nominal-io/nominal-client/issues/583)) ([35398ef](https://github.com/nominal-io/nominal-client/commit/35398ef63633c2b2fa198d9915a85a8e19e40323))
9
+
10
+ ## [1.107.0](https://github.com/nominal-io/nominal-client/compare/v1.106.0...v1.107.0) (2026-01-22)
11
+
12
+
13
+ ### Features
14
+
15
+ * 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))
16
+
3
17
  ## [1.106.0](https://github.com/nominal-io/nominal-client/compare/v1.105.0...v1.106.0) (2026-01-21)
4
18
 
5
19
 
@@ -28,6 +28,7 @@ from nominal_api import (
28
28
  storage_writer_api,
29
29
  timeseries_channelmetadata,
30
30
  timeseries_logicalseries,
31
+ timeseries_metadata,
31
32
  upload_api,
32
33
  )
33
34
  from typing_extensions import Self
@@ -139,6 +140,7 @@ class ClientsBunch:
139
140
  proto_write: ProtoWriteService
140
141
  event: event.EventService
141
142
  channel_metadata: timeseries_channelmetadata.ChannelMetadataService
143
+ series_metadata: timeseries_metadata.SeriesMetadataService
142
144
  workspace: security_api_workspace.WorkspaceService
143
145
  containerized_extractors: ingest_api.ContainerizedExtractorService
144
146
  secrets: secrets_api.SecretService
@@ -179,6 +181,7 @@ class ClientsBunch:
179
181
  proto_write=client_factory(ProtoWriteService),
180
182
  event=client_factory(event.EventService),
181
183
  channel_metadata=client_factory(timeseries_channelmetadata.ChannelMetadataService),
184
+ series_metadata=client_factory(timeseries_metadata.SeriesMetadataService),
182
185
  workspace=client_factory(security_api_workspace.WorkspaceService),
183
186
  containerized_extractors=client_factory(ingest_api.ContainerizedExtractorService),
184
187
  secrets=client_factory(secrets_api.SecretService),
nominal/core/channel.py CHANGED
@@ -12,6 +12,7 @@ from nominal_api import (
12
12
  scout_compute_api,
13
13
  scout_dataexport_api,
14
14
  scout_datasource,
15
+ storage_series_api,
15
16
  timeseries_channelmetadata,
16
17
  timeseries_channelmetadata_api,
17
18
  timeseries_logicalseries_api,
@@ -52,6 +53,18 @@ class ChannelDataType(enum.Enum):
52
53
  else:
53
54
  return cls("UNKNOWN")
54
55
 
56
+ def _to_nominal_data_type(self) -> storage_series_api.NominalDataType:
57
+ if self == ChannelDataType.DOUBLE:
58
+ return storage_series_api.NominalDataType.DOUBLE
59
+ elif self == ChannelDataType.STRING:
60
+ return storage_series_api.NominalDataType.STRING
61
+ elif self == ChannelDataType.LOG:
62
+ return storage_series_api.NominalDataType.LOG
63
+ elif self == ChannelDataType.INT:
64
+ return storage_series_api.NominalDataType.INT64
65
+ else:
66
+ return storage_series_api.NominalDataType.UNKNOWN
67
+
55
68
 
56
69
  @dataclass
57
70
  class Channel(RefreshableMixin[timeseries_channelmetadata_api.ChannelMetadata]):
@@ -19,6 +19,8 @@ from nominal_api import (
19
19
  timeseries_channelmetadata,
20
20
  timeseries_channelmetadata_api,
21
21
  timeseries_logicalseries,
22
+ timeseries_metadata,
23
+ timeseries_metadata_api,
22
24
  upload_api,
23
25
  )
24
26
 
@@ -67,6 +69,8 @@ class DataSource(HasRid):
67
69
  @property
68
70
  def channel_metadata(self) -> timeseries_channelmetadata.ChannelMetadataService: ...
69
71
  @property
72
+ def series_metadata(self) -> timeseries_metadata.SeriesMetadataService: ...
73
+ @property
70
74
  def containerized_extractors(self) -> ingest_api.ContainerizedExtractorService: ...
71
75
 
72
76
  def get_channel(self, name: str) -> Channel:
@@ -287,6 +291,53 @@ class DataSource(HasRid):
287
291
  request = datasource_api.IndexChannelPrefixTreeRequest(self.rid, delimiter=delimiter)
288
292
  self._clients.datasource.index_channel_prefix_tree(self._clients.auth_header, request)
289
293
 
294
+ def add_channel(
295
+ self,
296
+ name: str,
297
+ data_type: ChannelDataType,
298
+ *,
299
+ description: str | None = None,
300
+ unit: str | None = None,
301
+ ) -> Channel:
302
+ """Create a new channel (series metadata) for this data source.
303
+
304
+ This creates the channel metadata entry without uploading any data points.
305
+ Use this to pre-register channels before streaming data to them.
306
+
307
+ Args:
308
+ name: The name of the channel to create.
309
+ data_type: The data type of the channel (e.g., DOUBLE, STRING, LOG, INT).
310
+ description: Optional human-readable description of the channel.
311
+ unit: Optional unit symbol to associate with the channel.
312
+
313
+ Returns:
314
+ The created Channel object.
315
+
316
+ Raises:
317
+ conjure_python_client.ConjureHTTPError: If a channel with this name already exists
318
+ or if there's an error creating the channel.
319
+ """
320
+ nominal_data_type = data_type._to_nominal_data_type()
321
+
322
+ nominal_locator = timeseries_metadata_api.NominalLocatorTemplate(
323
+ channel=name,
324
+ type=nominal_data_type,
325
+ )
326
+
327
+ locator = timeseries_metadata_api.LocatorTemplate(nominal=nominal_locator)
328
+
329
+ create_request = timeseries_metadata_api.CreateSeriesMetadataRequest(
330
+ channel=name,
331
+ data_source_rid=self.rid,
332
+ locator=locator,
333
+ tags={},
334
+ description=description,
335
+ unit=unit,
336
+ )
337
+ self._clients.series_metadata.create(self._clients.auth_header, create_request)
338
+
339
+ return self.get_channel(name)
340
+
290
341
 
291
342
  def _construct_export_request(
292
343
  channels: Sequence[Channel],
@@ -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
@@ -27,6 +27,7 @@ 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
29
  from nominal.experimental.dataset_utils import create_dataset_with_uuid
30
+ from nominal.experimental.migration.migration_data_config import MigrationDatasetConfig
30
31
  from nominal.experimental.migration.migration_resources import MigrationResources
31
32
  from nominal.ts import (
32
33
  IntegralNanosecondsDuration,
@@ -639,7 +640,7 @@ def clone_asset(
639
640
  return copy_asset_from(
640
641
  source_asset=source_asset,
641
642
  destination_client=destination_client,
642
- include_data=True,
643
+ dataset_config=MigrationDatasetConfig(preserve_dataset_uuid=True, include_dataset_files=True),
643
644
  include_events=True,
644
645
  include_runs=True,
645
646
  )
@@ -653,7 +654,7 @@ def copy_asset_from(
653
654
  new_asset_description: str | None = None,
654
655
  new_asset_properties: dict[str, Any] | None = None,
655
656
  new_asset_labels: Sequence[str] | None = None,
656
- include_data: bool = False,
657
+ dataset_config: MigrationDatasetConfig | None = None,
657
658
  include_events: bool = False,
658
659
  include_runs: bool = False,
659
660
  ) -> Asset:
@@ -666,7 +667,7 @@ def copy_asset_from(
666
667
  new_asset_description: Optional new description for the copied asset. If not provided, original description used
667
668
  new_asset_properties: Optional new properties for the copied asset. If not provided, original properties used.
668
669
  new_asset_labels: Optional new labels for the copied asset. If not provided, the original labels are used.
669
- include_data: Whether to include data in the copied asset.
670
+ dataset_config: Configuration for dataset migration.
670
671
  include_events: Whether to include events in the copied dataset.
671
672
  include_runs: Whether to include runs in the copied asset.
672
673
 
@@ -683,15 +684,16 @@ def copy_asset_from(
683
684
  properties=new_asset_properties if new_asset_properties is not None else source_asset.properties,
684
685
  labels=new_asset_labels if new_asset_labels is not None else source_asset.labels,
685
686
  )
686
- if include_data:
687
+ if dataset_config is not None:
687
688
  source_datasets = source_asset.list_datasets()
688
689
  for data_scope, source_dataset in source_datasets:
689
- new_dataset = clone_dataset(
690
+ new_dataset = copy_dataset_from(
690
691
  source_dataset=source_dataset,
691
692
  destination_client=destination_client,
693
+ preserve_uuid=dataset_config.preserve_dataset_uuid,
694
+ include_files=dataset_config.include_dataset_files,
692
695
  )
693
696
  new_asset.add_dataset(data_scope, new_dataset)
694
- source_asset._list_dataset_scopes
695
697
 
696
698
  if include_events:
697
699
  source_events = source_asset.search_events(origin_types=SearchEventOriginType.get_manual_origin_types())
@@ -710,6 +712,7 @@ def copy_asset_from(
710
712
  def copy_resources_to_destination_client(
711
713
  destination_client: NominalClient,
712
714
  migration_resources: MigrationResources,
715
+ dataset_config: MigrationDatasetConfig | None = None,
713
716
  ) -> tuple[Sequence[tuple[str, Dataset]], Sequence[Asset], Sequence[WorkbookTemplate], Sequence[Workbook]]:
714
717
  """Based on a list of assets and workbook templates, copy resources to destination client, creating
715
718
  new datasets, datafiles, and workbooks along the way.
@@ -717,6 +720,7 @@ def copy_resources_to_destination_client(
717
720
  Args:
718
721
  destination_client (NominalClient): client of the tenant/workspace to copy resources to.
719
722
  migration_resources (MigrationResources): resources to copy.
723
+ dataset_config (MigrationDataConfig | None): Configuration for dataset migration.
720
724
 
721
725
  Returns:
722
726
  All of the created resources.
@@ -731,7 +735,13 @@ def copy_resources_to_destination_client(
731
735
 
732
736
  new_data_scopes_and_datasets: list[tuple[str, Dataset]] = []
733
737
  for source_asset in migration_resources.source_assets:
734
- 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
+ )
735
745
  new_assets.append(new_asset)
736
746
  new_data_scopes_and_datasets.extend(new_asset.list_datasets())
737
747
 
@@ -739,7 +749,7 @@ def copy_resources_to_destination_client(
739
749
  new_template = clone_workbook_template(source_workbook_template, destination_client)
740
750
  new_templates.append(new_template)
741
751
  new_workbook = new_template.create_workbook(
742
- title=new_template.title, description=new_template.description, asset=new_assets[0]
752
+ title=new_template.title, description=new_template.description, asset=new_asset
743
753
  )
744
754
  logger.debug(
745
755
  "Created new workbook %s (rid: %s) from template %s (rid: %s)",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nominal
3
- Version: 1.106.0
3
+ Version: 1.108.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
@@ -1,4 +1,4 @@
1
- CHANGELOG.md,sha256=roEVqpYae9DRMQRYCiJQm7ei603DhmGY7_Il7PmaoDg,88865
1
+ CHANGELOG.md,sha256=-ls_mO5aZCjyBFUqYkMVzrqOswFIEqKJFz7r0mqSziQ,89542
2
2
  LICENSE,sha256=zEGHG9mjDjaIS3I79O8mweQo-yiTbqx8jJvUPppVAwk,1067
3
3
  README.md,sha256=KKe0dxh_pHXCtB7I9G4qWGQYvot_BZU8yW6MJyuyUHM,311
4
4
  nominal/__init__.py,sha256=rbraORnXUrNn1hywLXM0XwSQCd9UmQt20PDYlsBalfE,2167
@@ -28,14 +28,14 @@ nominal/cli/util/verify_connection.py,sha256=KU17ejaDfKBLmLiZ3MZSVLyfrqNE7c6mFBv
28
28
  nominal/config/__init__.py,sha256=wV8cq8X3J4NTJ5H_uR5THaMT_NQpWQO5qCUGEb-rPnM,3157
29
29
  nominal/config/_config.py,sha256=yKq_H1iYJDoxRfLz2iXLbbVdoL0MTEY0FS4eVL12w0g,2004
30
30
  nominal/core/__init__.py,sha256=1MiCC44cxHYFofP4hf2fz4EIkepK-OAhDzpPFIzHbWw,2422
31
- nominal/core/_clientsbunch.py,sha256=YwciugX7rQ9AOPHyvKuavG7b9SlX1PURRquP37nvLqE,8458
31
+ nominal/core/_clientsbunch.py,sha256=sn-ajYsJ2FuZGLiEYiPwkYCxLKr13w35aqWHE3TX5us,8633
32
32
  nominal/core/_constants.py,sha256=SrxgaSqAEB1MvTSrorgGam3eO29iCmRr6VIdajxX3gI,56
33
33
  nominal/core/_event_types.py,sha256=Cq_8x-zv_5EDvRo9UTbaOpenAy92bTfQxlsEuHPOhtE,3706
34
34
  nominal/core/_types.py,sha256=FktMmcQ5_rD2rbXv8_p-WISzSo8T2NtO-exsLm-iadU,122
35
35
  nominal/core/asset.py,sha256=-hNMGXiU1dPWfrzmOngbab-Hf6vfq2Rm_j0FP-woJ-s,23120
36
36
  nominal/core/attachment.py,sha256=yOtDUdkLY5MT_Rk9kUlr1yupIJN7a5pt5sJWx4RLQV8,4355
37
37
  nominal/core/bounds.py,sha256=742BWmGL3FBryRAjoiJRg2N6aVinjYkQLxN7kfnJ40Q,581
38
- nominal/core/channel.py,sha256=dbe8wpfMiWqHu98x66w6GOmC9Ro33Wv9AhBVx2DvtVk,18970
38
+ nominal/core/channel.py,sha256=e0RtbjrXYMAqyt8noe610iIFzv30z2P7oqrwq08atD8,19558
39
39
  nominal/core/checklist.py,sha256=rO1RPDYV3o2miPKF7DcCiYpj6bUN-sdtZNhJkXzkfYE,7110
40
40
  nominal/core/client.py,sha256=Awt9WPkE-YXBfOwJMTL7Su8AZFJY3UMH7IKp5hI26YQ,68328
41
41
  nominal/core/connection.py,sha256=LYllr3a1H2xp8-i4MaX1M7yK8X-HnwuIkciyK9XgLtQ,5175
@@ -43,7 +43,7 @@ nominal/core/containerized_extractors.py,sha256=fUz3-NHoNWYKqOCD15gLwGXDKVfdsW-x
43
43
  nominal/core/data_review.py,sha256=Z_W1Okp_FSQDiVCk6aKb9gV0EXbE2jtiQaPqc6TaL0g,11038
44
44
  nominal/core/dataset.py,sha256=LqofzNAlOd3S_3Aaw6b7DoY50rj6GyMHbUClIA2TmpY,46792
45
45
  nominal/core/dataset_file.py,sha256=8rCW6MO89MFbQ2NH0WtFWmJfRWeTxhmyuoGojuQQ4Qg,16545
46
- nominal/core/datasource.py,sha256=V5UahbqsCNIdml978kOHiY6boIxKxbp76KscNBpN5xc,16934
46
+ nominal/core/datasource.py,sha256=i9db5OHNdfJyb3BJmhz2uJ398MfW8zx3Ek9pw5vYn3c,18739
47
47
  nominal/core/event.py,sha256=8trZXyuAqRlKedgcqSgDIimXAAJBmEfDLyHkOOBwUC0,7762
48
48
  nominal/core/exceptions.py,sha256=GUpwXRgdYamLl6684FE8ttCRHkBx6WEhOZ3NPE-ybD4,2671
49
49
  nominal/core/filetype.py,sha256=R8goHGW4SP0iO6AoQiUil2tNVuDgaQoHclftRbw44oc,5558
@@ -89,8 +89,9 @@ nominal/experimental/logging/click_log_handler.py,sha256=ANLf4IGgmh95V0kJlr756wQ
89
89
  nominal/experimental/logging/nominal_log_handler.py,sha256=hyTxyjsvFnE7vtyrDJpunAqADHmXekNWALwxXPIJGCk,5120
90
90
  nominal/experimental/logging/rich_log_handler.py,sha256=8yz_VtxNgJg2oiesnXz2iXoBvQrUP5pAsYkxknOXgXA,1231
91
91
  nominal/experimental/migration/__init__.py,sha256=E2IgWJLwJ5bN6jbl8k5nHECKFx5aT11jKAzVYcyXn3o,460
92
+ nominal/experimental/migration/migration_data_config.py,sha256=sPwZjyLmL-_pHvDZvQspxrfW6yNZhEsQjDVwKA8IaXM,522
92
93
  nominal/experimental/migration/migration_resources.py,sha256=Tf_7kNBeSaY8z2fTF7DAxk-9q3a7F8xXFVvxI8tTc9c,415
93
- nominal/experimental/migration/migration_utils.py,sha256=E3K2_kMsWIG9p2oIwxM64aXJLRemhsCbqfJ4Jv4SJ4M,31214
94
+ nominal/experimental/migration/migration_utils.py,sha256=isvxBH7pOjvT9PuJRfoMNHGJR17TYsSapDPVSRESCys,31804
94
95
  nominal/experimental/rust_streaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
96
  nominal/experimental/rust_streaming/rust_write_stream.py,sha256=oQ6ixwm8ct8ZDc_qNB7AucDt8o5-_aBVlW2fFCQ_nmA,1541
96
97
  nominal/experimental/stream_v2/__init__.py,sha256=W39vK46pssx5sXvmsImMuJiEPs7iGtwrbYBI0bWnXCY,2313
@@ -109,8 +110,8 @@ nominal/thirdparty/polars/polars_export_handler.py,sha256=hGCSwXX9dC4MG01CmmjlTb
109
110
  nominal/thirdparty/tdms/__init__.py,sha256=6n2ImFr2Wiil6JM1P5Q7Mpr0VzLcnDkmup_ftNpPq-s,142
110
111
  nominal/thirdparty/tdms/_tdms.py,sha256=m4gxbpxB9MTLi2FuYvGlbUGSyDAZKFxbM3ia2x1wIz0,8746
111
112
  nominal/ts/__init__.py,sha256=hmd0ENvDhxRnzDKGLxIub6QG8LpcxCgcyAct029CaEs,21442
112
- nominal-1.106.0.dist-info/METADATA,sha256=cdf0KpzkFmrFb5XsWVEa6F4UNUr1WMp79uekN4hIkq8,2307
113
- nominal-1.106.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
114
- nominal-1.106.0.dist-info/entry_points.txt,sha256=-mCLhxgg9R_lm5efT7vW9wuBH12izvY322R0a3TYxbE,66
115
- nominal-1.106.0.dist-info/licenses/LICENSE,sha256=zEGHG9mjDjaIS3I79O8mweQo-yiTbqx8jJvUPppVAwk,1067
116
- nominal-1.106.0.dist-info/RECORD,,
113
+ nominal-1.108.0.dist-info/METADATA,sha256=BFL54ilX_SW2XErNJm9Q4kEow-SCxGdJcIVvqlJ0b5A,2307
114
+ nominal-1.108.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
115
+ nominal-1.108.0.dist-info/entry_points.txt,sha256=-mCLhxgg9R_lm5efT7vW9wuBH12izvY322R0a3TYxbE,66
116
+ nominal-1.108.0.dist-info/licenses/LICENSE,sha256=zEGHG9mjDjaIS3I79O8mweQo-yiTbqx8jJvUPppVAwk,1067
117
+ nominal-1.108.0.dist-info/RECORD,,