cognite-toolkit 0.7.35__py3-none-any.whl → 0.7.37__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_tk/apps/_migrate_app.py +101 -0
- cognite_toolkit/_cdf_tk/client/api/infield.py +16 -17
- cognite_toolkit/_cdf_tk/client/api/streams.py +3 -4
- cognite_toolkit/_cdf_tk/client/api/three_d.py +266 -13
- cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py +13 -0
- cognite_toolkit/_cdf_tk/client/data_classes/base.py +6 -10
- cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +7 -7
- cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +59 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +7 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +50 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/data_model.py +1 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +119 -8
- cognite_toolkit/_cdf_tk/commands/_purge.py +9 -11
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +17 -15
- cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py +1 -3
- 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 +1 -1
- cognite_toolkit/_version.py +1 -1
- {cognite_toolkit-0.7.35.dist-info → cognite_toolkit-0.7.37.dist-info}/METADATA +1 -1
- {cognite_toolkit-0.7.35.dist-info → cognite_toolkit-0.7.37.dist-info}/RECORD +23 -23
- {cognite_toolkit-0.7.35.dist-info → cognite_toolkit-0.7.37.dist-info}/WHEEL +1 -1
- {cognite_toolkit-0.7.35.dist-info → cognite_toolkit-0.7.37.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from collections.abc import Hashable
|
|
1
2
|
from typing import Literal
|
|
2
3
|
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
3
6
|
from .base import BaseModelObject, RequestResource, ResponseResource
|
|
4
7
|
|
|
5
8
|
|
|
@@ -51,3 +54,59 @@ class ThreeDModelResponse(ResponseResource[ThreeDModelRequest]):
|
|
|
51
54
|
return ThreeDModelClassicRequest._load(self.dump())
|
|
52
55
|
else:
|
|
53
56
|
return ThreeDModelDMSRequest._load(self.dump())
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class AssetMappingDMRequest(RequestResource):
|
|
60
|
+
node_id: int
|
|
61
|
+
asset_instance_id: NodeReference
|
|
62
|
+
# These fields are part of the path request and not the body schema.
|
|
63
|
+
model_id: int = Field(exclude=True)
|
|
64
|
+
revision_id: int = Field(exclude=True)
|
|
65
|
+
|
|
66
|
+
def as_id(self) -> Hashable:
|
|
67
|
+
return (
|
|
68
|
+
self.model_id,
|
|
69
|
+
self.revision_id,
|
|
70
|
+
self.node_id,
|
|
71
|
+
self.asset_instance_id.space,
|
|
72
|
+
self.asset_instance_id.external_id,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AssetMappingClassicRequest(RequestResource):
|
|
77
|
+
node_id: int
|
|
78
|
+
asset_id: int | None = None
|
|
79
|
+
asset_instance_id: NodeReference | None = None
|
|
80
|
+
# These fields are part of the path request and not the body schema.
|
|
81
|
+
model_id: int = Field(exclude=True)
|
|
82
|
+
revision_id: int = Field(exclude=True)
|
|
83
|
+
|
|
84
|
+
def as_id(self) -> Hashable:
|
|
85
|
+
if self.asset_id:
|
|
86
|
+
return self.model_id, self.revision_id, self.node_id, self.asset_id
|
|
87
|
+
elif self.asset_instance_id:
|
|
88
|
+
return (
|
|
89
|
+
self.model_id,
|
|
90
|
+
self.revision_id,
|
|
91
|
+
self.node_id,
|
|
92
|
+
self.asset_instance_id.space,
|
|
93
|
+
self.asset_instance_id.external_id,
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
raise AttributeError("asset_id or asset_instance_id is required")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class AssetMappingResponse(ResponseResource[AssetMappingClassicRequest]):
|
|
100
|
+
node_id: int
|
|
101
|
+
asset_id: int | None = None
|
|
102
|
+
asset_instance_id: NodeReference | None = None
|
|
103
|
+
tree_index: int | None = None
|
|
104
|
+
subtree_size: int | None = None
|
|
105
|
+
# These fields are part of the path request and response, but they are included here for convenience.
|
|
106
|
+
model_id: int = Field(exclude=True)
|
|
107
|
+
revision_id: int = Field(exclude=True)
|
|
108
|
+
|
|
109
|
+
def as_request_resource(self) -> AssetMappingClassicRequest:
|
|
110
|
+
return AssetMappingClassicRequest.model_validate(
|
|
111
|
+
{**self.dump(), "modelId": self.model_id, "revisionId": self.revision_id}
|
|
112
|
+
)
|
|
@@ -9,6 +9,7 @@ from cognite.client.data_classes.data_modeling import (
|
|
|
9
9
|
MappedProperty,
|
|
10
10
|
NodeApply,
|
|
11
11
|
NodeId,
|
|
12
|
+
ViewId,
|
|
12
13
|
)
|
|
13
14
|
from cognite.client.data_classes.data_modeling.instances import EdgeApply, NodeOrEdgeData, PropertyValueWrite
|
|
14
15
|
from cognite.client.data_classes.data_modeling.views import ViewProperty
|
|
@@ -29,7 +30,7 @@ from cognite_toolkit._cdf_tk.utils.useful_types import (
|
|
|
29
30
|
AssetCentricTypeExtended,
|
|
30
31
|
)
|
|
31
32
|
|
|
32
|
-
from .data_model import INSTANCE_SOURCE_VIEW_ID
|
|
33
|
+
from .data_model import COGNITE_MIGRATION_SPACE_ID, INSTANCE_SOURCE_VIEW_ID
|
|
33
34
|
from .issues import ConversionIssue, FailedConversion, InvalidPropertyDataType
|
|
34
35
|
|
|
35
36
|
|
|
@@ -169,6 +170,7 @@ def asset_centric_to_dm(
|
|
|
169
170
|
view_source: ResourceViewMappingApply,
|
|
170
171
|
view_properties: dict[str, ViewProperty],
|
|
171
172
|
direct_relation_cache: DirectRelationCache,
|
|
173
|
+
preferred_consumer_view: ViewId | None = None,
|
|
172
174
|
) -> tuple[NodeApply | EdgeApply | None, ConversionIssue]:
|
|
173
175
|
"""Convert an asset-centric resource to a data model instance.
|
|
174
176
|
|
|
@@ -178,6 +180,7 @@ def asset_centric_to_dm(
|
|
|
178
180
|
view_source (ResourceViewMappingApply): The view source defining how to map the resource to the data model.
|
|
179
181
|
view_properties (dict[str, ViewProperty]): The defined properties referenced in the view source mapping.
|
|
180
182
|
direct_relation_cache (DirectRelationCache): Cache for direct relation references.
|
|
183
|
+
preferred_consumer_view (ViewId | None): The preferred consumer view for the instance.
|
|
181
184
|
|
|
182
185
|
Returns:
|
|
183
186
|
tuple[NodeApply | EdgeApply, ConversionIssue]: A tuple containing the converted NodeApply and any ConversionIssue encountered.
|
|
@@ -213,7 +216,10 @@ def asset_centric_to_dm(
|
|
|
213
216
|
"id": id_,
|
|
214
217
|
"dataSetId": data_set_id,
|
|
215
218
|
"classicExternalId": external_id,
|
|
219
|
+
"resourceViewMapping": {"space": COGNITE_MIGRATION_SPACE_ID, "externalId": view_source.external_id},
|
|
216
220
|
}
|
|
221
|
+
if preferred_consumer_view:
|
|
222
|
+
instance_source_properties["preferredConsumerViewId"] = preferred_consumer_view.dump()
|
|
217
223
|
sources.append(NodeOrEdgeData(source=INSTANCE_SOURCE_VIEW_ID, properties=instance_source_properties))
|
|
218
224
|
|
|
219
225
|
instance: NodeApply | EdgeApply
|
|
@@ -29,7 +29,13 @@ from cognite_toolkit._cdf_tk.client.data_classes.legacy.canvas import (
|
|
|
29
29
|
)
|
|
30
30
|
from cognite_toolkit._cdf_tk.client.data_classes.legacy.charts import Chart, ChartWrite
|
|
31
31
|
from cognite_toolkit._cdf_tk.client.data_classes.legacy.migration import ResourceViewMappingApply
|
|
32
|
-
from cognite_toolkit._cdf_tk.client.data_classes.three_d import
|
|
32
|
+
from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
|
|
33
|
+
AssetMappingDMRequest,
|
|
34
|
+
AssetMappingResponse,
|
|
35
|
+
NodeReference,
|
|
36
|
+
RevisionStatus,
|
|
37
|
+
ThreeDModelResponse,
|
|
38
|
+
)
|
|
33
39
|
from cognite_toolkit._cdf_tk.commands._migrate.conversion import DirectRelationCache, asset_centric_to_dm
|
|
34
40
|
from cognite_toolkit._cdf_tk.commands._migrate.data_classes import (
|
|
35
41
|
Model,
|
|
@@ -141,6 +147,7 @@ class AssetCentricMapper(
|
|
|
141
147
|
view_source=view_source,
|
|
142
148
|
view_properties=view_properties,
|
|
143
149
|
direct_relation_cache=self._direct_relation_cache,
|
|
150
|
+
preferred_consumer_view=mapping.preferred_consumer_view,
|
|
144
151
|
)
|
|
145
152
|
if mapping.instance_id.space == MISSING_INSTANCE_SPACE:
|
|
146
153
|
conversion_issue.missing_instance_space = f"Missing instance space for dataset ID {mapping.data_set_id!r}"
|
|
@@ -469,3 +476,45 @@ class ThreeDMapper(DataMapper[ThreeDSelector, ThreeDModelResponse, ThreeDMigrati
|
|
|
469
476
|
return "PointCloud"
|
|
470
477
|
else:
|
|
471
478
|
return None
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class ThreeDAssetMapper(DataMapper[ThreeDSelector, AssetMappingResponse, AssetMappingDMRequest]):
|
|
482
|
+
def __init__(self, client: ToolkitClient) -> None:
|
|
483
|
+
self.client = client
|
|
484
|
+
|
|
485
|
+
def map(
|
|
486
|
+
self, source: Sequence[AssetMappingResponse]
|
|
487
|
+
) -> Sequence[tuple[AssetMappingDMRequest | None, MigrationIssue]]:
|
|
488
|
+
output: list[tuple[AssetMappingDMRequest | None, MigrationIssue]] = []
|
|
489
|
+
self._populate_cache(source)
|
|
490
|
+
for item in source:
|
|
491
|
+
mapped_item, issue = self._map_single_item(item)
|
|
492
|
+
output.append((mapped_item, issue))
|
|
493
|
+
return output
|
|
494
|
+
|
|
495
|
+
def _populate_cache(self, source: Sequence[AssetMappingResponse]) -> None:
|
|
496
|
+
asset_ids: set[int] = set()
|
|
497
|
+
for mapping in source:
|
|
498
|
+
if mapping.asset_id is not None:
|
|
499
|
+
asset_ids.add(mapping.asset_id)
|
|
500
|
+
self.client.migration.lookup.assets(list(asset_ids))
|
|
501
|
+
|
|
502
|
+
def _map_single_item(
|
|
503
|
+
self, item: AssetMappingResponse
|
|
504
|
+
) -> tuple[AssetMappingDMRequest | None, ThreeDModelMigrationIssue]:
|
|
505
|
+
issue = ThreeDModelMigrationIssue(model_name=f"AssetMapping_{item.model_id}", model_id=item.model_id)
|
|
506
|
+
asset_instance_id = item.asset_instance_id
|
|
507
|
+
if item.asset_id and asset_instance_id is None:
|
|
508
|
+
asset_node_id = self.client.migration.lookup.assets(item.asset_id)
|
|
509
|
+
if asset_node_id is None:
|
|
510
|
+
issue.error_message.append(f"Missing asset instance for asset ID {item.asset_id!r}")
|
|
511
|
+
return None, issue
|
|
512
|
+
asset_instance_id = NodeReference(space=asset_node_id.space, externalId=asset_node_id.external_id)
|
|
513
|
+
|
|
514
|
+
if asset_instance_id is None:
|
|
515
|
+
issue.error_message.append("Neither assetInstanceId nor assetId provided for mapping.")
|
|
516
|
+
return None, issue
|
|
517
|
+
mapped_request = AssetMappingDMRequest(
|
|
518
|
+
modelId=item.model_id, revisionId=item.revision_id, nodeId=item.node_id, assetInstanceId=asset_instance_id
|
|
519
|
+
)
|
|
520
|
+
return mapped_request, issue
|
|
@@ -4,6 +4,7 @@ from cognite.client.data_classes.data_modeling.containers import BTreeIndex
|
|
|
4
4
|
SPACE = dm.SpaceApply(
|
|
5
5
|
"cognite_migration", description="Space for the asset-centric to data modeling migration", name="cdf_migration"
|
|
6
6
|
)
|
|
7
|
+
COGNITE_MIGRATION_SPACE_ID = SPACE.space
|
|
7
8
|
|
|
8
9
|
RESOURCE_VIEW_MAPPING = dm.ContainerApply(
|
|
9
10
|
space=SPACE.space,
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
from collections.abc import Iterable, Iterator, Mapping, Sequence
|
|
2
|
-
from typing import ClassVar, cast
|
|
2
|
+
from typing import ClassVar, Literal, cast
|
|
3
3
|
|
|
4
4
|
from cognite.client.data_classes import Annotation
|
|
5
5
|
from cognite.client.data_classes.data_modeling import EdgeId, InstanceApply, NodeId
|
|
6
6
|
|
|
7
7
|
from cognite_toolkit._cdf_tk.client import ToolkitClient
|
|
8
8
|
from cognite_toolkit._cdf_tk.client.data_classes.legacy.pending_instances_ids import PendingInstanceId
|
|
9
|
-
from cognite_toolkit._cdf_tk.client.data_classes.three_d import
|
|
9
|
+
from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
|
|
10
|
+
AssetMappingDMRequest,
|
|
11
|
+
AssetMappingResponse,
|
|
12
|
+
ThreeDModelResponse,
|
|
13
|
+
)
|
|
10
14
|
from cognite_toolkit._cdf_tk.commands._migrate.data_classes import ThreeDMigrationRequest
|
|
11
15
|
from cognite_toolkit._cdf_tk.constants import MISSING_EXTERNAL_ID, MISSING_INSTANCE_SPACE
|
|
12
16
|
from cognite_toolkit._cdf_tk.exceptions import ToolkitNotImplementedError, ToolkitValueError
|
|
@@ -14,6 +18,7 @@ from cognite_toolkit._cdf_tk.storageio import (
|
|
|
14
18
|
AnnotationIO,
|
|
15
19
|
HierarchyIO,
|
|
16
20
|
InstanceIO,
|
|
21
|
+
T_Selector,
|
|
17
22
|
UploadableStorageIO,
|
|
18
23
|
)
|
|
19
24
|
from cognite_toolkit._cdf_tk.storageio._base import Page, UploadItem
|
|
@@ -366,6 +371,14 @@ class AnnotationMigrationIO(
|
|
|
366
371
|
|
|
367
372
|
|
|
368
373
|
class ThreeDMigrationIO(UploadableStorageIO[ThreeDSelector, ThreeDModelResponse, ThreeDMigrationRequest]):
|
|
374
|
+
"""IO class for downloading and migrating 3D models.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
client: The ToolkitClient to use for CDF interactions.
|
|
378
|
+
data_model_type: The type of 3D data model to download. Either "classic" or "DM".
|
|
379
|
+
|
|
380
|
+
"""
|
|
381
|
+
|
|
369
382
|
KIND = "3DMigration"
|
|
370
383
|
SUPPORTED_DOWNLOAD_FORMATS = frozenset({".ndjson"})
|
|
371
384
|
SUPPORTED_COMPRESSIONS = frozenset({".gz"})
|
|
@@ -375,9 +388,22 @@ class ThreeDMigrationIO(UploadableStorageIO[ThreeDSelector, ThreeDModelResponse,
|
|
|
375
388
|
UPLOAD_ENDPOINT = "/3d/migrate/models"
|
|
376
389
|
REVISION_ENDPOINT = "/3d/migrate/revisions"
|
|
377
390
|
|
|
391
|
+
def __init__(self, client: ToolkitClient, data_model_type: Literal["classic", "data modeling"] = "classic") -> None:
|
|
392
|
+
super().__init__(client)
|
|
393
|
+
self.data_model_type = data_model_type
|
|
394
|
+
|
|
378
395
|
def as_id(self, item: ThreeDModelResponse) -> str:
|
|
379
396
|
return f"{item.name}_{item.id!s}"
|
|
380
397
|
|
|
398
|
+
def _is_selected(self, item: ThreeDModelResponse, included_models: set[int] | None) -> bool:
|
|
399
|
+
return self._is_correct_type(item) and (included_models is None or item.id in included_models)
|
|
400
|
+
|
|
401
|
+
def _is_correct_type(self, item: ThreeDModelResponse) -> bool:
|
|
402
|
+
if self.data_model_type == "classic":
|
|
403
|
+
return item.space is None
|
|
404
|
+
else:
|
|
405
|
+
return item.space is not None
|
|
406
|
+
|
|
381
407
|
def stream_data(self, selector: ThreeDSelector, limit: int | None = None) -> Iterable[Page[ThreeDModelResponse]]:
|
|
382
408
|
published: bool | None = None
|
|
383
409
|
if isinstance(selector, ThreeDModelFilteredSelector):
|
|
@@ -392,12 +418,7 @@ class ThreeDMigrationIO(UploadableStorageIO[ThreeDSelector, ThreeDModelResponse,
|
|
|
392
418
|
response = self.client.tool.three_d.models.iterate(
|
|
393
419
|
published=published, include_revision_info=True, limit=request_limit, cursor=cursor
|
|
394
420
|
)
|
|
395
|
-
|
|
396
|
-
items = [
|
|
397
|
-
item
|
|
398
|
-
for item in response.items
|
|
399
|
-
if item.space is None and (included_models is None or item.id in included_models)
|
|
400
|
-
]
|
|
421
|
+
items = [item for item in response.items if self._is_selected(item, included_models)]
|
|
401
422
|
total += len(items)
|
|
402
423
|
if items:
|
|
403
424
|
yield Page(worker_id="main", items=items, next_cursor=response.next_cursor)
|
|
@@ -454,3 +475,93 @@ class ThreeDMigrationIO(UploadableStorageIO[ThreeDSelector, ThreeDModelResponse,
|
|
|
454
475
|
)
|
|
455
476
|
results.extend(revision.as_item_responses(data.source_id))
|
|
456
477
|
return results
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
class ThreeDAssetMappingMigrationIO(UploadableStorageIO[ThreeDSelector, AssetMappingResponse, AssetMappingDMRequest]):
|
|
481
|
+
KIND = "3DMigrationAssetMapping"
|
|
482
|
+
SUPPORTED_DOWNLOAD_FORMATS = frozenset({".ndjson"})
|
|
483
|
+
SUPPORTED_COMPRESSIONS = frozenset({".gz"})
|
|
484
|
+
SUPPORTED_READ_FORMATS = frozenset({".ndjson"})
|
|
485
|
+
DOWNLOAD_LIMIT = 1000
|
|
486
|
+
CHUNK_SIZE = 100
|
|
487
|
+
UPLOAD_ENDPOINT = "/3d/models/{modelId}/revisions/{revisionId}/mappings"
|
|
488
|
+
|
|
489
|
+
def __init__(self, client: ToolkitClient, object_3D_space: str, cad_node_space: str) -> None:
|
|
490
|
+
super().__init__(client)
|
|
491
|
+
self.object_3D_space = object_3D_space
|
|
492
|
+
self.cad_node_space = cad_node_space
|
|
493
|
+
# We can only migrate asset mappings for 3D models that are already migrated to data modeling.
|
|
494
|
+
self._3D_io = ThreeDMigrationIO(client, data_model_type="data modeling")
|
|
495
|
+
|
|
496
|
+
def as_id(self, item: AssetMappingResponse) -> str:
|
|
497
|
+
return f"AssetMapping_{item.model_id!s}_{item.revision_id!s}_{item.asset_id!s}"
|
|
498
|
+
|
|
499
|
+
def stream_data(self, selector: ThreeDSelector, limit: int | None = None) -> Iterable[Page[AssetMappingResponse]]:
|
|
500
|
+
total = 0
|
|
501
|
+
for three_d_page in self._3D_io.stream_data(selector, None):
|
|
502
|
+
for model in three_d_page.items:
|
|
503
|
+
if model.last_revision_info is None or model.last_revision_info.revision_id is None:
|
|
504
|
+
# No revisions, so no asset mappings to
|
|
505
|
+
continue
|
|
506
|
+
cursor: str | None = None
|
|
507
|
+
while True:
|
|
508
|
+
request_limit = (
|
|
509
|
+
min(self.DOWNLOAD_LIMIT, limit - total) if limit is not None else self.DOWNLOAD_LIMIT
|
|
510
|
+
)
|
|
511
|
+
if limit is not None and total >= limit:
|
|
512
|
+
return
|
|
513
|
+
response = self.client.tool.three_d.asset_mappings.iterate(
|
|
514
|
+
model_id=model.id,
|
|
515
|
+
revision_id=model.last_revision_info.revision_id,
|
|
516
|
+
cursor=cursor,
|
|
517
|
+
limit=request_limit,
|
|
518
|
+
)
|
|
519
|
+
items = response.items
|
|
520
|
+
total += len(items)
|
|
521
|
+
if items:
|
|
522
|
+
yield Page(worker_id="main", items=items, next_cursor=response.next_cursor)
|
|
523
|
+
if response.next_cursor is None:
|
|
524
|
+
break
|
|
525
|
+
cursor = response.next_cursor
|
|
526
|
+
|
|
527
|
+
def count(self, selector: ThreeDSelector) -> int | None:
|
|
528
|
+
# There is no efficient way to count 3D asset mappings in CDF.
|
|
529
|
+
return None
|
|
530
|
+
|
|
531
|
+
def upload_items(
|
|
532
|
+
self,
|
|
533
|
+
data_chunk: Sequence[UploadItem[AssetMappingDMRequest]],
|
|
534
|
+
http_client: HTTPClient,
|
|
535
|
+
selector: T_Selector | None = None,
|
|
536
|
+
) -> Sequence[HTTPMessage]:
|
|
537
|
+
"""Migrate 3D asset mappings by uploading them to the migrate/asset-mappings endpoint."""
|
|
538
|
+
if not data_chunk:
|
|
539
|
+
return []
|
|
540
|
+
# Assume all items in the chunk belong to the same model and revision, they should
|
|
541
|
+
# if the .stream_data method is used for downloading.
|
|
542
|
+
first = data_chunk[0]
|
|
543
|
+
model_id = first.item.model_id
|
|
544
|
+
revision_id = first.item.revision_id
|
|
545
|
+
endpoint = self.UPLOAD_ENDPOINT.format(modelId=model_id, revisionId=revision_id)
|
|
546
|
+
responses = http_client.request_with_retries(
|
|
547
|
+
ItemsRequest(
|
|
548
|
+
endpoint_url=self.client.config.create_api_url(endpoint),
|
|
549
|
+
method="POST",
|
|
550
|
+
items=list(data_chunk),
|
|
551
|
+
extra_body_fields={
|
|
552
|
+
"dmsContextualizationConfig": {
|
|
553
|
+
"object3DSpace": self.object_3D_space,
|
|
554
|
+
"cadNodeSpace": self.cad_node_space,
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
)
|
|
558
|
+
)
|
|
559
|
+
return responses
|
|
560
|
+
|
|
561
|
+
def json_to_resource(self, item_json: dict[str, JsonVal]) -> AssetMappingDMRequest:
|
|
562
|
+
raise NotImplementedError("Deserializing 3D Asset Mappings from JSON is not supported.")
|
|
563
|
+
|
|
564
|
+
def data_to_json_chunk(
|
|
565
|
+
self, data_chunk: Sequence[AssetMappingResponse], selector: ThreeDSelector | None = None
|
|
566
|
+
) -> list[dict[str, JsonVal]]:
|
|
567
|
+
raise NotImplementedError("Serializing 3D Asset Mappings to JSON is not supported.")
|
|
@@ -16,6 +16,7 @@ from rich.console import Console
|
|
|
16
16
|
from rich.panel import Panel
|
|
17
17
|
|
|
18
18
|
from cognite_toolkit._cdf_tk.client import ToolkitClient
|
|
19
|
+
from cognite_toolkit._cdf_tk.client.data_classes.instance_api import TypedInstanceIdentifier
|
|
19
20
|
from cognite_toolkit._cdf_tk.cruds import (
|
|
20
21
|
AssetCRUD,
|
|
21
22
|
ContainerCRUD,
|
|
@@ -59,10 +60,10 @@ from cognite_toolkit._cdf_tk.utils.aggregators import (
|
|
|
59
60
|
TimeSeriesAggregator,
|
|
60
61
|
)
|
|
61
62
|
from cognite_toolkit._cdf_tk.utils.http_client import (
|
|
62
|
-
FailedRequestItems,
|
|
63
|
-
FailedResponseItems,
|
|
64
63
|
HTTPClient,
|
|
65
64
|
ItemsRequest,
|
|
65
|
+
ItemsRequest2,
|
|
66
|
+
ItemsSuccessResponse2,
|
|
66
67
|
SuccessResponseItems,
|
|
67
68
|
)
|
|
68
69
|
from cognite_toolkit._cdf_tk.utils.producer_worker import ProducerWorkerExecutor
|
|
@@ -714,21 +715,18 @@ class PurgeCommand(ToolkitCommand):
|
|
|
714
715
|
results.deleted += len(items)
|
|
715
716
|
return
|
|
716
717
|
|
|
717
|
-
responses = delete_client.
|
|
718
|
-
|
|
719
|
-
delete_client.config.create_api_url("/models/instances/delete"),
|
|
718
|
+
responses = delete_client.request_items_retries(
|
|
719
|
+
ItemsRequest2(
|
|
720
|
+
endpoint_url=delete_client.config.create_api_url("/models/instances/delete"),
|
|
720
721
|
method="POST",
|
|
721
|
-
|
|
722
|
-
items=[DeleteItem(item=item, as_id_fun=InstanceId.load) for item in items], # type: ignore[arg-type]
|
|
722
|
+
items=[TypedInstanceIdentifier._load(item) for item in items],
|
|
723
723
|
)
|
|
724
724
|
)
|
|
725
725
|
for response in responses:
|
|
726
|
-
if isinstance(response,
|
|
726
|
+
if isinstance(response, ItemsSuccessResponse2):
|
|
727
727
|
results.deleted += len(response.ids)
|
|
728
|
-
elif isinstance(response, FailedResponseItems | FailedRequestItems):
|
|
729
|
-
results.failed += len(response.ids)
|
|
730
728
|
else:
|
|
731
|
-
results.failed += len(
|
|
729
|
+
results.failed += len(response.ids)
|
|
732
730
|
|
|
733
731
|
@staticmethod
|
|
734
732
|
def _unlink_timeseries(
|
|
@@ -13,7 +13,7 @@ from cognite_toolkit._cdf_tk.client.data_classes.infield import (
|
|
|
13
13
|
InfieldLocationConfig,
|
|
14
14
|
InfieldLocationConfigList,
|
|
15
15
|
)
|
|
16
|
-
from cognite_toolkit._cdf_tk.client.data_classes.instance_api import InstanceResult,
|
|
16
|
+
from cognite_toolkit._cdf_tk.client.data_classes.instance_api import InstanceResult, TypedNodeIdentifier
|
|
17
17
|
from cognite_toolkit._cdf_tk.client.data_classes.legacy.apm_config_v1 import (
|
|
18
18
|
APMConfig,
|
|
19
19
|
APMConfigList,
|
|
@@ -246,7 +246,7 @@ class InfieldV1CRUD(ResourceCRUD[str, APMConfigWrite, APMConfig]):
|
|
|
246
246
|
|
|
247
247
|
|
|
248
248
|
@final
|
|
249
|
-
class InFieldLocationConfigCRUD(ResourceCRUD[
|
|
249
|
+
class InFieldLocationConfigCRUD(ResourceCRUD[TypedNodeIdentifier, InfieldLocationConfig, InfieldLocationConfig]):
|
|
250
250
|
folder_name = "cdf_applications"
|
|
251
251
|
resource_cls = InfieldLocationConfig
|
|
252
252
|
resource_write_cls = InfieldLocationConfig
|
|
@@ -260,13 +260,13 @@ class InFieldLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InfieldLocationConf
|
|
|
260
260
|
return "infield location configs"
|
|
261
261
|
|
|
262
262
|
@classmethod
|
|
263
|
-
def get_id(cls, item: InfieldLocationConfig | dict) ->
|
|
263
|
+
def get_id(cls, item: InfieldLocationConfig | dict) -> TypedNodeIdentifier:
|
|
264
264
|
if isinstance(item, dict):
|
|
265
|
-
return
|
|
266
|
-
return
|
|
265
|
+
return TypedNodeIdentifier(space=item["space"], external_id=item["externalId"])
|
|
266
|
+
return TypedNodeIdentifier(space=item.space, external_id=item.external_id)
|
|
267
267
|
|
|
268
268
|
@classmethod
|
|
269
|
-
def dump_id(cls, id:
|
|
269
|
+
def dump_id(cls, id: TypedNodeIdentifier) -> dict[str, Any]:
|
|
270
270
|
return id.dump(include_type=False)
|
|
271
271
|
|
|
272
272
|
@classmethod
|
|
@@ -308,13 +308,13 @@ class InFieldLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InfieldLocationConf
|
|
|
308
308
|
# as we only want to count the infield location configs here.
|
|
309
309
|
return [res for res in created if res.as_id() in config_ids]
|
|
310
310
|
|
|
311
|
-
def retrieve(self, ids: SequenceNotStr[
|
|
311
|
+
def retrieve(self, ids: SequenceNotStr[TypedNodeIdentifier]) -> InfieldLocationConfigList:
|
|
312
312
|
return InfieldLocationConfigList(self.client.infield.config.retrieve(list(ids)))
|
|
313
313
|
|
|
314
314
|
def update(self, items: Sequence[InfieldLocationConfig]) -> Sized:
|
|
315
315
|
return self.create(items)
|
|
316
316
|
|
|
317
|
-
def delete(self, ids: SequenceNotStr[
|
|
317
|
+
def delete(self, ids: SequenceNotStr[TypedNodeIdentifier]) -> int:
|
|
318
318
|
# We must retrieve the full resource to get hte DataExplorationConfig linked resource deleted as well.
|
|
319
319
|
retrieved = self.retrieve(list(ids))
|
|
320
320
|
# Then, we pass the entire resource to the delete method, which will delete both the InfieldLocationConfig
|
|
@@ -345,7 +345,9 @@ class InFieldLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InfieldLocationConf
|
|
|
345
345
|
|
|
346
346
|
|
|
347
347
|
@final
|
|
348
|
-
class InFieldCDMLocationConfigCRUD(
|
|
348
|
+
class InFieldCDMLocationConfigCRUD(
|
|
349
|
+
ResourceCRUD[TypedNodeIdentifier, InFieldCDMLocationConfig, InFieldCDMLocationConfig]
|
|
350
|
+
):
|
|
349
351
|
folder_name = "cdf_applications"
|
|
350
352
|
resource_cls = InFieldCDMLocationConfig
|
|
351
353
|
resource_write_cls = InFieldCDMLocationConfig
|
|
@@ -359,13 +361,13 @@ class InFieldCDMLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InFieldCDMLocati
|
|
|
359
361
|
return "infield CDM location configs"
|
|
360
362
|
|
|
361
363
|
@classmethod
|
|
362
|
-
def get_id(cls, item: InFieldCDMLocationConfig | dict) ->
|
|
364
|
+
def get_id(cls, item: InFieldCDMLocationConfig | dict) -> TypedNodeIdentifier:
|
|
363
365
|
if isinstance(item, dict):
|
|
364
|
-
return
|
|
365
|
-
return
|
|
366
|
+
return TypedNodeIdentifier(space=item["space"], external_id=item["externalId"])
|
|
367
|
+
return TypedNodeIdentifier(space=item.space, external_id=item.external_id)
|
|
366
368
|
|
|
367
369
|
@classmethod
|
|
368
|
-
def dump_id(cls, id:
|
|
370
|
+
def dump_id(cls, id: TypedNodeIdentifier) -> dict[str, Any]:
|
|
369
371
|
return id.dump(include_type=False)
|
|
370
372
|
|
|
371
373
|
@classmethod
|
|
@@ -397,13 +399,13 @@ class InFieldCDMLocationConfigCRUD(ResourceCRUD[NodeIdentifier, InFieldCDMLocati
|
|
|
397
399
|
def create(self, items: Sequence[InFieldCDMLocationConfig]) -> list[InstanceResult]:
|
|
398
400
|
return self.client.infield.cdm_config.apply(items)
|
|
399
401
|
|
|
400
|
-
def retrieve(self, ids: SequenceNotStr[
|
|
402
|
+
def retrieve(self, ids: SequenceNotStr[TypedNodeIdentifier]) -> list[InFieldCDMLocationConfig]:
|
|
401
403
|
return self.client.infield.cdm_config.retrieve(list(ids))
|
|
402
404
|
|
|
403
405
|
def update(self, items: Sequence[InFieldCDMLocationConfig]) -> Sized:
|
|
404
406
|
return self.create(items)
|
|
405
407
|
|
|
406
|
-
def delete(self, ids: SequenceNotStr[
|
|
408
|
+
def delete(self, ids: SequenceNotStr[TypedNodeIdentifier]) -> int:
|
|
407
409
|
# We must retrieve the full resource to delete it.
|
|
408
410
|
retrieved = self.retrieve(list(ids))
|
|
409
411
|
_ = self.client.infield.cdm_config.delete(retrieved)
|
|
@@ -26,9 +26,7 @@ class HTTPResult2(BaseModel):
|
|
|
26
26
|
if isinstance(self, SuccessResponse2):
|
|
27
27
|
return self
|
|
28
28
|
elif isinstance(self, FailedResponse2):
|
|
29
|
-
raise ToolkitAPIError(
|
|
30
|
-
f"Request failed with status code {self.status_code}: {self.error.code} - {self.error.message}"
|
|
31
|
-
)
|
|
29
|
+
raise ToolkitAPIError(f"Request failed with status code {self.status_code}: {self.error.message}")
|
|
32
30
|
elif isinstance(self, FailedRequest2):
|
|
33
31
|
raise ToolkitAPIError(f"Request failed with error: {self.error}")
|
|
34
32
|
else:
|
cognite_toolkit/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.7.
|
|
1
|
+
__version__ = "0.7.37"
|