cognite-neat 0.78.1__py3-none-any.whl → 0.78.3__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.
- cognite/neat/_version.py +1 -1
- cognite/neat/rules/exporters/_rules2ontology.py +10 -6
- cognite/neat/rules/importers/_inference2rules.py +52 -56
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +3 -6
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +3 -8
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +2 -4
- cognite/neat/rules/importers/_owl2rules/_owl2rules.py +12 -17
- cognite/neat/rules/models/entities.py +71 -6
- cognite/neat/rules/models/information/_rules.py +8 -1
- cognite/neat/rules/models/information/_rules_input.py +15 -3
- cognite/neat/workflows/steps/lib/current/rules_importer.py +2 -23
- {cognite_neat-0.78.1.dist-info → cognite_neat-0.78.3.dist-info}/METADATA +1 -1
- {cognite_neat-0.78.1.dist-info → cognite_neat-0.78.3.dist-info}/RECORD +16 -16
- {cognite_neat-0.78.1.dist-info → cognite_neat-0.78.3.dist-info}/LICENSE +0 -0
- {cognite_neat-0.78.1.dist-info → cognite_neat-0.78.3.dist-info}/WHEEL +0 -0
- {cognite_neat-0.78.1.dist-info → cognite_neat-0.78.3.dist-info}/entry_points.txt +0 -0
cognite/neat/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.78.
|
|
1
|
+
__version__ = "0.78.3"
|
|
@@ -331,7 +331,7 @@ class OWLProperty(OntologyModel):
|
|
|
331
331
|
elif isinstance(definition.value_type, ClassEntity):
|
|
332
332
|
owl_property.range_.add(namespace[str(definition.value_type.suffix)])
|
|
333
333
|
else:
|
|
334
|
-
raise ValueError(f"Value type {definition.value_type} is not supported")
|
|
334
|
+
raise ValueError(f"Value type {definition.value_type.type_} is not supported")
|
|
335
335
|
owl_property.domain.add(namespace[str(definition.class_.suffix)])
|
|
336
336
|
owl_property.label.add(definition.name or definition.property_)
|
|
337
337
|
if definition.description:
|
|
@@ -549,15 +549,19 @@ class SHACLPropertyShape(OntologyModel):
|
|
|
549
549
|
|
|
550
550
|
@classmethod
|
|
551
551
|
def from_property(cls, definition: InformationProperty, namespace: Namespace) -> "SHACLPropertyShape":
|
|
552
|
+
# TODO requires PR to fix MultiValueType and UnknownValueType
|
|
553
|
+
if isinstance(definition.value_type, ClassEntity):
|
|
554
|
+
expected_value_type = namespace[f"{definition.value_type.suffix}Shape"]
|
|
555
|
+
elif isinstance(definition.value_type, DataType):
|
|
556
|
+
expected_value_type = XSD[definition.value_type.xsd]
|
|
557
|
+
else:
|
|
558
|
+
raise ValueError(f"Value type {definition.value_type.type_} is not supported")
|
|
559
|
+
|
|
552
560
|
return cls(
|
|
553
561
|
id_=BNode(),
|
|
554
562
|
path=namespace[definition.property_],
|
|
555
563
|
node_kind=SHACL.IRI if definition.type_ == EntityTypes.object_property else SHACL.Literal,
|
|
556
|
-
expected_value_type=
|
|
557
|
-
namespace[f"{definition.value_type.suffix}Shape"]
|
|
558
|
-
if isinstance(definition.value_type, ClassEntity)
|
|
559
|
-
else XSD[definition.value_type.xsd]
|
|
560
|
-
),
|
|
564
|
+
expected_value_type=expected_value_type,
|
|
561
565
|
min_count=definition.min_count,
|
|
562
566
|
max_count=(
|
|
563
567
|
int(definition.max_count) if definition.max_count and definition.max_count != float("inf") else None
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from datetime import datetime
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Literal, cast, overload
|
|
@@ -12,12 +13,11 @@ from cognite.neat.rules.importers._base import BaseImporter, Rules, _handle_issu
|
|
|
12
13
|
from cognite.neat.rules.issues import IssueList
|
|
13
14
|
from cognite.neat.rules.models import InformationRules, RoleTypes
|
|
14
15
|
from cognite.neat.rules.models._base import MatchType
|
|
15
|
-
from cognite.neat.rules.models.entities import ClassEntity
|
|
16
16
|
from cognite.neat.rules.models.information import (
|
|
17
17
|
InformationMetadata,
|
|
18
18
|
InformationRulesInput,
|
|
19
19
|
)
|
|
20
|
-
from cognite.neat.utils.utils import get_namespace, remove_namespace
|
|
20
|
+
from cognite.neat.utils.utils import get_namespace, remove_namespace
|
|
21
21
|
|
|
22
22
|
ORDERED_CLASSES_QUERY = """SELECT ?class (count(?s) as ?instances )
|
|
23
23
|
WHERE { ?s a ?class . }
|
|
@@ -39,29 +39,21 @@ class InferenceImporter(BaseImporter):
|
|
|
39
39
|
issue_list: Issue list to store issues
|
|
40
40
|
graph: Knowledge graph
|
|
41
41
|
max_number_of_instance: Maximum number of instances to be used in inference
|
|
42
|
-
make_compliant: If True, NEAT will attempt to make the imported rules compliant with CDF
|
|
43
42
|
"""
|
|
44
43
|
|
|
45
|
-
def __init__(
|
|
46
|
-
self, issue_list: IssueList, graph: Graph, make_compliant: bool = False, max_number_of_instance: int = -1
|
|
47
|
-
):
|
|
44
|
+
def __init__(self, issue_list: IssueList, graph: Graph, max_number_of_instance: int = -1):
|
|
48
45
|
self.issue_list = issue_list
|
|
49
46
|
self.graph = graph
|
|
50
47
|
self.max_number_of_instance = max_number_of_instance
|
|
51
|
-
self.make_compliant = make_compliant
|
|
52
48
|
|
|
53
49
|
@classmethod
|
|
54
|
-
def from_graph_store(
|
|
55
|
-
cls, store: NeatGraphStoreBase, make_compliant: bool = False, max_number_of_instance: int = -1
|
|
56
|
-
):
|
|
50
|
+
def from_graph_store(cls, store: NeatGraphStoreBase, max_number_of_instance: int = -1):
|
|
57
51
|
issue_list = IssueList(title="Inferred from graph store")
|
|
58
52
|
|
|
59
|
-
return cls(
|
|
60
|
-
issue_list, store.graph, make_compliant=make_compliant, max_number_of_instance=max_number_of_instance
|
|
61
|
-
)
|
|
53
|
+
return cls(issue_list, store.graph, max_number_of_instance=max_number_of_instance)
|
|
62
54
|
|
|
63
55
|
@classmethod
|
|
64
|
-
def from_rdf_file(cls, filepath: Path,
|
|
56
|
+
def from_rdf_file(cls, filepath: Path, max_number_of_instance: int = -1):
|
|
65
57
|
issue_list = IssueList(title=f"'{filepath.name}'")
|
|
66
58
|
|
|
67
59
|
graph = Graph()
|
|
@@ -70,18 +62,18 @@ class InferenceImporter(BaseImporter):
|
|
|
70
62
|
except Exception:
|
|
71
63
|
issue_list.append(issues.fileread.FileReadError(filepath))
|
|
72
64
|
|
|
73
|
-
return cls(issue_list, graph,
|
|
65
|
+
return cls(issue_list, graph, max_number_of_instance=max_number_of_instance)
|
|
74
66
|
|
|
75
67
|
@classmethod
|
|
76
|
-
def from_json_file(cls, filepath: Path,
|
|
68
|
+
def from_json_file(cls, filepath: Path, max_number_of_instance: int = -1):
|
|
77
69
|
raise NotImplementedError("JSON file format is not supported yet.")
|
|
78
70
|
|
|
79
71
|
@classmethod
|
|
80
|
-
def from_yaml_file(cls, filepath: Path,
|
|
72
|
+
def from_yaml_file(cls, filepath: Path, max_number_of_instance: int = -1):
|
|
81
73
|
raise NotImplementedError("YAML file format is not supported yet.")
|
|
82
74
|
|
|
83
75
|
@classmethod
|
|
84
|
-
def from_xml_file(cls, filepath: Path,
|
|
76
|
+
def from_xml_file(cls, filepath: Path, max_number_of_instance: int = -1):
|
|
85
77
|
raise NotImplementedError("JSON file format is not supported yet.")
|
|
86
78
|
|
|
87
79
|
@overload
|
|
@@ -117,9 +109,6 @@ class InferenceImporter(BaseImporter):
|
|
|
117
109
|
if future.result == "failure" or self.issue_list.has_errors:
|
|
118
110
|
return self._return_or_raise(self.issue_list, errors)
|
|
119
111
|
|
|
120
|
-
if self.make_compliant and rules:
|
|
121
|
-
self._make_dms_compliant_rules(rules)
|
|
122
|
-
|
|
123
112
|
return self._to_output(
|
|
124
113
|
rules,
|
|
125
114
|
self.issue_list,
|
|
@@ -155,7 +144,7 @@ class InferenceImporter(BaseImporter):
|
|
|
155
144
|
"class_": class_id,
|
|
156
145
|
"reference": class_uri,
|
|
157
146
|
"match_type": MatchType.exact,
|
|
158
|
-
"comment": f"Inferred from knowledge graph, where this class has {no_instances} instances",
|
|
147
|
+
"comment": f"Inferred from knowledge graph, where this class has <{no_instances}> instances",
|
|
159
148
|
}
|
|
160
149
|
|
|
161
150
|
# Infers all the properties of the class
|
|
@@ -179,7 +168,7 @@ class InferenceImporter(BaseImporter):
|
|
|
179
168
|
|
|
180
169
|
self._add_uri_namespace_to_prefixes(cast(URIRef, value_type_uri), prefixes)
|
|
181
170
|
value_type_id = remove_namespace(value_type_uri)
|
|
182
|
-
id_ = f"{class_id}:{property_id}
|
|
171
|
+
id_ = f"{class_id}:{property_id}"
|
|
183
172
|
|
|
184
173
|
definition = {
|
|
185
174
|
"class_": class_id,
|
|
@@ -187,15 +176,41 @@ class InferenceImporter(BaseImporter):
|
|
|
187
176
|
"max_count": cast(RdfLiteral, occurrence).value,
|
|
188
177
|
"value_type": value_type_id,
|
|
189
178
|
"reference": property_uri,
|
|
179
|
+
"comment": (
|
|
180
|
+
f"Class <{class_id}> has property <{property_id}> with "
|
|
181
|
+
f"value type <{value_type_id}> which occurs <1> times in the graph"
|
|
182
|
+
),
|
|
190
183
|
}
|
|
191
184
|
|
|
192
185
|
# USE CASE 1: If property is not present in properties
|
|
193
186
|
if id_ not in properties:
|
|
194
187
|
properties[id_] = definition
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
|
|
189
|
+
# USE CASE 2: first time redefinition, value type change to multi
|
|
190
|
+
elif id_ in properties and definition["value_type"] not in properties[id_]["value_type"]:
|
|
191
|
+
properties[id_]["value_type"] = properties[id_]["value_type"] + " | " + definition["value_type"]
|
|
192
|
+
properties[id_]["comment"] = (
|
|
193
|
+
properties[id_]["comment"] + ", with" + definition["comment"].split("with")[1]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# USE CASE 3: existing but max count is different
|
|
197
|
+
elif (
|
|
198
|
+
id_ in properties
|
|
199
|
+
and definition["value_type"] in properties[id_]["value_type"]
|
|
200
|
+
and not (properties[id_]["max_count"] == definition["max_count"])
|
|
201
|
+
):
|
|
197
202
|
properties[id_]["max_count"] = max(properties[id_]["max_count"], definition["max_count"])
|
|
198
203
|
|
|
204
|
+
properties[id_]["comment"] = self._update_value_type_occurrence_in_comment(
|
|
205
|
+
definition["value_type"], properties[id_]["comment"]
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# USE CASE 4: Just update the comment with occurrence
|
|
209
|
+
else:
|
|
210
|
+
properties[id_]["comment"] = self._update_value_type_occurrence_in_comment(
|
|
211
|
+
definition["value_type"], properties[id_]["comment"]
|
|
212
|
+
)
|
|
213
|
+
|
|
199
214
|
return {
|
|
200
215
|
"metadata": self._default_metadata().model_dump(),
|
|
201
216
|
"classes": list(classes.values()),
|
|
@@ -228,36 +243,17 @@ class InferenceImporter(BaseImporter):
|
|
|
228
243
|
)
|
|
229
244
|
|
|
230
245
|
@classmethod
|
|
231
|
-
def
|
|
232
|
-
cls.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
seen = set()
|
|
238
|
-
for i, property_ in enumerate(rules.properties.data):
|
|
239
|
-
prop_id = f"{property_.class_}.{property_.property_}"
|
|
240
|
-
if prop_id in seen:
|
|
241
|
-
property_.property_ = f"{property_.property_}_{i+1}"
|
|
242
|
-
seen.add(f"{property_.class_}.{property_.property_}")
|
|
243
|
-
else:
|
|
244
|
-
seen.add(prop_id)
|
|
246
|
+
def _update_value_type_occurrence_in_comment(cls, value_type: str, comment: str) -> str:
|
|
247
|
+
occurrence = cls._read_value_type_occurrence_from_comment(value_type, comment)
|
|
248
|
+
return comment.replace(
|
|
249
|
+
f"with value type <{value_type}> which occurs <{occurrence}> times in the graph",
|
|
250
|
+
f"with value type <{value_type}> which occurs <{occurrence+1}> times in the graph",
|
|
251
|
+
)
|
|
245
252
|
|
|
246
253
|
@classmethod
|
|
247
|
-
def
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
# Fixing property definitions
|
|
254
|
-
for property_ in rules.properties:
|
|
255
|
-
# fix class id
|
|
256
|
-
property_.class_ = property_.class_.as_dms_compliant_entity()
|
|
257
|
-
|
|
258
|
-
# fix property id
|
|
259
|
-
property_.property_ = replace_non_alphanumeric_with_underscore(property_.property_)
|
|
260
|
-
|
|
261
|
-
# fix value type
|
|
262
|
-
if isinstance(property_.value_type, ClassEntity):
|
|
263
|
-
property_.value_type = property_.value_type.as_dms_compliant_entity()
|
|
254
|
+
def _read_value_type_occurrence_from_comment(cls, value_type: str, comment: str) -> int:
|
|
255
|
+
return int(
|
|
256
|
+
cast(
|
|
257
|
+
re.Match, re.search(rf"with value type <{value_type}> which occurs <(\d+)> times in the graph", comment)
|
|
258
|
+
).group(1)
|
|
259
|
+
)
|
|
@@ -8,19 +8,17 @@ from cognite.neat.rules.models._base import MatchType
|
|
|
8
8
|
from cognite.neat.utils.utils import remove_namespace
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def parse_owl_classes(graph: Graph,
|
|
11
|
+
def parse_owl_classes(graph: Graph, language: str = "en") -> list[dict]:
|
|
12
12
|
"""Parse owl classes from graph to pandas dataframe.
|
|
13
13
|
|
|
14
14
|
Args:
|
|
15
15
|
graph: Graph containing owl classes
|
|
16
|
-
make_compliant: Flag for generating compliant classes, by default False
|
|
17
16
|
language: Language to use for parsing, by default "en"
|
|
18
17
|
|
|
19
18
|
Returns:
|
|
20
19
|
Dataframe containing owl classes
|
|
21
20
|
|
|
22
|
-
!!! note "
|
|
23
|
-
If `make_compliant` is set to True, in presence of errors, default values will be used instead.
|
|
21
|
+
!!! note "Compliant OWL classes"
|
|
24
22
|
This makes the method very opinionated, but results in a compliant classes.
|
|
25
23
|
"""
|
|
26
24
|
|
|
@@ -48,8 +46,7 @@ def parse_owl_classes(graph: Graph, make_compliant: bool = False, language: str
|
|
|
48
46
|
processed_df = _clean_up_classes(raw_df)
|
|
49
47
|
|
|
50
48
|
# make compliant
|
|
51
|
-
|
|
52
|
-
processed_df = make_classes_compliant(processed_df)
|
|
49
|
+
processed_df = make_classes_compliant(processed_df)
|
|
53
50
|
|
|
54
51
|
# Make Parent Class list elements into string joined with comma
|
|
55
52
|
processed_df["Parent Class"] = processed_df["Parent Class"].apply(
|
|
@@ -12,18 +12,16 @@ from cognite.neat.rules.models._types._base import (
|
|
|
12
12
|
from cognite.neat.utils.utils import convert_rdflib_content, remove_none_elements_from_set
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def parse_owl_metadata(graph: Graph
|
|
15
|
+
def parse_owl_metadata(graph: Graph) -> dict:
|
|
16
16
|
"""Parse owl metadata from graph to dict.
|
|
17
17
|
|
|
18
18
|
Args:
|
|
19
19
|
graph: Graph containing owl metadata
|
|
20
|
-
make_compliant: Flag for generating compliant metadata, by default False
|
|
21
20
|
|
|
22
21
|
Returns:
|
|
23
22
|
Dictionary containing owl metadata
|
|
24
23
|
|
|
25
|
-
!!! note "
|
|
26
|
-
If `make_compliant` is set to True, in presence of errors, default values will be used instead.
|
|
24
|
+
!!! note "Compliant OWL metadata"
|
|
27
25
|
This makes the method very opinionated, but results in a compliant metadata.
|
|
28
26
|
|
|
29
27
|
|
|
@@ -72,10 +70,7 @@ def parse_owl_metadata(graph: Graph, make_compliant: bool = False) -> dict:
|
|
|
72
70
|
}
|
|
73
71
|
)
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
return make_metadata_compliant(raw_metadata)
|
|
77
|
-
|
|
78
|
-
return raw_metadata
|
|
73
|
+
return make_metadata_compliant(raw_metadata)
|
|
79
74
|
|
|
80
75
|
|
|
81
76
|
def make_metadata_compliant(metadata: dict) -> dict:
|
|
@@ -10,12 +10,11 @@ from cognite.neat.utils.utils import remove_namespace
|
|
|
10
10
|
from ._owl2classes import _data_type_property_class, _object_property_class, _thing_class
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def parse_owl_properties(graph: Graph,
|
|
13
|
+
def parse_owl_properties(graph: Graph, language: str = "en") -> list[dict]:
|
|
14
14
|
"""Parse owl properties from graph to pandas dataframe.
|
|
15
15
|
|
|
16
16
|
Args:
|
|
17
17
|
graph: Graph containing owl properties
|
|
18
|
-
make_compliant: Flag for generating compliant properties, by default False
|
|
19
18
|
language: Language to use for parsing, by default "en"
|
|
20
19
|
|
|
21
20
|
Returns:
|
|
@@ -53,8 +52,7 @@ def parse_owl_properties(graph: Graph, make_compliant: bool = False, language: s
|
|
|
53
52
|
processed_df = _clean_up_properties(raw_df)
|
|
54
53
|
|
|
55
54
|
# make compliant
|
|
56
|
-
|
|
57
|
-
processed_df = make_properties_compliant(processed_df)
|
|
55
|
+
processed_df = make_properties_compliant(processed_df)
|
|
58
56
|
|
|
59
57
|
# drop column _property_type, which was a helper column:
|
|
60
58
|
processed_df.drop(columns=["_property_type"], inplace=True)
|
|
@@ -21,25 +21,21 @@ class OWLImporter(BaseImporter):
|
|
|
21
21
|
|
|
22
22
|
Args:
|
|
23
23
|
filepath: Path to OWL ontology
|
|
24
|
-
make_compliant: If True, NEAT will attempt to make the imported rules compliant with CDF
|
|
25
24
|
|
|
26
25
|
!!! Note
|
|
27
|
-
OWL Ontologies
|
|
28
|
-
data model
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
OWL Ontologies are information models which completeness varies. As such, constructing functional
|
|
27
|
+
data model directly will often be impossible, therefore the produced Rules object will be ill formed.
|
|
28
|
+
To avoid this, neat will automatically attempt to make the imported rules compliant by adding default
|
|
29
|
+
values for missing information, attaching dangling properties to default containers based on the
|
|
30
|
+
property type, etc.
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
properties to default containers based on the property type, etc. One has to be aware
|
|
35
|
-
that NEAT will be opinionated about how to make the ontology compliant, and that the resulting
|
|
36
|
-
rules may not be what you expect.
|
|
32
|
+
One has to be aware that NEAT will be opinionated about how to make the ontology
|
|
33
|
+
compliant, and that the resulting rules may not be what you expect.
|
|
37
34
|
|
|
38
35
|
"""
|
|
39
36
|
|
|
40
|
-
def __init__(self, filepath: Path
|
|
37
|
+
def __init__(self, filepath: Path):
|
|
41
38
|
self.owl_filepath = filepath
|
|
42
|
-
self.make_compliant = make_compliant
|
|
43
39
|
|
|
44
40
|
@overload
|
|
45
41
|
def to_rules(self, errors: Literal["raise"], role: RoleTypes | None = None) -> Rules: ...
|
|
@@ -67,13 +63,12 @@ class OWLImporter(BaseImporter):
|
|
|
67
63
|
graph.bind("skos", SKOS)
|
|
68
64
|
|
|
69
65
|
components = {
|
|
70
|
-
"Metadata": parse_owl_metadata(graph
|
|
71
|
-
"Classes": parse_owl_classes(graph
|
|
72
|
-
"Properties": parse_owl_properties(graph
|
|
66
|
+
"Metadata": parse_owl_metadata(graph),
|
|
67
|
+
"Classes": parse_owl_classes(graph),
|
|
68
|
+
"Properties": parse_owl_properties(graph),
|
|
73
69
|
}
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
components = make_components_compliant(components)
|
|
71
|
+
components = make_components_compliant(components)
|
|
77
72
|
|
|
78
73
|
rules = InformationRules.model_validate(components)
|
|
79
74
|
return self._to_output(rules, IssueList(), errors, role)
|
|
@@ -5,8 +5,17 @@ from functools import total_ordering
|
|
|
5
5
|
from typing import Annotated, Any, ClassVar, Generic, TypeVar, cast
|
|
6
6
|
|
|
7
7
|
from cognite.client.data_classes.data_modeling.ids import ContainerId, DataModelId, NodeId, PropertyId, ViewId
|
|
8
|
-
from pydantic import
|
|
8
|
+
from pydantic import (
|
|
9
|
+
AnyHttpUrl,
|
|
10
|
+
BaseModel,
|
|
11
|
+
BeforeValidator,
|
|
12
|
+
Field,
|
|
13
|
+
PlainSerializer,
|
|
14
|
+
model_serializer,
|
|
15
|
+
model_validator,
|
|
16
|
+
)
|
|
9
17
|
|
|
18
|
+
from cognite.neat.rules.models.data_types import DataType
|
|
10
19
|
from cognite.neat.utils.utils import replace_non_alphanumeric_with_underscore
|
|
11
20
|
|
|
12
21
|
if sys.version_info >= (3, 11):
|
|
@@ -38,6 +47,7 @@ class EntityTypes(StrEnum):
|
|
|
38
47
|
container = "container"
|
|
39
48
|
datamodel = "datamodel"
|
|
40
49
|
undefined = "undefined"
|
|
50
|
+
multi_value_type = "multi_value_type"
|
|
41
51
|
|
|
42
52
|
|
|
43
53
|
# ALLOWED
|
|
@@ -58,6 +68,7 @@ _CLASS_ID_REGEX_COMPILED = re.compile(rf"^{_CLASS_ID_REGEX}$")
|
|
|
58
68
|
_PROPERTY_ID_REGEX = rf"\((?P<{EntityTypes.property_}>{_ENTITY_ID_REGEX})\)"
|
|
59
69
|
|
|
60
70
|
_ENTITY_PATTERN = re.compile(r"^(?P<prefix>.*?):?(?P<suffix>[^(:]*)(\((?P<content>[^)]+)\))?$")
|
|
71
|
+
_MULTI_VALUE_TYPE_PATTERN = re.compile(r"^(?P<types>.*?)(\((?P<content>[^)]+)\))?$")
|
|
61
72
|
|
|
62
73
|
|
|
63
74
|
class _UndefinedType(BaseModel): ...
|
|
@@ -238,11 +249,6 @@ class ClassEntity(Entity):
|
|
|
238
249
|
space = default_space if isinstance(self.prefix, _UndefinedType) else self.prefix
|
|
239
250
|
return ContainerEntity(space=space, externalId=str(self.suffix))
|
|
240
251
|
|
|
241
|
-
def as_dms_compliant_entity(self) -> "Self":
|
|
242
|
-
new_entity = self.model_copy(deep=True)
|
|
243
|
-
new_entity.suffix = replace_non_alphanumeric_with_underscore(new_entity.suffix)
|
|
244
|
-
return new_entity
|
|
245
|
-
|
|
246
252
|
|
|
247
253
|
class ParentClassEntity(ClassEntity):
|
|
248
254
|
type_: ClassVar[EntityTypes] = EntityTypes.parent_class
|
|
@@ -264,6 +270,60 @@ class UnknownEntity(ClassEntity):
|
|
|
264
270
|
T_ID = TypeVar("T_ID", bound=ContainerId | ViewId | DataModelId | PropertyId | NodeId | None)
|
|
265
271
|
|
|
266
272
|
|
|
273
|
+
class MultiValueTypeInfo(BaseModel):
|
|
274
|
+
type_: ClassVar[EntityTypes] = EntityTypes.multi_value_type
|
|
275
|
+
types: list[DataType | ClassEntity]
|
|
276
|
+
|
|
277
|
+
def __str__(self) -> str:
|
|
278
|
+
return " | ".join([str(t) for t in self.types])
|
|
279
|
+
|
|
280
|
+
@model_serializer(when_used="unless-none", return_type=str)
|
|
281
|
+
def as_str(self) -> str:
|
|
282
|
+
return str(self)
|
|
283
|
+
|
|
284
|
+
@classmethod
|
|
285
|
+
def load(cls, data: Any) -> "MultiValueTypeInfo":
|
|
286
|
+
# already instance of MultiValueTypeInfo
|
|
287
|
+
if isinstance(data, cls):
|
|
288
|
+
return data
|
|
289
|
+
|
|
290
|
+
# it is a raw string that needs to be parsed
|
|
291
|
+
elif isinstance(data, str):
|
|
292
|
+
return cls.model_validate({_PARSE: data})
|
|
293
|
+
|
|
294
|
+
# it is dict that needs to be parsed
|
|
295
|
+
else:
|
|
296
|
+
return cls.model_validate(data)
|
|
297
|
+
|
|
298
|
+
@model_validator(mode="before")
|
|
299
|
+
def _load(cls, data: Any) -> "dict | MultiValueTypeInfo":
|
|
300
|
+
if isinstance(data, dict) and _PARSE in data:
|
|
301
|
+
data = data[_PARSE]
|
|
302
|
+
elif isinstance(data, dict):
|
|
303
|
+
return data
|
|
304
|
+
else:
|
|
305
|
+
raise ValueError(f"Cannot load {cls.__name__} from {data}")
|
|
306
|
+
|
|
307
|
+
result = cls._parse(data)
|
|
308
|
+
return result
|
|
309
|
+
|
|
310
|
+
@classmethod
|
|
311
|
+
def _parse(cls, raw: str) -> dict:
|
|
312
|
+
if not (types := [type_.strip() for type_ in raw.split("|")]):
|
|
313
|
+
return {"types": [UnknownEntity()]}
|
|
314
|
+
else:
|
|
315
|
+
return {
|
|
316
|
+
"types": [
|
|
317
|
+
DataType.load(type_) if DataType.is_data_type(type_) else ClassEntity.load(type_) for type_ in types
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
def set_default_prefix(self, prefix: str):
|
|
322
|
+
for type_ in self.types:
|
|
323
|
+
if isinstance(type_, ClassEntity) and type_.prefix is Undefined:
|
|
324
|
+
type_.prefix = prefix
|
|
325
|
+
|
|
326
|
+
|
|
267
327
|
class DMSEntity(Entity, Generic[T_ID], ABC):
|
|
268
328
|
type_: ClassVar[EntityTypes] = EntityTypes.undefined
|
|
269
329
|
prefix: str = Field(alias="space")
|
|
@@ -297,6 +357,11 @@ class DMSEntity(Entity, Generic[T_ID], ABC):
|
|
|
297
357
|
def as_class(self) -> ClassEntity:
|
|
298
358
|
return ClassEntity(prefix=self.space, suffix=self.external_id)
|
|
299
359
|
|
|
360
|
+
def as_dms_compliant_entity(self) -> "Self":
|
|
361
|
+
new_entity = self.model_copy(deep=True)
|
|
362
|
+
new_entity.suffix = replace_non_alphanumeric_with_underscore(new_entity.suffix)
|
|
363
|
+
return new_entity
|
|
364
|
+
|
|
300
365
|
|
|
301
366
|
T_DMSEntity = TypeVar("T_DMSEntity", bound=DMSEntity)
|
|
302
367
|
|
|
@@ -44,6 +44,7 @@ from cognite.neat.rules.models.domain import DomainRules
|
|
|
44
44
|
from cognite.neat.rules.models.entities import (
|
|
45
45
|
ClassEntity,
|
|
46
46
|
EntityTypes,
|
|
47
|
+
MultiValueTypeInfo,
|
|
47
48
|
ParentClassEntity,
|
|
48
49
|
ParentEntityList,
|
|
49
50
|
ReferenceEntity,
|
|
@@ -162,7 +163,9 @@ class InformationProperty(SheetEntity):
|
|
|
162
163
|
property_: PropertyType = Field(alias="Property")
|
|
163
164
|
name: str | None = Field(alias="Name", default=None)
|
|
164
165
|
description: str | None = Field(alias="Description", default=None)
|
|
165
|
-
value_type: DataType | ClassEntity | UnknownEntity = Field(
|
|
166
|
+
value_type: DataType | ClassEntity | MultiValueTypeInfo | UnknownEntity = Field(
|
|
167
|
+
alias="Value Type", union_mode="left_to_right"
|
|
168
|
+
)
|
|
166
169
|
min_count: int | None = Field(alias="Min Count", default=None)
|
|
167
170
|
max_count: int | float | None = Field(alias="Max Count", default=None)
|
|
168
171
|
default: Any | None = Field(alias="Default", default=None)
|
|
@@ -277,6 +280,10 @@ class InformationRules(BaseRules):
|
|
|
277
280
|
for property_ in self.properties:
|
|
278
281
|
if isinstance(property_.value_type, ClassEntity) and property_.value_type.prefix is Undefined:
|
|
279
282
|
property_.value_type.prefix = self.metadata.prefix
|
|
283
|
+
|
|
284
|
+
if isinstance(property_.value_type, MultiValueTypeInfo):
|
|
285
|
+
property_.value_type.set_default_prefix(self.metadata.prefix)
|
|
286
|
+
|
|
280
287
|
if property_.class_.prefix is Undefined:
|
|
281
288
|
property_.class_.prefix = self.metadata.prefix
|
|
282
289
|
|
|
@@ -7,7 +7,13 @@ from rdflib import Namespace
|
|
|
7
7
|
|
|
8
8
|
from cognite.neat.rules.models._base import DataModelType, ExtensionCategory, SchemaCompleteness, _add_alias
|
|
9
9
|
from cognite.neat.rules.models.data_types import DataType
|
|
10
|
-
from cognite.neat.rules.models.entities import
|
|
10
|
+
from cognite.neat.rules.models.entities import (
|
|
11
|
+
ClassEntity,
|
|
12
|
+
MultiValueTypeInfo,
|
|
13
|
+
ParentClassEntity,
|
|
14
|
+
Unknown,
|
|
15
|
+
UnknownEntity,
|
|
16
|
+
)
|
|
11
17
|
|
|
12
18
|
from ._rules import InformationClass, InformationMetadata, InformationProperty, InformationRules
|
|
13
19
|
|
|
@@ -121,10 +127,15 @@ class InformationPropertyInput:
|
|
|
121
127
|
)
|
|
122
128
|
|
|
123
129
|
def dump(self, default_prefix: str) -> dict[str, Any]:
|
|
124
|
-
value_type: DataType | ClassEntity | UnknownEntity
|
|
130
|
+
value_type: MultiValueTypeInfo | DataType | ClassEntity | UnknownEntity
|
|
125
131
|
|
|
126
132
|
# property holding xsd data type
|
|
127
|
-
if
|
|
133
|
+
# check if it is multi value type
|
|
134
|
+
if "|" in self.value_type:
|
|
135
|
+
value_type = MultiValueTypeInfo.load(self.value_type)
|
|
136
|
+
value_type.set_default_prefix(default_prefix)
|
|
137
|
+
|
|
138
|
+
elif DataType.is_data_type(self.value_type):
|
|
128
139
|
value_type = DataType.load(self.value_type)
|
|
129
140
|
|
|
130
141
|
# unknown value type
|
|
@@ -257,6 +268,7 @@ class InformationRulesInput:
|
|
|
257
268
|
elif isinstance(self.last, InformationRules):
|
|
258
269
|
# We need to load through the InformationRulesInput to set the correct default space and version
|
|
259
270
|
last = InformationRulesInput.load(self.last.model_dump()).dump()
|
|
271
|
+
|
|
260
272
|
return dict(
|
|
261
273
|
Metadata=self.metadata.dump(),
|
|
262
274
|
Properties=[prop.dump(default_prefix) for prop in self.properties],
|
|
@@ -119,15 +119,6 @@ class OntologyToRules(Step):
|
|
|
119
119
|
label="For what role Rules are intended?",
|
|
120
120
|
options=["infer", *RoleTypes.__members__.keys()],
|
|
121
121
|
),
|
|
122
|
-
Configurable(
|
|
123
|
-
name="Make compliant",
|
|
124
|
-
value="True",
|
|
125
|
-
label=(
|
|
126
|
-
"Attempt to make the imported Rules compliant, by converting "
|
|
127
|
-
"the information model provided in the ontology to data model."
|
|
128
|
-
),
|
|
129
|
-
options=["True", "False"],
|
|
130
|
-
),
|
|
131
122
|
]
|
|
132
123
|
|
|
133
124
|
def run(self, flow_message: FlowMessage) -> (FlowMessage, MultiRuleData): # type: ignore[syntax, override]
|
|
@@ -136,7 +127,6 @@ class OntologyToRules(Step):
|
|
|
136
127
|
|
|
137
128
|
file_name = self.configs.get("File name", None)
|
|
138
129
|
full_path = flow_message.payload.get("full_path", None) if flow_message.payload else None
|
|
139
|
-
make_compliant = self.configs.get("Make compliant", "True") == "True"
|
|
140
130
|
|
|
141
131
|
if file_name:
|
|
142
132
|
rules_file_path = self.config.rules_store_path / file_name
|
|
@@ -152,7 +142,7 @@ class OntologyToRules(Step):
|
|
|
152
142
|
if role != "infer" and role is not None:
|
|
153
143
|
role_enum = RoleTypes[role]
|
|
154
144
|
|
|
155
|
-
ontology_importer = importers.OWLImporter(filepath=rules_file_path
|
|
145
|
+
ontology_importer = importers.OWLImporter(filepath=rules_file_path)
|
|
156
146
|
rules, issues = ontology_importer.to_rules(errors="continue", role=role_enum)
|
|
157
147
|
|
|
158
148
|
if rules is None:
|
|
@@ -286,16 +276,6 @@ class RulesInferenceFromRdfFile(Step):
|
|
|
286
276
|
label="For what role Rules are intended?",
|
|
287
277
|
options=["infer", *RoleTypes.__members__.keys()],
|
|
288
278
|
),
|
|
289
|
-
Configurable(
|
|
290
|
-
name="Make compliant",
|
|
291
|
-
value="True",
|
|
292
|
-
label=(
|
|
293
|
-
"Attempt to make the imported Rules compliant, by fixing "
|
|
294
|
-
"redefinition of properties and by making ids of entities compliant with"
|
|
295
|
-
" CDF-allowed set of characters ."
|
|
296
|
-
),
|
|
297
|
-
options=["True", "False"],
|
|
298
|
-
),
|
|
299
279
|
Configurable(
|
|
300
280
|
name="Maximum number of instances to process",
|
|
301
281
|
value="-1",
|
|
@@ -312,7 +292,6 @@ class RulesInferenceFromRdfFile(Step):
|
|
|
312
292
|
|
|
313
293
|
file_path = self.configs.get("File path", None)
|
|
314
294
|
full_path = flow_message.payload.get("full_path", None) if flow_message.payload else None
|
|
315
|
-
make_compliant = self.configs.get("Make compliant", "True") == "True"
|
|
316
295
|
|
|
317
296
|
try:
|
|
318
297
|
max_number_of_instance = int(self.configs.get("Maximum number of instances to process", -1))
|
|
@@ -335,7 +314,7 @@ class RulesInferenceFromRdfFile(Step):
|
|
|
335
314
|
role_enum = RoleTypes[role]
|
|
336
315
|
|
|
337
316
|
inference_importer = importers.InferenceImporter.from_rdf_file(
|
|
338
|
-
rdf_file_path,
|
|
317
|
+
rdf_file_path, max_number_of_instance=max_number_of_instance
|
|
339
318
|
)
|
|
340
319
|
rules, issues = inference_importer.to_rules(errors="continue", role=role_enum)
|
|
341
320
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
cognite/neat/__init__.py,sha256=v-rRiDOgZ3sQSMQKq0vgUQZvpeOkoHFXissAx6Ktg84,61
|
|
2
|
-
cognite/neat/_version.py,sha256=
|
|
2
|
+
cognite/neat/_version.py,sha256=oEHe9RtsScnXCOzPdKOEz68VfnXPGqwRpt9BirfOZTw,23
|
|
3
3
|
cognite/neat/app/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
cognite/neat/app/api/asgi/metrics.py,sha256=nxFy7L5cChTI0a-zkCiJ59Aq8yLuIJp5c9Dg0wRXtV0,152
|
|
5
5
|
cognite/neat/app/api/configuration.py,sha256=2U5M6M252swvQPQyooA1EBzFUZNtcTmuSaywfJDgckM,4232
|
|
@@ -167,7 +167,7 @@ cognite/neat/rules/exporters/_base.py,sha256=m63iw8xjlZbZAxGL8mn7pjGf1pW3rVv8C20
|
|
|
167
167
|
cognite/neat/rules/exporters/_models.py,sha256=vRd0P_YsrZ1eaAGGHfdTeFunaqHdaa0ZtnWiVZBR1nc,1976
|
|
168
168
|
cognite/neat/rules/exporters/_rules2dms.py,sha256=US2IO4YTJSF_CDzau1dTpXyeHntJWVSPkMCQoUoKeC0,13688
|
|
169
169
|
cognite/neat/rules/exporters/_rules2excel.py,sha256=HvUdXYHxfLMijYWdTnfqCsw3Izf8S-XDSve-2ZbqF8Y,14248
|
|
170
|
-
cognite/neat/rules/exporters/_rules2ontology.py,sha256=
|
|
170
|
+
cognite/neat/rules/exporters/_rules2ontology.py,sha256=Od53uLdcC2Q7UiF5PA2P0gw3O14eTD3MeJ1-trd64ZM,20388
|
|
171
171
|
cognite/neat/rules/exporters/_rules2yaml.py,sha256=GA8eUYRxUfIU6IMvlyGO5JidkOD5eUKSbH3qAiFiaCg,3026
|
|
172
172
|
cognite/neat/rules/exporters/_validation.py,sha256=OlKIyf4nhSDehJwFHDQ8Zdf6HpNfW7dSe2s67eywHu4,4078
|
|
173
173
|
cognite/neat/rules/importers/__init__.py,sha256=gR6_TAEa3iO5NCLKRztHg-FMiLdBnx47Z3iSzbwLfcE,481
|
|
@@ -178,12 +178,12 @@ cognite/neat/rules/importers/_dtdl2rules/_unit_lookup.py,sha256=wW4saKva61Q_i17g
|
|
|
178
178
|
cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py,sha256=ysmWUxZ0npwrTB0uiH5jA0v37sfCwowGaYk17IyxPUU,12663
|
|
179
179
|
cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py,sha256=QDyGt5YBaxzF4v_oCFSgKRSpwVdVruDU3-VW0DEiHbY,6718
|
|
180
180
|
cognite/neat/rules/importers/_dtdl2rules/spec.py,sha256=tim_MfN1J0F3Oeqk3BMgIA82d_MZvhRuRMsLK3B4PYc,11897
|
|
181
|
-
cognite/neat/rules/importers/_inference2rules.py,sha256=
|
|
181
|
+
cognite/neat/rules/importers/_inference2rules.py,sha256=JsV3Ii2wmgRELtpV0GC4Y1KtjhyyGR0dtEFpBonHoA8,11213
|
|
182
182
|
cognite/neat/rules/importers/_owl2rules/__init__.py,sha256=tdGcrgtozdQyST-pTlxIa4cLBNTLvtk1nNYR4vOdFSw,63
|
|
183
|
-
cognite/neat/rules/importers/_owl2rules/_owl2classes.py,sha256=
|
|
184
|
-
cognite/neat/rules/importers/_owl2rules/_owl2metadata.py,sha256=
|
|
185
|
-
cognite/neat/rules/importers/_owl2rules/_owl2properties.py,sha256=
|
|
186
|
-
cognite/neat/rules/importers/_owl2rules/_owl2rules.py,sha256=
|
|
183
|
+
cognite/neat/rules/importers/_owl2rules/_owl2classes.py,sha256=QpTxvrTGczIa48X8lgXGnMN1AWPhHK0DR6uNq175xak,7357
|
|
184
|
+
cognite/neat/rules/importers/_owl2rules/_owl2metadata.py,sha256=nwnUaBNAAYMoBre2UmsnkJXUuaqGEpR3U3txDrH2w6g,7527
|
|
185
|
+
cognite/neat/rules/importers/_owl2rules/_owl2properties.py,sha256=eKr-e-ZTTV54PJ9UXNVPTT_c9XxszNPraS4Y43AF7qQ,7297
|
|
186
|
+
cognite/neat/rules/importers/_owl2rules/_owl2rules.py,sha256=41_wZFvt0A6TI55zlT04oQkvU7V73li4aGLgc4T4Lxo,6358
|
|
187
187
|
cognite/neat/rules/importers/_spreadsheet2rules.py,sha256=nKSJyZGoTho0bqQ_5_1XB9Z1C-MwovRgkVrC-AhOuzs,12438
|
|
188
188
|
cognite/neat/rules/importers/_yaml2rules.py,sha256=F0uksSz1A3po5OlRM2152_w5j8D9oYTLB9NFTkSMlWI,4275
|
|
189
189
|
cognite/neat/rules/issues/__init__.py,sha256=Ms6jgCxCezc5IgTOwCFtXQPtoVFfOvdcXj84_rs917I,563
|
|
@@ -210,11 +210,11 @@ cognite/neat/rules/models/dms/_schema.py,sha256=A4z8CINmLQgWzHoScxejRPMRo40ngKly
|
|
|
210
210
|
cognite/neat/rules/models/dms/_serializer.py,sha256=iqp2zyyf8jEcU-R3PERuN8nu248xIqyxiWj4owAn92g,6406
|
|
211
211
|
cognite/neat/rules/models/dms/_validation.py,sha256=nPSyfM1vGZ7d9Uv_2vF2HvMetygtehXW7eNtPD6eW8E,13937
|
|
212
212
|
cognite/neat/rules/models/domain.py,sha256=tkKcHvDXnZ5IkOr1wHiuNBtE1h8OCFmf6GZSqzHzxjI,2814
|
|
213
|
-
cognite/neat/rules/models/entities.py,sha256=
|
|
213
|
+
cognite/neat/rules/models/entities.py,sha256=lkLsKg8U3Xto30PCB85ScDpv2SPRVq1ukVEQHzH53_g,18868
|
|
214
214
|
cognite/neat/rules/models/information/__init__.py,sha256=HR6g8xgyU53U7Ck8pPdbT70817Q4NC1r1pCRq5SA8iw,291
|
|
215
215
|
cognite/neat/rules/models/information/_converter.py,sha256=JN63_G5bygdL5WCz-q0_ygiU0NHkzUxm5mZ3WD8yUes,11029
|
|
216
|
-
cognite/neat/rules/models/information/_rules.py,sha256=
|
|
217
|
-
cognite/neat/rules/models/information/_rules_input.py,sha256=
|
|
216
|
+
cognite/neat/rules/models/information/_rules.py,sha256=bUzDzwFyWK9EH0FKCQPH9DCfKG3AUtBHaOshTW7VMRY,13476
|
|
217
|
+
cognite/neat/rules/models/information/_rules_input.py,sha256=xmcQQl2vBYSG_IbxOwb6x4CdN3nIg_TY2-3RAeGDYic,10418
|
|
218
218
|
cognite/neat/rules/models/information/_serializer.py,sha256=yti9I_xJruxrib66YIBInhze___Io-oPTQH6uWDumPE,3503
|
|
219
219
|
cognite/neat/rules/models/information/_validation.py,sha256=Is2GzL2lZU3A5zPu3NjvlXfmIU2_Y10C5Nxi5Denz4g,7528
|
|
220
220
|
cognite/neat/rules/models/wrapped_entities.py,sha256=ThhjnNNrpgz0HeORIQ8Q894trxP73P7T_TuZj6qH2CU,7157
|
|
@@ -259,7 +259,7 @@ cognite/neat/workflows/steps/lib/current/graph_extractor.py,sha256=vW9UpJScx5dFV
|
|
|
259
259
|
cognite/neat/workflows/steps/lib/current/graph_loader.py,sha256=HfGg1HRZhbV58TFu89FTjKeUxGsbCYLeFJIQFDN_pQM,2341
|
|
260
260
|
cognite/neat/workflows/steps/lib/current/graph_store.py,sha256=r7VTxdaz8jJQU7FJbnRDMxvEYbSAZFNMABhPyfNwiFk,6295
|
|
261
261
|
cognite/neat/workflows/steps/lib/current/rules_exporter.py,sha256=iFwzDWgUDBSPajaNAcXvu9pVw-jX66upvwyMXyTq7SE,23822
|
|
262
|
-
cognite/neat/workflows/steps/lib/current/rules_importer.py,sha256=
|
|
262
|
+
cognite/neat/workflows/steps/lib/current/rules_importer.py,sha256=QaGEVJ0JFoDbUt4YuJMQQrmL8jVubIzC077g4Wq9iCs,14697
|
|
263
263
|
cognite/neat/workflows/steps/lib/current/rules_validator.py,sha256=LwF9lXlnuPOxDSsOMMTHBi2BHc5rk08IF5zahS9yQuw,4844
|
|
264
264
|
cognite/neat/workflows/steps/lib/io/__init__.py,sha256=k7IPbIq3ey19oRc5sA_15F99-O6dxzqbm1LihGRRo5A,32
|
|
265
265
|
cognite/neat/workflows/steps/lib/io/io_steps.py,sha256=QAGypoi1vP32BRiIgBZ0B4qsbFMcwhzpRiVUUnWysLA,16874
|
|
@@ -276,8 +276,8 @@ cognite/neat/workflows/steps_registry.py,sha256=fkTX14ZA7_gkUYfWIlx7A1XbCidvqR23
|
|
|
276
276
|
cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
|
|
277
277
|
cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
|
|
278
278
|
cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
|
|
279
|
-
cognite_neat-0.78.
|
|
280
|
-
cognite_neat-0.78.
|
|
281
|
-
cognite_neat-0.78.
|
|
282
|
-
cognite_neat-0.78.
|
|
283
|
-
cognite_neat-0.78.
|
|
279
|
+
cognite_neat-0.78.3.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
|
|
280
|
+
cognite_neat-0.78.3.dist-info/METADATA,sha256=rvUofmdp5RnFDBlXQwKlBytNiizOBnvhA1rJcEOunjg,9306
|
|
281
|
+
cognite_neat-0.78.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
282
|
+
cognite_neat-0.78.3.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
|
|
283
|
+
cognite_neat-0.78.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|