cognite-toolkit 0.7.35__py3-none-any.whl → 0.7.36__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/data_mapper.py +49 -1
- 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.36.dist-info}/METADATA +1 -1
- {cognite_toolkit-0.7.35.dist-info → cognite_toolkit-0.7.36.dist-info}/RECORD +21 -21
- {cognite_toolkit-0.7.35.dist-info → cognite_toolkit-0.7.36.dist-info}/WHEEL +0 -0
- {cognite_toolkit-0.7.35.dist-info → cognite_toolkit-0.7.36.dist-info}/entry_points.txt +0 -0
|
@@ -19,11 +19,13 @@ from cognite_toolkit._cdf_tk.commands._migrate.data_mapper import (
|
|
|
19
19
|
AssetCentricMapper,
|
|
20
20
|
CanvasMapper,
|
|
21
21
|
ChartMapper,
|
|
22
|
+
ThreeDAssetMapper,
|
|
22
23
|
ThreeDMapper,
|
|
23
24
|
)
|
|
24
25
|
from cognite_toolkit._cdf_tk.commands._migrate.migration_io import (
|
|
25
26
|
AnnotationMigrationIO,
|
|
26
27
|
AssetCentricMigrationIO,
|
|
28
|
+
ThreeDAssetMappingMigrationIO,
|
|
27
29
|
ThreeDMigrationIO,
|
|
28
30
|
)
|
|
29
31
|
from cognite_toolkit._cdf_tk.commands._migrate.selectors import (
|
|
@@ -68,6 +70,7 @@ class MigrateApp(typer.Typer):
|
|
|
68
70
|
self.command("canvas")(self.canvas)
|
|
69
71
|
self.command("charts")(self.charts)
|
|
70
72
|
self.command("3d")(self.three_d)
|
|
73
|
+
self.command("3d-mappings")(self.three_d_asset_mapping)
|
|
71
74
|
# Uncomment when infield v2 config migration is ready
|
|
72
75
|
# self.command("infield-configs")(self.infield_configs)
|
|
73
76
|
|
|
@@ -1054,6 +1057,104 @@ class MigrateApp(typer.Typer):
|
|
|
1054
1057
|
)
|
|
1055
1058
|
)
|
|
1056
1059
|
|
|
1060
|
+
@staticmethod
|
|
1061
|
+
def three_d_asset_mapping(
|
|
1062
|
+
ctx: typer.Context,
|
|
1063
|
+
model_id: Annotated[
|
|
1064
|
+
list[int] | None,
|
|
1065
|
+
typer.Argument(
|
|
1066
|
+
help="The IDs of the 3D model to migrate asset mappings for. If not provided, an interactive selection will be "
|
|
1067
|
+
"performed to select the."
|
|
1068
|
+
),
|
|
1069
|
+
] = None,
|
|
1070
|
+
object_3D_space: Annotated[
|
|
1071
|
+
str | None,
|
|
1072
|
+
typer.Option(
|
|
1073
|
+
"--object-3d-space",
|
|
1074
|
+
"-o",
|
|
1075
|
+
help="The instance space to ceate the 3D object nodes in.",
|
|
1076
|
+
),
|
|
1077
|
+
] = None,
|
|
1078
|
+
cad_node_space: Annotated[
|
|
1079
|
+
str | None,
|
|
1080
|
+
typer.Option(
|
|
1081
|
+
"--cad-node-space",
|
|
1082
|
+
"-c",
|
|
1083
|
+
help="The instance space to create the CAD node nodes in.",
|
|
1084
|
+
),
|
|
1085
|
+
] = None,
|
|
1086
|
+
log_dir: Annotated[
|
|
1087
|
+
Path,
|
|
1088
|
+
typer.Option(
|
|
1089
|
+
"--log-dir",
|
|
1090
|
+
"-l",
|
|
1091
|
+
help="Path to the directory where migration logs will be stored.",
|
|
1092
|
+
),
|
|
1093
|
+
] = Path(f"migration_logs_{TODAY}"),
|
|
1094
|
+
dry_run: Annotated[
|
|
1095
|
+
bool,
|
|
1096
|
+
typer.Option(
|
|
1097
|
+
"--dry-run",
|
|
1098
|
+
"-d",
|
|
1099
|
+
help="If set, the migration will not be executed, but only a report of what would be done is printed.",
|
|
1100
|
+
),
|
|
1101
|
+
] = False,
|
|
1102
|
+
verbose: Annotated[
|
|
1103
|
+
bool,
|
|
1104
|
+
typer.Option(
|
|
1105
|
+
"--verbose",
|
|
1106
|
+
"-v",
|
|
1107
|
+
help="Turn on to get more verbose output when running the command",
|
|
1108
|
+
),
|
|
1109
|
+
] = False,
|
|
1110
|
+
) -> None:
|
|
1111
|
+
"""Migrate 3D Model Asset Mappings from Asset-Centric to data modeling in CDF.
|
|
1112
|
+
|
|
1113
|
+
This command expects that the selected 3D model has already been migrated to data modeling.
|
|
1114
|
+
"""
|
|
1115
|
+
client = EnvironmentVariables.create_from_environment().get_client()
|
|
1116
|
+
selected_ids: list[int]
|
|
1117
|
+
if model_id is not None:
|
|
1118
|
+
selected_ids = model_id
|
|
1119
|
+
else:
|
|
1120
|
+
# Interactive selection
|
|
1121
|
+
selected_models = ThreeDInteractiveSelect(client, "migrate").select_three_d_models("dm")
|
|
1122
|
+
selected_ids = [model.id for model in selected_models]
|
|
1123
|
+
space_selector = DataModelingSelect(client, "migrate")
|
|
1124
|
+
object_3D_space = space_selector.select_instance_space(
|
|
1125
|
+
multiselect=False,
|
|
1126
|
+
message="In which instance space do you want to create the 3D Object nodes?",
|
|
1127
|
+
include_empty=False,
|
|
1128
|
+
)
|
|
1129
|
+
cad_node_space = space_selector.select_instance_space(
|
|
1130
|
+
multiselect=False,
|
|
1131
|
+
message="In which instance space do you want to create the CAD Node nodes?",
|
|
1132
|
+
include_empty=False,
|
|
1133
|
+
)
|
|
1134
|
+
dry_run = questionary.confirm("Do you want to perform a dry run?", default=dry_run).ask()
|
|
1135
|
+
verbose = questionary.confirm("Do you want verbose output?", default=verbose).ask()
|
|
1136
|
+
if any(res is None for res in [dry_run, verbose]):
|
|
1137
|
+
raise typer.Abort()
|
|
1138
|
+
|
|
1139
|
+
if object_3D_space is None or cad_node_space is None:
|
|
1140
|
+
raise typer.BadParameter(
|
|
1141
|
+
"--object-3d-space and --cad-node-space are required when specifying IDs directly."
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1144
|
+
cmd = MigrationCommand()
|
|
1145
|
+
cmd.run(
|
|
1146
|
+
lambda: cmd.migrate(
|
|
1147
|
+
selected=ThreeDModelIdSelector(ids=tuple(selected_ids)),
|
|
1148
|
+
data=ThreeDAssetMappingMigrationIO(
|
|
1149
|
+
client, object_3D_space=object_3D_space, cad_node_space=cad_node_space
|
|
1150
|
+
),
|
|
1151
|
+
mapper=ThreeDAssetMapper(client),
|
|
1152
|
+
log_dir=log_dir,
|
|
1153
|
+
dry_run=dry_run,
|
|
1154
|
+
verbose=verbose,
|
|
1155
|
+
)
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1057
1158
|
@staticmethod
|
|
1058
1159
|
def infield_configs(
|
|
1059
1160
|
ctx: typer.Context,
|
|
@@ -4,7 +4,7 @@ from typing import Any, cast
|
|
|
4
4
|
from pydantic import TypeAdapter
|
|
5
5
|
from rich.console import Console
|
|
6
6
|
|
|
7
|
-
from cognite_toolkit._cdf_tk.client.data_classes.api_classes import
|
|
7
|
+
from cognite_toolkit._cdf_tk.client.data_classes.api_classes import QueryResponse
|
|
8
8
|
from cognite_toolkit._cdf_tk.client.data_classes.infield import (
|
|
9
9
|
DataExplorationConfig,
|
|
10
10
|
InFieldCDMLocationConfig,
|
|
@@ -13,12 +13,11 @@ from cognite_toolkit._cdf_tk.client.data_classes.infield import (
|
|
|
13
13
|
from cognite_toolkit._cdf_tk.client.data_classes.instance_api import (
|
|
14
14
|
InstanceResponseItem,
|
|
15
15
|
InstanceResult,
|
|
16
|
-
|
|
16
|
+
TypedNodeIdentifier,
|
|
17
17
|
)
|
|
18
18
|
from cognite_toolkit._cdf_tk.tk_warnings import HighSeverityWarning
|
|
19
19
|
from cognite_toolkit._cdf_tk.utils.http_client import (
|
|
20
20
|
HTTPClient,
|
|
21
|
-
ItemsRequest,
|
|
22
21
|
ItemsRequest2,
|
|
23
22
|
RequestMessage2,
|
|
24
23
|
)
|
|
@@ -56,7 +55,7 @@ class InfieldConfigAPI:
|
|
|
56
55
|
responses.raise_for_status()
|
|
57
56
|
return TypeAdapter(list[InstanceResult]).validate_python(responses.get_items())
|
|
58
57
|
|
|
59
|
-
def retrieve(self, items: Sequence[
|
|
58
|
+
def retrieve(self, items: Sequence[TypedNodeIdentifier]) -> list[InfieldLocationConfig]:
|
|
60
59
|
if len(items) > 100:
|
|
61
60
|
raise ValueError("Cannot retrieve more than 100 InfieldLocationConfig items at once.")
|
|
62
61
|
if not items:
|
|
@@ -73,7 +72,7 @@ class InfieldConfigAPI:
|
|
|
73
72
|
parsed_response = QueryResponse[InstanceResponseItem].model_validate(success.body_json)
|
|
74
73
|
return self._parse_retrieve_response(parsed_response)
|
|
75
74
|
|
|
76
|
-
def delete(self, items: Sequence[InfieldLocationConfig]) -> list[
|
|
75
|
+
def delete(self, items: Sequence[InfieldLocationConfig]) -> list[TypedNodeIdentifier]:
|
|
77
76
|
if len(items) > 500:
|
|
78
77
|
raise ValueError("Cannot delete more than 500 InfieldLocationConfig items at once.")
|
|
79
78
|
|
|
@@ -83,18 +82,18 @@ class InfieldConfigAPI:
|
|
|
83
82
|
else [item.as_id(), item.data_exploration_config.as_id()]
|
|
84
83
|
for item in items
|
|
85
84
|
)
|
|
86
|
-
responses = self._http_client.
|
|
87
|
-
|
|
85
|
+
responses = self._http_client.request_items_retries(
|
|
86
|
+
ItemsRequest2(
|
|
88
87
|
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/delete"),
|
|
89
88
|
method="POST",
|
|
90
89
|
items=[identifier for sublist in identifiers for identifier in sublist],
|
|
91
90
|
)
|
|
92
91
|
)
|
|
93
92
|
responses.raise_for_status()
|
|
94
|
-
return
|
|
93
|
+
return TypeAdapter(list[TypedNodeIdentifier]).validate_python(responses.get_items())
|
|
95
94
|
|
|
96
95
|
@classmethod
|
|
97
|
-
def _retrieve_query(cls, items: Sequence[
|
|
96
|
+
def _retrieve_query(cls, items: Sequence[TypedNodeIdentifier]) -> dict[str, Any]:
|
|
98
97
|
return {
|
|
99
98
|
"with": {
|
|
100
99
|
cls.LOCATION_REF: {
|
|
@@ -184,7 +183,7 @@ class InFieldCDMConfigAPI:
|
|
|
184
183
|
results.raise_for_status()
|
|
185
184
|
return TypeAdapter(list[InstanceResult]).validate_python(results.get_items())
|
|
186
185
|
|
|
187
|
-
def retrieve(self, items: Sequence[
|
|
186
|
+
def retrieve(self, items: Sequence[TypedNodeIdentifier]) -> list[InFieldCDMLocationConfig]:
|
|
188
187
|
if len(items) > 100:
|
|
189
188
|
raise ValueError("Cannot retrieve more than 100 InFieldCDMLocationConfig items at once.")
|
|
190
189
|
if not items:
|
|
@@ -200,23 +199,23 @@ class InFieldCDMConfigAPI:
|
|
|
200
199
|
parsed_response = QueryResponse[InstanceResponseItem].model_validate(success.body_json)
|
|
201
200
|
return self._parse_retrieve_response(parsed_response)
|
|
202
201
|
|
|
203
|
-
def delete(self, items: Sequence[InFieldCDMLocationConfig]) -> list[
|
|
202
|
+
def delete(self, items: Sequence[InFieldCDMLocationConfig]) -> list[TypedNodeIdentifier]:
|
|
204
203
|
if len(items) > 500:
|
|
205
204
|
raise ValueError("Cannot delete more than 500 InFieldCDMLocationConfig items at once.")
|
|
206
205
|
|
|
207
|
-
identifiers
|
|
208
|
-
responses = self._http_client.
|
|
209
|
-
|
|
206
|
+
identifiers = [item.as_id() for item in items]
|
|
207
|
+
responses = self._http_client.request_items_retries(
|
|
208
|
+
ItemsRequest2(
|
|
210
209
|
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/delete"),
|
|
211
210
|
method="POST",
|
|
212
|
-
items=identifiers,
|
|
211
|
+
items=identifiers,
|
|
213
212
|
)
|
|
214
213
|
)
|
|
215
214
|
responses.raise_for_status()
|
|
216
|
-
return
|
|
215
|
+
return TypeAdapter(list[TypedNodeIdentifier]).validate_python(responses.get_items())
|
|
217
216
|
|
|
218
217
|
@classmethod
|
|
219
|
-
def _retrieve_query(cls, items: Sequence[
|
|
218
|
+
def _retrieve_query(cls, items: Sequence[TypedNodeIdentifier]) -> dict[str, Any]:
|
|
220
219
|
return {
|
|
221
220
|
"with": {
|
|
222
221
|
cls.LOCATION_REF: {
|
|
@@ -8,7 +8,6 @@ from cognite_toolkit._cdf_tk.client.data_classes.streams import StreamRequest, S
|
|
|
8
8
|
from cognite_toolkit._cdf_tk.utils.http_client import (
|
|
9
9
|
HTTPClient,
|
|
10
10
|
ItemsRequest2,
|
|
11
|
-
ParamRequest,
|
|
12
11
|
RequestMessage2,
|
|
13
12
|
)
|
|
14
13
|
|
|
@@ -46,13 +45,13 @@ class StreamsAPI:
|
|
|
46
45
|
Args:
|
|
47
46
|
external_id: External ID of the stream to delete.
|
|
48
47
|
"""
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
response = self._http_client.request_single_retries(
|
|
49
|
+
RequestMessage2(
|
|
51
50
|
endpoint_url=self._config.create_api_url(f"{self.ENDPOINT}/{external_id}"),
|
|
52
51
|
method="DELETE",
|
|
53
52
|
)
|
|
54
53
|
)
|
|
55
|
-
|
|
54
|
+
_ = response.get_success_or_raise()
|
|
56
55
|
|
|
57
56
|
def list(self) -> list[StreamResponse]:
|
|
58
57
|
"""List streams.
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
from collections
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from collections.abc import Iterable, Sequence
|
|
3
|
+
from typing import Any, TypeVar
|
|
2
4
|
|
|
5
|
+
from pydantic import TypeAdapter
|
|
3
6
|
from rich.console import Console
|
|
4
7
|
|
|
5
|
-
from cognite_toolkit._cdf_tk.client.data_classes.api_classes import PagedResponse
|
|
6
|
-
from cognite_toolkit._cdf_tk.client.data_classes.three_d import
|
|
8
|
+
from cognite_toolkit._cdf_tk.client.data_classes.api_classes import InternalIdRequest, PagedResponse
|
|
9
|
+
from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
|
|
10
|
+
AssetMappingClassicRequest,
|
|
11
|
+
AssetMappingDMRequest,
|
|
12
|
+
AssetMappingResponse,
|
|
13
|
+
ThreeDModelClassicRequest,
|
|
14
|
+
ThreeDModelResponse,
|
|
15
|
+
)
|
|
16
|
+
from cognite_toolkit._cdf_tk.utils.collection import chunker_sequence
|
|
7
17
|
from cognite_toolkit._cdf_tk.utils.http_client import (
|
|
8
18
|
HTTPClient,
|
|
9
|
-
|
|
19
|
+
ItemsRequest2,
|
|
10
20
|
RequestMessage2,
|
|
11
|
-
SimpleBodyRequest,
|
|
12
21
|
)
|
|
13
22
|
from cognite_toolkit._cdf_tk.utils.useful_types import PrimitiveType
|
|
14
23
|
|
|
@@ -37,16 +46,15 @@ class ThreeDModelAPI:
|
|
|
37
46
|
return []
|
|
38
47
|
if len(models) > self.MAX_CLASSIC_MODELS_PER_CREATE_REQUEST:
|
|
39
48
|
raise ValueError("Cannot create more than 1000 3D models in a single request.")
|
|
40
|
-
responses = self._http_client.
|
|
41
|
-
|
|
49
|
+
responses = self._http_client.request_items_retries(
|
|
50
|
+
ItemsRequest2(
|
|
42
51
|
endpoint_url=self._config.create_api_url(self.ENDPOINT),
|
|
43
52
|
method="POST",
|
|
44
|
-
items=
|
|
53
|
+
items=models,
|
|
45
54
|
)
|
|
46
55
|
)
|
|
47
56
|
responses.raise_for_status()
|
|
48
|
-
|
|
49
|
-
return PagedResponse[ThreeDModelResponse].model_validate(body).items
|
|
57
|
+
return TypeAdapter(list[ThreeDModelResponse]).validate_python(responses.get_items())
|
|
50
58
|
|
|
51
59
|
def delete(self, ids: Sequence[int]) -> None:
|
|
52
60
|
"""Delete 3D models by their IDs.
|
|
@@ -58,11 +66,11 @@ class ThreeDModelAPI:
|
|
|
58
66
|
return None
|
|
59
67
|
if len(ids) > self.MAX_MODELS_PER_DELETE_REQUEST:
|
|
60
68
|
raise ValueError("Cannot delete more than 1000 3D models in a single request.")
|
|
61
|
-
responses = self._http_client.
|
|
62
|
-
|
|
69
|
+
responses = self._http_client.request_items_retries(
|
|
70
|
+
ItemsRequest2(
|
|
63
71
|
endpoint_url=self._config.create_api_url(self.ENDPOINT + "/delete"),
|
|
64
72
|
method="POST",
|
|
65
|
-
|
|
73
|
+
items=InternalIdRequest.from_ids(list(ids)),
|
|
66
74
|
)
|
|
67
75
|
)
|
|
68
76
|
responses.raise_for_status()
|
|
@@ -126,6 +134,251 @@ class ThreeDModelAPI:
|
|
|
126
134
|
return results
|
|
127
135
|
|
|
128
136
|
|
|
137
|
+
T_RequestMapping = TypeVar("T_RequestMapping", bound=AssetMappingClassicRequest | AssetMappingDMRequest)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ThreeDAssetMappingAPI:
|
|
141
|
+
ENDPOINT = "/3d/models/{modelId}/revisions/{revisionId}/mappings"
|
|
142
|
+
CREATE_CLASSIC_MAX_MAPPINGS_PER_REQUEST = 1000
|
|
143
|
+
CREATE_DM_MAX_MAPPINGS_PER_REQUEST = 100
|
|
144
|
+
DELETE_CLASSIC_MAX_MAPPINGS_PER_REQUEST = 1000
|
|
145
|
+
DELETE_DM_MAX_MAPPINGS_PER_REQUEST = 100
|
|
146
|
+
LIST_REQUEST_MAX_LIMIT = 1000
|
|
147
|
+
|
|
148
|
+
def __init__(self, http_client: HTTPClient, console: Console) -> None:
|
|
149
|
+
self._http_client = http_client
|
|
150
|
+
self._console = console
|
|
151
|
+
self._config = http_client.config
|
|
152
|
+
|
|
153
|
+
def create(self, mappings: Sequence[AssetMappingClassicRequest]) -> list[AssetMappingResponse]:
|
|
154
|
+
"""Create 3D asset mappings.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
mappings (Sequence[AssetMappingClassicRequest]):
|
|
158
|
+
The 3D asset mapping(s) to create.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
list[AssetMappingResponse]: The created 3D asset mapping(s).
|
|
162
|
+
"""
|
|
163
|
+
results: list[AssetMappingResponse] = []
|
|
164
|
+
for endpoint, model_id, revision_id, revision_mappings in self._chunk_mappings_by_endpoint(
|
|
165
|
+
mappings, self.CREATE_CLASSIC_MAX_MAPPINGS_PER_REQUEST
|
|
166
|
+
):
|
|
167
|
+
responses = self._http_client.request_items_retries(
|
|
168
|
+
ItemsRequest2(
|
|
169
|
+
endpoint_url=self._config.create_api_url(endpoint),
|
|
170
|
+
method="POST",
|
|
171
|
+
items=revision_mappings,
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
responses.raise_for_status()
|
|
175
|
+
items = responses.get_items()
|
|
176
|
+
for item in items:
|
|
177
|
+
# We append modelId and revisionId to each item since the API does not return them
|
|
178
|
+
# this is needed to fully populate the AssetMappingResponse data class
|
|
179
|
+
item["modelId"] = model_id
|
|
180
|
+
item["revisionId"] = revision_id
|
|
181
|
+
results.extend(TypeAdapter(list[AssetMappingResponse]).validate_python(items))
|
|
182
|
+
return results
|
|
183
|
+
|
|
184
|
+
def create_dm(
|
|
185
|
+
self, mappings: Sequence[AssetMappingDMRequest], object_3d_space: str, cad_node_space: str
|
|
186
|
+
) -> list[AssetMappingResponse]:
|
|
187
|
+
"""Create 3D asset mappings in Data Modeling format.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
mappings (Sequence[AssetMappingDMRequest]):
|
|
191
|
+
The 3D asset mapping(s) to create
|
|
192
|
+
object_3d_space (str):
|
|
193
|
+
The instance space where the Cognite3DObject are located.
|
|
194
|
+
cad_node_space (str):
|
|
195
|
+
The instance space where the CogniteCADNode are located.
|
|
196
|
+
Returns:
|
|
197
|
+
list[AssetMappingResponse]: The created 3D asset mapping(s).
|
|
198
|
+
"""
|
|
199
|
+
results: list[AssetMappingResponse] = []
|
|
200
|
+
for endpoint, model_id, revision_id, revision_mappings in self._chunk_mappings_by_endpoint(
|
|
201
|
+
mappings, self.CREATE_DM_MAX_MAPPINGS_PER_REQUEST
|
|
202
|
+
):
|
|
203
|
+
responses = self._http_client.request_items_retries(
|
|
204
|
+
ItemsRequest2(
|
|
205
|
+
endpoint_url=self._config.create_api_url(endpoint),
|
|
206
|
+
method="POST",
|
|
207
|
+
items=revision_mappings,
|
|
208
|
+
extra_body_fields={
|
|
209
|
+
"dmsContextualizationConfig": {
|
|
210
|
+
"object3DSpace": object_3d_space,
|
|
211
|
+
"cadNodeSpace": cad_node_space,
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
responses.raise_for_status()
|
|
217
|
+
items = responses.get_items()
|
|
218
|
+
for item in items:
|
|
219
|
+
# We append modelId and revisionId to each item since the API does not return them
|
|
220
|
+
# this is needed to fully populate the AssetMappingResponse data class
|
|
221
|
+
item["modelId"] = model_id
|
|
222
|
+
item["revisionId"] = revision_id
|
|
223
|
+
results.extend(TypeAdapter(list[AssetMappingResponse]).validate_python(items))
|
|
224
|
+
return results
|
|
225
|
+
|
|
226
|
+
@classmethod
|
|
227
|
+
def _chunk_mappings_by_endpoint(
|
|
228
|
+
cls, mappings: Sequence[T_RequestMapping], chunk_size: int
|
|
229
|
+
) -> Iterable[tuple[str, int, int, list[T_RequestMapping]]]:
|
|
230
|
+
chunked_mappings: dict[tuple[int, int], list[T_RequestMapping]] = defaultdict(list)
|
|
231
|
+
for mapping in mappings:
|
|
232
|
+
key = mapping.model_id, mapping.revision_id
|
|
233
|
+
chunked_mappings[key].append(mapping)
|
|
234
|
+
for (model_id, revision_id), revision_mappings in chunked_mappings.items():
|
|
235
|
+
endpoint = cls.ENDPOINT.format(modelId=model_id, revisionId=revision_id)
|
|
236
|
+
for chunk in chunker_sequence(revision_mappings, chunk_size):
|
|
237
|
+
yield endpoint, model_id, revision_id, chunk
|
|
238
|
+
|
|
239
|
+
def delete(self, mappings: Sequence[AssetMappingClassicRequest]) -> None:
|
|
240
|
+
"""Delete 3D asset mappings.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
mappings (Sequence[AssetMappingClassicRequest]):
|
|
244
|
+
The 3D asset mapping(s) to delete.
|
|
245
|
+
"""
|
|
246
|
+
for endpoint, *_, revision_mappings in self._chunk_mappings_by_endpoint(
|
|
247
|
+
mappings, self.DELETE_CLASSIC_MAX_MAPPINGS_PER_REQUEST
|
|
248
|
+
):
|
|
249
|
+
responses = self._http_client.request_items_retries(
|
|
250
|
+
ItemsRequest2(
|
|
251
|
+
endpoint_url=self._config.create_api_url(f"{endpoint}/delete"),
|
|
252
|
+
method="DELETE",
|
|
253
|
+
items=revision_mappings,
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
responses.raise_for_status()
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
def delete_dm(self, mappings: Sequence[AssetMappingDMRequest], object_3d_space: str, cad_node_space: str) -> None:
|
|
260
|
+
"""Delete 3D asset mappings in Data Modeling format.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
mappings (Sequence[AssetMappingDMRequest]):
|
|
264
|
+
The 3D asset mapping(s) to delete.
|
|
265
|
+
object_3d_space (str):
|
|
266
|
+
The instance space where the Cognite3DObject are located.
|
|
267
|
+
cad_node_space (str):
|
|
268
|
+
The instance space where the CogniteCADNode are located.
|
|
269
|
+
"""
|
|
270
|
+
for endpoint, *_, revision_mappings in self._chunk_mappings_by_endpoint(
|
|
271
|
+
mappings, self.DELETE_DM_MAX_MAPPINGS_PER_REQUEST
|
|
272
|
+
):
|
|
273
|
+
responses = self._http_client.request_items_retries(
|
|
274
|
+
ItemsRequest2(
|
|
275
|
+
endpoint_url=self._config.create_api_url(f"{endpoint}/delete"),
|
|
276
|
+
method="DELETE",
|
|
277
|
+
items=revision_mappings,
|
|
278
|
+
extra_body_fields={
|
|
279
|
+
"dmsContextualizationConfig": {
|
|
280
|
+
"object3DSpace": object_3d_space,
|
|
281
|
+
"cadNodeSpace": cad_node_space,
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
responses.raise_for_status()
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
def iterate(
|
|
290
|
+
self,
|
|
291
|
+
model_id: int,
|
|
292
|
+
revision_id: int,
|
|
293
|
+
asset_ids: list[int] | None = None,
|
|
294
|
+
asset_instance_ids: list[str] | None = None,
|
|
295
|
+
node_ids: list[int] | None = None,
|
|
296
|
+
tree_indexes: list[int] | None = None,
|
|
297
|
+
get_dms_instances: bool = False,
|
|
298
|
+
limit: int = 100,
|
|
299
|
+
cursor: str | None = None,
|
|
300
|
+
) -> PagedResponse[AssetMappingResponse]:
|
|
301
|
+
if not (0 < limit <= self.LIST_REQUEST_MAX_LIMIT):
|
|
302
|
+
raise ValueError(f"Limit must be between 1 and {self.LIST_REQUEST_MAX_LIMIT}, got {limit}.")
|
|
303
|
+
if sum(param is not None for param in [asset_ids, asset_instance_ids, node_ids, tree_indexes]) > 1:
|
|
304
|
+
raise ValueError("Only one of asset_ids, asset_instance_ids, node_ids, or tree_indexes can be provided.")
|
|
305
|
+
body: dict[str, Any] = {
|
|
306
|
+
"getDmsInstances": get_dms_instances,
|
|
307
|
+
"limit": limit,
|
|
308
|
+
}
|
|
309
|
+
if asset_ids is not None:
|
|
310
|
+
if not (0 < len(asset_ids) <= 100):
|
|
311
|
+
raise ValueError("asset_ids must contain between 1 and 100 IDs.")
|
|
312
|
+
body["filter"] = {"assetIds": asset_ids}
|
|
313
|
+
elif asset_instance_ids is not None:
|
|
314
|
+
if not (0 < len(asset_instance_ids) <= 100):
|
|
315
|
+
raise ValueError("asset_instance_ids must contain between 1 and 100 IDs.")
|
|
316
|
+
body["filter"] = {"assetInstanceIds": asset_instance_ids}
|
|
317
|
+
elif node_ids is not None:
|
|
318
|
+
if not (0 < len(node_ids) <= 100):
|
|
319
|
+
raise ValueError("node_ids must contain between 1 and 100 IDs.")
|
|
320
|
+
body["filter"] = {"nodeIds": node_ids}
|
|
321
|
+
elif tree_indexes is not None:
|
|
322
|
+
if not (0 < len(tree_indexes) <= 100):
|
|
323
|
+
raise ValueError("tree_indexes must contain between 1 and 100 indexes.")
|
|
324
|
+
body["filter"] = {"treeIndexes": tree_indexes}
|
|
325
|
+
if cursor is not None:
|
|
326
|
+
body["cursor"] = cursor
|
|
327
|
+
|
|
328
|
+
endpoint = self.ENDPOINT.format(modelId=model_id, revisionId=revision_id)
|
|
329
|
+
responses = self._http_client.request_single_retries(
|
|
330
|
+
RequestMessage2(
|
|
331
|
+
endpoint_url=self._config.create_api_url(f"{endpoint}/list"),
|
|
332
|
+
method="POST",
|
|
333
|
+
body_content=body,
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
success_response = responses.get_success_or_raise()
|
|
337
|
+
body_json = success_response.body_json
|
|
338
|
+
# Add modelId and revisionId to items since the API does not return them
|
|
339
|
+
for item in body_json.get("items", []):
|
|
340
|
+
item["modelId"] = model_id
|
|
341
|
+
item["revisionId"] = revision_id
|
|
342
|
+
return PagedResponse[AssetMappingResponse].model_validate(body_json)
|
|
343
|
+
|
|
344
|
+
def list(
|
|
345
|
+
self,
|
|
346
|
+
model_id: int,
|
|
347
|
+
revision_id: int,
|
|
348
|
+
asset_ids: list[int] | None = None,
|
|
349
|
+
asset_instance_ids: list[str] | None = None,
|
|
350
|
+
node_ids: list[int] | None = None,
|
|
351
|
+
tree_indexes: list[int] | None = None,
|
|
352
|
+
get_dms_instances: bool = False,
|
|
353
|
+
limit: int | None = 100,
|
|
354
|
+
) -> list[AssetMappingResponse]:
|
|
355
|
+
results: list[AssetMappingResponse] = []
|
|
356
|
+
cursor: str | None = None
|
|
357
|
+
while True:
|
|
358
|
+
request_limit = (
|
|
359
|
+
self.LIST_REQUEST_MAX_LIMIT if limit is None else min(limit - len(results), self.LIST_REQUEST_MAX_LIMIT)
|
|
360
|
+
)
|
|
361
|
+
if request_limit <= 0:
|
|
362
|
+
break
|
|
363
|
+
page = self.iterate(
|
|
364
|
+
model_id=model_id,
|
|
365
|
+
revision_id=revision_id,
|
|
366
|
+
asset_ids=asset_ids,
|
|
367
|
+
asset_instance_ids=asset_instance_ids,
|
|
368
|
+
node_ids=node_ids,
|
|
369
|
+
tree_indexes=tree_indexes,
|
|
370
|
+
get_dms_instances=get_dms_instances,
|
|
371
|
+
limit=request_limit,
|
|
372
|
+
cursor=cursor,
|
|
373
|
+
)
|
|
374
|
+
results.extend(page.items)
|
|
375
|
+
if page.next_cursor is None:
|
|
376
|
+
break
|
|
377
|
+
cursor = page.next_cursor
|
|
378
|
+
return results
|
|
379
|
+
|
|
380
|
+
|
|
129
381
|
class ThreeDAPI:
|
|
130
382
|
def __init__(self, http_client: HTTPClient, console: Console) -> None:
|
|
131
383
|
self.models = ThreeDModelAPI(http_client, console)
|
|
384
|
+
self.asset_mappings = ThreeDAssetMappingAPI(http_client, console)
|
|
@@ -2,6 +2,8 @@ from typing import Generic, TypeVar
|
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, Field, JsonValue
|
|
4
4
|
|
|
5
|
+
from cognite_toolkit._cdf_tk.utils.http_client._data_classes2 import RequestResource
|
|
6
|
+
|
|
5
7
|
T = TypeVar("T", bound=BaseModel)
|
|
6
8
|
|
|
7
9
|
|
|
@@ -15,3 +17,14 @@ class QueryResponse(BaseModel, Generic[T]):
|
|
|
15
17
|
typing: dict[str, JsonValue] | None = None
|
|
16
18
|
next_cursor: dict[str, str] = Field(alias="nextCursor")
|
|
17
19
|
debug: dict[str, JsonValue] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InternalIdRequest(RequestResource):
|
|
23
|
+
id: int
|
|
24
|
+
|
|
25
|
+
def as_id(self) -> int:
|
|
26
|
+
return self.id
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_ids(cls, ids: list[int]) -> list["InternalIdRequest"]:
|
|
30
|
+
return [cls(id=id_) for id_ in ids]
|
|
@@ -3,7 +3,7 @@ from abc import ABC, abstractmethod
|
|
|
3
3
|
from collections import UserList
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
5
5
|
|
|
6
|
-
from pydantic import
|
|
6
|
+
from pydantic import ConfigDict
|
|
7
7
|
from pydantic.alias_generators import to_camel
|
|
8
8
|
|
|
9
9
|
from cognite_toolkit._cdf_tk.utils.http_client._data_classes2 import BaseModelObject, RequestResource
|
|
@@ -31,21 +31,17 @@ class ResponseResource(BaseModelObject, Generic[T_RequestResource], ABC):
|
|
|
31
31
|
return self.as_request_resource()
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
class Identifier(
|
|
34
|
+
class Identifier(RequestResource, ABC):
|
|
35
35
|
"""Base class for all identifier classes."""
|
|
36
36
|
|
|
37
37
|
model_config = ConfigDict(alias_generator=to_camel, extra="ignore", populate_by_name=True, frozen=True)
|
|
38
38
|
|
|
39
|
-
def dump(self, include_type: bool = True) -> dict[str, Any]:
|
|
40
|
-
"""Dump the
|
|
39
|
+
def dump(self, camel_case: bool = True, include_type: bool = True) -> dict[str, Any]:
|
|
40
|
+
"""Dump the resource to a dictionary.
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
include_type (bool): Whether to include the type of the identifier in the output.
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
dict[str, Any]: The dumped identifier.
|
|
42
|
+
This is the default serialization method for request resources.
|
|
47
43
|
"""
|
|
48
|
-
return self.model_dump(mode="json", by_alias=
|
|
44
|
+
return self.model_dump(mode="json", by_alias=camel_case, exclude_unset=not include_type)
|
|
49
45
|
|
|
50
46
|
def as_id(self) -> Self:
|
|
51
47
|
return self
|
|
@@ -15,19 +15,19 @@ class TypedInstanceIdentifier(Identifier):
|
|
|
15
15
|
external_id: str
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class
|
|
19
|
-
space: str
|
|
20
|
-
external_id: str
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class NodeIdentifier(TypedInstanceIdentifier):
|
|
18
|
+
class TypedNodeIdentifier(TypedInstanceIdentifier):
|
|
24
19
|
instance_type: Literal["node"] = "node"
|
|
25
20
|
|
|
26
21
|
|
|
27
|
-
class
|
|
22
|
+
class TypedEdgeIdentifier(TypedInstanceIdentifier):
|
|
28
23
|
instance_type: Literal["edge"] = "edge"
|
|
29
24
|
|
|
30
25
|
|
|
26
|
+
class InstanceIdentifier(Identifier):
|
|
27
|
+
space: str
|
|
28
|
+
external_id: str
|
|
29
|
+
|
|
30
|
+
|
|
31
31
|
class InstanceResult(BaseModelObject):
|
|
32
32
|
instance_type: InstanceType
|
|
33
33
|
version: int
|
|
@@ -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
|
+
)
|
|
@@ -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,
|
|
@@ -469,3 +475,45 @@ class ThreeDMapper(DataMapper[ThreeDSelector, ThreeDModelResponse, ThreeDMigrati
|
|
|
469
475
|
return "PointCloud"
|
|
470
476
|
else:
|
|
471
477
|
return None
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
class ThreeDAssetMapper(DataMapper[ThreeDSelector, AssetMappingResponse, AssetMappingDMRequest]):
|
|
481
|
+
def __init__(self, client: ToolkitClient) -> None:
|
|
482
|
+
self.client = client
|
|
483
|
+
|
|
484
|
+
def map(
|
|
485
|
+
self, source: Sequence[AssetMappingResponse]
|
|
486
|
+
) -> Sequence[tuple[AssetMappingDMRequest | None, MigrationIssue]]:
|
|
487
|
+
output: list[tuple[AssetMappingDMRequest | None, MigrationIssue]] = []
|
|
488
|
+
self._populate_cache(source)
|
|
489
|
+
for item in source:
|
|
490
|
+
mapped_item, issue = self._map_single_item(item)
|
|
491
|
+
output.append((mapped_item, issue))
|
|
492
|
+
return output
|
|
493
|
+
|
|
494
|
+
def _populate_cache(self, source: Sequence[AssetMappingResponse]) -> None:
|
|
495
|
+
asset_ids: set[int] = set()
|
|
496
|
+
for mapping in source:
|
|
497
|
+
if mapping.asset_id is not None:
|
|
498
|
+
asset_ids.add(mapping.asset_id)
|
|
499
|
+
self.client.migration.lookup.assets(list(asset_ids))
|
|
500
|
+
|
|
501
|
+
def _map_single_item(
|
|
502
|
+
self, item: AssetMappingResponse
|
|
503
|
+
) -> tuple[AssetMappingDMRequest | None, ThreeDModelMigrationIssue]:
|
|
504
|
+
issue = ThreeDModelMigrationIssue(model_name=f"AssetMapping_{item.model_id}", model_id=item.model_id)
|
|
505
|
+
asset_instance_id = item.asset_instance_id
|
|
506
|
+
if item.asset_id and asset_instance_id is None:
|
|
507
|
+
asset_node_id = self.client.migration.lookup.assets(item.asset_id)
|
|
508
|
+
if asset_node_id is None:
|
|
509
|
+
issue.error_message.append(f"Missing asset instance for asset ID {item.asset_id!r}")
|
|
510
|
+
return None, issue
|
|
511
|
+
asset_instance_id = NodeReference(space=asset_node_id.space, externalId=asset_node_id.external_id)
|
|
512
|
+
|
|
513
|
+
if asset_instance_id is None:
|
|
514
|
+
issue.error_message.append("Neither assetInstanceId nor assetId provided for mapping.")
|
|
515
|
+
return None, issue
|
|
516
|
+
mapped_request = AssetMappingDMRequest(
|
|
517
|
+
modelId=item.model_id, revisionId=item.revision_id, nodeId=item.node_id, assetInstanceId=asset_instance_id
|
|
518
|
+
)
|
|
519
|
+
return mapped_request, issue
|
|
@@ -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.36"
|
|
@@ -10,7 +10,7 @@ cognite_toolkit/_cdf_tk/apps/_download_app.py,sha256=s8-lkfvzGwQSuBcLEetlTTg7wVR
|
|
|
10
10
|
cognite_toolkit/_cdf_tk/apps/_dump_app.py,sha256=X7uh9lQAE-U24z0FrNsechSOyczjVHW8BGjw3wFfwUU,37134
|
|
11
11
|
cognite_toolkit/_cdf_tk/apps/_import_app.py,sha256=5n5AF40HJ0Q_3LENCknG0MxH4pMUS8OwsqvtCYj_t7w,2086
|
|
12
12
|
cognite_toolkit/_cdf_tk/apps/_landing_app.py,sha256=YR9z83OY7PhhgBVC5gmRLgo9iTXoGoZfRhOU3gd_r2o,888
|
|
13
|
-
cognite_toolkit/_cdf_tk/apps/_migrate_app.py,sha256=
|
|
13
|
+
cognite_toolkit/_cdf_tk/apps/_migrate_app.py,sha256=YUs5V5oJzLeWqFSTW2NfwTptJUC22kjZIQ30xUw-U7s,47665
|
|
14
14
|
cognite_toolkit/_cdf_tk/apps/_modules_app.py,sha256=t0SPvulgbgkF_OO2E68mQ_ZUcJ6HoaurYe0IkmXie0o,7449
|
|
15
15
|
cognite_toolkit/_cdf_tk/apps/_profile_app.py,sha256=vSRJW54bEvIul8_4rOqyOYA7ztXx7TFOvZRZWZTxMbg,7007
|
|
16
16
|
cognite_toolkit/_cdf_tk/apps/_purge.py,sha256=KYI1wFy7yHFEM1qJnTYc4_8E2FVGu4QhPsWsxop1sZA,14242
|
|
@@ -31,7 +31,7 @@ cognite_toolkit/_cdf_tk/client/__init__.py,sha256=a6rQXDGfW2g7K5WwrOW5oakh1TdFlB
|
|
|
31
31
|
cognite_toolkit/_cdf_tk/client/_constants.py,sha256=COUGcea37mDF2sf6MGqJXWmecTY_6aCImslxXrYW1I0,73
|
|
32
32
|
cognite_toolkit/_cdf_tk/client/_toolkit_client.py,sha256=S2D0N3Cea39Bhl2NIu8jbQy8Xbq1RS1rldG238CvfQ4,3750
|
|
33
33
|
cognite_toolkit/_cdf_tk/client/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
-
cognite_toolkit/_cdf_tk/client/api/infield.py,sha256=
|
|
34
|
+
cognite_toolkit/_cdf_tk/client/api/infield.py,sha256=B7sydnhE1cKhp9OJxkjmTAfRREWZTaD8EuSvscZoE8w,11039
|
|
35
35
|
cognite_toolkit/_cdf_tk/client/api/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
36
|
cognite_toolkit/_cdf_tk/client/api/legacy/canvas.py,sha256=2TVKPlgtXviPxW9tOX_tv7I06K-SnYovg5c9sxBE3f4,8843
|
|
37
37
|
cognite_toolkit/_cdf_tk/client/api/legacy/charts.py,sha256=YZ2jjHoKinRiqOBzosq6abvzStA4B6VezI1wSy3_U2k,4960
|
|
@@ -57,19 +57,19 @@ cognite_toolkit/_cdf_tk/client/api/lookup.py,sha256=c-cvtgfGGGYyk8ROcJu44qlo1ocq
|
|
|
57
57
|
cognite_toolkit/_cdf_tk/client/api/migration.py,sha256=6q3ncT-_I2P7fWYzAcv_UG55VnrVRTSymwaf10aosJA,23102
|
|
58
58
|
cognite_toolkit/_cdf_tk/client/api/project.py,sha256=5DDnyDF9WN8m8wNP3WhqX0gT6nDaG-2ekC1VSMMrdV8,1088
|
|
59
59
|
cognite_toolkit/_cdf_tk/client/api/search.py,sha256=wl6MjmkELCWmkXf9yYM03LElLC5l0_DwwifsZc_tXSg,694
|
|
60
|
-
cognite_toolkit/_cdf_tk/client/api/streams.py,sha256=
|
|
61
|
-
cognite_toolkit/_cdf_tk/client/api/three_d.py,sha256=
|
|
60
|
+
cognite_toolkit/_cdf_tk/client/api/streams.py,sha256=uOD5UBsfkmMaiJ-UrtZI9I0O4fQlp2H627o0PJqzefY,3048
|
|
61
|
+
cognite_toolkit/_cdf_tk/client/api/three_d.py,sha256=C8lPVMceJnK1qJOvNNeQgbic0MIEY2up4oL-REZXN5I,15934
|
|
62
62
|
cognite_toolkit/_cdf_tk/client/api/token.py,sha256=8SiA44Dwsx0j_X8lgIxl2rdNCQSdEiSfoD_4ybxMtFA,5131
|
|
63
63
|
cognite_toolkit/_cdf_tk/client/api/verify.py,sha256=-x6z6lMaOZG91adi0m9NtJ4wIQgoZURbzluPALXM-ps,3730
|
|
64
64
|
cognite_toolkit/_cdf_tk/client/api_client.py,sha256=CQdD_gfDqQkz5OYHrTnKvBvEvzHPdHDB1BkZPWRoahg,440
|
|
65
65
|
cognite_toolkit/_cdf_tk/client/config.py,sha256=weMR43z-gqHMn-Jqvfmh_nJ0HbgEdyeCGtISuEf3OuY,4269
|
|
66
66
|
cognite_toolkit/_cdf_tk/client/data_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
|
-
cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py,sha256=
|
|
68
|
-
cognite_toolkit/_cdf_tk/client/data_classes/base.py,sha256=
|
|
67
|
+
cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py,sha256=_B0BRfLbhibwx-K4rfsq4IRq1V05KzUNCNN_2AlDhFg,801
|
|
68
|
+
cognite_toolkit/_cdf_tk/client/data_classes/base.py,sha256=0XsvidoFPAQ_j1cTcBJ0dLv2fP2Nwh7zQAW49hj-ZAk,2224
|
|
69
69
|
cognite_toolkit/_cdf_tk/client/data_classes/capabilities.py,sha256=muqpAC2JLCFcEpRPzuh_3sS3o_q42WFyfsGzl-LfB_U,8773
|
|
70
70
|
cognite_toolkit/_cdf_tk/client/data_classes/charts_data.py,sha256=-dFfY53cos5DwASLU18aBfYF1VC6bfaUshC2HiGJ2uI,5571
|
|
71
71
|
cognite_toolkit/_cdf_tk/client/data_classes/infield.py,sha256=xZDpHw190FgX2vK6zk_r8dUJA7J6UzdS8227VOu74ms,4298
|
|
72
|
-
cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py,sha256=
|
|
72
|
+
cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py,sha256=3NPKpD0OLzac_tvxXm54NeYw9LO6RC8NEUolthwPD_Q,5075
|
|
73
73
|
cognite_toolkit/_cdf_tk/client/data_classes/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
74
74
|
cognite_toolkit/_cdf_tk/client/data_classes/legacy/apm_config_v1.py,sha256=0bPq7R0qvdf8SMFS06kX7TXHIClDcJNHwdTBweQB-GU,20150
|
|
75
75
|
cognite_toolkit/_cdf_tk/client/data_classes/legacy/canvas.py,sha256=T5XLaUAFlh4NjWiJLGh4rjdS9BSeTUEsbhh9nbRd0s4,50728
|
|
@@ -91,7 +91,7 @@ cognite_toolkit/_cdf_tk/client/data_classes/legacy/search_config.py,sha256=Reo_r
|
|
|
91
91
|
cognite_toolkit/_cdf_tk/client/data_classes/legacy/sequences.py,sha256=02d34fPcJ1H7U5ZnCCfOi36z5WJ4WnRfCWwkp99mW2E,6234
|
|
92
92
|
cognite_toolkit/_cdf_tk/client/data_classes/legacy/streamlit_.py,sha256=nEk00FH3i-px2r6ql4kk1VVL4sytjUn0_sTkEdDSHVc,6746
|
|
93
93
|
cognite_toolkit/_cdf_tk/client/data_classes/streams.py,sha256=DHSDrBax81fUzneIikn9hUMVgQVbdaiQ9aY-bRaTK38,2459
|
|
94
|
-
cognite_toolkit/_cdf_tk/client/data_classes/three_d.py,sha256=
|
|
94
|
+
cognite_toolkit/_cdf_tk/client/data_classes/three_d.py,sha256=Tk8ujB_aN7rfpqbBNV2Ps2Onp_F7Ipt-EUItUIspTaE,3424
|
|
95
95
|
cognite_toolkit/_cdf_tk/client/testing.py,sha256=xD1Pg7Cb5Th_o_XbyqyMSNGVlBEa-AlS54ezjRsQ4JA,7386
|
|
96
96
|
cognite_toolkit/_cdf_tk/client/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
97
97
|
cognite_toolkit/_cdf_tk/client/utils/_concurrency.py,sha256=3GtQbKDaosyKHEt-KzxKK9Yie4TvZPdoou2vUk6dUa8,2298
|
|
@@ -107,15 +107,15 @@ cognite_toolkit/_cdf_tk/commands/_migrate/command.py,sha256=l2P0Em05aEJvNZH4WkEI
|
|
|
107
107
|
cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py,sha256=VxoFLS0Xi9uviICShxHHXbUU_wliqNZL3PxD0qveOSU,16810
|
|
108
108
|
cognite_toolkit/_cdf_tk/commands/_migrate/creators.py,sha256=hQ9o_N2aY9nqHOKlwP7tI7mAYLCPkPLSJGe_QTwglWc,9612
|
|
109
109
|
cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py,sha256=__o2JZ3GkD0uqtjjpumHJF8KLdqCFrbMZOoEQHAeOLo,11488
|
|
110
|
-
cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py,sha256=
|
|
110
|
+
cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py,sha256=282o0zDiY5_zqMNTQApn5uaM73vgo_FuNnQo3sicq9Y,24366
|
|
111
111
|
cognite_toolkit/_cdf_tk/commands/_migrate/data_model.py,sha256=i1eUsNX6Dueol9STIEwyksBnBsWUk13O8qHIjW964pM,7860
|
|
112
112
|
cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py,sha256=HRh-VlVWbOLjQfP968lReYtZFptjUEa4JbbZxiw5Qy8,5551
|
|
113
113
|
cognite_toolkit/_cdf_tk/commands/_migrate/issues.py,sha256=uEMI5azCpHWAvEauRY9ZvsvNO0_aaUpdNP7_nM7oBC4,7547
|
|
114
|
-
cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py,sha256=
|
|
114
|
+
cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py,sha256=7gD7uKOy8kPa9u9rvRcOnZsuLGTD0WhU8szKGQ3GDoM,26782
|
|
115
115
|
cognite_toolkit/_cdf_tk/commands/_migrate/prepare.py,sha256=RfqaNoso5CyBwc-p6ckwcYqBfZXKhdJgdGIyd0TATaI,2635
|
|
116
116
|
cognite_toolkit/_cdf_tk/commands/_migrate/selectors.py,sha256=N1H_-rBpPUD6pbrlcofn1uEK1bA694EUXEe1zIXeqyo,2489
|
|
117
117
|
cognite_toolkit/_cdf_tk/commands/_profile.py,sha256=8E3U4WyJmGPeOXLEWtRaFpB05MFWgA5Y4vFKPay6OfY,43127
|
|
118
|
-
cognite_toolkit/_cdf_tk/commands/_purge.py,sha256=
|
|
118
|
+
cognite_toolkit/_cdf_tk/commands/_purge.py,sha256=MzBi-fTiNWFdAodOtraOWgtqG9rOSttKS49rCGL3XfQ,32764
|
|
119
119
|
cognite_toolkit/_cdf_tk/commands/_questionary_style.py,sha256=h-w7fZKkGls3TrzIGBKjsZSGoXJJIYchgD1StfA40r8,806
|
|
120
120
|
cognite_toolkit/_cdf_tk/commands/_upload.py,sha256=SBqevUZjNW_-hGXSI8YzwgQ7AgJH15M0S3avm7Wmqpw,13040
|
|
121
121
|
cognite_toolkit/_cdf_tk/commands/_utils.py,sha256=UxMJW5QYKts4om5n6x2Tq2ihvfO9gWjhQKeqZNFTlKg,402
|
|
@@ -149,7 +149,7 @@ cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py,sha256=OniGxzx0XM
|
|
|
149
149
|
cognite_toolkit/_cdf_tk/cruds/_resource_cruds/data_organization.py,sha256=U0ItuoNr1KEtoFQAiIe-K19_72ht9-kGndFVgF-iC10,9524
|
|
150
150
|
cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py,sha256=4uTr3Mx89yaPsr23mcR7hD7h9XBpB4dUPCeNacI8mB0,64874
|
|
151
151
|
cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py,sha256=nvMIDeAksJ_0iWvorK8k1BemSrTAUhll_JgCMNiUjZ4,17708
|
|
152
|
-
cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py,sha256=
|
|
152
|
+
cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py,sha256=4DL6koP_wwBqfgRveSAYbTVkcTJfjvrj0s2JXJ_p8yQ,20750
|
|
153
153
|
cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py,sha256=YGsrp0_yLcpyUvVc_-EW7Rb6LTl_cKtWNmuJDbxbqhs,14862
|
|
154
154
|
cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py,sha256=QHihJsyCc9EJImPQSIZDQsxDewCglBc6iSHXjLeutMs,28402
|
|
155
155
|
cognite_toolkit/_cdf_tk/cruds/_resource_cruds/group_scoped.py,sha256=WEg8-CxMP64WfE_XXIlH114zM51K0uLaYa4atd992zI,1690
|
|
@@ -289,7 +289,7 @@ cognite_toolkit/_cdf_tk/utils/hashing.py,sha256=3NyNfljyYNTqAyAFBd6XlyWaj43jRzEN
|
|
|
289
289
|
cognite_toolkit/_cdf_tk/utils/http_client/__init__.py,sha256=MG3rAi5IiLxqlCMyVvzyfsKXMb_3fVxwAZ7uyPXrMvs,1483
|
|
290
290
|
cognite_toolkit/_cdf_tk/utils/http_client/_client.py,sha256=1nKMop_ORrGXybG-KDmC3rlcKs8cseLWSkPkMTFWRyw,22493
|
|
291
291
|
cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py,sha256=tpU2pswtbGn5E45izm-B4qIc4RSyh5gGDBj8kA0Gs7o,14964
|
|
292
|
-
cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py,sha256=
|
|
292
|
+
cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py,sha256=kes6bUeHv8icMqcPbIXszl8GKtxpc5AIN4nUIHFoSV4,8761
|
|
293
293
|
cognite_toolkit/_cdf_tk/utils/http_client/_exception.py,sha256=fC9oW6BN0HbUe2AkYABMP7Kj0-9dNYXVFBY5RQztq2c,126
|
|
294
294
|
cognite_toolkit/_cdf_tk/utils/http_client/_tracker.py,sha256=pu6oA-XpOeaOLdoeD_mGfJXC3BFGWfh5oGcRDpb6maw,1407
|
|
295
295
|
cognite_toolkit/_cdf_tk/utils/interactive_select.py,sha256=XeTyxfcrbgxo5a7rIGXB0hF1rJ_vHYsat3Wl31XM-hI,37907
|
|
@@ -309,14 +309,14 @@ cognite_toolkit/_repo_files/.gitignore,sha256=ip9kf9tcC5OguF4YF4JFEApnKYw0nG0vPi
|
|
|
309
309
|
cognite_toolkit/_repo_files/AzureDevOps/.devops/README.md,sha256=OLA0D7yCX2tACpzvkA0IfkgQ4_swSd-OlJ1tYcTBpsA,240
|
|
310
310
|
cognite_toolkit/_repo_files/AzureDevOps/.devops/deploy-pipeline.yml,sha256=brULcs8joAeBC_w_aoWjDDUHs3JheLMIR9ajPUK96nc,693
|
|
311
311
|
cognite_toolkit/_repo_files/AzureDevOps/.devops/dry-run-pipeline.yml,sha256=OBFDhFWK1mlT4Dc6mDUE2Es834l8sAlYG50-5RxRtHk,723
|
|
312
|
-
cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml,sha256=
|
|
313
|
-
cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml,sha256=
|
|
314
|
-
cognite_toolkit/_resources/cdf.toml,sha256=
|
|
315
|
-
cognite_toolkit/_version.py,sha256=
|
|
312
|
+
cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml,sha256=ofqsT_RZbUsrih1RE9qrzOOQK72T9aM4HwIu5OOdM20,667
|
|
313
|
+
cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml,sha256=Lul-1240VOiishKOBBWDJua7KIEuJziNJXcpgz8mRgE,2430
|
|
314
|
+
cognite_toolkit/_resources/cdf.toml,sha256=DIpC1r6ql-BAasLk7nvhpPLpQeykwAU-XYG57VF8-iQ,475
|
|
315
|
+
cognite_toolkit/_version.py,sha256=s_fO2Pb37oajrH9N1MYPwjM5YVV5bZvqGHD_GGAAiec,23
|
|
316
316
|
cognite_toolkit/config.dev.yaml,sha256=M33FiIKdS3XKif-9vXniQ444GTZ-bLXV8aFH86u9iUQ,332
|
|
317
317
|
cognite_toolkit/demo/__init__.py,sha256=-m1JoUiwRhNCL18eJ6t7fZOL7RPfowhCuqhYFtLgrss,72
|
|
318
318
|
cognite_toolkit/demo/_base.py,sha256=6xKBUQpXZXGQ3fJ5f7nj7oT0s2n7OTAGIa17ZlKHZ5U,8052
|
|
319
|
-
cognite_toolkit-0.7.
|
|
320
|
-
cognite_toolkit-0.7.
|
|
321
|
-
cognite_toolkit-0.7.
|
|
322
|
-
cognite_toolkit-0.7.
|
|
319
|
+
cognite_toolkit-0.7.36.dist-info/WHEEL,sha256=xDCZ-UyfvkGuEHPeI7BcJzYKIZzdqN8A8o1M5Om8IyA,79
|
|
320
|
+
cognite_toolkit-0.7.36.dist-info/entry_points.txt,sha256=EtZ17K2mUjh-AY0QNU1CPIB_aDSSOdmtNI_4Fj967mA,84
|
|
321
|
+
cognite_toolkit-0.7.36.dist-info/METADATA,sha256=IVa9xEH1DSyQh4n7Ipr0HyVjKHPLoZUAgI0hole3qjo,4507
|
|
322
|
+
cognite_toolkit-0.7.36.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|