cognite-toolkit 0.6.97__py3-none-any.whl → 0.7.30__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.
- cognite_toolkit/_cdf.py +16 -17
- cognite_toolkit/_cdf_tk/apps/__init__.py +2 -0
- cognite_toolkit/_cdf_tk/apps/_core_app.py +13 -5
- cognite_toolkit/_cdf_tk/apps/_data_app.py +1 -1
- cognite_toolkit/_cdf_tk/apps/_dev_app.py +86 -0
- cognite_toolkit/_cdf_tk/apps/_download_app.py +692 -24
- cognite_toolkit/_cdf_tk/apps/_dump_app.py +43 -101
- cognite_toolkit/_cdf_tk/apps/_landing_app.py +18 -4
- cognite_toolkit/_cdf_tk/apps/_migrate_app.py +249 -9
- cognite_toolkit/_cdf_tk/apps/_modules_app.py +0 -3
- cognite_toolkit/_cdf_tk/apps/_purge.py +15 -43
- cognite_toolkit/_cdf_tk/apps/_run.py +11 -0
- cognite_toolkit/_cdf_tk/apps/_upload_app.py +45 -6
- cognite_toolkit/_cdf_tk/builders/__init__.py +2 -2
- cognite_toolkit/_cdf_tk/builders/_base.py +28 -42
- cognite_toolkit/_cdf_tk/cdf_toml.py +20 -1
- cognite_toolkit/_cdf_tk/client/_toolkit_client.py +23 -3
- cognite_toolkit/_cdf_tk/client/api/extended_functions.py +6 -9
- cognite_toolkit/_cdf_tk/client/api/infield.py +93 -1
- cognite_toolkit/_cdf_tk/client/api/migration.py +175 -1
- cognite_toolkit/_cdf_tk/client/api/streams.py +84 -0
- cognite_toolkit/_cdf_tk/client/api/three_d.py +50 -0
- cognite_toolkit/_cdf_tk/client/data_classes/base.py +25 -1
- cognite_toolkit/_cdf_tk/client/data_classes/canvas.py +46 -3
- cognite_toolkit/_cdf_tk/client/data_classes/charts.py +3 -3
- cognite_toolkit/_cdf_tk/client/data_classes/charts_data.py +95 -213
- cognite_toolkit/_cdf_tk/client/data_classes/infield.py +32 -18
- cognite_toolkit/_cdf_tk/client/data_classes/migration.py +10 -2
- cognite_toolkit/_cdf_tk/client/data_classes/streams.py +90 -0
- cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +47 -0
- cognite_toolkit/_cdf_tk/client/testing.py +18 -2
- cognite_toolkit/_cdf_tk/commands/__init__.py +6 -6
- cognite_toolkit/_cdf_tk/commands/_changes.py +3 -42
- cognite_toolkit/_cdf_tk/commands/_download.py +21 -11
- cognite_toolkit/_cdf_tk/commands/_migrate/__init__.py +0 -2
- cognite_toolkit/_cdf_tk/commands/_migrate/command.py +22 -20
- cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +133 -91
- cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +73 -22
- cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +311 -43
- cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py +5 -5
- cognite_toolkit/_cdf_tk/commands/_migrate/issues.py +33 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +157 -8
- cognite_toolkit/_cdf_tk/commands/_migrate/selectors.py +9 -4
- cognite_toolkit/_cdf_tk/commands/_purge.py +27 -28
- cognite_toolkit/_cdf_tk/commands/_questionary_style.py +16 -0
- cognite_toolkit/_cdf_tk/commands/_upload.py +109 -86
- cognite_toolkit/_cdf_tk/commands/about.py +221 -0
- cognite_toolkit/_cdf_tk/commands/auth.py +19 -12
- cognite_toolkit/_cdf_tk/commands/build_cmd.py +15 -61
- cognite_toolkit/_cdf_tk/commands/clean.py +63 -16
- cognite_toolkit/_cdf_tk/commands/deploy.py +20 -17
- cognite_toolkit/_cdf_tk/commands/dump_resource.py +6 -4
- cognite_toolkit/_cdf_tk/commands/init.py +225 -3
- cognite_toolkit/_cdf_tk/commands/modules.py +20 -44
- cognite_toolkit/_cdf_tk/commands/pull.py +6 -19
- cognite_toolkit/_cdf_tk/commands/resources.py +179 -0
- cognite_toolkit/_cdf_tk/constants.py +20 -1
- cognite_toolkit/_cdf_tk/cruds/__init__.py +19 -5
- cognite_toolkit/_cdf_tk/cruds/_base_cruds.py +14 -70
- cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +8 -17
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/__init__.py +4 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/agent.py +11 -9
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +4 -14
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +44 -43
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py +4 -11
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/data_organization.py +4 -13
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py +205 -66
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py +5 -17
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +116 -27
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +6 -27
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py +9 -28
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/hosted_extractors.py +12 -30
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/industrial_tool.py +3 -7
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +3 -15
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/migration.py +4 -12
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/raw.py +4 -10
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +3 -8
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/robotics.py +15 -44
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +94 -0
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/three_d_model.py +3 -7
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +5 -15
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/transformation.py +39 -31
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/workflow.py +20 -40
- cognite_toolkit/_cdf_tk/cruds/_worker.py +24 -36
- cognite_toolkit/_cdf_tk/feature_flags.py +16 -36
- cognite_toolkit/_cdf_tk/plugins.py +2 -1
- cognite_toolkit/_cdf_tk/resource_classes/__init__.py +4 -0
- cognite_toolkit/_cdf_tk/resource_classes/capabilities.py +12 -0
- cognite_toolkit/_cdf_tk/resource_classes/functions.py +3 -1
- cognite_toolkit/_cdf_tk/resource_classes/infield_cdm_location_config.py +109 -0
- cognite_toolkit/_cdf_tk/resource_classes/migration.py +8 -17
- cognite_toolkit/_cdf_tk/resource_classes/streams.py +29 -0
- cognite_toolkit/_cdf_tk/storageio/__init__.py +9 -21
- cognite_toolkit/_cdf_tk/storageio/_annotations.py +19 -16
- cognite_toolkit/_cdf_tk/storageio/_applications.py +338 -26
- cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +67 -104
- cognite_toolkit/_cdf_tk/storageio/_base.py +61 -29
- cognite_toolkit/_cdf_tk/storageio/_datapoints.py +276 -20
- cognite_toolkit/_cdf_tk/storageio/_file_content.py +436 -0
- cognite_toolkit/_cdf_tk/storageio/_instances.py +34 -2
- cognite_toolkit/_cdf_tk/storageio/_raw.py +26 -0
- cognite_toolkit/_cdf_tk/storageio/selectors/__init__.py +62 -4
- cognite_toolkit/_cdf_tk/storageio/selectors/_base.py +14 -2
- cognite_toolkit/_cdf_tk/storageio/selectors/_canvas.py +14 -0
- cognite_toolkit/_cdf_tk/storageio/selectors/_charts.py +14 -0
- cognite_toolkit/_cdf_tk/storageio/selectors/_datapoints.py +23 -3
- cognite_toolkit/_cdf_tk/storageio/selectors/_file_content.py +164 -0
- cognite_toolkit/_cdf_tk/tk_warnings/other.py +4 -0
- cognite_toolkit/_cdf_tk/tracker.py +2 -2
- cognite_toolkit/_cdf_tk/utils/dtype_conversion.py +9 -3
- cognite_toolkit/_cdf_tk/utils/fileio/__init__.py +2 -0
- cognite_toolkit/_cdf_tk/utils/fileio/_base.py +5 -1
- cognite_toolkit/_cdf_tk/utils/fileio/_readers.py +112 -20
- cognite_toolkit/_cdf_tk/utils/fileio/_writers.py +15 -15
- cognite_toolkit/_cdf_tk/utils/http_client/_client.py +284 -18
- cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py +50 -4
- cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py +187 -0
- cognite_toolkit/_cdf_tk/utils/interactive_select.py +9 -14
- cognite_toolkit/_cdf_tk/utils/sql_parser.py +2 -3
- cognite_toolkit/_cdf_tk/utils/useful_types.py +6 -2
- cognite_toolkit/_cdf_tk/validation.py +79 -1
- cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
- cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
- cognite_toolkit/_resources/cdf.toml +5 -4
- cognite_toolkit/_version.py +1 -1
- cognite_toolkit/config.dev.yaml +13 -0
- {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.30.dist-info}/METADATA +24 -24
- {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.30.dist-info}/RECORD +153 -143
- cognite_toolkit-0.7.30.dist-info/WHEEL +4 -0
- {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.30.dist-info}/entry_points.txt +1 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/canvas.py +0 -201
- cognite_toolkit/_cdf_tk/commands/dump_data.py +0 -489
- cognite_toolkit/_cdf_tk/commands/featureflag.py +0 -27
- cognite_toolkit/_cdf_tk/utils/table_writers.py +0 -434
- cognite_toolkit-0.6.97.dist-info/WHEEL +0 -4
- cognite_toolkit-0.6.97.dist-info/licenses/LICENSE +0 -18
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
from collections.abc import Sequence
|
|
3
3
|
from itertools import groupby
|
|
4
|
-
from typing import TypeVar, overload
|
|
4
|
+
from typing import Literal, TypeVar, cast, overload
|
|
5
5
|
|
|
6
6
|
from cognite.client._constants import DEFAULT_LIMIT_READ
|
|
7
7
|
from cognite.client.data_classes.data_modeling import (
|
|
@@ -10,6 +10,7 @@ from cognite.client.data_classes.data_modeling import (
|
|
|
10
10
|
NodeApplyResultList,
|
|
11
11
|
NodeId,
|
|
12
12
|
NodeList,
|
|
13
|
+
ViewId,
|
|
13
14
|
filters,
|
|
14
15
|
query,
|
|
15
16
|
)
|
|
@@ -347,9 +348,182 @@ class SpaceSourceAPI:
|
|
|
347
348
|
return results
|
|
348
349
|
|
|
349
350
|
|
|
351
|
+
_T_Cached = TypeVar("_T_Cached", bound=NodeId | ViewId)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class LookupAPI:
|
|
355
|
+
def __init__(self, instance_api: ExtendedInstancesAPI, resource_type: AssetCentricType) -> None:
|
|
356
|
+
self._instance_api = instance_api
|
|
357
|
+
self._resource_type = resource_type
|
|
358
|
+
self._view_id = InstanceSource.get_source()
|
|
359
|
+
self._node_id_by_id: dict[int, NodeId | None] = {}
|
|
360
|
+
self._node_id_by_external_id: dict[str, NodeId | None] = {}
|
|
361
|
+
self._consumer_view_id_by_id: dict[int, ViewId | None] = {}
|
|
362
|
+
self._consumer_view_id_by_external_id: dict[str, ViewId | None] = {}
|
|
363
|
+
self._RETRIEVE_LIMIT = 1000
|
|
364
|
+
|
|
365
|
+
@overload
|
|
366
|
+
def __call__(self, id: int, external_id: None = None) -> NodeId | None: ...
|
|
367
|
+
|
|
368
|
+
@overload
|
|
369
|
+
def __call__(self, id: SequenceNotStr[int], external_id: None = None) -> dict[int, NodeId]: ...
|
|
370
|
+
|
|
371
|
+
@overload
|
|
372
|
+
def __call__(self, *, external_id: str) -> NodeId | None: ...
|
|
373
|
+
|
|
374
|
+
@overload
|
|
375
|
+
def __call__(self, *, external_id: SequenceNotStr[str]) -> dict[str, NodeId]: ...
|
|
376
|
+
|
|
377
|
+
def __call__(
|
|
378
|
+
self, id: int | SequenceNotStr[int] | None = None, external_id: str | SequenceNotStr[str] | None = None
|
|
379
|
+
) -> dict[int, NodeId] | dict[str, NodeId] | NodeId | None:
|
|
380
|
+
"""Lookup NodeId by either internal ID or external ID.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
id (int | Sequence[int] | None): The internal ID(s) to lookup.
|
|
384
|
+
external_id (str | SequenceNotStr[str] | None): The external ID(s) to lookup.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
NodeId | dict[int, NodeId] | dict[str, NodeId] | None: The corresponding NodeId(s) if found, otherwise None.
|
|
388
|
+
|
|
389
|
+
"""
|
|
390
|
+
if id is not None and external_id is None:
|
|
391
|
+
return self._lookup(
|
|
392
|
+
identifier=id,
|
|
393
|
+
cache=self._node_id_by_id,
|
|
394
|
+
property_name="id",
|
|
395
|
+
return_type=NodeId,
|
|
396
|
+
input_type=int,
|
|
397
|
+
)
|
|
398
|
+
elif external_id is not None and id is None:
|
|
399
|
+
return self._lookup(
|
|
400
|
+
identifier=external_id,
|
|
401
|
+
cache=self._node_id_by_external_id,
|
|
402
|
+
property_name="classicExternalId",
|
|
403
|
+
return_type=NodeId,
|
|
404
|
+
input_type=str,
|
|
405
|
+
)
|
|
406
|
+
else:
|
|
407
|
+
raise TypeError("Either id or external_id must be provided, but not both.")
|
|
408
|
+
|
|
409
|
+
@overload
|
|
410
|
+
def consumer_view(self, id: int, external_id: None = None) -> ViewId | None: ...
|
|
411
|
+
|
|
412
|
+
@overload
|
|
413
|
+
def consumer_view(self, id: SequenceNotStr[int], external_id: None = None) -> dict[int, ViewId]: ...
|
|
414
|
+
|
|
415
|
+
@overload
|
|
416
|
+
def consumer_view(self, *, external_id: str) -> ViewId | None: ...
|
|
417
|
+
|
|
418
|
+
@overload
|
|
419
|
+
def consumer_view(self, *, external_id: SequenceNotStr[str]) -> dict[str, ViewId]: ...
|
|
420
|
+
|
|
421
|
+
def consumer_view(
|
|
422
|
+
self, id: int | SequenceNotStr[int] | None = None, external_id: str | SequenceNotStr[str] | None = None
|
|
423
|
+
) -> dict[int, ViewId] | dict[str, ViewId] | ViewId | None:
|
|
424
|
+
"""Lookup Consumer ViewId by either internal ID or external ID.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
id (int | Sequence[int] | None): The internal ID(s) to lookup.
|
|
428
|
+
external_id (str | SequenceNotStr[str] | None): The external ID(s) to lookup.
|
|
429
|
+
Returns:
|
|
430
|
+
ViewId | dict[int, ViewId] | dict[str, ViewId] | None
|
|
431
|
+
"""
|
|
432
|
+
if id is not None and external_id is None:
|
|
433
|
+
return self._lookup(
|
|
434
|
+
identifier=id,
|
|
435
|
+
cache=self._consumer_view_id_by_id,
|
|
436
|
+
property_name="id",
|
|
437
|
+
return_type=ViewId,
|
|
438
|
+
input_type=int,
|
|
439
|
+
)
|
|
440
|
+
elif external_id is not None and id is None:
|
|
441
|
+
return self._lookup(
|
|
442
|
+
identifier=external_id,
|
|
443
|
+
cache=self._consumer_view_id_by_external_id,
|
|
444
|
+
property_name="classicExternalId",
|
|
445
|
+
return_type=ViewId,
|
|
446
|
+
input_type=str,
|
|
447
|
+
)
|
|
448
|
+
else:
|
|
449
|
+
raise TypeError("Either id or external_id must be provided, but not both.")
|
|
450
|
+
|
|
451
|
+
def _lookup(
|
|
452
|
+
self,
|
|
453
|
+
identifier: _T | SequenceNotStr[_T],
|
|
454
|
+
cache: dict[_T, _T_Cached | None],
|
|
455
|
+
property_name: Literal["id", "classicExternalId"],
|
|
456
|
+
return_type: type[_T_Cached],
|
|
457
|
+
input_type: type[_T],
|
|
458
|
+
) -> dict[_T, _T_Cached] | _T_Cached | None:
|
|
459
|
+
"""Generic lookup method for both NodeId and ViewId by id or external_id."""
|
|
460
|
+
is_single = isinstance(identifier, input_type)
|
|
461
|
+
# MyPy does not understand that if is_single is True, identifier is _T, else SequenceNotStr[_T].
|
|
462
|
+
identifiers: list[_T] = [identifier] if is_single else list(identifier) # type: ignore[arg-type, list-item]
|
|
463
|
+
|
|
464
|
+
missing = [id_ for id_ in identifiers if id_ not in cache]
|
|
465
|
+
if missing:
|
|
466
|
+
self._fetch_and_cache(missing, by=property_name)
|
|
467
|
+
|
|
468
|
+
if is_single:
|
|
469
|
+
return cache.get(identifier) # type: ignore[arg-type]
|
|
470
|
+
|
|
471
|
+
return {id_: value for id_ in identifiers if isinstance(value := cache.get(id_), return_type)}
|
|
472
|
+
|
|
473
|
+
def _fetch_and_cache(self, identifiers: Sequence[int | str], by: Literal["id", "classicExternalId"]) -> None:
|
|
474
|
+
for chunk in chunker_sequence(identifiers, self._RETRIEVE_LIMIT):
|
|
475
|
+
retrieve_query = query.Query(
|
|
476
|
+
with_={
|
|
477
|
+
"instanceSource": query.NodeResultSetExpression(
|
|
478
|
+
filter=filters.And(
|
|
479
|
+
filters.HasData(views=[self._view_id]),
|
|
480
|
+
filters.Equals(self._view_id.as_property_ref("resourceType"), self._resource_type),
|
|
481
|
+
filters.In(self._view_id.as_property_ref(by), list(chunk)),
|
|
482
|
+
),
|
|
483
|
+
limit=len(chunk),
|
|
484
|
+
),
|
|
485
|
+
},
|
|
486
|
+
select={"instanceSource": query.Select([query.SourceSelector(self._view_id, ["*"])])},
|
|
487
|
+
)
|
|
488
|
+
chunk_response = self._instance_api.query(retrieve_query)
|
|
489
|
+
items = chunk_response.get("instanceSource", [])
|
|
490
|
+
for item in items:
|
|
491
|
+
instance_source = InstanceSource._load(item.dump())
|
|
492
|
+
node_id = instance_source.as_id()
|
|
493
|
+
self._node_id_by_id[instance_source.id_] = node_id
|
|
494
|
+
self._consumer_view_id_by_id[instance_source.id_] = instance_source.consumer_view()
|
|
495
|
+
if instance_source.classic_external_id:
|
|
496
|
+
self._node_id_by_external_id[instance_source.classic_external_id] = node_id
|
|
497
|
+
self._consumer_view_id_by_external_id[instance_source.classic_external_id] = (
|
|
498
|
+
instance_source.consumer_view()
|
|
499
|
+
)
|
|
500
|
+
missing = set(chunk) - set(self._node_id_by_id.keys()) - set(self._node_id_by_external_id.keys())
|
|
501
|
+
if by == "id":
|
|
502
|
+
for missing_id in cast(set[int], missing):
|
|
503
|
+
if missing_id not in self._node_id_by_id:
|
|
504
|
+
self._node_id_by_id[missing_id] = None
|
|
505
|
+
if missing_id not in self._consumer_view_id_by_id:
|
|
506
|
+
self._consumer_view_id_by_id[missing_id] = None
|
|
507
|
+
elif by == "classicExternalId":
|
|
508
|
+
for missing_ext_id in cast(set[str], missing):
|
|
509
|
+
if missing_ext_id not in self._node_id_by_external_id:
|
|
510
|
+
self._node_id_by_external_id[missing_ext_id] = None
|
|
511
|
+
if missing_ext_id not in self._consumer_view_id_by_external_id:
|
|
512
|
+
self._consumer_view_id_by_external_id[missing_ext_id] = None
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
class MigrationLookupAPI:
|
|
516
|
+
def __init__(self, instance_api: ExtendedInstancesAPI) -> None:
|
|
517
|
+
self.assets = LookupAPI(instance_api, "asset")
|
|
518
|
+
self.events = LookupAPI(instance_api, "event")
|
|
519
|
+
self.files = LookupAPI(instance_api, "file")
|
|
520
|
+
self.time_series = LookupAPI(instance_api, "timeseries")
|
|
521
|
+
|
|
522
|
+
|
|
350
523
|
class MigrationAPI:
|
|
351
524
|
def __init__(self, instance_api: ExtendedInstancesAPI) -> None:
|
|
352
525
|
self.instance_source = InstanceSourceAPI(instance_api)
|
|
353
526
|
self.resource_view_mapping = ResourceViewMappingAPI(instance_api)
|
|
354
527
|
self.created_source_system = CreatedSourceSystemAPI(instance_api)
|
|
355
528
|
self.space_source = SpaceSourceAPI(instance_api)
|
|
529
|
+
self.lookup = MigrationLookupAPI(instance_api)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
5
|
+
from cognite_toolkit._cdf_tk.client.data_classes.api_classes import PagedResponse
|
|
6
|
+
from cognite_toolkit._cdf_tk.client.data_classes.streams import StreamRequest, StreamResponse
|
|
7
|
+
from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient, ItemsRequest, ParamRequest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StreamsAPI:
|
|
11
|
+
ENDPOINT = "/streams"
|
|
12
|
+
|
|
13
|
+
def __init__(self, http_client: HTTPClient, console: Console) -> None:
|
|
14
|
+
self._http_client = http_client
|
|
15
|
+
self._console = console
|
|
16
|
+
self._config = http_client.config
|
|
17
|
+
|
|
18
|
+
def create(self, items: Sequence[StreamRequest]) -> list[StreamResponse]:
|
|
19
|
+
"""Create one or more streams.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
items: Sequence of StreamRequest items to create.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
List of created StreamResponse items.
|
|
26
|
+
"""
|
|
27
|
+
responses = self._http_client.request_with_retries(
|
|
28
|
+
ItemsRequest(
|
|
29
|
+
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
30
|
+
method="POST",
|
|
31
|
+
items=list(items),
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
responses.raise_for_status()
|
|
35
|
+
return PagedResponse[StreamResponse].model_validate(responses.get_first_body()).items
|
|
36
|
+
|
|
37
|
+
def delete(self, external_id: str) -> None:
|
|
38
|
+
"""Delete stream using its external ID.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
external_id: External ID of the stream to delete.
|
|
42
|
+
"""
|
|
43
|
+
responses = self._http_client.request_with_retries(
|
|
44
|
+
ParamRequest(
|
|
45
|
+
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/{external_id}"),
|
|
46
|
+
method="DELETE",
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
responses.raise_for_status()
|
|
50
|
+
|
|
51
|
+
def list(self) -> list[StreamResponse]:
|
|
52
|
+
"""List streams.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
StreamResponseList containing the listed streams.
|
|
56
|
+
"""
|
|
57
|
+
responses = self._http_client.request_with_retries(
|
|
58
|
+
ParamRequest(
|
|
59
|
+
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
60
|
+
method="GET",
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
responses.raise_for_status()
|
|
64
|
+
return PagedResponse[StreamResponse].model_validate(responses.get_first_body()).items
|
|
65
|
+
|
|
66
|
+
def retrieve(self, external_id: str, include_statistics: bool = True) -> StreamResponse:
|
|
67
|
+
"""Retrieve a stream by its external ID.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
external_id: External ID of the stream to retrieve.
|
|
71
|
+
include_statistics: Whether to include usage statistics in the response.
|
|
72
|
+
Returns:
|
|
73
|
+
StreamResponse item.
|
|
74
|
+
"""
|
|
75
|
+
responses = self._http_client.request_with_retries(
|
|
76
|
+
ParamRequest(
|
|
77
|
+
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/{external_id}"),
|
|
78
|
+
method="GET",
|
|
79
|
+
parameters={"includeStatistics": include_statistics},
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
responses.raise_for_status()
|
|
83
|
+
response_body = responses.get_first_body()
|
|
84
|
+
return StreamResponse.model_validate(response_body)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from rich.console import Console
|
|
2
|
+
|
|
3
|
+
from cognite_toolkit._cdf_tk.client.data_classes.api_classes import PagedResponse
|
|
4
|
+
from cognite_toolkit._cdf_tk.client.data_classes.three_d import ThreeDModelResponse
|
|
5
|
+
from cognite_toolkit._cdf_tk.utils.http_client import HTTPClient, ParamRequest
|
|
6
|
+
from cognite_toolkit._cdf_tk.utils.useful_types import PrimitiveType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ThreeDModelAPI:
|
|
10
|
+
ENDPOINT = "/3d/models"
|
|
11
|
+
|
|
12
|
+
def __init__(self, http_client: HTTPClient, console: Console) -> None:
|
|
13
|
+
self._http_client = http_client
|
|
14
|
+
self._console = console
|
|
15
|
+
self._config = http_client.config
|
|
16
|
+
|
|
17
|
+
def iterate(
|
|
18
|
+
self,
|
|
19
|
+
published: bool | None = None,
|
|
20
|
+
include_revision_info: bool = False,
|
|
21
|
+
limit: int = 100,
|
|
22
|
+
cursor: str | None = None,
|
|
23
|
+
) -> PagedResponse[ThreeDModelResponse]:
|
|
24
|
+
if not (0 < limit <= 1000):
|
|
25
|
+
raise ValueError("Limit must be between 1 and 1000.")
|
|
26
|
+
parameters: dict[str, PrimitiveType] = {
|
|
27
|
+
# There is a bug in the API. The parameter includeRevisionInfo is expected to be lower case and not
|
|
28
|
+
# camel case as documented. You get error message: Unrecognized query parameter includeRevisionInfo,
|
|
29
|
+
# did you mean includerevisioninfo?
|
|
30
|
+
"includerevisioninfo": include_revision_info,
|
|
31
|
+
"limit": limit,
|
|
32
|
+
}
|
|
33
|
+
if published is not None:
|
|
34
|
+
parameters["published"] = published
|
|
35
|
+
if cursor is not None:
|
|
36
|
+
parameters["cursor"] = cursor
|
|
37
|
+
responses = self._http_client.request_with_retries(
|
|
38
|
+
ParamRequest(
|
|
39
|
+
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
40
|
+
method="GET",
|
|
41
|
+
parameters=parameters,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
responses.raise_for_status()
|
|
45
|
+
return PagedResponse[ThreeDModelResponse].model_validate(responses.get_first_body())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ThreeDAPI:
|
|
49
|
+
def __init__(self, http_client: HTTPClient, console: Console) -> None:
|
|
50
|
+
self.models = ThreeDModelAPI(http_client, console)
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
|
-
from
|
|
3
|
+
from collections import UserList
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
4
5
|
|
|
5
6
|
from pydantic import BaseModel, ConfigDict
|
|
6
7
|
from pydantic.alias_generators import to_camel
|
|
7
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from cognite.client import CogniteClient
|
|
11
|
+
|
|
8
12
|
if sys.version_info >= (3, 11):
|
|
9
13
|
from typing import Self
|
|
10
14
|
else:
|
|
@@ -61,3 +65,23 @@ class Identifier(BaseModel):
|
|
|
61
65
|
|
|
62
66
|
def as_id(self) -> Self:
|
|
63
67
|
return self
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
T_Resource = TypeVar("T_Resource", bound=RequestResource | ResponseResource)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class BaseResourceList(UserList[T_Resource]):
|
|
74
|
+
"""Base class for resource lists."""
|
|
75
|
+
|
|
76
|
+
_RESOURCE: type[T_Resource]
|
|
77
|
+
|
|
78
|
+
def __init__(self, initlist: list[T_Resource] | None = None, **_: Any) -> None:
|
|
79
|
+
super().__init__(initlist or [])
|
|
80
|
+
|
|
81
|
+
def dump(self, camel_case: bool = True) -> list[dict[str, Any]]:
|
|
82
|
+
return [item.dump(camel_case) for item in self.data]
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def load(cls, data: list[dict[str, Any]], cognite_client: "CogniteClient | None" = None) -> Self:
|
|
86
|
+
items = [cls._RESOURCE.model_validate(item) for item in data]
|
|
87
|
+
return cls(items) # type: ignore[arg-type]
|
|
@@ -2,7 +2,7 @@ import sys
|
|
|
2
2
|
from abc import ABC
|
|
3
3
|
from collections.abc import Sequence
|
|
4
4
|
from datetime import datetime, timezone
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any, TypeVar
|
|
6
6
|
from uuid import uuid4
|
|
7
7
|
|
|
8
8
|
from cognite.client import CogniteClient
|
|
@@ -31,6 +31,7 @@ from cognite.client.data_classes.data_modeling.instances import (
|
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
from cognite_toolkit._cdf_tk.client.data_classes.migration import AssetCentricId
|
|
34
|
+
from cognite_toolkit._cdf_tk.utils.useful_types import JsonVal
|
|
34
35
|
|
|
35
36
|
if sys.version_info >= (3, 11):
|
|
36
37
|
from typing import Self
|
|
@@ -68,6 +69,9 @@ class ExtendedTypedNodeApply(TypedNodeApply, ABC):
|
|
|
68
69
|
return output
|
|
69
70
|
|
|
70
71
|
|
|
72
|
+
T_ExtendedTypedNodeApply = TypeVar("T_ExtendedTypedNodeApply", bound=ExtendedTypedNodeApply)
|
|
73
|
+
|
|
74
|
+
|
|
71
75
|
class _CanvasProperties:
|
|
72
76
|
created_by = PropertyOptions("createdBy")
|
|
73
77
|
updated_at = PropertyOptions("updatedAt")
|
|
@@ -901,7 +905,7 @@ class IndustrialCanvasApply(CogniteResource):
|
|
|
901
905
|
raise TypeError(f"Unexpected instance type: {type(instance)}")
|
|
902
906
|
return ids
|
|
903
907
|
|
|
904
|
-
def dump(self, keep_existing_version: bool = True) -> dict[str,
|
|
908
|
+
def dump(self, keep_existing_version: bool = True) -> dict[str, JsonVal]:
|
|
905
909
|
"""Dump the IndustrialCanvasApply to a dictionary."""
|
|
906
910
|
return {
|
|
907
911
|
"canvas": self.canvas.dump(keep_existing_version=keep_existing_version),
|
|
@@ -953,7 +957,46 @@ class IndustrialCanvasApply(CogniteResource):
|
|
|
953
957
|
@classmethod
|
|
954
958
|
def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> Self:
|
|
955
959
|
"""Load an IndustrialCanvasApply instance from a resource dictionary."""
|
|
956
|
-
|
|
960
|
+
if "canvas" not in resource:
|
|
961
|
+
raise ValueError("Resource does not contain a canvas node.")
|
|
962
|
+
canvas_resource = resource["canvas"]
|
|
963
|
+
if isinstance(canvas_resource, dict):
|
|
964
|
+
canvas = CanvasApply._load(canvas_resource)
|
|
965
|
+
elif isinstance(canvas_resource, CanvasApply):
|
|
966
|
+
canvas = canvas_resource
|
|
967
|
+
elif isinstance(canvas_resource, NodeApply):
|
|
968
|
+
canvas = CanvasApply._load(canvas_resource.dump())
|
|
969
|
+
else:
|
|
970
|
+
raise TypeError(f"Canvas resource {type(canvas_resource)} is not supported.")
|
|
971
|
+
return cls(
|
|
972
|
+
canvas=canvas,
|
|
973
|
+
annotations=cls._load_apply_items(resource.get("annotations"), CanvasAnnotationApply),
|
|
974
|
+
container_references=cls._load_apply_items(resource.get("containerReferences"), ContainerReferenceApply),
|
|
975
|
+
fdm_instance_container_references=cls._load_apply_items(
|
|
976
|
+
resource.get("fdmInstanceContainerReferences"), FdmInstanceContainerReferenceApply
|
|
977
|
+
),
|
|
978
|
+
solution_tags=cls._load_apply_items(resource.get("solutionTags"), CogniteSolutionTagApply),
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
@classmethod
|
|
982
|
+
def _load_apply_items(
|
|
983
|
+
cls, items: object | None, node_cls: type[T_ExtendedTypedNodeApply]
|
|
984
|
+
) -> list[T_ExtendedTypedNodeApply]:
|
|
985
|
+
if items is None:
|
|
986
|
+
return []
|
|
987
|
+
elif isinstance(items, Sequence):
|
|
988
|
+
nodes: list[T_ExtendedTypedNodeApply] = []
|
|
989
|
+
for node in items:
|
|
990
|
+
if isinstance(node, dict):
|
|
991
|
+
nodes.append(node_cls._load(node))
|
|
992
|
+
elif isinstance(node, node_cls):
|
|
993
|
+
nodes.append(node)
|
|
994
|
+
elif isinstance(node, NodeApply):
|
|
995
|
+
nodes.append(node_cls._load(node.dump()))
|
|
996
|
+
else:
|
|
997
|
+
raise TypeError(f"Expected a sequence of {node_cls.__name__}, got {type(node).__name__}")
|
|
998
|
+
return nodes
|
|
999
|
+
raise TypeError(f"Expected a sequence of {node_cls.__name__}, got {type(items).__name__}")
|
|
957
1000
|
|
|
958
1001
|
|
|
959
1002
|
class IndustrialCanvas(WriteableCogniteResource[IndustrialCanvasApply]):
|
|
@@ -36,7 +36,7 @@ class ChartCore(WriteableCogniteResource["ChartWrite"], ABC):
|
|
|
36
36
|
def dump(self, camel_case: bool = True) -> dict[str, Any]:
|
|
37
37
|
"""Convert the chart to a dictionary representation."""
|
|
38
38
|
output = super().dump(camel_case=camel_case)
|
|
39
|
-
output["data"] = self.data.
|
|
39
|
+
output["data"] = self.data.model_dump(mode="json", by_alias=camel_case, exclude_unset=True)
|
|
40
40
|
return output
|
|
41
41
|
|
|
42
42
|
|
|
@@ -58,7 +58,7 @@ class ChartWrite(ChartCore):
|
|
|
58
58
|
return cls(
|
|
59
59
|
external_id=resource["externalId"],
|
|
60
60
|
visibility=resource["visibility"],
|
|
61
|
-
data=ChartData._load(resource["data"]
|
|
61
|
+
data=ChartData._load(resource["data"]),
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
|
|
@@ -98,7 +98,7 @@ class Chart(ChartCore):
|
|
|
98
98
|
created_time=resource["createdTime"],
|
|
99
99
|
last_updated_time=resource["lastUpdatedTime"],
|
|
100
100
|
visibility=resource["visibility"],
|
|
101
|
-
data=ChartData._load(resource["data"]
|
|
101
|
+
data=ChartData._load(resource["data"]),
|
|
102
102
|
owner_id=resource["ownerId"],
|
|
103
103
|
)
|
|
104
104
|
|