cognite-neat 0.123.5__py3-none-any.whl → 0.123.7__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/_version.py +1 -1
- cognite/neat/core/_client/_api/data_modeling_loaders.py +79 -1
- cognite/neat/core/_store/_data_model.py +1 -0
- cognite/neat/core/_utils/tarjan.py +44 -0
- {cognite_neat-0.123.5.dist-info → cognite_neat-0.123.7.dist-info}/METADATA +1 -1
- {cognite_neat-0.123.5.dist-info → cognite_neat-0.123.7.dist-info}/RECORD +8 -7
- {cognite_neat-0.123.5.dist-info → cognite_neat-0.123.7.dist-info}/WHEEL +0 -0
- {cognite_neat-0.123.5.dist-info → cognite_neat-0.123.7.dist-info}/licenses/LICENSE +0 -0
cognite/neat/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.123.
|
|
1
|
+
__version__ = "0.123.7"
|
|
2
2
|
__engine__ = "^2.0.4"
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import re
|
|
1
2
|
import warnings
|
|
2
3
|
from abc import ABC, abstractmethod
|
|
4
|
+
from collections import defaultdict
|
|
3
5
|
from collections.abc import Callable, Collection, Iterable, Sequence
|
|
4
6
|
from dataclasses import dataclass, field
|
|
5
|
-
from graphlib import TopologicalSorter
|
|
7
|
+
from graphlib import CycleError, TopologicalSorter
|
|
6
8
|
from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal, TypeVar, cast, overload
|
|
7
9
|
|
|
8
10
|
from cognite.client.data_classes import filters
|
|
@@ -56,6 +58,7 @@ from cognite.neat.core._client.data_classes.data_modeling import Component
|
|
|
56
58
|
from cognite.neat.core._client.data_classes.schema import DMSSchema
|
|
57
59
|
from cognite.neat.core._issues.warnings import CDFMaxIterationsWarning
|
|
58
60
|
from cognite.neat.core._shared import T_ID
|
|
61
|
+
from cognite.neat.core._utils.tarjan import tarjan
|
|
59
62
|
|
|
60
63
|
if TYPE_CHECKING:
|
|
61
64
|
from cognite.neat.core._client._api_client import NeatClient
|
|
@@ -620,6 +623,81 @@ class ViewLoader(DataModelingLoader[ViewId, ViewApply, View, ViewApplyList, View
|
|
|
620
623
|
def _create(self, items: Sequence[ViewApply]) -> ViewList:
|
|
621
624
|
return self._client.data_modeling.views.apply(items)
|
|
622
625
|
|
|
626
|
+
def create(self, items: Sequence[ViewApply]) -> ViewList:
|
|
627
|
+
try:
|
|
628
|
+
return self._create(items)
|
|
629
|
+
except CogniteAPIError as e1:
|
|
630
|
+
if self._is_auto_retryable(e1):
|
|
631
|
+
# Fallback to creating one by one if the error is auto-retryable.
|
|
632
|
+
return self._fallback_one_by_one(self._create, items)
|
|
633
|
+
elif self._is_false_not_exists(e1, {item.as_id() for item in items}):
|
|
634
|
+
return self._try_to_recover_coupled(items, e1)
|
|
635
|
+
raise
|
|
636
|
+
|
|
637
|
+
def _try_to_recover_coupled(self, items: Sequence[ViewApply], original_error: CogniteAPIError) -> ViewList:
|
|
638
|
+
"""The /models/views endpoint can give faulty 400 about missing views that are part of the request.
|
|
639
|
+
|
|
640
|
+
This method tries to recover from such errors by identifying the strongly connected components in the graph
|
|
641
|
+
defined by the implements and through properties of the views. We then create the components in topological
|
|
642
|
+
order.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
items: The items that failed to create.
|
|
646
|
+
original_error: The original error that was raised. If the problem is not recoverable, this error is raised.
|
|
647
|
+
|
|
648
|
+
Returns:
|
|
649
|
+
The views that were created.
|
|
650
|
+
|
|
651
|
+
"""
|
|
652
|
+
views_by_id = {self.get_id(item): item for item in items}
|
|
653
|
+
parents_by_child: dict[ViewId, set[ViewId]] = {
|
|
654
|
+
view_id: {parent for parent in view.implements or [] if parent in views_by_id}
|
|
655
|
+
for view_id, view in views_by_id.items()
|
|
656
|
+
}
|
|
657
|
+
# Check for cycles in the implements graph
|
|
658
|
+
try:
|
|
659
|
+
TopologicalSorter(parents_by_child).static_order()
|
|
660
|
+
except CycleError as e:
|
|
661
|
+
raise CycleError(f"Failed to deploy views. This likely due to a cycle in implements. {e.args[1]}") from None
|
|
662
|
+
|
|
663
|
+
dependencies_by_id: dict[ViewId, set[ViewId]] = defaultdict(set)
|
|
664
|
+
for view_id, view in views_by_id.items():
|
|
665
|
+
dependencies_by_id[view_id].update([parent for parent in view.implements or [] if parent in views_by_id])
|
|
666
|
+
for properties in (view.properties or {}).values():
|
|
667
|
+
if isinstance(properties, ReverseDirectRelationApply):
|
|
668
|
+
if isinstance(properties.through.source, ViewId) and properties.through.source in views_by_id:
|
|
669
|
+
dependencies_by_id[view_id].add(properties.through.source)
|
|
670
|
+
|
|
671
|
+
created = ViewList([])
|
|
672
|
+
for strongly_connected in tarjan(dependencies_by_id):
|
|
673
|
+
to_create = [views_by_id[view_id] for view_id in strongly_connected]
|
|
674
|
+
try:
|
|
675
|
+
created_set = self._client.data_modeling.views.apply(to_create)
|
|
676
|
+
except CogniteAPIError:
|
|
677
|
+
self._client.data_modeling.views.delete(created.as_ids())
|
|
678
|
+
raise original_error from None
|
|
679
|
+
created.extend(created_set)
|
|
680
|
+
return created
|
|
681
|
+
|
|
682
|
+
@staticmethod
|
|
683
|
+
def _is_auto_retryable(e: CogniteAPIError) -> bool:
|
|
684
|
+
return isinstance(e.extra, dict) and "isAutoRetryable" in e.extra and e.extra["isAutoRetryable"]
|
|
685
|
+
|
|
686
|
+
@staticmethod
|
|
687
|
+
def _is_false_not_exists(e: CogniteAPIError, request_views: set[ViewId]) -> bool:
|
|
688
|
+
"""This is a bug in the API where it returns a 400 status complaining that a views does not exist,
|
|
689
|
+
even though they are part of the request. This bug is reported to Cognite. The workaround is to do a
|
|
690
|
+
topological sort of the strongly connected views and retry the request.
|
|
691
|
+
"""
|
|
692
|
+
if "not exist" not in e.message and 400 <= e.code < 500:
|
|
693
|
+
return False
|
|
694
|
+
results = re.findall(r"'([a-zA-Z0-9_-]+):([a-zA-Z0-9_]+)/([.a-zA-Z0-9_-]+)'", e.message)
|
|
695
|
+
if not results:
|
|
696
|
+
# No view references in the message
|
|
697
|
+
return False
|
|
698
|
+
error_message_views = {ViewId(*result) for result in results}
|
|
699
|
+
return error_message_views.issubset(request_views)
|
|
700
|
+
|
|
623
701
|
@overload
|
|
624
702
|
def retrieve(
|
|
625
703
|
self,
|
|
@@ -355,6 +355,7 @@ class NeatDataModelStore:
|
|
|
355
355
|
|
|
356
356
|
agent = exporter.agent
|
|
357
357
|
start = datetime.now(timezone.utc)
|
|
358
|
+
result: UploadResultList | Path | URIRef | None = None
|
|
358
359
|
with catch_issues() as issue_list:
|
|
359
360
|
# Validate the type of the result
|
|
360
361
|
result = action(input_, *exporter_args)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
|
|
3
|
+
T = TypeVar("T")
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def tarjan(dependencies_by_id: dict[T, set[T]]) -> list[set[T]]:
|
|
7
|
+
"""Returns the strongly connected components of the dependency graph
|
|
8
|
+
in topological order.
|
|
9
|
+
Args:
|
|
10
|
+
dependencies_by_id: A dictionary where the keys are ids and the values are sets of ids that the key depends on.
|
|
11
|
+
Returns:
|
|
12
|
+
A list of sets of ids that are strongly connected components in the dependency graph.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
stack = []
|
|
16
|
+
stack_set = set()
|
|
17
|
+
index: dict[T, int] = {}
|
|
18
|
+
lowlink = {}
|
|
19
|
+
result = []
|
|
20
|
+
|
|
21
|
+
def visit(v: T) -> None:
|
|
22
|
+
index[v] = len(index)
|
|
23
|
+
lowlink[v] = index[v]
|
|
24
|
+
stack.append(v)
|
|
25
|
+
stack_set.add(v)
|
|
26
|
+
for w in dependencies_by_id.get(v, []):
|
|
27
|
+
if w not in index:
|
|
28
|
+
visit(w)
|
|
29
|
+
lowlink[v] = min(lowlink[w], lowlink[v])
|
|
30
|
+
elif w in stack_set:
|
|
31
|
+
lowlink[v] = min(lowlink[v], index[w])
|
|
32
|
+
if lowlink[v] == index[v]:
|
|
33
|
+
scc = set()
|
|
34
|
+
dependency: T | None = None
|
|
35
|
+
while v != dependency:
|
|
36
|
+
dependency = stack.pop()
|
|
37
|
+
scc.add(dependency)
|
|
38
|
+
stack_set.remove(dependency)
|
|
39
|
+
result.append(scc)
|
|
40
|
+
|
|
41
|
+
for view_id in dependencies_by_id.keys():
|
|
42
|
+
if view_id not in index:
|
|
43
|
+
visit(view_id)
|
|
44
|
+
return result
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
cognite/neat/__init__.py,sha256=12StS1dzH9_MElqxGvLWrNsxCJl9Hv8A2a9D0E5OD_U,193
|
|
2
|
-
cognite/neat/_version.py,sha256=
|
|
2
|
+
cognite/neat/_version.py,sha256=qkkjBeBYYbUi5FDXyPZvuIZ1C3jviVckqO158UiM9mA,46
|
|
3
3
|
cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
cognite/neat/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
cognite/neat/core/_config.py,sha256=WT1BS8uADcFvGoUYOOfwFOVq_VBl472TisdoA3wLick,280
|
|
@@ -9,7 +9,7 @@ cognite/neat/core/_client/__init__.py,sha256=RQ7MwL8mwGqGHokRzsPqO3XStDzmI4-c12_
|
|
|
9
9
|
cognite/neat/core/_client/_api_client.py,sha256=CqgG4kEArI9jiKAh82YrRZv_SzeMKA5guIZOvDg2R5I,860
|
|
10
10
|
cognite/neat/core/_client/testing.py,sha256=rZGd-TFwNtfUqT8LV0u3FT0kHwNrjnvDNZU_Mcd5yx4,1329
|
|
11
11
|
cognite/neat/core/_client/_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
cognite/neat/core/_client/_api/data_modeling_loaders.py,sha256=
|
|
12
|
+
cognite/neat/core/_client/_api/data_modeling_loaders.py,sha256=6ZiL_btoqMlJF2TtW9q2y8zEWds7QWLn4YlFH72v1Ew,43794
|
|
13
13
|
cognite/neat/core/_client/_api/neat_instances.py,sha256=8HcQO1sp8zjXdnRBRQ4yeQzt1O906HNSrDDCgrgTe8A,4805
|
|
14
14
|
cognite/neat/core/_client/_api/schema.py,sha256=lbA8Cka_7K_RjmaxdeqkVkIwKPfWeDvpYvvEOGI07xo,6967
|
|
15
15
|
cognite/neat/core/_client/_api/statistics.py,sha256=M0JpCHD6WMfggoe-HyXfeigwRCvZJjVF-xNB9CgB4UE,3796
|
|
@@ -136,7 +136,7 @@ cognite/neat/core/_issues/warnings/_properties.py,sha256=h7jGO6wfjAd1-T9u-BByUf7
|
|
|
136
136
|
cognite/neat/core/_issues/warnings/_resources.py,sha256=_iPRq0pRMmRu3LFjqZTaG3OqOzw4f8-Vc9G4Im__FHc,3578
|
|
137
137
|
cognite/neat/core/_issues/warnings/user_modeling.py,sha256=Qn_S8TLw7MMYQaJcZBScJA48kz_PrTWz0NaepSR70Fk,4144
|
|
138
138
|
cognite/neat/core/_store/__init__.py,sha256=wpsF8xjIQ5V21NOh45XQV813n_EzgyPOt0VVinYjnDI,140
|
|
139
|
-
cognite/neat/core/_store/_data_model.py,sha256=
|
|
139
|
+
cognite/neat/core/_store/_data_model.py,sha256=6Eg_QH1ZlS4AO3c0_Ye8M-Y4_Ycf3mQPJBKyardGu9w,19646
|
|
140
140
|
cognite/neat/core/_store/_instance.py,sha256=QFrhOyaVA-mOtRaKZDE0aYyEJlSYx1jImKC8iE3b75E,17406
|
|
141
141
|
cognite/neat/core/_store/_provenance.py,sha256=aMEsq27dZ4NZ6XEC8hA0fIDF13i1ZP3QwnclLCRaNk4,7326
|
|
142
142
|
cognite/neat/core/_store/exceptions.py,sha256=jcd1Gv65mfTdC4cipFAMWUNghEmdLS_lwPH1FB_ebxI,1656
|
|
@@ -148,6 +148,7 @@ cognite/neat/core/_utils/graph_transformations_report.py,sha256=ORVH7lw357TPOq4e
|
|
|
148
148
|
cognite/neat/core/_utils/io_.py,sha256=D2Mg8sOxfBoDg3fC0jBzaxO3vkXmr0QvZSgYIv6xRkM,386
|
|
149
149
|
cognite/neat/core/_utils/rdf_.py,sha256=8AALp8H_nXEDSBo6jZ1idyT_x3K4PJT5ZyBEyxPmgxI,10403
|
|
150
150
|
cognite/neat/core/_utils/spreadsheet.py,sha256=MMI_1zxeHEf9Ggu_-t_ryjj54ky085QIf8eArt5hXEY,5749
|
|
151
|
+
cognite/neat/core/_utils/tarjan.py,sha256=IZvwaIITryGVNbo9Bv5EA9_sW3DyfUNAe7uYyPOCL0g,1357
|
|
151
152
|
cognite/neat/core/_utils/text.py,sha256=ON4ihfscFJkQqQ-Rj46XXtf-9tAobwXbbfa3wuekSu4,8519
|
|
152
153
|
cognite/neat/core/_utils/time_.py,sha256=7ayUm0OWZm1JDmy32E4ip8WRr2o0GLwrHwJA8sJ43Z4,357
|
|
153
154
|
cognite/neat/core/_utils/upload.py,sha256=yR-BvvrWPh0XHoIGByXMEVi3JONzmc5xwXbmEDBdA8U,5860
|
|
@@ -185,7 +186,7 @@ cognite/neat/session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4dvc
|
|
|
185
186
|
cognite/neat/session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
|
|
186
187
|
cognite/neat/session/engine/_interface.py,sha256=3W-cYr493c_mW3P5O6MKN1xEQg3cA7NHR_ev3zdF9Vk,533
|
|
187
188
|
cognite/neat/session/engine/_load.py,sha256=g52uYakQM03VqHt_RDHtpHso1-mFFifH5M4T2ScuH8A,5198
|
|
188
|
-
cognite_neat-0.123.
|
|
189
|
-
cognite_neat-0.123.
|
|
190
|
-
cognite_neat-0.123.
|
|
191
|
-
cognite_neat-0.123.
|
|
189
|
+
cognite_neat-0.123.7.dist-info/METADATA,sha256=qLpEiC8HWa4kFXIfty5_vNMFWfJ89CybIzFVA0FMxD0,9171
|
|
190
|
+
cognite_neat-0.123.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
191
|
+
cognite_neat-0.123.7.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
|
|
192
|
+
cognite_neat-0.123.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|