Skip to content

Commit

Permalink
feat(validate-data): knora-api:LinkObj (DEV-4463) (#1342)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nora-Olivia-Ammann authored Dec 17, 2024
1 parent f5d66a5 commit f9dee0f
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 23 deletions.
2 changes: 0 additions & 2 deletions src/dsp_tools/commands/validate_data/deserialise_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ def _deserialise_all_resources(root: etree._Element) -> DataDeserialised:
res_type = res.attrib["restype"]
if res_type == REGION_RESOURCE:
dsp_type = REGION_RESOURCE
elif res_type == LINKOBJ_RESOURCE:
dsp_type = LINKOBJ_RESOURCE
elif res_type == VIDEO_SEGMENT_RESOURCE:
dsp_type = VIDEO_SEGMENT_RESOURCE
elif res_type == AUDIO_SEGMENT_RESOURCE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def _construct_1_cardinality(onto_graph: Graph) -> Graph:
?propRestriction knora-api:isEditable true .
FILTER NOT EXISTS { ?propRestriction knora-api:isLinkValueProperty true }
FILTER (?class NOT IN (
knora-api:Region, knora-api:AudioSegment, knora-api:VideoSegment, knora-api:LinkObj)
knora-api:Region, knora-api:AudioSegment, knora-api:VideoSegment)
)
FILTER (?propRestriction NOT IN (
knora-api:hasArchiveFileValue, knora-api:hasAudioFileValue, knora-api:hasDocumentFileValue,
Expand Down Expand Up @@ -132,7 +132,7 @@ def _construct_0_1_cardinality(onto_graph: Graph) -> Graph:
?propRestriction knora-api:isEditable true .
FILTER NOT EXISTS { ?propRestriction knora-api:isLinkValueProperty true }
FILTER (?class NOT IN (
knora-api:Region, knora-api:AudioSegment, knora-api:VideoSegment, knora-api:LinkObj)
knora-api:Region, knora-api:AudioSegment, knora-api:VideoSegment)
)
}
"""
Expand Down Expand Up @@ -170,7 +170,7 @@ def _construct_1_n_cardinality(onto_graph: Graph) -> Graph:
?propRestriction knora-api:isEditable true .
FILTER NOT EXISTS { ?propRestriction knora-api:isLinkValueProperty true }
FILTER (?class NOT IN (
knora-api:Region, knora-api:AudioSegment, knora-api:VideoSegment, knora-api:LinkObj)
knora-api:Region, knora-api:AudioSegment, knora-api:VideoSegment)
)
}
"""
Expand Down Expand Up @@ -205,7 +205,7 @@ def _construct_0_n_cardinality(onto_graph: Graph) -> Graph:
?propRestriction knora-api:isEditable true .
FILTER NOT EXISTS { ?propRestriction knora-api:isLinkValueProperty true }
FILTER (?class NOT IN (
knora-api:Region, knora-api:AudioSegment, knora-api:VideoSegment, knora-api:LinkObj)
knora-api:Region, knora-api:AudioSegment, knora-api:VideoSegment)
)
}
"""
Expand Down
1 change: 1 addition & 0 deletions src/dsp_tools/commands/validate_data/validate_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def _inform_about_experimental_feature() -> None:
"Content of the values",
"Missing files",
"If the file type matches the ontology",
"DSP in-built resources: link (LinkObj)",
]
print(BOLD_CYAN + LIST_SEPARATOR.join(what_is_validated) + RESET_TO_DEFAULT)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix dash: <http://datashapes.org/dash#> .
@prefix knora-api: <http://api.knora.org/ontology/knora-api/v2#> .

@prefix api-shapes: <http://api.knora.org/ontology/knora-api/shapes/v2#> .
Expand Down Expand Up @@ -37,7 +38,7 @@ api-shapes:ArchiveFileValue_ClassShape
sh:minCount 1 ;
sh:maxCount 1 ;
sh:pattern ".+\\.(zip|tar|gz|z|tgz|gzip|7z)$" ;
sh:flags "i" ; # case insensitive
sh:flags "i" ; # case insensitive
sh:severity sh:Violation ;
sh:message """
An ArchiveRepresentation requires a file with one of the following extensions
Expand Down Expand Up @@ -70,7 +71,7 @@ api-shapes:AudioFileValue_ClassShape
sh:minCount 1 ;
sh:maxCount 1 ;
sh:pattern ".+\\.(mp3|wav)$" ;
sh:flags "i" ; # case insensitive
sh:flags "i" ; # case insensitive
sh:severity sh:Violation ;
sh:message "An AudioRepresentation requires a file with one of the following extensions 'mp3', 'wav'." ;
] ;
Expand Down Expand Up @@ -103,7 +104,7 @@ api-shapes:DocumentFileValue_ClassShape
sh:minCount 1 ;
sh:maxCount 1 ;
sh:pattern ".+\\.(pdf|doc|docx|xls|xlsx|ppt|pptx)$" ;
sh:flags "i" ; # case insensitive
sh:flags "i" ; # case insensitive
sh:severity sh:Violation ;
sh:message """
A DocumentRepresentation requires a file with one of the following extensions
Expand Down Expand Up @@ -136,7 +137,7 @@ api-shapes:MovingImageFileValue_ClassShape
sh:minCount 1 ;
sh:maxCount 1 ;
sh:pattern ".+\\.mp4$" ;
sh:flags "i" ; # case insensitive
sh:flags "i" ; # case insensitive
sh:severity sh:Violation ;
sh:message "A MovingImageRepresentation requires a file with the extension 'mp4'." ;
] ;
Expand All @@ -156,10 +157,7 @@ api-shapes:hasStillImageFileValue_PropShape
A StillImageRepresentation requires an external IIIF-URI or a file with one of the extensions:
'jpg', 'jpeg', 'png', 'tif', 'tiff', 'jp2'.
""" ;
sh:or (
[ sh:node api-shapes:StillImageFileValue_ClassShape ]
[ sh:node api-shapes:StillImageExternalFileValue_ClassShape ]
) .
sh:or ( [ sh:node api-shapes:StillImageFileValue_ClassShape ] [ sh:node api-shapes:StillImageExternalFileValue_ClassShape ] ) .

###
# StillImageFileValue
Expand All @@ -175,7 +173,7 @@ api-shapes:StillImageFileValue_ClassShape
sh:minCount 1 ;
sh:maxCount 1 ;
sh:pattern ".+\\.(jpg|jpeg|png|tif|tiff|jp2|jpx)$" ; # jpx is the extension of the files returned by dsp-ingest
sh:flags "i" ; # case insensitive
sh:flags "i" ; # case insensitive
sh:datatype xsd:string ;
sh:severity sh:Violation ;
sh:message """
Expand Down Expand Up @@ -232,7 +230,7 @@ api-shapes:TextFileValue_ClassShape
sh:minCount 1 ;
sh:maxCount 1 ;
sh:pattern ".+\\.(odd|rng|txt|xml|xsd|xsl|csv|json)$" ;
sh:flags "i" ; # case insensitive
sh:flags "i" ; # case insensitive
sh:severity sh:Violation ;
sh:message """
A TextRepresentation requires a file with one of the following extensions
Expand Down
40 changes: 34 additions & 6 deletions src/dsp_tools/resources/validate_data/api-shapes.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,35 @@ api-shapes:UriValue_ClassShape
sh:message "UriValue" ;
sh:class knora-api:UriValue ;
sh:severity sh:Violation .


#########################################
# KNORA-API IN-BUILT PROPERTIES
#########################################

### knora-api:hasLinkTo

api-shapes:hasLinkTo_PropertyShape
a sh:PropertyShape ;
sh:path knora-api:hasLinkTo ;
sh:node api-shapes:LinkValue_ClassShape, api-shapes:hasLinkTo_NodeShape .

api-shapes:hasLinkTo_NodeShape
a sh:NodeShape ;
sh:name "This ensures that the target of the property is of type Resource, i.e. exists in the graph." ;
sh:property [
a sh:PropertyShape ;
sh:class knora-api:Resource ;
sh:message "Range is knora-api:Resource or a subclass." ;
sh:path api-shapes:linkValueHasTargetID ;
] ;
sh:severity sh:Violation .


### knora-api:hasComment

api-shapes:hasComment_PropertyShape
a sh:PropertyShape ;
sh:node api-shapes:FormattedTextValue_ClassShape ;
sh:path knora-api:hasComment .

#########################################
# DSP BUILT IN RESOURCES
Expand All @@ -261,19 +284,24 @@ api-shapes:UriValue_ClassShape

###########################
# Region
###########################


###########################
# LinkObj
###########################

api-shapes:LinkObj_ResourceShape
a sh:NodeShape ;
sh:name "Validates the LinkObj resource" ;
sh:targetClass knora-api:LinkObj ;
sh:property api-shapes:rdfsLabel_Shape ,
api-shapes:hasLinkTo_PropertyShape ,
api-shapes:hasComment_PropertyShape ;
sh:severity sh:Violation .


###########################
# VideoSegment
###########################


###########################
# AudioSegment
###########################
34 changes: 34 additions & 0 deletions test/e2e_validate_data/test_validate_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,22 @@ def file_value_violation(_create_project_generic: Iterator[None], api_con: ApiCo
return _get_validation_result(graphs, api_con, file, DONT_SAVE_GRAPHS)


@lru_cache(maxsize=None)
@pytest.fixture
def dsp_inbuilt_correct(_create_project_generic: Iterator[None], api_con: ApiConnection) -> ValidationReportGraphs:
file = Path("testdata/validate-data/generic/dsp_inbuilt_correct.xml")
graphs = _get_parsed_graphs(api_con, file)
return _get_validation_result(graphs, api_con, file, DONT_SAVE_GRAPHS)


@lru_cache(maxsize=None)
@pytest.fixture
def dsp_inbuilt_violation(_create_project_generic: Iterator[None], api_con: ApiConnection) -> ValidationReportGraphs:
file = Path("testdata/validate-data/generic/dsp_inbuilt_violation.xml")
graphs = _get_parsed_graphs(api_con, file)
return _get_validation_result(graphs, api_con, file, DONT_SAVE_GRAPHS)


@lru_cache(maxsize=None)
@pytest.fixture
def _create_project_special() -> Iterator[None]:
Expand Down Expand Up @@ -257,6 +273,12 @@ def test_file_value_correct(self, file_value_correct: ValidationReportGraphs) ->
def test_file_value_cardinality_violation(self, file_value_violation: ValidationReportGraphs) -> None:
assert not file_value_violation.conforms

def test_dsp_inbuilt_correct(self, dsp_inbuilt_correct: ValidationReportGraphs) -> None:
assert dsp_inbuilt_correct.conforms

def test_dsp_inbuilt_violation(self, dsp_inbuilt_violation: ValidationReportGraphs) -> None:
assert not dsp_inbuilt_violation.conforms

def test_special_characters_correct(self, special_characters_correct: ValidationReportGraphs) -> None:
assert special_characters_correct.conforms

Expand Down Expand Up @@ -416,6 +438,18 @@ def test_reformat_file_value_violation(self, file_value_violation: ValidationRep
assert isinstance(one_result, expected_info[1])
assert one_result.res_id == expected_info[0]

def test_reformat_dsp_inbuilt_violation(self, dsp_inbuilt_violation: ValidationReportGraphs) -> None:
result = reformat_validation_graph(dsp_inbuilt_violation)
expected_info_tuples = [
("link_obj_target_non_existent", LinkedResourceDoesNotExistProblem),
]
assert not result.unexpected_results
assert len(result.problems) == len(expected_info_tuples)
sorted_problems = sorted(result.problems, key=lambda x: x.res_id)
for one_result, expected_info in zip(sorted_problems, expected_info_tuples):
assert isinstance(one_result, expected_info[1])
assert one_result.res_id == expected_info[0]

def test_reformat_special_characters_violation(self, special_characters_violation: ValidationReportGraphs) -> None:
result = reformat_validation_graph(special_characters_violation)
expected_tuples = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_to_data_rdf(data_xml: etree._Element) -> None:
assert isinstance(res, ProjectDeserialised)
assert res.info.shortcode == "9999"
assert res.info.default_onto == "onto"
assert len(res.data.resources) == 17
assert len(res.data.resources) == 18


if __name__ == "__main__":
Expand Down
2 changes: 2 additions & 0 deletions test/integration/commands/validate_data/test_validate_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def test_to_data_rdf(data_xml: etree._Element) -> None:
"http://0.0.0.0:3333/ontology/9999/onto/v2#TestStillImageRepresentation",
"http://0.0.0.0:3333/ontology/9999/second-onto/v2#SecondOntoClass",
"http://api.knora.org/ontology/knora-api/v2#Region",
"http://api.knora.org/ontology/knora-api/v2#LinkObj",
}
expected_names = {
"http://0.0.0.0:3333/ontology/9999/onto/v2#testBoolean",
Expand All @@ -29,5 +30,6 @@ def test_to_data_rdf(data_xml: etree._Element) -> None:
"http://api.knora.org/ontology/knora-api/v2#isRegionOf",
"http://api.knora.org/ontology/knora-api/v2#hasGeometry",
"http://api.knora.org/ontology/knora-api/v2#hasComment",
"http://api.knora.org/ontology/knora-api/v2#hasLinkTo",
}
assert set(data_xml.xpath("//@name")) == expected_names
46 changes: 46 additions & 0 deletions testdata/validate-data/generic/dsp_inbuilt_correct.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version='1.0' encoding='utf-8'?>

<knora xmlns="https://dasch.swiss/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://dasch.swiss/schema ../../../src/dsp_tools/resources/schema/data.xsd"
shortcode="9999"
default-ontology="onto">

<permissions id="open">
<allow group="UnknownUser">V</allow>
<allow group="KnownUser">V</allow>
<allow group="ProjectMember">D</allow>
<allow group="ProjectAdmin">CR</allow>
</permissions>
<permissions id="restricted-view">
<allow group="UnknownUser">RV</allow>
<allow group="KnownUser">RV</allow>
<allow group="ProjectMember">D</allow>
<allow group="ProjectAdmin">CR</allow>
</permissions>
<permissions id="restricted">
<allow group="ProjectMember">D</allow>
<allow group="ProjectAdmin">CR</allow>
</permissions>

<resource label="Empty" restype=":ClassWithEverything" id="resource_1"/>
<resource label="Empty" restype=":ClassWithEverything" id="resource_2"/>

<link label="Link object" id="link_obj_no_comment">
<resptr-prop name="hasLinkTo">
<resptr>resource_1</resptr>
<resptr>resource_2</resptr>
</resptr-prop>
</link>

<link label="Link object" id="link_obj_with_comment">
<text-prop name="hasComment">
<text encoding="xml">Comment Text</text>
</text-prop>
<resptr-prop name="hasLinkTo">
<resptr>resource_1</resptr>
<resptr>resource_2</resptr>
</resptr-prop>
</link>

</knora>
37 changes: 37 additions & 0 deletions testdata/validate-data/generic/dsp_inbuilt_violation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version='1.0' encoding='utf-8'?>

<knora xmlns="https://dasch.swiss/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://dasch.swiss/schema ../../../src/dsp_tools/resources/schema/data.xsd"
shortcode="9999"
default-ontology="onto">

<permissions id="open">
<allow group="UnknownUser">V</allow>
<allow group="KnownUser">V</allow>
<allow group="ProjectMember">D</allow>
<allow group="ProjectAdmin">CR</allow>
</permissions>
<permissions id="restricted-view">
<allow group="UnknownUser">RV</allow>
<allow group="KnownUser">RV</allow>
<allow group="ProjectMember">D</allow>
<allow group="ProjectAdmin">CR</allow>
</permissions>
<permissions id="restricted">
<allow group="ProjectMember">D</allow>
<allow group="ProjectAdmin">CR</allow>
</permissions>


<!-- 1 Violations -->

<!-- link target does not exist -->

<link label="Link object" id="link_obj_target_non_existent">
<resptr-prop name="hasLinkTo">
<resptr>non_existing_link_target</resptr>
</resptr-prop>
</link>

</knora>
12 changes: 12 additions & 0 deletions testdata/validate-data/generic/minimal_correct.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@
</text-prop>
</resource>

<!-- DSP in-built resources -->

<link label="Link object" id="link_obj_with_comment">
<text-prop name="hasComment">
<text encoding="xml">Comment Text</text>
</text-prop>
<resptr-prop name="hasLinkTo">
<resptr>region_1</resptr>
<resptr>second_onto_class</resptr>
</resptr-prop>
</link>

<region label="Region" id="region_1">
<color-prop name="hasColor">
<color permissions="restricted">#5d1f1e</color>
Expand Down

0 comments on commit f9dee0f

Please sign in to comment.