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.

1102 lines
57 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. import os
  2. from django.test import override_settings
  3. from rest_framework import status
  4. from rest_framework.reverse import reverse
  5. from rest_framework.test import APITestCase
  6. from model_mommy import mommy
  7. from ..models import User, SequenceAnnotation, Document
  8. from ..models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ
  9. from ..utils import PlainTextParser, CoNLLParser, JSONParser, CSVParser
  10. from ..exceptions import FileParseException
  11. DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
  12. @override_settings(STATICFILES_STORAGE='django.contrib.staticfiles.storage.StaticFilesStorage')
  13. class TestProjectListAPI(APITestCase):
  14. @classmethod
  15. def setUpTestData(cls):
  16. cls.main_project_member_name = 'project_member_name'
  17. cls.main_project_member_pass = 'project_member_pass'
  18. cls.sub_project_member_name = 'sub_project_member_name'
  19. cls.sub_project_member_pass = 'sub_project_member_pass'
  20. cls.super_user_name = 'super_user_name'
  21. cls.super_user_pass = 'super_user_pass'
  22. main_project_member = User.objects.create_user(username=cls.main_project_member_name,
  23. password=cls.main_project_member_pass)
  24. sub_project_member = User.objects.create_user(username=cls.sub_project_member_name,
  25. password=cls.sub_project_member_pass)
  26. # Todo: change super_user to project_admin.
  27. super_user = User.objects.create_superuser(username=cls.super_user_name,
  28. password=cls.super_user_pass,
  29. email='fizz@buzz.com')
  30. cls.main_project = mommy.make('TextClassificationProject', users=[main_project_member])
  31. cls.sub_project = mommy.make('TextClassificationProject', users=[sub_project_member])
  32. cls.url = reverse(viewname='project_list')
  33. cls.data = {'name': 'example', 'project_type': 'DocumentClassification',
  34. 'description': 'example', 'guideline': 'example',
  35. 'resourcetype': 'TextClassificationProject'}
  36. cls.num_project = main_project_member.projects.count()
  37. def test_returns_main_project_to_main_project_member(self):
  38. self.client.login(username=self.main_project_member_name,
  39. password=self.main_project_member_pass)
  40. response = self.client.get(self.url, format='json')
  41. project = response.data[0]
  42. num_project = len(response.data)
  43. self.assertEqual(num_project, self.num_project)
  44. self.assertEqual(project['id'], self.main_project.id)
  45. def test_do_not_return_main_project_to_sub_project_member(self):
  46. self.client.login(username=self.sub_project_member_name,
  47. password=self.sub_project_member_pass)
  48. response = self.client.get(self.url, format='json')
  49. project = response.data[0]
  50. num_project = len(response.data)
  51. self.assertEqual(num_project, self.num_project)
  52. self.assertNotEqual(project['id'], self.main_project.id)
  53. def test_allows_superuser_to_create_project(self):
  54. self.client.login(username=self.super_user_name,
  55. password=self.super_user_pass)
  56. response = self.client.post(self.url, format='json', data=self.data)
  57. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  58. def test_disallows_project_member_to_create_project(self):
  59. self.client.login(username=self.main_project_member_name,
  60. password=self.main_project_member_pass)
  61. response = self.client.post(self.url, format='json', data=self.data)
  62. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  63. @override_settings(STATICFILES_STORAGE='django.contrib.staticfiles.storage.StaticFilesStorage')
  64. class TestProjectDetailAPI(APITestCase):
  65. @classmethod
  66. def setUpTestData(cls):
  67. cls.project_member_name = 'project_member_name'
  68. cls.project_member_pass = 'project_member_pass'
  69. cls.non_project_member_name = 'non_project_member_name'
  70. cls.non_project_member_pass = 'non_project_member_pass'
  71. cls.super_user_name = 'super_user_name'
  72. cls.super_user_pass = 'super_user_pass'
  73. cls.project_member = User.objects.create_user(username=cls.project_member_name,
  74. password=cls.project_member_pass)
  75. non_project_member = User.objects.create_user(username=cls.non_project_member_name,
  76. password=cls.non_project_member_pass)
  77. # Todo: change super_user to project_admin.
  78. super_user = User.objects.create_superuser(username=cls.super_user_name,
  79. password=cls.super_user_pass,
  80. email='fizz@buzz.com')
  81. cls.main_project = mommy.make('TextClassificationProject', users=[cls.project_member, super_user])
  82. mommy.make('TextClassificationProject', users=[non_project_member])
  83. cls.url = reverse(viewname='project_detail', args=[cls.main_project.id])
  84. cls.data = {'description': 'lorem'}
  85. def test_returns_main_project_detail_to_main_project_member(self):
  86. self.client.login(username=self.project_member_name,
  87. password=self.project_member_pass)
  88. response = self.client.get(self.url, format='json')
  89. self.assertEqual(response.data['id'], self.main_project.id)
  90. def test_do_not_return_main_project_to_sub_project_member(self):
  91. self.client.login(username=self.non_project_member_name,
  92. password=self.non_project_member_pass)
  93. response = self.client.get(self.url, format='json')
  94. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  95. def test_allows_superuser_to_update_project(self):
  96. self.client.login(username=self.super_user_name,
  97. password=self.super_user_pass)
  98. response = self.client.patch(self.url, format='json', data=self.data)
  99. self.assertEqual(response.data['description'], self.data['description'])
  100. def test_disallows_project_member_to_update_project(self):
  101. self.client.login(username=self.project_member_name,
  102. password=self.project_member_pass)
  103. response = self.client.patch(self.url, format='json', data=self.data)
  104. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  105. def test_allows_superuser_to_delete_project(self):
  106. self.client.login(username=self.super_user_name,
  107. password=self.super_user_pass)
  108. response = self.client.delete(self.url, format='json')
  109. self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
  110. def test_disallows_project_member_to_delete_project(self):
  111. self.client.login(username=self.project_member_name,
  112. password=self.project_member_pass)
  113. response = self.client.delete(self.url, format='json')
  114. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  115. class TestLabelListAPI(APITestCase):
  116. @classmethod
  117. def setUpTestData(cls):
  118. cls.project_member_name = 'project_member_name'
  119. cls.project_member_pass = 'project_member_pass'
  120. cls.non_project_member_name = 'non_project_member_name'
  121. cls.non_project_member_pass = 'non_project_member_pass'
  122. cls.super_user_name = 'super_user_name'
  123. cls.super_user_pass = 'super_user_pass'
  124. project_member = User.objects.create_user(username=cls.project_member_name,
  125. password=cls.project_member_pass)
  126. non_project_member = User.objects.create_user(username=cls.non_project_member_name,
  127. password=cls.non_project_member_pass)
  128. # Todo: change super_user to project_admin.
  129. super_user = User.objects.create_superuser(username=cls.super_user_name,
  130. password=cls.super_user_pass,
  131. email='fizz@buzz.com')
  132. cls.main_project = mommy.make('Project', users=[project_member, super_user])
  133. cls.main_project_label = mommy.make('Label', project=cls.main_project)
  134. sub_project = mommy.make('Project', users=[non_project_member])
  135. other_project = mommy.make('Project', users=[super_user])
  136. mommy.make('Label', project=sub_project)
  137. cls.url = reverse(viewname='label_list', args=[cls.main_project.id])
  138. cls.other_url = reverse(viewname='label_list', args=[other_project.id])
  139. cls.data = {'text': 'example'}
  140. def test_returns_labels_to_project_member(self):
  141. self.client.login(username=self.project_member_name,
  142. password=self.project_member_pass)
  143. response = self.client.get(self.url, format='json')
  144. self.assertEqual(response.status_code, status.HTTP_200_OK)
  145. def test_do_not_return_labels_to_non_project_member(self):
  146. self.client.login(username=self.non_project_member_name,
  147. password=self.non_project_member_pass)
  148. response = self.client.get(self.url, format='json')
  149. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  150. def test_do_not_return_labels_of_other_projects(self):
  151. self.client.login(username=self.project_member_name,
  152. password=self.project_member_pass)
  153. response = self.client.get(self.url, format='json')
  154. label = response.data[0]
  155. num_labels = len(response.data)
  156. self.assertEqual(num_labels, len(self.main_project.labels.all()))
  157. self.assertEqual(label['id'], self.main_project_label.id)
  158. def test_allows_superuser_to_create_label(self):
  159. self.client.login(username=self.super_user_name,
  160. password=self.super_user_pass)
  161. response = self.client.post(self.url, format='json', data=self.data)
  162. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  163. def test_can_create_multiple_labels_without_shortcut_key(self):
  164. self.client.login(username=self.super_user_name,
  165. password=self.super_user_pass)
  166. labels = [
  167. {'text': 'Ruby', 'prefix_key': None, 'suffix_key': None},
  168. {'text': 'PHP', 'prefix_key': None, 'suffix_key': None}
  169. ]
  170. for label in labels:
  171. response = self.client.post(self.url, format='json', data=label)
  172. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  173. def test_can_create_same_label_in_multiple_projects(self):
  174. self.client.login(username=self.super_user_name,
  175. password=self.super_user_pass)
  176. label = {'text': 'LOC', 'prefix_key': None, 'suffix_key': 'l'}
  177. response = self.client.post(self.url, format='json', data=label)
  178. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  179. response = self.client.post(self.other_url, format='json', data=label)
  180. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  181. def test_disallows_project_member_to_create_label(self):
  182. self.client.login(username=self.project_member_name,
  183. password=self.project_member_pass)
  184. response = self.client.post(self.url, format='json', data=self.data)
  185. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  186. class TestLabelDetailAPI(APITestCase):
  187. @classmethod
  188. def setUpTestData(cls):
  189. cls.project_member_name = 'project_member_name'
  190. cls.project_member_pass = 'project_member_pass'
  191. cls.non_project_member_name = 'non_project_member_name'
  192. cls.non_project_member_pass = 'non_project_member_pass'
  193. cls.super_user_name = 'super_user_name'
  194. cls.super_user_pass = 'super_user_pass'
  195. project_member = User.objects.create_user(username=cls.project_member_name,
  196. password=cls.project_member_pass)
  197. non_project_member = User.objects.create_user(username=cls.non_project_member_name,
  198. password=cls.non_project_member_pass)
  199. # Todo: change super_user to project_admin.
  200. super_user = User.objects.create_superuser(username=cls.super_user_name,
  201. password=cls.super_user_pass,
  202. email='fizz@buzz.com')
  203. project = mommy.make('Project', users=[project_member, super_user])
  204. cls.label = mommy.make('Label', project=project)
  205. cls.url = reverse(viewname='label_detail', args=[project.id, cls.label.id])
  206. cls.data = {'text': 'example'}
  207. def test_returns_label_to_project_member(self):
  208. self.client.login(username=self.project_member_name,
  209. password=self.project_member_pass)
  210. response = self.client.get(self.url, format='json')
  211. self.assertEqual(response.data['id'], self.label.id)
  212. def test_do_not_return_label_to_non_project_member(self):
  213. self.client.login(username=self.non_project_member_name,
  214. password=self.non_project_member_pass)
  215. response = self.client.get(self.url, format='json')
  216. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  217. def test_allows_superuser_to_update_label(self):
  218. self.client.login(username=self.super_user_name,
  219. password=self.super_user_pass)
  220. response = self.client.patch(self.url, format='json', data=self.data)
  221. self.assertEqual(response.data['text'], self.data['text'])
  222. def test_disallows_project_member_to_update_label(self):
  223. self.client.login(username=self.project_member_name,
  224. password=self.project_member_pass)
  225. response = self.client.patch(self.url, format='json', data=self.data)
  226. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  227. def test_allows_superuser_to_delete_label(self):
  228. self.client.login(username=self.super_user_name,
  229. password=self.super_user_pass)
  230. response = self.client.delete(self.url, format='json')
  231. self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
  232. def test_disallows_project_member_to_delete_label(self):
  233. self.client.login(username=self.project_member_name,
  234. password=self.project_member_pass)
  235. response = self.client.delete(self.url, format='json')
  236. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  237. class TestDocumentListAPI(APITestCase):
  238. @classmethod
  239. def setUpTestData(cls):
  240. cls.project_member_name = 'project_member_name'
  241. cls.project_member_pass = 'project_member_pass'
  242. cls.non_project_member_name = 'non_project_member_name'
  243. cls.non_project_member_pass = 'non_project_member_pass'
  244. cls.super_user_name = 'super_user_name'
  245. cls.super_user_pass = 'super_user_pass'
  246. project_member = User.objects.create_user(username=cls.project_member_name,
  247. password=cls.project_member_pass)
  248. non_project_member = User.objects.create_user(username=cls.non_project_member_name,
  249. password=cls.non_project_member_pass)
  250. # Todo: change super_user to project_admin.
  251. super_user = User.objects.create_superuser(username=cls.super_user_name,
  252. password=cls.super_user_pass,
  253. email='fizz@buzz.com')
  254. cls.main_project = mommy.make('TextClassificationProject', users=[project_member, super_user])
  255. mommy.make('Document', project=cls.main_project)
  256. cls.random_order_project = mommy.make('TextClassificationProject', users=[project_member, super_user],
  257. randomize_document_order=True)
  258. mommy.make('Document', 100, project=cls.random_order_project)
  259. sub_project = mommy.make('TextClassificationProject', users=[non_project_member])
  260. mommy.make('Document', project=sub_project)
  261. cls.url = reverse(viewname='doc_list', args=[cls.main_project.id])
  262. cls.random_order_project_url = reverse(viewname='doc_list', args=[cls.random_order_project.id])
  263. cls.data = {'text': 'example'}
  264. def test_returns_docs_to_project_member(self):
  265. self.client.login(username=self.project_member_name,
  266. password=self.project_member_pass)
  267. response = self.client.get(self.url, format='json')
  268. self.assertEqual(response.status_code, status.HTTP_200_OK)
  269. def test_returns_docs_in_consistent_order_for_all_users(self):
  270. self.client.login(username=self.project_member_name, password=self.project_member_pass)
  271. user1_documents = self.client.get(self.url, format='json').json().get('results')
  272. self.client.logout()
  273. self.client.login(username=self.super_user_name, password=self.super_user_pass)
  274. user2_documents = self.client.get(self.url, format='json').json().get('results')
  275. self.client.logout()
  276. self.assertEqual(user1_documents, user2_documents)
  277. def test_can_return_docs_in_consistent_random_order(self):
  278. self.client.login(username=self.project_member_name, password=self.project_member_pass)
  279. user1_documents1 = self.client.get(self.random_order_project_url, format='json').json().get('results')
  280. user1_documents2 = self.client.get(self.random_order_project_url, format='json').json().get('results')
  281. self.client.logout()
  282. self.assertEqual(user1_documents1, user1_documents2)
  283. self.client.login(username=self.super_user_name, password=self.super_user_pass)
  284. user2_documents1 = self.client.get(self.random_order_project_url, format='json').json().get('results')
  285. user2_documents2 = self.client.get(self.random_order_project_url, format='json').json().get('results')
  286. self.client.logout()
  287. self.assertEqual(user2_documents1, user2_documents2)
  288. self.assertNotEqual(user1_documents1, user2_documents1)
  289. self.assertNotEqual(user1_documents2, user2_documents2)
  290. def test_do_not_return_docs_to_non_project_member(self):
  291. self.client.login(username=self.non_project_member_name,
  292. password=self.non_project_member_pass)
  293. response = self.client.get(self.url, format='json')
  294. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  295. def test_do_not_return_docs_of_other_projects(self):
  296. self.client.login(username=self.project_member_name,
  297. password=self.project_member_pass)
  298. response = self.client.get(self.url, format='json')
  299. self.assertEqual(response.data['count'], self.main_project.documents.count())
  300. def test_allows_superuser_to_create_doc(self):
  301. self.client.login(username=self.super_user_name,
  302. password=self.super_user_pass)
  303. response = self.client.post(self.url, format='json', data=self.data)
  304. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  305. def test_disallows_project_member_to_create_doc(self):
  306. self.client.login(username=self.project_member_name,
  307. password=self.project_member_pass)
  308. response = self.client.post(self.url, format='json', data=self.data)
  309. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  310. class TestDocumentDetailAPI(APITestCase):
  311. @classmethod
  312. def setUpTestData(cls):
  313. cls.project_member_name = 'project_member_name'
  314. cls.project_member_pass = 'project_member_pass'
  315. cls.non_project_member_name = 'non_project_member_name'
  316. cls.non_project_member_pass = 'non_project_member_pass'
  317. cls.super_user_name = 'super_user_name'
  318. cls.super_user_pass = 'super_user_pass'
  319. project_member = User.objects.create_user(username=cls.project_member_name,
  320. password=cls.project_member_pass)
  321. non_project_member = User.objects.create_user(username=cls.non_project_member_name,
  322. password=cls.non_project_member_pass)
  323. # Todo: change super_user to project_admin.
  324. super_user = User.objects.create_superuser(username=cls.super_user_name,
  325. password=cls.super_user_pass,
  326. email='fizz@buzz.com')
  327. project = mommy.make('TextClassificationProject', users=[project_member, super_user])
  328. cls.doc = mommy.make('Document', project=project)
  329. cls.url = reverse(viewname='doc_detail', args=[project.id, cls.doc.id])
  330. cls.data = {'text': 'example'}
  331. def test_returns_doc_to_project_member(self):
  332. self.client.login(username=self.project_member_name,
  333. password=self.project_member_pass)
  334. response = self.client.get(self.url, format='json')
  335. self.assertEqual(response.data['id'], self.doc.id)
  336. def test_do_not_return_doc_to_non_project_member(self):
  337. self.client.login(username=self.non_project_member_name,
  338. password=self.non_project_member_pass)
  339. response = self.client.get(self.url, format='json')
  340. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  341. def test_allows_superuser_to_update_doc(self):
  342. self.client.login(username=self.super_user_name,
  343. password=self.super_user_pass)
  344. response = self.client.patch(self.url, format='json', data=self.data)
  345. self.assertEqual(response.data['text'], self.data['text'])
  346. def test_disallows_project_member_to_update_doc(self):
  347. self.client.login(username=self.project_member_name,
  348. password=self.project_member_pass)
  349. response = self.client.patch(self.url, format='json', data=self.data)
  350. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  351. def test_allows_superuser_to_delete_doc(self):
  352. self.client.login(username=self.super_user_name,
  353. password=self.super_user_pass)
  354. response = self.client.delete(self.url, format='json')
  355. self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
  356. def test_disallows_project_member_to_delete_doc(self):
  357. self.client.login(username=self.project_member_name,
  358. password=self.project_member_pass)
  359. response = self.client.delete(self.url, format='json')
  360. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  361. class TestApproveLabelsAPI(APITestCase):
  362. @classmethod
  363. def setUpTestData(cls):
  364. cls.project_member_name = 'project_member_name'
  365. cls.project_member_pass = 'project_member_pass'
  366. cls.super_user_name = 'super_user_name'
  367. cls.super_user_pass = 'super_user_pass'
  368. project_member = User.objects.create_user(username=cls.project_member_name,
  369. password=cls.project_member_pass)
  370. # Todo: change super_user to project_admin.
  371. super_user = User.objects.create_superuser(username=cls.super_user_name,
  372. password=cls.super_user_pass,
  373. email='fizz@buzz.com')
  374. project = mommy.make('TextClassificationProject', users=[project_member, super_user])
  375. cls.doc = mommy.make('Document', project=project)
  376. cls.url = reverse(viewname='approve_labels', args=[project.id, cls.doc.id])
  377. def test_allows_superuser_to_approve_and_disapprove_labels(self):
  378. self.client.login(username=self.super_user_name, password=self.super_user_pass)
  379. response = self.client.post(self.url, format='json', data={'approved': True})
  380. self.assertEqual(response.data['annotation_approver'], self.super_user_name)
  381. response = self.client.post(self.url, format='json', data={'approved': False})
  382. self.assertIsNone(response.data['annotation_approver'])
  383. def test_disallows_project_member_to_approve_and_disapprove_labels(self):
  384. self.client.login(username=self.project_member_name, password=self.project_member_pass)
  385. response = self.client.post(self.url, format='json', data={'approved': True})
  386. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  387. class TestAnnotationListAPI(APITestCase):
  388. @classmethod
  389. def setUpTestData(cls):
  390. cls.project_member_name = 'project_member_name'
  391. cls.project_member_pass = 'project_member_pass'
  392. cls.another_project_member_name = 'another_project_member_name'
  393. cls.another_project_member_pass = 'another_project_member_pass'
  394. cls.non_project_member_name = 'non_project_member_name'
  395. cls.non_project_member_pass = 'non_project_member_pass'
  396. project_member = User.objects.create_user(username=cls.project_member_name,
  397. password=cls.project_member_pass)
  398. another_project_member = User.objects.create_user(username=cls.another_project_member_name,
  399. password=cls.another_project_member_pass)
  400. non_project_member = User.objects.create_user(username=cls.non_project_member_name,
  401. password=cls.non_project_member_pass)
  402. main_project = mommy.make('SequenceLabelingProject', users=[project_member, another_project_member])
  403. main_project_label = mommy.make('Label', project=main_project)
  404. main_project_doc = mommy.make('Document', project=main_project)
  405. mommy.make('SequenceAnnotation', document=main_project_doc, user=project_member)
  406. mommy.make('SequenceAnnotation', document=main_project_doc, user=another_project_member)
  407. sub_project = mommy.make('SequenceLabelingProject', users=[non_project_member])
  408. sub_project_doc = mommy.make('Document', project=sub_project)
  409. mommy.make('SequenceAnnotation', document=sub_project_doc)
  410. cls.url = reverse(viewname='annotation_list', args=[main_project.id, main_project_doc.id])
  411. cls.post_data = {'start_offset': 0, 'end_offset': 1, 'label': main_project_label.id}
  412. cls.num_entity_of_project_member = SequenceAnnotation.objects.filter(document=main_project_doc,
  413. user=project_member).count()
  414. def test_returns_annotations_to_project_member(self):
  415. self.client.login(username=self.project_member_name,
  416. password=self.project_member_pass)
  417. response = self.client.get(self.url, format='json')
  418. self.assertEqual(response.status_code, status.HTTP_200_OK)
  419. def test_do_not_return_annotations_to_non_project_member(self):
  420. self.client.login(username=self.non_project_member_name,
  421. password=self.non_project_member_pass)
  422. response = self.client.get(self.url, format='json')
  423. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  424. def test_do_not_return_annotations_of_another_project_member(self):
  425. self.client.login(username=self.project_member_name,
  426. password=self.project_member_pass)
  427. response = self.client.get(self.url, format='json')
  428. self.assertEqual(len(response.data), self.num_entity_of_project_member)
  429. def test_allows_project_member_to_create_annotation(self):
  430. self.client.login(username=self.project_member_name,
  431. password=self.project_member_pass)
  432. response = self.client.post(self.url, format='json', data=self.post_data)
  433. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  434. def test_disallows_non_project_member_to_create_annotation(self):
  435. self.client.login(username=self.non_project_member_name,
  436. password=self.non_project_member_pass)
  437. response = self.client.post(self.url, format='json', data=self.post_data)
  438. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  439. class TestAnnotationDetailAPI(APITestCase):
  440. @classmethod
  441. def setUpTestData(cls):
  442. cls.project_member_name = 'project_member_name'
  443. cls.project_member_pass = 'project_member_pass'
  444. cls.another_project_member_name = 'another_project_member_name'
  445. cls.another_project_member_pass = 'another_project_member_pass'
  446. cls.non_project_member_name = 'non_project_member_name'
  447. cls.non_project_member_pass = 'non_project_member_pass'
  448. project_member = User.objects.create_user(username=cls.project_member_name,
  449. password=cls.project_member_pass)
  450. another_project_member = User.objects.create_user(username=cls.another_project_member_name,
  451. password=cls.another_project_member_pass)
  452. non_project_member = User.objects.create_user(username=cls.non_project_member_name,
  453. password=cls.non_project_member_pass)
  454. main_project = mommy.make('SequenceLabelingProject',
  455. users=[project_member, another_project_member])
  456. main_project_doc = mommy.make('Document', project=main_project)
  457. main_project_entity = mommy.make('SequenceAnnotation',
  458. document=main_project_doc, user=project_member)
  459. another_entity = mommy.make('SequenceAnnotation',
  460. document=main_project_doc, user=another_project_member)
  461. sub_project = mommy.make('SequenceLabelingProject', users=[non_project_member])
  462. sub_project_doc = mommy.make('Document', project=sub_project)
  463. mommy.make('SequenceAnnotation', document=sub_project_doc)
  464. cls.url = reverse(viewname='annotation_detail', args=[main_project.id,
  465. main_project_doc.id,
  466. main_project_entity.id])
  467. cls.another_url = reverse(viewname='annotation_detail', args=[main_project.id,
  468. main_project_doc.id,
  469. another_entity.id])
  470. cls.post_data = {'start_offset': 0, 'end_offset': 10}
  471. def test_returns_annotation_to_project_member(self):
  472. self.client.login(username=self.project_member_name,
  473. password=self.project_member_pass)
  474. response = self.client.get(self.url, format='json')
  475. self.assertEqual(response.status_code, status.HTTP_200_OK)
  476. def test_do_not_return_annotation_to_non_project_member(self):
  477. self.client.login(username=self.non_project_member_name,
  478. password=self.non_project_member_pass)
  479. response = self.client.get(self.url, format='json')
  480. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  481. def test_do_not_return_annotation_by_another_project_member(self):
  482. self.client.login(username=self.project_member_name,
  483. password=self.project_member_pass)
  484. response = self.client.get(self.another_url, format='json')
  485. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  486. def test_allows_project_member_to_update_annotation(self):
  487. self.client.login(username=self.project_member_name,
  488. password=self.project_member_pass)
  489. response = self.client.patch(self.url, format='json', data=self.post_data)
  490. self.assertEqual(response.status_code, status.HTTP_200_OK)
  491. def test_disallows_non_project_member_to_update_annotation(self):
  492. self.client.login(username=self.non_project_member_name,
  493. password=self.non_project_member_pass)
  494. response = self.client.patch(self.url, format='json', data=self.post_data)
  495. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  496. def test_disallows_project_member_to_update_annotation_of_another_member(self):
  497. self.client.login(username=self.project_member_name,
  498. password=self.project_member_pass)
  499. response = self.client.patch(self.another_url, format='json', data=self.post_data)
  500. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  501. def test_allows_project_member_to_delete_annotation(self):
  502. self.client.login(username=self.project_member_name,
  503. password=self.project_member_pass)
  504. response = self.client.delete(self.url, format='json')
  505. self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
  506. def test_disallows_project_member_to_delete_annotation(self):
  507. self.client.login(username=self.non_project_member_name,
  508. password=self.non_project_member_pass)
  509. response = self.client.delete(self.url, format='json')
  510. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  511. def test_disallows_project_member_to_delete_annotation_of_another_member(self):
  512. self.client.login(username=self.project_member_name,
  513. password=self.project_member_pass)
  514. response = self.client.delete(self.another_url, format='json', data=self.post_data)
  515. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  516. class TestSearch(APITestCase):
  517. @classmethod
  518. def setUpTestData(cls):
  519. cls.project_member_name = 'project_member_name'
  520. cls.project_member_pass = 'project_member_pass'
  521. cls.non_project_member_name = 'non_project_member_name'
  522. cls.non_project_member_pass = 'non_project_member_pass'
  523. project_member = User.objects.create_user(username=cls.project_member_name,
  524. password=cls.project_member_pass)
  525. non_project_member = User.objects.create_user(username=cls.non_project_member_name,
  526. password=cls.non_project_member_pass)
  527. cls.main_project = mommy.make('TextClassificationProject', users=[project_member])
  528. cls.search_term = 'example'
  529. doc1 = mommy.make('Document', text=cls.search_term, project=cls.main_project)
  530. doc2 = mommy.make('Document', text='Lorem', project=cls.main_project)
  531. label1 = mommy.make('Label', project=cls.main_project)
  532. label2 = mommy.make('Label', project=cls.main_project)
  533. mommy.make('SequenceAnnotation', document=doc1, user=project_member, label=label1)
  534. mommy.make('SequenceAnnotation', document=doc2, user=project_member, label=label2)
  535. sub_project = mommy.make('TextClassificationProject', users=[non_project_member])
  536. mommy.make('Document', text=cls.search_term, project=sub_project)
  537. cls.url = reverse(viewname='doc_list', args=[cls.main_project.id])
  538. cls.data = {'q': cls.search_term}
  539. def test_can_filter_doc_by_term(self):
  540. self.client.login(username=self.project_member_name,
  541. password=self.project_member_pass)
  542. response = self.client.get(self.url, format='json', data=self.data)
  543. count = Document.objects.filter(text__contains=self.search_term,
  544. project=self.main_project).count()
  545. self.assertEqual(response.data['count'], count)
  546. def test_can_order_doc_by_created_at_ascending(self):
  547. params = {'ordering': 'created_at'}
  548. self.client.login(username=self.project_member_name,
  549. password=self.project_member_pass)
  550. response = self.client.get(self.url, format='json', data=params)
  551. docs = Document.objects.filter(project=self.main_project).order_by('created_at').values()
  552. for d1, d2 in zip(response.data['results'], docs):
  553. self.assertEqual(d1['id'], d2['id'])
  554. def test_can_order_doc_by_created_at_descending(self):
  555. params = {'ordering': '-created_at'}
  556. self.client.login(username=self.project_member_name,
  557. password=self.project_member_pass)
  558. response = self.client.get(self.url, format='json', data=params)
  559. docs = Document.objects.filter(project=self.main_project).order_by('-created_at').values()
  560. for d1, d2 in zip(response.data['results'], docs):
  561. self.assertEqual(d1['id'], d2['id'])
  562. def test_can_order_doc_by_annotation_updated_at_ascending(self):
  563. params = {'ordering': 'seq_annotations__updated_at'}
  564. self.client.login(username=self.project_member_name,
  565. password=self.project_member_pass)
  566. response = self.client.get(self.url, format='json', data=params)
  567. docs = Document.objects.filter(project=self.main_project).order_by('seq_annotations__updated_at').values()
  568. for d1, d2 in zip(response.data['results'], docs):
  569. self.assertEqual(d1['id'], d2['id'])
  570. def test_can_order_doc_by_annotation_updated_at_descending(self):
  571. params = {'ordering': '-seq_annotations__updated_at'}
  572. self.client.login(username=self.project_member_name,
  573. password=self.project_member_pass)
  574. response = self.client.get(self.url, format='json', data=params)
  575. docs = Document.objects.filter(project=self.main_project).order_by('-seq_annotations__updated_at').values()
  576. for d1, d2 in zip(response.data['results'], docs):
  577. self.assertEqual(d1['id'], d2['id'])
  578. class TestFilter(APITestCase):
  579. @classmethod
  580. def setUpTestData(cls):
  581. cls.project_member_name = 'project_member_name'
  582. cls.project_member_pass = 'project_member_pass'
  583. project_member = User.objects.create_user(username=cls.project_member_name,
  584. password=cls.project_member_pass)
  585. cls.main_project = mommy.make('SequenceLabelingProject', users=[project_member])
  586. cls.label1 = mommy.make('Label', project=cls.main_project)
  587. cls.label2 = mommy.make('Label', project=cls.main_project)
  588. doc1 = mommy.make('Document', project=cls.main_project)
  589. doc2 = mommy.make('Document', project=cls.main_project)
  590. mommy.make('Document', project=cls.main_project)
  591. mommy.make('SequenceAnnotation', document=doc1, user=project_member, label=cls.label1)
  592. mommy.make('SequenceAnnotation', document=doc2, user=project_member, label=cls.label2)
  593. cls.url = reverse(viewname='doc_list', args=[cls.main_project.id])
  594. cls.params = {'seq_annotations__label__id': cls.label1.id}
  595. def test_can_filter_by_label(self):
  596. self.client.login(username=self.project_member_name,
  597. password=self.project_member_pass)
  598. response = self.client.get(self.url, format='json', data=self.params)
  599. docs = Document.objects.filter(project=self.main_project,
  600. seq_annotations__label__id=self.label1.id).values()
  601. for d1, d2 in zip(response.data['results'], docs):
  602. self.assertEqual(d1['id'], d2['id'])
  603. def test_can_filter_doc_with_annotation(self):
  604. params = {'seq_annotations__isnull': False}
  605. self.client.login(username=self.project_member_name,
  606. password=self.project_member_pass)
  607. response = self.client.get(self.url, format='json', data=params)
  608. docs = Document.objects.filter(project=self.main_project, seq_annotations__isnull=False).values()
  609. self.assertEqual(response.data['count'], docs.count())
  610. for d1, d2 in zip(response.data['results'], docs):
  611. self.assertEqual(d1['id'], d2['id'])
  612. def test_can_filter_doc_without_anotation(self):
  613. params = {'seq_annotations__isnull': True}
  614. self.client.login(username=self.project_member_name,
  615. password=self.project_member_pass)
  616. response = self.client.get(self.url, format='json', data=params)
  617. docs = Document.objects.filter(project=self.main_project, seq_annotations__isnull=True).values()
  618. self.assertEqual(response.data['count'], docs.count())
  619. for d1, d2 in zip(response.data['results'], docs):
  620. self.assertEqual(d1['id'], d2['id'])
  621. class TestUploader(APITestCase):
  622. @classmethod
  623. def setUpTestData(cls):
  624. cls.super_user_name = 'super_user_name'
  625. cls.super_user_pass = 'super_user_pass'
  626. # Todo: change super_user to project_admin.
  627. super_user = User.objects.create_superuser(username=cls.super_user_name,
  628. password=cls.super_user_pass,
  629. email='fizz@buzz.com')
  630. cls.classification_project = mommy.make('TextClassificationProject',
  631. users=[super_user], project_type=DOCUMENT_CLASSIFICATION)
  632. cls.labeling_project = mommy.make('SequenceLabelingProject',
  633. users=[super_user], project_type=SEQUENCE_LABELING)
  634. cls.seq2seq_project = mommy.make('Seq2seqProject', users=[super_user], project_type=SEQ2SEQ)
  635. def setUp(self):
  636. self.client.login(username=self.super_user_name,
  637. password=self.super_user_pass)
  638. def upload_test_helper(self, project_id, filename, file_format, expected_status, **kwargs):
  639. url = reverse(viewname='doc_uploader', args=[project_id])
  640. with open(os.path.join(DATA_DIR, filename)) as f:
  641. response = self.client.post(url, data={'file': f, 'format': file_format})
  642. self.assertEqual(response.status_code, expected_status)
  643. def label_test_helper(self, project_id, expected_labels, expected_label_keys):
  644. url = reverse(viewname='label_list', args=[project_id])
  645. expected_keys = {key for label in expected_labels for key in label}
  646. response = self.client.get(url).json()
  647. actual_labels = [{key: value for (key, value) in label.items() if key in expected_keys}
  648. for label in response]
  649. self.assertCountEqual(actual_labels, expected_labels)
  650. for label in response:
  651. for expected_label_key in expected_label_keys:
  652. self.assertIsNotNone(label.get(expected_label_key))
  653. def test_can_upload_conll_format_file(self):
  654. self.upload_test_helper(project_id=self.labeling_project.id,
  655. filename='labeling.conll',
  656. file_format='conll',
  657. expected_status=status.HTTP_201_CREATED)
  658. def test_cannot_upload_wrong_conll_format_file(self):
  659. self.upload_test_helper(project_id=self.labeling_project.id,
  660. filename='labeling.invalid.conll',
  661. file_format='conll',
  662. expected_status=status.HTTP_400_BAD_REQUEST)
  663. def test_can_upload_classification_csv(self):
  664. self.upload_test_helper(project_id=self.classification_project.id,
  665. filename='example.csv',
  666. file_format='csv',
  667. expected_status=status.HTTP_201_CREATED)
  668. def test_can_upload_seq2seq_csv(self):
  669. self.upload_test_helper(project_id=self.seq2seq_project.id,
  670. filename='example.csv',
  671. file_format='csv',
  672. expected_status=status.HTTP_201_CREATED)
  673. def test_cannot_upload_csv_file_does_not_match_column_and_row(self):
  674. self.upload_test_helper(project_id=self.classification_project.id,
  675. filename='example.invalid.1.csv',
  676. file_format='csv',
  677. expected_status=status.HTTP_400_BAD_REQUEST)
  678. def test_cannot_upload_csv_file_has_too_many_columns(self):
  679. self.upload_test_helper(project_id=self.classification_project.id,
  680. filename='example.invalid.2.csv',
  681. file_format='csv',
  682. expected_status=status.HTTP_400_BAD_REQUEST)
  683. def test_can_upload_classification_jsonl(self):
  684. self.upload_test_helper(project_id=self.classification_project.id,
  685. filename='classification.jsonl',
  686. file_format='json',
  687. expected_status=status.HTTP_201_CREATED)
  688. self.label_test_helper(
  689. project_id=self.classification_project.id,
  690. expected_labels=[
  691. {'text': 'positive', 'suffix_key': 'p', 'prefix_key': None},
  692. {'text': 'negative', 'suffix_key': 'n', 'prefix_key': None},
  693. {'text': 'neutral', 'suffix_key': 'n', 'prefix_key': 'ctrl'},
  694. ],
  695. expected_label_keys=[
  696. 'background_color',
  697. 'text_color',
  698. ])
  699. def test_can_upload_labeling_jsonl(self):
  700. self.upload_test_helper(project_id=self.labeling_project.id,
  701. filename='labeling.jsonl',
  702. file_format='json',
  703. expected_status=status.HTTP_201_CREATED)
  704. self.label_test_helper(
  705. project_id=self.labeling_project.id,
  706. expected_labels=[
  707. {'text': 'LOC', 'suffix_key': 'l', 'prefix_key': None},
  708. {'text': 'ORG', 'suffix_key': 'o', 'prefix_key': None},
  709. {'text': 'PER', 'suffix_key': 'p', 'prefix_key': None},
  710. ],
  711. expected_label_keys=[
  712. 'background_color',
  713. 'text_color',
  714. ])
  715. def test_can_upload_seq2seq_jsonl(self):
  716. self.upload_test_helper(project_id=self.seq2seq_project.id,
  717. filename='seq2seq.jsonl',
  718. file_format='json',
  719. expected_status=status.HTTP_201_CREATED)
  720. def test_can_upload_plain_text(self):
  721. self.upload_test_helper(project_id=self.classification_project.id,
  722. filename='example.txt',
  723. file_format='plain',
  724. expected_status=status.HTTP_201_CREATED)
  725. def test_can_upload_data_without_label(self):
  726. self.upload_test_helper(project_id=self.classification_project.id,
  727. filename='example.jsonl',
  728. file_format='json',
  729. expected_status=status.HTTP_201_CREATED)
  730. @override_settings(CLOUD_BROWSER_APACHE_LIBCLOUD_PROVIDER='LOCAL')
  731. @override_settings(CLOUD_BROWSER_APACHE_LIBCLOUD_ACCOUNT=os.path.dirname(DATA_DIR))
  732. @override_settings(CLOUD_BROWSER_APACHE_LIBCLOUD_SECRET_KEY='not-used')
  733. class TestCloudUploader(TestUploader):
  734. def upload_test_helper(self, project_id, filename, file_format, expected_status, **kwargs):
  735. query_params = {
  736. 'project_id': project_id,
  737. 'upload_format': file_format,
  738. 'container': kwargs.pop('container', os.path.basename(DATA_DIR)),
  739. 'object': filename,
  740. }
  741. query_params.update(kwargs)
  742. response = self.client.get(reverse('cloud_uploader'), query_params)
  743. self.assertEqual(response.status_code, expected_status)
  744. def test_cannot_upload_with_missing_file(self):
  745. self.upload_test_helper(project_id=self.classification_project.id,
  746. filename='does-not-exist',
  747. file_format='json',
  748. expected_status=status.HTTP_400_BAD_REQUEST)
  749. def test_cannot_upload_with_missing_container(self):
  750. self.upload_test_helper(project_id=self.classification_project.id,
  751. filename='example.jsonl',
  752. container='does-not-exist',
  753. file_format='json',
  754. expected_status=status.HTTP_400_BAD_REQUEST)
  755. def test_cannot_upload_with_missing_query_parameters(self):
  756. response = self.client.get(reverse('cloud_uploader'), {'project_id': self.classification_project.id})
  757. self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
  758. def test_can_upload_with_redirect(self):
  759. self.upload_test_helper(project_id=self.classification_project.id,
  760. filename='example.jsonl',
  761. next='http://somewhere',
  762. file_format='json',
  763. expected_status=status.HTTP_302_FOUND)
  764. def test_can_upload_with_redirect_to_blank(self):
  765. self.upload_test_helper(project_id=self.classification_project.id,
  766. filename='example.jsonl',
  767. next='about:blank',
  768. file_format='json',
  769. expected_status=status.HTTP_201_CREATED)
  770. class TestFeatures(APITestCase):
  771. @classmethod
  772. def setUpTestData(cls):
  773. cls.user_name = 'user_name'
  774. cls.user_pass = 'user_pass'
  775. cls.user = User.objects.create_user(username=cls.user_name, password=cls.user_pass, email='fizz@buzz.com')
  776. def setUp(self):
  777. self.client.login(username=self.user_name, password=self.user_pass)
  778. @override_settings(CLOUD_BROWSER_APACHE_LIBCLOUD_PROVIDER=None)
  779. def test_no_cloud_upload(self):
  780. response = self.client.get(reverse('features'))
  781. self.assertFalse(response.json().get('cloud_upload'))
  782. @override_settings(IMPORT_BATCH_SIZE=2)
  783. class TestParser(APITestCase):
  784. def parser_helper(self, filename, parser, include_label=True):
  785. with open(os.path.join(DATA_DIR, filename), mode='rb') as f:
  786. result = list(parser.parse(f))
  787. for data in result:
  788. for r in data:
  789. self.assertIn('text', r)
  790. if include_label:
  791. self.assertIn('labels', r)
  792. return result
  793. def test_give_valid_data_to_conll_parser(self):
  794. self.parser_helper(filename='labeling.conll', parser=CoNLLParser())
  795. def test_give_valid_data_to_conll_parser_with_trailing_newlines(self):
  796. result = self.parser_helper(filename='labeling.trailing.conll', parser=CoNLLParser())
  797. self.assertEqual(len(result), 1)
  798. self.assertEqual(len(result[0]), 1)
  799. def test_plain_parser(self):
  800. self.parser_helper(filename='example.txt', parser=PlainTextParser(), include_label=False)
  801. def test_give_invalid_data_to_conll_parser(self):
  802. with self.assertRaises(FileParseException):
  803. self.parser_helper(filename='labeling.invalid.conll',
  804. parser=CoNLLParser())
  805. def test_give_classification_data_to_csv_parser(self):
  806. self.parser_helper(filename='example.csv', parser=CSVParser())
  807. def test_give_seq2seq_data_to_csv_parser(self):
  808. self.parser_helper(filename='example.csv', parser=CSVParser())
  809. def test_give_classification_data_to_json_parser(self):
  810. self.parser_helper(filename='classification.jsonl', parser=JSONParser())
  811. def test_give_labeling_data_to_json_parser(self):
  812. self.parser_helper(filename='labeling.jsonl', parser=JSONParser())
  813. def test_give_seq2seq_data_to_json_parser(self):
  814. self.parser_helper(filename='seq2seq.jsonl', parser=JSONParser())
  815. def test_give_data_without_label_to_json_parser(self):
  816. self.parser_helper(filename='example.jsonl', parser=JSONParser(), include_label=False)
  817. class TestDownloader(APITestCase):
  818. @classmethod
  819. def setUpTestData(cls):
  820. cls.super_user_name = 'super_user_name'
  821. cls.super_user_pass = 'super_user_pass'
  822. # Todo: change super_user to project_admin.
  823. super_user = User.objects.create_superuser(username=cls.super_user_name,
  824. password=cls.super_user_pass,
  825. email='fizz@buzz.com')
  826. cls.classification_project = mommy.make('TextClassificationProject',
  827. users=[super_user], project_type=DOCUMENT_CLASSIFICATION)
  828. cls.labeling_project = mommy.make('SequenceLabelingProject',
  829. users=[super_user], project_type=SEQUENCE_LABELING)
  830. cls.seq2seq_project = mommy.make('Seq2seqProject', users=[super_user], project_type=SEQ2SEQ)
  831. cls.classification_url = reverse(viewname='doc_downloader', args=[cls.classification_project.id])
  832. cls.labeling_url = reverse(viewname='doc_downloader', args=[cls.labeling_project.id])
  833. cls.seq2seq_url = reverse(viewname='doc_downloader', args=[cls.seq2seq_project.id])
  834. def setUp(self):
  835. self.client.login(username=self.super_user_name,
  836. password=self.super_user_pass)
  837. def download_test_helper(self, url, format, expected_status):
  838. response = self.client.get(url, data={'q': format})
  839. self.assertEqual(response.status_code, expected_status)
  840. def test_cannot_download_conll_format_file(self):
  841. self.download_test_helper(url=self.labeling_url,
  842. format='conll',
  843. expected_status=status.HTTP_400_BAD_REQUEST)
  844. def test_can_download_classification_csv(self):
  845. self.download_test_helper(url=self.classification_url,
  846. format='csv',
  847. expected_status=status.HTTP_200_OK)
  848. def test_can_download_labeling_csv(self):
  849. self.download_test_helper(url=self.labeling_url,
  850. format='csv',
  851. expected_status=status.HTTP_200_OK)
  852. def test_can_download_seq2seq_csv(self):
  853. self.download_test_helper(url=self.seq2seq_url,
  854. format='csv',
  855. expected_status=status.HTTP_200_OK)
  856. def test_can_download_classification_jsonl(self):
  857. self.download_test_helper(url=self.classification_url,
  858. format='json',
  859. expected_status=status.HTTP_200_OK)
  860. def test_can_download_labeling_jsonl(self):
  861. self.download_test_helper(url=self.labeling_url,
  862. format='json',
  863. expected_status=status.HTTP_200_OK)
  864. def test_can_download_seq2seq_jsonl(self):
  865. self.download_test_helper(url=self.seq2seq_url,
  866. format='json',
  867. expected_status=status.HTTP_200_OK)
  868. def test_can_download_labelling_json1(self):
  869. self.download_test_helper(url=self.labeling_url,
  870. format='json1',
  871. expected_status=status.HTTP_200_OK)
  872. def test_can_download_plain_text(self):
  873. self.download_test_helper(url=self.classification_url,
  874. format='plain',
  875. expected_status=status.HTTP_400_BAD_REQUEST)
  876. class TestStatisticsAPI(APITestCase):
  877. @classmethod
  878. def setUpTestData(cls):
  879. cls.super_user_name = 'super_user_name'
  880. cls.super_user_pass = 'super_user_pass'
  881. # Todo: change super_user to project_admin.
  882. super_user = User.objects.create_superuser(username=cls.super_user_name,
  883. password=cls.super_user_pass,
  884. email='fizz@buzz.com')
  885. main_project = mommy.make('TextClassificationProject', users=[super_user])
  886. doc1 = mommy.make('Document', project=main_project)
  887. mommy.make('Document', project=main_project)
  888. mommy.make('DocumentAnnotation', document=doc1, user=super_user)
  889. cls.url = reverse(viewname='statistics', args=[main_project.id])
  890. cls.doc = Document.objects.filter(project=main_project)
  891. def test_returns_exact_progress(self):
  892. self.client.login(username=self.super_user_name,
  893. password=self.super_user_pass)
  894. response = self.client.get(self.url, format='json')
  895. total = self.doc.count()
  896. remaining = self.doc.filter(doc_annotations__isnull=True).count()
  897. self.assertEqual(response.data['total'], total)
  898. self.assertEqual(response.data['remaining'], remaining)
  899. def test_returns_user_count(self):
  900. self.client.login(username=self.super_user_name,
  901. password=self.super_user_pass)
  902. response = self.client.get(self.url, format='json')
  903. self.assertIn('label', response.data)
  904. self.assertIsInstance(response.data['label'], dict)
  905. def test_returns_label_count(self):
  906. self.client.login(username=self.super_user_name,
  907. password=self.super_user_pass)
  908. response = self.client.get(self.url, format='json')
  909. self.assertIn('user', response.data)
  910. self.assertIsInstance(response.data['user'], dict)