cognite-toolkit 0.7.42__py3-none-any.whl → 0.7.43__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 (39) hide show
  1. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +1 -1
  2. cognite_toolkit/_cdf_tk/client/api/events.py +20 -2
  3. cognite_toolkit/_cdf_tk/client/api/timeseries.py +20 -2
  4. cognite_toolkit/_cdf_tk/client/data_classes/agent.py +6 -9
  5. cognite_toolkit/_cdf_tk/client/data_classes/asset.py +7 -14
  6. cognite_toolkit/_cdf_tk/client/data_classes/base.py +2 -2
  7. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/__init__.py +148 -0
  8. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_constraints.py +37 -0
  9. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_container.py +50 -0
  10. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_data_model.py +73 -0
  11. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_data_types.py +116 -0
  12. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_indexes.py +26 -0
  13. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_references.py +78 -0
  14. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_space.py +26 -0
  15. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_view.py +143 -0
  16. cognite_toolkit/_cdf_tk/client/data_classes/data_modeling/_view_property.py +152 -0
  17. cognite_toolkit/_cdf_tk/client/data_classes/event.py +12 -15
  18. cognite_toolkit/_cdf_tk/client/data_classes/filemetadata.py +8 -18
  19. cognite_toolkit/_cdf_tk/client/data_classes/simulator_model.py +50 -0
  20. cognite_toolkit/_cdf_tk/client/data_classes/timeseries.py +15 -18
  21. cognite_toolkit/_cdf_tk/client/testing.py +1 -1
  22. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +7 -5
  23. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +4 -4
  24. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +5 -1
  25. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +40 -39
  26. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +2 -2
  27. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +48 -47
  28. cognite_toolkit/_cdf_tk/resource_classes/__init__.py +2 -0
  29. cognite_toolkit/_cdf_tk/resource_classes/simulator_model.py +17 -0
  30. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +54 -46
  31. cognite_toolkit/_cdf_tk/utils/useful_types2.py +5 -3
  32. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  33. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  34. cognite_toolkit/_resources/cdf.toml +1 -1
  35. cognite_toolkit/_version.py +1 -1
  36. {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.43.dist-info}/METADATA +1 -1
  37. {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.43.dist-info}/RECORD +39 -27
  38. {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.43.dist-info}/WHEEL +0 -0
  39. {cognite_toolkit-0.7.42.dist-info → cognite_toolkit-0.7.43.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,78 @@
1
+ from typing import Literal
2
+
3
+ from pydantic import Field
4
+
5
+ from cognite_toolkit._cdf_tk.client.data_classes.identifiers import Identifier
6
+
7
+
8
+ class SpaceReference(Identifier):
9
+ space: str
10
+
11
+ def __str__(self) -> str:
12
+ return self.space
13
+
14
+
15
+ class ContainerReference(Identifier):
16
+ type: Literal["container"] = Field("container", exclude=True)
17
+ space: str
18
+ external_id: str
19
+
20
+ def __str__(self) -> str:
21
+ return f"{self.space}:{self.external_id}"
22
+
23
+
24
+ class ViewReference(Identifier):
25
+ type: Literal["view"] = Field("view", exclude=True)
26
+ space: str
27
+ external_id: str
28
+ version: str
29
+
30
+ def __str__(self) -> str:
31
+ return f"{self.space}:{self.external_id}(version={self.version})"
32
+
33
+
34
+ class DataModelReference(Identifier):
35
+ space: str
36
+ external_id: str
37
+ version: str
38
+
39
+ def __str__(self) -> str:
40
+ return f"{self.space}:{self.external_id}(version={self.version})"
41
+
42
+
43
+ class NodeReference(Identifier):
44
+ space: str
45
+ external_id: str
46
+
47
+ def __str__(self) -> str:
48
+ return f"{self.space}:{self.external_id}"
49
+
50
+
51
+ class ContainerDirectReference(Identifier):
52
+ source: ContainerReference
53
+ identifier: str
54
+
55
+ def __str__(self) -> str:
56
+ return f"{self.source!s}.{self.identifier}"
57
+
58
+
59
+ class ViewDirectReference(Identifier):
60
+ source: ViewReference
61
+ identifier: str
62
+
63
+ def __str__(self) -> str:
64
+ return f"{self.source!s}.{self.identifier}"
65
+
66
+
67
+ class ContainerIndexReference(ContainerReference):
68
+ identifier: str
69
+
70
+ def __str__(self) -> str:
71
+ return f"{self.space}:{self.external_id}(index={self.identifier})"
72
+
73
+
74
+ class ContainerConstraintReference(ContainerReference):
75
+ identifier: str
76
+
77
+ def __str__(self) -> str:
78
+ return f"{self.space}:{self.external_id}(constraint={self.identifier})"
@@ -0,0 +1,26 @@
1
+ from abc import ABC
2
+
3
+ from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject, RequestResource, ResponseResource
4
+
5
+ from ._references import SpaceReference
6
+
7
+
8
+ class Space(BaseModelObject, ABC):
9
+ space: str
10
+ name: str | None = None
11
+ description: str | None = None
12
+
13
+ def as_id(self) -> SpaceReference:
14
+ return SpaceReference(space=self.space)
15
+
16
+
17
+ class SpaceRequest(Space, RequestResource): ...
18
+
19
+
20
+ class SpaceResponse(Space, ResponseResource[SpaceRequest]):
21
+ created_time: int
22
+ last_updated_time: int
23
+ is_global: bool
24
+
25
+ def as_request_resource(self) -> SpaceRequest:
26
+ return SpaceRequest.model_validate(self.model_dump(by_alias=True), extra="ignore")
@@ -0,0 +1,143 @@
1
+ from abc import ABC
2
+ from typing import Any, Literal
3
+
4
+ from pydantic import Field, JsonValue, field_serializer, model_validator
5
+ from pydantic_core.core_schema import FieldSerializationInfo
6
+
7
+ from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject, RequestResource, ResponseResource
8
+
9
+ from ._data_types import DirectNodeRelation
10
+ from ._references import ContainerReference, ViewReference
11
+ from ._view_property import (
12
+ MultiReverseDirectRelationPropertyResponse,
13
+ SingleReverseDirectRelationPropertyResponse,
14
+ ViewCorePropertyRequest,
15
+ ViewCorePropertyResponse,
16
+ ViewRequestProperty,
17
+ ViewResponseProperty,
18
+ )
19
+
20
+
21
+ class View(BaseModelObject, ABC):
22
+ space: str
23
+ external_id: str
24
+ version: str
25
+ name: str | None = None
26
+ description: str | None = None
27
+ filter: JsonValue | None
28
+ implements: list[ViewReference] | None = None
29
+
30
+ def as_id(self) -> ViewReference:
31
+ return ViewReference(space=self.space, external_id=self.external_id, version=self.version)
32
+
33
+ @model_validator(mode="before")
34
+ def set_connection_type_on_primary_properties(cls, data: dict[str, Any]) -> dict[str, Any]:
35
+ if "properties" not in data:
36
+ return data
37
+ properties = data["properties"]
38
+ if not isinstance(properties, dict):
39
+ return data
40
+ # We assume all properties without connectionType are core properties.
41
+ # The reason we set connectionType is to make it easy for pydantic to discriminate the union.
42
+ # This also leads to better error messages, as if there is a union and pydantic does not know which
43
+ # type to pick it will give errors from all type in the union.
44
+ new_properties: dict[str, Any] = {}
45
+ for prop_id, prop in properties.items():
46
+ if isinstance(prop, dict) and "connectionType" not in prop:
47
+ prop_copy = prop.copy()
48
+ prop_copy["connectionType"] = "primary_property"
49
+ new_properties[prop_id] = prop_copy
50
+ else:
51
+ new_properties[prop_id] = prop
52
+ if new_properties:
53
+ new_data = data.copy()
54
+ new_data["properties"] = new_properties
55
+ return new_data
56
+
57
+ return data
58
+
59
+ @field_serializer("implements", mode="plain")
60
+ @classmethod
61
+ def serialize_implements(
62
+ cls, implements: list[ViewReference] | None, info: FieldSerializationInfo
63
+ ) -> list[dict[str, Any]] | None:
64
+ if implements is None:
65
+ return None
66
+ output: list[dict[str, Any]] = []
67
+ for view in implements:
68
+ dumped = view.model_dump(**vars(info))
69
+ dumped["type"] = "view"
70
+ output.append(dumped)
71
+ return output
72
+
73
+
74
+ class ViewRequest(View, RequestResource):
75
+ properties: dict[str, ViewRequestProperty] = Field(
76
+ description="View with included properties and expected edges, indexed by a unique space-local identifier."
77
+ )
78
+
79
+ @property
80
+ def used_containers(self) -> set[ContainerReference]:
81
+ """Get all containers referenced by this view."""
82
+ return {prop.container for prop in self.properties.values() if isinstance(prop, ViewCorePropertyRequest)}
83
+
84
+
85
+ class ViewResponse(View, ResponseResource[ViewRequest]):
86
+ properties: dict[str, ViewResponseProperty]
87
+
88
+ created_time: int
89
+ last_updated_time: int
90
+ writable: bool
91
+ queryable: bool
92
+ used_for: Literal["node", "edge", "all"]
93
+ is_global: bool
94
+ mapped_containers: list[ContainerReference]
95
+
96
+ def as_request_resource(self) -> ViewRequest:
97
+ dumped = self.model_dump(by_alias=True, exclude={"properties"})
98
+ properties: dict[str, Any] = {}
99
+ for key, value in self.properties.items():
100
+ if isinstance(value, ViewCorePropertyResponse) and isinstance(value.type, DirectNodeRelation):
101
+ # Special case. In the request the source of DirectNodeRelation is set on the Property object,
102
+ # while in the response it is set on the DirectNodeRelation object.
103
+ request_object = value.as_request().model_dump(by_alias=True)
104
+ request_object["source"] = value.type.source.model_dump(by_alias=True) if value.type.source else None
105
+ properties[key] = request_object
106
+ elif isinstance(
107
+ value,
108
+ ViewCorePropertyResponse
109
+ | SingleReverseDirectRelationPropertyResponse
110
+ | MultiReverseDirectRelationPropertyResponse,
111
+ ):
112
+ properties[key] = value.as_request().model_dump(by_alias=True)
113
+ else:
114
+ properties[key] = value.model_dump(by_alias=True)
115
+
116
+ dumped["properties"] = properties
117
+ return ViewRequest.model_validate(dumped, extra="ignore")
118
+
119
+ @field_serializer("mapped_containers", mode="plain")
120
+ @classmethod
121
+ def serialize_mapped_containers(
122
+ cls, mapped_containers: list[ContainerReference], info: FieldSerializationInfo
123
+ ) -> list[dict[str, Any]]:
124
+ return [container.model_dump(**vars(info)) | {"type": "container"} for container in mapped_containers]
125
+
126
+ @field_serializer("properties", mode="plain")
127
+ @classmethod
128
+ def serialize_properties_special_handling_direct_relation_with_source(
129
+ cls, properties: dict[str, ViewResponseProperty], info: FieldSerializationInfo
130
+ ) -> dict[str, dict[str, Any]]:
131
+ output: dict[str, dict[str, Any]] = {}
132
+ for prop_id, prop in properties.items():
133
+ output[prop_id] = prop.model_dump(**vars(info))
134
+ if (
135
+ isinstance(prop, ViewCorePropertyResponse)
136
+ and isinstance(prop.type, DirectNodeRelation)
137
+ and prop.type.source
138
+ ):
139
+ # We manually include the source as this is excluded by default. The reason why this is excluded
140
+ # is that the DirectNodeRelation is used for both request and response, and in the request the source
141
+ # does not exist on the DirectNodeRelation, but on the Property object.
142
+ output[prop_id]["type"]["source"] = prop.type.source.model_dump(**vars(info)) | {"type": "view"}
143
+ return output
@@ -0,0 +1,152 @@
1
+ from abc import ABC
2
+ from typing import Annotated, Any, Literal
3
+
4
+ from pydantic import Field, JsonValue, TypeAdapter, field_serializer
5
+ from pydantic_core.core_schema import FieldSerializationInfo
6
+
7
+ from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject
8
+
9
+ from ._data_types import DataType
10
+ from ._references import ContainerDirectReference, ContainerReference, NodeReference, ViewDirectReference, ViewReference
11
+
12
+
13
+ class ViewPropertyDefinition(BaseModelObject, ABC):
14
+ connection_type: str
15
+
16
+
17
+ class ViewCoreProperty(ViewPropertyDefinition, ABC):
18
+ # Core properties do not have connection type in the API, but we add it here such that
19
+ # we can use it as a discriminator in unions. The exclude=True ensures that it is not
20
+ # sent to the API.
21
+ connection_type: Literal["primary_property"] = Field(default="primary_property", exclude=True)
22
+ name: str | None = None
23
+ description: str | None = None
24
+ container: ContainerReference
25
+ container_property_identifier: str
26
+
27
+ @field_serializer("container", mode="plain")
28
+ @classmethod
29
+ def serialize_container(cls, container: ContainerReference, info: FieldSerializationInfo) -> dict[str, Any]:
30
+ return {**container.model_dump(**vars(info)), "type": "container"}
31
+
32
+
33
+ class ViewCorePropertyRequest(ViewCoreProperty):
34
+ source: ViewReference | None = None
35
+
36
+ @field_serializer("source", mode="plain")
37
+ @classmethod
38
+ def serialize_source(cls, source: ViewReference | None, info: FieldSerializationInfo) -> dict[str, Any] | None:
39
+ if source is None:
40
+ return None
41
+ return {**source.model_dump(**vars(info)), "type": "view"}
42
+
43
+
44
+ class ConstraintOrIndexState(BaseModelObject):
45
+ nullability: Literal["current", "pending", "failed"] | None = None
46
+ max_list_size: Literal["current", "pending", "failed"] | None = None
47
+ max_text_size: Literal["current", "pending", "failed"] | None = None
48
+
49
+
50
+ class ViewCorePropertyResponse(ViewCoreProperty):
51
+ immutable: bool | None = None
52
+ nullable: bool | None = None
53
+ auto_increment: bool | None = None
54
+ default_value: str | int | bool | dict[str, JsonValue] | None = None
55
+ constraint_state: ConstraintOrIndexState
56
+ type: DataType
57
+
58
+ def as_request(self) -> ViewCorePropertyRequest:
59
+ return ViewCorePropertyRequest.model_validate(self.model_dump(by_alias=True))
60
+
61
+
62
+ class ConnectionPropertyDefinition(ViewPropertyDefinition, ABC):
63
+ name: str | None = None
64
+ description: str | None = None
65
+
66
+
67
+ class EdgeProperty(ConnectionPropertyDefinition, ABC):
68
+ source: ViewReference
69
+ type: NodeReference
70
+ edge_source: ViewReference | None = None
71
+ direction: Literal["outwards", "inwards"] = "outwards"
72
+
73
+ @field_serializer("source", "edge_source", mode="plain")
74
+ @classmethod
75
+ def serialize_source(cls, source: ViewReference | None, info: FieldSerializationInfo) -> dict[str, Any] | None:
76
+ if source is None:
77
+ return None
78
+ return {**source.model_dump(**vars(info)), "type": "view"}
79
+
80
+
81
+ class SingleEdgeProperty(EdgeProperty):
82
+ connection_type: Literal["single_edge_connection"] = "single_edge_connection"
83
+
84
+
85
+ class MultiEdgeProperty(EdgeProperty):
86
+ connection_type: Literal["multi_edge_connection"] = "multi_edge_connection"
87
+
88
+
89
+ class ReverseDirectRelationProperty(ConnectionPropertyDefinition, ABC):
90
+ source: ViewReference
91
+ through: ContainerDirectReference | ViewDirectReference
92
+
93
+ @field_serializer("source", mode="plain")
94
+ @classmethod
95
+ def serialize_source(cls, source: ViewReference, info: FieldSerializationInfo) -> dict[str, Any]:
96
+ return {**source.model_dump(**vars(info)), "type": "view"}
97
+
98
+ @field_serializer("through", mode="plain")
99
+ @classmethod
100
+ def serialize_through(
101
+ cls, through: ContainerDirectReference | ViewDirectReference, info: FieldSerializationInfo
102
+ ) -> dict[str, Any]:
103
+ output = through.model_dump(**vars(info))
104
+ if isinstance(through, ContainerDirectReference):
105
+ output["source"]["type"] = "container"
106
+ else:
107
+ output["source"]["type"] = "view"
108
+ return output
109
+
110
+
111
+ class SingleReverseDirectRelationPropertyRequest(ReverseDirectRelationProperty):
112
+ connection_type: Literal["single_reverse_direct_relation"] = "single_reverse_direct_relation"
113
+
114
+
115
+ class MultiReverseDirectRelationPropertyRequest(ReverseDirectRelationProperty):
116
+ connection_type: Literal["multi_reverse_direct_relation"] = "multi_reverse_direct_relation"
117
+
118
+
119
+ class SingleReverseDirectRelationPropertyResponse(ReverseDirectRelationProperty):
120
+ connection_type: Literal["single_reverse_direct_relation"] = "single_reverse_direct_relation"
121
+ targets_list: bool
122
+
123
+ def as_request(self) -> SingleReverseDirectRelationPropertyRequest:
124
+ return SingleReverseDirectRelationPropertyRequest.model_validate(self.model_dump(by_alias=True))
125
+
126
+
127
+ class MultiReverseDirectRelationPropertyResponse(ReverseDirectRelationProperty):
128
+ connection_type: Literal["multi_reverse_direct_relation"] = "multi_reverse_direct_relation"
129
+ targets_list: bool
130
+
131
+ def as_request(self) -> MultiReverseDirectRelationPropertyRequest:
132
+ return MultiReverseDirectRelationPropertyRequest.model_validate(self.model_dump(by_alias=True))
133
+
134
+
135
+ ViewRequestProperty = Annotated[
136
+ SingleEdgeProperty
137
+ | MultiEdgeProperty
138
+ | SingleReverseDirectRelationPropertyRequest
139
+ | MultiReverseDirectRelationPropertyRequest
140
+ | ViewCorePropertyRequest,
141
+ Field(discriminator="connection_type"),
142
+ ]
143
+ ViewResponseProperty = Annotated[
144
+ SingleEdgeProperty
145
+ | MultiEdgeProperty
146
+ | SingleReverseDirectRelationPropertyResponse
147
+ | MultiReverseDirectRelationPropertyResponse
148
+ | ViewCorePropertyResponse,
149
+ Field(discriminator="connection_type"),
150
+ ]
151
+
152
+ ViewRequestPropertyAdapter: TypeAdapter[ViewRequestProperty] = TypeAdapter(ViewRequestProperty)
@@ -1,12 +1,15 @@
1
1
  from typing import ClassVar
2
2
 
3
- from cognite_toolkit._cdf_tk.client.data_classes.base import RequestUpdateable, ResponseResource
3
+ from cognite_toolkit._cdf_tk.client.data_classes.base import (
4
+ BaseModelObject,
5
+ RequestUpdateable,
6
+ ResponseResource,
7
+ )
4
8
 
5
9
  from .identifiers import ExternalId, InternalOrExternalId
6
10
 
7
11
 
8
- class EventRequest(RequestUpdateable):
9
- container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "asset_ids"})
12
+ class Event(BaseModelObject):
10
13
  external_id: str | None = None
11
14
  data_set_id: int | None = None
12
15
  start_time: int | None = None
@@ -24,20 +27,14 @@ class EventRequest(RequestUpdateable):
24
27
  return ExternalId(external_id=self.external_id)
25
28
 
26
29
 
27
- class EventResponse(ResponseResource[EventRequest]):
28
- external_id: str | None = None
29
- data_set_id: int | None = None
30
- start_time: int | None = None
31
- end_time: int | None = None
32
- type: str | None = None
33
- subtype: str | None = None
34
- description: str | None = None
35
- metadata: dict[str, str] | None = None
36
- asset_ids: list[int] | None = None
37
- source: str | None = None
30
+ class EventRequest(Event, RequestUpdateable):
31
+ container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "asset_ids"})
32
+
33
+
34
+ class EventResponse(Event, ResponseResource[EventRequest]):
38
35
  id: int
39
36
  created_time: int
40
37
  last_updated_time: int
41
38
 
42
39
  def as_request_resource(self) -> EventRequest:
43
- return EventRequest.model_validate(self.dump())
40
+ return EventRequest.model_validate(self.dump(), extra="ignore")
@@ -2,15 +2,13 @@ from typing import ClassVar, Literal
2
2
 
3
3
  from pydantic import JsonValue
4
4
 
5
- from cognite_toolkit._cdf_tk.client.data_classes.base import RequestUpdateable, ResponseResource
5
+ from cognite_toolkit._cdf_tk.client.data_classes.base import BaseModelObject, RequestUpdateable, ResponseResource
6
6
 
7
7
  from .identifiers import ExternalId
8
8
  from .instance_api import NodeReference
9
9
 
10
10
 
11
- class FileMetadataRequest(RequestUpdateable):
12
- container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "labels", "asset_ids", "security_categories"})
13
- non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"asset_ids", "security_categories"})
11
+ class FileMetadata(BaseModelObject):
14
12
  external_id: str | None = None
15
13
  name: str
16
14
  directory: str | None = None
@@ -31,26 +29,18 @@ class FileMetadataRequest(RequestUpdateable):
31
29
  return ExternalId(external_id=self.external_id)
32
30
 
33
31
 
34
- class FileMetadataResponse(ResponseResource[FileMetadataRequest]):
32
+ class FileMetadataRequest(FileMetadata, RequestUpdateable):
33
+ container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "labels", "asset_ids", "security_categories"})
34
+ non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"asset_ids", "security_categories"})
35
+
36
+
37
+ class FileMetadataResponse(FileMetadata, ResponseResource[FileMetadataRequest]):
35
38
  created_time: int
36
39
  last_updated_time: int
37
40
  uploaded_time: int | None = None
38
41
  uploaded: bool
39
42
  id: int
40
- external_id: str | None = None
41
- name: str
42
- directory: str | None = None
43
43
  instance_id: NodeReference | None = None
44
- source: str | None = None
45
- mime_type: str | None = None
46
- metadata: dict[str, str] | None = None
47
- asset_ids: list[int] | None = None
48
- data_set_id: int | None = None
49
- labels: list[dict[Literal["externalId"], str]] | None = None
50
- geo_location: JsonValue | None = None
51
- source_created_time: int | None = None
52
- source_modified_time: int | None = None
53
- security_categories: list[int] | None = None
54
44
 
55
45
  def as_request_resource(self) -> FileMetadataRequest:
56
46
  return FileMetadataRequest.model_validate(self.dump(), extra="ignore")
@@ -0,0 +1,50 @@
1
+ from typing import Any, Literal
2
+
3
+ from pydantic import Field
4
+
5
+ from cognite_toolkit._cdf_tk.client.data_classes.base import RequestUpdateable, ResponseResource
6
+
7
+ from .identifiers import ExternalId
8
+
9
+
10
+ class SimulatorModelRequest(RequestUpdateable):
11
+ # The 'id' field is not part of the request when creating a new resource,
12
+ # but is needed when updating an existing resource.
13
+ id: int | None = Field(default=None, exclude=True)
14
+ external_id: str
15
+ simulator_external_id: str
16
+ name: str
17
+ description: str | None = None
18
+ data_set_id: int
19
+ type: str
20
+
21
+ def as_id(self) -> ExternalId:
22
+ return ExternalId(external_id=self.external_id)
23
+
24
+ def as_update(self, mode: Literal["patch", "replace"]) -> dict[str, Any]:
25
+ if self.id is None:
26
+ raise ValueError("id must be provided to create an update dictionary")
27
+ return {
28
+ "id": self.id,
29
+ "update": {
30
+ "name": {"set": self.name},
31
+ **{"description": {"set": self.description} if self.description is not None else {}},
32
+ },
33
+ }
34
+
35
+
36
+ class SimulatorModelResponse(ResponseResource[SimulatorModelRequest]):
37
+ id: int
38
+ external_id: str
39
+ simulator_external_id: str
40
+ name: str
41
+ description: str | None = None
42
+ data_set_id: int
43
+ type: str | None = None
44
+ created_time: int
45
+ last_updated_time: int
46
+
47
+ def as_request_resource(self) -> SimulatorModelRequest:
48
+ if self.type is None:
49
+ raise ValueError("Cannot convert SimulatorModelResponse to SimulatorModelRequest when type is None")
50
+ return SimulatorModelRequest.model_validate(self.dump(), extra="ignore")
@@ -1,14 +1,16 @@
1
1
  from typing import Any, ClassVar, Literal
2
2
 
3
- from cognite_toolkit._cdf_tk.client.data_classes.base import RequestUpdateable, ResponseResource
3
+ from cognite_toolkit._cdf_tk.client.data_classes.base import (
4
+ BaseModelObject,
5
+ RequestUpdateable,
6
+ ResponseResource,
7
+ )
4
8
 
5
9
  from .identifiers import ExternalId, InternalOrExternalId
6
10
  from .instance_api import NodeReference
7
11
 
8
12
 
9
- class TimeSeriesRequest(RequestUpdateable):
10
- container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "security_categories"})
11
- non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"is_step"})
13
+ class TimeSeries(BaseModelObject):
12
14
  external_id: str | None = None
13
15
  name: str | None = None
14
16
  is_string: bool = False
@@ -26,30 +28,25 @@ class TimeSeriesRequest(RequestUpdateable):
26
28
  raise ValueError("Cannot convert TimeSeriesRequest to ExternalId when external_id is None")
27
29
  return ExternalId(external_id=self.external_id)
28
30
 
31
+
32
+ class TimeSeriesRequest(TimeSeries, RequestUpdateable):
33
+ container_fields: ClassVar[frozenset[str]] = frozenset({"metadata", "security_categories"})
34
+ non_nullable_fields: ClassVar[frozenset[str]] = frozenset({"is_step"})
35
+
29
36
  def as_update(self, mode: Literal["patch", "replace"]) -> dict[str, Any]:
30
37
  dumped = super().as_update(mode)
38
+ update = dumped["update"] if "update" in dumped else dumped
31
39
  # isString is immutable in CDF, so we remove it from update payloads
32
- dumped.pop("isString", None)
40
+ update.pop("isString", None)
33
41
  return dumped
34
42
 
35
43
 
36
- class TimeSeriesResponse(ResponseResource[TimeSeriesRequest]):
44
+ class TimeSeriesResponse(TimeSeries, ResponseResource[TimeSeriesRequest]):
37
45
  id: int
38
46
  instance_id: NodeReference | None = None
39
- external_id: str | None = None
40
- name: str | None = None
41
- is_string: bool
42
- metadata: dict[str, str] | None = None
43
- unit: str | None = None
44
47
  type: str
45
- unit_external_id: str | None = None
46
- asset_id: int | None = None
47
- is_step: bool
48
- description: str | None = None
49
- security_categories: list[int] | None = None
50
- data_set_id: int | None = None
51
48
  created_time: int
52
49
  last_updated_time: int
53
50
 
54
51
  def as_request_resource(self) -> TimeSeriesRequest:
55
- return TimeSeriesRequest.model_validate(self.dump())
52
+ return TimeSeriesRequest.model_validate(self.dump(), extra="ignore")
@@ -142,7 +142,7 @@ class ToolkitClientMock(CogniteClientMock):
142
142
  self.tool.three_d = MagicMock(spec=ThreeDAPI)
143
143
  self.tool.three_d.models = MagicMock(spec_set=ThreeDModelAPI)
144
144
  self.tool.assets = MagicMock(spec_set=AssetsAPI)
145
- self.tool.time_series = MagicMock(spec_set=TimeSeriesAPI)
145
+ self.tool.timeseries = MagicMock(spec_set=TimeSeriesAPI)
146
146
  self.tool.events = MagicMock(spec_set=EventsAPI)
147
147
 
148
148
  self.streams = MagicMock(spec=StreamsAPI)
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Iterable, Mapping, Set
2
2
  from typing import Any, ClassVar, cast
3
3
 
4
- from cognite.client.data_classes import Annotation, Event, FileMetadata, TimeSeries
4
+ from cognite.client.data_classes import Annotation, FileMetadata
5
5
  from cognite.client.data_classes.data_modeling import (
6
6
  DirectRelation,
7
7
  DirectRelationReference,
@@ -17,10 +17,12 @@ from cognite.client.utils._identifier import InstanceId
17
17
 
18
18
  from cognite_toolkit._cdf_tk.client import ToolkitClient
19
19
  from cognite_toolkit._cdf_tk.client.data_classes.asset import AssetResponse
20
+ from cognite_toolkit._cdf_tk.client.data_classes.event import EventResponse
20
21
  from cognite_toolkit._cdf_tk.client.data_classes.legacy.migration import (
21
22
  AssetCentricId,
22
23
  ResourceViewMappingApply,
23
24
  )
25
+ from cognite_toolkit._cdf_tk.client.data_classes.timeseries import TimeSeriesResponse
24
26
  from cognite_toolkit._cdf_tk.utils.collection import flatten_dict_json_path
25
27
  from cognite_toolkit._cdf_tk.utils.dtype_conversion import (
26
28
  asset_centric_convert_to_primary_property,
@@ -120,12 +122,12 @@ class DirectRelationCache:
120
122
  source_ids.add(resource.source)
121
123
  if resource.asset_ids:
122
124
  asset_ids.update(resource.asset_ids)
123
- elif isinstance(resource, Event):
125
+ elif isinstance(resource, EventResponse):
124
126
  if resource.source:
125
127
  source_ids.add(resource.source)
126
128
  if resource.asset_ids:
127
129
  asset_ids.update(resource.asset_ids)
128
- elif isinstance(resource, TimeSeries):
130
+ elif isinstance(resource, TimeSeriesResponse):
129
131
  if resource.asset_id is not None:
130
132
  asset_ids.add(resource.asset_id)
131
133
  if asset_ids:
@@ -248,9 +250,9 @@ def _lookup_resource_type(resource_type: AssetCentricResourceExtended) -> AssetC
248
250
  return "asset"
249
251
  elif isinstance(resource_type, FileMetadata):
250
252
  return "file"
251
- elif isinstance(resource_type, Event):
253
+ elif isinstance(resource_type, EventResponse):
252
254
  return "event"
253
- elif isinstance(resource_type, TimeSeries):
255
+ elif isinstance(resource_type, TimeSeriesResponse):
254
256
  return "timeseries"
255
257
  elif isinstance(resource_type, Annotation):
256
258
  if resource_type.annotated_resource_type == "file" and resource_type.annotation_type in (