From ea7deb431bdfbe3541bb92f0cb12e47305ba7369 Mon Sep 17 00:00:00 2001 From: Glen Robson Date: Wed, 4 Dec 2019 01:02:15 +0000 Subject: [PATCH 1/5] Cleaning up debug messaging --- schema/schemavalidator.py | 13 +++++-------- tests/test_validator.py | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/schema/schemavalidator.py b/schema/schemavalidator.py index 2e30e07..d7b832c 100755 --- a/schema/schemavalidator.py +++ b/schema/schemavalidator.py @@ -46,19 +46,16 @@ def validate(data, version, url): detail = '' if 'title' in err.schema: detail = err.schema['title'] - print (' Test message: {}'.format(detail)) + # print (' Test message: {}'.format(detail)) description = '' if 'description' in err.schema: description = err.schema['description'] - print (' Test description: {}'.format(description)) - print('\n Path for error: {}'.format(printPath(err.path, err.message))) + # print (' Test description: {}'.format(description)) + #print('\n Path for error: {}'.format(printPath(err.path, err.message))) context = err.instance - print (json.dumps(err.instance, indent=4)) + #print (json.dumps(err.instance, indent=4)) if isinstance(context, dict): for key in context: - print('****') - print(key) - print('****') if isinstance(context[key], list): context[key] = '[ ... ]' elif isinstance(context[key], dict): @@ -71,7 +68,7 @@ def validate(data, version, url): 'context': context }) - print (json.dumps(err.instance, indent=4)) + #print (json.dumps(err.instance, indent=4)) errorCount += 1 # Return: diff --git a/tests/test_validator.py b/tests/test_validator.py index 95b1c04..e672e90 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -139,14 +139,30 @@ def test07_check_manifest3(self): with open(good, 'r') as fh: data = fh.read() j = json.loads(v.check_manifest(data, '3.0')) + if j['okay'] != 1: + self.printValidationerror(good, j['errorList']) + self.assertEqual(j['okay'], 1) for bad_data in ['fixtures/3/broken_simple_image.json', 'fixtures/3/broken_choice.json']: with open(bad_data, 'r') as fh: - data = fh.read() j = json.loads(v.check_manifest(data, '3.0')) + self.assertEqual(j['okay'], 0) + + + def printValidationerror(self, filename, errors): + print ('Failed to validate: {}'.format(filename)) + errorCount = 1 + for err in errors: + print(err['title']) + if 'description' in err: + print (' Test description: {}'.format(err.schema['description'])) + print('\n Path for error: {}'.format(err['path'])) + print('\n Context: {}'.format(err['context'])) + errorCount += 1 + if __name__ == '__main__': unittest.main() From 9ce0b1a04cfdac2f248af7735bf2f32d6de99d0b Mon Sep 17 00:00:00 2001 From: Glen Robson Date: Wed, 4 Dec 2019 01:02:38 +0000 Subject: [PATCH 2/5] Making id optional for TextualBody annotations --- fixtures/3/full_example.json | 1 - schema/iiif_3_0.json | 103 ++++++++++++++++++++--------------- 2 files changed, 59 insertions(+), 45 deletions(-) diff --git a/fixtures/3/full_example.json b/fixtures/3/full_example.json index eed67b9..40a2f2c 100644 --- a/fixtures/3/full_example.json +++ b/fixtures/3/full_example.json @@ -233,7 +233,6 @@ "type": "Annotation", "motivation": "commenting", "body": { - "id": "https://example.org/iiif/book1/annotationPage/Body/id", "type": "TextualBody", "language": "en", "value": "I love this manifest!" diff --git a/schema/iiif_3_0.json b/schema/iiif_3_0.json index 2396c04..3fd1403 100644 --- a/schema/iiif_3_0.json +++ b/schema/iiif_3_0.json @@ -128,56 +128,71 @@ "required": ["type", "items"] }, "resource": { - "type": "object", - "properties": { - "id": { "$ref": "#/types/id" }, - "type": { - "oneOf": [ - { - "type": "string", - "pattern": "^Image$" - }, - { - "type": "string", - "pattern": "^Sound$" - }, - { - "type": "string", - "pattern": "^Video$" + "oneOf": [ + { + "title": "Annotation bodies MUST have an id and type property.", + "type": "object", + "properties": { + "id": { "$ref": "#/types/id" }, + "type": { + "oneOf": [ + { + "type": "string", + "pattern": "^Image$" + }, + { + "type": "string", + "pattern": "^Sound$" + }, + { + "type": "string", + "pattern": "^Video$" + }, + { + "type": "string", + "pattern": "^Model$" + }, + { + "type": "string", + "pattern": "^Dataset$" + }, + { + "type": "string", + "pattern": "^Text$" + } ] }, - { - "type": "string", - "pattern": "^Model$" + "height": { + "type": "integer" }, - { - "type": "string", - "pattern": "^Dataset$" + "width": { + "type": "integer" }, - { - "type": "string", - "pattern": "^Text$" + "duration": { + "$ref": "#/types/duration" }, - { + "language": { "type": "string"}, + "service": { "$ref": "#/classes/service" }, + "format": { "$ref": "#/types/format" }, + "label": {"$ref": "#/types/lngString" } + }, + "required": ["id", "type"] + }, + { + "title": "Annotation bodies which are TextualBody MUST have an type and value property.", + "type": "object", + "properties": { + "id": { "$ref": "#/types/id" }, + "type": { "type": "string", "pattern": "^TextualBody$" - } - ] - }, - "height": { - "type": "integer" - }, - "width": { - "type": "integer" - }, - "duration": { - "$ref": "#/types/duration" - }, - "language": { "type": "string"}, - "service": { "$ref": "#/classes/service" }, - "format": { "$ref": "#/types/format" }, - "label": {"$ref": "#/types/lngString" } - }, - "required": ["id","type"] + }, + "value": { "type": "string" }, + "format": { "$ref": "#/types/format" }, + "language": { "type": "string"} + }, + "required": ["value", "type"] + } + ] }, "imgSvr": { "allOf": [ From 67960c6f0a178dd504a3d1dbdf52d9fb9e4b5881 Mon Sep 17 00:00:00 2001 From: Glen Robson Date: Wed, 4 Dec 2019 01:07:16 +0000 Subject: [PATCH 3/5] Ensuring Collections have a label --- fixtures/3/broken_collection.json | 37 +++++++++++++++++++++++++++++++ schema/iiif_3_0.json | 3 ++- tests/test_validator.py | 6 ++++- 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 fixtures/3/broken_collection.json diff --git a/fixtures/3/broken_collection.json b/fixtures/3/broken_collection.json new file mode 100644 index 0000000..c1c0b65 --- /dev/null +++ b/fixtures/3/broken_collection.json @@ -0,0 +1,37 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "https://example.org/iiif/collection/top", + "type": "Collection", + "summary": { "en": [ "Short summary of the Collection" ] }, + "requiredStatement": { + "label": { "en": [ "Attribution" ] }, + "value": { "en": [ "Provided by Example Organization" ] } + }, + "behavior": [ "multi-part" ], + "items": [ + { + "id": "https://example.org/iiif/1/manifest", + "type": "Manifest", + "label": { "en": [ "Example Manifest 1" ] }, + "thumbnail": [ + { + "id": "https://example.org/manifest1/thumbnail.jpg", + "type": "Image", + "format": "image/jpeg" + } + ] + }, + { + "id": "https://example.org/iiif/2/manifest", + "type": "Manifest", + "label": { "en": [ "Example Manifest 2" ] }, + "thumbnail": [ + { + "id": "https://example.org/manifest2/thumbnail.jpg", + "type": "Image", + "format": "image/jpeg" + } + ] + } + ] +} diff --git a/schema/iiif_3_0.json b/schema/iiif_3_0.json index 3fd1403..cc443b6 100644 --- a/schema/iiif_3_0.json +++ b/schema/iiif_3_0.json @@ -383,7 +383,8 @@ "$ref": "#/classes/annotationPage" } } - } + }, + "required": ["id", "type", "label"] } ] }, diff --git a/tests/test_validator.py b/tests/test_validator.py index e672e90..dc85e20 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -145,11 +145,15 @@ def test07_check_manifest3(self): self.assertEqual(j['okay'], 1) for bad_data in ['fixtures/3/broken_simple_image.json', - 'fixtures/3/broken_choice.json']: + 'fixtures/3/broken_choice.json', + 'fixtures/3/broken_collection.json']: with open(bad_data, 'r') as fh: data = fh.read() j = json.loads(v.check_manifest(data, '3.0')) + if j['okay'] == 1: + print ("Expected {} to fail validation but it passed....".format(bad_data)) + self.assertEqual(j['okay'], 0) From cffa9e7fea3a5b8e0b2e6b55f35f327e84d5d1e2 Mon Sep 17 00:00:00 2001 From: Glen Robson Date: Wed, 4 Dec 2019 01:21:14 +0000 Subject: [PATCH 4/5] Supporting version 2.0 Image server Fixes https://github.com/IIIF/presentation-validator/issues/78 --- fixtures/3/version2image.json | 49 +++++++++++++++++++++++++++++++++++ schema/iiif_3_0.json | 20 +++++++++++--- tests/test_validator.py | 5 ++-- 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 fixtures/3/version2image.json diff --git a/fixtures/3/version2image.json b/fixtures/3/version2image.json new file mode 100644 index 0000000..2f6ea54 --- /dev/null +++ b/fixtures/3/version2image.json @@ -0,0 +1,49 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "https://example.org/iiif/book1/manifest", + "type": "Manifest", + "label": { "en": [ "Book 1" ] }, + "items": [ + { + "id": "https://example.org/iiif/book1/canvas/p1", + "type": "Canvas", + "label": { "none": [ "p. 1" ] }, + "height": 1000, + "width": 750, + "items": [ + { + "id": "https://example.org/iiif/book1/page/p1/1", + "type": "AnnotationPage", + "items": [ + { + "id": "https://example.org/iiif/book1/annotation/p0001-image", + "type": "Annotation", + "motivation": "painting", + "body": { + "id": "https://example.org/iiif/book1/page1/full/full/0/default.jpg", + "type": "Image", + "format": "image/jpeg", + "service": [ + { + "@id": "https://example.org/iiif2/image1/identifier", + "@type": "ImageService2", + "profile": "http://iiif.io/api/image/2/level2.json" + } + ], + "height": 2000, + "width": 1500 + }, + "target": "https://example.org/iiif/book1/canvas/p1" + } + ] + } + ], + "annotations": [ + { + "id": "https://example.org/iiif/book1/comments/p1/1", + "type": "AnnotationPage" + } + ] + } + ] +} diff --git a/schema/iiif_3_0.json b/schema/iiif_3_0.json index cc443b6..7b88084 100644 --- a/schema/iiif_3_0.json +++ b/schema/iiif_3_0.json @@ -209,14 +209,28 @@ "service": { "type": "array", "items": { - "allOf": [ - { "$ref": "#/types/class" }, + "oneOf": [ + { + "allOf": [ + { "$ref": "#/types/class" }, + { + "type": "object", + "properties": { + "profile": { "type": "string" }, + "service": { "$ref": "#/classes/service" } + } + } + ] + }, { "type": "object", "properties": { + "@id": { "$ref": "#/types/id" }, + "@type": { "type": "string" }, "profile": { "type": "string" }, "service": { "$ref": "#/classes/service" } - } + }, + "required": ["@id", "@type"] } ] } diff --git a/tests/test_validator.py b/tests/test_validator.py index dc85e20..b071407 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -134,7 +134,8 @@ def test07_check_manifest3(self): 'fixtures/3/full_example.json', 'fixtures/3/choice.json', 'fixtures/3/collection.json', - 'fixtures/3/collection_of_collections.json' + 'fixtures/3/collection_of_collections.json', + 'fixtures/3/version2image.json' ]: with open(good, 'r') as fh: data = fh.read() @@ -163,7 +164,7 @@ def printValidationerror(self, filename, errors): for err in errors: print(err['title']) if 'description' in err: - print (' Test description: {}'.format(err.schema['description'])) + print (' Test description: {}'.format(err['description'])) print('\n Path for error: {}'.format(err['path'])) print('\n Context: {}'.format(err['context'])) errorCount += 1 From f2b9b5c9cece296af178b92d011feacfdd1e5404 Mon Sep 17 00:00:00 2001 From: Glen Robson Date: Wed, 4 Dec 2019 01:47:47 +0000 Subject: [PATCH 5/5] Testing for Correct rights URIs Fixes https://github.com/IIIF/presentation-validator/issues/79 --- fixtures/3/full_example.json | 2 +- schema/iiif_3_0.json | 101 +++++++++++++++++++++++++++++++++-- schema/schemavalidator.py | 6 +-- tests/test_validator.py | 3 +- 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/fixtures/3/full_example.json b/fixtures/3/full_example.json index 40a2f2c..fc0060e 100644 --- a/fixtures/3/full_example.json +++ b/fixtures/3/full_example.json @@ -50,7 +50,7 @@ "behavior": [ "paged" ], "navDate": "1856-01-01T00:00:00Z", - "rights": "https://creativecommons.org/licenses/by/4.0/", + "rights": "http://creativecommons.org/licenses/by/4.0", "requiredStatement": { "label": { "en": [ "Attribution" ] }, "value": { "en": [ "Provided by Example Organization" ] } diff --git a/schema/iiif_3_0.json b/schema/iiif_3_0.json index 7b88084..1e3fe74 100644 --- a/schema/iiif_3_0.json +++ b/schema/iiif_3_0.json @@ -236,9 +236,104 @@ } }, "rights": { - "type": "string", - "format": "uri", - "pattern": "^http.*$" + "title": "Rights URI isn't from either Creative Commons or Rights statements.org. Both require http links.", + "oneOf": [ + { + "type": "string", + "format": "uri", + "pattern": "http://creativecommons.org/licenses/by/4.0" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://creativecommons.org/licenses/by-sa/4.0" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://creativecommons.org/licenses/by-nd/4.0" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://creativecommons.org/licenses/by-nc/4.0" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://creativecommons.org/licenses/by-nc-sa/4.0" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://creativecommons.org/licenses/by-nc-nd/4.0" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://creativecommons.org/publicdomain/mark/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/InC/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/InC-OW-EU/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/InC-EDU/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/InC-NC/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/InC-RUU/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/NoC-CR/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/NoC-NC/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/NoC-OKLR/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/NoC-US/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/CNE/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/UND/1.0/" + }, + { + "type": "string", + "format": "uri", + "pattern": "http://rightsstatements.org/vocab/NKC/1.0/" + } + ] }, "navDate": { "type": "string", diff --git a/schema/schemavalidator.py b/schema/schemavalidator.py index d7b832c..650b893 100755 --- a/schema/schemavalidator.py +++ b/schema/schemavalidator.py @@ -42,16 +42,12 @@ def validate(data, version, url): print('Validation Failed') errorCount = 1 for err in errors: - error += 'Error {} of {}.\n Message: {}'.format(errorCount, len(errors), err.message) detail = '' if 'title' in err.schema: detail = err.schema['title'] - # print (' Test message: {}'.format(detail)) description = '' if 'description' in err.schema: - description = err.schema['description'] - # print (' Test description: {}'.format(description)) - #print('\n Path for error: {}'.format(printPath(err.path, err.message))) + detail += err.schema['description'] context = err.instance #print (json.dumps(err.instance, indent=4)) if isinstance(context, dict): diff --git a/tests/test_validator.py b/tests/test_validator.py index b071407..f931731 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -163,8 +163,7 @@ def printValidationerror(self, filename, errors): errorCount = 1 for err in errors: print(err['title']) - if 'description' in err: - print (' Test description: {}'.format(err['description'])) + print(err['detail']) print('\n Path for error: {}'.format(err['path'])) print('\n Context: {}'.format(err['context'])) errorCount += 1