udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30454__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of udata might be problematic. Click here for more details.

Files changed (413) hide show
  1. tasks/__init__.py +109 -107
  2. tasks/helpers.py +18 -18
  3. udata/__init__.py +4 -4
  4. udata/admin/views.py +5 -5
  5. udata/api/__init__.py +111 -134
  6. udata/api/commands.py +45 -37
  7. udata/api/errors.py +5 -4
  8. udata/api/fields.py +23 -21
  9. udata/api/oauth2.py +55 -74
  10. udata/api/parsers.py +15 -15
  11. udata/api/signals.py +1 -1
  12. udata/api_fields.py +137 -89
  13. udata/app.py +58 -55
  14. udata/assets.py +5 -5
  15. udata/auth/__init__.py +37 -26
  16. udata/auth/forms.py +23 -15
  17. udata/auth/helpers.py +1 -1
  18. udata/auth/mails.py +3 -3
  19. udata/auth/password_validation.py +19 -15
  20. udata/auth/views.py +94 -68
  21. udata/commands/__init__.py +71 -69
  22. udata/commands/cache.py +7 -7
  23. udata/commands/db.py +201 -140
  24. udata/commands/dcat.py +36 -30
  25. udata/commands/fixtures.py +100 -84
  26. udata/commands/images.py +21 -20
  27. udata/commands/info.py +17 -20
  28. udata/commands/init.py +10 -10
  29. udata/commands/purge.py +12 -13
  30. udata/commands/serve.py +41 -29
  31. udata/commands/static.py +16 -18
  32. udata/commands/test.py +20 -20
  33. udata/commands/tests/fixtures.py +26 -24
  34. udata/commands/worker.py +31 -33
  35. udata/core/__init__.py +12 -12
  36. udata/core/activity/__init__.py +0 -1
  37. udata/core/activity/api.py +59 -49
  38. udata/core/activity/models.py +28 -26
  39. udata/core/activity/signals.py +1 -1
  40. udata/core/activity/tasks.py +16 -10
  41. udata/core/badges/api.py +6 -6
  42. udata/core/badges/commands.py +14 -13
  43. udata/core/badges/fields.py +8 -5
  44. udata/core/badges/forms.py +7 -4
  45. udata/core/badges/models.py +16 -31
  46. udata/core/badges/permissions.py +1 -3
  47. udata/core/badges/signals.py +2 -2
  48. udata/core/badges/tasks.py +3 -2
  49. udata/core/badges/tests/test_commands.py +10 -10
  50. udata/core/badges/tests/test_model.py +24 -31
  51. udata/core/contact_point/api.py +19 -18
  52. udata/core/contact_point/api_fields.py +21 -14
  53. udata/core/contact_point/factories.py +2 -2
  54. udata/core/contact_point/forms.py +7 -6
  55. udata/core/contact_point/models.py +3 -5
  56. udata/core/dataservices/api.py +26 -21
  57. udata/core/dataservices/factories.py +13 -11
  58. udata/core/dataservices/models.py +35 -40
  59. udata/core/dataservices/permissions.py +4 -4
  60. udata/core/dataservices/rdf.py +40 -17
  61. udata/core/dataservices/tasks.py +4 -3
  62. udata/core/dataset/actions.py +10 -10
  63. udata/core/dataset/activities.py +21 -23
  64. udata/core/dataset/api.py +321 -298
  65. udata/core/dataset/api_fields.py +443 -271
  66. udata/core/dataset/apiv2.py +305 -229
  67. udata/core/dataset/commands.py +38 -36
  68. udata/core/dataset/constants.py +61 -54
  69. udata/core/dataset/csv.py +70 -74
  70. udata/core/dataset/events.py +39 -32
  71. udata/core/dataset/exceptions.py +8 -4
  72. udata/core/dataset/factories.py +57 -65
  73. udata/core/dataset/forms.py +87 -63
  74. udata/core/dataset/models.py +336 -280
  75. udata/core/dataset/permissions.py +9 -6
  76. udata/core/dataset/preview.py +15 -17
  77. udata/core/dataset/rdf.py +156 -122
  78. udata/core/dataset/search.py +92 -77
  79. udata/core/dataset/signals.py +1 -1
  80. udata/core/dataset/tasks.py +63 -54
  81. udata/core/discussions/actions.py +5 -5
  82. udata/core/discussions/api.py +124 -120
  83. udata/core/discussions/factories.py +2 -2
  84. udata/core/discussions/forms.py +9 -7
  85. udata/core/discussions/metrics.py +1 -3
  86. udata/core/discussions/models.py +25 -24
  87. udata/core/discussions/notifications.py +18 -14
  88. udata/core/discussions/permissions.py +3 -3
  89. udata/core/discussions/signals.py +4 -4
  90. udata/core/discussions/tasks.py +24 -28
  91. udata/core/followers/api.py +32 -33
  92. udata/core/followers/models.py +9 -9
  93. udata/core/followers/signals.py +3 -3
  94. udata/core/jobs/actions.py +7 -7
  95. udata/core/jobs/api.py +99 -92
  96. udata/core/jobs/commands.py +48 -49
  97. udata/core/jobs/forms.py +11 -11
  98. udata/core/jobs/models.py +6 -6
  99. udata/core/metrics/__init__.py +2 -2
  100. udata/core/metrics/commands.py +34 -30
  101. udata/core/metrics/models.py +2 -4
  102. udata/core/metrics/signals.py +1 -1
  103. udata/core/metrics/tasks.py +3 -3
  104. udata/core/organization/activities.py +12 -15
  105. udata/core/organization/api.py +167 -174
  106. udata/core/organization/api_fields.py +183 -124
  107. udata/core/organization/apiv2.py +32 -32
  108. udata/core/organization/commands.py +20 -22
  109. udata/core/organization/constants.py +11 -11
  110. udata/core/organization/csv.py +17 -15
  111. udata/core/organization/factories.py +8 -11
  112. udata/core/organization/forms.py +32 -26
  113. udata/core/organization/metrics.py +2 -1
  114. udata/core/organization/models.py +87 -67
  115. udata/core/organization/notifications.py +18 -14
  116. udata/core/organization/permissions.py +10 -11
  117. udata/core/organization/rdf.py +14 -14
  118. udata/core/organization/search.py +30 -28
  119. udata/core/organization/signals.py +7 -7
  120. udata/core/organization/tasks.py +42 -61
  121. udata/core/owned.py +38 -27
  122. udata/core/post/api.py +82 -81
  123. udata/core/post/constants.py +8 -5
  124. udata/core/post/factories.py +4 -4
  125. udata/core/post/forms.py +13 -14
  126. udata/core/post/models.py +20 -22
  127. udata/core/post/tests/test_api.py +30 -32
  128. udata/core/reports/api.py +8 -7
  129. udata/core/reports/constants.py +1 -3
  130. udata/core/reports/models.py +10 -10
  131. udata/core/reuse/activities.py +15 -19
  132. udata/core/reuse/api.py +123 -126
  133. udata/core/reuse/api_fields.py +120 -85
  134. udata/core/reuse/apiv2.py +11 -10
  135. udata/core/reuse/constants.py +23 -23
  136. udata/core/reuse/csv.py +18 -18
  137. udata/core/reuse/factories.py +5 -9
  138. udata/core/reuse/forms.py +24 -21
  139. udata/core/reuse/models.py +55 -51
  140. udata/core/reuse/permissions.py +2 -2
  141. udata/core/reuse/search.py +49 -46
  142. udata/core/reuse/signals.py +1 -1
  143. udata/core/reuse/tasks.py +4 -5
  144. udata/core/site/api.py +47 -50
  145. udata/core/site/factories.py +2 -2
  146. udata/core/site/forms.py +4 -5
  147. udata/core/site/models.py +94 -63
  148. udata/core/site/rdf.py +14 -14
  149. udata/core/spam/api.py +16 -9
  150. udata/core/spam/constants.py +4 -4
  151. udata/core/spam/fields.py +13 -7
  152. udata/core/spam/models.py +27 -20
  153. udata/core/spam/signals.py +1 -1
  154. udata/core/spam/tests/test_spam.py +6 -5
  155. udata/core/spatial/api.py +72 -80
  156. udata/core/spatial/api_fields.py +73 -58
  157. udata/core/spatial/commands.py +67 -64
  158. udata/core/spatial/constants.py +3 -3
  159. udata/core/spatial/factories.py +37 -54
  160. udata/core/spatial/forms.py +27 -26
  161. udata/core/spatial/geoids.py +17 -17
  162. udata/core/spatial/models.py +43 -47
  163. udata/core/spatial/tasks.py +2 -1
  164. udata/core/spatial/tests/test_api.py +115 -130
  165. udata/core/spatial/tests/test_fields.py +74 -77
  166. udata/core/spatial/tests/test_geoid.py +22 -22
  167. udata/core/spatial/tests/test_models.py +5 -7
  168. udata/core/spatial/translations.py +16 -16
  169. udata/core/storages/__init__.py +16 -18
  170. udata/core/storages/api.py +66 -64
  171. udata/core/storages/tasks.py +7 -7
  172. udata/core/storages/utils.py +15 -15
  173. udata/core/storages/views.py +5 -6
  174. udata/core/tags/api.py +17 -14
  175. udata/core/tags/csv.py +4 -4
  176. udata/core/tags/models.py +8 -5
  177. udata/core/tags/tasks.py +11 -13
  178. udata/core/tags/views.py +4 -4
  179. udata/core/topic/api.py +84 -73
  180. udata/core/topic/apiv2.py +157 -127
  181. udata/core/topic/factories.py +3 -4
  182. udata/core/topic/forms.py +12 -14
  183. udata/core/topic/models.py +14 -19
  184. udata/core/topic/parsers.py +26 -26
  185. udata/core/user/activities.py +30 -29
  186. udata/core/user/api.py +151 -152
  187. udata/core/user/api_fields.py +132 -100
  188. udata/core/user/apiv2.py +7 -7
  189. udata/core/user/commands.py +38 -38
  190. udata/core/user/factories.py +8 -9
  191. udata/core/user/forms.py +14 -11
  192. udata/core/user/metrics.py +2 -2
  193. udata/core/user/models.py +68 -69
  194. udata/core/user/permissions.py +4 -5
  195. udata/core/user/rdf.py +7 -8
  196. udata/core/user/tasks.py +2 -2
  197. udata/core/user/tests/test_user_model.py +24 -16
  198. udata/cors.py +99 -0
  199. udata/db/tasks.py +2 -1
  200. udata/entrypoints.py +35 -31
  201. udata/errors.py +2 -1
  202. udata/event/values.py +6 -6
  203. udata/factories.py +2 -2
  204. udata/features/identicon/api.py +5 -6
  205. udata/features/identicon/backends.py +48 -55
  206. udata/features/identicon/tests/test_backends.py +4 -5
  207. udata/features/notifications/__init__.py +0 -1
  208. udata/features/notifications/actions.py +9 -9
  209. udata/features/notifications/api.py +17 -13
  210. udata/features/territories/__init__.py +12 -10
  211. udata/features/territories/api.py +14 -15
  212. udata/features/territories/models.py +23 -28
  213. udata/features/transfer/actions.py +8 -11
  214. udata/features/transfer/api.py +84 -77
  215. udata/features/transfer/factories.py +2 -1
  216. udata/features/transfer/models.py +11 -12
  217. udata/features/transfer/notifications.py +19 -15
  218. udata/features/transfer/permissions.py +5 -5
  219. udata/forms/__init__.py +5 -2
  220. udata/forms/fields.py +164 -172
  221. udata/forms/validators.py +19 -22
  222. udata/forms/widgets.py +9 -13
  223. udata/frontend/__init__.py +31 -26
  224. udata/frontend/csv.py +68 -58
  225. udata/frontend/markdown.py +40 -44
  226. udata/harvest/actions.py +89 -77
  227. udata/harvest/api.py +294 -238
  228. udata/harvest/backends/__init__.py +4 -4
  229. udata/harvest/backends/base.py +128 -111
  230. udata/harvest/backends/dcat.py +80 -66
  231. udata/harvest/commands.py +56 -60
  232. udata/harvest/csv.py +8 -8
  233. udata/harvest/exceptions.py +6 -3
  234. udata/harvest/filters.py +24 -23
  235. udata/harvest/forms.py +27 -28
  236. udata/harvest/models.py +88 -80
  237. udata/harvest/notifications.py +15 -10
  238. udata/harvest/signals.py +13 -13
  239. udata/harvest/tasks.py +11 -10
  240. udata/harvest/tests/factories.py +23 -24
  241. udata/harvest/tests/test_actions.py +136 -166
  242. udata/harvest/tests/test_api.py +220 -214
  243. udata/harvest/tests/test_base_backend.py +117 -112
  244. udata/harvest/tests/test_dcat_backend.py +380 -308
  245. udata/harvest/tests/test_filters.py +33 -22
  246. udata/harvest/tests/test_models.py +11 -14
  247. udata/harvest/tests/test_notifications.py +6 -7
  248. udata/harvest/tests/test_tasks.py +7 -6
  249. udata/i18n.py +237 -78
  250. udata/linkchecker/backends.py +5 -11
  251. udata/linkchecker/checker.py +23 -22
  252. udata/linkchecker/commands.py +4 -6
  253. udata/linkchecker/models.py +6 -6
  254. udata/linkchecker/tasks.py +18 -20
  255. udata/mail.py +21 -21
  256. udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
  257. udata/migrations/2020-08-24-add-fs-filename.py +9 -8
  258. udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
  259. udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
  260. udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
  261. udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
  262. udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
  263. udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
  264. udata/migrations/2021-08-17-follow-integrity.py +5 -4
  265. udata/migrations/2021-08-17-harvest-integrity.py +13 -12
  266. udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
  267. udata/migrations/2021-08-17-transfer-integrity.py +5 -4
  268. udata/migrations/2021-08-17-users-integrity.py +9 -8
  269. udata/migrations/2021-12-14-reuse-topics.py +7 -6
  270. udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
  271. udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
  272. udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
  273. udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
  274. udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
  275. udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
  276. udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
  277. udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
  278. udata/migrations/__init__.py +123 -105
  279. udata/models/__init__.py +4 -4
  280. udata/mongo/__init__.py +13 -11
  281. udata/mongo/badges_field.py +3 -2
  282. udata/mongo/datetime_fields.py +13 -12
  283. udata/mongo/document.py +17 -16
  284. udata/mongo/engine.py +15 -16
  285. udata/mongo/errors.py +2 -1
  286. udata/mongo/extras_fields.py +30 -20
  287. udata/mongo/queryset.py +12 -12
  288. udata/mongo/slug_fields.py +38 -28
  289. udata/mongo/taglist_field.py +1 -2
  290. udata/mongo/url_field.py +5 -5
  291. udata/mongo/uuid_fields.py +4 -3
  292. udata/notifications/__init__.py +1 -1
  293. udata/notifications/mattermost.py +10 -9
  294. udata/rdf.py +167 -188
  295. udata/routing.py +40 -45
  296. udata/search/__init__.py +18 -19
  297. udata/search/adapter.py +17 -16
  298. udata/search/commands.py +44 -51
  299. udata/search/fields.py +13 -20
  300. udata/search/query.py +23 -18
  301. udata/search/result.py +9 -10
  302. udata/sentry.py +21 -19
  303. udata/settings.py +262 -198
  304. udata/sitemap.py +8 -6
  305. udata/storage/s3.py +20 -13
  306. udata/tags.py +4 -5
  307. udata/tasks.py +43 -42
  308. udata/tests/__init__.py +9 -6
  309. udata/tests/api/__init__.py +8 -6
  310. udata/tests/api/test_auth_api.py +395 -321
  311. udata/tests/api/test_base_api.py +33 -35
  312. udata/tests/api/test_contact_points.py +7 -9
  313. udata/tests/api/test_dataservices_api.py +211 -158
  314. udata/tests/api/test_datasets_api.py +823 -812
  315. udata/tests/api/test_follow_api.py +13 -15
  316. udata/tests/api/test_me_api.py +95 -112
  317. udata/tests/api/test_organizations_api.py +301 -339
  318. udata/tests/api/test_reports_api.py +35 -25
  319. udata/tests/api/test_reuses_api.py +134 -139
  320. udata/tests/api/test_swagger.py +5 -5
  321. udata/tests/api/test_tags_api.py +18 -25
  322. udata/tests/api/test_topics_api.py +94 -94
  323. udata/tests/api/test_transfer_api.py +53 -48
  324. udata/tests/api/test_user_api.py +128 -141
  325. udata/tests/apiv2/test_datasets.py +290 -198
  326. udata/tests/apiv2/test_me_api.py +10 -11
  327. udata/tests/apiv2/test_organizations.py +56 -74
  328. udata/tests/apiv2/test_swagger.py +5 -5
  329. udata/tests/apiv2/test_topics.py +69 -87
  330. udata/tests/cli/test_cli_base.py +8 -8
  331. udata/tests/cli/test_db_cli.py +21 -19
  332. udata/tests/dataservice/test_dataservice_tasks.py +8 -12
  333. udata/tests/dataset/test_csv_adapter.py +44 -35
  334. udata/tests/dataset/test_dataset_actions.py +2 -3
  335. udata/tests/dataset/test_dataset_commands.py +7 -8
  336. udata/tests/dataset/test_dataset_events.py +36 -29
  337. udata/tests/dataset/test_dataset_model.py +224 -217
  338. udata/tests/dataset/test_dataset_rdf.py +142 -131
  339. udata/tests/dataset/test_dataset_tasks.py +15 -15
  340. udata/tests/dataset/test_resource_preview.py +10 -13
  341. udata/tests/features/territories/__init__.py +9 -13
  342. udata/tests/features/territories/test_territories_api.py +71 -91
  343. udata/tests/forms/test_basic_fields.py +7 -7
  344. udata/tests/forms/test_current_user_field.py +39 -66
  345. udata/tests/forms/test_daterange_field.py +31 -39
  346. udata/tests/forms/test_dict_field.py +28 -26
  347. udata/tests/forms/test_extras_fields.py +102 -76
  348. udata/tests/forms/test_form_field.py +8 -8
  349. udata/tests/forms/test_image_field.py +33 -26
  350. udata/tests/forms/test_model_field.py +134 -123
  351. udata/tests/forms/test_model_list_field.py +7 -7
  352. udata/tests/forms/test_nested_model_list_field.py +117 -79
  353. udata/tests/forms/test_publish_as_field.py +36 -65
  354. udata/tests/forms/test_reference_field.py +34 -53
  355. udata/tests/forms/test_user_forms.py +23 -21
  356. udata/tests/forms/test_uuid_field.py +6 -10
  357. udata/tests/frontend/__init__.py +9 -6
  358. udata/tests/frontend/test_auth.py +7 -6
  359. udata/tests/frontend/test_csv.py +81 -96
  360. udata/tests/frontend/test_hooks.py +43 -43
  361. udata/tests/frontend/test_markdown.py +211 -191
  362. udata/tests/helpers.py +32 -37
  363. udata/tests/models.py +2 -2
  364. udata/tests/organization/test_csv_adapter.py +21 -16
  365. udata/tests/organization/test_notifications.py +11 -18
  366. udata/tests/organization/test_organization_model.py +13 -13
  367. udata/tests/organization/test_organization_rdf.py +29 -22
  368. udata/tests/organization/test_organization_tasks.py +16 -17
  369. udata/tests/plugin.py +79 -73
  370. udata/tests/reuse/test_reuse_model.py +21 -21
  371. udata/tests/reuse/test_reuse_task.py +11 -13
  372. udata/tests/search/__init__.py +11 -12
  373. udata/tests/search/test_adapter.py +60 -70
  374. udata/tests/search/test_query.py +16 -16
  375. udata/tests/search/test_results.py +10 -7
  376. udata/tests/site/test_site_api.py +11 -16
  377. udata/tests/site/test_site_metrics.py +20 -30
  378. udata/tests/site/test_site_model.py +4 -5
  379. udata/tests/site/test_site_rdf.py +94 -78
  380. udata/tests/test_activity.py +17 -17
  381. udata/tests/test_cors.py +62 -0
  382. udata/tests/test_discussions.py +292 -299
  383. udata/tests/test_i18n.py +37 -40
  384. udata/tests/test_linkchecker.py +91 -85
  385. udata/tests/test_mail.py +13 -17
  386. udata/tests/test_migrations.py +219 -180
  387. udata/tests/test_model.py +164 -157
  388. udata/tests/test_notifications.py +17 -17
  389. udata/tests/test_owned.py +14 -14
  390. udata/tests/test_rdf.py +25 -23
  391. udata/tests/test_routing.py +89 -93
  392. udata/tests/test_storages.py +137 -128
  393. udata/tests/test_tags.py +44 -46
  394. udata/tests/test_topics.py +7 -7
  395. udata/tests/test_transfer.py +42 -49
  396. udata/tests/test_uris.py +160 -161
  397. udata/tests/test_utils.py +79 -71
  398. udata/tests/user/test_user_rdf.py +5 -9
  399. udata/tests/workers/test_jobs_commands.py +57 -58
  400. udata/tests/workers/test_tasks_routing.py +23 -29
  401. udata/tests/workers/test_workers_api.py +125 -131
  402. udata/tests/workers/test_workers_helpers.py +6 -6
  403. udata/tracking.py +4 -6
  404. udata/uris.py +45 -46
  405. udata/utils.py +68 -66
  406. udata/wsgi.py +1 -1
  407. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/METADATA +7 -3
  408. udata-9.1.2.dev30454.dist-info/RECORD +706 -0
  409. udata-9.1.2.dev30355.dist-info/RECORD +0 -704
  410. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/LICENSE +0 -0
  411. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/WHEEL +0 -0
  412. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/entry_points.txt +0 -0
  413. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/top_level.txt +0 -0
@@ -1,28 +1,31 @@
1
1
  from datetime import datetime
2
2
 
3
- from flask import url_for
4
3
  import pytest
4
+ from flask import url_for
5
5
 
6
- from udata.models import Dataset, Member
7
- from udata.core.discussions.models import Message, Discussion
6
+ from udata.core.dataset.factories import DatasetFactory
8
7
  from udata.core.discussions.metrics import update_discussions_metric # noqa
8
+ from udata.core.discussions.models import Discussion, Message
9
9
  from udata.core.discussions.notifications import discussions_notifications
10
10
  from udata.core.discussions.signals import (
11
- on_new_discussion, on_new_discussion_comment,
12
- on_discussion_closed, on_discussion_deleted,
11
+ on_discussion_closed,
12
+ on_discussion_deleted,
13
+ on_new_discussion,
14
+ on_new_discussion_comment,
13
15
  )
14
- from udata.core.spam.signals import on_new_potential_spam
15
16
  from udata.core.discussions.tasks import (
16
- notify_new_discussion, notify_new_discussion_comment,
17
- notify_discussion_closed
17
+ notify_discussion_closed,
18
+ notify_new_discussion,
19
+ notify_new_discussion_comment,
18
20
  )
19
- from udata.core.dataset.factories import DatasetFactory
20
21
  from udata.core.organization.factories import OrganizationFactory
21
- from udata.core.user.factories import UserFactory, AdminFactory
22
+ from udata.core.spam.signals import on_new_potential_spam
23
+ from udata.core.user.factories import AdminFactory, UserFactory
24
+ from udata.models import Dataset, Member
22
25
  from udata.tests.helpers import capture_mails
23
26
  from udata.utils import faker
24
27
 
25
- from . import TestCase, DBTestMixin
28
+ from . import DBTestMixin, TestCase
26
29
  from .api import APITestCase
27
30
  from .helpers import assert_emit, assert_not_emit
28
31
 
@@ -30,24 +33,27 @@ from .helpers import assert_emit, assert_not_emit
30
33
  class DiscussionsTest(APITestCase):
31
34
  modules = []
32
35
 
33
- @pytest.mark.options(SPAM_WORDS=['spam'])
36
+ @pytest.mark.options(SPAM_WORDS=["spam"])
34
37
  def test_new_discussion(self):
35
38
  user = self.login()
36
- dataset = Dataset.objects.create(title='Test dataset')
39
+ dataset = Dataset.objects.create(title="Test dataset")
37
40
 
38
41
  with assert_emit(on_new_discussion):
39
- response = self.post(url_for('api.discussions'), {
40
- 'title': 'test title',
41
- 'comment': 'bla bla',
42
- 'subject': {
43
- 'class': 'Dataset',
44
- 'id': dataset.id,
45
- }
46
- })
42
+ response = self.post(
43
+ url_for("api.discussions"),
44
+ {
45
+ "title": "test title",
46
+ "comment": "bla bla",
47
+ "subject": {
48
+ "class": "Dataset",
49
+ "id": dataset.id,
50
+ },
51
+ },
52
+ )
47
53
  self.assert201(response)
48
54
 
49
55
  dataset.reload()
50
- self.assertEqual(dataset.get_metrics()['discussions'], 1)
56
+ self.assertEqual(dataset.get_metrics()["discussions"], 1)
51
57
 
52
58
  discussions = Discussion.objects(subject=dataset)
53
59
  self.assertEqual(len(discussions), 1)
@@ -58,37 +64,44 @@ class DiscussionsTest(APITestCase):
58
64
  self.assertIsNotNone(discussion.created)
59
65
  self.assertIsNone(discussion.closed)
60
66
  self.assertIsNone(discussion.closed_by)
61
- self.assertEqual(discussion.title, 'test title')
67
+ self.assertEqual(discussion.title, "test title")
62
68
  self.assertFalse(discussion.is_spam())
63
69
 
64
70
  message = discussion.discussion[0]
65
- self.assertEqual(message.content, 'bla bla')
71
+ self.assertEqual(message.content, "bla bla")
66
72
  self.assertEqual(message.posted_by, user)
67
73
  self.assertIsNotNone(message.posted_on)
68
74
  self.assertFalse(message.is_spam())
69
75
 
70
- @pytest.mark.options(SPAM_WORDS=['spam'])
76
+ @pytest.mark.options(SPAM_WORDS=["spam"])
71
77
  def test_spam_in_new_discussion_title(self):
72
78
  self.login()
73
- dataset = Dataset.objects.create(title='Test dataset')
79
+ dataset = Dataset.objects.create(title="Test dataset")
74
80
 
75
81
  with assert_not_emit(on_new_discussion):
76
82
  discussion_id = None
83
+
77
84
  def check_signal(args):
78
85
  self.assertIsNotNone(discussion_id)
79
- self.assertIn(f'http://local.test/api/1/datasets/{dataset.slug}/#discussion-{discussion_id}', args[1]['message'])
86
+ self.assertIn(
87
+ f"http://local.test/api/1/datasets/{dataset.slug}/#discussion-{discussion_id}",
88
+ args[1]["message"],
89
+ )
80
90
 
81
91
  with assert_emit(on_new_potential_spam, assertions_callback=check_signal):
82
- response = self.post(url_for('api.discussions'), {
83
- 'title': 'spam and blah',
84
- 'comment': 'bla bla',
85
- 'subject': {
86
- 'class': 'Dataset',
87
- 'id': dataset.id,
88
- }
89
- })
92
+ response = self.post(
93
+ url_for("api.discussions"),
94
+ {
95
+ "title": "spam and blah",
96
+ "comment": "bla bla",
97
+ "subject": {
98
+ "class": "Dataset",
99
+ "id": dataset.id,
100
+ },
101
+ },
102
+ )
90
103
  self.assertStatus(response, 201)
91
- discussion_id = response.json['id']
104
+ discussion_id = response.json["id"]
92
105
 
93
106
  discussions = Discussion.objects(subject=dataset)
94
107
  self.assertEqual(len(discussions), 1)
@@ -96,70 +109,81 @@ class DiscussionsTest(APITestCase):
96
109
  discussion = discussions[0]
97
110
  self.assertTrue(discussion.is_spam())
98
111
  self.assertFalse(discussion.discussion[0].is_spam())
99
- self.assertTrue('signal_new' in discussion.spam.callbacks)
112
+ self.assertTrue("signal_new" in discussion.spam.callbacks)
100
113
 
101
114
  with assert_not_emit(on_new_discussion):
102
- response = self.delete(url_for('api.discussion_spam', id=discussion.id))
115
+ response = self.delete(url_for("api.discussion_spam", id=discussion.id))
103
116
  self.assertStatus(response, 403)
104
117
  self.assertTrue(discussion.reload().is_spam())
105
118
 
106
119
  self.login(AdminFactory())
107
- response = self.get(url_for('api.spam'))
120
+ response = self.get(url_for("api.spam"))
108
121
  self.assertStatus(response, 200)
109
- self.assertEqual(response.json, [{
110
- 'message': discussion.spam_report_message([discussion]),
111
- }])
122
+ self.assertEqual(
123
+ response.json,
124
+ [
125
+ {
126
+ "message": discussion.spam_report_message([discussion]),
127
+ }
128
+ ],
129
+ )
112
130
 
113
131
  with assert_emit(on_new_discussion):
114
- response = self.delete(url_for('api.discussion_spam', id=discussion.id))
132
+ response = self.delete(url_for("api.discussion_spam", id=discussion.id))
115
133
  self.assertStatus(response, 200)
116
134
  self.assertFalse(discussion.reload().is_spam())
117
135
 
118
136
  # Adding a new comment / modifying the not spam discussion
119
- response = self.post(url_for('api.discussion', id=discussion.id), {
120
- 'comment': 'A new normal comment'
121
- })
137
+ response = self.post(
138
+ url_for("api.discussion", id=discussion.id), {"comment": "A new normal comment"}
139
+ )
122
140
  self.assertStatus(response, 200)
123
141
  self.assertFalse(discussion.reload().is_spam())
124
142
 
125
-
126
- @pytest.mark.options(SPAM_WORDS=['spam'])
143
+ @pytest.mark.options(SPAM_WORDS=["spam"])
127
144
  def test_spam_by_owner(self):
128
145
  user = self.login()
129
- dataset = Dataset.objects.create(title='Test dataset', owner=user)
146
+ dataset = Dataset.objects.create(title="Test dataset", owner=user)
130
147
 
131
148
  with assert_not_emit(on_new_potential_spam):
132
- response = self.post(url_for('api.discussions'), {
133
- 'title': 'spam and blah',
134
- 'comment': 'bla bla',
135
- 'subject': {
136
- 'class': 'Dataset',
137
- 'id': dataset.id,
138
- }
139
- })
149
+ response = self.post(
150
+ url_for("api.discussions"),
151
+ {
152
+ "title": "spam and blah",
153
+ "comment": "bla bla",
154
+ "subject": {
155
+ "class": "Dataset",
156
+ "id": dataset.id,
157
+ },
158
+ },
159
+ )
140
160
  self.assertStatus(response, 201)
141
-
161
+
142
162
  with assert_not_emit(on_new_potential_spam):
143
- response = self.post(url_for('api.discussion', id=response.json['id']), {
144
- 'comment': 'A comment with spam by owner'
145
- })
163
+ response = self.post(
164
+ url_for("api.discussion", id=response.json["id"]),
165
+ {"comment": "A comment with spam by owner"},
166
+ )
146
167
  self.assertStatus(response, 200)
147
168
 
148
- @pytest.mark.options(SPAM_WORDS=['spam'])
169
+ @pytest.mark.options(SPAM_WORDS=["spam"])
149
170
  def test_spam_in_new_discussion_comment(self):
150
171
  self.login()
151
- dataset = Dataset.objects.create(title='Test dataset')
172
+ dataset = Dataset.objects.create(title="Test dataset")
152
173
 
153
174
  with assert_not_emit(on_new_discussion):
154
175
  with assert_emit(on_new_potential_spam):
155
- response = self.post(url_for('api.discussions'), {
156
- 'title': 'title and blah',
157
- 'comment': 'bla bla spam',
158
- 'subject': {
159
- 'class': 'Dataset',
160
- 'id': dataset.id,
161
- }
162
- })
176
+ response = self.post(
177
+ url_for("api.discussions"),
178
+ {
179
+ "title": "title and blah",
180
+ "comment": "bla bla spam",
181
+ "subject": {
182
+ "class": "Dataset",
183
+ "id": dataset.id,
184
+ },
185
+ },
186
+ )
163
187
  self.assertStatus(response, 201)
164
188
 
165
189
  discussions = Discussion.objects(subject=dataset)
@@ -171,53 +195,60 @@ class DiscussionsTest(APITestCase):
171
195
 
172
196
  def test_new_discussion_missing_comment(self):
173
197
  self.login()
174
- dataset = Dataset.objects.create(title='Test dataset')
175
-
176
- response = self.post(url_for('api.discussions'), {
177
- 'title': 'test title',
178
- 'subject': {
179
- 'class': 'Dataset',
180
- 'id': dataset.id,
181
- }
182
- })
198
+ dataset = Dataset.objects.create(title="Test dataset")
199
+
200
+ response = self.post(
201
+ url_for("api.discussions"),
202
+ {
203
+ "title": "test title",
204
+ "subject": {
205
+ "class": "Dataset",
206
+ "id": dataset.id,
207
+ },
208
+ },
209
+ )
183
210
  self.assertStatus(response, 400)
184
211
 
185
212
  def test_new_discussion_missing_title(self):
186
213
  self.login()
187
- dataset = Dataset.objects.create(title='Test dataset')
188
-
189
- response = self.post(url_for('api.discussions'), {
190
- 'comment': 'bla bla',
191
- 'subject': {
192
- 'class': 'Dataset',
193
- 'id': dataset.id,
194
- }
195
- })
214
+ dataset = Dataset.objects.create(title="Test dataset")
215
+
216
+ response = self.post(
217
+ url_for("api.discussions"),
218
+ {
219
+ "comment": "bla bla",
220
+ "subject": {
221
+ "class": "Dataset",
222
+ "id": dataset.id,
223
+ },
224
+ },
225
+ )
196
226
  self.assertStatus(response, 400)
197
227
 
198
228
  def test_new_discussion_missing_subject(self):
199
229
  self.login()
200
- response = self.post(url_for('api.discussions'), {
201
- 'title': 'test title',
202
- 'comment': 'bla bla'
203
- })
230
+ response = self.post(
231
+ url_for("api.discussions"), {"title": "test title", "comment": "bla bla"}
232
+ )
204
233
  self.assertStatus(response, 400)
205
234
 
206
235
  def test_new_discussion_with_extras(self):
207
236
  user = self.login()
208
- dataset = Dataset.objects.create(title='Test dataset',
209
- extras={'key': 'value'})
237
+ dataset = Dataset.objects.create(title="Test dataset", extras={"key": "value"})
210
238
 
211
239
  with assert_emit(on_new_discussion):
212
- response = self.post(url_for('api.discussions'), {
213
- 'title': 'test title',
214
- 'comment': 'bla bla',
215
- 'subject': {
216
- 'class': 'Dataset',
217
- 'id': dataset.id,
240
+ response = self.post(
241
+ url_for("api.discussions"),
242
+ {
243
+ "title": "test title",
244
+ "comment": "bla bla",
245
+ "subject": {
246
+ "class": "Dataset",
247
+ "id": dataset.id,
248
+ },
249
+ "extras": {"key": "value"},
218
250
  },
219
- 'extras': {'key': 'value'}
220
- })
251
+ )
221
252
  self.assert201(response)
222
253
 
223
254
  discussions = Discussion.objects(subject=dataset)
@@ -226,16 +257,16 @@ class DiscussionsTest(APITestCase):
226
257
  discussion = discussions[0]
227
258
  self.assertEqual(discussion.user, user)
228
259
  self.assertEqual(len(discussion.discussion), 1)
229
- self.assertEqual(discussion.title, 'test title')
230
- self.assertEqual(discussion.extras, {u'key': u'value'})
260
+ self.assertEqual(discussion.title, "test title")
261
+ self.assertEqual(discussion.extras, {"key": "value"})
231
262
 
232
263
  message = discussion.discussion[0]
233
- self.assertEqual(message.content, 'bla bla')
264
+ self.assertEqual(message.content, "bla bla")
234
265
  self.assertEqual(message.posted_by, user)
235
266
  self.assertIsNotNone(message.posted_on)
236
267
 
237
268
  def test_list_discussions(self):
238
- dataset = Dataset.objects.create(title='Test dataset')
269
+ dataset = Dataset.objects.create(title="Test dataset")
239
270
  open_discussions = []
240
271
  closed_discussions = []
241
272
  for i in range(2):
@@ -244,8 +275,8 @@ class DiscussionsTest(APITestCase):
244
275
  discussion = Discussion.objects.create(
245
276
  subject=dataset,
246
277
  user=user,
247
- title='test discussion {}'.format(i),
248
- discussion=[message]
278
+ title="test discussion {}".format(i),
279
+ discussion=[message],
249
280
  )
250
281
  open_discussions.append(discussion)
251
282
  for i in range(3):
@@ -254,21 +285,20 @@ class DiscussionsTest(APITestCase):
254
285
  discussion = Discussion.objects.create(
255
286
  subject=dataset,
256
287
  user=user,
257
- title='test discussion {}'.format(i),
288
+ title="test discussion {}".format(i),
258
289
  discussion=[message],
259
290
  closed=datetime.utcnow(),
260
- closed_by=user
291
+ closed_by=user,
261
292
  )
262
293
  closed_discussions.append(discussion)
263
294
 
264
- response = self.get(url_for('api.discussions'))
295
+ response = self.get(url_for("api.discussions"))
265
296
  self.assert200(response)
266
297
 
267
- self.assertEqual(len(response.json['data']),
268
- len(open_discussions + closed_discussions))
298
+ self.assertEqual(len(response.json["data"]), len(open_discussions + closed_discussions))
269
299
 
270
300
  def test_list_discussions_closed_filter(self):
271
- dataset = Dataset.objects.create(title='Test dataset')
301
+ dataset = Dataset.objects.create(title="Test dataset")
272
302
  open_discussions = []
273
303
  closed_discussions = []
274
304
  for i in range(2):
@@ -277,8 +307,8 @@ class DiscussionsTest(APITestCase):
277
307
  discussion = Discussion.objects.create(
278
308
  subject=dataset,
279
309
  user=user,
280
- title='test discussion {}'.format(i),
281
- discussion=[message]
310
+ title="test discussion {}".format(i),
311
+ discussion=[message],
282
312
  )
283
313
  open_discussions.append(discussion)
284
314
  for i in range(3):
@@ -287,18 +317,18 @@ class DiscussionsTest(APITestCase):
287
317
  discussion = Discussion.objects.create(
288
318
  subject=dataset,
289
319
  user=user,
290
- title='test discussion {}'.format(i),
320
+ title="test discussion {}".format(i),
291
321
  discussion=[message],
292
322
  closed=datetime.utcnow(),
293
- closed_by=user
323
+ closed_by=user,
294
324
  )
295
325
  closed_discussions.append(discussion)
296
326
 
297
- response = self.get(url_for('api.discussions', closed=True))
327
+ response = self.get(url_for("api.discussions", closed=True))
298
328
  self.assert200(response)
299
- self.assertEqual(len(response.json['data']), len(closed_discussions))
300
- for discussion in response.json['data']:
301
- self.assertIsNotNone(discussion['closed'])
329
+ self.assertEqual(len(response.json["data"]), len(closed_discussions))
330
+ for discussion in response.json["data"]:
331
+ self.assertIsNotNone(discussion["closed"])
302
332
 
303
333
  def test_list_discussions_for(self):
304
334
  dataset = DatasetFactory()
@@ -309,8 +339,8 @@ class DiscussionsTest(APITestCase):
309
339
  discussion = Discussion.objects.create(
310
340
  subject=dataset,
311
341
  user=user,
312
- title='test discussion {}'.format(i),
313
- discussion=[message]
342
+ title="test discussion {}".format(i),
343
+ discussion=[message],
314
344
  )
315
345
  discussions.append(discussion)
316
346
  user = UserFactory()
@@ -318,15 +348,15 @@ class DiscussionsTest(APITestCase):
318
348
  Discussion.objects.create(
319
349
  subject=DatasetFactory(),
320
350
  user=user,
321
- title='test discussion {}'.format(i),
322
- discussion=[message]
351
+ title="test discussion {}".format(i),
352
+ discussion=[message],
323
353
  )
324
354
 
325
- kwargs = {'for': str(dataset.id)}
326
- response = self.get(url_for('api.discussions', **kwargs))
355
+ kwargs = {"for": str(dataset.id)}
356
+ response = self.get(url_for("api.discussions", **kwargs))
327
357
  self.assert200(response)
328
358
 
329
- self.assertEqual(len(response.json['data']), len(discussions))
359
+ self.assertEqual(len(response.json["data"]), len(discussions))
330
360
 
331
361
  def test_list_discussions_user(self):
332
362
  dataset = DatasetFactory()
@@ -337,8 +367,8 @@ class DiscussionsTest(APITestCase):
337
367
  discussion = Discussion.objects.create(
338
368
  subject=dataset,
339
369
  user=user,
340
- title='test discussion {}'.format(i),
341
- discussion=[message]
370
+ title="test discussion {}".format(i),
371
+ discussion=[message],
342
372
  )
343
373
  discussions.append(discussion)
344
374
  user = UserFactory()
@@ -346,293 +376,262 @@ class DiscussionsTest(APITestCase):
346
376
  Discussion.objects.create(
347
377
  subject=DatasetFactory(),
348
378
  user=user,
349
- title='test discussion {}'.format(i+1),
350
- discussion=[message]
379
+ title="test discussion {}".format(i + 1),
380
+ discussion=[message],
351
381
  )
352
382
 
353
- kwargs = {'user': str(user.id)}
354
- response = self.get(url_for('api.discussions', **kwargs))
383
+ kwargs = {"user": str(user.id)}
384
+ response = self.get(url_for("api.discussions", **kwargs))
355
385
  self.assert200(response)
356
386
 
357
- self.assertEqual(len(response.json['data']), 1)
358
- self.assertEqual(response.json['data'][0]['user']['id'], str(user.id))
387
+ self.assertEqual(len(response.json["data"]), 1)
388
+ self.assertEqual(response.json["data"][0]["user"]["id"], str(user.id))
359
389
 
360
390
  def test_get_discussion(self):
361
- dataset = Dataset.objects.create(title='Test dataset')
391
+ dataset = Dataset.objects.create(title="Test dataset")
362
392
  user = UserFactory()
363
- message = Message(content='bla bla', posted_by=user)
393
+ message = Message(content="bla bla", posted_by=user)
364
394
  discussion = Discussion.objects.create(
365
- subject=dataset,
366
- user=user,
367
- title='test discussion',
368
- discussion=[message]
395
+ subject=dataset, user=user, title="test discussion", discussion=[message]
369
396
  )
370
397
 
371
- response = self.get(url_for('api.discussion', **{'id': discussion.id}))
398
+ response = self.get(url_for("api.discussion", **{"id": discussion.id}))
372
399
  self.assert200(response)
373
400
 
374
401
  data = response.json
375
402
 
376
- self.assertEqual(data['subject']['class'], 'Dataset')
377
- self.assertEqual(data['subject']['id'], str(dataset.id))
378
- self.assertEqual(data['user']['id'], str(user.id))
379
- self.assertEqual(data['title'], 'test discussion')
380
- self.assertIsNotNone(data['created'])
381
- self.assertEqual(len(data['discussion']), 1)
382
- self.assertEqual(data['discussion'][0]['content'], 'bla bla')
383
- self.assertEqual(
384
- data['discussion'][0]['posted_by']['id'], str(user.id))
385
- self.assertIsNotNone(data['discussion'][0]['posted_on'])
386
-
387
- @pytest.mark.options(SPAM_WORDS=['spam'])
403
+ self.assertEqual(data["subject"]["class"], "Dataset")
404
+ self.assertEqual(data["subject"]["id"], str(dataset.id))
405
+ self.assertEqual(data["user"]["id"], str(user.id))
406
+ self.assertEqual(data["title"], "test discussion")
407
+ self.assertIsNotNone(data["created"])
408
+ self.assertEqual(len(data["discussion"]), 1)
409
+ self.assertEqual(data["discussion"][0]["content"], "bla bla")
410
+ self.assertEqual(data["discussion"][0]["posted_by"]["id"], str(user.id))
411
+ self.assertIsNotNone(data["discussion"][0]["posted_on"])
412
+
413
+ @pytest.mark.options(SPAM_WORDS=["spam"])
388
414
  def test_add_comment_to_discussion(self):
389
- dataset = Dataset.objects.create(title='Test dataset')
415
+ dataset = Dataset.objects.create(title="Test dataset")
390
416
  user = UserFactory()
391
- message = Message(content='bla bla', posted_by=user)
417
+ message = Message(content="bla bla", posted_by=user)
392
418
  discussion = Discussion.objects.create(
393
- subject=dataset,
394
- user=user,
395
- title='test discussion',
396
- discussion=[message]
419
+ subject=dataset, user=user, title="test discussion", discussion=[message]
397
420
  )
398
421
  on_new_discussion.send(discussion) # Updating metrics.
399
422
 
400
423
  poster = self.login()
401
424
  with assert_emit(on_new_discussion_comment):
402
- response = self.post(url_for('api.discussion', id=discussion.id), {
403
- 'comment': 'new bla bla'
404
- })
425
+ response = self.post(
426
+ url_for("api.discussion", id=discussion.id), {"comment": "new bla bla"}
427
+ )
405
428
  self.assert200(response)
406
429
 
407
430
  dataset.reload()
408
431
  discussion.reload()
409
- self.assertEqual(dataset.get_metrics()['discussions'], 1)
432
+ self.assertEqual(dataset.get_metrics()["discussions"], 1)
410
433
 
411
434
  data = response.json
412
435
 
413
- self.assertEqual(data['subject']['class'], 'Dataset')
414
- self.assertEqual(data['subject']['id'], str(dataset.id))
415
- self.assertEqual(data['user']['id'], str(user.id))
416
- self.assertEqual(data['title'], 'test discussion')
417
- self.assertIsNotNone(data['created'])
418
- self.assertIsNone(data['closed'])
419
- self.assertIsNone(data['closed_by'])
420
- self.assertEqual(len(data['discussion']), 2)
421
- self.assertEqual(data['discussion'][1]['content'], 'new bla bla')
422
- self.assertEqual(
423
- data['discussion'][1]['posted_by']['id'], str(poster.id))
424
- self.assertIsNotNone(data['discussion'][1]['posted_on'])
436
+ self.assertEqual(data["subject"]["class"], "Dataset")
437
+ self.assertEqual(data["subject"]["id"], str(dataset.id))
438
+ self.assertEqual(data["user"]["id"], str(user.id))
439
+ self.assertEqual(data["title"], "test discussion")
440
+ self.assertIsNotNone(data["created"])
441
+ self.assertIsNone(data["closed"])
442
+ self.assertIsNone(data["closed_by"])
443
+ self.assertEqual(len(data["discussion"]), 2)
444
+ self.assertEqual(data["discussion"][1]["content"], "new bla bla")
445
+ self.assertEqual(data["discussion"][1]["posted_by"]["id"], str(poster.id))
446
+ self.assertIsNotNone(data["discussion"][1]["posted_on"])
425
447
  self.assertFalse(discussion.discussion[1].is_spam())
426
448
 
427
- @pytest.mark.options(SPAM_WORDS=['spam'])
449
+ @pytest.mark.options(SPAM_WORDS=["spam"])
428
450
  def test_add_spam_comment_to_discussion(self):
429
-
430
- dataset = Dataset.objects.create(title='Test dataset')
451
+ dataset = Dataset.objects.create(title="Test dataset")
431
452
  user = UserFactory()
432
- message = Message(content='bla bla', posted_by=user)
453
+ message = Message(content="bla bla", posted_by=user)
433
454
  discussion = Discussion.objects.create(
434
- subject=dataset,
435
- user=user,
436
- title='test discussion',
437
- discussion=[message]
455
+ subject=dataset, user=user, title="test discussion", discussion=[message]
438
456
  )
439
457
  on_new_discussion.send(discussion) # Updating metrics.
440
458
 
441
459
  self.login()
442
460
  with assert_not_emit(on_new_discussion_comment):
461
+
443
462
  def check_signal(args):
444
- self.assertIn(discussion.external_url, args[1]['message'])
463
+ self.assertIn(discussion.external_url, args[1]["message"])
445
464
 
446
465
  with assert_emit(on_new_potential_spam, assertions_callback=check_signal):
447
- response = self.post(url_for('api.discussion', id=discussion.id), {
448
- 'comment': 'spam new bla bla'
449
- })
466
+ response = self.post(
467
+ url_for("api.discussion", id=discussion.id), {"comment": "spam new bla bla"}
468
+ )
450
469
  self.assert200(response)
451
470
 
452
471
  discussion.reload()
453
472
  self.assertFalse(discussion.is_spam())
454
473
  self.assertTrue(discussion.discussion[1].is_spam())
455
- self.assertTrue('signal_comment' in discussion.discussion[1].spam.callbacks)
474
+ self.assertTrue("signal_comment" in discussion.discussion[1].spam.callbacks)
456
475
 
457
476
  self.login(AdminFactory())
458
- response = self.get(url_for('api.spam'))
477
+ response = self.get(url_for("api.spam"))
459
478
  self.assertStatus(response, 200)
460
- self.assertEqual(response.json, [{
461
- 'message': discussion.spam_report_message([discussion]),
462
- }])
479
+ self.assertEqual(
480
+ response.json,
481
+ [
482
+ {
483
+ "message": discussion.spam_report_message([discussion]),
484
+ }
485
+ ],
486
+ )
463
487
 
464
488
  with assert_emit(on_new_discussion_comment):
465
- response = self.delete(url_for('api.discussion_comment_spam', id=discussion.id, cidx=1))
489
+ response = self.delete(url_for("api.discussion_comment_spam", id=discussion.id, cidx=1))
466
490
  self.assertStatus(response, 200)
467
491
  self.assertFalse(discussion.reload().discussion[1].is_spam())
468
492
 
469
- response = self.post(url_for('api.discussion', id=discussion.id), {
470
- 'comment': 'New comment'
471
- })
493
+ response = self.post(
494
+ url_for("api.discussion", id=discussion.id), {"comment": "New comment"}
495
+ )
472
496
  self.assert200(response)
473
497
 
474
498
  # The spam comment marked as no spam is still a no spam
475
499
  self.assertFalse(discussion.reload().discussion[1].is_spam())
476
500
 
477
-
478
501
  def test_close_discussion(self):
479
502
  owner = self.login()
480
503
  user = UserFactory()
481
- dataset = Dataset.objects.create(title='Test dataset', owner=owner)
482
- message = Message(content='bla bla', posted_by=user)
504
+ dataset = Dataset.objects.create(title="Test dataset", owner=owner)
505
+ message = Message(content="bla bla", posted_by=user)
483
506
  discussion = Discussion.objects.create(
484
- subject=dataset,
485
- user=user,
486
- title='test discussion',
487
- discussion=[message]
507
+ subject=dataset, user=user, title="test discussion", discussion=[message]
488
508
  )
489
509
  on_new_discussion.send(discussion) # Updating metrics.
490
510
 
491
511
  with assert_emit(on_discussion_closed):
492
- response = self.post(url_for('api.discussion', id=discussion.id), {
493
- 'comment': 'close bla bla',
494
- 'close': True
495
- })
512
+ response = self.post(
513
+ url_for("api.discussion", id=discussion.id),
514
+ {"comment": "close bla bla", "close": True},
515
+ )
496
516
  self.assert200(response)
497
517
 
498
518
  dataset.reload()
499
- self.assertEqual(dataset.get_metrics()['discussions'], 0)
519
+ self.assertEqual(dataset.get_metrics()["discussions"], 0)
500
520
 
501
521
  data = response.json
502
522
 
503
- self.assertEqual(data['subject']['class'], 'Dataset')
504
- self.assertEqual(data['subject']['id'], str(dataset.id))
505
- self.assertEqual(data['user']['id'], str(user.id))
506
- self.assertEqual(data['title'], 'test discussion')
507
- self.assertIsNotNone(data['created'])
508
- self.assertIsNotNone(data['closed'])
509
- self.assertEqual(data['closed_by']['id'], str(owner.id))
510
- self.assertEqual(len(data['discussion']), 2)
511
- self.assertEqual(data['discussion'][1]['content'], 'close bla bla')
512
- self.assertEqual(
513
- data['discussion'][1]['posted_by']['id'], str(owner.id))
514
- self.assertIsNotNone(data['discussion'][1]['posted_on'])
523
+ self.assertEqual(data["subject"]["class"], "Dataset")
524
+ self.assertEqual(data["subject"]["id"], str(dataset.id))
525
+ self.assertEqual(data["user"]["id"], str(user.id))
526
+ self.assertEqual(data["title"], "test discussion")
527
+ self.assertIsNotNone(data["created"])
528
+ self.assertIsNotNone(data["closed"])
529
+ self.assertEqual(data["closed_by"]["id"], str(owner.id))
530
+ self.assertEqual(len(data["discussion"]), 2)
531
+ self.assertEqual(data["discussion"][1]["content"], "close bla bla")
532
+ self.assertEqual(data["discussion"][1]["posted_by"]["id"], str(owner.id))
533
+ self.assertIsNotNone(data["discussion"][1]["posted_on"])
515
534
 
516
535
  # Can't add anymore comments
517
- response = self.post(url_for('api.discussion', id=discussion.id),
518
- {'comment': "can't comment"})
536
+ response = self.post(
537
+ url_for("api.discussion", id=discussion.id), {"comment": "can't comment"}
538
+ )
519
539
  self.assert403(response)
520
540
 
521
541
  def test_close_discussion_permissions(self):
522
- dataset = Dataset.objects.create(title='Test dataset')
542
+ dataset = Dataset.objects.create(title="Test dataset")
523
543
  user = UserFactory()
524
- message = Message(content='bla bla', posted_by=user)
544
+ message = Message(content="bla bla", posted_by=user)
525
545
  discussion = Discussion.objects.create(
526
- subject=dataset,
527
- user=user,
528
- title='test discussion',
529
- discussion=[message]
546
+ subject=dataset, user=user, title="test discussion", discussion=[message]
530
547
  )
531
548
  on_new_discussion.send(discussion) # Updating metrics.
532
549
 
533
550
  self.login()
534
- response = self.post(url_for('api.discussion', id=discussion.id), {
535
- 'comment': 'close bla bla',
536
- 'close': True
537
- })
551
+ response = self.post(
552
+ url_for("api.discussion", id=discussion.id), {"comment": "close bla bla", "close": True}
553
+ )
538
554
  self.assert403(response)
539
555
 
540
556
  dataset.reload()
541
557
  # Metrics unchanged after attempt to close the discussion.
542
- self.assertEqual(dataset.get_metrics()['discussions'], 1)
558
+ self.assertEqual(dataset.get_metrics()["discussions"], 1)
543
559
 
544
560
  def test_delete_discussion(self):
545
561
  owner = self.login(AdminFactory())
546
562
  user = UserFactory()
547
- dataset = Dataset.objects.create(title='Test dataset', owner=owner)
548
- message = Message(content='bla bla', posted_by=user)
563
+ dataset = Dataset.objects.create(title="Test dataset", owner=owner)
564
+ message = Message(content="bla bla", posted_by=user)
549
565
  discussion = Discussion.objects.create(
550
- subject=dataset,
551
- user=user,
552
- title='test discussion',
553
- discussion=[message]
566
+ subject=dataset, user=user, title="test discussion", discussion=[message]
554
567
  )
555
568
  on_new_discussion.send(discussion) # Updating metrics.
556
569
  self.assertEqual(Discussion.objects(subject=dataset).count(), 1)
557
570
 
558
571
  with assert_emit(on_discussion_deleted):
559
- response = self.delete(url_for('api.discussion', id=discussion.id))
572
+ response = self.delete(url_for("api.discussion", id=discussion.id))
560
573
  self.assertStatus(response, 204)
561
574
 
562
575
  dataset.reload()
563
- self.assertEqual(dataset.get_metrics()['discussions'], 0)
576
+ self.assertEqual(dataset.get_metrics()["discussions"], 0)
564
577
  self.assertEqual(Discussion.objects(subject=dataset).count(), 0)
565
578
 
566
579
  def test_delete_discussion_comment(self):
567
580
  owner = self.login(AdminFactory())
568
581
  user = UserFactory()
569
- dataset = Dataset.objects.create(title='Test dataset', owner=owner)
570
- message = Message(content='bla bla', posted_by=user)
571
- message2 = Message(content='bla bla bla', posted_by=user)
582
+ dataset = Dataset.objects.create(title="Test dataset", owner=owner)
583
+ message = Message(content="bla bla", posted_by=user)
584
+ message2 = Message(content="bla bla bla", posted_by=user)
572
585
  discussion = Discussion.objects.create(
573
- subject=dataset,
574
- user=user,
575
- title='test discussion',
576
- discussion=[message, message2]
586
+ subject=dataset, user=user, title="test discussion", discussion=[message, message2]
577
587
  )
578
588
  self.assertEqual(len(discussion.discussion), 2)
579
589
 
580
590
  # test first comment deletion
581
- response = self.delete(url_for('api.discussion_comment',
582
- id=discussion.id, cidx=0))
591
+ response = self.delete(url_for("api.discussion_comment", id=discussion.id, cidx=0))
583
592
  self.assertStatus(response, 400)
584
593
 
585
594
  # test effective deletion
586
- response = self.delete(url_for('api.discussion_comment',
587
- id=discussion.id, cidx=1))
595
+ response = self.delete(url_for("api.discussion_comment", id=discussion.id, cidx=1))
588
596
  self.assertStatus(response, 204)
589
597
  discussion.reload()
590
598
  self.assertEqual(len(discussion.discussion), 1)
591
- self.assertEqual(discussion.discussion[0].content, 'bla bla')
599
+ self.assertEqual(discussion.discussion[0].content, "bla bla")
592
600
 
593
601
  # delete again to test list overflow
594
- response = self.delete(url_for('api.discussion_comment',
595
- id=discussion.id, cidx=3))
602
+ response = self.delete(url_for("api.discussion_comment", id=discussion.id, cidx=3))
596
603
  self.assertStatus(response, 404)
597
604
 
598
605
  # delete again to test last comment deletion
599
- response = self.delete(url_for('api.discussion_comment',
600
- id=discussion.id, cidx=0))
606
+ response = self.delete(url_for("api.discussion_comment", id=discussion.id, cidx=0))
601
607
  self.assertStatus(response, 400)
602
608
 
603
609
  def test_delete_discussion_permissions(self):
604
- dataset = Dataset.objects.create(title='Test dataset')
610
+ dataset = Dataset.objects.create(title="Test dataset")
605
611
  user = UserFactory()
606
- message = Message(content='bla bla', posted_by=user)
612
+ message = Message(content="bla bla", posted_by=user)
607
613
  discussion = Discussion.objects.create(
608
- subject=dataset,
609
- user=user,
610
- title='test discussion',
611
- discussion=[message]
614
+ subject=dataset, user=user, title="test discussion", discussion=[message]
612
615
  )
613
616
  on_new_discussion.send(discussion) # Updating metrics.
614
617
 
615
618
  self.login()
616
- response = self.delete(url_for('api.discussion', id=discussion.id))
619
+ response = self.delete(url_for("api.discussion", id=discussion.id))
617
620
  self.assert403(response)
618
621
 
619
622
  dataset.reload()
620
623
  # Metrics unchanged after attempt to delete the discussion.
621
- self.assertEqual(dataset.get_metrics()['discussions'], 1)
624
+ self.assertEqual(dataset.get_metrics()["discussions"], 1)
622
625
 
623
626
  def test_delete_discussion_comment_permissions(self):
624
- dataset = Dataset.objects.create(title='Test dataset')
627
+ dataset = Dataset.objects.create(title="Test dataset")
625
628
  user = UserFactory()
626
- message = Message(content='bla bla', posted_by=user)
629
+ message = Message(content="bla bla", posted_by=user)
627
630
  discussion = Discussion.objects.create(
628
- subject=dataset,
629
- user=user,
630
- title='test discussion',
631
- discussion=[message]
631
+ subject=dataset, user=user, title="test discussion", discussion=[message]
632
632
  )
633
633
  self.login()
634
- response = self.delete(url_for('api.discussion_comment',
635
- id=discussion.id, cidx=0))
634
+ response = self.delete(url_for("api.discussion_comment", id=discussion.id, cidx=0))
636
635
  self.assert403(response)
637
636
 
638
637
 
@@ -646,10 +645,7 @@ class DiscussionsNotificationsTest(TestCase, DBTestMixin):
646
645
  user = UserFactory()
647
646
  message = Message(content=faker.sentence(), posted_by=user)
648
647
  discussion = Discussion.objects.create(
649
- subject=dataset,
650
- user=user,
651
- title=faker.sentence(),
652
- discussion=[message]
648
+ subject=dataset, user=user, title=faker.sentence(), discussion=[message]
653
649
  )
654
650
  open_discussions[discussion.id] = discussion
655
651
  # Creating a closed discussion that shouldn't show up in response.
@@ -661,7 +657,7 @@ class DiscussionsNotificationsTest(TestCase, DBTestMixin):
661
657
  title=faker.sentence(),
662
658
  discussion=[message],
663
659
  closed=datetime.utcnow(),
664
- closed_by=user
660
+ closed_by=user,
665
661
  )
666
662
 
667
663
  notifications = discussions_notifications(owner)
@@ -669,14 +665,14 @@ class DiscussionsNotificationsTest(TestCase, DBTestMixin):
669
665
  self.assertEqual(len(notifications), len(open_discussions))
670
666
 
671
667
  for dt, details in notifications:
672
- discussion = open_discussions[details['id']]
673
- self.assertEqual(details['title'], discussion.title)
674
- self.assertEqual(details['subject']['id'], discussion.subject.id)
675
- self.assertEqual(details['subject']['type'], 'dataset')
668
+ discussion = open_discussions[details["id"]]
669
+ self.assertEqual(details["title"], discussion.title)
670
+ self.assertEqual(details["subject"]["id"], discussion.subject.id)
671
+ self.assertEqual(details["subject"]["type"], "dataset")
676
672
 
677
673
  def test_notify_org_discussions(self):
678
674
  recipient = UserFactory()
679
- member = Member(user=recipient, role='editor')
675
+ member = Member(user=recipient, role="editor")
680
676
  org = OrganizationFactory(members=[member])
681
677
  dataset = DatasetFactory(organization=org)
682
678
 
@@ -685,10 +681,7 @@ class DiscussionsNotificationsTest(TestCase, DBTestMixin):
685
681
  user = UserFactory()
686
682
  message = Message(content=faker.sentence(), posted_by=user)
687
683
  discussion = Discussion.objects.create(
688
- subject=dataset,
689
- user=user,
690
- title=faker.sentence(),
691
- discussion=[message]
684
+ subject=dataset, user=user, title=faker.sentence(), discussion=[message]
692
685
  )
693
686
  open_discussions[discussion.id] = discussion
694
687
  # Creating a closed discussion that shouldn't show up in response.
@@ -700,7 +693,7 @@ class DiscussionsNotificationsTest(TestCase, DBTestMixin):
700
693
  title=faker.sentence(),
701
694
  discussion=[message],
702
695
  closed=datetime.utcnow(),
703
- closed_by=user
696
+ closed_by=user,
704
697
  )
705
698
 
706
699
  notifications = discussions_notifications(recipient)
@@ -708,10 +701,10 @@ class DiscussionsNotificationsTest(TestCase, DBTestMixin):
708
701
  self.assertEqual(len(notifications), len(open_discussions))
709
702
 
710
703
  for dt, details in notifications:
711
- discussion = open_discussions[details['id']]
712
- self.assertEqual(details['title'], discussion.title)
713
- self.assertEqual(details['subject']['id'], discussion.subject.id)
714
- self.assertEqual(details['subject']['type'], 'dataset')
704
+ discussion = open_discussions[details["id"]]
705
+ self.assertEqual(details["title"], discussion.title)
706
+ self.assertEqual(details["subject"]["id"], discussion.subject.id)
707
+ self.assertEqual(details["subject"]["type"], "dataset")
715
708
 
716
709
 
717
710
  class DiscussionsMailsTest(APITestCase):
@@ -725,7 +718,7 @@ class DiscussionsMailsTest(APITestCase):
725
718
  subject=DatasetFactory(owner=owner),
726
719
  user=user,
727
720
  title=faker.sentence(),
728
- discussion=[message]
721
+ discussion=[message],
729
722
  )
730
723
 
731
724
  with capture_mails() as mails:
@@ -746,7 +739,7 @@ class DiscussionsMailsTest(APITestCase):
746
739
  subject=DatasetFactory(owner=owner),
747
740
  user=poster,
748
741
  title=faker.sentence(),
749
- discussion=[message, second_message, new_message]
742
+ discussion=[message, second_message, new_message],
750
743
  )
751
744
 
752
745
  with capture_mails() as mails:
@@ -772,7 +765,7 @@ class DiscussionsMailsTest(APITestCase):
772
765
  subject=DatasetFactory(owner=owner),
773
766
  user=poster,
774
767
  title=faker.sentence(),
775
- discussion=[message, second_message, closing_message]
768
+ discussion=[message, second_message, closing_message],
776
769
  )
777
770
 
778
771
  with capture_mails() as mails: