cognite-neat 1.0.21__py3-none-any.whl → 1.0.23__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.
- cognite/neat/_data_model/_analysis.py +47 -14
- cognite/neat/_data_model/validation/dms/__init__.py +2 -0
- cognite/neat/_data_model/validation/dms/_containers.py +38 -0
- cognite/neat/_data_model/validation/dms/_views.py +38 -0
- cognite/neat/_version.py +1 -1
- {cognite_neat-1.0.21.dist-info → cognite_neat-1.0.23.dist-info}/METADATA +3 -3
- {cognite_neat-1.0.21.dist-info → cognite_neat-1.0.23.dist-info}/RECORD +8 -8
- {cognite_neat-1.0.21.dist-info → cognite_neat-1.0.23.dist-info}/WHEEL +2 -2
|
@@ -23,7 +23,7 @@ from cognite.neat._data_model.models.dms._view_property import (
|
|
|
23
23
|
ViewRequestProperty,
|
|
24
24
|
)
|
|
25
25
|
from cognite.neat._data_model.models.dms._views import ViewRequest
|
|
26
|
-
from cognite.neat._utils.useful_types import ModusOperandi
|
|
26
|
+
from cognite.neat._utils.useful_types import ModusOperandi, T_Reference
|
|
27
27
|
|
|
28
28
|
# Type aliases for better readability
|
|
29
29
|
ViewsByReference: TypeAlias = dict[ViewReference, ViewRequest]
|
|
@@ -458,9 +458,42 @@ class ValidationResources:
|
|
|
458
458
|
view_sets = [self.views_by_container.get(c, set()) for c in containers]
|
|
459
459
|
return set.intersection(*view_sets)
|
|
460
460
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
461
|
+
@cached_property
|
|
462
|
+
def implements_graph(self) -> nx.DiGraph:
|
|
463
|
+
"""Build a weighted directed graph of view implements.
|
|
464
|
+
|
|
465
|
+
Nodes are ViewReferences, edges represent implements.
|
|
466
|
+
An edge A → B means view A implements view B. Order of views in implements is used to set weight of an edge.
|
|
467
|
+
|
|
468
|
+
Includes views from both merged schema and CDF
|
|
469
|
+
"""
|
|
470
|
+
graph: nx.DiGraph = nx.DiGraph()
|
|
471
|
+
|
|
472
|
+
for view_ref in self.cdf.views:
|
|
473
|
+
graph.add_node(view_ref)
|
|
474
|
+
for view_ref in self.merged.views:
|
|
475
|
+
graph.add_node(view_ref)
|
|
476
|
+
|
|
477
|
+
# Add edges for implements
|
|
478
|
+
for view_ref in graph.nodes():
|
|
479
|
+
view = self.select_view(view_ref)
|
|
480
|
+
if not view or not view.implements:
|
|
481
|
+
continue
|
|
482
|
+
|
|
483
|
+
# Adding weight to preserve order of implements
|
|
484
|
+
for i, implement in enumerate(view.implements):
|
|
485
|
+
graph.add_edge(view_ref, implement, weight=i + 1)
|
|
486
|
+
|
|
487
|
+
return graph
|
|
488
|
+
|
|
489
|
+
@cached_property
|
|
490
|
+
def implements_cycles(self) -> list[list[ViewReference]]:
|
|
491
|
+
"""Find all cycles in the implements graph.
|
|
492
|
+
Returns:
|
|
493
|
+
List of lists, where each list contains the ordered Views involved in forming the implements cycle.
|
|
494
|
+
"""
|
|
495
|
+
|
|
496
|
+
return self.graph_cycles(self.implements_graph)
|
|
464
497
|
|
|
465
498
|
@cached_property
|
|
466
499
|
def requires_constraint_graph(self) -> nx.DiGraph:
|
|
@@ -524,15 +557,15 @@ class ValidationResources:
|
|
|
524
557
|
return False
|
|
525
558
|
|
|
526
559
|
@cached_property
|
|
527
|
-
def requires_constraint_cycles(self) -> list[
|
|
528
|
-
"""Find all cycles in the requires constraint graph
|
|
529
|
-
|
|
530
|
-
Uses strongly connected components (SCC) to identify cycles efficiently.
|
|
531
|
-
An SCC with more than one node indicates a cycle.
|
|
532
|
-
|
|
560
|
+
def requires_constraint_cycles(self) -> list[list[ContainerReference]]:
|
|
561
|
+
"""Find all cycles in the requires constraint graph.
|
|
533
562
|
Returns:
|
|
534
|
-
List of
|
|
563
|
+
List of lists, where each list contains the ordered containers involved in forming the requires cycle.
|
|
535
564
|
"""
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
565
|
+
|
|
566
|
+
return self.graph_cycles(self.requires_constraint_graph)
|
|
567
|
+
|
|
568
|
+
@staticmethod
|
|
569
|
+
def graph_cycles(graph: nx.DiGraph) -> list[list[T_Reference]]:
|
|
570
|
+
"""Returns cycles in the graph otherwise empty list"""
|
|
571
|
+
return [candidate for candidate in nx.simple_cycles(graph) if len(candidate) > 1]
|
|
@@ -26,6 +26,7 @@ from ._containers import (
|
|
|
26
26
|
ExternalContainerDoesNotExist,
|
|
27
27
|
ExternalContainerPropertyDoesNotExist,
|
|
28
28
|
RequiredContainerDoesNotExist,
|
|
29
|
+
RequiresConstraintCycle,
|
|
29
30
|
)
|
|
30
31
|
from ._limits import (
|
|
31
32
|
ContainerPropertyCountIsOutOfLimits,
|
|
@@ -54,6 +55,7 @@ __all__ = [
|
|
|
54
55
|
"ExternalContainerPropertyDoesNotExist",
|
|
55
56
|
"ImplementedViewNotExisting",
|
|
56
57
|
"RequiredContainerDoesNotExist",
|
|
58
|
+
"RequiresConstraintCycle",
|
|
57
59
|
"ReverseConnectionContainerMissing",
|
|
58
60
|
"ReverseConnectionContainerPropertyMissing",
|
|
59
61
|
"ReverseConnectionContainerPropertyWrongType",
|
|
@@ -197,3 +197,41 @@ class RequiredContainerDoesNotExist(DataModelValidator):
|
|
|
197
197
|
)
|
|
198
198
|
|
|
199
199
|
return errors
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class RequiresConstraintCycle(DataModelValidator):
|
|
203
|
+
"""
|
|
204
|
+
Validates that requires constraints between containers do not form cycles.
|
|
205
|
+
|
|
206
|
+
## What it does
|
|
207
|
+
This validator checks if the requires constraints between containers form a cycle.
|
|
208
|
+
For example, if container A requires B, B requires C, and C requires A, this forms
|
|
209
|
+
a cycle.
|
|
210
|
+
|
|
211
|
+
## Why is this bad?
|
|
212
|
+
Cycles in requires constraints will be rejected by the CDF API. The deployment
|
|
213
|
+
of the data model will fail if any such cycle exists.
|
|
214
|
+
|
|
215
|
+
## Example
|
|
216
|
+
Container `my_space:OrderContainer` requires `my_space:CustomerContainer`, which
|
|
217
|
+
requires `my_space:OrderContainer`. This creates a cycle and will be rejected.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
code = f"{BASE_CODE}-005"
|
|
221
|
+
issue_type = ConsistencyError
|
|
222
|
+
alpha = True # Still in development
|
|
223
|
+
|
|
224
|
+
def run(self) -> list[ConsistencyError]:
|
|
225
|
+
errors: list[ConsistencyError] = []
|
|
226
|
+
|
|
227
|
+
for cycle in self.validation_resources.requires_constraint_cycles:
|
|
228
|
+
cycle_str = " -> ".join(str(c) for c in cycle) + f" -> {cycle[0]!s}"
|
|
229
|
+
errors.append(
|
|
230
|
+
ConsistencyError(
|
|
231
|
+
message=f"Requires constraints form a cycle: {cycle_str}",
|
|
232
|
+
fix="Remove one of the requires constraints to break the cycle",
|
|
233
|
+
code=self.code,
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return errors
|
|
@@ -124,3 +124,41 @@ class ImplementedViewNotExisting(DataModelValidator):
|
|
|
124
124
|
)
|
|
125
125
|
|
|
126
126
|
return errors
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class CyclicImplements(DataModelValidator):
|
|
130
|
+
"""Validates that view implements are not forming a cycle (i.e. cyclic graph of implements)
|
|
131
|
+
|
|
132
|
+
## What it does
|
|
133
|
+
Runs graph analysis of the implements graph finding every cycle within the graph
|
|
134
|
+
|
|
135
|
+
## Why is this bad?
|
|
136
|
+
You will not be able to deploy the data model to CDF, since cyclic implements are impossible to resolve
|
|
137
|
+
in terms of inheritance of properties.
|
|
138
|
+
|
|
139
|
+
## Example
|
|
140
|
+
Say we have following views: A, B, and C, where A implements B, B implements C, and C implements A. This forms
|
|
141
|
+
the cyclic graph of implements A->B->C->A.
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
code = f"{BASE_CODE}-003"
|
|
146
|
+
issue_type = ConsistencyError
|
|
147
|
+
alpha = True
|
|
148
|
+
|
|
149
|
+
def run(self) -> list[ConsistencyError]:
|
|
150
|
+
errors: list[ConsistencyError] = []
|
|
151
|
+
|
|
152
|
+
for cycle in self.validation_resources.implements_cycles:
|
|
153
|
+
errors.append(
|
|
154
|
+
ConsistencyError(
|
|
155
|
+
message=(
|
|
156
|
+
"Detected cycle of view implements "
|
|
157
|
+
f"{'->'.join([str(view) for view in cycle]) + f'->{cycle[0]}'}"
|
|
158
|
+
),
|
|
159
|
+
fix="Inspect involved views and make necessary fix",
|
|
160
|
+
code=self.code,
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return errors
|
cognite/neat/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "1.0.
|
|
1
|
+
__version__ = "1.0.23"
|
|
2
2
|
__engine__ = "^2.0.4"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cognite-neat
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.23
|
|
4
4
|
Summary: Knowledge graph transformation
|
|
5
5
|
Author: Nikola Vasiljevic, Anders Albert
|
|
6
6
|
Author-email: Nikola Vasiljevic <nikola.vasiljevic@cognite.com>, Anders Albert <anders.albert@cognite.com>
|
|
@@ -55,10 +55,10 @@ Requires-Dist: lxml>=5.3.0,<6.0.0 ; extra == 'lxml'
|
|
|
55
55
|
Requires-Dist: oxrdflib>=0.4.0,<0.5.0 ; extra == 'oxi'
|
|
56
56
|
Requires-Dist: pyoxigraph>=0.4.3,<0.5.0 ; extra == 'oxi'
|
|
57
57
|
Requires-Python: >=3.10
|
|
58
|
-
Project-URL: Changelog, https://github.com/cognitedata/neat/releases
|
|
59
58
|
Project-URL: Documentation, https://cognite-neat.readthedocs-hosted.com/
|
|
60
|
-
Project-URL: GitHub, https://github.com/cognitedata/neat
|
|
61
59
|
Project-URL: Homepage, https://cognite-neat.readthedocs-hosted.com/
|
|
60
|
+
Project-URL: GitHub, https://github.com/cognitedata/neat
|
|
61
|
+
Project-URL: Changelog, https://github.com/cognitedata/neat/releases
|
|
62
62
|
Provides-Extra: docs
|
|
63
63
|
Provides-Extra: google
|
|
64
64
|
Provides-Extra: lxml
|
|
@@ -15,7 +15,7 @@ cognite/neat/_client/statistics_api.py,sha256=HcYb2nNC9M_iaI1xyjjLn2Cz1tcyu7BJea
|
|
|
15
15
|
cognite/neat/_client/views_api.py,sha256=Qzk_wiLtaWszxCQFDBoWCH1yDc4GOEJsVOcL061rcK0,5639
|
|
16
16
|
cognite/neat/_config.py,sha256=ZvCkcaRVAvH4-ClvinoWaLWhRJpRByqdvncGFsf5gLk,9886
|
|
17
17
|
cognite/neat/_data_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
cognite/neat/_data_model/_analysis.py,sha256=
|
|
18
|
+
cognite/neat/_data_model/_analysis.py,sha256=dN-udKm_5oD3217O4B_QIps2Hx4v50-Pu2fR0bQNQg0,23504
|
|
19
19
|
cognite/neat/_data_model/_constants.py,sha256=E2axzdYjsIy7lTHsjW91wsv6r-pUwko8g6K8C_oRnxk,1707
|
|
20
20
|
cognite/neat/_data_model/_identifiers.py,sha256=lDLvMvYDgRNFgk5GmxWzOUunG7M3synAciNjzJI0m_o,1913
|
|
21
21
|
cognite/neat/_data_model/_shared.py,sha256=H0gFqa8tKFNWuvdat5jL6OwySjCw3aQkLPY3wtb9Wrw,1302
|
|
@@ -74,15 +74,15 @@ cognite/neat/_data_model/models/entities/_data_types.py,sha256=DfdEWGek7gODro-_0
|
|
|
74
74
|
cognite/neat/_data_model/models/entities/_identifiers.py,sha256=a7ojJKY1ErZgUANHscEwkctX4RJ7bWEEWOQt5g5Tsdk,1915
|
|
75
75
|
cognite/neat/_data_model/models/entities/_parser.py,sha256=zef_pSDZYMZrJl4IKreFDR577KutfhtN1xpH3Ayjt2o,7669
|
|
76
76
|
cognite/neat/_data_model/validation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
|
-
cognite/neat/_data_model/validation/dms/__init__.py,sha256=
|
|
77
|
+
cognite/neat/_data_model/validation/dms/__init__.py,sha256=STb0mYmyD-E6CfhjaXNOgWM1LvYSm59m6-Khu9cEhNI,2702
|
|
78
78
|
cognite/neat/_data_model/validation/dms/_ai_readiness.py,sha256=bffMQJ5pqumU5P3KaEdQP67OO5eMKqzN2BAWbUjG6KE,16143
|
|
79
79
|
cognite/neat/_data_model/validation/dms/_base.py,sha256=G_gMPTgKwyBW62UcCkKIBVHWp9ufAZPJ2p7o69_dJI0,820
|
|
80
80
|
cognite/neat/_data_model/validation/dms/_connections.py,sha256=-kUXf2_3V50ckxwXRwJoTHsKkS5zxiBKkkkHg8Dm4WI,30353
|
|
81
81
|
cognite/neat/_data_model/validation/dms/_consistency.py,sha256=IKSUoRQfQQcsymviESW9VuTFX7jsZMXfsObeZHPdov4,2435
|
|
82
|
-
cognite/neat/_data_model/validation/dms/_containers.py,sha256=
|
|
82
|
+
cognite/neat/_data_model/validation/dms/_containers.py,sha256=8pVnmeX6G9tQaGzzwRB_40y7TYUm4guaNbRiFgoGILU,9895
|
|
83
83
|
cognite/neat/_data_model/validation/dms/_limits.py,sha256=U7z8sN-kAyJsF5hYHPNBBg25Fvz1F8njhzYVSQOIiOU,14779
|
|
84
84
|
cognite/neat/_data_model/validation/dms/_orchestrator.py,sha256=qiuUSUmNhekFyBARUUO2yhG-X9AeU_LL49UrJ65JXFA,2964
|
|
85
|
-
cognite/neat/_data_model/validation/dms/_views.py,sha256=
|
|
85
|
+
cognite/neat/_data_model/validation/dms/_views.py,sha256=pRdnj5ZBnHNnbLKleXGbipteGma8_l5AYsDIfqgAil4,6345
|
|
86
86
|
cognite/neat/_exceptions.py,sha256=mO19TEecZYDNqSvzuc6JmCLFQ70eniT1-Gb0AEbgbzE,2090
|
|
87
87
|
cognite/neat/_issues.py,sha256=wH1mnkrpBsHUkQMGUHFLUIQWQlfJ_qMfdF7q0d9wNhY,1871
|
|
88
88
|
cognite/neat/_session/__init__.py,sha256=owqW5Mml2DSZx1AvPvwNRTBngfhBNrQ6EH-7CKL7Jp0,61
|
|
@@ -321,9 +321,9 @@ cognite/neat/_v0/session/_template.py,sha256=BNcvrW5y7LWzRM1XFxZkfR1Nc7e8UgjBClH
|
|
|
321
321
|
cognite/neat/_v0/session/_to.py,sha256=AnsRSDDdfFyYwSgi0Z-904X7WdLtPfLlR0x1xsu_jAo,19447
|
|
322
322
|
cognite/neat/_v0/session/_wizard.py,sha256=baPJgXAAF3d1bn4nbIzon1gWfJOeS5T43UXRDJEnD3c,1490
|
|
323
323
|
cognite/neat/_v0/session/exceptions.py,sha256=jv52D-SjxGfgqaHR8vnpzo0SOJETIuwbyffSWAxSDJw,3495
|
|
324
|
-
cognite/neat/_version.py,sha256=
|
|
324
|
+
cognite/neat/_version.py,sha256=rj_LKbqpGZavJZekkd20Oo3-5Wi6i3pBDjeRkCt22zY,45
|
|
325
325
|
cognite/neat/legacy.py,sha256=eI2ecxOV8ilGHyLZlN54ve_abtoK34oXognkFv3yvF0,219
|
|
326
326
|
cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
327
|
-
cognite_neat-1.0.
|
|
328
|
-
cognite_neat-1.0.
|
|
329
|
-
cognite_neat-1.0.
|
|
327
|
+
cognite_neat-1.0.23.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
|
|
328
|
+
cognite_neat-1.0.23.dist-info/METADATA,sha256=hByYgnOhvq27boiIAHGRyCwddaWgqPxcP_jX8TytSMs,6689
|
|
329
|
+
cognite_neat-1.0.23.dist-info/RECORD,,
|