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_fields.py CHANGED
@@ -1,14 +1,15 @@
1
- from udata.api import api
2
1
  import flask_restx.fields as restx_fields
3
- import udata.api.fields as custom_restx_fields
4
- from bson import ObjectId
5
2
  import mongoengine
6
3
  import mongoengine.fields as mongo_fields
4
+ from bson import ObjectId
7
5
 
6
+ import udata.api.fields as custom_restx_fields
7
+ from udata.api import api
8
8
  from udata.mongo.errors import FieldValidationError
9
9
 
10
- def convert_db_to_field(key, field, info = {}):
11
- '''
10
+
11
+ def convert_db_to_field(key, field, info={}):
12
+ """
12
13
  This function maps a Mongo field to a Flask RestX field.
13
14
  Most of the types are a simple 1-to-1 mapping except lists and references that requires
14
15
  more work.
@@ -17,11 +18,11 @@ def convert_db_to_field(key, field, info = {}):
17
18
  In the first part of the function we save the RestX constructor as a lambda because we need to call it with the
18
19
  params. Since merging the params involve a litte bit of work (merging default params with read/write params and then with
19
20
  user-supplied overrides, setting the readonly flag…), it's easier to have do this one time at the end of the function.
20
- '''
21
- info = { **getattr(field, '__additional_field_info__', {}), **info }
21
+ """
22
+ info = {**getattr(field, "__additional_field_info__", {}), **info}
22
23
 
23
24
  params = {}
24
- params['required'] = field.required
25
+ params["required"] = field.required
25
26
 
26
27
  read_params = {}
27
28
  write_params = {}
@@ -30,21 +31,21 @@ def convert_db_to_field(key, field, info = {}):
30
31
  constructor_read = None
31
32
  constructor_write = None
32
33
 
33
- if info.get('convert_to'):
34
+ if info.get("convert_to"):
34
35
  # TODO: this is currently never used. We may remove it if the auto-conversion
35
36
  # is always good enough.
36
- return info.get('convert_to'), info.get('convert_to')
37
+ return info.get("convert_to"), info.get("convert_to")
37
38
  elif isinstance(field, mongo_fields.StringField):
38
39
  constructor = restx_fields.String
39
- params['min_length'] = field.min_length
40
- params['max_length'] = field.max_length
41
- params['enum'] = field.choices
40
+ params["min_length"] = field.min_length
41
+ params["max_length"] = field.max_length
42
+ params["enum"] = field.choices
42
43
  elif isinstance(field, mongo_fields.ObjectIdField):
43
44
  constructor = restx_fields.String
44
45
  elif isinstance(field, mongo_fields.FloatField):
45
46
  constructor = restx_fields.Float
46
- params['min'] = field.min # TODO min_value?
47
- params['max'] = field.max
47
+ params["min"] = field.min # TODO min_value?
48
+ params["max"] = field.max
48
49
  elif isinstance(field, mongo_fields.BooleanField):
49
50
  constructor = restx_fields.Boolean
50
51
  elif isinstance(field, mongo_fields.DateTimeField):
@@ -54,7 +55,9 @@ def convert_db_to_field(key, field, info = {}):
54
55
  elif isinstance(field, mongo_fields.ListField):
55
56
  # For lists, we convert the inner value from Mongo to RestX then we create
56
57
  # the `List` RestX type with this converted inner value.
57
- field_read, field_write = convert_db_to_field(f"{key}.inner", field.field, info.get('inner_field_info', {}))
58
+ field_read, field_write = convert_db_to_field(
59
+ f"{key}.inner", field.field, info.get("inner_field_info", {})
60
+ )
58
61
  constructor_read = lambda **kwargs: restx_fields.List(field_read, **kwargs)
59
62
  constructor_write = lambda **kwargs: restx_fields.List(field_write, **kwargs)
60
63
  elif isinstance(field, mongo_fields.ReferenceField):
@@ -62,69 +65,83 @@ def convert_db_to_field(key, field, info = {}):
62
65
  # For reading, if the user supplied a `nested_fields` (RestX model), we use it to convert
63
66
  # the referenced model, if not we return a String (and RestX will call the `str()` of the model
64
67
  # when returning from an endpoint)
65
- nested_fields = info.get('nested_fields')
68
+ nested_fields = info.get("nested_fields")
66
69
  if nested_fields is None:
67
70
  # If there is no `nested_fields` convert the object to the string representation.
68
71
  constructor_read = restx_fields.String
69
72
  else:
70
73
  constructor_read = lambda **kwargs: restx_fields.Nested(nested_fields, **kwargs)
71
74
 
72
- write_params['description'] = "ID of the reference"
75
+ write_params["description"] = "ID of the reference"
73
76
  constructor_write = restx_fields.String
74
77
  elif isinstance(field, mongo_fields.EmbeddedDocumentField):
75
- nested_fields = info.get('nested_fields')
78
+ nested_fields = info.get("nested_fields")
76
79
  if nested_fields is not None:
77
80
  constructor = lambda **kwargs: restx_fields.Nested(nested_fields, **kwargs)
78
- elif hasattr(field.document_type_obj, '__read_fields__'):
79
- constructor_read = lambda **kwargs: restx_fields.Nested(field.document_type_obj.__read_fields__, **kwargs)
80
- constructor_write = lambda **kwargs: restx_fields.Nested(field.document_type_obj.__write_fields__, **kwargs)
81
+ elif hasattr(field.document_type_obj, "__read_fields__"):
82
+ constructor_read = lambda **kwargs: restx_fields.Nested(
83
+ field.document_type_obj.__read_fields__, **kwargs
84
+ )
85
+ constructor_write = lambda **kwargs: restx_fields.Nested(
86
+ field.document_type_obj.__write_fields__, **kwargs
87
+ )
81
88
  else:
82
- raise ValueError(f"EmbeddedDocumentField `{key}` requires a `nested_fields` param to serialize/deserialize or a `@generate_fields()` definition.")
89
+ raise ValueError(
90
+ f"EmbeddedDocumentField `{key}` requires a `nested_fields` param to serialize/deserialize or a `@generate_fields()` definition."
91
+ )
83
92
 
84
93
  else:
85
94
  raise ValueError(f"Unsupported MongoEngine field type {field.__class__.__name__}")
86
-
95
+
87
96
  read_params = {**params, **read_params, **info}
88
97
  write_params = {**params, **write_params, **info}
89
98
 
90
99
  read = constructor_read(**read_params) if constructor_read else constructor(**read_params)
91
- if write_params.get('readonly', False):
100
+ if write_params.get("readonly", False):
92
101
  write = None
93
102
  else:
94
- write = constructor_write(**write_params) if constructor_write else constructor(**write_params)
103
+ write = (
104
+ constructor_write(**write_params) if constructor_write else constructor(**write_params)
105
+ )
95
106
  return read, write
96
107
 
108
+
97
109
  def generate_fields(**kwargs):
98
- '''
110
+ """
99
111
  This decorator will create two auto-generated attributes on the class `__read_fields__` and `__write_fields__`
100
112
  that can be used in API endpoint inside `expect()` and `marshall_with()`.
101
- '''
113
+ """
114
+
102
115
  def wrapper(cls):
103
116
  read_fields = {}
104
117
  write_fields = {}
105
118
  sortables = []
106
119
  filterables = []
107
120
 
108
- read_fields['id'] = restx_fields.String(required=True)
121
+ read_fields["id"] = restx_fields.String(required=True)
109
122
 
110
123
  for key, field in cls._fields.items():
111
- info = getattr(field, '__additional_field_info__', None)
112
- if info is None: continue
124
+ info = getattr(field, "__additional_field_info__", None)
125
+ if info is None:
126
+ continue
113
127
 
114
- if info.get('sortable', False):
128
+ if info.get("sortable", False):
115
129
  sortables.append(key)
116
130
 
117
- filterable = info.get('filterable', None)
131
+ filterable = info.get("filterable", None)
118
132
  if filterable is not None:
119
- if 'key' not in filterable:
120
- filterable['key'] = key
121
- if 'column' not in filterable:
122
- filterable['column'] = key
123
-
124
- if 'constraints' not in filterable:
125
- filterable['constraints'] = []
126
- if isinstance(field, mongo_fields.ReferenceField) or (isinstance(field, mongo_fields.ListField) and isinstance(field.field, mongo_fields.ReferenceField)):
127
- filterable['constraints'].append('objectid')
133
+ if "key" not in filterable:
134
+ filterable["key"] = key
135
+ if "column" not in filterable:
136
+ filterable["column"] = key
137
+
138
+ if "constraints" not in filterable:
139
+ filterable["constraints"] = []
140
+ if isinstance(field, mongo_fields.ReferenceField) or (
141
+ isinstance(field, mongo_fields.ListField)
142
+ and isinstance(field.field, mongo_fields.ReferenceField)
143
+ ):
144
+ filterable["constraints"].append("objectid")
128
145
 
129
146
  # We may add more information later here:
130
147
  # - type of mongo query to execute (right now only simple =)
@@ -139,78 +156,105 @@ def generate_fields(**kwargs):
139
156
  write_fields[key] = write
140
157
 
141
158
  # The goal of this loop is to fetch all functions (getters) of the class
142
- # If a function has an `__additional_field_info__` attribute it means
159
+ # If a function has an `__additional_field_info__` attribute it means
143
160
  # it has been decorated with `@function_field()` and should be included
144
161
  # in the API response.
145
162
  for method_name in dir(cls):
146
- if method_name == 'objects': continue
147
- if method_name.startswith('_'): continue
148
- if method_name in read_fields: continue # Do not override if the attribute is also callable like for Extras
163
+ if method_name == "objects":
164
+ continue
165
+ if method_name.startswith("_"):
166
+ continue
167
+ if method_name in read_fields:
168
+ continue # Do not override if the attribute is also callable like for Extras
149
169
 
150
170
  method = getattr(cls, method_name)
151
- if not callable(method): continue
171
+ if not callable(method):
172
+ continue
152
173
 
153
- info = getattr(method, '__additional_field_info__', None)
154
- if info is None: continue
174
+ info = getattr(method, "__additional_field_info__", None)
175
+ if info is None:
176
+ continue
155
177
 
156
178
  def make_lambda(method):
157
- '''
179
+ """
158
180
  Factory function to create a lambda with the correct scope.
159
- If we don't have this factory function, the `method` will be the
181
+ If we don't have this factory function, the `method` will be the
160
182
  last method assigned in this loop?
161
- '''
183
+ """
162
184
  return lambda o: method(o)
163
185
 
164
- read_fields[method_name] = restx_fields.String(attribute=make_lambda(method), **{ 'readonly':True, **info })
165
-
186
+ read_fields[method_name] = restx_fields.String(
187
+ attribute=make_lambda(method), **{"readonly": True, **info}
188
+ )
166
189
 
167
190
  cls.__read_fields__ = api.model(f"{cls.__name__} (read)", read_fields, **kwargs)
168
191
  cls.__write_fields__ = api.model(f"{cls.__name__} (write)", write_fields, **kwargs)
169
192
 
170
- mask = kwargs.pop('mask', None)
193
+ mask = kwargs.pop("mask", None)
171
194
  if mask is not None:
172
- mask = 'data{{{0}}},*'.format(mask)
173
- cls.__page_fields__ = api.model(f"{cls.__name__}Page", custom_restx_fields.pager(cls.__read_fields__), mask=mask, **kwargs)
195
+ mask = "data{{{0}}},*".format(mask)
196
+ cls.__page_fields__ = api.model(
197
+ f"{cls.__name__}Page",
198
+ custom_restx_fields.pager(cls.__read_fields__),
199
+ mask=mask,
200
+ **kwargs,
201
+ )
174
202
 
175
203
  # Parser for index sort/filters
176
- paginable = kwargs.get('paginable', True)
204
+ paginable = kwargs.get("paginable", True)
177
205
  parser = api.parser()
178
206
 
179
207
  if paginable:
180
- parser.add_argument('page', type=int, location='args', default=1, help='The page to display')
181
- parser.add_argument('page_size', type=int, location='args', default=20, help='The page size')
182
-
208
+ parser.add_argument(
209
+ "page", type=int, location="args", default=1, help="The page to display"
210
+ )
211
+ parser.add_argument(
212
+ "page_size", type=int, location="args", default=20, help="The page size"
213
+ )
214
+
183
215
  if sortables:
184
- choices = sortables + ['-' + k for k in sortables]
185
- parser.add_argument('sort', type=str, location='args', choices=choices, help='The field (and direction) on which sorting apply')
216
+ choices = sortables + ["-" + k for k in sortables]
217
+ parser.add_argument(
218
+ "sort",
219
+ type=str,
220
+ location="args",
221
+ choices=choices,
222
+ help="The field (and direction) on which sorting apply",
223
+ )
186
224
 
187
225
  for filterable in filterables:
188
- parser.add_argument(filterable['key'], type=str, location='args')
226
+ parser.add_argument(filterable["key"], type=str, location="args")
189
227
 
190
228
  cls.__index_parser__ = parser
229
+
191
230
  def apply_sort_filters_and_pagination(base_query):
192
231
  args = cls.__index_parser__.parse_args()
193
232
 
194
- if sortables and args['sort']:
195
- base_query = base_query.order_by(args['sort'])
233
+ if sortables and args["sort"]:
234
+ base_query = base_query.order_by(args["sort"])
196
235
 
197
236
  for filterable in filterables:
198
- if args.get(filterable['key']):
199
- for constraint in filterable['constraints']:
200
- if constraint == 'objectid' and not ObjectId.is_valid(args[filterable['key']]):
237
+ if args.get(filterable["key"]):
238
+ for constraint in filterable["constraints"]:
239
+ if constraint == "objectid" and not ObjectId.is_valid(
240
+ args[filterable["key"]]
241
+ ):
201
242
  api.abort(400, f'`{filterable["key"]}` must be an identifier')
202
243
 
203
- base_query = base_query.filter(**{
204
- filterable['column']: args[filterable['key']],
205
- })
244
+ base_query = base_query.filter(
245
+ **{
246
+ filterable["column"]: args[filterable["key"]],
247
+ }
248
+ )
206
249
 
207
250
  if paginable:
208
- base_query = base_query.paginate(args['page'], args['page_size'])
251
+ base_query = base_query.paginate(args["page"], args["page_size"])
209
252
 
210
253
  return base_query
211
254
 
212
255
  cls.apply_sort_filters_and_pagination = apply_sort_filters_and_pagination
213
256
  return cls
257
+
214
258
  return wrapper
215
259
 
216
260
 
@@ -221,50 +265,53 @@ def function_field(**info):
221
265
 
222
266
  return inner
223
267
 
268
+
224
269
  def field(inner, **kwargs):
225
- '''
270
+ """
226
271
  Simple decorator to mark a field as visible for the API fields.
227
272
  We can pass additional arguments that will be forward to the RestX field constructor.
228
- '''
273
+ """
229
274
  inner.__additional_field_info__ = kwargs
230
275
  return inner
231
276
 
232
277
 
233
- def patch(obj, request):
234
- '''
278
+ def patch(obj, request):
279
+ """
235
280
  Patch the object with the data from the request.
236
281
  Only fields decorated with the `field()` decorator will be read (and not readonly).
237
- '''
282
+ """
238
283
  for key, value in request.json.items():
239
284
  field = obj.__write_fields__.get(key)
240
285
  if field is not None and not field.readonly:
241
286
  model_attribute = getattr(obj.__class__, key)
242
- if isinstance(model_attribute, mongoengine.fields.ListField) and isinstance(model_attribute.field, mongoengine.fields.ReferenceField):
287
+ if isinstance(model_attribute, mongoengine.fields.ListField) and isinstance(
288
+ model_attribute.field, mongoengine.fields.ReferenceField
289
+ ):
243
290
  # TODO `wrap_primary_key` do Mongo request, do a first pass to fetch all documents before calling it (to avoid multiple queries).
244
291
  value = [wrap_primary_key(key, model_attribute.field, id) for id in value]
245
292
  if isinstance(model_attribute, mongoengine.fields.ReferenceField):
246
293
  value = wrap_primary_key(key, model_attribute, value)
247
294
 
248
-
249
- info = getattr(model_attribute, '__additional_field_info__', {})
295
+ info = getattr(model_attribute, "__additional_field_info__", {})
250
296
 
251
297
  # `check` field attribute allows to do validation from the request before setting
252
298
  # the attribute
253
- check = info.get('check', None)
299
+ check = info.get("check", None)
254
300
  if check is not None:
255
- check(**{key: value}) # TODO add other model attributes in function parameters
301
+ check(**{key: value}) # TODO add other model attributes in function parameters
256
302
 
257
303
  setattr(obj, key, value)
258
304
 
259
305
  return obj
260
306
 
307
+
261
308
  def wrap_primary_key(field_name: str, foreign_field: mongoengine.fields.ReferenceField, value: str):
262
- '''
263
- We need to wrap the `String` inside an `ObjectId` most of the time. If the foreign ID is a `String` we need to get
309
+ """
310
+ We need to wrap the `String` inside an `ObjectId` most of the time. If the foreign ID is a `String` we need to get
264
311
  a `DBRef` from the database.
265
312
 
266
313
  TODO: we only check the document reference if the ID is a `String` field (not in the case of a classic `ObjectId`).
267
- '''
314
+ """
268
315
  document_type = foreign_field.document_type()
269
316
  id_field_name = document_type.__class__._meta["id_field"]
270
317
 
@@ -275,7 +322,7 @@ def wrap_primary_key(field_name: str, foreign_field: mongoengine.fields.Referenc
275
322
  foreign_document = document_type.__class__.objects(**{id_field_name: value}).first()
276
323
  if foreign_document is None:
277
324
  raise FieldValidationError(field=field_name, message=f"Unknown reference '{value}'")
278
-
325
+
279
326
  if isinstance(id_field, mongoengine.fields.ObjectIdField):
280
327
  return ObjectId(value)
281
328
  elif isinstance(id_field, mongoengine.fields.StringField):
@@ -288,5 +335,6 @@ def wrap_primary_key(field_name: str, foreign_field: mongoengine.fields.Referenc
288
335
  # … but it may be important to check before-hand that the reference point to a correct document.
289
336
  return foreign_document.to_dbref()
290
337
  else:
291
- raise ValueError(f"Unknown ID field type {id_field.__class__} for {document_type.__class__} (ID field name is {id_field_name}, value was {value})")
292
-
338
+ raise ValueError(
339
+ f"Unknown ID field type {id_field.__class__} for {document_type.__class__} (ID field name is {id_field_name}, value was {value})"
340
+ )
udata/app.py CHANGED
@@ -1,26 +1,21 @@
1
- import bson
2
1
  import datetime
2
+ import importlib
3
3
  import logging
4
4
  import os
5
- import importlib
6
5
  import types
6
+ from os.path import abspath, dirname, exists, isfile, join
7
7
 
8
- from os.path import abspath, join, dirname, isfile, exists
9
-
10
- from flask import (
11
- Flask, abort, g, send_from_directory, json, Blueprint as BaseBlueprint,
12
- make_response
13
- )
8
+ import bson
9
+ from flask import Blueprint as BaseBlueprint
10
+ from flask import Flask, abort, g, json, make_response, send_from_directory
14
11
  from flask_caching import Cache
15
-
16
12
  from flask_wtf.csrf import CSRFProtect
17
13
  from speaklater import is_lazy_string
18
14
  from werkzeug.middleware.proxy_fix import ProxyFix
19
15
 
20
16
  from udata import entrypoints
21
17
 
22
-
23
- APP_NAME = __name__.split('.')[0]
18
+ APP_NAME = __name__.split(".")[0]
24
19
  ROOT_DIR = abspath(join(dirname(__file__)))
25
20
 
26
21
  log = logging.getLogger(__name__)
@@ -32,81 +27,78 @@ csrf = CSRFProtect()
32
27
  def send_static(directory, filename, cache_timeout):
33
28
  out = send_from_directory(directory, filename, cache_timeout=cache_timeout)
34
29
  response = make_response(out)
35
- response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
36
- response.headers['Access-Control-Allow-Origin'] = '*'
30
+ response.headers["Access-Control-Allow-Methods"] = "GET, OPTIONS"
31
+ response.headers["Access-Control-Allow-Origin"] = "*"
37
32
  return response
38
33
 
39
34
 
40
35
  class UDataApp(Flask):
41
- debug_log_format = '[%(levelname)s][%(name)s:%(lineno)d] %(message)s'
36
+ debug_log_format = "[%(levelname)s][%(name)s:%(lineno)d] %(message)s"
42
37
 
43
38
  # Keep track of static dirs given as register_blueprint argument
44
39
  static_prefixes = {}
45
40
 
46
41
  def send_static_file(self, filename):
47
- '''
42
+ """
48
43
  Override default static handling:
49
44
  - raises 404 if not debug
50
45
  - handle static aliases
51
- '''
46
+ """
52
47
  if not self.debug:
53
- self.logger.error('Static files are only served in debug')
48
+ self.logger.error("Static files are only served in debug")
54
49
  abort(404)
55
50
 
56
51
  cache_timeout = self.get_send_file_max_age(filename)
57
52
 
58
53
  # Default behavior
59
54
  if isfile(join(self.static_folder, filename)):
60
- return send_static(self.static_folder, filename,
61
- cache_timeout=cache_timeout)
55
+ return send_static(self.static_folder, filename, cache_timeout=cache_timeout)
62
56
 
63
57
  # Handle aliases
64
- for prefix, directory in self.config.get('STATIC_DIRS', tuple()):
58
+ for prefix, directory in self.config.get("STATIC_DIRS", tuple()):
65
59
  if filename.startswith(prefix):
66
- real_filename = filename[len(prefix):]
67
- if real_filename.startswith('/'):
60
+ real_filename = filename[len(prefix) :]
61
+ if real_filename.startswith("/"):
68
62
  real_filename = real_filename[1:]
69
63
  if isfile(join(directory, real_filename)):
70
- return send_static(directory, real_filename,
71
- cache_timeout=cache_timeout)
64
+ return send_static(directory, real_filename, cache_timeout=cache_timeout)
72
65
  abort(404)
73
66
 
74
67
  def handle_http_exception(self, e):
75
68
  # Make exception/HTTPError available for context processors
76
- if 'error' not in g:
69
+ if "error" not in g:
77
70
  g.error = e
78
71
  return super(UDataApp, self).handle_http_exception(e)
79
72
 
80
73
  def register_blueprint(self, blueprint, **kwargs):
81
-
82
74
  if blueprint.name in self.blueprints:
83
75
  # TODO: remove this warning and let Flask return a ValueError once
84
76
  # we can set a custom blueprint name in Flask-storage
85
- self.logger.warning('Blueprint already loaded')
77
+ self.logger.warning("Blueprint already loaded")
86
78
  return self.blueprints[blueprint.name]
87
79
 
88
- if blueprint.has_static_folder and 'url_prefix' in kwargs:
89
- self.static_prefixes[blueprint.name] = kwargs['url_prefix']
80
+ if blueprint.has_static_folder and "url_prefix" in kwargs:
81
+ self.static_prefixes[blueprint.name] = kwargs["url_prefix"]
90
82
  return super(UDataApp, self).register_blueprint(blueprint, **kwargs)
91
83
 
92
84
 
93
85
  class Blueprint(BaseBlueprint):
94
- '''A blueprint allowing to decorate class too'''
86
+ """A blueprint allowing to decorate class too"""
87
+
95
88
  def route(self, rule, **options):
96
89
  def wrapper(func_or_cls):
97
- endpoint = str(options.pop('endpoint', func_or_cls.__name__))
90
+ endpoint = str(options.pop("endpoint", func_or_cls.__name__))
98
91
  if isinstance(func_or_cls, types.FunctionType):
99
92
  self.add_url_rule(rule, endpoint, func_or_cls, **options)
100
93
  else:
101
- self.add_url_rule(rule,
102
- view_func=func_or_cls.as_view(endpoint),
103
- **options)
94
+ self.add_url_rule(rule, view_func=func_or_cls.as_view(endpoint), **options)
104
95
  return func_or_cls
96
+
105
97
  return wrapper
106
98
 
107
99
 
108
100
  class UDataJsonEncoder(json.JSONEncoder):
109
- '''
101
+ """
110
102
  A JSONEncoder subclass to encode unsupported types:
111
103
 
112
104
  - ObjectId
@@ -115,7 +107,8 @@ class UDataJsonEncoder(json.JSONEncoder):
115
107
 
116
108
  Handle special serialize() method and _data attribute.
117
109
  Ensure an app context is always present.
118
- '''
110
+ """
111
+
119
112
  def default(self, obj):
120
113
  if is_lazy_string(obj):
121
114
  return str(obj)
@@ -123,12 +116,12 @@ class UDataJsonEncoder(json.JSONEncoder):
123
116
  return str(obj)
124
117
  elif isinstance(obj, datetime.datetime):
125
118
  return obj.isoformat()
126
- elif hasattr(obj, 'to_dict'):
119
+ elif hasattr(obj, "to_dict"):
127
120
  return obj.to_dict()
128
- elif hasattr(obj, 'serialize'):
121
+ elif hasattr(obj, "serialize"):
129
122
  return obj.serialize()
130
123
  # Serialize Raw data for Document and EmbeddedDocument.
131
- elif hasattr(obj, '_data'):
124
+ elif hasattr(obj, "_data"):
132
125
  return obj._data
133
126
  return super(UDataJsonEncoder, self).default(obj)
134
127
 
@@ -136,12 +129,12 @@ class UDataJsonEncoder(json.JSONEncoder):
136
129
  # These loggers are very verbose
137
130
  # We need to put them in WARNING level
138
131
  # even if the main level is INFO or DEBUG
139
- VERBOSE_LOGGERS = 'requests',
132
+ VERBOSE_LOGGERS = ("requests",)
140
133
 
141
134
 
142
135
  def init_logging(app):
143
136
  logging.captureWarnings(True) # Display warnings
144
- debug = app.debug or app.config.get('TESTING')
137
+ debug = app.debug or app.config.get("TESTING")
145
138
  log_level = logging.DEBUG if debug else logging.WARNING
146
139
  app.logger.setLevel(log_level)
147
140
  for name in entrypoints.get_roots(): # Entrypoints loggers
@@ -151,13 +144,12 @@ def init_logging(app):
151
144
  return app
152
145
 
153
146
 
154
- def create_app(config='udata.settings.Defaults', override=None,
155
- init_logging=init_logging):
156
- '''Factory for a minimal application'''
147
+ def create_app(config="udata.settings.Defaults", override=None, init_logging=init_logging):
148
+ """Factory for a minimal application"""
157
149
  app = UDataApp(APP_NAME)
158
150
  app.config.from_object(config)
159
151
 
160
- settings = os.environ.get('UDATA_SETTINGS', join(os.getcwd(), 'udata.cfg'))
152
+ settings = os.environ.get("UDATA_SETTINGS", join(os.getcwd(), "udata.cfg"))
161
153
  if exists(settings):
162
154
  app.settings_file = settings # Keep track of loaded settings for diagnostic
163
155
  app.config.from_pyfile(settings)
@@ -167,24 +159,24 @@ def create_app(config='udata.settings.Defaults', override=None,
167
159
 
168
160
  # Loads defaults from plugins
169
161
  for pkg in entrypoints.get_roots(app):
170
- if pkg == 'udata':
162
+ if pkg == "udata":
171
163
  continue # Defaults are already loaded
172
- module = '{}.settings'.format(pkg)
164
+ module = "{}.settings".format(pkg)
173
165
  try:
174
166
  settings = importlib.import_module(module)
175
167
  except ImportError:
176
168
  continue
177
169
  for key, default in settings.__dict__.items():
178
- if key.startswith('__'):
170
+ if key.startswith("__"):
179
171
  continue
180
172
  app.config.setdefault(key, default)
181
173
 
182
174
  app.json_encoder = UDataJsonEncoder
183
175
 
184
176
  # `ujson` doesn't support `cls` parameter https://github.com/ultrajson/ultrajson/issues/124
185
- app.config['RESTX_JSON'] = { 'cls': UDataJsonEncoder }
177
+ app.config["RESTX_JSON"] = {"cls": UDataJsonEncoder}
186
178
 
187
- app.debug = app.config['DEBUG'] and not app.config['TESTING']
179
+ app.debug = app.config["DEBUG"] and not app.config["TESTING"]
188
180
 
189
181
  app.wsgi_app = ProxyFix(app.wsgi_app)
190
182
 
@@ -195,7 +187,7 @@ def create_app(config='udata.settings.Defaults', override=None,
195
187
 
196
188
 
197
189
  def standalone(app):
198
- '''Factory for an all in one application'''
190
+ """Factory for an all in one application"""
199
191
  from udata import api, core, frontend
200
192
 
201
193
  core.init_app(app)
@@ -209,9 +201,19 @@ def standalone(app):
209
201
 
210
202
  def register_extensions(app):
211
203
  from udata import (
212
- models, mongo, routing, tasks, mail, i18n, auth, search, sitemap,
213
- sentry, notifications
204
+ auth,
205
+ i18n,
206
+ mail,
207
+ models,
208
+ mongo,
209
+ notifications,
210
+ routing,
211
+ search,
212
+ sentry,
213
+ sitemap,
214
+ tasks,
214
215
  )
216
+
215
217
  tasks.init_app(app)
216
218
  i18n.init_app(app)
217
219
  mongo.init_app(app)
@@ -232,5 +234,5 @@ def register_features(app):
232
234
 
233
235
  notifications.init_app(app)
234
236
 
235
- for ep in entrypoints.get_enabled('udata.plugins', app).values():
237
+ for ep in entrypoints.get_enabled("udata.plugins", app).values():
236
238
  ep.init_app(app)