diff --git a/src/dsp_tools/commands/validate_data/make_data_rdf.py b/src/dsp_tools/commands/validate_data/make_data_rdf.py index 7dc55ceacc..bc7b437cb2 100644 --- a/src/dsp_tools/commands/validate_data/make_data_rdf.py +++ b/src/dsp_tools/commands/validate_data/make_data_rdf.py @@ -34,11 +34,11 @@ from dsp_tools.commands.validate_data.models.data_rdf import DataRDF from dsp_tools.commands.validate_data.models.data_rdf import DateValueRDF from dsp_tools.commands.validate_data.models.data_rdf import DecimalValueRDF -from dsp_tools.commands.validate_data.models.data_rdf import GenericFileValueRDF from dsp_tools.commands.validate_data.models.data_rdf import GeonameValueRDF from dsp_tools.commands.validate_data.models.data_rdf import IntValueRDF from dsp_tools.commands.validate_data.models.data_rdf import LinkValueRDF from dsp_tools.commands.validate_data.models.data_rdf import ListValueRDF +from dsp_tools.commands.validate_data.models.data_rdf import MovingImageFileValueRDF from dsp_tools.commands.validate_data.models.data_rdf import RDFTriples from dsp_tools.commands.validate_data.models.data_rdf import ResourceRDF from dsp_tools.commands.validate_data.models.data_rdf import RichtextRDF @@ -65,7 +65,9 @@ def make_data_rdf(data_deserialised: DataDeserialised) -> DataRDF: all_triples: list[RDFTriples] = [] for r in data_deserialised.resources: all_triples.extend(_transform_one_resource(r)) - file_values: list[RDFTriples] = [_transform_file_value(x) for x in data_deserialised.file_values] + file_values: list[RDFTriples] = [ + transformed for x in data_deserialised.file_values if (transformed := _transform_file_value(x)) + ] all_triples.extend(file_values) return DataRDF(all_triples) @@ -205,19 +207,25 @@ def _transform_uri_value(val: ValueDeserialised, res_iri: URIRef) -> ValueRDF: return UriValueRDF(URIRef(val.prop_name), content, res_iri) -def _transform_file_value(val: AbstractFileValueDeserialised) -> AbstractFileValueRDF: - res_iri = DATA[val.res_id] +def _transform_file_value(val: AbstractFileValueDeserialised) -> AbstractFileValueRDF | None: if isinstance(val, IIIFUriDeserialised): - return GenericFileValueRDF(res_iri=res_iri, value=Literal(str(val.value))) + return None + return _map_into_correct_file_value(val) + + +def _map_into_correct_file_value(val: AbstractFileValueDeserialised) -> AbstractFileValueRDF | None: + res_iri = DATA[val.res_id] + file_literal = Literal(val.value) file_extension = _get_file_extension(val.value) - return GenericFileValueRDF(res_iri=res_iri, value=Literal(file_extension)) + match file_extension: + case "mp4": + return MovingImageFileValueRDF(res_iri=res_iri, value=file_literal) + case _: + return None def _get_file_extension(value: str | None) -> str: - file_extension = "No file path was given." - if value: - if "." not in value: - file_extension = f"This file is missing a valid extension, actual value: {value}" - else: - file_extension = value.split(".")[-1].lower() + file_extension = "" + if value and "." in value: + file_extension = value.split(".")[-1].lower() return file_extension diff --git a/src/dsp_tools/commands/validate_data/models/data_rdf.py b/src/dsp_tools/commands/validate_data/models/data_rdf.py index 35582440f5..410c2d723f 100644 --- a/src/dsp_tools/commands/validate_data/models/data_rdf.py +++ b/src/dsp_tools/commands/validate_data/models/data_rdf.py @@ -224,13 +224,11 @@ def make_graph(self) -> Graph: @dataclass -class GenericFileValueRDF(AbstractFileValueRDF): - """This class is a placeholder for all types of file values that are not yet implemented.""" - +class MovingImageFileValueRDF(AbstractFileValueRDF): def make_graph(self) -> Graph: g = Graph() val_iri = DATA[str(uuid4())] - g.add((val_iri, RDF.type, API_SHAPES.GenericFileValue)) - g.add((val_iri, API_SHAPES.fileValueHasValue, self.value)) - g.add((self.res_iri, API_SHAPES.hasGenericFileValue, val_iri)) + g.add((val_iri, RDF.type, KNORA_API.MovingImageFileValue)) + g.add((val_iri, KNORA_API.fileValueHasFilename, self.value)) + g.add((self.res_iri, KNORA_API.hasMovingImageFileValue, val_iri)) return g diff --git a/src/dsp_tools/commands/validate_data/models/input_problems.py b/src/dsp_tools/commands/validate_data/models/input_problems.py index 9acc27b6f1..c58df95b7a 100644 --- a/src/dsp_tools/commands/validate_data/models/input_problems.py +++ b/src/dsp_tools/commands/validate_data/models/input_problems.py @@ -383,12 +383,12 @@ def sort_value(self) -> str: @dataclass -class MissingFileValueProblem(InputProblem): +class FileValueProblem(InputProblem): expected: str @property def problem(self) -> str: - return "Required file value is missing" + return self.expected def get_msg(self) -> str: return f"{self.problem}" diff --git a/src/dsp_tools/commands/validate_data/reformat_validaton_result.py b/src/dsp_tools/commands/validate_data/reformat_validaton_result.py index 05ad1c90ec..cc642d233e 100644 --- a/src/dsp_tools/commands/validate_data/reformat_validaton_result.py +++ b/src/dsp_tools/commands/validate_data/reformat_validaton_result.py @@ -11,13 +11,13 @@ from dsp_tools.commands.validate_data.models.input_problems import AllProblems from dsp_tools.commands.validate_data.models.input_problems import ContentRegexProblem from dsp_tools.commands.validate_data.models.input_problems import DuplicateValueProblem +from dsp_tools.commands.validate_data.models.input_problems import FileValueProblem from dsp_tools.commands.validate_data.models.input_problems import GenericProblem from dsp_tools.commands.validate_data.models.input_problems import InputProblem from dsp_tools.commands.validate_data.models.input_problems import LinkedResourceDoesNotExistProblem from dsp_tools.commands.validate_data.models.input_problems import LinkTargetTypeMismatchProblem from dsp_tools.commands.validate_data.models.input_problems import MaxCardinalityProblem from dsp_tools.commands.validate_data.models.input_problems import MinCardinalityProblem -from dsp_tools.commands.validate_data.models.input_problems import MissingFileValueProblem from dsp_tools.commands.validate_data.models.input_problems import NonExistentCardinalityProblem from dsp_tools.commands.validate_data.models.input_problems import UnexpectedResults from dsp_tools.commands.validate_data.models.input_problems import ValueTypeProblem @@ -358,9 +358,9 @@ def _reformat_one_validation_result(validation_result: ValidationResult) -> Inpu def _reformat_min_cardinality_validation_result(validation_result: ResultMinCardinalityViolation) -> InputProblem: iris = _reformat_main_iris(validation_result) - file_value_properties = ["shapes:hasGenericFileValue"] + file_value_properties = ["hasMovingImageFileValue"] if iris.prop_name in file_value_properties: - return MissingFileValueProblem( + return FileValueProblem( res_id=iris.res_id, res_type=iris.res_type, prop_name="bitstream / iiif-uri", diff --git a/src/dsp_tools/commands/validate_data/sparql/file_value_shacl.py b/src/dsp_tools/commands/validate_data/sparql/file_value_shacl.py index 6653a67ba4..9bbcd54b75 100644 --- a/src/dsp_tools/commands/validate_data/sparql/file_value_shacl.py +++ b/src/dsp_tools/commands/validate_data/sparql/file_value_shacl.py @@ -14,12 +14,19 @@ def construct_file_value_cardinality(onto: Graph) -> Graph: Returns: Graph with file cardinalities """ + val_prop_mapper = {"MovingImageRepresentation": "hasMovingImageFileValue"} + + def as_class_type_and_shacl_shape(cls_name: str) -> tuple[str, str]: + return f"knora-api:{cls_name}", f"api-shapes:{val_prop_mapper[cls_name]}_PropShape" + g = Graph() - g += _construct_generic_file_value_cardinality(onto) + for t in val_prop_mapper.keys(): + representation_type, shacl_shape = as_class_type_and_shacl_shape(t) + g += _construct_one_representation_shape(onto, representation_type, shacl_shape) return g -def _construct_generic_file_value_cardinality(onto: Graph) -> Graph: +def _construct_one_representation_shape(onto: Graph, representation_type: str, shacl_shape: str) -> Graph: query_s = """ PREFIX owl: PREFIX sh: @@ -30,27 +37,12 @@ def _construct_generic_file_value_cardinality(onto: Graph) -> Graph: PREFIX dash: CONSTRUCT { - ?class sh:property [ - a sh:PropertyShape ; - sh:path api-shapes:hasGenericFileValue ; - sh:minCount 1 ; - sh:maxCount 1 ; - sh:severity sh:Violation ; - sh:message "A file is required for this resource" ; - ] . + ?class sh:property %(shacl_shape)s . } WHERE { ?class a owl:Class ; - rdfs:subClassOf ?superClass . - VALUES ?superClass { - knora-api:ArchiveRepresentation - knora-api:AudioRepresentation - knora-api:DocumentRepresentation - knora-api:MovingImageRepresentation - knora-api:StillImageRepresentation - knora-api:TextRepresentation - } + rdfs:subClassOf %(representation_type)s . } - """ + """ % {"representation_type": representation_type, "shacl_shape": shacl_shape} # noqa: UP031 (printf-string-formatting) if results_graph := onto.query(query_s).graph: return results_graph return Graph() diff --git a/src/dsp_tools/commands/validate_data/validate_data.py b/src/dsp_tools/commands/validate_data/validate_data.py index 0ced8cd3a4..3d8e9d814a 100644 --- a/src/dsp_tools/commands/validate_data/validate_data.py +++ b/src/dsp_tools/commands/validate_data/validate_data.py @@ -84,6 +84,7 @@ def _inform_about_experimental_feature() -> None: "The following information of your data is being validated:", "Cardinalities", "If the value type used matches the ontology", + "Content of the values", ] cprint(LIST_SEPARATOR.join(what_is_validated), color="magenta", attrs=["bold"]) @@ -117,7 +118,6 @@ def _get_all_onto_classes(ontos: Graph) -> tuple[set[str], set[str]]: is_value_iri = URIRef(KNORA_API + "isValueClass") value_classes = set(ontos.subjects(is_value_iri, Literal(True))) value_cls = {reformat_onto_iri(x) for x in value_classes} - value_cls.add("shapes:GenericFileValue") return res_cls, value_cls @@ -144,12 +144,15 @@ def _create_graphs(onto_client: OntologyClient, list_client: ListClient, data_rd shapes = construct_shapes_graphs(onto_for_construction, all_lists) api_shapes = Graph() api_shapes.parse("src/dsp_tools/resources/validate_data/api-shapes.ttl") + file_shapes = Graph() + file_shapes.parse("src/dsp_tools/resources/validate_data/file_value_cardinalities.ttl") content_shapes = shapes.content + api_shapes + card_shapes = shapes.cardinality + file_shapes data = data_rdf.make_graph() return RDFGraphs( data=data, ontos=ontologies, - cardinality_shapes=shapes.cardinality, + cardinality_shapes=card_shapes, content_shapes=content_shapes, knora_api=knora_api, ) diff --git a/src/dsp_tools/resources/validate_data/api-shapes.ttl b/src/dsp_tools/resources/validate_data/api-shapes.ttl index 8348756d68..f47de81148 100644 --- a/src/dsp_tools/resources/validate_data/api-shapes.ttl +++ b/src/dsp_tools/resources/validate_data/api-shapes.ttl @@ -254,55 +254,6 @@ api-shapes:UriValue_ClassShape ######################################### -######################################### -# RESOURCES WITH FILES -######################################### - -########################### -# ArchiveRepresentation -########################### - -# knora-api:ArchiveFileValue -> knora-api:hasArchiveFileValue - - -########################### -# AudioRepresentation -########################### - -# knora-api:AudioFileValue -> knora-api:hasAudioFileValue - - -########################### -# DocumentRepresentation -########################### - -# knora-api:DocumentFileValue -> knora-api:hasDocumentFileValue - - -########################### -# MovingImageRepresentation -########################### - -# knora-api:MovingImageFileValue -> knora-api:hasMovingImageFileValue - - -########################### -# StillImageRepresentation -########################### - -# knora-api:StillImageExternalFileValue -> knora-api:fileValueHasExternalUrl - - -# knora-api:StillImageFileValue -> knora-api:hasStillImageFileValue - - -########################### -# TextRepresentation -########################### - -# knora-api:TextFileValue -> knora-api:hasTextFileValue - - ######################################### # DSP BUILT IN RESOURCES ######################################### diff --git a/src/dsp_tools/resources/validate_data/file_value_cardinalities.ttl b/src/dsp_tools/resources/validate_data/file_value_cardinalities.ttl new file mode 100644 index 0000000000..1582f6b681 --- /dev/null +++ b/src/dsp_tools/resources/validate_data/file_value_cardinalities.ttl @@ -0,0 +1,77 @@ +@prefix sh: . +@prefix rdfs: . +@prefix xsd: . +@prefix owl: . +@prefix knora-api: . + +@prefix api-shapes: . + + +######################################### +# RESOURCES WITH FILES +######################################### + +########################### +# ArchiveRepresentation +########################### + +# knora-api:ArchiveFileValue -> knora-api:hasArchiveFileValue + + +########################### +# AudioRepresentation +########################### + +# knora-api:AudioFileValue -> knora-api:hasAudioFileValue + + +########################### +# DocumentRepresentation +########################### + +# knora-api:DocumentFileValue -> knora-api:hasDocumentFileValue + + +########################### +# MovingImageRepresentation +########################### + +api-shapes:hasMovingImageFileValue_PropShape + a sh:PropertyShape ; + sh:path knora-api:hasMovingImageFileValue ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:message "A MovingImageRepresentation requires a file with the extension 'mp4'." ; + sh:node api-shapes:MovingImageFileValue_ClassShape . + +api-shapes:MovingImageFileValue_ClassShape + a sh:NodeShape ; + sh:name "Validates the class type" ; + sh:message "MovingImageFileValue" ; + sh:class knora-api:MovingImageFileValue ; + sh:property [ + a sh:PropertyShape ; + sh:path knora-api:fileValueHasFilename ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:pattern ".+\\.mp4$" ; + sh:severity sh:Violation ; + sh:message "A MovingImageRepresentation requires a file with the extension 'mp4'." ; + ] ; + sh:severity sh:Violation . + +########################### +# StillImageRepresentation +########################### + +# knora-api:StillImageExternalFileValue -> knora-api:fileValueHasExternalUrl + + +# knora-api:StillImageFileValue -> knora-api:hasStillImageFileValue + + +########################### +# TextRepresentation +########################### + +# knora-api:TextFileValue -> knora-api:hasTextFileValue \ No newline at end of file diff --git a/test/e2e_validate_data/test_validate_data.py b/test/e2e_validate_data/test_validate_data.py index a3f1bf0a0d..285738d5b3 100644 --- a/test/e2e_validate_data/test_validate_data.py +++ b/test/e2e_validate_data/test_validate_data.py @@ -11,12 +11,12 @@ from dsp_tools.commands.validate_data.api_connection import ApiConnection from dsp_tools.commands.validate_data.models.input_problems import ContentRegexProblem from dsp_tools.commands.validate_data.models.input_problems import DuplicateValueProblem +from dsp_tools.commands.validate_data.models.input_problems import FileValueProblem from dsp_tools.commands.validate_data.models.input_problems import GenericProblem from dsp_tools.commands.validate_data.models.input_problems import LinkedResourceDoesNotExistProblem from dsp_tools.commands.validate_data.models.input_problems import LinkTargetTypeMismatchProblem from dsp_tools.commands.validate_data.models.input_problems import MaxCardinalityProblem from dsp_tools.commands.validate_data.models.input_problems import MinCardinalityProblem -from dsp_tools.commands.validate_data.models.input_problems import MissingFileValueProblem from dsp_tools.commands.validate_data.models.input_problems import NonExistentCardinalityProblem from dsp_tools.commands.validate_data.models.input_problems import UnknownClassesInData from dsp_tools.commands.validate_data.models.input_problems import ValueTypeProblem @@ -131,10 +131,8 @@ def file_value_correct(_create_project_generic: Iterator[None], api_con: ApiConn @lru_cache(maxsize=None) @pytest.fixture -def file_value_cardinality_violation( - _create_project_generic: Iterator[None], api_con: ApiConnection -) -> ValidationReportGraphs: - file = Path("testdata/validate-data/generic/file_value_cardinality_violation.xml") +def file_value_violation(_create_project_generic: Iterator[None], api_con: ApiConnection) -> ValidationReportGraphs: + file = Path("testdata/validate-data/generic/file_value_violation.xml") graphs = _get_parsed_graphs(api_con, file) return _get_validation_result(graphs, api_con, file, DONT_SAVE_GRAPHS) @@ -255,8 +253,8 @@ def test_unique_value_violation(self, unique_value_violation: ValidationReportGr def test_file_value_correct(self, file_value_correct: ValidationReportGraphs) -> None: assert file_value_correct.conforms - def test_file_value_cardinality_violation(self, file_value_cardinality_violation: ValidationReportGraphs) -> None: - assert not file_value_cardinality_violation.conforms + def test_file_value_cardinality_violation(self, file_value_violation: ValidationReportGraphs) -> None: + assert not file_value_violation.conforms def test_special_characters_correct(self, special_characters_correct: ValidationReportGraphs) -> None: assert special_characters_correct.conforms @@ -362,7 +360,7 @@ def test_reformat_every_constraint_once(self, every_combination_once: Validation ("id_card_one", MinCardinalityProblem), ("id_closed_constraint", NonExistentCardinalityProblem), ("id_max_card", MaxCardinalityProblem), - ("id_missing_file_value", MissingFileValueProblem), + ("id_missing_file_value", FileValueProblem), ("id_simpletext", ValueTypeProblem), ("id_uri", ValueTypeProblem), ("identical_values", DuplicateValueProblem), @@ -392,24 +390,14 @@ def test_reformat_unique_value_violation(self, unique_value_violation: Validatio assert isinstance(one_result, DuplicateValueProblem) assert one_result.res_id == expected_id - def test_reformat_file_value_cardinality_violation( - self, file_value_cardinality_violation: ValidationReportGraphs - ) -> None: - result = reformat_validation_graph(file_value_cardinality_violation) - expected_info_tuples = [ - "id_archive_missing", - "id_audio_missing", - "id_document_missing", - "id_moving_image_missing", - "id_still_image_file_missing", - "id_still_image_iiif_missing", - "id_text_missing", - ] + def test_reformat_file_value_violation(self, file_value_violation: ValidationReportGraphs) -> None: + result = reformat_validation_graph(file_value_violation) + expected_info_tuples = ["id_video_missing", "id_video_wrong_extension"] 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, MissingFileValueProblem) + assert isinstance(one_result, FileValueProblem) assert one_result.res_id == expected_info def test_reformat_special_characters_violation(self, special_characters_violation: ValidationReportGraphs) -> None: diff --git a/test/unittests/commands/validate_data/fixtures/validation_result.py b/test/unittests/commands/validate_data/fixtures/validation_result.py index 344cc87844..9c880d41cb 100644 --- a/test/unittests/commands/validate_data/fixtures/validation_result.py +++ b/test/unittests/commands/validate_data/fixtures/validation_result.py @@ -17,7 +17,6 @@ from dsp_tools.commands.validate_data.models.validation import ResultUniqueValueViolation from dsp_tools.commands.validate_data.models.validation import ResultValueTypeViolation from dsp_tools.commands.validate_data.models.validation import ValidationResultBaseInfo -from test.unittests.commands.validate_data.constants import API_SHAPES from test.unittests.commands.validate_data.constants import DASH from test.unittests.commands.validate_data.constants import DATA from test.unittests.commands.validate_data.constants import KNORA_API @@ -761,18 +760,19 @@ def extracted_unknown_list_name() -> ResultGenericViolation: def report_missing_file_value(onto_graph: Graph) -> tuple[Graph, Graph, ValidationResultBaseInfo]: validation_str = f"""{PREFIXES} [ a sh:ValidationResult ; - sh:focusNode ; - sh:resultMessage "A file is required for this resource" ; - sh:resultPath ; + sh:focusNode ; + sh:resultMessage "A MovingImageRepresentation requires a file with the extension 'mp4'." ; + sh:resultPath ; sh:resultSeverity sh:Violation ; sh:sourceConstraintComponent sh:MinCountConstraintComponent ; - sh:sourceShape [ ] ] . + sh:sourceShape + ] . """ validation_g = Graph() validation_g.parse(data=validation_str, format="ttl") data_str = f"""{PREFIXES} - a onto:TestArchiveRepresentation ; - rdfs:label "TestArchiveRepresentation"^^xsd:string . + a ; + rdfs:label "TestMovingImageRepresentation"^^xsd:string . """ onto_data_g = Graph() onto_data_g += onto_graph @@ -781,9 +781,9 @@ def report_missing_file_value(onto_graph: Graph) -> tuple[Graph, Graph, Validati base_info = ValidationResultBaseInfo( result_bn=val_bn, source_constraint_component=SH.MinCountConstraintComponent, - resource_iri=DATA.id_missing_file_value, - res_class_type=ONTO.TestArchiveRepresentation, - result_path=API_SHAPES.hasGenericFileValue, + resource_iri=DATA.id_video_missing, + res_class_type=ONTO.TestMovingImageRepresentation, + result_path=KNORA_API.hasMovingImageFileValue, ) return validation_g, onto_data_g, base_info @@ -791,10 +791,10 @@ def report_missing_file_value(onto_graph: Graph) -> tuple[Graph, Graph, Validati @pytest.fixture def extracted_missing_file_value() -> ResultMinCardinalityViolation: return ResultMinCardinalityViolation( - res_iri=DATA.id_missing_file_value, - res_class=ONTO.TestArchiveRepresentation, - property=API_SHAPES.hasGenericFileValue, - results_message="A file is required for this resource", + res_iri=DATA.id_video_missing, + res_class=ONTO.TestMovingImageRepresentation, + property=KNORA_API.hasMovingImageFileValue, + results_message="A MovingImageRepresentation requires a file with the extension 'mp4'.", ) diff --git a/test/unittests/commands/validate_data/sparql/test_file_value_shacl.py b/test/unittests/commands/validate_data/sparql/test_file_value_shacl.py index 7d1a528fde..0e17dd8f5f 100644 --- a/test/unittests/commands/validate_data/sparql/test_file_value_shacl.py +++ b/test/unittests/commands/validate_data/sparql/test_file_value_shacl.py @@ -2,22 +2,26 @@ from rdflib import SH from rdflib import Graph -from dsp_tools.commands.validate_data.sparql.file_value_shacl import _construct_generic_file_value_cardinality from dsp_tools.commands.validate_data.sparql.file_value_shacl import construct_file_value_cardinality +from test.unittests.commands.validate_data.constants import API_SHAPES +from test.unittests.commands.validate_data.constants import ONTO -def test_construct_file_value_cardinality(onto_graph: Graph) -> None: - res = construct_file_value_cardinality(onto_graph) - number_of_classes_with_files = 6 - class_shapes = list(res.subjects(predicate=SH.property)) - assert len(class_shapes) == number_of_classes_with_files +@pytest.fixture +def file_value_shacl(onto_graph: Graph) -> Graph: + return construct_file_value_cardinality(onto_graph) -def test_construct_generic_file_value_cardinality(onto_graph: Graph) -> None: - res = _construct_generic_file_value_cardinality(onto_graph) - number_of_classes_without_specific_sparql = 6 - class_shapes = list(res.subjects(predicate=SH.property)) - assert len(class_shapes) == number_of_classes_without_specific_sparql +def test_construct_file_value_cardinality(file_value_shacl: Graph) -> None: + number_of_classes_implemented = 1 + assert len(list(file_value_shacl.subjects(SH.property))) == number_of_classes_implemented + + +def test_construct_moving_image(file_value_shacl: Graph) -> None: + result_list = list(file_value_shacl.subjects(SH.property, API_SHAPES.hasMovingImageFileValue_PropShape)) + assert len(result_list) == 1 + moving_image = result_list[0] + assert moving_image == ONTO.TestMovingImageRepresentation if __name__ == "__main__": diff --git a/test/unittests/commands/validate_data/test_make_data_rdf.py b/test/unittests/commands/validate_data/test_make_data_rdf.py index d877cb7f1e..eccac18de9 100644 --- a/test/unittests/commands/validate_data/test_make_data_rdf.py +++ b/test/unittests/commands/validate_data/test_make_data_rdf.py @@ -7,14 +7,12 @@ from dsp_tools.commands.validate_data.make_data_rdf import _transform_file_value from dsp_tools.commands.validate_data.make_data_rdf import _transform_one_resource from dsp_tools.commands.validate_data.make_data_rdf import _transform_one_value -from dsp_tools.commands.validate_data.models.data_deserialised import AbstractFileValueDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import BitstreamDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import BooleanValueDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import ColorValueDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import DateValueDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import DecimalValueDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import GeonameValueDeserialised -from dsp_tools.commands.validate_data.models.data_deserialised import IIIFUriDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import IntValueDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import LinkValueDeserialised from dsp_tools.commands.validate_data.models.data_deserialised import ListValueDeserialised @@ -31,6 +29,7 @@ from dsp_tools.commands.validate_data.models.data_rdf import IntValueRDF from dsp_tools.commands.validate_data.models.data_rdf import LinkValueRDF from dsp_tools.commands.validate_data.models.data_rdf import ListValueRDF +from dsp_tools.commands.validate_data.models.data_rdf import MovingImageFileValueRDF from dsp_tools.commands.validate_data.models.data_rdf import ResourceRDF from dsp_tools.commands.validate_data.models.data_rdf import RichtextRDF from dsp_tools.commands.validate_data.models.data_rdf import SimpleTextRDF @@ -269,17 +268,17 @@ def test_none(self, uri_value_deserialised_none: UriValueDeserialised) -> None: assert val.object_value == Literal("", datatype=XSD.string) -@pytest.mark.parametrize( - ("value", "expected"), - [ - (BitstreamDeserialised("id", "test.jpg"), "jpg"), - (BitstreamDeserialised("id", None), "No file path was given."), - (IIIFUriDeserialised("id", "https://uri.org"), "https://uri.org"), - (IIIFUriDeserialised("id", None), "None"), - ], -) -def test_transform_file_value(value: AbstractFileValueDeserialised, expected: str) -> None: - assert _transform_file_value(value).value == Literal(expected) +class TestTransformFileValue: + def test_moving_image(self) -> None: + bitstream = BitstreamDeserialised("id", "test.mp4") + result = _transform_file_value(bitstream) + assert isinstance(result, MovingImageFileValueRDF) + assert result.value == Literal(bitstream.value) + + def test_other(self) -> None: + bitstream = BitstreamDeserialised("id", "test.other") + result = _transform_file_value(bitstream) + assert not result @pytest.mark.parametrize( @@ -287,8 +286,8 @@ def test_transform_file_value(value: AbstractFileValueDeserialised, expected: st [ ("test.jpg", "jpg"), ("test.JPG", "jpg"), - (None, "No file path was given."), - ("test", "This file is missing a valid extension, actual value: test"), + (None, ""), + ("test", ""), ], ) def test_get_file_extension(value: str | None, expected: str) -> None: diff --git a/test/unittests/commands/validate_data/test_reformat_validaton_result.py b/test/unittests/commands/validate_data/test_reformat_validaton_result.py index 2bf74151fc..6326371552 100644 --- a/test/unittests/commands/validate_data/test_reformat_validaton_result.py +++ b/test/unittests/commands/validate_data/test_reformat_validaton_result.py @@ -7,12 +7,12 @@ from dsp_tools.commands.validate_data.models.input_problems import ContentRegexProblem from dsp_tools.commands.validate_data.models.input_problems import DuplicateValueProblem +from dsp_tools.commands.validate_data.models.input_problems import FileValueProblem from dsp_tools.commands.validate_data.models.input_problems import GenericProblem from dsp_tools.commands.validate_data.models.input_problems import LinkedResourceDoesNotExistProblem from dsp_tools.commands.validate_data.models.input_problems import LinkTargetTypeMismatchProblem from dsp_tools.commands.validate_data.models.input_problems import MaxCardinalityProblem from dsp_tools.commands.validate_data.models.input_problems import MinCardinalityProblem -from dsp_tools.commands.validate_data.models.input_problems import MissingFileValueProblem from dsp_tools.commands.validate_data.models.input_problems import NonExistentCardinalityProblem from dsp_tools.commands.validate_data.models.input_problems import ValueTypeProblem from dsp_tools.commands.validate_data.models.validation import DetailBaseInfo @@ -31,7 +31,6 @@ from dsp_tools.commands.validate_data.reformat_validaton_result import _query_one_without_detail from dsp_tools.commands.validate_data.reformat_validaton_result import _reformat_one_validation_result from dsp_tools.commands.validate_data.reformat_validaton_result import _separate_result_types -from test.unittests.commands.validate_data.constants import API_SHAPES from test.unittests.commands.validate_data.constants import DATA from test.unittests.commands.validate_data.constants import KNORA_API from test.unittests.commands.validate_data.constants import ONTO @@ -291,8 +290,8 @@ def test_missing_file_value(self, report_missing_file_value: tuple[Graph, Graph, assert isinstance(result, ResultMinCardinalityViolation) assert result.res_iri == info.resource_iri assert result.res_class == info.res_class_type - assert result.property == API_SHAPES.hasGenericFileValue - assert result.results_message == "A file is required for this resource" + assert result.property == KNORA_API.hasMovingImageFileValue + assert result.results_message == "A MovingImageRepresentation requires a file with the extension 'mp4'." class TestReformatResult: @@ -409,11 +408,11 @@ def test_unknown_list_name(self, extracted_unknown_list_name: ResultGenericViola def test_missing_file_value(self, extracted_missing_file_value: ResultMinCardinalityViolation) -> None: result = _reformat_one_validation_result(extracted_missing_file_value) - assert isinstance(result, MissingFileValueProblem) - assert result.res_id == "id_missing_file_value" - assert result.res_type == "onto:TestArchiveRepresentation" + assert isinstance(result, FileValueProblem) + assert result.res_id == "id_video_missing" + assert result.res_type == "onto:TestMovingImageRepresentation" assert result.prop_name == "bitstream / iiif-uri" - assert result.expected == "A file is required for this resource" + assert result.expected == "A MovingImageRepresentation requires a file with the extension 'mp4'." if __name__ == "__main__": diff --git a/test/unittests/commands/validate_data/test_validate_data.py b/test/unittests/commands/validate_data/test_validate_data.py index 9ff180381e..b91fa5b440 100644 --- a/test/unittests/commands/validate_data/test_validate_data.py +++ b/test/unittests/commands/validate_data/test_validate_data.py @@ -112,7 +112,7 @@ def test_get_all_used_classes(self, data_ok: Graph) -> None: def test_get_all_onto_classes(self, onto: Graph) -> None: res_cls, value_cls = _get_all_onto_classes(onto) assert res_cls == {"onto:One"} - assert value_cls == {"TextValue", "shapes:GenericFileValue"} + assert value_cls == {"TextValue"} if __name__ == "__main__": diff --git a/testdata/validate-data/generic/every_combination_once.xml b/testdata/validate-data/generic/every_combination_once.xml index 6b9b4c29f1..97cce7d18b 100644 --- a/testdata/validate-data/generic/every_combination_once.xml +++ b/testdata/validate-data/generic/every_combination_once.xml @@ -53,11 +53,11 @@ - + - - - - - - - - - - - - - - - - diff --git a/testdata/validate-data/generic/file_value_violation.xml b/testdata/validate-data/generic/file_value_violation.xml new file mode 100644 index 0000000000..b536d44f6d --- /dev/null +++ b/testdata/validate-data/generic/file_value_violation.xml @@ -0,0 +1,17 @@ + + + + + + + + + + this/is/filepath/file.other + + + diff --git a/testdata/validate-data/validationResultTypes.csv b/testdata/validate-data/validationResultTypes.csv index 80ea01ce6a..5cee361463 100644 --- a/testdata/validate-data/validationResultTypes.csv +++ b/testdata/validate-data/validationResultTypes.csv @@ -9,4 +9,4 @@ Cardinality,MaxCardinalityProblem,sourceConstraintComponent,sourceConstraintComp Cardinality,MinCardinalityProblem,sourceConstraintComponent,sourceConstraintComponent,sh:MinCountConstraintComponent,onto specific,,,,sh:focusNode,sh:focusNode - rdf:type,sh:resultPath,sh:resultMessage,, Content,ContentRegexProblem,"message, no detail, with value","sourceConstraintComponent, sourceShape (empty label)",sh:PatternConstraintComponent,api-shapes:rdfsLabel_Shape,,,,sh:focusNode,sh:focusNode - rdf:type,sh:resultPath,sh:resultMessage,,sh:value (Literal) Duplicate,DuplicateValueProblem,sourceConstraintComponent,sourceConstraintComponent,sh:SPARQLConstraintComponent,onto specific,,,,sh:focusNode,sh:focusNode - rdf:type,sh:resultPath,-,,sh:value (Literal or IRI) -File Value,MissingFileValueProblem,"message, no detail, with value",property name,sh:MinCountConstraintComponent,onto specific,,,,sh:focusNode,sh:focusNode - rdf:type,sh:resultPath,sh:resultMessage,"", \ No newline at end of file +File Value,FileValueProblem,"message, no detail, with value",property name,sh:MinCountConstraintComponent,onto specific,,,,sh:focusNode,sh:focusNode - rdf:type,sh:resultPath,sh:resultMessage,"", \ No newline at end of file