cognite-neat 0.127.26__py3-none-any.whl → 0.127.28__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.
@@ -14,6 +14,7 @@ from cognite.neat._data_model.models.dms._view_property import (
14
14
  EdgeProperty,
15
15
  ReverseDirectRelationProperty,
16
16
  ViewCorePropertyRequest,
17
+ ViewRequestProperty,
17
18
  )
18
19
  from cognite.neat._data_model.models.dms._views import ViewRequest
19
20
 
@@ -82,6 +83,15 @@ class DataModelAnalysis:
82
83
 
83
84
  return view_by_reference
84
85
 
86
+ @property
87
+ def properties_by_view(self) -> dict[ViewReference, dict[str, ViewRequestProperty]]:
88
+ """Get a mapping of view references to their corresponding properties."""
89
+ view_by_reference = self.view_by_reference(include_inherited_properties=False)
90
+ return {
91
+ view_ref: {prop_name: prop for prop_name, prop in view.properties.items()}
92
+ for view_ref, view in view_by_reference.items()
93
+ }
94
+
85
95
  @staticmethod
86
96
  def ancestors_by_view(views: list[ViewRequest]) -> dict[ViewReference, set[ViewReference]]:
87
97
  """Get a mapping of each view to its ancestors in the physical data model."""
@@ -1,4 +1,11 @@
1
- from ._ai_readiness import DataModelMissingDescription, DataModelMissingName, ViewMissingDescription, ViewMissingName
1
+ from ._ai_readiness import (
2
+ DataModelMissingDescription,
3
+ DataModelMissingName,
4
+ ViewMissingDescription,
5
+ ViewMissingName,
6
+ ViewPropertyMissingDescription,
7
+ ViewPropertyMissingName,
8
+ )
2
9
  from ._connections import (
3
10
  ConnectionValueTypeUndefined,
4
11
  ConnectionValueTypeUnexisting,
@@ -57,6 +64,8 @@ __all__ = [
57
64
  "ViewMissingDescription",
58
65
  "ViewMissingName",
59
66
  "ViewPropertyCountIsOutOfLimits",
67
+ "ViewPropertyMissingDescription",
68
+ "ViewPropertyMissingName",
60
69
  "ViewSpaceVersionInconsistentWithDataModel",
61
70
  "ViewToContainerMappingNotPossible",
62
71
  ]
@@ -165,3 +165,88 @@ class ViewMissingDescription(DataModelValidator):
165
165
  )
166
166
 
167
167
  return recommendations
168
+
169
+
170
+ class ViewPropertyMissingName(DataModelValidator):
171
+ """Validates that a view property has a human-readable name.
172
+
173
+ ## What it does
174
+ Validates that each view property in the data model has a human-readable name.
175
+
176
+ ## Why is this bad?
177
+ A missing name makes it harder for users (humans or machines) to understand the purpose of the view property.
178
+ This is important as view property's ids are often based on technical identifiers, abbreviations, etc.
179
+ Providing a clear name improves usability, maintainability, searchability, and AI-readiness.
180
+
181
+ ## Example
182
+ A view WindTurbine has a property pc which has no name. Users may find it difficult to understand what this view
183
+ property represents, unless they look up the id in documentation or other resources. Adding name "power curve"
184
+ would increase clarity and usability.
185
+ """
186
+
187
+ code = f"{BASE_CODE}-005"
188
+
189
+ def run(self) -> list[Recommendation]:
190
+ recommendations: list[Recommendation] = []
191
+
192
+ for view_ref in self.local_resources.data_model_views:
193
+ if properties := self.local_resources.properties_by_view.get(view_ref):
194
+ for prop_ref, definition in properties.items():
195
+ if not definition.name:
196
+ recommendations.append(
197
+ Recommendation(
198
+ message=f"View {view_ref!s} property {prop_ref!s} is missing a human-readable name.",
199
+ fix="Add a clear and concise name to the view property.",
200
+ code=self.code,
201
+ )
202
+ )
203
+
204
+ return recommendations
205
+
206
+
207
+ class ViewPropertyMissingDescription(DataModelValidator):
208
+ """Validates that a View property has a human-readable description.
209
+
210
+ ## What it does
211
+ Validates that each view property in the data model has a human-readable description.
212
+
213
+ ## Why is this bad?
214
+ A missing description makes it harder for users (humans or machines) to understand in what context the view property
215
+ should be used. The description can provide important information about the view property's purpose,
216
+ scope, and usage.
217
+
218
+
219
+ ## Example
220
+ A view WindTurbine has a property status with no description. Users may find it difficult to understand what this
221
+ property represents, unless extra context is provided. Even if we know that status is related to wind turbine
222
+ operations, a description is necessary as it can have different meanings in various contexts:
223
+
224
+ Option 1 — Operational status
225
+ Current operational state of the wind turbine (e.g., running, stopped, maintenance, fault).
226
+
227
+ Option 2 — Connection status
228
+ Grid connection status indicating whether the turbine is connected to the electrical grid.
229
+
230
+ Option 3 — Availability status
231
+ Availability state for production indicating whether the turbine is available for power generation.
232
+
233
+ """
234
+
235
+ code = f"{BASE_CODE}-006"
236
+
237
+ def run(self) -> list[Recommendation]:
238
+ recommendations: list[Recommendation] = []
239
+
240
+ for view_ref in self.local_resources.data_model_views:
241
+ if properties := self.local_resources.properties_by_view.get(view_ref):
242
+ for prop_ref, definition in properties.items():
243
+ if not definition.description:
244
+ recommendations.append(
245
+ Recommendation(
246
+ message=f"View {view_ref!s} property {prop_ref!s} is missing a description.",
247
+ fix="Add a clear and concise description to the view property.",
248
+ code=self.code,
249
+ )
250
+ )
251
+
252
+ return recommendations
@@ -13,6 +13,7 @@ from cognite.neat._data_model.models.dms._references import (
13
13
  ViewDirectReference,
14
14
  ViewReference,
15
15
  )
16
+ from cognite.neat._data_model.models.dms._view_property import ViewRequestProperty
16
17
  from cognite.neat._data_model.models.dms._views import ViewRequest
17
18
  from cognite.neat._issues import ConsistencyError, Recommendation
18
19
  from cognite.neat._utils.useful_types import ModusOperandi
@@ -36,6 +37,7 @@ class LocalResources:
36
37
  data_model_description: str | None
37
38
  data_model_name: str | None
38
39
  views_by_reference: ViewsByReference
40
+ properties_by_view: dict[ViewReference, dict[str, ViewRequestProperty]]
39
41
  ancestors_by_view_reference: AncestorsByReference
40
42
  reverse_to_direct_mapping: ReverseToDirectMapping
41
43
  containers_by_reference: ContainersByReference
@@ -19,7 +19,14 @@ from cognite.neat._data_model.validation.dms._limits import (
19
19
  )
20
20
  from cognite.neat._utils.useful_types import ModusOperandi
21
21
 
22
- from ._ai_readiness import DataModelMissingDescription, DataModelMissingName, ViewMissingDescription, ViewMissingName
22
+ from ._ai_readiness import (
23
+ DataModelMissingDescription,
24
+ DataModelMissingName,
25
+ ViewMissingDescription,
26
+ ViewMissingName,
27
+ ViewPropertyMissingDescription,
28
+ ViewPropertyMissingName,
29
+ )
23
30
  from ._base import CDFResources, DataModelValidator, LocalResources
24
31
  from ._connections import (
25
32
  ConnectionValueTypeUndefined,
@@ -74,6 +81,7 @@ class DmsDataModelValidation(OnSuccessIssuesChecker):
74
81
  data_model_name=data_model.data_model.name,
75
82
  data_model_reference=data_model.data_model.as_reference(),
76
83
  views_by_reference=local_views_by_reference,
84
+ properties_by_view=analysis.properties_by_view,
77
85
  ancestors_by_view_reference=local_ancestors_by_view_reference,
78
86
  reverse_to_direct_mapping=local_reverse_to_direct_mapping,
79
87
  containers_by_reference=local_containers_by_reference,
@@ -141,6 +149,8 @@ class DmsDataModelValidation(OnSuccessIssuesChecker):
141
149
  DataModelMissingDescription(local_resources, cdf_resources, self._modus_operandi),
142
150
  ViewMissingName(local_resources, cdf_resources, self._modus_operandi),
143
151
  ViewMissingDescription(local_resources, cdf_resources, self._modus_operandi),
152
+ ViewPropertyMissingName(local_resources, cdf_resources, self._modus_operandi),
153
+ ViewPropertyMissingDescription(local_resources, cdf_resources, self._modus_operandi),
144
154
  ]
145
155
 
146
156
  # Run validators
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.127.26"
1
+ __version__ = "0.127.28"
2
2
  __engine__ = "^2.0.4"
@@ -243,17 +243,21 @@ class _DMSExporter:
243
243
  connection = cast(EdgeEntity, prop.connection)
244
244
 
245
245
  if connection.direction == "inwards" and isinstance(prop.value_type, ViewEntity):
246
- edge_type_candidates = outwards_type_by_view_value_type.get((prop.view, prop.value_type), [])
247
- if len(edge_type_candidates) == 0:
248
- # Warning in validation, should not have an inwards connection without an outwards connection
249
- edge_type = cls._default_edge_type_from_view_id(prop.view.as_id(), prop_id)
250
- elif len(edge_type_candidates) == 1:
251
- edge_type = edge_type_candidates[0]
246
+ if connection.edge_type is not None:
247
+ edge_type = connection.edge_type.as_reference()
252
248
  else:
253
- raise NeatValueError(
254
- f"Cannot infer edge type for {view_id}.{prop_id}, multiple candidates: {edge_type_candidates}."
255
- "Please specify edge type explicitly, i.e., edge(type=<YOUR_TYPE>)."
256
- )
249
+ edge_type_candidates = outwards_type_by_view_value_type.get((prop.view, prop.value_type), [])
250
+ if len(edge_type_candidates) == 0:
251
+ # Warning in validation, should not have an inwards connection without an outwards connection
252
+ edge_type = cls._default_edge_type_from_view_id(prop.view.as_id(), prop_id)
253
+ elif len(edge_type_candidates) == 1:
254
+ edge_type = edge_type_candidates[0]
255
+ else:
256
+ raise NeatValueError(
257
+ f"Cannot infer edge type for {view_id}.{prop_id}, multiple candidates: "
258
+ f"{edge_type_candidates}. "
259
+ "Please specify edge type explicitly, i.e., edge(type=<YOUR_TYPE>)."
260
+ )
257
261
  view_property_id = (prop.view, prop.view_property)
258
262
  edge_types_by_view_property_id[view_property_id] = edge_type
259
263
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite-neat
3
- Version: 0.127.26
3
+ Version: 0.127.28
4
4
  Summary: Knowledge graph transformation
5
5
  Project-URL: Documentation, https://cognite-neat.readthedocs-hosted.com/
6
6
  Project-URL: Homepage, https://cognite-neat.readthedocs-hosted.com/
@@ -1,7 +1,7 @@
1
1
  cognite/neat/__init__.py,sha256=Lo4DbjDOwnhCYUoAgPp5RG1fDdF7OlnomalTe7n1ydw,211
2
2
  cognite/neat/_exceptions.py,sha256=ox-5hXpee4UJlPE7HpuEHV2C96aLbLKo-BhPDoOAzhA,1650
3
3
  cognite/neat/_issues.py,sha256=wH1mnkrpBsHUkQMGUHFLUIQWQlfJ_qMfdF7q0d9wNhY,1871
4
- cognite/neat/_version.py,sha256=zp1q6MhRkZE6P6k0LZ7xa5KjU1ZbL4JI5oVv8qlg_5Q,47
4
+ cognite/neat/_version.py,sha256=gGuCUahtO-YTqwVek2YFhvGIxG6LRyAI0xQ1jo5pojk,47
5
5
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  cognite/neat/v1.py,sha256=owqW5Mml2DSZx1AvPvwNRTBngfhBNrQ6EH-7CKL7Jp0,61
7
7
  cognite/neat/_client/__init__.py,sha256=75Bh7eGhaN4sOt3ZcRzHl7pXaheu1z27kmTHeaI05vo,114
@@ -15,7 +15,7 @@ cognite/neat/_client/spaces_api.py,sha256=xHtSMt_2k2YwZ5_8kH2dfa7fWxQQrky7wra4Ar
15
15
  cognite/neat/_client/statistics_api.py,sha256=HcYb2nNC9M_iaI1xyjjLn2Cz1tcyu7BJeaqVps79tg4,773
16
16
  cognite/neat/_client/views_api.py,sha256=vZh851Yw8X-Y90sahOiGnq-pwDOBBXegcxTtypkcMvA,4956
17
17
  cognite/neat/_data_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- cognite/neat/_data_model/_analysis.py,sha256=WJNMRXMBngrjdSPxEZLMg75_6mDZhOuJxrflG4n6Anc,8355
18
+ cognite/neat/_data_model/_analysis.py,sha256=d_gP0M_8-HjSAhWT28cfdOsydNAKmOOT4pTh5W_CQcU,8827
19
19
  cognite/neat/_data_model/_constants.py,sha256=txKFNRPkCRrIkXbf_bLCYBCMkoE_7nc5LliohzMGwOs,1596
20
20
  cognite/neat/_data_model/_identifiers.py,sha256=a0LcQ_h0NffxSKTCrzCDpYkrlaUTk-D_rfaQUh-BhWc,1921
21
21
  cognite/neat/_data_model/_shared.py,sha256=H0gFqa8tKFNWuvdat5jL6OwySjCw3aQkLPY3wtb9Wrw,1302
@@ -73,14 +73,14 @@ cognite/neat/_data_model/models/entities/_data_types.py,sha256=DfdEWGek7gODro-_0
73
73
  cognite/neat/_data_model/models/entities/_identifiers.py,sha256=uBiK4ot3V0b_LGXuJ7bfha6AEcFI3p2letr1z2iSvig,1923
74
74
  cognite/neat/_data_model/models/entities/_parser.py,sha256=zef_pSDZYMZrJl4IKreFDR577KutfhtN1xpH3Ayjt2o,7669
75
75
  cognite/neat/_data_model/validation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
- cognite/neat/_data_model/validation/dms/__init__.py,sha256=qK9zBaXHj7x9k7geA1fRk6ewRB_hGm7bcq69WlDyffc,2357
77
- cognite/neat/_data_model/validation/dms/_ai_readiness.py,sha256=SI46roGc2pbZQwY-6tuSWyS7uMbg2mIzXcVA3NPRCk4,6742
78
- cognite/neat/_data_model/validation/dms/_base.py,sha256=y95k6HWGxX_EnnyuV2ooftgiyC-fwJaaHW28oEf9oBs,13113
76
+ cognite/neat/_data_model/validation/dms/__init__.py,sha256=Ps26r8bY4sLPsceaS6CpgS-s6JxN_zKbiteF5RrQYYw,2512
77
+ cognite/neat/_data_model/validation/dms/_ai_readiness.py,sha256=_7R4BVVcJw27OYmMS-GxxcvQdocr67sU1gGouUo81EU,10525
78
+ cognite/neat/_data_model/validation/dms/_base.py,sha256=HJRoo1fX2Fu0bzlPKhn3VOsrtaOv4ea49BxvEpfOjIY,13272
79
79
  cognite/neat/_data_model/validation/dms/_connections.py,sha256=goIcsGV4yYb-d9ltXg4eM5qxt8iErIIxtkiKJXZUYyM,26937
80
80
  cognite/neat/_data_model/validation/dms/_consistency.py,sha256=K0mfAwwpW6zQXGDEldrA7Lgqk-giV_Y-gUKyb8Dw47k,2445
81
81
  cognite/neat/_data_model/validation/dms/_containers.py,sha256=Jqwu2JH36eyqA7xt6_UaLEl-zhZi4itI1dmb73GpJMY,7431
82
82
  cognite/neat/_data_model/validation/dms/_limits.py,sha256=LVxF1qmeEdUVVAPmywCgxWJ9BR7Ai6nI4X1ylJ7pv8I,15982
83
- cognite/neat/_data_model/validation/dms/_orchestrator.py,sha256=4_KHtfJcuXSSIP7sazhBzM4_nSEz0d77CrsX5rmShOc,10337
83
+ cognite/neat/_data_model/validation/dms/_orchestrator.py,sha256=1cwGrtsSs6Q1vT-8vbZ2DM_3WYu9YTSRDbDGJhkTCBs,10672
84
84
  cognite/neat/_data_model/validation/dms/_views.py,sha256=3bHEEbFKTR_QH_tiJYHppQsZ9ruApv-kdyfehEjIlCU,4198
85
85
  cognite/neat/_session/__init__.py,sha256=owqW5Mml2DSZx1AvPvwNRTBngfhBNrQ6EH-7CKL7Jp0,61
86
86
  cognite/neat/_session/_issues.py,sha256=E8UQeSJURg2dm4MF1pfD9dp-heSRT7pgQZgKlD1-FGs,2723
@@ -190,7 +190,7 @@ cognite/neat/v0/core/_data_model/models/mapping/__init__.py,sha256=T68Hf7rhiXa7b
190
190
  cognite/neat/v0/core/_data_model/models/mapping/_classic2core.py,sha256=F0zusTh9pPR4z-RExPw3o4EMBSU2si6FJLuej2a3JzM,1430
191
191
  cognite/neat/v0/core/_data_model/models/mapping/_classic2core.yaml,sha256=ei-nuivNWVW9HmvzDBKIPF6ZdgaMq64XHw_rKm0CMxg,22584
192
192
  cognite/neat/v0/core/_data_model/models/physical/__init__.py,sha256=pH5ZF8jiW0A2w7VCSoHUsXxe894QFvTtgjxXNGVVaxk,990
193
- cognite/neat/v0/core/_data_model/models/physical/_exporter.py,sha256=ega1Yyosq-D9XYCypx9wtuMTFwS0EMRo_e3JwW434XU,30335
193
+ cognite/neat/v0/core/_data_model/models/physical/_exporter.py,sha256=slECWjHinQscoR86dDmvB4OalyCvE9D_bSKp4v_GINg,30555
194
194
  cognite/neat/v0/core/_data_model/models/physical/_unverified.py,sha256=eE2D2DPfqEfOnYaUmOTPhBpHp9oN-gxjn8CTe_GKtz0,22419
195
195
  cognite/neat/v0/core/_data_model/models/physical/_validation.py,sha256=g0uSZImYaGmMZEwHUpb8KF9LTPiztz7k7cFQT3jrs4Y,41326
196
196
  cognite/neat/v0/core/_data_model/models/physical/_verified.py,sha256=49dn3K_gWO3VgfS-SK4kHpbSJywtj4mwES3iy-43z4M,29824
@@ -312,7 +312,7 @@ cognite/neat/v0/session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4
312
312
  cognite/neat/v0/session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
313
313
  cognite/neat/v0/session/engine/_interface.py,sha256=3W-cYr493c_mW3P5O6MKN1xEQg3cA7NHR_ev3zdF9Vk,533
314
314
  cognite/neat/v0/session/engine/_load.py,sha256=u0x7vuQCRoNcPt25KJBJRn8sJabonYK4vtSZpiTdP4k,5201
315
- cognite_neat-0.127.26.dist-info/METADATA,sha256=W1EY7XRW7rN7oqdEfS1UMJW1drv2KQyTFuDIH8V1Yh4,9150
316
- cognite_neat-0.127.26.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
317
- cognite_neat-0.127.26.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
318
- cognite_neat-0.127.26.dist-info/RECORD,,
315
+ cognite_neat-0.127.28.dist-info/METADATA,sha256=RnR8WLYTAdMA9I6LEPpn0cpFXgecvAokGDrbhFrpkXc,9150
316
+ cognite_neat-0.127.28.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
317
+ cognite_neat-0.127.28.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
318
+ cognite_neat-0.127.28.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any