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,179 +1,177 @@
1
1
  from datetime import datetime
2
2
 
3
- from flask import request, url_for, redirect, make_response
3
+ from flask import make_response, redirect, request, url_for
4
4
  from mongoengine.queryset.visitor import Q
5
5
 
6
- from udata.api import api, API, errors
6
+ from udata.api import API, api, errors
7
7
  from udata.api.parsers import ModelApiParser
8
8
  from udata.auth import admin_permission, current_user
9
9
  from udata.core.badges import api as badges_api
10
10
  from udata.core.badges.fields import badge_fields
11
- from udata.core.followers.api import FollowAPI
12
- from udata.utils import multi_to_dict
13
- from udata.rdf import (
14
- RDF_EXTENSIONS, negociate_content, graph_response
15
- )
16
-
17
- from .forms import (
18
- OrganizationForm, MembershipRequestForm, MembershipRefuseForm, MemberForm
19
- )
20
- from .models import Organization, MembershipRequest, Member
21
- from .constants import ORG_ROLES
22
- from .permissions import (
23
- EditOrganizationPermission, OrganizationPrivatePermission
24
- )
25
- from .rdf import build_org_catalog
26
- from .tasks import notify_membership_request, notify_membership_response
27
- from .api_fields import (
28
- org_fields,
29
- org_page_fields,
30
- org_role_fields,
31
- request_fields,
32
- member_fields,
33
- refuse_membership_fields,
34
- org_suggestion_fields
35
- )
36
-
37
11
  from udata.core.dataset.api import DatasetApiParser
38
12
  from udata.core.dataset.api_fields import dataset_page_fields
39
13
  from udata.core.dataset.models import Dataset
40
14
  from udata.core.discussions.api import discussion_fields
41
15
  from udata.core.discussions.models import Discussion
16
+ from udata.core.followers.api import FollowAPI
42
17
  from udata.core.reuse.api_fields import reuse_fields
43
18
  from udata.core.reuse.models import Reuse
44
19
  from udata.core.storages.api import (
45
- uploaded_image_fields, image_parser, parse_uploaded_image
20
+ image_parser,
21
+ parse_uploaded_image,
22
+ uploaded_image_fields,
46
23
  )
24
+ from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
25
+ from udata.utils import multi_to_dict
47
26
 
27
+ from .api_fields import (
28
+ member_fields,
29
+ org_fields,
30
+ org_page_fields,
31
+ org_role_fields,
32
+ org_suggestion_fields,
33
+ refuse_membership_fields,
34
+ request_fields,
35
+ )
36
+ from .constants import ORG_ROLES
37
+ from .forms import (
38
+ MemberForm,
39
+ MembershipRefuseForm,
40
+ MembershipRequestForm,
41
+ OrganizationForm,
42
+ )
43
+ from .models import Member, MembershipRequest, Organization
44
+ from .permissions import EditOrganizationPermission, OrganizationPrivatePermission
45
+ from .rdf import build_org_catalog
46
+ from .tasks import notify_membership_request, notify_membership_response
48
47
 
49
- DEFAULT_SORTING = '-created_at'
50
- SUGGEST_SORTING = '-metrics.followers'
48
+ DEFAULT_SORTING = "-created_at"
49
+ SUGGEST_SORTING = "-metrics.followers"
51
50
 
52
51
 
53
52
  class OrgApiParser(ModelApiParser):
54
53
  sorts = {
55
- 'name': 'name',
56
- 'reuses': 'metrics.reuses',
57
- 'datasets': 'metrics.datasets',
58
- 'followers': 'metrics.followers',
59
- 'views': 'metrics.views',
60
- 'created': 'created_at',
61
- 'last_modified': 'last_modified',
54
+ "name": "name",
55
+ "reuses": "metrics.reuses",
56
+ "datasets": "metrics.datasets",
57
+ "followers": "metrics.followers",
58
+ "views": "metrics.views",
59
+ "created": "created_at",
60
+ "last_modified": "last_modified",
62
61
  }
63
62
 
64
63
  @staticmethod
65
64
  def parse_filters(organizations, args):
66
- if args.get('q'):
65
+ if args.get("q"):
67
66
  # Following code splits the 'q' argument by spaces to surround
68
67
  # every word in it with quotes before rebuild it.
69
68
  # This allows the search_text method to tokenise with an AND
70
69
  # between tokens whereas an OR is used without it.
71
- phrase_query = ' '.join([f'"{elem}"' for elem in args['q'].split(' ')])
70
+ phrase_query = " ".join([f'"{elem}"' for elem in args["q"].split(" ")])
72
71
  organizations = organizations.search_text(phrase_query)
73
72
  return organizations
74
73
 
75
74
 
76
- ns = api.namespace('organizations', 'Organization related operations')
75
+ ns = api.namespace("organizations", "Organization related operations")
77
76
 
78
77
  organization_parser = OrgApiParser()
79
78
 
80
- common_doc = {
81
- 'params': {'org': 'The organization ID or slug'}
82
- }
79
+ common_doc = {"params": {"org": "The organization ID or slug"}}
83
80
 
84
81
 
85
- @ns.route('/', endpoint='organizations')
82
+ @ns.route("/", endpoint="organizations")
86
83
  class OrganizationListAPI(API):
87
- '''Organizations collection endpoint'''
88
- @api.doc('list_organizations')
84
+ """Organizations collection endpoint"""
85
+
86
+ @api.doc("list_organizations")
89
87
  @api.expect(organization_parser.parser)
90
88
  @api.marshal_with(org_page_fields)
91
89
  def get(self):
92
- '''List or search all organizations'''
90
+ """List or search all organizations"""
93
91
  args = organization_parser.parse()
94
92
  organizations = Organization.objects(deleted=None)
95
93
  organizations = organization_parser.parse_filters(organizations, args)
96
94
 
97
- sort = args['sort'] or ('$text_score' if args['q'] else None) or DEFAULT_SORTING
98
- return organizations.order_by(sort).paginate(args['page'], args['page_size'])
95
+ sort = args["sort"] or ("$text_score" if args["q"] else None) or DEFAULT_SORTING
96
+ return organizations.order_by(sort).paginate(args["page"], args["page_size"])
99
97
 
100
98
  @api.secure
101
- @api.doc('create_organization', responses={400: 'Validation error'})
99
+ @api.doc("create_organization", responses={400: "Validation error"})
102
100
  @api.expect(org_fields)
103
101
  @api.marshal_with(org_fields, code=201)
104
102
  def post(self):
105
- '''Create a new organization'''
103
+ """Create a new organization"""
106
104
  form = api.validate(OrganizationForm)
107
105
  organization = form.save()
108
106
  return organization, 201
109
107
 
110
108
 
111
- @ns.route('/<org:org>/', endpoint='organization', doc=common_doc)
112
- @api.response(404, 'Organization not found')
113
- @api.response(410, 'Organization has been deleted')
109
+ @ns.route("/<org:org>/", endpoint="organization", doc=common_doc)
110
+ @api.response(404, "Organization not found")
111
+ @api.response(410, "Organization has been deleted")
114
112
  class OrganizationAPI(API):
115
- @api.doc('get_organization')
113
+ @api.doc("get_organization")
116
114
  @api.marshal_with(org_fields)
117
115
  def get(self, org):
118
- '''Get a organization given its identifier'''
116
+ """Get a organization given its identifier"""
119
117
  if org.deleted and not OrganizationPrivatePermission(org).can():
120
- api.abort(410, 'Organization has been deleted')
118
+ api.abort(410, "Organization has been deleted")
121
119
  return org
122
120
 
123
121
  @api.secure
124
- @api.doc('update_organization')
122
+ @api.doc("update_organization")
125
123
  @api.expect(org_fields)
126
124
  @api.marshal_with(org_fields)
127
125
  @api.response(400, errors.VALIDATION_ERROR)
128
- @api.response(410, 'Organization has been deleted')
126
+ @api.response(410, "Organization has been deleted")
129
127
  def put(self, org):
130
- '''
128
+ """
131
129
  Update a organization given its identifier
132
130
 
133
131
  :raises PermissionDenied:
134
- '''
135
- request_deleted = request.json.get('deleted', True)
132
+ """
133
+ request_deleted = request.json.get("deleted", True)
136
134
  if org.deleted and request_deleted is not None:
137
- api.abort(410, 'Organization has been deleted')
135
+ api.abort(410, "Organization has been deleted")
138
136
  EditOrganizationPermission(org).test()
139
137
  form = api.validate(OrganizationForm, org)
140
138
  return form.save()
141
139
 
142
140
  @api.secure
143
- @api.doc('delete_organization')
144
- @api.response(204, 'Organization deleted')
141
+ @api.doc("delete_organization")
142
+ @api.response(204, "Organization deleted")
145
143
  def delete(self, org):
146
- '''Delete a organization given its identifier'''
144
+ """Delete a organization given its identifier"""
147
145
  if org.deleted:
148
- api.abort(410, 'Organization has been deleted')
146
+ api.abort(410, "Organization has been deleted")
149
147
  EditOrganizationPermission(org).test()
150
148
  org.deleted = datetime.utcnow()
151
149
  org.save()
152
- return '', 204
150
+ return "", 204
153
151
 
154
152
 
155
- @ns.route('/<org:org>/catalog', endpoint='organization_rdf', doc=common_doc)
156
- @api.response(404, 'Organization not found')
157
- @api.response(410, 'Organization has been deleted')
153
+ @ns.route("/<org:org>/catalog", endpoint="organization_rdf", doc=common_doc)
154
+ @api.response(404, "Organization not found")
155
+ @api.response(410, "Organization has been deleted")
158
156
  class OrganizationRdfAPI(API):
159
- @api.doc('rdf_organization')
157
+ @api.doc("rdf_organization")
160
158
  def get(self, org):
161
159
  format = RDF_EXTENSIONS[negociate_content()]
162
- url = url_for('api.organization_rdf_format', org=org.id, format=format)
160
+ url = url_for("api.organization_rdf_format", org=org.id, format=format)
163
161
  return redirect(url)
164
162
 
165
163
 
166
- @ns.route('/<org:org>/catalog.<format>', endpoint='organization_rdf_format', doc=common_doc)
167
- @api.response(404, 'Organization not found')
168
- @api.response(410, 'Organization has been deleted')
164
+ @ns.route("/<org:org>/catalog.<format>", endpoint="organization_rdf_format", doc=common_doc)
165
+ @api.response(404, "Organization not found")
166
+ @api.response(410, "Organization has been deleted")
169
167
  class OrganizationRdfFormatAPI(API):
170
- @api.doc('rdf_organization_format')
168
+ @api.doc("rdf_organization_format")
171
169
  def get(self, org, format):
172
170
  if org.deleted:
173
171
  api.abort(410)
174
172
  params = multi_to_dict(request.args)
175
- page = int(params.get('page', 1))
176
- page_size = int(params.get('page_size', 100))
173
+ page = int(params.get("page", 1))
174
+ page_size = int(params.get("page_size", 100))
177
175
  datasets = Dataset.objects(organization=org).visible().paginate(page, page_size)
178
176
  catalog = build_org_catalog(org, datasets, format=format)
179
177
  # bypass flask-restplus make_response, since graph_response
@@ -181,85 +179,79 @@ class OrganizationRdfFormatAPI(API):
181
179
  return make_response(*graph_response(catalog, format))
182
180
 
183
181
 
184
- @ns.route('/badges/', endpoint='available_organization_badges')
182
+ @ns.route("/badges/", endpoint="available_organization_badges")
185
183
  class AvailableOrganizationBadgesAPI(API):
186
- @api.doc('available_organization_badges')
184
+ @api.doc("available_organization_badges")
187
185
  def get(self):
188
- '''List all available organization badges and their labels'''
186
+ """List all available organization badges and their labels"""
189
187
  return Organization.__badges__
190
188
 
191
189
 
192
- @ns.route('/<org:org>/badges/', endpoint='organization_badges')
190
+ @ns.route("/<org:org>/badges/", endpoint="organization_badges")
193
191
  class OrganizationBadgesAPI(API):
194
- @api.doc('add_organization_badge', **common_doc)
192
+ @api.doc("add_organization_badge", **common_doc)
195
193
  @api.expect(badge_fields)
196
194
  @api.marshal_with(badge_fields)
197
195
  @api.secure(admin_permission)
198
196
  def post(self, org):
199
- '''Create a new badge for a given organization'''
197
+ """Create a new badge for a given organization"""
200
198
  return badges_api.add(org)
201
199
 
202
200
 
203
- @ns.route('/<org:org>/badges/<badge_kind>/', endpoint='organization_badge')
201
+ @ns.route("/<org:org>/badges/<badge_kind>/", endpoint="organization_badge")
204
202
  class OrganizationBadgeAPI(API):
205
- @api.doc('delete_organization_badge', **common_doc)
203
+ @api.doc("delete_organization_badge", **common_doc)
206
204
  @api.secure(admin_permission)
207
205
  def delete(self, org, badge_kind):
208
- '''Delete a badge for a given organization'''
206
+ """Delete a badge for a given organization"""
209
207
  return badges_api.remove(org, badge_kind)
210
208
 
211
209
 
212
- from udata.models import ContactPoint
213
210
  from udata.core.contact_point.api import ContactPointApiParser
214
211
  from udata.core.contact_point.api_fields import contact_point_page_fields
215
-
212
+ from udata.models import ContactPoint
216
213
 
217
214
  contact_point_parser = ContactPointApiParser()
218
215
 
219
216
 
220
- @ns.route('/<org:org>/contacts/', endpoint='org_contact_points')
217
+ @ns.route("/<org:org>/contacts/", endpoint="org_contact_points")
221
218
  class OrgContactAPI(API):
222
- @api.doc('get_organization_contact_point')
219
+ @api.doc("get_organization_contact_point")
223
220
  @api.marshal_with(contact_point_page_fields)
224
221
  def get(self, org):
225
- '''List all organization contact points'''
222
+ """List all organization contact points"""
226
223
  args = contact_point_parser.parse()
227
224
  contact_points = ContactPoint.objects.owned_by(org)
228
- return contact_points.paginate(args['page'], args['page_size'])
225
+ return contact_points.paginate(args["page"], args["page_size"])
229
226
 
230
227
 
231
228
  requests_parser = api.parser()
232
229
  requests_parser.add_argument(
233
- 'status',
234
- type=str,
235
- help='If provided, only return requests ith a given status',
236
- location='args'
230
+ "status", type=str, help="If provided, only return requests ith a given status", location="args"
237
231
  )
238
232
 
239
233
 
240
- @ns.route('/<org:org>/membership/', endpoint='request_membership',
241
- doc=common_doc)
234
+ @ns.route("/<org:org>/membership/", endpoint="request_membership", doc=common_doc)
242
235
  class MembershipRequestAPI(API):
243
236
  @api.secure
244
- @api.doc('list_membership_requests')
237
+ @api.doc("list_membership_requests")
245
238
  @api.expect(requests_parser)
246
- @api.response(403, 'Not Authorized')
239
+ @api.response(403, "Not Authorized")
247
240
  @api.marshal_list_with(request_fields)
248
241
  def get(self, org):
249
- '''List membership requests for a given organization'''
242
+ """List membership requests for a given organization"""
250
243
  OrganizationPrivatePermission(org).test()
251
244
  args = requests_parser.parse_args()
252
- if args['status']:
253
- return [r for r in org.requests if r.status == args['status']]
245
+ if args["status"]:
246
+ return [r for r in org.requests if r.status == args["status"]]
254
247
  else:
255
248
  return org.requests
256
249
 
257
250
  @api.secure
258
251
  @api.marshal_with(request_fields)
259
252
  def post(self, org):
260
- '''Apply for membership to a given organization.'''
261
- membership_request = org.pending_request(
262
- current_user._get_current_object())
253
+ """Apply for membership to a given organization."""
254
+ membership_request = org.pending_request(current_user._get_current_object())
263
255
  code = 200 if membership_request else 201
264
256
 
265
257
  form = api.validate(MembershipRequestForm, membership_request)
@@ -281,27 +273,26 @@ class MembershipAPI(API):
281
273
  for membership_request in org.requests:
282
274
  if membership_request.id == id:
283
275
  return membership_request
284
- api.abort(404, 'Unknown membership request id')
276
+ api.abort(404, "Unknown membership request id")
285
277
 
286
278
 
287
- @ns.route('/<org:org>/membership/<uuid:id>/accept/',
288
- endpoint='accept_membership')
279
+ @ns.route("/<org:org>/membership/<uuid:id>/accept/", endpoint="accept_membership")
289
280
  class MembershipAcceptAPI(MembershipAPI):
290
281
  @api.secure
291
- @api.doc('accept_membership', **common_doc)
282
+ @api.doc("accept_membership", **common_doc)
292
283
  @api.marshal_with(member_fields)
293
284
  def post(self, org, id):
294
- '''Accept user membership to a given organization.'''
285
+ """Accept user membership to a given organization."""
295
286
  EditOrganizationPermission(org).test()
296
287
  membership_request = self.get_or_404(org, id)
297
288
 
298
289
  if org.is_member(membership_request.user):
299
290
  return org.member(membership_request.user), 409
300
291
 
301
- membership_request.status = 'accepted'
292
+ membership_request.status = "accepted"
302
293
  membership_request.handled_by = current_user._get_current_object()
303
294
  membership_request.handled_on = datetime.utcnow()
304
- member = Member(user=membership_request.user, role='editor')
295
+ member = Member(user=membership_request.user, role="editor")
305
296
 
306
297
  org.members.append(member)
307
298
  org.count_members()
@@ -312,18 +303,17 @@ class MembershipAcceptAPI(MembershipAPI):
312
303
  return member
313
304
 
314
305
 
315
- @ns.route('/<org:org>/membership/<uuid:id>/refuse/',
316
- endpoint='refuse_membership')
306
+ @ns.route("/<org:org>/membership/<uuid:id>/refuse/", endpoint="refuse_membership")
317
307
  class MembershipRefuseAPI(MembershipAPI):
318
308
  @api.secure
319
309
  @api.expect(refuse_membership_fields)
320
- @api.doc('refuse_membership', **common_doc)
310
+ @api.doc("refuse_membership", **common_doc)
321
311
  def post(self, org, id):
322
- '''Refuse user membership to a given organization.'''
312
+ """Refuse user membership to a given organization."""
323
313
  EditOrganizationPermission(org).test()
324
314
  membership_request = self.get_or_404(org, id)
325
315
  form = api.validate(MembershipRefuseForm)
326
- membership_request.status = 'refused'
316
+ membership_request.status = "refused"
327
317
  membership_request.handled_by = current_user._get_current_object()
328
318
  membership_request.handled_on = datetime.utcnow()
329
319
  membership_request.refusal_comment = form.comment.data
@@ -335,16 +325,16 @@ class MembershipRefuseAPI(MembershipAPI):
335
325
  return {}, 200
336
326
 
337
327
 
338
- @ns.route('/<org:org>/member/<user:user>', endpoint='member', doc=common_doc)
328
+ @ns.route("/<org:org>/member/<user:user>", endpoint="member", doc=common_doc)
339
329
  class MemberAPI(API):
340
330
  @api.secure
341
331
  @api.expect(member_fields)
342
332
  @api.marshal_with(member_fields, code=201)
343
- @api.doc('create_organization_member')
344
- @api.response(403, 'Not Authorized')
345
- @api.response(409, 'User is already member', member_fields)
333
+ @api.doc("create_organization_member")
334
+ @api.response(403, "Not Authorized")
335
+ @api.response(409, "User is already member", member_fields)
346
336
  def post(self, org, user):
347
- '''Add a member into a given organization.'''
337
+ """Add a member into a given organization."""
348
338
  EditOrganizationPermission(org).test()
349
339
  if org.is_member(user):
350
340
  return org.member(user), 409
@@ -360,9 +350,9 @@ class MemberAPI(API):
360
350
  @api.secure
361
351
  @api.expect(member_fields)
362
352
  @api.marshal_with(member_fields)
363
- @api.doc('update_organization_member', responses={403: 'Not Authorized'})
353
+ @api.doc("update_organization_member", responses={403: "Not Authorized"})
364
354
  def put(self, org, user):
365
- '''Update member status into a given organization.'''
355
+ """Update member status into a given organization."""
366
356
  EditOrganizationPermission(org).test()
367
357
  member = org.member(user)
368
358
  form = api.validate(MemberForm, member)
@@ -372,130 +362,133 @@ class MemberAPI(API):
372
362
  return member
373
363
 
374
364
  @api.secure
375
- @api.doc('delete_organization_member', responses={403: 'Not Authorized'})
365
+ @api.doc("delete_organization_member", responses={403: "Not Authorized"})
376
366
  def delete(self, org, user):
377
- '''Delete member from an organization'''
367
+ """Delete member from an organization"""
378
368
  EditOrganizationPermission(org).test()
379
369
  member = org.member(user)
380
370
  if member:
381
371
  Organization.objects(id=org.id).update_one(pull__members=member)
382
372
  org.reload()
383
373
  org.count_members()
384
- return '', 204
374
+ return "", 204
385
375
  else:
386
376
  api.abort(404)
387
377
 
388
378
 
389
- @ns.route('/<id>/followers/', endpoint='organization_followers')
390
- @ns.doc(get={'id': 'list_organization_followers'},
391
- post={'id': 'follow_organization'},
392
- delete={'id': 'unfollow_organization'})
379
+ @ns.route("/<id>/followers/", endpoint="organization_followers")
380
+ @ns.doc(
381
+ get={"id": "list_organization_followers"},
382
+ post={"id": "follow_organization"},
383
+ delete={"id": "unfollow_organization"},
384
+ )
393
385
  class FollowOrgAPI(FollowAPI):
394
386
  model = Organization
395
387
 
396
388
 
397
389
  suggest_parser = api.parser()
398
390
  suggest_parser.add_argument(
399
- 'q', help='The string to autocomplete/suggest', location='args',
400
- required=True)
391
+ "q", help="The string to autocomplete/suggest", location="args", required=True
392
+ )
401
393
  suggest_parser.add_argument(
402
- 'size', type=int, help='The amount of suggestion to fetch',
403
- location='args', default=10)
394
+ "size", type=int, help="The amount of suggestion to fetch", location="args", default=10
395
+ )
404
396
 
405
397
 
406
- @ns.route('/suggest/', endpoint='suggest_organizations')
398
+ @ns.route("/suggest/", endpoint="suggest_organizations")
407
399
  class OrganizationSuggestAPI(API):
408
- @api.doc('suggest_organizations')
400
+ @api.doc("suggest_organizations")
409
401
  @api.expect(suggest_parser)
410
402
  @api.marshal_list_with(org_suggestion_fields)
411
403
  def get(self):
412
- '''Organizations suggest endpoint using mongoDB contains'''
404
+ """Organizations suggest endpoint using mongoDB contains"""
413
405
  args = suggest_parser.parse_args()
414
- orgs = Organization.objects(Q(name__icontains=args['q']) | Q(acronym__icontains=args['q']), deleted=None)
406
+ orgs = Organization.objects(
407
+ Q(name__icontains=args["q"]) | Q(acronym__icontains=args["q"]), deleted=None
408
+ )
415
409
  return [
416
410
  {
417
- 'id': org.id,
418
- 'name': org.name,
419
- 'acronym': org.acronym,
420
- 'slug': org.slug,
421
- 'image_url': org.logo,
411
+ "id": org.id,
412
+ "name": org.name,
413
+ "acronym": org.acronym,
414
+ "slug": org.slug,
415
+ "image_url": org.logo,
422
416
  }
423
- for org in orgs.order_by(SUGGEST_SORTING).limit(args['size'])
417
+ for org in orgs.order_by(SUGGEST_SORTING).limit(args["size"])
424
418
  ]
425
419
 
426
420
 
427
- @ns.route('/<org:org>/logo', endpoint='organization_logo')
421
+ @ns.route("/<org:org>/logo", endpoint="organization_logo")
428
422
  @api.doc(**common_doc)
429
423
  class AvatarAPI(API):
430
424
  @api.secure
431
- @api.doc('organization_logo')
425
+ @api.doc("organization_logo")
432
426
  @api.expect(image_parser) # Swagger 2.0 does not support formData at path level
433
427
  @api.marshal_with(uploaded_image_fields)
434
428
  def post(self, org):
435
- '''Upload a new logo'''
429
+ """Upload a new logo"""
436
430
  EditOrganizationPermission(org).test()
437
431
  parse_uploaded_image(org.logo)
438
432
  org.save()
439
- return {'image': org.logo}
433
+ return {"image": org.logo}
440
434
 
441
435
  @api.secure
442
- @api.doc('resize_organization_logo')
436
+ @api.doc("resize_organization_logo")
443
437
  @api.expect(image_parser) # Swagger 2.0 does not support formData at path level
444
438
  @api.marshal_with(uploaded_image_fields)
445
439
  def put(self, org):
446
- '''Set the logo BBox'''
440
+ """Set the logo BBox"""
447
441
  EditOrganizationPermission(org).test()
448
442
  parse_uploaded_image(org.logo)
449
- return {'image': org.logo}
443
+ return {"image": org.logo}
450
444
 
451
445
 
452
446
  dataset_parser = DatasetApiParser()
453
447
 
454
448
 
455
- @ns.route('/<org:org>/datasets/', endpoint='org_datasets')
449
+ @ns.route("/<org:org>/datasets/", endpoint="org_datasets")
456
450
  class OrgDatasetsAPI(API):
457
- @api.doc('list_organization_datasets')
451
+ @api.doc("list_organization_datasets")
458
452
  @api.expect(dataset_parser.parser)
459
453
  @api.marshal_with(dataset_page_fields)
460
454
  def get(self, org):
461
- '''List organization datasets (including private ones when member)'''
455
+ """List organization datasets (including private ones when member)"""
462
456
  args = dataset_parser.parse()
463
457
  qs = Dataset.objects.owned_by(org)
464
458
  if not OrganizationPrivatePermission(org).can():
465
459
  qs = qs(private__ne=True)
466
- return (qs.order_by(args['sort'])
467
- .paginate(args['page'], args['page_size']))
460
+ return qs.order_by(args["sort"]).paginate(args["page"], args["page_size"])
468
461
 
469
462
 
470
- @ns.route('/<org:org>/reuses/', endpoint='org_reuses')
463
+ @ns.route("/<org:org>/reuses/", endpoint="org_reuses")
471
464
  class OrgReusesAPI(API):
472
- @api.doc('list_organization_reuses')
465
+ @api.doc("list_organization_reuses")
473
466
  @api.marshal_list_with(reuse_fields)
474
467
  def get(self, org):
475
- '''List organization reuses (including private ones when member)'''
468
+ """List organization reuses (including private ones when member)"""
476
469
  qs = Reuse.objects.owned_by(org)
477
470
  if not OrganizationPrivatePermission(org).can():
478
471
  qs = qs(private__ne=True)
479
472
  return list(qs)
480
473
 
481
474
 
482
- @ns.route('/<org:org>/discussions/', endpoint='org_discussions')
475
+ @ns.route("/<org:org>/discussions/", endpoint="org_discussions")
483
476
  class OrgDiscussionsAPI(API):
484
- @api.doc('list_organization_discussions')
477
+ @api.doc("list_organization_discussions")
485
478
  @api.marshal_list_with(discussion_fields)
486
479
  def get(self, org):
487
- '''List organization discussions'''
488
- reuses = Reuse.objects(organization=org).only('id')
489
- datasets = Dataset.objects(organization=org).only('id')
480
+ """List organization discussions"""
481
+ reuses = Reuse.objects(organization=org).only("id")
482
+ datasets = Dataset.objects(organization=org).only("id")
490
483
  subjects = list(reuses) + list(datasets)
491
- qs = Discussion.objects(subject__in=subjects).order_by('-created')
484
+ qs = Discussion.objects(subject__in=subjects).order_by("-created")
492
485
  return list(qs)
493
486
 
494
487
 
495
- @ns.route('/roles/', endpoint='org_roles')
488
+ @ns.route("/roles/", endpoint="org_roles")
496
489
  class OrgRolesAPI(API):
497
- @api.doc('org_roles')
490
+ @api.doc("org_roles")
498
491
  @api.marshal_list_with(org_role_fields)
499
492
  def get(self):
500
- '''List all possible organization roles'''
501
- return [{'id': key, 'label': value} for (key, value) in ORG_ROLES.items()]
493
+ """List all possible organization roles"""
494
+ return [{"id": key, "label": value} for (key, value) in ORG_ROLES.items()]