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,115 +1,147 @@
1
+ from udata.api import api, base_reference, fields
1
2
  from udata.auth.helpers import current_user_is_admin_or_self
2
- from udata.api import api, fields, base_reference
3
3
 
4
4
  from .constants import BIGGEST_AVATAR_SIZE
5
5
 
6
-
7
- user_ref_fields = api.inherit('UserReference', base_reference, {
8
- 'first_name': fields.String(
9
- description='The user first name', readonly=True),
10
- 'last_name': fields.String(
11
- description='The user larst name', readonly=True),
12
- 'slug': fields.String(
13
- description='The user permalink string', required=True),
14
- 'page': fields.UrlFor(
15
- 'users.show', lambda u: {'user': u},
16
- description='The user profile page URL', readonly=True, fallback_endpoint='api.user'),
17
- 'uri': fields.UrlFor(
18
- 'api.user', lambda o: {'user': o},
19
- description='The user API URI', required=True),
20
- 'avatar': fields.ImageField(original=True,
21
- description='The user avatar URL'),
22
- 'avatar_thumbnail': fields.ImageField(attribute='avatar', size=BIGGEST_AVATAR_SIZE,
23
- description='The user avatar thumbnail URL. This is the square '
24
- '({0}x{0}) and cropped version.'.format(BIGGEST_AVATAR_SIZE)),
25
- })
6
+ user_ref_fields = api.inherit(
7
+ "UserReference",
8
+ base_reference,
9
+ {
10
+ "first_name": fields.String(description="The user first name", readonly=True),
11
+ "last_name": fields.String(description="The user larst name", readonly=True),
12
+ "slug": fields.String(description="The user permalink string", required=True),
13
+ "page": fields.UrlFor(
14
+ "users.show",
15
+ lambda u: {"user": u},
16
+ description="The user profile page URL",
17
+ readonly=True,
18
+ fallback_endpoint="api.user",
19
+ ),
20
+ "uri": fields.UrlFor(
21
+ "api.user", lambda o: {"user": o}, description="The user API URI", required=True
22
+ ),
23
+ "avatar": fields.ImageField(original=True, description="The user avatar URL"),
24
+ "avatar_thumbnail": fields.ImageField(
25
+ attribute="avatar",
26
+ size=BIGGEST_AVATAR_SIZE,
27
+ description="The user avatar thumbnail URL. This is the square "
28
+ "({0}x{0}) and cropped version.".format(BIGGEST_AVATAR_SIZE),
29
+ ),
30
+ },
31
+ )
26
32
 
27
33
  from udata.core.organization.api_fields import org_ref_fields # noqa
28
34
 
29
- user_fields = api.model('User', {
30
- 'id': fields.String(
31
- description='The user identifier', required=True),
32
- 'slug': fields.String(
33
- description='The user permalink string', required=True),
34
- 'first_name': fields.String(
35
- description='The user first name', required=True),
36
- 'last_name': fields.String(
37
- description='The user last name', required=True),
38
- 'email': fields.Raw(
39
- attribute=lambda o: o.email if current_user_is_admin_or_self() else None,
40
- description='The user email', readonly=True),
41
- 'avatar': fields.ImageField(original=True,
42
- description='The user avatar URL'),
43
- 'avatar_thumbnail': fields.ImageField(attribute='avatar', size=BIGGEST_AVATAR_SIZE,
44
- description='The user avatar thumbnail URL. This is the square '
45
- '({0}x{0}) and cropped version.'.format(BIGGEST_AVATAR_SIZE)),
46
- 'website': fields.String(description='The user website'),
47
- 'about': fields.Markdown(description='The user self description'),
48
- 'roles': fields.List(fields.String, description='Site wide user roles'),
49
- 'active': fields.Boolean(),
50
- 'organizations': fields.List(
51
- fields.Nested(org_ref_fields),
52
- description='The organization the user belongs to'),
53
- 'since': fields.ISODateTime(
54
- attribute='created_at',
55
- description='The registeration date', required=True),
56
- 'page': fields.UrlFor(
57
- 'users.show', lambda u: {'user': u},
58
- description='The user profile page URL', readonly=True, fallback_endpoint='api.user'),
59
- 'uri': fields.UrlFor(
60
- 'api.user', lambda o: {'user': o},
61
- description='The user API URI', required=True),
62
- 'metrics': fields.Raw(attribute=lambda o: o.get_metrics(),
63
- description='The user metrics', readonly=True),
64
- })
35
+ user_fields = api.model(
36
+ "User",
37
+ {
38
+ "id": fields.String(description="The user identifier", required=True),
39
+ "slug": fields.String(description="The user permalink string", required=True),
40
+ "first_name": fields.String(description="The user first name", required=True),
41
+ "last_name": fields.String(description="The user last name", required=True),
42
+ "email": fields.Raw(
43
+ attribute=lambda o: o.email if current_user_is_admin_or_self() else None,
44
+ description="The user email",
45
+ readonly=True,
46
+ ),
47
+ "avatar": fields.ImageField(original=True, description="The user avatar URL"),
48
+ "avatar_thumbnail": fields.ImageField(
49
+ attribute="avatar",
50
+ size=BIGGEST_AVATAR_SIZE,
51
+ description="The user avatar thumbnail URL. This is the square "
52
+ "({0}x{0}) and cropped version.".format(BIGGEST_AVATAR_SIZE),
53
+ ),
54
+ "website": fields.String(description="The user website"),
55
+ "about": fields.Markdown(description="The user self description"),
56
+ "roles": fields.List(fields.String, description="Site wide user roles"),
57
+ "active": fields.Boolean(),
58
+ "organizations": fields.List(
59
+ fields.Nested(org_ref_fields), description="The organization the user belongs to"
60
+ ),
61
+ "since": fields.ISODateTime(
62
+ attribute="created_at", description="The registeration date", required=True
63
+ ),
64
+ "page": fields.UrlFor(
65
+ "users.show",
66
+ lambda u: {"user": u},
67
+ description="The user profile page URL",
68
+ readonly=True,
69
+ fallback_endpoint="api.user",
70
+ ),
71
+ "uri": fields.UrlFor(
72
+ "api.user", lambda o: {"user": o}, description="The user API URI", required=True
73
+ ),
74
+ "metrics": fields.Raw(
75
+ attribute=lambda o: o.get_metrics(), description="The user metrics", readonly=True
76
+ ),
77
+ },
78
+ )
65
79
 
66
- me_fields = api.inherit('Me', user_fields, {
67
- 'apikey': fields.String(description='The user API Key', readonly=True),
68
- })
80
+ me_fields = api.inherit(
81
+ "Me",
82
+ user_fields,
83
+ {
84
+ "apikey": fields.String(description="The user API Key", readonly=True),
85
+ },
86
+ )
69
87
 
70
- me_metrics_fields = api.model('MyMetrics', {
71
- 'id': fields.String(
72
- description='The user identifier', required=True),
73
- 'resources_availability': fields.Float(
74
- description="The user's resources availability percentage",
75
- readonly=True),
76
- 'datasets_org_count': fields.Integer(
77
- description="The user's orgs datasets number", readonly=True),
78
- 'followers_org_count': fields.Integer(
79
- description="The user's orgs followers number", readonly=True),
80
- 'datasets_count': fields.Integer(
81
- description="The user's datasets number", readonly=True),
82
- 'followers_count': fields.Integer(
83
- description="The user's followers number", readonly=True),
84
- })
88
+ me_metrics_fields = api.model(
89
+ "MyMetrics",
90
+ {
91
+ "id": fields.String(description="The user identifier", required=True),
92
+ "resources_availability": fields.Float(
93
+ description="The user's resources availability percentage", readonly=True
94
+ ),
95
+ "datasets_org_count": fields.Integer(
96
+ description="The user's orgs datasets number", readonly=True
97
+ ),
98
+ "followers_org_count": fields.Integer(
99
+ description="The user's orgs followers number", readonly=True
100
+ ),
101
+ "datasets_count": fields.Integer(description="The user's datasets number", readonly=True),
102
+ "followers_count": fields.Integer(description="The user's followers number", readonly=True),
103
+ },
104
+ )
85
105
 
86
- apikey_fields = api.model('ApiKey', {
87
- 'apikey': fields.String(description='The user API Key', readonly=True),
88
- })
106
+ apikey_fields = api.model(
107
+ "ApiKey",
108
+ {
109
+ "apikey": fields.String(description="The user API Key", readonly=True),
110
+ },
111
+ )
89
112
 
90
- user_page_fields = api.model('UserPage', fields.pager(user_fields))
113
+ user_page_fields = api.model("UserPage", fields.pager(user_fields))
91
114
 
92
- user_suggestion_fields = api.model('UserSuggestion', {
93
- 'id': fields.String(description='The user identifier', readonly=True),
94
- 'first_name': fields.String(description='The user first name',
95
- readonly=True),
96
- 'last_name': fields.String(description='The user last name',
97
- readonly=True),
98
- 'avatar_url': fields.ImageField(size=BIGGEST_AVATAR_SIZE, description='The user avatar URL', readonly=True),
99
- 'slug': fields.String(
100
- description='The user permalink string', readonly=True),
101
- })
115
+ user_suggestion_fields = api.model(
116
+ "UserSuggestion",
117
+ {
118
+ "id": fields.String(description="The user identifier", readonly=True),
119
+ "first_name": fields.String(description="The user first name", readonly=True),
120
+ "last_name": fields.String(description="The user last name", readonly=True),
121
+ "avatar_url": fields.ImageField(
122
+ size=BIGGEST_AVATAR_SIZE, description="The user avatar URL", readonly=True
123
+ ),
124
+ "slug": fields.String(description="The user permalink string", readonly=True),
125
+ },
126
+ )
102
127
 
103
- notifications_fields = api.model('Notification', {
104
- 'type': fields.String(description='The notification type', readonly=True),
105
- 'created_on': fields.ISODateTime(
106
- description='The notification creation datetime', readonly=True),
107
- 'details': fields.Raw(
108
- description='Key-Value details depending on notification type',
109
- readonly=True)
110
- })
128
+ notifications_fields = api.model(
129
+ "Notification",
130
+ {
131
+ "type": fields.String(description="The notification type", readonly=True),
132
+ "created_on": fields.ISODateTime(
133
+ description="The notification creation datetime", readonly=True
134
+ ),
135
+ "details": fields.Raw(
136
+ description="Key-Value details depending on notification type", readonly=True
137
+ ),
138
+ },
139
+ )
111
140
 
112
141
 
113
- user_role_fields = api.model('UserRole', {
114
- 'name': fields.String(description='The role name', readonly=True),
115
- })
142
+ user_role_fields = api.model(
143
+ "UserRole",
144
+ {
145
+ "name": fields.String(description="The role name", readonly=True),
146
+ },
147
+ )
udata/core/user/apiv2.py CHANGED
@@ -1,28 +1,28 @@
1
1
  from flask_security import current_user
2
2
 
3
- from udata.api import apiv2, API
3
+ from udata.api import API, apiv2
4
4
  from udata.core.topic.apiv2 import topic_page_fields
5
5
  from udata.core.topic.parsers import TopicApiParser
6
6
  from udata.models import Topic
7
7
 
8
- me = apiv2.namespace('me', 'Connected user related operations (v2)')
8
+ me = apiv2.namespace("me", "Connected user related operations (v2)")
9
9
 
10
10
  # we will force include_private to True, no need for this arg
11
11
  topic_parser = TopicApiParser(with_include_private=False)
12
12
 
13
13
 
14
- @me.route('/org_topics/', endpoint='my_org_topics')
14
+ @me.route("/org_topics/", endpoint="my_org_topics")
15
15
  class MyOrgTopicsAPI(API):
16
16
  @apiv2.secure
17
- @apiv2.doc('my_org_topics')
17
+ @apiv2.doc("my_org_topics")
18
18
  @apiv2.expect(topic_parser.parser)
19
19
  @apiv2.marshal_list_with(topic_page_fields)
20
20
  def get(self):
21
- '''List all topics related to me and my organizations.'''
21
+ """List all topics related to me and my organizations."""
22
22
  args = topic_parser.parse()
23
23
  args["include_private"] = True
24
24
  owners = list(current_user.organizations) + [current_user.id]
25
25
  topics = Topic.objects.owned_by(*owners)
26
26
  topics = topic_parser.parse_filters(topics, args)
27
- sort = args['sort'] or ('$text_score' if args['q'] else None) or '-last-modified'
28
- return topics.order_by(sort).paginate(args['page'], args['page_size'])
27
+ sort = args["sort"] or ("$text_score" if args["q"] else None) or "-last-modified"
28
+ return topics.order_by(sort).paginate(args["page"], args["page_size"])
@@ -1,100 +1,100 @@
1
- import click
2
1
  import logging
3
-
4
2
  from datetime import datetime
3
+
4
+ import click
5
5
  from flask import current_app
6
6
  from flask_security.forms import RegisterForm
7
7
  from flask_security.utils import hash_password
8
8
  from werkzeug.datastructures import MultiDict
9
9
 
10
+ from udata.commands import cli, exit_with_error, success
10
11
  from udata.models import User, datastore
11
12
 
12
- from udata.commands import cli, success, exit_with_error
13
-
14
13
  log = logging.getLogger(__name__)
15
14
 
16
15
 
17
- @cli.group('user')
16
+ @cli.group("user")
18
17
  def grp():
19
- '''User related operations'''
18
+ """User related operations"""
20
19
  pass
21
20
 
22
21
 
23
22
  @grp.command()
24
23
  def create():
25
- '''Create a new user'''
24
+ """Create a new user"""
26
25
  data = {
27
- 'first_name': click.prompt('First name'),
28
- 'last_name': click.prompt('Last name'),
29
- 'email': click.prompt('Email'),
30
- 'password': click.prompt('Password', hide_input=True),
31
- 'password_confirm': click.prompt('Confirm Password', hide_input=True),
26
+ "first_name": click.prompt("First name"),
27
+ "last_name": click.prompt("Last name"),
28
+ "email": click.prompt("Email"),
29
+ "password": click.prompt("Password", hide_input=True),
30
+ "password_confirm": click.prompt("Confirm Password", hide_input=True),
32
31
  }
33
32
  # Until https://github.com/mattupstate/flask-security/issues/672 is fixed
34
33
  with current_app.test_request_context():
35
- form = RegisterForm(MultiDict(data), meta={'csrf': False})
34
+ form = RegisterForm(MultiDict(data), meta={"csrf": False})
36
35
  if form.validate():
37
- data['password'] = hash_password(data['password'])
38
- del data['password_confirm']
39
- data['confirmed_at'] = datetime.utcnow()
36
+ data["password"] = hash_password(data["password"])
37
+ del data["password_confirm"]
38
+ data["confirmed_at"] = datetime.utcnow()
40
39
  user = datastore.create_user(**data)
41
- success('User(id={u.id} email={u.email}) created'.format(u=user))
40
+ success("User(id={u.id} email={u.email}) created".format(u=user))
42
41
  return user
43
- errors = '\n'.join('\n'.join([str(m) for m in e]) for e in form.errors.values())
44
- exit_with_error('Error creating user', errors)
42
+ errors = "\n".join("\n".join([str(m) for m in e]) for e in form.errors.values())
43
+ exit_with_error("Error creating user", errors)
45
44
 
46
45
 
47
46
  @grp.command()
48
47
  def activate():
49
- '''Activate an existing user (validate their email confirmation)'''
50
- email = click.prompt('Email')
48
+ """Activate an existing user (validate their email confirmation)"""
49
+ email = click.prompt("Email")
51
50
  user = User.objects(email=email).first()
52
51
  if not user:
53
- exit_with_error('Invalid user')
52
+ exit_with_error("Invalid user")
54
53
  if user.confirmed_at is not None:
55
- exit_with_error('User email address already confirmed')
54
+ exit_with_error("User email address already confirmed")
56
55
  return
57
56
  user.confirmed_at = datetime.utcnow()
58
57
  user.save()
59
- success('User activated successfully')
58
+ success("User activated successfully")
60
59
 
61
60
 
62
61
  @grp.command()
63
62
  def delete():
64
- '''Delete an existing user'''
65
- email = click.prompt('Email')
63
+ """Delete an existing user"""
64
+ email = click.prompt("Email")
66
65
  user = User.objects(email=email).first()
67
66
  if not user:
68
- exit_with_error('Invalid user')
67
+ exit_with_error("Invalid user")
69
68
  user.mark_as_deleted()
70
- success('User marked as deleted successfully')
69
+ success("User marked as deleted successfully")
71
70
 
72
71
 
73
72
  @grp.command()
74
- @click.argument('email')
73
+ @click.argument("email")
75
74
  def set_admin(email):
76
- '''Set an user as administrator'''
75
+ """Set an user as administrator"""
77
76
  user = datastore.find_user(email=email)
78
- log.info('Adding admin role to user %s (%s)', user.fullname, user.email)
79
- role = datastore.find_or_create_role('admin')
77
+ log.info("Adding admin role to user %s (%s)", user.fullname, user.email)
78
+ role = datastore.find_or_create_role("admin")
80
79
  datastore.add_role_to_user(user, role)
81
- success('User %s (%s) is now administrator' % (user.fullname, user.email))
80
+ success("User %s (%s) is now administrator" % (user.fullname, user.email))
82
81
 
83
82
 
84
83
  @grp.command()
85
- @click.argument('email')
84
+ @click.argument("email")
86
85
  def password(email):
87
86
  user = datastore.find_user(email=email)
88
- password = click.prompt('Enter new password', hide_input=True)
87
+ password = click.prompt("Enter new password", hide_input=True)
89
88
  user.password = hash_password(password)
90
89
  user.save()
91
90
 
91
+
92
92
  @grp.command()
93
- @click.argument('email')
93
+ @click.argument("email")
94
94
  def rotate_password(email):
95
- '''
95
+ """
96
96
  Ask user for password rotation on next login and reset any current session
97
- '''
97
+ """
98
98
  user = datastore.find_user(email=email)
99
99
  user.password_rotation_demanded = datetime.utcnow()
100
100
  user.save()
@@ -1,32 +1,31 @@
1
1
  import factory
2
-
3
2
  from flask_security.utils import hash_password
4
3
 
5
4
  from udata.factories import ModelFactory
6
5
 
7
- from .models import User, Role
6
+ from .models import Role, User
8
7
 
9
8
 
10
9
  class UserFactory(ModelFactory):
11
10
  class Meta:
12
11
  model = User
13
12
 
14
- first_name = factory.Faker('first_name')
15
- last_name = factory.Faker('last_name')
16
- email = factory.Faker('email')
17
- fs_uniquifier = factory.Faker('uuid4')
13
+ first_name = factory.Faker("first_name")
14
+ last_name = factory.Faker("last_name")
15
+ email = factory.Faker("email")
16
+ fs_uniquifier = factory.Faker("uuid4")
18
17
  active = True
19
18
 
20
19
  @classmethod
21
20
  def _adjust_kwargs(cls, **kwargs):
22
- if 'password' in kwargs:
21
+ if "password" in kwargs:
23
22
  # Password is stored hashed
24
- kwargs['password'] = hash_password(kwargs['password'])
23
+ kwargs["password"] = hash_password(kwargs["password"])
25
24
  return kwargs
26
25
 
27
26
 
28
27
  class AdminFactory(UserFactory):
29
28
  @factory.lazy_attribute
30
29
  def roles(self):
31
- admin_role, _ = Role.objects.get_or_create(name='admin')
30
+ admin_role, _ = Role.objects.get_or_create(name="admin")
32
31
  return [admin_role]
udata/core/user/forms.py CHANGED
@@ -4,23 +4,26 @@ from udata.models import User
4
4
 
5
5
  from .constants import AVATAR_SIZES
6
6
 
7
-
8
- __all__ = ('UserProfileForm', 'UserProfileAdminForm')
7
+ __all__ = ("UserProfileForm", "UserProfileAdminForm")
9
8
 
10
9
 
11
10
  class UserProfileForm(ModelForm):
12
11
  model_class = User
13
12
 
14
- first_name = fields.StringField(_('First name'), [validators.DataRequired(),
15
- validators.NoURLs(_('URLs not allowed in this field'))])
16
- last_name = fields.StringField(_('Last name'), [validators.DataRequired(),
17
- validators.NoURLs(_('URLs not allowed in this field'))])
18
- email = fields.StringField(_('Email'), [validators.DataRequired(), validators.Email()])
19
- avatar = fields.ImageField(_('Avatar'), sizes=AVATAR_SIZES)
20
- website = fields.URLField(_('Website'))
21
- about = fields.MarkdownField(_('About'))
13
+ first_name = fields.StringField(
14
+ _("First name"),
15
+ [validators.DataRequired(), validators.NoURLs(_("URLs not allowed in this field"))],
16
+ )
17
+ last_name = fields.StringField(
18
+ _("Last name"),
19
+ [validators.DataRequired(), validators.NoURLs(_("URLs not allowed in this field"))],
20
+ )
21
+ email = fields.StringField(_("Email"), [validators.DataRequired(), validators.Email()])
22
+ avatar = fields.ImageField(_("Avatar"), sizes=AVATAR_SIZES)
23
+ website = fields.URLField(_("Website"))
24
+ about = fields.MarkdownField(_("About"))
22
25
 
23
26
 
24
27
  class UserProfileAdminForm(UserProfileForm):
25
- roles = fields.RolesField(_('Roles'))
28
+ roles = fields.RolesField(_("Roles"))
26
29
  active = fields.BooleanField()
@@ -1,6 +1,7 @@
1
- from udata.models import db, Dataset, Reuse, User
2
1
  from udata.core.followers.signals import on_follow, on_unfollow
3
2
  from udata.core.owned import Owned
3
+ from udata.models import Dataset, Reuse, User, db
4
+
4
5
 
5
6
  @Dataset.on_create.connect
6
7
  @Dataset.on_update.connect
@@ -32,4 +33,3 @@ def update_owner_metrics(document, previous):
32
33
  previous.count_datasets()
33
34
  elif isinstance(document, Reuse):
34
35
  previous.count_reuses()
35
-