From c342b0c19d39df89b33c7f55f2447d0954047708 Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Thu, 18 Nov 2021 10:43:09 +0100 Subject: [PATCH 1/7] fix failing tests after api update --- knora/dsplib/models/listnode.py | 2 +- knora/dsplib/utils/xml_upload.py | 15 +- test/e2e/test_tools.py | 4 +- testdata/test-onto.json | 1048 +++++++++++++++--------------- 4 files changed, 546 insertions(+), 523 deletions(-) diff --git a/knora/dsplib/models/listnode.py b/knora/dsplib/models/listnode.py index ed7a94264..67f496929 100644 --- a/knora/dsplib/models/listnode.py +++ b/knora/dsplib/models/listnode.py @@ -184,7 +184,7 @@ def __init__(self, self._project = project.id if isinstance(project, Project) else str(project) if project is not None else None self._id = str(id) if id is not None else None self._label = LangString(label) - self._comments = LangString(comments) + self._comments = LangString(comments) if comments is not None else LangString({"en": ""}) self._name = str(name) if name is not None else None if parent and isinstance(parent, ListNode): self._parent = parent.id diff --git a/knora/dsplib/utils/xml_upload.py b/knora/dsplib/utils/xml_upload.py index eae78765a..257471958 100644 --- a/knora/dsplib/utils/xml_upload.py +++ b/knora/dsplib/utils/xml_upload.py @@ -10,7 +10,7 @@ from knora.dsplib.models.group import Group from knora.dsplib.models.permission import Permissions from knora.dsplib.models.project import Project -from knora.dsplib.models.resource import ResourceInstanceFactory +from knora.dsplib.models.resource import ResourceInstanceFactory, ResourceInstance from knora.dsplib.models.sipi import Sipi from knora.dsplib.models.value import KnoraStandoffXml @@ -613,10 +613,11 @@ def xml_upload(input_file: str, server: str, user: str, password: str, imgdir: s bitstream = None # create the resource on the server - instance = res_classes[resource.restype](con=con, label=resource.label, - permissions=permissions_lookup.get(resource.permissions), - bitstream=bitstream, - values=resource.get_propvals(res_iri_lookup, - permissions_lookup)).create() + instance: ResourceInstance = res_classes[resource.restype](con=con, label=resource.label, + permissions=permissions_lookup.get( + resource.permissions), + bitstream=bitstream, + values=resource.get_propvals(res_iri_lookup, + permissions_lookup)).create() res_iri_lookup[resource.id] = instance.iri - print("Created resource: ", instance.label, " (", resource.id, ") with IRI ", instance.iri) + print("Created resource:", instance.label, "(", resource.id, ") with IRI", instance.iri) diff --git a/test/e2e/test_tools.py b/test/e2e/test_tools.py index 4d3c191f7..cec745353 100644 --- a/test/e2e/test_tools.py +++ b/test/e2e/test_tools.py @@ -107,7 +107,7 @@ def test_create_ontology(self) -> None: server=self.server, user=self.user, password='test', - verbose=True, + verbose=False, dump=True) def test_xml_upload(self) -> None: @@ -117,7 +117,7 @@ def test_xml_upload(self) -> None: password='test', imgdir='testdata/bitstreams', sipi='http://0.0.0.0:1024', - verbose=True, + verbose=False, validate_only=False) diff --git a/testdata/test-onto.json b/testdata/test-onto.json index c0fdd6999..58600024e 100644 --- a/testdata/test-onto.json +++ b/testdata/test-onto.json @@ -1,526 +1,548 @@ { - "prefixes": { - "foaf": "http://xmlns.com/foaf/0.1/", - "dcterms": "http://purl.org/dc/terms/" - }, - "$schema": "../knora/dsplib/schemas/ontology.json", - "project": { - "shortcode": "4123", - "shortname": "tp", - "longname": "test project", - "descriptions": { - "en": "A systematic test project", - "de": "Ein systematisches Testprojekt" + "prefixes": { + "foaf": "http://xmlns.com/foaf/0.1/", + "dcterms": "http://purl.org/dc/terms/" }, - "keywords": [ - "test", - "testing" - ], - "lists": [ - { - "name": "testlist", - "labels": { - "en": "Testlist" + "$schema": "../knora/dsplib/schemas/ontology.json", + "project": { + "shortcode": "4123", + "shortname": "tp", + "longname": "test project", + "descriptions": { + "en": "A systematic test project", + "de": "Ein systematisches Testprojekt" }, - "comments": {"en": "no comment"}, - "nodes": [ - { - "name": "a", - "labels": { - "en": "a_label" - } - }, - { - "name": "b", - "labels": { - "en": "b_label" - }, - "nodes": [ - { - "name": "b1", + "keywords": [ + "test", + "testing" + ], + "lists": [ + { + "name": "testlist", "labels": { - "en": "b1_label" - } - }, - { - "name": "b2", + "en": "Testlist" + }, + "comments": { + "en": "no comment", + "de": "kein Kommentar" + }, + "nodes": [ + { + "name": "a", + "labels": { + "en": "a_label" + }, + "comments": { + "en": "no comment" + } + }, + { + "name": "b", + "labels": { + "en": "b_label" + }, + "comments": { + "en": "no comment" + }, + "nodes": [ + { + "name": "b1", + "labels": { + "en": "b1_label" + }, + "comments": { + "en": "no comment" + } + }, + { + "name": "b2", + "labels": { + "en": "b2_label" + }, + "comments": { + "en": "no comment" + } + } + ] + }, + { + "name": "c", + "labels": { + "en": "c_label" + }, + "comments": { + "en": "no comment" + } + } + ] + }, + { + "name": "my-list-from-excel", "labels": { - "en": "b2_label" + "en": "My list from Excel" + }, + "comments": { + "en": "no comment", + "de": "ein Kommentar", + "fr": "un commentaire" + }, + "nodes": { + "folder": "testdata/lists", + "comments": { + "en": "" + } } - } - ] - }, - { - "name": "c", - "labels": { - "en": "c_label" } - } - ] - }, - { - "name": "my-list-from-excel", - "labels": { - "en": "My list from Excel" - }, - "comments": { - "en": "no comment", - "de": "ein Kommentar", - "fr": "un commentaire" - }, - "nodes": { - "folder": "testdata/lists" - } - } - ], - "groups": [ - { - "name": "testgroup", - "descriptions": { - "en": "Test group", - "de": "Testgruppe"}, - "selfjoin": false, - "status": true - } - ], - "users": [ - { - "username": "tester", - "email": "tester@test.org", - "givenName": "Testing", - "familyName": "tester", - "password": "test0815", - "lang": "en", - "groups": [ - ":testgroup" ], - "projects": [ - ] - } - ], - "ontologies": [ - { - "name": "testonto", - "label": "Test ontology", - "properties": [ - { - "name": "hasText", - "super": [ - "hasValue" - ], - "object": "TextValue", - "labels": { - "en": "Text" - }, - "gui_element": "SimpleText", - "gui_attributes": { - "maxlength": 255, - "size": 80 - } - }, - { - "name": "hasRichtext", - "super": [ - "hasValue" - ], - "object": "TextValue", - "labels": { - "en": "Text" - }, - "gui_element": "Richtext" - }, - { - "name": "hasUri", - "super": [ - "hasValue" - ], - "object": "UriValue", - "labels": { - "en": "URI" - }, - "gui_element": "SimpleText", - "gui_attributes": { - "maxlength": 255, - "size": 80 - } - }, - { - "name": "hasBoolean", - "super": [ - "hasValue" - ], - "object": "BooleanValue", - "labels": { - "en": "Boolean value" - }, - "gui_element": "Checkbox" - }, - { - "name": "hasDate", - "super": [ - "hasValue" - ], - "object": "DateValue", - "labels": { - "en": "Date" - }, - "gui_element": "Date" - }, - { - "name": "hasInteger", - "super": [ - "hasValue" - ], - "object": "IntValue", - "labels": { - "en": "Integer" - }, - "gui_element": "Spinbox", - "gui_attributes": { - "max": -1.0, - "min": 0.0 - } - }, - { - "name": "hasDecimal", - "super": [ - "hasValue" - ], - "object": "DecimalValue", - "labels": { - "en": "Decimal number" - }, - "gui_element": "SimpleText", - "gui_attributes": { - "maxlength": 255, - "size": 80 + "groups": [ + { + "name": "testgroup", + "descriptions": { + "en": "Test group", + "de": "Testgruppe" + }, + "selfjoin": false, + "status": true } - }, - { - "name": "hasGeometry", - "super": [ - "hasValue" - ], - "object": "GeomValue", - "labels": { - "en": "Geometry" - }, - "gui_element": "Geometry" - }, - { - "name": "hasGeoname", - "super": [ - "hasValue" - ], - "object": "GeonameValue", - "labels": { - "en": "Geoname" - }, - "gui_element": "Geonames" - }, - { - "name": "hasInterval", - "super": [ - "hasValue" - ], - "object": "IntervalValue", - "labels": { - "en": "Time interval" - }, - "gui_element": "Interval" - }, - { - "name": "hasColor", - "super": [ - "hasValue" - ], - "object": "ColorValue", - "labels": { - "en": "Color" - }, - "gui_element": "Colorpicker" - }, - { - "name": "hasListItem", - "super": [ - "hasValue" - ], - "object": "ListValue", - "labels": { - "en": "List element" - }, - "gui_element": "List", - "gui_attributes": { - "hlist": "testlist" + ], + "users": [ + { + "username": "tester", + "email": "tester@test.org", + "givenName": "Testing", + "familyName": "tester", + "password": "test0815", + "lang": "en", + "groups": [ + ":testgroup" + ], + "projects": [ + ] } - }, - { - "name": "hasTestRegion", - "super": [ - "hasLinkTo" - ], - "object": "Region", - "labels": { - "en": "has region" - }, - "gui_element": "Searchbox" - }, - { - "name": "hasTestThing2", - "super": [ - "hasLinkTo" - ], - "object": ":TestThing2", - "labels": { - "en": "Another thing" - }, - "gui_element": "Searchbox" - } ], - "resources": [ - { - "name": "TestThing", - "super": "Resource", - "labels": { - "en": "TestThing" - }, - "comments": { - "en": "A thing to test things", - "de": "Ein Ding um allerlei Dinge zu testen." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1-n" - }, - { - "propname": ":hasRichtext", - "gui_order": 2, - "cardinality": "0-n" - }, - { - "propname": ":hasUri", - "gui_order": 3, - "cardinality": "0-n" - }, - { - "propname": ":hasBoolean", - "gui_order": 4, - "cardinality": "1" - }, - { - "propname": ":hasDate", - "gui_order": 5, - "cardinality": "0-n" - }, - { - "propname": ":hasInteger", - "gui_order": 6, - "cardinality": "0-n" - }, - { - "propname": ":hasDecimal", - "gui_order": 7, - "cardinality": "0-n" - }, - { - "propname": ":hasGeometry", - "gui_order": 8, - "cardinality": "0-n" - }, - { - "propname": ":hasGeoname", - "gui_order": 9, - "cardinality": "0-n" - }, - { - "propname": ":hasInterval", - "gui_order": 10, - "cardinality": "0-n" - }, - { - "propname": ":hasColor", - "gui_order": 11, - "cardinality": "0-n" - }, - { - "propname": ":hasListItem", - "gui_order": 12, - "cardinality": "0-n" - }, - { - "propname": ":hasTestRegion", - "gui_order": 13, - "cardinality": "0-n" - }, - { - "propname": ":hasTestThing2", - "gui_order": 14, - "cardinality": "0-n" - } - ] - }, - { - "name": "TestThing2", - "super": "Resource", - "labels": { - "en": "Another Test Thing" - }, - "comments": { - "en": "Another thing for testing things." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1" - } - ] - }, - { - "name": "CompoundThing", - "super": "Resource", - "labels": { - "en": "A Compound Thing" - }, - "comments": { - "en": "A thing for testing compound things." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1" - } - ] - }, - { - "name": "ImageThing", - "super": "StillImageRepresentation", - "labels": { - "en": "An Image Thing" - }, - "comments": { - "en": "An image thing for testing image things." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1" - } - ] - }, - { - "name": "AudioThing", - "super": "AudioRepresentation", - "labels": { - "en": "An Audio Thing" - }, - "comments": { - "en": "An audio thing for testing audio things." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1" - } - ] - }, - { - "name": "MovieThing", - "super": "MovingImageRepresentation", - "labels": { - "en": "An Movie Thing" - }, - "comments": { - "en": "An movie thing for testing moving image things." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1" - } - ] - }, - { - "name": "DocumentThing", - "super": "DocumentRepresentation", - "labels": { - "en": "A Document Thing" - }, - "comments": { - "en": "A second things for testing different things." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1" - } - ] - }, - { - "name": "ZipThing", - "super": "DocumentRepresentation", - "labels": { - "en": "A ZIP Thing" - }, - "comments": { - "en": "A things for testing ZIPS." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1" - } - ] - }, - { - "name": "TextThing", - "super": "TextRepresentation", - "labels": { - "en": "A Text Thing" - }, - "comments": { - "en": "A things for testing TEXTS." - }, - "cardinalities": [ - { - "propname": ":hasText", - "gui_order": 1, - "cardinality": "1" - } - ] - }, - { - "name": "PartOfThing", - "super": "StillImageRepresentation", - "labels": { - "en": "A Thing having a partOf and seqnum property" - }, - "comments": { - "en": "A thing for testing partOf and seqnum properties." - }, - "cardinalities": [ - { - "propname": "isPartOf", - "gui_order": 1, - "cardinality": "1" - }, - { - "propname": "seqnum", - "gui_order": 2, - "cardinality": "1" - } - ] - } + "ontologies": [ + { + "name": "testonto", + "label": "Test ontology", + "properties": [ + { + "name": "hasText", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "en": "Text" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 255, + "size": 80 + } + }, + { + "name": "hasRichtext", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "en": "Text" + }, + "gui_element": "Richtext" + }, + { + "name": "hasUri", + "super": [ + "hasValue" + ], + "object": "UriValue", + "labels": { + "en": "URI" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 255, + "size": 80 + } + }, + { + "name": "hasBoolean", + "super": [ + "hasValue" + ], + "object": "BooleanValue", + "labels": { + "en": "Boolean value" + }, + "gui_element": "Checkbox" + }, + { + "name": "hasDate", + "super": [ + "hasValue" + ], + "object": "DateValue", + "labels": { + "en": "Date" + }, + "gui_element": "Date" + }, + { + "name": "hasInteger", + "super": [ + "hasValue" + ], + "object": "IntValue", + "labels": { + "en": "Integer" + }, + "gui_element": "Spinbox", + "gui_attributes": { + "max": -1.0, + "min": 0.0 + } + }, + { + "name": "hasDecimal", + "super": [ + "hasValue" + ], + "object": "DecimalValue", + "labels": { + "en": "Decimal number" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 255, + "size": 80 + } + }, + { + "name": "hasGeometry", + "super": [ + "hasValue" + ], + "object": "GeomValue", + "labels": { + "en": "Geometry" + }, + "gui_element": "Geometry" + }, + { + "name": "hasGeoname", + "super": [ + "hasValue" + ], + "object": "GeonameValue", + "labels": { + "en": "Geoname" + }, + "gui_element": "Geonames" + }, + { + "name": "hasInterval", + "super": [ + "hasValue" + ], + "object": "IntervalValue", + "labels": { + "en": "Time interval" + }, + "gui_element": "Interval" + }, + { + "name": "hasColor", + "super": [ + "hasValue" + ], + "object": "ColorValue", + "labels": { + "en": "Color" + }, + "gui_element": "Colorpicker" + }, + { + "name": "hasListItem", + "super": [ + "hasValue" + ], + "object": "ListValue", + "labels": { + "en": "List element" + }, + "gui_element": "List", + "gui_attributes": { + "hlist": "testlist" + } + }, + { + "name": "hasTestRegion", + "super": [ + "hasLinkTo" + ], + "object": "Region", + "labels": { + "en": "has region" + }, + "gui_element": "Searchbox" + }, + { + "name": "hasTestThing2", + "super": [ + "hasLinkTo" + ], + "object": ":TestThing2", + "labels": { + "en": "Another thing" + }, + "gui_element": "Searchbox" + } + ], + "resources": [ + { + "name": "TestThing", + "super": "Resource", + "labels": { + "en": "TestThing" + }, + "comments": { + "en": "A thing to test things", + "de": "Ein Ding um allerlei Dinge zu testen." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1-n" + }, + { + "propname": ":hasRichtext", + "gui_order": 2, + "cardinality": "0-n" + }, + { + "propname": ":hasUri", + "gui_order": 3, + "cardinality": "0-n" + }, + { + "propname": ":hasBoolean", + "gui_order": 4, + "cardinality": "1" + }, + { + "propname": ":hasDate", + "gui_order": 5, + "cardinality": "0-n" + }, + { + "propname": ":hasInteger", + "gui_order": 6, + "cardinality": "0-n" + }, + { + "propname": ":hasDecimal", + "gui_order": 7, + "cardinality": "0-n" + }, + { + "propname": ":hasGeometry", + "gui_order": 8, + "cardinality": "0-n" + }, + { + "propname": ":hasGeoname", + "gui_order": 9, + "cardinality": "0-n" + }, + { + "propname": ":hasInterval", + "gui_order": 10, + "cardinality": "0-n" + }, + { + "propname": ":hasColor", + "gui_order": 11, + "cardinality": "0-n" + }, + { + "propname": ":hasListItem", + "gui_order": 12, + "cardinality": "0-n" + }, + { + "propname": ":hasTestRegion", + "gui_order": 13, + "cardinality": "0-n" + }, + { + "propname": ":hasTestThing2", + "gui_order": 14, + "cardinality": "0-n" + } + ] + }, + { + "name": "TestThing2", + "super": "Resource", + "labels": { + "en": "Another Test Thing" + }, + "comments": { + "en": "Another thing for testing things." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1" + } + ] + }, + { + "name": "CompoundThing", + "super": "Resource", + "labels": { + "en": "A Compound Thing" + }, + "comments": { + "en": "A thing for testing compound things." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1" + } + ] + }, + { + "name": "ImageThing", + "super": "StillImageRepresentation", + "labels": { + "en": "An Image Thing" + }, + "comments": { + "en": "An image thing for testing image things." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1" + } + ] + }, + { + "name": "AudioThing", + "super": "AudioRepresentation", + "labels": { + "en": "An Audio Thing" + }, + "comments": { + "en": "An audio thing for testing audio things." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1" + } + ] + }, + { + "name": "MovieThing", + "super": "MovingImageRepresentation", + "labels": { + "en": "An Movie Thing" + }, + "comments": { + "en": "An movie thing for testing moving image things." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1" + } + ] + }, + { + "name": "DocumentThing", + "super": "DocumentRepresentation", + "labels": { + "en": "A Document Thing" + }, + "comments": { + "en": "A second things for testing different things." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1" + } + ] + }, + { + "name": "ZipThing", + "super": "DocumentRepresentation", + "labels": { + "en": "A ZIP Thing" + }, + "comments": { + "en": "A things for testing ZIPS." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1" + } + ] + }, + { + "name": "TextThing", + "super": "TextRepresentation", + "labels": { + "en": "A Text Thing" + }, + "comments": { + "en": "A things for testing TEXTS." + }, + "cardinalities": [ + { + "propname": ":hasText", + "gui_order": 1, + "cardinality": "1" + } + ] + }, + { + "name": "PartOfThing", + "super": "StillImageRepresentation", + "labels": { + "en": "A Thing having a partOf and seqnum property" + }, + "comments": { + "en": "A thing for testing partOf and seqnum properties." + }, + "cardinalities": [ + { + "propname": "isPartOf", + "gui_order": 1, + "cardinality": "1" + }, + { + "propname": "seqnum", + "gui_order": 2, + "cardinality": "1" + } + ] + } + ] + } ] - } - ] - } + } } From 987bf0668c536f0e7682b423bedd9f6467aaeb07 Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Thu, 18 Nov 2021 14:34:23 +0100 Subject: [PATCH 2/7] Remove comment instead of using empty comment in lists --- knora/dsplib/models/listnode.py | 11 +++++------ knora/dsplib/utils/onto_create_ontology.py | 2 +- test/e2e/test_tools.py | 2 +- testdata/test-onto.json | 13 ++----------- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/knora/dsplib/models/listnode.py b/knora/dsplib/models/listnode.py index 67f496929..f2a30b91c 100644 --- a/knora/dsplib/models/listnode.py +++ b/knora/dsplib/models/listnode.py @@ -184,7 +184,7 @@ def __init__(self, self._project = project.id if isinstance(project, Project) else str(project) if project is not None else None self._id = str(id) if id is not None else None self._label = LangString(label) - self._comments = LangString(comments) if comments is not None else LangString({"en": ""}) + self._comments = LangString(comments) if comments is not None else None self._name = str(name) if name is not None else None if parent and isinstance(parent, ListNode): self._parent = parent.id @@ -427,14 +427,13 @@ def toJsonObj(self, action: Actions, listIri: str = None) -> Any: if self._label.isEmpty(): raise BaseError("There must be a valid ListNode label!") tmp['labels'] = self._label.toJsonObj() - if not self._comments.isEmpty(): + if self._comments: tmp['comments'] = self._comments.toJsonObj() - else: - tmp['comments'] = [] if self._name is not None: tmp['name'] = self._name - if self._parent is not None: + if self._parent: tmp['parentNodeIri'] = self._parent + elif action == Actions.Update: if self.id is None: raise BaseError("There must be a node id given!") @@ -446,7 +445,7 @@ def toJsonObj(self, action: Actions, listIri: str = None) -> Any: tmp['labels'] = self._label.toJsonObj() if not self._comments.isEmpty() and 'comments' in self._changed: tmp['comments'] = self._comments.toJsonObj() - if self._name is not None and 'name' in self._changed: + if self._name and 'name' in self._changed: tmp['name'] = self._name # # temporary fix for bug in dsp-api which prevents labels from having diff --git a/knora/dsplib/utils/onto_create_ontology.py b/knora/dsplib/utils/onto_create_ontology.py index 458fa155b..1768cbc88 100644 --- a/knora/dsplib/utils/onto_create_ontology.py +++ b/knora/dsplib/utils/onto_create_ontology.py @@ -321,7 +321,7 @@ def create_ontology(input_file: str, print("Creating resource class failed:", err.message) exit(105) newresclasses[newresclass.id] = newresclass - if verbose is not None: + if verbose: print("New resource class:") newresclass.print() diff --git a/test/e2e/test_tools.py b/test/e2e/test_tools.py index cec745353..1697b1e16 100644 --- a/test/e2e/test_tools.py +++ b/test/e2e/test_tools.py @@ -37,7 +37,7 @@ def test_get(self) -> None: server=self.server, user=self.user, password='test', - verbose=True) + verbose=False) with open('_anything-onto.json') as f: onto_json_str = f.read() diff --git a/testdata/test-onto.json b/testdata/test-onto.json index 58600024e..46546523e 100644 --- a/testdata/test-onto.json +++ b/testdata/test-onto.json @@ -49,18 +49,12 @@ "name": "b1", "labels": { "en": "b1_label" - }, - "comments": { - "en": "no comment" } }, { "name": "b2", "labels": { "en": "b2_label" - }, - "comments": { - "en": "no comment" } } ] @@ -82,15 +76,12 @@ "en": "My list from Excel" }, "comments": { - "en": "no comment", + "en": "a comment", "de": "ein Kommentar", "fr": "un commentaire" }, "nodes": { - "folder": "testdata/lists", - "comments": { - "en": "" - } + "folder": "testdata/lists" } } ], From 4037a3267b5f77cc189ddc4a7fe44b26f0a11909 Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Thu, 18 Nov 2021 14:54:59 +0100 Subject: [PATCH 3/7] remove outdated fix --- knora/dsplib/models/listnode.py | 15 ++++----------- testdata/test-onto.json | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/knora/dsplib/models/listnode.py b/knora/dsplib/models/listnode.py index f2a30b91c..5c9f3f96f 100644 --- a/knora/dsplib/models/listnode.py +++ b/knora/dsplib/models/listnode.py @@ -416,10 +416,13 @@ def toJsonObj(self, action: Actions, listIri: str = None) -> Any: Creates a JSON-object from the ListNode instance that can be used to call Knora :param action: Action the object is used for (Action.CREATE or Action.UPDATE) + :param listIri: The IRI of the list node, only used for update action :return: JSON-object + """ tmp = {} + if action == Actions.Create: if self._project is None: raise BaseError("There must be a project id given!") @@ -447,17 +450,7 @@ def toJsonObj(self, action: Actions, listIri: str = None) -> Any: tmp['comments'] = self._comments.toJsonObj() if self._name and 'name' in self._changed: tmp['name'] = self._name - # - # temporary fix for bug in dsp-api which prevents labels from having - # escaped double-quotes in the string, e.g. "this \"label\" not works"! - # The double quotes will be replaced by single quotes... - # - if tmp.get('labels'): - print(tmp['labels']) - tmp['labels'] = [{'language': ele['language'], 'value': ele['value'].replace('"', "'")} for ele in - tmp['labels']] - # tmp['labels'] = {k: v.replace('"', "'") for k, v in tmp['labels'].items()} - # End of FIX + return tmp def create(self) -> 'ListNode': diff --git a/testdata/test-onto.json b/testdata/test-onto.json index 46546523e..97853c2a3 100644 --- a/testdata/test-onto.json +++ b/testdata/test-onto.json @@ -30,7 +30,7 @@ { "name": "a", "labels": { - "en": "a_label" + "en": "a_label with a\"post'rophes" }, "comments": { "en": "no comment" From 8c6a1ef4fea7a283ee3974b32a5b1155b732db7e Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Thu, 18 Nov 2021 15:23:42 +0100 Subject: [PATCH 4/7] Remove is not None in listnode.py --- knora/dsplib/models/listnode.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/knora/dsplib/models/listnode.py b/knora/dsplib/models/listnode.py index 5c9f3f96f..c2a14d49a 100644 --- a/knora/dsplib/models/listnode.py +++ b/knora/dsplib/models/listnode.py @@ -57,7 +57,7 @@ def list_creator(con: Connection, project: Project, parent_node: 'ListNode', nod name=node["name"], parent=parent_node ) - if node.get('nodes') is not None: + if node.get('nodes'): newnode.children = list_creator(con, project, newnode, node['nodes']) nodelist.append(newnode) return nodelist @@ -181,24 +181,24 @@ def __init__(self, super().__init__(con) - self._project = project.id if isinstance(project, Project) else str(project) if project is not None else None - self._id = str(id) if id is not None else None + self._project = project.id if isinstance(project, Project) else str(project) if project else None + self._id = str(id) if id else None self._label = LangString(label) - self._comments = LangString(comments) if comments is not None else None - self._name = str(name) if name is not None else None + self._comments = LangString(comments) if comments else None + self._name = str(name) if name else None if parent and isinstance(parent, ListNode): self._parent = parent.id else: self._parent = str(parent) if parent else None self._isRootNode = isRootNode - if children is not None: + if children: if isinstance(children, List) and len(children) > 0 and isinstance(children[0], ListNode): self._children = children else: raise BaseError('Children must be list of ListNodes!') else: self._children = None - if not isinstance(rootNodeIri, str) and rootNodeIri is not None: + if not isinstance(rootNodeIri, str) and rootNodeIri: raise BaseError('rootNodeIri must be a str!') self._rootNodeIri = rootNodeIri @@ -382,7 +382,7 @@ def fromJsonObj(cls, con: Connection, json_obj: Any) -> Any: project = json_obj.get('projectIri') label = LangString.fromJsonObj(json_obj.get('labels')) comments = LangString.fromJsonObj(json_obj.get('comments')) - if json_obj.get('name') is not None: + if json_obj.get('name'): name = json_obj['name'] else: name = id.rsplit('/', 1)[-1] @@ -391,7 +391,7 @@ def fromJsonObj(cls, con: Connection, json_obj: Any) -> Any: child_info = json_obj.get('children') children = None - if child_info is not None: + if child_info: children = ListNode.__getChildren(con=con, parent_iri=id, project_iri=project, @@ -432,7 +432,7 @@ def toJsonObj(self, action: Actions, listIri: str = None) -> Any: tmp['labels'] = self._label.toJsonObj() if self._comments: tmp['comments'] = self._comments.toJsonObj() - if self._name is not None: + if self._name: tmp['name'] = self._name if self._parent: tmp['parentNodeIri'] = self._parent @@ -462,7 +462,7 @@ def create(self) -> 'ListNode': jsonobj = self.toJsonObj(Actions.Create) jsondata = json.dumps(jsonobj, cls=SetEncoder) - if self._parent is not None: + if self._parent: result = self._con.post('/admin/lists/' + quote_plus(self._parent), jsondata) return ListNode.fromJsonObj(self._con, result['nodeinfo']) else: From ae5d7633fb25da25e70f0b9d6047eeabaaa4e950 Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Thu, 18 Nov 2021 16:16:29 +0100 Subject: [PATCH 5/7] Update developer documentation --- README.md | 6 +++--- docs/dsp-tools-information-for-developers.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a5780861b..2577147da 100644 --- a/README.md +++ b/README.md @@ -120,12 +120,12 @@ The documentation is a collection of [markdown](https://en.wikipedia.org/wiki/Ma After updates of the files, build and check the result with the following commands: ```bash -make build-docs -make serve-docs +make docs-build +make docs-serve ``` To update the changes to the official documentation pages run: ```bash -make publish-docs +make docs-publish ``` diff --git a/docs/dsp-tools-information-for-developers.md b/docs/dsp-tools-information-for-developers.md index 2201f3e88..4c586e88b 100644 --- a/docs/dsp-tools-information-for-developers.md +++ b/docs/dsp-tools-information-for-developers.md @@ -114,12 +114,12 @@ The documentation is a collection of [markdown](https://en.wikipedia.org/wiki/Ma After updates of the files, build and check the result with the following commands: ```bash -make build-docs -make serve-docs +make docs-build +make docs-serve ``` To update the changes to the official documentation pages run: ```bash -make publish-docs +make docs-publish ``` From 2cbb533818bb58ed875152166335790df8eab568 Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Thu, 18 Nov 2021 17:23:33 +0100 Subject: [PATCH 6/7] refactor SetEncoder --- knora/dsplib/models/BUILD.bazel | 10 +++++++--- knora/dsplib/models/project.py | 8 +------- knora/dsplib/models/propertyclass.py | 8 +------- knora/dsplib/models/resourceclass.py | 9 +-------- knora/dsplib/utils/BUILD.bazel | 6 ++++++ knora/dsplib/utils/set_encoder.py | 8 ++++++++ 6 files changed, 24 insertions(+), 25 deletions(-) create mode 100644 knora/dsplib/utils/set_encoder.py diff --git a/knora/dsplib/models/BUILD.bazel b/knora/dsplib/models/BUILD.bazel index 05a69de08..76a4bf004 100644 --- a/knora/dsplib/models/BUILD.bazel +++ b/knora/dsplib/models/BUILD.bazel @@ -54,12 +54,13 @@ py_library( visibility = ["//visibility:public"], srcs = ["listnode.py"], deps = [ + "//knora/dsplib/utils:set_encoder", ":connection", ":helpers", ":langstring", ":project", ], - imports = ["."], + imports = [".", ".."], ) py_library( @@ -101,6 +102,7 @@ py_library( visibility = ["//visibility:public"], srcs = ["project.py"], deps = [ + "//knora/dsplib/utils:set_encoder", ":helpers", ":langstring", ":connection", @@ -114,12 +116,13 @@ py_library( visibility = ["//visibility:public"], srcs = ["propertyclass.py"], deps = [ + "//knora/dsplib/utils:set_encoder", ":connection", ":helpers", ":langstring", ":listnode" ], - imports = ["."], + imports = [".", ".."], ) py_library( @@ -146,11 +149,12 @@ py_library( visibility = ["//visibility:public"], srcs = ["resourceclass.py"], deps = [ + "//knora/dsplib/utils:set_encoder", ":connection", ":helpers", ":langstring", ], - imports = ["."], + imports = [".", ".."], ) py_library( diff --git a/knora/dsplib/models/project.py b/knora/dsplib/models/project.py index decd5eac6..fd6539a69 100644 --- a/knora/dsplib/models/project.py +++ b/knora/dsplib/models/project.py @@ -5,19 +5,13 @@ from pystrict import strict +from knora.dsplib.utils.set_encoder import SetEncoder from .connection import Connection from .helpers import Actions, BaseError from .langstring import Languages, LangStringParam, LangString from .model import Model -class SetEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, set): - return list(obj) - return json.JSONEncoder.default(self, obj) - - """ This module implements the handling (CRUD) of Knora projects. diff --git a/knora/dsplib/models/propertyclass.py b/knora/dsplib/models/propertyclass.py index fdd6a229d..e2907097f 100644 --- a/knora/dsplib/models/propertyclass.py +++ b/knora/dsplib/models/propertyclass.py @@ -10,13 +10,7 @@ from .langstring import Languages, LangString from .listnode import ListNode from .model import Model - - -class SetEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, set): - return list(obj) - return json.JSONEncoder.default(self, obj) +from ..utils.set_encoder import SetEncoder @strict diff --git a/knora/dsplib/models/resourceclass.py b/knora/dsplib/models/resourceclass.py index 6e63a080c..58a407669 100644 --- a/knora/dsplib/models/resourceclass.py +++ b/knora/dsplib/models/resourceclass.py @@ -10,14 +10,7 @@ from .helpers import Actions, BaseError, Context, Cardinality, LastModificationDate from .langstring import Languages, LangString from .model import Model - - -class SetEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, set): - return list(obj) - return json.JSONEncoder.default(self, obj) - +from ..utils.set_encoder import SetEncoder """ This model implements the handling of resource classes. It contains two classes that work closely together: diff --git a/knora/dsplib/utils/BUILD.bazel b/knora/dsplib/utils/BUILD.bazel index e057a2abe..3cf5f706c 100644 --- a/knora/dsplib/utils/BUILD.bazel +++ b/knora/dsplib/utils/BUILD.bazel @@ -102,6 +102,12 @@ py_library( imports = [".", ".."], ) +py_library( + name = "set_encoder", + visibility = ["//visibility:public"], + srcs = ["set_encoder.py"] +) + py_library( name = "xml_upload", visibility = ["//visibility:public"], diff --git a/knora/dsplib/utils/set_encoder.py b/knora/dsplib/utils/set_encoder.py new file mode 100644 index 000000000..0a36cf5da --- /dev/null +++ b/knora/dsplib/utils/set_encoder.py @@ -0,0 +1,8 @@ +import json + + +class SetEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, set): + return list(obj) + return json.JSONEncoder.default(self, obj) From 46b6e060bd457d033652392b7c8e64893545b110 Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Thu, 18 Nov 2021 17:26:19 +0100 Subject: [PATCH 7/7] update docstrings and type issues --- knora/dsplib/models/listnode.py | 156 +++++++++++++++----------------- 1 file changed, 75 insertions(+), 81 deletions(-) diff --git a/knora/dsplib/models/listnode.py b/knora/dsplib/models/listnode.py index c2a14d49a..2ac977868 100644 --- a/knora/dsplib/models/listnode.py +++ b/knora/dsplib/models/listnode.py @@ -10,46 +10,40 @@ from .langstring import Languages, LangString from .model import Model from .project import Project - - -class SetEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, set): - return list(obj) - return json.JSONEncoder.default(self, obj) - +from ..utils.set_encoder import SetEncoder """ -This module implements the handling (CRUD) of Knora ListNodes and adds some function to read whole lists. +This module implements the handling (CRUD) of list nodes and adds some function to read whole lists. CREATE: - * Instantiate a new object of the class ListNode with all required parameters - * Call the ``create``-method on the instance + * Instantiate a new object of the class ListNode + * Call the ``create`` method on the instance READ: - * Instantiate a new object with ``id``(IRI of list(-node) given - * Call the ``read``-method on the instance - * Access the information that has been provided to the instance + * Instantiate a new object with ``id`` (IRI of listnode) + * Call the ``read`` method on the instance + * Access information about the instance UPDATE: * Only partially implemented. Only "label" and "comment" attributes may be changed. * You need an instance of an existing ListNode by reading an instance * Change the attributes by assigning the new values - * Call the ``update```method on the instance + * Call the ``update`` method on the instance DELETE - * NOT YET IMPLEMENTED BY Knora backend! - * Instantiate a new objects with ``id``(IRI of project) given, or use any instance that has the id set - * Call the ``delete``-method on the instance - -In addition there is a static methods ``getAllProjects`` which returns a list of all projects + * NOT YET IMPLEMENTED! """ -def list_creator(con: Connection, project: Project, parent_node: 'ListNode', nodes: List[dict]): - nodelist: List['ListNode'] = [] +def list_creator(con: Connection, + project: Project, + parent_node: 'ListNode', + nodes: List[dict]) -> List['ListNode']: + + nodelist: List[ListNode] = [] + for node in nodes: - newnode = ListNode( + new_node = ListNode( con=con, project=project, label=node["labels"], @@ -57,102 +51,105 @@ def list_creator(con: Connection, project: Project, parent_node: 'ListNode', nod name=node["name"], parent=parent_node ) + if node.get('nodes'): - newnode.children = list_creator(con, project, newnode, node['nodes']) - nodelist.append(newnode) + new_node.children = list_creator(con, project, new_node, node['nodes']) + + nodelist.append(new_node) + return nodelist @strict class ListNode(Model): """ - This class represents a list node or a while list from Knora + This class represents a list node Attributes ---------- con : Connection - A Connection instance to a Knora server (for some operation a login has to be performedwith valid credentials) + A Connection instance to a DSP server (for some operation a login has to be performed with valid credentials) id : str - IRI of the project [readonly, cannot be modified after creation of instance] + IRI of the list node [readonly, cannot be modified after creation of instance] project : str IRI of project. Only used for the creation of a new list (root node) [write]. label : LangString - A LangString instance with language depenedent labels. Setting this attributes overwites all entries + A LangString instance with language dependent labels. Setting this attribute overwrites all entries with the new ones. In order to add/remove a specific entry, use "addLabel" or "rmLabel". At least one label is required [read/write]. - comment : LangString - A LangString instance with language depenedent comments. Setting this attributes overwites all entries - with the new ones.In order to add/remove a specific entry, use "addComment" or "rmComment". + comments : LangString + A LangString instance with language dependent comments. Setting this attributes overwrites all entries + with the new ones. In order to add/remove a specific entry, use "addComment" or "rmComment". name : str - A unique name for the ListNode (unique regarding the whole list) [read/write]. + A unique name for the ListNode (unique inside this list) [read/write]. parent : IRI | ListNode Is required and allowed only for the CREATE operation. Otherwise use the "children" attribute [write]. isRootNode : bool - Is True if the ListNode is a root node of a list Cannot be set [read]. + Is True if the ListNode is the root node of a list. Cannot be set [read]. children : List[ListNode] - Contains a list of children nodes. This attribute is only avaliable for nodes that have been read by the - method "getAllNodes()"! [read] + Contains a list of child nodes. This attribute is only available for nodes that have been read by the + method "getAllNodes()" [read]. rootNodeIri : str - IRI of the root node. This attribute is only avaliable for nodes that have been read by the - method "getAllNodes()"! [read] + IRI of the root node. This attribute is only available for nodes that have been read by the + method "getAllNodes()" [read]. Methods ------- - create : Knora ListNode information object - Creates a new project and returns the information from the project as it is in Knora. Used to create new lists - or append new ListNodes to an existing list. If appending, the attribute "parent" must not be None! + create : ListNode information object + Creates a new list (node) and returns the information from the list (node). Use it to create new lists + or append new ListNodes to an existing list. - read : Knora ListNode information object - Read single list node + read : ListNode information object + Returns information about a single list node - update : Knora ListNode information object - Updates the changed attributes and returns the updated information from the ListNode as it is in Knora + update : ListNode information object + Updates the changed attributes and returns the updated information from the ListNode - delete : Knora result code - Deletes a ListNode and returns the result code [NOT YET IMPLEMENTED!] + delete : + NOT YET IMPLEMENTED getAllNodes : ListNode - Get all nodes of a list. The IRI of the root node must be supplied. + Get all nodes of a list. The IRI of the root node has to be supplied. getAllLists [static]: - returns all lists of a given project. + Returns all lists of a project. print : None - Prints the ListNode information to stdout (no recursion for children!) + Prints the ListNode information to stdout (not recursive) """ - _id: Union[str, None] - _project: Union[str, None] - _label: LangString - _comments: LangString - _name: Union[str, None] - _parent: Union[str, None] + _id: Optional[str] + _project: Optional[str] + _label: Optional[LangString] + _comments: Optional[LangString] + _name: Optional[str] + _parent: Optional[str] _isRootNode: bool - _children: Union[List['ListNode'], None] - _rootNodeIri: Union[str, None] + _children: Optional[List['ListNode']] + _rootNodeIri: Optional[str] def __init__(self, con: Connection, id: Optional[str] = None, project: Optional[Union[Project, str]] = None, label: LangString = None, - comments: LangString = None, + comments: Optional[LangString] = None, name: Optional[str] = None, parent: Optional[Union['ListNode', str]] = None, - isRootNode: Optional[bool] = None, + isRootNode: bool = False, children: Optional[List['ListNode']] = None, rootNodeIri: Optional[str] = None): """ @@ -165,18 +162,18 @@ def __init__(self, UPDATE: * Only "label", "comments" and "name" may be changed DELETE: - * Not yet implemented in the Knora-backend + * Not yet implemented :param con: A valid Connection instance with a user logged in that has the appropriate permissions :param id: IRI of the project [readonly, cannot be modified after creation of instance] :param project: IRI of project. Only used for the creation of a new list (root node) [write]. - :param label: A LangString instance with language depenedent labels. Setting this attributes overwites all entries with the new ones. In order to add/remove a specific entry, use "addLabel" or "rmLabel". At least one label is required [read/write]. - :param comments: A LangString instance with language depenedent comments. Setting this attributes overwites all entries with the new ones.In order to add/remove a specific entry, use "addComment" or "rmComment". + :param label: A LangString instance with language dependent labels. Setting this attributes overwites all entries with the new ones. In order to add/remove a specific entry, use "addLabel" or "rmLabel". At least one label is required [read/write]. + :param comments: A LangString instance with language dependent comments. Setting this attributes overwites all entries with the new ones.In order to add/remove a specific entry, use "addComment" or "rmComment". :param name: A unique name for the ListNode (unique regarding the whole list) [read/write]. :param parent: Is required and allowed only for the CREATE operation. Otherwise use the "children" attribute [write]. :param isRootNode: Is True if the ListNode is a root node of a list Cannot be set [read]. - :param children: Contains a list of children nodes. This attribute is only avaliable for nodes that have been read by the method "getAllNodes()"! [read] - :param rootNodeIri: IRI of the root node. This attribute is only avaliable for nodes that have been read by the method "getAllNodes()"! [read] + :param children: Contains a list of children nodes. This attribute is only available for nodes that have been read by the method "getAllNodes()"! [read] + :param rootNodeIri: IRI of the root node. This attribute is only available for nodes that have been read by the method "getAllNodes()"! [read] """ super().__init__(con) @@ -195,16 +192,13 @@ def __init__(self, if isinstance(children, List) and len(children) > 0 and isinstance(children[0], ListNode): self._children = children else: - raise BaseError('Children must be list of ListNodes!') + raise BaseError('ERROR Children must be list of ListNodes!') else: self._children = None if not isinstance(rootNodeIri, str) and rootNodeIri: - raise BaseError('rootNodeIri must be a str!') + raise BaseError('ERROR rootNodeIri must be of type string') self._rootNodeIri = rootNodeIri - # - # Here follows a list of getters/setters - # @property def id(self) -> Optional[str]: return self._id @@ -369,10 +363,10 @@ def fromJsonObj(cls, con: Connection, json_obj: Any) -> Any: """ Internal method! Should not be used directly! - This method is used to create a ListNode instance from the JSON data returned by Knora + This method is used to create a ListNode instance from the JSON data returned by DSP :param con: Connection instance - :param json_obj: JSON data returned by Knora as python3 object + :param json_obj: JSON data returned by DSP as python3 object :return: ListNode instance """ @@ -413,7 +407,7 @@ def toJsonObj(self, action: Actions, listIri: str = None) -> Any: """ Internal method! Should not be used directly! - Creates a JSON-object from the ListNode instance that can be used to call Knora + Creates a JSON-object from the ListNode instance that can be used to call DSP-API :param action: Action the object is used for (Action.CREATE or Action.UPDATE) :param listIri: The IRI of the list node, only used for update action @@ -455,9 +449,9 @@ def toJsonObj(self, action: Actions, listIri: str = None) -> Any: def create(self) -> 'ListNode': """ - Create a new List in Knora + Create a new List - :return: JSON-object from Knora + :return: JSON-object from DSP-API """ jsonobj = self.toJsonObj(Actions.Create) @@ -471,9 +465,9 @@ def create(self) -> 'ListNode': def read(self) -> Any: """ - Read a project from Knora + Read a project from DSP-API - :return: JSON-object from Knora + :return: JSON-object from DSP-API """ result = self._con.get('/admin/lists/nodes/' + quote_plus(self._id)) @@ -486,9 +480,9 @@ def read(self) -> Any: def update(self) -> Union[Any, None]: """ - Udate the ListNode info in Knora with the modified data in this ListNode instance + Update the ListNode info in DSP with the modified data in this ListNode instance - :return: JSON-object from Knora refecting the update + :return: JSON-object from DSP-API reflecting the update """ jsonobj = self.toJsonObj(Actions.Update, self.id) @@ -504,9 +498,9 @@ def delete(self) -> None: """ Delete the given ListNode - :return: Knora response + :return: DSP-API response """ - raise BaseError("NOT YET IMPLEMENTED BY KNORA BACKEND!") + raise BaseError("NOT YET IMPLEMENTED") result = self._con.delete('/admin/lists/' + quote_plus(self._id)) return result # return Project.fromJsonObj(self.con, result['project'])