cognite-toolkit 0.7.47__py3-none-any.whl → 0.7.49__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 +6 -6
- cognite_toolkit/_cdf_tk/client/_toolkit_client.py +6 -4
- cognite_toolkit/_cdf_tk/client/api/instances.py +139 -0
- cognite_toolkit/_cdf_tk/client/api/location_filters.py +177 -0
- cognite_toolkit/_cdf_tk/client/api/raw.py +2 -2
- cognite_toolkit/_cdf_tk/client/api/robotics.py +19 -0
- cognite_toolkit/_cdf_tk/client/api/robotics_capabilities.py +127 -0
- cognite_toolkit/_cdf_tk/client/api/robotics_data_postprocessing.py +138 -0
- cognite_toolkit/_cdf_tk/client/api/robotics_frames.py +122 -0
- cognite_toolkit/_cdf_tk/client/api/robotics_locations.py +127 -0
- cognite_toolkit/_cdf_tk/client/api/robotics_maps.py +122 -0
- cognite_toolkit/_cdf_tk/client/api/robotics_robots.py +122 -0
- cognite_toolkit/_cdf_tk/client/api/search_config.py +101 -0
- cognite_toolkit/_cdf_tk/client/api/streams.py +63 -55
- cognite_toolkit/_cdf_tk/client/api/three_d.py +293 -277
- cognite_toolkit/_cdf_tk/client/cdf_client/api.py +34 -5
- cognite_toolkit/_cdf_tk/client/http_client/_client.py +5 -2
- cognite_toolkit/_cdf_tk/client/http_client/_data_classes2.py +4 -3
- cognite_toolkit/_cdf_tk/client/request_classes/filters.py +45 -1
- cognite_toolkit/_cdf_tk/client/resource_classes/apm_config.py +128 -0
- cognite_toolkit/_cdf_tk/client/resource_classes/cognite_file.py +53 -0
- cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/__init__.py +4 -0
- cognite_toolkit/_cdf_tk/client/resource_classes/data_modeling/_instance.py +22 -11
- cognite_toolkit/_cdf_tk/client/resource_classes/identifiers.py +7 -0
- cognite_toolkit/_cdf_tk/client/resource_classes/location_filter.py +9 -2
- cognite_toolkit/_cdf_tk/client/resource_classes/resource_view_mapping.py +38 -0
- cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_map.py +6 -1
- cognite_toolkit/_cdf_tk/client/resource_classes/robotics/_robot.py +10 -5
- cognite_toolkit/_cdf_tk/client/resource_classes/streams.py +1 -20
- cognite_toolkit/_cdf_tk/client/resource_classes/three_d.py +30 -9
- cognite_toolkit/_cdf_tk/client/testing.py +2 -2
- cognite_toolkit/_cdf_tk/commands/_migrate/command.py +103 -108
- cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +6 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +119 -41
- cognite_toolkit/_cdf_tk/commands/_migrate/issues.py +21 -38
- cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +14 -12
- cognite_toolkit/_cdf_tk/commands/build_v2/_module_parser.py +138 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/_modules_parser.py +163 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/build_cmd.py +83 -96
- cognite_toolkit/_cdf_tk/commands/build_v2/{build_input.py → build_parameters.py} +8 -22
- cognite_toolkit/_cdf_tk/commands/build_v2/data_classes/_modules.py +27 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/data_classes/_resource.py +22 -0
- cognite_toolkit/_cdf_tk/cruds/__init__.py +11 -5
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +14 -30
- cognite_toolkit/_cdf_tk/data_classes/__init__.py +3 -0
- cognite_toolkit/_cdf_tk/data_classes/_issues.py +36 -0
- cognite_toolkit/_cdf_tk/data_classes/_module_directories.py +2 -1
- cognite_toolkit/_cdf_tk/storageio/_base.py +2 -0
- cognite_toolkit/_cdf_tk/storageio/logger.py +162 -0
- cognite_toolkit/_cdf_tk/utils/__init__.py +8 -1
- cognite_toolkit/_cdf_tk/utils/interactive_select.py +3 -1
- cognite_toolkit/_cdf_tk/utils/modules.py +7 -0
- 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.47.dist-info → cognite_toolkit-0.7.49.dist-info}/METADATA +1 -1
- {cognite_toolkit-0.7.47.dist-info → cognite_toolkit-0.7.49.dist-info}/RECORD +61 -43
- cognite_toolkit/_cdf_tk/commands/build_v2/build_issues.py +0 -27
- /cognite_toolkit/_cdf_tk/client/resource_classes/{search_config_resource.py → search_config.py} +0 -0
- {cognite_toolkit-0.7.47.dist-info → cognite_toolkit-0.7.49.dist-info}/WHEEL +0 -0
- {cognite_toolkit-0.7.47.dist-info → cognite_toolkit-0.7.49.dist-info}/entry_points.txt +0 -0
|
@@ -1,80 +1,95 @@
|
|
|
1
|
-
from collections import defaultdict
|
|
2
1
|
from collections.abc import Iterable, Sequence
|
|
3
|
-
from typing import
|
|
2
|
+
from typing import TypeVar
|
|
4
3
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from cognite_toolkit._cdf_tk.client.cdf_client.responses import PagedResponse
|
|
4
|
+
from cognite_toolkit._cdf_tk.client.cdf_client import CDFResourceAPI, PagedResponse
|
|
5
|
+
from cognite_toolkit._cdf_tk.client.cdf_client.api import Endpoint
|
|
9
6
|
from cognite_toolkit._cdf_tk.client.http_client import (
|
|
10
7
|
HTTPClient,
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
ItemsSuccessResponse2,
|
|
9
|
+
SuccessResponse2,
|
|
13
10
|
)
|
|
11
|
+
from cognite_toolkit._cdf_tk.client.request_classes.filters import ThreeDAssetMappingFilter
|
|
14
12
|
from cognite_toolkit._cdf_tk.client.resource_classes.identifiers import InternalId
|
|
15
13
|
from cognite_toolkit._cdf_tk.client.resource_classes.three_d import (
|
|
16
14
|
AssetMappingClassicRequest,
|
|
15
|
+
AssetMappingClassicResponse,
|
|
17
16
|
AssetMappingDMRequest,
|
|
18
|
-
|
|
17
|
+
AssetMappingDMResponse,
|
|
19
18
|
ThreeDModelClassicRequest,
|
|
20
19
|
ThreeDModelResponse,
|
|
21
20
|
)
|
|
22
|
-
from cognite_toolkit._cdf_tk.utils.collection import chunker_sequence
|
|
23
|
-
from cognite_toolkit._cdf_tk.utils.useful_types import PrimitiveType
|
|
24
21
|
|
|
25
22
|
|
|
26
|
-
class
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
class ThreeDClassicModelsAPI(CDFResourceAPI[InternalId, ThreeDModelClassicRequest, ThreeDModelResponse]):
|
|
24
|
+
def __init__(self, http_client: HTTPClient) -> None:
|
|
25
|
+
super().__init__(
|
|
26
|
+
http_client=http_client,
|
|
27
|
+
method_endpoint_map={
|
|
28
|
+
"create": Endpoint(method="POST", path="/3d/models", item_limit=1000),
|
|
29
|
+
"delete": Endpoint(method="POST", path="/3d/models/delete", item_limit=1000),
|
|
30
|
+
"update": Endpoint(method="POST", path="/3d/models/update", item_limit=1000),
|
|
31
|
+
"retrieve": Endpoint(method="GET", path="/3d/models/{modelId}", item_limit=1000),
|
|
32
|
+
"list": Endpoint(method="GET", path="/3d/models", item_limit=1000),
|
|
33
|
+
},
|
|
34
|
+
)
|
|
31
35
|
|
|
32
|
-
def
|
|
33
|
-
self
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
def _validate_page_response(
|
|
37
|
+
self, response: SuccessResponse2 | ItemsSuccessResponse2
|
|
38
|
+
) -> PagedResponse[ThreeDModelResponse]:
|
|
39
|
+
return PagedResponse[ThreeDModelResponse].model_validate_json(response.body)
|
|
36
40
|
|
|
37
|
-
def create(self,
|
|
41
|
+
def create(self, items: Sequence[ThreeDModelClassicRequest]) -> list[ThreeDModelResponse]:
|
|
38
42
|
"""Create 3D models in classic format.
|
|
39
43
|
|
|
40
44
|
Args:
|
|
41
|
-
|
|
45
|
+
items (Sequence[ThreeDModelClassicRequest]): The 3D model(s) to create.
|
|
42
46
|
|
|
43
47
|
Returns:
|
|
44
48
|
list[ThreeDModelResponse]: The created 3D model(s).
|
|
45
49
|
"""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
return self._request_item_response(items, "create")
|
|
51
|
+
|
|
52
|
+
def retrieve(self, ids: Sequence[InternalId]) -> list[ThreeDModelResponse]:
|
|
53
|
+
"""Retrieve 3D models by their IDs.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
ids (Sequence[int]): The IDs of the 3D models to retrieve.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
list[ThreeDModelResponse]: The retrieved 3D model(s).
|
|
60
|
+
"""
|
|
61
|
+
return self._request_item_response(ids, "retrieve")
|
|
62
|
+
|
|
63
|
+
def update(self, items: Sequence[ThreeDModelClassicRequest]) -> list[ThreeDModelResponse]:
|
|
64
|
+
"""Update 3D models in classic format.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
items (Sequence[ThreeDModelClassicRequest]): The 3D model(s) to update.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
list[ThreeDModelResponse]: The updated 3D model(s).
|
|
71
|
+
"""
|
|
72
|
+
return self._request_item_response(items, "update")
|
|
59
73
|
|
|
60
|
-
def delete(self, ids: Sequence[
|
|
74
|
+
def delete(self, ids: Sequence[InternalId]) -> None:
|
|
61
75
|
"""Delete 3D models by their IDs.
|
|
62
76
|
|
|
63
77
|
Args:
|
|
64
78
|
ids (Sequence[int]): The IDs of the 3D models to delete.
|
|
65
79
|
"""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
self._request_no_response(ids, "delete")
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _create_list_filter(include_revision_info: bool, published: bool | None) -> dict[str, bool]:
|
|
84
|
+
params = {
|
|
85
|
+
# There is a bug in the API. The parameter includeRevisionInfo is expected to be lower case and not
|
|
86
|
+
# camel case as documented. You get error message: Unrecognized query parameter includeRevisionInfo,
|
|
87
|
+
# did you mean includerevisioninfo?
|
|
88
|
+
"includerevisioninfo": include_revision_info,
|
|
89
|
+
}
|
|
90
|
+
if published is not None:
|
|
91
|
+
params["published"] = published
|
|
92
|
+
return params
|
|
78
93
|
|
|
79
94
|
def paginate(
|
|
80
95
|
self,
|
|
@@ -83,75 +98,54 @@ class ThreeDModelAPI:
|
|
|
83
98
|
limit: int = 100,
|
|
84
99
|
cursor: str | None = None,
|
|
85
100
|
) -> PagedResponse[ThreeDModelResponse]:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
parameters["cursor"] = cursor
|
|
99
|
-
responses = self._http_client.request_single_retries(
|
|
100
|
-
RequestMessage2(
|
|
101
|
-
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
102
|
-
method="GET",
|
|
103
|
-
parameters=parameters,
|
|
104
|
-
)
|
|
105
|
-
)
|
|
106
|
-
success_response = responses.get_success_or_raise()
|
|
107
|
-
return PagedResponse[ThreeDModelResponse].model_validate(success_response.body_json)
|
|
101
|
+
params = self._create_list_filter(include_revision_info, published)
|
|
102
|
+
return self._paginate(limit=limit, cursor=cursor, params=params)
|
|
103
|
+
|
|
104
|
+
def iterate(
|
|
105
|
+
self,
|
|
106
|
+
published: bool | None = None,
|
|
107
|
+
include_revision_info: bool = False,
|
|
108
|
+
limit: int = 100,
|
|
109
|
+
cursor: str | None = None,
|
|
110
|
+
) -> Iterable[list[ThreeDModelResponse]]:
|
|
111
|
+
params = self._create_list_filter(include_revision_info, published)
|
|
112
|
+
return self._iterate(limit=limit, cursor=cursor, params=params)
|
|
108
113
|
|
|
109
114
|
def list(
|
|
110
115
|
self,
|
|
111
116
|
published: bool | None = None,
|
|
112
117
|
include_revision_info: bool = False,
|
|
113
118
|
limit: int | None = 100,
|
|
114
|
-
cursor: str | None = None,
|
|
115
119
|
) -> list[ThreeDModelResponse]:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
request_limit = (
|
|
119
|
-
self._LIST_REQUEST_MAX_LIMIT
|
|
120
|
-
if limit is None
|
|
121
|
-
else min(limit - len(results), self._LIST_REQUEST_MAX_LIMIT)
|
|
122
|
-
)
|
|
123
|
-
if request_limit <= 0:
|
|
124
|
-
break
|
|
125
|
-
page = self.paginate(
|
|
126
|
-
published=published,
|
|
127
|
-
include_revision_info=include_revision_info,
|
|
128
|
-
limit=request_limit,
|
|
129
|
-
cursor=cursor,
|
|
130
|
-
)
|
|
131
|
-
results.extend(page.items)
|
|
132
|
-
if page.next_cursor is None:
|
|
133
|
-
break
|
|
134
|
-
cursor = page.next_cursor
|
|
135
|
-
return results
|
|
120
|
+
params = self._create_list_filter(include_revision_info, published)
|
|
121
|
+
return self._list(limit=limit, params=params)
|
|
136
122
|
|
|
137
123
|
|
|
138
124
|
T_RequestMapping = TypeVar("T_RequestMapping", bound=AssetMappingClassicRequest | AssetMappingDMRequest)
|
|
139
125
|
|
|
140
126
|
|
|
141
|
-
class
|
|
127
|
+
class ThreeDClassicAssetMappingAPI(
|
|
128
|
+
CDFResourceAPI[AssetMappingClassicRequest, AssetMappingClassicRequest, AssetMappingClassicResponse]
|
|
129
|
+
):
|
|
142
130
|
ENDPOINT = "/3d/models/{modelId}/revisions/{revisionId}/mappings"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
131
|
+
|
|
132
|
+
def __init__(self, http_client: HTTPClient) -> None:
|
|
133
|
+
super().__init__(
|
|
134
|
+
http_client=http_client,
|
|
135
|
+
method_endpoint_map={
|
|
136
|
+
# These endpoints are parameterized, so the paths are templates
|
|
137
|
+
"create": Endpoint(method="POST", path=self.ENDPOINT, item_limit=1000),
|
|
138
|
+
"delete": Endpoint(method="POST", path=f"{self.ENDPOINT}/delete", item_limit=1000),
|
|
139
|
+
"list": Endpoint(method="POST", path=f"{self.ENDPOINT}/list", item_limit=1000),
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def _validate_page_response(
|
|
144
|
+
self, response: SuccessResponse2 | ItemsSuccessResponse2
|
|
145
|
+
) -> PagedResponse[AssetMappingClassicResponse]:
|
|
146
|
+
return PagedResponse[AssetMappingClassicResponse].model_validate_json(response.body)
|
|
147
|
+
|
|
148
|
+
def create(self, mappings: Sequence[AssetMappingClassicRequest]) -> list[AssetMappingClassicResponse]:
|
|
155
149
|
"""Create 3D asset mappings.
|
|
156
150
|
|
|
157
151
|
Args:
|
|
@@ -159,32 +153,123 @@ class ThreeDAssetMappingAPI:
|
|
|
159
153
|
The 3D asset mapping(s) to create.
|
|
160
154
|
|
|
161
155
|
Returns:
|
|
162
|
-
list[
|
|
156
|
+
list[AssetMappingClassicResponse]: The created 3D asset mapping(s).
|
|
163
157
|
"""
|
|
164
|
-
results: list[
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
items=revision_mappings,
|
|
173
|
-
)
|
|
174
|
-
)
|
|
175
|
-
responses.raise_for_status()
|
|
176
|
-
items = responses.get_items()
|
|
177
|
-
for item in items:
|
|
158
|
+
results: list[AssetMappingClassicResponse] = []
|
|
159
|
+
endpoint = self._method_endpoint_map["create"]
|
|
160
|
+
for (model_id, revision_id), group in self._group_items_by_text_field(
|
|
161
|
+
mappings, "model_id", "revision_id"
|
|
162
|
+
).items():
|
|
163
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
164
|
+
result = self._request_item_response(group, "create", endpoint=path)
|
|
165
|
+
for item in result:
|
|
178
166
|
# We append modelId and revisionId to each item since the API does not return them
|
|
179
167
|
# this is needed to fully populate the AssetMappingResponse data class
|
|
180
|
-
item
|
|
181
|
-
item
|
|
182
|
-
results.extend(
|
|
168
|
+
object.__setattr__(item, "model_id", int(model_id))
|
|
169
|
+
object.__setattr__(item, "revision_id", int(revision_id))
|
|
170
|
+
results.extend(result)
|
|
183
171
|
return results
|
|
184
172
|
|
|
185
|
-
def
|
|
173
|
+
def delete(self, mappings: Sequence[AssetMappingClassicRequest]) -> None:
|
|
174
|
+
"""Delete 3D asset mappings.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
mappings (Sequence[AssetMappingClassicRequest]):
|
|
178
|
+
The 3D asset mapping(s) to delete.
|
|
179
|
+
"""
|
|
180
|
+
endpoint = self._method_endpoint_map["delete"]
|
|
181
|
+
for (model_id, revision_id), group in self._group_items_by_text_field(
|
|
182
|
+
mappings, "model_id", "revision_id"
|
|
183
|
+
).items():
|
|
184
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
185
|
+
self._request_no_response(group, "delete", endpoint=path)
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
def paginate(
|
|
189
|
+
self,
|
|
190
|
+
model_id: int,
|
|
191
|
+
revision_id: int,
|
|
192
|
+
filter: ThreeDAssetMappingFilter | None = None,
|
|
193
|
+
limit: int = 100,
|
|
194
|
+
cursor: str | None = None,
|
|
195
|
+
) -> PagedResponse[AssetMappingClassicResponse]:
|
|
196
|
+
endpoint = self._method_endpoint_map["list"]
|
|
197
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
198
|
+
page = self._paginate(
|
|
199
|
+
limit=limit,
|
|
200
|
+
cursor=cursor,
|
|
201
|
+
body={"filter": filter.dump() if filter else None, "getDmsInstances": False},
|
|
202
|
+
endpoint_path=path,
|
|
203
|
+
)
|
|
204
|
+
# Add modelId and revisionId to items since the API does not return them
|
|
205
|
+
for item in page.items:
|
|
206
|
+
object.__setattr__(item, "model_id", model_id)
|
|
207
|
+
object.__setattr__(item, "revision_id", revision_id)
|
|
208
|
+
return page
|
|
209
|
+
|
|
210
|
+
def iterate(
|
|
211
|
+
self,
|
|
212
|
+
model_id: int,
|
|
213
|
+
revision_id: int,
|
|
214
|
+
filter: ThreeDAssetMappingFilter | None = None,
|
|
215
|
+
limit: int = 100,
|
|
216
|
+
) -> Iterable[list[AssetMappingClassicResponse]]:
|
|
217
|
+
endpoint = self._method_endpoint_map["list"]
|
|
218
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
219
|
+
for items in self._iterate(
|
|
220
|
+
body={"filter": filter.dump() if filter else None, "getDmsInstances": False},
|
|
221
|
+
limit=limit,
|
|
222
|
+
endpoint_path=path,
|
|
223
|
+
):
|
|
224
|
+
# Add modelId and revisionId to items since the API does not return them
|
|
225
|
+
for item in items:
|
|
226
|
+
object.__setattr__(item, "model_id", model_id)
|
|
227
|
+
object.__setattr__(item, "revision_id", revision_id)
|
|
228
|
+
yield items
|
|
229
|
+
|
|
230
|
+
def list(
|
|
231
|
+
self,
|
|
232
|
+
model_id: int,
|
|
233
|
+
revision_id: int,
|
|
234
|
+
filter: ThreeDAssetMappingFilter | None = None,
|
|
235
|
+
limit: int | None = 100,
|
|
236
|
+
) -> list[AssetMappingClassicResponse]:
|
|
237
|
+
endpoint = self._method_endpoint_map["list"]
|
|
238
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
239
|
+
items = self._list(
|
|
240
|
+
body={"filter": filter.dump() if filter else None, "getDmsInstances": False},
|
|
241
|
+
limit=limit,
|
|
242
|
+
endpoint_path=path,
|
|
243
|
+
)
|
|
244
|
+
# Add modelId and revisionId to items since the API does not return them
|
|
245
|
+
for item in items:
|
|
246
|
+
object.__setattr__(item, "model_id", model_id)
|
|
247
|
+
object.__setattr__(item, "revision_id", revision_id)
|
|
248
|
+
return items
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class ThreeDDMAssetMappingAPI(CDFResourceAPI[AssetMappingDMRequest, AssetMappingDMRequest, AssetMappingDMResponse]):
|
|
252
|
+
ENDPOINT = "/3d/models/{modelId}/revisions/{revisionId}/mappings"
|
|
253
|
+
|
|
254
|
+
def __init__(self, http_client: HTTPClient) -> None:
|
|
255
|
+
super().__init__(
|
|
256
|
+
http_client=http_client,
|
|
257
|
+
method_endpoint_map={
|
|
258
|
+
# These endpoints are parameterized, so the paths are templates
|
|
259
|
+
"create": Endpoint(method="POST", path=self.ENDPOINT, item_limit=100),
|
|
260
|
+
"delete": Endpoint(method="POST", path=f"{self.ENDPOINT}/delete", item_limit=100),
|
|
261
|
+
"list": Endpoint(method="POST", path=f"{self.ENDPOINT}/list", item_limit=1000),
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
def _validate_page_response(
|
|
266
|
+
self, response: SuccessResponse2 | ItemsSuccessResponse2
|
|
267
|
+
) -> PagedResponse[AssetMappingDMResponse]:
|
|
268
|
+
return PagedResponse[AssetMappingDMResponse].model_validate_json(response.body)
|
|
269
|
+
|
|
270
|
+
def create(
|
|
186
271
|
self, mappings: Sequence[AssetMappingDMRequest], object_3d_space: str, cad_node_space: str
|
|
187
|
-
) -> list[
|
|
272
|
+
) -> list[AssetMappingDMResponse]:
|
|
188
273
|
"""Create 3D asset mappings in Data Modeling format.
|
|
189
274
|
|
|
190
275
|
Args:
|
|
@@ -195,69 +280,33 @@ class ThreeDAssetMappingAPI:
|
|
|
195
280
|
cad_node_space (str):
|
|
196
281
|
The instance space where the CogniteCADNode are located.
|
|
197
282
|
Returns:
|
|
198
|
-
list[
|
|
283
|
+
list[AssetMappingDMResponse]: The created 3D asset mapping(s).
|
|
199
284
|
"""
|
|
200
|
-
results: list[
|
|
201
|
-
for
|
|
202
|
-
mappings,
|
|
203
|
-
):
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
)
|
|
285
|
+
results: list[AssetMappingDMResponse] = []
|
|
286
|
+
for (model_id, revision_id), group in self._group_items_by_text_field(
|
|
287
|
+
mappings, "model_id", "revision_id"
|
|
288
|
+
).items():
|
|
289
|
+
path = self.ENDPOINT.format(modelId=model_id, revisionId=revision_id)
|
|
290
|
+
result = self._request_item_response(
|
|
291
|
+
group,
|
|
292
|
+
"create",
|
|
293
|
+
endpoint=path,
|
|
294
|
+
extra_body={
|
|
295
|
+
"dmsContextualizationConfig": {
|
|
296
|
+
"object3DSpace": object_3d_space,
|
|
297
|
+
"cadNodeSpace": cad_node_space,
|
|
298
|
+
}
|
|
299
|
+
},
|
|
216
300
|
)
|
|
217
|
-
|
|
218
|
-
items = responses.get_items()
|
|
219
|
-
for item in items:
|
|
301
|
+
for item in result:
|
|
220
302
|
# We append modelId and revisionId to each item since the API does not return them
|
|
221
|
-
# this is needed to fully populate the
|
|
222
|
-
item
|
|
223
|
-
item
|
|
224
|
-
results.extend(
|
|
303
|
+
# this is needed to fully populate the AssetMappingDMResponse data class
|
|
304
|
+
object.__setattr__(item, "model_id", int(model_id))
|
|
305
|
+
object.__setattr__(item, "revision_id", int(revision_id))
|
|
306
|
+
results.extend(result)
|
|
225
307
|
return results
|
|
226
308
|
|
|
227
|
-
|
|
228
|
-
def _chunk_mappings_by_endpoint(
|
|
229
|
-
cls, mappings: Sequence[T_RequestMapping], chunk_size: int
|
|
230
|
-
) -> Iterable[tuple[str, int, int, list[T_RequestMapping]]]:
|
|
231
|
-
chunked_mappings: dict[tuple[int, int], list[T_RequestMapping]] = defaultdict(list)
|
|
232
|
-
for mapping in mappings:
|
|
233
|
-
key = mapping.model_id, mapping.revision_id
|
|
234
|
-
chunked_mappings[key].append(mapping)
|
|
235
|
-
for (model_id, revision_id), revision_mappings in chunked_mappings.items():
|
|
236
|
-
endpoint = cls.ENDPOINT.format(modelId=model_id, revisionId=revision_id)
|
|
237
|
-
for chunk in chunker_sequence(revision_mappings, chunk_size):
|
|
238
|
-
yield endpoint, model_id, revision_id, chunk
|
|
239
|
-
|
|
240
|
-
def delete(self, mappings: Sequence[AssetMappingClassicRequest]) -> None:
|
|
241
|
-
"""Delete 3D asset mappings.
|
|
242
|
-
|
|
243
|
-
Args:
|
|
244
|
-
mappings (Sequence[AssetMappingClassicRequest]):
|
|
245
|
-
The 3D asset mapping(s) to delete.
|
|
246
|
-
"""
|
|
247
|
-
for endpoint, *_, revision_mappings in self._chunk_mappings_by_endpoint(
|
|
248
|
-
mappings, self.DELETE_CLASSIC_MAX_MAPPINGS_PER_REQUEST
|
|
249
|
-
):
|
|
250
|
-
responses = self._http_client.request_items_retries(
|
|
251
|
-
ItemsRequest2(
|
|
252
|
-
endpoint_url=self._config.create_api_url(f"{endpoint}/delete"),
|
|
253
|
-
method="DELETE",
|
|
254
|
-
items=revision_mappings,
|
|
255
|
-
)
|
|
256
|
-
)
|
|
257
|
-
responses.raise_for_status()
|
|
258
|
-
return None
|
|
259
|
-
|
|
260
|
-
def delete_dm(self, mappings: Sequence[AssetMappingDMRequest], object_3d_space: str, cad_node_space: str) -> None:
|
|
309
|
+
def delete(self, mappings: Sequence[AssetMappingDMRequest], object_3d_space: str, cad_node_space: str) -> None:
|
|
261
310
|
"""Delete 3D asset mappings in Data Modeling format.
|
|
262
311
|
|
|
263
312
|
Args:
|
|
@@ -268,118 +317,85 @@ class ThreeDAssetMappingAPI:
|
|
|
268
317
|
cad_node_space (str):
|
|
269
318
|
The instance space where the CogniteCADNode are located.
|
|
270
319
|
"""
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
320
|
+
endpoint = self._method_endpoint_map["delete"]
|
|
321
|
+
for (model_id, revision_id), group in self._group_items_by_text_field(
|
|
322
|
+
mappings, "model_id", "revision_id"
|
|
323
|
+
).items():
|
|
324
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
325
|
+
self._request_no_response(
|
|
326
|
+
group,
|
|
327
|
+
"delete",
|
|
328
|
+
endpoint=path,
|
|
329
|
+
extra_body={
|
|
330
|
+
"dmsContextualizationConfig": {
|
|
331
|
+
"object3DSpace": object_3d_space,
|
|
332
|
+
"cadNodeSpace": cad_node_space,
|
|
333
|
+
}
|
|
334
|
+
},
|
|
286
335
|
)
|
|
287
|
-
responses.raise_for_status()
|
|
288
336
|
return None
|
|
289
337
|
|
|
290
338
|
def paginate(
|
|
291
339
|
self,
|
|
292
340
|
model_id: int,
|
|
293
341
|
revision_id: int,
|
|
294
|
-
|
|
295
|
-
asset_instance_ids: list[str] | None = None,
|
|
296
|
-
node_ids: list[int] | None = None,
|
|
297
|
-
tree_indexes: list[int] | None = None,
|
|
298
|
-
get_dms_instances: bool = False,
|
|
342
|
+
filter: ThreeDAssetMappingFilter | None = None,
|
|
299
343
|
limit: int = 100,
|
|
300
344
|
cursor: str | None = None,
|
|
301
|
-
) -> PagedResponse[
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
"getDmsInstances":
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
if asset_ids is not None:
|
|
311
|
-
if not (0 < len(asset_ids) <= 100):
|
|
312
|
-
raise ValueError("asset_ids must contain between 1 and 100 IDs.")
|
|
313
|
-
body["filter"] = {"assetIds": asset_ids}
|
|
314
|
-
elif asset_instance_ids is not None:
|
|
315
|
-
if not (0 < len(asset_instance_ids) <= 100):
|
|
316
|
-
raise ValueError("asset_instance_ids must contain between 1 and 100 IDs.")
|
|
317
|
-
body["filter"] = {"assetInstanceIds": asset_instance_ids}
|
|
318
|
-
elif node_ids is not None:
|
|
319
|
-
if not (0 < len(node_ids) <= 100):
|
|
320
|
-
raise ValueError("node_ids must contain between 1 and 100 IDs.")
|
|
321
|
-
body["filter"] = {"nodeIds": node_ids}
|
|
322
|
-
elif tree_indexes is not None:
|
|
323
|
-
if not (0 < len(tree_indexes) <= 100):
|
|
324
|
-
raise ValueError("tree_indexes must contain between 1 and 100 indexes.")
|
|
325
|
-
body["filter"] = {"treeIndexes": tree_indexes}
|
|
326
|
-
if cursor is not None:
|
|
327
|
-
body["cursor"] = cursor
|
|
328
|
-
|
|
329
|
-
endpoint = self.ENDPOINT.format(modelId=model_id, revisionId=revision_id)
|
|
330
|
-
responses = self._http_client.request_single_retries(
|
|
331
|
-
RequestMessage2(
|
|
332
|
-
endpoint_url=self._config.create_api_url(f"{endpoint}/list"),
|
|
333
|
-
method="POST",
|
|
334
|
-
body_content=body,
|
|
335
|
-
)
|
|
345
|
+
) -> PagedResponse[AssetMappingDMResponse]:
|
|
346
|
+
endpoint = self._method_endpoint_map["list"]
|
|
347
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
348
|
+
page = self._paginate(
|
|
349
|
+
limit=limit,
|
|
350
|
+
cursor=cursor,
|
|
351
|
+
body={"filter": filter.dump() if filter else None, "getDmsInstances": True},
|
|
352
|
+
endpoint_path=path,
|
|
336
353
|
)
|
|
337
|
-
success_response = responses.get_success_or_raise()
|
|
338
|
-
body_json = success_response.body_json
|
|
339
354
|
# Add modelId and revisionId to items since the API does not return them
|
|
340
|
-
for item in
|
|
341
|
-
item
|
|
342
|
-
item
|
|
343
|
-
return
|
|
355
|
+
for item in page.items:
|
|
356
|
+
object.__setattr__(item, "model_id", model_id)
|
|
357
|
+
object.__setattr__(item, "revision_id", revision_id)
|
|
358
|
+
return page
|
|
359
|
+
|
|
360
|
+
def iterate(
|
|
361
|
+
self,
|
|
362
|
+
model_id: int,
|
|
363
|
+
revision_id: int,
|
|
364
|
+
filter: ThreeDAssetMappingFilter | None = None,
|
|
365
|
+
limit: int = 100,
|
|
366
|
+
) -> Iterable[list[AssetMappingDMResponse]]:
|
|
367
|
+
endpoint = self._method_endpoint_map["list"]
|
|
368
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
369
|
+
for items in self._iterate(
|
|
370
|
+
body={"filter": filter.dump() if filter else None, "getDmsInstances": True}, limit=limit, endpoint_path=path
|
|
371
|
+
):
|
|
372
|
+
# Add modelId and revisionId to items since the API does not return them
|
|
373
|
+
for item in items:
|
|
374
|
+
object.__setattr__(item, "model_id", model_id)
|
|
375
|
+
object.__setattr__(item, "revision_id", revision_id)
|
|
376
|
+
yield items
|
|
344
377
|
|
|
345
378
|
def list(
|
|
346
379
|
self,
|
|
347
380
|
model_id: int,
|
|
348
381
|
revision_id: int,
|
|
349
|
-
|
|
350
|
-
asset_instance_ids: list[str] | None = None,
|
|
351
|
-
node_ids: list[int] | None = None,
|
|
352
|
-
tree_indexes: list[int] | None = None,
|
|
353
|
-
get_dms_instances: bool = False,
|
|
382
|
+
filter: ThreeDAssetMappingFilter | None = None,
|
|
354
383
|
limit: int | None = 100,
|
|
355
|
-
) -> list[
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
revision_id=revision_id,
|
|
367
|
-
asset_ids=asset_ids,
|
|
368
|
-
asset_instance_ids=asset_instance_ids,
|
|
369
|
-
node_ids=node_ids,
|
|
370
|
-
tree_indexes=tree_indexes,
|
|
371
|
-
get_dms_instances=get_dms_instances,
|
|
372
|
-
limit=request_limit,
|
|
373
|
-
cursor=cursor,
|
|
374
|
-
)
|
|
375
|
-
results.extend(page.items)
|
|
376
|
-
if page.next_cursor is None:
|
|
377
|
-
break
|
|
378
|
-
cursor = page.next_cursor
|
|
379
|
-
return results
|
|
384
|
+
) -> list[AssetMappingDMResponse]:
|
|
385
|
+
endpoint = self._method_endpoint_map["list"]
|
|
386
|
+
path = endpoint.path.format(modelId=model_id, revisionId=revision_id)
|
|
387
|
+
items = self._list(
|
|
388
|
+
body={"filter": filter.dump() if filter else None, "getDmsInstances": True}, limit=limit, endpoint_path=path
|
|
389
|
+
)
|
|
390
|
+
# Add modelId and revisionId to items since the API does not return them
|
|
391
|
+
for item in items:
|
|
392
|
+
object.__setattr__(item, "model_id", model_id)
|
|
393
|
+
object.__setattr__(item, "revision_id", revision_id)
|
|
394
|
+
return items
|
|
380
395
|
|
|
381
396
|
|
|
382
397
|
class ThreeDAPI:
|
|
383
|
-
def __init__(self, http_client: HTTPClient
|
|
384
|
-
self.
|
|
385
|
-
self.
|
|
398
|
+
def __init__(self, http_client: HTTPClient) -> None:
|
|
399
|
+
self.models_classic = ThreeDClassicModelsAPI(http_client)
|
|
400
|
+
self.asset_mappings_classic = ThreeDClassicAssetMappingAPI(http_client)
|
|
401
|
+
self.asset_mappings_dm = ThreeDDMAssetMappingAPI(http_client)
|