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

Files changed (54) hide show
  1. cognite/neat/_config.py +6 -260
  2. cognite/neat/_graph/extractors/__init__.py +5 -1
  3. cognite/neat/_graph/extractors/_base.py +32 -0
  4. cognite/neat/_graph/extractors/_classic_cdf/_base.py +42 -16
  5. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +78 -8
  6. cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +2 -0
  7. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +10 -3
  8. cognite/neat/_graph/extractors/_dms.py +48 -14
  9. cognite/neat/_graph/extractors/_dms_graph.py +149 -0
  10. cognite/neat/_graph/extractors/_rdf_file.py +32 -5
  11. cognite/neat/_graph/loaders/_rdf2dms.py +119 -20
  12. cognite/neat/_graph/queries/_construct.py +1 -1
  13. cognite/neat/_graph/transformers/__init__.py +5 -0
  14. cognite/neat/_graph/transformers/_base.py +13 -9
  15. cognite/neat/_graph/transformers/_classic_cdf.py +141 -44
  16. cognite/neat/_graph/transformers/_rdfpath.py +4 -4
  17. cognite/neat/_graph/transformers/_value_type.py +54 -44
  18. cognite/neat/_issues/warnings/_external.py +1 -1
  19. cognite/neat/_rules/analysis/_base.py +1 -1
  20. cognite/neat/_rules/analysis/_information.py +14 -13
  21. cognite/neat/_rules/catalog/__init__.py +1 -0
  22. cognite/neat/_rules/catalog/classic_model.xlsx +0 -0
  23. cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
  24. cognite/neat/_rules/importers/_dms2rules.py +7 -5
  25. cognite/neat/_rules/importers/_rdf/_inference2rules.py +5 -3
  26. cognite/neat/_rules/models/_base_rules.py +0 -12
  27. cognite/neat/_rules/models/_types.py +5 -0
  28. cognite/neat/_rules/models/dms/_rules.py +50 -2
  29. cognite/neat/_rules/models/information/_rules.py +48 -5
  30. cognite/neat/_rules/models/information/_rules_input.py +1 -1
  31. cognite/neat/_rules/models/mapping/_classic2core.py +4 -5
  32. cognite/neat/_rules/models/mapping/_classic2core.yaml +70 -58
  33. cognite/neat/_rules/transformers/__init__.py +4 -0
  34. cognite/neat/_rules/transformers/_converters.py +209 -62
  35. cognite/neat/_rules/transformers/_mapping.py +3 -2
  36. cognite/neat/_session/_base.py +8 -13
  37. cognite/neat/_session/_inspect.py +6 -2
  38. cognite/neat/_session/_mapping.py +22 -13
  39. cognite/neat/_session/_prepare.py +9 -57
  40. cognite/neat/_session/_read.py +96 -29
  41. cognite/neat/_session/_set.py +9 -0
  42. cognite/neat/_session/_state.py +10 -1
  43. cognite/neat/_session/_to.py +51 -15
  44. cognite/neat/_session/exceptions.py +7 -3
  45. cognite/neat/_store/_graph_store.py +85 -39
  46. cognite/neat/_store/_rules_store.py +22 -0
  47. cognite/neat/_utils/auth.py +2 -0
  48. cognite/neat/_utils/collection_.py +32 -11
  49. cognite/neat/_version.py +1 -1
  50. {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/METADATA +2 -8
  51. {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/RECORD +54 -52
  52. {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/WHEEL +1 -1
  53. {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/LICENSE +0 -0
  54. {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/entry_points.txt +0 -0
@@ -5,7 +5,6 @@ its sub-models and validators.
5
5
  import math
6
6
  import sys
7
7
  import types
8
- import uuid
9
8
  from abc import ABC, abstractmethod
10
9
  from collections.abc import Callable, Hashable, Iterator, MutableSequence, Sequence
11
10
  from datetime import datetime
@@ -31,7 +30,6 @@ from pydantic import (
31
30
  PlainSerializer,
32
31
  field_validator,
33
32
  model_serializer,
34
- model_validator,
35
33
  )
36
34
  from pydantic.main import IncEx
37
35
  from pydantic_core import core_schema
@@ -343,10 +341,6 @@ class BaseRules(SchemaModel, ABC):
343
341
  return output
344
342
 
345
343
 
346
- def make_neat_id() -> URIRef:
347
- return DEFAULT_NAMESPACE[f"neatId_{str(uuid.uuid4()).replace('-', '_')}"]
348
-
349
-
350
344
  class SheetRow(SchemaModel):
351
345
  neatId: URIRefType | None = Field(
352
346
  alias="Neat ID",
@@ -354,12 +348,6 @@ class SheetRow(SchemaModel):
354
348
  default=None,
355
349
  )
356
350
 
357
- @model_validator(mode="after")
358
- def set_neat_id(self) -> "SheetRow":
359
- if self.neatId is None:
360
- self.neatId = DEFAULT_NAMESPACE[f"neatId_{str(uuid.uuid4()).replace('-', '_')}"]
361
- return self
362
-
363
351
  @abstractmethod
364
352
  def _identifier(self) -> tuple[Hashable, ...]:
365
353
  raise NotImplementedError()
@@ -76,6 +76,11 @@ NamespaceType = Annotated[
76
76
  URIRefType = Annotated[
77
77
  rdflib.URIRef,
78
78
  BeforeValidator(lambda value: rdflib.URIRef(value)),
79
+ PlainSerializer(
80
+ lambda value: str(value),
81
+ return_type=str,
82
+ when_used="unless-none",
83
+ ),
79
84
  ]
80
85
 
81
86
  PrefixType = Annotated[
@@ -1,10 +1,10 @@
1
1
  import warnings
2
2
  from collections.abc import Hashable
3
- from typing import Any, ClassVar, Literal
3
+ from typing import TYPE_CHECKING, Any, ClassVar, Literal
4
4
 
5
5
  import pandas as pd
6
6
  from cognite.client import data_modeling as dm
7
- from pydantic import Field, field_serializer, field_validator
7
+ from pydantic import Field, field_serializer, field_validator, model_validator
8
8
  from pydantic_core.core_schema import SerializationInfo, ValidationInfo
9
9
 
10
10
  from cognite.neat._client.data_classes.schema import DMSSchema
@@ -48,6 +48,9 @@ from cognite.neat._rules.models.entities import (
48
48
  ViewEntityList,
49
49
  )
50
50
 
51
+ if TYPE_CHECKING:
52
+ from cognite.neat._rules.models import InformationRules
53
+
51
54
  _DEFAULT_VERSION = "1"
52
55
 
53
56
 
@@ -442,6 +445,51 @@ class DMSRules(BaseRules):
442
445
  )
443
446
  return value
444
447
 
448
+ @model_validator(mode="after")
449
+ def set_neat_id(self) -> "DMSRules":
450
+ namespace = self.metadata.namespace
451
+
452
+ for view in self.views:
453
+ if not view.neatId:
454
+ view.neatId = namespace[view.view.suffix]
455
+
456
+ for property_ in self.properties:
457
+ if not property_.neatId:
458
+ property_.neatId = namespace[f"{property_.view.suffix}/{property_.view_property}"]
459
+
460
+ return self
461
+
462
+ def update_neat_id(self) -> None:
463
+ """Update neat ids"""
464
+
465
+ namespace = self.metadata.namespace
466
+
467
+ for view in self.views:
468
+ view.neatId = namespace[view.view.suffix]
469
+
470
+ for property_ in self.properties:
471
+ property_.neatId = namespace[f"{property_.view.suffix}/{property_.view_property}"]
472
+
473
+ def sync_with_info_rules(self, info_rules: "InformationRules") -> None:
474
+ # Sync at the metadata level
475
+ if info_rules.metadata.physical == self.metadata.identifier:
476
+ self.metadata.logical = info_rules.metadata.identifier
477
+ else:
478
+ # if models are not linked to start with, we skip
479
+ return None
480
+
481
+ info_properties_by_neat_id = {prop.neatId: prop for prop in info_rules.properties}
482
+ dms_properties_by_neat_id = {prop.neatId: prop for prop in self.properties}
483
+ for neat_id, prop in info_properties_by_neat_id.items():
484
+ if prop.physical in dms_properties_by_neat_id:
485
+ dms_properties_by_neat_id[prop.physical].logical = neat_id
486
+
487
+ info_classes_by_neat_id = {cls.neatId: cls for cls in info_rules.classes}
488
+ dms_views_by_neat_id = {view.neatId: view for view in self.views}
489
+ for neat_id, class_ in info_classes_by_neat_id.items():
490
+ if class_.physical in dms_views_by_neat_id:
491
+ dms_views_by_neat_id[class_.physical].logical = neat_id
492
+
445
493
  def as_schema(self, instance_space: str | None = None, remove_cdf_spaces: bool = False) -> DMSSchema:
446
494
  from ._exporter import _DMSExporter
447
495
 
@@ -121,7 +121,7 @@ class InformationProperty(SheetRow):
121
121
  min_count: Minimum count of the property values. Defaults to 0
122
122
  max_count: Maximum count of the property values. Defaults to None
123
123
  default: Default value of the property
124
- transformation: Actual rule for the transformation from source to target representation of
124
+ instance_source: Actual rule for the transformation from source to target representation of
125
125
  knowledge graph. Defaults to None (no transformation)
126
126
  """
127
127
 
@@ -153,10 +153,10 @@ class InformationProperty(SheetRow):
153
153
  "which means that the property can hold any number of values (listable).",
154
154
  )
155
155
  default: Any | None = Field(alias="Default", default=None, description="Default value of the property.")
156
- transformation: RDFPath | None = Field(
157
- alias="Transformation",
156
+ instance_source: RDFPath | None = Field(
157
+ alias="Instance Source",
158
158
  default=None,
159
- description="The rule that is used to populate the data model. "
159
+ description="The link to to the instance property for the model. "
160
160
  "The rule is provided in a RDFPath query syntax which is converted to downstream solution query (e.g. SPARQL).",
161
161
  )
162
162
  inherited: bool = Field(
@@ -181,7 +181,7 @@ class InformationProperty(SheetRow):
181
181
  return float("inf")
182
182
  return value
183
183
 
184
- @field_validator("transformation", mode="before")
184
+ @field_validator("instance_source", mode="before")
185
185
  def generate_rdfpath(cls, value: str | RDFPath | None) -> RDFPath | None:
186
186
  if value is None or isinstance(value, RDFPath):
187
187
  return value
@@ -267,6 +267,49 @@ class InformationRules(BaseRules):
267
267
  values = get_default_prefixes_and_namespaces()
268
268
  return values
269
269
 
270
+ @model_validator(mode="after")
271
+ def set_neat_id(self) -> "InformationRules":
272
+ namespace = self.metadata.namespace
273
+
274
+ for class_ in self.classes:
275
+ if not class_.neatId:
276
+ class_.neatId = namespace[class_.class_.suffix]
277
+ for property_ in self.properties:
278
+ if not property_.neatId:
279
+ property_.neatId = namespace[f"{property_.class_.suffix}/{property_.property_}"]
280
+
281
+ return self
282
+
283
+ def update_neat_id(self) -> None:
284
+ """Update neat ids"""
285
+
286
+ namespace = self.metadata.namespace
287
+
288
+ for class_ in self.classes:
289
+ class_.neatId = namespace[class_.class_.suffix]
290
+ for property_ in self.properties:
291
+ property_.neatId = namespace[f"{property_.class_.suffix}/{property_.property_}"]
292
+
293
+ def sync_with_dms_rules(self, dms_rules: "DMSRules") -> None:
294
+ # Sync at the metadata level
295
+ if dms_rules.metadata.logical == self.metadata.identifier:
296
+ self.metadata.physical = dms_rules.metadata.identifier
297
+ else:
298
+ # if models are not linked to start with, we skip
299
+ return None
300
+
301
+ info_properties_by_neat_id = {prop.neatId: prop for prop in self.properties}
302
+ dms_properties_by_neat_id = {prop.neatId: prop for prop in dms_rules.properties}
303
+ for neat_id, prop in dms_properties_by_neat_id.items():
304
+ if prop.logical in info_properties_by_neat_id:
305
+ info_properties_by_neat_id[prop.logical].physical = neat_id
306
+
307
+ info_classes_by_neat_id = {cls.neatId: cls for cls in self.classes}
308
+ dms_views_by_neat_id = {view.neatId: view for view in dms_rules.views}
309
+ for neat_id, view in dms_views_by_neat_id.items():
310
+ if view.logical in info_classes_by_neat_id:
311
+ info_classes_by_neat_id[view.logical].physical = neat_id
312
+
270
313
  def as_dms_rules(self) -> "DMSRules":
271
314
  from cognite.neat._rules.transformers._converters import _InformationRulesConverter
272
315
 
@@ -79,7 +79,7 @@ class InformationInputProperty(InputComponent[InformationProperty]):
79
79
  min_count: int | None = None
80
80
  max_count: int | float | None = None
81
81
  default: Any | None = None
82
- transformation: str | None = None
82
+ instance_source: str | None = None
83
83
  # Only used internally
84
84
  inherited: bool = False
85
85
  neatId: str | URIRef | None = None
@@ -15,14 +15,13 @@ def _read_source_file() -> str:
15
15
  return _CLASSIC_TO_CORE_MAPPING.read_text()
16
16
 
17
17
 
18
- def load_classic_to_core_mapping(org_name: str, source_space: str, source_version: str) -> DMSRules:
19
- if not org_name:
20
- raise NeatValueError("Organization name must be provided.")
21
-
18
+ def load_classic_to_core_mapping(org_name: str | None, source_space: str, source_version: str) -> DMSRules:
22
19
  from cognite.neat._rules.importers import YAMLImporter
23
20
  from cognite.neat._rules.transformers import VerifyDMSRules
24
21
 
25
- raw_str = _read_source_file().replace("Classic", org_name)
22
+ raw_str = _read_source_file()
23
+ if org_name is not None:
24
+ raw_str = raw_str.replace("Classic", org_name)
26
25
 
27
26
  loaded = yaml.safe_load(raw_str)
28
27
  loaded["metadata"]["space"] = source_space