From 991d4245fbd23a0b88b706b7e1e89859a5692d7a Mon Sep 17 00:00:00 2001 From: Johannes Nussbaum <39048939+jnussbaum@users.noreply.github.com> Date: Tue, 16 Aug 2022 09:39:00 +0200 Subject: [PATCH] feat: add isSequenceOf (DEV-746) (#214) --- Pipfile | 2 +- docs/dsp-tools-create-ontologies.md | 230 ++++++++++++++++++--- knora/dsplib/models/resource.py | 17 +- knora/dsplib/schemas/ontology.json | 8 +- knora/dsplib/schemas/properties-only.json | 4 +- knora/dsplib/schemas/resources-only.json | 4 +- test/e2e/test_tools.py | 2 +- test/unittests/test_create_ontology.py | 9 +- test/unittests/test_excel_to_properties.py | 2 +- testdata/Properties.xlsx | Bin 13681 -> 13688 bytes testdata/test-data-systematic.xml | 58 +++++- testdata/test-project-systematic.json | 113 +++++++++- 12 files changed, 382 insertions(+), 67 deletions(-) diff --git a/Pipfile b/Pipfile index 5a89bc3f4..6e5842566 100644 --- a/Pipfile +++ b/Pipfile @@ -32,4 +32,4 @@ pytest = "*" types-requests = "*" [requires] -python_version = "3.9" +python_version = "3" diff --git a/docs/dsp-tools-create-ontologies.md b/docs/dsp-tools-create-ontologies.md index f6c722488..a59aca91c 100644 --- a/docs/dsp-tools-create-ontologies.md +++ b/docs/dsp-tools-create-ontologies.md @@ -46,10 +46,14 @@ Example of an `ontologies` object: ``` + + ## Ontologies Object in Detail The following properties can occur within each object in `ontologies`. + + ### Name (required) @@ -59,6 +63,8 @@ The following properties can occur within each object in `ontologies`. The ontology's (short) name should be in the form of a [xsd:NCNAME](https://www.w3.org/TR/xmlschema11-2/#NCName). This means a string without blanks or special characters but `-` and `_` are allowed (although not as first character). + + ### Label (required) @@ -67,6 +73,8 @@ means a string without blanks or special characters but `-` and `_` are allowed A string that provides the full name of the ontology. + + ### Properties (required) @@ -92,6 +100,8 @@ The following fields are optional: A detailed description of `properties` can be found [below](#properties-object-in-detail). + + ### Resources (required) @@ -117,8 +127,12 @@ The following field is optional: A detailed description of `resources` can be found [below](#properties-object-in-detail). + + ## Properties Object in Detail + + ### Name (required) @@ -131,6 +145,8 @@ but `-` and `_` are allowed (although not as first character). By convention, property names start with a lower case letter. + + ### Labels (required) @@ -140,6 +156,8 @@ By convention, property names start with a lower case letter. Collection of `labels` for the property as strings with language tag (currently "en", "de", "fr", "it", and "rm" are supported). + + ### Comments (optional) @@ -148,6 +166,8 @@ and "rm" are supported). Comments with language tags. Currently, "de", "en", "fr", "it", and "rm" are supported. The `comments` element is optional. + + ### Super (required) @@ -163,7 +183,7 @@ super-property: The syntax how to refer to these different groups of properties is described [here](#referencing-ontologies). -The following base properties can be used as super-property: +The following DSP base properties are available: - `hasValue`: This is the most general case, to be used in all cases when your property is none of the special cases below. - `hasLinkTo`: a link to another resource @@ -173,7 +193,10 @@ The following base properties can be used as super-property: pages in a book. A resource that has a property derived from `seqnum` must also have a property derived from `isPartOf`. - `hasColor`: Defines a color value. - `hasComment`: Defines a standard comment. - +- `isSequenceOf`: A special variant of `hasLinkTo`. It says that an instance of the given resource class is a section + of an audio/video resource. +- `hasSequenceBounds`: This base property is used together with `isSequenceOf`. It denotes a time interval of an audio/ + video resource. Example of a `properties` object: @@ -220,6 +243,8 @@ Example of a `properties` object: } ``` + + ### Subject (optional) @@ -229,6 +254,8 @@ Example of a `properties` object: The `subject` defines the resource class the property can be used on. It has to be provided as prefixed name of the resource class (see [below](#referencing-ontologies) on how prefixed names are used). + + ### Object / gui_element / gui_attributes These three are related as follows: @@ -237,7 +264,7 @@ These three are related as follows: - `gui_element` (required) depends on the value of `object`. - `gui_attributes` (optional) depends on the value of `gui_element`. -The following data types are allowed: +The following `object`s are available, and will be discussed below, in this order: - `BooleanValue` - `ColorValue` @@ -250,7 +277,9 @@ The following data types are allowed: - `TextValue` - `TimeValue` - `UriValue` -- in case of a link property: any resource class +- `Representation` +- in case of the supers `hasLinkTo` or `isPartOf`: any resource class + #### BooleanValue @@ -279,6 +308,7 @@ Represents a Boolean ("true" or "false). } ``` + #### ColorValue `"object": "ColorValue"` @@ -307,6 +337,7 @@ A string representation of the color in the hexadecimal form e.g. "#ff8000". } ``` + #### DateValue `object": "DateValue"` @@ -347,6 +378,7 @@ which means anytime in between 1925 and the 22nd March 1927. } ``` + #### DecimalValue `"object": "DecimalValue"` @@ -385,6 +417,7 @@ A number with decimal point. } ``` + #### GeonameValue Represents a location ID in geonames.org. DSP uses identifiers provided by @@ -412,11 +445,17 @@ Represents a location ID in geonames.org. DSP uses identifiers provided by } ``` + #### IntervalValue `"object": "IntervalValue"` -Represents a time-interval +This `object` belongs to the DSP base property `super: hasSequenceBounds`. It represents a time interval of an +audio or video. It can be used together with an `isSequenceOf` property on a resource that represents the sequence. The +`isSequenceOf` would then point to the audio/video resource, and the `hasSequenceBounds` would be the time interval of +the sequence. + +See the [`isSequenceOf` property](#issequenceof-property) for more detailed explanations. *gui-elements / gui_attributes*: @@ -432,18 +471,20 @@ Represents a time-interval ```json { - "name": "hasInterval", + "name": "hasBounds", "super": [ - "hasValue" + "hasSequenceBounds" ], + "subject": ":AudioSequence", "object": "IntervalValue", "labels": { - "en": "Time interval" + "en": "Interval defining the start and end point of a sequence of an audio or video file" }, "gui_element": "Interval" } ``` + #### IntValue `"object": "IntValue"` @@ -483,6 +524,7 @@ Represents an integer value. } ``` + #### ListValue `"object": "ListValue"` @@ -518,6 +560,7 @@ Represents a node of a (possibly hierarchical) list } ``` + #### TextValue `"object": "TextValue"` @@ -559,6 +602,7 @@ Represents a text that may contain standoff markup. } ``` + #### TimeValue `"object": "TimeValue"` @@ -586,6 +630,7 @@ A time value represents a precise moment in time in the Gregorian calendar. Sinc } ``` + #### UriValue `"object": "UriValue"` @@ -620,6 +665,7 @@ Represents an URI } ``` + #### Representation `"object": "Representation"` @@ -650,6 +696,7 @@ A property pointing to a `knora-base:Representation`. Has to be used in combinat } ``` + #### hasLinkTo Property `"object": ""` @@ -692,11 +739,16 @@ When defining a link property, its "super" element has to be `hasLinkTo` or deri } ``` + #### isPartOf Property A special case of linked resources are resources in a part-whole relation, i.e. resources that are composed of other resources. A `isPartOf` property has to be added to the resource that is part of another resource. In case of resources that are of type `StillImageRepresentation`, an additional property derived from `seqnum` with object `IntValue` -is required. When defined, a client is able to leaf through the parts of a compound object, p.ex. to leaf through pages of a book. +is required. When defined, the user is able to leaf through the parts of a compound object, p.ex. to leaf through pages +of a book. + +The DSP base properties `isPartOf` and `seqnum` can be used to derive a custom property from them, or they can be used +directly as cardinalities in a resource. The example belows shows both possibilities. *gui-elements/gui_attributes*: @@ -707,31 +759,58 @@ is required. When defined, a client is able to leaf through the parts of a compo *Example:* ```json -{ - "name": "partOfBook", - "super": [ - "isPartOf" - ], - "object": ":Book", - "labels": { - "en": "is part of" - }, - "gui_element": "Searchbox" -}, -{ - "name": "hasPageNumber", - "super": [ - "seqnum" - ], - "object": "IntValue", - "labels": { - "en": "has page number" - }, - "gui_element": "Spinbox" -} +"properties": [ + { + "name": "partOfBook", + "super": ["isPartOf"], + "object": ":Book", + "labels": {"en": "is part of"}, + "gui_element": "Searchbox" + }, + { + "name": "hasPageNumber", + "super": ["seqnum"], + "object": "IntValue", + "labels": {"en": "has page number"}, + "gui_element": "Spinbox" + } +], +"resources": [ + { + "name": "Page", + "labels": {"en": "Page using properties derived from 'isPartOf' and 'seqnum'"}, + "super": "StillImageRepresentation", + "cardinalities": [ + { + "propname": ":partOfBook", + "cardinality": "1" + }, + { + "propname": ":hasPageNumber", + "cardinality": "1" + } + ] + }, + { + "name": "MinimalisticPage", + "labels": {"en": "Page using 'isPartOf' and 'seqnum' directly"}, + "super": "StillImageRepresentation", + "cardinalities": [ + { + "propname": "isPartOf", + "cardinality": "1" + }, + { + "propname": "seqnum", + "cardinality": "1" + } + ] + } +] ``` -#### hasComment Property + +#### hasComment property `"object": "TextValue"` @@ -755,6 +834,82 @@ This property is actually very similar to a simple text field. } ``` + +#### isSequenceOf property + +`"object": (AudioRepresentation/MovingImageRepresentation or a subclass of one of them)` + +This property can be used, together with a `hasSequenceBounds` property, on a resource representing a sequence of an +audio/video resource. The `isSequenceOf` would then point to the audio/video resource, and the `hasSequenceBounds` +would be the time interval of the sequence. + +The DSP base properties `isSequenceOf` and `hasSequenceBounds` can be used to derive a custom property from them, or +they can be used directly as cardinalities in a resource. The example below shows both possibilities. + +*gui-elements/gui_attributes*: + +- `Searchbox`: The only GUI element for _isSequenceOf_. Allows searching resources by entering the target resource name. + - _gui_attributes_: + - `numprops=integer` (optional): Number of search results to be displayed + +*Example:* + +```json +"properties": [ + { + "name": "sequenceOfAudio", + "super": ["isSequenceOf"], + "subject": ":AudioSequence", + "object": ":Audio", + "labels": {"en": "is sequence of"}, + "gui_element": "Searchbox" + }, + { + "name": "hasBounds", + "super": ["hasSequenceBounds"], + "subject": ":AudioSequence", + "object": "IntervalValue", + "labels": {"en": "Start and end point of a sequence of an audio/video"}, + "gui_element": "Interval" + } +], +"resources": [ + { + "name": "AudioSequence", + "labels": {"en": "Sequence of audio using properties derived from 'isSequenceOf' and 'hasSequenceBounds'"}, + "super": "Resource", + "cardinalities": [ + { + "propname": ":sequenceOfAudio", + "cardinality": "1" + }, + { + "propname": ":hasBounds", + "cardinality": "1" + } + ] + }, + { + "name": "MinimalisticAudioSequence", + "labels": {"en": "Sequence of audio using 'isSequenceOf' and 'hasSequenceBounds' directly"}, + "super": "Resource", + "cardinalities": [ + { + "propname": "isSequenceOf", + "cardinality": "1" + }, + { + "propname": "hasSequenceBounds", + "cardinality": "1" + } + ] + } +] +``` + + + + ## Resources Object in Detail ### Name @@ -769,6 +924,8 @@ but `-` and `_` are allowed (although not as first character). By convention, resource names start with a upper case letter. + + ### Labels (required) @@ -778,6 +935,8 @@ By convention, resource names start with a upper case letter. Collection of `labels` for the resource as strings with language tag (currently "en", "de", "fr", "it", and "rm" are supported). + + ### Super (required) @@ -806,6 +965,7 @@ used in all cases when your resource is none of the special cases below. - `TextRepresentation`: A resource representing a text + ### Cardinalities (required) @@ -826,6 +986,8 @@ resource can have as well as how many times the relation is established. - `"1-n"`: At least one value must be present, but multiple values may be present. - `"0-n"`: The value may be omitted, but may also occur multiple times. + + ### Comments (optional) @@ -877,6 +1039,8 @@ Example for a resource definition: ``` + + ## Referencing Ontologies For several fields, such as `super` in both `resources` and `properties` or `propname` in `cardinalities` @@ -897,6 +1061,8 @@ it is necessary to reference entities that are defined elsewhere. The following `first-onto` to the prefixes. + + ## DSP base resources / base properties to be used directly in the XML file There is a number of DSP base resources that must not be subclassed in a project ontology. They are directly available in the XML data file: diff --git a/knora/dsplib/models/resource.py b/knora/dsplib/models/resource.py index 1678ed34a..8e3b79cd1 100644 --- a/knora/dsplib/models/resource.py +++ b/knora/dsplib/models/resource.py @@ -64,7 +64,9 @@ class ResourceInstance(Model): "knora-api:isPartOf", "knora-api:isRegionOf", "knora-api:isAnnotationOf", - "knora-api:seqnum" + "knora-api:seqnum", + "knora-api:isSequenceOf", + "knora-api:hasSequenceBounds" } _iri: Optional[str] _ark: Optional[str] @@ -407,12 +409,8 @@ def get_resclass_type(self, prefixedresclass: str) -> Type: 'knora-api:LinkValue': LinkValue, } for propname, has_property in resclass.has_properties.items(): - if any([ - propname == "knora-api:isAnnotationOf", - propname == "knora-api:isRegionOf", - propname == "knora-api:isPartOf", - propname == "knora-api:hasLinkTo" - ]): + if propname in ["knora-api:isAnnotationOf", "knora-api:isRegionOf", "knora-api:isPartOf", + "knora-api:hasLinkTo", "knora-api:isSequenceOf"]: valtype = LinkValue props[propname] = Propinfo(valtype=valtype, cardinality=has_property.cardinality, @@ -438,6 +436,11 @@ def get_resclass_type(self, prefixedresclass: str) -> Type: props[propname] = Propinfo(valtype=valtype, cardinality=has_property.cardinality, gui_order=has_property.gui_order) + elif propname == "knora-api:hasSequenceBounds": + valtype = IntervalValue + props[propname] = Propinfo(valtype=valtype, + cardinality=has_property.cardinality, + gui_order=has_property.gui_order) elif has_property.ptype == HasProperty.Ptype.other: valtype = switcher.get(self._properties[propname].object) if valtype == LinkValue: diff --git a/knora/dsplib/schemas/ontology.json b/knora/dsplib/schemas/ontology.json index ea4558041..71e3de06c 100644 --- a/knora/dsplib/schemas/ontology.json +++ b/knora/dsplib/schemas/ontology.json @@ -264,6 +264,8 @@ "hasColor", "hasComment", "isPartOf", + "isSequenceOf", + "hasSequenceBounds", "hasRepresentation", "seqnum" ] @@ -766,7 +768,7 @@ "oneOf": [ { "enum": [ - "hasValue" + "hasSequenceBounds" ] }, { @@ -1054,7 +1056,9 @@ { "enum": [ "isPartOf", - "seqnum" + "seqnum", + "isSequenceOf", + "hasSequenceBounds" ] }, { diff --git a/knora/dsplib/schemas/properties-only.json b/knora/dsplib/schemas/properties-only.json index 72cb5e52d..6720d6758 100644 --- a/knora/dsplib/schemas/properties-only.json +++ b/knora/dsplib/schemas/properties-only.json @@ -47,6 +47,8 @@ "hasColor", "hasComment", "isPartOf", + "isSequenceOf", + "hasSequenceBounds", "hasRepresentation", "seqnum" ] @@ -548,7 +550,7 @@ "oneOf": [ { "enum": [ - "hasValue" + "hasSequenceBounds" ] }, { diff --git a/knora/dsplib/schemas/resources-only.json b/knora/dsplib/schemas/resources-only.json index c87cf448d..5b2f0531d 100644 --- a/knora/dsplib/schemas/resources-only.json +++ b/knora/dsplib/schemas/resources-only.json @@ -98,7 +98,9 @@ { "enum": [ "isPartOf", - "seqnum" + "seqnum", + "isSequenceOf", + "hasSequenceBounds" ] }, { diff --git a/test/e2e/test_tools.py b/test/e2e/test_tools.py index ccc8f264d..1f851a8c6 100644 --- a/test/e2e/test_tools.py +++ b/test/e2e/test_tools.py @@ -191,7 +191,7 @@ def test_xml_upload(self) -> None: password=self.password, imgdir=self.imgdir, sipi=self.sipi, - verbose=True, + verbose=False, validate_only=False, incremental=False) self.assertTrue(result) diff --git a/test/unittests/test_create_ontology.py b/test/unittests/test_create_ontology.py index 20937c0ed..af3b97c15 100644 --- a/test/unittests/test_create_ontology.py +++ b/test/unittests/test_create_ontology.py @@ -25,12 +25,11 @@ def test_sort_resources(self) -> None: unsorted_resources: list[dict[str, Any]] = self.ontology['resources'] sorted_resources = _sort_resources(unsorted_resources, onto_name) - unsorted_resources = sorted(unsorted_resources, key=lambda a: a['name']) - sorted_resources = sorted(sorted_resources, key=lambda a: a['name']) + unsorted_resources = sorted(unsorted_resources, key=lambda a: str(a['name'])) + sorted_resources = sorted(sorted_resources, key=lambda a: str(a['name'])) self.assertListEqual(unsorted_resources, sorted_resources) - def test_sort_prop_classes(self) -> None: """ The 'properties' section of an onto is a list of dictionaries. The safest way to test @@ -41,8 +40,8 @@ def test_sort_prop_classes(self) -> None: unsorted_props: list[dict[str, Any]] = self.ontology['resources'] sorted_props = _sort_prop_classes(unsorted_props, onto_name) - unsorted_props = sorted(unsorted_props, key=lambda a: a['name']) - sorted_props = sorted(sorted_props, key=lambda a: a['name']) + unsorted_props = sorted(unsorted_props, key=lambda a: str(a['name'])) + sorted_props = sorted(sorted_props, key=lambda a: str(a['name'])) self.assertListEqual(unsorted_props, sorted_props) diff --git a/test/unittests/test_excel_to_properties.py b/test/unittests/test_excel_to_properties.py index 0bc548a03..9a3a6727e 100644 --- a/test/unittests/test_excel_to_properties.py +++ b/test/unittests/test_excel_to_properties.py @@ -27,7 +27,7 @@ def test_excel2json(self) -> None: excel_supers = [["hasLinkTo"], ["hasValue", "dcterms:creator"], ["hasValue"], ["hasValue"], ["hasLinkTo"], ["hasValue"], ["hasValue"], ["hasValue"], ["hasRepresentation"], ["hasValue", "dcterms:description"], ["hasValue"],["hasValue"], ["hasColor"], ["hasValue"], - ["hasValue"], ["hasValue"], ["hasValue"], ["hasValue"], ["isPartOf"]] + ["hasValue"], ["hasSequenceBounds"], ["hasValue"], ["hasValue"], ["isPartOf"]] excel_objects = [":GenericAnthroponym", "TextValue", "ListValue", "ListValue", ":Titles", "ListValue", "IntValue", "DateValue", "Representation", "TextValue", "DateValue", "UriValue", "ColorValue", "DecimalValue", "TimeValue", "IntervalValue", "BooleanValue", "GeonameValue", diff --git a/testdata/Properties.xlsx b/testdata/Properties.xlsx index 31ea63db0907dc60bac83abf23a145a06f50ba3d..9d5e8d9181b2034940e473f6430b1fc595e33deb 100644 GIT binary patch delta 4486 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 WzibKmSYDk(8!ZF9N2x0Nul0W)30+G7 delta 4489 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 bDHc#m1qJ{B000aC3;-qr001>I00000a|wQW diff --git a/testdata/test-data-systematic.xml b/testdata/test-data-systematic.xml index e7340642b..10682cf6f 100644 --- a/testdata/test-data-systematic.xml +++ b/testdata/test-data-systematic.xml @@ -102,9 +102,6 @@ 5416656 - - 12.5:14.2 - #00ff00 @@ -198,9 +195,6 @@ 5416656 - - 12.5:14.2 - #00ff00 @@ -208,7 +202,7 @@ test_thing_0 - + + + + + Sequence of a video + + + video_thing_1 + + + 2.0:10.0 + + + + + + + Sequence of an Audio Resource + + + audio_thing_1 + + + 8.0:12.0 + + + + + + + Sequence of a Video Resource + + + video_thing_1 + + + 8.0:12.0 + + + + + + Sequence of an Audio Resource + + + audio_thing_1 + + + 8.0:12.0 + + diff --git a/testdata/test-project-systematic.json b/testdata/test-project-systematic.json index 69e0dafc8..bf6f6155c 100644 --- a/testdata/test-project-systematic.json +++ b/testdata/test-project-systematic.json @@ -253,7 +253,7 @@ "size": 80 } }, - { + { "name": "hasRichtext", "super": [ "hasValue" @@ -343,13 +343,14 @@ "gui_element": "Geonames" }, { - "name": "hasInterval", + "name": "hasBounds", "super": [ - "hasValue" + "hasSequenceBounds" ], "object": "IntervalValue", "labels": { - "en": "Time interval" + "de": "Intervall, das den Start- und Endpunkt der Sequenz definiert", + "en": "Interval defining the start and end point of the sequence" }, "gui_element": "Interval" }, @@ -443,6 +444,32 @@ "en": "hasMovingImageRepresentation" }, "gui_element": "Searchbox" + }, + { + "name": "sequenceOfVideo", + "super": [ + "isSequenceOf" + ], + "subject": ":VideoSequence", + "object": ":MovieThing", + "labels": { + "de": "ist Sequenz von", + "en": "is sequence of" + }, + "gui_element": "Searchbox" + }, + { + "name": "sequenceOfAudio", + "super": [ + "isSequenceOf" + ], + "subject": ":AudioSequence", + "object": ":AudioThing", + "labels": { + "de": "ist Sequenz von", + "en": "is sequence of" + }, + "gui_element": "Searchbox" } ], "resources": [ @@ -498,11 +525,6 @@ "gui_order": 9, "cardinality": "0-n" }, - { - "propname": ":hasInterval", - "gui_order": 10, - "cardinality": "0-n" - }, { "propname": ":hasColor", "gui_order": 11, @@ -729,6 +751,79 @@ "cardinality": "1" } ] + }, + { + "name": "VideoSequence", + "labels": { + "de": "Sequenz einer Video-Ressource", + "en": "Sequence of a video resource" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasText", + "cardinality": "0-1" + }, + { + "propname": ":sequenceOfVideo", + "cardinality": "1" + }, + { + "propname": ":hasBounds", + "cardinality": "1", + "gui_order": 1 + } + ] + }, + { + "name": "AudioSequence", + "labels": { + "de": "Sequenz einer Audio-Ressource", + "en": "Sequence of an audio resource" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasText", + "cardinality": "0-1" + }, + { + "propname": ":sequenceOfAudio", + "cardinality": "1" + }, + { + "propname": ":hasBounds", + "cardinality": "1", + "gui_order": 1 + } + ] + }, + { + "name": "MinimalisticSequence", + "labels": { + "de": "Minimalistische Sequenz einer Audio-/Video-Ressource", + "en": "Minimalistic sequence of an audio/video resource" + }, + "comments": { + "de": "Sequenz einer Audio-/Video-Ressource, welche die base properties 'isSequenceOf' und 'hasSequenceBounds' direkt verwendet", + "en": "Sequence of an audio/video resource that uses the base properties 'isSequenceOf' and 'hasSequenceBounds' directly" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasText", + "cardinality": "0-1" + }, + { + "propname": "isSequenceOf", + "cardinality": "1" + }, + { + "propname": "hasSequenceBounds", + "cardinality": "1", + "gui_order": 1 + } + ] } ] },