cognite-neat 0.121.0__py3-none-any.whl → 0.121.2__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 (150) 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/{_rules → _data_model}/_constants.py +25 -18
  8. cognite/neat/core/_data_model/_shared.py +59 -0
  9. cognite/neat/core/_data_model/analysis/__init__.py +3 -0
  10. cognite/neat/core/{_rules → _data_model}/analysis/_base.py +202 -195
  11. cognite/neat/core/{_rules → _data_model}/catalog/__init__.py +1 -1
  12. cognite/neat/core/{_rules → _data_model}/exporters/__init__.py +5 -5
  13. cognite/neat/core/{_rules → _data_model}/exporters/_base.py +10 -8
  14. cognite/neat/core/{_rules/exporters/_rules2dms.py → _data_model/exporters/_data_model2dms.py} +22 -18
  15. cognite/neat/core/{_rules/exporters/_rules2excel.py → _data_model/exporters/_data_model2excel.py} +61 -56
  16. cognite/neat/core/{_rules/exporters/_rules2instance_template.py → _data_model/exporters/_data_model2instance_template.py} +11 -9
  17. cognite/neat/core/{_rules/exporters/_rules2ontology.py → _data_model/exporters/_data_model2ontology.py} +64 -61
  18. cognite/neat/core/{_rules/exporters/_rules2yaml.py → _data_model/exporters/_data_model2yaml.py} +21 -18
  19. cognite/neat/core/{_rules → _data_model}/importers/__init__.py +6 -8
  20. cognite/neat/core/{_rules → _data_model}/importers/_base.py +8 -6
  21. cognite/neat/core/_data_model/importers/_base_file_reader.py +56 -0
  22. cognite/neat/core/{_rules/importers/_yaml2rules.py → _data_model/importers/_dict2data_model.py} +41 -21
  23. cognite/neat/core/{_rules/importers/_dms2rules.py → _data_model/importers/_dms2data_model.py} +79 -66
  24. cognite/neat/core/{_rules/importers/_dtdl2rules → _data_model/importers/_dtdl2data_model}/dtdl_converter.py +41 -41
  25. cognite/neat/core/{_rules/importers/_dtdl2rules → _data_model/importers/_dtdl2data_model}/dtdl_importer.py +16 -16
  26. cognite/neat/core/{_rules/importers/_dtdl2rules → _data_model/importers/_dtdl2data_model}/spec.py +3 -3
  27. cognite/neat/core/{_rules → _data_model}/importers/_rdf/_base.py +18 -16
  28. cognite/neat/core/{_rules → _data_model}/importers/_rdf/_imf2rules.py +17 -17
  29. cognite/neat/core/{_rules → _data_model}/importers/_rdf/_inference2rules.py +50 -50
  30. cognite/neat/core/{_rules → _data_model}/importers/_rdf/_owl2rules.py +14 -14
  31. cognite/neat/core/{_rules → _data_model}/importers/_rdf/_shared.py +25 -25
  32. cognite/neat/core/{_rules/importers/_spreadsheet2rules.py → _data_model/importers/_spreadsheet2data_model.py} +69 -38
  33. cognite/neat/core/_data_model/models/__init__.py +36 -0
  34. cognite/neat/core/{_rules/models/_base_input.py → _data_model/models/_base_unverified.py} +12 -12
  35. cognite/neat/core/{_rules/models/_base_rules.py → _data_model/models/_base_verified.py} +13 -13
  36. cognite/neat/core/{_rules → _data_model}/models/_types.py +13 -13
  37. cognite/neat/core/_data_model/models/conceptual/__init__.py +25 -0
  38. cognite/neat/core/{_rules/models/information/_rules_input.py → _data_model/models/conceptual/_unverified.py} +46 -43
  39. cognite/neat/core/{_rules/models/information → _data_model/models/conceptual}/_validation.py +93 -79
  40. cognite/neat/core/{_rules/models/information/_rules.py → _data_model/models/conceptual/_verified.py} +83 -83
  41. cognite/neat/core/{_rules → _data_model}/models/data_types.py +4 -4
  42. cognite/neat/core/{_rules → _data_model}/models/entities/__init__.py +8 -8
  43. cognite/neat/core/{_rules → _data_model}/models/entities/_loaders.py +12 -11
  44. cognite/neat/core/{_rules → _data_model}/models/entities/_multi_value.py +7 -7
  45. cognite/neat/core/{_rules → _data_model}/models/entities/_single_value.py +45 -39
  46. cognite/neat/core/{_rules → _data_model}/models/entities/_types.py +9 -3
  47. cognite/neat/core/{_rules → _data_model}/models/entities/_wrapped.py +3 -3
  48. cognite/neat/core/{_rules → _data_model}/models/mapping/_classic2core.py +12 -9
  49. cognite/neat/core/_data_model/models/physical/__init__.py +40 -0
  50. cognite/neat/core/{_rules/models/dms → _data_model/models/physical}/_exporter.py +83 -64
  51. cognite/neat/core/{_rules/models/dms/_rules_input.py → _data_model/models/physical/_unverified.py} +56 -44
  52. cognite/neat/core/{_rules/models/dms → _data_model/models/physical}/_validation.py +20 -17
  53. cognite/neat/core/{_rules/models/dms/_rules.py → _data_model/models/physical/_verified.py} +79 -71
  54. cognite/neat/core/{_rules → _data_model}/transformers/__init__.py +27 -23
  55. cognite/neat/core/{_rules → _data_model}/transformers/_base.py +29 -19
  56. cognite/neat/core/{_rules → _data_model}/transformers/_converters.py +758 -659
  57. cognite/neat/core/{_rules → _data_model}/transformers/_mapping.py +79 -60
  58. cognite/neat/core/_data_model/transformers/_verification.py +120 -0
  59. cognite/neat/core/{_graph → _instances}/extractors/_base.py +2 -2
  60. cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_base.py +1 -1
  61. cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_classic.py +17 -11
  62. cognite/neat/core/{_graph → _instances}/extractors/_dms_graph.py +47 -39
  63. cognite/neat/core/{_graph → _instances}/extractors/_mock_graph_generator.py +102 -99
  64. cognite/neat/core/{_graph → _instances}/extractors/_rdf_file.py +2 -2
  65. cognite/neat/core/{_graph → _instances}/loaders/_base.py +2 -2
  66. cognite/neat/core/{_graph → _instances}/loaders/_rdf2dms.py +16 -14
  67. cognite/neat/core/{_graph → _instances}/transformers/_base.py +7 -4
  68. cognite/neat/core/{_graph → _instances}/transformers/_classic_cdf.py +1 -1
  69. cognite/neat/core/{_graph → _instances}/transformers/_value_type.py +2 -6
  70. cognite/neat/core/_issues/_base.py +4 -4
  71. cognite/neat/core/_issues/errors/__init__.py +2 -2
  72. cognite/neat/core/_issues/errors/_wrapper.py +2 -2
  73. cognite/neat/core/_issues/warnings/__init__.py +2 -0
  74. cognite/neat/core/_issues/warnings/_models.py +4 -4
  75. cognite/neat/core/_issues/warnings/_properties.py +7 -0
  76. cognite/neat/core/_store/__init__.py +3 -3
  77. cognite/neat/core/_store/{_rules_store.py → _data_model.py} +128 -121
  78. cognite/neat/core/_store/{_graph_store.py → _instance.py} +7 -8
  79. cognite/neat/core/_store/_provenance.py +2 -2
  80. cognite/neat/core/_store/exceptions.py +4 -4
  81. cognite/neat/core/_utils/rdf_.py +14 -0
  82. cognite/neat/core/_utils/spreadsheet.py +1 -1
  83. cognite/neat/core/_utils/text.py +2 -2
  84. cognite/neat/session/_base.py +29 -25
  85. cognite/neat/session/_drop.py +3 -3
  86. cognite/neat/session/_fix.py +2 -2
  87. cognite/neat/session/_inspect.py +5 -5
  88. cognite/neat/session/_mapping.py +11 -9
  89. cognite/neat/session/_prepare.py +4 -4
  90. cognite/neat/session/_read.py +15 -15
  91. cognite/neat/session/_set.py +5 -5
  92. cognite/neat/session/_show.py +11 -11
  93. cognite/neat/session/_state.py +17 -17
  94. cognite/neat/session/_subset.py +14 -11
  95. cognite/neat/session/_template.py +19 -19
  96. cognite/neat/session/_to.py +21 -21
  97. cognite/neat/session/_wizard.py +1 -1
  98. {cognite_neat-0.121.0.dist-info → cognite_neat-0.121.2.dist-info}/METADATA +1 -1
  99. cognite_neat-0.121.2.dist-info/RECORD +189 -0
  100. cognite/neat/core/_rules/_shared.py +0 -43
  101. cognite/neat/core/_rules/analysis/__init__.py +0 -3
  102. cognite/neat/core/_rules/exporters/_validation.py +0 -14
  103. cognite/neat/core/_rules/models/__init__.py +0 -34
  104. cognite/neat/core/_rules/models/dms/__init__.py +0 -32
  105. cognite/neat/core/_rules/models/information/__init__.py +0 -20
  106. cognite/neat/core/_rules/transformers/_verification.py +0 -111
  107. cognite_neat-0.121.0.dist-info/RECORD +0 -187
  108. /cognite/neat/core/{_graph → _data_model}/__init__.py +0 -0
  109. /cognite/neat/core/{_rules → _data_model}/catalog/classic_model.xlsx +0 -0
  110. /cognite/neat/core/{_rules/catalog/info-rules-imf.xlsx → _data_model/catalog/conceptual-imf-data-model.xlsx} +0 -0
  111. /cognite/neat/core/{_rules → _data_model}/catalog/hello_world_pump.xlsx +0 -0
  112. /cognite/neat/core/{_rules/importers/_dtdl2rules → _data_model/importers/_dtdl2data_model}/__init__.py +0 -0
  113. /cognite/neat/core/{_rules/importers/_dtdl2rules → _data_model/importers/_dtdl2data_model}/_unit_lookup.py +0 -0
  114. /cognite/neat/core/{_rules → _data_model}/importers/_rdf/__init__.py +0 -0
  115. /cognite/neat/core/{_rules → _data_model}/models/entities/_constants.py +0 -0
  116. /cognite/neat/core/{_rules → _data_model}/models/mapping/__init__.py +0 -0
  117. /cognite/neat/core/{_rules → _data_model}/models/mapping/_classic2core.yaml +0 -0
  118. /cognite/neat/core/{_graph/extractors/_classic_cdf → _instances}/__init__.py +0 -0
  119. /cognite/neat/core/{_graph → _instances}/_shared.py +0 -0
  120. /cognite/neat/core/{_graph → _instances}/_tracking/__init__.py +0 -0
  121. /cognite/neat/core/{_graph → _instances}/_tracking/base.py +0 -0
  122. /cognite/neat/core/{_graph → _instances}/_tracking/log.py +0 -0
  123. /cognite/neat/core/{_graph → _instances}/examples/Knowledge-Graph-Nordic44-dirty.xml +0 -0
  124. /cognite/neat/core/{_graph → _instances}/examples/Knowledge-Graph-Nordic44.xml +0 -0
  125. /cognite/neat/core/{_graph → _instances}/examples/__init__.py +0 -0
  126. /cognite/neat/core/{_graph → _instances}/examples/skos-capturing-sheet-wind-topics.xlsx +0 -0
  127. /cognite/neat/core/{_graph → _instances}/extractors/__init__.py +0 -0
  128. /cognite/neat/core/{_rules → _instances/extractors/_classic_cdf}/__init__.py +0 -0
  129. /cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_assets.py +0 -0
  130. /cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_data_sets.py +0 -0
  131. /cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_events.py +0 -0
  132. /cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_files.py +0 -0
  133. /cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_labels.py +0 -0
  134. /cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_relationships.py +0 -0
  135. /cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_sequences.py +0 -0
  136. /cognite/neat/core/{_graph → _instances}/extractors/_classic_cdf/_timeseries.py +0 -0
  137. /cognite/neat/core/{_graph → _instances}/extractors/_dict.py +0 -0
  138. /cognite/neat/core/{_graph → _instances}/extractors/_dms.py +0 -0
  139. /cognite/neat/core/{_graph → _instances}/extractors/_raw.py +0 -0
  140. /cognite/neat/core/{_graph → _instances}/loaders/__init__.py +0 -0
  141. /cognite/neat/core/{_graph → _instances}/queries/__init__.py +0 -0
  142. /cognite/neat/core/{_graph → _instances}/queries/_base.py +0 -0
  143. /cognite/neat/core/{_graph → _instances}/queries/_queries.py +0 -0
  144. /cognite/neat/core/{_graph → _instances}/queries/_select.py +0 -0
  145. /cognite/neat/core/{_graph → _instances}/queries/_update.py +0 -0
  146. /cognite/neat/core/{_graph → _instances}/transformers/__init__.py +0 -0
  147. /cognite/neat/core/{_graph → _instances}/transformers/_prune_graph.py +0 -0
  148. /cognite/neat/core/{_graph → _instances}/transformers/_rdfpath.py +0 -0
  149. {cognite_neat-0.121.0.dist-info → cognite_neat-0.121.2.dist-info}/WHEEL +0 -0
  150. {cognite_neat-0.121.0.dist-info → cognite_neat-0.121.2.dist-info}/licenses/LICENSE +0 -0
@@ -11,58 +11,58 @@ import pandas as pd
11
11
  from cognite.client import data_modeling as dm
12
12
  from rdflib import URIRef
13
13
 
14
- from cognite.neat.core._issues.errors import NeatValueError
15
- from cognite.neat.core._issues.warnings import NeatValueWarning
16
- from cognite.neat.core._rules.models import DMSRules, InformationRules
17
- from cognite.neat.core._rules.models.dms import DMSProperty
18
- from cognite.neat.core._rules.models.dms._rules import DMSView
19
- from cognite.neat.core._rules.models.entities import (
20
- ClassEntity,
14
+ from cognite.neat.core._data_model.models import ConceptualDataModel, PhysicalDataModel
15
+ from cognite.neat.core._data_model.models.conceptual import (
16
+ Concept,
17
+ ConceptualProperty,
18
+ )
19
+ from cognite.neat.core._data_model.models.entities import (
20
+ ConceptEntity,
21
21
  MultiValueTypeInfo,
22
22
  ViewEntity,
23
23
  )
24
- from cognite.neat.core._rules.models.entities._single_value import (
24
+ from cognite.neat.core._data_model.models.entities._single_value import (
25
25
  UnknownEntity,
26
26
  )
27
- from cognite.neat.core._rules.models.information import (
28
- InformationClass,
29
- InformationProperty,
30
- )
27
+ from cognite.neat.core._data_model.models.physical import PhysicalProperty
28
+ from cognite.neat.core._data_model.models.physical._verified import PhysicalView
29
+ from cognite.neat.core._issues.errors import NeatValueError
30
+ from cognite.neat.core._issues.warnings import NeatValueWarning
31
31
 
32
32
  T_Hashable = TypeVar("T_Hashable", bound=Hashable)
33
33
 
34
34
 
35
35
  @dataclass(frozen=True)
36
36
  class Linkage:
37
- source_class: ClassEntity
37
+ source_concept: ConceptEntity
38
38
  connecting_property: str
39
- target_class: ClassEntity
39
+ target_concept: ConceptEntity
40
40
  max_occurrence: int | float | None
41
41
 
42
42
 
43
43
  class LinkageSet(set, Set[Linkage]):
44
44
  @property
45
- def source_class(self) -> set[ClassEntity]:
46
- return {link.source_class for link in self}
45
+ def source_concept(self) -> set[ConceptEntity]:
46
+ return {link.source_concept for link in self}
47
47
 
48
48
  @property
49
- def target_class(self) -> set[ClassEntity]:
50
- return {link.target_class for link in self}
49
+ def target_concept(self) -> set[ConceptEntity]:
50
+ return {link.target_concept for link in self}
51
51
 
52
- def get_target_classes_by_source(self) -> dict[ClassEntity, set[ClassEntity]]:
53
- target_classes_by_source: dict[ClassEntity, set[ClassEntity]] = defaultdict(set)
52
+ def get_target_concepts_by_source(self) -> dict[ConceptEntity, set[ConceptEntity]]:
53
+ target_concepts_by_source: dict[ConceptEntity, set[ConceptEntity]] = defaultdict(set)
54
54
  for link in self:
55
- target_classes_by_source[link.source_class].add(link.target_class)
56
- return target_classes_by_source
55
+ target_concepts_by_source[link.source_concept].add(link.target_concept)
56
+ return target_concepts_by_source
57
57
 
58
58
  def to_pandas(self) -> pd.DataFrame:
59
59
  # Todo: Remove this method
60
60
  return pd.DataFrame(
61
61
  [
62
62
  {
63
- "source_class": link.source_class,
63
+ "source_concept": link.source_concept,
64
64
  "connecting_property": link.connecting_property,
65
- "target_class": link.target_class,
65
+ "target_concept": link.target_concept,
66
66
  "max_occurrence": link.max_occurrence,
67
67
  }
68
68
  for link in self
@@ -110,105 +110,112 @@ class ViewQueryDict(dict, MutableMapping[dm.ViewId, ViewQuery]):
110
110
  return super().popitem()
111
111
 
112
112
 
113
- class RulesAnalysis:
114
- def __init__(self, information: InformationRules | None = None, dms: DMSRules | None = None) -> None:
115
- self._information = information
116
- self._dms = dms
113
+ class DataModelAnalysis:
114
+ def __init__(
115
+ self,
116
+ conceptual: ConceptualDataModel | None = None,
117
+ physical: PhysicalDataModel | None = None,
118
+ ) -> None:
119
+ self._conceptual = conceptual
120
+ self._physical = physical
117
121
 
118
122
  @property
119
- def information(self) -> InformationRules:
120
- if self._information is None:
121
- raise NeatValueError("Information rules are required for this analysis")
122
- return self._information
123
+ def conceptual(self) -> ConceptualDataModel:
124
+ if self._conceptual is None:
125
+ raise NeatValueError("Conceptual Data Model is required for this analysis")
126
+ return self._conceptual
123
127
 
124
128
  @property
125
- def dms(self) -> DMSRules:
126
- if self._dms is None:
127
- raise NeatValueError("DMS rules are required for this analysis")
128
- return self._dms
129
+ def physical(self) -> PhysicalDataModel:
130
+ if self._physical is None:
131
+ raise NeatValueError("Physical Data Model is required for this analysis")
132
+ return self._physical
129
133
 
130
- def parents_by_class(
134
+ def parents_by_concept(
131
135
  self, include_ancestors: bool = False, include_different_space: bool = False
132
- ) -> dict[ClassEntity, set[ClassEntity]]:
133
- """Get a dictionary of classes and their parents.
136
+ ) -> dict[ConceptEntity, set[ConceptEntity]]:
137
+ """Get a dictionary of concepts and their parents.
134
138
 
135
139
  Args:
136
140
  include_ancestors (bool, optional): Include ancestors of the parents. Defaults to False.
137
141
  include_different_space (bool, optional): Include parents from different spaces. Defaults to False.
138
142
 
139
143
  Returns:
140
- dict[ClassEntity, set[ClassEntity]]: Values parents with class as key.
144
+ dict[ConceptEntity, set[ConceptEntity]]: Values parents with concept as key.
141
145
  """
142
- parents_by_class: dict[ClassEntity, set[ClassEntity]] = {}
143
- for class_ in self.information.classes:
144
- parents_by_class[class_.class_] = set()
145
- for parent in class_.implements or []:
146
- if include_different_space or parent.prefix == class_.class_.prefix:
147
- parents_by_class[class_.class_].add(parent)
146
+ parents_by_concept: dict[ConceptEntity, set[ConceptEntity]] = {}
147
+ for concept in self.conceptual.concepts:
148
+ parents_by_concept[concept.concept] = set()
149
+ for parent in concept.implements or []:
150
+ if include_different_space or parent.prefix == concept.concept.prefix:
151
+ parents_by_concept[concept.concept].add(parent)
148
152
  else:
149
153
  warnings.warn(
150
154
  NeatValueWarning(
151
- f"Parent class {parent} of class {class_} is not in the same namespace, skipping!"
155
+ f"Parent concept {parent} of concept {concept} is not in the same namespace, skipping!"
152
156
  ),
153
157
  stacklevel=2,
154
158
  )
155
159
  if include_ancestors:
156
- self._include_ancestors(parents_by_class)
160
+ self._include_ancestors(parents_by_concept)
157
161
 
158
- return parents_by_class
162
+ return parents_by_concept
159
163
 
160
164
  @staticmethod
161
- def _include_ancestors(parents_by_class: dict[T_Hashable, set[T_Hashable]]) -> None:
162
- # Topological sort to ensure that classes include all ancestors
163
- for class_entity in list(TopologicalSorter(parents_by_class).static_order()):
164
- if class_entity not in parents_by_class:
165
+ def _include_ancestors(
166
+ parents_by_concept: dict[T_Hashable, set[T_Hashable]],
167
+ ) -> None:
168
+ # Topological sort to ensure that concepts include all ancestors
169
+ for concept_entity in list(TopologicalSorter(parents_by_concept).static_order()):
170
+ if concept_entity not in parents_by_concept:
165
171
  continue
166
- parents_by_class[class_entity] |= {
172
+ parents_by_concept[concept_entity] |= {
167
173
  grand_parent
168
- for parent in parents_by_class[class_entity]
169
- for grand_parent in parents_by_class.get(parent, set())
174
+ for parent in parents_by_concept[concept_entity]
175
+ for grand_parent in parents_by_concept.get(parent, set())
170
176
  }
171
177
 
172
- def properties_by_class(
178
+ def properties_by_concepts(
173
179
  self, include_ancestors: bool = False, include_different_space: bool = False
174
- ) -> dict[ClassEntity, list[InformationProperty]]:
175
- """Get a dictionary of classes and their properties.
180
+ ) -> dict[ConceptEntity, list[ConceptualProperty]]:
181
+ """Get a dictionary of concepts and their properties.
176
182
 
177
183
  Args:
178
- include_ancestors: Whether to include properties from parent classes.
179
- include_different_space: Whether to include properties from parent classes in different spaces.
184
+ include_ancestors: Whether to include properties from parent concepts.
185
+ include_different_space: Whether to include properties from parent concepts in different spaces.
180
186
 
181
187
  Returns:
182
- dict[ClassEntity, list[InformationProperty]]: Values properties with class as key.
188
+ dict[ConceptEntity, list[ConceptualProperty]]: Values properties with concept as key.
183
189
 
184
190
  """
185
- properties_by_classes: dict[ClassEntity, list[InformationProperty]] = defaultdict(list)
186
- for prop in self.information.properties:
187
- properties_by_classes[prop.class_].append(prop)
191
+ properties_by_concepts: dict[ConceptEntity, list[ConceptualProperty]] = defaultdict(list)
192
+ for prop in self.conceptual.properties:
193
+ properties_by_concepts[prop.concept].append(prop)
188
194
 
189
195
  if include_ancestors:
190
- parents_by_classes = self.parents_by_class(
191
- include_ancestors=include_ancestors, include_different_space=include_different_space
196
+ parents_by_concepts = self.parents_by_concept(
197
+ include_ancestors=include_ancestors,
198
+ include_different_space=include_different_space,
192
199
  )
193
- for class_, parents in parents_by_classes.items():
194
- class_properties = {prop.property_ for prop in properties_by_classes[class_]}
200
+ for concept, parents in parents_by_concepts.items():
201
+ concept_properties = {prop.property_ for prop in properties_by_concepts[concept]}
195
202
  for parent in parents:
196
- for parent_prop in properties_by_classes[parent]:
197
- if parent_prop.property_ not in class_properties:
198
- child_prop = parent_prop.model_copy(update={"class_": class_})
199
- properties_by_classes[class_].append(child_prop)
200
- class_properties.add(child_prop.property_)
203
+ for parent_prop in properties_by_concepts[parent]:
204
+ if parent_prop.property_ not in concept_properties:
205
+ child_prop = parent_prop.model_copy(update={"concept": concept})
206
+ properties_by_concepts[concept].append(child_prop)
207
+ concept_properties.add(child_prop.property_)
201
208
 
202
- return properties_by_classes
209
+ return properties_by_concepts
203
210
 
204
211
  def implements_by_view(
205
212
  self, include_ancestors: bool = False, include_different_space: bool = False
206
213
  ) -> dict[ViewEntity, set[ViewEntity]]:
207
214
  """Get a dictionary of views and their implemented views."""
208
- # This is a duplicate fo the parent_by_class method, but for views
215
+ # This is a duplicate fo the parent_by_concept method, but for views
209
216
  # The choice to duplicate the code is to avoid generics which will make the code less readable
210
217
  implements_by_view: dict[ViewEntity, set[ViewEntity]] = {}
211
- for view in self.dms.views:
218
+ for view in self.physical.views:
212
219
  implements_by_view[view.view] = set()
213
220
  for implements in view.implements or []:
214
221
  if include_different_space or implements.space == view.view.space:
@@ -226,12 +233,12 @@ class RulesAnalysis:
226
233
 
227
234
  def properties_by_view(
228
235
  self, include_ancestors: bool = False, include_different_space: bool = False
229
- ) -> dict[ViewEntity, list[DMSProperty]]:
236
+ ) -> dict[ViewEntity, list[PhysicalProperty]]:
230
237
  """Get a dictionary of views and their properties."""
231
- # This is a duplicate fo the properties_by_class method, but for views
238
+ # This is a duplicate fo the properties_by_concept method, but for views
232
239
  # The choice to duplicate the code is to avoid generics which will make the code less readable.
233
- properties_by_views: dict[ViewEntity, list[DMSProperty]] = defaultdict(list)
234
- for prop in self.dms.properties:
240
+ properties_by_views: dict[ViewEntity, list[PhysicalProperty]] = defaultdict(list)
241
+ for prop in self.physical.properties:
235
242
  properties_by_views[prop.view].append(prop)
236
243
 
237
244
  if include_ancestors:
@@ -250,11 +257,11 @@ class RulesAnalysis:
250
257
  return properties_by_views
251
258
 
252
259
  @property
253
- def logical_uri_by_view(self) -> dict[ViewEntity, URIRef]:
260
+ def conceptual_uri_by_view(self) -> dict[ViewEntity, URIRef]:
254
261
  """Get the logical URI by view."""
255
- return {view.view: view.logical for view in self.dms.views if view.logical}
262
+ return {view.view: view.conceptual for view in self.physical.views if view.conceptual}
256
263
 
257
- def logical_uri_by_property_by_view(
264
+ def conceptual_uri_by_property_by_view(
258
265
  self,
259
266
  include_ancestors: bool = False,
260
267
  include_different_space: bool = False,
@@ -263,68 +270,68 @@ class RulesAnalysis:
263
270
  properties_by_view = self.properties_by_view(include_ancestors, include_different_space)
264
271
 
265
272
  return {
266
- view: {prop.view_property: prop.logical for prop in properties if prop.logical}
273
+ view: {prop.view_property: prop.conceptual for prop in properties if prop.conceptual}
267
274
  for view, properties in properties_by_view.items()
268
275
  }
269
276
 
270
277
  @property
271
- def _class_by_neat_id(self) -> dict[URIRef, InformationClass]:
272
- """Get a dictionary of class neat IDs to
273
- class entities."""
278
+ def _concept_by_neat_id(self) -> dict[URIRef, Concept]:
279
+ """Get a dictionary of concept neat IDs to
280
+ concept entities."""
274
281
 
275
- return {cls.neatId: cls for cls in self.information.classes if cls.neatId}
282
+ return {cls.neatId: cls for cls in self.conceptual.concepts if cls.neatId}
276
283
 
277
- def class_by_suffix(self) -> dict[str, InformationClass]:
278
- """Get a dictionary of class suffixes to class entities."""
284
+ def concept_by_suffix(self) -> dict[str, Concept]:
285
+ """Get a dictionary of concept suffixes to concept entities."""
279
286
  # TODO: Remove this method
280
- class_dict: dict[str, InformationClass] = {}
281
- for definition in self.information.classes:
282
- entity = definition.class_
283
- if entity.suffix in class_dict:
287
+ concept_dict: dict[str, Concept] = {}
288
+ for definition in self.conceptual.concepts:
289
+ entity = definition.concept
290
+ if entity.suffix in concept_dict:
284
291
  warnings.warn(
285
292
  NeatValueWarning(
286
- f"Class {entity} has been defined more than once! Only the first definition "
293
+ f"Concept {entity} has been defined more than once! Only the first definition "
287
294
  "will be considered, skipping the rest.."
288
295
  ),
289
296
  stacklevel=2,
290
297
  )
291
298
  continue
292
- class_dict[entity.suffix] = definition
293
- return class_dict
299
+ concept_dict[entity.suffix] = definition
300
+ return concept_dict
294
301
 
295
302
  @property
296
- def class_by_class_entity(self) -> dict[ClassEntity, InformationClass]:
297
- """Get a dictionary of class entities to class entities."""
298
- rules = self.information
299
- return {cls.class_: cls for cls in rules.classes}
303
+ def concept_by_concept_entity(self) -> dict[ConceptEntity, Concept]:
304
+ """Get a dictionary of concept entities to concept entities."""
305
+ data_model = self.conceptual
306
+ return {cls.concept: cls for cls in data_model.concepts}
300
307
 
301
308
  @property
302
- def view_by_view_entity(self) -> dict[ViewEntity, DMSView]:
303
- """Get a dictionary of class entities to class entities."""
304
- rules = self.dms
305
- return {view.view: view for view in rules.views}
309
+ def view_by_view_entity(self) -> dict[ViewEntity, PhysicalView]:
310
+ """Get a dictionary of views to view entities."""
311
+ data_model = self.physical
312
+ return {view.view: view for view in data_model.views}
306
313
 
307
- def property_by_id(self) -> dict[str, list[InformationProperty]]:
314
+ def property_by_id(self) -> dict[str, list[ConceptualProperty]]:
308
315
  """Get a dictionary of property IDs to property entities."""
309
- property_dict: dict[str, list[InformationProperty]] = defaultdict(list)
310
- for prop in self.information.properties:
316
+ property_dict: dict[str, list[ConceptualProperty]] = defaultdict(list)
317
+ for prop in self.conceptual.properties:
311
318
  property_dict[prop.property_].append(prop)
312
319
  return property_dict
313
320
 
314
- def properties_by_id_by_class(
321
+ def properties_by_id_by_concept(
315
322
  self,
316
323
  has_instance_source: bool = False,
317
324
  include_ancestors: bool = False,
318
- ) -> dict[ClassEntity, dict[str, InformationProperty]]:
319
- """Get a dictionary of class entities to dictionaries of property IDs to property entities."""
320
- class_property_pairs: dict[ClassEntity, dict[str, InformationProperty]] = {}
321
- for class_, properties in self.properties_by_class(include_ancestors).items():
322
- processed_properties: dict[str, InformationProperty] = {}
325
+ ) -> dict[ConceptEntity, dict[str, ConceptualProperty]]:
326
+ """Get a dictionary of concept entities to dictionaries of property IDs to property entities."""
327
+ concept_property_pairs: dict[ConceptEntity, dict[str, ConceptualProperty]] = {}
328
+ for concept, properties in self.properties_by_concepts(include_ancestors).items():
329
+ processed_properties: dict[str, ConceptualProperty] = {}
323
330
  for prop in properties:
324
331
  if prop.property_ in processed_properties:
325
332
  warnings.warn(
326
333
  NeatValueWarning(
327
- f"Property {processed_properties} for {class_} has been defined more than once!"
334
+ f"Property {processed_properties} for {concept} has been defined more than once!"
328
335
  " Only the first definition will be considered, skipping the rest.."
329
336
  ),
330
337
  stacklevel=2,
@@ -333,34 +340,34 @@ class RulesAnalysis:
333
340
  if has_instance_source and prop.instance_source is None:
334
341
  continue
335
342
  processed_properties[prop.property_] = prop
336
- class_property_pairs[class_] = processed_properties
343
+ concept_property_pairs[concept] = processed_properties
337
344
 
338
- return class_property_pairs
345
+ return concept_property_pairs
339
346
 
340
347
  def defined_views(self, include_ancestors: bool = False) -> set[ViewEntity]:
341
348
  properties_by_view = self.properties_by_view(include_ancestors)
342
349
  return {prop.view for prop in itertools.chain.from_iterable(properties_by_view.values())}
343
350
 
344
- def defined_classes(
351
+ def defined_concepts(
345
352
  self,
346
353
  include_ancestors: bool = False,
347
- ) -> set[ClassEntity]:
348
- """Returns classes that have properties defined for them in the data model.
354
+ ) -> set[ConceptEntity]:
355
+ """Returns concepts that have properties defined for them in the data model.
349
356
 
350
357
  Args:
351
358
  include_ancestors: Whether to consider inheritance or not. Defaults False
352
359
 
353
360
  Returns:
354
- Set of classes that have been defined in the data model
361
+ Set of concepts that have been defined in the data model
355
362
  """
356
- properties_by_class = self.properties_by_class(include_ancestors)
357
- return {prop.class_ for prop in itertools.chain.from_iterable(properties_by_class.values())}
363
+ properties_by_concept = self.properties_by_concepts(include_ancestors)
364
+ return {prop.concept for prop in itertools.chain.from_iterable(properties_by_concept.values())}
358
365
 
359
- def class_linkage(
366
+ def concept_linkage(
360
367
  self,
361
368
  include_ancestors: bool = False,
362
369
  ) -> LinkageSet:
363
- """Returns a set of class linkages in the data model.
370
+ """Returns a set of concept linkages in the data model.
364
371
 
365
372
  Args:
366
373
  include_ancestors: Whether to consider inheritance or not. Defaults False
@@ -368,105 +375,105 @@ class RulesAnalysis:
368
375
  Returns:
369
376
 
370
377
  """
371
- class_linkage = LinkageSet()
378
+ concept_linkage = LinkageSet()
372
379
 
373
- properties_by_class = self.properties_by_class(include_ancestors)
380
+ properties_by_concept = self.properties_by_concepts(include_ancestors)
374
381
 
375
- prop: InformationProperty
376
- for prop in itertools.chain.from_iterable(properties_by_class.values()):
377
- if not isinstance(prop.value_type, ClassEntity):
382
+ prop: ConceptualProperty
383
+ for prop in itertools.chain.from_iterable(properties_by_concept.values()):
384
+ if not isinstance(prop.value_type, ConceptEntity):
378
385
  continue
379
- class_linkage.add(
386
+ concept_linkage.add(
380
387
  Linkage(
381
- source_class=prop.class_,
388
+ source_concept=prop.concept,
382
389
  connecting_property=prop.property_,
383
- target_class=prop.value_type,
390
+ target_concept=prop.value_type,
384
391
  max_occurrence=prop.max_count,
385
392
  )
386
393
  )
387
394
 
388
- return class_linkage
395
+ return concept_linkage
389
396
 
390
- def symmetrically_connected_classes(
397
+ def symmetrically_connected_concepts(
391
398
  self,
392
399
  include_ancestors: bool = False,
393
- ) -> set[tuple[ClassEntity, ClassEntity]]:
394
- """Returns a set of pairs of symmetrically linked classes.
400
+ ) -> set[tuple[ConceptEntity, ConceptEntity]]:
401
+ """Returns a set of pairs of symmetrically linked concepts.
395
402
 
396
403
  Args:
397
404
  include_ancestors: Whether to consider inheritance or not. Defaults False
398
405
 
399
406
  Returns:
400
- Set of pairs of symmetrically linked classes
407
+ Set of pairs of symmetrically linked concepts
401
408
 
402
- !!! note "Symmetrically Connected Classes"
403
- Symmetrically connected classes are classes that are connected to each other
404
- in both directions. For example, if class A is connected to class B, and class B
405
- is connected to class A, then classes A and B are symmetrically connected.
409
+ !!! note "Symmetrically Connected Concepts"
410
+ Symmetrically connected concepts are concepts that are connected to each other
411
+ in both directions. For example, if concept A is connected to concept B, and concept B
412
+ is connected to concept A, then concepts A and B are symmetrically connected.
406
413
  """
407
- sym_pairs: set[tuple[ClassEntity, ClassEntity]] = set()
408
- class_linkage = self.class_linkage(include_ancestors)
409
- if not class_linkage:
414
+ sym_pairs: set[tuple[ConceptEntity, ConceptEntity]] = set()
415
+ concept_linkage = self.concept_linkage(include_ancestors)
416
+ if not concept_linkage:
410
417
  return sym_pairs
411
418
 
412
- targets_by_source = class_linkage.get_target_classes_by_source()
413
- for link in class_linkage:
414
- source = link.source_class
415
- target = link.target_class
419
+ targets_by_source = concept_linkage.get_target_concepts_by_source()
420
+ for link in concept_linkage:
421
+ source = link.source_concept
422
+ target = link.target_concept
416
423
 
417
424
  if source in targets_by_source[source] and (source, target) not in sym_pairs:
418
425
  sym_pairs.add((source, target))
419
426
  return sym_pairs
420
427
 
421
428
  @overload
422
- def _properties_by_neat_id(self, format: Literal["info"] = "info") -> dict[URIRef, InformationProperty]: ...
429
+ def _properties_by_neat_id(self, format: Literal["info"] = "info") -> dict[URIRef, ConceptualProperty]: ...
423
430
 
424
431
  @overload
425
- def _properties_by_neat_id(self, format: Literal["dms"] = "dms") -> dict[URIRef, DMSProperty]: ...
432
+ def _properties_by_neat_id(self, format: Literal["dms"] = "dms") -> dict[URIRef, PhysicalProperty]: ...
426
433
 
427
434
  def _properties_by_neat_id(
428
435
  self, format: Literal["info", "dms"] = "info"
429
- ) -> dict[URIRef, InformationProperty] | dict[URIRef, DMSProperty]:
436
+ ) -> dict[URIRef, ConceptualProperty] | dict[URIRef, PhysicalProperty]:
430
437
  if format == "info":
431
- return {prop.neatId: prop for prop in self.information.properties if prop.neatId}
438
+ return {prop.neatId: prop for prop in self.conceptual.properties if prop.neatId}
432
439
  elif format == "dms":
433
- return {prop.neatId: prop for prop in self.dms.properties if prop.neatId}
440
+ return {prop.neatId: prop for prop in self.physical.properties if prop.neatId}
434
441
  else:
435
442
  raise NeatValueError(f"Invalid format: {format}")
436
443
 
437
444
  @property
438
- def classes_by_neat_id(self) -> dict[URIRef, InformationClass]:
439
- return {class_.neatId: class_ for class_ in self.information.classes if class_.neatId}
445
+ def concepts_by_neat_id(self) -> dict[URIRef, Concept]:
446
+ return {concept.neatId: concept for concept in self.conceptual.concepts if concept.neatId}
440
447
 
441
448
  @property
442
- def multi_value_properties(self) -> list[InformationProperty]:
443
- return [prop_ for prop_ in self.information.properties if isinstance(prop_.value_type, MultiValueTypeInfo)]
449
+ def multi_value_properties(self) -> list[ConceptualProperty]:
450
+ return [prop_ for prop_ in self.conceptual.properties if isinstance(prop_.value_type, MultiValueTypeInfo)]
444
451
 
445
452
  @property
446
453
  def view_query_by_id(
447
454
  self,
448
455
  ) -> "ViewQueryDict":
449
456
  # Trigger error if any of these are missing
450
- _ = self.information
451
- _ = self.dms
457
+ _ = self.conceptual
458
+ _ = self.physical
452
459
 
453
460
  # caching results for faster access
454
- classes_by_neat_id = self._class_by_neat_id
455
- properties_by_class = self.properties_by_class(include_ancestors=True)
456
- logical_uri_by_view = self.logical_uri_by_view
457
- logical_uri_by_property_by_view = self.logical_uri_by_property_by_view(include_ancestors=True)
458
- information_properties_by_neat_id = self._properties_by_neat_id()
461
+ concepts_by_neat_id = self._concept_by_neat_id
462
+ properties_by_concept = self.properties_by_concepts(include_ancestors=True)
463
+ conceptual_uri_by_view = self.conceptual_uri_by_view
464
+ conceptual_uri_by_property_by_view = self.conceptual_uri_by_property_by_view(include_ancestors=True)
465
+ conceptual_properties_by_neat_id = self._properties_by_neat_id()
459
466
 
460
467
  query_configs = ViewQueryDict()
461
- for view in self.dms.views:
468
+ for view in self.physical.views:
462
469
  # this entire block of sequential if statements checks:
463
- # 1. connection of dms to info rules
464
- # 2. correct paring of information and dms rules
465
- # 3. connection of info rules to instances
470
+ # 1. connection of physical and conceptual data model
471
+ # 2. correct paring of conceptual and physical data model
472
+ # 3. connection of conceptual data model to instances
466
473
  if (
467
- (neat_id := logical_uri_by_view.get(view.view))
468
- and (class_ := classes_by_neat_id.get(neat_id))
469
- and (uri := class_.instance_source)
474
+ (neat_id := conceptual_uri_by_view.get(view.view))
475
+ and (concept := concepts_by_neat_id.get(neat_id))
476
+ and (uri := concept.instance_source)
470
477
  ):
471
478
  view_query = ViewQuery(
472
479
  view_id=view.view.as_id(),
@@ -475,14 +482,14 @@ class RulesAnalysis:
475
482
  # this is to encounter for special cases of e.g. space, startNode and endNode
476
483
  property_renaming_config=(
477
484
  {uri: prop_.property_ for prop_ in info_properties for uri in prop_.instance_source or []}
478
- if (info_properties := properties_by_class.get(class_.class_))
485
+ if (info_properties := properties_by_concept.get(concept.concept))
479
486
  else {}
480
487
  ),
481
488
  )
482
489
 
483
- if logical_uri_by_property := logical_uri_by_property_by_view.get(view.view):
484
- for target_name, neat_id in logical_uri_by_property.items():
485
- if (property_ := information_properties_by_neat_id.get(neat_id)) and (
490
+ if conceptual_uri_by_property := conceptual_uri_by_property_by_view.get(view.view):
491
+ for target_name, neat_id in conceptual_uri_by_property.items():
492
+ if (property_ := conceptual_properties_by_neat_id.get(neat_id)) and (
486
493
  uris := property_.instance_source
487
494
  ):
488
495
  for uri in uris:
@@ -492,14 +499,14 @@ class RulesAnalysis:
492
499
 
493
500
  return query_configs
494
501
 
495
- def _dms_di_graph(self, format: Literal["data-model", "implements"] = "data-model") -> nx.MultiDiGraph:
496
- """Generate a MultiDiGraph from the DMS rules."""
502
+ def _physical_di_graph(self, format: Literal["data-model", "implements"] = "data-model") -> nx.MultiDiGraph:
503
+ """Generate a MultiDiGraph from the Physical Data Model."""
497
504
  di_graph = nx.MultiDiGraph()
498
505
 
499
- rules = self.dms
506
+ data_model = self.physical
500
507
 
501
508
  # Add nodes and edges from Views sheet
502
- for view in rules.views:
509
+ for view in data_model.views:
503
510
  di_graph.add_node(view.view.suffix, label=view.view.suffix)
504
511
 
505
512
  if format == "implements" and view.implements:
@@ -514,7 +521,7 @@ class RulesAnalysis:
514
521
 
515
522
  if format == "data-model":
516
523
  # Add nodes and edges from Properties sheet
517
- for prop_ in rules.properties:
524
+ for prop_ in data_model.properties:
518
525
  if prop_.connection and isinstance(prop_.value_type, ViewEntity):
519
526
  di_graph.add_node(prop_.view.suffix, label=prop_.view.suffix)
520
527
  di_graph.add_node(prop_.value_type.suffix, label=prop_.value_type.suffix)
@@ -526,26 +533,26 @@ class RulesAnalysis:
526
533
 
527
534
  return di_graph
528
535
 
529
- def _info_di_graph(self, format: Literal["data-model", "implements"] = "data-model") -> nx.MultiDiGraph:
530
- """Generate MultiDiGraph representing information data model."""
536
+ def _conceptual_di_graph(self, format: Literal["data-model", "implements"] = "data-model") -> nx.MultiDiGraph:
537
+ """Generate MultiDiGraph representing conceptual data model."""
531
538
 
532
- rules = self.information
539
+ data_model = self.conceptual
533
540
  di_graph = nx.MultiDiGraph()
534
541
 
535
542
  # Add nodes and edges from Views sheet
536
- for class_ in rules.classes:
543
+ for concept in data_model.concepts:
537
544
  # if possible use human readable label coming from the view name
538
545
 
539
546
  di_graph.add_node(
540
- class_.class_.suffix,
541
- label=class_.name or class_.class_.suffix,
547
+ concept.concept.suffix,
548
+ label=concept.name or concept.concept.suffix,
542
549
  )
543
550
 
544
- if format == "implements" and class_.implements:
545
- for parent in class_.implements:
551
+ if format == "implements" and concept.implements:
552
+ for parent in concept.implements:
546
553
  di_graph.add_node(parent.suffix, label=parent.suffix)
547
554
  di_graph.add_edge(
548
- class_.class_.suffix,
555
+ concept.concept.suffix,
549
556
  parent.suffix,
550
557
  label="implements",
551
558
  dashes=True,
@@ -553,13 +560,13 @@ class RulesAnalysis:
553
560
 
554
561
  if format == "data-model":
555
562
  # Add nodes and edges from Properties sheet
556
- for prop_ in rules.properties:
557
- if isinstance(prop_.value_type, ClassEntity) and not isinstance(prop_.value_type, UnknownEntity):
558
- di_graph.add_node(prop_.class_.suffix, label=prop_.class_.suffix)
563
+ for prop_ in data_model.properties:
564
+ if isinstance(prop_.value_type, ConceptEntity) and not isinstance(prop_.value_type, UnknownEntity):
565
+ di_graph.add_node(prop_.concept.suffix, label=prop_.concept.suffix)
559
566
  di_graph.add_node(prop_.value_type.suffix, label=prop_.value_type.suffix)
560
567
 
561
568
  di_graph.add_edge(
562
- prop_.class_.suffix,
569
+ prop_.concept.suffix,
563
570
  prop_.value_type.suffix,
564
571
  label=prop_.name or prop_.property_,
565
572
  )