diff --git a/fixtures/3/anno_pointselector.json b/fixtures/3/anno_pointselector.json index c829189..0ed6f44 100644 --- a/fixtures/3/anno_pointselector.json +++ b/fixtures/3/anno_pointselector.json @@ -13,6 +13,7 @@ "format": "text/plain" }, "target": { + "type": "SpecificResource", "source": "https://preview.iiif.io/cookbook/0103-poetry/recipe/0103-poetry-reading-annotations/canvas/1", "selector": { "type": "PointSelector", diff --git a/fixtures/3/anno_source.json b/fixtures/3/anno_source.json new file mode 100644 index 0000000..8b80dd2 --- /dev/null +++ b/fixtures/3/anno_source.json @@ -0,0 +1,98 @@ +{ + "@context": "http://iiif.io/api/presentation/3/context.json", + "id": "https://preview.iiif.io/cookbook/0326-annotating-image-layer/recipe/0326-annotating-image-layer/manifest.json", + "type": "Manifest", + "label": { + "en": [ + "Choice Example with layer specific annotation" + ] + }, + "items": [ + { + "id": "https://preview.iiif.io/cookbook/0326-annotating-image-layer/recipe/0326-annotating-image-layer/canvas/p1", + "type": "Canvas", + "height": 1271, + "width": 2000, + "items": [ + { + "id": "https://preview.iiif.io/cookbook/0326-annotating-image-layer/recipe/0326-annotating-image-layer/page/p1/1", + "type": "AnnotationPage", + "items": [ + { + "id": "https://preview.iiif.io/cookbook/0326-annotating-image-layer/recipe/0326-annotating-image-layer/annotation/p0001-image", + "type": "Annotation", + "motivation": "painting", + "body": { + "type": "Choice", + "items": [ + { + "id": "https://iiif.io/api/image/3.0/example/reference/421e65be2ce95439b3ad6ef1f2ab87a9-dee-natural/full/max/0/default.jpg", + "type": "Image", + "label": { + "en": [ + "Natural Light" + ] + }, + "format": "image/jpeg", + "height": 1271, + "width": 2000, + "service": [ + { + "id": "https://iiif.io/api/image/3.0/example/reference/421e65be2ce95439b3ad6ef1f2ab87a9-dee-natural", + "type": "ImageService3", + "profile": "level1" + } + ] + }, + { + "id": "https://iiif.io/api/image/3.0/example/reference/421e65be2ce95439b3ad6ef1f2ab87a9-dee-xray/full/max/0/default.jpg", + "type": "Image", + "label": { + "en": [ + "X-Ray" + ] + }, + "format": "image/jpeg", + "height": 1271, + "width": 2000, + "service": [ + { + "id": "https://iiif.io/api/image/3.0/example/reference/421e65be2ce95439b3ad6ef1f2ab87a9-dee-xray", + "type": "ImageService3", + "profile": "level1" + } + ], + "annotations": [ + { + "id": "https://preview.iiif.io/cookbook/0326-annotating-image-layer/recipe/0326-annotating-image-layer/page/p2/1", + "type": "AnnotationPage", + "items": [ + { + "id": "https://preview.iiif.io/cookbook/0326-annotating-image-layer/recipe/0326-annotating-image-layer/annotation/p0002-tag", + "type": "Annotation", + "motivation": "tagging", + "body": { + "type": "TextualBody", + "value": "A group of skulls.", + "language": "en", + "format": "text/plain" + }, + "target": { + "source": "https://iiif.io/api/image/3.0/example/reference/421e65be2ce95439b3ad6ef1f2ab87a9-dee-xray/full/max/0/default.jpg#xywh=810,900,260,370", + "scope": "https://preview.iiif.io/cookbook/0326-annotating-image-layer/recipe/0326-annotating-image-layer/canvas/p1" + } + } + ] + } + ] + } + ] + }, + "target": "https://preview.iiif.io/cookbook/0326-annotating-image-layer/recipe/0326-annotating-image-layer/canvas/p1" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/schema/iiif_3_0.json b/schema/iiif_3_0.json index 0c57963..dbe958c 100644 --- a/schema/iiif_3_0.json +++ b/schema/iiif_3_0.json @@ -707,6 +707,15 @@ }, { "$ref": "#/classes/specificResource" + }, + { + "title": "Annotation target can also contain just scope and source", + "type": "object", + "properties": { + "source": { "$ref": "#/types/id" }, + "scope": { "$ref": "#/types/id" } + }, + "required": ["source", "scope"] } ] }, diff --git a/tests/test_validator.py b/tests/test_validator.py index 8257a99..0c76921 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -144,15 +144,21 @@ def test07_check_manifest3(self): 'fixtures/3/extension_anno.json', 'fixtures/3/multi_bodies.json', 'fixtures/3/publicdomain.json', - 'fixtures/3/navPlace.json' + 'fixtures/3/navPlace.json', + 'fixtures/3/anno_source.json' ]: with open(good, 'r') as fh: + print ('Testing: {}'.format(good)) data = fh.read() j = json.loads(v.check_manifest(data, '3.0')) if j['okay'] != 1: - self.printValidationerror(good, j['errorList']) + if 'errorList' in j: + self.printValidationerror(good, j['errorList']) + else: + print ('Failed to find errors but manifest {} failed validation'.format(good)) + print (j) - self.assertEqual(j['okay'], 1) + self.assertEqual(j['okay'], 1, 'Expected manifest {} to pass validation but it failed'.format(good)) for bad_data in ['fixtures/3/broken_simple_image.json', 'fixtures/3/broken_choice.json',