Repository: iiif-prezi/iiif-prezi3 (language: python)

Issue:
Add Annotation
<!-- Add a short name of the proposed helper method in the Title above -->

## Description
<!-- Provide a short description of the helper method -->
This helper will add an existing Annotation to the object's `annotations` key

## Function name / Parameters / Return
<!-- Suggest a name for the helper's function, the parameters it should accept, and what it should return (if anything) -->
Function name: `.add_annotation(annotation)`

Parameters: `annotation` - the Prezi3 `Annotation` object to be added

Returns: `None`

## Objects
<!-- Which objects should this helper apply to? -->
- [ ] Base Object (all Prezi3 Classes)
- [x] Collection
- [x] Manifest
- [x] Canvas
- [x] Range
- [ ] AnnotationCollection
- [ ] AnnotationPage
- [ ] Annotation
- [x] Content Resource - Image
- [x] Content Resource - Video
- [x] Content Resource - Text
- [ ] Service
- [ ] Provider
- [ ] Other - please list below
<!-- Add other Objects/Classes here -->

## Considerations
<!-- Any extra considerations for the behavior of this helper -->
Annotations must exist within an `AnnotationPage` object, so the helper must create and add one if one is not already present. Otherwise the Annotation should be added to the first existing `AnnotationPage`

Make Annotation
## Description

This helper will create an Annotation and add it to the object's `annotations` key (via the add_annotation (https://github.com/iiif-prezi/iiif-prezi3/issues/40) helper), and returns it.


## Function name / Parameters / Return

Function name: `.make_annotation()`

Parameters: None

Returns: An Annotation object


## Objects
<!-- Which objects should this helper apply to? -->
- [ ] Base Object (all Prezi3 Classes)
- [x] Collection
- [x] Manifest
- [x] Canvas
- [x] Range
- [ ] AnnotationCollection
- [ ] AnnotationPage
- [ ] Annotation
- [x] Content Resource - Image
- [x] Content Resource - Video
- [x] Content Resource - Text
- [ ] Service
- [ ] Provider
- [ ] Other - please list below

## Considerations

Annotations must exist within an AnnotationPage object, so the helper must create and add one if one is not already present. Otherwise the Annotation should be added to the first existing AnnotationPage



Discussion / hints:
Use case for content resources is if you want to annotate a particular image / layer rather than the constructed canvas. 
I was wondering if a user wants to create multiple annotation pages (if this makes sense) should we provide a parameter to append the new annotation to a new annotation page?


Fix patch:
diff --git a/iiif_prezi3/helpers/__init__.py b/iiif_prezi3/helpers/__init__.py
index fa5f6da..46ada0d 100644
--- a/iiif_prezi3/helpers/__init__.py
+++ b/iiif_prezi3/helpers/__init__.py
@@ -1,3 +1,4 @@
+from .annotation_helpers import AnnotationHelpers  # noqa: F401
 from .add_item import AddItem, AddItemByReference  # noqa: F401
 from .add_label import AddLabel  # noqa: F401
 from .add_metadata import AddMetadata  # noqa: F401
diff --git a/iiif_prezi3/helpers/annotation_helpers.py b/iiif_prezi3/helpers/annotation_helpers.py
new file mode 100644
index 0000000..9053296
--- /dev/null
+++ b/iiif_prezi3/helpers/annotation_helpers.py
@@ -0,0 +1,40 @@
+from ..loader import monkeypatch_schema
+from ..skeleton import (Annotation, AnnotationPage, Canvas, Collection,
+                        Manifest, Range)
+
+
+class AnnotationHelpers:
+
+    def add_annotation(self, annotation):
+        """Adds the annotation object to the (AnnotationPage object in the) annotations property.
+
+        Args:
+          annotation (iiif-prezi3.Annotation): the Annotation to add
+
+        Creates an AnnotationPage object if it doesn't exist.
+        """
+        if not self.annotations:
+            self.annotations = list()
+
+        if len(self.annotations) == 0:
+            # add empty AnnotationPage
+            anno_page = AnnotationPage(items=[])
+            self.annotations.append(anno_page)
+        else:
+            anno_page = self.annotations[0]
+
+        anno_page.items.append(annotation)
+
+        return annotation
+
+    def make_annotation(self, **kwargs):
+        """Creates an annotation object and adds it to the annotations property using .add_annotation().
+
+        Args: (see iiif-prezi3.Annotation)
+        """
+        annotation = Annotation(**kwargs)
+        self.add_annotation(annotation)
+        return annotation
+
+
+monkeypatch_schema([Collection, Manifest, Canvas, Range], AnnotationHelpers)


Test patch:
diff --git a/tests/test_annotation_helpers.py b/tests/test_annotation_helpers.py
new file mode 100644
index 0000000..5eac6ba
--- /dev/null
+++ b/tests/test_annotation_helpers.py
@@ -0,0 +1,35 @@
+import unittest
+
+from iiif_prezi3 import Annotation, AnnotationPage, Manifest
+
+
+class AnnotationHelpersTests(unittest.TestCase):
+
+    def test_manifest_add_annotation(self):
+        anno_target = 'http://iiif.example.org/prezi/Canvas/0'
+        anno = Annotation(target=anno_target)
+        m = Manifest(id='http://iiif.example.org/prezi/Manifest/0', label={'en': ['Test Manifest']})
+        m.add_annotation(anno)
+        # test Manifest
+        self.assertEqual(len(m.annotations), 1)
+        anno_page = m.annotations[0]
+        self.assertTrue(isinstance(anno_page, AnnotationPage), '`annotations` should contain an AnnotationPage')
+        self.assertEqual(len(anno_page.items), 1)
+        self.assertTrue(anno_page.items[0].id.startswith('http://example.org/iiif/'))  # autogenerated Annotation id
+        self.assertEqual(anno_page.items[0].target, anno_target)
+
+    def test_manifest_make_annotation(self):
+        anno_target = 'http://iiif.example.org/prezi/Canvas/0'
+        m = Manifest(id='http://iiif.example.org/prezi/Manifest/0', label={'en': ['Test Manifest']})
+        anno = m.make_annotation(target=anno_target)
+        # test returned Annotation
+        self.assertTrue(isinstance(anno, Annotation), 'returned annotation should be an Annotation')
+        self.assertTrue(anno.id.startswith('http://example.org/iiif/'))
+        self.assertEqual(anno.target, anno_target)
+        # test Manifest
+        self.assertEqual(len(m.annotations), 1)
+        anno_page = m.annotations[0]
+        self.assertTrue(isinstance(anno_page, AnnotationPage), '`annotations` should contain an AnnotationPage')
+        self.assertEqual(len(anno_page.items), 1)
+        self.assertEqual(anno_page.items[0].target, anno_target)
+        self.assertTrue(anno.id.startswith('http://example.org/iiif/'))  # autogenerated Annotation id