cognite-toolkit 0.6.97__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 (198) hide show
  1. cognite_toolkit/_cdf.py +21 -23
  2. cognite_toolkit/_cdf_tk/apps/__init__.py +4 -0
  3. cognite_toolkit/_cdf_tk/apps/_core_app.py +19 -5
  4. cognite_toolkit/_cdf_tk/apps/_data_app.py +1 -1
  5. cognite_toolkit/_cdf_tk/apps/_dev_app.py +86 -0
  6. cognite_toolkit/_cdf_tk/apps/_download_app.py +693 -25
  7. cognite_toolkit/_cdf_tk/apps/_dump_app.py +44 -102
  8. cognite_toolkit/_cdf_tk/apps/_import_app.py +41 -0
  9. cognite_toolkit/_cdf_tk/apps/_landing_app.py +18 -4
  10. cognite_toolkit/_cdf_tk/apps/_migrate_app.py +424 -9
  11. cognite_toolkit/_cdf_tk/apps/_modules_app.py +0 -3
  12. cognite_toolkit/_cdf_tk/apps/_purge.py +15 -43
  13. cognite_toolkit/_cdf_tk/apps/_run.py +11 -0
  14. cognite_toolkit/_cdf_tk/apps/_upload_app.py +45 -6
  15. cognite_toolkit/_cdf_tk/builders/__init__.py +2 -2
  16. cognite_toolkit/_cdf_tk/builders/_base.py +28 -42
  17. cognite_toolkit/_cdf_tk/builders/_raw.py +1 -1
  18. cognite_toolkit/_cdf_tk/cdf_toml.py +20 -1
  19. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +32 -12
  20. cognite_toolkit/_cdf_tk/client/api/infield.py +114 -17
  21. cognite_toolkit/_cdf_tk/client/api/{canvas.py → legacy/canvas.py} +15 -7
  22. cognite_toolkit/_cdf_tk/client/api/{charts.py → legacy/charts.py} +1 -1
  23. cognite_toolkit/_cdf_tk/client/api/{extended_data_modeling.py → legacy/extended_data_modeling.py} +1 -1
  24. cognite_toolkit/_cdf_tk/client/api/{extended_files.py → legacy/extended_files.py} +2 -2
  25. cognite_toolkit/_cdf_tk/client/api/{extended_functions.py → legacy/extended_functions.py} +15 -18
  26. cognite_toolkit/_cdf_tk/client/api/{extended_raw.py → legacy/extended_raw.py} +1 -1
  27. cognite_toolkit/_cdf_tk/client/api/{extended_timeseries.py → legacy/extended_timeseries.py} +5 -2
  28. cognite_toolkit/_cdf_tk/client/api/{location_filters.py → legacy/location_filters.py} +1 -1
  29. cognite_toolkit/_cdf_tk/client/api/legacy/robotics/__init__.py +8 -0
  30. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/capabilities.py +1 -1
  31. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/data_postprocessing.py +1 -1
  32. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/frames.py +1 -1
  33. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/locations.py +1 -1
  34. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/maps.py +1 -1
  35. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/robots.py +2 -2
  36. cognite_toolkit/_cdf_tk/client/api/{search_config.py → legacy/search_config.py} +5 -1
  37. cognite_toolkit/_cdf_tk/client/api/migration.py +177 -4
  38. cognite_toolkit/_cdf_tk/client/api/project.py +9 -8
  39. cognite_toolkit/_cdf_tk/client/api/search.py +2 -2
  40. cognite_toolkit/_cdf_tk/client/api/streams.py +88 -0
  41. cognite_toolkit/_cdf_tk/client/api/three_d.py +384 -0
  42. cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py +13 -0
  43. cognite_toolkit/_cdf_tk/client/data_classes/base.py +37 -33
  44. cognite_toolkit/_cdf_tk/client/data_classes/charts_data.py +95 -213
  45. cognite_toolkit/_cdf_tk/client/data_classes/infield.py +32 -18
  46. cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +18 -13
  47. cognite_toolkit/_cdf_tk/client/data_classes/legacy/__init__.py +0 -0
  48. cognite_toolkit/_cdf_tk/client/data_classes/{canvas.py → legacy/canvas.py} +47 -4
  49. cognite_toolkit/_cdf_tk/client/data_classes/{charts.py → legacy/charts.py} +3 -3
  50. cognite_toolkit/_cdf_tk/client/data_classes/{migration.py → legacy/migration.py} +10 -2
  51. cognite_toolkit/_cdf_tk/client/data_classes/streams.py +90 -0
  52. cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +112 -0
  53. cognite_toolkit/_cdf_tk/client/testing.py +42 -18
  54. cognite_toolkit/_cdf_tk/commands/__init__.py +7 -6
  55. cognite_toolkit/_cdf_tk/commands/_changes.py +3 -42
  56. cognite_toolkit/_cdf_tk/commands/_download.py +21 -11
  57. cognite_toolkit/_cdf_tk/commands/_migrate/__init__.py +0 -2
  58. cognite_toolkit/_cdf_tk/commands/_migrate/command.py +22 -20
  59. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +140 -92
  60. cognite_toolkit/_cdf_tk/commands/_migrate/creators.py +1 -1
  61. cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +108 -26
  62. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +448 -45
  63. cognite_toolkit/_cdf_tk/commands/_migrate/data_model.py +1 -0
  64. cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py +6 -6
  65. cognite_toolkit/_cdf_tk/commands/_migrate/issues.py +52 -1
  66. cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +377 -11
  67. cognite_toolkit/_cdf_tk/commands/_migrate/selectors.py +9 -4
  68. cognite_toolkit/_cdf_tk/commands/_profile.py +1 -1
  69. cognite_toolkit/_cdf_tk/commands/_purge.py +36 -39
  70. cognite_toolkit/_cdf_tk/commands/_questionary_style.py +16 -0
  71. cognite_toolkit/_cdf_tk/commands/_upload.py +109 -86
  72. cognite_toolkit/_cdf_tk/commands/about.py +221 -0
  73. cognite_toolkit/_cdf_tk/commands/auth.py +19 -12
  74. cognite_toolkit/_cdf_tk/commands/build_cmd.py +16 -62
  75. cognite_toolkit/_cdf_tk/commands/build_v2/__init__.py +0 -0
  76. cognite_toolkit/_cdf_tk/commands/build_v2/build_cmd.py +241 -0
  77. cognite_toolkit/_cdf_tk/commands/build_v2/build_input.py +85 -0
  78. cognite_toolkit/_cdf_tk/commands/build_v2/build_issues.py +27 -0
  79. cognite_toolkit/_cdf_tk/commands/clean.py +63 -16
  80. cognite_toolkit/_cdf_tk/commands/deploy.py +20 -17
  81. cognite_toolkit/_cdf_tk/commands/dump_resource.py +10 -8
  82. cognite_toolkit/_cdf_tk/commands/init.py +225 -3
  83. cognite_toolkit/_cdf_tk/commands/modules.py +20 -44
  84. cognite_toolkit/_cdf_tk/commands/pull.py +6 -19
  85. cognite_toolkit/_cdf_tk/commands/resources.py +179 -0
  86. cognite_toolkit/_cdf_tk/commands/run.py +1 -1
  87. cognite_toolkit/_cdf_tk/constants.py +20 -1
  88. cognite_toolkit/_cdf_tk/cruds/__init__.py +19 -5
  89. cognite_toolkit/_cdf_tk/cruds/_base_cruds.py +14 -70
  90. cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +10 -19
  91. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/__init__.py +4 -1
  92. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/agent.py +11 -9
  93. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +5 -15
  94. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +45 -44
  95. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py +5 -12
  96. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/data_organization.py +4 -13
  97. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py +206 -67
  98. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py +6 -18
  99. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +126 -35
  100. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +7 -28
  101. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py +23 -30
  102. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/hosted_extractors.py +12 -30
  103. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/industrial_tool.py +4 -8
  104. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +4 -16
  105. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/migration.py +5 -13
  106. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/raw.py +5 -11
  107. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +3 -8
  108. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/robotics.py +16 -45
  109. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +94 -0
  110. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/three_d_model.py +3 -7
  111. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +5 -15
  112. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/transformation.py +75 -32
  113. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/workflow.py +20 -40
  114. cognite_toolkit/_cdf_tk/cruds/_worker.py +24 -36
  115. cognite_toolkit/_cdf_tk/data_classes/_module_toml.py +1 -0
  116. cognite_toolkit/_cdf_tk/feature_flags.py +16 -36
  117. cognite_toolkit/_cdf_tk/plugins.py +2 -1
  118. cognite_toolkit/_cdf_tk/resource_classes/__init__.py +4 -0
  119. cognite_toolkit/_cdf_tk/resource_classes/capabilities.py +12 -0
  120. cognite_toolkit/_cdf_tk/resource_classes/functions.py +3 -1
  121. cognite_toolkit/_cdf_tk/resource_classes/infield_cdm_location_config.py +109 -0
  122. cognite_toolkit/_cdf_tk/resource_classes/migration.py +8 -17
  123. cognite_toolkit/_cdf_tk/resource_classes/search_config.py +1 -1
  124. cognite_toolkit/_cdf_tk/resource_classes/streams.py +29 -0
  125. cognite_toolkit/_cdf_tk/resource_classes/workflow_version.py +164 -5
  126. cognite_toolkit/_cdf_tk/storageio/__init__.py +9 -21
  127. cognite_toolkit/_cdf_tk/storageio/_annotations.py +19 -16
  128. cognite_toolkit/_cdf_tk/storageio/_applications.py +340 -28
  129. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +67 -104
  130. cognite_toolkit/_cdf_tk/storageio/_base.py +61 -29
  131. cognite_toolkit/_cdf_tk/storageio/_datapoints.py +276 -20
  132. cognite_toolkit/_cdf_tk/storageio/_file_content.py +435 -0
  133. cognite_toolkit/_cdf_tk/storageio/_instances.py +35 -3
  134. cognite_toolkit/_cdf_tk/storageio/_raw.py +26 -0
  135. cognite_toolkit/_cdf_tk/storageio/selectors/__init__.py +71 -4
  136. cognite_toolkit/_cdf_tk/storageio/selectors/_base.py +14 -2
  137. cognite_toolkit/_cdf_tk/storageio/selectors/_canvas.py +14 -0
  138. cognite_toolkit/_cdf_tk/storageio/selectors/_charts.py +14 -0
  139. cognite_toolkit/_cdf_tk/storageio/selectors/_datapoints.py +23 -3
  140. cognite_toolkit/_cdf_tk/storageio/selectors/_file_content.py +164 -0
  141. cognite_toolkit/_cdf_tk/storageio/selectors/_three_d.py +34 -0
  142. cognite_toolkit/_cdf_tk/tk_warnings/other.py +4 -0
  143. cognite_toolkit/_cdf_tk/tracker.py +2 -2
  144. cognite_toolkit/_cdf_tk/utils/cdf.py +1 -1
  145. cognite_toolkit/_cdf_tk/utils/dtype_conversion.py +9 -3
  146. cognite_toolkit/_cdf_tk/utils/fileio/__init__.py +2 -0
  147. cognite_toolkit/_cdf_tk/utils/fileio/_base.py +5 -1
  148. cognite_toolkit/_cdf_tk/utils/fileio/_readers.py +112 -20
  149. cognite_toolkit/_cdf_tk/utils/fileio/_writers.py +15 -15
  150. cognite_toolkit/_cdf_tk/utils/http_client/__init__.py +28 -0
  151. cognite_toolkit/_cdf_tk/utils/http_client/_client.py +285 -18
  152. cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py +56 -4
  153. cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py +247 -0
  154. cognite_toolkit/_cdf_tk/utils/http_client/_tracker.py +5 -2
  155. cognite_toolkit/_cdf_tk/utils/interactive_select.py +60 -18
  156. cognite_toolkit/_cdf_tk/utils/sql_parser.py +2 -3
  157. cognite_toolkit/_cdf_tk/utils/useful_types.py +6 -2
  158. cognite_toolkit/_cdf_tk/validation.py +83 -1
  159. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  160. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  161. cognite_toolkit/_resources/cdf.toml +5 -4
  162. cognite_toolkit/_version.py +1 -1
  163. cognite_toolkit/config.dev.yaml +13 -0
  164. {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.39.dist-info}/METADATA +24 -24
  165. cognite_toolkit-0.7.39.dist-info/RECORD +322 -0
  166. cognite_toolkit-0.7.39.dist-info/WHEEL +4 -0
  167. {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.39.dist-info}/entry_points.txt +1 -0
  168. cognite_toolkit/_cdf_tk/client/api/robotics/__init__.py +0 -3
  169. cognite_toolkit/_cdf_tk/commands/_migrate/canvas.py +0 -201
  170. cognite_toolkit/_cdf_tk/commands/dump_data.py +0 -489
  171. cognite_toolkit/_cdf_tk/commands/featureflag.py +0 -27
  172. cognite_toolkit/_cdf_tk/prototypes/import_app.py +0 -41
  173. cognite_toolkit/_cdf_tk/utils/table_writers.py +0 -434
  174. cognite_toolkit-0.6.97.dist-info/RECORD +0 -306
  175. cognite_toolkit-0.6.97.dist-info/WHEEL +0 -4
  176. cognite_toolkit-0.6.97.dist-info/licenses/LICENSE +0 -18
  177. /cognite_toolkit/_cdf_tk/{prototypes/commands → client/api/legacy}/__init__.py +0 -0
  178. /cognite_toolkit/_cdf_tk/client/api/{dml.py → legacy/dml.py} +0 -0
  179. /cognite_toolkit/_cdf_tk/client/api/{fixed_transformations.py → legacy/fixed_transformations.py} +0 -0
  180. /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/api.py +0 -0
  181. /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/utlis.py +0 -0
  182. /cognite_toolkit/_cdf_tk/client/data_classes/{apm_config_v1.py → legacy/apm_config_v1.py} +0 -0
  183. /cognite_toolkit/_cdf_tk/client/data_classes/{extendable_cognite_file.py → legacy/extendable_cognite_file.py} +0 -0
  184. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetadata.py → legacy/extended_filemetadata.py} +0 -0
  185. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetdata.py → legacy/extended_filemetdata.py} +0 -0
  186. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_timeseries.py → legacy/extended_timeseries.py} +0 -0
  187. /cognite_toolkit/_cdf_tk/client/data_classes/{functions.py → legacy/functions.py} +0 -0
  188. /cognite_toolkit/_cdf_tk/client/data_classes/{graphql_data_models.py → legacy/graphql_data_models.py} +0 -0
  189. /cognite_toolkit/_cdf_tk/client/data_classes/{instances.py → legacy/instances.py} +0 -0
  190. /cognite_toolkit/_cdf_tk/client/data_classes/{location_filters.py → legacy/location_filters.py} +0 -0
  191. /cognite_toolkit/_cdf_tk/client/data_classes/{pending_instances_ids.py → legacy/pending_instances_ids.py} +0 -0
  192. /cognite_toolkit/_cdf_tk/client/data_classes/{project.py → legacy/project.py} +0 -0
  193. /cognite_toolkit/_cdf_tk/client/data_classes/{raw.py → legacy/raw.py} +0 -0
  194. /cognite_toolkit/_cdf_tk/client/data_classes/{robotics.py → legacy/robotics.py} +0 -0
  195. /cognite_toolkit/_cdf_tk/client/data_classes/{search_config.py → legacy/search_config.py} +0 -0
  196. /cognite_toolkit/_cdf_tk/client/data_classes/{sequences.py → legacy/sequences.py} +0 -0
  197. /cognite_toolkit/_cdf_tk/client/data_classes/{streamlit_.py → legacy/streamlit_.py} +0 -0
  198. /cognite_toolkit/_cdf_tk/{prototypes/commands/import_.py → commands/_import_cmd.py} +0 -0
@@ -0,0 +1,384 @@
1
+ from collections import defaultdict
2
+ from collections.abc import Iterable, Sequence
3
+ from typing import Any, TypeVar
4
+
5
+ from pydantic import TypeAdapter
6
+ from rich.console import Console
7
+
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
17
+ from cognite_toolkit._cdf_tk.utils.http_client import (
18
+ HTTPClient,
19
+ ItemsRequest2,
20
+ RequestMessage2,
21
+ )
22
+ from cognite_toolkit._cdf_tk.utils.useful_types import PrimitiveType
23
+
24
+
25
+ class ThreeDModelAPI:
26
+ ENDPOINT = "/3d/models"
27
+ MAX_CLASSIC_MODELS_PER_CREATE_REQUEST = 1000
28
+ MAX_MODELS_PER_DELETE_REQUEST = 1000
29
+ _LIST_REQUEST_MAX_LIMIT = 1000
30
+
31
+ def __init__(self, http_client: HTTPClient, console: Console) -> None:
32
+ self._http_client = http_client
33
+ self._console = console
34
+ self._config = http_client.config
35
+
36
+ def create(self, models: Sequence[ThreeDModelClassicRequest]) -> list[ThreeDModelResponse]:
37
+ """Create 3D models in classic format.
38
+
39
+ Args:
40
+ models (Sequence[ThreeDModelClassicRequest]): The 3D model(s) to create.
41
+
42
+ Returns:
43
+ list[ThreeDModelResponse]: The created 3D model(s).
44
+ """
45
+ if not models:
46
+ return []
47
+ if len(models) > self.MAX_CLASSIC_MODELS_PER_CREATE_REQUEST:
48
+ raise ValueError("Cannot create more than 1000 3D models in a single request.")
49
+ responses = self._http_client.request_items_retries(
50
+ ItemsRequest2(
51
+ endpoint_url=self._config.create_api_url(self.ENDPOINT),
52
+ method="POST",
53
+ items=models,
54
+ )
55
+ )
56
+ responses.raise_for_status()
57
+ return TypeAdapter(list[ThreeDModelResponse]).validate_python(responses.get_items())
58
+
59
+ def delete(self, ids: Sequence[int]) -> None:
60
+ """Delete 3D models by their IDs.
61
+
62
+ Args:
63
+ ids (Sequence[int]): The IDs of the 3D models to delete.
64
+ """
65
+ if not ids:
66
+ return None
67
+ if len(ids) > self.MAX_MODELS_PER_DELETE_REQUEST:
68
+ raise ValueError("Cannot delete more than 1000 3D models in a single request.")
69
+ responses = self._http_client.request_items_retries(
70
+ ItemsRequest2(
71
+ endpoint_url=self._config.create_api_url(self.ENDPOINT + "/delete"),
72
+ method="POST",
73
+ items=InternalIdRequest.from_ids(list(ids)),
74
+ )
75
+ )
76
+ responses.raise_for_status()
77
+
78
+ def iterate(
79
+ self,
80
+ published: bool | None = None,
81
+ include_revision_info: bool = False,
82
+ limit: int = 100,
83
+ cursor: str | None = None,
84
+ ) -> PagedResponse[ThreeDModelResponse]:
85
+ if not (0 < limit <= self._LIST_REQUEST_MAX_LIMIT):
86
+ raise ValueError(f"Limit must be between 1 and {self._LIST_REQUEST_MAX_LIMIT}, got {limit}.")
87
+ parameters: dict[str, PrimitiveType] = {
88
+ # There is a bug in the API. The parameter includeRevisionInfo is expected to be lower case and not
89
+ # camel case as documented. You get error message: Unrecognized query parameter includeRevisionInfo,
90
+ # did you mean includerevisioninfo?
91
+ "includerevisioninfo": include_revision_info,
92
+ "limit": limit,
93
+ }
94
+ if published is not None:
95
+ parameters["published"] = published
96
+ if cursor is not None:
97
+ parameters["cursor"] = cursor
98
+ responses = self._http_client.request_single_retries(
99
+ RequestMessage2(
100
+ endpoint_url=self._config.create_api_url(self.ENDPOINT),
101
+ method="GET",
102
+ parameters=parameters,
103
+ )
104
+ )
105
+ success_response = responses.get_success_or_raise()
106
+ return PagedResponse[ThreeDModelResponse].model_validate(success_response.body_json)
107
+
108
+ def list(
109
+ self,
110
+ published: bool | None = None,
111
+ include_revision_info: bool = False,
112
+ limit: int | None = 100,
113
+ cursor: str | None = None,
114
+ ) -> list[ThreeDModelResponse]:
115
+ results: list[ThreeDModelResponse] = []
116
+ while True:
117
+ request_limit = (
118
+ self._LIST_REQUEST_MAX_LIMIT
119
+ if limit is None
120
+ else min(limit - len(results), self._LIST_REQUEST_MAX_LIMIT)
121
+ )
122
+ if request_limit <= 0:
123
+ break
124
+ page = self.iterate(
125
+ published=published,
126
+ include_revision_info=include_revision_info,
127
+ limit=request_limit,
128
+ cursor=cursor,
129
+ )
130
+ results.extend(page.items)
131
+ if page.next_cursor is None:
132
+ break
133
+ cursor = page.next_cursor
134
+ return results
135
+
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
+
381
+ class ThreeDAPI:
382
+ def __init__(self, http_client: HTTPClient, console: Console) -> None:
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]
@@ -1,38 +1,22 @@
1
1
  import sys
2
2
  from abc import ABC, abstractmethod
3
- from typing import Any, Generic, TypeVar
3
+ from collections import UserList
4
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar
4
5
 
5
- from pydantic import BaseModel, ConfigDict
6
+ from pydantic import ConfigDict
6
7
  from pydantic.alias_generators import to_camel
7
8
 
9
+ from cognite_toolkit._cdf_tk.utils.http_client._data_classes2 import BaseModelObject, RequestResource
10
+
11
+ if TYPE_CHECKING:
12
+ from cognite.client import CogniteClient
13
+
8
14
  if sys.version_info >= (3, 11):
9
15
  from typing import Self
10
16
  else:
11
17
  from typing_extensions import Self
12
18
 
13
19
 
14
- class BaseModelObject(BaseModel):
15
- """Base class for all object. This includes resources and nested objects."""
16
-
17
- # We allow extra fields to support forward compatibility.
18
- model_config = ConfigDict(alias_generator=to_camel, extra="allow")
19
-
20
- def dump(self, camel_case: bool = True) -> dict[str, Any]:
21
- """Dump the resource to a dictionary.
22
-
23
- This is the default serialization method for request resources.
24
- """
25
- return self.model_dump(mode="json", by_alias=camel_case)
26
-
27
- @classmethod
28
- def _load(cls, resource: dict[str, Any]) -> "Self":
29
- """Load method to match CogniteResource signature."""
30
- return cls.model_validate(resource)
31
-
32
-
33
- class RequestResource(BaseModelObject): ...
34
-
35
-
36
20
  T_RequestResource = TypeVar("T_RequestResource", bound=RequestResource)
37
21
 
38
22
 
@@ -42,22 +26,42 @@ class ResponseResource(BaseModelObject, Generic[T_RequestResource], ABC):
42
26
  """Convert the response resource to a request resource."""
43
27
  ...
44
28
 
29
+ def as_write(self) -> T_RequestResource:
30
+ """Alias for as_request_resource to match protocol signature."""
31
+ return self.as_request_resource()
45
32
 
46
- class Identifier(BaseModel):
33
+
34
+ class Identifier(RequestResource, ABC):
47
35
  """Base class for all identifier classes."""
48
36
 
49
37
  model_config = ConfigDict(alias_generator=to_camel, extra="ignore", populate_by_name=True, frozen=True)
50
38
 
51
- def dump(self, include_type: bool = True) -> dict[str, Any]:
52
- """Dump the identifier to a dictionary.
53
-
54
- Args:
55
- include_type (bool): Whether to include the type of the identifier in the output.
39
+ def dump(self, camel_case: bool = True, include_type: bool = True) -> dict[str, Any]:
40
+ """Dump the resource to a dictionary.
56
41
 
57
- Returns:
58
- dict[str, Any]: The dumped identifier.
42
+ This is the default serialization method for request resources.
59
43
  """
60
- return self.model_dump(mode="json", by_alias=True, exclude_defaults=not include_type)
44
+ return self.model_dump(mode="json", by_alias=camel_case, exclude_unset=not include_type)
61
45
 
62
46
  def as_id(self) -> Self:
63
47
  return self
48
+
49
+
50
+ T_Resource = TypeVar("T_Resource", bound=RequestResource | ResponseResource)
51
+
52
+
53
+ class BaseResourceList(UserList[T_Resource]):
54
+ """Base class for resource lists."""
55
+
56
+ _RESOURCE: type[T_Resource]
57
+
58
+ def __init__(self, initlist: list[T_Resource] | None = None, **_: Any) -> None:
59
+ super().__init__(initlist or [])
60
+
61
+ def dump(self, camel_case: bool = True) -> list[dict[str, Any]]:
62
+ return [item.dump(camel_case) for item in self.data]
63
+
64
+ @classmethod
65
+ def load(cls, data: list[dict[str, Any]], cognite_client: "CogniteClient | None" = None) -> Self:
66
+ items = [cls._RESOURCE.model_validate(item) for item in data]
67
+ return cls(items) # type: ignore[arg-type]