cognite-neat 0.121.1__py3-none-any.whl → 0.122.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/core/_client/_api/statistics.py +91 -0
- cognite/neat/core/_client/_api_client.py +2 -0
- cognite/neat/core/_client/data_classes/statistics.py +125 -0
- cognite/neat/core/_client/testing.py +4 -0
- cognite/neat/core/_constants.py +6 -7
- cognite/neat/core/_data_model/_constants.py +23 -16
- cognite/neat/core/_data_model/_shared.py +33 -17
- cognite/neat/core/_data_model/analysis/__init__.py +2 -2
- cognite/neat/core/_data_model/analysis/_base.py +186 -183
- cognite/neat/core/_data_model/catalog/__init__.py +2 -2
- cognite/neat/core/_data_model/exporters/__init__.py +6 -6
- cognite/neat/core/_data_model/exporters/_base.py +10 -8
- cognite/neat/core/_data_model/exporters/{_rules2dms.py → _data_model2dms.py} +22 -18
- cognite/neat/core/_data_model/exporters/{_rules2excel.py → _data_model2excel.py} +51 -51
- cognite/neat/core/_data_model/exporters/{_rules2instance_template.py → _data_model2instance_template.py} +14 -14
- cognite/neat/core/_data_model/exporters/{_rules2ontology.py → _data_model2ontology.py} +50 -50
- cognite/neat/core/_data_model/exporters/{_rules2yaml.py → _data_model2yaml.py} +21 -18
- cognite/neat/core/_data_model/importers/__init__.py +8 -8
- cognite/neat/core/_data_model/importers/_base.py +8 -6
- cognite/neat/core/_data_model/importers/_base_file_reader.py +56 -0
- cognite/neat/core/_data_model/importers/{_yaml2rules.py → _dict2data_model.py} +50 -25
- cognite/neat/core/_data_model/importers/{_dms2rules.py → _dms2data_model.py} +58 -49
- cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/dtdl_converter.py +22 -22
- cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/dtdl_importer.py +7 -7
- cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/spec.py +3 -3
- cognite/neat/core/_data_model/importers/_rdf/__init__.py +3 -3
- cognite/neat/core/_data_model/importers/_rdf/_base.py +15 -15
- cognite/neat/core/_data_model/importers/_rdf/{_imf2rules.py → _imf2data_model.py} +17 -17
- cognite/neat/core/_data_model/importers/_rdf/{_inference2rules.py → _inference2rdata_model.py} +59 -59
- cognite/neat/core/_data_model/importers/_rdf/{_owl2rules.py → _owl2data_model.py} +17 -17
- cognite/neat/core/_data_model/importers/_rdf/_shared.py +25 -25
- cognite/neat/core/_data_model/importers/{_spreadsheet2rules.py → _spreadsheet2data_model.py} +76 -19
- cognite/neat/core/_data_model/models/__init__.py +11 -9
- cognite/neat/core/_data_model/models/_base_unverified.py +12 -12
- cognite/neat/core/_data_model/models/_base_verified.py +9 -14
- cognite/neat/core/_data_model/models/_types.py +6 -6
- cognite/neat/core/_data_model/models/conceptual/__init__.py +6 -6
- cognite/neat/core/_data_model/models/conceptual/_unverified.py +20 -20
- cognite/neat/core/_data_model/models/conceptual/_validation.py +88 -78
- cognite/neat/core/_data_model/models/conceptual/_verified.py +54 -52
- cognite/neat/core/_data_model/models/data_types.py +2 -2
- cognite/neat/core/_data_model/models/entities/__init__.py +8 -8
- cognite/neat/core/_data_model/models/entities/_loaders.py +11 -10
- cognite/neat/core/_data_model/models/entities/_multi_value.py +5 -5
- cognite/neat/core/_data_model/models/entities/_single_value.py +44 -38
- cognite/neat/core/_data_model/models/entities/_types.py +9 -3
- cognite/neat/core/_data_model/models/entities/_wrapped.py +3 -3
- cognite/neat/core/_data_model/models/mapping/_classic2core.py +12 -9
- cognite/neat/core/_data_model/models/physical/__init__.py +40 -0
- cognite/neat/core/_data_model/models/{dms → physical}/_exporter.py +75 -55
- cognite/neat/core/_data_model/models/{dms/_rules_input.py → physical/_unverified.py} +48 -39
- cognite/neat/core/_data_model/models/{dms → physical}/_validation.py +17 -15
- cognite/neat/core/_data_model/models/{dms/_rules.py → physical/_verified.py} +68 -60
- cognite/neat/core/_data_model/transformers/__init__.py +29 -25
- cognite/neat/core/_data_model/transformers/_base.py +27 -20
- cognite/neat/core/_data_model/transformers/_converters.py +707 -622
- cognite/neat/core/_data_model/transformers/_mapping.py +74 -55
- cognite/neat/core/_data_model/transformers/_verification.py +64 -55
- cognite/neat/core/_instances/extractors/_base.py +2 -2
- cognite/neat/core/_instances/extractors/_classic_cdf/_classic.py +9 -9
- cognite/neat/core/_instances/extractors/_dms_graph.py +42 -34
- cognite/neat/core/_instances/extractors/_mock_graph_generator.py +107 -103
- cognite/neat/core/_instances/loaders/_base.py +3 -3
- cognite/neat/core/_instances/loaders/_rdf2dms.py +22 -22
- cognite/neat/core/_instances/transformers/_base.py +7 -4
- cognite/neat/core/_instances/transformers/_rdfpath.py +1 -1
- cognite/neat/core/_instances/transformers/_value_type.py +2 -6
- cognite/neat/core/_issues/_base.py +4 -4
- cognite/neat/core/_issues/_factory.py +1 -1
- cognite/neat/core/_issues/errors/__init__.py +2 -2
- cognite/neat/core/_issues/errors/_resources.py +1 -1
- cognite/neat/core/_issues/errors/_wrapper.py +2 -2
- cognite/neat/core/_issues/warnings/_models.py +4 -4
- cognite/neat/core/_issues/warnings/_properties.py +1 -1
- cognite/neat/core/_store/__init__.py +3 -3
- cognite/neat/core/_store/{_rules_store.py → _data_model.py} +119 -112
- cognite/neat/core/_store/{_graph_store.py → _instance.py} +3 -4
- cognite/neat/core/_store/_provenance.py +2 -2
- cognite/neat/core/_store/exceptions.py +2 -2
- cognite/neat/core/_utils/rdf_.py +14 -0
- cognite/neat/core/_utils/text.py +1 -1
- cognite/neat/session/_base.py +42 -36
- cognite/neat/session/_drop.py +2 -2
- cognite/neat/session/_experimental.py +1 -1
- cognite/neat/session/_inspect.py +13 -13
- cognite/neat/session/_mapping.py +15 -9
- cognite/neat/session/_read.py +39 -37
- cognite/neat/session/_set.py +6 -6
- cognite/neat/session/_show.py +24 -21
- cognite/neat/session/_state/README.md +1 -1
- cognite/neat/session/_state.py +27 -27
- cognite/neat/session/_subset.py +14 -11
- cognite/neat/session/_template.py +23 -21
- cognite/neat/session/_to.py +42 -42
- {cognite_neat-0.121.1.dist-info → cognite_neat-0.122.0.dist-info}/METADATA +14 -7
- {cognite_neat-0.121.1.dist-info → cognite_neat-0.122.0.dist-info}/RECORD +102 -100
- cognite/neat/core/_data_model/exporters/_validation.py +0 -14
- cognite/neat/core/_data_model/models/dms/__init__.py +0 -32
- /cognite/neat/core/_data_model/catalog/{info-rules-imf.xlsx → conceptual-imf-data-model.xlsx} +0 -0
- /cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/__init__.py +0 -0
- /cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/_unit_lookup.py +0 -0
- {cognite_neat-0.121.1.dist-info → cognite_neat-0.122.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.121.1.dist-info → cognite_neat-0.122.0.dist-info}/licenses/LICENSE +0 -0
cognite/neat/core/_data_model/importers/{_spreadsheet2rules.py → _spreadsheet2data_model.py}
RENAMED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
"""This module performs importing of
|
|
2
|
-
In more details, it traverses the graph and abstracts class and properties, basically
|
|
3
|
-
generating a list of rules based on which nodes that form the graph are made.
|
|
4
|
-
"""
|
|
1
|
+
"""This module performs importing of data model from spreadsheets."""
|
|
5
2
|
|
|
3
|
+
import tempfile
|
|
6
4
|
from collections import UserDict, defaultdict
|
|
7
5
|
from dataclasses import dataclass
|
|
8
6
|
from pathlib import Path
|
|
9
7
|
from typing import Literal, cast
|
|
10
8
|
|
|
11
9
|
import pandas as pd
|
|
10
|
+
from openpyxl import load_workbook
|
|
11
|
+
from openpyxl.worksheet.worksheet import Worksheet
|
|
12
12
|
from pandas import ExcelFile
|
|
13
13
|
from rdflib import Namespace, URIRef
|
|
14
14
|
|
|
15
|
-
from cognite.neat.core._data_model._shared import
|
|
15
|
+
from cognite.neat.core._data_model._shared import (
|
|
16
|
+
ImportedDataModel,
|
|
17
|
+
T_UnverifiedDataModel,
|
|
18
|
+
)
|
|
16
19
|
from cognite.neat.core._data_model.models import (
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
UNVERIFIED_DATA_MODEL_BY_ROLE,
|
|
21
|
+
VERIFIED_DATA_MODEL_BY_ROLE,
|
|
19
22
|
RoleTypes,
|
|
20
23
|
SchemaCompleteness,
|
|
21
24
|
)
|
|
@@ -36,11 +39,11 @@ SOURCE_SHEET__TARGET_FIELD__HEADERS = [
|
|
|
36
39
|
"Properties",
|
|
37
40
|
"Properties",
|
|
38
41
|
{
|
|
39
|
-
RoleTypes.information: ["
|
|
42
|
+
RoleTypes.information: ["Concept", "Property"],
|
|
40
43
|
RoleTypes.dms: ["View", "View Property"],
|
|
41
44
|
},
|
|
42
45
|
),
|
|
43
|
-
("
|
|
46
|
+
("Concepts", "Concepts", ["Concept"]),
|
|
44
47
|
("Containers", "Containers", ["Container"]),
|
|
45
48
|
("Views", "Views", ["View"]),
|
|
46
49
|
("Enum", "Enum", ["Collection"]),
|
|
@@ -52,8 +55,8 @@ MANDATORY_SHEETS_BY_ROLE: dict[RoleTypes, set[str]] = {
|
|
|
52
55
|
role_type: {
|
|
53
56
|
str(sheet_name)
|
|
54
57
|
for sheet_name in (
|
|
55
|
-
|
|
56
|
-
if
|
|
58
|
+
VERIFIED_DATA_MODEL_BY_ROLE.get(role_type).mandatory_fields(use_alias=True) # type: ignore
|
|
59
|
+
if VERIFIED_DATA_MODEL_BY_ROLE.get(role_type)
|
|
57
60
|
else []
|
|
58
61
|
)
|
|
59
62
|
if sheet_name is not None
|
|
@@ -242,8 +245,8 @@ class SpreadsheetReader:
|
|
|
242
245
|
return sheets, read_info_by_sheet
|
|
243
246
|
|
|
244
247
|
|
|
245
|
-
class ExcelImporter(BaseImporter[
|
|
246
|
-
"""Import
|
|
248
|
+
class ExcelImporter(BaseImporter[T_UnverifiedDataModel]):
|
|
249
|
+
"""Import data_model from an Excel file.
|
|
247
250
|
|
|
248
251
|
Args:
|
|
249
252
|
filepath (Path): The path to the Excel file.
|
|
@@ -252,14 +255,15 @@ class ExcelImporter(BaseImporter[T_InputRules]):
|
|
|
252
255
|
def __init__(self, filepath: Path):
|
|
253
256
|
self.filepath = filepath
|
|
254
257
|
|
|
255
|
-
def
|
|
258
|
+
def to_data_model(self) -> ImportedDataModel[T_UnverifiedDataModel]:
|
|
256
259
|
issue_list = IssueList(title=f"'{self.filepath.name}'")
|
|
257
260
|
if not self.filepath.exists():
|
|
258
261
|
raise FileNotFoundNeatError(self.filepath)
|
|
259
262
|
|
|
263
|
+
self.filepath = self._make_forward_compatible_spreadsheet(self.filepath)
|
|
264
|
+
|
|
260
265
|
with pd.ExcelFile(self.filepath) as excel_file:
|
|
261
266
|
user_reader = SpreadsheetReader(issue_list)
|
|
262
|
-
|
|
263
267
|
user_read = user_reader.read(excel_file, self.filepath)
|
|
264
268
|
|
|
265
269
|
issue_list.trigger_warnings()
|
|
@@ -267,15 +271,23 @@ class ExcelImporter(BaseImporter[T_InputRules]):
|
|
|
267
271
|
raise MultiValueError(issue_list.errors)
|
|
268
272
|
|
|
269
273
|
if user_read is None:
|
|
270
|
-
return
|
|
274
|
+
return ImportedDataModel(None, {})
|
|
271
275
|
|
|
272
276
|
sheets = user_read.sheets
|
|
273
277
|
original_role = user_read.role
|
|
274
278
|
read_info_by_sheet = user_read.read_info_by_sheet
|
|
275
279
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
280
|
+
data_model_cls = UNVERIFIED_DATA_MODEL_BY_ROLE[original_role]
|
|
281
|
+
data_model = cast(T_UnverifiedDataModel, data_model_cls.load(sheets))
|
|
282
|
+
|
|
283
|
+
# Delete the temporary file if it was created
|
|
284
|
+
if "temp_neat_file" in self.filepath.name:
|
|
285
|
+
try:
|
|
286
|
+
self.filepath.unlink()
|
|
287
|
+
except Exception as e:
|
|
288
|
+
issue_list.append(FileReadError(self.filepath, f"Failed to delete temporary file: {e}"))
|
|
289
|
+
|
|
290
|
+
return ImportedDataModel(data_model, read_info_by_sheet)
|
|
279
291
|
|
|
280
292
|
@property
|
|
281
293
|
def description(self) -> str:
|
|
@@ -284,3 +296,48 @@ class ExcelImporter(BaseImporter[T_InputRules]):
|
|
|
284
296
|
@property
|
|
285
297
|
def source_uri(self) -> URIRef:
|
|
286
298
|
return URIRef(f"file://{self.filepath.name}")
|
|
299
|
+
|
|
300
|
+
def _make_forward_compatible_spreadsheet(self, filepath: Path) -> Path:
|
|
301
|
+
"""Makes the spreadsheet forward compatible by renaming legacy class with concept
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
filepath (Path): The path to the Excel file.
|
|
305
|
+
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
workbook = load_workbook(filepath)
|
|
309
|
+
|
|
310
|
+
if "Classes" in workbook.sheetnames:
|
|
311
|
+
print(
|
|
312
|
+
(
|
|
313
|
+
"You are using a legacy spreadsheet format, "
|
|
314
|
+
"which we will support until v1.0 of neat."
|
|
315
|
+
" Please update your spreadsheet to the new format."
|
|
316
|
+
),
|
|
317
|
+
)
|
|
318
|
+
_replace_class_with_concept_cell(workbook["Classes"])
|
|
319
|
+
sheet = workbook["Classes"]
|
|
320
|
+
sheet.title = "Concepts"
|
|
321
|
+
|
|
322
|
+
if "Properties" in workbook.sheetnames:
|
|
323
|
+
_replace_class_with_concept_cell(workbook["Properties"])
|
|
324
|
+
|
|
325
|
+
with tempfile.NamedTemporaryFile(prefix="temp_neat_file", suffix=".xlsx", delete=False) as temp_file:
|
|
326
|
+
workbook.save(temp_file.name)
|
|
327
|
+
return Path(temp_file.name)
|
|
328
|
+
|
|
329
|
+
else:
|
|
330
|
+
return filepath
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _replace_class_with_concept_cell(sheet: Worksheet) -> None:
|
|
334
|
+
"""
|
|
335
|
+
Replaces the word "Class" with "Concept" in the first row of the given sheet.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
sheet (Worksheet): The sheet in which to replace the word "Class".
|
|
339
|
+
"""
|
|
340
|
+
for row in sheet.iter_rows():
|
|
341
|
+
for cell in row:
|
|
342
|
+
if cell.value == "Class":
|
|
343
|
+
cell.value = "Concept"
|
|
@@ -7,30 +7,32 @@ from cognite.neat.core._data_model.models.conceptual._verified import (
|
|
|
7
7
|
)
|
|
8
8
|
|
|
9
9
|
from ._base_verified import DataModelType, ExtensionCategory, RoleTypes, SchemaCompleteness, SheetList, SheetRow
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
10
|
+
from .physical._unverified import UnverifiedPhysicalDataModel
|
|
11
|
+
from .physical._verified import PhysicalDataModel
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
UNVERIFIED_DATA_MODEL_BY_ROLE: dict[
|
|
14
|
+
RoleTypes, type[UnverifiedConceptualDataModel] | type[UnverifiedPhysicalDataModel]
|
|
15
|
+
] = {
|
|
14
16
|
RoleTypes.information: UnverifiedConceptualDataModel,
|
|
15
|
-
RoleTypes.dms:
|
|
17
|
+
RoleTypes.dms: UnverifiedPhysicalDataModel,
|
|
16
18
|
}
|
|
17
|
-
|
|
19
|
+
VERIFIED_DATA_MODEL_BY_ROLE: dict[RoleTypes, type[ConceptualDataModel] | type[PhysicalDataModel]] = {
|
|
18
20
|
RoleTypes.information: ConceptualDataModel,
|
|
19
|
-
RoleTypes.dms:
|
|
21
|
+
RoleTypes.dms: PhysicalDataModel,
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
__all__ = [
|
|
24
|
-
"
|
|
26
|
+
"UNVERIFIED_DATA_MODEL_BY_ROLE",
|
|
25
27
|
"ConceptualDataModel",
|
|
26
|
-
"DMSInputRules",
|
|
27
|
-
"DMSRules",
|
|
28
28
|
"DMSSchema",
|
|
29
29
|
"DataModelType",
|
|
30
30
|
"ExtensionCategory",
|
|
31
|
+
"PhysicalDataModel",
|
|
31
32
|
"RoleTypes",
|
|
32
33
|
"SchemaCompleteness",
|
|
33
34
|
"SheetList",
|
|
34
35
|
"SheetRow",
|
|
35
36
|
"UnverifiedConceptualDataModel",
|
|
37
|
+
"UnverifiedPhysicalDataModel",
|
|
36
38
|
]
|
|
@@ -27,29 +27,29 @@ if sys.version_info >= (3, 11):
|
|
|
27
27
|
else:
|
|
28
28
|
from typing_extensions import Self
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
T_BaseDataModel = TypeVar("T_BaseDataModel", bound=BaseVerifiedDataModel)
|
|
31
|
+
T_DataModel = TypeVar("T_DataModel", bound=SchemaModel)
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@dataclass
|
|
35
|
-
class UnverifiedDataModel(Generic[
|
|
36
|
-
"""Input
|
|
35
|
+
class UnverifiedDataModel(Generic[T_BaseDataModel], ABC):
|
|
36
|
+
"""Input data model are raw data that is not yet validated."""
|
|
37
37
|
|
|
38
38
|
@classmethod
|
|
39
39
|
@abstractmethod
|
|
40
|
-
def _get_verified_cls(cls) -> type[
|
|
40
|
+
def _get_verified_cls(cls) -> type[T_BaseDataModel]:
|
|
41
41
|
raise NotImplementedError("This method should be implemented in the subclass.")
|
|
42
42
|
|
|
43
43
|
@classmethod
|
|
44
44
|
@overload
|
|
45
|
-
def load(cls: "type[
|
|
45
|
+
def load(cls: "type[T_UnverifiedDataModel]", data: dict[str, Any]) -> "T_UnverifiedDataModel": ...
|
|
46
46
|
|
|
47
47
|
@classmethod
|
|
48
48
|
@overload
|
|
49
|
-
def load(cls: "type[
|
|
49
|
+
def load(cls: "type[T_UnverifiedDataModel]", data: None) -> None: ...
|
|
50
50
|
|
|
51
51
|
@classmethod
|
|
52
|
-
def load(cls: "type[
|
|
52
|
+
def load(cls: "type[T_UnverifiedDataModel]", data: dict | None) -> "T_UnverifiedDataModel | None":
|
|
53
53
|
if data is None:
|
|
54
54
|
return None
|
|
55
55
|
return cls._load(data)
|
|
@@ -110,7 +110,7 @@ class UnverifiedDataModel(Generic[T_BaseRules], ABC):
|
|
|
110
110
|
def _dataclass_fields(self) -> list[Field]:
|
|
111
111
|
return list(fields(self))
|
|
112
112
|
|
|
113
|
-
def
|
|
113
|
+
def as_verified_data_model(self) -> T_BaseDataModel:
|
|
114
114
|
cls_ = self._get_verified_cls()
|
|
115
115
|
return cls_.model_validate(self.dump())
|
|
116
116
|
|
|
@@ -127,14 +127,14 @@ class UnverifiedDataModel(Generic[T_BaseRules], ABC):
|
|
|
127
127
|
return output
|
|
128
128
|
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
T_UnverifiedDataModel = TypeVar("T_UnverifiedDataModel", bound=UnverifiedDataModel)
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
@dataclass
|
|
134
|
-
class UnverifiedComponent(ABC, Generic[
|
|
134
|
+
class UnverifiedComponent(ABC, Generic[T_DataModel]):
|
|
135
135
|
@classmethod
|
|
136
136
|
@abstractmethod
|
|
137
|
-
def _get_verified_cls(cls) -> type[
|
|
137
|
+
def _get_verified_cls(cls) -> type[T_DataModel]:
|
|
138
138
|
raise NotImplementedError("This method should be implemented in the subclass.")
|
|
139
139
|
|
|
140
140
|
@classmethod
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""This module contains the definition of `
|
|
1
|
+
"""This module contains the definition of `DataModel` pydantic model and all
|
|
2
2
|
its sub-models and validators.
|
|
3
3
|
"""
|
|
4
4
|
|
|
@@ -30,7 +30,7 @@ from cognite.neat.core._constants import DEFAULT_NAMESPACE
|
|
|
30
30
|
from cognite.neat.core._data_model.models._types import (
|
|
31
31
|
ContainerEntityType,
|
|
32
32
|
DataModelExternalIdType,
|
|
33
|
-
|
|
33
|
+
PhysicalPropertyType,
|
|
34
34
|
SpaceType,
|
|
35
35
|
StrListType,
|
|
36
36
|
URIRefType,
|
|
@@ -160,7 +160,7 @@ class BaseVerifiedMetadata(SchemaModel):
|
|
|
160
160
|
|
|
161
161
|
creator: StrListType = Field(
|
|
162
162
|
description=(
|
|
163
|
-
"List of contributors (comma
|
|
163
|
+
"List of contributors (comma separated) to the data model creation, "
|
|
164
164
|
"typically information architects are considered as contributors."
|
|
165
165
|
),
|
|
166
166
|
)
|
|
@@ -175,7 +175,7 @@ class BaseVerifiedMetadata(SchemaModel):
|
|
|
175
175
|
|
|
176
176
|
source_id: URIRefType | None = Field(
|
|
177
177
|
None,
|
|
178
|
-
description="Id of source that produced this
|
|
178
|
+
description="Id of source that produced this data model",
|
|
179
179
|
alias="sourceId",
|
|
180
180
|
)
|
|
181
181
|
|
|
@@ -254,12 +254,7 @@ class BaseVerifiedMetadata(SchemaModel):
|
|
|
254
254
|
|
|
255
255
|
class BaseVerifiedDataModel(SchemaModel, ABC):
|
|
256
256
|
"""
|
|
257
|
-
|
|
258
|
-
definitions and (optionally) the transformation rules used to transform the data/graph
|
|
259
|
-
from the source representation to the target representation defined by the data model.
|
|
260
|
-
The rules are defined in an Excel sheet and then parsed into a `Rules` object. The
|
|
261
|
-
`Rules` object is then used to generate data model and the `RDF` graph made of data
|
|
262
|
-
model instances.
|
|
257
|
+
Data Model is a core concept in `neat`.
|
|
263
258
|
|
|
264
259
|
Args:
|
|
265
260
|
metadata: Data model metadata
|
|
@@ -317,11 +312,11 @@ class BaseVerifiedDataModel(SchemaModel, ABC):
|
|
|
317
312
|
) -> dict[str, Any]:
|
|
318
313
|
"""Dump the model to a dictionary.
|
|
319
314
|
|
|
320
|
-
This is used in the Exporters to dump
|
|
315
|
+
This is used in the Exporters to the dump data model in the required format.
|
|
321
316
|
|
|
322
317
|
Args:
|
|
323
318
|
entities_exclude_defaults: Whether to exclude default prefix (and version) for entities.
|
|
324
|
-
For example, given a class that is dumped as 'my_prefix:MyClass', if the prefix for the
|
|
319
|
+
For example, given a class that is dumped as 'my_prefix:MyClass', if the prefix for the data model
|
|
325
320
|
set in metadata.prefix = 'my_prefix', then this class will be dumped as 'MyClass' when this flag is set.
|
|
326
321
|
Defaults to True.
|
|
327
322
|
sort: Whether to sort the entities in the output.
|
|
@@ -438,7 +433,7 @@ ExtensionCategoryType = Annotated[
|
|
|
438
433
|
# Immutable such that this can be used as a key in a dictionary
|
|
439
434
|
class ContainerProperty(BaseModel, frozen=True):
|
|
440
435
|
container: ContainerEntityType
|
|
441
|
-
property_:
|
|
436
|
+
property_: PhysicalPropertyType
|
|
442
437
|
|
|
443
438
|
|
|
444
439
|
class ContainerDestinationProperty(ContainerProperty, frozen=True):
|
|
@@ -451,4 +446,4 @@ class ViewRef(BaseModel, frozen=True):
|
|
|
451
446
|
|
|
452
447
|
|
|
453
448
|
class ViewProperty(ViewRef, frozen=True):
|
|
454
|
-
property_:
|
|
449
|
+
property_: PhysicalPropertyType
|
|
@@ -23,14 +23,14 @@ from cognite.neat.core._data_model._constants import (
|
|
|
23
23
|
)
|
|
24
24
|
from cognite.neat.core._data_model.models.entities._multi_value import MultiValueTypeInfo
|
|
25
25
|
from cognite.neat.core._data_model.models.entities._single_value import (
|
|
26
|
-
|
|
26
|
+
ConceptEntity,
|
|
27
27
|
ContainerEntity,
|
|
28
28
|
ViewEntity,
|
|
29
29
|
)
|
|
30
30
|
from cognite.neat.core._issues.errors import RegexViolationError
|
|
31
31
|
from cognite.neat.core._issues.warnings import RegexViolationWarning
|
|
32
32
|
|
|
33
|
-
Entities: TypeAlias =
|
|
33
|
+
Entities: TypeAlias = ConceptEntity | ViewEntity | ContainerEntity
|
|
34
34
|
T_Entities = TypeVar("T_Entities", bound=Entities)
|
|
35
35
|
|
|
36
36
|
|
|
@@ -139,9 +139,9 @@ ConceptualPropertyType = Annotated[
|
|
|
139
139
|
str,
|
|
140
140
|
AfterValidator(_external_id_validation_factory(EntityTypes.conceptual_property, "Property column in properties")),
|
|
141
141
|
]
|
|
142
|
-
|
|
142
|
+
PhysicalPropertyType = Annotated[
|
|
143
143
|
str,
|
|
144
|
-
AfterValidator(_external_id_validation_factory(EntityTypes.
|
|
144
|
+
AfterValidator(_external_id_validation_factory(EntityTypes.physical_property, "Property column in properties")),
|
|
145
145
|
]
|
|
146
146
|
|
|
147
147
|
|
|
@@ -152,7 +152,7 @@ def _entity_validation(value: Entities, location: str) -> Entities:
|
|
|
152
152
|
return value
|
|
153
153
|
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
ConceptEntityType = Annotated[ConceptEntity, AfterValidator(lambda v: _entity_validation(v, "the Class column"))]
|
|
156
156
|
ViewEntityType = Annotated[ViewEntity, AfterValidator(lambda v: _entity_validation(v, "the View column"))]
|
|
157
157
|
ContainerEntityType = Annotated[
|
|
158
158
|
ContainerEntity, AfterValidator(lambda v: _entity_validation(v, "the Container column"))
|
|
@@ -161,7 +161,7 @@ ContainerEntityType = Annotated[
|
|
|
161
161
|
|
|
162
162
|
def _multi_value_type_validation(value: MultiValueTypeInfo, location: str) -> MultiValueTypeInfo:
|
|
163
163
|
for type_ in value.types:
|
|
164
|
-
if isinstance(type_,
|
|
164
|
+
if isinstance(type_, ConceptEntity):
|
|
165
165
|
_entity_validation(type_, location)
|
|
166
166
|
return value
|
|
167
167
|
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
from ._unverified import (
|
|
2
|
-
|
|
2
|
+
UnverifiedConcept,
|
|
3
3
|
UnverifiedConceptualDataModel,
|
|
4
4
|
UnverifiedConceptualMetadata,
|
|
5
5
|
UnverifiedConceptualProperty,
|
|
6
6
|
)
|
|
7
|
-
from ._validation import
|
|
7
|
+
from ._validation import ConceptualValidation
|
|
8
8
|
from ._verified import (
|
|
9
|
-
|
|
9
|
+
Concept,
|
|
10
10
|
ConceptualDataModel,
|
|
11
11
|
ConceptualMetadata,
|
|
12
12
|
ConceptualProperty,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
__all__ = [
|
|
16
|
-
"
|
|
16
|
+
"Concept",
|
|
17
17
|
"ConceptualDataModel",
|
|
18
18
|
"ConceptualMetadata",
|
|
19
19
|
"ConceptualProperty",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
20
|
+
"ConceptualValidation",
|
|
21
|
+
"UnverifiedConcept",
|
|
22
22
|
"UnverifiedConceptualDataModel",
|
|
23
23
|
"UnverifiedConceptualMetadata",
|
|
24
24
|
"UnverifiedConceptualProperty",
|
|
@@ -13,7 +13,7 @@ from cognite.neat.core._data_model.models._base_unverified import (
|
|
|
13
13
|
)
|
|
14
14
|
from cognite.neat.core._data_model.models.data_types import DataType
|
|
15
15
|
from cognite.neat.core._data_model.models.entities import (
|
|
16
|
-
|
|
16
|
+
ConceptEntity,
|
|
17
17
|
MultiValueTypeInfo,
|
|
18
18
|
UnknownEntity,
|
|
19
19
|
load_value_type,
|
|
@@ -21,7 +21,7 @@ from cognite.neat.core._data_model.models.entities import (
|
|
|
21
21
|
from cognite.neat.core._utils.rdf_ import uri_display_name
|
|
22
22
|
|
|
23
23
|
from ._verified import (
|
|
24
|
-
|
|
24
|
+
Concept,
|
|
25
25
|
ConceptualDataModel,
|
|
26
26
|
ConceptualMetadata,
|
|
27
27
|
ConceptualProperty,
|
|
@@ -68,7 +68,7 @@ class UnverifiedConceptualMetadata(UnverifiedComponent[ConceptualMetadata]):
|
|
|
68
68
|
Unlike namespace, the identifier does not end with "/" or "#".
|
|
69
69
|
|
|
70
70
|
"""
|
|
71
|
-
return DEFAULT_NAMESPACE[f"data-model/unverified/
|
|
71
|
+
return DEFAULT_NAMESPACE[f"data-model/unverified/conceptual/{self.space}/{self.external_id}/{self.version}"]
|
|
72
72
|
|
|
73
73
|
@property
|
|
74
74
|
def namespace(self) -> Namespace:
|
|
@@ -78,9 +78,9 @@ class UnverifiedConceptualMetadata(UnverifiedComponent[ConceptualMetadata]):
|
|
|
78
78
|
|
|
79
79
|
@dataclass
|
|
80
80
|
class UnverifiedConceptualProperty(UnverifiedComponent[ConceptualProperty]):
|
|
81
|
-
|
|
81
|
+
concept: ConceptEntity | str
|
|
82
82
|
property_: str
|
|
83
|
-
value_type: DataType |
|
|
83
|
+
value_type: DataType | ConceptEntity | MultiValueTypeInfo | UnknownEntity | str
|
|
84
84
|
name: str | None = None
|
|
85
85
|
description: str | None = None
|
|
86
86
|
min_count: int | None = None
|
|
@@ -100,7 +100,7 @@ class UnverifiedConceptualProperty(UnverifiedComponent[ConceptualProperty]):
|
|
|
100
100
|
|
|
101
101
|
def dump(self, default_prefix: str, **kwargs) -> dict[str, Any]: # type: ignore
|
|
102
102
|
output = super().dump()
|
|
103
|
-
output["
|
|
103
|
+
output["Concept"] = ConceptEntity.load(self.concept, prefix=default_prefix)
|
|
104
104
|
output["Value Type"] = load_value_type(self.value_type, default_prefix)
|
|
105
105
|
return output
|
|
106
106
|
|
|
@@ -112,33 +112,33 @@ class UnverifiedConceptualProperty(UnverifiedComponent[ConceptualProperty]):
|
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
@dataclass
|
|
115
|
-
class
|
|
116
|
-
|
|
115
|
+
class UnverifiedConcept(UnverifiedComponent[Concept]):
|
|
116
|
+
concept: ConceptEntity | str
|
|
117
117
|
name: str | None = None
|
|
118
118
|
description: str | None = None
|
|
119
|
-
implements: str | list[
|
|
119
|
+
implements: str | list[ConceptEntity] | None = None
|
|
120
120
|
instance_source: str | None = None
|
|
121
121
|
neatId: str | URIRef | None = None
|
|
122
122
|
# linking
|
|
123
123
|
physical: str | URIRef | None = None
|
|
124
124
|
|
|
125
125
|
@classmethod
|
|
126
|
-
def _get_verified_cls(cls) -> type[
|
|
127
|
-
return
|
|
126
|
+
def _get_verified_cls(cls) -> type[Concept]:
|
|
127
|
+
return Concept
|
|
128
128
|
|
|
129
129
|
@property
|
|
130
|
-
def
|
|
131
|
-
return str(self.
|
|
130
|
+
def concept_str(self) -> str:
|
|
131
|
+
return str(self.concept)
|
|
132
132
|
|
|
133
133
|
def dump(self, default_prefix: str, **kwargs) -> dict[str, Any]: # type: ignore
|
|
134
134
|
output = super().dump()
|
|
135
|
-
parent: list[
|
|
135
|
+
parent: list[ConceptEntity] | None = None
|
|
136
136
|
if isinstance(self.implements, str):
|
|
137
137
|
self.implements = self.implements.strip()
|
|
138
|
-
parent = [
|
|
138
|
+
parent = [ConceptEntity.load(parent, prefix=default_prefix) for parent in self.implements.split(",")]
|
|
139
139
|
elif isinstance(self.implements, list):
|
|
140
|
-
parent = [
|
|
141
|
-
output["
|
|
140
|
+
parent = [ConceptEntity.load(parent_, prefix=default_prefix) for parent_ in self.implements]
|
|
141
|
+
output["Concept"] = ConceptEntity.load(self.concept, prefix=default_prefix)
|
|
142
142
|
output["Implements"] = parent
|
|
143
143
|
return output
|
|
144
144
|
|
|
@@ -147,7 +147,7 @@ class UnverifiedConceptualClass(UnverifiedComponent[ConceptualClass]):
|
|
|
147
147
|
class UnverifiedConceptualDataModel(UnverifiedDataModel[ConceptualDataModel]):
|
|
148
148
|
metadata: UnverifiedConceptualMetadata
|
|
149
149
|
properties: list[UnverifiedConceptualProperty] = field(default_factory=list)
|
|
150
|
-
|
|
150
|
+
concepts: list[UnverifiedConcept] = field(default_factory=list)
|
|
151
151
|
prefixes: dict[str, Namespace] | None = None
|
|
152
152
|
|
|
153
153
|
@classmethod
|
|
@@ -160,7 +160,7 @@ class UnverifiedConceptualDataModel(UnverifiedDataModel[ConceptualDataModel]):
|
|
|
160
160
|
return dict(
|
|
161
161
|
Metadata=self.metadata.dump(),
|
|
162
162
|
Properties=[prop.dump(default_prefix) for prop in self.properties],
|
|
163
|
-
|
|
163
|
+
Concepts=[concept.dump(default_prefix) for concept in self.concepts],
|
|
164
164
|
Prefixes=self.prefixes,
|
|
165
165
|
)
|
|
166
166
|
|
|
@@ -180,7 +180,7 @@ class UnverifiedConceptualDataModel(UnverifiedDataModel[ConceptualDataModel]):
|
|
|
180
180
|
"external_id": self.metadata.external_id,
|
|
181
181
|
"space": self.metadata.space,
|
|
182
182
|
"version": self.metadata.version,
|
|
183
|
-
"
|
|
183
|
+
"concepts": len(self.concepts),
|
|
184
184
|
"properties": len(self.properties),
|
|
185
185
|
}
|
|
186
186
|
|