cognite-neat 0.97.3__py3-none-any.whl → 0.99.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 (109) hide show
  1. cognite/neat/_client/__init__.py +4 -0
  2. cognite/neat/_client/_api/data_modeling_loaders.py +512 -0
  3. cognite/neat/_client/_api/schema.py +50 -0
  4. cognite/neat/_client/_api_client.py +17 -0
  5. cognite/neat/_client/data_classes/__init__.py +0 -0
  6. cognite/neat/{_utils/cdf/data_classes.py → _client/data_classes/data_modeling.py} +8 -135
  7. cognite/neat/{_rules/models/dms/_schema.py → _client/data_classes/schema.py} +32 -281
  8. cognite/neat/_graph/_shared.py +14 -15
  9. cognite/neat/_graph/extractors/_classic_cdf/_assets.py +14 -154
  10. cognite/neat/_graph/extractors/_classic_cdf/_base.py +154 -7
  11. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +23 -12
  12. cognite/neat/_graph/extractors/_classic_cdf/_data_sets.py +17 -92
  13. cognite/neat/_graph/extractors/_classic_cdf/_events.py +13 -162
  14. cognite/neat/_graph/extractors/_classic_cdf/_files.py +15 -179
  15. cognite/neat/_graph/extractors/_classic_cdf/_labels.py +32 -100
  16. cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +27 -178
  17. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +14 -139
  18. cognite/neat/_graph/extractors/_classic_cdf/_timeseries.py +15 -173
  19. cognite/neat/_graph/extractors/_rdf_file.py +6 -7
  20. cognite/neat/_graph/loaders/__init__.py +1 -2
  21. cognite/neat/_graph/queries/_base.py +17 -1
  22. cognite/neat/_graph/transformers/_classic_cdf.py +50 -134
  23. cognite/neat/_graph/transformers/_prune_graph.py +1 -1
  24. cognite/neat/_graph/transformers/_rdfpath.py +1 -1
  25. cognite/neat/_issues/warnings/__init__.py +6 -0
  26. cognite/neat/_issues/warnings/_external.py +8 -0
  27. cognite/neat/_issues/warnings/_models.py +9 -0
  28. cognite/neat/_issues/warnings/_properties.py +16 -0
  29. cognite/neat/_rules/_constants.py +7 -6
  30. cognite/neat/_rules/_shared.py +3 -8
  31. cognite/neat/_rules/analysis/__init__.py +1 -2
  32. cognite/neat/_rules/analysis/_base.py +10 -27
  33. cognite/neat/_rules/analysis/_dms.py +4 -10
  34. cognite/neat/_rules/analysis/_information.py +2 -10
  35. cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
  36. cognite/neat/_rules/exporters/_base.py +3 -4
  37. cognite/neat/_rules/exporters/_rules2dms.py +29 -40
  38. cognite/neat/_rules/exporters/_rules2excel.py +15 -72
  39. cognite/neat/_rules/exporters/_rules2ontology.py +4 -4
  40. cognite/neat/_rules/importers/_base.py +3 -4
  41. cognite/neat/_rules/importers/_dms2rules.py +21 -45
  42. cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py +1 -7
  43. cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +7 -10
  44. cognite/neat/_rules/importers/_rdf/_base.py +17 -29
  45. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +2 -2
  46. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +5 -10
  47. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +1 -2
  48. cognite/neat/_rules/importers/_rdf/_inference2rules.py +55 -51
  49. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +2 -2
  50. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +5 -8
  51. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -2
  52. cognite/neat/_rules/importers/_rdf/_shared.py +25 -140
  53. cognite/neat/_rules/importers/_spreadsheet2rules.py +10 -41
  54. cognite/neat/_rules/models/__init__.py +3 -17
  55. cognite/neat/_rules/models/_base_rules.py +118 -62
  56. cognite/neat/_rules/models/dms/__init__.py +2 -2
  57. cognite/neat/_rules/models/dms/_exporter.py +20 -178
  58. cognite/neat/_rules/models/dms/_rules.py +65 -128
  59. cognite/neat/_rules/models/dms/_rules_input.py +72 -56
  60. cognite/neat/_rules/models/dms/_validation.py +16 -109
  61. cognite/neat/_rules/models/entities/_single_value.py +32 -4
  62. cognite/neat/_rules/models/information/_rules.py +19 -122
  63. cognite/neat/_rules/models/information/_rules_input.py +32 -41
  64. cognite/neat/_rules/models/information/_validation.py +34 -102
  65. cognite/neat/_rules/models/mapping/__init__.py +2 -3
  66. cognite/neat/_rules/models/mapping/_classic2core.py +36 -146
  67. cognite/neat/_rules/models/mapping/_classic2core.yaml +339 -0
  68. cognite/neat/_rules/transformers/__init__.py +3 -6
  69. cognite/neat/_rules/transformers/_converters.py +128 -206
  70. cognite/neat/_rules/transformers/_mapping.py +105 -34
  71. cognite/neat/_rules/transformers/_verification.py +5 -16
  72. cognite/neat/_session/_base.py +83 -21
  73. cognite/neat/_session/_collector.py +126 -0
  74. cognite/neat/_session/_drop.py +35 -0
  75. cognite/neat/_session/_inspect.py +22 -10
  76. cognite/neat/_session/_mapping.py +39 -0
  77. cognite/neat/_session/_prepare.py +222 -27
  78. cognite/neat/_session/_read.py +109 -19
  79. cognite/neat/_session/_set.py +2 -2
  80. cognite/neat/_session/_show.py +11 -11
  81. cognite/neat/_session/_to.py +27 -14
  82. cognite/neat/_session/exceptions.py +20 -3
  83. cognite/neat/_store/_base.py +27 -24
  84. cognite/neat/_store/_provenance.py +2 -2
  85. cognite/neat/_utils/auxiliary.py +19 -0
  86. cognite/neat/_utils/rdf_.py +28 -1
  87. cognite/neat/_version.py +1 -1
  88. cognite/neat/_workflows/steps/data_contracts.py +2 -10
  89. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +14 -49
  90. cognite/neat/_workflows/steps/lib/current/rules_importer.py +4 -1
  91. cognite/neat/_workflows/steps/lib/current/rules_validator.py +5 -9
  92. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/METADATA +4 -3
  93. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/RECORD +97 -100
  94. cognite/neat/_graph/loaders/_rdf2asset.py +0 -416
  95. cognite/neat/_rules/analysis/_asset.py +0 -173
  96. cognite/neat/_rules/models/asset/__init__.py +0 -13
  97. cognite/neat/_rules/models/asset/_rules.py +0 -109
  98. cognite/neat/_rules/models/asset/_rules_input.py +0 -101
  99. cognite/neat/_rules/models/asset/_validation.py +0 -45
  100. cognite/neat/_rules/models/domain.py +0 -136
  101. cognite/neat/_rules/models/mapping/_base.py +0 -131
  102. cognite/neat/_utils/cdf/loaders/__init__.py +0 -25
  103. cognite/neat/_utils/cdf/loaders/_base.py +0 -54
  104. cognite/neat/_utils/cdf/loaders/_data_modeling.py +0 -339
  105. cognite/neat/_utils/cdf/loaders/_ingestion.py +0 -167
  106. /cognite/neat/{_utils/cdf → _client/_api}/__init__.py +0 -0
  107. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/LICENSE +0 -0
  108. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/WHEEL +0 -0
  109. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/entry_points.txt +0 -0
@@ -1,416 +0,0 @@
1
- import json
2
- from collections.abc import Iterable, Sequence
3
- from pathlib import Path
4
- from typing import Any, cast
5
-
6
- import yaml
7
- from cognite.client import CogniteClient
8
- from cognite.client.data_classes import (
9
- AssetWrite,
10
- LabelDefinitionWrite,
11
- RelationshipWrite,
12
- )
13
- from cognite.client.data_classes.capabilities import (
14
- AssetsAcl,
15
- Capability,
16
- RelationshipsAcl,
17
- )
18
- from cognite.client.exceptions import CogniteAPIError, CogniteDuplicatedError
19
-
20
- from cognite.neat._graph._tracking.base import Tracker
21
- from cognite.neat._graph._tracking.log import LogTracker
22
- from cognite.neat._issues import IssueList, NeatError, NeatIssue
23
- from cognite.neat._issues.errors import ResourceCreationError, ResourceNotFoundError
24
- from cognite.neat._rules._constants import EntityTypes
25
- from cognite.neat._rules.analysis._asset import AssetAnalysis
26
- from cognite.neat._rules.models import AssetRules
27
- from cognite.neat._rules.models.entities import ClassEntity
28
- from cognite.neat._store import NeatGraphStore
29
- from cognite.neat._utils.auxiliary import create_sha256_hash
30
- from cognite.neat._utils.upload import UploadResult
31
-
32
- from ._base import _END_OF_CLASS, CDFLoader
33
-
34
-
35
- class AssetLoader(CDFLoader[AssetWrite]):
36
- """Load Assets and their relationships from NeatGraph to Cognite Data Fusions.
37
-
38
- Args:
39
- graph_store (NeatGraphStore): The graph store to load the data into.
40
- rules (AssetRules): The rules to load the assets with.
41
- data_set_id (int): The CDF data set id to load the Assets into.
42
- use_orphanage (bool): Whether to use an orphanage for assets that are not part
43
- of the hierarchy. Defaults to False.
44
- use_labels (bool): Whether to use labels for assets. Defaults to False.
45
- external_id_prefix (str | None): The prefix to use for the external ids. Defaults to None.
46
- create_issues (Sequence[NeatIssue] | None): A list of issues that occurred during reading. Defaults to None.
47
- tracker (type[Tracker] | None): The tracker to use. Defaults to None.
48
- """
49
-
50
- def __init__(
51
- self,
52
- graph_store: NeatGraphStore,
53
- rules: AssetRules,
54
- data_set_id: int,
55
- use_orphanage: bool = False,
56
- use_labels: bool = False,
57
- external_id_prefix: str | None = None,
58
- create_issues: Sequence[NeatIssue] | None = None,
59
- tracker: type[Tracker] | None = None,
60
- ):
61
- super().__init__(graph_store)
62
-
63
- self.rules = rules
64
- self.data_set_id = data_set_id
65
-
66
- self.use_labels = use_labels
67
-
68
- self.orphanage = (
69
- AssetWrite.load(
70
- {
71
- "dataSetId": self.data_set_id,
72
- "externalId": (f"{external_id_prefix or ''}orphanage-{data_set_id}" if use_orphanage else None),
73
- "name": "Orphanage",
74
- "description": "Orphanage for assets whose parents do not exist",
75
- }
76
- )
77
- if use_orphanage
78
- else None
79
- )
80
-
81
- self.external_id_prefix = external_id_prefix
82
-
83
- self.processed_assets: set[str] = set()
84
- self._issues = IssueList(create_issues or [])
85
- self._tracker: type[Tracker] = tracker or LogTracker
86
-
87
- def _load(self, stop_on_exception: bool = False) -> Iterable[AssetWrite | NeatIssue | type[_END_OF_CLASS]]:
88
- if self._issues.has_errors and stop_on_exception:
89
- raise self._issues.as_exception()
90
- elif self._issues.has_errors:
91
- yield from self._issues
92
- return
93
- if not self.rules:
94
- # There should already be an error in this case.
95
- return
96
-
97
- ordered_classes = AssetAnalysis(self.rules).class_topological_sort()
98
-
99
- tracker = self._tracker(
100
- type(self).__name__,
101
- [repr(class_.id) for class_ in ordered_classes],
102
- "classes",
103
- )
104
-
105
- if self.use_labels:
106
- yield from self._create_labels()
107
-
108
- if self.orphanage:
109
- yield self.orphanage
110
- self.processed_assets.add(cast(str, self.orphanage.external_id))
111
-
112
- yield from self._create_assets(ordered_classes, tracker, stop_on_exception)
113
- yield from self._create_relationship(ordered_classes, tracker, stop_on_exception)
114
-
115
- def _create_labels(self) -> Iterable[Any]:
116
- for label in AssetAnalysis(self.rules).define_labels():
117
- yield LabelDefinitionWrite(name=label, external_id=label, data_set_id=self.data_set_id)
118
- yield _END_OF_CLASS
119
-
120
- def _create_assets(
121
- self,
122
- ordered_classes: list[ClassEntity],
123
- tracker: Tracker,
124
- stop_on_exception: bool,
125
- ) -> Iterable[Any]:
126
- error: NeatError
127
- for class_ in ordered_classes:
128
- tracker.start(repr(class_.id))
129
-
130
- property_renaming_config = AssetAnalysis(self.rules).define_asset_property_renaming_config(class_)
131
-
132
- for identifier, properties in self.graph_store.read(class_.suffix):
133
- identifier = f"{self.external_id_prefix or ''}{identifier}"
134
-
135
- fields = _process_asset_properties(properties, property_renaming_config)
136
- # set data set id and external id
137
- fields["dataSetId"] = self.data_set_id
138
- fields["externalId"] = identifier
139
-
140
- if self.use_labels:
141
- fields["labels"] = [class_.suffix]
142
-
143
- if parent_external_id := fields.get("parentExternalId", None):
144
- fields["parentExternalId"] = f"{self.external_id_prefix or ''}{parent_external_id}"
145
-
146
- # check on parent
147
- if "parentExternalId" in fields and fields["parentExternalId"] not in self.processed_assets:
148
- error = ResourceNotFoundError(
149
- fields["parentExternalId"],
150
- EntityTypes.asset,
151
- identifier,
152
- EntityTypes.asset,
153
- f"Moving the asset {identifier} under orphanage {self.orphanage.external_id}"
154
- if self.orphanage
155
- else "",
156
- )
157
- tracker.issue(error)
158
- if stop_on_exception:
159
- raise error
160
- yield error
161
-
162
- # if orphanage is set asset will use orphanage as parent
163
- if self.orphanage:
164
- fields["parentExternalId"] = self.orphanage.external_id
165
-
166
- # otherwise asset will be skipped
167
- else:
168
- continue
169
-
170
- try:
171
- yield AssetWrite.load(fields)
172
- self.processed_assets.add(identifier)
173
- except KeyError as e:
174
- error = ResourceCreationError(identifier, EntityTypes.asset, error=str(e))
175
- tracker.issue(error)
176
- if stop_on_exception:
177
- raise error from e
178
- yield error
179
-
180
- yield _END_OF_CLASS
181
-
182
- def _create_relationship(
183
- self,
184
- ordered_classes: list[ClassEntity],
185
- tracker: Tracker,
186
- stop_on_exception: bool,
187
- ) -> Iterable[Any]:
188
- for class_ in ordered_classes:
189
- tracker.start(repr(class_.id))
190
-
191
- property_renaming_config = AssetAnalysis(self.rules).define_relationship_property_renaming_config(class_)
192
-
193
- # class does not have any relationship properties
194
- if not property_renaming_config:
195
- continue
196
-
197
- for source_external_id, properties in self.graph_store.read(class_.suffix):
198
- relationships = _process_relationship_properties(properties, property_renaming_config)
199
-
200
- source_external_id = f"{self.external_id_prefix or ''}{source_external_id}"
201
-
202
- # check if source asset exists
203
- if source_external_id not in self.processed_assets:
204
- error = ResourceCreationError(
205
- resource_type=EntityTypes.relationship,
206
- identifier=source_external_id,
207
- error=(
208
- f"Asset {source_external_id} does not exist! "
209
- "Aborting creation of relationships which use this asset as the source."
210
- ),
211
- )
212
- tracker.issue(error)
213
- if stop_on_exception:
214
- raise error
215
- yield error
216
- continue
217
-
218
- for label, target_external_ids in relationships.items():
219
- # we can have 1-many relationships
220
- for target_external_id in target_external_ids:
221
- target_external_id = f"{self.external_id_prefix or ''}{target_external_id}"
222
- # check if source asset exists
223
- if target_external_id not in self.processed_assets:
224
- error = ResourceCreationError(
225
- resource_type=EntityTypes.relationship,
226
- identifier=target_external_id,
227
- error=(
228
- f"Asset {target_external_id} does not exist! "
229
- f"Cannot create relationship between {source_external_id}"
230
- f" and {target_external_id}. "
231
- ),
232
- )
233
- tracker.issue(error)
234
- if stop_on_exception:
235
- raise error
236
- yield error
237
- continue
238
-
239
- external_id = "relationship_" + create_sha256_hash(f"{source_external_id}_{target_external_id}")
240
- try:
241
- yield RelationshipWrite(
242
- external_id=external_id,
243
- source_external_id=source_external_id,
244
- target_external_id=target_external_id,
245
- source_type="asset",
246
- target_type="asset",
247
- data_set_id=self.data_set_id,
248
- labels=[label] if self.use_labels else None,
249
- )
250
- except KeyError as e:
251
- error = ResourceCreationError(
252
- resource_type=EntityTypes.relationship,
253
- identifier=external_id,
254
- error=str(e),
255
- )
256
- tracker.issue(error)
257
- if stop_on_exception:
258
- raise error from e
259
- yield error
260
-
261
- yield _END_OF_CLASS
262
-
263
- def _get_required_capabilities(self) -> list[Capability]:
264
- return [
265
- AssetsAcl(
266
- actions=[
267
- AssetsAcl.Action.Write,
268
- AssetsAcl.Action.Read,
269
- ],
270
- scope=AssetsAcl.Scope.DataSet([self.data_set_id]),
271
- ),
272
- RelationshipsAcl(
273
- actions=[
274
- RelationshipsAcl.Action.Write,
275
- RelationshipsAcl.Action.Read,
276
- ],
277
- scope=RelationshipsAcl.Scope.DataSet([self.data_set_id]),
278
- ),
279
- ]
280
-
281
- def _upload_to_cdf(
282
- self,
283
- client: CogniteClient,
284
- items: list[AssetWrite] | list[RelationshipWrite] | list[LabelDefinitionWrite],
285
- dry_run: bool,
286
- read_issues: IssueList,
287
- ) -> Iterable[UploadResult]:
288
- if isinstance(items[0], AssetWrite) and all(isinstance(item, AssetWrite) for item in items):
289
- yield from self._upload_assets_to_cdf(client, cast(list[AssetWrite], items), dry_run, read_issues)
290
- elif isinstance(items[0], RelationshipWrite) and all(isinstance(item, RelationshipWrite) for item in items):
291
- yield from self._upload_relationships_to_cdf(
292
- client, cast(list[RelationshipWrite], items), dry_run, read_issues
293
- )
294
- elif isinstance(items[0], LabelDefinitionWrite) and all(
295
- isinstance(item, LabelDefinitionWrite) for item in items
296
- ):
297
- yield from self._upload_labels_to_cdf(client, cast(list[LabelDefinitionWrite], items), dry_run, read_issues)
298
- else:
299
- raise ValueError(f"Item {items[0]} is not supported. This is a bug in neat please report it.")
300
-
301
- def _upload_labels_to_cdf(
302
- self,
303
- client: CogniteClient,
304
- items: list[LabelDefinitionWrite],
305
- dry_run: bool,
306
- read_issues: IssueList,
307
- ) -> Iterable[UploadResult]:
308
- try:
309
- created = client.labels.create(items)
310
- except (CogniteAPIError, CogniteDuplicatedError) as e:
311
- result = UploadResult[str](name="Label", issues=read_issues)
312
- result.error_messages.append(str(e))
313
- result.failed_created.update(item.external_id for item in e.failed + e.unknown)
314
- result.created.update(item.external_id for item in e.successful)
315
- yield result
316
- else:
317
- for label in created:
318
- result = UploadResult[str](name="Label", issues=read_issues)
319
- result.upserted.add(cast(str, label.external_id))
320
- yield result
321
-
322
- def _upload_assets_to_cdf(
323
- self,
324
- client: CogniteClient,
325
- items: list[AssetWrite],
326
- dry_run: bool,
327
- read_issues: IssueList,
328
- ) -> Iterable[UploadResult]:
329
- try:
330
- upserted = client.assets.upsert(items, mode="replace")
331
- except CogniteAPIError as e:
332
- result = UploadResult[str](name="Asset", issues=read_issues)
333
- result.error_messages.append(str(e))
334
- result.failed_upserted.update(item.external_id for item in e.failed + e.unknown)
335
- result.upserted.update(item.external_id for item in e.successful)
336
- yield result
337
- else:
338
- for asset in upserted:
339
- result = UploadResult[str](name="Asset", issues=read_issues)
340
- result.upserted.add(cast(str, asset.external_id))
341
- yield result
342
-
343
- def _upload_relationships_to_cdf(
344
- self,
345
- client: CogniteClient,
346
- items: list[RelationshipWrite],
347
- dry_run: bool,
348
- read_issues: IssueList,
349
- ) -> Iterable[UploadResult]:
350
- try:
351
- upserted = client.relationships.upsert(items, mode="replace")
352
- except CogniteAPIError as e:
353
- result = UploadResult[str](name="Relationship", issues=read_issues)
354
- result.error_messages.append(str(e))
355
- result.failed_upserted.update(item.external_id for item in e.failed + e.unknown)
356
- result.upserted.update(item.external_id for item in e.successful)
357
- yield result
358
- else:
359
- for relationship in upserted:
360
- result = UploadResult[str](name="relationship", issues=read_issues)
361
- result.upserted.add(cast(str, relationship.external_id))
362
- yield result
363
-
364
- def write_to_file(self, filepath: Path) -> None:
365
- if filepath.suffix not in [".json", ".yaml", ".yml"]:
366
- raise ValueError(f"File format {filepath.suffix} is not supported")
367
- dumped: dict[str, list] = {"assets": [], "relationship": []}
368
- for item in self.load(stop_on_exception=False):
369
- key = {
370
- AssetWrite: "assets",
371
- RelationshipWrite: "relationship",
372
- NeatIssue: "issues",
373
- _END_OF_CLASS: "end_of_class",
374
- }.get(type(item))
375
- if key is None:
376
- # This should never happen, and is a bug in neat
377
- raise ValueError(f"Item {item} is not supported. This is a bug in neat please report it.")
378
- if key == "end_of_class":
379
- continue
380
- dumped[key].append(item.dump())
381
- with filepath.open("w", encoding=self._encoding, newline=self._new_line) as f:
382
- if filepath.suffix == ".json":
383
- json.dump(dumped, f, indent=2)
384
- else:
385
- yaml.safe_dump(dumped, f, sort_keys=False)
386
-
387
-
388
- def _process_asset_properties(properties: dict[str, list[str]], property_renaming_config: dict[str, str]) -> dict:
389
- metadata: dict[str, str] = {}
390
- fields: dict[str, str | dict] = {}
391
-
392
- for original_property, values in properties.items():
393
- if renamed_property := property_renaming_config.get(original_property, None):
394
- if renamed_property.startswith("metadata."):
395
- # Asset metadata contains only string values
396
- metadata[original_property] = ", ".join(values)
397
- else:
398
- # Asset fields can contain only one value
399
- fields[renamed_property] = values[0]
400
-
401
- if metadata:
402
- fields["metadata"] = metadata
403
-
404
- return fields
405
-
406
-
407
- def _process_relationship_properties(
408
- properties: dict[str, list[str]], property_renaming_config: dict[str, str]
409
- ) -> dict:
410
- relationships: dict[str, list[str]] = {}
411
-
412
- for original_property, values in properties.items():
413
- if renamed_property := property_renaming_config.get(original_property, None):
414
- relationships[renamed_property] = values
415
-
416
- return relationships
@@ -1,173 +0,0 @@
1
- import warnings
2
- from graphlib import TopologicalSorter
3
- from typing import cast
4
-
5
- from cognite.neat._rules._constants import EntityTypes
6
- from cognite.neat._rules.models import AssetRules
7
- from cognite.neat._rules.models._rdfpath import RDFPath
8
- from cognite.neat._rules.models.asset import AssetClass, AssetProperty
9
- from cognite.neat._rules.models.entities import (
10
- AssetEntity,
11
- AssetFields,
12
- ClassEntity,
13
- ReferenceEntity,
14
- RelationshipEntity,
15
- )
16
-
17
- from ._base import BaseAnalysis
18
-
19
-
20
- class AssetAnalysis(BaseAnalysis[AssetRules, AssetClass, AssetProperty, ClassEntity, str]):
21
- """Assumes analysis over only the complete schema"""
22
-
23
- def _get_reference(self, class_or_property: AssetClass | AssetProperty) -> ReferenceEntity | None:
24
- return class_or_property.reference if isinstance(class_or_property.reference, ReferenceEntity) else None
25
-
26
- def _get_cls_entity(self, class_: AssetClass | AssetProperty) -> ClassEntity:
27
- return class_.class_
28
-
29
- def _get_prop_entity(self, property_: AssetProperty) -> str:
30
- return property_.property_
31
-
32
- def _get_cls_parents(self, class_: AssetClass) -> list[ClassEntity] | None:
33
- return list(class_.parent or []) or None
34
-
35
- def _get_reference_rules(self) -> AssetRules | None:
36
- return self.rules.reference
37
-
38
- @classmethod
39
- def _set_cls_entity(cls, property_: AssetProperty, class_: ClassEntity) -> None:
40
- property_.class_ = class_
41
-
42
- def _get_object(self, property_: AssetProperty) -> ClassEntity | None:
43
- return property_.value_type if isinstance(property_.value_type, ClassEntity) else None
44
-
45
- def _get_max_occurrence(self, property_: AssetProperty) -> int | float | None:
46
- return property_.max_count
47
-
48
- def _get_classes(self) -> list[AssetClass]:
49
- return list(self.rules.classes)
50
-
51
- def _get_properties(self) -> list[AssetProperty]:
52
- return list(self.rules.properties)
53
-
54
- def subset_rules(self, desired_classes: set[ClassEntity]) -> AssetRules:
55
- raise NotImplementedError("Method not implemented")
56
-
57
- def class_property_pairs(
58
- self,
59
- only_rdfpath: bool = False,
60
- consider_inheritance: bool = False,
61
- implementation_type: EntityTypes = EntityTypes.asset,
62
- ) -> dict[ClassEntity, dict[str, AssetProperty]]:
63
- class_property_pairs = {}
64
-
65
- T_implementation = AssetEntity if implementation_type == EntityTypes.asset else RelationshipEntity
66
-
67
- for class_, properties in self.classes_with_properties(consider_inheritance).items():
68
- processed_properties = {}
69
- for property_ in properties:
70
- if property_.property_ in processed_properties:
71
- # TODO: use appropriate Warning class from _exceptions.py
72
- # if missing make one !
73
- warnings.warn(
74
- f"Property {property_.property_} for {class_} has been defined more than once!"
75
- " Only the first definition will be considered, skipping the rest..",
76
- stacklevel=2,
77
- )
78
- continue
79
-
80
- if (
81
- property_.implementation
82
- and any(isinstance(implementation, T_implementation) for implementation in property_.implementation)
83
- and (not only_rdfpath or (only_rdfpath and isinstance(property_.transformation, RDFPath)))
84
- ):
85
- implementation = [
86
- implementation
87
- for implementation in property_.implementation
88
- if isinstance(implementation, T_implementation)
89
- ]
90
-
91
- processed_properties[property_.property_] = property_.model_copy(
92
- deep=True, update={"implementation": implementation}
93
- )
94
-
95
- if processed_properties:
96
- class_property_pairs[class_] = processed_properties
97
-
98
- return class_property_pairs
99
-
100
- def class_topological_sort(self) -> list[ClassEntity]:
101
- child_parent_asset: dict[ClassEntity, set[ClassEntity]] = {}
102
- for class_, properties in self.asset_definition().items():
103
- child_parent_asset[class_] = set()
104
- for property_ in properties.values():
105
- if any(
106
- cast(AssetEntity, implementation).property_ == AssetFields.parentExternalId
107
- for implementation in property_.implementation
108
- ):
109
- child_parent_asset[property_.class_].add(cast(ClassEntity, property_.value_type))
110
-
111
- return list(TopologicalSorter(child_parent_asset).static_order())
112
-
113
- def asset_definition(
114
- self, only_rdfpath: bool = False, consider_inheritance: bool = False
115
- ) -> dict[ClassEntity, dict[str, AssetProperty]]:
116
- return self.class_property_pairs(
117
- consider_inheritance=consider_inheritance,
118
- only_rdfpath=only_rdfpath,
119
- implementation_type=EntityTypes.asset,
120
- )
121
-
122
- def relationship_definition(
123
- self, only_rdfpath: bool = False, consider_inheritance: bool = False
124
- ) -> dict[ClassEntity, dict[str, AssetProperty]]:
125
- return self.class_property_pairs(
126
- consider_inheritance=consider_inheritance,
127
- only_rdfpath=only_rdfpath,
128
- implementation_type=EntityTypes.relationship,
129
- )
130
-
131
- def define_asset_property_renaming_config(self, class_: ClassEntity) -> dict[str, str]:
132
- property_renaming_configuration = {}
133
-
134
- if asset_definition := self.asset_definition().get(class_, None):
135
- for property_, transformation in asset_definition.items():
136
- asset_property = cast(list[AssetEntity], transformation.implementation)[0].property_
137
-
138
- if asset_property != "metadata":
139
- property_renaming_configuration[property_] = str(asset_property)
140
- else:
141
- property_renaming_configuration[property_] = f"{asset_property}.{property_}"
142
-
143
- return property_renaming_configuration
144
-
145
- def define_relationship_property_renaming_config(self, class_: ClassEntity) -> dict[str, str]:
146
- property_renaming_configuration = {}
147
-
148
- if relationship_definition := self.relationship_definition().get(class_, None):
149
- for property_, transformation in relationship_definition.items():
150
- relationship = cast(list[RelationshipEntity], transformation.implementation)[0]
151
-
152
- if relationship.label:
153
- property_renaming_configuration[property_] = relationship.label
154
- else:
155
- property_renaming_configuration[property_] = property_
156
-
157
- return property_renaming_configuration
158
-
159
- def define_labels(self) -> set:
160
- labels = set()
161
-
162
- for _, properties in AssetAnalysis(self.rules).relationship_definition().items():
163
- for property_, definition in properties.items():
164
- labels.add(
165
- cast(RelationshipEntity, definition.implementation[0]).label
166
- if cast(RelationshipEntity, definition.implementation[0]).label
167
- else property_
168
- )
169
-
170
- for class_ in AssetAnalysis(self.rules).asset_definition().keys():
171
- labels.add(class_.suffix)
172
-
173
- return labels
@@ -1,13 +0,0 @@
1
- from ._rules import AssetClass, AssetMetadata, AssetProperty, AssetRules
2
- from ._rules_input import AssetInputClass, AssetInputMetadata, AssetInputProperty, AssetInputRules
3
-
4
- __all__ = [
5
- "AssetRules",
6
- "AssetMetadata",
7
- "AssetClass",
8
- "AssetProperty",
9
- "AssetInputRules",
10
- "AssetInputMetadata",
11
- "AssetInputClass",
12
- "AssetInputProperty",
13
- ]