cognite-toolkit 0.7.30__py3-none-any.whl → 0.7.39__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.
Files changed (121) hide show
  1. cognite_toolkit/_cdf.py +5 -6
  2. cognite_toolkit/_cdf_tk/apps/__init__.py +2 -0
  3. cognite_toolkit/_cdf_tk/apps/_core_app.py +7 -1
  4. cognite_toolkit/_cdf_tk/apps/_download_app.py +2 -2
  5. cognite_toolkit/_cdf_tk/apps/_dump_app.py +1 -1
  6. cognite_toolkit/_cdf_tk/apps/_import_app.py +41 -0
  7. cognite_toolkit/_cdf_tk/apps/_migrate_app.py +177 -2
  8. cognite_toolkit/_cdf_tk/builders/_raw.py +1 -1
  9. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +9 -9
  10. cognite_toolkit/_cdf_tk/client/api/infield.py +38 -33
  11. cognite_toolkit/_cdf_tk/client/api/{canvas.py → legacy/canvas.py} +15 -7
  12. cognite_toolkit/_cdf_tk/client/api/{charts.py → legacy/charts.py} +1 -1
  13. cognite_toolkit/_cdf_tk/client/api/{extended_data_modeling.py → legacy/extended_data_modeling.py} +1 -1
  14. cognite_toolkit/_cdf_tk/client/api/{extended_files.py → legacy/extended_files.py} +2 -2
  15. cognite_toolkit/_cdf_tk/client/api/{extended_functions.py → legacy/extended_functions.py} +9 -9
  16. cognite_toolkit/_cdf_tk/client/api/{extended_raw.py → legacy/extended_raw.py} +1 -1
  17. cognite_toolkit/_cdf_tk/client/api/{extended_timeseries.py → legacy/extended_timeseries.py} +5 -2
  18. cognite_toolkit/_cdf_tk/client/api/{location_filters.py → legacy/location_filters.py} +1 -1
  19. cognite_toolkit/_cdf_tk/client/api/legacy/robotics/__init__.py +8 -0
  20. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/capabilities.py +1 -1
  21. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/data_postprocessing.py +1 -1
  22. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/frames.py +1 -1
  23. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/locations.py +1 -1
  24. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/maps.py +1 -1
  25. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/robots.py +2 -2
  26. cognite_toolkit/_cdf_tk/client/api/{search_config.py → legacy/search_config.py} +5 -1
  27. cognite_toolkit/_cdf_tk/client/api/migration.py +2 -3
  28. cognite_toolkit/_cdf_tk/client/api/project.py +9 -8
  29. cognite_toolkit/_cdf_tk/client/api/search.py +2 -2
  30. cognite_toolkit/_cdf_tk/client/api/streams.py +21 -17
  31. cognite_toolkit/_cdf_tk/client/api/three_d.py +343 -9
  32. cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py +13 -0
  33. cognite_toolkit/_cdf_tk/client/data_classes/base.py +12 -32
  34. cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +18 -13
  35. cognite_toolkit/_cdf_tk/client/data_classes/legacy/__init__.py +0 -0
  36. cognite_toolkit/_cdf_tk/client/data_classes/{canvas.py → legacy/canvas.py} +1 -1
  37. cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +65 -0
  38. cognite_toolkit/_cdf_tk/client/testing.py +24 -16
  39. cognite_toolkit/_cdf_tk/commands/__init__.py +1 -0
  40. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +8 -2
  41. cognite_toolkit/_cdf_tk/commands/_migrate/creators.py +1 -1
  42. cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +35 -4
  43. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +149 -14
  44. cognite_toolkit/_cdf_tk/commands/_migrate/data_model.py +1 -0
  45. cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py +1 -1
  46. cognite_toolkit/_cdf_tk/commands/_migrate/issues.py +19 -1
  47. cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +220 -3
  48. cognite_toolkit/_cdf_tk/commands/_profile.py +1 -1
  49. cognite_toolkit/_cdf_tk/commands/_purge.py +9 -11
  50. cognite_toolkit/_cdf_tk/commands/build_cmd.py +1 -1
  51. cognite_toolkit/_cdf_tk/commands/build_v2/__init__.py +0 -0
  52. cognite_toolkit/_cdf_tk/commands/build_v2/build_cmd.py +241 -0
  53. cognite_toolkit/_cdf_tk/commands/build_v2/build_input.py +85 -0
  54. cognite_toolkit/_cdf_tk/commands/build_v2/build_issues.py +27 -0
  55. cognite_toolkit/_cdf_tk/commands/dump_resource.py +4 -4
  56. cognite_toolkit/_cdf_tk/commands/run.py +1 -1
  57. cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +2 -2
  58. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +1 -1
  59. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +1 -1
  60. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py +1 -1
  61. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py +1 -1
  62. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py +1 -1
  63. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +22 -20
  64. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +1 -1
  65. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py +14 -2
  66. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/industrial_tool.py +1 -1
  67. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +1 -1
  68. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/migration.py +1 -1
  69. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/raw.py +1 -1
  70. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/robotics.py +1 -1
  71. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/transformation.py +49 -14
  72. cognite_toolkit/_cdf_tk/data_classes/_module_toml.py +1 -0
  73. cognite_toolkit/_cdf_tk/resource_classes/search_config.py +1 -1
  74. cognite_toolkit/_cdf_tk/resource_classes/workflow_version.py +164 -5
  75. cognite_toolkit/_cdf_tk/storageio/_applications.py +2 -2
  76. cognite_toolkit/_cdf_tk/storageio/_file_content.py +1 -2
  77. cognite_toolkit/_cdf_tk/storageio/_instances.py +1 -1
  78. cognite_toolkit/_cdf_tk/storageio/selectors/__init__.py +10 -1
  79. cognite_toolkit/_cdf_tk/storageio/selectors/_three_d.py +34 -0
  80. cognite_toolkit/_cdf_tk/utils/cdf.py +1 -1
  81. cognite_toolkit/_cdf_tk/utils/http_client/__init__.py +28 -0
  82. cognite_toolkit/_cdf_tk/utils/http_client/_client.py +3 -2
  83. cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py +6 -0
  84. cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py +67 -7
  85. cognite_toolkit/_cdf_tk/utils/http_client/_tracker.py +5 -2
  86. cognite_toolkit/_cdf_tk/utils/interactive_select.py +51 -4
  87. cognite_toolkit/_cdf_tk/validation.py +4 -0
  88. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  89. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  90. cognite_toolkit/_resources/cdf.toml +1 -1
  91. cognite_toolkit/_version.py +1 -1
  92. {cognite_toolkit-0.7.30.dist-info → cognite_toolkit-0.7.39.dist-info}/METADATA +1 -1
  93. {cognite_toolkit-0.7.30.dist-info → cognite_toolkit-0.7.39.dist-info}/RECORD +119 -113
  94. {cognite_toolkit-0.7.30.dist-info → cognite_toolkit-0.7.39.dist-info}/WHEEL +1 -1
  95. cognite_toolkit/_cdf_tk/client/api/robotics/__init__.py +0 -3
  96. cognite_toolkit/_cdf_tk/prototypes/import_app.py +0 -41
  97. /cognite_toolkit/_cdf_tk/{prototypes/commands → client/api/legacy}/__init__.py +0 -0
  98. /cognite_toolkit/_cdf_tk/client/api/{dml.py → legacy/dml.py} +0 -0
  99. /cognite_toolkit/_cdf_tk/client/api/{fixed_transformations.py → legacy/fixed_transformations.py} +0 -0
  100. /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/api.py +0 -0
  101. /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/utlis.py +0 -0
  102. /cognite_toolkit/_cdf_tk/client/data_classes/{apm_config_v1.py → legacy/apm_config_v1.py} +0 -0
  103. /cognite_toolkit/_cdf_tk/client/data_classes/{charts.py → legacy/charts.py} +0 -0
  104. /cognite_toolkit/_cdf_tk/client/data_classes/{extendable_cognite_file.py → legacy/extendable_cognite_file.py} +0 -0
  105. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetadata.py → legacy/extended_filemetadata.py} +0 -0
  106. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetdata.py → legacy/extended_filemetdata.py} +0 -0
  107. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_timeseries.py → legacy/extended_timeseries.py} +0 -0
  108. /cognite_toolkit/_cdf_tk/client/data_classes/{functions.py → legacy/functions.py} +0 -0
  109. /cognite_toolkit/_cdf_tk/client/data_classes/{graphql_data_models.py → legacy/graphql_data_models.py} +0 -0
  110. /cognite_toolkit/_cdf_tk/client/data_classes/{instances.py → legacy/instances.py} +0 -0
  111. /cognite_toolkit/_cdf_tk/client/data_classes/{location_filters.py → legacy/location_filters.py} +0 -0
  112. /cognite_toolkit/_cdf_tk/client/data_classes/{migration.py → legacy/migration.py} +0 -0
  113. /cognite_toolkit/_cdf_tk/client/data_classes/{pending_instances_ids.py → legacy/pending_instances_ids.py} +0 -0
  114. /cognite_toolkit/_cdf_tk/client/data_classes/{project.py → legacy/project.py} +0 -0
  115. /cognite_toolkit/_cdf_tk/client/data_classes/{raw.py → legacy/raw.py} +0 -0
  116. /cognite_toolkit/_cdf_tk/client/data_classes/{robotics.py → legacy/robotics.py} +0 -0
  117. /cognite_toolkit/_cdf_tk/client/data_classes/{search_config.py → legacy/search_config.py} +0 -0
  118. /cognite_toolkit/_cdf_tk/client/data_classes/{sequences.py → legacy/sequences.py} +0 -0
  119. /cognite_toolkit/_cdf_tk/client/data_classes/{streamlit_.py → legacy/streamlit_.py} +0 -0
  120. /cognite_toolkit/_cdf_tk/{prototypes/commands/import_.py → commands/_import_cmd.py} +0 -0
  121. {cognite_toolkit-0.7.30.dist-info → cognite_toolkit-0.7.39.dist-info}/entry_points.txt +0 -0
@@ -38,9 +38,12 @@ from ._instances import (
38
38
  SelectedView,
39
39
  )
40
40
  from ._raw import RawTableSelector, SelectedTable
41
+ from ._three_d import ThreeDModelFilteredSelector, ThreeDModelIdSelector, ThreeDSelector
41
42
 
42
43
  Selector = Annotated[
43
44
  RawTableSelector
45
+ | ThreeDModelIdSelector
46
+ | ThreeDModelFilteredSelector
44
47
  | InstanceViewSelector
45
48
  | InstanceFileSelector
46
49
  | InstanceSpaceSelector
@@ -60,7 +63,7 @@ Selector = Annotated[
60
63
  ]
61
64
 
62
65
  ALPHA_SELECTORS = {FileIdentifierSelector}
63
-
66
+ INTERNAL = {ThreeDModelIdSelector, ThreeDModelFilteredSelector}
64
67
  SelectorAdapter: TypeAdapter[Selector] = TypeAdapter(Selector)
65
68
 
66
69
 
@@ -83,6 +86,10 @@ def load_selector(manifest_file: Path) -> Selector | ToolkitWarning:
83
86
  return MediumSeverityWarning(
84
87
  f"Selector type '{type(selector).__name__}' in file '{manifest_file}' is in alpha. To enable it set the alpha flag 'extend-upload = true' in your CDF.toml file."
85
88
  )
89
+ elif type(selector) in INTERNAL:
90
+ return MediumSeverityWarning(
91
+ f"Selector type '{type(selector).__name__}' in file '{manifest_file}' is for internal use only and cannot be used."
92
+ )
86
93
  return selector
87
94
 
88
95
 
@@ -119,6 +126,8 @@ __all__ = [
119
126
  "SelectedView",
120
127
  "Selector",
121
128
  "SelectorAdapter",
129
+ "ThreeDModelIdSelector",
130
+ "ThreeDSelector",
122
131
  "TimeSeriesColumn",
123
132
  "load_selector",
124
133
  ]
@@ -0,0 +1,34 @@
1
+ import hashlib
2
+ from abc import ABC
3
+ from typing import Literal
4
+
5
+ from ._base import DataSelector
6
+
7
+
8
+ class ThreeDSelector(DataSelector, ABC):
9
+ kind: Literal["3D"] = "3D"
10
+
11
+ @property
12
+ def group(self) -> str:
13
+ return "3DModels"
14
+
15
+
16
+ class ThreeDModelFilteredSelector(ThreeDSelector):
17
+ type: Literal["3DFiltered"] = "3DFiltered"
18
+ model_type: Literal["Classic", "DataModel"] = "Classic"
19
+ published: bool | None = None
20
+
21
+ def __str__(self) -> str:
22
+ suffix = f"3DModels_{self.model_type}"
23
+ if self.published is not None:
24
+ return f"{suffix}_published_{self.published}"
25
+ return suffix
26
+
27
+
28
+ class ThreeDModelIdSelector(ThreeDSelector):
29
+ type: Literal["3DId"] = "3DId"
30
+ ids: tuple[int, ...]
31
+
32
+ def __str__(self) -> str:
33
+ hash_ = hashlib.md5(",".join(sorted(map(str, self.ids))).encode()).hexdigest()[:8]
34
+ return f"3DModels_ids_count_{len(self.ids)}_hash_{hash_}"
@@ -21,7 +21,7 @@ from filelock import BaseFileLock, FileLock, Timeout
21
21
  from rich.console import Console
22
22
 
23
23
  from cognite_toolkit._cdf_tk.client import ToolkitClient, ToolkitClientConfig
24
- from cognite_toolkit._cdf_tk.client.data_classes.raw import RawTable
24
+ from cognite_toolkit._cdf_tk.client.data_classes.legacy.raw import RawTable
25
25
  from cognite_toolkit._cdf_tk.constants import ENV_VAR_PATTERN, MAX_ROW_ITERATION_RUN_QUERY, MAX_RUN_QUERY_FREQUENCY_MIN
26
26
  from cognite_toolkit._cdf_tk.exceptions import (
27
27
  ToolkitError,
@@ -17,25 +17,53 @@ from ._data_classes import (
17
17
  SuccessResponse,
18
18
  SuccessResponseItems,
19
19
  )
20
+ from ._data_classes2 import (
21
+ BaseModelObject,
22
+ ErrorDetails2,
23
+ FailedRequest2,
24
+ FailedResponse2,
25
+ HTTPResult2,
26
+ ItemsFailedRequest2,
27
+ ItemsFailedResponse2,
28
+ ItemsRequest2,
29
+ ItemsResultMessage2,
30
+ ItemsSuccessResponse2,
31
+ RequestMessage2,
32
+ RequestResource,
33
+ SuccessResponse2,
34
+ )
20
35
  from ._exception import ToolkitAPIError
21
36
 
22
37
  __all__ = [
38
+ "BaseModelObject",
23
39
  "DataBodyRequest",
24
40
  "ErrorDetails",
41
+ "ErrorDetails2",
42
+ "FailedRequest2",
25
43
  "FailedRequestItems",
26
44
  "FailedRequestMessage",
27
45
  "FailedResponse",
46
+ "FailedResponse2",
28
47
  "FailedResponseItems",
29
48
  "HTTPClient",
30
49
  "HTTPMessage",
50
+ "HTTPResult2",
31
51
  "ItemMessage",
52
+ "ItemsFailedRequest2",
53
+ "ItemsFailedResponse2",
32
54
  "ItemsRequest",
55
+ "ItemsRequest2",
56
+ "ItemsResultMessage2",
57
+ "ItemsSuccessResponse2",
33
58
  "ParamRequest",
34
59
  "RequestMessage",
60
+ "RequestMessage2",
61
+ "RequestResource",
35
62
  "ResponseList",
36
63
  "ResponseMessage",
37
64
  "SimpleBodyRequest",
38
65
  "SuccessResponse",
66
+ "SuccessResponse2",
39
67
  "SuccessResponseItems",
40
68
  "ToolkitAPIError",
41
69
  ]
@@ -32,6 +32,7 @@ from cognite_toolkit._cdf_tk.utils.http_client._data_classes2 import (
32
32
  ItemsFailedRequest2,
33
33
  ItemsFailedResponse2,
34
34
  ItemsRequest2,
35
+ ItemsResultList,
35
36
  ItemsResultMessage2,
36
37
  ItemsSuccessResponse2,
37
38
  RequestMessage2,
@@ -424,7 +425,7 @@ class HTTPClient:
424
425
  results = self._handle_items_error(e, message)
425
426
  return results
426
427
 
427
- def request_items_retries(self, message: ItemsRequest2) -> Sequence[ItemsResultMessage2]:
428
+ def request_items_retries(self, message: ItemsRequest2) -> ItemsResultList:
428
429
  """Send an HTTP request with multiple items and handle retries.
429
430
 
430
431
  This method will keep retrying the request until it either succeeds or
@@ -442,7 +443,7 @@ class HTTPClient:
442
443
  raise RuntimeError(f"ItemsRequest2 has already been attempted {message.total_attempts} times.")
443
444
  pending_requests: deque[ItemsRequest2] = deque()
444
445
  pending_requests.append(message)
445
- final_responses: list[ItemsResultMessage2] = []
446
+ final_responses = ItemsResultList([])
446
447
  while pending_requests:
447
448
  current_request = pending_requests.popleft()
448
449
  results = self.request_items(current_request)
@@ -76,6 +76,9 @@ class ErrorDetails:
76
76
  class FailedRequestMessage(HTTPMessage):
77
77
  error: str
78
78
 
79
+ def __str__(self) -> str:
80
+ return self.error
81
+
79
82
 
80
83
  @dataclass
81
84
  class ResponseMessage(HTTPMessage):
@@ -135,6 +138,9 @@ class FailedResponse(ResponseMessage):
135
138
  output["error"] = self.error.dump()
136
139
  return output
137
140
 
141
+ def __str__(self) -> str:
142
+ return f"{self.error.code} | {self.error.message}"
143
+
138
144
 
139
145
  @dataclass
140
146
  class SimpleRequest(RequestMessage):
@@ -1,7 +1,8 @@
1
1
  import gzip
2
2
  import sys
3
3
  from abc import ABC, abstractmethod
4
- from collections.abc import Hashable
4
+ from collections import UserList
5
+ from collections.abc import Hashable, Sequence
5
6
  from typing import Any, Literal
6
7
 
7
8
  import httpx
@@ -9,6 +10,7 @@ from cognite.client import global_config
9
10
  from pydantic import BaseModel, ConfigDict, Field, JsonValue, TypeAdapter, model_validator
10
11
  from pydantic.alias_generators import to_camel
11
12
 
13
+ from cognite_toolkit._cdf_tk.utils.http_client._exception import ToolkitAPIError
12
14
  from cognite_toolkit._cdf_tk.utils.http_client._tracker import ItemsRequestTracker
13
15
  from cognite_toolkit._cdf_tk.utils.useful_types import PrimitiveType
14
16
 
@@ -18,7 +20,17 @@ else:
18
20
  from typing_extensions import Self
19
21
 
20
22
 
21
- class HTTPResult2(BaseModel): ...
23
+ class HTTPResult2(BaseModel):
24
+ def get_success_or_raise(self) -> "SuccessResponse2":
25
+ """Raises an exception if any response in the list indicates a failure."""
26
+ if isinstance(self, SuccessResponse2):
27
+ return self
28
+ elif isinstance(self, FailedResponse2):
29
+ raise ToolkitAPIError(f"Request failed with status code {self.status_code}: {self.error.message}")
30
+ elif isinstance(self, FailedRequest2):
31
+ raise ToolkitAPIError(f"Request failed with error: {self.error}")
32
+ else:
33
+ raise ToolkitAPIError("Unknown HTTPResult2 type")
22
34
 
23
35
 
24
36
  class FailedRequest2(HTTPResult2):
@@ -30,6 +42,11 @@ class SuccessResponse2(HTTPResult2):
30
42
  body: str
31
43
  content: bytes
32
44
 
45
+ @property
46
+ def body_json(self) -> dict[str, Any]:
47
+ """Parse the response body as JSON."""
48
+ return TypeAdapter(dict[str, JsonValue]).validate_json(self.body)
49
+
33
50
 
34
51
  class ErrorDetails2(BaseModel):
35
52
  """This is the expected structure of error details in the CDF API"""
@@ -98,8 +115,8 @@ class RequestMessage2(BaseRequestMessage):
98
115
  # We serialize using pydantic instead of json.dumps. This is because pydantic is faster
99
116
  # and handles more complex types such as datetime, float('nan'), etc.
100
117
  data = _BODY_SERIALIZER.dump_json(self.body_content)
101
- if not global_config.disable_gzip and isinstance(data, str):
102
- data = gzip.compress(data.encode("utf-8"))
118
+ if not global_config.disable_gzip and isinstance(data, bytes):
119
+ data = gzip.compress(data)
103
120
  return data
104
121
 
105
122
 
@@ -158,7 +175,7 @@ def _set_default_tracker(data: dict[str, Any]) -> ItemsRequestTracker:
158
175
 
159
176
  class ItemsRequest2(BaseRequestMessage):
160
177
  model_config = ConfigDict(arbitrary_types_allowed=True)
161
- items: list[RequestResource]
178
+ items: Sequence[RequestResource]
162
179
  extra_body_fields: dict[str, JsonValue] | None = None
163
180
  max_failures_before_abort: int = 50
164
181
  tracker: ItemsRequestTracker = Field(init=False, default_factory=_set_default_tracker, exclude=True)
@@ -169,8 +186,8 @@ class ItemsRequest2(BaseRequestMessage):
169
186
  if self.extra_body_fields:
170
187
  body.update(self.extra_body_fields)
171
188
  res = _BODY_SERIALIZER.dump_json(body)
172
- if not global_config.disable_gzip and isinstance(res, str):
173
- return gzip.compress(res.encode("utf-8"))
189
+ if not global_config.disable_gzip and isinstance(res, bytes):
190
+ return gzip.compress(res)
174
191
  return res
175
192
 
176
193
  def split(self, status_attempts: int) -> list["ItemsRequest2"]:
@@ -185,3 +202,46 @@ class ItemsRequest2(BaseRequestMessage):
185
202
  new_request.tracker = self.tracker
186
203
  messages.append(new_request)
187
204
  return messages
205
+
206
+
207
+ class ItemResponse(BaseModel):
208
+ items: list[dict[str, JsonValue]]
209
+
210
+
211
+ class ItemsResultList(UserList[ItemsResultMessage2]):
212
+ def __init__(self, collection: Sequence[ItemsResultMessage2] | None = None) -> None:
213
+ super().__init__(collection or [])
214
+
215
+ def raise_for_status(self) -> None:
216
+ """Raises an exception if any response in the list indicates a failure."""
217
+ failed_responses = [resp for resp in self.data if isinstance(resp, ItemsFailedResponse2)]
218
+ failed_requests = [resp for resp in self.data if isinstance(resp, ItemsFailedRequest2)]
219
+ if not failed_responses and not failed_requests:
220
+ return
221
+ error_messages = "; ".join(f"Status {err.status_code}: {err.error.message}" for err in failed_responses)
222
+ if failed_requests:
223
+ if error_messages:
224
+ error_messages += "; "
225
+ error_messages += "; ".join(f"Request error: {err.error_message}" for err in failed_requests)
226
+ raise ToolkitAPIError(f"One or more requests failed: {error_messages}")
227
+
228
+ @property
229
+ def has_failed(self) -> bool:
230
+ """Indicates whether any response in the list indicates a failure.
231
+
232
+ Returns:
233
+ bool: True if there are any failed responses or requests, False otherwise.
234
+ """
235
+ for resp in self.data:
236
+ if isinstance(resp, ItemsFailedResponse2 | ItemsFailedRequest2):
237
+ return True
238
+ return False
239
+
240
+ def get_items(self) -> list[dict[str, JsonValue]]:
241
+ """Get the items from all successful responses."""
242
+ items: list[dict[str, JsonValue]] = []
243
+ for resp in self.data:
244
+ if isinstance(resp, ItemsSuccessResponse2):
245
+ body_json = ItemResponse.model_validate_json(resp.body)
246
+ items.extend(body_json.items)
247
+ return items
@@ -1,5 +1,6 @@
1
1
  import threading
2
2
  from dataclasses import dataclass, field
3
+ from typing import Any
3
4
 
4
5
 
5
6
  @dataclass
@@ -9,13 +10,15 @@ class ItemsRequestTracker:
9
10
  Attributes:
10
11
  max_failures_before_abort (int): Maximum number of allowed failed split requests before aborting
11
12
  the entire operation. A value of -1 indicates no early abort.
12
- lock (threading.Lock): A lock to ensure thread-safe updates to the failure count.
13
+ lock (Any): A lock to ensure thread-safe updates to the failure count.
13
14
  failed_split_count (int): The current count of failed split requests.
14
15
 
15
16
  """
16
17
 
17
18
  max_failures_before_abort: int = -1 # -1 means no early abort
18
- lock: threading.Lock = field(default_factory=threading.Lock, init=False)
19
+ # NOTE: `threading.Lock` is a factory function (backed by `_thread.allocate_lock`), not a type.
20
+ # Annotate as `Any` so Pydantic won't attempt to generate a schema for the lock field.
21
+ lock: Any = field(default_factory=threading.Lock, init=False)
19
22
  failed_split_count: int = field(default=0, init=False)
20
23
 
21
24
  def register_failure(self) -> None:
@@ -21,10 +21,11 @@ from questionary import Choice
21
21
  from rich.console import Console
22
22
 
23
23
  from cognite_toolkit._cdf_tk.client import ToolkitClient
24
- from cognite_toolkit._cdf_tk.client.data_classes.canvas import Canvas
25
- from cognite_toolkit._cdf_tk.client.data_classes.charts import Chart, ChartList, Visibility
26
- from cognite_toolkit._cdf_tk.client.data_classes.migration import ResourceViewMapping
27
- from cognite_toolkit._cdf_tk.client.data_classes.raw import RawTable
24
+ from cognite_toolkit._cdf_tk.client.data_classes.legacy.canvas import Canvas
25
+ from cognite_toolkit._cdf_tk.client.data_classes.legacy.charts import Chart, ChartList, Visibility
26
+ from cognite_toolkit._cdf_tk.client.data_classes.legacy.migration import ResourceViewMapping
27
+ from cognite_toolkit._cdf_tk.client.data_classes.legacy.raw import RawTable
28
+ from cognite_toolkit._cdf_tk.client.data_classes.three_d import ThreeDModelResponse
28
29
  from cognite_toolkit._cdf_tk.exceptions import ToolkitMissingResourceError, ToolkitValueError
29
30
 
30
31
  from . import humanize_collection
@@ -823,3 +824,49 @@ class ResourceViewMappingInteractiveSelect:
823
824
  f"Selected Resource View Mapping is not a valid ResourceViewMapping object: {selected_mapping!r}"
824
825
  )
825
826
  return selected_mapping
827
+
828
+
829
+ class ThreeDInteractiveSelect:
830
+ def __init__(self, client: ToolkitClient, operation: str) -> None:
831
+ self.client = client
832
+ self.operation = operation
833
+
834
+ def select_three_d_models(self, model_type: Literal["classic", "dm"] | None = None) -> list[ThreeDModelResponse]:
835
+ """Select multiple 3D models interactively."""
836
+ if model_type is None:
837
+ model_type = questionary.select(
838
+ f"What type of 3D models do you want to {self.operation}?",
839
+ choices=[
840
+ Choice(title="Classic models", value="classic"),
841
+ Choice(title="Data modeling 3D", value="dm"),
842
+ ],
843
+ ).ask()
844
+ if model_type is None:
845
+ raise ToolkitValueError("No 3D model type selected.")
846
+ published = questionary.select(
847
+ f"Do you want to {self.operation} published or unpublished 3D models?",
848
+ choices=[
849
+ Choice(title="Published models", value=True),
850
+ Choice(title="Unpublished models", value=False),
851
+ Choice(title="Both published and unpublished models", value=None),
852
+ ],
853
+ ).ask()
854
+
855
+ models = self.client.tool.three_d.models.list(published=published, include_revision_info=True, limit=None)
856
+ if model_type == "classic":
857
+ models = [model for model in models if model.space is None]
858
+ else:
859
+ models = [model for model in models if model.space is not None]
860
+ if not models:
861
+ raise ToolkitMissingResourceError(
862
+ f"No 3D models found for type {model_type!r} with published={published!r}."
863
+ )
864
+
865
+ choices = [Choice(title=f"{model.name} ({model.id})", value=model) for model in models]
866
+ selected_models = questionary.checkbox(
867
+ f"Select 3D models to {self.operation}:",
868
+ choices=choices,
869
+ ).ask()
870
+ if selected_models is None or len(selected_models) == 0:
871
+ raise ToolkitValueError("No 3D models selected.")
872
+ return selected_models
@@ -182,6 +182,10 @@ def humanize_validation_error(error: ValidationError) -> list[str]:
182
182
  "dict_type",
183
183
  }:
184
184
  msg = f"{item['msg']}. Got {item['input']!r} of type {type(item['input']).__name__}."
185
+ elif error_type == "union_tag_not_found" and "ctx" in item and "discriminator" in item["ctx"]:
186
+ # This is when we use a discriminator field to determine the type in a union. For the user, this means they
187
+ # are missing a required field.
188
+ msg = f"Missing required field: {item['ctx']['discriminator']}"
185
189
  else:
186
190
  # Default to the Pydantic error message
187
191
  msg = item["msg"]
@@ -12,7 +12,7 @@ jobs:
12
12
  environment: dev
13
13
  name: Deploy
14
14
  container:
15
- image: cognite/toolkit:0.7.30
15
+ image: cognite/toolkit:0.7.39
16
16
  env:
17
17
  CDF_CLUSTER: ${{ vars.CDF_CLUSTER }}
18
18
  CDF_PROJECT: ${{ vars.CDF_PROJECT }}
@@ -10,7 +10,7 @@ jobs:
10
10
  environment: dev
11
11
  name: Deploy Dry Run
12
12
  container:
13
- image: cognite/toolkit:0.7.30
13
+ image: cognite/toolkit:0.7.39
14
14
  env:
15
15
  CDF_CLUSTER: ${{ vars.CDF_CLUSTER }}
16
16
  CDF_PROJECT: ${{ vars.CDF_PROJECT }}
@@ -4,7 +4,7 @@ default_env = "<DEFAULT_ENV_PLACEHOLDER>"
4
4
  [modules]
5
5
  # This is the version of the modules. It should not be changed manually.
6
6
  # It will be updated by the 'cdf modules upgrade' command.
7
- version = "0.7.30"
7
+ version = "0.7.39"
8
8
 
9
9
 
10
10
  [plugins]
@@ -1 +1 @@
1
- __version__ = "0.7.30"
1
+ __version__ = "0.7.39"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite_toolkit
3
- Version: 0.7.30
3
+ Version: 0.7.39
4
4
  Summary: Official Cognite Data Fusion tool for project templates and configuration deployment
5
5
  Author: Cognite AS
6
6
  Author-email: Cognite AS <support@cognite.com>