Skip to content

Commit 2e7e202

Browse files
committed
Fix a bug that appeared when calling ".exists()" on a ModelVersion (which did exist in the KG) with a repository that had no @id.
The fix requires openminds >= 0.5.1 The error trace looked like: ``` /Users/adavison/dev/data/fairgraph/fairgraph/kgobject.py(566)exists() -> self._update_empty_properties(data) # also updates `remote_data` /Users/adavison/dev/data/fairgraph/fairgraph/kgobject.py(502)_update_empty_properties() -> locally_modified = self.modified_data() > /Users/adavison/dev/data/fairgraph/fairgraph/kgobject.py(650)modified_data() -> self.to_jsonld(include_empty_properties=True, embed_linked_nodes=False), self.context /Users/adavison/dev/validation/env/lib/python3.9/site-packages/openminds/base.py(107)to_jsonld() -> data[property.path] = value_to_jsonld( /Users/adavison/dev/validation/env/lib/python3.9/site-packages/openminds/base.py(30)value_to_jsonld() -> raise ValueError("Exporting as a stand-alone JSON-LD document requires @id to be defined.") ```
1 parent 50e2160 commit 2e7e202

6 files changed

Lines changed: 69 additions & 18 deletions

File tree

fairgraph/embedded.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from typing import Optional, TYPE_CHECKING, Union
2424
from warnings import warn
2525

26+
from openminds.base import LinkedNodeEmbedding
27+
2628
from .utility import as_list, ActivityLog
2729
from .base import JSONdict
2830
from .node import KGNode
@@ -66,8 +68,8 @@ def __repr__(self):
6668
return template.format(self=self)
6769

6870
def __eq__(self, other):
69-
return isinstance(other, self.__class__) and self.to_jsonld(embed_linked_nodes=False) == other.to_jsonld(
70-
embed_linked_nodes=False
71+
return isinstance(other, self.__class__) and self.to_jsonld(embed_linked_nodes=LinkedNodeEmbedding.NEVER) == other.to_jsonld(
72+
embed_linked_nodes=LinkedNodeEmbedding.NEVER
7173
)
7274

7375
@classmethod

fairgraph/kgobject.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
from openminds.registry import lookup_type
3838
from openminds import IRI, LinkedMetadata
39+
from openminds.base import LinkedNodeEmbedding
3940

4041
from .utility import expand_uri, as_list, expand_filter, ActivityLog, normalize_data, handle_scope_keyword
4142
from .queries import Query, QueryProperty
@@ -96,7 +97,7 @@ def __init__(
9697
if data:
9798
try:
9899
self.remote_data = normalize_data(
99-
self.to_jsonld(include_empty_properties=False, embed_linked_nodes=False),
100+
self.to_jsonld(include_empty_properties=False, embed_linked_nodes=LinkedNodeEmbedding.NEVER),
100101
data.get("@context", self.context)
101102
)
102103
except ValueError as err:
@@ -647,7 +648,11 @@ def values_are_equal(local, remote):
647648
return local == remote
648649

649650
current_data = normalize_data(
650-
self.to_jsonld(include_empty_properties=True, embed_linked_nodes=False), self.context
651+
self.to_jsonld(
652+
include_empty_properties=True,
653+
embed_linked_nodes=LinkedNodeEmbedding.IF_NECESSARY
654+
),
655+
self.context
651656
)
652657
modified_data = {}
653658
for key, current_value in current_data.items():
@@ -741,14 +746,15 @@ def save(
741746
else:
742747
# update
743748
local_data = normalize_data(
744-
self.to_jsonld(include_empty_properties=False, embed_linked_nodes=False),
749+
self.to_jsonld(include_empty_properties=False, embed_linked_nodes=LinkedNodeEmbedding.NEVER),
745750
self.context
746751
)
747752
if replace:
748753
logger.info(f" - replacing - {self.__class__.__name__}(id={self.id})")
749754
if activity_log:
750755
activity_log.update(item=self, delta=local_data, space=space, entry_type="replacement")
751756
try:
757+
assert self.uuid is not None
752758
client.replace_instance(self.uuid, local_data)
753759
# what does this return? Can we use it to update `remote_data`?
754760
except AuthorizationError as err:
@@ -778,6 +784,7 @@ def save(
778784
# Note: if modified_data includes embedded objects
779785
# then _all_ fields of the embedded objects must be provided,
780786
# not only those that have changed.
787+
assert self.uuid is not None
781788
client.update_instance(self.uuid, modified_data)
782789
except AuthorizationError as err:
783790
if ignore_auth_errors:
@@ -800,7 +807,7 @@ def save(
800807
else:
801808
# create new
802809
local_data = normalize_data(
803-
self.to_jsonld(include_empty_properties=False, embed_linked_nodes=False),
810+
self.to_jsonld(include_empty_properties=False, embed_linked_nodes=LinkedNodeEmbedding.NEVER),
804811
self.context
805812
)
806813
logger.info(" - creating instance with data {}".format(local_data))
@@ -836,7 +843,7 @@ def save(
836843
if self.id:
837844
logger.debug(
838845
"Updating cache for object {}. Current state: {}".format(
839-
self.id, self.to_jsonld(embed_linked_nodes=False)
846+
self.id, self.to_jsonld(embed_linked_nodes=LinkedNodeEmbedding.NEVER)
840847
)
841848
)
842849
object_cache[self.id] = self

fairgraph/node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66
from warnings import warn
77

8-
from openminds.base import value_to_jsonld, Link
8+
from openminds.base import value_to_jsonld, Link, LinkedNodeEmbedding
99
from openminds.properties import Property
1010

1111
from .registry import NodeMeta
@@ -480,7 +480,7 @@ def _build_existence_query(self) -> Union[None, Dict[str, Any]]:
480480
elif value is None:
481481
raise CannotBuildExistenceQuery(f"Required value for '{query_property_name}' is missing")
482482
else:
483-
query_val = value_to_jsonld(value, include_empty_properties=False, embed_linked_nodes=False)
483+
query_val = value_to_jsonld(value, include_empty_properties=False, embed_linked_nodes=LinkedNodeEmbedding.NEVER)
484484
if query_val is None:
485485
raise CannotBuildExistenceQuery(f"Required value for '{query_property_name}' is missing")
486486
query[query_property_name] = query_val

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ classifiers = [
1717
keywords = ["ebrains", "hbp", "metadata", "electrophysiology", "knowledge-graph"]
1818
dependencies=[
1919
"ebrains-kg-core",
20-
"openminds>=0.4.0",
20+
"openminds>=0.5.1",
2121
"python-dateutil",
2222
"tabulate",
2323
"requests"

test/test_base.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
from datetime import date, datetime
77
from numbers import Real
8-
from openminds.base import LinkedMetadata, EmbeddedMetadata as OMEmbeddedMetadata
8+
from openminds.base import LinkedMetadata, EmbeddedMetadata as OMEmbeddedMetadata, LinkedNodeEmbedding
9+
910
from openminds.properties import Property
1011
from fairgraph.embedded import KGEmbedded
1112
from fairgraph.kgobject import KGObject
@@ -473,7 +474,7 @@ def test_build_data_all_properties(self):
473474
"https://openminds.ebrains.eu/vocab/anOptionalListOfStrings": ["plum, peach, apricot"],
474475
"https://openminds.ebrains.eu/vocab/anOptionalString": "melon",
475476
}
476-
assert obj.to_jsonld(include_empty_properties=False, embed_linked_nodes=False) == expected
477+
assert obj.to_jsonld(include_empty_properties=False, embed_linked_nodes=LinkedNodeEmbedding.NEVER) == expected
477478

478479
def test_modified_data(self):
479480
obj = self._construct_object_all_properties()
@@ -527,7 +528,7 @@ def test_exists__it_does_exist(self):
527528

528529
class MockClient:
529530
def instance_from_full_uri(self, id, use_cache=True, release_status="in progress", require_full_data=True):
530-
data = orig_object.to_jsonld(include_empty_properties=False, embed_linked_nodes=False)
531+
data = orig_object.to_jsonld(include_empty_properties=False, embed_linked_nodes=LinkedNodeEmbedding.NEVER)
531532
data["https://core.kg.ebrains.eu/vocab/meta/space"] = "collab-foobar"
532533
data["@id"] = orig_object.id
533534
data["@type"] = orig_object.type_

test/test_openminds_core.py

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
import pytest
1212

1313
from openminds import IRI
14+
from openminds.base import LinkedNodeEmbedding
1415

1516
from fairgraph.utility import as_list
1617
from fairgraph.kgproxy import KGProxy
1718
from fairgraph.kgquery import KGQuery
1819
from fairgraph.kgobject import KGObject
1920
import fairgraph.openminds.core as omcore
2021
import fairgraph.openminds.controlled_terms as omterms
21-
from fairgraph.utility import ActivityLog, sha1sum
22+
from fairgraph.utility import ActivityLog, sha1sum, normalize_data
2223

2324
from test.utils import mock_client, kg_client, skip_if_no_connection, skip_if_using_production_server
2425

@@ -433,6 +434,40 @@ def test_exists_method_without_id(kg_client):
433434
assert new_model == model
434435

435436

437+
def test_modified_data_method_with_local_changes():
438+
dsv = omcore.DatasetVersion(
439+
id="https://kg.ebrains.eu/api/instances/12345678-90ab-cdef-0123-4567890abcde", full_name="test1234"
440+
)
441+
dsv.remote_data = normalize_data(
442+
dsv.to_jsonld(include_empty_properties=True, embed_linked_nodes=LinkedNodeEmbedding.NEVER), dsv.context
443+
)
444+
assert dsv.modified_data() == {}
445+
dsv.repository = omcore.FileRepository(iri="http://example.org")
446+
assert dsv.modified_data() == {
447+
"https://openminds.om-i.org/props/repository": {
448+
"@type": "https://openminds.om-i.org/types/FileRepository",
449+
"https://openminds.om-i.org/props/IRI": "http://example.org",
450+
"https://openminds.om-i.org/props/contentTypePattern": None,
451+
"https://openminds.om-i.org/props/format": None,
452+
"https://openminds.om-i.org/props/hash": None,
453+
"https://openminds.om-i.org/props/hostedBy": None,
454+
"https://openminds.om-i.org/props/name": None,
455+
"https://openminds.om-i.org/props/storageSize": None,
456+
"https://openminds.om-i.org/props/structurePattern": None,
457+
"https://openminds.om-i.org/props/type": None,
458+
}
459+
}
460+
dsv.repository = omcore.FileRepository(
461+
id="https://kg.ebrains.eu/api/instances/23456789-0abc-def0-1234-567890abcdef",
462+
iri="http://example.org"
463+
)
464+
assert dsv.modified_data() == {
465+
"https://openminds.om-i.org/props/repository": {
466+
"@id": dsv.repository.id
467+
}
468+
}
469+
470+
436471
def test__update():
437472
example_data = {
438473
"@id": "https://kg.ebrains.eu/api/instances/e90fc25a-fc35-4066-9ff2-ca3583a2d008",
@@ -518,9 +553,11 @@ def test_to_jsonld():
518553
"familyName": "Oakenshield",
519554
"givenName": "Thorin",
520555
}
521-
assert person1.to_jsonld(embed_linked_nodes=True, include_empty_properties=False) == expected1
556+
assert (
557+
person1.to_jsonld(embed_linked_nodes=LinkedNodeEmbedding.ALWAYS, include_empty_properties=False) == expected1
558+
)
522559
with pytest.raises(ValueError) as err:
523-
person1.to_jsonld(embed_linked_nodes=False, include_empty_properties=False)
560+
person1.to_jsonld(embed_linked_nodes=LinkedNodeEmbedding.NEVER, include_empty_properties=False)
524561
assert "Exporting as a stand-alone JSON-LD document requires @id to be defined" in err
525562

526563
person2 = omcore.Person(
@@ -559,8 +596,12 @@ def test_to_jsonld():
559596
"familyName": "Oakenshield",
560597
"givenName": "Thorin",
561598
}
562-
assert person2.to_jsonld(embed_linked_nodes=True, include_empty_properties=False) == expected2a
563-
assert person2.to_jsonld(embed_linked_nodes=False, include_empty_properties=False) == expected2b
599+
assert (
600+
person2.to_jsonld(embed_linked_nodes=LinkedNodeEmbedding.ALWAYS, include_empty_properties=False) == expected2a
601+
)
602+
assert (
603+
person2.to_jsonld(embed_linked_nodes=LinkedNodeEmbedding.NEVER, include_empty_properties=False) == expected2b
604+
)
564605

565606

566607
@skip_if_using_production_server

0 commit comments

Comments
 (0)