cognite-neat 0.98.0__py3-none-any.whl → 0.99.1__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 (103) hide show
  1. cognite/neat/_client/__init__.py +4 -0
  2. cognite/neat/_client/_api/data_modeling_loaders.py +585 -0
  3. cognite/neat/_client/_api/schema.py +111 -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/_client/data_classes/schema.py +495 -0
  8. cognite/neat/_constants.py +27 -4
  9. cognite/neat/_graph/_shared.py +14 -15
  10. cognite/neat/_graph/extractors/_classic_cdf/_assets.py +14 -154
  11. cognite/neat/_graph/extractors/_classic_cdf/_base.py +154 -7
  12. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +25 -14
  13. cognite/neat/_graph/extractors/_classic_cdf/_data_sets.py +17 -92
  14. cognite/neat/_graph/extractors/_classic_cdf/_events.py +13 -162
  15. cognite/neat/_graph/extractors/_classic_cdf/_files.py +15 -179
  16. cognite/neat/_graph/extractors/_classic_cdf/_labels.py +32 -100
  17. cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +27 -178
  18. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +14 -139
  19. cognite/neat/_graph/extractors/_classic_cdf/_timeseries.py +15 -173
  20. cognite/neat/_graph/extractors/_rdf_file.py +6 -7
  21. cognite/neat/_graph/loaders/_rdf2dms.py +2 -2
  22. cognite/neat/_graph/queries/_base.py +17 -1
  23. cognite/neat/_graph/transformers/_classic_cdf.py +74 -147
  24. cognite/neat/_graph/transformers/_prune_graph.py +1 -1
  25. cognite/neat/_graph/transformers/_rdfpath.py +1 -1
  26. cognite/neat/_issues/_base.py +26 -17
  27. cognite/neat/_issues/errors/__init__.py +4 -2
  28. cognite/neat/_issues/errors/_external.py +7 -0
  29. cognite/neat/_issues/errors/_properties.py +2 -7
  30. cognite/neat/_issues/errors/_resources.py +1 -1
  31. cognite/neat/_issues/warnings/__init__.py +8 -0
  32. cognite/neat/_issues/warnings/_external.py +16 -0
  33. cognite/neat/_issues/warnings/_properties.py +16 -0
  34. cognite/neat/_issues/warnings/_resources.py +26 -2
  35. cognite/neat/_issues/warnings/user_modeling.py +4 -4
  36. cognite/neat/_rules/_constants.py +8 -11
  37. cognite/neat/_rules/analysis/_base.py +8 -4
  38. cognite/neat/_rules/exporters/_base.py +3 -4
  39. cognite/neat/_rules/exporters/_rules2dms.py +33 -46
  40. cognite/neat/_rules/importers/__init__.py +1 -3
  41. cognite/neat/_rules/importers/_base.py +1 -1
  42. cognite/neat/_rules/importers/_dms2rules.py +6 -29
  43. cognite/neat/_rules/importers/_rdf/__init__.py +5 -0
  44. cognite/neat/_rules/importers/_rdf/_base.py +34 -11
  45. cognite/neat/_rules/importers/_rdf/_imf2rules.py +91 -0
  46. cognite/neat/_rules/importers/_rdf/_inference2rules.py +43 -35
  47. cognite/neat/_rules/importers/_rdf/_owl2rules.py +80 -0
  48. cognite/neat/_rules/importers/_rdf/_shared.py +138 -441
  49. cognite/neat/_rules/models/__init__.py +1 -1
  50. cognite/neat/_rules/models/_base_rules.py +22 -12
  51. cognite/neat/_rules/models/dms/__init__.py +4 -2
  52. cognite/neat/_rules/models/dms/_exporter.py +45 -48
  53. cognite/neat/_rules/models/dms/_rules.py +20 -17
  54. cognite/neat/_rules/models/dms/_rules_input.py +52 -8
  55. cognite/neat/_rules/models/dms/_validation.py +391 -119
  56. cognite/neat/_rules/models/entities/_single_value.py +32 -4
  57. cognite/neat/_rules/models/information/__init__.py +2 -0
  58. cognite/neat/_rules/models/information/_rules.py +0 -67
  59. cognite/neat/_rules/models/information/_validation.py +9 -9
  60. cognite/neat/_rules/models/mapping/__init__.py +2 -3
  61. cognite/neat/_rules/models/mapping/_classic2core.py +36 -146
  62. cognite/neat/_rules/models/mapping/_classic2core.yaml +343 -0
  63. cognite/neat/_rules/transformers/__init__.py +2 -2
  64. cognite/neat/_rules/transformers/_converters.py +110 -11
  65. cognite/neat/_rules/transformers/_mapping.py +105 -30
  66. cognite/neat/_rules/transformers/_pipelines.py +1 -1
  67. cognite/neat/_rules/transformers/_verification.py +31 -3
  68. cognite/neat/_session/_base.py +24 -8
  69. cognite/neat/_session/_drop.py +35 -0
  70. cognite/neat/_session/_inspect.py +17 -5
  71. cognite/neat/_session/_mapping.py +39 -0
  72. cognite/neat/_session/_prepare.py +219 -23
  73. cognite/neat/_session/_read.py +49 -12
  74. cognite/neat/_session/_to.py +8 -5
  75. cognite/neat/_session/exceptions.py +4 -0
  76. cognite/neat/_store/_base.py +27 -24
  77. cognite/neat/_utils/rdf_.py +34 -5
  78. cognite/neat/_version.py +1 -1
  79. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +5 -88
  80. cognite/neat/_workflows/steps/lib/current/rules_importer.py +3 -14
  81. cognite/neat/_workflows/steps/lib/current/rules_validator.py +6 -7
  82. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/METADATA +3 -3
  83. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/RECORD +87 -92
  84. cognite/neat/_rules/importers/_rdf/_imf2rules/__init__.py +0 -3
  85. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +0 -86
  86. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +0 -29
  87. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +0 -130
  88. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2rules.py +0 -154
  89. cognite/neat/_rules/importers/_rdf/_owl2rules/__init__.py +0 -3
  90. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +0 -58
  91. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +0 -65
  92. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +0 -59
  93. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2rules.py +0 -39
  94. cognite/neat/_rules/models/dms/_schema.py +0 -1101
  95. cognite/neat/_rules/models/mapping/_base.py +0 -131
  96. cognite/neat/_utils/cdf/loaders/__init__.py +0 -25
  97. cognite/neat/_utils/cdf/loaders/_base.py +0 -54
  98. cognite/neat/_utils/cdf/loaders/_data_modeling.py +0 -339
  99. cognite/neat/_utils/cdf/loaders/_ingestion.py +0 -167
  100. /cognite/neat/{_utils/cdf → _client/_api}/__init__.py +0 -0
  101. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/LICENSE +0 -0
  102. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/WHEEL +0 -0
  103. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,343 @@
1
+ enum:
2
+ - collection: timeseriesType
3
+ description: Time series with double floating point data points.
4
+ name: numeric
5
+ value: numeric
6
+ - collection: timeseriesType
7
+ description: Time series with string data points.
8
+ name: string
9
+ value: string
10
+ metadata:
11
+ created: '2024-08-29T13:49:56.696000'
12
+ creator: Anders Albert,Nikola Vasiljevic,Thorkild Stray
13
+ description: Mapping from Classic to Core
14
+ external_id: ClassicCoreMapping
15
+ name: data_product data model
16
+ role: DMS Architect
17
+ space: mapping
18
+ updated: '2024-08-29T13:49:56.696000'
19
+ version: v1.0
20
+ properties:
21
+ - container: cdf_cdm:CogniteDescribable
22
+ container_property: description
23
+ description: Description of the instance
24
+ immutable: false
25
+ is_list: false
26
+ nullable: true
27
+ value_type: text
28
+ view: ClassicAsset
29
+ view_property: description
30
+ - container: cdf_cdm:CogniteDescribable
31
+ container_property: tags
32
+ description: Text based labels for generic use, limited to 1000
33
+ immutable: false
34
+ is_list: true
35
+ nullable: true
36
+ value_type: text
37
+ view: ClassicAsset
38
+ view_property: labels
39
+ - container: cdf_cdm:CogniteDescribable
40
+ container_property: name
41
+ description: Name of the instance
42
+ immutable: false
43
+ index: name
44
+ is_list: false
45
+ nullable: true
46
+ value_type: text
47
+ view: ClassicAsset
48
+ view_property: name
49
+ - connection: direct
50
+ container: cdf_cdm:CogniteAsset
51
+ container_property: assetHierarchy_parent
52
+ description: The parent of the asset.
53
+ immutable: false
54
+ is_list: false
55
+ name: Parent
56
+ nullable: true
57
+ value_type: ClassicAsset
58
+ view: ClassicAsset
59
+ view_property: parentId
60
+ - connection: direct
61
+ container: cdf_cdm:CogniteSourceable
62
+ container_property: source
63
+ description: Direct relation to a source system
64
+ immutable: false
65
+ index: source
66
+ is_list: false
67
+ nullable: true
68
+ value_type: cdf_cdm:CogniteSourceSystem(version=v1)
69
+ view: ClassicAsset
70
+ view_property: source
71
+ - container: cdf_cdm:CogniteDescribable
72
+ container_property: aliases
73
+ description: Alternative names for the node
74
+ immutable: false
75
+ is_list: true
76
+ nullable: true
77
+ value_type: text
78
+ view: ClassicEvent
79
+ view_property: aliases
80
+ - connection: direct
81
+ container: cdf_cdm:CogniteActivity
82
+ container_property: assets
83
+ description: A list of assets the activity is related to.
84
+ immutable: false
85
+ is_list: true
86
+ name: Assets
87
+ nullable: true
88
+ value_type: ClassicAsset
89
+ view: ClassicEvent
90
+ view_property: assetIds
91
+ - container: cdf_cdm:CogniteDescribable
92
+ container_property: description
93
+ description: Description of the instance
94
+ immutable: false
95
+ is_list: false
96
+ nullable: true
97
+ value_type: text
98
+ view: ClassicEvent
99
+ view_property: description
100
+ - container: cdf_cdm:CogniteSchedulable
101
+ container_property: endTime
102
+ description: The actual end time of an activity (or similar that extends this)
103
+ immutable: false
104
+ index: endTime
105
+ is_list: false
106
+ nullable: true
107
+ value_type: timestamp
108
+ view: ClassicEvent
109
+ view_property: endTime
110
+ - container: cdf_cdm:CogniteDescribable
111
+ container_property: tags
112
+ description: Text based labels for generic use, limited to 1000
113
+ immutable: false
114
+ is_list: true
115
+ nullable: true
116
+ value_type: text
117
+ view: ClassicEvent
118
+ view_property: labels
119
+ - connection: direct
120
+ container: cdf_cdm:CogniteSourceable
121
+ container_property: source
122
+ description: Direct relation to a source system
123
+ immutable: false
124
+ index: source
125
+ is_list: false
126
+ nullable: true
127
+ value_type: cdf_cdm:CogniteSourceSystem(version=v1)
128
+ view: ClassicEvent
129
+ view_property: source
130
+ - container: cdf_cdm:CogniteSchedulable
131
+ container_property: startTime
132
+ description: The actual start time of an activity (or similar that extends this)
133
+ immutable: false
134
+ index: startTime
135
+ is_list: false
136
+ nullable: true
137
+ value_type: timestamp
138
+ view: ClassicEvent
139
+ view_property: startTime
140
+ - connection: direct
141
+ container: cdf_cdm:CogniteFile
142
+ container_property: assets
143
+ description: A list of assets this file is related to.
144
+ immutable: false
145
+ is_list: true
146
+ name: Assets
147
+ nullable: true
148
+ value_type: ClassicAsset
149
+ view: ClassicFile
150
+ view_property: assetIds
151
+ - container: cdf_cdm:CogniteFile
152
+ container_property: directory
153
+ description: Contains the path elements from the source (if the source system has
154
+ a file system hierarchy or similar.)
155
+ immutable: false
156
+ is_list: false
157
+ name: Directory
158
+ nullable: true
159
+ value_type: text
160
+ view: ClassicFile
161
+ view_property: directory
162
+ - container: cdf_cdm:CogniteDescribable
163
+ container_property: tags
164
+ description: Text based labels for generic use, limited to 1000
165
+ immutable: false
166
+ is_list: true
167
+ nullable: true
168
+ value_type: text
169
+ view: ClassicFile
170
+ view_property: labels
171
+ - container: cdf_cdm:CogniteFile
172
+ container_property: mimeType
173
+ description: The MIME type of the file.
174
+ immutable: false
175
+ is_list: false
176
+ name: MIME type
177
+ nullable: true
178
+ value_type: text
179
+ view: ClassicFile
180
+ view_property: mimeType
181
+ - container: cdf_cdm:CogniteDescribable
182
+ container_property: name
183
+ description: Name of the instance
184
+ immutable: false
185
+ index: name
186
+ is_list: false
187
+ nullable: true
188
+ value_type: text
189
+ view: ClassicFile
190
+ view_property: name
191
+ - connection: direct
192
+ container: cdf_cdm:CogniteSourceable
193
+ container_property: source
194
+ description: Direct relation to a source system
195
+ immutable: false
196
+ index: source
197
+ is_list: false
198
+ nullable: true
199
+ value_type: cdf_cdm:CogniteSourceSystem(version=v1)
200
+ view: ClassicFile
201
+ view_property: source
202
+ - container: cdf_cdm:CogniteSourceable
203
+ container_property: sourceCreatedTime
204
+ description: When the instance was created in source system (if available)
205
+ immutable: false
206
+ index: sourceCreatedTime
207
+ is_list: false
208
+ nullable: true
209
+ value_type: timestamp
210
+ view: ClassicFile
211
+ view_property: sourceCreatedTime
212
+ - container: cdf_cdm:CogniteSourceable
213
+ container_property: sourceUpdatedTime
214
+ description: When the instance was last updated in the source system (if available)
215
+ immutable: false
216
+ index: sourceUpdatedTime
217
+ is_list: false
218
+ nullable: true
219
+ value_type: timestamp
220
+ view: ClassicFile
221
+ view_property: sourceModifiedTime
222
+ - connection: direct
223
+ container: cdf_cdm:CogniteTimeSeries
224
+ container_property: assets
225
+ description: A list of assets the time series is related to.
226
+ immutable: false
227
+ is_list: true
228
+ name: Assets
229
+ nullable: true
230
+ value_type: ClassicAsset
231
+ view: ClassicTimeSeries
232
+ view_property: assetId
233
+ - container: cdf_cdm:CogniteDescribable
234
+ container_property: description
235
+ description: Description of the instance
236
+ immutable: false
237
+ is_list: false
238
+ nullable: true
239
+ value_type: text
240
+ view: ClassicTimeSeries
241
+ view_property: description
242
+ - container: cdf_cdm:CogniteTimeSeries
243
+ container_property: isStep
244
+ default: 0
245
+ description: Specifies whether the time series is a step time series or not.
246
+ immutable: false
247
+ is_list: false
248
+ name: Is step
249
+ nullable: false
250
+ value_type: boolean
251
+ view: ClassicTimeSeries
252
+ view_property: isStep
253
+ - container: cdf_cdm:CogniteTimeSeries
254
+ container_property: type
255
+ description: Specifies the data type of the data points.
256
+ immutable: true
257
+ is_list: false
258
+ name: Type
259
+ nullable: false
260
+ value_type: enum(collection=timeseriesType)
261
+ view: ClassicTimeSeries
262
+ view_property: isString
263
+ - container: cdf_cdm:CogniteDescribable
264
+ container_property: aliases
265
+ description: Alternative names for the node
266
+ immutable: false
267
+ is_list: true
268
+ nullable: true
269
+ value_type: text
270
+ view: ClassicTimeSeries
271
+ view_property: legacyName
272
+ - container: cdf_cdm:CogniteDescribable
273
+ container_property: name
274
+ description: Name of the instance
275
+ immutable: false
276
+ index: name
277
+ is_list: false
278
+ nullable: true
279
+ value_type: text
280
+ view: ClassicTimeSeries
281
+ view_property: name
282
+ - container: cdf_cdm:CogniteDescribable
283
+ container_property: tags
284
+ description: Text based labels for generic use, limited to 1000
285
+ immutable: false
286
+ is_list: true
287
+ nullable: true
288
+ value_type: text
289
+ view: ClassicTimeSeries
290
+ view_property: securityCategories
291
+ - container: cdf_cdm:CogniteTimeSeries
292
+ container_property: sourceUnit
293
+ description: The unit specified in the source system.
294
+ immutable: false
295
+ is_list: false
296
+ name: Source unit
297
+ nullable: true
298
+ value_type: text
299
+ view: ClassicTimeSeries
300
+ view_property: unit
301
+ - connection: direct
302
+ container: cdf_cdm:CogniteTimeSeries
303
+ container_property: unit
304
+ description: The unit of the time series.
305
+ immutable: false
306
+ is_list: false
307
+ name: Unit
308
+ nullable: true
309
+ value_type: cdf_cdm:CogniteUnit(version=v1)
310
+ view: ClassicTimeSeries
311
+ view_property: unitExternalId
312
+ views:
313
+ - description: The CogniteSourceSystem core concept is used to standardize the way
314
+ source system is stored.
315
+ implements: cdf_cdm:CogniteDescribable(version=v1)
316
+ in_model: true
317
+ view: cdf_cdm:CogniteSourceSystem(version=v1)
318
+ - description: Represents a single unit of measurement
319
+ implements: CogniteDescribable
320
+ in_model: true
321
+ view: cdf_cdm:CogniteUnit(version=v1)
322
+ - description: Assets represent systems that support industrial functions or processes.
323
+ Assets are often called 'functional location'.
324
+ implements: cdf_cdm:CogniteAsset(version=v1)
325
+ in_model: true
326
+ name: Asset
327
+ view: ClassicAsset
328
+ - description: Represents activities. Activities typically happen over a period and
329
+ have a start and end time.
330
+ implements: cdf_cdm:CogniteActivity(version=v1)
331
+ in_model: true
332
+ name: Activity
333
+ view: ClassicEvent
334
+ - description: Represents files.
335
+ implements: cdf_cdm:CogniteFile(version=v1)
336
+ in_model: true
337
+ name: File
338
+ view: ClassicFile
339
+ - description: Represents a series of data points in time order.
340
+ implements: cdf_cdm:CogniteTimeSeries(version=v1)
341
+ in_model: true
342
+ name: Time series
343
+ view: ClassicTimeSeries
@@ -3,6 +3,7 @@ from ._converters import (
3
3
  ConvertToRules,
4
4
  DMSToInformation,
5
5
  InformationToDMS,
6
+ PrefixEntities,
6
7
  ReduceCogniteModel,
7
8
  SetIDDMSModel,
8
9
  ToCompliantEntities,
@@ -17,9 +18,8 @@ __all__ = [
17
18
  "RulesTransformer",
18
19
  "RulesPipeline",
19
20
  "InformationToDMS",
20
- "InformationToAsset",
21
21
  "ConvertToRules",
22
- "AssetToInformation",
22
+ "PrefixEntities",
23
23
  "DMSToInformation",
24
24
  "VerifyDMSRules",
25
25
  "VerifyInformationRules",
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
4
4
  from collections import Counter, defaultdict
5
5
  from collections.abc import Collection, Mapping
6
6
  from datetime import date, datetime
7
- from typing import Literal, TypeVar, cast
7
+ from typing import Literal, TypeVar, cast, overload
8
8
 
9
9
  from cognite.client.data_classes import data_modeling as dms
10
10
  from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier, ViewId
@@ -42,8 +42,10 @@ from cognite.neat._rules.models.entities import (
42
42
  ContainerEntity,
43
43
  DMSUnknownEntity,
44
44
  EdgeEntity,
45
+ Entity,
45
46
  MultiValueTypeInfo,
46
47
  ReverseConnectionEntity,
48
+ T_Entity,
47
49
  UnknownEntity,
48
50
  ViewEntity,
49
51
  )
@@ -176,6 +178,74 @@ class ToCompliantEntities(RulesTransformer[InformationInputRules, InformationInp
176
178
  return fixed_definitions
177
179
 
178
180
 
181
+ class PrefixEntities(RulesTransformer[InputRules, InputRules]): # type: ignore[misc]
182
+ """Prefixes all entities with a given prefix."""
183
+
184
+ def __init__(self, prefix: str) -> None:
185
+ self._prefix = prefix
186
+
187
+ def transform(self, rules: InputRules | OutRules[InputRules]) -> ReadRules[InputRules]:
188
+ return ReadRules(self._transform(self._to_rules(rules)), IssueList(), {})
189
+
190
+ def _transform(self, rules: InputRules) -> InputRules:
191
+ rules.metadata.version += f"_prefixed_{self._prefix}"
192
+
193
+ if isinstance(rules, InformationInputRules):
194
+ # Todo Make Not mutate input class
195
+ prefixed_by_class: dict[str, str] = {}
196
+ for cls in rules.classes:
197
+ prefixed = str(self._with_prefix(cls.class_))
198
+ prefixed_by_class[str(cls.class_)] = prefixed
199
+ cls.class_ = prefixed
200
+ for prop in rules.properties:
201
+ prop.class_ = self._with_prefix(prop.class_)
202
+ if str(prop.value_type) in prefixed_by_class:
203
+ prop.value_type = prefixed_by_class[str(prop.value_type)]
204
+ return rules
205
+ elif isinstance(rules, DMSInputRules):
206
+ # Todo not mutate input class new_dms = copy.deepcopy(rules)
207
+ prefixed_by_view: dict[str, str] = {}
208
+ for view in rules.views:
209
+ prefixed = str(self._with_prefix(view.view))
210
+ prefixed_by_view[str(view.view)] = prefixed
211
+ view.view = prefixed
212
+ for dms_prop in rules.properties:
213
+ dms_prop.view = self._with_prefix(dms_prop.view)
214
+ if str(dms_prop.value_type) in prefixed_by_view:
215
+ dms_prop.value_type = prefixed_by_view[str(dms_prop.value_type)]
216
+ if rules.containers:
217
+ for container in rules.containers:
218
+ container.container = self._with_prefix(container.container)
219
+ return rules
220
+ raise NeatValueError(f"Unsupported rules type: {type(rules)}")
221
+
222
+ @overload
223
+ def _with_prefix(self, raw: str) -> str: ...
224
+
225
+ @overload
226
+ def _with_prefix(self, raw: T_Entity) -> T_Entity: ...
227
+
228
+ def _with_prefix(self, raw: str | T_Entity) -> str | T_Entity:
229
+ is_entity_format = not isinstance(raw, str)
230
+ entity = Entity.load(raw)
231
+ output: ClassEntity | ViewEntity | ContainerEntity
232
+ if isinstance(entity, ClassEntity):
233
+ output = ClassEntity(prefix=entity.prefix, suffix=f"{self._prefix}{entity.suffix}", version=entity.version)
234
+ elif isinstance(entity, ViewEntity):
235
+ output = ViewEntity(
236
+ space=entity.space, externalId=f"{self._prefix}{entity.external_id}", version=entity.version
237
+ )
238
+ elif isinstance(entity, ContainerEntity):
239
+ output = ContainerEntity(space=entity.space, externalId=f"{self._prefix}{entity.external_id}")
240
+ elif isinstance(entity, UnknownEntity | Entity):
241
+ return f"{self._prefix}{raw}"
242
+ else:
243
+ raise NeatValueError(f"Unsupported entity type: {type(entity)}")
244
+ if is_entity_format:
245
+ return cast(T_Entity, output)
246
+ return str(output)
247
+
248
+
179
249
  class InformationToDMS(ConversionTransformer[InformationRules, DMSRules]):
180
250
  """Converts InformationRules to DMSRules."""
181
251
 
@@ -233,10 +303,11 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
233
303
  self,
234
304
  new_model_id: DataModelIdentifier,
235
305
  org_name: str = "My",
236
- type_: Literal["enterprise", "solution"] = "enterprise",
306
+ type_: Literal["enterprise", "solution", "data_product"] = "enterprise",
237
307
  mode: Literal["read", "write"] = "read",
238
308
  dummy_property: str = "GUID",
239
309
  move_connections: bool = False,
310
+ include: Literal["same-space", "all"] = "same-space",
240
311
  ):
241
312
  self.new_model_id = DataModelId.load(new_model_id)
242
313
  if not self.new_model_id.version:
@@ -247,6 +318,7 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
247
318
  self.type_ = type_
248
319
  self.dummy_property = dummy_property
249
320
  self.move_connections = move_connections
321
+ self.include = include
250
322
 
251
323
  def transform(self, rules: DMSRules | OutRules[DMSRules]) -> JustRules[DMSRules]:
252
324
  # Copy to ensure immutability
@@ -274,6 +346,16 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
274
346
  )
275
347
 
276
348
  return self._to_enterprise(reference_model)
349
+ elif self.type_ == "data_product":
350
+ expanded = self._expand_properties(reference_model.model_copy(deep=True))
351
+ if self.include == "same-space":
352
+ expanded.properties = SheetList[DMSProperty](
353
+ [prop for prop in expanded.properties if prop.view.space == expanded.metadata.space]
354
+ )
355
+ expanded.views = SheetList[DMSView](
356
+ [view for view in expanded.views if view.view.space == expanded.metadata.space]
357
+ )
358
+ return self._to_solution(expanded, remove_views_in_other_space=False)
277
359
 
278
360
  else:
279
361
  raise NeatValueError(f"Unsupported data model type: {self.type_}")
@@ -281,7 +363,7 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
281
363
  def _has_views_in_multiple_space(self, rules: DMSRules) -> bool:
282
364
  return any(view.view.space != rules.metadata.space for view in rules.views)
283
365
 
284
- def _to_solution(self, reference_rules: DMSRules) -> JustRules[DMSRules]:
366
+ def _to_solution(self, reference_rules: DMSRules, remove_views_in_other_space: bool = True) -> JustRules[DMSRules]:
285
367
  """For creation of solution data model / rules specifically for mapping over existing containers."""
286
368
 
287
369
  dump = reference_rules.dump()
@@ -292,16 +374,11 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
292
374
  dump["metadata"]["external_id"] = self.new_model_id.external_id
293
375
  dump["metadata"]["version"] = self.new_model_id.version
294
376
 
295
- # dropping reference and last from the dump as they can cause validation
296
- # issues especially if reference is enterprise model build on top of CDM
297
- dump.pop("reference", None)
298
- dump.pop("last", None)
299
-
300
377
  # Set implement to NONE for all views
301
378
  for view in dump["views"]:
302
379
  view["implements"] = None
303
380
 
304
- if self._has_views_in_multiple_space(reference_rules):
381
+ if remove_views_in_other_space and self._has_views_in_multiple_space(reference_rules):
305
382
  views_to_remove = []
306
383
  for view in dump["views"]:
307
384
  if ":" in view["view"]:
@@ -312,7 +389,7 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
312
389
  solution_model = DMSRules.model_validate(DMSInputRules.load(dump).dump())
313
390
 
314
391
  # Dropping containers coming from reference model
315
- solution_model.containers = SheetList[DMSContainer]()
392
+ solution_model.containers = None
316
393
 
317
394
  # We want to map properties to existing containers allowing extension
318
395
  for prop in solution_model.properties:
@@ -338,7 +415,7 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
338
415
 
339
416
  # Here we add ONLY dummy properties of the solution model and
340
417
  # corresponding solution model space containers to hold them
341
- solution_model.containers.extend(new_containers)
418
+ solution_model.containers = new_containers
342
419
  solution_model.properties.extend(new_properties)
343
420
 
344
421
  return JustRules(solution_model)
@@ -377,6 +454,28 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
377
454
 
378
455
  return JustRules(enterprise_model)
379
456
 
457
+ @staticmethod
458
+ def _expand_properties(rules: DMSRules) -> DMSRules:
459
+ probe = DMSAnalysis(rules)
460
+ ancestor_properties_by_view = probe.classes_with_properties(
461
+ consider_inheritance=True, allow_different_namespace=True
462
+ )
463
+ property_ids_by_view = {
464
+ view: {prop.view_property for prop in properties}
465
+ for view, properties in probe.classes_with_properties(consider_inheritance=False).items()
466
+ }
467
+ for view, property_ids in property_ids_by_view.items():
468
+ ancestor_properties = ancestor_properties_by_view.get(view, [])
469
+ for prop in ancestor_properties:
470
+ if isinstance(prop.connection, ReverseConnectionEntity):
471
+ # If you try to add a reverse direct relation of a parent, it will fail as the ValueType of the
472
+ # original property will point to the parent view, and not the child.
473
+ continue
474
+ if prop.view_property not in property_ids:
475
+ rules.properties.append(prop)
476
+ property_ids.add(prop.view_property)
477
+ return rules
478
+
380
479
  def _remove_cognite_affix(self, entity: _T_Entity) -> _T_Entity:
381
480
  """This method removes `Cognite` affix from the entity."""
382
481
  new_suffix = entity.suffix.replace("Cognite", self.org_name or "")