From 7233f6b4a82df0a58e504765854f0be6c1741c49 Mon Sep 17 00:00:00 2001 From: Glen Robson Date: Wed, 11 Dec 2019 12:20:17 +0000 Subject: [PATCH] Tightening up definition of AnnotationPage --- fixtures/3/broken_embedded_annos.json | 81 +++++++++++++++++++++++++++ schema/iiif_3_0.json | 8 ++- schema/schemavalidator.py | 58 ++++++++++++------- tests/test_validator.py | 3 +- 4 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 fixtures/3/broken_embedded_annos.json diff --git a/fixtures/3/broken_embedded_annos.json b/fixtures/3/broken_embedded_annos.json new file mode 100644 index 0000000..84c4958 --- /dev/null +++ b/fixtures/3/broken_embedded_annos.json @@ -0,0 +1,81 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "https://example.com/broken_embedded_annos.json", + "type": "Manifest", + "label": { + "en": [ + "Audio Recording annotation with annotations in Canvas items rather than canvas/annotations" + ] + }, + "items": [ + { + "id": "https://example.com/annos/canvas/1", + "type": "Canvas", + "duration": 107, + "items": [ + { + "id": "https://example.com/annos/canvas/1/paintings", + "type": "AnnotationPage", + "items": [ + { + "id": "https://example.com/annos/canvas/1/painting/1", + "type": "Annotation", + "motivation": "painting", + "body": { + "id": "https://library.harvard.edu/poetry/audio/listeningbooth/PS3537E915A6x1974/Her_Kind.mp3", + "type": "Sound", + "format": "audio/mp3", + "duration": 107 + }, + "target": "https://example.com/annos/canvas/1" + } + ], + "annotations": [ + { + "id": "https://example.com/annos/annotations.json", + "type": "AnnotationPage", + "items": [ + { + "@context": "http://www.w3.org/ns/anno.jsonld", + "id": "https://example.com/annos/canvas/1/annotation/1", + "type": "Annotation", + "motivation": "commenting", + "body": { + "type": "TextualBody", + "value": "breath", + "format": "text/plain" + }, + "target": { + "source": "https://example.com/annos/canvas/1", + "selector": { + "type": "PointSelector", + "t": "27.660653" + } + } + }, + { + "@context": "http://www.w3.org/ns/anno.jsonld", + "id": "https://example.com/annos/canvas/1/annotation/2", + "type": "Annotation", + "motivation": "commenting", + "body": { + "type": "TextualBody", + "value": "her kind", + "format": "text/plain" + }, + "target": { + "source": "https://example.com/annos/canvas/1", + "selector": { + "type": "RangeSelector", + "t": "46.734653,47.875068" + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/schema/iiif_3_0.json b/schema/iiif_3_0.json index 1e3fe74..166d7d1 100644 --- a/schema/iiif_3_0.json +++ b/schema/iiif_3_0.json @@ -461,7 +461,9 @@ "properties": { "type": { "type": "string", - "pattern": "^Collection" + "pattern": "^Collection", + "title": "Are you validating a collection?", + "description":"If you are validating a manifest, you may get this error if there are errors in the manifest. The validator first validates it as a manifest and if that fails it will try and validate it using the other types." }, "metadata": { "$ref": "#/classes/metadata" }, "summary": { "$ref": "#/types/lngString" }, @@ -656,6 +658,7 @@ { "type": "object", "properties": { + "id": { "$ref": "#/types/id" }, "type": { "type": "string", "pattern": "^AnnotationPage$" @@ -666,7 +669,8 @@ "$ref": "#/classes/annotation" } } - } + }, + "additionalProperties": false } ] }, diff --git a/schema/schemavalidator.py b/schema/schemavalidator.py index 650b893..01eb4f3 100755 --- a/schema/schemavalidator.py +++ b/schema/schemavalidator.py @@ -41,29 +41,45 @@ def validate(data, version, url): if errors: print('Validation Failed') errorCount = 1 + if len(errors) == 1 and 'is not valid under any of the given schemas' in errors[0].message: + errors = errors[0].context for err in errors: - detail = '' - if 'title' in err.schema: - detail = err.schema['title'] - description = '' - if 'description' in err.schema: - detail += err.schema['description'] - context = err.instance - #print (json.dumps(err.instance, indent=4)) - if isinstance(context, dict): - for key in context: - if isinstance(context[key], list): - context[key] = '[ ... ]' - elif isinstance(context[key], dict): - context[key] = '{ ... }' - errorsJson.append({ - 'title': 'Error {} of {}.\n Message: {}'.format(errorCount, len(errors), err.message), - 'detail': detail, - 'description': description, - 'path': printPath(err.path, err.message), - 'context': context + if 'is not valid under any of the given schemas' in err.message: + subErrorMessages = [] + for subErr in err.context: + if 'is not valid under any of the given schemas' not in subErr.message: + subErrorMessages.append(subErr.message) + errorsJson.append({ + 'title': 'Error {} of {}.\n Message: Failed to process submission due to many errors'.format(errorCount, len(errors)), + 'detail': 'This error is likely due to other listed errors. Fix those errors first.', + 'description': "{}".format(subErrorMessages), + 'path': '', + 'context': '' + }) + + else: + detail = '' + if 'title' in err.schema: + detail = err.schema['title'] + description = '' + if 'description' in err.schema: + detail += ' ' + err.schema['description'] + context = err.instance + #print (json.dumps(err.instance, indent=4)) + if isinstance(context, dict): + for key in context: + if isinstance(context[key], list): + context[key] = '[ ... ]' + elif isinstance(context[key], dict): + context[key] = '{ ... }' + errorsJson.append({ + 'title': 'Error {} of {}.\n Message: {}'.format(errorCount, len(errors), err.message), + 'detail': detail, + 'description': description, + 'path': printPath(err.path, err.message), + 'context': context - }) + }) #print (json.dumps(err.instance, indent=4)) errorCount += 1 diff --git a/tests/test_validator.py b/tests/test_validator.py index f931731..4b4d0e1 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -147,7 +147,8 @@ def test07_check_manifest3(self): for bad_data in ['fixtures/3/broken_simple_image.json', 'fixtures/3/broken_choice.json', - 'fixtures/3/broken_collection.json']: + 'fixtures/3/broken_collection.json', + 'fixtures/3/broken_embedded_annos.json']: with open(bad_data, 'r') as fh: data = fh.read() j = json.loads(v.check_manifest(data, '3.0'))