cognite-neat 0.72.1__py3-none-any.whl → 0.72.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.
- cognite/neat/_version.py +1 -1
- cognite/neat/app/api/routers/core.py +32 -2
- cognite/neat/constants.py +3 -2
- cognite/neat/rules/exporters/_rules2dms.py +3 -7
- cognite/neat/rules/exporters/_rules2ontology.py +2 -0
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +15 -15
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +2 -0
- cognite/neat/rules/importers/_owl2rules/_owl2rules.py +1 -1
- cognite/neat/rules/importers/_spreadsheet2rules.py +52 -31
- cognite/neat/rules/issues/dms.py +43 -0
- cognite/neat/rules/models/_rules/_types/_base.py +6 -16
- cognite/neat/rules/models/_rules/dms_architect_rules.py +77 -63
- cognite/neat/workflows/steps/lib/rules_exporter.py +0 -1
- {cognite_neat-0.72.1.dist-info → cognite_neat-0.72.3.dist-info}/METADATA +1 -1
- {cognite_neat-0.72.1.dist-info → cognite_neat-0.72.3.dist-info}/RECORD +18 -18
- {cognite_neat-0.72.1.dist-info → cognite_neat-0.72.3.dist-info}/LICENSE +0 -0
- {cognite_neat-0.72.1.dist-info → cognite_neat-0.72.3.dist-info}/WHEEL +0 -0
- {cognite_neat-0.72.1.dist-info → cognite_neat-0.72.3.dist-info}/entry_points.txt +0 -0
cognite/neat/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.72.
|
|
1
|
+
__version__ = "0.72.3"
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import shutil
|
|
3
3
|
import tempfile
|
|
4
|
+
from copy import deepcopy
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import cast
|
|
6
7
|
|
|
7
8
|
from fastapi import APIRouter, UploadFile
|
|
8
9
|
|
|
10
|
+
from cognite.neat.app.api.configuration import NEAT_APP
|
|
9
11
|
from cognite.neat.rules import exporters, importers
|
|
10
12
|
from cognite.neat.rules.models._rules import DMSRules
|
|
11
13
|
from cognite.neat.rules.models._rules.base import RoleTypes
|
|
@@ -55,8 +57,36 @@ async def convert_data_model_to_rules(file: UploadFile):
|
|
|
55
57
|
@router.post("/api/core/rules2dms")
|
|
56
58
|
async def convert_rules_to_dms(rules: DMSRules):
|
|
57
59
|
dms_schema = exporters.DMSExporter().export(rules)
|
|
60
|
+
containers = {f"{container.space}:{container.external_id}": container.dump() for container in dms_schema.containers}
|
|
61
|
+
views = {f"{view.space}:{view.external_id}": view.dump() for view in dms_schema.views}
|
|
62
|
+
|
|
63
|
+
if views and containers:
|
|
64
|
+
_to_visualization_compliant_views(views, containers)
|
|
58
65
|
|
|
59
66
|
return {
|
|
60
|
-
"views":
|
|
61
|
-
"containers":
|
|
67
|
+
"views": list(views.values()) if views else None,
|
|
68
|
+
"containers": list(containers.values()) if containers else None,
|
|
62
69
|
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _to_visualization_compliant_views(views, containers):
|
|
73
|
+
for view in views.values():
|
|
74
|
+
for property in view["properties"].values():
|
|
75
|
+
# needs coping information from container:
|
|
76
|
+
if property.get("container", None) and property["container"]["type"] == "container":
|
|
77
|
+
container_id = f"{property['container']['space']}:{property['container']['externalId']}"
|
|
78
|
+
container_property_def = deepcopy(
|
|
79
|
+
containers[container_id]["properties"][property["containerPropertyIdentifier"]]
|
|
80
|
+
)
|
|
81
|
+
property["type"] = container_property_def["type"]
|
|
82
|
+
container_property_def.pop("type")
|
|
83
|
+
property.update(container_property_def)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@router.post("/api/core/publish-rules")
|
|
87
|
+
async def publish_rules_as_data_model(rules: DMSRules):
|
|
88
|
+
if NEAT_APP.cdf_client:
|
|
89
|
+
uploaded = exporters.DMSExporter().export_to_cdf(rules, NEAT_APP.cdf_client)
|
|
90
|
+
return {"uploaded": uploaded}
|
|
91
|
+
else:
|
|
92
|
+
return {"uploaded": []}
|
cognite/neat/constants.py
CHANGED
|
@@ -11,6 +11,7 @@ EXAMPLE_RULES = PACKAGE_DIRECTORY / "rules" / "examples"
|
|
|
11
11
|
EXAMPLE_GRAPHS = PACKAGE_DIRECTORY / "graph" / "examples"
|
|
12
12
|
EXAMPLE_WORKFLOWS = PACKAGE_DIRECTORY / "workflows" / "examples"
|
|
13
13
|
|
|
14
|
+
DEFAULT_NAMESPACE = Namespace("http://purl.org/cognite/neat#")
|
|
14
15
|
|
|
15
16
|
PREFIXES = {
|
|
16
17
|
"rdf": RDF._NS,
|
|
@@ -27,10 +28,10 @@ PREFIXES = {
|
|
|
27
28
|
"md": Namespace("http://iec.ch/TC57/61970-552/ModelDescription/1#"),
|
|
28
29
|
"pti": Namespace("http://www.pti-us.com/PTI_CIM-schema-cim16#"),
|
|
29
30
|
"tnt": Namespace("http://purl.org/cognite/tnt#"),
|
|
30
|
-
"neat":
|
|
31
|
+
"neat": DEFAULT_NAMESPACE,
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
|
|
34
35
|
DEFAULT_URI = ""
|
|
35
36
|
|
|
36
37
|
DEFAULT_DOCS_URL = "https://cognite-neat.readthedocs-hosted.com/en/latest/"
|
|
@@ -40,8 +40,6 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
40
40
|
If set, only export components in the given spaces. Defaults to None which means all spaces.
|
|
41
41
|
existing_handling (Literal["fail", "skip", "update", "force"], optional): How to handle existing components.
|
|
42
42
|
Defaults to "update". See below for details.
|
|
43
|
-
standardize_casing(bool, optional): Whether to standardize the casing. This means PascalCase for external ID
|
|
44
|
-
of views, containers, and data models, and camelCase for properties.
|
|
45
43
|
export_pipeline (bool, optional): Whether to export the pipeline. Defaults to False. This means setting
|
|
46
44
|
up transformations, RAW databases and tables to populate the data model.
|
|
47
45
|
instance_space (str, optional): The space to use for the instance. Defaults to None.
|
|
@@ -59,14 +57,12 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
59
57
|
export_components: Component | Collection[Component] = "all",
|
|
60
58
|
include_space: set[str] | None = None,
|
|
61
59
|
existing_handling: Literal["fail", "skip", "update", "force"] = "update",
|
|
62
|
-
standardize_casing: bool = True,
|
|
63
60
|
export_pipeline: bool = False,
|
|
64
61
|
instance_space: str | None = None,
|
|
65
62
|
):
|
|
66
63
|
self.export_components = {export_components} if isinstance(export_components, str) else set(export_components)
|
|
67
64
|
self.include_space = include_space
|
|
68
65
|
self.existing_handling = existing_handling
|
|
69
|
-
self.standardize_casing = standardize_casing
|
|
70
66
|
self.export_pipeline = export_pipeline
|
|
71
67
|
self.instance_space = instance_space
|
|
72
68
|
self._schema: DMSSchema | None = None
|
|
@@ -119,11 +115,11 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
119
115
|
)
|
|
120
116
|
is_new_model = dms_rules.reference is None
|
|
121
117
|
if is_new_model or is_solution_model:
|
|
122
|
-
return dms_rules.as_schema(self.
|
|
118
|
+
return dms_rules.as_schema(self.export_pipeline, self.instance_space)
|
|
123
119
|
|
|
124
120
|
# This is an extension of an existing model.
|
|
125
121
|
reference_rules = cast(DMSRules, dms_rules.reference).copy(deep=True)
|
|
126
|
-
reference_schema = reference_rules.as_schema(self.
|
|
122
|
+
reference_schema = reference_rules.as_schema(self.export_pipeline)
|
|
127
123
|
|
|
128
124
|
# Todo Move this to an appropriate location
|
|
129
125
|
# Merging Reference with User Rules
|
|
@@ -146,7 +142,7 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
146
142
|
property_.reference = None
|
|
147
143
|
combined_rules.properties.append(property_)
|
|
148
144
|
|
|
149
|
-
schema = combined_rules.as_schema(self.
|
|
145
|
+
schema = combined_rules.as_schema(self.export_pipeline, self.instance_space)
|
|
150
146
|
|
|
151
147
|
if dms_rules.metadata.extension in (ExtensionCategory.addition, ExtensionCategory.reshape):
|
|
152
148
|
# We do not freeze views as they might be changed, even for addition,
|
|
@@ -8,6 +8,7 @@ from pydantic import BaseModel, ConfigDict, ValidationInfo, field_validator
|
|
|
8
8
|
from rdflib import DCTERMS, OWL, RDF, RDFS, XSD, BNode, Graph, Literal, Namespace, URIRef
|
|
9
9
|
from rdflib.collection import Collection as GraphCollection
|
|
10
10
|
|
|
11
|
+
from cognite.neat.constants import DEFAULT_NAMESPACE as NEAT_NAMESPACE
|
|
11
12
|
from cognite.neat.rules import exceptions
|
|
12
13
|
from cognite.neat.rules._analysis._information_rules import InformationArchitectRulesAnalysis
|
|
13
14
|
from cognite.neat.rules.models._rules import DMSRules
|
|
@@ -217,6 +218,7 @@ class OWLMetadata(InformationMetadata):
|
|
|
217
218
|
(URIRef(self.namespace), DCTERMS.hasVersion, Literal(self.version)),
|
|
218
219
|
(URIRef(self.namespace), OWL.versionInfo, Literal(self.version)),
|
|
219
220
|
(URIRef(self.namespace), RDFS.label, Literal(self.name)),
|
|
221
|
+
(URIRef(self.namespace), NEAT_NAMESPACE.prefix, Literal(self.prefix)),
|
|
220
222
|
(URIRef(self.namespace), DCTERMS.title, Literal(self.name)),
|
|
221
223
|
(URIRef(self.namespace), DCTERMS.created, Literal(self.created, datatype=XSD.dateTime)),
|
|
222
224
|
(URIRef(self.namespace), DCTERMS.description, Literal(self.description)),
|
|
@@ -3,6 +3,7 @@ import re
|
|
|
3
3
|
|
|
4
4
|
from rdflib import Graph, Namespace
|
|
5
5
|
|
|
6
|
+
from cognite.neat.constants import DEFAULT_NAMESPACE
|
|
6
7
|
from cognite.neat.rules.models._rules.base import RoleTypes, SchemaCompleteness
|
|
7
8
|
from cognite.neat.rules.models.rules import (
|
|
8
9
|
prefix_compliance_regex,
|
|
@@ -29,23 +30,23 @@ def parse_owl_metadata(graph: Graph, make_compliant: bool = False) -> dict:
|
|
|
29
30
|
"""
|
|
30
31
|
# TODO: Move dataframe to dict representation
|
|
31
32
|
|
|
32
|
-
query = """SELECT ?namespace ?prefix ?version ?created ?updated ?title ?description ?creator ?rights ?license
|
|
33
|
-
WHERE {
|
|
33
|
+
query = f"""SELECT ?namespace ?prefix ?version ?created ?updated ?title ?description ?creator ?rights ?license
|
|
34
|
+
WHERE {{
|
|
34
35
|
?namespace a owl:Ontology .
|
|
35
|
-
OPTIONAL {?namespace owl:versionInfo ?version }.
|
|
36
|
-
OPTIONAL {?namespace dcterms:creator ?creator }.
|
|
37
|
-
OPTIONAL {?namespace
|
|
38
|
-
OPTIONAL {?namespace dcterms:
|
|
39
|
-
OPTIONAL {?namespace dcterms:
|
|
40
|
-
OPTIONAL {?namespace dcterms:
|
|
41
|
-
|
|
42
|
-
OPTIONAL {?namespace dcterms:rights|dc:rights ?rights }.
|
|
43
|
-
|
|
44
|
-
OPTIONAL {?namespace dcterms:license|dc:license ?license }.
|
|
36
|
+
OPTIONAL {{?namespace owl:versionInfo ?version }}.
|
|
37
|
+
OPTIONAL {{?namespace dcterms:creator ?creator }}.
|
|
38
|
+
OPTIONAL {{?namespace <{DEFAULT_NAMESPACE.prefix}> ?prefix }}.
|
|
39
|
+
OPTIONAL {{?namespace dcterms:title|rdfs:label|skos:prefLabel ?title }}.
|
|
40
|
+
OPTIONAL {{?namespace dcterms:modified ?updated }}.
|
|
41
|
+
OPTIONAL {{?namespace dcterms:created ?created }}.
|
|
42
|
+
OPTIONAL {{?namespace dcterms:description ?description }}.
|
|
43
|
+
OPTIONAL {{?namespace dcterms:rights|dc:rights ?rights }}.
|
|
44
|
+
|
|
45
|
+
OPTIONAL {{?namespace dcterms:license|dc:license ?license }}.
|
|
45
46
|
FILTER (!isBlank(?namespace))
|
|
46
47
|
FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
|
|
47
48
|
FILTER (!bound(?title) || LANG(?title) = "" || LANGMATCHES(LANG(?title), "en"))
|
|
48
|
-
}
|
|
49
|
+
}}
|
|
49
50
|
"""
|
|
50
51
|
|
|
51
52
|
results = [{item for item in sublist} for sublist in list(zip(*graph.query(query), strict=True))]
|
|
@@ -72,7 +73,6 @@ def parse_owl_metadata(graph: Graph, make_compliant: bool = False) -> dict:
|
|
|
72
73
|
)
|
|
73
74
|
|
|
74
75
|
if make_compliant:
|
|
75
|
-
raw_metadata.pop("created")
|
|
76
76
|
return make_metadata_compliant(raw_metadata)
|
|
77
77
|
|
|
78
78
|
return raw_metadata
|
|
@@ -176,7 +176,7 @@ def fix_date(
|
|
|
176
176
|
if date := metadata.get(date_type, None):
|
|
177
177
|
try:
|
|
178
178
|
if isinstance(date, datetime.datetime):
|
|
179
|
-
|
|
179
|
+
return metadata
|
|
180
180
|
elif isinstance(date, datetime.date):
|
|
181
181
|
metadata[date_type] = datetime.datetime.combine(metadata[date_type], datetime.datetime.min.time())
|
|
182
182
|
elif isinstance(date, str):
|
|
@@ -40,6 +40,8 @@ def parse_owl_properties(graph: Graph, make_compliant: bool = False, language: s
|
|
|
40
40
|
FILTER (!bound(?class) || !isBlank(?class))
|
|
41
41
|
FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en"))
|
|
42
42
|
FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
|
|
43
|
+
BIND(IF(bound(?minCount), ?minCount, 0) AS ?minCount)
|
|
44
|
+
BIND(IF(bound(?maxCount), ?maxCount, 1) AS ?maxCount)
|
|
43
45
|
}
|
|
44
46
|
"""
|
|
45
47
|
|
|
@@ -38,7 +38,7 @@ class OWLImporter(BaseImporter):
|
|
|
38
38
|
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
|
-
def __init__(self, owl_filepath: Path, make_compliant: bool =
|
|
41
|
+
def __init__(self, owl_filepath: Path, make_compliant: bool = False):
|
|
42
42
|
self.owl_filepath = owl_filepath
|
|
43
43
|
self.make_compliant = make_compliant
|
|
44
44
|
|
|
@@ -4,6 +4,7 @@ generating a list of rules based on which nodes that form the graph are made.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from collections import UserDict, defaultdict
|
|
7
|
+
from dataclasses import dataclass
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import Literal, cast, overload
|
|
9
10
|
|
|
@@ -49,6 +50,12 @@ class MetadataRaw(UserDict):
|
|
|
49
50
|
def has_schema_field(self) -> bool:
|
|
50
51
|
return self.get("schema") in [schema.value for schema in SchemaCompleteness.__members__.values()]
|
|
51
52
|
|
|
53
|
+
@property
|
|
54
|
+
def schema(self) -> SchemaCompleteness | None:
|
|
55
|
+
if not self.has_schema_field:
|
|
56
|
+
return None
|
|
57
|
+
return SchemaCompleteness(self["schema"])
|
|
58
|
+
|
|
52
59
|
def is_valid(self, issue_list: IssueList, filepath: Path) -> bool:
|
|
53
60
|
if not self.has_role_field:
|
|
54
61
|
issue_list.append(issues.spreadsheet_file.RoleMissingOrUnsupportedError(filepath))
|
|
@@ -61,6 +68,14 @@ class MetadataRaw(UserDict):
|
|
|
61
68
|
return True
|
|
62
69
|
|
|
63
70
|
|
|
71
|
+
@dataclass
|
|
72
|
+
class ReadResult:
|
|
73
|
+
sheets: dict[str, dict | list]
|
|
74
|
+
read_info_by_sheet: dict[str, SpreadsheetRead]
|
|
75
|
+
role: RoleTypes
|
|
76
|
+
schema: SchemaCompleteness | None
|
|
77
|
+
|
|
78
|
+
|
|
64
79
|
class SpreadsheetReader:
|
|
65
80
|
def __init__(self, issue_list: IssueList, is_reference: bool = False):
|
|
66
81
|
self.issue_list = issue_list
|
|
@@ -79,7 +94,7 @@ class SpreadsheetReader:
|
|
|
79
94
|
def to_reference_sheet(cls, sheet_name: str) -> str:
|
|
80
95
|
return f"Ref{sheet_name}"
|
|
81
96
|
|
|
82
|
-
def read(self, filepath: Path) ->
|
|
97
|
+
def read(self, filepath: Path) -> None | ReadResult:
|
|
83
98
|
with pd.ExcelFile(filepath) as excel_file:
|
|
84
99
|
if self.metadata_sheet_name not in excel_file.sheet_names:
|
|
85
100
|
self.issue_list.append(
|
|
@@ -95,21 +110,10 @@ class SpreadsheetReader:
|
|
|
95
110
|
return None
|
|
96
111
|
|
|
97
112
|
sheets, read_info_by_sheet = self._read_sheets(metadata, excel_file)
|
|
98
|
-
if self.issue_list.has_errors:
|
|
99
|
-
return None
|
|
100
|
-
|
|
101
|
-
rules_cls = RULES_PER_ROLE[metadata.role]
|
|
102
|
-
with _handle_issues(
|
|
103
|
-
self.issue_list,
|
|
104
|
-
error_cls=issues.spreadsheet.InvalidSheetError,
|
|
105
|
-
error_args={"read_info_by_sheet": read_info_by_sheet},
|
|
106
|
-
) as future:
|
|
107
|
-
rules = rules_cls.model_validate(sheets) # type: ignore[attr-defined]
|
|
108
|
-
|
|
109
|
-
if future.result == "failure" or self.issue_list.has_errors:
|
|
113
|
+
if sheets is None or self.issue_list.has_errors:
|
|
110
114
|
return None
|
|
111
115
|
|
|
112
|
-
|
|
116
|
+
return ReadResult(sheets, read_info_by_sheet, metadata.role, metadata.schema)
|
|
113
117
|
|
|
114
118
|
def _read_sheets(
|
|
115
119
|
self, metadata: MetadataRaw, excel_file: ExcelFile
|
|
@@ -167,43 +171,60 @@ class ExcelImporter(BaseImporter):
|
|
|
167
171
|
is_reference: bool = False,
|
|
168
172
|
) -> tuple[Rules | None, IssueList] | Rules:
|
|
169
173
|
issue_list = IssueList(title=f"'{self.filepath.name}'")
|
|
170
|
-
|
|
171
174
|
if not self.filepath.exists():
|
|
172
175
|
issue_list.append(issues.spreadsheet_file.SpreadsheetNotFoundError(self.filepath))
|
|
173
176
|
return self._return_or_raise(issue_list, errors)
|
|
174
177
|
|
|
175
|
-
|
|
178
|
+
user_result: ReadResult | None = None
|
|
176
179
|
if not is_reference:
|
|
177
|
-
|
|
178
|
-
if issue_list.has_errors:
|
|
180
|
+
user_result = SpreadsheetReader(issue_list, is_reference=False).read(self.filepath)
|
|
181
|
+
if user_result is None or issue_list.has_errors:
|
|
179
182
|
return self._return_or_raise(issue_list, errors)
|
|
180
183
|
|
|
181
|
-
|
|
184
|
+
reference_result: ReadResult | None = None
|
|
182
185
|
if is_reference or (
|
|
183
|
-
|
|
184
|
-
and
|
|
185
|
-
and
|
|
186
|
+
user_result
|
|
187
|
+
and user_result.role != RoleTypes.domain_expert
|
|
188
|
+
and user_result.schema == SchemaCompleteness.extended
|
|
186
189
|
):
|
|
187
|
-
|
|
190
|
+
reference_result = SpreadsheetReader(issue_list, is_reference=True).read(self.filepath)
|
|
188
191
|
if issue_list.has_errors:
|
|
189
192
|
return self._return_or_raise(issue_list, errors)
|
|
190
193
|
|
|
191
|
-
if
|
|
194
|
+
if user_result and reference_result and user_result.role != reference_result.role:
|
|
192
195
|
issue_list.append(issues.spreadsheet_file.RoleMismatchError(self.filepath))
|
|
193
196
|
return self._return_or_raise(issue_list, errors)
|
|
194
197
|
|
|
195
|
-
if
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
198
|
+
if user_result and reference_result:
|
|
199
|
+
user_result.sheets["reference"] = reference_result.sheets
|
|
200
|
+
sheets = user_result.sheets
|
|
201
|
+
original_role = user_result.role
|
|
202
|
+
read_info_by_sheet = user_result.read_info_by_sheet
|
|
203
|
+
read_info_by_sheet.update(reference_result.read_info_by_sheet)
|
|
204
|
+
elif user_result:
|
|
205
|
+
sheets = user_result.sheets
|
|
206
|
+
original_role = user_result.role
|
|
207
|
+
read_info_by_sheet = user_result.read_info_by_sheet
|
|
208
|
+
elif reference_result:
|
|
209
|
+
sheets = reference_result.sheets
|
|
210
|
+
original_role = reference_result.role
|
|
211
|
+
read_info_by_sheet = reference_result.read_info_by_sheet
|
|
202
212
|
else:
|
|
203
213
|
raise ValueError(
|
|
204
214
|
"No rules were generated. This should have been caught earlier. " f"Bug in {type(self).__name__}."
|
|
205
215
|
)
|
|
206
216
|
|
|
217
|
+
rules_cls = RULES_PER_ROLE[original_role]
|
|
218
|
+
with _handle_issues(
|
|
219
|
+
issue_list,
|
|
220
|
+
error_cls=issues.spreadsheet.InvalidSheetError,
|
|
221
|
+
error_args={"read_info_by_sheet": read_info_by_sheet},
|
|
222
|
+
) as future:
|
|
223
|
+
rules = rules_cls.model_validate(sheets) # type: ignore[attr-defined]
|
|
224
|
+
|
|
225
|
+
if future.result == "failure" or issue_list.has_errors:
|
|
226
|
+
return self._return_or_raise(issue_list, errors)
|
|
227
|
+
|
|
207
228
|
return self._to_output(
|
|
208
229
|
rules,
|
|
209
230
|
issue_list,
|
cognite/neat/rules/issues/dms.py
CHANGED
|
@@ -24,6 +24,8 @@ __all__ = [
|
|
|
24
24
|
"EmptyContainerWarning",
|
|
25
25
|
"UnsupportedRelationWarning",
|
|
26
26
|
"MultipleReferenceWarning",
|
|
27
|
+
"HasDataFilterOnNoPropertiesViewWarning",
|
|
28
|
+
"NodeTypeFilterOnParentViewWarning",
|
|
27
29
|
]
|
|
28
30
|
|
|
29
31
|
|
|
@@ -329,3 +331,44 @@ class MultipleReferenceWarning(DMSSchemaWarning):
|
|
|
329
331
|
output["view_id"] = self.view_id.dump()
|
|
330
332
|
output["implements"] = [view.dump() for view in self.implements]
|
|
331
333
|
return output
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@dataclass(frozen=True)
|
|
337
|
+
class HasDataFilterOnNoPropertiesViewWarning(DMSSchemaWarning):
|
|
338
|
+
description = "Attempting to set a HasData filter on a view without properties."
|
|
339
|
+
fix = "Add properties to the view or use a node type filter"
|
|
340
|
+
error_name: ClassVar[str] = "HasDataFilterOnNoPropertiesViewWarning"
|
|
341
|
+
view_id: dm.ViewId
|
|
342
|
+
|
|
343
|
+
def message(self) -> str:
|
|
344
|
+
return (
|
|
345
|
+
f"Cannot set hasData filter on view {self.view_id} as it does not have properties in any containers. "
|
|
346
|
+
"Using a node type filter instead."
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
def dump(self) -> dict[str, Any]:
|
|
350
|
+
output = super().dump()
|
|
351
|
+
output["view_id"] = self.view_id.dump()
|
|
352
|
+
return output
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
@dataclass(frozen=True)
|
|
356
|
+
class NodeTypeFilterOnParentViewWarning(DMSSchemaWarning):
|
|
357
|
+
description = (
|
|
358
|
+
"Setting a node type filter on a parent view. This is no "
|
|
359
|
+
"recommended as parent views are typically used for multiple type of nodes."
|
|
360
|
+
)
|
|
361
|
+
fix = "Use a HasData filter instead"
|
|
362
|
+
error_name: ClassVar[str] = "NodeTypeFilterOnParentViewWarning"
|
|
363
|
+
view_id: dm.ViewId
|
|
364
|
+
|
|
365
|
+
def message(self) -> str:
|
|
366
|
+
return (
|
|
367
|
+
f"Setting a node type filter on parent view {self.view_id}. This is not recommended as "
|
|
368
|
+
"parent views are typically used for multiple types of nodes."
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
def dump(self) -> dict[str, Any]:
|
|
372
|
+
output = super().dump()
|
|
373
|
+
output["view_id"] = self.view_id.dump()
|
|
374
|
+
return output
|
|
@@ -14,7 +14,6 @@ from cognite.neat.rules.models.rdfpath import (
|
|
|
14
14
|
TransformationRuleType,
|
|
15
15
|
parse_rule,
|
|
16
16
|
)
|
|
17
|
-
from cognite.neat.utils.text import to_pascal
|
|
18
17
|
|
|
19
18
|
if sys.version_info >= (3, 11):
|
|
20
19
|
from enum import StrEnum
|
|
@@ -197,15 +196,14 @@ class ContainerEntity(Entity):
|
|
|
197
196
|
def from_id(cls, container_id: ContainerId) -> "ContainerEntity":
|
|
198
197
|
return ContainerEntity(prefix=container_id.space, suffix=container_id.external_id)
|
|
199
198
|
|
|
200
|
-
def as_id(self, default_space: str | None
|
|
199
|
+
def as_id(self, default_space: str | None) -> ContainerId:
|
|
201
200
|
if self.space is Undefined and default_space is None:
|
|
202
201
|
raise ValueError("Space is Undefined! Set default_space!")
|
|
203
202
|
|
|
204
|
-
external_id = to_pascal(self.external_id) if standardize_casing else self.external_id
|
|
205
203
|
if self.space is Undefined:
|
|
206
|
-
return ContainerId(space=cast(str, default_space), external_id=external_id)
|
|
204
|
+
return ContainerId(space=cast(str, default_space), external_id=self.external_id)
|
|
207
205
|
else:
|
|
208
|
-
return ContainerId(space=self.space, external_id=external_id)
|
|
206
|
+
return ContainerId(space=self.space, external_id=self.external_id)
|
|
209
207
|
|
|
210
208
|
|
|
211
209
|
class ViewEntity(Entity):
|
|
@@ -233,7 +231,6 @@ class ViewEntity(Entity):
|
|
|
233
231
|
allow_none: Literal[False] = False,
|
|
234
232
|
default_space: str | None = None,
|
|
235
233
|
default_version: str | None = None,
|
|
236
|
-
standardize_casing: bool = True,
|
|
237
234
|
) -> ViewId:
|
|
238
235
|
...
|
|
239
236
|
|
|
@@ -243,7 +240,6 @@ class ViewEntity(Entity):
|
|
|
243
240
|
allow_none: Literal[True],
|
|
244
241
|
default_space: str | None = None,
|
|
245
242
|
default_version: str | None = None,
|
|
246
|
-
standardize_casing: bool = True,
|
|
247
243
|
) -> ViewId | None:
|
|
248
244
|
...
|
|
249
245
|
|
|
@@ -252,7 +248,6 @@ class ViewEntity(Entity):
|
|
|
252
248
|
allow_none: bool = False,
|
|
253
249
|
default_space: str | None = None,
|
|
254
250
|
default_version: str | None = None,
|
|
255
|
-
standardize_casing: bool = True,
|
|
256
251
|
) -> ViewId | None:
|
|
257
252
|
if self.suffix is Unknown and allow_none:
|
|
258
253
|
return None
|
|
@@ -265,8 +260,7 @@ class ViewEntity(Entity):
|
|
|
265
260
|
raise ValueError("space is required")
|
|
266
261
|
if version is None:
|
|
267
262
|
raise ValueError("version is required")
|
|
268
|
-
|
|
269
|
-
return ViewId(space=space, external_id=external_id, version=version)
|
|
263
|
+
return ViewId(space=space, external_id=self.external_id, version=version)
|
|
270
264
|
|
|
271
265
|
|
|
272
266
|
class ViewPropEntity(ViewEntity):
|
|
@@ -303,14 +297,10 @@ class ViewPropEntity(ViewEntity):
|
|
|
303
297
|
property_=prop_id.property,
|
|
304
298
|
)
|
|
305
299
|
|
|
306
|
-
def as_prop_id(
|
|
307
|
-
self, default_space: str | None = None, default_version: str | None = None, standardize_casing: bool = True
|
|
308
|
-
) -> PropertyId:
|
|
300
|
+
def as_prop_id(self, default_space: str | None = None, default_version: str | None = None) -> PropertyId:
|
|
309
301
|
if self.property_ is None:
|
|
310
302
|
raise ValueError("property is required to create PropertyId")
|
|
311
|
-
return PropertyId(
|
|
312
|
-
source=self.as_id(False, default_space, default_version, standardize_casing), property=self.property_
|
|
313
|
-
)
|
|
303
|
+
return PropertyId(source=self.as_id(False, default_space, default_version), property=self.property_)
|
|
314
304
|
|
|
315
305
|
@property
|
|
316
306
|
def versioned_id(self) -> str:
|
|
@@ -19,7 +19,6 @@ from rdflib import Namespace
|
|
|
19
19
|
import cognite.neat.rules.issues.spreadsheet
|
|
20
20
|
from cognite.neat.rules import issues
|
|
21
21
|
from cognite.neat.rules.models._rules.domain_rules import DomainRules
|
|
22
|
-
from cognite.neat.utils.text import to_camel
|
|
23
22
|
|
|
24
23
|
from ._types import (
|
|
25
24
|
CdfValueType,
|
|
@@ -207,11 +206,11 @@ class DMSContainer(SheetEntity):
|
|
|
207
206
|
reference: ReferenceType = Field(alias="Reference", default=None)
|
|
208
207
|
constraint: ContainerListType | None = Field(None, alias="Constraint")
|
|
209
208
|
|
|
210
|
-
def as_container(self, default_space: str
|
|
211
|
-
container_id = self.container.as_id(default_space
|
|
209
|
+
def as_container(self, default_space: str) -> dm.ContainerApply:
|
|
210
|
+
container_id = self.container.as_id(default_space)
|
|
212
211
|
constraints: dict[str, dm.Constraint] = {}
|
|
213
212
|
for constraint in self.constraint or []:
|
|
214
|
-
requires = dm.RequiresConstraint(constraint.as_id(default_space
|
|
213
|
+
requires = dm.RequiresConstraint(constraint.as_id(default_space))
|
|
215
214
|
constraints[f"{constraint.space}_{constraint.external_id}"] = requires
|
|
216
215
|
|
|
217
216
|
return dm.ContainerApply(
|
|
@@ -243,20 +242,18 @@ class DMSView(SheetEntity):
|
|
|
243
242
|
view: ViewType = Field(alias="View")
|
|
244
243
|
implements: ViewListType | None = Field(None, alias="Implements")
|
|
245
244
|
reference: ReferenceType = Field(alias="Reference", default=None)
|
|
245
|
+
filter_: Literal["hasData", "nodeType"] | None = Field(None, alias="Filter")
|
|
246
246
|
in_model: bool = Field(True, alias="InModel")
|
|
247
247
|
|
|
248
|
-
def as_view(self, default_space: str, default_version: str
|
|
249
|
-
view_id = self.view.as_id(False, default_space, default_version
|
|
248
|
+
def as_view(self, default_space: str, default_version: str) -> dm.ViewApply:
|
|
249
|
+
view_id = self.view.as_id(False, default_space, default_version)
|
|
250
250
|
return dm.ViewApply(
|
|
251
251
|
space=view_id.space,
|
|
252
252
|
external_id=view_id.external_id,
|
|
253
253
|
version=view_id.version or default_version,
|
|
254
254
|
name=self.name or None,
|
|
255
255
|
description=self.description,
|
|
256
|
-
implements=[
|
|
257
|
-
parent.as_id(False, default_space, default_version, standardize_casing)
|
|
258
|
-
for parent in self.implements or []
|
|
259
|
-
]
|
|
256
|
+
implements=[parent.as_id(False, default_space, default_version) for parent in self.implements or []]
|
|
260
257
|
or None,
|
|
261
258
|
properties={},
|
|
262
259
|
)
|
|
@@ -563,10 +560,8 @@ class DMSRules(BaseRules):
|
|
|
563
560
|
"Containers" if info.by_alias else "containers": containers,
|
|
564
561
|
}
|
|
565
562
|
|
|
566
|
-
def as_schema(
|
|
567
|
-
|
|
568
|
-
) -> DMSSchema:
|
|
569
|
-
return _DMSExporter(standardize_casing, include_pipeline, instance_space).to_schema(self)
|
|
563
|
+
def as_schema(self, include_pipeline: bool = False, instance_space: str | None = None) -> DMSSchema:
|
|
564
|
+
return _DMSExporter(include_pipeline, instance_space).to_schema(self)
|
|
570
565
|
|
|
571
566
|
def as_information_architect_rules(self) -> "InformationRules":
|
|
572
567
|
return _DMSRulesConverter(self).as_information_architect_rules()
|
|
@@ -598,17 +593,12 @@ class _DMSExporter:
|
|
|
598
593
|
(This module cannot have a dependency on the exporter module, as it would create a circular dependency.)
|
|
599
594
|
|
|
600
595
|
Args
|
|
601
|
-
standardize_casing (bool): If True, the casing of the identifiers will be standardized. This means external IDs
|
|
602
|
-
are PascalCase and property names are camelCase.
|
|
603
596
|
include_pipeline (bool): If True, the pipeline will be included with the schema. Pipeline means the
|
|
604
597
|
raw tables and transformations necessary to populate the data model.
|
|
605
598
|
instance_space (str): The space to use for the instance. Defaults to None,`Rules.metadata.space` will be used
|
|
606
599
|
"""
|
|
607
600
|
|
|
608
|
-
def __init__(
|
|
609
|
-
self, standardize_casing: bool = True, include_pipeline: bool = False, instance_space: str | None = None
|
|
610
|
-
):
|
|
611
|
-
self.standardize_casing = standardize_casing
|
|
601
|
+
def __init__(self, include_pipeline: bool = False, instance_space: str | None = None):
|
|
612
602
|
self.include_pipeline = include_pipeline
|
|
613
603
|
self.instance_space = instance_space
|
|
614
604
|
|
|
@@ -627,9 +617,7 @@ class _DMSExporter:
|
|
|
627
617
|
)
|
|
628
618
|
|
|
629
619
|
views_not_in_model = {
|
|
630
|
-
view.view.as_id(False, default_space, default_version
|
|
631
|
-
for view in rules.views
|
|
632
|
-
if not view.in_model
|
|
620
|
+
view.view.as_id(False, default_space, default_version) for view in rules.views if not view.in_model
|
|
633
621
|
}
|
|
634
622
|
data_model = rules.metadata.as_data_model()
|
|
635
623
|
data_model.views = sorted(
|
|
@@ -674,9 +662,11 @@ class _DMSExporter:
|
|
|
674
662
|
default_space: str,
|
|
675
663
|
default_version: str,
|
|
676
664
|
) -> tuple[dm.ViewApplyList, dm.NodeApplyList]:
|
|
677
|
-
views = dm.ViewApplyList(
|
|
678
|
-
|
|
679
|
-
|
|
665
|
+
views = dm.ViewApplyList([dms_view.as_view(default_space, default_version) for dms_view in dms_views])
|
|
666
|
+
dms_view_by_id = {
|
|
667
|
+
dms_view.view.as_id(False, default_space, default_version): dms_view for dms_view in dms_views
|
|
668
|
+
}
|
|
669
|
+
|
|
680
670
|
for view in views:
|
|
681
671
|
view_id = view.as_id()
|
|
682
672
|
view.properties = {}
|
|
@@ -688,7 +678,7 @@ class _DMSExporter:
|
|
|
688
678
|
# This is not yet supported in the CDF API, a warning has already been issued, here we convert it to
|
|
689
679
|
# a multi-edge connection.
|
|
690
680
|
if isinstance(prop.value_type, ViewEntity):
|
|
691
|
-
source = prop.value_type.as_id(False, default_space, default_version
|
|
681
|
+
source = prop.value_type.as_id(False, default_space, default_version)
|
|
692
682
|
else:
|
|
693
683
|
raise ValueError(
|
|
694
684
|
"Direct relation must have a view as value type. "
|
|
@@ -703,43 +693,48 @@ class _DMSExporter:
|
|
|
703
693
|
direction="outwards",
|
|
704
694
|
)
|
|
705
695
|
elif prop.container and prop.container_property and prop.view_property:
|
|
706
|
-
container_prop_identifier =
|
|
707
|
-
to_camel(prop.container_property) if self.standardize_casing else prop.container_property
|
|
708
|
-
)
|
|
696
|
+
container_prop_identifier = prop.container_property
|
|
709
697
|
extra_args: dict[str, Any] = {}
|
|
710
698
|
if prop.relation == "direct" and isinstance(prop.value_type, ViewEntity):
|
|
711
|
-
extra_args["source"] = prop.value_type.as_id(
|
|
712
|
-
True, default_space, default_version, self.standardize_casing
|
|
713
|
-
)
|
|
699
|
+
extra_args["source"] = prop.value_type.as_id(True, default_space, default_version)
|
|
714
700
|
elif prop.relation == "direct" and not isinstance(prop.value_type, ViewEntity):
|
|
715
701
|
raise ValueError(
|
|
716
702
|
"Direct relation must have a view as value type. "
|
|
717
703
|
"This should have been validated in the rules"
|
|
718
704
|
)
|
|
719
705
|
view_property = dm.MappedPropertyApply(
|
|
720
|
-
container=prop.container.as_id(default_space
|
|
706
|
+
container=prop.container.as_id(default_space),
|
|
721
707
|
container_property_identifier=container_prop_identifier,
|
|
722
708
|
**extra_args,
|
|
723
709
|
)
|
|
724
710
|
elif prop.view and prop.view_property and prop.relation == "multiedge":
|
|
725
711
|
if isinstance(prop.value_type, ViewEntity):
|
|
726
|
-
source = prop.value_type.as_id(False, default_space, default_version
|
|
712
|
+
source = prop.value_type.as_id(False, default_space, default_version)
|
|
727
713
|
else:
|
|
728
714
|
raise ValueError(
|
|
729
715
|
"Multiedge relation must have a view as value type. "
|
|
730
716
|
"This should have been validated in the rules"
|
|
731
717
|
)
|
|
732
|
-
|
|
733
|
-
|
|
718
|
+
if isinstance(prop.reference, ReferenceEntity):
|
|
719
|
+
ref_view_prop = prop.reference.as_prop_id(default_space, default_version)
|
|
720
|
+
edge_type = dm.DirectRelationReference(
|
|
721
|
+
space=ref_view_prop.source.space,
|
|
722
|
+
external_id=f"{ref_view_prop.source.external_id}.{ref_view_prop.property}",
|
|
723
|
+
)
|
|
724
|
+
else:
|
|
725
|
+
edge_type = dm.DirectRelationReference(
|
|
734
726
|
space=source.space,
|
|
735
727
|
external_id=f"{prop.view.external_id}.{prop.view_property}",
|
|
736
|
-
)
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
view_property = dm.MultiEdgeConnectionApply(
|
|
731
|
+
type=edge_type,
|
|
737
732
|
source=source,
|
|
738
733
|
direction="outwards",
|
|
739
734
|
)
|
|
740
735
|
elif prop.view and prop.view_property and prop.relation == "reversedirect":
|
|
741
736
|
if isinstance(prop.value_type, ViewPropEntity):
|
|
742
|
-
source = prop.value_type.as_id(False, default_space, default_version
|
|
737
|
+
source = prop.value_type.as_id(False, default_space, default_version)
|
|
743
738
|
else:
|
|
744
739
|
raise ValueError(
|
|
745
740
|
"Reverse direct relation must have a view as value type. "
|
|
@@ -759,11 +754,19 @@ class _DMSExporter:
|
|
|
759
754
|
warnings.warn(
|
|
760
755
|
issues.dms.ReverseOfDirectRelationListWarning(view_id, prop.property_), stacklevel=2
|
|
761
756
|
)
|
|
762
|
-
|
|
763
|
-
|
|
757
|
+
if isinstance(reverse_prop.reference, ReferenceEntity):
|
|
758
|
+
ref_view_prop = reverse_prop.reference.as_prop_id(default_space, default_version)
|
|
759
|
+
edge_type = dm.DirectRelationReference(
|
|
760
|
+
space=ref_view_prop.source.space,
|
|
761
|
+
external_id=f"{ref_view_prop.source.external_id}.{ref_view_prop.property}",
|
|
762
|
+
)
|
|
763
|
+
else:
|
|
764
|
+
edge_type = dm.DirectRelationReference(
|
|
764
765
|
space=source.space,
|
|
765
766
|
external_id=f"{reverse_prop.view.external_id}.{reverse_prop.view_property}",
|
|
766
|
-
)
|
|
767
|
+
)
|
|
768
|
+
view_property = dm.MultiEdgeConnectionApply(
|
|
769
|
+
type=edge_type,
|
|
767
770
|
source=source,
|
|
768
771
|
direction="inwards",
|
|
769
772
|
)
|
|
@@ -791,29 +794,43 @@ class _DMSExporter:
|
|
|
791
794
|
continue
|
|
792
795
|
else:
|
|
793
796
|
continue
|
|
794
|
-
prop_name =
|
|
797
|
+
prop_name = prop.view_property
|
|
795
798
|
view.properties[prop_name] = view_property
|
|
796
799
|
|
|
797
800
|
node_types = dm.NodeApplyList([])
|
|
798
801
|
parent_views = {parent for view in views for parent in view.implements or []}
|
|
799
|
-
node_type_flag = False
|
|
800
802
|
for view in views:
|
|
801
803
|
ref_containers = sorted(view.referenced_containers(), key=lambda c: c.as_tuple())
|
|
804
|
+
dms_view = dms_view_by_id.get(view.as_id())
|
|
802
805
|
has_data = dm.filters.HasData(containers=list(ref_containers)) if ref_containers else None
|
|
803
|
-
|
|
806
|
+
if dms_view and isinstance(dms_view.reference, ReferenceEntity):
|
|
807
|
+
# If the view is a reference, we implement the reference view,
|
|
808
|
+
# and need the filter to match the reference
|
|
809
|
+
ref_view = dms_view.reference.as_id(False, default_space, default_version)
|
|
810
|
+
node_type = dm.filters.Equals(
|
|
811
|
+
["node", "type"], {"space": ref_view.space, "externalId": ref_view.external_id}
|
|
812
|
+
)
|
|
813
|
+
else:
|
|
814
|
+
node_type = dm.filters.Equals(["node", "type"], {"space": view.space, "externalId": view.external_id})
|
|
804
815
|
if view.as_id() in parent_views:
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
# Child filter without container properties
|
|
808
|
-
if node_type_flag:
|
|
809
|
-
# Transformations do not yet support setting node type.
|
|
816
|
+
if dms_view and dms_view.filter_ == "nodeType":
|
|
817
|
+
warnings.warn(issues.dms.NodeTypeFilterOnParentViewWarning(view.as_id()), stacklevel=2)
|
|
810
818
|
view.filter = node_type
|
|
811
819
|
node_types.append(dm.NodeApply(space=view.space, external_id=view.external_id, sources=[]))
|
|
820
|
+
else:
|
|
821
|
+
view.filter = has_data
|
|
822
|
+
elif has_data is None:
|
|
823
|
+
# Child filter without container properties
|
|
824
|
+
if dms_view and dms_view.filter_ == "hasData":
|
|
825
|
+
warnings.warn(issues.dms.HasDataFilterOnNoPropertiesViewWarning(view.as_id()), stacklevel=2)
|
|
826
|
+
view.filter = node_type
|
|
827
|
+
node_types.append(dm.NodeApply(space=view.space, external_id=view.external_id, sources=[]))
|
|
812
828
|
else:
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
829
|
+
if dms_view and (dms_view.filter_ == "hasData" or dms_view.filter_ is None):
|
|
830
|
+
# Default option
|
|
831
|
+
view.filter = has_data
|
|
832
|
+
elif dms_view and dms_view.filter_ == "nodeType":
|
|
833
|
+
view.filter = node_type
|
|
817
834
|
node_types.append(dm.NodeApply(space=view.space, external_id=view.external_id, sources=[]))
|
|
818
835
|
else:
|
|
819
836
|
view.filter = has_data
|
|
@@ -826,10 +843,7 @@ class _DMSExporter:
|
|
|
826
843
|
default_space: str,
|
|
827
844
|
) -> dm.ContainerApplyList:
|
|
828
845
|
containers = dm.ContainerApplyList(
|
|
829
|
-
[
|
|
830
|
-
dms_container.as_container(default_space, self.standardize_casing)
|
|
831
|
-
for dms_container in dms_container or []
|
|
832
|
-
]
|
|
846
|
+
[dms_container.as_container(default_space) for dms_container in dms_container or []]
|
|
833
847
|
)
|
|
834
848
|
container_to_drop = set()
|
|
835
849
|
for container in containers:
|
|
@@ -846,7 +860,7 @@ class _DMSExporter:
|
|
|
846
860
|
else:
|
|
847
861
|
type_cls = dm.DirectRelation
|
|
848
862
|
|
|
849
|
-
prop_name =
|
|
863
|
+
prop_name = prop.container_property
|
|
850
864
|
|
|
851
865
|
if type_cls is dm.DirectRelation:
|
|
852
866
|
container.properties[prop_name] = dm.ContainerProperty(
|
|
@@ -907,21 +921,21 @@ class _DMSExporter:
|
|
|
907
921
|
container_properties_by_id: dict[dm.ContainerId, list[DMSProperty]] = defaultdict(list)
|
|
908
922
|
view_properties_by_id: dict[dm.ViewId, list[DMSProperty]] = defaultdict(list)
|
|
909
923
|
for prop in rules.properties:
|
|
910
|
-
view_id = prop.view.as_id(False, default_space, default_version
|
|
924
|
+
view_id = prop.view.as_id(False, default_space, default_version)
|
|
911
925
|
view_properties_by_id[view_id].append(prop)
|
|
912
926
|
|
|
913
927
|
if prop.container and prop.container_property:
|
|
914
928
|
if prop.relation == "direct" and prop.is_list:
|
|
915
929
|
warnings.warn(
|
|
916
930
|
issues.dms.DirectRelationListWarning(
|
|
917
|
-
container_id=prop.container.as_id(default_space
|
|
918
|
-
view_id=prop.view.as_id(False, default_space, default_version
|
|
931
|
+
container_id=prop.container.as_id(default_space),
|
|
932
|
+
view_id=prop.view.as_id(False, default_space, default_version),
|
|
919
933
|
property=prop.container_property,
|
|
920
934
|
),
|
|
921
935
|
stacklevel=2,
|
|
922
936
|
)
|
|
923
937
|
continue
|
|
924
|
-
container_id = prop.container.as_id(default_space
|
|
938
|
+
container_id = prop.container.as_id(default_space)
|
|
925
939
|
container_properties_by_id[container_id].append(prop)
|
|
926
940
|
|
|
927
941
|
return container_properties_by_id, view_properties_by_id
|
|
@@ -102,7 +102,6 @@ class RulesToDMS(Step):
|
|
|
102
102
|
if multi_space_components_create
|
|
103
103
|
else {input_rules.metadata.space if isinstance(input_rules, DMSRules) else input_rules.metadata.prefix},
|
|
104
104
|
existing_handling=existing_components_handling,
|
|
105
|
-
standardize_casing=False,
|
|
106
105
|
)
|
|
107
106
|
|
|
108
107
|
output_dir = self.data_store_path / Path("staging")
|
|
@@ -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=AZKRaRERnGxRM4Vpp2s8T1LNhTACd4GALrf4Zjj-jCM,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=xnKdBE_dtq1nRvKa79YGA_wimI5UhoSRuBQz4LkLzQw,4606
|
|
@@ -10,7 +10,7 @@ cognite/neat/app/api/data_classes/configuration.py,sha256=oBnnX6Zam7TOstiLpQbi3G
|
|
|
10
10
|
cognite/neat/app/api/data_classes/rest.py,sha256=yVWqFkBCDCGooOWaE5nun4No8B-PBa6svdenIjBINdo,1675
|
|
11
11
|
cognite/neat/app/api/explorer.py,sha256=OlLI-RbQGjXEuDgtmFfBuTXfnRVemTJDKbL9VvXLr6Y,1891
|
|
12
12
|
cognite/neat/app/api/routers/configuration.py,sha256=tFiEbtFHNVehMwM8T-IvnWpDOL_y-wCt-wd5w-Z4PQk,554
|
|
13
|
-
cognite/neat/app/api/routers/core.py,sha256=
|
|
13
|
+
cognite/neat/app/api/routers/core.py,sha256=ZU7sZ1QgFtrNHR-Y6DVzxLZSIJoZZeCvlXLgp2oO46U,3583
|
|
14
14
|
cognite/neat/app/api/routers/crud.py,sha256=Cnvw77JWCs_wzeoQYdWwGfFns8LgtYmsYWgKPtud3BA,4646
|
|
15
15
|
cognite/neat/app/api/routers/data_exploration.py,sha256=XlpEbggy1mK1XmwVtLZmrXWulTzdaGQaKAyHs3H04qU,13653
|
|
16
16
|
cognite/neat/app/api/routers/metrics.py,sha256=S_bUQk_GjfQq7WbEhSVdow4MUYBZ_bZNafzgcKogXK8,210
|
|
@@ -39,7 +39,7 @@ cognite/neat/app/ui/neat-app/build/static/js/main.2efd96b2.js.LICENSE.txt,sha256
|
|
|
39
39
|
cognite/neat/app/ui/neat-app/build/static/js/main.2efd96b2.js.map,sha256=zUzgUBAKbtIFkXu3E6wz1hL7IKv8Xi86iym_P5OPATU,6234629
|
|
40
40
|
cognite/neat/app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg,sha256=EYf9q9JoVJ1L1np-XloeEZXCmaibzKmmpXCKn_44xzA,240334
|
|
41
41
|
cognite/neat/config.py,sha256=5_dTuoL5crCmbSdKNuNOMQJVPNGiPy7ib2MlMkgPlOM,1503
|
|
42
|
-
cognite/neat/constants.py,sha256=
|
|
42
|
+
cognite/neat/constants.py,sha256=5ujOhkmobXBleswTahlLltQwDu9bl7w00nwrVyr5_0k,1205
|
|
43
43
|
cognite/neat/exceptions.py,sha256=CM7aCvbek9klOgjTsJ9bfEA8t7KTAL6dc7Mviu4NvSI,4268
|
|
44
44
|
cognite/neat/graph/__init__.py,sha256=31uTeejWOSd-I8iUG8GOZFhHZcQCsBitJ6X8vu2r1nU,73
|
|
45
45
|
cognite/neat/graph/examples/Knowledge-Graph-Nordic44-dirty.xml,sha256=ujJip6XBs5n8enVDPzNnuGkMBwv8g21tIr1sEVJpK5M,1439359
|
|
@@ -113,9 +113,9 @@ cognite/neat/rules/exporter/_validation.py,sha256=CnEer181-EdnWl3yOuZJKOJ80W7H7q
|
|
|
113
113
|
cognite/neat/rules/exporters/__init__.py,sha256=Gn3CjkVKHJF9Po1ZPH4wAJ-sRW9up7b2CpXm-eReV3Q,413
|
|
114
114
|
cognite/neat/rules/exporters/_base.py,sha256=aRCzRjGDoXILkGvASev2UjVvLXrVCRTFiKgYwzJZqAo,1518
|
|
115
115
|
cognite/neat/rules/exporters/_models.py,sha256=f_RbFhoyD27z6Dsk4upYMHp7JHZRCfIvZsEzH5JSvtc,1645
|
|
116
|
-
cognite/neat/rules/exporters/_rules2dms.py,sha256=
|
|
116
|
+
cognite/neat/rules/exporters/_rules2dms.py,sha256=JPwaD42STCLi3N2zH8fKeKhWTNUsT-KsM3t56Pzap3A,11854
|
|
117
117
|
cognite/neat/rules/exporters/_rules2excel.py,sha256=uK-aS9sBwXUCFQLIO1jciBQrB4w7ySF6MafDqG23hrQ,9080
|
|
118
|
-
cognite/neat/rules/exporters/_rules2ontology.py,sha256=
|
|
118
|
+
cognite/neat/rules/exporters/_rules2ontology.py,sha256=PqE62FIPWFWUkivyxzOUVFf9Nx8yVJghZBB9A_7us4Q,19922
|
|
119
119
|
cognite/neat/rules/exporters/_rules2yaml.py,sha256=HKgFlpgD7U2vWLQC_VdSvkDk19QgeVAd_J8wQ9ZgrN8,3038
|
|
120
120
|
cognite/neat/rules/exporters/_validation.py,sha256=E6nn1QicaJpDFiXrYRzwTqsJ5VULVzzeqhCBVlRxH4M,4092
|
|
121
121
|
cognite/neat/rules/importer/__init__.py,sha256=h1owL8pBPEtOmlIFAdkqAABH1A_Op5plh8C53og0uag,636
|
|
@@ -142,14 +142,14 @@ cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py,sha256=QThkjUuEVWI19yn
|
|
|
142
142
|
cognite/neat/rules/importers/_dtdl2rules/spec.py,sha256=7Kd4WJEVoRoM8ssvl5zv2ALHDh3Mc0EUJ7v_6i8xJOk,11926
|
|
143
143
|
cognite/neat/rules/importers/_owl2rules/__init__.py,sha256=tdGcrgtozdQyST-pTlxIa4cLBNTLvtk1nNYR4vOdFSw,63
|
|
144
144
|
cognite/neat/rules/importers/_owl2rules/_owl2classes.py,sha256=tCEwwJPCdFLodM0q0gXfbyEe0rZvIG4emDTcUzDYRWI,7597
|
|
145
|
-
cognite/neat/rules/importers/_owl2rules/_owl2metadata.py,sha256=
|
|
146
|
-
cognite/neat/rules/importers/_owl2rules/_owl2properties.py,sha256=
|
|
147
|
-
cognite/neat/rules/importers/_owl2rules/_owl2rules.py,sha256=
|
|
148
|
-
cognite/neat/rules/importers/_spreadsheet2rules.py,sha256=
|
|
145
|
+
cognite/neat/rules/importers/_owl2rules/_owl2metadata.py,sha256=UPOPBYpOjpOdV8Bcbx31O62Tkjzg-nGjx8uE4JD-HoQ,7791
|
|
146
|
+
cognite/neat/rules/importers/_owl2rules/_owl2properties.py,sha256=VW0pC09Hm_EC0O89HX8RrGCpcemaWshIW7hKHWWZ63I,7443
|
|
147
|
+
cognite/neat/rules/importers/_owl2rules/_owl2rules.py,sha256=xVGOo5wnoXLI3E9OUIDfvpNLOIAhkbcC6--PVkUhgk4,7120
|
|
148
|
+
cognite/neat/rules/importers/_spreadsheet2rules.py,sha256=QIYTW-3Uc5ffFp3rJNyiqigWvc6x2eZTvhxqrXlGGSs,11481
|
|
149
149
|
cognite/neat/rules/importers/_yaml2rules.py,sha256=sIaYY3Zo--v1cXSu65n4ZPv47cS-5InvSbpkw3Ahov4,4198
|
|
150
150
|
cognite/neat/rules/issues/__init__.py,sha256=Ms6jgCxCezc5IgTOwCFtXQPtoVFfOvdcXj84_rs917I,563
|
|
151
151
|
cognite/neat/rules/issues/base.py,sha256=dS4lmbW9VQ8awgOIB-Ah9z9LdNbCa-fPd8tbyPb8sM4,5856
|
|
152
|
-
cognite/neat/rules/issues/dms.py,sha256=
|
|
152
|
+
cognite/neat/rules/issues/dms.py,sha256=WB8N6MPbLFxdScJLGGY_zdErcrEXJnAsMladMB5aKa4,12722
|
|
153
153
|
cognite/neat/rules/issues/fileread.py,sha256=n-GZaULOJF_MKkBIh1maaOuGZXOvZYw7Y6fDAS0jrBI,4492
|
|
154
154
|
cognite/neat/rules/issues/formatters.py,sha256=_pSogWtfkt2JK0PZgWQffbj2On8vumFNshxOKAi5fYw,3346
|
|
155
155
|
cognite/neat/rules/issues/importing.py,sha256=GqUywhBD840Fbc4DD5L2I0oEllJ78MTjpmXogVEjihA,7493
|
|
@@ -159,11 +159,11 @@ cognite/neat/rules/models/__init__.py,sha256=23T73EaHuS0dsYTh6tww6gXAc7S4rdx8e5n
|
|
|
159
159
|
cognite/neat/rules/models/_base.py,sha256=1WNXBJHJ3nwnVoeNhpm9B6TBuqWYYgChkrdK4FJIpQM,4989
|
|
160
160
|
cognite/neat/rules/models/_rules/__init__.py,sha256=jA4kMOAg4GJZjhCW1ovSjUCv-Top7O2HocZV0zJ78o4,517
|
|
161
161
|
cognite/neat/rules/models/_rules/_types/__init__.py,sha256=Px0uB5fqk-8qH-HRi0ZvgGkLhYcS5A8EJ9QDB2TFwNQ,1299
|
|
162
|
-
cognite/neat/rules/models/_rules/_types/_base.py,sha256=
|
|
162
|
+
cognite/neat/rules/models/_rules/_types/_base.py,sha256=okf8ebEcKrXf1rZQNp_cMsTS4pNKdJk2QMz_8-wi_so,16681
|
|
163
163
|
cognite/neat/rules/models/_rules/_types/_field.py,sha256=dOVAU1jWCupFVnrYYwLfI-nNUC4rv4vXHMzpiObtWiw,10295
|
|
164
164
|
cognite/neat/rules/models/_rules/_types/_value.py,sha256=ubyWmU6neyNxx17fqcciIjyB-CIpYNUuM97Xh2sVrYo,6308
|
|
165
165
|
cognite/neat/rules/models/_rules/base.py,sha256=9DgtdCmpz84sMFxZB_stWkalVbjA4HQKsTMpSjjOVLU,10635
|
|
166
|
-
cognite/neat/rules/models/_rules/dms_architect_rules.py,sha256=
|
|
166
|
+
cognite/neat/rules/models/_rules/dms_architect_rules.py,sha256=bsuwCFiVFnDQuoAdwTu_-kp5oJGepuD24kLO9B_5YYc,50424
|
|
167
167
|
cognite/neat/rules/models/_rules/dms_schema.py,sha256=-ru40beGY2WJvf9_sd5eO2Wh8x2qLQ2UmgzExloBWac,30229
|
|
168
168
|
cognite/neat/rules/models/_rules/domain_rules.py,sha256=mOE4M6wOurmnAehxbnxvP9vIVXsFuKSiyMmD1shXKpA,2051
|
|
169
169
|
cognite/neat/rules/models/_rules/information_rules.py,sha256=kETaZ3HsPw0EFXIeU-YhfEZeK4FhAh5RvNQAWbrcE-E,21026
|
|
@@ -211,7 +211,7 @@ cognite/neat/workflows/steps/lib/graph_extractor.py,sha256=vW9UpJScx5dFVCSairpOd
|
|
|
211
211
|
cognite/neat/workflows/steps/lib/graph_loader.py,sha256=HfGg1HRZhbV58TFu89FTjKeUxGsbCYLeFJIQFDN_pQM,2341
|
|
212
212
|
cognite/neat/workflows/steps/lib/graph_store.py,sha256=NV5SZFTrbq_gtZZZdoEL8tIohdbsieKmpwrdWAHis-E,6288
|
|
213
213
|
cognite/neat/workflows/steps/lib/io_steps.py,sha256=QAGypoi1vP32BRiIgBZ0B4qsbFMcwhzpRiVUUnWysLA,16874
|
|
214
|
-
cognite/neat/workflows/steps/lib/rules_exporter.py,sha256=
|
|
214
|
+
cognite/neat/workflows/steps/lib/rules_exporter.py,sha256=RDsRaRb9xRgFLO-x5qodDfyfDzQqXT0A-i2Se-FcFfI,18234
|
|
215
215
|
cognite/neat/workflows/steps/lib/rules_importer.py,sha256=tckiHIS9Vnqx3df62jdtNwSm-Nk_XQwgo1sM0zKOw_s,7327
|
|
216
216
|
cognite/neat/workflows/steps/lib/rules_validator.py,sha256=uGXcMncrtAu3IAMY3dqDNLfoL7sKSLzU1jz85xNrtO4,4785
|
|
217
217
|
cognite/neat/workflows/steps/lib/v1/__init__.py,sha256=725aFzVqhE0tbVOAW70zWXTGKFiYImVupRZ4C5_IkUo,274
|
|
@@ -227,8 +227,8 @@ cognite/neat/workflows/steps_registry.py,sha256=PZVoHX4d6Vmjz6XzUFnFFWMCnrVnqkUC
|
|
|
227
227
|
cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
|
|
228
228
|
cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
|
|
229
229
|
cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
|
|
230
|
-
cognite_neat-0.72.
|
|
231
|
-
cognite_neat-0.72.
|
|
232
|
-
cognite_neat-0.72.
|
|
233
|
-
cognite_neat-0.72.
|
|
234
|
-
cognite_neat-0.72.
|
|
230
|
+
cognite_neat-0.72.3.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
|
|
231
|
+
cognite_neat-0.72.3.dist-info/METADATA,sha256=4KC49y0DSL83A7qqb5OkxQv78ryoxQR22uLNhJs24Js,9321
|
|
232
|
+
cognite_neat-0.72.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
233
|
+
cognite_neat-0.72.3.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
|
|
234
|
+
cognite_neat-0.72.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|