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.
- cognite_toolkit/_cdf.py +5 -6
- cognite_toolkit/_cdf_tk/apps/__init__.py +2 -0
- cognite_toolkit/_cdf_tk/apps/_core_app.py +7 -1
- cognite_toolkit/_cdf_tk/apps/_download_app.py +2 -2
- cognite_toolkit/_cdf_tk/apps/_dump_app.py +1 -1
- cognite_toolkit/_cdf_tk/apps/_import_app.py +41 -0
- cognite_toolkit/_cdf_tk/apps/_migrate_app.py +177 -2
- cognite_toolkit/_cdf_tk/builders/_raw.py +1 -1
- cognite_toolkit/_cdf_tk/client/_toolkit_client.py +9 -9
- cognite_toolkit/_cdf_tk/client/api/infield.py +38 -33
- cognite_toolkit/_cdf_tk/client/api/{canvas.py → legacy/canvas.py} +15 -7
- cognite_toolkit/_cdf_tk/client/api/{charts.py → legacy/charts.py} +1 -1
- cognite_toolkit/_cdf_tk/client/api/{extended_data_modeling.py → legacy/extended_data_modeling.py} +1 -1
- cognite_toolkit/_cdf_tk/client/api/{extended_files.py → legacy/extended_files.py} +2 -2
- cognite_toolkit/_cdf_tk/client/api/{extended_functions.py → legacy/extended_functions.py} +9 -9
- cognite_toolkit/_cdf_tk/client/api/{extended_raw.py → legacy/extended_raw.py} +1 -1
- cognite_toolkit/_cdf_tk/client/api/{extended_timeseries.py → legacy/extended_timeseries.py} +5 -2
- cognite_toolkit/_cdf_tk/client/api/{location_filters.py → legacy/location_filters.py} +1 -1
- cognite_toolkit/_cdf_tk/client/api/legacy/robotics/__init__.py +8 -0
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/capabilities.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/data_postprocessing.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/frames.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/locations.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/maps.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/robots.py +2 -2
- cognite_toolkit/_cdf_tk/client/api/{search_config.py → legacy/search_config.py} +5 -1
- cognite_toolkit/_cdf_tk/client/api/migration.py +2 -3
- cognite_toolkit/_cdf_tk/client/api/project.py +9 -8
- cognite_toolkit/_cdf_tk/client/api/search.py +2 -2
- cognite_toolkit/_cdf_tk/client/api/streams.py +21 -17
- cognite_toolkit/_cdf_tk/client/api/three_d.py +343 -9
- cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py +13 -0
- cognite_toolkit/_cdf_tk/client/data_classes/base.py +12 -32
- cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +18 -13
- cognite_toolkit/_cdf_tk/client/data_classes/legacy/__init__.py +0 -0
- cognite_toolkit/_cdf_tk/client/data_classes/{canvas.py → legacy/canvas.py} +1 -1
- cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +65 -0
- cognite_toolkit/_cdf_tk/client/testing.py +24 -16
- cognite_toolkit/_cdf_tk/commands/__init__.py +1 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +8 -2
- cognite_toolkit/_cdf_tk/commands/_migrate/creators.py +1 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +35 -4
- cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +149 -14
- cognite_toolkit/_cdf_tk/commands/_migrate/data_model.py +1 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py +1 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/issues.py +19 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +220 -3
- cognite_toolkit/_cdf_tk/commands/_profile.py +1 -1
- cognite_toolkit/_cdf_tk/commands/_purge.py +9 -11
- cognite_toolkit/_cdf_tk/commands/build_cmd.py +1 -1
- cognite_toolkit/_cdf_tk/commands/build_v2/__init__.py +0 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/build_cmd.py +241 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/build_input.py +85 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/build_issues.py +27 -0
- cognite_toolkit/_cdf_tk/commands/dump_resource.py +4 -4
- cognite_toolkit/_cdf_tk/commands/run.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +2 -2
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +22 -20
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py +14 -2
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/industrial_tool.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/migration.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/raw.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/robotics.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/transformation.py +49 -14
- cognite_toolkit/_cdf_tk/data_classes/_module_toml.py +1 -0
- cognite_toolkit/_cdf_tk/resource_classes/search_config.py +1 -1
- cognite_toolkit/_cdf_tk/resource_classes/workflow_version.py +164 -5
- cognite_toolkit/_cdf_tk/storageio/_applications.py +2 -2
- cognite_toolkit/_cdf_tk/storageio/_file_content.py +1 -2
- cognite_toolkit/_cdf_tk/storageio/_instances.py +1 -1
- cognite_toolkit/_cdf_tk/storageio/selectors/__init__.py +10 -1
- cognite_toolkit/_cdf_tk/storageio/selectors/_three_d.py +34 -0
- cognite_toolkit/_cdf_tk/utils/cdf.py +1 -1
- cognite_toolkit/_cdf_tk/utils/http_client/__init__.py +28 -0
- cognite_toolkit/_cdf_tk/utils/http_client/_client.py +3 -2
- cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py +6 -0
- cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py +67 -7
- cognite_toolkit/_cdf_tk/utils/http_client/_tracker.py +5 -2
- cognite_toolkit/_cdf_tk/utils/interactive_select.py +51 -4
- cognite_toolkit/_cdf_tk/validation.py +4 -0
- cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
- cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
- cognite_toolkit/_resources/cdf.toml +1 -1
- cognite_toolkit/_version.py +1 -1
- {cognite_toolkit-0.7.30.dist-info → cognite_toolkit-0.7.39.dist-info}/METADATA +1 -1
- {cognite_toolkit-0.7.30.dist-info → cognite_toolkit-0.7.39.dist-info}/RECORD +119 -113
- {cognite_toolkit-0.7.30.dist-info → cognite_toolkit-0.7.39.dist-info}/WHEEL +1 -1
- cognite_toolkit/_cdf_tk/client/api/robotics/__init__.py +0 -3
- cognite_toolkit/_cdf_tk/prototypes/import_app.py +0 -41
- /cognite_toolkit/_cdf_tk/{prototypes/commands → client/api/legacy}/__init__.py +0 -0
- /cognite_toolkit/_cdf_tk/client/api/{dml.py → legacy/dml.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/api/{fixed_transformations.py → legacy/fixed_transformations.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/api.py +0 -0
- /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/utlis.py +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{apm_config_v1.py → legacy/apm_config_v1.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{charts.py → legacy/charts.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{extendable_cognite_file.py → legacy/extendable_cognite_file.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetadata.py → legacy/extended_filemetadata.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetdata.py → legacy/extended_filemetdata.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{extended_timeseries.py → legacy/extended_timeseries.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{functions.py → legacy/functions.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{graphql_data_models.py → legacy/graphql_data_models.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{instances.py → legacy/instances.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{location_filters.py → legacy/location_filters.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{migration.py → legacy/migration.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{pending_instances_ids.py → legacy/pending_instances_ids.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{project.py → legacy/project.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{raw.py → legacy/raw.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{robotics.py → legacy/robotics.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{search_config.py → legacy/search_config.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{sequences.py → legacy/sequences.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{streamlit_.py → legacy/streamlit_.py} +0 -0
- /cognite_toolkit/_cdf_tk/{prototypes/commands/import_.py → commands/_import_cmd.py} +0 -0
- {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) ->
|
|
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
|
|
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
|
|
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,
|
|
102
|
-
data = gzip.compress(data
|
|
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:
|
|
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,
|
|
173
|
-
return gzip.compress(res
|
|
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 (
|
|
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
|
-
|
|
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"]
|
cognite_toolkit/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.7.
|
|
1
|
+
__version__ = "0.7.39"
|