cognite-neat 0.99.0__py3-none-any.whl → 0.99.1__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 (62) hide show
  1. cognite/neat/_client/_api/data_modeling_loaders.py +77 -4
  2. cognite/neat/_client/_api/schema.py +63 -2
  3. cognite/neat/_client/data_classes/schema.py +2 -348
  4. cognite/neat/_constants.py +27 -4
  5. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +5 -5
  6. cognite/neat/_graph/loaders/_rdf2dms.py +2 -2
  7. cognite/neat/_graph/transformers/_classic_cdf.py +24 -13
  8. cognite/neat/_issues/_base.py +26 -17
  9. cognite/neat/_issues/errors/__init__.py +4 -2
  10. cognite/neat/_issues/errors/_external.py +7 -0
  11. cognite/neat/_issues/errors/_properties.py +2 -7
  12. cognite/neat/_issues/errors/_resources.py +1 -1
  13. cognite/neat/_issues/warnings/__init__.py +4 -2
  14. cognite/neat/_issues/warnings/_external.py +9 -1
  15. cognite/neat/_issues/warnings/_resources.py +26 -2
  16. cognite/neat/_issues/warnings/user_modeling.py +4 -4
  17. cognite/neat/_rules/_constants.py +2 -6
  18. cognite/neat/_rules/exporters/_rules2dms.py +4 -6
  19. cognite/neat/_rules/importers/__init__.py +1 -3
  20. cognite/neat/_rules/importers/_base.py +1 -1
  21. cognite/neat/_rules/importers/_dms2rules.py +3 -25
  22. cognite/neat/_rules/importers/_rdf/__init__.py +5 -0
  23. cognite/neat/_rules/importers/_rdf/_base.py +34 -11
  24. cognite/neat/_rules/importers/_rdf/_imf2rules.py +91 -0
  25. cognite/neat/_rules/importers/_rdf/_inference2rules.py +18 -2
  26. cognite/neat/_rules/importers/_rdf/_owl2rules.py +80 -0
  27. cognite/neat/_rules/importers/_rdf/_shared.py +138 -441
  28. cognite/neat/_rules/models/dms/__init__.py +2 -0
  29. cognite/neat/_rules/models/dms/_exporter.py +32 -30
  30. cognite/neat/_rules/models/dms/_rules.py +3 -45
  31. cognite/neat/_rules/models/dms/_validation.py +389 -122
  32. cognite/neat/_rules/models/information/__init__.py +2 -0
  33. cognite/neat/_rules/models/information/_rules.py +0 -59
  34. cognite/neat/_rules/models/information/_validation.py +9 -9
  35. cognite/neat/_rules/models/mapping/_classic2core.py +1 -1
  36. cognite/neat/_rules/models/mapping/_classic2core.yaml +8 -4
  37. cognite/neat/_rules/transformers/_pipelines.py +1 -1
  38. cognite/neat/_rules/transformers/_verification.py +29 -4
  39. cognite/neat/_session/_base.py +16 -41
  40. cognite/neat/_session/_prepare.py +6 -5
  41. cognite/neat/_session/_to.py +5 -2
  42. cognite/neat/_session/exceptions.py +4 -0
  43. cognite/neat/_utils/rdf_.py +6 -4
  44. cognite/neat/_version.py +1 -1
  45. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +0 -88
  46. cognite/neat/_workflows/steps/lib/current/rules_importer.py +2 -16
  47. cognite/neat/_workflows/steps/lib/current/rules_validator.py +3 -5
  48. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/METADATA +1 -1
  49. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/RECORD +52 -60
  50. cognite/neat/_rules/importers/_rdf/_imf2rules/__init__.py +0 -3
  51. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +0 -86
  52. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +0 -29
  53. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +0 -130
  54. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2rules.py +0 -154
  55. cognite/neat/_rules/importers/_rdf/_owl2rules/__init__.py +0 -3
  56. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +0 -58
  57. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +0 -65
  58. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +0 -59
  59. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2rules.py +0 -39
  60. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/LICENSE +0 -0
  61. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/WHEEL +0 -0
  62. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,4 @@
1
1
  import math
2
- import sys
3
2
  from collections.abc import Hashable
4
3
  from typing import TYPE_CHECKING, Any, ClassVar
5
4
 
@@ -34,8 +33,6 @@ from cognite.neat._rules.models.entities import (
34
33
  ClassEntity,
35
34
  ClassEntityList,
36
35
  Entity,
37
- MultiValueTypeInfo,
38
- Undefined,
39
36
  UnknownEntity,
40
37
  )
41
38
 
@@ -43,12 +40,6 @@ if TYPE_CHECKING:
43
40
  from cognite.neat._rules.models import DMSRules
44
41
 
45
42
 
46
- if sys.version_info >= (3, 11):
47
- from typing import Self
48
- else:
49
- from typing_extensions import Self
50
-
51
-
52
43
  class InformationMetadata(BaseMetadata):
53
44
  role: ClassVar[RoleTypes] = RoleTypes.information
54
45
  aspect: ClassVar[DataModelAspect] = DataModelAspect.logical
@@ -155,21 +146,6 @@ class InformationProperty(SheetRow):
155
146
  else:
156
147
  raise NeatValueError(f"Invalid RDF Path: {value!s}")
157
148
 
158
- @model_validator(mode="after")
159
- def set_default_as_list(self):
160
- if (
161
- self.type_ == EntityTypes.data_property
162
- and self.default
163
- and self.is_list
164
- and not isinstance(self.default, list)
165
- ):
166
- if isinstance(self.default, str):
167
- if self.default:
168
- self.default = self.default.replace(", ", ",").split(",")
169
- else:
170
- self.default = [self.default]
171
- return self
172
-
173
149
  @model_validator(mode="after")
174
150
  def set_type_for_default(self):
175
151
  if self.type_ == EntityTypes.data_property and self.default:
@@ -243,41 +219,6 @@ class InformationRules(BaseRules):
243
219
  values = get_default_prefixes()
244
220
  return values
245
221
 
246
- @model_validator(mode="after")
247
- def update_entities_prefix(self) -> Self:
248
- # update expected_value_types
249
- for property_ in self.properties:
250
- if isinstance(property_.value_type, ClassEntity) and property_.value_type.prefix is Undefined:
251
- property_.value_type.prefix = self.metadata.prefix
252
-
253
- if isinstance(property_.value_type, MultiValueTypeInfo):
254
- property_.value_type.set_default_prefix(self.metadata.prefix)
255
-
256
- if property_.class_.prefix is Undefined:
257
- property_.class_.prefix = self.metadata.prefix
258
-
259
- # update implements
260
- for class_ in self.classes:
261
- if class_.implements:
262
- for parent in class_.implements:
263
- if not isinstance(parent.prefix, str):
264
- parent.prefix = self.metadata.prefix
265
- if class_.class_.prefix is Undefined:
266
- class_.class_.prefix = self.metadata.prefix
267
-
268
- return self
269
-
270
- @model_validator(mode="after")
271
- def post_validation(self) -> "InformationRules":
272
- from ._validation import InformationPostValidation
273
-
274
- issue_list = InformationPostValidation(self).validate()
275
- if issue_list.warnings:
276
- issue_list.trigger_warnings()
277
- if issue_list.has_errors:
278
- raise issue_list.as_exception()
279
- return self
280
-
281
222
  def as_dms_rules(self) -> "DMSRules":
282
223
  from cognite.neat._rules.transformers._converters import _InformationRulesConverter
283
224
 
@@ -1,17 +1,17 @@
1
1
  import itertools
2
2
  from collections import Counter
3
- from typing import cast
4
3
 
5
4
  from cognite.neat._issues import IssueList
6
- from cognite.neat._issues.errors import NeatValueError, ResourceNotDefinedError
5
+ from cognite.neat._issues.errors import NeatValueError
7
6
  from cognite.neat._issues.warnings._models import UndefinedClassWarning
7
+ from cognite.neat._issues.warnings._resources import ResourceNotDefinedWarning
8
8
  from cognite.neat._rules._constants import EntityTypes
9
9
  from cognite.neat._rules.models.entities import ClassEntity, UnknownEntity
10
10
 
11
11
  from ._rules import InformationRules
12
12
 
13
13
 
14
- class InformationPostValidation:
14
+ class InformationValidation:
15
15
  """This class does all the validation of the Information rules that have dependencies
16
16
  between components."""
17
17
 
@@ -44,10 +44,10 @@ class InformationPostValidation:
44
44
  # same prefix, meaning same space
45
45
  if not class_parent_pairs[class_] and class_.prefix == self.metadata.prefix:
46
46
  self.issue_list.append(
47
- ResourceNotDefinedError[ClassEntity](
47
+ ResourceNotDefinedWarning(
48
48
  resource_type="class",
49
49
  identifier=class_,
50
- location="Classes sheet",
50
+ location="Properties sheet",
51
51
  )
52
52
  )
53
53
 
@@ -63,7 +63,7 @@ class InformationPostValidation:
63
63
  self.issue_list.append(UndefinedClassWarning(class_id=str(parent)))
64
64
  else:
65
65
  self.issue_list.append(
66
- ResourceNotDefinedError[ClassEntity](
66
+ ResourceNotDefinedWarning(
67
67
  resource_type="class",
68
68
  identifier=parent,
69
69
  location="Classes sheet",
@@ -79,7 +79,7 @@ class InformationPostValidation:
79
79
  if missing_classes := classes_with_explicit_properties.difference(defined_classes):
80
80
  for class_ in missing_classes:
81
81
  self.issue_list.append(
82
- ResourceNotDefinedError[ClassEntity](
82
+ ResourceNotDefinedWarning(
83
83
  resource_type="class",
84
84
  identifier=class_,
85
85
  location="Classes sheet",
@@ -99,9 +99,9 @@ class InformationPostValidation:
99
99
  # Todo: include row and column number
100
100
  for missing in missing_value_types:
101
101
  self.issue_list.append(
102
- ResourceNotDefinedError[ClassEntity](
102
+ ResourceNotDefinedWarning(
103
103
  resource_type="class",
104
- identifier=cast(ClassEntity, missing),
104
+ identifier=missing,
105
105
  location="Classes sheet",
106
106
  )
107
107
  )
@@ -32,7 +32,7 @@ def load_classic_to_core_mapping(org_name: str, source_space: str, source_versio
32
32
  if not isinstance(read.rules, DMSInputRules):
33
33
  raise NeatValueError(f"Expected DMS rules, but got {type(read.rules).__name__}")
34
34
 
35
- verified = VerifyDMSRules(errors="raise", post_validate=False).transform(read)
35
+ verified = VerifyDMSRules(errors="raise", validate=False).transform(read)
36
36
 
37
37
  if verified.rules is None:
38
38
  raise NeatValueError("Failed to verify the rules.")
@@ -65,7 +65,7 @@ properties:
65
65
  index: source
66
66
  is_list: false
67
67
  nullable: true
68
- value_type: cdf_cdm:SourceSystem(version=v1)
68
+ value_type: cdf_cdm:CogniteSourceSystem(version=v1)
69
69
  view: ClassicAsset
70
70
  view_property: source
71
71
  - container: cdf_cdm:CogniteDescribable
@@ -124,7 +124,7 @@ properties:
124
124
  index: source
125
125
  is_list: false
126
126
  nullable: true
127
- value_type: cdf_cdm:SourceSystem(version=v1)
127
+ value_type: cdf_cdm:CogniteSourceSystem(version=v1)
128
128
  view: ClassicEvent
129
129
  view_property: source
130
130
  - container: cdf_cdm:CogniteSchedulable
@@ -196,7 +196,7 @@ properties:
196
196
  index: source
197
197
  is_list: false
198
198
  nullable: true
199
- value_type: cdf_cdm:SourceSystem(version=v1)
199
+ value_type: cdf_cdm:CogniteSourceSystem(version=v1)
200
200
  view: ClassicFile
201
201
  view_property: source
202
202
  - container: cdf_cdm:CogniteSourceable
@@ -306,7 +306,7 @@ properties:
306
306
  is_list: false
307
307
  name: Unit
308
308
  nullable: true
309
- value_type: Unit
309
+ value_type: cdf_cdm:CogniteUnit(version=v1)
310
310
  view: ClassicTimeSeries
311
311
  view_property: unitExternalId
312
312
  views:
@@ -315,6 +315,10 @@ views:
315
315
  implements: cdf_cdm:CogniteDescribable(version=v1)
316
316
  in_model: true
317
317
  view: cdf_cdm:CogniteSourceSystem(version=v1)
318
+ - description: Represents a single unit of measurement
319
+ implements: CogniteDescribable
320
+ in_model: true
321
+ view: cdf_cdm:CogniteUnit(version=v1)
318
322
  - description: Assets represent systems that support industrial functions or processes.
319
323
  Assets are often called 'functional location'.
320
324
  implements: cdf_cdm:CogniteAsset(version=v1)
@@ -23,7 +23,7 @@ class ImporterPipeline(RulesPipeline[InputRules, VerifiedRules]):
23
23
 
24
24
  @classmethod
25
25
  def _create_pipeline(cls, importer: BaseImporter[InputRules], role: RoleTypes | None = None) -> "ImporterPipeline":
26
- items: list[RulesTransformer] = [VerifyAnyRules(errors="continue")]
26
+ items: list[RulesTransformer] = [VerifyAnyRules(errors="continue", validate=True)]
27
27
  if role is not None:
28
28
  out_cls = VERIFIED_RULES_BY_ROLE[role]
29
29
  items.append(ConvertToRules(out_cls))
@@ -1,7 +1,7 @@
1
1
  from abc import ABC
2
2
  from typing import Any, Literal
3
3
 
4
- from cognite.neat._issues import IssueList, NeatError, NeatWarning, catch_issues
4
+ from cognite.neat._issues import IssueList, MultiValueError, NeatError, NeatWarning, catch_issues
5
5
  from cognite.neat._issues.errors import NeatTypeError
6
6
  from cognite.neat._rules._shared import (
7
7
  InputRules,
@@ -18,6 +18,8 @@ from cognite.neat._rules.models import (
18
18
  InformationInputRules,
19
19
  InformationRules,
20
20
  )
21
+ from cognite.neat._rules.models.dms import DMSValidation
22
+ from cognite.neat._rules.models.information import InformationValidation
21
23
 
22
24
  from ._base import RulesTransformer
23
25
 
@@ -26,10 +28,11 @@ class VerificationTransformer(RulesTransformer[T_InputRules, T_VerifiedRules], A
26
28
  """Base class for all verification transformers."""
27
29
 
28
30
  _rules_cls: type[T_VerifiedRules]
31
+ _validation_cls: type
29
32
 
30
- def __init__(self, errors: Literal["raise", "continue"], post_validate: bool = True) -> None:
33
+ def __init__(self, errors: Literal["raise", "continue"], validate: bool = True) -> None:
31
34
  self.errors = errors
32
- self.post_validate = post_validate
35
+ self.validate = validate
33
36
 
34
37
  def transform(self, rules: T_InputRules | OutRules[T_InputRules]) -> MaybeRules[T_VerifiedRules]:
35
38
  issues = IssueList()
@@ -41,8 +44,17 @@ class VerificationTransformer(RulesTransformer[T_InputRules, T_VerifiedRules], A
41
44
  with catch_issues(issues, NeatError, NeatWarning, error_args) as future:
42
45
  rules_cls = self._get_rules_cls(in_)
43
46
  dumped = in_.dump()
44
- dumped["post_validate"] = self.post_validate
45
47
  verified_rules = rules_cls.model_validate(dumped) # type: ignore[assignment]
48
+ if self.validate:
49
+ validation_cls = self._get_validation_cls(verified_rules) # type: ignore[arg-type]
50
+ validation_issues = validation_cls(verified_rules).validate()
51
+ # We need to trigger warnings are raise exceptions such that they are caught by the context manager
52
+ # and processed with the read context
53
+ if validation_issues.warnings:
54
+ validation_issues.trigger_warnings()
55
+ if validation_issues.has_errors:
56
+ verified_rules = None
57
+ raise MultiValueError(validation_issues.errors)
46
58
 
47
59
  if (future.result == "failure" or issues.has_errors or verified_rules is None) and self.errors == "raise":
48
60
  raise issues.as_errors()
@@ -54,17 +66,22 @@ class VerificationTransformer(RulesTransformer[T_InputRules, T_VerifiedRules], A
54
66
  def _get_rules_cls(self, in_: T_InputRules) -> type[T_VerifiedRules]:
55
67
  return self._rules_cls
56
68
 
69
+ def _get_validation_cls(self, rules: T_VerifiedRules) -> type:
70
+ return self._validation_cls
71
+
57
72
 
58
73
  class VerifyDMSRules(VerificationTransformer[DMSInputRules, DMSRules]):
59
74
  """Class to verify DMS rules."""
60
75
 
61
76
  _rules_cls = DMSRules
77
+ _validation_cls = DMSValidation
62
78
 
63
79
 
64
80
  class VerifyInformationRules(VerificationTransformer[InformationInputRules, InformationRules]):
65
81
  """Class to verify Information rules."""
66
82
 
67
83
  _rules_cls = InformationRules
84
+ _validation_cls = InformationValidation
68
85
 
69
86
 
70
87
  class VerifyAnyRules(VerificationTransformer[InputRules, VerifiedRules]):
@@ -77,3 +94,11 @@ class VerifyAnyRules(VerificationTransformer[InputRules, VerifiedRules]):
77
94
  return DMSRules
78
95
  else:
79
96
  raise NeatTypeError(f"Unsupported rules type: {type(in_)}")
97
+
98
+ def _get_validation_cls(self, rules: T_VerifiedRules) -> type:
99
+ if isinstance(rules, InformationRules):
100
+ return InformationValidation
101
+ elif isinstance(rules, DMSRules):
102
+ return DMSValidation
103
+ else:
104
+ raise NeatTypeError(f"Unsupported rules type: {type(rules)}")
@@ -10,8 +10,9 @@ from cognite.neat._issues import IssueList, catch_issues
10
10
  from cognite.neat._issues.errors import RegexViolationError
11
11
  from cognite.neat._rules import importers
12
12
  from cognite.neat._rules._shared import ReadRules, VerifiedRules
13
- from cognite.neat._rules.importers import DMSImporter
14
- from cognite.neat._rules.models import DMSInputRules, DMSRules, SheetList
13
+ from cognite.neat._rules.models import DMSRules
14
+ from cognite.neat._rules.models.dms import DMSValidation
15
+ from cognite.neat._rules.models.information import InformationValidation
15
16
  from cognite.neat._rules.models.information._rules import InformationRules
16
17
  from cognite.neat._rules.models.information._rules_input import InformationInputRules
17
18
  from cognite.neat._rules.transformers import ConvertToRules, VerifyAnyRules
@@ -66,31 +67,21 @@ class NeatSession:
66
67
 
67
68
  def verify(self) -> IssueList:
68
69
  source_id, last_unverified_rule = self._state.data_model.last_unverified_rule
69
-
70
- reference_rules: DMSInputRules | None = None
71
- if isinstance(last_unverified_rule.rules, DMSInputRules):
72
- dms_rules = last_unverified_rule.rules
73
- views_ids, containers_ids = dms_rules.imported_views_and_containers_ids()
74
- if views_ids or containers_ids:
75
- if self._client is None:
76
- raise NeatSessionError(
77
- "No client provided. You are referencing unknown views and containers in your data model, "
78
- "NEAT needs a client to lookup the definitions. "
79
- "Please set the client in the session, NeatSession(client=client)."
80
- )
81
- schema = self._client.schema.retrieve(list(views_ids), list(containers_ids))
82
-
83
- importer = DMSImporter(schema)
84
- reference_rules = importer.to_rules().rules
85
-
86
- if reference_rules is not None:
87
- dms_rules.views.extend(reference_rules.views)
88
- if dms_rules.containers:
89
- dms_rules.containers.extend(reference_rules.containers or [])
90
-
91
- transformer = VerifyAnyRules("continue")
70
+ transformer = VerifyAnyRules("continue", validate=False)
92
71
  start = datetime.now(timezone.utc)
93
72
  output = transformer.try_transform(last_unverified_rule)
73
+ if isinstance(output.rules, DMSRules):
74
+ issues = DMSValidation(output.rules, self._client).validate()
75
+ elif isinstance(output.rules, InformationRules):
76
+ issues = InformationValidation(output.rules).validate()
77
+ else:
78
+ raise NeatSessionError("Unsupported rule type")
79
+ if issues.has_errors:
80
+ # This is up for discussion, but I think we should not return rules that
81
+ # only pass the verification but not the validation.
82
+ output.rules = None
83
+ output.issues.extend(issues)
84
+
94
85
  end = datetime.now(timezone.utc)
95
86
 
96
87
  if output.rules:
@@ -103,22 +94,6 @@ class NeatSession:
103
94
  self._state.data_model.provenance.source_entity(source_id)
104
95
  or self._state.data_model.provenance.target_entity(source_id),
105
96
  )
106
- if reference_rules is not None and isinstance(output.rules, DMSRules):
107
- # Remove the referenced views and containers from the rules
108
- ref_view_ids = set(reference_rules.as_view_entities())
109
- if ref_view_ids:
110
- output.rules.views = SheetList(
111
- [view for view in output.rules.views if view.view not in ref_view_ids]
112
- )
113
- ref_container_ids = reference_rules.as_container_entities()
114
- if output.rules.containers and ref_container_ids:
115
- output.rules.containers = SheetList(
116
- [
117
- container
118
- for container in output.rules.containers
119
- if container.container not in ref_container_ids
120
- ]
121
- )
122
97
 
123
98
  self._state.data_model.write(output.rules, change)
124
99
 
@@ -13,6 +13,7 @@ from cognite.neat._graph.transformers._rdfpath import MakeConnectionOnExactMatch
13
13
  from cognite.neat._rules._shared import InputRules, ReadRules
14
14
  from cognite.neat._rules.importers import DMSImporter
15
15
  from cognite.neat._rules.models import DMSRules
16
+ from cognite.neat._rules.models.dms import DMSValidation
16
17
  from cognite.neat._rules.models.information._rules_input import InformationInputRules
17
18
  from cognite.neat._rules.transformers import (
18
19
  PrefixEntities,
@@ -316,7 +317,7 @@ class DataModelPrepareAPI:
316
317
  source_id, rules = self._state.data_model.last_verified_dms_rules
317
318
 
318
319
  dms_ref: DMSRules | None = None
319
- view_ids, container_ids = rules.imported_views_and_containers_ids(include_model_views_with_no_properties=True)
320
+ view_ids, container_ids = DMSValidation(rules, self._client).imported_views_and_containers_ids()
320
321
  if view_ids or container_ids:
321
322
  if self._client is None:
322
323
  raise NeatSessionError(
@@ -324,7 +325,7 @@ class DataModelPrepareAPI:
324
325
  "NEAT needs a client to lookup the definitions. "
325
326
  "Please set the client in the session, NeatSession(client=client)."
326
327
  )
327
- schema = self._client.schema.retrieve(list(view_ids), list(container_ids))
328
+ schema = self._client.schema.retrieve([v.as_id() for v in view_ids], [c.as_id() for c in container_ids])
328
329
 
329
330
  importer = DMSImporter(schema)
330
331
  reference_rules = importer.to_rules().rules
@@ -409,7 +410,7 @@ class DataModelPrepareAPI:
409
410
  start = datetime.now(timezone.utc)
410
411
 
411
412
  source_id, rules = self._state.data_model.last_verified_dms_rules
412
- view_ids, container_ids = rules.imported_views_and_containers_ids(include_model_views_with_no_properties=True)
413
+ view_ids, container_ids = DMSValidation(rules, self._client).imported_views_and_containers_ids()
413
414
  if not (view_ids or container_ids):
414
415
  print(
415
416
  f"Data model {rules.metadata.as_data_model_id()} does not have any referenced views or containers."
@@ -422,7 +423,7 @@ class DataModelPrepareAPI:
422
423
  "NEAT needs a client to lookup the definitions. "
423
424
  "Please set the client in the session, NeatSession(client=client)."
424
425
  )
425
- schema = self._client.schema.retrieve(list(view_ids), list(container_ids))
426
+ schema = self._client.schema.retrieve([v.as_id() for v in view_ids], [c.as_id() for c in container_ids])
426
427
  copy_ = rules.model_copy(deep=True)
427
428
  copy_.metadata.version = f"{rules.metadata.version}_completed"
428
429
  importer = DMSImporter(schema)
@@ -433,7 +434,7 @@ class DataModelPrepareAPI:
433
434
  "Could not import the referenced views and containers. "
434
435
  "See `neat.inspect.issues()` for more information."
435
436
  )
436
- verified = VerifyDMSRules("continue", post_validate=False).transform(imported.rules)
437
+ verified = VerifyDMSRules("continue", validate=False).transform(imported.rules)
437
438
  if verified.rules is None:
438
439
  self._state.data_model.issue_lists.append(verified.issues)
439
440
  raise NeatSessionError(
@@ -66,9 +66,12 @@ class ToAPI:
66
66
  "This is required for the 'toolkit' format."
67
67
  )
68
68
  dms_rule = self._state.data_model.last_verified_dms_rules[1]
69
- exporters.DMSExporter().export_to_file(dms_rule, Path(io))
69
+ user_path = Path(io)
70
+ if user_path.suffix == "" and not user_path.exists():
71
+ user_path.mkdir(parents=True)
72
+ exporters.DMSExporter().export_to_file(dms_rule, user_path)
70
73
  else:
71
- raise NeatSessionError("Please provide a valid format. {['neat', 'toolkit']}")
74
+ raise NeatSessionError("Please provide a valid format. 'neat' or 'toolkit'")
72
75
 
73
76
  return None
74
77
 
@@ -2,6 +2,8 @@ import functools
2
2
  from collections.abc import Callable
3
3
  from typing import Any
4
4
 
5
+ from cognite.neat._issues.errors import CDFMissingClientError
6
+
5
7
  from ._collector import _COLLECTOR
6
8
 
7
9
  try:
@@ -27,6 +29,8 @@ def _session_method_wrapper(func: Callable, cls_name: str):
27
29
  except NeatSessionError as e:
28
30
  action = _get_action()
29
31
  print(f"{_PREFIX} Cannot {action}: {e}")
32
+ except CDFMissingClientError as e:
33
+ print(f"{_PREFIX} {e.as_message()}")
30
34
  except ModuleNotFoundError as e:
31
35
  if e.name == "neatengine":
32
36
  action = _get_action()
@@ -115,13 +115,15 @@ def as_neat_compliant_uri(uri: URIRef) -> URIRef:
115
115
  return URIRef(f"{namespace}{compliant_uri}")
116
116
 
117
117
 
118
- def convert_rdflib_content(content: RdfLiteral | URIRef | dict | list) -> Any:
119
- if isinstance(content, RdfLiteral) or isinstance(content, URIRef):
118
+ def convert_rdflib_content(content: RdfLiteral | URIRef | dict | list, remove_namespace: bool = False) -> Any:
119
+ if isinstance(content, RdfLiteral):
120
120
  return content.toPython()
121
+ elif isinstance(content, URIRef):
122
+ return remove_namespace_from_uri(content) if remove_namespace else content.toPython()
121
123
  elif isinstance(content, dict):
122
- return {key: convert_rdflib_content(value) for key, value in content.items()}
124
+ return {key: convert_rdflib_content(value, remove_namespace) for key, value in content.items()}
123
125
  elif isinstance(content, list):
124
- return [convert_rdflib_content(item) for item in content]
126
+ return [convert_rdflib_content(item, remove_namespace) for item in content]
125
127
  else:
126
128
  return content
127
129
 
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.99.0"
1
+ __version__ = "0.99.1"
2
2
  __engine__ = "^1.0.3"
@@ -21,7 +21,6 @@ __all__ = [
21
21
  "RulesToOntology",
22
22
  "RulesToSHACL",
23
23
  "RulesToSemanticDataModel",
24
- "RulesToCDFTransformations",
25
24
  "DeleteDataModelFromCDF",
26
25
  ]
27
26
 
@@ -532,93 +531,6 @@ class RulesToSemanticDataModel(Step):
532
531
  return FlowMessage(output_text=output_text)
533
532
 
534
533
 
535
- class RulesToCDFTransformations(Step):
536
- description = "This step exports transformations and RAW tables to populate a data model in CDF"
537
- version = "private-alpha"
538
- category = CATEGORY
539
- configurables: ClassVar[list[Configurable]] = [
540
- Configurable(
541
- name="Dry run",
542
- value="False",
543
- label=("Whether to perform a dry run of the export. "),
544
- options=["True", "False"],
545
- ),
546
- Configurable(
547
- name="Instance space",
548
- value="",
549
- label=(
550
- "The space to use for the transformations instances. If provided, "
551
- "the transformations will be set to populate"
552
- "this space. If not provided, the space from the input rules will be used."
553
- ),
554
- ),
555
- ]
556
-
557
- def run(self, rules: MultiRuleData, cdf_client: CogniteClient) -> FlowMessage: # type: ignore[override]
558
- if self.configs is None or self.data_store_path is None:
559
- raise WorkflowStepNotInitializedError(type(self).__name__)
560
-
561
- input_rules = rules.dms or rules.information
562
- if input_rules is None:
563
- return FlowMessage(
564
- error_text="Missing DMS or Information rules in the input data! "
565
- "Please ensure that a DMS or Information rules is provided!",
566
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
567
- )
568
- if isinstance(input_rules, DMSRules):
569
- dms_rules = input_rules
570
- elif isinstance(input_rules, InformationRules):
571
- dms_rules = InformationToDMS().transform(input_rules).rules
572
- else:
573
- raise NotImplementedError(f"Unsupported rules type {type(input_rules)}")
574
-
575
- instance_space = self.configs.get("Instance space") or dms_rules.metadata.space
576
- dry_run = self.configs.get("Dry run", "False") == "True"
577
- dms_exporter = exporters.DMSExporter(
578
- export_pipeline=True, instance_space=instance_space, export_components=["spaces"]
579
- )
580
- output_dir = self.config.staging_path
581
- output_dir.mkdir(parents=True, exist_ok=True)
582
- file_name = dms_rules.metadata.external_id.replace(":", "_")
583
- schema_zip = f"{file_name}_pipeline.zip"
584
- schema_full_path = output_dir / schema_zip
585
-
586
- dms_exporter.export_to_file(dms_rules, schema_full_path)
587
-
588
- report_lines = ["# DMS Schema Export to CDF\n\n"]
589
- errors = []
590
- for result in dms_exporter.export_to_cdf_iterable(
591
- rules=dms_rules, client=NeatClient(cdf_client), dry_run=dry_run
592
- ):
593
- report_lines.append(str(result))
594
- errors.extend(result.error_messages)
595
-
596
- report_lines.append("\n\n# ERRORS\n\n")
597
- report_lines.extend(errors)
598
-
599
- output_dir = self.config.staging_path
600
- output_dir.mkdir(parents=True, exist_ok=True)
601
- report_file = "pipeline_creation_report.txt"
602
- report_full_path = output_dir / report_file
603
- report_full_path.write_text("\n".join(report_lines))
604
-
605
- output_text = (
606
- "<p></p>"
607
- "Download Pipeline Export "
608
- f'<a href="/data/staging/{report_file}?{time.time()}" '
609
- f'target="_blank">Report</a>'
610
- "<p></p>"
611
- "Download Pipeline exported schema"
612
- f'- <a href="/data/staging/{schema_zip}?{time.time()}" '
613
- f'target="_blank">{schema_zip}</a>'
614
- )
615
-
616
- if errors:
617
- return FlowMessage(error_text=output_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
618
- else:
619
- return FlowMessage(output_text=output_text)
620
-
621
-
622
534
  def _get_default_file_name(rules: MultiRuleData, file_category: str = "ontology", extension: str = "ttl") -> str:
623
535
  name = rules.information.metadata.prefix if rules.information else cast(DMSRules, rules.dms).metadata.space
624
536
  version = rules.information.metadata.version if rules.information else cast(DMSRules, rules.dms).metadata.version
@@ -3,7 +3,6 @@ from pathlib import Path
3
3
  from typing import ClassVar
4
4
 
5
5
  from cognite.client import CogniteClient
6
- from cognite.client.data_classes.data_modeling import DataModelId
7
6
 
8
7
  from cognite.neat._client import NeatClient
9
8
  from cognite.neat._issues.errors import WorkflowStepNotInitializedError
@@ -288,21 +287,8 @@ class DMSToRules(Step):
288
287
  f"or 'my_space:my_data_model', failed to parse space from {datamodel_id_str}"
289
288
  )
290
289
  return FlowMessage(error_text=error_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
291
- ref_datamodel_str = self.configs.get("Reference data model id", "")
292
- ref_model_id: DataModelId | None = None
293
- if ref_datamodel_str:
294
- ref_model = DataModelEntity.load(ref_datamodel_str)
295
- if isinstance(ref_model, DMSUnknownEntity):
296
- error_text = (
297
- f"Reference data model id should be in the format 'my_space:my_data_model(version=1)' "
298
- f"or 'my_space:my_data_model', failed to parse space from {ref_datamodel_str}"
299
- )
300
- return FlowMessage(error_text=error_text, step_execution_status=StepExecutionStatus.ABORT_AND_FAIL)
301
- ref_model_id = ref_model.as_id()
302
-
303
- dms_importer = importers.DMSImporter.from_data_model_id(
304
- NeatClient(cdf_client), datamodel_entity.as_id(), ref_model_id
305
- )
290
+
291
+ dms_importer = importers.DMSImporter.from_data_model_id(NeatClient(cdf_client), datamodel_entity.as_id())
306
292
 
307
293
  # if role is None, it will be inferred from the rules file
308
294
  role = self.configs.get("Role")
@@ -11,6 +11,7 @@ from cognite.neat._issues import NeatIssueList
11
11
  from cognite.neat._issues.errors import ResourceNotFoundError, WorkflowStepNotInitializedError
12
12
  from cognite.neat._issues.formatters import FORMATTER_BY_NAME
13
13
  from cognite.neat._rules.models import DMSRules
14
+ from cognite.neat._rules.models.dms import DMSValidation
14
15
  from cognite.neat._workflows.model import FlowMessage, StepExecutionStatus
15
16
  from cognite.neat._workflows.steps.data_contracts import MultiRuleData
16
17
  from cognite.neat._workflows.steps.step_model import Configurable, Step
@@ -51,10 +52,7 @@ class ValidateRulesAgainstCDF(Step):
51
52
  )
52
53
  dms_rules = rules.dms
53
54
 
54
- schema = dms_rules.as_schema()
55
- errors = schema.validate()
56
- if not errors:
57
- return FlowMessage(output_text="Rules are complete and valid. No need to fetch from CDF.")
55
+ errors = DMSValidation(dms_rules, NeatClient(cdf_client)).validate()
58
56
 
59
57
  missing_spaces = [
60
58
  error.identifier
@@ -85,11 +83,11 @@ class ValidateRulesAgainstCDF(Step):
85
83
  f"and {len(retrieved_views)} views from CDF."
86
84
  )
87
85
 
86
+ schema = dms_rules.as_schema()
88
87
  schema.spaces.update({space.space: space for space in retrieved_spaces})
89
88
  schema.containers.update({container.as_id(): container for container in retrieved_containers})
90
89
  schema.views.update({view.as_id(): view for view in retrieved_views})
91
90
 
92
- errors = schema.validate()
93
91
  if errors:
94
92
  output_dir = self.data_store_path / Path("staging")
95
93
  report_writer = FORMATTER_BY_NAME[self.configs["Report Formatter"]]()