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.

302 lines
10 KiB

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