cognite-neat 0.101.0__py3-none-any.whl → 0.102.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.

@@ -71,7 +71,7 @@ UNKNOWN_TYPE = DEFAULT_NAMESPACE.UnknownType
71
71
  XML_SCHEMA_NAMESPACE = Namespace("http://www.w3.org/2001/XMLSchema#")
72
72
 
73
73
 
74
- def get_default_prefixes() -> dict[str, Namespace]:
74
+ def get_default_prefixes_and_namespaces() -> dict[str, Namespace]:
75
75
  return {
76
76
  "owl": OWL._NS,
77
77
  "rdf": RDF._NS,
@@ -84,6 +84,10 @@ def get_default_prefixes() -> dict[str, Namespace]:
84
84
  "imf": Namespace("http://ns.imfid.org/imf#"),
85
85
  "pav": Namespace("http://purl.org/pav/"),
86
86
  "foaf": FOAF._NS,
87
+ "dexpi": Namespace("http://sandbox.dexpi.org/rdl/"),
88
+ "qudt": Namespace("https://qudt.org/vocab/unit/"),
89
+ "iodd": Namespace("http://www.io-link.com/IODD/2010/10/"),
90
+ "aml": Namespace("https://www.automationml.org/"),
87
91
  }
88
92
 
89
93
 
@@ -197,7 +197,7 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
197
197
  json_fields: list[str] = []
198
198
  for prop_name, prop in view.properties.items():
199
199
  if isinstance(prop, dm.EdgeConnection):
200
- edge_by_property[prop.type.external_id] = prop_name, prop
200
+ edge_by_property[prop_name] = prop_name, prop
201
201
  if isinstance(prop, dm.MappedProperty):
202
202
  if isinstance(prop.type, dm.DirectRelation):
203
203
  direct_relation_by_property[prop_name] = prop.type
@@ -209,7 +209,10 @@ class Queries:
209
209
  elif (
210
210
  isinstance(object_, URIRef)
211
211
  and property_types
212
- and property_types.get(property_, None) == EntityTypes.object_property
212
+ and (
213
+ property_types.get(property_, None) == EntityTypes.object_property
214
+ or property_types.get(property_, None) == EntityTypes.undefined
215
+ )
213
216
  ):
214
217
  value = remove_namespace_from_uri(object_, validation="prefix")
215
218
 
@@ -233,7 +236,6 @@ class Queries:
233
236
  else:
234
237
  # we should not have multiple rdf:type values
235
238
  continue
236
-
237
239
  if property_values:
238
240
  return (
239
241
  identifier,
@@ -358,3 +360,21 @@ class Queries:
358
360
  dropped_types[t] = len(instance_ids)
359
361
  remove_instance_ids_in_batch(self.graph, instance_ids)
360
362
  return dropped_types
363
+
364
+ def multi_type_instances(self) -> dict[str, list[str]]:
365
+ """Find instances with multiple types"""
366
+
367
+ query = """
368
+ SELECT ?instance (GROUP_CONCAT(str(?type); SEPARATOR=",") AS ?types)
369
+ WHERE {
370
+ ?instance a ?type .
371
+ }
372
+ GROUP BY ?instance
373
+ HAVING (COUNT(?type) > 1)
374
+ """
375
+
376
+ result = {}
377
+ for instance, types in self.graph.query(query): # type: ignore
378
+ result[remove_namespace_from_uri(instance)] = remove_namespace_from_uri(types.split(","))
379
+
380
+ return result
@@ -6,7 +6,7 @@ from pydantic import BaseModel, ConfigDict, Field
6
6
  from rdflib import Graph, Literal, Namespace
7
7
  from rdflib.term import URIRef
8
8
 
9
- from cognite.neat._constants import get_default_prefixes
9
+ from cognite.neat._constants import get_default_prefixes_and_namespaces
10
10
  from cognite.neat._rules.models._rdfpath import (
11
11
  Hop,
12
12
  Step,
@@ -51,7 +51,7 @@ def generate_prefix_header(prefixes: dict[str, Namespace] | None = None) -> str:
51
51
  Prefix header
52
52
  """
53
53
 
54
- prefixes = prefixes or get_default_prefixes()
54
+ prefixes = prefixes or get_default_prefixes_and_namespaces()
55
55
 
56
56
  return "".join(f"PREFIX {key}:<{value}>\n" for key, value in prefixes.items())
57
57
 
@@ -81,7 +81,7 @@ def get_predicate_id(
81
81
  ID of predicate (aka property) connecting subject and object
82
82
  """
83
83
 
84
- prefixes = prefixes or get_default_prefixes()
84
+ prefixes = prefixes or get_default_prefixes_and_namespaces()
85
85
 
86
86
  query = """
87
87
 
@@ -123,7 +123,7 @@ def hop2property_path(
123
123
  str
124
124
  Property path string for hop traversal (e.g. ^rdf:type/rdfs:subClassOf)
125
125
  """
126
- prefixes = prefixes if prefixes else get_default_prefixes()
126
+ prefixes = prefixes if prefixes else get_default_prefixes_and_namespaces()
127
127
 
128
128
  # setting previous step to origin, as we are starting from there
129
129
  previous_step = Step(class_=hop.class_, direction="origin")
@@ -7,7 +7,13 @@ from ._classic_cdf import (
7
7
  AssetTimeSeriesConnector,
8
8
  RelationshipAsEdgeTransformer,
9
9
  )
10
- from ._prune_graph import AttachPropertyFromTargetToSource, PruneDanglingNodes
10
+ from ._prune_graph import (
11
+ AttachPropertyFromTargetToSource,
12
+ PruneDanglingNodes,
13
+ PruneDeadEndEdges,
14
+ PruneInstancesOfUnknownType,
15
+ PruneTypes,
16
+ )
11
17
  from ._rdfpath import AddSelfReferenceProperty, MakeConnectionOnExactMatch
12
18
  from ._value_type import SplitMultiValueProperty
13
19
 
@@ -24,6 +30,9 @@ __all__ = [
24
30
  "MakeConnectionOnExactMatch",
25
31
  "AttachPropertyFromTargetToSource",
26
32
  "PruneDanglingNodes",
33
+ "PruneTypes",
34
+ "PruneDeadEndEdges",
35
+ "PruneInstancesOfUnknownType",
27
36
  ]
28
37
 
29
38
  Transformers = (
@@ -39,4 +48,7 @@ Transformers = (
39
48
  | MakeConnectionOnExactMatch
40
49
  | AttachPropertyFromTargetToSource
41
50
  | PruneDanglingNodes
51
+ | PruneTypes
52
+ | PruneDeadEndEdges
53
+ | PruneInstancesOfUnknownType
42
54
  )
@@ -6,7 +6,7 @@ from rdflib import Graph
6
6
 
7
7
  class BaseTransformer(ABC):
8
8
  description: str
9
- _use_only_once: bool
9
+ _use_only_once: bool = False
10
10
  _need_changes: ClassVar[frozenset[str]] = frozenset()
11
11
 
12
12
  @abstractmethod
@@ -17,9 +17,9 @@ class IODDAttachPropertyFromTargetToSource(AttachPropertyFromTargetToSource):
17
17
  def __init__(self):
18
18
  super().__init__(
19
19
  target_node_type=IODD.TextObject,
20
- namespace=IODD,
21
- target_property="value",
20
+ target_property=IODD.value,
22
21
  delete_target_node=True,
22
+ namespace=IODD,
23
23
  )
24
24
 
25
25
 
@@ -1,6 +1,9 @@
1
+ from typing import cast
2
+
1
3
  from rdflib import Graph, Namespace, URIRef
2
- from rdflib.query import ResultRow
3
4
 
5
+ from cognite.neat._constants import DEFAULT_NAMESPACE
6
+ from cognite.neat._shared import Triple
4
7
  from cognite.neat._utils.rdf_ import as_neat_compliant_uri
5
8
  from cognite.neat._utils.text import sentence_or_string_to_camel
6
9
 
@@ -55,12 +58,13 @@ class AttachPropertyFromTargetToSource(BaseTransformer):
55
58
 
56
59
  Args:
57
60
  target_node_type: RDF.type of edge Node
58
- namespace: RDF Namespace to use when querying the graph
59
- target_property: str with name of the property that holds the value attached to the intermediate node
60
- target_property_holding_new_property_name: Optional str of the property name that holds
61
- the new predicate to use when attaching the new property to the source node.
61
+ target_property: URIRef of the property that holds the value attached to the intermediate node
62
+ target_property_holding_new_property: URIRef of the property which value will be new
63
+ property that will be added to the source node
62
64
  delete_target_node: bool if the intermediate Node and Edge between source Node
63
65
  and target property should be deleted. Defaults to False.
66
+ convert_literal_to_uri: bool if the value of the new property should be converted to URIRef. Defaults to False.
67
+ namespace: Namespace to use when converting value to URIRef. Defaults to DEFAULT_NAMESPACE.
64
68
  """
65
69
 
66
70
  description: str = "Attaches a target property from a target node that is connected to a source node."
@@ -82,25 +86,26 @@ class AttachPropertyFromTargetToSource(BaseTransformer):
82
86
  def __init__(
83
87
  self,
84
88
  target_node_type: URIRef,
85
- namespace: Namespace,
86
- target_property: str,
87
- target_property_holding_new_property_name: str | None = None,
89
+ target_property: URIRef,
90
+ target_property_holding_new_property: URIRef | None = None,
88
91
  delete_target_node: bool = False,
92
+ convert_literal_to_uri: bool = False,
93
+ namespace: Namespace | None = None,
89
94
  ):
90
95
  self.target_node_type = target_node_type
91
- self.namespace = namespace
92
- self.target_property = self.namespace[target_property]
96
+ self.target_property = target_property
93
97
  self.delete_target_node = delete_target_node
94
- self.target_property_holding_new_property_name = target_property_holding_new_property_name
98
+ self.target_property_holding_new_property = target_property_holding_new_property
99
+ self.convert_literal_to_uri = convert_literal_to_uri
100
+ self.namespace = namespace or DEFAULT_NAMESPACE
95
101
 
96
102
  def transform(self, graph) -> None:
97
103
  nodes_to_delete: list[tuple] = []
98
104
 
99
- if self.target_property_holding_new_property_name is not None:
100
- target_property_holding_new_property_name = self.namespace[self.target_property_holding_new_property_name]
105
+ if self.target_property_holding_new_property is not None:
101
106
  query = self._query_template_use_case_b.format(
102
107
  target_node_type=self.target_node_type,
103
- target_property_holding_new_property_name=target_property_holding_new_property_name,
108
+ target_property_holding_new_property_name=self.target_property_holding_new_property,
104
109
  target_property=self.target_property,
105
110
  )
106
111
  else:
@@ -116,15 +121,20 @@ class AttachPropertyFromTargetToSource(BaseTransformer):
116
121
  new_predicate_value,
117
122
  new_property_value,
118
123
  ) in graph.query(query):
119
- if self.target_property_holding_new_property_name is not None:
124
+ if self.target_property_holding_new_property is not None:
120
125
  # Ensure new predicate is URI compliant as we are creating a new predicate
121
126
  new_predicate_value_string = sentence_or_string_to_camel(str(new_predicate_value))
122
127
  predicate = as_neat_compliant_uri(self.namespace[new_predicate_value_string])
123
128
  else:
124
129
  predicate = old_predicate
125
-
126
130
  # Create new connection from source node to value
127
- graph.add((source_node, predicate, new_property_value))
131
+ graph.add(
132
+ (
133
+ source_node,
134
+ predicate,
135
+ (self.namespace[new_property_value] if self.convert_literal_to_uri else new_property_value),
136
+ )
137
+ )
128
138
  # Remove old relationship between source node and destination node
129
139
  graph.remove((source_node, old_predicate, target_node))
130
140
 
@@ -158,7 +168,7 @@ class PruneDanglingNodes(BaseTransformer):
158
168
  node_prune_types: list of RDF types to prune from the Graph if they are stand-alone Nodes
159
169
  """
160
170
 
161
- description: str = "Prunes the graph of specified rdf types that do not have connections to other nodes."
171
+ description: str = "Prunes nodes of specific rdf types that do not have any connection to them."
162
172
  _query_template = """
163
173
  SELECT ?subject
164
174
  WHERE {{
@@ -174,10 +184,74 @@ class PruneDanglingNodes(BaseTransformer):
174
184
  self.node_prune_types = node_prune_types
175
185
 
176
186
  def transform(self, graph: Graph) -> None:
177
- for object_type in self.node_prune_types:
178
- nodes_without_neighbours = list(graph.query(self._query_template.format(rdf_type=object_type)))
187
+ for type_ in self.node_prune_types:
188
+ for (subject,) in list(graph.query(self._query_template.format(rdf_type=type_))): # type: ignore
189
+ graph.remove((subject, None, None))
190
+
191
+
192
+ class PruneTypes(BaseTransformer):
193
+ """
194
+ Removes all the instances of specific type
195
+ """
196
+
197
+ description: str = "Prunes nodes of specific rdf types"
198
+ _query_template = """
199
+ SELECT ?subject
200
+ WHERE {{
201
+ ?subject a <{rdf_type}> .
202
+ }}
203
+ """
204
+
205
+ def __init__(
206
+ self,
207
+ node_prune_types: list[URIRef],
208
+ ):
209
+ self.node_prune_types = node_prune_types
210
+
211
+ def transform(self, graph: Graph) -> None:
212
+ for type_ in self.node_prune_types:
213
+ for (subject,) in list(graph.query(self._query_template.format(rdf_type=type_))): # type: ignore
214
+ graph.remove((subject, None, None))
179
215
 
180
- for node in nodes_without_neighbours:
181
- # Remove node and its property triples in the graph
182
- if isinstance(node, ResultRow):
183
- graph.remove((node["subject"], None, None))
216
+
217
+ class PruneDeadEndEdges(BaseTransformer):
218
+ """
219
+ Removes all the triples where object is a node that is not found in graph
220
+ """
221
+
222
+ description: str = "Prunes the graph of specified rdf types that do not have connections to other nodes."
223
+ _query_template = """
224
+ SELECT ?subject ?predicate ?object
225
+ WHERE {
226
+ ?subject ?predicate ?object .
227
+ FILTER (isIRI(?object) && ?predicate != rdf:type)
228
+ FILTER NOT EXISTS {?object ?p ?o .}
229
+
230
+ }
231
+
232
+ """
233
+
234
+ def transform(self, graph: Graph) -> None:
235
+ for triple in graph.query(self._query_template):
236
+ graph.remove(cast(Triple, triple))
237
+
238
+
239
+ class PruneInstancesOfUnknownType(BaseTransformer):
240
+ """
241
+ Removes all the triples where object is a node that is not found in graph
242
+ """
243
+
244
+ description: str = "Prunes the graph of specified rdf types that do not have connections to other nodes."
245
+ _query_template = """
246
+ SELECT DISTINCT ?subject
247
+ WHERE {
248
+ ?subject ?p ?o .
249
+ FILTER NOT EXISTS {?subject a ?object .}
250
+
251
+ }
252
+
253
+ """
254
+
255
+ def transform(self, graph: Graph) -> None:
256
+ for (subject,) in graph.query(self._query_template): # type: ignore
257
+ graph.remove((subject, None, None))
@@ -34,8 +34,6 @@ class ExcelExporter(BaseExporter[VerifiedRules, Workbook]):
34
34
  Args:
35
35
  styling: The styling to use for the Excel file. Defaults to "default". See below for details
36
36
  on the different styles.
37
- output_role: The role to use for the exported spreadsheet. If provided, the rules will be converted to
38
- this role formate before being written to excel. If not provided, the role from the rules will be used.
39
37
  new_model_id: The new model ID to use for the exported spreadsheet. This is only applicable if the input
40
38
  rules have 'is_reference' set. If provided, the model ID will be used to automatically create the
41
39
  new metadata sheet in the Excel file. The model id is expected to be a tuple of (prefix, title)
@@ -120,6 +118,10 @@ class ExcelExporter(BaseExporter[VerifiedRules, Workbook]):
120
118
  main_header = self._main_header_by_sheet_name[sheet_name]
121
119
  sheet.append([main_header] + [""] * (len(headers) - 1))
122
120
  sheet.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(headers))
121
+ if headers[0] == "Neat ID":
122
+ # Move the Neat ID to the end of the columns
123
+ headers = headers[1:] + ["Neat ID"]
124
+
123
125
  sheet.append(headers)
124
126
 
125
127
  fill_colors = itertools.cycle(["CADCFC", "FFFFFF"])
@@ -127,6 +129,9 @@ class ExcelExporter(BaseExporter[VerifiedRules, Workbook]):
127
129
  last_class: str | None = None
128
130
  item: dict[str, Any]
129
131
  for item in dumped_rules.get(sheet_name) or []:
132
+ if "Neat ID" in item:
133
+ # Move the Neat ID to the end of the columns
134
+ item["Neat ID"] = item.pop("Neat ID")
130
135
  row = list(item.values())
131
136
  class_ = row[0]
132
137
 
@@ -1,4 +1,4 @@
1
- from collections import Counter
1
+ from collections import Counter, defaultdict
2
2
  from collections.abc import Collection, Iterable, Sequence
3
3
  from datetime import datetime, timezone
4
4
  from pathlib import Path
@@ -222,12 +222,18 @@ class DMSImporter(BaseImporter[DMSInputRules]):
222
222
  schema: DMSSchema,
223
223
  metadata: DMSInputMetadata | None = None,
224
224
  ) -> DMSInputRules:
225
+ enum_by_container_property = self._create_enum_collections(schema.containers.values())
226
+ enum_collection_by_container_property = {
227
+ key: enum_list[0].collection for key, enum_list in enum_by_container_property.items() if enum_list
228
+ }
229
+
225
230
  properties: list[DMSInputProperty] = []
226
231
  for view_id, view in schema.views.items():
227
232
  view_entity = ViewEntity.from_id(view_id)
228
- class_entity = view_entity.as_class()
229
233
  for prop_id, prop in (view.properties or {}).items():
230
- dms_property = self._create_dms_property(prop_id, prop, view_entity, class_entity)
234
+ dms_property = self._create_dms_property(
235
+ prop_id, prop, view_entity, enum_collection_by_container_property
236
+ )
231
237
  if dms_property is not None:
232
238
  properties.append(dms_property)
233
239
 
@@ -237,8 +243,6 @@ class DMSImporter(BaseImporter[DMSInputRules]):
237
243
 
238
244
  metadata = metadata or DMSInputMetadata.from_data_model(data_model)
239
245
 
240
- enum = self._create_enum_collections(schema.containers.values())
241
-
242
246
  return DMSInputRules(
243
247
  metadata=metadata,
244
248
  properties=properties,
@@ -248,7 +252,7 @@ class DMSImporter(BaseImporter[DMSInputRules]):
248
252
  for view_id, view in schema.views.items()
249
253
  ],
250
254
  nodes=[DMSInputNode.from_node_type(node_type) for node_type in schema.node_types.values()],
251
- enum=enum,
255
+ enum=[enum for enum_list in enum_by_container_property.values() for enum in enum_list] or None,
252
256
  )
253
257
 
254
258
  @classmethod
@@ -267,7 +271,11 @@ class DMSImporter(BaseImporter[DMSInputRules]):
267
271
  )
268
272
 
269
273
  def _create_dms_property(
270
- self, prop_id: str, prop: ViewPropertyApply, view_entity: ViewEntity, class_entity: ClassEntity
274
+ self,
275
+ prop_id: str,
276
+ prop: ViewPropertyApply,
277
+ view_entity: ViewEntity,
278
+ enum_collection_by_container_property: dict[tuple[dm.ContainerId, str], str],
271
279
  ) -> DMSInputProperty | None:
272
280
  if isinstance(prop, dm.MappedPropertyApply) and prop.container not in self._all_containers_by_id:
273
281
  self.issue_list.append(
@@ -300,7 +308,7 @@ class DMSImporter(BaseImporter[DMSInputRules]):
300
308
  )
301
309
  return None
302
310
 
303
- value_type = self._get_value_type(prop, view_entity, prop_id)
311
+ value_type = self._get_value_type(prop, view_entity, prop_id, enum_collection_by_container_property)
304
312
  if value_type is None:
305
313
  return None
306
314
 
@@ -347,7 +355,11 @@ class DMSImporter(BaseImporter[DMSInputRules]):
347
355
  return None
348
356
 
349
357
  def _get_value_type(
350
- self, prop: ViewPropertyApply, view_entity: ViewEntity, prop_id
358
+ self,
359
+ prop: ViewPropertyApply,
360
+ view_entity: ViewEntity,
361
+ prop_id: str,
362
+ enum_collection_by_container_property: dict[tuple[dm.ContainerId, str], str],
351
363
  ) -> DataType | ViewEntity | DMSUnknownEntity | None:
352
364
  if isinstance(
353
365
  prop,
@@ -367,7 +379,16 @@ class DMSImporter(BaseImporter[DMSInputRules]):
367
379
  elif isinstance(container_prop.type, PropertyTypeWithUnit) and container_prop.type.unit:
368
380
  return DataType.load(f"{container_prop.type._type}(unit={container_prop.type.unit.external_id})")
369
381
  elif isinstance(container_prop.type, DMSEnum):
370
- return Enum(collection=ClassEntity(suffix=prop_id), unknownValue=container_prop.type.unknown_value)
382
+ collection = enum_collection_by_container_property.get(
383
+ (prop.container, prop.container_property_identifier)
384
+ )
385
+ if collection is None:
386
+ # This should never happen
387
+ raise ValueError(
388
+ f"BUG in Neat: Enum for {prop.container}.{prop.container_property_identifier} not found."
389
+ )
390
+
391
+ return Enum(collection=ClassEntity(suffix=collection), unknownValue=container_prop.type.unknown_value)
371
392
  else:
372
393
  return DataType.load(container_prop.type._type)
373
394
  else:
@@ -477,15 +498,26 @@ class DMSImporter(BaseImporter[DMSInputRules]):
477
498
  return candidates[0]
478
499
 
479
500
  @staticmethod
480
- def _create_enum_collections(containers: Collection[dm.ContainerApply]) -> list[DMSInputEnum] | None:
481
- enum_collections: list[DMSInputEnum] = []
501
+ def _create_enum_collections(
502
+ containers: Collection[dm.ContainerApply],
503
+ ) -> dict[tuple[dm.ContainerId, str], list[DMSInputEnum]]:
504
+ enum_by_container_property: dict[tuple[dm.ContainerId, str], list[DMSInputEnum]] = defaultdict(list)
505
+
506
+ is_external_id_unique = len({container.external_id for container in containers}) == len(containers)
507
+
482
508
  for container in containers:
509
+ container_id = container.as_id()
483
510
  for prop_id, prop in container.properties.items():
484
- if isinstance(prop.type, DMSEnum):
485
- for identifier, value in prop.type.values.items():
486
- enum_collections.append(
487
- DMSInputEnum(
488
- collection=prop_id, value=identifier, name=value.name, description=value.description
489
- )
511
+ if not isinstance(prop.type, DMSEnum):
512
+ continue
513
+ if is_external_id_unique:
514
+ collection = f"{container.external_id}.{prop_id}"
515
+ else:
516
+ collection = f"{container.space}:{container.external_id}.{prop_id}"
517
+ for identifier, value in prop.type.values.items():
518
+ enum_by_container_property[(container_id, prop_id)].append(
519
+ DMSInputEnum(
520
+ collection=collection, value=identifier, name=value.name, description=value.description
490
521
  )
491
- return enum_collections
522
+ )
523
+ return enum_by_container_property
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  from cognite.client import data_modeling as dm
5
5
  from rdflib import Graph, Namespace, URIRef
6
6
 
7
- from cognite.neat._constants import get_default_prefixes
7
+ from cognite.neat._constants import get_default_prefixes_and_namespaces
8
8
  from cognite.neat._issues import IssueList
9
9
  from cognite.neat._issues.errors import FileReadError
10
10
  from cognite.neat._issues.errors._general import NeatValueError
@@ -97,7 +97,7 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
97
97
  issue_list.append(FileReadError(filepath, str(e)))
98
98
 
99
99
  # bind key namespaces
100
- for prefix, namespace in get_default_prefixes().items():
100
+ for prefix, namespace in get_default_prefixes_and_namespaces().items():
101
101
  graph.bind(prefix, namespace)
102
102
 
103
103
  return cls(
@@ -7,7 +7,7 @@ from pydantic import Field, field_serializer, field_validator, model_validator
7
7
  from pydantic_core.core_schema import SerializationInfo
8
8
  from rdflib import Namespace, URIRef
9
9
 
10
- from cognite.neat._constants import get_default_prefixes
10
+ from cognite.neat._constants import get_default_prefixes_and_namespaces
11
11
  from cognite.neat._issues.errors import NeatValueError, PropertyDefinitionError
12
12
  from cognite.neat._rules._constants import EntityTypes
13
13
  from cognite.neat._rules.models._base_rules import (
@@ -255,7 +255,7 @@ class InformationRules(BaseRules):
255
255
  classes: SheetList[InformationClass] = Field(alias="Classes", description="List of classes")
256
256
  prefixes: dict[str, Namespace] = Field(
257
257
  alias="Prefixes",
258
- default_factory=get_default_prefixes,
258
+ default_factory=get_default_prefixes_and_namespaces,
259
259
  description="the definition of the prefixes that are used in the semantic data model",
260
260
  )
261
261
 
@@ -264,7 +264,7 @@ class InformationRules(BaseRules):
264
264
  if isinstance(values, dict):
265
265
  return {key: Namespace(value) if isinstance(value, str) else value for key, value in values.items()}
266
266
  elif values is None:
267
- values = get_default_prefixes()
267
+ values = get_default_prefixes_and_namespaces()
268
268
  return values
269
269
 
270
270
  def as_dms_rules(self) -> "DMSRules":
@@ -7,8 +7,18 @@ from cognite.client.data_classes.data_modeling import DataModelIdentifier
7
7
  from rdflib import URIRef
8
8
 
9
9
  from cognite.neat._client import NeatClient
10
- from cognite.neat._constants import DEFAULT_NAMESPACE
11
- from cognite.neat._graph.transformers import RelationshipAsEdgeTransformer
10
+ from cognite.neat._constants import (
11
+ DEFAULT_NAMESPACE,
12
+ get_default_prefixes_and_namespaces,
13
+ )
14
+ from cognite.neat._graph.transformers import (
15
+ AttachPropertyFromTargetToSource,
16
+ PruneDeadEndEdges,
17
+ PruneInstancesOfUnknownType,
18
+ PruneTypes,
19
+ RelationshipAsEdgeTransformer,
20
+ Transformers,
21
+ )
12
22
  from cognite.neat._graph.transformers._rdfpath import MakeConnectionOnExactMatch
13
23
  from cognite.neat._rules._shared import InputRules, ReadRules
14
24
  from cognite.neat._rules.importers import DMSImporter
@@ -50,6 +60,79 @@ class InstancePrepareAPI:
50
60
  self._state = state
51
61
  self._verbose = verbose
52
62
 
63
+ def dexpi(self) -> None:
64
+ """Prepares extracted DEXPI graph for further usage in CDF
65
+
66
+ This method bundles several graph transformers which:
67
+ - attach values of generic attributes to nodes
68
+ - create associations between nodes
69
+ - remove unused generic attributes
70
+ - remove associations between nodes that do not exist in the extracted graph
71
+ - remove edges to nodes that do not exist in the extracted graph
72
+
73
+ and therefore safeguard CDF from a bad graph
74
+ """
75
+
76
+ DEXPI = get_default_prefixes_and_namespaces()["dexpi"]
77
+
78
+ transformers = [
79
+ # Remove any instance which type is unknown
80
+ PruneInstancesOfUnknownType(),
81
+ # Directly connect generic attributes
82
+ AttachPropertyFromTargetToSource(
83
+ target_property=DEXPI.Value,
84
+ target_property_holding_new_property=DEXPI.Name,
85
+ target_node_type=DEXPI.GenericAttribute,
86
+ delete_target_node=True,
87
+ ),
88
+ # Directly connect associations
89
+ AttachPropertyFromTargetToSource(
90
+ target_property=DEXPI.ItemID,
91
+ target_property_holding_new_property=DEXPI.Type,
92
+ target_node_type=DEXPI.Association,
93
+ delete_target_node=True,
94
+ ),
95
+ # Remove unused generic attributes and associations
96
+ PruneTypes([DEXPI.GenericAttribute, DEXPI.Association]),
97
+ # Remove edges to nodes that do not exist in the extracted graph
98
+ PruneDeadEndEdges(),
99
+ ]
100
+
101
+ for transformer in transformers:
102
+ self._state.instances.store.transform(cast(Transformers, transformer))
103
+
104
+ def aml(self) -> None:
105
+ """Prepares extracted AutomationML graph for further usage in CDF
106
+
107
+ This method bundles several graph transformers which:
108
+ - attach values of attributes to nodes
109
+ - remove unused attributes
110
+ - remove edges to nodes that do not exist in the extracted graph
111
+
112
+ and therefore safeguard CDF from a bad graph
113
+ """
114
+
115
+ AML = get_default_prefixes_and_namespaces()["aml"]
116
+
117
+ transformers = [
118
+ # Remove any instance which type is unknown
119
+ PruneInstancesOfUnknownType(),
120
+ # Directly connect generic attributes
121
+ AttachPropertyFromTargetToSource(
122
+ target_property=AML.Value,
123
+ target_property_holding_new_property=AML.Name,
124
+ target_node_type=AML.Attribute,
125
+ delete_target_node=True,
126
+ ),
127
+ # Prune unused attributes
128
+ PruneTypes([AML.Attribute]),
129
+ # # Remove edges to nodes that do not exist in the extracted graph
130
+ PruneDeadEndEdges(),
131
+ ]
132
+
133
+ for transformer in transformers:
134
+ self._state.instances.store.transform(cast(Transformers, transformer))
135
+
53
136
  def make_connection_on_exact_match(
54
137
  self,
55
138
  source: tuple[str, str],
@@ -41,8 +41,10 @@ def load_neat_engine(client: CogniteClient | None, location: Literal["newest", "
41
41
 
42
42
  if location == "newest" or not candidates:
43
43
  # Loading in reverse order of priority
44
- # 3. Downloads folder
44
+ # 4. Downloads folder
45
45
  candidates = _load_from_path(Path.home() / "Downloads", pattern, lower_bound, upper_bound)
46
+ # 3. Current working directory
47
+ candidates.update(_load_from_path(Path.cwd(), pattern, lower_bound, upper_bound))
46
48
  # 2. CDF
47
49
  if client:
48
50
  candidates.update(_load_from_cdf(client, pattern, lower_bound, upper_bound, cache_dir))
@@ -195,8 +195,13 @@ class NeatGraphStore:
195
195
  self, class_neat_id: URIRef, property_link_pairs: dict[str, URIRef] | None
196
196
  ) -> Iterable[tuple[str, dict[str | InstanceType, list[str]]]]:
197
197
  if self.rules is None:
198
- warnings.warn("Rules not found in graph store!", stacklevel=2)
198
+ warnings.warn("Rules not found in graph store! Aborting!", stacklevel=2)
199
199
  return
200
+ if self.multi_type_instances:
201
+ warnings.warn(
202
+ "Multi typed instances detected, issues with loading can occur!",
203
+ stacklevel=2,
204
+ )
200
205
 
201
206
  if cls := InformationAnalysis(self.rules).classes_by_neat_id.get(class_neat_id):
202
207
  if property_link_pairs:
@@ -225,6 +230,11 @@ class NeatGraphStore:
225
230
  if self.rules is None:
226
231
  warnings.warn("Rules not found in graph store!", stacklevel=2)
227
232
  return
233
+ if self.multi_type_instances:
234
+ warnings.warn(
235
+ "Multi typed instances detected, issues with loading can occur!",
236
+ stacklevel=2,
237
+ )
228
238
 
229
239
  if class_entity not in [definition.class_ for definition in self.rules.classes]:
230
240
  warnings.warn("Desired type not found in graph!", stacklevel=2)
@@ -267,7 +277,6 @@ class NeatGraphStore:
267
277
 
268
278
  # get property types to guide process of removing or not namespaces from results
269
279
  property_types = InformationAnalysis(self.rules).property_types(class_entity)
270
-
271
280
  for instance_id in instance_ids:
272
281
  if res := self.queries.describe(
273
282
  instance_id=instance_id,
@@ -292,6 +301,11 @@ class NeatGraphStore:
292
301
  if not self.rules:
293
302
  warnings.warn("Rules not found in graph store!", stacklevel=2)
294
303
  return
304
+ if self.multi_type_instances:
305
+ warnings.warn(
306
+ "Multi typed instances detected, issues with loading can occur!",
307
+ stacklevel=2,
308
+ )
295
309
 
296
310
  class_entity = ClassEntity(prefix=self.rules.metadata.prefix, suffix=class_)
297
311
 
@@ -390,6 +404,10 @@ class NeatGraphStore:
390
404
  def summary(self) -> pd.DataFrame:
391
405
  return pd.DataFrame(self.queries.summarize_instances(), columns=["Type", "Occurrence"])
392
406
 
407
+ @property
408
+ def multi_type_instances(self) -> dict[str, list[str]]:
409
+ return self.queries.multi_type_instances()
410
+
393
411
  def _repr_html_(self) -> str:
394
412
  provenance = self.provenance._repr_html_()
395
413
  summary: pd.DataFrame = self.summary
@@ -404,6 +422,9 @@ class NeatGraphStore:
404
422
  f"{cast(pd.DataFrame, self._shorten_summary(summary))._repr_html_()}" # type: ignore[operator]
405
423
  )
406
424
 
425
+ if self.multi_type_instances:
426
+ summary_text += "<br><strong>Multi value instances detected! Loading could have issues!</strong></br>" # type: ignore
427
+
407
428
  return f"{summary_text}" f"{provenance}"
408
429
 
409
430
  def _shorten_summary(self, summary: pd.DataFrame) -> pd.DataFrame:
@@ -32,10 +32,7 @@ def get_cognite_client(env_file_name: str) -> CogniteClient:
32
32
  """
33
33
  if not env_file_name.endswith(".env"):
34
34
  raise ValueError("env_file_name must end with '.env'")
35
- with suppress(KeyError):
36
- variables = EnvironmentVariables.create_from_environ()
37
- return variables.get_client()
38
-
35
+ # First try to load from .env file in repository root
39
36
  repo_root = _repo_root()
40
37
  if repo_root:
41
38
  with suppress(KeyError, FileNotFoundError, TypeError):
@@ -43,6 +40,11 @@ def get_cognite_client(env_file_name: str) -> CogniteClient:
43
40
  client = variables.get_client()
44
41
  print(f"Found {env_file_name} file in repository root. Loaded variables from {env_file_name} file.")
45
42
  return client
43
+ # Then try to load from environment variables
44
+ with suppress(KeyError):
45
+ variables = EnvironmentVariables.create_from_environ()
46
+ return variables.get_client()
47
+ # If not found, prompt the user
46
48
  variables = _prompt_user()
47
49
  if repo_root and _env_in_gitignore(repo_root, env_file_name):
48
50
  local_import("rich", "jupyter")
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.101.0"
2
- __engine__ = "^2.0.1"
1
+ __version__ = "0.102.0"
2
+ __engine__ = "^2.0.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cognite-neat
3
- Version: 0.101.0
3
+ Version: 0.102.0
4
4
  Summary: Knowledge graph transformation
5
5
  Home-page: https://cognite-neat.readthedocs-hosted.com/
6
6
  License: Apache-2.0
@@ -80,7 +80,7 @@ cognite/neat/_client/data_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
80
80
  cognite/neat/_client/data_classes/data_modeling.py,sha256=RvpUp9ygd-yffQFJ7O2mQqMLDPIa-dmip5zPb8QVIiw,6672
81
81
  cognite/neat/_client/data_classes/schema.py,sha256=hYKcrxXf90tA2NIqwBOzw9_hb4OanF9dwwPnur6MEfg,22904
82
82
  cognite/neat/_config.py,sha256=f9Py4SEHwYYquIg-k1rC7MbXBLENXQauoZtLyUbWvJQ,10118
83
- cognite/neat/_constants.py,sha256=KtAU74aQUy6FpmejwxDaXP5UwsqA8DJy-MTEoFw1UHg,2621
83
+ cognite/neat/_constants.py,sha256=ksyp8NKHEmjxz-773EUXfFZHsvjNOVrCpUXx85aStZo,2882
84
84
  cognite/neat/_graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
85
  cognite/neat/_graph/_shared.py,sha256=g7XFITbVxdDyGZ6mlgFUv5cBycrU7QbPktRikdUVkks,863
86
86
  cognite/neat/_graph/_tracking/__init__.py,sha256=pYj7c-YAUIP4hvN-4mlWnwaeZFerzL9_gM-oZhex7cE,91
@@ -110,16 +110,16 @@ cognite/neat/_graph/extractors/_mock_graph_generator.py,sha256=yEqQdbnRQjBXVQIEV
110
110
  cognite/neat/_graph/extractors/_rdf_file.py,sha256=YgPZN4Ayk6UlbwFFjdWn4Yo3P74D8KeNUb3slXg6Ox8,1604
111
111
  cognite/neat/_graph/loaders/__init__.py,sha256=1eam_rG1BXTUJ8iDm8_IYZldEe177vn2GmHihDBi8qk,718
112
112
  cognite/neat/_graph/loaders/_base.py,sha256=tjplRd-vbWhWyys0Ll3KgHR3F3ETlP_dXJ3e8F8w15M,3984
113
- cognite/neat/_graph/loaders/_rdf2dms.py,sha256=6EaCnwnnFA8Vye8Ph439yTtbJCP_NGNpBD24XLc7PQA,17437
113
+ cognite/neat/_graph/loaders/_rdf2dms.py,sha256=N_6PH9PLpa5H2u2tJwgnJR21HuXnFIwiRj7g_ursdVI,17425
114
114
  cognite/neat/_graph/queries/__init__.py,sha256=BgDd-037kvtWwAoGAy8eORVNMiZ5-E9sIV0txIpeaN4,50
115
- cognite/neat/_graph/queries/_base.py,sha256=dKEk8TDYUxIc71peqD9TfHHoILG9cKzjkFp7CAkbF78,14246
115
+ cognite/neat/_graph/queries/_base.py,sha256=Y3Amuave6xdQsDE5ZXCrYLUgOMIH8BXa4n5Pc9czAF0,14926
116
116
  cognite/neat/_graph/queries/_construct.py,sha256=CW8uHtXXACUXDj1AcEjROXtvoiuyx0CTgZ0bURY5Neo,7213
117
- cognite/neat/_graph/queries/_shared.py,sha256=K3svLkvw-DWPZUwIwpJRjPKg5UIRKFCn5jBLpuJjiHc,5330
118
- cognite/neat/_graph/transformers/__init__.py,sha256=82a7rV6bGhOc5iAznBOtlQOMWxgi0KpGXYV4zh4HnsE,1187
119
- cognite/neat/_graph/transformers/_base.py,sha256=b37Ek-9njuM5pTR_3XhnxCMrg_ip_2BMwM7ZhKpAAlw,328
117
+ cognite/neat/_graph/queries/_shared.py,sha256=uhw-nY4jJvivgtj1msdCRrfTWgauU7ybSHUqqUaFOUU,5390
118
+ cognite/neat/_graph/transformers/__init__.py,sha256=8nN901mwxtNMvX9xAtWVuaU6jDjJ5yiXDfc-oYGzYfE,1425
119
+ cognite/neat/_graph/transformers/_base.py,sha256=3AJogynIiuTcXtGj70xU1Iz7l7HO3MJD4dTVvx2zqEk,336
120
120
  cognite/neat/_graph/transformers/_classic_cdf.py,sha256=8vzvoHH2YJMg2mMTEH_ASGWn1Maars1N1RZ9jWhLTkY,19291
121
- cognite/neat/_graph/transformers/_iodd.py,sha256=AajIWbCzQcR5ZNz6qSb2dKoNEJOedgEB7L2se94u6qw,863
122
- cognite/neat/_graph/transformers/_prune_graph.py,sha256=i0YnpfcFq_y_bZIlZJRRZDRzoj5ya9qpnozgUqKOD8o,8220
121
+ cognite/neat/_graph/transformers/_iodd.py,sha256=KNz1fPdKK40UaHgPMECzNZgSeU5PdPRyLdaRYdq1iug,866
122
+ cognite/neat/_graph/transformers/_prune_graph.py,sha256=ZW_UHFAPjYiAm_B32utdxR6Dl1aq9C4ODmmmW8WrkUo,10616
123
123
  cognite/neat/_graph/transformers/_rdfpath.py,sha256=0ZH7d62kfdCyWGrCyY2oJSnGEPsHQd0sMrZAsTibCCI,4155
124
124
  cognite/neat/_graph/transformers/_value_type.py,sha256=JorH-AgDXVZUkG_GCcwn51Mw0M2WIOV834t0kF_Nwvo,2614
125
125
  cognite/neat/_issues/__init__.py,sha256=IEZBpvL88hdghX7JgndhxcZcxreZowuoQFIXuSeIKDs,556
@@ -150,21 +150,21 @@ cognite/neat/_rules/catalog/info-rules-imf.xlsx,sha256=7odm5CoAU72-VTZk_z1u7Gbyc
150
150
  cognite/neat/_rules/exporters/__init__.py,sha256=jCwXAeyZJv7GNJ3hGG-80gb8LAidozsyFMzdNIsGt_Y,1204
151
151
  cognite/neat/_rules/exporters/_base.py,sha256=vadYWb5hVbjiPIhVIzlY6ARisoqhtJa6Ce0ysNHPXQc,1328
152
152
  cognite/neat/_rules/exporters/_rules2dms.py,sha256=pwqy0wzLpSh-BNukLOOB12gTVNeCHEPefX5idtTp2sI,17198
153
- cognite/neat/_rules/exporters/_rules2excel.py,sha256=puFgIf_dolxv38Lkgzl9lDDREWDPdTApqgYCu9H-hf4,11689
153
+ cognite/neat/_rules/exporters/_rules2excel.py,sha256=d_UwE1tL2JaFcmdLtnRTu_dzNatUsHsVFYLsndPGaaY,11777
154
154
  cognite/neat/_rules/exporters/_rules2instance_template.py,sha256=8HM1SkzcucaEYpQi96ncMnL8STArX9Oe09JBhJJAN4I,5810
155
155
  cognite/neat/_rules/exporters/_rules2ontology.py,sha256=ioMi1GUhnbvcfVOPb3Z0a24mIEe74AfxySETWMDS9Lc,21776
156
156
  cognite/neat/_rules/exporters/_rules2yaml.py,sha256=O9vnzDHf1ep1Qu0po0GVjgu945HNx3-zRmhxv65sv6I,3052
157
157
  cognite/neat/_rules/exporters/_validation.py,sha256=DVJGdNNU2WtAFgUg0h4SWVhveRErEPOcYdT65y5toV0,682
158
158
  cognite/neat/_rules/importers/__init__.py,sha256=DZN_MIy_rQwyudvdAYtLPOVPB3YytmDoIfKGgT55StQ,1316
159
159
  cognite/neat/_rules/importers/_base.py,sha256=P5bksMf7MNfLDX0SVQxjmK3Y-psoe0HcI_sWFoQJbFs,3264
160
- cognite/neat/_rules/importers/_dms2rules.py,sha256=177CYuhO1w3IvEN5eiQWaxHjyRsDGagrTkEhBf1994E,20297
160
+ cognite/neat/_rules/importers/_dms2rules.py,sha256=u-zWfunrEXJEstgVfh3PVd33eJLz_s4hvFu2ayGmCME,21802
161
161
  cognite/neat/_rules/importers/_dtdl2rules/__init__.py,sha256=CNR-sUihs2mnR1bPMKs3j3L4ds3vFTsrl6YycExZTfU,68
162
162
  cognite/neat/_rules/importers/_dtdl2rules/_unit_lookup.py,sha256=wW4saKva61Q_i17guY0dc4OseJDQfqHy_QZBtm0OD6g,12134
163
163
  cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py,sha256=j38U0um1ZWI-yRvEUR3z33vOvMCYXJxSO9L_B7m9xDE,11707
164
164
  cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py,sha256=y3UMwrBysT3pl_wXJj-qKYKYbJDKa424AAfn6e8489k,5955
165
165
  cognite/neat/_rules/importers/_dtdl2rules/spec.py,sha256=u__f08rAiYG0FIRiWoecBN83bVN3GEy--Lvm7463Q68,12166
166
166
  cognite/neat/_rules/importers/_rdf/__init__.py,sha256=F0x60kVLtJ9H8Md8_aMeKMBkhPCMiljyxt2_YmQfnMU,183
167
- cognite/neat/_rules/importers/_rdf/_base.py,sha256=V-Y5NNP_ang3sXLJNx4AZZZPv1akzZWIRwVLafY3aac,5419
167
+ cognite/neat/_rules/importers/_rdf/_base.py,sha256=03YUWpcxjTrmYoVmZYxQKlMo4v54VJFfe_qx0fc8AGA,5449
168
168
  cognite/neat/_rules/importers/_rdf/_imf2rules.py,sha256=xemIv5G6JXJJ6Jn_1P5UldwEDNsgMIGiDF28DnOTtjU,3597
169
169
  cognite/neat/_rules/importers/_rdf/_inference2rules.py,sha256=Zu6GTsG8A48PQs8F7H8iskTj9zu2R-l4NlWejCwSwqo,12518
170
170
  cognite/neat/_rules/importers/_rdf/_owl2rules.py,sha256=KhQAM9ai2ckhpB7ohfRi0n8ztRwbPADfNMFed8f-5V8,3267
@@ -190,7 +190,7 @@ cognite/neat/_rules/models/entities/_single_value.py,sha256=dVfqIx3_Agi_LddhsqPO
190
190
  cognite/neat/_rules/models/entities/_types.py,sha256=df9rnXJJKciv2Bp-Ve2q4xdEJt6WWniq12Z0hW2d6sk,1917
191
191
  cognite/neat/_rules/models/entities/_wrapped.py,sha256=FxC8HztW_tUUtuArAOwxyFfkdJnSEB4bgZoNmmmfiPk,7137
192
192
  cognite/neat/_rules/models/information/__init__.py,sha256=ex9JOyiZYXefFl9oi1VaHhyUOtYjXWytSmwuq4pqKqc,556
193
- cognite/neat/_rules/models/information/_rules.py,sha256=F8Tjb_ymOEEkt1k2yDjboS7hCthd0PsVYSZzandATwE,11753
193
+ cognite/neat/_rules/models/information/_rules.py,sha256=c_vS0GM9hv2ESanuso5H5x5vR1aBVtG5rwzjQhHij_k,11798
194
194
  cognite/neat/_rules/models/information/_rules_input.py,sha256=PdXTPN0QfO4vxN5M4xzaaDwHEQo53RbY_jIr5TfBszY,5443
195
195
  cognite/neat/_rules/models/information/_validation.py,sha256=HbaLShj6uumu-t9I3FUI_iKQfUDiwEkuFENHgWIPfrk,10202
196
196
  cognite/neat/_rules/models/mapping/__init__.py,sha256=T68Hf7rhiXa7b03h4RMwarAmkGnB-Bbhc1H07b2PyC4,100
@@ -208,7 +208,7 @@ cognite/neat/_session/_collector.py,sha256=zS5JxLIYnAWV-dBzF6WgUfo662iYSzEyDhtnM
208
208
  cognite/neat/_session/_drop.py,sha256=XlEaKb_HpqI5EQuUuUFjcf3Qx6BfcBBWdqqGdwkGhmE,1137
209
209
  cognite/neat/_session/_inspect.py,sha256=s3R6vz8HbqZSdknyXTSkmA3JvvInlQF5yNKECTA-I1w,7264
210
210
  cognite/neat/_session/_mapping.py,sha256=MZ_xRhapc2mVwM-W3tda2zZGaPncj_ZqnzuasvY05po,6109
211
- cognite/neat/_session/_prepare.py,sha256=_jU0TJWLcg727wKh8NK3a-5duVnRkGJrnkNxLO_D83Q,21883
211
+ cognite/neat/_session/_prepare.py,sha256=saGqkQUxdclU840d2IUMSeYltSoVn0b9ZkC2ntfOuu0,24994
212
212
  cognite/neat/_session/_read.py,sha256=Kfq1RAT5soXSI7vgxY-HN-GjMzAW5RZQD9E7JiuKBmU,15152
213
213
  cognite/neat/_session/_set.py,sha256=NY0Vz8xD_a-UA8qWE1GFxHdmBl1BkT1KHQehQVKUorI,1914
214
214
  cognite/neat/_session/_show.py,sha256=_ev_Z41rkW4nlvhLf69PmttggKksnkCchOow7CmICyU,14124
@@ -218,14 +218,14 @@ cognite/neat/_session/_wizard.py,sha256=O8d0FA87RIoiTOD2KLyTVsxwA2-ewAG2By4JfxXS
218
218
  cognite/neat/_session/engine/__init__.py,sha256=aeI5pzljU5n1B-SVu3LwjYVsN1tSVhnJj-4ddflEo4U,120
219
219
  cognite/neat/_session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
220
220
  cognite/neat/_session/engine/_interface.py,sha256=ItJ1VMrPt-pKKvpSpglD9n9yFC6ehF9xV2DUVCyfQB0,533
221
- cognite/neat/_session/engine/_load.py,sha256=HAzrAiR3FQz881ZMbaK6EIvMNRxHUK8VbSoD2Obd-4g,5064
221
+ cognite/neat/_session/engine/_load.py,sha256=LcoYVthQyCzLWKzRE_75_nElS-n_eMWSPAgNJBnh5dA,5193
222
222
  cognite/neat/_session/exceptions.py,sha256=dvhwF7d8AADxqEI13nZreLCPBPN_7A3KiAqlWjkpuqc,2303
223
223
  cognite/neat/_shared.py,sha256=JXebp3LREqBq9TPNXb7QCHwF7_nbWo622WAzllxSaaU,1671
224
224
  cognite/neat/_store/__init__.py,sha256=G-VG_YwfRt1kuPao07PDJyZ3w_0-eguzLUM13n-Z_RA,64
225
- cognite/neat/_store/_base.py,sha256=X77CkrCLEg9g_qpcqTIIid-1KZZ7NCU1wbrQqVR6Prc,16265
225
+ cognite/neat/_store/_base.py,sha256=WfVFynBRbhY-DgzpA0cApLUpaPMfiyolzVOycDgnm7c,17143
226
226
  cognite/neat/_store/_provenance.py,sha256=BiVOuwl65qWZBaBlPYbVv0Dy_Ibg7U3tLpXt8H7KRuw,8722
227
227
  cognite/neat/_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
228
- cognite/neat/_utils/auth.py,sha256=UbFNq4BIqxc1459xJtI1FZz91K5XEMkfY5cfpBHyUHU,13301
228
+ cognite/neat/_utils/auth.py,sha256=YRzvkjT9lAYSJd3wsPov2wtmGVCtt1wOlmp84RRTEXw,13444
229
229
  cognite/neat/_utils/auxiliary.py,sha256=WFOycFgoYipvDmtGvn6ZNH3H8iNZmHamrfe2kXRb8lM,6667
230
230
  cognite/neat/_utils/collection_.py,sha256=Q_LN1qypr0SeAV3dAR5KLD1szHNohSdYxyA8hr3n4T8,1433
231
231
  cognite/neat/_utils/rdf_.py,sha256=_nmZqo6_Ef7Aep0kXR_uoiRxGQR2u5lMK4g5mxWneeY,8586
@@ -236,7 +236,7 @@ cognite/neat/_utils/text.py,sha256=0IffvBIAmeGh92F4T6xiEdd-vv3B7FOGEMbfuTktO5Y,4
236
236
  cognite/neat/_utils/time_.py,sha256=O30LUiDH9TdOYz8_a9pFqTtJdg8vEjC3qHCk8xZblG8,345
237
237
  cognite/neat/_utils/upload.py,sha256=iWKmsQgw4EHLv-11NjYu7zAj5LtqTAfNa87a1kWeuaU,5727
238
238
  cognite/neat/_utils/xml_.py,sha256=FQkq84u35MUsnKcL6nTMJ9ajtG9D5i1u4VBnhGqP2DQ,1710
239
- cognite/neat/_version.py,sha256=YC9egL_60_4y49kwtdUsQ37p_zPCkAdak7ziTTA-JL8,46
239
+ cognite/neat/_version.py,sha256=vjgGQ3RZK2Kvz8gvZ9ElmNuxvNfbYPv4p9coXriN5Ic,46
240
240
  cognite/neat/_workflows/__init__.py,sha256=S0fZq7kvoqDKodHu1UIPsqcpdvXoefUWRPt1lqeQkQs,420
241
241
  cognite/neat/_workflows/base.py,sha256=O1pcmfbme2gIVF2eOGrKZSUDmhZc8L9rI8UfvLN2YAM,26839
242
242
  cognite/neat/_workflows/cdf_store.py,sha256=3pebnATPo6In4-1srpa3wzstynTOi3T6hwFX5uaie4c,18050
@@ -265,8 +265,8 @@ cognite/neat/_workflows/tasks.py,sha256=dr2xuIb8P5e5e9p_fjzRlvDbKsre2xGYrkc3wnRx
265
265
  cognite/neat/_workflows/triggers.py,sha256=u69xOsaTtM8_WD6ZeIIBB-XKwvlZmPHAsZQh_TnyHcM,7073
266
266
  cognite/neat/_workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
267
267
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
268
- cognite_neat-0.101.0.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
269
- cognite_neat-0.101.0.dist-info/METADATA,sha256=0qjVPqhZDNDDWTYxyQJS7v2f-JpEu2uQrtbGv4km-7c,5708
270
- cognite_neat-0.101.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
271
- cognite_neat-0.101.0.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
272
- cognite_neat-0.101.0.dist-info/RECORD,,
268
+ cognite_neat-0.102.0.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
269
+ cognite_neat-0.102.0.dist-info/METADATA,sha256=7qilQkBii0rF3dUUBQGeuEf74Yr4VaUyKQ6DgyOPQM4,5708
270
+ cognite_neat-0.102.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
271
+ cognite_neat-0.102.0.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
272
+ cognite_neat-0.102.0.dist-info/RECORD,,