udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30382__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 (425) 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 +135 -124
  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 +56 -54
  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/db/tasks.py +2 -1
  199. udata/entrypoints.py +35 -31
  200. udata/errors.py +2 -1
  201. udata/event/values.py +6 -6
  202. udata/factories.py +2 -2
  203. udata/features/identicon/api.py +5 -6
  204. udata/features/identicon/backends.py +48 -55
  205. udata/features/identicon/tests/test_backends.py +4 -5
  206. udata/features/notifications/__init__.py +0 -1
  207. udata/features/notifications/actions.py +9 -9
  208. udata/features/notifications/api.py +17 -13
  209. udata/features/territories/__init__.py +12 -10
  210. udata/features/territories/api.py +14 -15
  211. udata/features/territories/models.py +23 -28
  212. udata/features/transfer/actions.py +8 -11
  213. udata/features/transfer/api.py +84 -77
  214. udata/features/transfer/factories.py +2 -1
  215. udata/features/transfer/models.py +11 -12
  216. udata/features/transfer/notifications.py +19 -15
  217. udata/features/transfer/permissions.py +5 -5
  218. udata/forms/__init__.py +5 -2
  219. udata/forms/fields.py +164 -172
  220. udata/forms/validators.py +19 -22
  221. udata/forms/widgets.py +9 -13
  222. udata/frontend/__init__.py +31 -26
  223. udata/frontend/csv.py +68 -58
  224. udata/frontend/markdown.py +40 -44
  225. udata/harvest/actions.py +89 -77
  226. udata/harvest/api.py +294 -238
  227. udata/harvest/backends/__init__.py +4 -4
  228. udata/harvest/backends/base.py +128 -111
  229. udata/harvest/backends/dcat.py +80 -66
  230. udata/harvest/commands.py +56 -60
  231. udata/harvest/csv.py +8 -8
  232. udata/harvest/exceptions.py +6 -3
  233. udata/harvest/filters.py +24 -23
  234. udata/harvest/forms.py +27 -28
  235. udata/harvest/models.py +88 -80
  236. udata/harvest/notifications.py +15 -10
  237. udata/harvest/signals.py +13 -13
  238. udata/harvest/tasks.py +11 -10
  239. udata/harvest/tests/factories.py +23 -24
  240. udata/harvest/tests/test_actions.py +136 -166
  241. udata/harvest/tests/test_api.py +220 -214
  242. udata/harvest/tests/test_base_backend.py +117 -112
  243. udata/harvest/tests/test_dcat_backend.py +380 -308
  244. udata/harvest/tests/test_filters.py +33 -22
  245. udata/harvest/tests/test_models.py +11 -14
  246. udata/harvest/tests/test_notifications.py +6 -7
  247. udata/harvest/tests/test_tasks.py +7 -6
  248. udata/i18n.py +237 -78
  249. udata/linkchecker/backends.py +5 -11
  250. udata/linkchecker/checker.py +23 -22
  251. udata/linkchecker/commands.py +4 -6
  252. udata/linkchecker/models.py +6 -6
  253. udata/linkchecker/tasks.py +18 -20
  254. udata/mail.py +21 -21
  255. udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
  256. udata/migrations/2020-08-24-add-fs-filename.py +9 -8
  257. udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
  258. udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
  259. udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
  260. udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
  261. udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
  262. udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
  263. udata/migrations/2021-08-17-follow-integrity.py +5 -4
  264. udata/migrations/2021-08-17-harvest-integrity.py +13 -12
  265. udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
  266. udata/migrations/2021-08-17-transfer-integrity.py +5 -4
  267. udata/migrations/2021-08-17-users-integrity.py +9 -8
  268. udata/migrations/2021-12-14-reuse-topics.py +7 -6
  269. udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
  270. udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
  271. udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
  272. udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
  273. udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
  274. udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
  275. udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
  276. udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
  277. udata/migrations/__init__.py +123 -105
  278. udata/models/__init__.py +4 -4
  279. udata/mongo/__init__.py +13 -11
  280. udata/mongo/badges_field.py +3 -2
  281. udata/mongo/datetime_fields.py +13 -12
  282. udata/mongo/document.py +17 -16
  283. udata/mongo/engine.py +15 -16
  284. udata/mongo/errors.py +2 -1
  285. udata/mongo/extras_fields.py +30 -20
  286. udata/mongo/queryset.py +12 -12
  287. udata/mongo/slug_fields.py +38 -28
  288. udata/mongo/taglist_field.py +1 -2
  289. udata/mongo/url_field.py +5 -5
  290. udata/mongo/uuid_fields.py +4 -3
  291. udata/notifications/__init__.py +1 -1
  292. udata/notifications/mattermost.py +10 -9
  293. udata/rdf.py +167 -188
  294. udata/routing.py +40 -45
  295. udata/search/__init__.py +18 -19
  296. udata/search/adapter.py +17 -16
  297. udata/search/commands.py +44 -51
  298. udata/search/fields.py +13 -20
  299. udata/search/query.py +23 -18
  300. udata/search/result.py +9 -10
  301. udata/sentry.py +21 -19
  302. udata/settings.py +262 -198
  303. udata/sitemap.py +8 -6
  304. udata/static/chunks/{11.e9b9ca1f3e03d4020377.js → 11.52e531c19f8de80c00cf.js} +3 -3
  305. udata/static/chunks/{11.e9b9ca1f3e03d4020377.js.map → 11.52e531c19f8de80c00cf.js.map} +1 -1
  306. udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js → 13.c3343a7f1070061c0e10.js} +2 -2
  307. udata/static/chunks/{13.038c0d9aa0dfa0181c4b.js.map → 13.c3343a7f1070061c0e10.js.map} +1 -1
  308. udata/static/chunks/{16.0baa2b64a74a2dcde25c.js → 16.8fa42440ad75ca172e6d.js} +2 -2
  309. udata/static/chunks/{16.0baa2b64a74a2dcde25c.js.map → 16.8fa42440ad75ca172e6d.js.map} +1 -1
  310. udata/static/chunks/{19.350a9f150b074b4ecefa.js → 19.9c6c8412729cd6d59cfa.js} +3 -3
  311. udata/static/chunks/{19.350a9f150b074b4ecefa.js.map → 19.9c6c8412729cd6d59cfa.js.map} +1 -1
  312. udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js → 5.71d15c2e4f21feee2a9a.js} +3 -3
  313. udata/static/chunks/{5.6ebbce2b9b3e696d3da5.js.map → 5.71d15c2e4f21feee2a9a.js.map} +1 -1
  314. udata/static/chunks/{6.d8a5f7b017bcbd083641.js → 6.9139dc098b8ea640b890.js} +3 -3
  315. udata/static/chunks/{6.d8a5f7b017bcbd083641.js.map → 6.9139dc098b8ea640b890.js.map} +1 -1
  316. udata/static/common.js +1 -1
  317. udata/static/common.js.map +1 -1
  318. udata/storage/s3.py +20 -13
  319. udata/tags.py +4 -5
  320. udata/tasks.py +43 -42
  321. udata/tests/__init__.py +9 -6
  322. udata/tests/api/__init__.py +5 -6
  323. udata/tests/api/test_auth_api.py +395 -321
  324. udata/tests/api/test_base_api.py +31 -33
  325. udata/tests/api/test_contact_points.py +7 -9
  326. udata/tests/api/test_dataservices_api.py +211 -158
  327. udata/tests/api/test_datasets_api.py +823 -812
  328. udata/tests/api/test_follow_api.py +13 -15
  329. udata/tests/api/test_me_api.py +95 -112
  330. udata/tests/api/test_organizations_api.py +301 -339
  331. udata/tests/api/test_reports_api.py +35 -25
  332. udata/tests/api/test_reuses_api.py +134 -139
  333. udata/tests/api/test_swagger.py +5 -5
  334. udata/tests/api/test_tags_api.py +18 -25
  335. udata/tests/api/test_topics_api.py +94 -94
  336. udata/tests/api/test_transfer_api.py +53 -48
  337. udata/tests/api/test_user_api.py +128 -141
  338. udata/tests/apiv2/test_datasets.py +290 -198
  339. udata/tests/apiv2/test_me_api.py +10 -11
  340. udata/tests/apiv2/test_organizations.py +56 -74
  341. udata/tests/apiv2/test_swagger.py +5 -5
  342. udata/tests/apiv2/test_topics.py +69 -87
  343. udata/tests/cli/test_cli_base.py +8 -8
  344. udata/tests/cli/test_db_cli.py +21 -19
  345. udata/tests/dataservice/test_dataservice_tasks.py +8 -12
  346. udata/tests/dataset/test_csv_adapter.py +44 -35
  347. udata/tests/dataset/test_dataset_actions.py +2 -3
  348. udata/tests/dataset/test_dataset_commands.py +7 -8
  349. udata/tests/dataset/test_dataset_events.py +36 -29
  350. udata/tests/dataset/test_dataset_model.py +224 -217
  351. udata/tests/dataset/test_dataset_rdf.py +142 -131
  352. udata/tests/dataset/test_dataset_tasks.py +15 -15
  353. udata/tests/dataset/test_resource_preview.py +10 -13
  354. udata/tests/features/territories/__init__.py +9 -13
  355. udata/tests/features/territories/test_territories_api.py +71 -91
  356. udata/tests/forms/test_basic_fields.py +7 -7
  357. udata/tests/forms/test_current_user_field.py +39 -66
  358. udata/tests/forms/test_daterange_field.py +31 -39
  359. udata/tests/forms/test_dict_field.py +28 -26
  360. udata/tests/forms/test_extras_fields.py +102 -76
  361. udata/tests/forms/test_form_field.py +8 -8
  362. udata/tests/forms/test_image_field.py +33 -26
  363. udata/tests/forms/test_model_field.py +134 -123
  364. udata/tests/forms/test_model_list_field.py +7 -7
  365. udata/tests/forms/test_nested_model_list_field.py +117 -79
  366. udata/tests/forms/test_publish_as_field.py +36 -65
  367. udata/tests/forms/test_reference_field.py +34 -53
  368. udata/tests/forms/test_user_forms.py +23 -21
  369. udata/tests/forms/test_uuid_field.py +6 -10
  370. udata/tests/frontend/__init__.py +9 -6
  371. udata/tests/frontend/test_auth.py +7 -6
  372. udata/tests/frontend/test_csv.py +81 -96
  373. udata/tests/frontend/test_hooks.py +43 -43
  374. udata/tests/frontend/test_markdown.py +211 -191
  375. udata/tests/helpers.py +32 -37
  376. udata/tests/models.py +2 -2
  377. udata/tests/organization/test_csv_adapter.py +21 -16
  378. udata/tests/organization/test_notifications.py +11 -18
  379. udata/tests/organization/test_organization_model.py +13 -13
  380. udata/tests/organization/test_organization_rdf.py +29 -22
  381. udata/tests/organization/test_organization_tasks.py +16 -17
  382. udata/tests/plugin.py +76 -73
  383. udata/tests/reuse/test_reuse_model.py +21 -21
  384. udata/tests/reuse/test_reuse_task.py +11 -13
  385. udata/tests/search/__init__.py +11 -12
  386. udata/tests/search/test_adapter.py +60 -70
  387. udata/tests/search/test_query.py +16 -16
  388. udata/tests/search/test_results.py +10 -7
  389. udata/tests/site/test_site_api.py +11 -16
  390. udata/tests/site/test_site_metrics.py +20 -30
  391. udata/tests/site/test_site_model.py +4 -5
  392. udata/tests/site/test_site_rdf.py +94 -78
  393. udata/tests/test_activity.py +17 -17
  394. udata/tests/test_discussions.py +292 -299
  395. udata/tests/test_i18n.py +37 -40
  396. udata/tests/test_linkchecker.py +91 -85
  397. udata/tests/test_mail.py +13 -17
  398. udata/tests/test_migrations.py +219 -180
  399. udata/tests/test_model.py +164 -157
  400. udata/tests/test_notifications.py +17 -17
  401. udata/tests/test_owned.py +14 -14
  402. udata/tests/test_rdf.py +25 -23
  403. udata/tests/test_routing.py +89 -93
  404. udata/tests/test_storages.py +137 -128
  405. udata/tests/test_tags.py +44 -46
  406. udata/tests/test_topics.py +7 -7
  407. udata/tests/test_transfer.py +42 -49
  408. udata/tests/test_uris.py +160 -161
  409. udata/tests/test_utils.py +79 -71
  410. udata/tests/user/test_user_rdf.py +5 -9
  411. udata/tests/workers/test_jobs_commands.py +57 -58
  412. udata/tests/workers/test_tasks_routing.py +23 -29
  413. udata/tests/workers/test_workers_api.py +125 -131
  414. udata/tests/workers/test_workers_helpers.py +6 -6
  415. udata/tracking.py +4 -6
  416. udata/uris.py +45 -46
  417. udata/utils.py +68 -66
  418. udata/wsgi.py +1 -1
  419. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/METADATA +3 -2
  420. udata-9.1.2.dev30382.dist-info/RECORD +704 -0
  421. udata-9.1.2.dev30355.dist-info/RECORD +0 -704
  422. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/LICENSE +0 -0
  423. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/WHEEL +0 -0
  424. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/entry_points.txt +0 -0
  425. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30382.dist-info}/top_level.txt +0 -0
udata/api/__init__.py CHANGED
@@ -1,67 +1,70 @@
1
- import itertools
2
1
  import inspect
2
+ import itertools
3
3
  import logging
4
4
  import urllib.parse
5
-
6
5
  from functools import wraps
7
6
  from importlib import import_module
8
7
 
9
8
  from flask import (
10
- current_app, g, request, url_for, json, make_response, redirect, Blueprint
9
+ Blueprint,
10
+ current_app,
11
+ g,
12
+ json,
13
+ make_response,
14
+ redirect,
15
+ request,
16
+ url_for,
11
17
  )
12
- from flask_storage import UnauthorizedFileType
13
- from flask_restx import Api, Resource
14
18
  from flask_cors import CORS
19
+ from flask_restx import Api, Resource
20
+ from flask_storage import UnauthorizedFileType
15
21
 
16
- from udata import tracking, entrypoints
22
+ from udata import entrypoints, tracking
17
23
  from udata.app import csrf
24
+ from udata.auth import Permission, PermissionDenied, RoleNeed, current_user, login_user
18
25
  from udata.i18n import get_locale
19
- from udata.auth import (
20
- current_user, login_user, Permission, RoleNeed, PermissionDenied
21
- )
22
- from udata.utils import safe_unicode
23
26
  from udata.mongo.errors import FieldValidationError
27
+ from udata.utils import safe_unicode
24
28
 
25
29
  from . import fields
26
30
  from .signals import on_api_call
27
31
 
28
-
29
32
  log = logging.getLogger(__name__)
30
33
 
31
- apiv1_blueprint = Blueprint('api', __name__, url_prefix='/api/1')
32
- apiv2_blueprint = Blueprint('apiv2', __name__, url_prefix='/api/2')
34
+ apiv1_blueprint = Blueprint("api", __name__, url_prefix="/api/1")
35
+ apiv2_blueprint = Blueprint("apiv2", __name__, url_prefix="/api/2")
33
36
 
34
37
  DEFAULT_PAGE_SIZE = 50
35
- HEADER_API_KEY = 'X-API-KEY'
38
+ HEADER_API_KEY = "X-API-KEY"
36
39
 
37
40
  # TODO: make upstream flask-restplus automatically handle
38
41
  # flask-restplus headers and allow lazy evaluation
39
42
  # of headers (ie. callable)
40
43
  PREFLIGHT_HEADERS = (
41
44
  HEADER_API_KEY,
42
- 'X-Fields',
43
- 'Content-Type',
44
- 'Accept',
45
- 'Accept-Charset',
46
- 'Accept-Language',
47
- 'Authorization',
48
- 'Cache-Control',
49
- 'Content-Encoding',
50
- 'Content-Length',
51
- 'Content-Security-Policy',
52
- 'Content-Type',
53
- 'Cookie',
54
- 'ETag',
55
- 'Host',
56
- 'If-Modified-Since',
57
- 'Keep-Alive',
58
- 'Last-Modified',
59
- 'Origin',
60
- 'Referer',
61
- 'User-Agent',
62
- 'X-Forwarded-For',
63
- 'X-Forwarded-Port',
64
- 'X-Forwarded-Proto',
45
+ "X-Fields",
46
+ "Content-Type",
47
+ "Accept",
48
+ "Accept-Charset",
49
+ "Accept-Language",
50
+ "Authorization",
51
+ "Cache-Control",
52
+ "Content-Encoding",
53
+ "Content-Length",
54
+ "Content-Security-Policy",
55
+ "Content-Type",
56
+ "Cookie",
57
+ "ETag",
58
+ "Host",
59
+ "If-Modified-Since",
60
+ "Keep-Alive",
61
+ "Last-Modified",
62
+ "Origin",
63
+ "Referer",
64
+ "User-Agent",
65
+ "X-Forwarded-For",
66
+ "X-Forwarded-Port",
67
+ "X-Forwarded-Proto",
65
68
  )
66
69
 
67
70
  cors = CORS(allow_headers=PREFLIGHT_HEADERS)
@@ -69,21 +72,15 @@ cors = CORS(allow_headers=PREFLIGHT_HEADERS)
69
72
 
70
73
  class UDataApi(Api):
71
74
  def __init__(self, app=None, **kwargs):
72
- decorators = kwargs.pop('decorators', []) or []
73
- kwargs['decorators'] = [self.authentify] + decorators
75
+ decorators = kwargs.pop("decorators", []) or []
76
+ kwargs["decorators"] = [self.authentify] + decorators
74
77
  super(UDataApi, self).__init__(app, **kwargs)
75
- self.authorizations = {
76
- 'apikey': {
77
- 'type': 'apiKey',
78
- 'in': 'header',
79
- 'name': HEADER_API_KEY
80
- }
81
- }
78
+ self.authorizations = {"apikey": {"type": "apiKey", "in": "header", "name": HEADER_API_KEY}}
82
79
 
83
80
  def secure(self, func):
84
- '''Enforce authentication on a given method/verb
81
+ """Enforce authentication on a given method/verb
85
82
  and optionally check a given permission
86
- '''
83
+ """
87
84
  if isinstance(func, str):
88
85
  return self._apply_permission(Permission(RoleNeed(func)))
89
86
  elif isinstance(func, Permission):
@@ -94,21 +91,25 @@ class UDataApi(Api):
94
91
  def _apply_permission(self, permission):
95
92
  def wrapper(func):
96
93
  return self._apply_secure(func, permission)
94
+
97
95
  return wrapper
98
96
 
99
97
  def _apply_secure(self, func, permission=None):
100
- '''Enforce authentication on a given method/verb'''
101
- self._build_doc(func, {'security': 'apikey'})
98
+ """Enforce authentication on a given method/verb"""
99
+ self._build_doc(func, {"security": "apikey"})
102
100
 
103
101
  @wraps(func)
104
102
  def wrapper(*args, **kwargs):
105
103
  if (
106
- not current_user.is_anonymous and
107
- not current_user.sysadmin and
108
- current_app.config['READ_ONLY_MODE'] and
109
- any(ext in str(func) for ext in current_app.config['METHOD_BLOCKLIST'])
104
+ not current_user.is_anonymous
105
+ and not current_user.sysadmin
106
+ and current_app.config["READ_ONLY_MODE"]
107
+ and any(ext in str(func) for ext in current_app.config["METHOD_BLOCKLIST"])
110
108
  ):
111
- self.abort(423, 'Due to security reasons, the creation of new content is currently disabled.')
109
+ self.abort(
110
+ 423,
111
+ "Due to security reasons, the creation of new content is currently disabled.",
112
+ )
112
113
 
113
114
  if not current_user.is_authenticated:
114
115
  self.abort(401)
@@ -125,11 +126,12 @@ class UDataApi(Api):
125
126
  return wrapper
126
127
 
127
128
  def authentify(self, func):
128
- '''Authentify the user if credentials are given'''
129
+ """Authentify the user if credentials are given"""
130
+
129
131
  @wraps(func)
130
132
  def wrapper(*args, **kwargs):
131
- from udata.core.user.models import User
132
133
  from udata.api.oauth2 import check_credentials
134
+ from udata.core.user.models import User
133
135
 
134
136
  if current_user.is_authenticated:
135
137
  return func(*args, **kwargs)
@@ -139,72 +141,79 @@ class UDataApi(Api):
139
141
  try:
140
142
  user = User.objects.get(apikey=apikey)
141
143
  except User.DoesNotExist:
142
- self.abort(401, 'Invalid API Key')
144
+ self.abort(401, "Invalid API Key")
143
145
 
144
146
  if not login_user(user, False):
145
- self.abort(401, 'Inactive user')
147
+ self.abort(401, "Inactive user")
146
148
  else:
147
149
  check_credentials()
148
150
  return func(*args, **kwargs)
151
+
149
152
  return wrapper
150
153
 
151
154
  def validate(self, form_cls, obj=None):
152
- '''Validate a form from the request and handle errors'''
153
- if 'application/json' not in request.headers.get('Content-Type', ''):
154
- errors = {'Content-Type': 'expecting application/json'}
155
+ """Validate a form from the request and handle errors"""
156
+ if "application/json" not in request.headers.get("Content-Type", ""):
157
+ errors = {"Content-Type": "expecting application/json"}
155
158
  self.abort(400, errors=errors)
156
- form = form_cls.from_json(request.json, obj=obj, instance=obj,
157
- meta={'csrf': False})
159
+ form = form_cls.from_json(request.json, obj=obj, instance=obj, meta={"csrf": False})
158
160
  if not form.validate():
159
161
  self.abort(400, errors=form.errors)
160
162
  return form
161
163
 
162
164
  def render_ui(self):
163
- return redirect(current_app.config.get('API_DOC_EXTERNAL_LINK'))
165
+ return redirect(current_app.config.get("API_DOC_EXTERNAL_LINK"))
164
166
 
165
167
  def unauthorized(self, response):
166
- '''Override to change the WWW-Authenticate challenge'''
167
- realm = current_app.config.get('HTTP_OAUTH_REALM', 'uData')
168
+ """Override to change the WWW-Authenticate challenge"""
169
+ realm = current_app.config.get("HTTP_OAUTH_REALM", "uData")
168
170
  challenge = 'Bearer realm="{0}"'.format(realm)
169
171
 
170
- response.headers['WWW-Authenticate'] = challenge
172
+ response.headers["WWW-Authenticate"] = challenge
171
173
  return response
172
174
 
173
175
  def page_parser(self):
174
176
  parser = self.parser()
175
- parser.add_argument('page', type=int, default=1, location='args',
176
- help='The page to fetch')
177
- parser.add_argument('page_size', type=int, default=20, location='args',
178
- help='The page size to fetch')
177
+ parser.add_argument("page", type=int, default=1, location="args", help="The page to fetch")
178
+ parser.add_argument(
179
+ "page_size", type=int, default=20, location="args", help="The page size to fetch"
180
+ )
179
181
  return parser
180
182
 
181
183
 
182
184
  api = UDataApi(
183
185
  apiv1_blueprint,
184
186
  decorators=[csrf.exempt],
185
- version='1.0', title='uData API',
186
- description='uData API', default='site',
187
- default_label='Site global namespace'
187
+ version="1.0",
188
+ title="uData API",
189
+ description="uData API",
190
+ default="site",
191
+ default_label="Site global namespace",
188
192
  )
189
193
 
190
194
  apiv2 = UDataApi(
191
195
  apiv2_blueprint,
192
196
  decorators=[csrf.exempt],
193
- version='2.0', title='uData API',
194
- description='udata API v2', default='site',
195
- default_label='Site global namespace',
197
+ version="2.0",
198
+ title="uData API",
199
+ description="udata API v2",
200
+ default="site",
201
+ default_label="Site global namespace",
196
202
  )
197
203
 
198
204
 
199
- api.model_reference = api.model('ModelReference', {
200
- 'class': fields.ClassName(description='The model class', required=True),
201
- 'id': fields.String(description='The object identifier', required=True),
202
- })
205
+ api.model_reference = api.model(
206
+ "ModelReference",
207
+ {
208
+ "class": fields.ClassName(description="The model class", required=True),
209
+ "id": fields.String(description="The object identifier", required=True),
210
+ },
211
+ )
203
212
 
204
213
 
205
- @api.representation('application/json')
214
+ @api.representation("application/json")
206
215
  def output_json(data, code, headers=None):
207
- '''Use Flask JSON to serialize'''
216
+ """Use Flask JSON to serialize"""
208
217
  resp = make_response(json.dumps(data), code)
209
218
  resp.headers.extend(headers or {})
210
219
  return resp
@@ -213,8 +222,8 @@ def output_json(data, code, headers=None):
213
222
  @apiv1_blueprint.before_request
214
223
  @apiv2_blueprint.before_request
215
224
  def set_api_language():
216
- if 'lang' in request.args:
217
- g.lang_code = request.args['lang']
225
+ if "lang" in request.args:
226
+ g.lang_code = request.args["lang"]
218
227
  else:
219
228
  g.lang_code = get_locale()
220
229
 
@@ -225,17 +234,18 @@ def extract_name_from_path(path):
225
234
  Useful to log requests on Piwik with categories tree structure.
226
235
  See: http://piwik.org/faq/how-to/#faq_62
227
236
  """
228
- base_path, query_string = path.split('?')
229
- infos = base_path.strip('/').split('/')[2:] # Removes api/version.
230
- if base_path == '/api/1/' or base_path == '/api/2/': # The API root endpoint redirects to swagger doc.
231
- return safe_unicode('apidoc')
237
+ base_path, query_string = path.split("?")
238
+ infos = base_path.strip("/").split("/")[2:] # Removes api/version.
239
+ if (
240
+ base_path == "/api/1/" or base_path == "/api/2/"
241
+ ): # The API root endpoint redirects to swagger doc.
242
+ return safe_unicode("apidoc")
232
243
  if len(infos) > 1: # This is an object.
233
- name = '{category} / {name}'.format(
234
- category=infos[0].title(),
235
- name=infos[1].replace('-', ' ').title()
244
+ name = "{category} / {name}".format(
245
+ category=infos[0].title(), name=infos[1].replace("-", " ").title()
236
246
  )
237
247
  else: # This is a collection.
238
- name = '{category}'.format(category=infos[0].title())
248
+ name = "{category}".format(category=infos[0].title())
239
249
  return safe_unicode(name)
240
250
 
241
251
 
@@ -243,71 +253,71 @@ def extract_name_from_path(path):
243
253
  @apiv2_blueprint.after_request
244
254
  def collect_stats(response):
245
255
  action_name = extract_name_from_path(request.full_path)
246
- blacklist = current_app.config.get('TRACKING_BLACKLIST', [])
247
- if (not current_app.config['TESTING'] and
248
- request.endpoint not in blacklist):
256
+ blacklist = current_app.config.get("TRACKING_BLACKLIST", [])
257
+ if not current_app.config["TESTING"] and request.endpoint not in blacklist:
249
258
  extras = {
250
- 'action_name': urllib.parse.quote(action_name),
259
+ "action_name": urllib.parse.quote(action_name),
251
260
  }
252
261
  tracking.send_signal(on_api_call, request, current_user, **extras)
253
262
  return response
254
263
 
255
264
 
256
- default_error = api.model('Error', {
257
- 'message': fields.String
258
- })
265
+ default_error = api.model("Error", {"message": fields.String})
259
266
 
260
267
 
261
268
  @api.errorhandler(PermissionDenied)
262
269
  @api.marshal_with(default_error, code=403)
263
270
  def handle_permission_denied(error):
264
- '''Error occuring when the user does not have the required permissions'''
265
- message = 'You do not have the permission to modify that object.'
266
- return {'message': message}, 403
271
+ """Error occuring when the user does not have the required permissions"""
272
+ message = "You do not have the permission to modify that object."
273
+ return {"message": message}, 403
267
274
 
268
275
 
269
276
  @api.errorhandler(ValueError)
270
277
  @api.marshal_with(default_error, code=400)
271
278
  def handle_value_error(error):
272
- '''A generic value error'''
273
- return {'message': str(error)}, 400
279
+ """A generic value error"""
280
+ return {"message": str(error)}, 400
274
281
 
275
282
 
276
283
  @api.errorhandler(UnauthorizedFileType)
277
284
  @api.marshal_with(default_error, code=400)
278
285
  def handle_unauthorized_file_type(error):
279
- '''Error occuring when the user try to upload a non-allowed file type'''
280
- url = url_for('api.allowed_extensions', _external=True)
286
+ """Error occuring when the user try to upload a non-allowed file type"""
287
+ url = url_for("api.allowed_extensions", _external=True)
281
288
  msg = (
282
- 'This file type is not allowed.'
283
- 'The allowed file type list is available at {url}'
289
+ "This file type is not allowed." "The allowed file type list is available at {url}"
284
290
  ).format(url=url)
285
- return {'message': msg}, 400
291
+ return {"message": msg}, 400
292
+
286
293
 
294
+ validation_error_fields = api.model("ValidationError", {"errors": fields.Raw})
287
295
 
288
- validation_error_fields = api.model('ValidationError', {
289
- 'errors': fields.Raw
290
- })
291
296
 
292
297
  @api.errorhandler(FieldValidationError)
293
298
  @api.marshal_with(validation_error_fields, code=400)
294
299
  def handle_validation_error(error: FieldValidationError):
295
- '''A validation error'''
300
+ """A validation error"""
296
301
  errors = {}
297
302
  errors[error.field] = [error.message]
298
303
 
299
- return { 'errors': errors}, 400
304
+ return {"errors": errors}, 400
305
+
300
306
 
301
307
  class API(Resource): # Avoid name collision as resource is a core model
302
308
  pass
303
309
 
304
310
 
305
- base_reference = api.model('BaseReference', {
306
- 'id': fields.String(description='The object unique identifier',
307
- required=True),
308
- 'class': fields.ClassName(description='The object class',
309
- discriminator=True, required=True),
310
- }, description='Base model for reference field, aka. inline model reference')
311
+ base_reference = api.model(
312
+ "BaseReference",
313
+ {
314
+ "id": fields.String(description="The object unique identifier", required=True),
315
+ "class": fields.ClassName(
316
+ description="The object class", discriminator=True, required=True
317
+ ),
318
+ },
319
+ description="Base model for reference field, aka. inline model reference",
320
+ )
311
321
 
312
322
 
313
323
  def marshal_page(page, page_fields):
@@ -340,14 +350,14 @@ def init_app(app):
340
350
  import udata.core.topic.api # noqa
341
351
  import udata.core.topic.apiv2 # noqa
342
352
  import udata.core.post.api # noqa
343
- import udata.core.contact_point.api # noqa
353
+ import udata.core.contact_point.api # noqa
344
354
  import udata.features.transfer.api # noqa
345
355
  import udata.features.notifications.api # noqa
346
356
  import udata.features.identicon.api # noqa
347
357
  import udata.features.territories.api # noqa
348
358
  import udata.harvest.api # noqa
349
359
 
350
- for module in entrypoints.get_enabled('udata.apis', app).values():
360
+ for module in entrypoints.get_enabled("udata.apis", app).values():
351
361
  api_module = module if inspect.ismodule(module) else import_module(module)
352
362
 
353
363
  # api.init_app(app)
@@ -355,5 +365,6 @@ def init_app(app):
355
365
  app.register_blueprint(apiv2_blueprint)
356
366
 
357
367
  from udata.api.oauth2 import init_app as oauth2_init_app
368
+
358
369
  oauth2_init_app(app)
359
370
  cors.init_app(app)
udata/api/commands.py CHANGED
@@ -2,79 +2,87 @@ import logging
2
2
  import os
3
3
 
4
4
  import click
5
-
6
- from werkzeug.security import gen_salt
7
- from flask import json, current_app
5
+ from flask import current_app, json
8
6
  from flask_restx import schemas
7
+ from werkzeug.security import gen_salt
9
8
 
10
9
  from udata.api import api
11
- from udata.commands import cli, success, exit_with_error
12
- from udata.models import User
13
10
  from udata.api.oauth2 import OAuth2Client
11
+ from udata.commands import cli, exit_with_error, success
12
+ from udata.models import User
14
13
 
15
14
  log = logging.getLogger(__name__)
16
15
 
17
16
 
18
- @cli.group('api')
17
+ @cli.group("api")
19
18
  def grp():
20
- '''API related operations'''
19
+ """API related operations"""
21
20
 
22
21
 
23
22
  def json_to_file(data, filename, pretty=False):
24
- '''Dump JSON data to a file'''
23
+ """Dump JSON data to a file"""
25
24
  kwargs = dict(indent=4) if pretty else {}
26
25
  dirname = os.path.dirname(filename)
27
26
  if not os.path.exists(dirname):
28
27
  os.makedirs(dirname)
29
28
  dump = json.dumps(api.__schema__, **kwargs)
30
- with open(filename, 'wb') as f:
31
- f.write(dump.encode('utf-8'))
29
+ with open(filename, "wb") as f:
30
+ f.write(dump.encode("utf-8"))
32
31
 
33
32
 
34
33
  @grp.command()
35
- @click.argument('filename')
36
- @click.option('-p', '--pretty', is_flag=True, help='Pretty print')
34
+ @click.argument("filename")
35
+ @click.option("-p", "--pretty", is_flag=True, help="Pretty print")
37
36
  def swagger(filename, pretty):
38
- '''Dump the swagger specifications'''
37
+ """Dump the swagger specifications"""
39
38
  json_to_file(api.__schema__, filename, pretty)
40
39
 
41
40
 
42
41
  @grp.command()
43
- @click.argument('filename')
44
- @click.option('-p', '--pretty', is_flag=True, help='Pretty print')
45
- @click.option('-u', '--urlvars', is_flag=True, help='Export query strings')
46
- @click.option('-s', '--swagger', is_flag=True,
47
- help='Export Swagger specifications')
42
+ @click.argument("filename")
43
+ @click.option("-p", "--pretty", is_flag=True, help="Pretty print")
44
+ @click.option("-u", "--urlvars", is_flag=True, help="Export query strings")
45
+ @click.option("-s", "--swagger", is_flag=True, help="Export Swagger specifications")
48
46
  def postman(filename, pretty, urlvars, swagger):
49
- '''Dump the API as a Postman collection'''
47
+ """Dump the API as a Postman collection"""
50
48
  data = api.as_postman(urlvars=urlvars, swagger=swagger)
51
49
  json_to_file(data, filename, pretty)
52
50
 
53
51
 
54
52
  @grp.command()
55
53
  def validate():
56
- '''Validate the Swagger/OpenAPI specification with your config'''
54
+ """Validate the Swagger/OpenAPI specification with your config"""
57
55
  with current_app.test_request_context():
58
56
  schema = json.loads(json.dumps(api.__schema__))
59
57
  try:
60
58
  schemas.validate(schema)
61
- success('API specifications are valid')
59
+ success("API specifications are valid")
62
60
  except schemas.SchemaValidationError as e:
63
- exit_with_error('API specifications are not valid', e)
61
+ exit_with_error("API specifications are not valid", e)
64
62
 
65
63
 
66
64
  @grp.command()
67
- @click.option('-n', '--client-name', default='client-01', help='Client\'s name')
68
- @click.option('-u', '--user-email', help='User\'s email')
69
- @click.option('--uri', multiple=True, default=['http://localhost:8080/login'], help='Client\'s redirect uri')
70
- @click.option('-g', '--grant-types', multiple=True, default=['authorization_code'], help='Client\'s grant types')
71
- @click.option('-s', '--scope', default='default', help='Client\'s scope')
72
- @click.option('-r', '--response-types', multiple=True, default=['code'], help='Client\'s response types')
65
+ @click.option("-n", "--client-name", default="client-01", help="Client's name")
66
+ @click.option("-u", "--user-email", help="User's email")
67
+ @click.option(
68
+ "--uri", multiple=True, default=["http://localhost:8080/login"], help="Client's redirect uri"
69
+ )
70
+ @click.option(
71
+ "-g",
72
+ "--grant-types",
73
+ multiple=True,
74
+ default=["authorization_code"],
75
+ help="Client's grant types",
76
+ )
77
+ @click.option("-s", "--scope", default="default", help="Client's scope")
78
+ @click.option(
79
+ "-r", "--response-types", multiple=True, default=["code"], help="Client's response types"
80
+ )
73
81
  def create_oauth_client(client_name, user_email, uri, grant_types, scope, response_types):
74
- '''Creates an OAuth2Client instance in DB'''
82
+ """Creates an OAuth2Client instance in DB"""
75
83
  user = User.objects(email=user_email).first()
76
84
  if user is None:
77
- exit_with_error('No matching user to email')
85
+ exit_with_error("No matching user to email")
78
86
 
79
87
  client = OAuth2Client.objects.create(
80
88
  name=client_name,
@@ -82,12 +90,12 @@ def create_oauth_client(client_name, user_email, uri, grant_types, scope, respon
82
90
  grant_types=grant_types,
83
91
  scope=scope,
84
92
  response_types=response_types,
85
- redirect_uris=uri
93
+ redirect_uris=uri,
86
94
  )
87
95
 
88
- click.echo(f'New OAuth client: {client.name}')
89
- click.echo(f'Client\'s ID {client.id}')
90
- click.echo(f'Client\'s secret {client.secret}')
91
- click.echo(f'Client\'s grant_types {client.grant_types}')
92
- click.echo(f'Client\'s response_types {client.response_types}')
93
- click.echo(f'Client\'s URI {client.redirect_uris}')
96
+ click.echo(f"New OAuth client: {client.name}")
97
+ click.echo(f"Client's ID {client.id}")
98
+ click.echo(f"Client's secret {client.secret}")
99
+ click.echo(f"Client's grant_types {client.grant_types}")
100
+ click.echo(f"Client's response_types {client.response_types}")
101
+ click.echo(f"Client's URI {client.redirect_uris}")
udata/api/errors.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from udata.i18n import gettext as _
2
2
 
3
-
4
- VALIDATION_ERROR = _('Validation error: your data cannot be updated for '
5
- 'now, we have been notified of the error and we will '
6
- 'fix it as soon as possible.')
3
+ VALIDATION_ERROR = _(
4
+ "Validation error: your data cannot be updated for "
5
+ "now, we have been notified of the error and we will "
6
+ "fix it as soon as possible."
7
+ )