diff --git a/fixtures/3/collection_of_canvases.json b/fixtures/3/collection_of_canvases.json new file mode 100644 index 0000000..9834598 --- /dev/null +++ b/fixtures/3/collection_of_canvases.json @@ -0,0 +1,32 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "https://example.org/iiif/collection/invalid", + "type": "Collection", + "label": { "en": [ "Invalid collection of canvases and ranges" ] }, + "items": [ + { + "id": "https://example.org/iiif/canvas/1", + "type": "Canvas", + "label": { "en": [ "Canvas 1" ] }, + "thumbnail": [ + { + "id": "https://example.org/manifest1/thumbnail.jpg", + "type": "Image", + "format": "image/jpeg" + } + ] + }, + { + "id": "https://example.org/iiif/2/range/1", + "type": "Range", + "label": { "en": [ "Range example" ] }, + "thumbnail": [ + { + "id": "https://example.org/manifest2/thumbnail.jpg", + "type": "Image", + "format": "image/jpeg" + } + ] + } + ] +} diff --git a/fixtures/3/range_range.json b/fixtures/3/range_range.json new file mode 100644 index 0000000..177ab21 --- /dev/null +++ b/fixtures/3/range_range.json @@ -0,0 +1,108 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "https://example.org/iiif/book1/manifest", + "type": "Manifest", + "label": { "en": [ "Range containng ranges" ] }, + "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/max/0/default.jpg", + "type": "Image", + "format": "image/jpeg", + "service": [ + { + "id": "https://example.org/iiif/book1/page1", + "type": "ImageService3", + "profile": "level2" + } + ], + "height": 2000, + "width": 1500 + }, + "target": "https://example.org/iiif/book1/canvas/p1" + } + ] + } + ] + }, + { + "id": "https://example.org/iiif/book1/canvas/p2", + "type": "Canvas", + "label": { "none": [ "p. 2" ] }, + "height": 1000, + "width": 750, + "items": [ + { + "id": "https://example.org/iiif/book1/page/p2/1", + "type": "AnnotationPage", + "items": [ + { + "id": "https://example.org/iiif/book1/annotation/p0002-image", + "type": "Annotation", + "motivation": "painting", + "body": { + "id": "https://example.org/iiif/book1/page2/full/max/0/default.jpg", + "type": "Image", + "format": "image/jpeg", + "service": [ + { + "id": "https://example.org/iiif/book1/page2", + "type": "ImageService3", + "profile": "level2" + } + ], + "height": 2000, + "width": 1500 + }, + "target": "https://example.org/iiif/book1/canvas/p2" + } + ] + } + ] + } + ], + + "structures": [ + { + "id": "https://example.org/iiif/book1/range/r0", + "type": "Range", + "label": { "en": [ "Table of Contents" ] }, + "items": [ + { + "id": "https://example.org/iiif/book1/range/r1", + "type": "Range", + "label": { "en": [ "toc2" ] }, + "items": [ + { + "id": "https://example.org/iiif/book1/canvas/p1", + "type": "Canvas" + }, + { + "id": "https://example.org/iiif/book1/range/r1", + "type": "Range", + "label": { "en": [ "toc2" ] }, + "items":[{ + "id": "https://example.org/iiif/book1/canvas/p2", + "type": "Canvas" + }] + } + ] + } + ] + } + ] +} diff --git a/iiif-presentation-validator.py b/iiif-presentation-validator.py index 9c1edbb..48b6927 100755 --- a/iiif-presentation-validator.py +++ b/iiif-presentation-validator.py @@ -111,13 +111,13 @@ def check_manifest(self, data, version, url=None, warnings=[]): # Passed! okay = 1 except KeyError as e: - print ('Failed falidation due to:') + print ('Failed validation due to:') traceback.print_exc() err = 'Failed due to KeyError {}, check trace for details'.format(e) okay = 0 except Exception as e: # Failed - print ('Failed falidation due to:') + print ('Failed validation due to:') traceback.print_exc() err = e okay = 0 diff --git a/schema/error_processor.py b/schema/error_processor.py index 2c6ce7d..f2ad3a8 100755 --- a/schema/error_processor.py +++ b/schema/error_processor.py @@ -116,6 +116,7 @@ def diagnoseWhichOneOf(self, error_path, IIIFJsonPath): # its not another oneOf error if addErrors: # if error is also a oneOf then diagnoise again + #print ('Schema path: {} error path: {}'.format(err.absolute_schema_path, error_path)) if err.absolute_schema_path[-1] == 'oneOf' and err.absolute_schema_path != error_path and 'rights' not in err.absolute_schema_path: error_path.append(oneOfIndex) # this is is related to one of the original oneOfs at index oneOfIndex error_path.extend(err.absolute_schema_path) # but we found another oneOf test at this location @@ -323,9 +324,10 @@ def isTypeMatch(self, iiifPath, iiif_asset, schemaType, IIIFJsonPath): indexDelta += len(str(indexes[count])) -1 count += 1 - #print ('JsonPath: {} IIIF Path {} '.format(iiifPath, IIIFJsonPath)) + #print ('JsonPath: {} IIIF Path {} type: {}'.format(iiifPath, IIIFJsonPath, schemaType)) path = parse(iiifPath) results = path.find(iiif_asset) + #print ('Path: {} Results: '.format(path, results)) if not results: # type not found so return True as this maybe the correct error return True diff --git a/schema/iiif_3_0.json b/schema/iiif_3_0.json index dbe958c..c6fea39 100644 --- a/schema/iiif_3_0.json +++ b/schema/iiif_3_0.json @@ -23,6 +23,10 @@ }, "additionalProperties": false }, + "dimension": { + "type": "integer", + "exclusiveMinimum": 0 + }, "keyValueString": { "type": "object", "properties": { @@ -59,7 +63,7 @@ }, "duration": { "type": "number", - "minimum": 0 + "exclusiveMinimum": 0 }, "external": { "type": "array", @@ -77,6 +81,28 @@ } ] } + }, + "reference": { + "allOf": [ + { + "type": "object", + "additionalProperties": true, + "properties": { + "id": { "$ref": "#/types/id" }, + "label": {"$ref": "#/types/lngString" }, + "type": { + "type": "string", + "pattern": "^Manifest$|^AnnotationPage$|^Collection$|^AnnotationCollection$|^Canvas$|^Range$" + }, + "thumbnail": { + "type": "array", + "items": { "$ref": "#/classes/resource" } + } + }, + "required": ["id", "type", "label"], + "not": { "required": [ "items" ] } + } + ] } }, @@ -138,19 +164,17 @@ "type": { "type": "string" }, - "height": { - "type": "integer" - }, - "width": { - "type": "integer" - }, - "duration": { - "$ref": "#/types/duration" - }, + "height": { "$ref": "#/types/dimension" }, + "width": { "$ref": "#/types/dimension" }, + "duration": { "$ref": "#/types/duration" }, "language": { "type": "string"}, "service": { "$ref": "#/classes/service" }, "format": { "$ref": "#/types/format" }, "label": {"$ref": "#/types/lngString" }, + "thumbnail": { + "type": "array", + "items": { "$ref": "#/classes/resource" } + }, "annotations": { "type": "array", "items": { @@ -409,7 +433,8 @@ "type": "array", "items": { "anyOf": [ - { "$ref": "#/classes/reference" }, + { "$ref": "#/classes/manifestRef" }, + { "$ref": "#/classes/collectionRef" }, { "$ref": "#/classes/collection" } ] } @@ -498,28 +523,62 @@ } ] }, - "reference": { + "manifestRef": { "allOf": [ + { "$ref": "#/types/reference" }, { "type": "object", - "additionalProperties": true, "properties": { - "id": { "$ref": "#/types/id" }, - "label": {"$ref": "#/types/lngString" }, "type": { "type": "string", - "pattern": "^Manifest$|^AnnotationPage$|^Collection$|^AnnotationCollection$|^Canvas$|^Range$" - }, - "thumbnail": { - "type": "array", - "items": { "$ref": "#/classes/resource" } + "pattern": "^Manifest$" } - }, - "required": ["id", "type", "label"], - "not": { "required": [ "items" ] } + } } - ] - }, + ] + }, + "collectionRef": { + "allOf": [ + { "$ref": "#/types/reference" }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "pattern": "^Collection$" + } + } + } + ] + }, + "rangeRef": { + "allOf": [ + { "$ref": "#/types/reference" }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "pattern": "^Range$" + } + } + } + ] + }, + "canvasRef": { + "allOf": [ + { "$ref": "#/types/reference" }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "pattern": "^Canvas$" + } + } + } + ] + }, "canvas": { "allOf": [ { "$ref": "#/types/class" }, @@ -531,15 +590,9 @@ "pattern": "^Canvas$", "default": "Canvas" }, - "height": { - "type": "integer" - }, - "width": { - "type": "integer" - }, - "duration": { - "$ref": "#/types/duration" - }, + "height": { "$ref": "#/types/dimension" }, + "width": { "$ref": "#/types/dimension" }, + "duration": { "$ref": "#/types/duration" }, "metadata": { "$ref": "#/classes/metadata" }, "summary": { "$ref": "#/types/lngString" }, "requiredStatement": { "$ref": "#/types/keyValueString" }, @@ -569,6 +622,7 @@ } } }, + "required": ["items"], "anyOf":[ { "required": ["width"] }, { "required": ["height"] }, @@ -597,6 +651,10 @@ "first": { "$ref": "#/classes/annotationPage" }, "last": { "$ref": "#/classes/annotationPage" }, "service": { "$ref": "#/classes/service" }, + "thumbnail": { + "type": "array", + "items": { "$ref": "#/classes/resource" } + }, "items": { "type": "array", "items": { @@ -619,6 +677,10 @@ }, "label": {"$ref": "#/types/lngString" }, "service": { "$ref": "#/classes/service" }, + "thumbnail": { + "type": "array", + "items": { "$ref": "#/classes/resource" } + }, "items": { "type": "array", "items": { @@ -641,6 +703,10 @@ "default": "Annotation" }, "service": { "$ref": "#/classes/service" }, + "thumbnail": { + "type": "array", + "items": { "$ref": "#/classes/resource" } + }, "motivation": { "oneOf": [ { "type": "string" }, @@ -781,13 +847,15 @@ "$ref": "#/classes/annotationPage" } }, + "thumbnail": { + "type": "array", + "items": { "$ref": "#/classes/resource" } + }, "items": { "type": "array", "items": { "oneOf": [ - { - "$ref": "#/classes/specificResource" - }, + { "$ref": "#/classes/specificResource" }, { "allOf": [ { "$ref": "#/types/class" }, @@ -804,13 +872,14 @@ } ] }, - { - "$ref": "#/classes/range" - } + { "$ref": "#/classes/range" }, + { "$ref": "#/classes/canvasRef" }, + { "$ref": "#/classes/rangeRef" } ] } } - } + }, + "required":["items"] } ] } diff --git a/tests/test_validator.py b/tests/test_validator.py index 0c76921..c3bba34 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -145,7 +145,8 @@ def test07_check_manifest3(self): 'fixtures/3/multi_bodies.json', 'fixtures/3/publicdomain.json', 'fixtures/3/navPlace.json', - 'fixtures/3/anno_source.json' + 'fixtures/3/anno_source.json', + 'fixtures/3/range_range.json' ]: with open(good, 'r') as fh: print ('Testing: {}'.format(good)) @@ -164,7 +165,8 @@ def test07_check_manifest3(self): 'fixtures/3/broken_choice.json', 'fixtures/3/broken_collection.json', 'fixtures/3/broken_embedded_annos.json', - 'fixtures/3/non_cc_license.json']: + 'fixtures/3/non_cc_license.json', + 'fixtures/3/collection_of_canvases.json']: with open(bad_data, 'r') as fh: data = fh.read() j = json.loads(v.check_manifest(data, '3.0')) @@ -184,7 +186,7 @@ def test08_errortrees(self): errorParser = IIIFErrorParser(schema, iiif_json) - print (errorParser) + #print (errorParser) # annotationPage path = [ u'oneOf', 2, u'properties', u'items', u'items', u'properties', u'type', u'pattern'] iiifPath = [u'items', 0, u'type']