You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
9.6 KiB

3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
2 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
  1. from rest_framework import status
  2. from rest_framework.reverse import reverse
  3. from api.tests.utils import CRUDMixin
  4. from examples.tests.utils import make_doc
  5. from labels.models import Category, Span, TextLabel
  6. from label_types.tests.utils import make_label
  7. from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ
  8. from projects.tests.utils import prepare_project
  9. from users.tests.utils import make_user
  10. from .utils import make_annotation
  11. class TestLabelList:
  12. model = Category
  13. task = DOCUMENT_CLASSIFICATION
  14. view_name = "annotation_list"
  15. @classmethod
  16. def setUpTestData(cls):
  17. cls.project = prepare_project(task=cls.task)
  18. cls.non_member = make_user()
  19. doc = make_doc(cls.project.item)
  20. for member in cls.project.members:
  21. cls.make_annotation(doc, member)
  22. cls.url = reverse(viewname=cls.view_name, args=[cls.project.item.id, doc.id])
  23. @classmethod
  24. def make_annotation(cls, doc, member):
  25. make_annotation(cls.task, doc=doc, user=member)
  26. def test_allows_project_member_to_fetch_annotation(self):
  27. for member in self.project.members:
  28. response = self.assert_fetch(member, status.HTTP_200_OK)
  29. self.assertEqual(len(response.data), 1) # fetch only own annotation
  30. def test_denies_non_project_member_to_fetch_annotation(self):
  31. self.assert_fetch(self.non_member, status.HTTP_403_FORBIDDEN)
  32. def test_denies_unauthenticated_user_to_fetch_annotation(self):
  33. self.assert_fetch(self.non_member, status.HTTP_403_FORBIDDEN)
  34. def test_allows_project_member_to_bulk_delete_annotation(self):
  35. self.assert_delete(self.project.admin, status.HTTP_204_NO_CONTENT)
  36. count = self.model.objects.count()
  37. self.assertEqual(count, 2) # delete only own annotation
  38. class TestCategoryList(TestLabelList, CRUDMixin):
  39. model = Category
  40. task = DOCUMENT_CLASSIFICATION
  41. view_name = "category_list"
  42. class TestSpanList(TestLabelList, CRUDMixin):
  43. model = Span
  44. task = SEQUENCE_LABELING
  45. view_name = "span_list"
  46. @classmethod
  47. def make_annotation(cls, doc, member):
  48. make_annotation(cls.task, doc=doc, user=member, start_offset=0, end_offset=1)
  49. class TestTextList(TestLabelList, CRUDMixin):
  50. model = TextLabel
  51. task = SEQ2SEQ
  52. view_name = "text_list"
  53. class TestSharedLabelList:
  54. model = Category
  55. task = DOCUMENT_CLASSIFICATION
  56. view_name = "annotation_list"
  57. @classmethod
  58. def setUpTestData(cls):
  59. cls.project = prepare_project(task=cls.task, collaborative_annotation=True)
  60. doc = make_doc(cls.project.item)
  61. for member in cls.project.members:
  62. cls.make_annotation(doc, member)
  63. cls.url = reverse(viewname=cls.view_name, args=[cls.project.item.id, doc.id])
  64. @classmethod
  65. def make_annotation(cls, doc, member):
  66. make_annotation(cls.task, doc=doc, user=member)
  67. def test_allows_project_member_to_fetch_all_annotation(self):
  68. for member in self.project.members:
  69. response = self.assert_fetch(member, status.HTTP_200_OK)
  70. self.assertEqual(len(response.data), 3)
  71. def test_allows_project_member_to_bulk_delete_annotation(self):
  72. self.assert_delete(self.project.admin, status.HTTP_204_NO_CONTENT)
  73. count = self.model.objects.count()
  74. self.assertEqual(count, 0) # delete all annotation in the doc
  75. class TestSharedCategoryList(TestSharedLabelList, CRUDMixin):
  76. model = Category
  77. task = DOCUMENT_CLASSIFICATION
  78. view_name = "category_list"
  79. class TestSharedSpanList(TestSharedLabelList, CRUDMixin):
  80. model = Span
  81. task = SEQUENCE_LABELING
  82. view_name = "span_list"
  83. start_offset = 0
  84. @classmethod
  85. def make_annotation(cls, doc, member):
  86. make_annotation(cls.task, doc=doc, user=member, start_offset=cls.start_offset, end_offset=cls.start_offset + 1)
  87. cls.start_offset += 1
  88. class TestSharedTextList(TestSharedLabelList, CRUDMixin):
  89. model = TextLabel
  90. task = SEQ2SEQ
  91. view_name = "text_list"
  92. class TestDataLabeling:
  93. task = DOCUMENT_CLASSIFICATION
  94. view_name = "annotation_list"
  95. def setUp(self):
  96. self.project = prepare_project(task=self.task)
  97. self.non_member = make_user()
  98. doc = make_doc(self.project.item)
  99. self.data = self.create_data()
  100. self.url = reverse(viewname=self.view_name, args=[self.project.item.id, doc.id])
  101. def create_data(self):
  102. label = make_label(self.project.item)
  103. return {"label": label.id}
  104. def test_allows_project_member_to_annotate(self):
  105. for member in self.project.members:
  106. self.assert_create(member, status.HTTP_201_CREATED)
  107. def test_denies_non_project_member_to_annotate(self):
  108. self.assert_create(self.non_member, status.HTTP_403_FORBIDDEN)
  109. def test_denies_unauthenticated_user_to_annotate(self):
  110. self.assert_create(expected=status.HTTP_403_FORBIDDEN)
  111. class TestCategoryCreation(TestDataLabeling, CRUDMixin):
  112. view_name = "category_list"
  113. class TestSpanCreation(TestDataLabeling, CRUDMixin):
  114. task = SEQUENCE_LABELING
  115. view_name = "span_list"
  116. def create_data(self):
  117. label = make_label(self.project.item)
  118. return {"label": label.id, "start_offset": 0, "end_offset": 1}
  119. class TestTextLabelCreation(TestDataLabeling, CRUDMixin):
  120. task = SEQ2SEQ
  121. view_name = "text_list"
  122. def create_data(self):
  123. return {"text": "example"}
  124. class TestLabelDetail:
  125. task = SEQUENCE_LABELING
  126. view_name = "annotation_detail"
  127. def setUp(self):
  128. self.project = prepare_project(task=self.task)
  129. self.non_member = make_user()
  130. doc = make_doc(self.project.item)
  131. label = make_label(self.project.item)
  132. annotation = self.create_annotation_data(doc=doc)
  133. self.data = {"label": label.id}
  134. self.url = reverse(viewname=self.view_name, args=[self.project.item.id, doc.id, annotation.id])
  135. def create_annotation_data(self, doc):
  136. return make_annotation(task=self.task, doc=doc, user=self.project.admin, start_offset=0, end_offset=1)
  137. def test_allows_owner_to_get_annotation(self):
  138. self.assert_fetch(self.project.admin, status.HTTP_200_OK)
  139. def test_denies_non_owner_to_get_annotation(self):
  140. for member in self.project.staffs:
  141. self.assert_fetch(member, status.HTTP_403_FORBIDDEN)
  142. def test_denies_non_project_member_to_get_annotation(self):
  143. self.assert_fetch(self.non_member, status.HTTP_403_FORBIDDEN)
  144. def test_denies_unauthenticated_user_to_get_annotation(self):
  145. self.assert_fetch(expected=status.HTTP_403_FORBIDDEN)
  146. def test_allows_owner_to_update_annotation(self):
  147. self.assert_update(self.project.admin, status.HTTP_200_OK)
  148. def test_denies_non_owner_to_update_annotation(self):
  149. for member in self.project.staffs:
  150. self.assert_update(member, status.HTTP_403_FORBIDDEN)
  151. def test_denies_non_project_member_to_update_annotation(self):
  152. self.assert_update(self.non_member, status.HTTP_403_FORBIDDEN)
  153. def test_allows_owner_to_delete_annotation(self):
  154. self.assert_delete(self.project.admin, status.HTTP_204_NO_CONTENT)
  155. def test_denies_non_owner_to_delete_annotation(self):
  156. for member in self.project.staffs:
  157. self.assert_delete(member, status.HTTP_403_FORBIDDEN)
  158. def test_denies_non_project_member_to_delete_annotation(self):
  159. self.assert_delete(self.non_member, status.HTTP_403_FORBIDDEN)
  160. class TestCategoryDetail(TestLabelDetail, CRUDMixin):
  161. task = DOCUMENT_CLASSIFICATION
  162. view_name = "category_detail"
  163. def create_annotation_data(self, doc):
  164. return make_annotation(task=self.task, doc=doc, user=self.project.admin)
  165. class TestSpanDetail(TestLabelDetail, CRUDMixin):
  166. task = SEQUENCE_LABELING
  167. view_name = "span_detail"
  168. class TestTextDetail(TestLabelDetail, CRUDMixin):
  169. task = SEQ2SEQ
  170. view_name = "text_detail"
  171. def setUp(self):
  172. super().setUp()
  173. self.data = {"text": "changed"}
  174. def create_annotation_data(self, doc):
  175. return make_annotation(task=self.task, doc=doc, user=self.project.admin)
  176. class TestSharedLabelDetail:
  177. task = DOCUMENT_CLASSIFICATION
  178. view_name = "annotation_detail"
  179. def setUp(self):
  180. self.project = prepare_project(task=self.task, collaborative_annotation=True)
  181. doc = make_doc(self.project.item)
  182. annotation = self.make_annotation(doc, self.project.admin)
  183. label = make_label(self.project.item)
  184. self.data = {"label": label.id}
  185. self.url = reverse(viewname=self.view_name, args=[self.project.item.id, doc.id, annotation.id])
  186. def make_annotation(self, doc, member):
  187. return make_annotation(self.task, doc=doc, user=member)
  188. def test_allows_any_member_to_get_annotation(self):
  189. for member in self.project.members:
  190. self.assert_fetch(member, status.HTTP_200_OK)
  191. def test_allows_any_member_to_update_annotation(self):
  192. for member in self.project.members:
  193. self.assert_update(member, status.HTTP_200_OK)
  194. def test_allows_any_member_to_delete_annotation(self):
  195. self.assert_delete(self.project.approver, status.HTTP_204_NO_CONTENT)
  196. class TestSharedCategoryDetail(TestSharedLabelDetail, CRUDMixin):
  197. view_name = "category_detail"
  198. class TestSharedSpanDetail(TestSharedLabelDetail, CRUDMixin):
  199. task = SEQUENCE_LABELING
  200. view_name = "span_detail"
  201. def make_annotation(self, doc, member):
  202. return make_annotation(self.task, doc=doc, user=member, start_offset=0, end_offset=1)
  203. class TestSharedTextDetail(TestSharedLabelDetail, CRUDMixin):
  204. task = SEQ2SEQ
  205. view_name = "text_detail"
  206. def setUp(self):
  207. super().setUp()
  208. self.data = {"text": "changed"}