cognite-neat 0.117.9__py3-none-any.whl → 0.117.10__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.

@@ -307,6 +307,45 @@ class DMSSchema:
307
307
  resources.append(loaded_instance)
308
308
  return resources
309
309
 
310
+ @classmethod
311
+ def from_read_model(cls, model: dm.DataModel[dm.View]) -> Self:
312
+ """Load schema from a read model.
313
+
314
+ CAVEAT: This method infers the containers and spaces from the views. This means that
315
+ for example indexes and constraints will not be captured in the containers.
316
+
317
+ Args:
318
+ model (dm.DataModel): The read model to load the schema from.
319
+ """
320
+ write_model = model.as_write()
321
+ write_model.views = [view.as_id() for view in model.views or []]
322
+ views = ViewApplyDict([view.as_write() for view in model.views])
323
+ containers = ContainerApplyDict()
324
+ for view in model.views:
325
+ for prop in view.properties.values():
326
+ if not isinstance(prop, dm.MappedProperty):
327
+ continue
328
+ if prop.container not in containers:
329
+ containers[prop.container] = dm.ContainerApply(
330
+ space=prop.container.space,
331
+ external_id=prop.container.external_id,
332
+ properties={},
333
+ used_for=view.used_for,
334
+ )
335
+ containers[prop.container].properties[prop.container_property_identifier] = dm.ContainerProperty(
336
+ type=prop.type,
337
+ nullable=prop.nullable,
338
+ auto_increment=prop.auto_increment,
339
+ immutable=prop.immutable,
340
+ default_value=prop.default_value,
341
+ name=prop.name,
342
+ description=prop.description,
343
+ )
344
+
345
+ schema = cls(data_model=write_model, views=views, containers=containers)
346
+ schema.spaces = SpaceApplyDict(dm.SpaceApply(space) for space in schema.referenced_spaces())
347
+ return schema
348
+
310
349
  def dump(self, camel_case: bool = True, sort: bool = True) -> dict[str, Any]:
311
350
  """Dump the schema to a dictionary that can be serialized to JSON.
312
351
 
@@ -205,6 +205,7 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
205
205
  for missing_view in missing:
206
206
  issues.append(ResourceNotFoundError(missing_view, "view", more="The view is not found in CDF."))
207
207
  return [], issues
208
+ self._lookup_max_limits_size(self._client, views)
208
209
  else:
209
210
  views = dm.ViewList([])
210
211
  with catch_issues() as issues:
@@ -231,6 +232,29 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
231
232
  view_iterations.append(view_iteration)
232
233
  return view_iterations, issues
233
234
 
235
+ @staticmethod
236
+ def _lookup_max_limits_size(client: NeatClient, views: dm.ViewList) -> None:
237
+ """For listable container properties (mapped properties), the read version of the view does not
238
+ contain the max_list_size. This method will lookup the max_list_size from the containers definitions."""
239
+ containers = client.data_modeling.containers.retrieve(list(views.referenced_containers()))
240
+ properties_by_container_and_prop_id = {
241
+ (container.as_id(), prop_id): prop
242
+ for container in containers
243
+ for prop_id, prop in container.properties.items()
244
+ }
245
+
246
+ for view in views:
247
+ for prop in view.properties.values():
248
+ if not isinstance(prop, dm.MappedProperty):
249
+ continue
250
+ if not isinstance(prop.type, ListablePropertyType):
251
+ continue
252
+ prop_definition = properties_by_container_and_prop_id.get(
253
+ (prop.container, prop.container_property_identifier)
254
+ )
255
+ if prop_definition and isinstance(prop_definition.type, ListablePropertyType):
256
+ prop.type.max_list_size = prop_definition.type.max_list_size
257
+
234
258
  def _select_views_with_instances(self, view_query_by_id: ViewQueryDict) -> dict[dm.ViewId, _ViewIterator]:
235
259
  """Selects the views with data."""
236
260
  view_iterations: dict[dm.ViewId, _ViewIterator] = {}
@@ -414,23 +438,27 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
414
438
  def parse_direct_relation(cls: Any, value: list, info: ValidationInfo) -> dict | list[dict]:
415
439
  # We validate above that we only get one value for single direct relations.
416
440
  if list.__name__ in _get_field_value_types(cls, info):
417
- ids = (self._create_instance_id(v, "node", stop_on_exception=True) for v in value)
418
- result = [id_.dump(camel_case=True, include_instance_type=False) for id_ in ids]
419
- # Todo: Account for max_list_limit
420
- if len(result) <= DMS_DIRECT_RELATION_LIST_DEFAULT_LIMIT:
421
- return result
422
- warnings.warn(
423
- PropertyDirectRelationLimitWarning(
424
- identifier="unknown",
425
- resource_type="view property",
426
- property_name=cast(str, cls.model_fields[info.field_name].alias or info.field_name),
427
- limit=DMS_DIRECT_RELATION_LIST_DEFAULT_LIMIT,
428
- ),
429
- stacklevel=2,
441
+ # To get deterministic results
442
+ value.sort()
443
+ limit = (
444
+ # We know that info.field_name will always be set due to *direct_relation_by_property.keys()
445
+ direct_relation_by_property[cast(str, info.field_name)].max_list_size
446
+ or DMS_DIRECT_RELATION_LIST_DEFAULT_LIMIT
430
447
  )
431
- # To get deterministic results, we sort by space and externalId
432
- result.sort(key=lambda x: (x["space"], x["externalId"]))
433
- return result[:DMS_DIRECT_RELATION_LIST_DEFAULT_LIMIT]
448
+ if len(value) > limit:
449
+ warnings.warn(
450
+ PropertyDirectRelationLimitWarning(
451
+ identifier="unknown",
452
+ resource_type="view property",
453
+ property_name=cast(str, cls.model_fields[info.field_name].alias or info.field_name),
454
+ limit=limit,
455
+ ),
456
+ stacklevel=2,
457
+ )
458
+ value = value[:limit]
459
+
460
+ ids = (self._create_instance_id(v, "node", stop_on_exception=True) for v in value)
461
+ return [id_.dump(camel_case=True, include_instance_type=False) for id_ in ids]
434
462
  elif value:
435
463
  return self._create_instance_id(value[0], "node", stop_on_exception=True).dump(
436
464
  camel_case=True, include_instance_type=False
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.117.9"
1
+ __version__ = "0.117.10"
2
2
  __engine__ = "^2.0.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: cognite-neat
3
- Version: 0.117.9
3
+ Version: 0.117.10
4
4
  Summary: Knowledge graph transformation
5
5
  License: Apache-2.0
6
6
  Author: Nikola Vasiljevic
@@ -9,7 +9,7 @@ cognite/neat/_client/_api_client.py,sha256=wsNK3abwoIppleWzVZmoXe9Ia_wrU4QqOxnxx
9
9
  cognite/neat/_client/data_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  cognite/neat/_client/data_classes/data_modeling.py,sha256=RvpUp9ygd-yffQFJ7O2mQqMLDPIa-dmip5zPb8QVIiw,6672
11
11
  cognite/neat/_client/data_classes/neat_sequence.py,sha256=QZWSfWnwk6KlYJvsInco4Wdwc1U8DnOQKWmHebArbQY,10830
12
- cognite/neat/_client/data_classes/schema.py,sha256=iIsPGm2fQfNsjhXYjCCp-ox72GDpRC5QVLuGrUv-q4E,23236
12
+ cognite/neat/_client/data_classes/schema.py,sha256=LFRj6-noSO23bxA2I-RdRXMHHc9AHptOXlSVHj1tVHg,25047
13
13
  cognite/neat/_client/testing.py,sha256=JnzLQegw2f6SATWRNDQ8Fui6qBYyz7vFgA5myjioohY,1175
14
14
  cognite/neat/_config.py,sha256=WT1BS8uADcFvGoUYOOfwFOVq_VBl472TisdoA3wLick,280
15
15
  cognite/neat/_constants.py,sha256=WcrInu37p56BP7gPRIvTVScu6D9Di4RQG-OiYg5NAek,8250
@@ -45,7 +45,7 @@ cognite/neat/_graph/extractors/_raw.py,sha256=xU3SmeLBCeqbs1WBdGCge8ZMnlOU6wgkKX
45
45
  cognite/neat/_graph/extractors/_rdf_file.py,sha256=8BWDk1oQPzo2w2HVmGah4rppIGqihX98SBbTB1SB_6o,2900
46
46
  cognite/neat/_graph/loaders/__init__.py,sha256=XS6vwmxgBzntg7UuG_ct_1hfhShVnFH5u0gGrdA8WfA,699
47
47
  cognite/neat/_graph/loaders/_base.py,sha256=Xq91-4GeQF2XN90-QgEFCU4aJabBXkeFeFXS2k4mWU4,4472
48
- cognite/neat/_graph/loaders/_rdf2dms.py,sha256=JtXyfTv6vwJugoc2gbg8mmvAIq6m4qQcj1h48lB0eM8,32485
48
+ cognite/neat/_graph/loaders/_rdf2dms.py,sha256=8XrrKfwQXIE7qRnU4Fz-7QlhUQfn-eXIP8AneJVm_rM,33873
49
49
  cognite/neat/_graph/queries/__init__.py,sha256=BgDd-037kvtWwAoGAy8eORVNMiZ5-E9sIV0txIpeaN4,50
50
50
  cognite/neat/_graph/queries/_base.py,sha256=xs_kCiqQFJfaPyYrKhpyPIAvyDOf19RgYcdg3WxjB6s,19344
51
51
  cognite/neat/_graph/transformers/__init__.py,sha256=YzC1Z8BuT77NwagWX4Z-F9R9BARLSS7zM4bCdxBbqKg,1761
@@ -177,10 +177,10 @@ cognite/neat/_utils/text.py,sha256=qy7lgMdRjzxSYkL8teAnWsq6T5baS_QcezHLK007_7M,7
177
177
  cognite/neat/_utils/time_.py,sha256=7ayUm0OWZm1JDmy32E4ip8WRr2o0GLwrHwJA8sJ43Z4,357
178
178
  cognite/neat/_utils/upload.py,sha256=xWtM6mFuD2QYQHaZ7zCAuGptbEpPIxcH-raWQu93-Ug,5845
179
179
  cognite/neat/_utils/xml_.py,sha256=FQkq84u35MUsnKcL6nTMJ9ajtG9D5i1u4VBnhGqP2DQ,1710
180
- cognite/neat/_version.py,sha256=QAfP-nKtktitR2MDA9DYnxpbDb854mbJ_-5pKo0A0L0,46
180
+ cognite/neat/_version.py,sha256=5zWfgiXewAsXGaDaOZXvCifFrtC_4gmggpTf0imvMUw,47
181
181
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
182
- cognite_neat-0.117.9.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
183
- cognite_neat-0.117.9.dist-info/METADATA,sha256=dlBeOE7p53mRzY2Xw0zuRYJ-6vM5uoZISd9vTn-DYtQ,5361
184
- cognite_neat-0.117.9.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
185
- cognite_neat-0.117.9.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
186
- cognite_neat-0.117.9.dist-info/RECORD,,
182
+ cognite_neat-0.117.10.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
183
+ cognite_neat-0.117.10.dist-info/METADATA,sha256=QyKyWeL6QHakal4Cs16YhZ3rwu8eG__54geUBj0nSD0,5362
184
+ cognite_neat-0.117.10.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
185
+ cognite_neat-0.117.10.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
186
+ cognite_neat-0.117.10.dist-info/RECORD,,