cognite-neat 1.0.31__py3-none-any.whl → 1.0.32__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.
Files changed (29) hide show
  1. cognite/neat/_client/data_classes.py +32 -0
  2. cognite/neat/_client/statistics_api.py +28 -1
  3. cognite/neat/_data_model/_analysis.py +3 -0
  4. cognite/neat/_data_model/_constants.py +4 -0
  5. cognite/neat/_data_model/{validation/dms → rules}/_base.py +11 -5
  6. cognite/neat/_data_model/rules/cdf/__init__.py +3 -0
  7. cognite/neat/_data_model/rules/cdf/_base.py +5 -0
  8. cognite/neat/_data_model/rules/cdf/_orchestrator.py +56 -0
  9. cognite/neat/_data_model/rules/cdf/_spaces.py +47 -0
  10. cognite/neat/_data_model/{validation → rules}/dms/__init__.py +2 -2
  11. cognite/neat/_data_model/{validation → rules}/dms/_ai_readiness.py +17 -17
  12. cognite/neat/_data_model/rules/dms/_base.py +5 -0
  13. cognite/neat/_data_model/{validation → rules}/dms/_connections.py +23 -23
  14. cognite/neat/_data_model/{validation → rules}/dms/_consistency.py +3 -3
  15. cognite/neat/_data_model/{validation → rules}/dms/_containers.py +9 -9
  16. cognite/neat/_data_model/{validation → rules}/dms/_limits.py +14 -14
  17. cognite/neat/_data_model/{validation → rules}/dms/_orchestrator.py +7 -7
  18. cognite/neat/_data_model/{validation → rules}/dms/_performance.py +7 -7
  19. cognite/neat/_data_model/{validation → rules}/dms/_views.py +7 -7
  20. cognite/neat/_session/_cdf.py +15 -1
  21. cognite/neat/_session/_physical.py +6 -6
  22. cognite/neat/_session/_wrappers.py +1 -1
  23. cognite/neat/_state_machine/_states.py +1 -1
  24. cognite/neat/_store/_store.py +19 -1
  25. cognite/neat/_version.py +1 -1
  26. {cognite_neat-1.0.31.dist-info → cognite_neat-1.0.32.dist-info}/METADATA +1 -1
  27. {cognite_neat-1.0.31.dist-info → cognite_neat-1.0.32.dist-info}/RECORD +29 -24
  28. {cognite_neat-1.0.31.dist-info → cognite_neat-1.0.32.dist-info}/WHEEL +1 -1
  29. /cognite/neat/_data_model/{validation → rules}/__init__.py +0 -0
@@ -2,6 +2,8 @@ from typing import Generic, TypeVar
2
2
 
3
3
  from pydantic import BaseModel, Field
4
4
 
5
+ from cognite.neat._utils.useful_types import BaseModelObject
6
+
5
7
  T = TypeVar("T", bound=BaseModel)
6
8
 
7
9
 
@@ -42,3 +44,33 @@ class StatisticsResponse(BaseModel, populate_by_name=True):
42
44
  concurrent_read_limit: int = Field(alias="concurrentReadLimit")
43
45
  concurrent_write_limit: int = Field(alias="concurrentWriteLimit")
44
46
  concurrent_delete_limit: int = Field(alias="concurrentDeleteLimit")
47
+
48
+
49
+ class SpaceStatisticsItem(BaseModelObject, populate_by_name=True):
50
+ """Individual space statistics item."""
51
+
52
+ space: str
53
+ containers: int
54
+ views: int
55
+ data_models: int
56
+ edges: int
57
+ soft_deleted_edges: int
58
+ nodes: int
59
+ soft_deleted_nodes: int
60
+
61
+ @property
62
+ def is_empty(self) -> bool:
63
+ """Check if the space has zero usage."""
64
+ return (
65
+ self.containers == 0 and self.views == 0 and self.data_models == 0 and self.edges == 0 and self.nodes == 0
66
+ )
67
+
68
+
69
+ class SpaceStatisticsResponse(BaseModelObject, populate_by_name=True):
70
+ """Response model for space statistics endpoint."""
71
+
72
+ items: list[SpaceStatisticsItem]
73
+
74
+ def empty_spaces(self) -> list[str]:
75
+ """Get a list of space identifiers that have zero usage."""
76
+ return [item.space for item in self.items if item.is_empty]
@@ -1,7 +1,10 @@
1
+ import json
2
+
1
3
  from cognite.neat._utils.http_client import HTTPClient, ParametersRequest
4
+ from cognite.neat._utils.http_client._data_classes import SimpleBodyRequest
2
5
 
3
6
  from .config import NeatClientConfig
4
- from .data_classes import StatisticsResponse
7
+ from .data_classes import SpaceStatisticsResponse, StatisticsResponse
5
8
 
6
9
 
7
10
  class StatisticsAPI:
@@ -27,3 +30,27 @@ class StatisticsAPI:
27
30
  result.raise_for_status()
28
31
  result = StatisticsResponse.model_validate_json(result.success_response.body)
29
32
  return result
33
+
34
+ def space_statistics(self, spaces: list[str]) -> SpaceStatisticsResponse:
35
+ """Retrieve space-wise usage data and limits.
36
+
37
+ Args:
38
+ spaces: List of space identifiers to retrieve statistics for.
39
+
40
+ Returns:
41
+ SpaceStatisticsResponse object.
42
+ """
43
+
44
+ body = {"items": [{"space": space} for space in spaces]}
45
+
46
+ result = self._http_client.request_with_retries(
47
+ SimpleBodyRequest(
48
+ endpoint_url=self._config.create_api_url("/models/statistics/spaces/byids"),
49
+ method="POST",
50
+ body=json.dumps(body),
51
+ )
52
+ )
53
+
54
+ result.raise_for_status()
55
+ result = SpaceStatisticsResponse.model_validate_json(result.success_response.body)
56
+ return result
@@ -8,6 +8,7 @@ from typing import Literal, TypeAlias, TypeVar
8
8
  import networkx as nx
9
9
  from pyparsing import cached_property
10
10
 
11
+ from cognite.neat._client.data_classes import SpaceStatisticsResponse
11
12
  from cognite.neat._data_model._constants import COGNITE_SPACES
12
13
  from cognite.neat._data_model._snapshot import SchemaSnapshot
13
14
  from cognite.neat._data_model.models.dms._constraints import RequiresConstraintDefinition
@@ -70,9 +71,11 @@ class ValidationResources:
70
71
  local: SchemaSnapshot,
71
72
  cdf: SchemaSnapshot,
72
73
  limits: SchemaLimits | None = None,
74
+ space_statistics: SpaceStatisticsResponse | None = None,
73
75
  ) -> None:
74
76
  self._modus_operandi = modus_operandi
75
77
  self.limits = limits or SchemaLimits()
78
+ self.space_statistics = space_statistics
76
79
 
77
80
  self.local = local
78
81
  self.cdf = cdf
@@ -67,8 +67,12 @@ COGNITE_SPACES = (
67
67
  "cdf_apm",
68
68
  "cdf_apps_shared",
69
69
  "cdf_cdm_3d",
70
+ "cdf_time_series_data",
71
+ "cdf_cdm_units",
70
72
  )
71
73
 
74
+ COGNITE_APP_SPACES = ("CommentInstanceSpace", "IndustrialCanvasInstanceSpace", "SolutionTagsInstanceSpace", "scene")
75
+
72
76
  # Defaults from https://docs.cognite.com/cdf/dm/dm_reference/dm_limits_and_restrictions#list-size-limits
73
77
 
74
78
  DEFAULT_MAX_LIST_SIZE = 1000
@@ -2,15 +2,17 @@ from abc import ABC, abstractmethod
2
2
  from typing import ClassVar
3
3
 
4
4
  from cognite.neat._data_model._analysis import ValidationResources
5
+ from cognite.neat._data_model.models.dms._schema import RequestSchema
5
6
  from cognite.neat._issues import ConsistencyError, Recommendation
6
7
 
7
8
 
8
- class DataModelValidator(ABC):
9
- """Assessors for fundamental data model principles."""
9
+ class NeatRule(ABC):
10
+ """Rules for data model principles."""
10
11
 
11
12
  code: ClassVar[str]
12
13
  issue_type: ClassVar[type[ConsistencyError] | type[Recommendation]]
13
14
  alpha: ClassVar[bool] = False
15
+ fixable: ClassVar[bool] = False
14
16
 
15
17
  def __init__(
16
18
  self,
@@ -19,7 +21,11 @@ class DataModelValidator(ABC):
19
21
  self.validation_resources = validation_resources
20
22
 
21
23
  @abstractmethod
22
- def run(self) -> list[ConsistencyError] | list[Recommendation] | list[ConsistencyError | Recommendation]:
23
- """Execute the success handler on the data model."""
24
- # do something with data model
24
+ def validate(self) -> list[ConsistencyError] | list[Recommendation] | list[ConsistencyError | Recommendation]:
25
+ """Execute rule validation."""
25
26
  ...
27
+
28
+ def fix(self) -> RequestSchema:
29
+ """Fix the issues found by the validator producing a fixed object."""
30
+
31
+ raise NotImplementedError("This rule does not implement fix()")
@@ -0,0 +1,3 @@
1
+ from ._spaces import EmptySpaces
2
+
3
+ __all__ = ["EmptySpaces"]
@@ -0,0 +1,5 @@
1
+ from cognite.neat._data_model.rules._base import NeatRule
2
+
3
+
4
+ class CDFRule(NeatRule):
5
+ """Rules for validating entire CDF."""
@@ -0,0 +1,56 @@
1
+ from collections.abc import Callable
2
+
3
+ from cognite.neat._client.data_classes import SpaceStatisticsResponse
4
+ from cognite.neat._data_model._analysis import ValidationResources
5
+ from cognite.neat._data_model._shared import OnSuccessIssuesChecker
6
+ from cognite.neat._data_model._snapshot import SchemaSnapshot
7
+ from cognite.neat._data_model.models.dms._limits import SchemaLimits
8
+ from cognite.neat._utils.auxiliary import get_concrete_subclasses
9
+
10
+ from ._base import CDFRule
11
+
12
+
13
+ class CDFRulesOrchestrator(OnSuccessIssuesChecker):
14
+ """CDF rules orchestrator, used to execute CDF rules on an entire CDF snapshot."""
15
+
16
+ def __init__(
17
+ self,
18
+ limits: SchemaLimits,
19
+ space_statistics: SpaceStatisticsResponse,
20
+ can_run_validator: Callable[[str, type], bool] | None = None,
21
+ enable_alpha_validators: bool = False,
22
+ ) -> None:
23
+ super().__init__()
24
+ self._limits = limits
25
+ self._can_run_validator = can_run_validator or (lambda code, issue_type: True) # type: ignore
26
+ self._has_run = False
27
+ self._enable_alpha_validators = enable_alpha_validators
28
+ self._space_statistics = space_statistics
29
+
30
+ def run(self, cdf_snapshot: SchemaSnapshot) -> None:
31
+ """Run quality assessment on the DMS data model."""
32
+
33
+ validation_resources = self._gather_validation_resources(cdf_snapshot)
34
+
35
+ # Initialize all validators
36
+ validators: list[CDFRule] = [validator(validation_resources) for validator in get_concrete_subclasses(CDFRule)]
37
+
38
+ # Run validators
39
+ for validator in validators:
40
+ if validator.alpha and not self._enable_alpha_validators:
41
+ continue
42
+ if self._can_run_validator(validator.code, validator.issue_type):
43
+ self._issues.extend(validator.validate())
44
+
45
+ self._has_run = True
46
+
47
+ def _gather_validation_resources(self, cdf_snapshot: SchemaSnapshot) -> ValidationResources:
48
+ # we do not want to modify the original request schema during validation
49
+
50
+ return ValidationResources(
51
+ cdf=cdf_snapshot,
52
+ local=cdf_snapshot,
53
+ limits=self._limits,
54
+ space_statistics=self._space_statistics,
55
+ modus_operandi="rebuild",
56
+ )
@@ -0,0 +1,47 @@
1
+ from cognite.neat._data_model._constants import COGNITE_APP_SPACES, COGNITE_SPACES
2
+ from cognite.neat._data_model.rules.cdf._base import CDFRule
3
+ from cognite.neat._issues import Recommendation
4
+
5
+ BASE_CODE = "NEAT-CDF-SPACES"
6
+
7
+
8
+ class EmptySpaces(CDFRule):
9
+ """Rule that checks for empty spaces in CDF.
10
+
11
+ ## What it does
12
+ This rule checks if there are any empty spaces in CDF.
13
+
14
+ ## Why is this bad?
15
+ CDF projects typically have limits of 100 spaces, and having empty spaces can waste these valuable resources.
16
+ Also, empty spaces can lead to confusion and mismanagement of resources within the CDF environment.
17
+ They may indicate incomplete configurations or unused resources that could be cleaned up.
18
+
19
+ ## Example
20
+ A space `iamempty` with no associated resources such as Views, Containers or Data Models.
21
+
22
+
23
+ """
24
+
25
+ code = f"{BASE_CODE}-001"
26
+ issue_type = Recommendation
27
+
28
+ def validate(self) -> list[Recommendation]:
29
+ issues: list[Recommendation] = []
30
+
31
+ if not self.validation_resources.space_statistics:
32
+ return issues
33
+
34
+ empty_spaces = set(self.validation_resources.space_statistics.empty_spaces()) - set(
35
+ COGNITE_APP_SPACES + COGNITE_SPACES
36
+ )
37
+
38
+ for space in empty_spaces:
39
+ issues.append(
40
+ Recommendation(
41
+ message=f"Space '{space}' is empty and has no associated resources.",
42
+ code=self.code,
43
+ fix="Consider removing the empty space to maintain a clean CDF environment.",
44
+ )
45
+ )
46
+
47
+ return issues
@@ -36,7 +36,7 @@ from ._limits import (
36
36
  ViewImplementsCountIsOutOfLimits,
37
37
  ViewPropertyCountIsOutOfLimits,
38
38
  )
39
- from ._orchestrator import DmsDataModelValidation
39
+ from ._orchestrator import DmsDataModelRulesOrchestrator
40
40
  from ._performance import (
41
41
  MissingRequiresConstraint,
42
42
  SuboptimalRequiresConstraint,
@@ -53,7 +53,7 @@ __all__ = [
53
53
  "DataModelMissingDescription",
54
54
  "DataModelMissingName",
55
55
  "DataModelViewCountIsOutOfLimits",
56
- "DmsDataModelValidation",
56
+ "DmsDataModelRulesOrchestrator",
57
57
  "EnumerationMissingDescription",
58
58
  "EnumerationMissingName",
59
59
  "ExternalContainerDoesNotExist",
@@ -1,13 +1,13 @@
1
1
  """Validators for checking if data model is AI-ready."""
2
2
 
3
3
  from cognite.neat._data_model.models.dms._data_types import EnumProperty
4
- from cognite.neat._data_model.validation.dms._base import DataModelValidator
4
+ from cognite.neat._data_model.rules.dms._base import DataModelRule
5
5
  from cognite.neat._issues import Recommendation
6
6
 
7
7
  BASE_CODE = "NEAT-DMS-AI-READINESS"
8
8
 
9
9
 
10
- class DataModelMissingName(DataModelValidator):
10
+ class DataModelMissingName(DataModelRule):
11
11
  """Validates that data model has a human-readable name.
12
12
 
13
13
  ## What it does
@@ -26,7 +26,7 @@ class DataModelMissingName(DataModelValidator):
26
26
  code = f"{BASE_CODE}-001"
27
27
  issue_type = Recommendation
28
28
 
29
- def run(self) -> list[Recommendation]:
29
+ def validate(self) -> list[Recommendation]:
30
30
  recommendations: list[Recommendation] = []
31
31
 
32
32
  if not self.validation_resources.merged_data_model.name:
@@ -41,7 +41,7 @@ class DataModelMissingName(DataModelValidator):
41
41
  return recommendations
42
42
 
43
43
 
44
- class DataModelMissingDescription(DataModelValidator):
44
+ class DataModelMissingDescription(DataModelRule):
45
45
  """Validates that data model has a human-readable description.
46
46
 
47
47
  ## What it does
@@ -65,7 +65,7 @@ class DataModelMissingDescription(DataModelValidator):
65
65
  code = f"{BASE_CODE}-002"
66
66
  issue_type = Recommendation
67
67
 
68
- def run(self) -> list[Recommendation]:
68
+ def validate(self) -> list[Recommendation]:
69
69
  recommendations: list[Recommendation] = []
70
70
 
71
71
  if not self.validation_resources.merged_data_model.description:
@@ -80,7 +80,7 @@ class DataModelMissingDescription(DataModelValidator):
80
80
  return recommendations
81
81
 
82
82
 
83
- class ViewMissingName(DataModelValidator):
83
+ class ViewMissingName(DataModelRule):
84
84
  """Validates that a View has a human-readable name.
85
85
 
86
86
  ## What it does
@@ -100,7 +100,7 @@ class ViewMissingName(DataModelValidator):
100
100
  code = f"{BASE_CODE}-003"
101
101
  issue_type = Recommendation
102
102
 
103
- def run(self) -> list[Recommendation]:
103
+ def validate(self) -> list[Recommendation]:
104
104
  recommendations: list[Recommendation] = []
105
105
 
106
106
  for view_ref in self.validation_resources.merged_data_model.views or []:
@@ -121,7 +121,7 @@ class ViewMissingName(DataModelValidator):
121
121
  return recommendations
122
122
 
123
123
 
124
- class ViewMissingDescription(DataModelValidator):
124
+ class ViewMissingDescription(DataModelRule):
125
125
  """Validates that a View has a human-readable description.
126
126
 
127
127
  ## What it does
@@ -151,7 +151,7 @@ class ViewMissingDescription(DataModelValidator):
151
151
  code = f"{BASE_CODE}-004"
152
152
  issue_type = Recommendation
153
153
 
154
- def run(self) -> list[Recommendation]:
154
+ def validate(self) -> list[Recommendation]:
155
155
  recommendations: list[Recommendation] = []
156
156
 
157
157
  for view_ref in self.validation_resources.merged_data_model.views or []:
@@ -172,7 +172,7 @@ class ViewMissingDescription(DataModelValidator):
172
172
  return recommendations
173
173
 
174
174
 
175
- class ViewPropertyMissingName(DataModelValidator):
175
+ class ViewPropertyMissingName(DataModelRule):
176
176
  """Validates that a view property has a human-readable name.
177
177
 
178
178
  ## What it does
@@ -192,7 +192,7 @@ class ViewPropertyMissingName(DataModelValidator):
192
192
  code = f"{BASE_CODE}-005"
193
193
  issue_type = Recommendation
194
194
 
195
- def run(self) -> list[Recommendation]:
195
+ def validate(self) -> list[Recommendation]:
196
196
  recommendations: list[Recommendation] = []
197
197
 
198
198
  for view_ref in self.validation_resources.merged_data_model.views or []:
@@ -217,7 +217,7 @@ class ViewPropertyMissingName(DataModelValidator):
217
217
  return recommendations
218
218
 
219
219
 
220
- class ViewPropertyMissingDescription(DataModelValidator):
220
+ class ViewPropertyMissingDescription(DataModelRule):
221
221
  """Validates that a View property has a human-readable description.
222
222
 
223
223
  ## What it does
@@ -248,7 +248,7 @@ class ViewPropertyMissingDescription(DataModelValidator):
248
248
  code = f"{BASE_CODE}-006"
249
249
  issue_type = Recommendation
250
250
 
251
- def run(self) -> list[Recommendation]:
251
+ def validate(self) -> list[Recommendation]:
252
252
  recommendations: list[Recommendation] = []
253
253
 
254
254
  for view_ref in self.validation_resources.merged_data_model.views or []:
@@ -273,7 +273,7 @@ class ViewPropertyMissingDescription(DataModelValidator):
273
273
  return recommendations
274
274
 
275
275
 
276
- class EnumerationMissingName(DataModelValidator):
276
+ class EnumerationMissingName(DataModelRule):
277
277
  """Validates that an enumeration has a human-readable name.
278
278
 
279
279
  ## What it does
@@ -293,7 +293,7 @@ class EnumerationMissingName(DataModelValidator):
293
293
  code = f"{BASE_CODE}-007"
294
294
  issue_type = Recommendation
295
295
 
296
- def run(self) -> list[Recommendation]:
296
+ def validate(self) -> list[Recommendation]:
297
297
  recommendations: list[Recommendation] = []
298
298
 
299
299
  for container_ref in self.validation_resources.merged.containers:
@@ -322,7 +322,7 @@ class EnumerationMissingName(DataModelValidator):
322
322
  return recommendations
323
323
 
324
324
 
325
- class EnumerationMissingDescription(DataModelValidator):
325
+ class EnumerationMissingDescription(DataModelRule):
326
326
  """Validates that an enumeration value has a human-readable description.
327
327
 
328
328
  ## What it does
@@ -353,7 +353,7 @@ class EnumerationMissingDescription(DataModelValidator):
353
353
  code = f"{BASE_CODE}-008"
354
354
  issue_type = Recommendation
355
355
 
356
- def run(self) -> list[Recommendation]:
356
+ def validate(self) -> list[Recommendation]:
357
357
  recommendations: list[Recommendation] = []
358
358
 
359
359
  for container_ref in self.validation_resources.merged.containers:
@@ -0,0 +1,5 @@
1
+ from cognite.neat._data_model.rules._base import NeatRule
2
+
3
+
4
+ class DataModelRule(NeatRule):
5
+ """Rules for data model principles."""
@@ -9,13 +9,13 @@ from cognite.neat._data_model.models.dms._references import (
9
9
  ViewReference,
10
10
  )
11
11
  from cognite.neat._data_model.models.dms._view_property import ViewCorePropertyRequest
12
- from cognite.neat._data_model.validation.dms._base import DataModelValidator
12
+ from cognite.neat._data_model.rules.dms._base import DataModelRule
13
13
  from cognite.neat._issues import ConsistencyError, Recommendation
14
14
 
15
15
  BASE_CODE = "NEAT-DMS-CONNECTIONS"
16
16
 
17
17
 
18
- class ConnectionValueTypeUnexisting(DataModelValidator):
18
+ class ConnectionValueTypeUnexisting(DataModelRule):
19
19
  """Validates that connection value types exist.
20
20
 
21
21
  ## What it does
@@ -33,7 +33,7 @@ class ConnectionValueTypeUnexisting(DataModelValidator):
33
33
  code = f"{BASE_CODE}-001"
34
34
  issue_type = ConsistencyError
35
35
 
36
- def run(self) -> list[ConsistencyError]:
36
+ def validate(self) -> list[ConsistencyError]:
37
37
  errors: list[ConsistencyError] = []
38
38
 
39
39
  for (view, property_), value_type in self.validation_resources.connection_end_node_types.items():
@@ -57,7 +57,7 @@ class ConnectionValueTypeUnexisting(DataModelValidator):
57
57
  return errors
58
58
 
59
59
 
60
- class ConnectionValueTypeUndefined(DataModelValidator):
60
+ class ConnectionValueTypeUndefined(DataModelRule):
61
61
  """Validates that connection value types are not None, i.e. undefined.
62
62
 
63
63
  ## What it does
@@ -77,7 +77,7 @@ class ConnectionValueTypeUndefined(DataModelValidator):
77
77
  code = f"{BASE_CODE}-002"
78
78
  issue_type = Recommendation
79
79
 
80
- def run(self) -> list[Recommendation]:
80
+ def validate(self) -> list[Recommendation]:
81
81
  recommendations: list[Recommendation] = []
82
82
 
83
83
  for (view, property_), value_type in self.validation_resources.connection_end_node_types.items():
@@ -128,7 +128,7 @@ def _normalize_through_reference(
128
128
  return through
129
129
 
130
130
 
131
- class ReverseConnectionSourceViewMissing(DataModelValidator):
131
+ class ReverseConnectionSourceViewMissing(DataModelRule):
132
132
  """Validates that source view referenced in reverse connection exist.
133
133
 
134
134
  ## What it does
@@ -146,7 +146,7 @@ class ReverseConnectionSourceViewMissing(DataModelValidator):
146
146
  code = f"{BASE_CODE}-REVERSE-001"
147
147
  issue_type = ConsistencyError
148
148
 
149
- def run(self) -> list[ConsistencyError]:
149
+ def validate(self) -> list[ConsistencyError]:
150
150
  errors: list[ConsistencyError] = []
151
151
 
152
152
  for (target_view_ref, reverse_prop_name), (
@@ -172,7 +172,7 @@ class ReverseConnectionSourceViewMissing(DataModelValidator):
172
172
  return errors
173
173
 
174
174
 
175
- class ReverseConnectionSourcePropertyMissing(DataModelValidator):
175
+ class ReverseConnectionSourcePropertyMissing(DataModelRule):
176
176
  """Validates that source property referenced in reverse connections exist.
177
177
 
178
178
  ## What it does
@@ -191,7 +191,7 @@ class ReverseConnectionSourcePropertyMissing(DataModelValidator):
191
191
  code = f"{BASE_CODE}-REVERSE-002"
192
192
  issue_type = ConsistencyError
193
193
 
194
- def run(self) -> list[ConsistencyError]:
194
+ def validate(self) -> list[ConsistencyError]:
195
195
  errors: list[ConsistencyError] = []
196
196
 
197
197
  for (target_view_ref, reverse_prop_name), (
@@ -224,7 +224,7 @@ class ReverseConnectionSourcePropertyMissing(DataModelValidator):
224
224
  return errors
225
225
 
226
226
 
227
- class ReverseConnectionSourcePropertyWrongType(DataModelValidator):
227
+ class ReverseConnectionSourcePropertyWrongType(DataModelRule):
228
228
  """Validates that source property for the reverse connections is a direct relation.
229
229
 
230
230
  ## What it does
@@ -243,7 +243,7 @@ class ReverseConnectionSourcePropertyWrongType(DataModelValidator):
243
243
  code = f"{BASE_CODE}-REVERSE-003"
244
244
  issue_type = ConsistencyError
245
245
 
246
- def run(self) -> list[ConsistencyError]:
246
+ def validate(self) -> list[ConsistencyError]:
247
247
  errors: list[ConsistencyError] = []
248
248
 
249
249
  for (target_view_ref, reverse_prop_name), (
@@ -280,7 +280,7 @@ class ReverseConnectionSourcePropertyWrongType(DataModelValidator):
280
280
  return errors
281
281
 
282
282
 
283
- class ReverseConnectionContainerMissing(DataModelValidator):
283
+ class ReverseConnectionContainerMissing(DataModelRule):
284
284
  """Validates that the container referenced by the reverse connection source properties exist.
285
285
 
286
286
  ## What it does
@@ -298,7 +298,7 @@ class ReverseConnectionContainerMissing(DataModelValidator):
298
298
  code = f"{BASE_CODE}-REVERSE-004"
299
299
  issue_type = ConsistencyError
300
300
 
301
- def run(self) -> list[ConsistencyError]:
301
+ def validate(self) -> list[ConsistencyError]:
302
302
  errors: list[ConsistencyError] = []
303
303
 
304
304
  for (target_view_ref, reverse_prop_name), (
@@ -344,7 +344,7 @@ class ReverseConnectionContainerMissing(DataModelValidator):
344
344
  return errors
345
345
 
346
346
 
347
- class ReverseConnectionContainerPropertyMissing(DataModelValidator):
347
+ class ReverseConnectionContainerPropertyMissing(DataModelRule):
348
348
  """Validates that container property referenced by the reverse connections exists.
349
349
 
350
350
  ## What it does
@@ -363,7 +363,7 @@ class ReverseConnectionContainerPropertyMissing(DataModelValidator):
363
363
  code = f"{BASE_CODE}-REVERSE-005"
364
364
  issue_type = ConsistencyError
365
365
 
366
- def run(self) -> list[ConsistencyError]:
366
+ def validate(self) -> list[ConsistencyError]:
367
367
  errors: list[ConsistencyError] = []
368
368
 
369
369
  for (target_view_ref, reverse_prop_name), (
@@ -412,7 +412,7 @@ class ReverseConnectionContainerPropertyMissing(DataModelValidator):
412
412
  return errors
413
413
 
414
414
 
415
- class ReverseConnectionContainerPropertyWrongType(DataModelValidator):
415
+ class ReverseConnectionContainerPropertyWrongType(DataModelRule):
416
416
  """Validates that the container property used in reverse connection is the direct relations.
417
417
 
418
418
  ## What it does
@@ -431,7 +431,7 @@ class ReverseConnectionContainerPropertyWrongType(DataModelValidator):
431
431
  code = f"{BASE_CODE}-REVERSE-006"
432
432
  issue_type = ConsistencyError
433
433
 
434
- def run(self) -> list[ConsistencyError]:
434
+ def validate(self) -> list[ConsistencyError]:
435
435
  errors: list[ConsistencyError] = []
436
436
 
437
437
  for (target_view_ref, reverse_prop_name), (
@@ -483,7 +483,7 @@ class ReverseConnectionContainerPropertyWrongType(DataModelValidator):
483
483
  return errors
484
484
 
485
485
 
486
- class ReverseConnectionTargetMissing(DataModelValidator):
486
+ class ReverseConnectionTargetMissing(DataModelRule):
487
487
  """Validates that the direct connection in reverse connection pair have target views specified.
488
488
 
489
489
  ## What it does
@@ -501,7 +501,7 @@ class ReverseConnectionTargetMissing(DataModelValidator):
501
501
  code = f"{BASE_CODE}-REVERSE-007"
502
502
  issue_type = Recommendation
503
503
 
504
- def run(self) -> list[Recommendation]:
504
+ def validate(self) -> list[Recommendation]:
505
505
  recommendations: list[Recommendation] = []
506
506
 
507
507
  for (target_view_ref, reverse_prop_name), (
@@ -546,7 +546,7 @@ class ReverseConnectionTargetMissing(DataModelValidator):
546
546
  return recommendations
547
547
 
548
548
 
549
- class ReverseConnectionPointsToAncestor(DataModelValidator):
549
+ class ReverseConnectionPointsToAncestor(DataModelRule):
550
550
  """Validates that direct connections point to specific views rather than ancestors.
551
551
 
552
552
  ## What it does
@@ -565,7 +565,7 @@ class ReverseConnectionPointsToAncestor(DataModelValidator):
565
565
  code = f"{BASE_CODE}-REVERSE-008"
566
566
  issue_type = Recommendation
567
567
 
568
- def run(self) -> list[Recommendation]:
568
+ def validate(self) -> list[Recommendation]:
569
569
  recommendations: list[Recommendation] = []
570
570
 
571
571
  for (target_view_ref, reverse_prop_name), (
@@ -613,7 +613,7 @@ class ReverseConnectionPointsToAncestor(DataModelValidator):
613
613
  return recommendations
614
614
 
615
615
 
616
- class ReverseConnectionTargetMismatch(DataModelValidator):
616
+ class ReverseConnectionTargetMismatch(DataModelRule):
617
617
  """Validates that direct connections point to the correct target views.
618
618
 
619
619
  ## What it does
@@ -632,7 +632,7 @@ class ReverseConnectionTargetMismatch(DataModelValidator):
632
632
  code = f"{BASE_CODE}-REVERSE-009"
633
633
  issue_type = Recommendation
634
634
 
635
- def run(self) -> list[Recommendation]:
635
+ def validate(self) -> list[Recommendation]:
636
636
  recommendations: list[Recommendation] = []
637
637
 
638
638
  for (target_view_ref, reverse_prop_name), (
@@ -1,13 +1,13 @@
1
1
  """Validators checking for consistency issues in data model."""
2
2
 
3
3
  from cognite.neat._data_model._constants import COGNITE_SPACES
4
- from cognite.neat._data_model.validation.dms._base import DataModelValidator
4
+ from cognite.neat._data_model.rules.dms._base import DataModelRule
5
5
  from cognite.neat._issues import Recommendation
6
6
 
7
7
  BASE_CODE = "NEAT-DMS-CONSISTENCY"
8
8
 
9
9
 
10
- class ViewSpaceVersionInconsistentWithDataModel(DataModelValidator):
10
+ class ViewSpaceVersionInconsistentWithDataModel(DataModelRule):
11
11
  """Validates that views have consistent space and version with the data model.
12
12
 
13
13
  ## What it does
@@ -25,7 +25,7 @@ class ViewSpaceVersionInconsistentWithDataModel(DataModelValidator):
25
25
  code = f"{BASE_CODE}-001"
26
26
  issue_type = Recommendation
27
27
 
28
- def run(self) -> list[Recommendation]:
28
+ def validate(self) -> list[Recommendation]:
29
29
  recommendations: list[Recommendation] = []
30
30
 
31
31
  if not self.validation_resources.merged_data_model.views: