udata 9.1.2.dev30355__py2.py3-none-any.whl → 9.1.2.dev30454__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of udata might be problematic. Click here for more details.

Files changed (413) hide show
  1. tasks/__init__.py +109 -107
  2. tasks/helpers.py +18 -18
  3. udata/__init__.py +4 -4
  4. udata/admin/views.py +5 -5
  5. udata/api/__init__.py +111 -134
  6. udata/api/commands.py +45 -37
  7. udata/api/errors.py +5 -4
  8. udata/api/fields.py +23 -21
  9. udata/api/oauth2.py +55 -74
  10. udata/api/parsers.py +15 -15
  11. udata/api/signals.py +1 -1
  12. udata/api_fields.py +137 -89
  13. udata/app.py +58 -55
  14. udata/assets.py +5 -5
  15. udata/auth/__init__.py +37 -26
  16. udata/auth/forms.py +23 -15
  17. udata/auth/helpers.py +1 -1
  18. udata/auth/mails.py +3 -3
  19. udata/auth/password_validation.py +19 -15
  20. udata/auth/views.py +94 -68
  21. udata/commands/__init__.py +71 -69
  22. udata/commands/cache.py +7 -7
  23. udata/commands/db.py +201 -140
  24. udata/commands/dcat.py +36 -30
  25. udata/commands/fixtures.py +100 -84
  26. udata/commands/images.py +21 -20
  27. udata/commands/info.py +17 -20
  28. udata/commands/init.py +10 -10
  29. udata/commands/purge.py +12 -13
  30. udata/commands/serve.py +41 -29
  31. udata/commands/static.py +16 -18
  32. udata/commands/test.py +20 -20
  33. udata/commands/tests/fixtures.py +26 -24
  34. udata/commands/worker.py +31 -33
  35. udata/core/__init__.py +12 -12
  36. udata/core/activity/__init__.py +0 -1
  37. udata/core/activity/api.py +59 -49
  38. udata/core/activity/models.py +28 -26
  39. udata/core/activity/signals.py +1 -1
  40. udata/core/activity/tasks.py +16 -10
  41. udata/core/badges/api.py +6 -6
  42. udata/core/badges/commands.py +14 -13
  43. udata/core/badges/fields.py +8 -5
  44. udata/core/badges/forms.py +7 -4
  45. udata/core/badges/models.py +16 -31
  46. udata/core/badges/permissions.py +1 -3
  47. udata/core/badges/signals.py +2 -2
  48. udata/core/badges/tasks.py +3 -2
  49. udata/core/badges/tests/test_commands.py +10 -10
  50. udata/core/badges/tests/test_model.py +24 -31
  51. udata/core/contact_point/api.py +19 -18
  52. udata/core/contact_point/api_fields.py +21 -14
  53. udata/core/contact_point/factories.py +2 -2
  54. udata/core/contact_point/forms.py +7 -6
  55. udata/core/contact_point/models.py +3 -5
  56. udata/core/dataservices/api.py +26 -21
  57. udata/core/dataservices/factories.py +13 -11
  58. udata/core/dataservices/models.py +35 -40
  59. udata/core/dataservices/permissions.py +4 -4
  60. udata/core/dataservices/rdf.py +40 -17
  61. udata/core/dataservices/tasks.py +4 -3
  62. udata/core/dataset/actions.py +10 -10
  63. udata/core/dataset/activities.py +21 -23
  64. udata/core/dataset/api.py +321 -298
  65. udata/core/dataset/api_fields.py +443 -271
  66. udata/core/dataset/apiv2.py +305 -229
  67. udata/core/dataset/commands.py +38 -36
  68. udata/core/dataset/constants.py +61 -54
  69. udata/core/dataset/csv.py +70 -74
  70. udata/core/dataset/events.py +39 -32
  71. udata/core/dataset/exceptions.py +8 -4
  72. udata/core/dataset/factories.py +57 -65
  73. udata/core/dataset/forms.py +87 -63
  74. udata/core/dataset/models.py +336 -280
  75. udata/core/dataset/permissions.py +9 -6
  76. udata/core/dataset/preview.py +15 -17
  77. udata/core/dataset/rdf.py +156 -122
  78. udata/core/dataset/search.py +92 -77
  79. udata/core/dataset/signals.py +1 -1
  80. udata/core/dataset/tasks.py +63 -54
  81. udata/core/discussions/actions.py +5 -5
  82. udata/core/discussions/api.py +124 -120
  83. udata/core/discussions/factories.py +2 -2
  84. udata/core/discussions/forms.py +9 -7
  85. udata/core/discussions/metrics.py +1 -3
  86. udata/core/discussions/models.py +25 -24
  87. udata/core/discussions/notifications.py +18 -14
  88. udata/core/discussions/permissions.py +3 -3
  89. udata/core/discussions/signals.py +4 -4
  90. udata/core/discussions/tasks.py +24 -28
  91. udata/core/followers/api.py +32 -33
  92. udata/core/followers/models.py +9 -9
  93. udata/core/followers/signals.py +3 -3
  94. udata/core/jobs/actions.py +7 -7
  95. udata/core/jobs/api.py +99 -92
  96. udata/core/jobs/commands.py +48 -49
  97. udata/core/jobs/forms.py +11 -11
  98. udata/core/jobs/models.py +6 -6
  99. udata/core/metrics/__init__.py +2 -2
  100. udata/core/metrics/commands.py +34 -30
  101. udata/core/metrics/models.py +2 -4
  102. udata/core/metrics/signals.py +1 -1
  103. udata/core/metrics/tasks.py +3 -3
  104. udata/core/organization/activities.py +12 -15
  105. udata/core/organization/api.py +167 -174
  106. udata/core/organization/api_fields.py +183 -124
  107. udata/core/organization/apiv2.py +32 -32
  108. udata/core/organization/commands.py +20 -22
  109. udata/core/organization/constants.py +11 -11
  110. udata/core/organization/csv.py +17 -15
  111. udata/core/organization/factories.py +8 -11
  112. udata/core/organization/forms.py +32 -26
  113. udata/core/organization/metrics.py +2 -1
  114. udata/core/organization/models.py +87 -67
  115. udata/core/organization/notifications.py +18 -14
  116. udata/core/organization/permissions.py +10 -11
  117. udata/core/organization/rdf.py +14 -14
  118. udata/core/organization/search.py +30 -28
  119. udata/core/organization/signals.py +7 -7
  120. udata/core/organization/tasks.py +42 -61
  121. udata/core/owned.py +38 -27
  122. udata/core/post/api.py +82 -81
  123. udata/core/post/constants.py +8 -5
  124. udata/core/post/factories.py +4 -4
  125. udata/core/post/forms.py +13 -14
  126. udata/core/post/models.py +20 -22
  127. udata/core/post/tests/test_api.py +30 -32
  128. udata/core/reports/api.py +8 -7
  129. udata/core/reports/constants.py +1 -3
  130. udata/core/reports/models.py +10 -10
  131. udata/core/reuse/activities.py +15 -19
  132. udata/core/reuse/api.py +123 -126
  133. udata/core/reuse/api_fields.py +120 -85
  134. udata/core/reuse/apiv2.py +11 -10
  135. udata/core/reuse/constants.py +23 -23
  136. udata/core/reuse/csv.py +18 -18
  137. udata/core/reuse/factories.py +5 -9
  138. udata/core/reuse/forms.py +24 -21
  139. udata/core/reuse/models.py +55 -51
  140. udata/core/reuse/permissions.py +2 -2
  141. udata/core/reuse/search.py +49 -46
  142. udata/core/reuse/signals.py +1 -1
  143. udata/core/reuse/tasks.py +4 -5
  144. udata/core/site/api.py +47 -50
  145. udata/core/site/factories.py +2 -2
  146. udata/core/site/forms.py +4 -5
  147. udata/core/site/models.py +94 -63
  148. udata/core/site/rdf.py +14 -14
  149. udata/core/spam/api.py +16 -9
  150. udata/core/spam/constants.py +4 -4
  151. udata/core/spam/fields.py +13 -7
  152. udata/core/spam/models.py +27 -20
  153. udata/core/spam/signals.py +1 -1
  154. udata/core/spam/tests/test_spam.py +6 -5
  155. udata/core/spatial/api.py +72 -80
  156. udata/core/spatial/api_fields.py +73 -58
  157. udata/core/spatial/commands.py +67 -64
  158. udata/core/spatial/constants.py +3 -3
  159. udata/core/spatial/factories.py +37 -54
  160. udata/core/spatial/forms.py +27 -26
  161. udata/core/spatial/geoids.py +17 -17
  162. udata/core/spatial/models.py +43 -47
  163. udata/core/spatial/tasks.py +2 -1
  164. udata/core/spatial/tests/test_api.py +115 -130
  165. udata/core/spatial/tests/test_fields.py +74 -77
  166. udata/core/spatial/tests/test_geoid.py +22 -22
  167. udata/core/spatial/tests/test_models.py +5 -7
  168. udata/core/spatial/translations.py +16 -16
  169. udata/core/storages/__init__.py +16 -18
  170. udata/core/storages/api.py +66 -64
  171. udata/core/storages/tasks.py +7 -7
  172. udata/core/storages/utils.py +15 -15
  173. udata/core/storages/views.py +5 -6
  174. udata/core/tags/api.py +17 -14
  175. udata/core/tags/csv.py +4 -4
  176. udata/core/tags/models.py +8 -5
  177. udata/core/tags/tasks.py +11 -13
  178. udata/core/tags/views.py +4 -4
  179. udata/core/topic/api.py +84 -73
  180. udata/core/topic/apiv2.py +157 -127
  181. udata/core/topic/factories.py +3 -4
  182. udata/core/topic/forms.py +12 -14
  183. udata/core/topic/models.py +14 -19
  184. udata/core/topic/parsers.py +26 -26
  185. udata/core/user/activities.py +30 -29
  186. udata/core/user/api.py +151 -152
  187. udata/core/user/api_fields.py +132 -100
  188. udata/core/user/apiv2.py +7 -7
  189. udata/core/user/commands.py +38 -38
  190. udata/core/user/factories.py +8 -9
  191. udata/core/user/forms.py +14 -11
  192. udata/core/user/metrics.py +2 -2
  193. udata/core/user/models.py +68 -69
  194. udata/core/user/permissions.py +4 -5
  195. udata/core/user/rdf.py +7 -8
  196. udata/core/user/tasks.py +2 -2
  197. udata/core/user/tests/test_user_model.py +24 -16
  198. udata/cors.py +99 -0
  199. udata/db/tasks.py +2 -1
  200. udata/entrypoints.py +35 -31
  201. udata/errors.py +2 -1
  202. udata/event/values.py +6 -6
  203. udata/factories.py +2 -2
  204. udata/features/identicon/api.py +5 -6
  205. udata/features/identicon/backends.py +48 -55
  206. udata/features/identicon/tests/test_backends.py +4 -5
  207. udata/features/notifications/__init__.py +0 -1
  208. udata/features/notifications/actions.py +9 -9
  209. udata/features/notifications/api.py +17 -13
  210. udata/features/territories/__init__.py +12 -10
  211. udata/features/territories/api.py +14 -15
  212. udata/features/territories/models.py +23 -28
  213. udata/features/transfer/actions.py +8 -11
  214. udata/features/transfer/api.py +84 -77
  215. udata/features/transfer/factories.py +2 -1
  216. udata/features/transfer/models.py +11 -12
  217. udata/features/transfer/notifications.py +19 -15
  218. udata/features/transfer/permissions.py +5 -5
  219. udata/forms/__init__.py +5 -2
  220. udata/forms/fields.py +164 -172
  221. udata/forms/validators.py +19 -22
  222. udata/forms/widgets.py +9 -13
  223. udata/frontend/__init__.py +31 -26
  224. udata/frontend/csv.py +68 -58
  225. udata/frontend/markdown.py +40 -44
  226. udata/harvest/actions.py +89 -77
  227. udata/harvest/api.py +294 -238
  228. udata/harvest/backends/__init__.py +4 -4
  229. udata/harvest/backends/base.py +128 -111
  230. udata/harvest/backends/dcat.py +80 -66
  231. udata/harvest/commands.py +56 -60
  232. udata/harvest/csv.py +8 -8
  233. udata/harvest/exceptions.py +6 -3
  234. udata/harvest/filters.py +24 -23
  235. udata/harvest/forms.py +27 -28
  236. udata/harvest/models.py +88 -80
  237. udata/harvest/notifications.py +15 -10
  238. udata/harvest/signals.py +13 -13
  239. udata/harvest/tasks.py +11 -10
  240. udata/harvest/tests/factories.py +23 -24
  241. udata/harvest/tests/test_actions.py +136 -166
  242. udata/harvest/tests/test_api.py +220 -214
  243. udata/harvest/tests/test_base_backend.py +117 -112
  244. udata/harvest/tests/test_dcat_backend.py +380 -308
  245. udata/harvest/tests/test_filters.py +33 -22
  246. udata/harvest/tests/test_models.py +11 -14
  247. udata/harvest/tests/test_notifications.py +6 -7
  248. udata/harvest/tests/test_tasks.py +7 -6
  249. udata/i18n.py +237 -78
  250. udata/linkchecker/backends.py +5 -11
  251. udata/linkchecker/checker.py +23 -22
  252. udata/linkchecker/commands.py +4 -6
  253. udata/linkchecker/models.py +6 -6
  254. udata/linkchecker/tasks.py +18 -20
  255. udata/mail.py +21 -21
  256. udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
  257. udata/migrations/2020-08-24-add-fs-filename.py +9 -8
  258. udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
  259. udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
  260. udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
  261. udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
  262. udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
  263. udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
  264. udata/migrations/2021-08-17-follow-integrity.py +5 -4
  265. udata/migrations/2021-08-17-harvest-integrity.py +13 -12
  266. udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
  267. udata/migrations/2021-08-17-transfer-integrity.py +5 -4
  268. udata/migrations/2021-08-17-users-integrity.py +9 -8
  269. udata/migrations/2021-12-14-reuse-topics.py +7 -6
  270. udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
  271. udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
  272. udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
  273. udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
  274. udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
  275. udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
  276. udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
  277. udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
  278. udata/migrations/__init__.py +123 -105
  279. udata/models/__init__.py +4 -4
  280. udata/mongo/__init__.py +13 -11
  281. udata/mongo/badges_field.py +3 -2
  282. udata/mongo/datetime_fields.py +13 -12
  283. udata/mongo/document.py +17 -16
  284. udata/mongo/engine.py +15 -16
  285. udata/mongo/errors.py +2 -1
  286. udata/mongo/extras_fields.py +30 -20
  287. udata/mongo/queryset.py +12 -12
  288. udata/mongo/slug_fields.py +38 -28
  289. udata/mongo/taglist_field.py +1 -2
  290. udata/mongo/url_field.py +5 -5
  291. udata/mongo/uuid_fields.py +4 -3
  292. udata/notifications/__init__.py +1 -1
  293. udata/notifications/mattermost.py +10 -9
  294. udata/rdf.py +167 -188
  295. udata/routing.py +40 -45
  296. udata/search/__init__.py +18 -19
  297. udata/search/adapter.py +17 -16
  298. udata/search/commands.py +44 -51
  299. udata/search/fields.py +13 -20
  300. udata/search/query.py +23 -18
  301. udata/search/result.py +9 -10
  302. udata/sentry.py +21 -19
  303. udata/settings.py +262 -198
  304. udata/sitemap.py +8 -6
  305. udata/storage/s3.py +20 -13
  306. udata/tags.py +4 -5
  307. udata/tasks.py +43 -42
  308. udata/tests/__init__.py +9 -6
  309. udata/tests/api/__init__.py +8 -6
  310. udata/tests/api/test_auth_api.py +395 -321
  311. udata/tests/api/test_base_api.py +33 -35
  312. udata/tests/api/test_contact_points.py +7 -9
  313. udata/tests/api/test_dataservices_api.py +211 -158
  314. udata/tests/api/test_datasets_api.py +823 -812
  315. udata/tests/api/test_follow_api.py +13 -15
  316. udata/tests/api/test_me_api.py +95 -112
  317. udata/tests/api/test_organizations_api.py +301 -339
  318. udata/tests/api/test_reports_api.py +35 -25
  319. udata/tests/api/test_reuses_api.py +134 -139
  320. udata/tests/api/test_swagger.py +5 -5
  321. udata/tests/api/test_tags_api.py +18 -25
  322. udata/tests/api/test_topics_api.py +94 -94
  323. udata/tests/api/test_transfer_api.py +53 -48
  324. udata/tests/api/test_user_api.py +128 -141
  325. udata/tests/apiv2/test_datasets.py +290 -198
  326. udata/tests/apiv2/test_me_api.py +10 -11
  327. udata/tests/apiv2/test_organizations.py +56 -74
  328. udata/tests/apiv2/test_swagger.py +5 -5
  329. udata/tests/apiv2/test_topics.py +69 -87
  330. udata/tests/cli/test_cli_base.py +8 -8
  331. udata/tests/cli/test_db_cli.py +21 -19
  332. udata/tests/dataservice/test_dataservice_tasks.py +8 -12
  333. udata/tests/dataset/test_csv_adapter.py +44 -35
  334. udata/tests/dataset/test_dataset_actions.py +2 -3
  335. udata/tests/dataset/test_dataset_commands.py +7 -8
  336. udata/tests/dataset/test_dataset_events.py +36 -29
  337. udata/tests/dataset/test_dataset_model.py +224 -217
  338. udata/tests/dataset/test_dataset_rdf.py +142 -131
  339. udata/tests/dataset/test_dataset_tasks.py +15 -15
  340. udata/tests/dataset/test_resource_preview.py +10 -13
  341. udata/tests/features/territories/__init__.py +9 -13
  342. udata/tests/features/territories/test_territories_api.py +71 -91
  343. udata/tests/forms/test_basic_fields.py +7 -7
  344. udata/tests/forms/test_current_user_field.py +39 -66
  345. udata/tests/forms/test_daterange_field.py +31 -39
  346. udata/tests/forms/test_dict_field.py +28 -26
  347. udata/tests/forms/test_extras_fields.py +102 -76
  348. udata/tests/forms/test_form_field.py +8 -8
  349. udata/tests/forms/test_image_field.py +33 -26
  350. udata/tests/forms/test_model_field.py +134 -123
  351. udata/tests/forms/test_model_list_field.py +7 -7
  352. udata/tests/forms/test_nested_model_list_field.py +117 -79
  353. udata/tests/forms/test_publish_as_field.py +36 -65
  354. udata/tests/forms/test_reference_field.py +34 -53
  355. udata/tests/forms/test_user_forms.py +23 -21
  356. udata/tests/forms/test_uuid_field.py +6 -10
  357. udata/tests/frontend/__init__.py +9 -6
  358. udata/tests/frontend/test_auth.py +7 -6
  359. udata/tests/frontend/test_csv.py +81 -96
  360. udata/tests/frontend/test_hooks.py +43 -43
  361. udata/tests/frontend/test_markdown.py +211 -191
  362. udata/tests/helpers.py +32 -37
  363. udata/tests/models.py +2 -2
  364. udata/tests/organization/test_csv_adapter.py +21 -16
  365. udata/tests/organization/test_notifications.py +11 -18
  366. udata/tests/organization/test_organization_model.py +13 -13
  367. udata/tests/organization/test_organization_rdf.py +29 -22
  368. udata/tests/organization/test_organization_tasks.py +16 -17
  369. udata/tests/plugin.py +79 -73
  370. udata/tests/reuse/test_reuse_model.py +21 -21
  371. udata/tests/reuse/test_reuse_task.py +11 -13
  372. udata/tests/search/__init__.py +11 -12
  373. udata/tests/search/test_adapter.py +60 -70
  374. udata/tests/search/test_query.py +16 -16
  375. udata/tests/search/test_results.py +10 -7
  376. udata/tests/site/test_site_api.py +11 -16
  377. udata/tests/site/test_site_metrics.py +20 -30
  378. udata/tests/site/test_site_model.py +4 -5
  379. udata/tests/site/test_site_rdf.py +94 -78
  380. udata/tests/test_activity.py +17 -17
  381. udata/tests/test_cors.py +62 -0
  382. udata/tests/test_discussions.py +292 -299
  383. udata/tests/test_i18n.py +37 -40
  384. udata/tests/test_linkchecker.py +91 -85
  385. udata/tests/test_mail.py +13 -17
  386. udata/tests/test_migrations.py +219 -180
  387. udata/tests/test_model.py +164 -157
  388. udata/tests/test_notifications.py +17 -17
  389. udata/tests/test_owned.py +14 -14
  390. udata/tests/test_rdf.py +25 -23
  391. udata/tests/test_routing.py +89 -93
  392. udata/tests/test_storages.py +137 -128
  393. udata/tests/test_tags.py +44 -46
  394. udata/tests/test_topics.py +7 -7
  395. udata/tests/test_transfer.py +42 -49
  396. udata/tests/test_uris.py +160 -161
  397. udata/tests/test_utils.py +79 -71
  398. udata/tests/user/test_user_rdf.py +5 -9
  399. udata/tests/workers/test_jobs_commands.py +57 -58
  400. udata/tests/workers/test_tasks_routing.py +23 -29
  401. udata/tests/workers/test_workers_api.py +125 -131
  402. udata/tests/workers/test_workers_helpers.py +6 -6
  403. udata/tracking.py +4 -6
  404. udata/uris.py +45 -46
  405. udata/utils.py +68 -66
  406. udata/wsgi.py +1 -1
  407. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/METADATA +7 -3
  408. udata-9.1.2.dev30454.dist-info/RECORD +706 -0
  409. udata-9.1.2.dev30355.dist-info/RECORD +0 -704
  410. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/LICENSE +0 -0
  411. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/WHEEL +0 -0
  412. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/entry_points.txt +0 -0
  413. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/top_level.txt +0 -0
udata/forms/fields.py CHANGED
@@ -1,37 +1,38 @@
1
1
  import uuid
2
2
 
3
3
  from dateutil.parser import parse
4
-
5
4
  from flask import url_for
6
5
  from flask_mongoengine.wtf import fields as mefields
7
6
  from flask_storage.mongo import ImageReference
8
7
  from speaklater import is_lazy_string
9
- from wtforms import Form as WTForm, Field as WTField, validators, fields, SubmitField # noqa
8
+ from wtforms import Field as WTField
9
+ from wtforms import Form as WTForm
10
+ from wtforms import SubmitField, fields, validators # noqa
10
11
  from wtforms.utils import unset_value
11
12
  from wtforms.widgets import TextInput
12
13
  from wtforms_json import flatten_json
13
14
 
14
- from . import widgets
15
-
16
- from udata.auth import current_user, admin_permission
17
- from udata.models import db, User, Organization, Dataset, Reuse, datastore, ContactPoint
18
- from udata.core.storages import tmp
19
- from udata.core.organization.permissions import OrganizationPrivatePermission
20
- from udata.i18n import lazy_gettext as _
21
15
  from udata import tags, uris
16
+ from udata.auth import admin_permission, current_user
17
+ from udata.core.organization.permissions import OrganizationPrivatePermission
18
+ from udata.core.storages import tmp
22
19
  from udata.forms import ModelForm
23
- from udata.utils import to_iso_date, get_by
20
+ from udata.i18n import lazy_gettext as _
21
+ from udata.models import ContactPoint, Dataset, Organization, Reuse, User, datastore, db
22
+ from udata.utils import get_by, to_iso_date
23
+
24
+ from . import widgets
24
25
 
25
26
 
26
27
  class FieldHelper(object):
27
28
  def __init__(self, *args, **kwargs):
28
- self._preprocessors = kwargs.pop('preprocessors', [])
29
+ self._preprocessors = kwargs.pop("preprocessors", [])
29
30
  super(FieldHelper, self).__init__(*args, **kwargs)
30
- self._form = kwargs.get('_form', None)
31
+ self._form = kwargs.get("_form", None)
31
32
 
32
33
  @property
33
34
  def id(self):
34
- return '{0}-id'.format(self.name)
35
+ return "{0}-id".format(self.name)
35
36
 
36
37
  @id.setter
37
38
  def id(self, value):
@@ -42,16 +43,16 @@ class FieldHelper(object):
42
43
  return False
43
44
 
44
45
  def __call__(self, **kwargs):
45
- placeholder = kwargs.pop('placeholder', self.label.text)
46
+ placeholder = kwargs.pop("placeholder", self.label.text)
46
47
  if placeholder:
47
- kwargs['placeholder'] = placeholder
48
- required = kwargs.pop('required', self.flags.required)
48
+ kwargs["placeholder"] = placeholder
49
+ required = kwargs.pop("required", self.flags.required)
49
50
  if required is True:
50
- kwargs['required'] = required
51
+ kwargs["required"] = required
51
52
  return super(FieldHelper, self).__call__(**kwargs)
52
53
 
53
54
  def pre_validate(self, form):
54
- '''Calls preprocessors before pre_validation'''
55
+ """Calls preprocessors before pre_validation"""
55
56
  for preprocessor in self._preprocessors:
56
57
  preprocessor(form, self)
57
58
  super(FieldHelper, self).pre_validate(form)
@@ -63,7 +64,7 @@ class Field(FieldHelper, WTField):
63
64
 
64
65
  class EmptyNone(object):
65
66
  def process_formdata(self, valuelist):
66
- '''Replace empty values by None'''
67
+ """Replace empty values by None"""
67
68
  super(EmptyNone, self).process_formdata(valuelist)
68
69
  self.data = self.data or None
69
70
 
@@ -95,10 +96,17 @@ class WTFDateTimeField(WTField):
95
96
  :param display_format:
96
97
  A format string to pass to strftime() to format dates for display.
97
98
  """
99
+
98
100
  widget = TextInput()
99
101
 
100
- def __init__(self, label=None, validators=None, parse_kwargs=None,
101
- display_format='%Y-%m-%d %H:%M', **kwargs):
102
+ def __init__(
103
+ self,
104
+ label=None,
105
+ validators=None,
106
+ parse_kwargs=None,
107
+ display_format="%Y-%m-%d %H:%M",
108
+ **kwargs,
109
+ ):
102
110
  super(WTFDateTimeField, self).__init__(label, validators, **kwargs)
103
111
  if parse_kwargs is None:
104
112
  parse_kwargs = {}
@@ -107,42 +115,45 @@ class WTFDateTimeField(WTField):
107
115
 
108
116
  def _value(self):
109
117
  if self.raw_data:
110
- return ' '.join(self.raw_data)
118
+ return " ".join(self.raw_data)
111
119
  else:
112
- return self.data and self.data.strftime(self.display_format) or ''
120
+ return self.data and self.data.strftime(self.display_format) or ""
113
121
 
114
122
  def process_formdata(self, valuelist):
115
123
  if valuelist:
116
- date_str = ' '.join(valuelist)
124
+ date_str = " ".join(valuelist)
117
125
  if not date_str:
118
126
  self.data = None
119
- raise validators.ValidationError(_('Please input a date/time value'))
127
+ raise validators.ValidationError(_("Please input a date/time value"))
120
128
 
121
129
  parse_kwargs = self.parse_kwargs.copy()
122
- if 'default' not in parse_kwargs:
130
+ if "default" not in parse_kwargs:
123
131
  try:
124
- parse_kwargs['default'] = self.default()
132
+ parse_kwargs["default"] = self.default()
125
133
  except TypeError:
126
- parse_kwargs['default'] = self.default
134
+ parse_kwargs["default"] = self.default
127
135
  try:
128
136
  self.data = parse(date_str, **parse_kwargs)
129
137
  except ValueError:
130
138
  self.data = None
131
- raise validators.ValidationError(_('Invalid date/time input'))
139
+ raise validators.ValidationError(_("Invalid date/time input"))
132
140
 
133
141
 
134
142
  class DateField(WTFDateTimeField):
135
143
  """
136
144
  Same as the DateTimeField, but stores only the date portion.
137
145
  """
138
- def __init__(self, label=None, validators=None, parse_kwargs=None,
139
- display_format='%Y-%m-%d', **kwargs):
140
- super(DateField, self).__init__(label, validators, parse_kwargs=parse_kwargs,
141
- display_format=display_format, **kwargs)
146
+
147
+ def __init__(
148
+ self, label=None, validators=None, parse_kwargs=None, display_format="%Y-%m-%d", **kwargs
149
+ ):
150
+ super(DateField, self).__init__(
151
+ label, validators, parse_kwargs=parse_kwargs, display_format=display_format, **kwargs
152
+ )
142
153
 
143
154
  def process_formdata(self, valuelist):
144
155
  super(DateField, self).process_formdata(valuelist)
145
- if self.data is not None and hasattr(self.data, 'date'):
156
+ if self.data is not None and hasattr(self.data, "date"):
146
157
  self.data = self.data.date()
147
158
 
148
159
 
@@ -155,7 +166,8 @@ class RolesField(Field):
155
166
  self.data.append(role)
156
167
  else:
157
168
  raise validators.ValidationError(
158
- _('The role {role} does not exist').format(role=name))
169
+ _("The role {role} does not exist").format(role=name)
170
+ )
159
171
 
160
172
 
161
173
  class DateTimeField(Field, WTFDateTimeField):
@@ -172,28 +184,28 @@ class UUIDField(Field):
172
184
  self.data = uuid.UUID(valuelist[0])
173
185
  except ValueError:
174
186
  self.data = None
175
- raise ValueError(self.gettext('Not a valid UUID'))
187
+ raise ValueError(self.gettext("Not a valid UUID"))
176
188
 
177
189
 
178
190
  class BooleanField(FieldHelper, fields.BooleanField):
179
191
  def __init__(self, *args, **kwargs):
180
- self.stacked = kwargs.pop('stacked', False)
192
+ self.stacked = kwargs.pop("stacked", False)
181
193
  super(BooleanField, self).__init__(*args, **kwargs)
182
194
 
183
195
  def process_formdata(self, valuelist):
184
196
  # We override this so that when no value is provided
185
- # the form doesn't think the value is `False` instead
197
+ # the form doesn't think the value is `False` instead
186
198
  # the value is not present and the model can keep the
187
199
  # existing value
188
200
  if not valuelist:
189
- return
201
+ return
190
202
 
191
203
  super().process_formdata(valuelist)
192
204
 
193
205
 
194
206
  class RadioField(FieldHelper, fields.RadioField):
195
207
  def __init__(self, *args, **kwargs):
196
- self.stacked = kwargs.pop('stacked', False)
208
+ self.stacked = kwargs.pop("stacked", False)
197
209
  super(RadioField, self).__init__(*args, **kwargs)
198
210
 
199
211
 
@@ -218,8 +230,8 @@ class URLField(EmptyNone, Field):
218
230
 
219
231
  class UploadableURLField(URLField):
220
232
  def __init__(self, *args, **kwargs):
221
- storage = kwargs.pop('storage')
222
- self.endpoint = url_for('storage.upload', name=storage.name)
233
+ storage = kwargs.pop("storage")
234
+ self.endpoint = url_for("storage.upload", name=storage.name)
223
235
  super(UploadableURLField, self).__init__(*args, **kwargs)
224
236
 
225
237
 
@@ -228,17 +240,18 @@ class TextAreaField(FieldHelper, EmptyNone, fields.TextAreaField):
228
240
 
229
241
 
230
242
  class FormWrapper(object):
231
- '''
243
+ """
232
244
  Wrap FormField nested form class to handle both
233
245
  JSON provisionning from wtforms-json
234
246
  and CSRF disabled from flask-wtf
235
- '''
247
+ """
248
+
236
249
  def __init__(self, cls):
237
250
  self.cls = cls
238
251
 
239
252
  def __call__(self, *args, **kwargs):
240
- meta = kwargs['meta'] = kwargs.get('meta', {})
241
- meta['csrf'] = False
253
+ meta = kwargs["meta"] = kwargs.get("meta", {})
254
+ meta["csrf"] = False
242
255
  return self.cls(*args, **kwargs)
243
256
 
244
257
  def __getattr__(self, name):
@@ -247,9 +260,8 @@ class FormWrapper(object):
247
260
 
248
261
  class FormField(FieldHelper, fields.FormField):
249
262
  def __init__(self, form_class, *args, **kwargs):
250
- super(FormField, self).__init__(FormWrapper(form_class),
251
- *args, **kwargs)
252
- self.prefix = '{0}-'.format(self.name)
263
+ super(FormField, self).__init__(FormWrapper(form_class), *args, **kwargs)
264
+ self.prefix = "{0}-".format(self.name)
253
265
  self._formdata = None
254
266
 
255
267
  def process(self, formdata, data=unset_value, **kwargs):
@@ -258,8 +270,10 @@ class FormField(FieldHelper, fields.FormField):
258
270
 
259
271
  def validate(self, form, extra_validators=tuple()):
260
272
  if extra_validators:
261
- raise TypeError('FormField does not accept in-line validators, '
262
- 'as it gets errors from the enclosed form.')
273
+ raise TypeError(
274
+ "FormField does not accept in-line validators, "
275
+ "as it gets errors from the enclosed form."
276
+ )
263
277
 
264
278
  # Run normal validation only if there is data for this form
265
279
  if self.has_data:
@@ -269,7 +283,7 @@ class FormField(FieldHelper, fields.FormField):
269
283
  def populate_obj(self, obj, name):
270
284
  if not self.has_data:
271
285
  return
272
- if getattr(self.form_class, 'model_class', None) and not self._obj:
286
+ if getattr(self.form_class, "model_class", None) and not self._obj:
273
287
  self._obj = self.form_class.model_class()
274
288
  super(FormField, self).populate_obj(obj, name)
275
289
 
@@ -279,26 +293,24 @@ class FormField(FieldHelper, fields.FormField):
279
293
 
280
294
  @property
281
295
  def has_data(self):
282
- return self._formdata and any(
283
- k.startswith(self.prefix) for k in self._formdata
284
- )
296
+ return self._formdata and any(k.startswith(self.prefix) for k in self._formdata)
285
297
 
286
298
 
287
299
  class TmpFilename(Field):
288
300
  def _value(self):
289
- return ''
301
+ return ""
290
302
 
291
303
 
292
304
  class BBoxField(Field):
293
305
  def _value(self):
294
306
  if self.data:
295
- return ','.join([str(x) for x in self.data])
307
+ return ",".join([str(x) for x in self.data])
296
308
  else:
297
- return ''
309
+ return ""
298
310
 
299
311
  def process_formdata(self, valuelist):
300
312
  if valuelist:
301
- self.data = [int(float(x)) for x in valuelist[0].split(',')]
313
+ self.data = [int(float(x)) for x in valuelist[0].split(",")]
302
314
  else:
303
315
  self.data = None
304
316
 
@@ -310,10 +322,9 @@ class ImageForm(WTForm):
310
322
 
311
323
  class ImageField(FormField):
312
324
  def __init__(self, label=None, validators=None, **kwargs):
313
- self.sizes = kwargs.pop('sizes', [100])
314
- self.placeholder = kwargs.pop('placeholder', 'default')
315
- super(ImageField, self).__init__(ImageForm, label, validators,
316
- **kwargs)
325
+ self.sizes = kwargs.pop("sizes", [100])
326
+ self.placeholder = kwargs.pop("placeholder", "default")
327
+ super(ImageField, self).__init__(ImageForm, label, validators, **kwargs)
317
328
 
318
329
  def process(self, formdata, data=unset_value, **kwargs):
319
330
  self.src = data(100) if isinstance(data, ImageReference) else None
@@ -324,29 +335,28 @@ class ImageField(FormField):
324
335
  bbox = self.form.bbox.data or None
325
336
  filename = self.form.filename.data or None
326
337
  if filename and filename in tmp:
327
- with tmp.open(filename, 'rb') as infile:
338
+ with tmp.open(filename, "rb") as infile:
328
339
  field.save(infile, filename, bbox=bbox)
329
340
  tmp.delete(filename)
330
341
 
331
342
  @property
332
343
  def endpoint(self):
333
- return url_for('storage.upload', name='tmp')
344
+ return url_for("storage.upload", name="tmp")
334
345
 
335
346
 
336
347
  def nullable_text(value):
337
- return None if value == 'None' else str(value)
348
+ return None if value == "None" else str(value)
338
349
 
339
350
 
340
351
  class SelectField(FieldHelper, fields.SelectField):
341
- def __init__(self, label=None, validators=None, coerce=nullable_text,
342
- **kwargs):
352
+ def __init__(self, label=None, validators=None, coerce=nullable_text, **kwargs):
343
353
  # self._choices = kwargs.pop('choices')
344
354
  super(SelectField, self).__init__(label, validators, coerce, **kwargs)
345
355
 
346
356
  @staticmethod
347
357
  def localize_label(label):
348
358
  if not label:
349
- return ''
359
+ return ""
350
360
  if is_lazy_string(label):
351
361
  return label
352
362
  return _(label)
@@ -354,11 +364,9 @@ class SelectField(FieldHelper, fields.SelectField):
354
364
  def iter_choices(self):
355
365
  localized_choices = [
356
366
  (value, self.localize_label(label), selected)
357
- for value, label, selected
358
- in super(SelectField, self).iter_choices()
367
+ for value, label, selected in super(SelectField, self).iter_choices()
359
368
  ]
360
- for value, label, selected in sorted(localized_choices,
361
- key=lambda c: c[1]):
369
+ for value, label, selected in sorted(localized_choices, key=lambda c: c[1]):
362
370
  yield (value, label, selected)
363
371
 
364
372
  @property
@@ -382,21 +390,19 @@ class SelectMultipleField(FieldHelper, fields.SelectMultipleField):
382
390
 
383
391
  def iter_choices(self):
384
392
  localized_choices = [
385
- (value, self._(label) if label else '', selected)
386
- for value, label, selected
387
- in super(SelectMultipleField, self).iter_choices()
393
+ (value, self._(label) if label else "", selected)
394
+ for value, label, selected in super(SelectMultipleField, self).iter_choices()
388
395
  ]
389
- for value, label, selected in sorted(localized_choices,
390
- key=lambda c: c[1]):
396
+ for value, label, selected in sorted(localized_choices, key=lambda c: c[1]):
391
397
  yield (value, label, selected)
392
398
 
393
399
 
394
400
  class TagField(Field):
395
401
  def _value(self):
396
402
  if self.data:
397
- return ','.join(self.data)
403
+ return ",".join(self.data)
398
404
  else:
399
- return ''
405
+ return ""
400
406
 
401
407
  def process_formdata(self, valuelist):
402
408
  if valuelist and len(valuelist) > 1:
@@ -412,16 +418,17 @@ class TagField(Field):
412
418
  for tag in self.data:
413
419
  if not tags.MIN_TAG_LENGTH <= len(tag) <= tags.MAX_TAG_LENGTH:
414
420
  message = _(
415
- 'Tag "%(tag)s" must be between %(min)d '
416
- 'and %(max)d characters long.',
421
+ 'Tag "%(tag)s" must be between %(min)d ' "and %(max)d characters long.",
417
422
  min=tags.MIN_TAG_LENGTH,
418
- max=tags.MAX_TAG_LENGTH, tag=tag)
423
+ max=tags.MAX_TAG_LENGTH,
424
+ tag=tag,
425
+ )
419
426
  raise validators.ValidationError(message)
420
427
 
421
428
 
422
429
  def clean_oid(oid, model):
423
- if (isinstance(oid, dict) and 'id' in oid):
424
- return clean_oid(oid['id'], model)
430
+ if isinstance(oid, dict) and "id" in oid:
431
+ return clean_oid(oid["id"], model)
425
432
  else:
426
433
  try:
427
434
  # Prevalidation is required as to_python is failsafe
@@ -429,21 +436,21 @@ def clean_oid(oid, model):
429
436
  model.id.validate(oid)
430
437
  return model.id.to_python(oid)
431
438
  except Exception: # Catch all exceptions as model.type is not predefined
432
- raise ValueError('Unsupported identifier: {0}'.format(oid))
439
+ raise ValueError("Unsupported identifier: {0}".format(oid))
433
440
 
434
441
 
435
442
  class ModelFieldMixin(object):
436
443
  model = None
437
444
 
438
445
  def __init__(self, *args, **kwargs):
439
- self.model = kwargs.pop('model', self.model)
446
+ self.model = kwargs.pop("model", self.model)
440
447
  super(ModelFieldMixin, self).__init__(*args, **kwargs)
441
448
 
442
449
  def _value(self):
443
450
  if self.data:
444
451
  return str(self.data.id)
445
452
  else:
446
- return ''
453
+ return ""
447
454
 
448
455
  def process_formdata(self, valuelist):
449
456
  if valuelist and len(valuelist) == 1 and valuelist[0]:
@@ -451,7 +458,7 @@ class ModelFieldMixin(object):
451
458
  id = clean_oid(valuelist[0], self.model)
452
459
  self.data = self.model.objects.get(id=id)
453
460
  except self.model.DoesNotExist:
454
- message = _('{0} does not exists').format(self.model.__name__)
461
+ message = _("{0} does not exists").format(self.model.__name__)
455
462
  raise validators.ValidationError(message)
456
463
 
457
464
 
@@ -460,11 +467,11 @@ class ModelField(Field):
460
467
  if formdata:
461
468
  # Process prefixed values as in FormField
462
469
  newdata = {}
463
- prefix = self.short_name + '-'
470
+ prefix = self.short_name + "-"
464
471
  for key in list(formdata.keys()):
465
472
  if key.startswith(prefix):
466
473
  value = formdata.pop(key)
467
- newdata[key.replace(prefix, '')] = value
474
+ newdata[key.replace(prefix, "")] = value
468
475
  if newdata:
469
476
  formdata.add(self.short_name, newdata)
470
477
  super(ModelField, self).process(formdata, data, **kwargs)
@@ -475,35 +482,35 @@ class ModelField(Field):
475
482
  specs = valuelist[0]
476
483
  model_field = getattr(self._form.model_class, self.name)
477
484
  if isinstance(specs, str):
478
- specs = {'id': specs}
479
- elif not specs.get('id', None):
485
+ specs = {"id": specs}
486
+ elif not specs.get("id", None):
480
487
  raise validators.ValidationError(_('Missing "id" field'))
481
488
 
482
489
  if isinstance(model_field, db.ReferenceField):
483
490
  expected_model = str(model_field.document_type.__name__)
484
- if 'class' not in specs:
485
- specs['class'] = expected_model
486
- elif specs['class'] != expected_model:
491
+ if "class" not in specs:
492
+ specs["class"] = expected_model
493
+ elif specs["class"] != expected_model:
487
494
  msg = _('Expect a "{0}" class but "{1}" was found').format(
488
- expected_model, specs['class']
495
+ expected_model, specs["class"]
489
496
  )
490
497
  raise validators.ValidationError(msg)
491
498
  elif isinstance(model_field, db.GenericReferenceField):
492
- if 'class' not in specs:
493
- msg = _('Expect both class and identifier')
499
+ if "class" not in specs:
500
+ msg = _("Expect both class and identifier")
494
501
  raise validators.ValidationError(msg)
495
502
 
496
503
  # No try/except required
497
504
  # In case of error, ValueError is raised
498
505
  # and is properly handled as form validation error
499
- model = db.resolve_model(specs['class'])
506
+ model = db.resolve_model(specs["class"])
500
507
  oid = clean_oid(specs, model)
501
508
 
502
509
  try:
503
- self.data = model.objects.only('id').get(id=oid)
510
+ self.data = model.objects.only("id").get(id=oid)
504
511
  except db.DoesNotExist:
505
- label = '{0}({1})'.format(model.__name__, oid)
506
- msg = _('{0} does not exists').format(label)
512
+ label = "{0}({1})".format(model.__name__, oid)
513
+ msg = _("{0} does not exists").format(label)
507
514
  raise validators.ValidationError(msg)
508
515
 
509
516
  def pre_validate(self, form):
@@ -518,25 +525,24 @@ class ModelChoiceField(StringField):
518
525
  models = None
519
526
 
520
527
  def __init__(self, *args, **kwargs):
521
- self.models = kwargs.pop('models', self.models)
528
+ self.models = kwargs.pop("models", self.models)
522
529
  super(ModelChoiceField, self).__init__(*args, **kwargs)
523
530
 
524
531
  def _value(self):
525
532
  if self.data:
526
533
  return str(self.data.id)
527
534
  else:
528
- return ''
535
+ return ""
529
536
 
530
537
  def process_formdata(self, valuelist):
531
538
  if valuelist and len(valuelist) == 1 and valuelist[0]:
532
539
  for model in self.models:
533
540
  try:
534
- self.data = model.objects.get(id=clean_oid(valuelist[0],
535
- model))
541
+ self.data = model.objects.get(id=clean_oid(valuelist[0], model))
536
542
  except model.DoesNotExist:
537
543
  pass
538
544
  if not self.data:
539
- message = _('Model for {0} not found').format(valuelist[0])
545
+ message = _("Model for {0} not found").format(valuelist[0])
540
546
  raise validators.ValidationError(message)
541
547
 
542
548
 
@@ -545,34 +551,34 @@ class ModelList(object):
545
551
 
546
552
  def _value(self):
547
553
  if self.data:
548
- return ','.join([str(o.id) for o in self.data])
554
+ return ",".join([str(o.id) for o in self.data])
549
555
  else:
550
- return ''
556
+ return ""
551
557
 
552
558
  def process_formdata(self, valuelist):
553
559
  if not valuelist:
554
560
  return []
555
561
  if len(valuelist) == 1 and isinstance(valuelist[0], str):
556
- oids = [clean_oid(id, self.model)
557
- for id in valuelist[0].split(',') if id]
562
+ oids = [clean_oid(id, self.model) for id in valuelist[0].split(",") if id]
558
563
  else:
559
564
  oids = [clean_oid(id, self.model) for id in valuelist]
560
565
  self.data = self.fetch_objects(oids)
561
566
 
562
567
  def fetch_objects(self, oids):
563
- '''
568
+ """
564
569
  This methods is used to fetch models
565
570
  from a list of identifiers.
566
571
 
567
572
  Default implementation performs a bulk query on identifiers.
568
573
 
569
574
  Override this method to customize the objects retrieval.
570
- '''
575
+ """
571
576
  objects = self.model.objects.in_bulk(oids)
572
577
  if len(objects.keys()) != len(oids):
573
578
  non_existants = set(oids) - set(objects.keys())
574
- msg = _('Unknown identifiers: {identifiers}').format(
575
- identifiers=', '.join(str(ne) for ne in non_existants))
579
+ msg = _("Unknown identifiers: {identifiers}").format(
580
+ identifiers=", ".join(str(ne) for ne in non_existants)
581
+ )
576
582
  raise validators.ValidationError(msg)
577
583
 
578
584
  return [objects[id] for id in oids]
@@ -580,22 +586,18 @@ class ModelList(object):
580
586
 
581
587
  class NestedModelList(fields.FieldList):
582
588
  def __init__(self, model_form, *args, **kwargs):
583
- super(NestedModelList, self).__init__(FormField(model_form),
584
- *args,
585
- **kwargs)
589
+ super(NestedModelList, self).__init__(FormField(model_form), *args, **kwargs)
586
590
  self.nested_form = model_form
587
591
  self.nested_model = model_form.model_class
588
592
  self.data_submitted = False
589
593
  self.initial_data = []
590
- self.prefix = '{0}-'.format(self.name)
594
+ self.prefix = "{0}-".format(self.name)
591
595
 
592
596
  def process(self, formdata, data=unset_value, **kwargs):
593
597
  self._formdata = formdata
594
598
  self.initial_data = data
595
599
  self.is_list_data = formdata and self.name in formdata
596
- self.is_dict_data = formdata and any(
597
- k.startswith(self.prefix) for k in formdata
598
- )
600
+ self.is_dict_data = formdata and any(k.startswith(self.prefix) for k in formdata)
599
601
  self.has_data = self.is_list_data or self.is_dict_data
600
602
  if self.has_data:
601
603
  super(NestedModelList, self).process(formdata, data, **kwargs)
@@ -604,7 +606,7 @@ class NestedModelList(fields.FieldList):
604
606
  # super(NestedModelList, self).process(None, data, **kwargs)
605
607
 
606
608
  def validate(self, form, extra_validators=tuple()):
607
- '''Perform validation only if data has been submitted'''
609
+ """Perform validation only if data has been submitted"""
608
610
  if not self.has_data:
609
611
  return True
610
612
  if self.is_list_data:
@@ -626,33 +628,31 @@ class NestedModelList(fields.FieldList):
626
628
 
627
629
  for idx, field in enumerate(self):
628
630
  initial = None
629
- if hasattr(self.nested_model, 'id') and 'id' in field.data:
630
- id = self.nested_model.id.to_python(field.data['id'])
631
- initial = get_by(initial_values, 'id', id)
631
+ if hasattr(self.nested_model, "id") and "id" in field.data:
632
+ id = self.nested_model.id.to_python(field.data["id"])
633
+ initial = get_by(initial_values, "id", id)
632
634
 
633
635
  holder.nested = initial or self.nested_model()
634
- field.populate_obj(holder, 'nested')
636
+ field.populate_obj(holder, "nested")
635
637
  new_values.append(holder.nested)
636
638
 
637
639
  setattr(obj, name, new_values)
638
640
 
639
641
  def _add_entry(self, formdata=None, data=unset_value, index=None):
640
- '''
642
+ """
641
643
  Fill the form with previous data if necessary to handle partial update
642
- '''
644
+ """
643
645
  if formdata:
644
- prefix = '-'.join((self.name, str(index)))
645
- basekey = '-'.join((prefix, '{0}'))
646
- idkey = basekey.format('id')
646
+ prefix = "-".join((self.name, str(index)))
647
+ basekey = "-".join((prefix, "{0}"))
648
+ idkey = basekey.format("id")
647
649
  if prefix in formdata:
648
650
  formdata[idkey] = formdata.pop(prefix)
649
- if hasattr(self.nested_model, 'id') and idkey in formdata:
651
+ if hasattr(self.nested_model, "id") and idkey in formdata:
650
652
  id = self.nested_model.id.to_python(formdata[idkey])
651
- data = get_by(self.initial_data, 'id', id)
653
+ data = get_by(self.initial_data, "id", id)
652
654
 
653
- initial = flatten_json(self.nested_form,
654
- data.to_mongo(),
655
- prefix)
655
+ initial = flatten_json(self.nested_form, data.to_mongo(), prefix)
656
656
 
657
657
  for key, value in initial.items():
658
658
  if key not in formdata:
@@ -685,34 +685,32 @@ class MarkdownField(FieldHelper, fields.TextAreaField):
685
685
  class DateRangeField(Field):
686
686
  def _value(self):
687
687
  if self.data:
688
- return ' - '.join([to_iso_date(self.data.start),
689
- to_iso_date(self.data.end)])
688
+ return " - ".join([to_iso_date(self.data.start), to_iso_date(self.data.end)])
690
689
  else:
691
- return ''
690
+ return ""
692
691
 
693
692
  def process_formdata(self, valuelist):
694
693
  if valuelist and valuelist[0]:
695
694
  value = valuelist[0]
696
695
  if isinstance(value, str):
697
- start, end = value.split(' - ')
696
+ start, end = value.split(" - ")
698
697
  self.data = db.DateRange(
699
698
  start=parse(start, yearfirst=True).date(),
700
699
  end=parse(end, yearfirst=True).date(),
701
700
  )
702
- elif 'start' in value and 'end' in value:
701
+ elif "start" in value and "end" in value:
703
702
  self.data = db.DateRange(
704
- start=parse(value['start'], yearfirst=True).date(),
705
- end=parse(value['end'], yearfirst=True).date(),
703
+ start=parse(value["start"], yearfirst=True).date(),
704
+ end=parse(value["end"], yearfirst=True).date(),
706
705
  )
707
706
  else:
708
- raise validators.ValidationError(
709
- _('Unable to parse date range'))
707
+ raise validators.ValidationError(_("Unable to parse date range"))
710
708
  else:
711
709
  self.data = None
712
710
 
713
711
 
714
712
  def default_owner():
715
- '''Default to current_user if authenticated'''
713
+ """Default to current_user if authenticated"""
716
714
  if current_user.is_authenticated:
717
715
  return current_user._get_current_object()
718
716
 
@@ -721,7 +719,7 @@ class CurrentUserField(ModelFieldMixin, Field):
721
719
  model = User
722
720
 
723
721
  def __init__(self, *args, **kwargs):
724
- kwargs['default'] = kwargs.pop('default', default_owner)
722
+ kwargs["default"] = kwargs.pop("default", default_owner)
725
723
  super(CurrentUserField, self).__init__(*args, **kwargs)
726
724
 
727
725
  def process(self, formdata, data=unset_value, **kwargs):
@@ -732,20 +730,18 @@ class CurrentUserField(ModelFieldMixin, Field):
732
730
  def pre_validate(self, form):
733
731
  if self.data:
734
732
  if current_user.is_anonymous:
735
- raise validators.ValidationError(
736
- _('You must be authenticated'))
733
+ raise validators.ValidationError(_("You must be authenticated"))
737
734
  elif not admin_permission and current_user.id != self.data.id:
738
- raise validators.ValidationError(
739
- _('You can only set yourself as owner'))
735
+ raise validators.ValidationError(_("You can only set yourself as owner"))
740
736
  return True
741
737
 
742
738
 
743
739
  class PublishAsField(ModelFieldMixin, Field):
744
740
  model = Organization
745
- owner_field = 'owner'
741
+ owner_field = "owner"
746
742
 
747
743
  def __init__(self, *args, **kwargs):
748
- self.owner_field = kwargs.pop('owner_field', self.owner_field)
744
+ self.owner_field = kwargs.pop("owner_field", self.owner_field)
749
745
  super(PublishAsField, self).__init__(*args, **kwargs)
750
746
 
751
747
  @property
@@ -755,11 +751,9 @@ class PublishAsField(ModelFieldMixin, Field):
755
751
  def pre_validate(self, form):
756
752
  if self.data:
757
753
  if not current_user.is_authenticated:
758
- raise validators.ValidationError(
759
- _('You must be authenticated'))
754
+ raise validators.ValidationError(_("You must be authenticated"))
760
755
  elif not OrganizationPrivatePermission(self.data).can():
761
- raise validators.ValidationError(
762
- _("Permission denied for this organization"))
756
+ raise validators.ValidationError(_("Permission denied for this organization"))
763
757
  # Ensure either owner field or this field value is unset
764
758
  owner_field = form._fields[self.owner_field]
765
759
  if self.raw_data:
@@ -779,8 +773,8 @@ class ContactPointField(ModelFieldMixin, Field):
779
773
 
780
774
 
781
775
  def field_parse(cls, value, *args, **kwargs):
782
- kwargs['_form'] = WTForm()
783
- kwargs['name'] = 'extra'
776
+ kwargs["_form"] = WTForm()
777
+ kwargs["name"] = "extra"
784
778
  field = cls(*args, **kwargs)
785
779
  field.process_formdata([value])
786
780
  return field.data
@@ -801,23 +795,21 @@ class ExtrasField(Field):
801
795
  def __init__(self, *args, **kwargs):
802
796
  super(ExtrasField, self).__init__(*args, **kwargs)
803
797
  if not isinstance(self._form, ModelForm):
804
- raise ValueError('ExtrasField can only be used within a ModelForm')
798
+ raise ValueError("ExtrasField can only be used within a ModelForm")
805
799
  model_field = getattr(self._form.model_class, self.short_name, None)
806
800
  if not model_field or not isinstance(model_field, db.ExtrasField):
807
- msg = 'Form ExtrasField can only be mapped to a model ExtraField'
801
+ msg = "Form ExtrasField can only be mapped to a model ExtraField"
808
802
  raise ValueError(msg)
809
803
 
810
804
  @property
811
805
  def extras(self):
812
- '''Getter to the model extras field'''
806
+ """Getter to the model extras field"""
813
807
  return getattr(self._form.model_class, self.short_name)
814
808
 
815
809
  def parse(self, data):
816
- '''Parse fields and store individual errors'''
810
+ """Parse fields and store individual errors"""
817
811
  self.field_errors = {}
818
- return dict(
819
- (k, self._parse_value(k, v)) for k, v in data.items()
820
- )
812
+ return dict((k, self._parse_value(k, v)) for k, v in data.items())
821
813
 
822
814
  def _parse_value(self, key, value):
823
815
  if key not in self.extras.registered:
@@ -827,7 +819,7 @@ class ExtrasField(Field):
827
819
  try:
828
820
  return field_parse(self.KNOWN_TYPES[expected], value)
829
821
  except (validators.ValidationError, ValueError) as e:
830
- self.field_errors[key] = getattr(e, 'message', str(e))
822
+ self.field_errors[key] = getattr(e, "message", str(e))
831
823
  else:
832
824
  return value
833
825
 
@@ -837,14 +829,14 @@ class ExtrasField(Field):
837
829
  if isinstance(data, dict):
838
830
  self.data = self.parse(data)
839
831
  else:
840
- raise ValueError('Unsupported data type')
832
+ raise ValueError("Unsupported data type")
841
833
  else:
842
834
  self.data = self.parse(self.data or {})
843
835
 
844
836
  def validate(self, form, extra_validators=tuple()):
845
837
  if self.process_errors:
846
838
  self.errors = list(self.process_errors)
847
- elif getattr(self, 'field_errors', None):
839
+ elif getattr(self, "field_errors", None):
848
840
  self.errors = self.field_errors
849
841
  elif self.data:
850
842
  try:
@@ -864,4 +856,4 @@ class DictField(Field):
864
856
  if isinstance(data, dict):
865
857
  self.data = data
866
858
  else:
867
- raise ValueError('Unsupported data type')
859
+ raise ValueError("Unsupported data type")