cognite-toolkit 0.7.70__py3-none-any.whl → 0.7.71__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.
@@ -1,35 +1,38 @@
1
- import sys
2
1
  from typing import Any, ClassVar, Literal
3
2
 
4
- from pydantic import JsonValue, field_validator
5
- from pydantic_core.core_schema import ValidationInfo
3
+ from pydantic import JsonValue, model_validator
6
4
 
7
- from cognite_toolkit._cdf_tk.client._resource_base import BaseResourceList, ResponseResource
8
- from cognite_toolkit._cdf_tk.protocols import (
9
- ResourceRequestListProtocol,
10
- ResourceResponseListProtocol,
11
- )
5
+ from cognite_toolkit._cdf_tk.client._resource_base import BaseModelObject
12
6
  from cognite_toolkit._cdf_tk.utils.text import sanitize_instance_external_id
13
7
 
14
- from .instance_api import InstanceRequestResource, ViewReference
15
-
16
- if sys.version_info >= (3, 11):
17
- from typing import Self
18
- else:
19
- from typing_extensions import Self
8
+ from .instance_api import (
9
+ TypedInstanceIdentifier,
10
+ TypedNodeIdentifier,
11
+ TypedViewReference,
12
+ WrappedInstanceListRequest,
13
+ WrappedInstanceListResponse,
14
+ WrappedInstanceRequest,
15
+ WrappedInstanceResponse,
16
+ move_properties,
17
+ )
20
18
 
21
- INFIELD_LOCATION_CONFIG_VIEW_ID = ViewReference(space="cdf_infield", external_id="InFieldLocationConfig", version="v1")
22
- INFIELD_CDM_LOCATION_CONFIG_VIEW_ID = ViewReference(
19
+ INFIELD_LOCATION_CONFIG_VIEW_ID = TypedViewReference(
20
+ space="cdf_infield", external_id="InFieldLocationConfig", version="v1"
21
+ )
22
+ INFIELD_CDM_LOCATION_CONFIG_VIEW_ID = TypedViewReference(
23
23
  space="infield_cdm_source_desc_sche_asset_file_ts", external_id="InFieldCDMLocationConfig", version="v1"
24
24
  )
25
- DATA_EXPLORATION_CONFIG_VIEW_ID = ViewReference(space="cdf_infield", external_id="DataExplorationConfig", version="v1")
25
+ DATA_EXPLORATION_CONFIG_VIEW_ID = TypedViewReference(
26
+ space="cdf_infield", external_id="DataExplorationConfig", version="v1"
27
+ )
26
28
 
27
29
 
28
- class DataExplorationConfig(InstanceRequestResource):
30
+ class DataExplorationConfig(BaseModelObject):
29
31
  """Data Exploration Configuration resource class."""
30
32
 
31
- VIEW_ID: ClassVar[ViewReference] = DATA_EXPLORATION_CONFIG_VIEW_ID
32
- instance_type: Literal["node"] = "node"
33
+ VIEW_ID: ClassVar[TypedViewReference] = DATA_EXPLORATION_CONFIG_VIEW_ID
34
+ space: str | None = None
35
+ external_id: str | None = None
33
36
 
34
37
  observations: dict[str, JsonValue] | None = None
35
38
  activities: dict[str, JsonValue] | None = None
@@ -37,19 +40,19 @@ class DataExplorationConfig(InstanceRequestResource):
37
40
  notifications: dict[str, JsonValue] | None = None
38
41
  assets: dict[str, JsonValue] | None = None
39
42
 
43
+ @model_validator(mode="before")
44
+ @classmethod
45
+ def move_properties(cls, data: dict[str, Any]) -> dict[str, Any]:
46
+ return move_properties(data, cls.VIEW_ID)
40
47
 
41
- class InfieldLocationConfig(
42
- ResponseResource["InfieldLocationConfig"],
43
- InstanceRequestResource,
44
- ):
48
+
49
+ class InFieldLocationConfig(BaseModelObject):
45
50
  """Infield Location Configuration resource class.
46
51
 
47
52
  This class is used for both the response and request resource for Infield Location Configuration nodes.
48
53
  """
49
54
 
50
- VIEW_ID: ClassVar[ViewReference] = INFIELD_LOCATION_CONFIG_VIEW_ID
51
- instance_type: Literal["node"] = "node"
52
-
55
+ VIEW_ID: ClassVar[TypedViewReference] = INFIELD_LOCATION_CONFIG_VIEW_ID
53
56
  root_location_external_id: str | None = None
54
57
  feature_toggles: dict[str, JsonValue] | None = None
55
58
  app_instance_space: str | None = None
@@ -57,48 +60,94 @@ class InfieldLocationConfig(
57
60
  data_filters: dict[str, JsonValue] | None = None
58
61
  data_exploration_config: DataExplorationConfig | None = None
59
62
 
60
- def as_request_resource(self) -> "InfieldLocationConfig":
61
- return self
62
-
63
- def as_write(self) -> Self:
64
- return self
65
-
66
- @field_validator("data_exploration_config", mode="before")
67
- @classmethod
68
- def generate_identifier_if_missing(cls, value: Any, info: ValidationInfo) -> Any:
69
- """We do not require the user to specify the space and externalId for the data exploration config."""
70
- if isinstance(value, dict):
71
- if value.get("space") is None:
72
- value["space"] = info.data["space"]
73
- if value.get("externalId") is None:
74
- external_id = info.data["external_id"]
75
- candidate = f"{external_id}_data_exploration_config"
76
- value["externalId"] = sanitize_instance_external_id(candidate)
77
- return value
78
-
79
-
80
- class InfieldLocationConfigList(
81
- BaseResourceList[InfieldLocationConfig],
82
- ResourceResponseListProtocol,
83
- ResourceRequestListProtocol,
84
- ):
85
- """A list of InfieldLocationConfig objects."""
86
-
87
- _RESOURCE = InfieldLocationConfig
88
-
89
- def as_write(self) -> Self:
90
- return self
91
-
92
-
93
- class InFieldCDMLocationConfig(ResponseResource["InFieldCDMLocationConfig"], InstanceRequestResource):
94
- """InField CDM Location Configuration resource class.
95
-
96
- This class is used for both the response and request resource for InField CDM Location Configuration nodes.
97
- """
98
-
99
- VIEW_ID: ClassVar[ViewReference] = INFIELD_CDM_LOCATION_CONFIG_VIEW_ID
100
- instance_type: Literal["node"] = "node"
101
63
 
64
+ class InFieldLocationConfigRequest(WrappedInstanceListRequest, InFieldLocationConfig):
65
+ def dump_instances(self) -> list[dict[str, Any]]:
66
+ space: str | None = None
67
+ external_id: str | None = None
68
+ if self.data_exploration_config:
69
+ space = self.data_exploration_config.space or self.space
70
+ if self.data_exploration_config.external_id:
71
+ external_id = self.data_exploration_config.external_id
72
+ else:
73
+ candidate = f"{self.external_id}_data_exploration_config"
74
+ external_id = sanitize_instance_external_id(candidate)
75
+
76
+ properties = self.model_dump(
77
+ by_alias=True,
78
+ exclude_unset=True,
79
+ exclude={"data_exploration_config", "instance_type", "space", "external_id"},
80
+ )
81
+ if space and external_id:
82
+ properties["dataExplorationConfig"] = {"space": space, "externalId": external_id}
83
+ output: list[dict[str, Any]] = [
84
+ {
85
+ "instanceType": self.instance_type,
86
+ "space": self.space,
87
+ "externalId": self.external_id,
88
+ "sources": [
89
+ {
90
+ "source": self.VIEW_ID.dump(),
91
+ "properties": properties,
92
+ }
93
+ ],
94
+ }
95
+ ]
96
+ if space and external_id and self.data_exploration_config:
97
+ output.append(
98
+ {
99
+ "instanceType": "node",
100
+ "space": space,
101
+ "externalId": external_id,
102
+ "sources": [
103
+ {
104
+ "source": DataExplorationConfig.VIEW_ID.dump(),
105
+ "properties": self.data_exploration_config.model_dump(
106
+ by_alias=True, exclude_unset=True, exclude={"space", "external_id"}
107
+ ),
108
+ }
109
+ ],
110
+ }
111
+ )
112
+ return output
113
+
114
+ def as_ids(self) -> list[TypedInstanceIdentifier]:
115
+ output: list[TypedInstanceIdentifier] = [self.as_id()]
116
+ if (
117
+ self.data_exploration_config
118
+ and self.data_exploration_config.space
119
+ and self.data_exploration_config.external_id
120
+ ):
121
+ output.append(
122
+ TypedNodeIdentifier(
123
+ space=self.data_exploration_config.space,
124
+ external_id=self.data_exploration_config.external_id,
125
+ )
126
+ )
127
+ return output
128
+
129
+
130
+ class InFieldLocationConfigResponse(WrappedInstanceListResponse, InFieldLocationConfig):
131
+ def as_request_resource(self) -> InFieldLocationConfigRequest:
132
+ return InFieldLocationConfigRequest.model_validate(self.dump(), extra="ignore")
133
+
134
+ def as_ids(self) -> list[TypedInstanceIdentifier]:
135
+ output: list[TypedInstanceIdentifier] = [TypedNodeIdentifier(space=self.space, external_id=self.external_id)]
136
+ if (
137
+ self.data_exploration_config
138
+ and self.data_exploration_config.space
139
+ and self.data_exploration_config.external_id
140
+ ):
141
+ output.append(
142
+ TypedNodeIdentifier(
143
+ space=self.data_exploration_config.space,
144
+ external_id=self.data_exploration_config.external_id,
145
+ )
146
+ )
147
+ return output
148
+
149
+
150
+ class InFieldCDMLocationConfig(BaseModelObject):
102
151
  name: str | None = None
103
152
  description: str | None = None
104
153
  feature_toggles: dict[str, JsonValue] | None = None
@@ -109,8 +158,20 @@ class InFieldCDMLocationConfig(ResponseResource["InFieldCDMLocationConfig"], Ins
109
158
  disciplines: list[dict[str, JsonValue]] | None = None
110
159
  data_exploration_config: dict[str, JsonValue] | None = None
111
160
 
112
- def as_request_resource(self) -> "InFieldCDMLocationConfig":
113
- return self
114
161
 
115
- def as_write(self) -> Self:
116
- return self
162
+ class InFieldCDMLocationConfigRequest(WrappedInstanceRequest, InFieldCDMLocationConfig):
163
+ VIEW_ID: ClassVar[TypedViewReference] = INFIELD_CDM_LOCATION_CONFIG_VIEW_ID
164
+ instance_type: Literal["node"] = "node"
165
+
166
+ def as_id(self) -> TypedNodeIdentifier:
167
+ return TypedNodeIdentifier(space=self.space, external_id=self.external_id)
168
+
169
+
170
+ class InFieldCDMLocationConfigResponse(
171
+ WrappedInstanceResponse[InFieldCDMLocationConfigRequest], InFieldCDMLocationConfig
172
+ ):
173
+ VIEW_ID: ClassVar[TypedViewReference] = INFIELD_CDM_LOCATION_CONFIG_VIEW_ID
174
+ instance_type: Literal["node"] = "node"
175
+
176
+ def as_request_resource(self) -> InFieldCDMLocationConfigRequest:
177
+ return InFieldCDMLocationConfigRequest.model_validate(self.dump(), extra="ignore")
@@ -1,8 +1,14 @@
1
- from typing import Any, ClassVar, Literal, TypeAlias
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, ClassVar, Literal, TypeAlias, TypeVar
2
3
 
3
- from pydantic import ConfigDict, JsonValue, model_serializer
4
+ from pydantic import model_validator
4
5
 
5
- from cognite_toolkit._cdf_tk.client._resource_base import BaseModelObject, Identifier, RequestResource
6
+ from cognite_toolkit._cdf_tk.client._resource_base import (
7
+ BaseModelObject,
8
+ Identifier,
9
+ RequestResource,
10
+ ResponseResource,
11
+ )
6
12
 
7
13
  InstanceType: TypeAlias = Literal["node", "edge"]
8
14
 
@@ -39,6 +45,9 @@ class TypedEdgeIdentifier(TypedInstanceIdentifier):
39
45
  return f"Edge({self.space}, {self.external_id})"
40
46
 
41
47
 
48
+ T_TypedInstanceIdentifier = TypeVar("T_TypedInstanceIdentifier", bound=TypedInstanceIdentifier)
49
+
50
+
42
51
  class InstanceIdentifier(Identifier):
43
52
  space: str
44
53
  external_id: str
@@ -64,7 +73,7 @@ class InstanceResult(BaseModelObject):
64
73
  )
65
74
 
66
75
 
67
- class ViewReference(Identifier):
76
+ class TypedViewReference(Identifier):
68
77
  type: Literal["view"] = "view"
69
78
  space: str
70
79
  external_id: str
@@ -88,108 +97,139 @@ class ViewReference(Identifier):
88
97
  #######################################################
89
98
 
90
99
 
91
- class InstanceRequestResource(RequestResource):
92
- """This is a base class for resources that are Instances."""
100
+ class WrappedInstanceRequest(RequestResource, ABC):
101
+ """This is a base class for resources that are Instances.
102
+ It is used to define resources that are
103
+ """
93
104
 
94
- VIEW_ID: ClassVar[ViewReference]
105
+ VIEW_ID: ClassVar[TypedViewReference]
95
106
  instance_type: InstanceType
96
107
  space: str
97
108
  external_id: str
109
+ existing_version: int | None = None
98
110
 
99
- def as_id(self) -> TypedInstanceIdentifier:
100
- return TypedInstanceIdentifier(
101
- instance_type=self.instance_type,
102
- space=self.space,
103
- external_id=self.external_id,
104
- )
105
-
106
- def as_request_item(self) -> "InstanceRequestItem":
107
- return InstanceRequestItem(
108
- instance_type=self.instance_type,
109
- space=self.space,
110
- external_id=self.external_id,
111
- sources=[InstanceSource(source=self.VIEW_ID, resource=self)],
112
- )
113
-
114
-
115
- class InstanceSource(BaseModelObject):
116
- source: ViewReference
117
- resource: InstanceRequestResource
111
+ def dump(
112
+ self, camel_case: bool = True, exclude_extra: bool = False, context: Literal["api", "toolkit"] = "api"
113
+ ) -> dict[str, Any]:
114
+ """Dump the resource to a dictionary.
118
115
 
119
- @model_serializer(mode="plain")
120
- def serialize_resource(self) -> dict[str, Any]:
121
- properties: dict[str, JsonValue] = {}
122
- for field_id, field in type(self.resource).model_fields.items():
123
- if field_id in InstanceRequestResource.model_fields:
124
- # Skip space, external_id, instance_type
125
- continue
126
- key = field.alias or field_id
127
- properties[key] = self._serialize_property(getattr(self.resource, field_id))
116
+ Args:
117
+ camel_case (bool): Whether to use camelCase for the keys. Default is True.
118
+ exclude_extra (bool): Whether to exclude extra fields not defined in the model. Default is False.
119
+ context (Literal["api", "toolkit"]): The context in which the dump is used. Default is "api".
128
120
 
121
+ """
122
+ exclude: set[str] = set()
123
+ if exclude_extra:
124
+ exclude |= set(self.__pydantic_extra__) if self.__pydantic_extra__ else set()
125
+ if context == "api":
126
+ exclude.update({"existing_version", "instance_type", "space", "external_id"})
127
+ dumped = self.model_dump(mode="json", by_alias=camel_case, exclude_unset=True, exclude=exclude)
128
+ if context == "toolkit":
129
+ return dumped
129
130
  return {
130
- "source": self.source.model_dump(by_alias=True),
131
- "properties": properties,
131
+ "instanceType": self.instance_type,
132
+ "space": self.space,
133
+ "externalId": self.external_id,
134
+ "sources": [
135
+ {
136
+ "source": self.VIEW_ID.dump(camel_case=camel_case, include_type=True),
137
+ "properties": dumped,
138
+ }
139
+ ],
132
140
  }
133
141
 
134
- @classmethod
135
- def _serialize_property(cls, value: Any) -> JsonValue:
136
- """Handles serialization of direct relations."""
137
- if isinstance(value, InstanceRequestResource):
138
- return {"space": value.space, "externalId": value.external_id}
139
- elif isinstance(value, list):
140
- return [cls._serialize_property(v) for v in value]
141
- return value
142
142
 
143
+ T_WrappedInstanceRequest = TypeVar("T_WrappedInstanceRequest", bound=WrappedInstanceRequest)
143
144
 
144
- class InstanceRequestItem(RequestResource):
145
- model_config = ConfigDict(populate_by_name=True)
146
- instance_type: InstanceType
147
- space: str
148
- external_id: str
149
- existing_version: int | None = None
150
- sources: list[InstanceSource] | None = None
151
-
152
- def as_id(self) -> TypedInstanceIdentifier:
153
- return TypedInstanceIdentifier(
154
- instance_type=self.instance_type,
155
- space=self.space,
156
- external_id=self.external_id,
157
- )
158
145
 
159
-
160
- class InstanceResponseItem(BaseModelObject):
146
+ class WrappedInstanceResponse(ResponseResource[T_WrappedInstanceRequest], ABC):
147
+ VIEW_ID: ClassVar[TypedViewReference]
161
148
  instance_type: InstanceType
162
149
  space: str
163
150
  external_id: str
151
+
164
152
  version: int
165
- type: TypedInstanceIdentifier | None = None
166
153
  created_time: int
167
154
  last_updated_time: int
168
155
  deleted_time: int | None = None
169
- properties: dict[str, dict[str, dict[str, JsonValue]]] | None = None
170
156
 
171
- def get_properties_for_source(
172
- self, source: ViewReference, include_identifier: bool = False
173
- ) -> dict[str, JsonValue]:
174
- output: dict[str, JsonValue] = (
175
- {"space": self.space, "externalId": self.external_id} if include_identifier else {}
176
- )
177
- if not self.properties:
178
- return output
179
- if source.space not in self.properties:
180
- return output
181
- space_properties = self.properties[source.space]
182
- view_version = f"{source.external_id}/{source.version}"
183
- output.update(space_properties.get(view_version, {}))
184
- return output
157
+ @model_validator(mode="before")
158
+ def move_properties(cls, values: dict[str, Any]) -> dict[str, Any]:
159
+ """Move properties from sources to the top level."""
160
+ return move_properties(values, cls.VIEW_ID)
185
161
 
186
- def as_id(self) -> TypedInstanceIdentifier:
187
- return TypedInstanceIdentifier(
162
+
163
+ def move_properties(values: dict[str, Any], view_id: TypedViewReference) -> dict[str, Any]:
164
+ """Help function to move properties from properties.space.externalId/version to the top level.
165
+
166
+ It is used in WrappedInstanceResponse to move properties from the response to the top level.
167
+ """
168
+ if "properties" not in values:
169
+ return values
170
+ values_copy = dict(values)
171
+ properties = values_copy.pop("properties")
172
+ if not isinstance(properties, dict) or view_id.space not in properties:
173
+ return values
174
+ view_properties = properties.pop(view_id.space)
175
+ identifier = f"{view_id.external_id}/{view_id.version}"
176
+ if not isinstance(view_properties, dict) or identifier not in view_properties:
177
+ return values
178
+ source_properties = view_properties.pop(identifier)
179
+ values_copy.update(source_properties)
180
+ return values_copy
181
+
182
+
183
+ T_WrappedInstanceResponse = TypeVar("T_WrappedInstanceResponse", bound=WrappedInstanceResponse)
184
+
185
+
186
+ class WrappedInstanceListRequest(RequestResource, ABC):
187
+ VIEW_ID: ClassVar[TypedViewReference]
188
+ instance_type: Literal["node"] = "node"
189
+ space: str
190
+ external_id: str
191
+
192
+ @abstractmethod
193
+ def dump_instances(self) -> list[dict[str, Any]]:
194
+ """Dumps the object to a list of instance request dictionaries."""
195
+ raise NotImplementedError()
196
+
197
+ def as_id(self) -> TypedNodeIdentifier:
198
+ return TypedNodeIdentifier(
188
199
  instance_type=self.instance_type,
189
200
  space=self.space,
190
201
  external_id=self.external_id,
191
202
  )
192
203
 
204
+ @abstractmethod
205
+ def as_ids(self) -> list[TypedInstanceIdentifier]:
206
+ """Convert the response to a list of typed instance identifiers."""
207
+ raise NotImplementedError()
208
+
209
+
210
+ T_InstancesListRequest = TypeVar("T_InstancesListRequest", bound=WrappedInstanceListRequest)
211
+
212
+
213
+ class WrappedInstanceListResponse(ResponseResource[T_InstancesListRequest], ABC):
214
+ VIEW_ID: ClassVar[TypedViewReference]
215
+ instance_type: Literal["node"] = "node"
216
+ space: str
217
+ external_id: str
218
+
219
+ @model_validator(mode="before")
220
+ @classmethod
221
+ def move_properties(cls, values: dict[str, Any]) -> dict[str, Any]:
222
+ """Move properties from sources to the top level."""
223
+ return move_properties(values, cls.VIEW_ID)
224
+
225
+ @abstractmethod
226
+ def as_ids(self) -> list[TypedInstanceIdentifier]:
227
+ """Convert the response to a list of typed instance identifiers."""
228
+ raise NotImplementedError()
229
+
230
+
231
+ T_InstancesListResponse = TypeVar("T_InstancesListResponse", bound=WrappedInstanceListResponse)
232
+
193
233
 
194
234
  class NodeReference(BaseModelObject):
195
235
  space: str