cognite-neat 0.87.6__py3-none-any.whl → 0.88.0__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/app/api/data_classes/rest.py +0 -19
- cognite/neat/app/api/explorer.py +6 -4
- cognite/neat/app/api/routers/crud.py +11 -21
- cognite/neat/app/api/routers/workflows.py +24 -94
- cognite/neat/graph/stores/_base.py +5 -0
- cognite/neat/rules/importers/_inference2rules.py +31 -35
- cognite/neat/workflows/steps/data_contracts.py +17 -43
- cognite/neat/workflows/steps/lib/current/graph_extractor.py +28 -24
- cognite/neat/workflows/steps/lib/current/graph_loader.py +4 -21
- cognite/neat/workflows/steps/lib/current/graph_store.py +18 -134
- cognite/neat/workflows/steps_registry.py +5 -7
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/RECORD +17 -125
- cognite/neat/app/api/routers/core.py +0 -91
- cognite/neat/app/api/routers/data_exploration.py +0 -336
- cognite/neat/app/api/routers/rules.py +0 -203
- cognite/neat/legacy/__init__.py +0 -0
- cognite/neat/legacy/graph/__init__.py +0 -3
- cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44-dirty.xml +0 -20182
- cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44.xml +0 -20163
- cognite/neat/legacy/graph/examples/__init__.py +0 -10
- cognite/neat/legacy/graph/examples/skos-capturing-sheet-wind-topics.xlsx +0 -0
- cognite/neat/legacy/graph/exceptions.py +0 -90
- cognite/neat/legacy/graph/extractors/__init__.py +0 -6
- cognite/neat/legacy/graph/extractors/_base.py +0 -14
- cognite/neat/legacy/graph/extractors/_dexpi.py +0 -44
- cognite/neat/legacy/graph/extractors/_graph_capturing_sheet.py +0 -403
- cognite/neat/legacy/graph/extractors/_mock_graph_generator.py +0 -361
- cognite/neat/legacy/graph/loaders/__init__.py +0 -23
- cognite/neat/legacy/graph/loaders/_asset_loader.py +0 -511
- cognite/neat/legacy/graph/loaders/_base.py +0 -67
- cognite/neat/legacy/graph/loaders/_exceptions.py +0 -85
- cognite/neat/legacy/graph/loaders/core/__init__.py +0 -0
- cognite/neat/legacy/graph/loaders/core/labels.py +0 -58
- cognite/neat/legacy/graph/loaders/core/models.py +0 -136
- cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py +0 -1046
- cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py +0 -559
- cognite/neat/legacy/graph/loaders/rdf_to_dms.py +0 -309
- cognite/neat/legacy/graph/loaders/validator.py +0 -87
- cognite/neat/legacy/graph/models.py +0 -6
- cognite/neat/legacy/graph/stores/__init__.py +0 -13
- cognite/neat/legacy/graph/stores/_base.py +0 -400
- cognite/neat/legacy/graph/stores/_graphdb_store.py +0 -52
- cognite/neat/legacy/graph/stores/_memory_store.py +0 -43
- cognite/neat/legacy/graph/stores/_oxigraph_store.py +0 -151
- cognite/neat/legacy/graph/stores/_oxrdflib.py +0 -247
- cognite/neat/legacy/graph/stores/_rdf_to_graph.py +0 -42
- cognite/neat/legacy/graph/transformations/__init__.py +0 -0
- cognite/neat/legacy/graph/transformations/entity_matcher.py +0 -101
- cognite/neat/legacy/graph/transformations/query_generator/__init__.py +0 -3
- cognite/neat/legacy/graph/transformations/query_generator/sparql.py +0 -575
- cognite/neat/legacy/graph/transformations/transformer.py +0 -322
- cognite/neat/legacy/rules/__init__.py +0 -0
- cognite/neat/legacy/rules/analysis.py +0 -231
- cognite/neat/legacy/rules/examples/Rules-Nordic44-to-graphql.xlsx +0 -0
- cognite/neat/legacy/rules/examples/Rules-Nordic44.xlsx +0 -0
- cognite/neat/legacy/rules/examples/__init__.py +0 -18
- cognite/neat/legacy/rules/examples/power-grid-containers.yaml +0 -124
- cognite/neat/legacy/rules/examples/power-grid-example.xlsx +0 -0
- cognite/neat/legacy/rules/examples/power-grid-model.yaml +0 -224
- cognite/neat/legacy/rules/examples/rules-template.xlsx +0 -0
- cognite/neat/legacy/rules/examples/sheet2cdf-transformation-rules.xlsx +0 -0
- cognite/neat/legacy/rules/examples/skos-rules.xlsx +0 -0
- cognite/neat/legacy/rules/examples/source-to-solution-mapping-rules.xlsx +0 -0
- cognite/neat/legacy/rules/examples/wind-energy.owl +0 -1511
- cognite/neat/legacy/rules/exceptions.py +0 -2972
- cognite/neat/legacy/rules/exporters/__init__.py +0 -20
- cognite/neat/legacy/rules/exporters/_base.py +0 -45
- cognite/neat/legacy/rules/exporters/_core/__init__.py +0 -5
- cognite/neat/legacy/rules/exporters/_core/rules2labels.py +0 -24
- cognite/neat/legacy/rules/exporters/_rules2dms.py +0 -885
- cognite/neat/legacy/rules/exporters/_rules2excel.py +0 -213
- cognite/neat/legacy/rules/exporters/_rules2graphql.py +0 -183
- cognite/neat/legacy/rules/exporters/_rules2ontology.py +0 -524
- cognite/neat/legacy/rules/exporters/_rules2pydantic_models.py +0 -748
- cognite/neat/legacy/rules/exporters/_rules2rules.py +0 -105
- cognite/neat/legacy/rules/exporters/_rules2triples.py +0 -38
- cognite/neat/legacy/rules/exporters/_validation.py +0 -146
- cognite/neat/legacy/rules/importers/__init__.py +0 -22
- cognite/neat/legacy/rules/importers/_base.py +0 -66
- cognite/neat/legacy/rules/importers/_dict2rules.py +0 -158
- cognite/neat/legacy/rules/importers/_dms2rules.py +0 -194
- cognite/neat/legacy/rules/importers/_graph2rules.py +0 -308
- cognite/neat/legacy/rules/importers/_json2rules.py +0 -39
- cognite/neat/legacy/rules/importers/_owl2rules/__init__.py +0 -3
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2classes.py +0 -239
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2metadata.py +0 -260
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2properties.py +0 -217
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2rules.py +0 -290
- cognite/neat/legacy/rules/importers/_spreadsheet2rules.py +0 -45
- cognite/neat/legacy/rules/importers/_xsd2rules.py +0 -20
- cognite/neat/legacy/rules/importers/_yaml2rules.py +0 -39
- cognite/neat/legacy/rules/models/__init__.py +0 -5
- cognite/neat/legacy/rules/models/_base.py +0 -151
- cognite/neat/legacy/rules/models/raw_rules.py +0 -316
- cognite/neat/legacy/rules/models/rdfpath.py +0 -237
- cognite/neat/legacy/rules/models/rules.py +0 -1289
- cognite/neat/legacy/rules/models/tables.py +0 -9
- cognite/neat/legacy/rules/models/value_types.py +0 -118
- cognite/neat/legacy/workflows/examples/Export_DMS/workflow.yaml +0 -89
- cognite/neat/legacy/workflows/examples/Export_Rules_to_Ontology/workflow.yaml +0 -152
- cognite/neat/legacy/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml +0 -139
- cognite/neat/legacy/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml +0 -270
- cognite/neat/legacy/workflows/examples/Import_DMS/workflow.yaml +0 -65
- cognite/neat/legacy/workflows/examples/Ontology_to_Data_Model/workflow.yaml +0 -116
- cognite/neat/legacy/workflows/examples/Validate_Rules/workflow.yaml +0 -67
- cognite/neat/legacy/workflows/examples/Validate_Solution_Model/workflow.yaml +0 -64
- cognite/neat/legacy/workflows/examples/Visualize_Data_Model_Using_Mock_Graph/workflow.yaml +0 -95
- cognite/neat/legacy/workflows/examples/Visualize_Semantic_Data_Model/workflow.yaml +0 -111
- cognite/neat/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml +0 -270
- cognite/neat/workflows/migration/__init__.py +0 -0
- cognite/neat/workflows/migration/steps.py +0 -91
- cognite/neat/workflows/migration/wf_manifests.py +0 -33
- cognite/neat/workflows/steps/lib/legacy/__init__.py +0 -7
- cognite/neat/workflows/steps/lib/legacy/graph_contextualization.py +0 -82
- cognite/neat/workflows/steps/lib/legacy/graph_extractor.py +0 -746
- cognite/neat/workflows/steps/lib/legacy/graph_loader.py +0 -606
- cognite/neat/workflows/steps/lib/legacy/graph_store.py +0 -307
- cognite/neat/workflows/steps/lib/legacy/graph_transformer.py +0 -58
- cognite/neat/workflows/steps/lib/legacy/rules_exporter.py +0 -511
- cognite/neat/workflows/steps/lib/legacy/rules_importer.py +0 -612
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"""This module provides set of methods that perform conversion of TransformationRules
|
|
2
|
-
to TransformationRules for purpose of for example:
|
|
3
|
-
|
|
4
|
-
- subsetting the data model to only include desired classes and their properties
|
|
5
|
-
- converting classes/properties ids/names to DMS compliant format
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import logging
|
|
9
|
-
import re
|
|
10
|
-
import warnings
|
|
11
|
-
from typing import Any
|
|
12
|
-
|
|
13
|
-
from cognite.neat.legacy.rules.analysis import get_defined_classes
|
|
14
|
-
from cognite.neat.legacy.rules.models.rules import Rules
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def subset_rules(rules: Rules, desired_classes: set, skip_validation: bool = False) -> Rules:
|
|
18
|
-
"""
|
|
19
|
-
Subset transformation rules to only include desired classes and their properties.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
transformation_rules: Instance of TransformationRules to subset
|
|
23
|
-
desired_classes: Desired classes to include in the reduced data model
|
|
24
|
-
skip_validation: Whether to skip underlying pydantic validation, by default False
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
Instance of TransformationRules
|
|
28
|
-
|
|
29
|
-
!!! note "Skipping Validation"
|
|
30
|
-
It is fine to skip validation since we are deriving the reduced data model from data
|
|
31
|
-
model (i.e. TransformationRules) which has already been validated.
|
|
32
|
-
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
defined_classes = get_defined_classes(rules)
|
|
36
|
-
possible_classes = defined_classes.intersection(desired_classes)
|
|
37
|
-
impossible_classes = desired_classes - possible_classes
|
|
38
|
-
|
|
39
|
-
if not possible_classes:
|
|
40
|
-
logging.error("None of the desired classes are defined in the data model!")
|
|
41
|
-
raise ValueError("None of the desired classes are defined in the data model!")
|
|
42
|
-
|
|
43
|
-
if impossible_classes:
|
|
44
|
-
logging.warning(f"Could not find the following classes defined in the data model: {impossible_classes}")
|
|
45
|
-
warnings.warn(
|
|
46
|
-
f"Could not find the following classes defined in the data model: {impossible_classes}", stacklevel=2
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
reduced_data_model: dict[str, Any] = {
|
|
50
|
-
"metadata": rules.metadata.model_copy(),
|
|
51
|
-
"prefixes": (rules.prefixes or {}).copy(),
|
|
52
|
-
"classes": {},
|
|
53
|
-
"properties": {},
|
|
54
|
-
"instances": (rules.instances or []).copy(),
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
logging.info(f"Reducing data model to only include the following classes: {possible_classes}")
|
|
58
|
-
for class_ in possible_classes:
|
|
59
|
-
reduced_data_model["classes"][class_] = rules.classes[class_]
|
|
60
|
-
|
|
61
|
-
for id_, property_definition in rules.properties.items():
|
|
62
|
-
if property_definition.class_id in possible_classes:
|
|
63
|
-
reduced_data_model["properties"][id_] = property_definition
|
|
64
|
-
|
|
65
|
-
if skip_validation:
|
|
66
|
-
return Rules.model_construct(**reduced_data_model)
|
|
67
|
-
else:
|
|
68
|
-
return Rules(**reduced_data_model)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def to_dms_compliant_rules(rules: Rules) -> Rules:
|
|
72
|
-
raise NotImplementedError()
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# to be used for conversion to DMS compliant format
|
|
76
|
-
def to_dms_name(name: str, entity_type: str, fix_casing: bool = False) -> str:
|
|
77
|
-
"""
|
|
78
|
-
Repairs an entity name to conform to GraphQL naming convention
|
|
79
|
-
>>> repair_name("wind-speed", "property")
|
|
80
|
-
'windspeed'
|
|
81
|
-
>>> repair_name("Wind.Speed", "property", True)
|
|
82
|
-
'windSpeed'
|
|
83
|
-
>>> repair_name("windSpeed", "class", True)
|
|
84
|
-
'WindSpeed'
|
|
85
|
-
>>> repair_name("22windSpeed", "class")
|
|
86
|
-
'_22windSpeed'
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
# Remove any non GraphQL compliant characters
|
|
90
|
-
repaired_string = re.sub(r"[^_a-zA-Z0-9]", "", name)
|
|
91
|
-
|
|
92
|
-
# Name must start with a letter or underscore
|
|
93
|
-
if repaired_string[0].isdigit():
|
|
94
|
-
repaired_string = f"_{repaired_string}"
|
|
95
|
-
|
|
96
|
-
if not fix_casing:
|
|
97
|
-
return repaired_string
|
|
98
|
-
# Property names must be camelCase
|
|
99
|
-
if entity_type == "property" and repaired_string[0].isupper():
|
|
100
|
-
return repaired_string[0].lower() + repaired_string[1:]
|
|
101
|
-
# Class names must be PascalCase
|
|
102
|
-
elif entity_type == "class" and repaired_string[0].islower():
|
|
103
|
-
return repaired_string[0].upper() + repaired_string[1:]
|
|
104
|
-
else:
|
|
105
|
-
return repaired_string
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
from rdflib.term import Node
|
|
4
|
-
|
|
5
|
-
from cognite.neat.legacy.rules.models.rules import Rules
|
|
6
|
-
|
|
7
|
-
from ._base import BaseExporter
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class TripleExporter(BaseExporter[list[tuple[Node, Node, Node]]]):
|
|
11
|
-
"""
|
|
12
|
-
Exporter for transformation rules instances sheet to RDF triples
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
def _export_to_file(self, filepath: Path) -> None:
|
|
16
|
-
raise NotImplementedError("Export to file not implemented")
|
|
17
|
-
|
|
18
|
-
def export(self) -> list[tuple[Node, Node, Node]]:
|
|
19
|
-
return get_instances_as_triples(self.rules)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def get_instances_as_triples(transformation_rules: Rules) -> list[tuple[Node, Node, Node]]:
|
|
23
|
-
"""
|
|
24
|
-
Converts transformation rules instances sheet to RDF triples
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
transformation_rules: An instance of TransformationRules pydantic class
|
|
28
|
-
|
|
29
|
-
Returns:
|
|
30
|
-
List of triples provided as tuples
|
|
31
|
-
|
|
32
|
-
"""
|
|
33
|
-
if transformation_rules.instances:
|
|
34
|
-
return [
|
|
35
|
-
(instance.instance, instance.property_, instance.value) # type: ignore[misc]
|
|
36
|
-
for instance in transformation_rules.instances
|
|
37
|
-
]
|
|
38
|
-
return []
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
import warnings
|
|
3
|
-
from typing import Literal, overload
|
|
4
|
-
|
|
5
|
-
from cognite.neat.exceptions import wrangle_warnings
|
|
6
|
-
from cognite.neat.legacy.rules import exceptions
|
|
7
|
-
from cognite.neat.legacy.rules.models.rules import (
|
|
8
|
-
Rules,
|
|
9
|
-
dms_property_id_compliance_regex,
|
|
10
|
-
value_id_compliance_regex,
|
|
11
|
-
view_id_compliance_regex,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@overload
|
|
16
|
-
def are_entity_names_dms_compliant(
|
|
17
|
-
transformation_rules: Rules, return_report: Literal[True]
|
|
18
|
-
) -> tuple[bool, list[dict]]: ...
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@overload
|
|
22
|
-
def are_entity_names_dms_compliant(transformation_rules: Rules, return_report: Literal[False] = False) -> bool: ...
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def are_entity_names_dms_compliant(
|
|
26
|
-
transformation_rules: Rules, return_report: bool = False
|
|
27
|
-
) -> bool | tuple[bool, list[dict]]:
|
|
28
|
-
"""Check if data model definitions are valid."""
|
|
29
|
-
|
|
30
|
-
flag: bool = True
|
|
31
|
-
with warnings.catch_warnings(record=True) as validation_warnings:
|
|
32
|
-
for class_ in transformation_rules.classes.values():
|
|
33
|
-
if not re.match(view_id_compliance_regex, class_.class_id):
|
|
34
|
-
warnings.warn(
|
|
35
|
-
exceptions.EntityIDNotDMSCompliant(
|
|
36
|
-
"Class", class_.class_id, f"[Classes/Class/{class_.class_id}]"
|
|
37
|
-
).message,
|
|
38
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
39
|
-
stacklevel=2,
|
|
40
|
-
)
|
|
41
|
-
flag = False
|
|
42
|
-
|
|
43
|
-
for row, property_ in transformation_rules.properties.items():
|
|
44
|
-
# check class id which would resolve as view/container id
|
|
45
|
-
if not re.match(view_id_compliance_regex, property_.class_id):
|
|
46
|
-
warnings.warn(
|
|
47
|
-
exceptions.EntityIDNotDMSCompliant(
|
|
48
|
-
"Class", property_.class_id, f"[Properties/Class/{row}]"
|
|
49
|
-
).message,
|
|
50
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
51
|
-
stacklevel=2,
|
|
52
|
-
)
|
|
53
|
-
flag = False
|
|
54
|
-
|
|
55
|
-
# check property id which would resolve as view/container id
|
|
56
|
-
if not re.match(dms_property_id_compliance_regex, property_.property_id):
|
|
57
|
-
warnings.warn(
|
|
58
|
-
exceptions.EntityIDNotDMSCompliant(
|
|
59
|
-
"Property", property_.property_id, f"[Properties/Property/{row}]"
|
|
60
|
-
).message,
|
|
61
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
62
|
-
stacklevel=2,
|
|
63
|
-
)
|
|
64
|
-
flag = False
|
|
65
|
-
|
|
66
|
-
# check container external id
|
|
67
|
-
if property_.container and not re.match(view_id_compliance_regex, property_.container.external_id):
|
|
68
|
-
warnings.warn(
|
|
69
|
-
exceptions.EntityIDNotDMSCompliant(
|
|
70
|
-
"Container", property_.container.external_id, f"[Properties/Container/{row}]"
|
|
71
|
-
).message,
|
|
72
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
73
|
-
stacklevel=2,
|
|
74
|
-
)
|
|
75
|
-
flag = False
|
|
76
|
-
|
|
77
|
-
# check container property external id
|
|
78
|
-
if property_.container_property and not re.match(
|
|
79
|
-
dms_property_id_compliance_regex, property_.container_property
|
|
80
|
-
):
|
|
81
|
-
warnings.warn(
|
|
82
|
-
exceptions.EntityIDNotDMSCompliant(
|
|
83
|
-
"Container Property", property_.container_property, f"[Properties/Container Property/{row}]"
|
|
84
|
-
).message,
|
|
85
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
86
|
-
stacklevel=2,
|
|
87
|
-
)
|
|
88
|
-
flag = False
|
|
89
|
-
|
|
90
|
-
# expected value type, as it is case sensitive should be ok
|
|
91
|
-
if not re.match(value_id_compliance_regex, property_.expected_value_type.external_id):
|
|
92
|
-
warnings.warn(
|
|
93
|
-
exceptions.EntityIDNotDMSCompliant(
|
|
94
|
-
"Value type", property_.expected_value_type.external_id, f"[Properties/Type/{row}]"
|
|
95
|
-
).message,
|
|
96
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
97
|
-
stacklevel=2,
|
|
98
|
-
)
|
|
99
|
-
flag = False
|
|
100
|
-
|
|
101
|
-
if return_report:
|
|
102
|
-
return flag, wrangle_warnings(validation_warnings)
|
|
103
|
-
else:
|
|
104
|
-
return flag
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
@overload
|
|
108
|
-
def are_properties_redefined(transformation_rules: Rules, return_report: Literal[True]) -> tuple[bool, list[dict]]: ...
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
@overload
|
|
112
|
-
def are_properties_redefined(transformation_rules: Rules, return_report: Literal[False] = False) -> bool: ...
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def are_properties_redefined(
|
|
116
|
-
transformation_rules: Rules, return_report: bool = False
|
|
117
|
-
) -> bool | tuple[bool, list[dict]]:
|
|
118
|
-
flag: bool = False
|
|
119
|
-
with warnings.catch_warnings(record=True) as validation_warnings:
|
|
120
|
-
analyzed_properties = {}
|
|
121
|
-
for property_ in transformation_rules.properties.values():
|
|
122
|
-
if property_.property_id not in analyzed_properties:
|
|
123
|
-
analyzed_properties[property_.property_id] = [property_.class_id]
|
|
124
|
-
elif property_.class_id in analyzed_properties[property_.property_id]:
|
|
125
|
-
flag = True
|
|
126
|
-
warnings.warn(
|
|
127
|
-
exceptions.PropertyRedefined(property_.property_id, property_.class_id).message,
|
|
128
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
129
|
-
stacklevel=2,
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
else:
|
|
133
|
-
analyzed_properties[property_.property_id].append(property_.class_id)
|
|
134
|
-
|
|
135
|
-
if return_report:
|
|
136
|
-
return flag, wrangle_warnings(validation_warnings)
|
|
137
|
-
else:
|
|
138
|
-
return flag
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def property_ids_camel_case_compliant(transformation_rules) -> bool | tuple[bool, list[dict]]:
|
|
142
|
-
raise NotImplementedError()
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def class_id_pascal_case_compliant(transformation_rules) -> bool | tuple[bool, list[dict]]:
|
|
146
|
-
raise NotImplementedError()
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
from ._base import BaseImporter
|
|
2
|
-
from ._dict2rules import ArbitraryDictImporter
|
|
3
|
-
from ._dms2rules import DMSImporter
|
|
4
|
-
from ._graph2rules import GraphImporter
|
|
5
|
-
from ._json2rules import ArbitraryJSONImporter
|
|
6
|
-
from ._owl2rules import OWLImporter
|
|
7
|
-
from ._spreadsheet2rules import ExcelImporter, GoogleSheetImporter
|
|
8
|
-
from ._xsd2rules import XSDImporter
|
|
9
|
-
from ._yaml2rules import ArbitraryYAMLImporter
|
|
10
|
-
|
|
11
|
-
__all__ = [
|
|
12
|
-
"BaseImporter",
|
|
13
|
-
"ArbitraryDictImporter",
|
|
14
|
-
"ArbitraryJSONImporter",
|
|
15
|
-
"ArbitraryYAMLImporter",
|
|
16
|
-
"DMSImporter",
|
|
17
|
-
"OWLImporter",
|
|
18
|
-
"XSDImporter",
|
|
19
|
-
"GraphImporter",
|
|
20
|
-
"ExcelImporter",
|
|
21
|
-
"GoogleSheetImporter",
|
|
22
|
-
]
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import getpass
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
|
|
5
|
-
import pandas as pd
|
|
6
|
-
from pydantic_core import ErrorDetails
|
|
7
|
-
|
|
8
|
-
from cognite.neat.legacy.rules.models.raw_rules import RawRules
|
|
9
|
-
from cognite.neat.legacy.rules.models.rules import Rules
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class BaseImporter(ABC):
|
|
13
|
-
"""
|
|
14
|
-
BaseImporter class which all importers inherit from.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
@abstractmethod
|
|
18
|
-
def to_tables(self) -> dict[str, pd.DataFrame]:
|
|
19
|
-
"""Creates raw tables from the data."""
|
|
20
|
-
raise NotImplementedError
|
|
21
|
-
|
|
22
|
-
def to_raw_rules(self) -> RawRules:
|
|
23
|
-
"""Creates `RawRules` object from the data."""
|
|
24
|
-
|
|
25
|
-
tables = self.to_tables()
|
|
26
|
-
|
|
27
|
-
return RawRules.from_tables(tables=tables, importer_type=self.__class__.__name__)
|
|
28
|
-
|
|
29
|
-
def to_rules(
|
|
30
|
-
self,
|
|
31
|
-
return_report: bool = False,
|
|
32
|
-
skip_validation: bool = False,
|
|
33
|
-
validators_to_skip: set[str] | None = None,
|
|
34
|
-
) -> tuple[Rules | None, list[ErrorDetails] | None, list | None] | Rules:
|
|
35
|
-
"""
|
|
36
|
-
Creates `Rules` object from the data.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
return_report: To return validation report. Defaults to False.
|
|
40
|
-
skip_validation: Bypasses Rules validation. Defaults to False.
|
|
41
|
-
validators_to_skip: List of validators to skip. Defaults to None.
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
Instance of `Rules`, which can be validated, not validated based on
|
|
45
|
-
`skip_validation` flag, or partially validated if `validators_to_skip` is set,
|
|
46
|
-
and optional list of errors and warnings if
|
|
47
|
-
`return_report` is set to True.
|
|
48
|
-
|
|
49
|
-
!!! Note "Skip Validation
|
|
50
|
-
`skip_validation` flag should be only used for purpose when `Rules` object
|
|
51
|
-
is exported to an Excel file. Do not use this flag for any other purpose!
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
raw_rules = self.to_raw_rules()
|
|
55
|
-
|
|
56
|
-
return raw_rules.to_rules(return_report, skip_validation, validators_to_skip)
|
|
57
|
-
|
|
58
|
-
def _default_metadata(self):
|
|
59
|
-
return {
|
|
60
|
-
"prefix": "neat",
|
|
61
|
-
"version": "0.1.0",
|
|
62
|
-
"title": "Neat Imported Data Model",
|
|
63
|
-
"created": datetime.now().replace(microsecond=0).isoformat(),
|
|
64
|
-
"creator": getpass.getuser(),
|
|
65
|
-
"description": f"Imported using {type(self).__name__}",
|
|
66
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
from datetime import datetime, timezone
|
|
2
|
-
from typing import Any, Literal
|
|
3
|
-
|
|
4
|
-
import pandas as pd
|
|
5
|
-
|
|
6
|
-
from cognite.neat.legacy.rules.models.tables import Tables
|
|
7
|
-
|
|
8
|
-
from ._base import BaseImporter
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class ArbitraryDictImporter(BaseImporter):
|
|
12
|
-
"""
|
|
13
|
-
Importer for an arbitrary dictionary.
|
|
14
|
-
|
|
15
|
-
This importer infers the data model from the dictionary based on the shape of the data.
|
|
16
|
-
|
|
17
|
-
Args:
|
|
18
|
-
data: dictionary containing Rules definitions.
|
|
19
|
-
relationship_direction: Direction of relationships, either "parent-to-child" or "child-to-parent". Dictionaries
|
|
20
|
-
are nested with children nested inside parents. This option determines whether the resulting rules
|
|
21
|
-
will have an edge from parents to children or from children to parents.
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
def __init__(
|
|
25
|
-
self,
|
|
26
|
-
data: dict[str, Any],
|
|
27
|
-
relationship_direction: Literal["parent-to-child", "child-to-parent"] = "parent-to-child",
|
|
28
|
-
):
|
|
29
|
-
self.data = data
|
|
30
|
-
self.relationship_direction = relationship_direction
|
|
31
|
-
|
|
32
|
-
def to_tables(self) -> dict[str, pd.DataFrame]:
|
|
33
|
-
metadata = pd.Series(
|
|
34
|
-
dict(
|
|
35
|
-
title="OpenAPI to DM transformation rules",
|
|
36
|
-
description="OpenAPI to DM transformation rules",
|
|
37
|
-
version="0.1",
|
|
38
|
-
creator="Cognite",
|
|
39
|
-
created=datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
|
|
40
|
-
namespace="http://purl.org/cognite/neat#",
|
|
41
|
-
prefix="neat",
|
|
42
|
-
data_model_name="OpenAPI",
|
|
43
|
-
cdf_space_name="OpenAPI",
|
|
44
|
-
)
|
|
45
|
-
).reset_index()
|
|
46
|
-
finder = _TripleFinder(self.relationship_direction)
|
|
47
|
-
finder.find_triples(self.data)
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
Tables.metadata: metadata,
|
|
51
|
-
Tables.classes: pd.DataFrame(finder.classes).T,
|
|
52
|
-
Tables.properties: pd.DataFrame(finder.properties).T,
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class _TripleFinder:
|
|
57
|
-
def __init__(self, relationship_direction: Literal["parent-to-child", "child-to-parent"]) -> None:
|
|
58
|
-
self.classes: dict[str, dict[str, Any]] = {}
|
|
59
|
-
self.properties: dict[str, dict[str, Any]] = {}
|
|
60
|
-
self.relationship_direction = relationship_direction
|
|
61
|
-
|
|
62
|
-
def find_triples(self, data: dict[str, Any]) -> None:
|
|
63
|
-
self._convert_dict_to_classes_and_props(data)
|
|
64
|
-
|
|
65
|
-
def _convert_dict_to_classes_and_props(
|
|
66
|
-
self, data: dict, parent_property_name: str | None = None, grand_parent_property_name: str | None = None
|
|
67
|
-
) -> None:
|
|
68
|
-
if isinstance(data, dict) and len(data) == 0:
|
|
69
|
-
return
|
|
70
|
-
elif isinstance(data, dict) and parent_property_name is None:
|
|
71
|
-
for key, value in data.items():
|
|
72
|
-
self._convert_dict_to_classes_and_props(value, key)
|
|
73
|
-
elif isinstance(data, dict):
|
|
74
|
-
self.add_class(parent_property_name, "missing", grand_parent_property_name, is_list=False)
|
|
75
|
-
for key, value in data.items():
|
|
76
|
-
self._convert_dict_to_classes_and_props(value, key, parent_property_name)
|
|
77
|
-
elif isinstance(data, list):
|
|
78
|
-
if parent_property_name is not None and grand_parent_property_name is not None:
|
|
79
|
-
data_type = self._get_list_type(data, parent_property_name)
|
|
80
|
-
self.add_property(grand_parent_property_name, parent_property_name, data_type, "missing", is_list=True)
|
|
81
|
-
for item in data:
|
|
82
|
-
self._convert_dict_to_classes_and_props(item, parent_property_name, grand_parent_property_name)
|
|
83
|
-
elif isinstance(data, bool | int | float | str) and parent_property_name is not None:
|
|
84
|
-
data_type = self._get_primitive_data_type(data)
|
|
85
|
-
self.add_property(grand_parent_property_name, parent_property_name, data_type, "missing")
|
|
86
|
-
else:
|
|
87
|
-
raise ValueError(f"Unknown type {type(data)}")
|
|
88
|
-
|
|
89
|
-
def add_class(
|
|
90
|
-
self, class_name: str, description: str = "", parent_class_name: str | None = None, is_list: bool = False
|
|
91
|
-
):
|
|
92
|
-
if class_name in self.classes:
|
|
93
|
-
return
|
|
94
|
-
class_ = {"Class": class_name, "description": description}
|
|
95
|
-
if parent_class_name:
|
|
96
|
-
if self.relationship_direction == "child-to-parent":
|
|
97
|
-
self.add_property(class_name, "parent", parent_class_name, "missing", is_list=False)
|
|
98
|
-
elif self.relationship_direction == "parent-to-child":
|
|
99
|
-
self.add_property(parent_class_name, class_name, class_name, "missing", is_list)
|
|
100
|
-
else:
|
|
101
|
-
raise ValueError(f"Unknown relationship direction {self.relationship_direction}")
|
|
102
|
-
self.classes[class_name] = class_
|
|
103
|
-
|
|
104
|
-
def add_property(
|
|
105
|
-
self,
|
|
106
|
-
class_name: str,
|
|
107
|
-
property_name: str,
|
|
108
|
-
property_type: str,
|
|
109
|
-
description: str = "missing",
|
|
110
|
-
is_list: bool = False,
|
|
111
|
-
):
|
|
112
|
-
if class_name + property_name in self.properties:
|
|
113
|
-
return
|
|
114
|
-
prop = dict(
|
|
115
|
-
class_id=class_name,
|
|
116
|
-
property_id=property_name,
|
|
117
|
-
property_name=property_name,
|
|
118
|
-
property_type="ObjectProperty",
|
|
119
|
-
description=description,
|
|
120
|
-
expected_value_type=property_type,
|
|
121
|
-
max_count=1 if not is_list else None,
|
|
122
|
-
cdf_resource_type="Asset",
|
|
123
|
-
resource_type_property="Asset",
|
|
124
|
-
rule_type="rdfpath",
|
|
125
|
-
rule=f"neat:{class_name}(neat:{property_name})",
|
|
126
|
-
label="linked to",
|
|
127
|
-
)
|
|
128
|
-
self.properties[class_name + property_name] = prop
|
|
129
|
-
|
|
130
|
-
@staticmethod
|
|
131
|
-
def _get_primitive_data_type(data: Any, errors: Literal["raise", "empty"] = "raise") -> str:
|
|
132
|
-
data_type = type(data)
|
|
133
|
-
if data_type is bool:
|
|
134
|
-
return "boolean"
|
|
135
|
-
elif data_type is int:
|
|
136
|
-
return "integer"
|
|
137
|
-
elif data_type is float:
|
|
138
|
-
return "float"
|
|
139
|
-
elif data_type is str and not pd.isna(pd.to_datetime(data, errors="coerce")):
|
|
140
|
-
return "dateTime"
|
|
141
|
-
elif data_type is str:
|
|
142
|
-
return "string"
|
|
143
|
-
|
|
144
|
-
if errors == "empty":
|
|
145
|
-
return ""
|
|
146
|
-
else:
|
|
147
|
-
raise ValueError(f"Unknown primitive type {data_type}")
|
|
148
|
-
|
|
149
|
-
@classmethod
|
|
150
|
-
def _get_list_type(cls, data: list[Any], class_name: str) -> str:
|
|
151
|
-
if isinstance(data[0], dict):
|
|
152
|
-
return class_name
|
|
153
|
-
|
|
154
|
-
data_types = {cls._get_primitive_data_type(item, "empty") for item in data}
|
|
155
|
-
if "" in data_types or len(data_types) > 1:
|
|
156
|
-
# Fallback to string
|
|
157
|
-
return "string"
|
|
158
|
-
return data_types.pop()
|