cognite-neat 0.123.39__py3-none-any.whl → 0.123.41__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.

Potentially problematic release.


This version of cognite-neat might be problematic. Click here for more details.

@@ -0,0 +1,3 @@
1
+ from ._base import DMSImporter
2
+
3
+ __all__ = ["DMSImporter"]
@@ -0,0 +1,16 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from cognite.neat._data_model.models.dms import RequestSchema
4
+
5
+
6
+ class DMSImporter(ABC):
7
+ """This is the base class for all DMS importers."""
8
+
9
+ @abstractmethod
10
+ def to_data_model(self) -> RequestSchema:
11
+ """Convert the imported data to a RequestSchema.
12
+
13
+ Returns:
14
+ RequestSchema: The data model as a RequestSchema.
15
+ """
16
+ raise NotImplementedError()
@@ -0,0 +1,67 @@
1
+ from collections import Counter
2
+
3
+ from pydantic import Field, ValidationInfo, field_validator
4
+ from pyparsing import cast
5
+
6
+ from cognite.neat._data_model.models.entities import ConceptEntity
7
+ from cognite.neat._data_model.models.entities._constants import PREFIX_PATTERN, SUFFIX_PATTERN, VERSION_PATTERN
8
+ from cognite.neat._utils.text import humanize_collection
9
+
10
+ from ._base import ResourceMetadata
11
+ from ._property import Property
12
+
13
+
14
+ class Concept(ResourceMetadata):
15
+ space: str = Field(
16
+ description="Id of the space that the concept belongs to.",
17
+ min_length=1,
18
+ max_length=43,
19
+ pattern=PREFIX_PATTERN,
20
+ alias="prefix",
21
+ )
22
+ external_id: str = Field(
23
+ description="External-id of the concept.",
24
+ min_length=1,
25
+ max_length=255,
26
+ pattern=SUFFIX_PATTERN,
27
+ alias="suffix",
28
+ )
29
+ version: str | None = Field(
30
+ default=None,
31
+ description="Version of the concept.",
32
+ max_length=43,
33
+ pattern=VERSION_PATTERN,
34
+ )
35
+
36
+ implements: list[ConceptEntity] | None = Field(
37
+ default=None,
38
+ description="References to the concepts from where this concept will inherit properties.",
39
+ )
40
+
41
+ properties: dict[str, Property] | None = Field(default=None, description="Properties associated with the concept.")
42
+
43
+ @field_validator("implements", mode="after")
44
+ def cannot_implement_itself(cls, value: list[ConceptEntity], info: ValidationInfo) -> list[ConceptEntity]:
45
+ if not value:
46
+ return value
47
+
48
+ this_concept = ConceptEntity(
49
+ prefix=info.data["space"],
50
+ suffix=info.data["external_id"],
51
+ version=cast(str, info.data.get("version")),
52
+ )
53
+
54
+ if this_concept in value:
55
+ raise ValueError("A concept cannot implement itself.")
56
+
57
+ return value
58
+
59
+ @field_validator("implements", mode="after")
60
+ def cannot_have_duplicates(cls, value: list[ConceptEntity], info: ValidationInfo) -> list[ConceptEntity]:
61
+ counts = Counter(value)
62
+ duplicates = {concept for concept, count in counts.items() if count > 1}
63
+
64
+ if duplicates:
65
+ raise ValueError(f"Duplicate concepts found: {humanize_collection(duplicates)}")
66
+
67
+ return value
@@ -0,0 +1,104 @@
1
+ from typing import Any
2
+
3
+ from pydantic import Field, ValidationInfo, field_validator, model_validator
4
+
5
+ from cognite.neat._data_model.models.entities import URI, ConceptEntity, DataType, UnknownEntity
6
+
7
+ from ._base import ResourceMetadata
8
+
9
+
10
+ class Property(ResourceMetadata):
11
+ value_type: DataType | ConceptEntity | UnknownEntity = Field(
12
+ union_mode="left_to_right",
13
+ description="Value type that the property can hold. It takes either subset of XSD type or a class defined.",
14
+ )
15
+ min_count: int | None = Field(
16
+ default=None,
17
+ ge=0,
18
+ description="Minimum number of values that the property can hold. "
19
+ "If no value is provided, the default value is None meaning `0`, "
20
+ "which means that the property is optional.",
21
+ )
22
+ max_count: int | None = Field(
23
+ default=None,
24
+ ge=0,
25
+ description="Maximum number of values that the property can hold. "
26
+ "If no value is provided, the default value is None meaning `inf`, "
27
+ "which means that the property can hold any number of values (listable).",
28
+ )
29
+ default: Any | None = Field(alias="Default", default=None, description="Default value of the property.")
30
+ instance_reference: list[URI] | None = Field(
31
+ default=None,
32
+ description="The URI(s) in the graph to get the value of the property.",
33
+ )
34
+
35
+ @model_validator(mode="after")
36
+ def check_min_max_count(self) -> "Property":
37
+ if self.min_count is not None and self.max_count is not None:
38
+ if self.min_count > self.max_count:
39
+ raise ValueError("min_count must be less than or equal to max_count")
40
+ return self
41
+
42
+ @field_validator("default", mode="after")
43
+ def check_default_value_primitive_type(cls, value: Any, info: ValidationInfo) -> Any:
44
+ if not value:
45
+ return value
46
+
47
+ value_type = info.data.get("value_type")
48
+ if not isinstance(value_type, DataType):
49
+ raise ValueError("Setting default value is only supported for primitive value types.")
50
+ return value
51
+
52
+ @field_validator("default", mode="after")
53
+ def check_default_value_python_type_exists(cls, value: Any, info: ValidationInfo) -> Any:
54
+ if not value:
55
+ return value
56
+
57
+ value_type = info.data.get("value_type")
58
+
59
+ if isinstance(value_type, DataType) and not hasattr(value_type, "python"):
60
+ raise ValueError(
61
+ f"DataType {value_type} does not have a python type defined."
62
+ " Setting default value for property is not possible."
63
+ )
64
+ return value
65
+
66
+ @field_validator("default", mode="after")
67
+ def check_default_value_not_list(cls, value: Any, info: ValidationInfo) -> Any:
68
+ if not value:
69
+ return value
70
+
71
+ if isinstance(value, list):
72
+ raise ValueError("Setting list as default value is not supported.")
73
+ return value
74
+
75
+ @field_validator("default", mode="after")
76
+ def check_default_value_single_valued(cls, value: Any, info: ValidationInfo) -> Any:
77
+ if not value:
78
+ return value
79
+
80
+ max_count = info.data.get("max_count")
81
+
82
+ if max_count is None or max_count > 1:
83
+ raise ValueError(
84
+ "Setting default value is only supported for single-valued properties."
85
+ f" Property has max_count={max_count or 'Inf'}."
86
+ )
87
+ return value
88
+
89
+ @field_validator("default", mode="after")
90
+ def check_default_value_type_match(cls, value: Any, info: ValidationInfo) -> Any:
91
+ if not value:
92
+ return value
93
+
94
+ value_type = info.data.get("value_type")
95
+
96
+ if (
97
+ isinstance(value_type, DataType)
98
+ and hasattr(value_type, "python")
99
+ and not isinstance(value, value_type.python)
100
+ ):
101
+ raise ValueError(
102
+ f"Default value type is {type(value)}, which does not match expected value type {value_type.python}."
103
+ )
104
+ return value
@@ -32,15 +32,53 @@ from cognite.neat._data_model.models.dms._data_types import (
32
32
  from cognite.neat._data_model.models.dms._indexes import BtreeIndex, Index, IndexDefinition, InvertedIndex
33
33
  from cognite.neat._data_model.models.dms._space import Space, SpaceRequest, SpaceResponse
34
34
 
35
+ from ._data_model import DataModelRequest, DataModelResponse
36
+ from ._references import (
37
+ ContainerDirectReference,
38
+ ContainerReference,
39
+ DataModelReference,
40
+ NodeReference,
41
+ ViewDirectReference,
42
+ ViewReference,
43
+ )
44
+ from ._schema import RequestSchema
45
+ from ._view_property import (
46
+ ConnectionPropertyDefinition,
47
+ ConstraintOrIndexState,
48
+ MultiEdgeProperty,
49
+ MultiReverseDirectRelationPropertyRequest,
50
+ MultiReverseDirectRelationPropertyResponse,
51
+ SingleEdgeProperty,
52
+ SingleReverseDirectRelationPropertyRequest,
53
+ SingleReverseDirectRelationPropertyResponse,
54
+ ViewCorePropertyRequest,
55
+ ViewCorePropertyResponse,
56
+ ViewPropertyDefinition,
57
+ ViewRequestProperty,
58
+ ViewResponseProperty,
59
+ )
60
+ from ._views import (
61
+ View,
62
+ ViewRequest,
63
+ ViewResponse,
64
+ )
65
+
35
66
  __all__ = [
36
67
  "BooleanProperty",
37
68
  "BtreeIndex",
69
+ "ConnectionPropertyDefinition",
38
70
  "Constraint",
39
71
  "ConstraintDefinition",
72
+ "ConstraintOrIndexState",
40
73
  "Container",
74
+ "ContainerDirectReference",
41
75
  "ContainerPropertyDefinition",
76
+ "ContainerReference",
42
77
  "ContainerRequest",
43
78
  "ContainerResponse",
79
+ "DataModelReference",
80
+ "DataModelRequest",
81
+ "DataModelResponse",
44
82
  "DataType",
45
83
  "DateProperty",
46
84
  "DirectNodeRelation",
@@ -54,16 +92,39 @@ __all__ = [
54
92
  "InvertedIndex",
55
93
  "JSONProperty",
56
94
  "ListablePropertyTypeDefinition",
95
+ "MultiEdgeProperty",
96
+ "MultiReverseDirectRelationPropertyRequest",
97
+ "MultiReverseDirectRelationPropertyResponse",
98
+ "NodeReference",
57
99
  "PropertyTypeDefinition",
100
+ "RequestSchema",
58
101
  "RequiresConstraintDefinition",
59
102
  "Resource",
60
103
  "SequenceCDFExternalIdReference",
104
+ "SequenceCDFExternalIdReference",
105
+ "SingleEdgeProperty",
106
+ "SingleReverseDirectRelationPropertyRequest",
107
+ "SingleReverseDirectRelationPropertyResponse",
61
108
  "Space",
62
109
  "SpaceRequest",
63
110
  "SpaceResponse",
64
111
  "TextProperty",
112
+ "TextProperty",
113
+ "TimeseriesCDFExternalIdReference",
65
114
  "TimeseriesCDFExternalIdReference",
66
115
  "TimestampProperty",
116
+ "TimestampProperty",
117
+ "UniquenessConstraintDefinition",
67
118
  "UniquenessConstraintDefinition",
119
+ "View",
120
+ "ViewCorePropertyRequest",
121
+ "ViewCorePropertyResponse",
122
+ "ViewDirectReference",
123
+ "ViewPropertyDefinition",
124
+ "ViewReference",
125
+ "ViewRequest",
126
+ "ViewRequestProperty",
127
+ "ViewResponse",
128
+ "ViewResponseProperty",
68
129
  "WriteableResource",
69
130
  ]
@@ -3,6 +3,7 @@ DM_EXTERNAL_ID_PATTERN = r"^[a-zA-Z]([a-zA-Z0-9_]{0,253}[a-zA-Z0-9])?$"
3
3
  CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN = r"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,253}[a-zA-Z0-9]?$"
4
4
  INSTANCE_ID_PATTERN = r"^[^\\x00]{1,256}$"
5
5
  ENUM_VALUE_IDENTIFIER_PATTERN = r"^[_A-Za-z][_0-9A-Za-z]{0,127}$"
6
+ DM_VERSION_PATTERN = r"^[a-zA-Z0-9]([.a-zA-Z0-9_-]{0,41}[a-zA-Z0-9])?$"
6
7
  FORBIDDEN_ENUM_VALUES = frozenset({"true", "false", "null"})
7
8
  FORBIDDEN_SPACES = frozenset(["space", "cdf", "dms", "pg3", "shared", "system", "node", "edge"])
8
9
  FORBIDDEN_CONTAINER_AND_VIEW_EXTERNAL_IDS = frozenset(
@@ -18,6 +18,7 @@ from ._constants import (
18
18
  from ._constraints import Constraint
19
19
  from ._data_types import DataType
20
20
  from ._indexes import Index
21
+ from ._references import ContainerReference
21
22
 
22
23
  KEY_PATTERN = re.compile(CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN)
23
24
 
@@ -132,6 +133,12 @@ class Container(Resource, ABC):
132
133
  )
133
134
  return val
134
135
 
136
+ def as_reference(self) -> ContainerReference:
137
+ return ContainerReference(
138
+ space=self.space,
139
+ externalId=self.external_id,
140
+ )
141
+
135
142
 
136
143
  class ContainerRequest(Container): ...
137
144
 
@@ -0,0 +1,78 @@
1
+ from abc import ABC
2
+
3
+ from pydantic import Field
4
+
5
+ from ._base import Resource, WriteableResource
6
+ from ._constants import (
7
+ DM_EXTERNAL_ID_PATTERN,
8
+ DM_VERSION_PATTERN,
9
+ SPACE_FORMAT_PATTERN,
10
+ )
11
+ from ._references import DataModelReference, ViewReference
12
+
13
+
14
+ class DataModel(Resource, ABC):
15
+ """Cognite Data Model resource.
16
+
17
+ Data models group and structure views into reusable collections.
18
+ A data model contains a set of views where the node types can
19
+ refer to each other with direct relations and edges.
20
+ """
21
+
22
+ space: str = Field(
23
+ description="The workspace for the data model, a unique identifier for the space.",
24
+ min_length=1,
25
+ max_length=43,
26
+ pattern=SPACE_FORMAT_PATTERN,
27
+ )
28
+ external_id: str = Field(
29
+ description="External-id of the data model.",
30
+ min_length=1,
31
+ max_length=255,
32
+ pattern=DM_EXTERNAL_ID_PATTERN,
33
+ )
34
+ version: str = Field(
35
+ description="Version of the data model.",
36
+ max_length=43,
37
+ pattern=DM_VERSION_PATTERN,
38
+ )
39
+ name: str | None = Field(
40
+ default=None,
41
+ description="Human readable name for the data model.",
42
+ max_length=255,
43
+ )
44
+ description: str | None = Field(
45
+ default=None,
46
+ description="Description of the data model.",
47
+ max_length=1024,
48
+ )
49
+ # The API supports View here, but in Neat we will only use ViewReference
50
+ views: list[ViewReference] | None = Field(
51
+ description="List of views included in this data model.",
52
+ default=None,
53
+ )
54
+
55
+ def as_reference(self) -> DataModelReference:
56
+ return DataModelReference(
57
+ space=self.space,
58
+ externalId=self.external_id,
59
+ version=self.version,
60
+ )
61
+
62
+
63
+ class DataModelRequest(DataModel): ...
64
+
65
+
66
+ class DataModelResponse(DataModel, WriteableResource[DataModelRequest]):
67
+ created_time: int = Field(
68
+ description="When the data model was created. The number of milliseconds since 00:00:00 Thursday, "
69
+ "1 January 1970, Coordinated Universal Time (UTC), minus leap seconds."
70
+ )
71
+ last_updated_time: int = Field(
72
+ description="When the data model was last updated. The number of milliseconds since 00:00:00 Thursday, "
73
+ "1 January 1970, Coordinated Universal Time (UTC), minus leap seconds."
74
+ )
75
+ is_global: bool = Field(description="Is this a global data model.")
76
+
77
+ def as_request(self) -> DataModelRequest:
78
+ return DataModelRequest.model_validate(self.model_dump(by_alias=True))
@@ -3,10 +3,19 @@ from typing import Literal
3
3
  from pydantic import Field
4
4
 
5
5
  from ._base import BaseModelObject
6
- from ._constants import DM_EXTERNAL_ID_PATTERN, SPACE_FORMAT_PATTERN
6
+ from ._constants import (
7
+ CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN,
8
+ DM_EXTERNAL_ID_PATTERN,
9
+ DM_VERSION_PATTERN,
10
+ INSTANCE_ID_PATTERN,
11
+ SPACE_FORMAT_PATTERN,
12
+ )
7
13
 
8
14
 
9
- class ContainerReference(BaseModelObject):
15
+ class ReferenceObject(BaseModelObject, frozen=True): ...
16
+
17
+
18
+ class ContainerReference(ReferenceObject):
10
19
  type: Literal["container"] = "container"
11
20
  space: str = Field(
12
21
  description="Id of the space hosting (containing) the container.",
@@ -20,3 +29,83 @@ class ContainerReference(BaseModelObject):
20
29
  max_length=255,
21
30
  pattern=DM_EXTERNAL_ID_PATTERN,
22
31
  )
32
+
33
+
34
+ class ViewReference(ReferenceObject):
35
+ type: Literal["view"] = "view"
36
+ space: str = Field(
37
+ description="Id of the space that the view belongs to.",
38
+ min_length=1,
39
+ max_length=43,
40
+ pattern=SPACE_FORMAT_PATTERN,
41
+ )
42
+ external_id: str = Field(
43
+ description="External-id of the view.",
44
+ min_length=1,
45
+ max_length=255,
46
+ pattern=DM_EXTERNAL_ID_PATTERN,
47
+ )
48
+ version: str = Field(
49
+ description="Version of the view.",
50
+ max_length=43,
51
+ pattern=DM_VERSION_PATTERN,
52
+ )
53
+
54
+
55
+ class DataModelReference(ReferenceObject):
56
+ space: str = Field(
57
+ description="Id of the space that the data model belongs to.",
58
+ min_length=1,
59
+ max_length=43,
60
+ pattern=SPACE_FORMAT_PATTERN,
61
+ )
62
+ external_id: str = Field(
63
+ description="External-id of the data model.",
64
+ min_length=1,
65
+ max_length=255,
66
+ pattern=DM_EXTERNAL_ID_PATTERN,
67
+ )
68
+ version: str = Field(
69
+ description="Version of the data model.",
70
+ max_length=43,
71
+ pattern=DM_VERSION_PATTERN,
72
+ )
73
+
74
+
75
+ class NodeReference(ReferenceObject):
76
+ space: str = Field(
77
+ description="Id of the space hosting (containing) the node.",
78
+ min_length=1,
79
+ max_length=43,
80
+ pattern=SPACE_FORMAT_PATTERN,
81
+ )
82
+ external_id: str = Field(
83
+ description="External-id of the node.",
84
+ min_length=1,
85
+ max_length=255,
86
+ pattern=INSTANCE_ID_PATTERN,
87
+ )
88
+
89
+
90
+ class ContainerDirectReference(ReferenceObject):
91
+ source: ContainerReference = Field(
92
+ description="Reference to the container from where this relation is inherited.",
93
+ )
94
+ identifier: str = Field(
95
+ description="Identifier of the relation in the source container.",
96
+ min_length=1,
97
+ max_length=255,
98
+ pattern=CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN,
99
+ )
100
+
101
+
102
+ class ViewDirectReference(ReferenceObject):
103
+ source: ViewReference = Field(
104
+ description="Reference to the view from where this relation is inherited.",
105
+ )
106
+ identifier: str = Field(
107
+ description="Identifier of the relation in the source view.",
108
+ min_length=1,
109
+ max_length=255,
110
+ pattern=CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN,
111
+ )
@@ -0,0 +1,18 @@
1
+ from pydantic import Field
2
+
3
+ from ._base import Resource
4
+ from ._container import ContainerRequest
5
+ from ._data_model import DataModelRequest
6
+ from ._references import NodeReference
7
+ from ._space import SpaceRequest
8
+ from ._views import ViewRequest
9
+
10
+
11
+ class RequestSchema(Resource):
12
+ """Represents a schema for creating or updating a data model in DMS."""
13
+
14
+ data_model: DataModelRequest
15
+ views: list[ViewRequest] = Field(default_factory=list)
16
+ containers: list[ContainerRequest] = Field(default_factory=list)
17
+ spaces: list[SpaceRequest] = Field(default_factory=list)
18
+ node_types: list[NodeReference] = Field(default_factory=list)
@@ -0,0 +1,198 @@
1
+ from abc import ABC
2
+ from typing import Annotated, Literal
3
+
4
+ from pydantic import Field, Json
5
+
6
+ from ._base import BaseModelObject, Resource, WriteableResource
7
+ from ._constants import CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN
8
+ from ._data_types import DataType
9
+ from ._references import ContainerDirectReference, ContainerReference, NodeReference, ViewDirectReference, ViewReference
10
+
11
+
12
+ class ViewPropertyDefinition(Resource, ABC):
13
+ connection_type: str
14
+
15
+
16
+ class ViewCoreProperty(ViewPropertyDefinition, ABC):
17
+ # Core properties do not have connection type in the API, but we add it here such that
18
+ # we can use it as a discriminator in unions. The exclude=True ensures that it is not
19
+ # sent to the API.
20
+ connection_type: Literal["primary_property"] = Field(default="primary_property", exclude=True)
21
+ name: str | None = Field(
22
+ default=None,
23
+ description="Readable property name.",
24
+ max_length=255,
25
+ )
26
+ description: str | None = Field(
27
+ default=None,
28
+ description="Description of the content and suggested use for this property..",
29
+ max_length=1024,
30
+ )
31
+ container: ContainerReference = Field(
32
+ description="Reference to the container where this property is defined.",
33
+ )
34
+ container_property_identifier: str = Field(
35
+ description="Identifier of the property in the container.",
36
+ min_length=1,
37
+ max_length=255,
38
+ pattern=CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN,
39
+ )
40
+ source: ViewReference | None = Field(
41
+ default=None,
42
+ description="Indicates on what type a referenced direct relation is expected to be. "
43
+ "Only applicable for direct relation properties.",
44
+ )
45
+
46
+
47
+ class ViewCorePropertyRequest(ViewCoreProperty): ...
48
+
49
+
50
+ class ConstraintOrIndexState(BaseModelObject):
51
+ nullability: Literal["current", "pending", "failed"] | None = Field(
52
+ description="""For properties that have isNullable set to false, this field describes the validity of the
53
+ not-null constraint. It is not specified for nullable properties.
54
+
55
+ Possible values are:
56
+
57
+ "failed": The property contains null values, violating the constraint. This can occur if a property with
58
+ existing nulls was made non-nullable. New null values will still be rejected.
59
+ "current": The constraint is satisfied; all values in the property are not null.
60
+ "pending": The constraint validity has not yet been computed.
61
+ """
62
+ )
63
+
64
+
65
+ class ViewCorePropertyResponse(ViewCoreProperty, WriteableResource[ViewCorePropertyRequest]):
66
+ immutable: bool | None = Field(
67
+ default=None,
68
+ description="Should updates to this property be rejected after the initial population?",
69
+ )
70
+ nullable: bool | None = Field(
71
+ default=None,
72
+ description="Does this property need to be set to a value, or not?",
73
+ )
74
+ auto_increment: bool | None = Field(
75
+ default=None,
76
+ description="Increment the property based on its highest current value (max value).",
77
+ )
78
+ default_value: str | int | bool | dict[str, Json] | None = Field(
79
+ default=None,
80
+ description="Default value to use when you do not specify a value for the property.",
81
+ )
82
+ constraint_state: ConstraintOrIndexState = Field(
83
+ description="Describes the validity of constraints defined on this property"
84
+ )
85
+ type: DataType = Field(description="The type of data you can store in this property.")
86
+
87
+ def as_request(self) -> ViewCorePropertyRequest:
88
+ return ViewCorePropertyRequest.model_validate(self.model_dump(by_alias=True))
89
+
90
+
91
+ class ConnectionPropertyDefinition(ViewPropertyDefinition, ABC):
92
+ name: str | None = Field(
93
+ default=None,
94
+ description="Readable property name.",
95
+ max_length=255,
96
+ )
97
+ description: str | None = Field(
98
+ default=None,
99
+ description="Description of the content and suggested use for this property..",
100
+ max_length=1024,
101
+ )
102
+
103
+
104
+ class EdgeProperty(ConnectionPropertyDefinition, ABC):
105
+ source: ViewReference = Field(
106
+ description="The target node(s) of this connection can be read through the view specified in 'source'."
107
+ )
108
+ type: NodeReference = Field(
109
+ description="Reference to the node pointed to by the direct relation. The reference consists of a "
110
+ "space and an external-id."
111
+ )
112
+ edge_source: ViewReference | None = Field(
113
+ None, description="The edge(s) of this connection can be read through the view specified in 'edgeSource'."
114
+ )
115
+ direction: Literal["outwards", "inwards"] = Field(
116
+ "outwards", description="The direction of the edge(s) of this connection."
117
+ )
118
+
119
+
120
+ class SingleEdgeProperty(EdgeProperty):
121
+ connection_type: Literal["single_edge_connection"] = "single_edge_connection"
122
+
123
+
124
+ class MultiEdgeProperty(EdgeProperty):
125
+ connection_type: Literal["multi_edge_connection"] = "multi_edge_connection"
126
+
127
+
128
+ class ReverseDirectRelationProperty(ConnectionPropertyDefinition, ABC):
129
+ source: ViewReference = Field(
130
+ description="The node(s) containing the direct relation property can be read "
131
+ "through the view specified in 'source'."
132
+ )
133
+
134
+
135
+ class SingleReverseDirectRelationPropertyRequest(ReverseDirectRelationProperty):
136
+ connection_type: Literal["single_reverse_direct_relation"] = "single_reverse_direct_relation"
137
+ # The API support through as either ViewDirectReference or ContainerDirectReference. However, in Neat
138
+ # we only use ContainerDirectReference. This is for simplicity and it improves performance as the server
139
+ # does not have to resolve the view to a container first.
140
+ through: ContainerDirectReference = Field(
141
+ description="The view of the node containing the direct relation property."
142
+ )
143
+
144
+
145
+ class SingleReverseDirectRelationPropertyResponse(
146
+ ReverseDirectRelationProperty, WriteableResource[SingleReverseDirectRelationPropertyRequest]
147
+ ):
148
+ connection_type: Literal["single_reverse_direct_relation"] = "single_reverse_direct_relation"
149
+ through: ContainerDirectReference | ViewDirectReference = Field(
150
+ description="The view of the node containing the direct relation property."
151
+ )
152
+
153
+ def as_request(self) -> SingleReverseDirectRelationPropertyRequest:
154
+ if isinstance(self.through, ViewDirectReference):
155
+ raise TypeError("Cannot convert to request when 'through' is a ViewDirectReference.")
156
+ return SingleReverseDirectRelationPropertyRequest.model_validate(self.model_dump(by_alias=True))
157
+
158
+
159
+ class MultiReverseDirectRelationPropertyRequest(ReverseDirectRelationProperty):
160
+ connection_type: Literal["multi_reverse_direct_relation"] = "multi_reverse_direct_relation"
161
+ # The API support through as either ViewDirectReference or ContainerDirectReference. However, in Neat
162
+ # we only use ContainerDirectReference. This is for simplicity and it improves performance as the server
163
+ # does not have to resolve the view to a container first.
164
+ through: ContainerDirectReference = Field(
165
+ description="The view of the node containing the direct relation property."
166
+ )
167
+
168
+
169
+ class MultiReverseDirectRelationPropertyResponse(
170
+ ReverseDirectRelationProperty, WriteableResource[MultiReverseDirectRelationPropertyRequest]
171
+ ):
172
+ connection_type: Literal["multi_reverse_direct_relation"] = "multi_reverse_direct_relation"
173
+ through: ContainerDirectReference | ViewDirectReference = Field(
174
+ description="The view of the node containing the direct relation property."
175
+ )
176
+
177
+ def as_request(self) -> MultiReverseDirectRelationPropertyRequest:
178
+ if isinstance(self.through, ViewDirectReference):
179
+ raise TypeError("Cannot convert to request when 'through' is a ViewDirectReference.")
180
+ return MultiReverseDirectRelationPropertyRequest.model_validate(self.model_dump(by_alias=True))
181
+
182
+
183
+ ViewRequestProperty = Annotated[
184
+ SingleEdgeProperty
185
+ | MultiEdgeProperty
186
+ | SingleReverseDirectRelationPropertyRequest
187
+ | MultiReverseDirectRelationPropertyRequest
188
+ | ViewCorePropertyRequest,
189
+ Field(discriminator="connection_type"),
190
+ ]
191
+ ViewResponseProperty = Annotated[
192
+ SingleEdgeProperty
193
+ | MultiEdgeProperty
194
+ | SingleReverseDirectRelationPropertyResponse
195
+ | MultiReverseDirectRelationPropertyResponse
196
+ | ViewCorePropertyResponse,
197
+ Field(discriminator="connection_type"),
198
+ ]
@@ -0,0 +1,164 @@
1
+ import re
2
+ from abc import ABC
3
+ from typing import Literal, TypeVar
4
+
5
+ from pydantic import Field, Json, field_validator, model_validator
6
+
7
+ from cognite.neat._utils.text import humanize_collection
8
+
9
+ from ._base import Resource, WriteableResource
10
+ from ._constants import (
11
+ CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN,
12
+ DM_EXTERNAL_ID_PATTERN,
13
+ DM_VERSION_PATTERN,
14
+ FORBIDDEN_CONTAINER_AND_VIEW_EXTERNAL_IDS,
15
+ FORBIDDEN_CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER,
16
+ SPACE_FORMAT_PATTERN,
17
+ )
18
+ from ._references import ContainerReference, ViewReference
19
+ from ._view_property import (
20
+ ViewRequestProperty,
21
+ ViewResponseProperty,
22
+ )
23
+
24
+ KEY_PATTERN = re.compile(CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER_PATTERN)
25
+
26
+
27
+ class View(Resource, ABC):
28
+ space: str = Field(
29
+ description="Id of the space that the view belongs to.",
30
+ min_length=1,
31
+ max_length=43,
32
+ pattern=SPACE_FORMAT_PATTERN,
33
+ )
34
+ external_id: str = Field(
35
+ description="External-id of the view.",
36
+ min_length=1,
37
+ max_length=255,
38
+ pattern=DM_EXTERNAL_ID_PATTERN,
39
+ )
40
+ version: str = Field(
41
+ description="Version of the view.",
42
+ max_length=43,
43
+ pattern=DM_VERSION_PATTERN,
44
+ )
45
+ name: str | None = Field(
46
+ default=None,
47
+ description="name for the view.",
48
+ max_length=255,
49
+ )
50
+ description: str | None = Field(
51
+ default=None,
52
+ description="Description of the view.",
53
+ max_length=1024,
54
+ )
55
+ filter: dict[str, Json] | None = Field(
56
+ default=None,
57
+ description="A filter Domain Specific Language (DSL) used to create advanced filter queries.",
58
+ )
59
+ implements: list[ViewReference] | None = Field(
60
+ default=None,
61
+ description="References to the views from where this view will inherit properties.",
62
+ )
63
+
64
+ def as_reference(self) -> ViewReference:
65
+ return ViewReference(space=self.space, externalId=self.external_id, version=self.version)
66
+
67
+ @model_validator(mode="before")
68
+ def set_connection_type_on_primary_properties(cls, data: dict) -> dict:
69
+ if "properties" not in data:
70
+ return data
71
+ properties = data["properties"]
72
+ if not isinstance(properties, dict):
73
+ return data
74
+ # We assume all properties without connectionType are core properties.
75
+ # The reason we set connectionType it easy for pydantic to discriminate the union.
76
+ # This also leads to better error messages, as if there is a union and pydantic do not know which
77
+ # type to pick it will give errors from all type in the union.
78
+ for prop in properties.values():
79
+ if isinstance(prop, dict) and "connectionType" not in prop:
80
+ prop["connectionType"] = "primary_property"
81
+ return data
82
+
83
+ @field_validator("external_id", mode="after")
84
+ def check_forbidden_external_id_value(cls, val: str) -> str:
85
+ """Check the external_id not present in forbidden set"""
86
+ if val in FORBIDDEN_CONTAINER_AND_VIEW_EXTERNAL_IDS:
87
+ raise ValueError(
88
+ f"'{val}' is a reserved view External ID. Reserved External IDs are: "
89
+ f"{humanize_collection(FORBIDDEN_CONTAINER_AND_VIEW_EXTERNAL_IDS)}"
90
+ )
91
+ return val
92
+
93
+
94
+ class ViewRequest(View):
95
+ properties: dict[str, ViewRequestProperty] = Field(
96
+ description="View with included properties and expected edges, indexed by a unique space-local identifier."
97
+ )
98
+
99
+ @field_validator("properties", mode="after")
100
+ def validate_properties_identifier(cls, val: dict[str, ViewRequestProperty]) -> dict[str, ViewRequestProperty]:
101
+ """Validate properties Identifier"""
102
+ return _validate_properties_keys(val)
103
+
104
+
105
+ class ViewResponse(View, WriteableResource[ViewRequest]):
106
+ properties: dict[str, ViewResponseProperty] = Field(
107
+ description="List of properties and connections included in this view."
108
+ )
109
+
110
+ created_time: int = Field(
111
+ description="When the view was created. The number of milliseconds since 00:00:00 Thursday, 1 January 1970, "
112
+ "Coordinated Universal Time (UTC), minus leap seconds."
113
+ )
114
+ last_updated_time: int = Field(
115
+ description="When the view was last updated. The number of milliseconds since 00:00:00 Thursday, "
116
+ "1 January 1970, Coordinated Universal Time (UTC), minus leap seconds."
117
+ )
118
+ writable: bool = Field(
119
+ description="oes the view support write operations, i.e. is it writable? "
120
+ "You can write to a view if the view maps all non-nullable properties."
121
+ )
122
+ queryable: bool = Field(
123
+ description="Does the view support queries, i.e. is it queryable? You can query a view if "
124
+ "it either has a filter or at least one property mapped to a container."
125
+ )
126
+ used_for: Literal["node", "edge", "all"] = Field(description="Should this operation apply to nodes, edges or both.")
127
+ is_global: bool = Field(description="Is this a global view.")
128
+ mapped_containers: list[ContainerReference] = Field(
129
+ description="List of containers with properties mapped by this view."
130
+ )
131
+
132
+ @field_validator("properties", mode="after")
133
+ def validate_properties_identifier(cls, val: dict[str, ViewResponseProperty]) -> dict[str, ViewResponseProperty]:
134
+ """Validate properties Identifier"""
135
+ return _validate_properties_keys(val)
136
+
137
+ def as_request(self) -> ViewRequest:
138
+ dumped = self.model_dump(by_alias=True, exclude={"properties"})
139
+ dumped["properties"] = {
140
+ key: value.as_request().model_dump(by_alias=True)
141
+ if isinstance(value, WriteableResource)
142
+ else value.model_dump(by_alias=True)
143
+ for key, value in self.properties.items()
144
+ }
145
+ return ViewRequest.model_validate(dumped)
146
+
147
+
148
+ T_Property = TypeVar("T_Property")
149
+
150
+
151
+ def _validate_properties_keys(properties: dict[str, T_Property]) -> dict[str, T_Property]:
152
+ """Validate keys of a properties dictionary."""
153
+ errors: list[str] = []
154
+ for key in properties:
155
+ if not KEY_PATTERN.match(key):
156
+ errors.append(f"Property '{key}' does not match the required pattern: {KEY_PATTERN.pattern}")
157
+ if key in FORBIDDEN_CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER:
158
+ errors.append(
159
+ f"'{key}' is a reserved property identifier. Reserved identifiers are: "
160
+ f"{humanize_collection(FORBIDDEN_CONTAINER_AND_VIEW_PROPERTIES_IDENTIFIER)}"
161
+ )
162
+ if errors:
163
+ raise ValueError("; ".join(errors))
164
+ return properties
@@ -4,6 +4,9 @@ from typing import Any
4
4
  from pydantic import BaseModel, Field, field_validator, model_serializer
5
5
 
6
6
  from ._constants import (
7
+ PREFIX_PATTERN,
8
+ SUFFIX_PATTERN,
9
+ VERSION_PATTERN,
7
10
  Undefined,
8
11
  Unknown,
9
12
  _UndefinedType,
@@ -15,10 +18,8 @@ from ._constants import (
15
18
  class Entity(BaseModel, extra="ignore", populate_by_name=True):
16
19
  """Entity is a concept, class, property, datatype in semantics sense."""
17
20
 
18
- prefix: str | _UndefinedType = Field(
19
- default=Undefined, pattern=r"^[a-zA-Z][a-zA-Z0-9_-]{0,41}[a-zA-Z0-9]?$", min_length=1, max_length=43
20
- )
21
- suffix: str = Field(min_length=1, max_length=255, pattern=r"^[a-zA-Z0-9._~?@!$&'*+,;=%-]+$")
21
+ prefix: str | _UndefinedType = Field(default=Undefined, pattern=PREFIX_PATTERN, min_length=1, max_length=43)
22
+ suffix: str = Field(min_length=1, max_length=255, pattern=SUFFIX_PATTERN)
22
23
 
23
24
  @model_serializer(when_used="unless-none", return_type=str)
24
25
  def as_str(self) -> str:
@@ -88,7 +89,7 @@ class Entity(BaseModel, extra="ignore", populate_by_name=True):
88
89
 
89
90
 
90
91
  class ConceptEntity(Entity):
91
- version: str | None = None
92
+ version: str | None = Field(default=None, pattern=VERSION_PATTERN, max_length=43)
92
93
 
93
94
 
94
95
  class UnknownEntity(ConceptEntity):
@@ -15,3 +15,8 @@ class _UnknownType(BaseModel):
15
15
  # This is a trick to make Undefined and Unknown singletons
16
16
  Undefined = _UndefinedType()
17
17
  Unknown = _UnknownType()
18
+
19
+
20
+ PREFIX_PATTERN = r"^[a-zA-Z][a-zA-Z0-9_-]{0,41}[a-zA-Z0-9]?$"
21
+ SUFFIX_PATTERN = r"^[a-zA-Z0-9._~?@!$&'*+,;=%-]+$"
22
+ VERSION_PATTERN = r"^[a-zA-Z0-9]([.a-zA-Z0-9_-]{0,41}[a-zA-Z0-9])?$"
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.123.39"
1
+ __version__ = "0.123.41"
2
2
  __engine__ = "^2.0.4"
@@ -450,3 +450,45 @@ class SelectQueries(BaseQuery):
450
450
  yield instance_id, str(space.toPython())
451
451
  else:
452
452
  yield instance_id, str(space)
453
+
454
+ def _get_graph_diff(
455
+ self, source_graph: URIRef, target_graph: URIRef
456
+ ) -> Iterable[tuple[URIRef, URIRef, URIRef | RdfLiteral]]:
457
+ query = f"""
458
+ SELECT ?s ?p ?o
459
+ WHERE {{
460
+ GRAPH <{source_graph}> {{ ?s ?p ?o }}
461
+ FILTER NOT EXISTS {{
462
+ GRAPH <{target_graph}> {{ ?s ?p ?o }}
463
+ }}
464
+ }}
465
+ """
466
+ return cast(Iterable[tuple[URIRef, URIRef, URIRef | RdfLiteral]], self.dataset.query(query))
467
+
468
+ def get_triples_to_delete(
469
+ self, old_graph: URIRef, new_graph: URIRef
470
+ ) -> Iterable[tuple[URIRef, URIRef, URIRef | RdfLiteral]]:
471
+ """Find triples that exist in old graph but not in new graph.
472
+
473
+ Args:
474
+ old_graph: URI of the old named graph
475
+ new_graph: URI of the new named graph
476
+
477
+ Returns:
478
+ List of triples (subject, predicate, object) to delete
479
+ """
480
+ return self._get_graph_diff(old_graph, new_graph)
481
+
482
+ def get_triples_to_add(
483
+ self, old_graph: URIRef, new_graph: URIRef
484
+ ) -> Iterable[tuple[URIRef, URIRef, URIRef | RdfLiteral]]:
485
+ """Find triples that exist in new graph but not in old graph.
486
+
487
+ Args:
488
+ old_graph: URI of the old named graph
489
+ new_graph: URI of the new named graph
490
+
491
+ Returns:
492
+ List of triples (subject, predicate, object) to add
493
+ """
494
+ return self._get_graph_diff(new_graph, old_graph)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite-neat
3
- Version: 0.123.39
3
+ Version: 0.123.41
4
4
  Summary: Knowledge graph transformation
5
5
  Project-URL: Documentation, https://cognite-neat.readthedocs-hosted.com/
6
6
  Project-URL: Homepage, https://cognite-neat.readthedocs-hosted.com/
@@ -1,26 +1,34 @@
1
1
  cognite/neat/__init__.py,sha256=Lo4DbjDOwnhCYUoAgPp5RG1fDdF7OlnomalTe7n1ydw,211
2
2
  cognite/neat/_issues.py,sha256=uv0fkkWwTKqNmTmHqyoBB3L6yMCh42EZpEkLGmIJYOY,812
3
- cognite/neat/_version.py,sha256=ZTWVKANnFlW6QqktADqgWQm0NPnrSBLdQ7Z_g2suOds,47
3
+ cognite/neat/_version.py,sha256=ufRXfLAZOiccgiTU2G1cLrNuGEalUnTHSuR_tpkSN2w,47
4
4
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  cognite/neat/_data_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  cognite/neat/_data_model/_constants.py,sha256=NGGvWHlQqhkkSBP_AqoofGYjNph3SiZX6QPINlMsy04,107
7
7
  cognite/neat/_data_model/_identifiers.py,sha256=a0LcQ_h0NffxSKTCrzCDpYkrlaUTk-D_rfaQUh-BhWc,1921
8
+ cognite/neat/_data_model/importers/__init__.py,sha256=GUp7WkjX6R02lQRHD-2ygkwNGPSWbFjrFettnCdrrAQ,58
9
+ cognite/neat/_data_model/importers/_base.py,sha256=NRB0FcEBj4GaethU68nRffBfTedBBA866A3zfJNfmiQ,433
8
10
  cognite/neat/_data_model/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
11
  cognite/neat/_data_model/models/conceptual/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
12
  cognite/neat/_data_model/models/conceptual/_base.py,sha256=SFkoBJDM51pqew_isHFJoB20OgfofpwVRnTrg-rKkNY,710
13
+ cognite/neat/_data_model/models/conceptual/_concept.py,sha256=0Pk4W2TJ_Y0Z7oPHpzely1kPXrAkmkyqw6a0n3il6LY,2248
11
14
  cognite/neat/_data_model/models/conceptual/_properties.py,sha256=CpF37vJYBTLT4DH4ZOu2U-JyWtkb_27V8fw52qiaE_k,4007
12
- cognite/neat/_data_model/models/dms/__init__.py,sha256=0YalizBE9PK_5JtzG1_fhrRhrRuqCUsWwWBkY4X4PHo,1876
15
+ cognite/neat/_data_model/models/conceptual/_property.py,sha256=CpF37vJYBTLT4DH4ZOu2U-JyWtkb_27V8fw52qiaE_k,4007
16
+ cognite/neat/_data_model/models/dms/__init__.py,sha256=I_yw2GoEjUdLk8VOiLSlU4BWgE3u_GgCHJayeEa1w1w,3583
13
17
  cognite/neat/_data_model/models/dms/_base.py,sha256=R8SP3Zi9daTBqewYKGjuNEkrWc-j91f-6t34CN-9YJ0,719
14
- cognite/neat/_data_model/models/dms/_constants.py,sha256=tLyle1N_ekNBhANleG7GvZB85P4uGaoWl2mqeR-vfmY,1272
18
+ cognite/neat/_data_model/models/dms/_constants.py,sha256=wBkLjAPwufPc2naxOfPA1XC0CM2RbDbo6Dpiv9dPrew,1344
15
19
  cognite/neat/_data_model/models/dms/_constraints.py,sha256=7Nv-EoNl6lSHUQh6r15Ei0LzR3gWV006K3RlAi1Ic68,956
16
- cognite/neat/_data_model/models/dms/_container.py,sha256=ovyAwsh2ACt0j9-j6ooDOdUl0uKlGdkjYA2UUvJfHrw,5756
20
+ cognite/neat/_data_model/models/dms/_container.py,sha256=SgrMCRo04A1gqayJsCia6oMhYT7q3NaeMBZMOQNI-lA,5967
21
+ cognite/neat/_data_model/models/dms/_data_model.py,sha256=GNa05JEj_eSRGoKB8vkR85-Qv-IhPlDR3gXZNQMfe6g,2537
17
22
  cognite/neat/_data_model/models/dms/_data_types.py,sha256=XSGQWVzYFRYAzKsDln20nC2kqiHQC6JAvDeTzCBPT-0,5202
18
23
  cognite/neat/_data_model/models/dms/_indexes.py,sha256=MsiHbGCH50QaF1mw_pIY-AZ5dY37vS3fQECOd2rBXWo,780
19
- cognite/neat/_data_model/models/dms/_references.py,sha256=yrmMo2JNfupG3tppdjLOaEhUAsRSIjZlBOI6hCol1z4,613
24
+ cognite/neat/_data_model/models/dms/_references.py,sha256=LJ7xlXATf7AP_Uj-8LlxatQk5GG1Tu4CHGK63cJqUU4,3078
25
+ cognite/neat/_data_model/models/dms/_schema.py,sha256=2JFLcm52smzPdtZ69Lf02UbYAD8I_hpRbI7ZAzdxJJs,641
20
26
  cognite/neat/_data_model/models/dms/_space.py,sha256=-1LkRmQaAdIj2EYDcVv5Mcejl5uswgAEVv7DztpIUk4,1680
27
+ cognite/neat/_data_model/models/dms/_view_property.py,sha256=wiRxb5qtzGnBooBUzN7xtwMBZiZ9aRFcN5ML4lCTXdQ,8331
28
+ cognite/neat/_data_model/models/dms/_views.py,sha256=afUZisM-6r0_IeSwn8ZVFAqWfodHBWrMcFnk_a-ewSI,6579
21
29
  cognite/neat/_data_model/models/entities/__init__.py,sha256=qdtIpyy2hjfdMwEHya-efOCvWassaalQla15RnqK00E,798
22
- cognite/neat/_data_model/models/entities/_base.py,sha256=PREgBqc6DM9pLn3VXFWxPBMVK0sexl6s3twSHn9wsf0,3874
23
- cognite/neat/_data_model/models/entities/_constants.py,sha256=P56zgsL2xqfegWOxEAyPm9qrZcxrjb1ZXqMG7cDmQxc,333
30
+ cognite/neat/_data_model/models/entities/_base.py,sha256=PaNrD29iwxuqTpRWbmESMTxRhhKXmRyDF_cLZEC69dg,3927
31
+ cognite/neat/_data_model/models/entities/_constants.py,sha256=EK9Bus8UgFgxK5cVFMTAqWSl6aWkDe7d59hpUmlHlBs,517
24
32
  cognite/neat/_data_model/models/entities/_data_types.py,sha256=DfdEWGek7gODro-_0SiiInhPGwul4zn-ASACQfn8HUY,2838
25
33
  cognite/neat/_data_model/models/entities/_identifiers.py,sha256=uBiK4ot3V0b_LGXuJ7bfha6AEcFI3p2letr1z2iSvig,1923
26
34
  cognite/neat/_data_model/models/entities/_parser.py,sha256=ZLGw0cFV-B7JuaAUJ65Jbjf6o-vidz9_BZvilS6lAZw,6455
@@ -147,7 +155,7 @@ cognite/neat/v0/core/_instances/loaders/_rdf_to_instance_space.py,sha256=T1nNzhY
147
155
  cognite/neat/v0/core/_instances/queries/__init__.py,sha256=W477LMyB4l6HIRbQhJoFgA_MUBwVCh2GBvtFeZu0AUA,53
148
156
  cognite/neat/v0/core/_instances/queries/_base.py,sha256=APevHeeWQDEoOQ0MlBtVlPf9hbZukVkI5fOvt5oPJCE,543
149
157
  cognite/neat/v0/core/_instances/queries/_queries.py,sha256=4BidSQXhdZYZ6_kyG7jMJ2iG0UtSrbQxfmwPM7V167A,466
150
- cognite/neat/v0/core/_instances/queries/_select.py,sha256=nVhXGA8oSVNfhXo7p2poM40ATbQ2wXa6TFeJiJl7Nto,19105
158
+ cognite/neat/v0/core/_instances/queries/_select.py,sha256=NYEEGafbb02poVb3vJ9Ov0XQq9f8YRxpfqq1WFc-36Q,20539
151
159
  cognite/neat/v0/core/_instances/queries/_update.py,sha256=WJmh0hGoKT4pbbWeED6udFAXiv_qFPd3v9tnZLORcNk,1293
152
160
  cognite/neat/v0/core/_instances/transformers/__init__.py,sha256=YzC1Z8BuT77NwagWX4Z-F9R9BARLSS7zM4bCdxBbqKg,1761
153
161
  cognite/neat/v0/core/_instances/transformers/_base.py,sha256=a8TVhgYGdt7Mj5-omT6gxOHeGvYnMd9vJCty6p7ctx4,4707
@@ -223,7 +231,7 @@ cognite/neat/v0/session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4
223
231
  cognite/neat/v0/session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
224
232
  cognite/neat/v0/session/engine/_interface.py,sha256=3W-cYr493c_mW3P5O6MKN1xEQg3cA7NHR_ev3zdF9Vk,533
225
233
  cognite/neat/v0/session/engine/_load.py,sha256=u0x7vuQCRoNcPt25KJBJRn8sJabonYK4vtSZpiTdP4k,5201
226
- cognite_neat-0.123.39.dist-info/METADATA,sha256=eQFt9KEee3JdoiaHaQE7FRyA0Jd53JEIOzsVZ53Gxbk,9148
227
- cognite_neat-0.123.39.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
228
- cognite_neat-0.123.39.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
229
- cognite_neat-0.123.39.dist-info/RECORD,,
234
+ cognite_neat-0.123.41.dist-info/METADATA,sha256=x1il3bStkdRMLwIwGMK5UABcb2tkCyc3Zr63dkY27Vw,9148
235
+ cognite_neat-0.123.41.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
236
+ cognite_neat-0.123.41.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
237
+ cognite_neat-0.123.41.dist-info/RECORD,,