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
@@ -2,16 +2,16 @@ from blinker import Namespace
2
2
 
3
3
  namespace = Namespace()
4
4
 
5
- organization_created = namespace.signal('organization-created')
5
+ organization_created = namespace.signal("organization-created")
6
6
 
7
- organization_updated = namespace.signal('organization-updated')
7
+ organization_updated = namespace.signal("organization-updated")
8
8
 
9
- organization_deleted = namespace.signal('organization-deleted')
9
+ organization_deleted = namespace.signal("organization-deleted")
10
10
 
11
- new_member = namespace.signal('new-member')
11
+ new_member = namespace.signal("new-member")
12
12
 
13
- new_membership_request = namespace.signal('new-membership-request')
13
+ new_membership_request = namespace.signal("new-membership-request")
14
14
 
15
- membership_accepted = namespace.signal('membership-accepted')
15
+ membership_accepted = namespace.signal("membership-accepted")
16
16
 
17
- membership_refused = namespace.signal('membership-refused')
17
+ membership_refused = namespace.signal("membership-refused")
@@ -1,22 +1,21 @@
1
1
  from udata import mail
2
- from udata.i18n import lazy_gettext as _
3
2
  from udata.core import storages
4
- from udata.models import Follow, Activity, Dataset, Transfer, ContactPoint
5
- from udata.search import reindex
6
- from udata.tasks import job, task, get_logger
7
-
8
3
  from udata.core.badges.tasks import notify_new_badge
4
+ from udata.i18n import lazy_gettext as _
5
+ from udata.models import Activity, ContactPoint, Dataset, Follow, Transfer
6
+ from udata.search import reindex
7
+ from udata.tasks import get_logger, job, task
9
8
 
9
+ from .constants import ASSOCIATION, CERTIFIED, COMPANY, LOCAL_AUTHORITY, PUBLIC_SERVICE
10
10
  from .models import Organization
11
- from .constants import CERTIFIED, PUBLIC_SERVICE, COMPANY, ASSOCIATION, LOCAL_AUTHORITY
12
11
 
13
12
  log = get_logger(__name__)
14
13
 
15
14
 
16
- @job('purge-organizations')
15
+ @job("purge-organizations")
17
16
  def purge_organizations(self):
18
17
  for organization in Organization.objects(deleted__ne=None):
19
- log.info(f'Purging organization {organization}')
18
+ log.info(f"Purging organization {organization}")
20
19
  # Remove followers
21
20
  Follow.objects(following=organization).delete()
22
21
  # Remove activity
@@ -43,28 +42,27 @@ def purge_organizations(self):
43
42
  reindex(Dataset.__name__, str(id))
44
43
 
45
44
 
46
- @task(route='high.mail')
45
+ @task(route="high.mail")
47
46
  def notify_membership_request(org_id, request_id):
48
47
  org = Organization.objects.get(pk=org_id)
49
48
  request = next((r for r in org.requests if str(r.id) == request_id), None)
50
49
 
51
- recipients = [m.user for m in org.by_role('admin')]
50
+ recipients = [m.user for m in org.by_role("admin")]
52
51
  mail.send(
53
- _('New membership request'), recipients, 'membership_request',
54
- org=org, request=request)
52
+ _("New membership request"), recipients, "membership_request", org=org, request=request
53
+ )
55
54
 
56
55
 
57
- @task(route='high.mail')
56
+ @task(route="high.mail")
58
57
  def notify_membership_response(org_id, request_id):
59
58
  org = Organization.objects.get(pk=org_id)
60
59
  request = next((r for r in org.requests if str(r.id) == request_id), None)
61
60
 
62
- if request.status == 'accepted':
63
- subject = _('You are now a member of the organization "%(org)s"',
64
- org=org)
65
- template = 'new_member'
61
+ if request.status == "accepted":
62
+ subject = _('You are now a member of the organization "%(org)s"', org=org)
63
+ template = "new_member"
66
64
  else:
67
- subject, template = _('Membership refused'), 'membership_refused'
65
+ subject, template = _("Membership refused"), "membership_refused"
68
66
  mail.send(subject, request.user, template, org=org, request=request)
69
67
 
70
68
 
@@ -74,104 +72,87 @@ def notify_new_member(org_id, email):
74
72
  member = next((m for m in org.members if m.user.email == email), None)
75
73
 
76
74
  subject = _('You are now a member of the organization "%(org)s"', org=org)
77
- mail.send(subject, member.user, 'new_member', org=org)
75
+ mail.send(subject, member.user, "new_member", org=org)
78
76
 
79
77
 
80
78
  @notify_new_badge(Organization, CERTIFIED)
81
79
  def notify_badge_certified(org_id):
82
- '''
80
+ """
83
81
  Send an email when a `CERTIFIED` badge is added to an `Organization`
84
- '''
82
+ """
85
83
  org = Organization.objects.get(pk=org_id)
86
84
  recipients = [member.user for member in org.members]
87
- subject = _(
88
- 'Your organization "%(name)s" has been certified',
89
- name=org.name
90
- )
85
+ subject = _('Your organization "%(name)s" has been certified', name=org.name)
91
86
  mail.send(
92
87
  subject,
93
88
  recipients,
94
- 'badge_added_certified',
89
+ "badge_added_certified",
95
90
  organization=org,
96
- badge=org.get_badge(CERTIFIED)
91
+ badge=org.get_badge(CERTIFIED),
97
92
  )
98
93
 
99
94
 
100
95
  @notify_new_badge(Organization, PUBLIC_SERVICE)
101
96
  def notify_badge_public_service(org_id):
102
- '''
97
+ """
103
98
  Send an email when a `PUBLIC_SERVICE` badge is added to an `Organization`
104
- '''
99
+ """
105
100
  org = Organization.objects.get(pk=org_id)
106
101
  recipients = [member.user for member in org.members]
107
- subject = _(
108
- 'Your organization "%(name)s" has been identified as public service',
109
- name=org.name
110
- )
102
+ subject = _('Your organization "%(name)s" has been identified as public service', name=org.name)
111
103
  mail.send(
112
104
  subject,
113
105
  recipients,
114
- 'badge_added_public_service',
106
+ "badge_added_public_service",
115
107
  organization=org,
116
- badge=org.get_badge(PUBLIC_SERVICE)
108
+ badge=org.get_badge(PUBLIC_SERVICE),
117
109
  )
118
110
 
119
111
 
120
112
  @notify_new_badge(Organization, COMPANY)
121
113
  def notify_badge_company(org_id):
122
- '''
114
+ """
123
115
  Send an email when a `COMPANY` badge is added to an `Organization`
124
- '''
116
+ """
125
117
  org = Organization.objects.get(pk=org_id)
126
118
  recipients = [member.user for member in org.members]
127
- subject = _(
128
- 'Your organization "%(name)s" has been identified as a company',
129
- name=org.name
130
- )
119
+ subject = _('Your organization "%(name)s" has been identified as a company', name=org.name)
131
120
  mail.send(
132
- subject,
133
- recipients,
134
- 'badge_added_company',
135
- organization=org,
136
- badge=org.get_badge(COMPANY)
121
+ subject, recipients, "badge_added_company", organization=org, badge=org.get_badge(COMPANY)
137
122
  )
138
123
 
139
124
 
140
125
  @notify_new_badge(Organization, ASSOCIATION)
141
126
  def notify_badge_association(org_id):
142
- '''
127
+ """
143
128
  Send an email when a `ASSOCIATION` badge is added to an `Organization`
144
- '''
129
+ """
145
130
  org = Organization.objects.get(pk=org_id)
146
131
  recipients = [member.user for member in org.members]
147
- subject = _(
148
- 'Your organization "%(name)s" has been identified as an association',
149
- name=org.name
150
- )
132
+ subject = _('Your organization "%(name)s" has been identified as an association', name=org.name)
151
133
  mail.send(
152
134
  subject,
153
135
  recipients,
154
- 'badge_added_association',
136
+ "badge_added_association",
155
137
  organization=org,
156
- badge=org.get_badge(ASSOCIATION)
138
+ badge=org.get_badge(ASSOCIATION),
157
139
  )
158
140
 
159
141
 
160
142
  @notify_new_badge(Organization, LOCAL_AUTHORITY)
161
143
  def notify_badge_local_authority(org_id):
162
- '''
144
+ """
163
145
  Send an email when a `LOCAL_AUTHORITY` badge is added to an `Organization`
164
- '''
146
+ """
165
147
  org = Organization.objects.get(pk=org_id)
166
148
  recipients = [member.user for member in org.members]
167
149
  subject = _(
168
- 'Your organization "%(name)s" has been identified as a local authority',
169
- name=org.name
150
+ 'Your organization "%(name)s" has been identified as a local authority', name=org.name
170
151
  )
171
152
  mail.send(
172
153
  subject,
173
154
  recipients,
174
- 'badge_added_local_authority',
155
+ "badge_added_local_authority",
175
156
  organization=org,
176
- badge=org.get_badge(LOCAL_AUTHORITY)
177
- )
157
+ badge=org.get_badge(LOCAL_AUTHORITY),
158
+ )
udata/core/owned.py CHANGED
@@ -5,14 +5,14 @@ from mongoengine import NULLIFY, Q, post_save
5
5
  from mongoengine.fields import ReferenceField
6
6
 
7
7
  from udata.api_fields import field
8
- from udata.core.organization.models import Organization
9
- from udata.core.user.models import User
10
- from udata.mongo.queryset import UDataQuerySet
11
- from udata.core.user.api_fields import user_ref_fields
12
8
  from udata.core.organization.api_fields import org_ref_fields
9
+ from udata.core.organization.models import Organization
13
10
  from udata.core.organization.permissions import OrganizationPrivatePermission
14
- from udata.mongo.errors import FieldValidationError
11
+ from udata.core.user.api_fields import user_ref_fields
12
+ from udata.core.user.models import User
15
13
  from udata.i18n import lazy_gettext as _
14
+ from udata.mongo.errors import FieldValidationError
15
+ from udata.mongo.queryset import UDataQuerySet
16
16
 
17
17
  log = logging.getLogger(__name__)
18
18
 
@@ -23,11 +23,19 @@ class OwnedQuerySet(UDataQuerySet):
23
23
  for owner in owners:
24
24
  qs |= Q(owner=owner) | Q(organization=owner)
25
25
  return self(qs)
26
-
26
+
27
+
27
28
  def check_owner_is_current_user(owner):
28
- from udata.auth import current_user, admin_permission
29
- if current_user.is_authenticated and owner and not admin_permission and current_user.id != owner:
30
- raise FieldValidationError(_('You can only set yourself as owner'), field="owner")
29
+ from udata.auth import admin_permission, current_user
30
+
31
+ if (
32
+ current_user.is_authenticated
33
+ and owner
34
+ and not admin_permission
35
+ and current_user.id != owner
36
+ ):
37
+ raise FieldValidationError(_("You can only set yourself as owner"), field="owner")
38
+
31
39
 
32
40
  def check_organization_is_valid_for_current_user(organization):
33
41
  from udata.auth import current_user
@@ -38,13 +46,16 @@ def check_organization_is_valid_for_current_user(organization):
38
46
  raise FieldValidationError(_("Unknown organization"), field="organization")
39
47
 
40
48
  if current_user.is_authenticated and org and not OrganizationPrivatePermission(org).can():
41
- raise FieldValidationError(_("Permission denied for this organization"), field="organization")
49
+ raise FieldValidationError(
50
+ _("Permission denied for this organization"), field="organization"
51
+ )
42
52
 
43
53
 
44
54
  class Owned(object):
45
- '''
55
+ """
46
56
  A mixin to factorize owning behvaior between users and organizations.
47
- '''
57
+ """
58
+
48
59
  owner = field(
49
60
  ReferenceField(User, reverse_delete_rule=NULLIFY),
50
61
  nested_fields=user_ref_fields,
@@ -60,26 +71,26 @@ class Owned(object):
60
71
  allow_null=True,
61
72
  )
62
73
 
63
- on_owner_change = signal('Owned.on_owner_change')
74
+ on_owner_change = signal("Owned.on_owner_change")
64
75
 
65
76
  meta = {
66
- 'indexes': [
67
- 'owner',
68
- 'organization',
77
+ "indexes": [
78
+ "owner",
79
+ "organization",
69
80
  ],
70
- 'queryset_class': OwnedQuerySet,
81
+ "queryset_class": OwnedQuerySet,
71
82
  }
72
83
 
73
84
  def clean(self):
74
- '''
85
+ """
75
86
  Verify owner consistency and fetch original owner before the new one erase it.
76
- '''
87
+ """
77
88
 
78
89
  changed_fields = self._get_changed_fields()
79
- if 'organization' in changed_fields and 'owner' in changed_fields:
90
+ if "organization" in changed_fields and "owner" in changed_fields:
80
91
  # Ownership changes (org to owner or the other way around) have already been made
81
92
  return
82
- if 'organization' in changed_fields:
93
+ if "organization" in changed_fields:
83
94
  if self.owner:
84
95
  # Change from owner to organization
85
96
  self._previous_owner = self.owner
@@ -87,9 +98,9 @@ class Owned(object):
87
98
  else:
88
99
  # Change from org to another
89
100
  # Need to fetch previous value in base
90
- original = self.__class__.objects.only('organization').get(pk=self.pk)
101
+ original = self.__class__.objects.only("organization").get(pk=self.pk)
91
102
  self._previous_owner = original.organization
92
- elif 'owner' in changed_fields:
103
+ elif "owner" in changed_fields:
93
104
  if self.organization:
94
105
  # Change from organization to owner
95
106
  self._previous_owner = self.organization
@@ -97,20 +108,20 @@ class Owned(object):
97
108
  else:
98
109
  # Change from owner to another
99
110
  # Need to fetch previous value in base
100
- original = self.__class__.objects.only('owner').get(pk=self.pk)
111
+ original = self.__class__.objects.only("owner").get(pk=self.pk)
101
112
  self._previous_owner = original.owner
102
113
 
103
114
 
104
115
  def owned_post_save(sender, document, **kwargs):
105
- '''
116
+ """
106
117
  Owned mongoengine.post_save signal handler
107
118
  Dispatch the `Owned.on_owner_change` signal
108
119
  once the document has been saved including the previous owner.
109
120
 
110
121
  The signal handler should have the following signature:
111
122
  ``def handler(document, previous)``
112
- '''
113
- if isinstance(document, Owned) and hasattr(document, '_previous_owner'):
123
+ """
124
+ if isinstance(document, Owned) and hasattr(document, "_previous_owner"):
114
125
  Owned.on_owner_change.send(document, previous=document._previous_owner)
115
126
 
116
127
 
udata/core/post/api.py CHANGED
@@ -1,156 +1,157 @@
1
1
  from datetime import datetime
2
2
 
3
- from udata.api import api, fields, API
3
+ from udata.api import API, api, fields
4
4
  from udata.auth import admin_permission
5
-
6
5
  from udata.core.dataset.api_fields import dataset_fields
7
6
  from udata.core.reuse.api_fields import reuse_fields
8
- from udata.core.user.api_fields import user_ref_fields
9
7
  from udata.core.storages.api import (
10
- uploaded_image_fields, image_parser, parse_uploaded_image
8
+ image_parser,
9
+ parse_uploaded_image,
10
+ uploaded_image_fields,
11
11
  )
12
+ from udata.core.user.api_fields import user_ref_fields
12
13
 
13
- from .models import Post
14
14
  from .forms import PostForm
15
+ from .models import Post
15
16
 
16
- ns = api.namespace('posts', 'Posts related operations')
17
-
18
- post_fields = api.model('Post', {
19
- 'id': fields.String(description='The post identifier'),
20
- 'name': fields.String(description='The post name', required=True),
21
- 'slug': fields.String(
22
- description='The post permalink string', readonly=True),
23
- 'headline': fields.String(description='The post headline', required=True),
24
- 'content': fields.Markdown(
25
- description='The post content in Markdown', required=True),
26
-
27
- 'image': fields.ImageField(description='The post image', readonly=True),
28
- 'credit_to': fields.String(
29
- description='An optional credit line (associated to the image)'),
30
- 'credit_url': fields.String(
31
- description='An optional link associated to the credits'),
32
-
33
- 'tags': fields.List(
34
- fields.String, description='Some keywords to help in search'),
35
- 'datasets': fields.List(
36
- fields.Nested(dataset_fields), description='The post datasets'),
37
- 'reuses': fields.List(
38
- fields.Nested(reuse_fields), description='The post reuses'),
39
-
40
- 'owner': fields.Nested(
41
- user_ref_fields, description='The owner user',
42
- readonly=True, allow_null=True),
43
- 'created_at': fields.ISODateTime(
44
- description='The post creation date', readonly=True),
45
- 'last_modified': fields.ISODateTime(
46
- description='The post last modification date', readonly=True),
47
- 'published': fields.ISODateTime(
48
- description='The post publication date', readonly=True),
49
- 'body_type': fields.String(description='HTML or markdown body type', default='markdown'),
50
- 'uri': fields.UrlFor(
51
- 'api.post', lambda o: {'post': o},
52
- description='The post API URI', readonly=True),
53
- 'page': fields.UrlFor(
54
- 'posts.show', lambda o: {'post': o},
55
- description='The post page URL', readonly=True, fallback_endpoint='api.post'),
56
- }, mask='*,datasets{id,title,acronym,uri,page},reuses{id,title,image,image_thumbnail,uri,page}')
57
-
58
- post_page_fields = api.model('PostPage', fields.pager(post_fields))
17
+ ns = api.namespace("posts", "Posts related operations")
18
+
19
+ post_fields = api.model(
20
+ "Post",
21
+ {
22
+ "id": fields.String(description="The post identifier"),
23
+ "name": fields.String(description="The post name", required=True),
24
+ "slug": fields.String(description="The post permalink string", readonly=True),
25
+ "headline": fields.String(description="The post headline", required=True),
26
+ "content": fields.Markdown(description="The post content in Markdown", required=True),
27
+ "image": fields.ImageField(description="The post image", readonly=True),
28
+ "credit_to": fields.String(description="An optional credit line (associated to the image)"),
29
+ "credit_url": fields.String(description="An optional link associated to the credits"),
30
+ "tags": fields.List(fields.String, description="Some keywords to help in search"),
31
+ "datasets": fields.List(fields.Nested(dataset_fields), description="The post datasets"),
32
+ "reuses": fields.List(fields.Nested(reuse_fields), description="The post reuses"),
33
+ "owner": fields.Nested(
34
+ user_ref_fields, description="The owner user", readonly=True, allow_null=True
35
+ ),
36
+ "created_at": fields.ISODateTime(description="The post creation date", readonly=True),
37
+ "last_modified": fields.ISODateTime(
38
+ description="The post last modification date", readonly=True
39
+ ),
40
+ "published": fields.ISODateTime(description="The post publication date", readonly=True),
41
+ "body_type": fields.String(description="HTML or markdown body type", default="markdown"),
42
+ "uri": fields.UrlFor(
43
+ "api.post", lambda o: {"post": o}, description="The post API URI", readonly=True
44
+ ),
45
+ "page": fields.UrlFor(
46
+ "posts.show",
47
+ lambda o: {"post": o},
48
+ description="The post page URL",
49
+ readonly=True,
50
+ fallback_endpoint="api.post",
51
+ ),
52
+ },
53
+ mask="*,datasets{id,title,acronym,uri,page},reuses{id,title,image,image_thumbnail,uri,page}",
54
+ )
55
+
56
+ post_page_fields = api.model("PostPage", fields.pager(post_fields))
59
57
 
60
58
  parser = api.page_parser()
61
59
 
62
- parser.add_argument('sort', type=str, default='-created_at', location='args',
63
- help='The sorting attribute')
60
+ parser.add_argument(
61
+ "sort", type=str, default="-created_at", location="args", help="The sorting attribute"
62
+ )
64
63
 
65
64
 
66
- @ns.route('/', endpoint='posts')
65
+ @ns.route("/", endpoint="posts")
67
66
  class PostsAPI(API):
68
-
69
- @api.doc('list_posts')
67
+ @api.doc("list_posts")
70
68
  @api.expect(parser)
71
69
  @api.marshal_with(post_page_fields)
72
70
  def get(self):
73
- '''List all posts'''
71
+ """List all posts"""
74
72
  args = parser.parse_args()
75
- return (Post.objects.published().order_by(args['sort'])
76
- .paginate(args['page'], args['page_size']))
73
+ return (
74
+ Post.objects.published()
75
+ .order_by(args["sort"])
76
+ .paginate(args["page"], args["page_size"])
77
+ )
77
78
 
78
- @api.doc('create_post')
79
+ @api.doc("create_post")
79
80
  @api.secure(admin_permission)
80
81
  @api.expect(post_fields)
81
82
  @api.marshal_with(post_fields)
82
- @api.response(400, 'Validation error')
83
+ @api.response(400, "Validation error")
83
84
  def post(self):
84
- '''Create a post'''
85
+ """Create a post"""
85
86
  form = api.validate(PostForm)
86
87
  return form.save(), 201
87
88
 
88
89
 
89
- @ns.route('/<post:post>/', endpoint='post')
90
- @api.response(404, 'Object not found')
91
- @api.param('post', 'The post ID or slug')
90
+ @ns.route("/<post:post>/", endpoint="post")
91
+ @api.response(404, "Object not found")
92
+ @api.param("post", "The post ID or slug")
92
93
  class PostAPI(API):
93
- @api.doc('get_post')
94
+ @api.doc("get_post")
94
95
  @api.marshal_with(post_fields)
95
96
  def get(self, post):
96
- '''Get a given post'''
97
+ """Get a given post"""
97
98
  return post
98
99
 
99
- @api.doc('update_post')
100
+ @api.doc("update_post")
100
101
  @api.secure(admin_permission)
101
102
  @api.expect(post_fields)
102
103
  @api.marshal_with(post_fields)
103
- @api.response(400, 'Validation error')
104
+ @api.response(400, "Validation error")
104
105
  def put(self, post):
105
- '''Update a given post'''
106
+ """Update a given post"""
106
107
  form = api.validate(PostForm, post)
107
108
  return form.save()
108
109
 
109
110
  @api.secure(admin_permission)
110
- @api.doc('delete_post')
111
- @api.response(204, 'Object deleted')
111
+ @api.doc("delete_post")
112
+ @api.response(204, "Object deleted")
112
113
  def delete(self, post):
113
- '''Delete a given post'''
114
+ """Delete a given post"""
114
115
  post.delete()
115
- return '', 204
116
+ return "", 204
116
117
 
117
118
 
118
- @ns.route('/<post:post>/publish', endpoint='publish_post')
119
+ @ns.route("/<post:post>/publish", endpoint="publish_post")
119
120
  class PublishPostAPI(API):
120
121
  @api.secure(admin_permission)
121
- @api.doc('publish_post')
122
+ @api.doc("publish_post")
122
123
  @api.marshal_with(post_fields)
123
124
  def post(self, post):
124
- '''Publish an existing post'''
125
+ """Publish an existing post"""
125
126
  post.modify(published=datetime.utcnow())
126
127
  return post
127
128
 
128
129
  @api.secure(admin_permission)
129
- @api.doc('unpublish_post')
130
+ @api.doc("unpublish_post")
130
131
  @api.marshal_with(post_fields)
131
132
  def delete(self, post):
132
- '''Publish an existing post'''
133
+ """Publish an existing post"""
133
134
  post.modify(published=None)
134
135
  return post
135
136
 
136
137
 
137
- @ns.route('/<post:post>/image', endpoint='post_image')
138
+ @ns.route("/<post:post>/image", endpoint="post_image")
138
139
  class PostImageAPI(API):
139
140
  @api.secure(admin_permission)
140
- @api.doc('post_image')
141
+ @api.doc("post_image")
141
142
  @api.expect(image_parser) # Swagger 2.0 does not support formData at path level
142
143
  @api.marshal_with(uploaded_image_fields)
143
144
  def post(self, post):
144
- '''Upload a new image'''
145
+ """Upload a new image"""
145
146
  parse_uploaded_image(post.image)
146
147
  post.save()
147
148
  return post
148
149
 
149
150
  @api.secure(admin_permission)
150
- @api.doc('resize_post_image')
151
+ @api.doc("resize_post_image")
151
152
  @api.expect(image_parser) # Swagger 2.0 does not support formData at path level
152
153
  @api.marshal_with(uploaded_image_fields)
153
154
  def put(self, post):
154
- '''Set the image BBox'''
155
+ """Set the image BBox"""
155
156
  parse_uploaded_image(post.image)
156
157
  return post
@@ -1,9 +1,12 @@
1
- from udata.i18n import lazy_gettext as _
2
1
  from collections import OrderedDict
3
2
 
3
+ from udata.i18n import lazy_gettext as _
4
+
4
5
  IMAGE_SIZES = [400, 100, 50]
5
6
 
6
- BODY_TYPES = OrderedDict([
7
- ('markdown', _('Markdown')),
8
- ('html', _('HTML')),
9
- ])
7
+ BODY_TYPES = OrderedDict(
8
+ [
9
+ ("markdown", _("Markdown")),
10
+ ("html", _("HTML")),
11
+ ]
12
+ )
@@ -11,10 +11,10 @@ class PostFactory(ModelFactory):
11
11
  class Meta:
12
12
  model = Post
13
13
 
14
- name = factory.Faker('sentence')
15
- headline = factory.Faker('sentence')
16
- content = factory.Faker('text')
17
- published = factory.Faker('past_datetime')
14
+ name = factory.Faker("sentence")
15
+ headline = factory.Faker("sentence")
16
+ content = factory.Faker("text")
17
+ published = factory.Faker("past_datetime")
18
18
 
19
19
  @factory.lazy_attribute
20
20
  def datasets(self):