cognite-toolkit 0.7.43__py3-none-any.whl → 0.7.45__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/client/_toolkit_client.py +6 -0
- cognite_toolkit/_cdf_tk/client/api/assets.py +2 -2
- cognite_toolkit/_cdf_tk/client/api/events.py +2 -2
- cognite_toolkit/_cdf_tk/client/api/filemetadata.py +150 -0
- cognite_toolkit/_cdf_tk/client/api/raw.py +174 -0
- cognite_toolkit/_cdf_tk/client/api/simulator_models.py +128 -0
- cognite_toolkit/_cdf_tk/client/api/simulators.py +8 -0
- cognite_toolkit/_cdf_tk/client/api/timeseries.py +2 -2
- cognite_toolkit/_cdf_tk/client/cdf_client/__init__.py +2 -1
- cognite_toolkit/_cdf_tk/client/cdf_client/api.py +114 -8
- cognite_toolkit/_cdf_tk/client/data_classes/annotation.py +79 -0
- cognite_toolkit/_cdf_tk/client/data_classes/base.py +13 -3
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/__init__.py +16 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_instance.py +143 -0
- cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_references.py +8 -0
- cognite_toolkit/_cdf_tk/client/data_classes/dataset.py +35 -0
- cognite_toolkit/_cdf_tk/client/data_classes/extraction_pipeline.py +59 -0
- cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +7 -1
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_destination.py +34 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_job.py +134 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_mapping.py +72 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/__init__.py +63 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_auth.py +63 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_base.py +26 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_certificate.py +20 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_eventhub.py +31 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_kafka.py +53 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_mqtt.py +36 -0
- cognite_toolkit/_cdf_tk/client/data_classes/hosted_extractor_source/_rest.py +49 -0
- cognite_toolkit/_cdf_tk/client/data_classes/identifiers.py +8 -0
- cognite_toolkit/_cdf_tk/client/data_classes/label.py +27 -0
- cognite_toolkit/_cdf_tk/client/data_classes/raw.py +3 -2
- cognite_toolkit/_cdf_tk/client/data_classes/securitycategory.py +24 -0
- cognite_toolkit/_cdf_tk/client/data_classes/sequence.py +45 -0
- cognite_toolkit/_cdf_tk/client/data_classes/transformation.py +140 -0
- cognite_toolkit/_cdf_tk/client/data_classes/workflow.py +27 -0
- cognite_toolkit/_cdf_tk/client/data_classes/workflow_trigger.py +63 -0
- cognite_toolkit/_cdf_tk/client/data_classes/workflow_version.py +155 -0
- cognite_toolkit/_cdf_tk/client/testing.py +5 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +4 -3
- cognite_toolkit/_cdf_tk/cruds/__init__.py +5 -1
- cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +7 -3
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/__init__.py +2 -0
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +56 -59
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +1 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/simulators.py +119 -0
- cognite_toolkit/_cdf_tk/feature_flags.py +4 -0
- cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +34 -29
- cognite_toolkit/_cdf_tk/storageio/_file_content.py +22 -19
- 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.43.dist-info → cognite_toolkit-0.7.45.dist-info}/METADATA +11 -1
- {cognite_toolkit-0.7.43.dist-info → cognite_toolkit-0.7.45.dist-info}/RECORD +57 -30
- {cognite_toolkit-0.7.43.dist-info → cognite_toolkit-0.7.45.dist-info}/WHEEL +1 -1
- {cognite_toolkit-0.7.43.dist-info → cognite_toolkit-0.7.45.dist-info}/entry_points.txt +0 -0
|
@@ -15,13 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
from collections.abc import Hashable, Iterable, Sequence
|
|
17
17
|
from datetime import date, datetime
|
|
18
|
-
from typing import Any,
|
|
18
|
+
from typing import Any, final
|
|
19
19
|
|
|
20
|
-
from cognite.client.data_classes import (
|
|
21
|
-
FileMetadata,
|
|
22
|
-
FileMetadataList,
|
|
23
|
-
FileMetadataWrite,
|
|
24
|
-
)
|
|
25
20
|
from cognite.client.data_classes.capabilities import (
|
|
26
21
|
Capability,
|
|
27
22
|
DataModelInstancesAcl,
|
|
@@ -31,9 +26,9 @@ from cognite.client.data_classes.data_modeling import NodeApplyResultList, NodeI
|
|
|
31
26
|
from cognite.client.exceptions import CogniteAPIError
|
|
32
27
|
from cognite.client.utils._time import convert_data_modelling_timestamp
|
|
33
28
|
from cognite.client.utils.useful_types import SequenceNotStr
|
|
34
|
-
from rich import print
|
|
35
29
|
|
|
36
|
-
from cognite_toolkit._cdf_tk.client.data_classes.
|
|
30
|
+
from cognite_toolkit._cdf_tk.client.data_classes.filemetadata import FileMetadataRequest, FileMetadataResponse
|
|
31
|
+
from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId, InternalOrExternalId
|
|
37
32
|
from cognite_toolkit._cdf_tk.client.data_classes.legacy.extendable_cognite_file import (
|
|
38
33
|
ExtendableCogniteFile,
|
|
39
34
|
ExtendableCogniteFileApply,
|
|
@@ -56,11 +51,11 @@ from .datamodel import SpaceCRUD, ViewCRUD
|
|
|
56
51
|
|
|
57
52
|
|
|
58
53
|
@final
|
|
59
|
-
class FileMetadataCRUD(ResourceContainerCRUD[
|
|
54
|
+
class FileMetadataCRUD(ResourceContainerCRUD[ExternalId, FileMetadataRequest, FileMetadataResponse]):
|
|
60
55
|
item_name = "file contents"
|
|
61
56
|
folder_name = "files"
|
|
62
|
-
resource_cls =
|
|
63
|
-
resource_write_cls =
|
|
57
|
+
resource_cls = FileMetadataResponse
|
|
58
|
+
resource_write_cls = FileMetadataRequest
|
|
64
59
|
yaml_cls = FileMetadataYAML
|
|
65
60
|
kind = "FileMetadata"
|
|
66
61
|
dependencies = frozenset({DataSetsCRUD, GroupAllScopedCRUD, LabelCRUD, AssetCRUD})
|
|
@@ -73,7 +68,7 @@ class FileMetadataCRUD(ResourceContainerCRUD[str, FileMetadataWrite, FileMetadat
|
|
|
73
68
|
|
|
74
69
|
@classmethod
|
|
75
70
|
def get_required_capability(
|
|
76
|
-
cls, items: Sequence[
|
|
71
|
+
cls, items: Sequence[FileMetadataRequest] | None, read_only: bool
|
|
77
72
|
) -> Capability | list[Capability]:
|
|
78
73
|
if not items and items is not None:
|
|
79
74
|
return []
|
|
@@ -88,22 +83,22 @@ class FileMetadataCRUD(ResourceContainerCRUD[str, FileMetadataWrite, FileMetadat
|
|
|
88
83
|
return FilesAcl(actions, scope)
|
|
89
84
|
|
|
90
85
|
@classmethod
|
|
91
|
-
def get_id(cls, item:
|
|
86
|
+
def get_id(cls, item: FileMetadataRequest | FileMetadataResponse | dict) -> ExternalId:
|
|
92
87
|
if isinstance(item, dict):
|
|
93
|
-
return item["externalId"]
|
|
88
|
+
return ExternalId(external_id=item["externalId"])
|
|
94
89
|
if item.external_id is None:
|
|
95
90
|
raise ToolkitRequiredValueError("FileMetadata must have external_id set.")
|
|
96
|
-
return item.external_id
|
|
91
|
+
return ExternalId(external_id=item.external_id)
|
|
97
92
|
|
|
98
93
|
@classmethod
|
|
99
|
-
def get_internal_id(cls, item:
|
|
94
|
+
def get_internal_id(cls, item: FileMetadataResponse | dict) -> int:
|
|
100
95
|
if isinstance(item, dict):
|
|
101
96
|
return item["id"]
|
|
102
97
|
return item.id
|
|
103
98
|
|
|
104
99
|
@classmethod
|
|
105
|
-
def dump_id(cls, id:
|
|
106
|
-
return
|
|
100
|
+
def dump_id(cls, id: ExternalId) -> dict[str, Any]:
|
|
101
|
+
return id.dump()
|
|
107
102
|
|
|
108
103
|
@classmethod
|
|
109
104
|
def get_dependent_items(cls, item: dict) -> Iterable[tuple[type[ResourceCRUD], Hashable]]:
|
|
@@ -121,69 +116,71 @@ class FileMetadataCRUD(ResourceContainerCRUD[str, FileMetadataWrite, FileMetadat
|
|
|
121
116
|
for asset_external_id in item.get("assetExternalIds", []):
|
|
122
117
|
yield AssetCRUD, ExternalId(external_id=asset_external_id)
|
|
123
118
|
|
|
124
|
-
def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) ->
|
|
125
|
-
if resource.
|
|
126
|
-
ds_external_id = resource.pop("dataSetExternalId")
|
|
119
|
+
def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> FileMetadataRequest:
|
|
120
|
+
if ds_external_id := resource.pop("dataSetExternalId", None):
|
|
127
121
|
resource["dataSetId"] = self.client.lookup.data_sets.id(ds_external_id, is_dry_run)
|
|
128
122
|
if security_categories_names := resource.pop("securityCategoryNames", []):
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
123
|
+
resource["securityCategories"] = self.client.lookup.security_categories.id(
|
|
124
|
+
security_categories_names, is_dry_run
|
|
125
|
+
)
|
|
126
|
+
if asset_external_ids := resource.pop("assetExternalIds", None):
|
|
127
|
+
resource["assetIds"] = self.client.lookup.assets.id(asset_external_ids, is_dry_run)
|
|
128
|
+
return FileMetadataRequest.model_validate(resource)
|
|
129
|
+
|
|
130
|
+
def dump_resource(self, resource: FileMetadataResponse, local: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
131
|
+
dumped = resource.as_request_resource().dump()
|
|
132
|
+
if data_set_id := dumped.pop("dataSetId", None):
|
|
133
|
+
dumped["dataSetExternalId"] = self.client.lookup.data_sets.external_id(data_set_id)
|
|
139
134
|
if security_categories := dumped.pop("securityCategories", []):
|
|
140
135
|
dumped["securityCategoryNames"] = self.client.lookup.security_categories.external_id(security_categories)
|
|
141
136
|
if asset_ids := dumped.pop("assetIds", []):
|
|
142
137
|
dumped["assetExternalIds"] = self.client.lookup.assets.external_id(asset_ids)
|
|
143
138
|
return dumped
|
|
144
139
|
|
|
145
|
-
def create(self, items: Sequence[
|
|
146
|
-
|
|
147
|
-
for meta in items:
|
|
148
|
-
try:
|
|
149
|
-
created.append(self.client.files.create(meta))
|
|
150
|
-
except CogniteAPIError as e:
|
|
151
|
-
if e.code == 409:
|
|
152
|
-
print(f" [bold yellow]WARNING:[/] File {meta.external_id} already exists, skipping upload.")
|
|
153
|
-
return created
|
|
140
|
+
def create(self, items: Sequence[FileMetadataRequest]) -> list[FileMetadataResponse]:
|
|
141
|
+
return self.client.tool.filemetadata.create(items, overwrite=True)
|
|
154
142
|
|
|
155
|
-
def retrieve(self, ids: SequenceNotStr[
|
|
156
|
-
return self.client.
|
|
143
|
+
def retrieve(self, ids: SequenceNotStr[ExternalId]) -> list[FileMetadataResponse]:
|
|
144
|
+
return self.client.tool.filemetadata.retrieve(list(ids), ignore_unknown_ids=True)
|
|
157
145
|
|
|
158
|
-
def update(self, items: Sequence[
|
|
159
|
-
return self.client.
|
|
146
|
+
def update(self, items: Sequence[FileMetadataRequest]) -> list[FileMetadataResponse]:
|
|
147
|
+
return self.client.tool.filemetadata.update(items, mode="replace")
|
|
160
148
|
|
|
161
|
-
def delete(self, ids:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
149
|
+
def delete(self, ids: SequenceNotStr[InternalOrExternalId]) -> int:
|
|
150
|
+
if not ids:
|
|
151
|
+
return 0
|
|
152
|
+
self.client.tool.filemetadata.delete(list(ids), ignore_unknown_ids=True)
|
|
153
|
+
return len(ids)
|
|
165
154
|
|
|
166
155
|
def _iterate(
|
|
167
156
|
self,
|
|
168
157
|
data_set_external_id: str | None = None,
|
|
169
158
|
space: str | None = None,
|
|
170
159
|
parent_ids: list[Hashable] | None = None,
|
|
171
|
-
) -> Iterable[
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
160
|
+
) -> Iterable[FileMetadataResponse]:
|
|
161
|
+
cursor: str | None = None
|
|
162
|
+
while True:
|
|
163
|
+
page = self.client.tool.filemetadata.iterate(
|
|
164
|
+
data_set_external_ids=[data_set_external_id] if data_set_external_id else None,
|
|
165
|
+
limit=1000,
|
|
166
|
+
cursor=cursor,
|
|
167
|
+
)
|
|
168
|
+
yield from page.items
|
|
169
|
+
if not page.next_cursor or not page.items:
|
|
170
|
+
break
|
|
171
|
+
cursor = page.next_cursor
|
|
172
|
+
|
|
173
|
+
def count(self, ids: SequenceNotStr[ExternalId]) -> int:
|
|
175
174
|
return sum(
|
|
176
|
-
1
|
|
177
|
-
for meta in self.client.files.retrieve_multiple(external_ids=list(ids), ignore_unknown_ids=True)
|
|
178
|
-
if meta.uploaded
|
|
175
|
+
1 for meta in self.client.tool.filemetadata.retrieve(list(ids), ignore_unknown_ids=True) if meta.uploaded
|
|
179
176
|
)
|
|
180
177
|
|
|
181
|
-
def drop_data(self, ids: SequenceNotStr[
|
|
182
|
-
existing = self.client.
|
|
178
|
+
def drop_data(self, ids: SequenceNotStr[ExternalId]) -> int:
|
|
179
|
+
existing = self.client.tool.filemetadata.retrieve(list(ids), ignore_unknown_ids=True)
|
|
183
180
|
# File and FileMetadata is tightly coupled, so we need to delete the metadata and recreate it
|
|
184
181
|
# without the source set to delete the file.
|
|
185
|
-
deleted_files = self.delete(
|
|
186
|
-
self.create(
|
|
182
|
+
deleted_files = self.delete([meta.as_id() for meta in existing])
|
|
183
|
+
self.create([meta.as_request_resource() for meta in existing])
|
|
187
184
|
return deleted_files
|
|
188
185
|
|
|
189
186
|
|
|
@@ -130,7 +130,7 @@ class RelationshipCRUD(ResourceCRUD[str, RelationshipWrite, Relationship]):
|
|
|
130
130
|
elif type_value == "timeseries":
|
|
131
131
|
yield TimeSeriesCRUD, ExternalId(external_id=id_value)
|
|
132
132
|
elif type_value == "file":
|
|
133
|
-
yield FileMetadataCRUD, id_value
|
|
133
|
+
yield FileMetadataCRUD, ExternalId(external_id=id_value)
|
|
134
134
|
elif type_value == "event":
|
|
135
135
|
yield EventCRUD, ExternalId(external_id=id_value)
|
|
136
136
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from collections.abc import Hashable, Iterable, Sequence
|
|
2
|
+
from typing import Any, final
|
|
3
|
+
|
|
4
|
+
from cognite.client.data_classes.capabilities import Capability
|
|
5
|
+
from cognite.client.utils.useful_types import SequenceNotStr
|
|
6
|
+
|
|
7
|
+
from cognite_toolkit._cdf_tk.client.data_classes.identifiers import ExternalId, InternalOrExternalId
|
|
8
|
+
from cognite_toolkit._cdf_tk.client.data_classes.simulator_model import SimulatorModelRequest, SimulatorModelResponse
|
|
9
|
+
from cognite_toolkit._cdf_tk.cruds._base_cruds import ResourceCRUD
|
|
10
|
+
from cognite_toolkit._cdf_tk.resource_classes import SimulatorModelYAML
|
|
11
|
+
|
|
12
|
+
from .data_organization import DataSetsCRUD
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@final
|
|
16
|
+
class SimulatorModelCRUD(ResourceCRUD[ExternalId, SimulatorModelRequest, SimulatorModelResponse]):
|
|
17
|
+
folder_name = "simulators"
|
|
18
|
+
resource_cls = SimulatorModelResponse
|
|
19
|
+
resource_write_cls = SimulatorModelRequest
|
|
20
|
+
yaml_cls = SimulatorModelYAML
|
|
21
|
+
kind = "SimulatorModel"
|
|
22
|
+
dependencies = frozenset({DataSetsCRUD})
|
|
23
|
+
_doc_url = "Simulator-Models/operation/create_simulator_model_simulators_models_post"
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def display_name(self) -> str:
|
|
27
|
+
return "simulator models"
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_id(cls, item: SimulatorModelRequest | SimulatorModelResponse | dict) -> ExternalId:
|
|
31
|
+
if isinstance(item, dict):
|
|
32
|
+
return ExternalId(external_id=item["externalId"])
|
|
33
|
+
if not item.external_id:
|
|
34
|
+
raise KeyError("SimulatorModel must have external_id")
|
|
35
|
+
return ExternalId(external_id=item.external_id)
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def get_internal_id(cls, item: SimulatorModelResponse | dict) -> int:
|
|
39
|
+
if isinstance(item, dict):
|
|
40
|
+
return item["id"]
|
|
41
|
+
if not item.id:
|
|
42
|
+
raise KeyError("SimulatorModel must have id")
|
|
43
|
+
return item.id
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def dump_id(cls, id: ExternalId) -> dict[str, Any]:
|
|
47
|
+
return id.dump()
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def get_required_capability(
|
|
51
|
+
cls, items: Sequence[SimulatorModelRequest] | None, read_only: bool
|
|
52
|
+
) -> Capability | list[Capability]:
|
|
53
|
+
# Simulator ACLs is not yet implemented in the PySDK, which means
|
|
54
|
+
# that we cannot check for specific capabilities.
|
|
55
|
+
return []
|
|
56
|
+
|
|
57
|
+
def create(self, items: Sequence[SimulatorModelRequest]) -> list[SimulatorModelResponse]:
|
|
58
|
+
return self.client.tool.simulators.models.create(items)
|
|
59
|
+
|
|
60
|
+
def retrieve(self, ids: SequenceNotStr[ExternalId]) -> list[SimulatorModelResponse]:
|
|
61
|
+
return self.client.tool.simulators.models.retrieve(list(ids), ignore_unknown_ids=True)
|
|
62
|
+
|
|
63
|
+
def update(self, items: Sequence[SimulatorModelRequest]) -> list[SimulatorModelResponse]:
|
|
64
|
+
return self.client.tool.simulators.models.update(items, mode="replace")
|
|
65
|
+
|
|
66
|
+
def delete(self, ids: SequenceNotStr[ExternalId | InternalOrExternalId]) -> int:
|
|
67
|
+
if not ids:
|
|
68
|
+
return 0
|
|
69
|
+
self.client.tool.simulators.models.delete(list(ids))
|
|
70
|
+
return len(ids)
|
|
71
|
+
|
|
72
|
+
def _iterate(
|
|
73
|
+
self,
|
|
74
|
+
data_set_external_id: str | None = None,
|
|
75
|
+
space: str | None = None,
|
|
76
|
+
parent_ids: list[Hashable] | None = None,
|
|
77
|
+
) -> Iterable[SimulatorModelResponse]:
|
|
78
|
+
# Note: The SimulatorModelsAPI doesn't support data_set_external_id filtering directly,
|
|
79
|
+
# so we iterate and filter in memory if needed.
|
|
80
|
+
cursor: str | None = None
|
|
81
|
+
data_set_id: int | None = None
|
|
82
|
+
if data_set_external_id:
|
|
83
|
+
data_set_id = self.client.lookup.data_sets.id(data_set_external_id, is_dry_run=False)
|
|
84
|
+
while True:
|
|
85
|
+
page = self.client.tool.simulators.models.iterate(
|
|
86
|
+
limit=1000,
|
|
87
|
+
cursor=cursor,
|
|
88
|
+
)
|
|
89
|
+
if data_set_id:
|
|
90
|
+
# Filter by data_set_external_id in memory
|
|
91
|
+
for item in page.items:
|
|
92
|
+
if item.data_set_id == data_set_id:
|
|
93
|
+
yield item
|
|
94
|
+
else:
|
|
95
|
+
yield from page.items
|
|
96
|
+
if not page.next_cursor or not page.items:
|
|
97
|
+
break
|
|
98
|
+
cursor = page.next_cursor
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def get_dependent_items(cls, item: dict) -> Iterable[tuple[type[ResourceCRUD], Hashable]]:
|
|
102
|
+
"""Returns all items that this item requires.
|
|
103
|
+
|
|
104
|
+
For example, a SimulatorModel requires a DataSet, so this method would return the
|
|
105
|
+
DataSetsCRUD and identifier of that dataset.
|
|
106
|
+
"""
|
|
107
|
+
if "dataSetExternalId" in item:
|
|
108
|
+
yield DataSetsCRUD, item["dataSetExternalId"]
|
|
109
|
+
|
|
110
|
+
def load_resource(self, resource: dict[str, Any], is_dry_run: bool = False) -> SimulatorModelRequest:
|
|
111
|
+
if ds_external_id := resource.pop("dataSetExternalId", None):
|
|
112
|
+
resource["dataSetId"] = self.client.lookup.data_sets.id(ds_external_id, is_dry_run)
|
|
113
|
+
return SimulatorModelRequest.model_validate(resource)
|
|
114
|
+
|
|
115
|
+
def dump_resource(self, resource: SimulatorModelResponse, local: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
116
|
+
dumped = resource.as_request_resource().dump()
|
|
117
|
+
if data_set_id := dumped.pop("dataSetId", None):
|
|
118
|
+
dumped["dataSetExternalId"] = self.client.lookup.data_sets.external_id(data_set_id)
|
|
119
|
+
return dumped
|
|
@@ -61,6 +61,10 @@ class Flags(Enum):
|
|
|
61
61
|
visible=True,
|
|
62
62
|
description="Enables extended upload to support uploading individual files",
|
|
63
63
|
)
|
|
64
|
+
SIMULATORS = FlagMetadata(
|
|
65
|
+
visible=True,
|
|
66
|
+
description="Enables the support for simulator model resources",
|
|
67
|
+
)
|
|
64
68
|
|
|
65
69
|
def is_enabled(self) -> bool:
|
|
66
70
|
return FeatureFlag.is_enabled(self)
|
|
@@ -3,16 +3,12 @@ from collections import defaultdict
|
|
|
3
3
|
from collections.abc import Iterable, Sequence
|
|
4
4
|
from typing import Any, ClassVar, Generic
|
|
5
5
|
|
|
6
|
-
from cognite.client.data_classes import
|
|
7
|
-
FileMetadata,
|
|
8
|
-
FileMetadataList,
|
|
9
|
-
Label,
|
|
10
|
-
LabelDefinition,
|
|
11
|
-
)
|
|
6
|
+
from cognite.client.data_classes import Label, LabelDefinition
|
|
12
7
|
|
|
13
8
|
from cognite_toolkit._cdf_tk.client import ToolkitClient
|
|
14
9
|
from cognite_toolkit._cdf_tk.client.data_classes.asset import AssetAggregateItem, AssetRequest, AssetResponse
|
|
15
10
|
from cognite_toolkit._cdf_tk.client.data_classes.event import EventRequest, EventResponse
|
|
11
|
+
from cognite_toolkit._cdf_tk.client.data_classes.filemetadata import FileMetadataResponse
|
|
16
12
|
from cognite_toolkit._cdf_tk.client.data_classes.identifiers import InternalId
|
|
17
13
|
from cognite_toolkit._cdf_tk.client.data_classes.timeseries import TimeSeriesRequest, TimeSeriesResponse
|
|
18
14
|
from cognite_toolkit._cdf_tk.cruds import (
|
|
@@ -107,13 +103,16 @@ class AssetCentricIO(
|
|
|
107
103
|
|
|
108
104
|
def _collect_dependencies(
|
|
109
105
|
self,
|
|
110
|
-
resources: Sequence[AssetResponse]
|
|
106
|
+
resources: Sequence[AssetResponse]
|
|
107
|
+
| Sequence[FileMetadataResponse]
|
|
108
|
+
| Sequence[TimeSeriesResponse]
|
|
109
|
+
| Sequence[EventResponse],
|
|
111
110
|
selector: AssetCentricSelector,
|
|
112
111
|
) -> None:
|
|
113
112
|
for resource in resources:
|
|
114
113
|
if resource.data_set_id:
|
|
115
114
|
self._downloaded_data_sets_by_selector[selector].add(resource.data_set_id)
|
|
116
|
-
if isinstance(resource, AssetResponse |
|
|
115
|
+
if isinstance(resource, AssetResponse | FileMetadataResponse):
|
|
117
116
|
for label in resource.labels or []:
|
|
118
117
|
if isinstance(label, str):
|
|
119
118
|
self._downloaded_labels_by_selector[selector].add(label)
|
|
@@ -146,18 +145,18 @@ class AssetCentricIO(
|
|
|
146
145
|
return f"INTERNAL_ID_project_{project}_{internal_id!s}"
|
|
147
146
|
|
|
148
147
|
def _populate_data_set_id_cache(
|
|
149
|
-
self, chunk: Sequence[AssetResponse |
|
|
148
|
+
self, chunk: Sequence[AssetResponse | FileMetadataResponse | TimeSeriesResponse | EventResponse]
|
|
150
149
|
) -> None:
|
|
151
150
|
data_set_ids = {item.data_set_id for item in chunk if item.data_set_id is not None}
|
|
152
151
|
self.client.lookup.data_sets.external_id(list(data_set_ids))
|
|
153
152
|
|
|
154
|
-
def _populate_security_category_cache(self, chunk: Sequence[
|
|
153
|
+
def _populate_security_category_cache(self, chunk: Sequence[FileMetadataResponse | TimeSeriesResponse]) -> None:
|
|
155
154
|
security_category_ids: set[int] = set()
|
|
156
155
|
for item in chunk:
|
|
157
156
|
security_category_ids.update(item.security_categories or [])
|
|
158
157
|
self.client.lookup.security_categories.external_id(list(security_category_ids))
|
|
159
158
|
|
|
160
|
-
def _populate_asset_id_cache(self, chunk: Sequence[
|
|
159
|
+
def _populate_asset_id_cache(self, chunk: Sequence[FileMetadataResponse | EventResponse]) -> None:
|
|
161
160
|
asset_ids: set[int] = set()
|
|
162
161
|
for item in chunk:
|
|
163
162
|
asset_ids.update(item.asset_ids or [])
|
|
@@ -379,7 +378,7 @@ class AssetIO(UploadableAssetCentricIO[AssetResponse, AssetRequest]):
|
|
|
379
378
|
current_depth += 1
|
|
380
379
|
|
|
381
380
|
|
|
382
|
-
class FileMetadataIO(AssetCentricIO[
|
|
381
|
+
class FileMetadataIO(AssetCentricIO[FileMetadataResponse]):
|
|
383
382
|
KIND = "FileMetadata"
|
|
384
383
|
RESOURCE_TYPE = "file"
|
|
385
384
|
SUPPORTED_DOWNLOAD_FORMATS = frozenset({".parquet", ".csv", ".ndjson"})
|
|
@@ -391,7 +390,7 @@ class FileMetadataIO(AssetCentricIO[FileMetadata]):
|
|
|
391
390
|
super().__init__(client)
|
|
392
391
|
self._crud = FileMetadataCRUD.create_loader(self.client)
|
|
393
392
|
|
|
394
|
-
def as_id(self, item:
|
|
393
|
+
def as_id(self, item: FileMetadataResponse) -> str:
|
|
395
394
|
return item.external_id if item.external_id is not None else self._create_identifier(item.id)
|
|
396
395
|
|
|
397
396
|
def _get_aggregator(self) -> AssetCentricAggregator:
|
|
@@ -434,25 +433,31 @@ class FileMetadataIO(AssetCentricIO[FileMetadata]):
|
|
|
434
433
|
]
|
|
435
434
|
return file_schema + metadata_schema
|
|
436
435
|
|
|
437
|
-
def stream_data(
|
|
436
|
+
def stream_data(
|
|
437
|
+
self, selector: AssetCentricSelector, limit: int | None = None
|
|
438
|
+
) -> Iterable[Page[FileMetadataResponse]]:
|
|
438
439
|
asset_subtree_external_ids, data_set_external_ids = self._get_hierarchy_dataset_pair(selector)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
self._collect_dependencies(
|
|
449
|
-
yield Page(worker_id="main", items=
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
440
|
+
cursor: str | None = None
|
|
441
|
+
total_count = 0
|
|
442
|
+
while True:
|
|
443
|
+
page = self.client.tool.filemetadata.iterate(
|
|
444
|
+
data_set_external_ids=data_set_external_ids,
|
|
445
|
+
asset_subtree_external_ids=asset_subtree_external_ids,
|
|
446
|
+
limit=self.CHUNK_SIZE,
|
|
447
|
+
cursor=cursor,
|
|
448
|
+
)
|
|
449
|
+
self._collect_dependencies(page.items, selector)
|
|
450
|
+
yield Page(worker_id="main", items=page.items)
|
|
451
|
+
total_count += len(page.items)
|
|
452
|
+
if page.next_cursor is None or (limit is not None and total_count >= limit):
|
|
453
|
+
break
|
|
454
|
+
cursor = page.next_cursor
|
|
455
|
+
|
|
456
|
+
def retrieve(self, ids: Sequence[int]) -> list[FileMetadataResponse]:
|
|
457
|
+
return self.client.tool.filemetadata.retrieve(InternalId.from_ids(ids))
|
|
453
458
|
|
|
454
459
|
def data_to_json_chunk(
|
|
455
|
-
self, data_chunk: Sequence[
|
|
460
|
+
self, data_chunk: Sequence[FileMetadataResponse], selector: AssetCentricSelector | None = None
|
|
456
461
|
) -> list[dict[str, JsonVal]]:
|
|
457
462
|
# Ensure data sets/assets/security-categories are looked up to populate cache.
|
|
458
463
|
# This is to avoid looking up each data set id individually in the .dump_resource call
|
|
@@ -6,10 +6,11 @@ from pathlib import Path
|
|
|
6
6
|
from typing import cast
|
|
7
7
|
|
|
8
8
|
import httpx
|
|
9
|
-
from cognite.client.data_classes import
|
|
10
|
-
from cognite.client.data_classes.data_modeling import NodeId, ViewId
|
|
9
|
+
from cognite.client.data_classes.data_modeling import ViewId
|
|
11
10
|
|
|
12
11
|
from cognite_toolkit._cdf_tk.client import ToolkitClient
|
|
12
|
+
from cognite_toolkit._cdf_tk.client.data_classes.data_modeling import NodeReference
|
|
13
|
+
from cognite_toolkit._cdf_tk.client.data_classes.filemetadata import FileMetadataRequest, FileMetadataResponse
|
|
13
14
|
from cognite_toolkit._cdf_tk.client.http_client import (
|
|
14
15
|
DataBodyRequest,
|
|
15
16
|
ErrorDetails,
|
|
@@ -45,21 +46,24 @@ COGNITE_FILE_VIEW = ViewId("cdf_cdm", "CogniteFile", "v1")
|
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
@dataclass
|
|
48
|
-
class UploadFileContentItem(UploadItem[
|
|
49
|
+
class UploadFileContentItem(UploadItem[FileMetadataRequest]):
|
|
49
50
|
file_path: Path
|
|
50
51
|
mime_type: str
|
|
51
52
|
|
|
53
|
+
def dump(self) -> JsonVal:
|
|
54
|
+
return self.item.dump(camel_case=True, exclude_extra=True)
|
|
55
|
+
|
|
52
56
|
|
|
53
57
|
@dataclass
|
|
54
58
|
class MetadataWithFilePath(ResourceResponseProtocol):
|
|
55
|
-
metadata:
|
|
59
|
+
metadata: FileMetadataResponse
|
|
56
60
|
file_path: Path
|
|
57
61
|
|
|
58
|
-
def as_write(self) ->
|
|
59
|
-
return self.metadata.
|
|
62
|
+
def as_write(self) -> FileMetadataRequest:
|
|
63
|
+
return self.metadata.as_request_resource()
|
|
60
64
|
|
|
61
65
|
|
|
62
|
-
class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePath,
|
|
66
|
+
class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePath, FileMetadataRequest]):
|
|
63
67
|
SUPPORTED_DOWNLOAD_FORMATS = frozenset({".ndjson"})
|
|
64
68
|
SUPPORTED_COMPRESSIONS = frozenset({".gz"})
|
|
65
69
|
CHUNK_SIZE = 10
|
|
@@ -116,7 +120,7 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
|
|
|
116
120
|
)
|
|
117
121
|
yield Page(items=downloaded_files, worker_id="Main")
|
|
118
122
|
|
|
119
|
-
def _retrieve_metadata(self, identifiers: Sequence[FileIdentifier]) -> Sequence[
|
|
123
|
+
def _retrieve_metadata(self, identifiers: Sequence[FileIdentifier]) -> Sequence[FileMetadataResponse] | None:
|
|
120
124
|
config = self.client.config
|
|
121
125
|
responses = self.client.http_client.request_with_retries(
|
|
122
126
|
message=SimpleBodyRequest(
|
|
@@ -137,12 +141,11 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
|
|
|
137
141
|
items_data = body.get("items", [])
|
|
138
142
|
if not isinstance(items_data, list):
|
|
139
143
|
return None
|
|
140
|
-
|
|
141
|
-
return [FileMetadata._load(item) for item in items_data] # type: ignore[arg-type]
|
|
144
|
+
return [FileMetadataResponse.model_validate(item) for item in items_data]
|
|
142
145
|
|
|
143
146
|
@staticmethod
|
|
144
|
-
def _as_metadata_map(metadata: Sequence[
|
|
145
|
-
identifiers_map: dict[FileIdentifier,
|
|
147
|
+
def _as_metadata_map(metadata: Sequence[FileMetadataResponse]) -> dict[FileIdentifier, FileMetadataResponse]:
|
|
148
|
+
identifiers_map: dict[FileIdentifier, FileMetadataResponse] = {}
|
|
146
149
|
for item in metadata:
|
|
147
150
|
if item.id is not None:
|
|
148
151
|
identifiers_map[FileInternalID(internal_id=item.id)] = item
|
|
@@ -158,9 +161,9 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
|
|
|
158
161
|
] = item
|
|
159
162
|
return identifiers_map
|
|
160
163
|
|
|
161
|
-
def _create_filepath(self, meta:
|
|
164
|
+
def _create_filepath(self, meta: FileMetadataResponse, selector: FileIdentifierSelector) -> Path:
|
|
162
165
|
# We now that metadata always have name set
|
|
163
|
-
filename = Path(sanitize_filename(
|
|
166
|
+
filename = Path(sanitize_filename(meta.name))
|
|
164
167
|
if len(filename.suffix) == 0 and meta.mime_type:
|
|
165
168
|
if mime_ext := mimetypes.guess_extension(meta.mime_type):
|
|
166
169
|
filename = filename.with_suffix(mime_ext)
|
|
@@ -245,12 +248,12 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
|
|
|
245
248
|
)
|
|
246
249
|
return result
|
|
247
250
|
|
|
248
|
-
def json_to_resource(self, item_json: dict[str, JsonVal]) ->
|
|
251
|
+
def json_to_resource(self, item_json: dict[str, JsonVal]) -> FileMetadataRequest:
|
|
249
252
|
return self._crud.load_resource(item_json)
|
|
250
253
|
|
|
251
254
|
def upload_items(
|
|
252
255
|
self,
|
|
253
|
-
data_chunk: Sequence[UploadItem[
|
|
256
|
+
data_chunk: Sequence[UploadItem[FileMetadataRequest]],
|
|
254
257
|
http_client: HTTPClient,
|
|
255
258
|
selector: FileContentSelector | None = None,
|
|
256
259
|
) -> Sequence[HTTPMessage]:
|
|
@@ -320,12 +323,12 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
|
|
|
320
323
|
|
|
321
324
|
"""
|
|
322
325
|
# We know that instance_id is always set for data modeling uploads
|
|
323
|
-
instance_id = cast(
|
|
326
|
+
instance_id = cast(NodeReference, item.item.instance_id)
|
|
324
327
|
responses = http_client.request_with_retries(
|
|
325
328
|
message=SimpleBodyRequest(
|
|
326
329
|
endpoint_url=http_client.config.create_api_url("/files/uploadlink"),
|
|
327
330
|
method="POST",
|
|
328
|
-
body_content={"items": [{"instanceId": instance_id.dump(
|
|
331
|
+
body_content={"items": [{"instanceId": instance_id.dump()}]},
|
|
329
332
|
)
|
|
330
333
|
)
|
|
331
334
|
# We know there is only one response since we only requested one upload link
|
|
@@ -340,7 +343,7 @@ class FileContentIO(UploadableStorageIO[FileContentSelector, MetadataWithFilePat
|
|
|
340
343
|
|
|
341
344
|
@classmethod
|
|
342
345
|
def _create_cognite_file_node(
|
|
343
|
-
cls, instance_id:
|
|
346
|
+
cls, instance_id: NodeReference, http_client: HTTPClient, upload_id: str, results: MutableSequence[HTTPMessage]
|
|
344
347
|
) -> bool:
|
|
345
348
|
node_creation = http_client.request_with_retries(
|
|
346
349
|
message=SimpleBodyRequest(
|
cognite_toolkit/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.7.
|
|
1
|
+
__version__ = "0.7.45"
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cognite_toolkit
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.45
|
|
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>
|
|
7
7
|
License-Expression: Apache-2.0
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
8
18
|
Requires-Dist: python-dotenv>=1.0.0
|
|
9
19
|
Requires-Dist: cognite-sdk>=7.87.0,<8.0.0
|
|
10
20
|
Requires-Dist: httpx>=0.28.1
|