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

cognite/neat/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.89.0"
1
+ __version__ = "0.90.0"
cognite/neat/constants.py CHANGED
@@ -7,11 +7,11 @@ from cognite import neat
7
7
  PACKAGE_DIRECTORY = Path(neat.__file__).parent
8
8
 
9
9
 
10
- EXAMPLE_RULES = PACKAGE_DIRECTORY / "legacy" / "rules" / "examples"
11
- EXAMPLE_GRAPHS = PACKAGE_DIRECTORY / "legacy" / "graph" / "examples"
12
- _OLD_WORKFLOWS = PACKAGE_DIRECTORY / "legacy" / "workflows" / "examples"
10
+ EXAMPLE_RULES = PACKAGE_DIRECTORY / "rules" / "examples"
11
+ EXAMPLE_GRAPHS = PACKAGE_DIRECTORY / "graph" / "examples"
13
12
  EXAMPLE_WORKFLOWS = PACKAGE_DIRECTORY / "workflows" / "examples"
14
13
 
14
+ DEFAULT_SPACE_URI = "http://purl.org/cognite/{space}#"
15
15
  DEFAULT_NAMESPACE = Namespace("http://purl.org/cognite/neat#")
16
16
 
17
17
 
@@ -7,6 +7,7 @@ from ._classic_cdf._relationships import RelationshipsExtractor
7
7
  from ._classic_cdf._sequences import SequencesExtractor
8
8
  from ._classic_cdf._timeseries import TimeSeriesExtractor
9
9
  from ._dexpi import DexpiExtractor
10
+ from ._dms import DMSExtractor
10
11
  from ._mock_graph_generator import MockGraphGenerator
11
12
  from ._rdf_file import RdfFileExtractor
12
13
 
@@ -22,6 +23,7 @@ __all__ = [
22
23
  "LabelsExtractor",
23
24
  "RdfFileExtractor",
24
25
  "DexpiExtractor",
26
+ "DMSExtractor",
25
27
  ]
26
28
 
27
29
 
@@ -5,7 +5,7 @@ from collections.abc import Callable, Iterable, Set
5
5
  from typing import Generic, TypeVar
6
6
 
7
7
  from cognite.client.data_classes._base import CogniteResource
8
- from rdflib import Literal, Namespace, URIRef
8
+ from rdflib import XSD, Literal, Namespace, URIRef
9
9
 
10
10
  from cognite.neat.constants import DEFAULT_NAMESPACE
11
11
  from cognite.neat.graph.extractors._base import BaseExtractor
@@ -93,7 +93,7 @@ class ClassicCDFExtractor(BaseExtractor, ABC, Generic[T_CogniteResource]):
93
93
  Literal(string_to_ideal_type(value)),
94
94
  )
95
95
  else:
96
- yield id_, self.namespace.metadata, Literal(json.dumps(metadata))
96
+ yield id_, self.namespace.metadata, Literal(json.dumps(metadata), datatype=XSD._NS["json"])
97
97
 
98
98
  def _get_rdf_type(self, item: T_CogniteResource) -> str:
99
99
  type_ = self._default_rdf_type
@@ -0,0 +1,158 @@
1
+ from collections.abc import Iterable, Iterator
2
+ from typing import cast
3
+
4
+ from cognite.client import CogniteClient
5
+ from cognite.client import data_modeling as dm
6
+ from cognite.client.data_classes.data_modeling import DataModelIdentifier
7
+ from cognite.client.data_classes.data_modeling.instances import Instance, PropertyValue
8
+ from rdflib import RDF, XSD, Literal, Namespace, URIRef
9
+
10
+ from cognite.neat.constants import DEFAULT_SPACE_URI
11
+ from cognite.neat.graph.models import Triple
12
+ from cognite.neat.issues.errors import ResourceRetrievalError
13
+
14
+ from ._base import BaseExtractor
15
+
16
+
17
+ class DMSExtractor(BaseExtractor):
18
+ """Extract data from Cognite Data Fusion DMS instances into Neat.
19
+
20
+ Args:
21
+ items: The items to extract.
22
+ total: The total number of items to extract. If provided, this will be used to estimate the progress.
23
+ limit: The maximum number of items to extract.
24
+ overwrite_namespace: If provided, this will overwrite the space of the extracted items.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ items: Iterable[Instance],
30
+ total: int | None = None,
31
+ limit: int | None = None,
32
+ overwrite_namespace: Namespace | None = None,
33
+ ) -> None:
34
+ self.items = items
35
+ self.total = total
36
+ self.limit = limit
37
+ self.overwrite_namespace = overwrite_namespace
38
+
39
+ @classmethod
40
+ def from_data_model(
41
+ cls, client: CogniteClient, data_model: DataModelIdentifier, limit: int | None = None
42
+ ) -> "DMSExtractor":
43
+ """Create an extractor from a data model.
44
+
45
+ Args:
46
+ client: The Cognite client to use.
47
+ data_model: The data model to extract.
48
+ limit: The maximum number of instances to extract.
49
+ """
50
+ retrieved = client.data_modeling.data_models.retrieve(data_model, inline_views=True)
51
+ if not retrieved:
52
+ raise ResourceRetrievalError(dm.DataModelId.load(data_model), "data model", "Data Model is missing in CDF")
53
+ return cls.from_views(client, retrieved.latest_version().views, limit)
54
+
55
+ @classmethod
56
+ def from_views(cls, client: CogniteClient, views: Iterable[dm.View], limit: int | None = None) -> "DMSExtractor":
57
+ """Create an extractor from a set of views.
58
+
59
+ Args:
60
+ client: The Cognite client to use.
61
+ views: The views to extract.
62
+ limit: The maximum number of instances to extract.
63
+ """
64
+ return cls(_InstanceIterator(client, views), total=None, limit=limit)
65
+
66
+ def extract(self) -> Iterable[Triple]:
67
+ for count, item in enumerate(self.items, 1):
68
+ if self.limit and count > self.limit:
69
+ break
70
+ yield from self._extract_instance(item)
71
+
72
+ def _extract_instance(self, instance: Instance) -> Iterable[Triple]:
73
+ if isinstance(instance, dm.Edge):
74
+ if not instance.properties:
75
+ yield (
76
+ self._as_uri_ref(instance.start_node),
77
+ self._as_uri_ref(instance.type),
78
+ self._as_uri_ref(instance.end_node),
79
+ )
80
+ return
81
+ else:
82
+ # If the edge has properties, we create a node for the edge and connect it to the start and end nodes.
83
+ id_ = self._as_uri_ref(instance)
84
+ yield id_, RDF.type, self._as_uri_ref(instance.type)
85
+ yield id_, RDF.type, self._get_namespace(instance.space).Edge
86
+ yield (
87
+ id_,
88
+ self._as_uri_ref(dm.DirectRelationReference(instance.space, "startNode")),
89
+ self._as_uri_ref(instance.start_node),
90
+ )
91
+ yield (
92
+ id_,
93
+ self._as_uri_ref(dm.DirectRelationReference(instance.space, "endNode")),
94
+ self._as_uri_ref(instance.end_node),
95
+ )
96
+
97
+ elif isinstance(instance, dm.Node):
98
+ id_ = self._as_uri_ref(instance)
99
+ if instance.type:
100
+ type_ = self._as_uri_ref(cast(dm.DirectRelationReference, instance.type))
101
+ else:
102
+ type_ = self._get_namespace(instance.space).Node
103
+
104
+ yield id_, RDF.type, type_
105
+ else:
106
+ raise NotImplementedError(f"Unknown instance type {type(instance)}")
107
+
108
+ for view_id, properties in instance.properties.items():
109
+ namespace = self._get_namespace(view_id.space)
110
+ for key, value in properties.items():
111
+ for object_ in self._get_objects(value):
112
+ yield id_, namespace[key], object_
113
+
114
+ def _get_objects(self, value: PropertyValue) -> Iterable[Literal | URIRef]:
115
+ if isinstance(value, str | float | bool | int):
116
+ yield Literal(value)
117
+ elif isinstance(value, dict) and "space" in value and "externalId" in value:
118
+ yield self._as_uri_ref(dm.DirectRelationReference.load(value))
119
+ elif isinstance(value, dict):
120
+ # This object is a json object.
121
+ yield Literal(str(value), datatype=XSD._NS["json"])
122
+ elif isinstance(value, list):
123
+ for item in value:
124
+ yield from self._get_objects(item)
125
+
126
+ def _as_uri_ref(self, instance: Instance | dm.DirectRelationReference) -> URIRef:
127
+ return self._get_namespace(instance.space)[instance.external_id]
128
+
129
+ def _get_namespace(self, space: str) -> Namespace:
130
+ if self.overwrite_namespace:
131
+ return self.overwrite_namespace
132
+ return Namespace(DEFAULT_SPACE_URI.format(space=space))
133
+
134
+
135
+ class _InstanceIterator(Iterator[Instance]):
136
+ def __init__(self, client: CogniteClient, views: Iterable[dm.View]):
137
+ self.client = client
138
+ self.views = views
139
+
140
+ def __iter__(self) -> Iterator[Instance]:
141
+ return self
142
+
143
+ def __next__(self) -> Instance: # type: ignore[misc]
144
+ for view in self.views:
145
+ # All nodes and edges with properties
146
+ yield from self.client.data_modeling.instances(chunk_size=None, instance_type="node", sources=[view])
147
+ yield from self.client.data_modeling.instances(chunk_size=None, instance_type="edge", sources=[view])
148
+
149
+ for prop in view.properties.values():
150
+ if isinstance(prop, dm.EdgeConnection):
151
+ # Get all edges with properties
152
+ yield from self.client.data_modeling.instances(
153
+ chunk_size=None,
154
+ instance_type="edge",
155
+ filter=dm.filters.Equals(
156
+ ["edge", "type"], {"space": prop.type.space, "externalId": prop.type.external_id}
157
+ ),
158
+ )
@@ -17,7 +17,6 @@ from cognite.neat.rules.models import DMSRules, InformationRules
17
17
  from cognite.neat.rules.models.data_types import DataType
18
18
  from cognite.neat.rules.models.entities import ClassEntity, EntityTypes
19
19
  from cognite.neat.rules.models.information import InformationProperty
20
- from cognite.neat.rules.transformers import DMSToInformation
21
20
  from cognite.neat.utils.rdf_ import remove_namespace_from_uri
22
21
 
23
22
  from ._base import BaseExtractor
@@ -43,6 +42,9 @@ class MockGraphGenerator(BaseExtractor):
43
42
  allow_isolated_classes: bool = True,
44
43
  ):
45
44
  if isinstance(rules, DMSRules):
45
+ # fixes potential issues with circular dependencies
46
+ from cognite.neat.rules.transformers import DMSToInformation
47
+
46
48
  self.rules = DMSToInformation().transform(rules).rules
47
49
  elif isinstance(rules, InformationRules):
48
50
  self.rules = rules
@@ -152,21 +154,33 @@ def generate_triples(
152
154
  # generate triples for connected classes
153
155
  for class_ in generation_order:
154
156
  triples += _generate_triples_per_class(
155
- class_, class_property_pairs, sym_pairs, instance_ids, namespace, stop_on_exception
157
+ class_,
158
+ class_property_pairs,
159
+ sym_pairs,
160
+ instance_ids,
161
+ namespace,
162
+ stop_on_exception,
156
163
  )
157
164
 
158
165
  # generate triples for isolated classes
159
166
  if allow_isolated_classes:
160
167
  for class_ in set(class_count.keys()) - set(generation_order):
161
168
  triples += _generate_triples_per_class(
162
- class_, class_property_pairs, sym_pairs, instance_ids, namespace, stop_on_exception
169
+ class_,
170
+ class_property_pairs,
171
+ sym_pairs,
172
+ instance_ids,
173
+ namespace,
174
+ stop_on_exception,
163
175
  )
164
176
 
165
177
  return triples
166
178
 
167
179
 
168
180
  def _get_generation_order(
169
- class_linkage: pd.DataFrame, parent_col: str = "source_class", child_col: str = "target_class"
181
+ class_linkage: pd.DataFrame,
182
+ parent_col: str = "source_class",
183
+ child_col: str = "target_class",
170
184
  ) -> dict:
171
185
  parent_child_list: list[list[str]] = class_linkage[[parent_col, child_col]].values.tolist()
172
186
  # Build a directed graph and a list of all names that have no parent
@@ -244,7 +258,10 @@ def _remove_non_requested_sym_pairs(class_linkage: pd.DataFrame, class_count: di
244
258
 
245
259
 
246
260
  def _generate_mock_data_property_triples(
247
- instance_ids: list[URIRef], property_: str, namespace: Namespace, value_type: DataType
261
+ instance_ids: list[URIRef],
262
+ property_: str,
263
+ namespace: Namespace,
264
+ value_type: DataType,
248
265
  ) -> list[tuple[URIRef, URIRef, Literal]]:
249
266
  """Generates triples for data properties."""
250
267
 
@@ -254,7 +271,13 @@ def _generate_mock_data_property_triples(
254
271
  if python_type is int:
255
272
  triples.append((id_, URIRef(namespace[property_]), Literal(random.randint(1, 1983))))
256
273
  elif python_type is float:
257
- triples.append((id_, URIRef(namespace[property_]), Literal(numpy.float32(random.uniform(1, 1983)))))
274
+ triples.append(
275
+ (
276
+ id_,
277
+ URIRef(namespace[property_]),
278
+ Literal(numpy.float32(random.uniform(1, 1983))),
279
+ )
280
+ )
258
281
  # generate string
259
282
  else:
260
283
  triples.append(
@@ -293,7 +316,12 @@ def _generate_mock_object_property_triples(
293
316
 
294
317
  if tuple((class_, property_definition.value_type)) in sym_pairs:
295
318
  symmetric_class_properties = class_property_pairs[cast(ClassEntity, property_definition.value_type)]
296
- candidates = list(filter(lambda instance: instance.value_type == class_, symmetric_class_properties))
319
+ candidates = list(
320
+ filter(
321
+ lambda instance: instance.value_type == class_,
322
+ symmetric_class_properties,
323
+ )
324
+ )
297
325
  symmetric_property = candidates[0]
298
326
  if len(candidates) > 1:
299
327
  warnings.warn(
@@ -310,10 +338,22 @@ def _generate_mock_object_property_triples(
310
338
  target = instance_ids[cast(ClassEntity, property_definition.value_type)][
311
339
  i % len(instance_ids[cast(ClassEntity, property_definition.value_type)])
312
340
  ]
313
- triples += [(URIRef(source), URIRef(namespace[property_definition.property_]), URIRef(target))]
341
+ triples += [
342
+ (
343
+ URIRef(source),
344
+ URIRef(namespace[property_definition.property_]),
345
+ URIRef(target),
346
+ )
347
+ ]
314
348
 
315
349
  if symmetric_property:
316
- triples += [(URIRef(target), URIRef(namespace[symmetric_property.property_]), URIRef(source))]
350
+ triples += [
351
+ (
352
+ URIRef(target),
353
+ URIRef(namespace[symmetric_property.property_]),
354
+ URIRef(source),
355
+ )
356
+ ]
317
357
 
318
358
  if symmetric_property:
319
359
  class_property_pairs[cast(ClassEntity, property_definition.value_type)].remove(symmetric_property)
@@ -13,8 +13,10 @@ from cognite.client.data_classes.data_modeling.ids import InstanceId
13
13
  from cognite.client.data_classes.data_modeling.views import SingleEdgeConnection
14
14
  from cognite.client.exceptions import CogniteAPIError
15
15
  from pydantic import BaseModel, ValidationInfo, create_model, field_validator
16
+ from rdflib import RDF
16
17
 
17
18
  from cognite.neat.graph._tracking import LogTracker, Tracker
19
+ from cognite.neat.graph.models import InstanceType
18
20
  from cognite.neat.issues import IssueList, NeatIssue, NeatIssueList
19
21
  from cognite.neat.issues.errors import (
20
22
  ResourceConvertionError,
@@ -108,7 +110,7 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
108
110
  for view in self.data_model.views:
109
111
  view_id = view.as_id()
110
112
  tracker.start(repr(view_id))
111
- pydantic_cls, edge_by_properties, issues = self._create_validation_classes(view) # type: ignore[var-annotated]
113
+ pydantic_cls, edge_by_type, issues = self._create_validation_classes(view) # type: ignore[var-annotated]
112
114
  yield from issues
113
115
  tracker.issue(issues)
114
116
  class_name = self.class_by_view_id.get(view.as_id(), view.external_id)
@@ -122,7 +124,7 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
122
124
  if stop_on_exception:
123
125
  raise error from e
124
126
  yield error
125
- yield from self._create_edges(identifier, properties, edge_by_properties, tracker)
127
+ yield from self._create_edges(identifier, properties, edge_by_type, tracker)
126
128
  tracker.finish(repr(view_id))
127
129
 
128
130
  def write_to_file(self, filepath: Path) -> None:
@@ -147,16 +149,16 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
147
149
 
148
150
  def _create_validation_classes(
149
151
  self, view: dm.View
150
- ) -> tuple[type[BaseModel], dict[str, dm.EdgeConnection], NeatIssueList]:
152
+ ) -> tuple[type[BaseModel], dict[str, tuple[str, dm.EdgeConnection]], NeatIssueList]:
151
153
  issues = IssueList()
152
154
  field_definitions: dict[str, tuple[type, Any]] = {}
153
- edge_by_property: dict[str, dm.EdgeConnection] = {}
155
+ edge_by_property: dict[str, tuple[str, dm.EdgeConnection]] = {}
154
156
  validators: dict[str, classmethod] = {}
155
157
  direct_relation_by_property: dict[str, dm.DirectRelation] = {}
156
158
  json_fields: list[str] = []
157
159
  for prop_name, prop in view.properties.items():
158
160
  if isinstance(prop, dm.EdgeConnection):
159
- edge_by_property[prop_name] = prop
161
+ edge_by_property[prop.type.external_id] = prop_name, prop
160
162
  if isinstance(prop, dm.MappedProperty):
161
163
  if isinstance(prop.type, dm.DirectRelation):
162
164
  direct_relation_by_property[prop_name] = prop.type
@@ -215,7 +217,7 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
215
217
 
216
218
  def parse_direct_relation(cls, value: list, info: ValidationInfo) -> dict | list[dict]:
217
219
  # We validate above that we only get one value for single direct relations.
218
- if cls.model_fields[info.field_name].annotation is list:
220
+ if list.__name__ in _get_field_value_types(cls, info):
219
221
  return [{"space": self.instance_space, "externalId": v} for v in value]
220
222
  elif value:
221
223
  return {"space": self.instance_space, "externalId": value[0]}
@@ -231,16 +233,17 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
231
233
  def _create_node(
232
234
  self,
233
235
  identifier: str,
234
- properties: dict[str, list[str]],
236
+ properties: dict[str | InstanceType, list[str]],
235
237
  pydantic_cls: type[BaseModel],
236
238
  view_id: dm.ViewId,
237
239
  ) -> dm.InstanceApply:
240
+ type_ = properties.pop(RDF.type, [None])[0]
238
241
  created = pydantic_cls.model_validate(properties)
239
242
 
240
243
  return dm.NodeApply(
241
244
  space=self.instance_space,
242
245
  external_id=identifier,
243
- # type=#RDF type
246
+ type=dm.DirectRelationReference(view_id.space, type_) if type_ is not None else None,
244
247
  sources=[dm.NodeOrEdgeData(source=view_id, properties=dict(created.model_dump().items()))],
245
248
  )
246
249
 
@@ -248,13 +251,13 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
248
251
  self,
249
252
  identifier: str,
250
253
  properties: dict[str, list[str]],
251
- edge_by_properties: dict[str, dm.EdgeConnection],
254
+ edge_by_type: dict[str, tuple[str, dm.EdgeConnection]],
252
255
  tracker: Tracker,
253
256
  ) -> Iterable[dm.EdgeApply | NeatIssue]:
254
- for prop, values in properties.items():
255
- if prop not in edge_by_properties:
257
+ for predicate, values in properties.items():
258
+ if predicate not in edge_by_type:
256
259
  continue
257
- edge = edge_by_properties[prop]
260
+ prop_id, edge = edge_by_type[predicate]
258
261
  if isinstance(edge, SingleEdgeConnection) and len(values) > 1:
259
262
  error = ResourceDuplicatedError(
260
263
  resource_type="edge",
@@ -264,7 +267,7 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
264
267
  tracker.issue(error)
265
268
  yield error
266
269
  for target in values:
267
- external_id = f"{identifier}.{prop}.{target}"
270
+ external_id = f"{identifier}.{prop_id}.{target}"
268
271
  yield dm.EdgeApply(
269
272
  space=self.instance_space,
270
273
  external_id=(external_id if len(external_id) < 256 else create_sha256_hash(external_id)),
@@ -4,3 +4,4 @@ from rdflib import Literal
4
4
  from rdflib.term import URIRef
5
5
 
6
6
  Triple: TypeAlias = tuple[URIRef, URIRef, Literal | URIRef]
7
+ InstanceType: TypeAlias = URIRef
@@ -6,6 +6,7 @@ from rdflib import RDF, Graph, URIRef
6
6
  from rdflib import Literal as RdfLiteral
7
7
  from rdflib.query import ResultRow
8
8
 
9
+ from cognite.neat.graph.models import InstanceType
9
10
  from cognite.neat.rules.models.entities import ClassEntity
10
11
  from cognite.neat.rules.models.information import InformationRules
11
12
  from cognite.neat.utils.rdf_ import remove_namespace_from_uri
@@ -98,7 +99,7 @@ class Queries:
98
99
  self,
99
100
  instance_id: URIRef,
100
101
  property_renaming_config: dict | None = None,
101
- ) -> tuple[str, dict[str, list[str]]] | None:
102
+ ) -> tuple[str, dict[str | InstanceType, list[str]]] | None:
102
103
  """DESCRIBE instance for a given class from the graph store
103
104
 
104
105
  Args:
@@ -126,7 +127,8 @@ class Queries:
126
127
  # losing the namespace from the predicate!
127
128
  if not property_renaming_config and predicate != RDF.type:
128
129
  property_values[remove_namespace_from_uri(predicate, validation="prefix")].append(value)
129
-
130
+ elif predicate == RDF.type:
131
+ property_values[RDF.type].append(value)
130
132
  # use-case: calling describe with renaming properties
131
133
  # renaming the property to the new name, if the property is defined
132
134
  # in the RULES sheet
@@ -45,6 +45,7 @@ ResourceType: TypeAlias = (
45
45
  "container property",
46
46
  "space",
47
47
  "class",
48
+ "property",
48
49
  "asset",
49
50
  "relationship",
50
51
  "data model",
@@ -26,6 +26,7 @@ from ._properties import (
26
26
  PropertyDefinitionDuplicatedWarning,
27
27
  PropertyNotFoundWarning,
28
28
  PropertyTypeNotSupportedWarning,
29
+ PropertyValueTypeUndefinedWarning,
29
30
  )
30
31
  from ._resources import (
31
32
  ResourceNeatWarning,
@@ -49,6 +50,7 @@ __all__ = [
49
50
  "PropertyDefinitionDuplicatedWarning",
50
51
  "PropertyTypeNotSupportedWarning",
51
52
  "PropertyNotFoundWarning",
53
+ "PropertyValueTypeUndefinedWarning",
52
54
  "ResourceNeatWarning",
53
55
  "ResourcesDuplicatedWarning",
54
56
  "RegexViolationWarning",
@@ -42,3 +42,15 @@ class PropertyDefinitionDuplicatedWarning(PropertyWarning[T_Identifier]):
42
42
  values: frozenset[str]
43
43
  default_action: str
44
44
  recommended_action: str | None = None
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class PropertyValueTypeUndefinedWarning(PropertyWarning[T_Identifier]):
49
+ """The {resource_type} with identifier {identifier} has a property {property_name}
50
+ which has undefined value type. This may result in unexpected behavior when exporting rules.
51
+ {default_action}"""
52
+
53
+ extra = "Recommended action: {recommended_action}"
54
+
55
+ default_action: str
56
+ recommended_action: str | None = None
@@ -113,7 +113,7 @@ class DMSImporter(BaseImporter[DMSInputRules]):
113
113
  DMSSchema(),
114
114
  [
115
115
  ResourceRetrievalError(
116
- dm.DataModelId.load(reference_model_id), # type: ignore[arg-type]
116
+ dm.DataModelId.load(data_model_id), # type: ignore[arg-type]
117
117
  "data model",
118
118
  "Data Model is missing in CDF",
119
119
  )
@@ -3,15 +3,17 @@ from datetime import datetime
3
3
  from pathlib import Path
4
4
  from typing import cast
5
5
 
6
- from rdflib import Graph, Namespace, URIRef
6
+ from rdflib import RDF, Graph, Namespace, URIRef
7
7
  from rdflib import Literal as RdfLiteral
8
8
 
9
9
  from cognite.neat.constants import DEFAULT_NAMESPACE, get_default_prefixes
10
10
  from cognite.neat.issues import IssueList
11
11
  from cognite.neat.issues.errors import FileReadError
12
+ from cognite.neat.issues.warnings import PropertyValueTypeUndefinedWarning
12
13
  from cognite.neat.rules._shared import ReadRules
13
14
  from cognite.neat.rules.importers._base import BaseImporter
14
15
  from cognite.neat.rules.models._base_rules import MatchType
16
+ from cognite.neat.rules.models.entities._single_value import UnknownEntity
15
17
  from cognite.neat.rules.models.information import (
16
18
  InformationInputRules,
17
19
  InformationMetadata,
@@ -25,16 +27,6 @@ ORDERED_CLASSES_QUERY = """SELECT ?class (count(?s) as ?instances )
25
27
 
26
28
  INSTANCES_OF_CLASS_QUERY = """SELECT ?s WHERE { ?s a <class> . }"""
27
29
 
28
- INSTANCE_PROPERTIES_JSON_DEFINITION = """SELECT ?property (count(?property) as ?occurrence) ?dataType ?objectType
29
- WHERE {<instance_id> ?property ?value .
30
-
31
- BIND(IF(REGEX(?value, "^\u007b(.*)\u007d$"),
32
- <http://www.w3.org/2001/XMLSchema#json>,
33
- datatype(?value)) AS ?dataType)
34
-
35
- OPTIONAL {?value rdf:type ?objectType .}}
36
- GROUP BY ?property ?dataType ?objectType"""
37
-
38
30
  INSTANCE_PROPERTIES_DEFINITION = """SELECT ?property (count(?property) as ?occurrence) ?dataType ?objectType
39
31
  WHERE {<instance_id> ?property ?value .
40
32
 
@@ -56,7 +48,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
56
48
  graph: Knowledge graph
57
49
  max_number_of_instance: Maximum number of instances to be used in inference
58
50
  prefix: Prefix to be used for the inferred model
59
- check_for_json_string: Check if values are JSON strings
60
51
  """
61
52
 
62
53
  def __init__(
@@ -65,15 +56,11 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
65
56
  graph: Graph,
66
57
  max_number_of_instance: int = -1,
67
58
  prefix: str = "inferred",
68
- check_for_json_string: bool = False,
69
59
  ) -> None:
70
60
  self.issue_list = issue_list
71
61
  self.graph = graph
72
62
  self.max_number_of_instance = max_number_of_instance
73
63
  self.prefix = prefix
74
- self.check_for_json_string = (
75
- check_for_json_string if graph.store.__class__.__name__ != "OxigraphStore" else False
76
- )
77
64
 
78
65
  @classmethod
79
66
  def from_graph_store(
@@ -81,7 +68,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
81
68
  store: NeatGraphStore,
82
69
  max_number_of_instance: int = -1,
83
70
  prefix: str = "inferred",
84
- check_for_json_string: bool = False,
85
71
  ) -> "InferenceImporter":
86
72
  issue_list = IssueList(title="Inferred from graph store")
87
73
 
@@ -90,7 +76,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
90
76
  store.graph,
91
77
  max_number_of_instance=max_number_of_instance,
92
78
  prefix=prefix,
93
- check_for_json_string=check_for_json_string,
94
79
  )
95
80
 
96
81
  @classmethod
@@ -99,7 +84,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
99
84
  filepath: Path,
100
85
  max_number_of_instance: int = -1,
101
86
  prefix: str = "inferred",
102
- check_for_json_string: bool = False,
103
87
  ) -> "InferenceImporter":
104
88
  issue_list = IssueList(title=f"'{filepath.name}'")
105
89
 
@@ -114,7 +98,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
114
98
  graph,
115
99
  max_number_of_instance=max_number_of_instance,
116
100
  prefix=prefix,
117
- check_for_json_string=check_for_json_string,
118
101
  )
119
102
 
120
103
  @classmethod
@@ -123,7 +106,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
123
106
  filepath: Path,
124
107
  max_number_of_instance: int = -1,
125
108
  prefix: str = "inferred",
126
- check_for_json_string: bool = False,
127
109
  ) -> "InferenceImporter":
128
110
  raise NotImplementedError("JSON file format is not supported yet.")
129
111
 
@@ -133,7 +115,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
133
115
  filepath: Path,
134
116
  max_number_of_instance: int = -1,
135
117
  prefix: str = "inferred",
136
- check_for_json_string: bool = False,
137
118
  ) -> "InferenceImporter":
138
119
  raise NotImplementedError("YAML file format is not supported yet.")
139
120
 
@@ -143,7 +124,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
143
124
  filepath: Path,
144
125
  max_number_of_instance: int = -1,
145
126
  prefix: str = "inferred",
146
- check_for_json_string: bool = False,
147
127
  ) -> "InferenceImporter":
148
128
  raise NotImplementedError("JSON file format is not supported yet.")
149
129
 
@@ -180,7 +160,6 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
180
160
  prefixes: dict[str, Namespace] = get_default_prefixes()
181
161
  count_by_value_type_by_property: dict[str, dict[str, int]] = defaultdict(Counter)
182
162
 
183
- query = INSTANCE_PROPERTIES_JSON_DEFINITION if self.check_for_json_string else INSTANCE_PROPERTIES_DEFINITION
184
163
  # Adds default namespace to prefixes
185
164
  prefixes[self._default_metadata().prefix] = self._default_metadata().namespace
186
165
 
@@ -208,19 +187,35 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
208
187
  + f" LIMIT {self.max_number_of_instance}"
209
188
  ):
210
189
  for property_uri, occurrence, data_type_uri, object_type_uri in self.graph.query( # type: ignore[misc]
211
- query.replace("instance_id", instance)
190
+ INSTANCE_PROPERTIES_DEFINITION.replace("instance_id", instance)
212
191
  ): # type: ignore[misc]
192
+ # this is to skip rdf:type property
193
+ if property_uri == RDF.type:
194
+ continue
195
+
213
196
  property_id = remove_namespace_from_uri(property_uri)
214
197
 
215
198
  self._add_uri_namespace_to_prefixes(cast(URIRef, property_uri), prefixes)
216
- value_type_uri = data_type_uri if data_type_uri else object_type_uri
217
199
 
218
- # this is to skip rdf:type property
219
- if not value_type_uri:
220
- continue
200
+ if value_type_uri := (data_type_uri or object_type_uri):
201
+ self._add_uri_namespace_to_prefixes(cast(URIRef, value_type_uri), prefixes)
202
+
203
+ value_type_id = remove_namespace_from_uri(value_type_uri)
204
+
205
+ # this handles situations when property points to node that is not present in graph
206
+ else:
207
+ value_type_id = str(UnknownEntity())
208
+
209
+ self.issue_list.append(
210
+ PropertyValueTypeUndefinedWarning(
211
+ resource_type="Property",
212
+ identifier=f"{class_id}{property_id}",
213
+ property_name=property_id,
214
+ default_action="Remove the property from the rules",
215
+ recommended_action="Make sure that graph is complete",
216
+ )
217
+ )
221
218
 
222
- self._add_uri_namespace_to_prefixes(cast(URIRef, value_type_uri), prefixes)
223
- value_type_id = remove_namespace_from_uri(value_type_uri)
224
219
  id_ = f"{class_id}:{property_id}"
225
220
 
226
221
  definition = {
@@ -265,7 +260,7 @@ class InferenceImporter(BaseImporter[InformationInputRules]):
265
260
  if len(count_list) == 1:
266
261
  type_, count = count_list[0]
267
262
  counts_str = f"with value type {base_string.format(value_type=type_, count=count)} in the graph"
268
- elif len(count_list) == 1:
263
+ elif len(count_list) == 2:
269
264
  first = base_string.format(value_type=count_list[0][0], count=count_list[0][1])
270
265
  second = base_string.format(value_type=count_list[1][0], count=count_list[1][1])
271
266
  counts_str = f"with value types {first} and {second} in the graph"
@@ -344,7 +344,8 @@ class _DMSExporter:
344
344
  # If not set, nullable is True and immutable is False
345
345
  nullable=prop.nullable if prop.nullable is not None else True,
346
346
  immutable=prop.immutable if prop.immutable is not None else False,
347
- default_value=prop.default,
347
+ # Guarding against default value being set for connection properties
348
+ default_value=prop.default if not prop.connection else None,
348
349
  name=prop.name,
349
350
  description=prop.description,
350
351
  )
@@ -535,23 +536,17 @@ class _DMSExporter:
535
536
  "If this error occurs it is a bug in NEAT, please report"
536
537
  f"Debug Info, Invalid valueType reverse connection: {prop.model_dump_json()}"
537
538
  )
538
- reverse_prop: DMSProperty | None = None
539
539
  edge_source = None
540
- if reverse_prop_id is not None:
541
- reverse_prop = next(
542
- (
543
- prop
544
- for prop in view_properties_by_id.get(source_view_id, [])
545
- if prop.property_ == reverse_prop_id
546
- ),
547
- None,
548
- )
549
- if (
550
- reverse_prop
551
- and isinstance(reverse_prop.connection, EdgeEntity)
552
- and reverse_prop.connection.properties is not None
553
- ):
554
- edge_source = reverse_prop.connection.properties.as_id()
540
+ reverse_prop = next(
541
+ (prop for prop in view_properties_by_id.get(source_view_id, []) if prop.property_ == reverse_prop_id),
542
+ None,
543
+ )
544
+ if (
545
+ reverse_prop
546
+ and isinstance(reverse_prop.connection, EdgeEntity)
547
+ and reverse_prop.connection.properties is not None
548
+ ):
549
+ edge_source = reverse_prop.connection.properties.as_id()
555
550
 
556
551
  if reverse_prop is None:
557
552
  warnings.warn(
@@ -577,9 +572,11 @@ class _DMSExporter:
577
572
  direction="inwards",
578
573
  edge_source=edge_source,
579
574
  )
580
- elif reverse_prop_id and reverse_prop and reverse_prop.connection == "direct":
575
+ elif reverse_prop and reverse_prop.connection == "direct":
581
576
  reverse_direct_cls = (
582
- dm.MultiReverseDirectRelationApply if prop.is_list is True else SingleReverseDirectRelationApply
577
+ dm.MultiReverseDirectRelationApply
578
+ if prop.is_list in [True, None]
579
+ else SingleReverseDirectRelationApply
583
580
  )
584
581
  return reverse_direct_cls(
585
582
  source=source_view_id,
@@ -62,8 +62,11 @@ class ConversionTransformer(RulesTransformer[T_VerifiedInRules, T_VerifiedOutRul
62
62
  class InformationToDMS(ConversionTransformer[InformationRules, DMSRules]):
63
63
  """Converts InformationRules to DMSRules."""
64
64
 
65
+ def __init__(self, ignore_undefined_value_types: bool = False):
66
+ self.ignore_undefined_value_types = ignore_undefined_value_types
67
+
65
68
  def _transform(self, rules: InformationRules) -> DMSRules:
66
- return _InformationRulesConverter(rules).as_dms_rules()
69
+ return _InformationRulesConverter(rules).as_dms_rules(self.ignore_undefined_value_types)
67
70
 
68
71
 
69
72
  class InformationToAsset(ConversionTransformer[InformationRules, AssetRules]):
@@ -153,7 +156,7 @@ class _InformationRulesConverter:
153
156
  prefixes=self.rules.prefixes,
154
157
  )
155
158
 
156
- def as_dms_rules(self) -> "DMSRules":
159
+ def as_dms_rules(self, ignore_undefined_value_types: bool = False) -> "DMSRules":
157
160
  from cognite.neat.rules.models.dms._rules import (
158
161
  DMSContainer,
159
162
  DMSProperty,
@@ -169,6 +172,8 @@ class _InformationRulesConverter:
169
172
  properties_by_class: dict[ClassEntity, list[DMSProperty]] = defaultdict(list)
170
173
  referenced_containers: dict[ContainerEntity, Counter[ClassEntity]] = defaultdict(Counter)
171
174
  for prop in self.rules.properties:
175
+ if ignore_undefined_value_types and isinstance(prop.value_type, UnknownEntity):
176
+ continue
172
177
  dms_property = self._as_dms_property(prop, default_space, default_version)
173
178
  properties_by_class[prop.class_].append(dms_property)
174
179
  if dms_property.container:
@@ -290,6 +295,10 @@ class _InformationRulesConverter:
290
295
  # Default connection type.
291
296
  connection = EdgeEntity() if prop.is_list else "direct"
292
297
 
298
+ # defaulting to direct connection
299
+ elif isinstance(value_type, DMSUnknownEntity):
300
+ connection = "direct"
301
+
293
302
  container: ContainerEntity | None = None
294
303
  container_property: str | None = None
295
304
  is_list: bool | None = prop.is_list
@@ -12,7 +12,7 @@ from rdflib.plugins.stores.sparqlstore import SPARQLUpdateStore
12
12
  from cognite.neat.constants import DEFAULT_NAMESPACE
13
13
  from cognite.neat.graph._shared import MIMETypes
14
14
  from cognite.neat.graph.extractors import RdfFileExtractor, TripleExtractors
15
- from cognite.neat.graph.models import Triple
15
+ from cognite.neat.graph.models import InstanceType, Triple
16
16
  from cognite.neat.graph.queries import Queries
17
17
  from cognite.neat.graph.transformers import Transformers
18
18
  from cognite.neat.rules.analysis import InformationAnalysis
@@ -173,7 +173,7 @@ class NeatGraphStore:
173
173
  )
174
174
  )
175
175
 
176
- def read(self, class_: str) -> Iterable[tuple[str, dict[str, list[str]]]]:
176
+ def read(self, class_: str) -> Iterable[tuple[str, dict[str | InstanceType, list[str]]]]:
177
177
  """Read instances for given view from the graph store."""
178
178
 
179
179
  if not self.rules:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cognite-neat
3
- Version: 0.89.0
3
+ Version: 0.90.0
4
4
  Summary: Knowledge graph transformation
5
5
  Home-page: https://cognite-neat.readthedocs-hosted.com/
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  cognite/neat/__init__.py,sha256=AiexNcHdAHFbrrbo9c65gtil1dqx_SGraDH1PSsXjKE,126
2
2
  cognite/neat/_shared.py,sha256=RSaHm2eJceTlvb-hMMe4nHgoHdPYDfN3XcxDXo24k3A,1530
3
- cognite/neat/_version.py,sha256=0dsP1JW1TkWXOWQ5JFTW7tQopcf-xbHKy_C8NKkqYeA,23
3
+ cognite/neat/_version.py,sha256=FvxNqJMDVJIASnIcUVgFB9VPINXWP_qWXCWjSsjUHO8,23
4
4
  cognite/neat/app/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  cognite/neat/app/api/asgi/metrics.py,sha256=nxFy7L5cChTI0a-zkCiJ59Aq8yLuIJp5c9Dg0wRXtV0,152
6
6
  cognite/neat/app/api/configuration.py,sha256=L1DCtLZ1HZku8I2z-JWd5RDsXhIsboFsKwAMhkrm-bY,3600
@@ -39,7 +39,7 @@ cognite/neat/app/ui/neat-app/build/static/js/main.5a52cf09.js.LICENSE.txt,sha256
39
39
  cognite/neat/app/ui/neat-app/build/static/js/main.5a52cf09.js.map,sha256=Lf93Q5-M2-Yc8NTq59fJ7floyiT9f-ayFYFKDvnVZqU,3161888
40
40
  cognite/neat/app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg,sha256=EYf9q9JoVJ1L1np-XloeEZXCmaibzKmmpXCKn_44xzA,240334
41
41
  cognite/neat/config.py,sha256=MbYOvlibOjODEPcCHNKwZHVjg9ft8kZSojnW4gQCiHQ,10116
42
- cognite/neat/constants.py,sha256=_Pkiux_g_YmrtqzkuvtahJ2PRJsd7LnvSTrzOn6Z1wQ,953
42
+ cognite/neat/constants.py,sha256=mhyB61-IKwREbq_8IL6Y0pNfZhCiceWlgwtWW42bkBE,913
43
43
  cognite/neat/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
44
  cognite/neat/graph/_shared.py,sha256=9QRETdm7hvqIeiHv_n1xi1DUq91Nq7oRRpnPKE0Pnag,181
45
45
  cognite/neat/graph/_tracking/__init__.py,sha256=pYj7c-YAUIP4hvN-4mlWnwaeZFerzL9_gM-oZhex7cE,91
@@ -49,11 +49,11 @@ cognite/neat/graph/examples/Knowledge-Graph-Nordic44-dirty.xml,sha256=ujJip6XBs5
49
49
  cognite/neat/graph/examples/Knowledge-Graph-Nordic44.xml,sha256=U2Ns-M4LRjT1fBkhmRj63ur7jDzlRtHK9yOLf_npZ_g,1437996
50
50
  cognite/neat/graph/examples/__init__.py,sha256=yAjHVY3b5jOjmbW-iLbhvu7BG014TpGi3K4igkDqW5I,368
51
51
  cognite/neat/graph/examples/skos-capturing-sheet-wind-topics.xlsx,sha256=CV_yK5ZSbYS_ktfIZUPD8Sevs47zpswLXQUDFkGE4Gw,45798
52
- cognite/neat/graph/extractors/__init__.py,sha256=nXcNp6i3-1HteIkr8Ujxk4b09W5jk27Q3eWuwjcnGnM,1647
52
+ cognite/neat/graph/extractors/__init__.py,sha256=timbuOI3YBPBFu1lY42KDAN5aDJ32gIseKsjT7e0fgU,1698
53
53
  cognite/neat/graph/extractors/_base.py,sha256=8IWygpkQTwo0UOmbbwWVI7540_klTVdUVX2JjVPFRIs,498
54
54
  cognite/neat/graph/extractors/_classic_cdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  cognite/neat/graph/extractors/_classic_cdf/_assets.py,sha256=xVqWC_lyqX5RKGfTxX0Gb96A0GAyM13d6YIjd1xEbpI,6524
56
- cognite/neat/graph/extractors/_classic_cdf/_base.py,sha256=FLMGeF5mefy-85B0wJxaAlX-YpDPYEq7-RYw_WYNhjE,4463
56
+ cognite/neat/graph/extractors/_classic_cdf/_base.py,sha256=oLqYxNkAgYCZlAGMqfOVTDbRnouyf72m54d1BfcpeAA,4494
57
57
  cognite/neat/graph/extractors/_classic_cdf/_events.py,sha256=8shnrQD9LZCrKPIt7xeU6ac0-lSkIwGe9KB0DdJ7Eik,5600
58
58
  cognite/neat/graph/extractors/_classic_cdf/_files.py,sha256=wa2jzbpOj4BssVY7PbgdGymFZGSrvd1GZaRVN-JjOXQ,6588
59
59
  cognite/neat/graph/extractors/_classic_cdf/_labels.py,sha256=Xvyv22jebq4s94Sl1h2SMEeBvOkoTIPujB9tczRgcDo,4359
@@ -61,15 +61,16 @@ cognite/neat/graph/extractors/_classic_cdf/_relationships.py,sha256=VxmwnZ_vN_P1
61
61
  cognite/neat/graph/extractors/_classic_cdf/_sequences.py,sha256=sTtVPngffQw2fyvoRG1ABC2yFkepA6skFT1XdCzg4cU,5086
62
62
  cognite/neat/graph/extractors/_classic_cdf/_timeseries.py,sha256=zmVf5pO-G4ro3elKt0sCTfTtLulkEFoEahLgP9J5kKE,6581
63
63
  cognite/neat/graph/extractors/_dexpi.py,sha256=Q3whJpEi3uFMzJGAAeUfgRnAzz6ZHmtuEdVBWqsZsTM,9384
64
- cognite/neat/graph/extractors/_mock_graph_generator.py,sha256=mw7tdqCEhaFxyzcKTWlXLj62KVo5D1rJFUPf6xqDKQU,14752
64
+ cognite/neat/graph/extractors/_dms.py,sha256=NbqY1nrisn-dJq8_qeCatpgJwm6-cKFsTISDR5afdbU,6688
65
+ cognite/neat/graph/extractors/_mock_graph_generator.py,sha256=ES-7__o750BJFC9rNWt5HRO5M53iZuzv-8lBqLIXdC0,15365
65
66
  cognite/neat/graph/extractors/_rdf_file.py,sha256=ialMCLv9WH5k6v1YMfozfcmAYhz8OVo9jVhsKMyQkDA,763
66
67
  cognite/neat/graph/loaders/__init__.py,sha256=TbeJqifd16JLOglPVNOeb6pN_w060UYag50KquBM_r0,769
67
68
  cognite/neat/graph/loaders/_base.py,sha256=5CpNsSj3WGCCjT2dC4GhnbArSD7DlWg_kXtze_ediLg,3614
68
69
  cognite/neat/graph/loaders/_rdf2asset.py,sha256=Opq7BNAzrAYq-ffF2ayAqNVopyBDrayn0hj07KVFsyI,17504
69
- cognite/neat/graph/loaders/_rdf2dms.py,sha256=s1rCOAsZjcmhkRsW9_VbD9zL2yY9lAcjOtQirmzDCEw,14713
70
- cognite/neat/graph/models.py,sha256=AtLgZh2qyRP6NRetjQCy9qLMuTQB0CH52Zsev-qa2sk,149
70
+ cognite/neat/graph/loaders/_rdf2dms.py,sha256=bFfl3UKVwewGxAGAFlpPrJCKHq-aZdpYtDNYugIWq7g,14977
71
+ cognite/neat/graph/models.py,sha256=Z9aj825ZS_BCU6rwekyxKq7LL0QMh6i0DXGp7QQf_D4,182
71
72
  cognite/neat/graph/queries/__init__.py,sha256=BgDd-037kvtWwAoGAy8eORVNMiZ5-E9sIV0txIpeaN4,50
72
- cognite/neat/graph/queries/_base.py,sha256=H1ypFF_Fjls_oViF_5rrfTZzFOjwA4AHnzPdnmLNdXM,8243
73
+ cognite/neat/graph/queries/_base.py,sha256=SCBPd4w5BkXU6vq1DWDqRtY4JDPxXm3zDIMRZzpV2-s,8404
73
74
  cognite/neat/graph/queries/_construct.py,sha256=lDquCxjiaUzL3G48ZQffrGJMcqPkWVtCdkfatgPAUKI,7208
74
75
  cognite/neat/graph/queries/_shared.py,sha256=Kk53TqJmwD2G-rxhLq_jJXYy8jvLsUAAWGJS5r9pOaY,5327
75
76
  cognite/neat/graph/transformers/__init__.py,sha256=FMvlDEDJHrZL4Jb_H0AiJZO5uHXBCuLhB5HVDuinnBI,665
@@ -77,7 +78,7 @@ cognite/neat/graph/transformers/_base.py,sha256=b37Ek-9njuM5pTR_3XhnxCMrg_ip_2BM
77
78
  cognite/neat/graph/transformers/_classic_cdf.py,sha256=6xX-OBSJT5DAQrTJ-nuhCfGNaSk5Iktxn-WIMfzEIqo,13189
78
79
  cognite/neat/graph/transformers/_rdfpath.py,sha256=VLtGJvTPT5SWhV98fuGGt0pptTXfzOOQSNehypsPHug,1861
79
80
  cognite/neat/issues/__init__.py,sha256=KkBEO-0Lg3vdvjrQtxKR6Wy2iV2mooc9utSO8-_9UuI,405
80
- cognite/neat/issues/_base.py,sha256=C8_FcjaMwloSpZgpkiHjtV1JeZF4Upw5bXpOd-1CyAY,15163
81
+ cognite/neat/issues/_base.py,sha256=g-_C2zIitewZLHQkV84aBRQJU2UnT_-pl98PFcfmNy0,15183
81
82
  cognite/neat/issues/errors/__init__.py,sha256=t23ZnbWOZEhd77Ae_gpAxMPAIWOTbt7-wi6yeCmlGZE,2077
82
83
  cognite/neat/issues/errors/_external.py,sha256=TUdihRyr5amdGSzSU49hWz7N7BSPCTVo7glKFNDrc5w,1583
83
84
  cognite/neat/issues/errors/_general.py,sha256=Dtt4kEfNyj-CxpowsVVrq2eAwWEzx68JpPnS48QoY2U,787
@@ -85,11 +86,11 @@ cognite/neat/issues/errors/_properties.py,sha256=7To9RvOyBzspZrs4jG3kAYJ3bzmMjDl
85
86
  cognite/neat/issues/errors/_resources.py,sha256=SqzHY4Mtm-JEYSKZO8x4Mh7vOVWMV3nOEu1JrUVdjIc,3863
86
87
  cognite/neat/issues/errors/_workflow.py,sha256=nC3sxqlJSyi-kmo2ZAfxEH23ZuNRMC-oBcwQcl_2hkU,950
87
88
  cognite/neat/issues/formatters.py,sha256=QCk41VLlpq-R9uaHpINYceZkIUoI9m4pwSq_yWPOmr8,3331
88
- cognite/neat/issues/warnings/__init__.py,sha256=ZnQIaA8WAG378DLjCTSrlGLtGO7A33453-7ShCaBqtw,2250
89
+ cognite/neat/issues/warnings/__init__.py,sha256=-PVXhGHmzMa35heEbBaheJyOUxYX520anfJJRTyi_nA,2330
89
90
  cognite/neat/issues/warnings/_external.py,sha256=pauqW4VjKnfflUiz7J8EXtTdpXxCuCcE-EuzvXlPJuI,953
90
91
  cognite/neat/issues/warnings/_general.py,sha256=yLfooeTwq57LQPbvvVgMZDBZpyrnalXHlA6bu0lSqFg,601
91
92
  cognite/neat/issues/warnings/_models.py,sha256=HIhn5_hICqE0sFiosI7HiJnn3gyJPkQxOk1kXpAdG-E,3026
92
- cognite/neat/issues/warnings/_properties.py,sha256=DPoUOlWJ0G5IllJEDHvVCTgngGkrzJDunoyzhW9RhGg,1511
93
+ cognite/neat/issues/warnings/_properties.py,sha256=zIXT1sezgyPPZgvytavP8bRRR9_DgvVM_awiDD-pdjM,1940
93
94
  cognite/neat/issues/warnings/_resources.py,sha256=RpmZ-KLEyTTckfAG9IvQVslq8yjN9NPS9Q_Oa6Y3ps8,1788
94
95
  cognite/neat/issues/warnings/user_modeling.py,sha256=RwslCYWLZzmoX7zX8O2Ha5pgghbhIOLQCZyev5CppO8,3602
95
96
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -110,7 +111,7 @@ cognite/neat/rules/exporters/_rules2yaml.py,sha256=2yAkU3b4P4UYdsCyAZn5K2OwphnRX
110
111
  cognite/neat/rules/exporters/_validation.py,sha256=A0kyrIEu51ZXl47CXmO4ZOT0whO5iKNwMY-BwMc2scA,680
111
112
  cognite/neat/rules/importers/__init__.py,sha256=z682_ktGKDjr52DIL6cPvOercZS6-TYD_ZDo-MGqtck,1207
112
113
  cognite/neat/rules/importers/_base.py,sha256=G9apdRztiAdnzX3LyZ-SNaavXagvtta2BQXFqEOQw_g,2851
113
- cognite/neat/rules/importers/_dms2rules.py,sha256=4RFk0hebKq2uiHMwoV9s8ZLU1ztWyTAQLo7sJHl2f_I,22900
114
+ cognite/neat/rules/importers/_dms2rules.py,sha256=us8I9drw_lBCn-n5-9ZZ-RTvbn-bCPWMno08UO2klDg,22895
114
115
  cognite/neat/rules/importers/_dtdl2rules/__init__.py,sha256=CNR-sUihs2mnR1bPMKs3j3L4ds3vFTsrl6YycExZTfU,68
115
116
  cognite/neat/rules/importers/_dtdl2rules/_unit_lookup.py,sha256=wW4saKva61Q_i17guY0dc4OseJDQfqHy_QZBtm0OD6g,12134
116
117
  cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py,sha256=WklOdKq1p-w-j3bloDGX-eJIltyD_1mwo4aZbKY9UG4,11904
@@ -122,7 +123,7 @@ cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py,sha256=bXlzybKhiCmh
122
123
  cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py,sha256=AHRyyt25Av_MS4iWFw49MCabz49Ng1mDGyLKXufisFM,957
123
124
  cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py,sha256=aF7Jy29Jo2hm8rybRDyBxoLGycOx3UsM6Gq-ztBF41Y,6170
124
125
  cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py,sha256=YFLXYjzsKCGL4rxE8gllpJ1LKeVEkNwXscE3aCyxlYs,6289
125
- cognite/neat/rules/importers/_rdf/_inference2rules.py,sha256=IZ7i5HeO7O3FA5JBrev0wVy8Sjcb8z3-pCoq0NjKuGk,13077
126
+ cognite/neat/rules/importers/_rdf/_inference2rules.py,sha256=ArD7zwiSbU1YXxycJ03F3Zju26XNUbsHtsR9PtlH5N8,12663
126
127
  cognite/neat/rules/importers/_rdf/_owl2rules/__init__.py,sha256=tdGcrgtozdQyST-pTlxIa4cLBNTLvtk1nNYR4vOdFSw,63
127
128
  cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py,sha256=VLlr2UHAia0xWaeg3tKJ3N0sq_nYur3UqcOXgjMYxzg,1934
128
129
  cognite/neat/rules/importers/_rdf/_owl2rules/_owl2metadata.py,sha256=3eTXm0irYJcjMV4H57xblP_iaNO_T0JErQuAYZfNaFA,2659
@@ -143,7 +144,7 @@ cognite/neat/rules/models/asset/_serializer.py,sha256=ixqRf9qEzvChgysRaDX4g_vHVD
143
144
  cognite/neat/rules/models/asset/_validation.py,sha256=3goorodISq_mlyXroaivcMOZ-QV8sd27IK9-iGKnQ28,2014
144
145
  cognite/neat/rules/models/data_types.py,sha256=jTYsWqQPuvwHytInRU0Y2TGF4aVBF83v0vp_SH9KgLA,9722
145
146
  cognite/neat/rules/models/dms/__init__.py,sha256=CUqUlVjz4yZX_-61F-2ofSoV7N9MlSYx2N7vM-omp7E,640
146
- cognite/neat/rules/models/dms/_exporter.py,sha256=YGFX6jCapxcWTcvhkQL821jMZ3lAEr3NkVZcgUiUeK4,28206
147
+ cognite/neat/rules/models/dms/_exporter.py,sha256=4XiFCeXZOZuj5Ke1E-_rQJvPIcyRl7lIb2dHCfkYS4M,28126
147
148
  cognite/neat/rules/models/dms/_rules.py,sha256=iwCQx0MVQVe1R0bFIIdDD5unyuzQ7tIZDQM7NmdbJp8,14871
148
149
  cognite/neat/rules/models/dms/_rules_input.py,sha256=v5-zlb4VJi5Q610rnPLU1aHKzXmGwoUTrDzAkJFfEQY,10911
149
150
  cognite/neat/rules/models/dms/_schema.py,sha256=lc6Q0EUchOAUSTRiJnWc6UPBz7LjCW5NEvIGwNakcSI,50724
@@ -164,12 +165,12 @@ cognite/neat/rules/models/information/_serializer.py,sha256=yti9I_xJruxrib66YIBI
164
165
  cognite/neat/rules/models/information/_validation.py,sha256=Fa5S-rQozSCkIDpS4dPQn7U0lM71DOecAMCqL8K8Uag,9230
165
166
  cognite/neat/rules/transformers/__init__.py,sha256=Iun5-3uDmzUzcO4IFneJ453PWAx6F_c-5LhkvrIrSc0,666
166
167
  cognite/neat/rules/transformers/_base.py,sha256=FABG_8Xg_LUZPwVLQmKvxlcIDtI5phGLpEGpxNcJWNM,3269
167
- cognite/neat/rules/transformers/_converters.py,sha256=AuCvFjwYQWIQpg7OMuYmm0vqUNC9sd1Zc2Xnletm3LU,22295
168
+ cognite/neat/rules/transformers/_converters.py,sha256=8qvgAbW4U7gcYEYkgFkrvOKE1P-PwNjg-nOIvOgbhnE,22763
168
169
  cognite/neat/rules/transformers/_map_onto.py,sha256=eXPhontrcJdRm2ILopwkFoPcwfM8L-BNv-CapL30klg,4824
169
170
  cognite/neat/rules/transformers/_pipelines.py,sha256=of3NJ4gsLeKr3NiTfBMQVl1J5b2IwI5JWm8FP7oQ3B4,2438
170
171
  cognite/neat/rules/transformers/_verification.py,sha256=rPSeDKkpe-hCJ0iiBx6yycvSANwnCS78PUFTDYgmNcA,4448
171
172
  cognite/neat/store/__init__.py,sha256=G-VG_YwfRt1kuPao07PDJyZ3w_0-eguzLUM13n-Z_RA,64
172
- cognite/neat/store/_base.py,sha256=HC7PpqQV_KMxdF61aocwU3e7ljvpVHp4xvNwNKIrhBU,12442
173
+ cognite/neat/store/_base.py,sha256=WcwVxRo7UpSNUJ221yq26_XtaeX6xW647qkf9xG8aK0,12471
173
174
  cognite/neat/store/_provenance.py,sha256=sniVJhLmvj0ulKhoUZzFLwjAsp7cQyRPxunZtLmMW4A,3902
174
175
  cognite/neat/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
175
176
  cognite/neat/utils/auth.py,sha256=k0sEfTpK_bamMjAkj7jN6n9yta8TaqHTFkZUjUgpwik,12770
@@ -216,8 +217,8 @@ cognite/neat/workflows/steps_registry.py,sha256=FjMsFBlFFy82ABUzDnWoFidYODV3pp3c
216
217
  cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
217
218
  cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
218
219
  cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
219
- cognite_neat-0.89.0.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
220
- cognite_neat-0.89.0.dist-info/METADATA,sha256=Clo2UFQRsNCKjslhU42UhpuMSU4HvN1Kvj0Od0BYbm4,9441
221
- cognite_neat-0.89.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
222
- cognite_neat-0.89.0.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
223
- cognite_neat-0.89.0.dist-info/RECORD,,
220
+ cognite_neat-0.90.0.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
221
+ cognite_neat-0.90.0.dist-info/METADATA,sha256=a6RmI8e8zGcHtk6RhHErdW-QGwuzSD3T6du_pzBBQhM,9441
222
+ cognite_neat-0.90.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
223
+ cognite_neat-0.90.0.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
224
+ cognite_neat-0.90.0.dist-info/RECORD,,