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.

Files changed (104) hide show
  1. cognite/neat/_version.py +1 -1
  2. cognite/neat/core/_client/_api/statistics.py +91 -0
  3. cognite/neat/core/_client/_api_client.py +2 -0
  4. cognite/neat/core/_client/data_classes/statistics.py +125 -0
  5. cognite/neat/core/_client/testing.py +4 -0
  6. cognite/neat/core/_constants.py +6 -7
  7. cognite/neat/core/_data_model/_constants.py +23 -16
  8. cognite/neat/core/_data_model/_shared.py +33 -17
  9. cognite/neat/core/_data_model/analysis/__init__.py +2 -2
  10. cognite/neat/core/_data_model/analysis/_base.py +186 -183
  11. cognite/neat/core/_data_model/catalog/__init__.py +2 -2
  12. cognite/neat/core/_data_model/exporters/__init__.py +6 -6
  13. cognite/neat/core/_data_model/exporters/_base.py +10 -8
  14. cognite/neat/core/_data_model/exporters/{_rules2dms.py → _data_model2dms.py} +22 -18
  15. cognite/neat/core/_data_model/exporters/{_rules2excel.py → _data_model2excel.py} +51 -51
  16. cognite/neat/core/_data_model/exporters/{_rules2instance_template.py → _data_model2instance_template.py} +14 -14
  17. cognite/neat/core/_data_model/exporters/{_rules2ontology.py → _data_model2ontology.py} +50 -50
  18. cognite/neat/core/_data_model/exporters/{_rules2yaml.py → _data_model2yaml.py} +21 -18
  19. cognite/neat/core/_data_model/importers/__init__.py +8 -8
  20. cognite/neat/core/_data_model/importers/_base.py +8 -6
  21. cognite/neat/core/_data_model/importers/_base_file_reader.py +56 -0
  22. cognite/neat/core/_data_model/importers/{_yaml2rules.py → _dict2data_model.py} +50 -25
  23. cognite/neat/core/_data_model/importers/{_dms2rules.py → _dms2data_model.py} +58 -49
  24. cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/dtdl_converter.py +22 -22
  25. cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/dtdl_importer.py +7 -7
  26. cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/spec.py +3 -3
  27. cognite/neat/core/_data_model/importers/_rdf/__init__.py +3 -3
  28. cognite/neat/core/_data_model/importers/_rdf/_base.py +15 -15
  29. cognite/neat/core/_data_model/importers/_rdf/{_imf2rules.py → _imf2data_model.py} +17 -17
  30. cognite/neat/core/_data_model/importers/_rdf/{_inference2rules.py → _inference2rdata_model.py} +59 -59
  31. cognite/neat/core/_data_model/importers/_rdf/{_owl2rules.py → _owl2data_model.py} +17 -17
  32. cognite/neat/core/_data_model/importers/_rdf/_shared.py +25 -25
  33. cognite/neat/core/_data_model/importers/{_spreadsheet2rules.py → _spreadsheet2data_model.py} +76 -19
  34. cognite/neat/core/_data_model/models/__init__.py +11 -9
  35. cognite/neat/core/_data_model/models/_base_unverified.py +12 -12
  36. cognite/neat/core/_data_model/models/_base_verified.py +9 -14
  37. cognite/neat/core/_data_model/models/_types.py +6 -6
  38. cognite/neat/core/_data_model/models/conceptual/__init__.py +6 -6
  39. cognite/neat/core/_data_model/models/conceptual/_unverified.py +20 -20
  40. cognite/neat/core/_data_model/models/conceptual/_validation.py +88 -78
  41. cognite/neat/core/_data_model/models/conceptual/_verified.py +54 -52
  42. cognite/neat/core/_data_model/models/data_types.py +2 -2
  43. cognite/neat/core/_data_model/models/entities/__init__.py +8 -8
  44. cognite/neat/core/_data_model/models/entities/_loaders.py +11 -10
  45. cognite/neat/core/_data_model/models/entities/_multi_value.py +5 -5
  46. cognite/neat/core/_data_model/models/entities/_single_value.py +44 -38
  47. cognite/neat/core/_data_model/models/entities/_types.py +9 -3
  48. cognite/neat/core/_data_model/models/entities/_wrapped.py +3 -3
  49. cognite/neat/core/_data_model/models/mapping/_classic2core.py +12 -9
  50. cognite/neat/core/_data_model/models/physical/__init__.py +40 -0
  51. cognite/neat/core/_data_model/models/{dms → physical}/_exporter.py +75 -55
  52. cognite/neat/core/_data_model/models/{dms/_rules_input.py → physical/_unverified.py} +48 -39
  53. cognite/neat/core/_data_model/models/{dms → physical}/_validation.py +17 -15
  54. cognite/neat/core/_data_model/models/{dms/_rules.py → physical/_verified.py} +68 -60
  55. cognite/neat/core/_data_model/transformers/__init__.py +29 -25
  56. cognite/neat/core/_data_model/transformers/_base.py +27 -20
  57. cognite/neat/core/_data_model/transformers/_converters.py +707 -622
  58. cognite/neat/core/_data_model/transformers/_mapping.py +74 -55
  59. cognite/neat/core/_data_model/transformers/_verification.py +64 -55
  60. cognite/neat/core/_instances/extractors/_base.py +2 -2
  61. cognite/neat/core/_instances/extractors/_classic_cdf/_classic.py +9 -9
  62. cognite/neat/core/_instances/extractors/_dms_graph.py +42 -34
  63. cognite/neat/core/_instances/extractors/_mock_graph_generator.py +107 -103
  64. cognite/neat/core/_instances/loaders/_base.py +3 -3
  65. cognite/neat/core/_instances/loaders/_rdf2dms.py +22 -22
  66. cognite/neat/core/_instances/transformers/_base.py +7 -4
  67. cognite/neat/core/_instances/transformers/_rdfpath.py +1 -1
  68. cognite/neat/core/_instances/transformers/_value_type.py +2 -6
  69. cognite/neat/core/_issues/_base.py +4 -4
  70. cognite/neat/core/_issues/_factory.py +1 -1
  71. cognite/neat/core/_issues/errors/__init__.py +2 -2
  72. cognite/neat/core/_issues/errors/_resources.py +1 -1
  73. cognite/neat/core/_issues/errors/_wrapper.py +2 -2
  74. cognite/neat/core/_issues/warnings/_models.py +4 -4
  75. cognite/neat/core/_issues/warnings/_properties.py +1 -1
  76. cognite/neat/core/_store/__init__.py +3 -3
  77. cognite/neat/core/_store/{_rules_store.py → _data_model.py} +119 -112
  78. cognite/neat/core/_store/{_graph_store.py → _instance.py} +3 -4
  79. cognite/neat/core/_store/_provenance.py +2 -2
  80. cognite/neat/core/_store/exceptions.py +2 -2
  81. cognite/neat/core/_utils/rdf_.py +14 -0
  82. cognite/neat/core/_utils/text.py +1 -1
  83. cognite/neat/session/_base.py +42 -36
  84. cognite/neat/session/_drop.py +2 -2
  85. cognite/neat/session/_experimental.py +1 -1
  86. cognite/neat/session/_inspect.py +13 -13
  87. cognite/neat/session/_mapping.py +15 -9
  88. cognite/neat/session/_read.py +39 -37
  89. cognite/neat/session/_set.py +6 -6
  90. cognite/neat/session/_show.py +24 -21
  91. cognite/neat/session/_state/README.md +1 -1
  92. cognite/neat/session/_state.py +27 -27
  93. cognite/neat/session/_subset.py +14 -11
  94. cognite/neat/session/_template.py +23 -21
  95. cognite/neat/session/_to.py +42 -42
  96. {cognite_neat-0.121.1.dist-info → cognite_neat-0.122.0.dist-info}/METADATA +14 -7
  97. {cognite_neat-0.121.1.dist-info → cognite_neat-0.122.0.dist-info}/RECORD +102 -100
  98. cognite/neat/core/_data_model/exporters/_validation.py +0 -14
  99. cognite/neat/core/_data_model/models/dms/__init__.py +0 -32
  100. /cognite/neat/core/_data_model/catalog/{info-rules-imf.xlsx → conceptual-imf-data-model.xlsx} +0 -0
  101. /cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/__init__.py +0 -0
  102. /cognite/neat/core/_data_model/importers/{_dtdl2rules → _dtdl2data_model}/_unit_lookup.py +0 -0
  103. {cognite_neat-0.121.1.dist-info → cognite_neat-0.122.0.dist-info}/WHEEL +0 -0
  104. {cognite_neat-0.121.1.dist-info → cognite_neat-0.122.0.dist-info}/licenses/LICENSE +0 -0
@@ -10,15 +10,16 @@ from rdflib.collection import Collection as GraphCollection
10
10
 
11
11
  from cognite.neat.core._constants import DEFAULT_NAMESPACE as NEAT_NAMESPACE
12
12
  from cognite.neat.core._data_model._constants import EntityTypes
13
- from cognite.neat.core._data_model.analysis import RulesAnalysis
13
+ from cognite.neat.core._data_model.analysis import DataModelAnalysis
14
14
  from cognite.neat.core._data_model.models.conceptual import (
15
- ConceptualClass,
15
+ Concept,
16
16
  ConceptualDataModel,
17
17
  ConceptualMetadata,
18
18
  ConceptualProperty,
19
19
  )
20
+ from cognite.neat.core._data_model.models.conceptual._validation import duplicated_properties
20
21
  from cognite.neat.core._data_model.models.data_types import DataType
21
- from cognite.neat.core._data_model.models.entities import ClassEntity
22
+ from cognite.neat.core._data_model.models.entities import ConceptEntity
22
23
  from cognite.neat.core._issues import MultiValueError
23
24
  from cognite.neat.core._issues.errors import (
24
25
  PropertyDefinitionDuplicatedError,
@@ -27,7 +28,6 @@ from cognite.neat.core._issues.warnings import PropertyDefinitionDuplicatedWarni
27
28
  from cognite.neat.core._utils.rdf_ import remove_namespace_from_uri
28
29
 
29
30
  from ._base import BaseExporter
30
- from ._validation import duplicated_properties
31
31
 
32
32
  if sys.version_info >= (3, 11):
33
33
  from typing import Self
@@ -36,26 +36,26 @@ else:
36
36
 
37
37
 
38
38
  class GraphExporter(BaseExporter[ConceptualDataModel, Graph], ABC):
39
- def export_to_file(self, rules: ConceptualDataModel, filepath: Path) -> None:
40
- self.export(rules).serialize(destination=filepath, encoding=self._encoding, newline=self._new_line)
39
+ def export_to_file(self, data_model: ConceptualDataModel, filepath: Path) -> None:
40
+ self.export(data_model).serialize(destination=filepath, encoding=self._encoding, newline=self._new_line)
41
41
 
42
42
 
43
43
  class OWLExporter(GraphExporter):
44
- """Exports verified information rules to an OWL ontology."""
44
+ """Exports verified conceptual data model to an OWL ontology."""
45
45
 
46
- def export(self, rules: ConceptualDataModel) -> Graph:
47
- return Ontology.from_rules(rules).as_owl()
46
+ def export(self, data_model: ConceptualDataModel) -> Graph:
47
+ return Ontology.from_data_model(data_model).as_owl()
48
48
 
49
49
  @property
50
50
  def description(self) -> str:
51
- return "Export verified information model to OWL."
51
+ return "Export verified conceptual data model to OWL."
52
52
 
53
53
 
54
54
  class SHACLExporter(GraphExporter):
55
- """Exports rules to a SHACL graph."""
55
+ """Exports data_model to a SHACL graph."""
56
56
 
57
- def export(self, rules: ConceptualDataModel) -> Graph:
58
- return Ontology.from_rules(rules).as_shacl()
57
+ def export(self, data_model: ConceptualDataModel) -> Graph:
58
+ return Ontology.from_data_model(data_model).as_shacl()
59
59
 
60
60
  @property
61
61
  def description(self) -> str:
@@ -65,8 +65,8 @@ class SHACLExporter(GraphExporter):
65
65
  class SemanticDataModelExporter(GraphExporter):
66
66
  """Exports verified information model to a semantic data model."""
67
67
 
68
- def export(self, rules: ConceptualDataModel) -> Graph:
69
- return Ontology.from_rules(rules).as_semantic_data_model()
68
+ def export(self, data_model: ConceptualDataModel) -> Graph:
69
+ return Ontology.from_data_model(data_model).as_semantic_data_model()
70
70
 
71
71
  @property
72
72
  def description(self) -> str:
@@ -79,7 +79,7 @@ class OntologyModel(BaseModel):
79
79
 
80
80
  class Ontology(OntologyModel):
81
81
  """
82
- Represents an ontology. Thi class is used to generate an OWL ontology from a set of transformation rules.
82
+ Represents an ontology. This class is used to generate an OWL ontology from conceptual data model.
83
83
 
84
84
  Args:
85
85
  properties: A list of OWL properties.
@@ -96,23 +96,23 @@ class Ontology(OntologyModel):
96
96
  prefixes: dict[str, Namespace]
97
97
 
98
98
  @classmethod
99
- def from_rules(cls, rules: ConceptualDataModel) -> Self:
99
+ def from_data_model(cls, data_model: ConceptualDataModel) -> Self:
100
100
  """
101
- Generates an ontology from a set of transformation rules.
101
+ Generates an ontology from a set of transformation data_model.
102
102
 
103
103
  Args:
104
- rules: The rules to generate the ontology from.
104
+ data_model: The data_model to generate the ontology from.
105
105
 
106
106
  Returns:
107
107
  An instance of Ontology.
108
108
  """
109
- if duplicates := duplicated_properties(rules.properties):
109
+ if duplicates := duplicated_properties(data_model.properties):
110
110
  errors = []
111
- for (class_, property_), definitions in duplicates.items():
111
+ for (concept, property_), definitions in duplicates.items():
112
112
  errors.append(
113
113
  PropertyDefinitionDuplicatedError(
114
- class_,
115
- "class",
114
+ concept,
115
+ "concept",
116
116
  property_,
117
117
  frozenset({str(definition[1].value_type) for definition in definitions}),
118
118
  tuple(definition[0] for definition in definitions),
@@ -121,35 +121,35 @@ class Ontology(OntologyModel):
121
121
  )
122
122
  raise MultiValueError(errors)
123
123
 
124
- analysis = RulesAnalysis(rules)
125
- class_dict = analysis.class_by_suffix()
124
+ analysis = DataModelAnalysis(data_model)
125
+ concept_by_suffix = analysis.concept_by_suffix()
126
126
  return cls(
127
127
  properties=[
128
- OWLProperty.from_list_of_properties(definition, rules.metadata.namespace)
128
+ OWLProperty.from_list_of_properties(definition, data_model.metadata.namespace)
129
129
  for definition in analysis.property_by_id().values()
130
130
  ],
131
131
  classes=[
132
- OWLClass.from_class(definition, rules.metadata.namespace, rules.prefixes)
133
- for definition in rules.classes
132
+ OWLClass.from_concept(definition, data_model.metadata.namespace, data_model.prefixes)
133
+ for definition in data_model.concepts
134
134
  ],
135
135
  shapes=[
136
- SHACLNodeShape.from_rules(
137
- class_dict[str(class_.suffix)],
136
+ SHACLNodeShape.from_data_model(
137
+ concept_by_suffix[str(concept.suffix)],
138
138
  list(properties.values()),
139
- rules.metadata.namespace,
139
+ data_model.metadata.namespace,
140
140
  )
141
- for class_, properties in analysis.properties_by_id_by_class().items()
141
+ for concept, properties in analysis.properties_by_id_by_concept().items()
142
142
  ]
143
143
  + [
144
- SHACLNodeShape.from_rules(
145
- class_,
144
+ SHACLNodeShape.from_data_model(
145
+ concept,
146
146
  [],
147
- rules.metadata.namespace,
147
+ data_model.metadata.namespace,
148
148
  )
149
- for class_ in class_dict.values()
149
+ for concept in concept_by_suffix.values()
150
150
  ],
151
- metadata=OWLMetadata(**rules.metadata.model_dump()),
152
- prefixes=rules.prefixes,
151
+ metadata=OWLMetadata(**data_model.metadata.model_dump()),
152
+ prefixes=data_model.prefixes,
153
153
  )
154
154
 
155
155
  def as_shacl(self) -> Graph:
@@ -259,7 +259,7 @@ class OWLClass(OntologyModel):
259
259
  namespace: Namespace
260
260
 
261
261
  @classmethod
262
- def from_class(cls, definition: ConceptualClass, namespace: Namespace, prefixes: dict) -> Self:
262
+ def from_concept(cls, definition: Concept, namespace: Namespace, prefixes: dict) -> Self:
263
263
  if definition.implements and isinstance(definition.implements, list):
264
264
  sub_class_of = []
265
265
  for parent_class in definition.implements:
@@ -271,7 +271,7 @@ class OWLClass(OntologyModel):
271
271
  sub_class_of = None
272
272
 
273
273
  return cls(
274
- id_=namespace[str(definition.class_.suffix)],
274
+ id_=namespace[str(definition.concept.suffix)],
275
275
  label=definition.name if definition.name else None,
276
276
  comment=definition.description,
277
277
  sub_class_of=sub_class_of,
@@ -338,7 +338,7 @@ class OWLProperty(OntologyModel):
338
338
  property_ids = {definition.property_ for definition in definitions}
339
339
  if len(property_ids) != 1:
340
340
  raise PropertyDefinitionDuplicatedError(
341
- definitions[0].class_,
341
+ definitions[0].concept,
342
342
  "class",
343
343
  definitions[0].property_,
344
344
  frozenset(property_ids),
@@ -358,11 +358,11 @@ class OWLProperty(OntologyModel):
358
358
 
359
359
  if isinstance(definition.value_type, DataType):
360
360
  owl_property.range_.add(XSD[definition.value_type.xsd])
361
- elif isinstance(definition.value_type, ClassEntity):
361
+ elif isinstance(definition.value_type, ConceptEntity):
362
362
  owl_property.range_.add(namespace[str(definition.value_type.suffix)])
363
363
  else:
364
364
  raise ValueError(f"Value type {definition.value_type.type_} is not supported")
365
- owl_property.domain.add(namespace[str(definition.class_.suffix)])
365
+ owl_property.domain.add(namespace[str(definition.concept.suffix)])
366
366
  if definition.name:
367
367
  owl_property.label.add(definition.name)
368
368
  if definition.description:
@@ -572,19 +572,19 @@ class SHACLNodeShape(OntologyModel):
572
572
  )
573
573
 
574
574
  @classmethod
575
- def from_rules(
575
+ def from_data_model(
576
576
  cls,
577
- class_definition: ConceptualClass,
577
+ concept_definition: Concept,
578
578
  property_definitions: list[ConceptualProperty],
579
579
  namespace: Namespace,
580
580
  ) -> "SHACLNodeShape":
581
- if class_definition.implements:
582
- parent = [namespace[str(parent.suffix) + "Shape"] for parent in class_definition.implements]
581
+ if concept_definition.implements:
582
+ parent = [namespace[str(parent.suffix) + "Shape"] for parent in concept_definition.implements]
583
583
  else:
584
584
  parent = None
585
585
  return cls(
586
- id_=namespace[f"{class_definition.class_.suffix!s}Shape"],
587
- target_class=namespace[str(class_definition.class_.suffix)],
586
+ id_=namespace[f"{concept_definition.concept.suffix!s}Shape"],
587
+ target_class=namespace[str(concept_definition.concept.suffix)],
588
588
  parent=parent,
589
589
  property_shapes=[SHACLPropertyShape.from_property(prop, namespace) for prop in property_definitions],
590
590
  namespace=namespace,
@@ -633,7 +633,7 @@ class SHACLPropertyShape(OntologyModel):
633
633
  @classmethod
634
634
  def from_property(cls, definition: ConceptualProperty, namespace: Namespace) -> "SHACLPropertyShape":
635
635
  # TODO requires PR to fix MultiValueType and UnknownValueType
636
- if isinstance(definition.value_type, ClassEntity):
636
+ if isinstance(definition.value_type, ConceptEntity):
637
637
  expected_value_type = namespace[f"{definition.value_type.suffix}Shape"]
638
638
  elif isinstance(definition.value_type, DataType):
639
639
  expected_value_type = XSD[definition.value_type.xsd]
@@ -5,27 +5,28 @@ from typing import Literal, get_args
5
5
 
6
6
  import yaml
7
7
 
8
- from cognite.neat.core._data_model._shared import VerifiedRules
8
+ from cognite.neat.core._data_model._shared import VerifiedDataModel
9
9
 
10
10
  from ._base import BaseExporter
11
11
 
12
12
 
13
- class YAMLExporter(BaseExporter[VerifiedRules, str]):
14
- """Export rules (Information, DMS or Domain) to YAML.
13
+ class YAMLExporter(BaseExporter[VerifiedDataModel, str]):
14
+ """Export data_model (Information, DMS or Domain) to YAML.
15
15
 
16
16
  Args:
17
17
  files: The number of files to output. Defaults to "single".
18
- output: The format to output the rules. Defaults to "yaml".
18
+ output: The format to output the data_model. Defaults to "yaml".
19
19
 
20
20
  The following formats are available:
21
21
 
22
- - "single": A single YAML file will contain the entire rules.
22
+ - "single": A single YAML file will contain the entire data_model.
23
23
 
24
24
  .. note::
25
25
 
26
- YAML files are typically used for storing rules when checked into version control systems, e.g., git-history.
27
- The advantage of using YAML files over Excel is that tools like git can show the differences between different
28
- versions of the rules.
26
+ YAML files are typically used for storing data_model when checked into version
27
+ control systems, e.g., git-history.The advantage of using YAML files over
28
+ Excel is that tools like git can show the differences between different
29
+ versions of the data_model.
29
30
 
30
31
  """
31
32
 
@@ -47,32 +48,34 @@ class YAMLExporter(BaseExporter[VerifiedRules, str]):
47
48
  def description(self) -> str:
48
49
  return "Export verified model to YAML."
49
50
 
50
- def export_to_file(self, rules: VerifiedRules, filepath: Path) -> None:
51
- """Exports transformation rules to YAML/JSON file(s)."""
51
+ def export_to_file(self, data_model: VerifiedDataModel, filepath: Path) -> None:
52
+ """Exports transformation data_model to YAML/JSON file(s)."""
52
53
  if self.files == "single":
53
54
  if filepath.suffix != f".{self.output}":
54
55
  warnings.warn(f"File extension is not .{self.output}, adding it to the file name", stacklevel=2)
55
56
  filepath = filepath.with_suffix(f".{self.output}")
56
- filepath.write_text(self.export(rules), encoding=self._encoding, newline=self._new_line)
57
+ filepath.write_text(self.export(data_model), encoding=self._encoding, newline=self._new_line)
57
58
  else:
58
59
  raise NotImplementedError(f"Exporting to {self.files} files is not supported")
59
60
 
60
- def export(self, rules: VerifiedRules) -> str:
61
- """Export rules to YAML (or JSON) format.
61
+ def export(self, data_model: VerifiedDataModel) -> str:
62
+ """Export data_model to YAML (or JSON) format.
62
63
 
63
- This will export the rules to YAML format if the output is set to "yaml" and JSON format if the output is set.
64
- All None and Unset values are excluded from the output to keep the output clean, i.e., only the values the user
64
+ This will export the data_model to YAML format if the output is
65
+ set to "yaml" and JSON format if the output is set.
66
+ All None and Unset values are excluded from the output
67
+ to keep the output clean, i.e., only the values the user
65
68
  has set.
66
69
 
67
70
  Args:
68
- rules: The rules to be exported.
71
+ data_model: The data_model to be exported.
69
72
 
70
73
  Returns:
71
- str: The rules in YAML (or JSON) format.
74
+ str: The data_model in YAML (or JSON) format.
72
75
  """
73
76
  # model_dump_json ensures that the output is in JSON format,
74
77
  # if we don't do this, we will get Enums and other types that are not serializable to YAML
75
- json_output = rules.dump(mode="json", sort=True, exclude_none=True, exclude_unset=True)
78
+ json_output = data_model.dump(mode="json", sort=True, exclude_none=True, exclude_unset=True)
76
79
  if self.output == "json":
77
80
  return json.dumps(json_output)
78
81
  elif self.output == "yaml":
@@ -1,29 +1,29 @@
1
1
  from ._base import BaseImporter
2
- from ._dms2rules import DMSImporter
3
- from ._dtdl2rules import DTDLImporter
2
+ from ._dict2data_model import DictImporter
3
+ from ._dms2data_model import DMSImporter
4
+ from ._dtdl2data_model import DTDLImporter
4
5
  from ._rdf import IMFImporter, InferenceImporter, OWLImporter, SubclassInferenceImporter
5
- from ._spreadsheet2rules import ExcelImporter
6
- from ._yaml2rules import YAMLImporter
6
+ from ._spreadsheet2data_model import ExcelImporter
7
7
 
8
8
  __all__ = [
9
9
  "BaseImporter",
10
10
  "DMSImporter",
11
11
  "DTDLImporter",
12
+ "DictImporter",
12
13
  "ExcelImporter",
13
14
  "IMFImporter",
14
15
  "InferenceImporter",
15
16
  "OWLImporter",
16
17
  "SubclassInferenceImporter",
17
- "YAMLImporter",
18
18
  ]
19
19
 
20
- RulesImporters = (
20
+ DataModelImporters = (
21
21
  OWLImporter
22
22
  | IMFImporter
23
23
  | DMSImporter
24
24
  | ExcelImporter
25
25
  | DTDLImporter
26
- | YAMLImporter
26
+ | DictImporter
27
27
  | InferenceImporter
28
28
  | SubclassInferenceImporter
29
29
  )
@@ -47,5 +47,5 @@ def _repr_html_() -> str:
47
47
 
48
48
  return (
49
49
  "<strong>Importer</strong> An importer reads data/schema/data model from a source"
50
- f" and converts it into Neat's representation of a data model called <em>Rules</em>.<br />{table}"
50
+ f" and converts it into Neat's representation of a data model.<br />{table}"
51
51
  )
@@ -6,21 +6,24 @@ from typing import TYPE_CHECKING, Any, Generic
6
6
  from rdflib import URIRef
7
7
 
8
8
  from cognite.neat.core._constants import DEFAULT_NAMESPACE
9
- from cognite.neat.core._data_model._shared import ReadRules, T_InputRules
9
+ from cognite.neat.core._data_model._shared import (
10
+ ImportedDataModel,
11
+ T_UnverifiedDataModel,
12
+ )
10
13
  from cognite.neat.core._utils.auxiliary import class_html_doc
11
14
 
12
15
  if TYPE_CHECKING:
13
16
  from cognite.neat.core._store._provenance import Agent as ProvenanceAgent
14
17
 
15
18
 
16
- class BaseImporter(ABC, Generic[T_InputRules]):
19
+ class BaseImporter(ABC, Generic[T_UnverifiedDataModel]):
17
20
  """
18
- BaseImporter class which all importers inherit from.
21
+ BaseImporter class which all data model importers inherit from.
19
22
  """
20
23
 
21
24
  @abstractmethod
22
- def to_rules(self) -> ReadRules[T_InputRules]:
23
- """Creates `Rules` object from the data for target role."""
25
+ def to_data_model(self) -> ImportedDataModel[T_UnverifiedDataModel]:
26
+ """Creates `DataModel` object from the data for target role."""
24
27
  raise NotImplementedError()
25
28
 
26
29
  def _default_metadata(self) -> dict[str, Any]:
@@ -31,7 +34,6 @@ class BaseImporter(ABC, Generic[T_InputRules]):
31
34
  creator = getpass.getuser()
32
35
 
33
36
  return {
34
- "schema": "partial",
35
37
  "space": "neat",
36
38
  "external_id": "NeatImportedDataModel",
37
39
  "version": "0.1.0",
@@ -0,0 +1,56 @@
1
+ from pathlib import Path
2
+ from typing import Protocol, TypeVar
3
+
4
+ from cognite.neat.core._issues import NeatIssue
5
+ from cognite.neat.core._issues.errors import (
6
+ FileNotAFileError,
7
+ FileNotFoundNeatError,
8
+ FileTypeUnexpectedError,
9
+ )
10
+
11
+ T = TypeVar("T")
12
+ DataT = TypeVar("DataT", covariant=True)
13
+
14
+
15
+ class FileReader(Protocol[DataT]):
16
+ """Protocol for file readers that parse files into structured data."""
17
+
18
+ def read_file(self, filepath: Path) -> tuple[DataT, list[NeatIssue]]:
19
+ """Read a file and return the data with any issues encountered.
20
+
21
+ Args:
22
+ filepath: Path to the file to read
23
+
24
+ Returns:
25
+ Tuple of (parsed_data, issues_list)
26
+ """
27
+ ...
28
+
29
+
30
+ class BaseFileReader:
31
+ """Base implementation with common file validation logic."""
32
+
33
+ @staticmethod
34
+ def validate_file(filepath: Path, allowed_extensions: frozenset[str]) -> list[NeatIssue]:
35
+ """Validate that a file exists and has the correct extension.
36
+
37
+ Args:
38
+ filepath: Path to the file to validate
39
+ allowed_extensions: Set of allowed file extensions
40
+
41
+ Returns:
42
+ List of issues found during validation, empty if valid
43
+ """
44
+ # Check if file exists
45
+ if not filepath.exists():
46
+ return [FileNotFoundNeatError(filepath)]
47
+
48
+ # Check if it's a file
49
+ if not filepath.is_file():
50
+ return [FileNotAFileError(filepath)]
51
+
52
+ # Check file extension
53
+ if filepath.suffix not in allowed_extensions:
54
+ return [FileTypeUnexpectedError(filepath, allowed_extensions)]
55
+
56
+ return []
@@ -3,32 +3,62 @@ from typing import Any, cast
3
3
 
4
4
  import yaml
5
5
 
6
- from cognite.neat.core._data_model._shared import ReadRules, T_InputRules
7
- from cognite.neat.core._data_model.models import INPUT_RULES_BY_ROLE, RoleTypes
6
+ from cognite.neat.core._data_model._shared import (
7
+ ImportedDataModel,
8
+ T_UnverifiedDataModel,
9
+ )
10
+ from cognite.neat.core._data_model.models import (
11
+ UNVERIFIED_DATA_MODEL_BY_ROLE,
12
+ RoleTypes,
13
+ )
8
14
  from cognite.neat.core._issues import IssueList, MultiValueError, NeatIssue
9
15
  from cognite.neat.core._issues.errors import (
10
16
  FileMissingRequiredFieldError,
11
- FileNotAFileError,
12
- FileNotFoundNeatError,
13
17
  FileReadError,
14
- FileTypeUnexpectedError,
15
18
  )
16
19
  from cognite.neat.core._issues.warnings import NeatValueWarning
17
20
 
18
21
  from ._base import BaseImporter
22
+ from ._base_file_reader import BaseFileReader
23
+
24
+
25
+ class YAMLReader:
26
+ """Handles reading and parsing YAML files with error handling."""
27
+
28
+ @staticmethod
29
+ def read_file(
30
+ filepath: Path, allowed_extensions: frozenset[str] = frozenset([".yaml", ".yml"])
31
+ ) -> tuple[dict[str, Any], list[NeatIssue]]:
32
+ """Read a YAML file and return the data with any issues encountered."""
33
+ issues = BaseFileReader.validate_file(filepath, allowed_extensions)
34
+ if issues:
35
+ return {}, issues
36
+ # Try to load the YAML
37
+ try:
38
+ data = yaml.safe_load(filepath.read_text())
39
+ if not isinstance(data, dict):
40
+ issues.append(FileReadError(filepath, "YAML content is not a dictionary"))
41
+ return {}, issues
42
+ return data, issues
43
+ except yaml.YAMLError as exc:
44
+ return {}, [FileReadError(filepath, f"Invalid YAML: {exc!s}")]
45
+ except Exception as exc:
46
+ return {}, [FileReadError(filepath, f"Error reading file: {exc!s}")]
19
47
 
20
48
 
21
- class YAMLImporter(BaseImporter[T_InputRules]):
22
- """Imports the rules from a YAML file.
49
+ class DictImporter(BaseImporter[T_UnverifiedDataModel]):
50
+ """Imports the data model from a YAML file.
23
51
 
24
52
  Args:
25
53
  raw_data: The raw data to be imported.
26
54
 
27
55
  .. note::
28
56
 
29
- YAML files are typically used for storing rules when checked into version control systems, e.g., git-history.
30
- The advantage of using YAML files over Excel is that tools like git can show the differences between different
31
- versions of the rules.
57
+ YAML files are typically used for storing data model when checked into version
58
+ control systems, e.g., git-history.
59
+ The advantage of using YAML files over Excel is that tools like git can
60
+ show the differences between different
61
+ versions of the data model.
32
62
 
33
63
  """
34
64
 
@@ -49,21 +79,16 @@ class YAMLImporter(BaseImporter[T_InputRules]):
49
79
  return f"YAML file {self._source_name} read as unverified data model"
50
80
 
51
81
  @classmethod
52
- def from_file(cls, filepath: Path, source_name: str = "Unknown") -> "YAMLImporter":
53
- if not filepath.exists():
54
- return cls({}, [FileNotFoundNeatError(filepath)])
55
- elif not filepath.is_file():
56
- return cls({}, [FileNotAFileError(filepath)])
57
- elif filepath.suffix not in [".yaml", ".yml"]:
58
- return cls({}, [FileTypeUnexpectedError(filepath, frozenset([".yaml", ".yml"]))])
59
- try:
60
- data = yaml.safe_load(filepath.read_text())
61
- except yaml.YAMLError as exc:
62
- return cls({}, [FileReadError(filepath, f"Invalid YAML: {exc!s}")])
82
+ def from_yaml_file(cls, filepath: Path, source_name: str = "Unknown") -> "DictImporter":
83
+ """Create a DictImporter from a YAML file."""
84
+ data, issues = YAMLReader.read_file(filepath)
85
+
86
+ if issues:
87
+ return cls({}, issues)
63
88
 
64
89
  return cls(data, filepaths=[filepath], source_name=source_name)
65
90
 
66
- def to_rules(self) -> ReadRules[T_InputRules]:
91
+ def to_data_model(self) -> ImportedDataModel[T_UnverifiedDataModel]:
67
92
  if self._read_issues.has_errors or not self.raw_data:
68
93
  self._read_issues.trigger_warnings()
69
94
  raise MultiValueError(self._read_issues.errors)
@@ -95,12 +120,12 @@ class YAMLImporter(BaseImporter[T_InputRules]):
95
120
 
96
121
  role_input = RoleTypes(metadata["role"])
97
122
  role_enum = RoleTypes(role_input)
98
- rules_cls = INPUT_RULES_BY_ROLE[role_enum]
123
+ data_model_cls = UNVERIFIED_DATA_MODEL_BY_ROLE[role_enum]
99
124
 
100
- rules = cast(T_InputRules, rules_cls.load(self.raw_data))
125
+ data_model = cast(T_UnverifiedDataModel, data_model_cls.load(self.raw_data))
101
126
 
102
127
  issue_list.trigger_warnings()
103
128
  if self._read_issues.has_errors:
104
129
  raise MultiValueError(self._read_issues.errors)
105
130
 
106
- return ReadRules[T_InputRules](rules, {})
131
+ return ImportedDataModel[T_UnverifiedDataModel](data_model, {})