From 33e8177b258737f1b034a499236867477054933e Mon Sep 17 00:00:00 2001
From: Eva Bardou <ebardou@teklia.com>
Date: Fri, 5 Mar 2021 10:54:49 +0000
Subject: [PATCH] Add create_elements function

---
 arkindex_worker/worker.py                   |  59 ++++
 tests/test_elements_worker/test_elements.py | 284 ++++++++++++++++++++
 2 files changed, 343 insertions(+)

diff --git a/arkindex_worker/worker.py b/arkindex_worker/worker.py
index e00e4952..3bfc1be0 100644
--- a/arkindex_worker/worker.py
+++ b/arkindex_worker/worker.py
@@ -394,6 +394,65 @@ class ElementsWorker(BaseWorker):
 
         return sub_element["id"]
 
+    def create_elements(self, parent, elements):
+        """
+        Create children elements on the given element through API
+        Return the IDs of created elements
+        """
+        assert parent and isinstance(
+            parent, Element
+        ), "element shouldn't be null and should be of type Element"
+        assert elements and isinstance(
+            elements, list
+        ), "elements shouldn't be null and should be of type list"
+
+        for index, element in enumerate(elements):
+            assert isinstance(
+                element, dict
+            ), f"Element at index {index} in elements: Should be of type dict"
+
+            name = element.get("name")
+            assert name and isinstance(
+                name, str
+            ), f"Element at index {index} in elements: name shouldn't be null and should be of type str"
+
+            type = element.get("type")
+            assert type and isinstance(
+                type, str
+            ), f"Element at index {index} in elements: type shouldn't be null and should be of type str"
+
+            polygon = element.get("polygon")
+            assert polygon and isinstance(
+                polygon, list
+            ), f"Element at index {index} in elements: polygon shouldn't be null and should be of type list"
+            assert (
+                len(polygon) >= 3
+            ), f"Element at index {index} in elements: polygon should have at least three points"
+            assert all(
+                isinstance(point, list) and len(point) == 2 for point in polygon
+            ), f"Element at index {index} in elements: polygon points should be lists of two items"
+            assert all(
+                isinstance(coord, (int, float)) for point in polygon for coord in point
+            ), f"Element at index {index} in elements: polygon points should be lists of two numbers"
+
+        if self.is_read_only:
+            logger.warning("Cannot create elements as this worker is in read-only mode")
+            return
+
+        created_ids = self.api_client.request(
+            "CreateElements",
+            id=parent.id,
+            body={
+                "worker_version": self.worker_version_id,
+                "elements": elements,
+            },
+        )
+
+        for element in elements:
+            self.report.add_element(parent.id, element["type"])
+
+        return created_ids
+
     def create_transcription(self, element, text, type=None, score=None):
         """
         Create a transcription on the given element through the API.
diff --git a/tests/test_elements_worker/test_elements.py b/tests/test_elements_worker/test_elements.py
index 87e9363c..a1faf67f 100644
--- a/tests/test_elements_worker/test_elements.py
+++ b/tests/test_elements_worker/test_elements.py
@@ -392,6 +392,290 @@ def test_create_sub_element(responses, mock_elements_worker):
     assert sub_element_id == "12345678-1234-1234-1234-123456789123"
 
 
+def test_create_elements_wrong_parent(mock_elements_worker):
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=None,
+            elements=[],
+        )
+    assert str(e.value) == "element shouldn't be null and should be of type Element"
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent="not element type",
+            elements=[],
+        )
+    assert str(e.value) == "element shouldn't be null and should be of type Element"
+
+
+def test_create_elements_wrong_elements(mock_elements_worker):
+    elt = Element({"zone": None})
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=None,
+        )
+    assert str(e.value) == "elements shouldn't be null and should be of type list"
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements="not a list",
+        )
+    assert str(e.value) == "elements shouldn't be null and should be of type list"
+
+
+def test_create_elements_wrong_elements_instance(mock_elements_worker):
+    elt = Element({"zone": None})
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=["not a dict"],
+        )
+    assert str(e.value) == "Element at index 0 in elements: Should be of type dict"
+
+
+def test_create_elements_wrong_elements_name(mock_elements_worker):
+    elt = Element({"zone": None})
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": None,
+                    "type": "something",
+                    "polygon": [[1, 1], [2, 2], [2, 1], [1, 2]],
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: name shouldn't be null and should be of type str"
+    )
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": 1234,
+                    "type": "something",
+                    "polygon": [[1, 1], [2, 2], [2, 1], [1, 2]],
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: name shouldn't be null and should be of type str"
+    )
+
+
+def test_create_elements_wrong_elements_type(mock_elements_worker):
+    elt = Element({"zone": None})
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": None,
+                    "polygon": [[1, 1], [2, 2], [2, 1], [1, 2]],
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: type shouldn't be null and should be of type str"
+    )
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": 1234,
+                    "polygon": [[1, 1], [2, 2], [2, 1], [1, 2]],
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: type shouldn't be null and should be of type str"
+    )
+
+
+def test_create_elements_wrong_elements_polygon(mock_elements_worker):
+    elt = Element({"zone": None})
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": "something",
+                    "polygon": None,
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: polygon shouldn't be null and should be of type list"
+    )
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": "something",
+                    "polygon": "not a polygon",
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: polygon shouldn't be null and should be of type list"
+    )
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": "something",
+                    "polygon": [[1, 1], [2, 2]],
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: polygon should have at least three points"
+    )
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": "something",
+                    "polygon": [[1, 1, 1], [2, 2, 1], [2, 1, 1], [1, 2, 1]],
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: polygon points should be lists of two items"
+    )
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": "something",
+                    "polygon": [[1], [2], [2], [1]],
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: polygon points should be lists of two items"
+    )
+
+    with pytest.raises(AssertionError) as e:
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": "something",
+                    "polygon": [["not a coord", 1], [2, 2], [2, 1], [1, 2]],
+                }
+            ],
+        )
+    assert (
+        str(e.value)
+        == "Element at index 0 in elements: polygon points should be lists of two numbers"
+    )
+
+
+def test_create_elements_api_error(responses, mock_elements_worker):
+    elt = Element({"id": "12341234-1234-1234-1234-123412341234"})
+    responses.add(
+        responses.POST,
+        "http://testserver/api/v1/element/12341234-1234-1234-1234-123412341234/children/bulk/",
+        status=500,
+    )
+
+    with pytest.raises(ErrorResponse):
+        mock_elements_worker.create_elements(
+            parent=elt,
+            elements=[
+                {
+                    "name": "0",
+                    "type": "something",
+                    "polygon": [[1, 1], [2, 2], [2, 1], [1, 2]],
+                }
+            ],
+        )
+
+    assert len(responses.calls) == 3
+    assert [call.request.url for call in responses.calls] == [
+        "http://testserver/api/v1/user/",
+        "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/",
+        "http://testserver/api/v1/element/12341234-1234-1234-1234-123412341234/children/bulk/",
+    ]
+
+
+def test_create_elements(responses, mock_elements_worker):
+    elt = Element({"id": "12341234-1234-1234-1234-123412341234"})
+    responses.add(
+        responses.POST,
+        "http://testserver/api/v1/element/12341234-1234-1234-1234-123412341234/children/bulk/",
+        status=200,
+        json=[{"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08"}],
+    )
+
+    created_ids = mock_elements_worker.create_elements(
+        parent=elt,
+        elements=[
+            {
+                "name": "0",
+                "type": "something",
+                "polygon": [[1, 1], [2, 2], [2, 1], [1, 2]],
+            }
+        ],
+    )
+
+    assert len(responses.calls) == 3
+    assert [call.request.url for call in responses.calls] == [
+        "http://testserver/api/v1/user/",
+        "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/",
+        "http://testserver/api/v1/element/12341234-1234-1234-1234-123412341234/children/bulk/",
+    ]
+    assert json.loads(responses.calls[2].request.body) == {
+        "elements": [
+            {
+                "name": "0",
+                "type": "something",
+                "polygon": [[1, 1], [2, 2], [2, 1], [1, 2]],
+            }
+        ],
+        "worker_version": "12341234-1234-1234-1234-123412341234",
+    }
+    assert created_ids == [{"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08"}]
+
+
 def test_list_element_children_wrong_element(mock_elements_worker):
     with pytest.raises(AssertionError) as e:
         mock_elements_worker.list_element_children(element=None)
-- 
GitLab