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

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

Potentially problematic release.


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

Files changed (413) hide show
  1. tasks/__init__.py +109 -107
  2. tasks/helpers.py +18 -18
  3. udata/__init__.py +4 -4
  4. udata/admin/views.py +5 -5
  5. udata/api/__init__.py +111 -134
  6. udata/api/commands.py +45 -37
  7. udata/api/errors.py +5 -4
  8. udata/api/fields.py +23 -21
  9. udata/api/oauth2.py +55 -74
  10. udata/api/parsers.py +15 -15
  11. udata/api/signals.py +1 -1
  12. udata/api_fields.py +137 -89
  13. udata/app.py +58 -55
  14. udata/assets.py +5 -5
  15. udata/auth/__init__.py +37 -26
  16. udata/auth/forms.py +23 -15
  17. udata/auth/helpers.py +1 -1
  18. udata/auth/mails.py +3 -3
  19. udata/auth/password_validation.py +19 -15
  20. udata/auth/views.py +94 -68
  21. udata/commands/__init__.py +71 -69
  22. udata/commands/cache.py +7 -7
  23. udata/commands/db.py +201 -140
  24. udata/commands/dcat.py +36 -30
  25. udata/commands/fixtures.py +100 -84
  26. udata/commands/images.py +21 -20
  27. udata/commands/info.py +17 -20
  28. udata/commands/init.py +10 -10
  29. udata/commands/purge.py +12 -13
  30. udata/commands/serve.py +41 -29
  31. udata/commands/static.py +16 -18
  32. udata/commands/test.py +20 -20
  33. udata/commands/tests/fixtures.py +26 -24
  34. udata/commands/worker.py +31 -33
  35. udata/core/__init__.py +12 -12
  36. udata/core/activity/__init__.py +0 -1
  37. udata/core/activity/api.py +59 -49
  38. udata/core/activity/models.py +28 -26
  39. udata/core/activity/signals.py +1 -1
  40. udata/core/activity/tasks.py +16 -10
  41. udata/core/badges/api.py +6 -6
  42. udata/core/badges/commands.py +14 -13
  43. udata/core/badges/fields.py +8 -5
  44. udata/core/badges/forms.py +7 -4
  45. udata/core/badges/models.py +16 -31
  46. udata/core/badges/permissions.py +1 -3
  47. udata/core/badges/signals.py +2 -2
  48. udata/core/badges/tasks.py +3 -2
  49. udata/core/badges/tests/test_commands.py +10 -10
  50. udata/core/badges/tests/test_model.py +24 -31
  51. udata/core/contact_point/api.py +19 -18
  52. udata/core/contact_point/api_fields.py +21 -14
  53. udata/core/contact_point/factories.py +2 -2
  54. udata/core/contact_point/forms.py +7 -6
  55. udata/core/contact_point/models.py +3 -5
  56. udata/core/dataservices/api.py +26 -21
  57. udata/core/dataservices/factories.py +13 -11
  58. udata/core/dataservices/models.py +35 -40
  59. udata/core/dataservices/permissions.py +4 -4
  60. udata/core/dataservices/rdf.py +40 -17
  61. udata/core/dataservices/tasks.py +4 -3
  62. udata/core/dataset/actions.py +10 -10
  63. udata/core/dataset/activities.py +21 -23
  64. udata/core/dataset/api.py +321 -298
  65. udata/core/dataset/api_fields.py +443 -271
  66. udata/core/dataset/apiv2.py +305 -229
  67. udata/core/dataset/commands.py +38 -36
  68. udata/core/dataset/constants.py +61 -54
  69. udata/core/dataset/csv.py +70 -74
  70. udata/core/dataset/events.py +39 -32
  71. udata/core/dataset/exceptions.py +8 -4
  72. udata/core/dataset/factories.py +57 -65
  73. udata/core/dataset/forms.py +87 -63
  74. udata/core/dataset/models.py +336 -280
  75. udata/core/dataset/permissions.py +9 -6
  76. udata/core/dataset/preview.py +15 -17
  77. udata/core/dataset/rdf.py +156 -122
  78. udata/core/dataset/search.py +92 -77
  79. udata/core/dataset/signals.py +1 -1
  80. udata/core/dataset/tasks.py +63 -54
  81. udata/core/discussions/actions.py +5 -5
  82. udata/core/discussions/api.py +124 -120
  83. udata/core/discussions/factories.py +2 -2
  84. udata/core/discussions/forms.py +9 -7
  85. udata/core/discussions/metrics.py +1 -3
  86. udata/core/discussions/models.py +25 -24
  87. udata/core/discussions/notifications.py +18 -14
  88. udata/core/discussions/permissions.py +3 -3
  89. udata/core/discussions/signals.py +4 -4
  90. udata/core/discussions/tasks.py +24 -28
  91. udata/core/followers/api.py +32 -33
  92. udata/core/followers/models.py +9 -9
  93. udata/core/followers/signals.py +3 -3
  94. udata/core/jobs/actions.py +7 -7
  95. udata/core/jobs/api.py +99 -92
  96. udata/core/jobs/commands.py +48 -49
  97. udata/core/jobs/forms.py +11 -11
  98. udata/core/jobs/models.py +6 -6
  99. udata/core/metrics/__init__.py +2 -2
  100. udata/core/metrics/commands.py +34 -30
  101. udata/core/metrics/models.py +2 -4
  102. udata/core/metrics/signals.py +1 -1
  103. udata/core/metrics/tasks.py +3 -3
  104. udata/core/organization/activities.py +12 -15
  105. udata/core/organization/api.py +167 -174
  106. udata/core/organization/api_fields.py +183 -124
  107. udata/core/organization/apiv2.py +32 -32
  108. udata/core/organization/commands.py +20 -22
  109. udata/core/organization/constants.py +11 -11
  110. udata/core/organization/csv.py +17 -15
  111. udata/core/organization/factories.py +8 -11
  112. udata/core/organization/forms.py +32 -26
  113. udata/core/organization/metrics.py +2 -1
  114. udata/core/organization/models.py +87 -67
  115. udata/core/organization/notifications.py +18 -14
  116. udata/core/organization/permissions.py +10 -11
  117. udata/core/organization/rdf.py +14 -14
  118. udata/core/organization/search.py +30 -28
  119. udata/core/organization/signals.py +7 -7
  120. udata/core/organization/tasks.py +42 -61
  121. udata/core/owned.py +38 -27
  122. udata/core/post/api.py +82 -81
  123. udata/core/post/constants.py +8 -5
  124. udata/core/post/factories.py +4 -4
  125. udata/core/post/forms.py +13 -14
  126. udata/core/post/models.py +20 -22
  127. udata/core/post/tests/test_api.py +30 -32
  128. udata/core/reports/api.py +8 -7
  129. udata/core/reports/constants.py +1 -3
  130. udata/core/reports/models.py +10 -10
  131. udata/core/reuse/activities.py +15 -19
  132. udata/core/reuse/api.py +123 -126
  133. udata/core/reuse/api_fields.py +120 -85
  134. udata/core/reuse/apiv2.py +11 -10
  135. udata/core/reuse/constants.py +23 -23
  136. udata/core/reuse/csv.py +18 -18
  137. udata/core/reuse/factories.py +5 -9
  138. udata/core/reuse/forms.py +24 -21
  139. udata/core/reuse/models.py +55 -51
  140. udata/core/reuse/permissions.py +2 -2
  141. udata/core/reuse/search.py +49 -46
  142. udata/core/reuse/signals.py +1 -1
  143. udata/core/reuse/tasks.py +4 -5
  144. udata/core/site/api.py +47 -50
  145. udata/core/site/factories.py +2 -2
  146. udata/core/site/forms.py +4 -5
  147. udata/core/site/models.py +94 -63
  148. udata/core/site/rdf.py +14 -14
  149. udata/core/spam/api.py +16 -9
  150. udata/core/spam/constants.py +4 -4
  151. udata/core/spam/fields.py +13 -7
  152. udata/core/spam/models.py +27 -20
  153. udata/core/spam/signals.py +1 -1
  154. udata/core/spam/tests/test_spam.py +6 -5
  155. udata/core/spatial/api.py +72 -80
  156. udata/core/spatial/api_fields.py +73 -58
  157. udata/core/spatial/commands.py +67 -64
  158. udata/core/spatial/constants.py +3 -3
  159. udata/core/spatial/factories.py +37 -54
  160. udata/core/spatial/forms.py +27 -26
  161. udata/core/spatial/geoids.py +17 -17
  162. udata/core/spatial/models.py +43 -47
  163. udata/core/spatial/tasks.py +2 -1
  164. udata/core/spatial/tests/test_api.py +115 -130
  165. udata/core/spatial/tests/test_fields.py +74 -77
  166. udata/core/spatial/tests/test_geoid.py +22 -22
  167. udata/core/spatial/tests/test_models.py +5 -7
  168. udata/core/spatial/translations.py +16 -16
  169. udata/core/storages/__init__.py +16 -18
  170. udata/core/storages/api.py +66 -64
  171. udata/core/storages/tasks.py +7 -7
  172. udata/core/storages/utils.py +15 -15
  173. udata/core/storages/views.py +5 -6
  174. udata/core/tags/api.py +17 -14
  175. udata/core/tags/csv.py +4 -4
  176. udata/core/tags/models.py +8 -5
  177. udata/core/tags/tasks.py +11 -13
  178. udata/core/tags/views.py +4 -4
  179. udata/core/topic/api.py +84 -73
  180. udata/core/topic/apiv2.py +157 -127
  181. udata/core/topic/factories.py +3 -4
  182. udata/core/topic/forms.py +12 -14
  183. udata/core/topic/models.py +14 -19
  184. udata/core/topic/parsers.py +26 -26
  185. udata/core/user/activities.py +30 -29
  186. udata/core/user/api.py +151 -152
  187. udata/core/user/api_fields.py +132 -100
  188. udata/core/user/apiv2.py +7 -7
  189. udata/core/user/commands.py +38 -38
  190. udata/core/user/factories.py +8 -9
  191. udata/core/user/forms.py +14 -11
  192. udata/core/user/metrics.py +2 -2
  193. udata/core/user/models.py +68 -69
  194. udata/core/user/permissions.py +4 -5
  195. udata/core/user/rdf.py +7 -8
  196. udata/core/user/tasks.py +2 -2
  197. udata/core/user/tests/test_user_model.py +24 -16
  198. udata/cors.py +99 -0
  199. udata/db/tasks.py +2 -1
  200. udata/entrypoints.py +35 -31
  201. udata/errors.py +2 -1
  202. udata/event/values.py +6 -6
  203. udata/factories.py +2 -2
  204. udata/features/identicon/api.py +5 -6
  205. udata/features/identicon/backends.py +48 -55
  206. udata/features/identicon/tests/test_backends.py +4 -5
  207. udata/features/notifications/__init__.py +0 -1
  208. udata/features/notifications/actions.py +9 -9
  209. udata/features/notifications/api.py +17 -13
  210. udata/features/territories/__init__.py +12 -10
  211. udata/features/territories/api.py +14 -15
  212. udata/features/territories/models.py +23 -28
  213. udata/features/transfer/actions.py +8 -11
  214. udata/features/transfer/api.py +84 -77
  215. udata/features/transfer/factories.py +2 -1
  216. udata/features/transfer/models.py +11 -12
  217. udata/features/transfer/notifications.py +19 -15
  218. udata/features/transfer/permissions.py +5 -5
  219. udata/forms/__init__.py +5 -2
  220. udata/forms/fields.py +164 -172
  221. udata/forms/validators.py +19 -22
  222. udata/forms/widgets.py +9 -13
  223. udata/frontend/__init__.py +31 -26
  224. udata/frontend/csv.py +68 -58
  225. udata/frontend/markdown.py +40 -44
  226. udata/harvest/actions.py +89 -77
  227. udata/harvest/api.py +294 -238
  228. udata/harvest/backends/__init__.py +4 -4
  229. udata/harvest/backends/base.py +128 -111
  230. udata/harvest/backends/dcat.py +80 -66
  231. udata/harvest/commands.py +56 -60
  232. udata/harvest/csv.py +8 -8
  233. udata/harvest/exceptions.py +6 -3
  234. udata/harvest/filters.py +24 -23
  235. udata/harvest/forms.py +27 -28
  236. udata/harvest/models.py +88 -80
  237. udata/harvest/notifications.py +15 -10
  238. udata/harvest/signals.py +13 -13
  239. udata/harvest/tasks.py +11 -10
  240. udata/harvest/tests/factories.py +23 -24
  241. udata/harvest/tests/test_actions.py +136 -166
  242. udata/harvest/tests/test_api.py +220 -214
  243. udata/harvest/tests/test_base_backend.py +117 -112
  244. udata/harvest/tests/test_dcat_backend.py +380 -308
  245. udata/harvest/tests/test_filters.py +33 -22
  246. udata/harvest/tests/test_models.py +11 -14
  247. udata/harvest/tests/test_notifications.py +6 -7
  248. udata/harvest/tests/test_tasks.py +7 -6
  249. udata/i18n.py +237 -78
  250. udata/linkchecker/backends.py +5 -11
  251. udata/linkchecker/checker.py +23 -22
  252. udata/linkchecker/commands.py +4 -6
  253. udata/linkchecker/models.py +6 -6
  254. udata/linkchecker/tasks.py +18 -20
  255. udata/mail.py +21 -21
  256. udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
  257. udata/migrations/2020-08-24-add-fs-filename.py +9 -8
  258. udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
  259. udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
  260. udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
  261. udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
  262. udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
  263. udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
  264. udata/migrations/2021-08-17-follow-integrity.py +5 -4
  265. udata/migrations/2021-08-17-harvest-integrity.py +13 -12
  266. udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
  267. udata/migrations/2021-08-17-transfer-integrity.py +5 -4
  268. udata/migrations/2021-08-17-users-integrity.py +9 -8
  269. udata/migrations/2021-12-14-reuse-topics.py +7 -6
  270. udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
  271. udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
  272. udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
  273. udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
  274. udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
  275. udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
  276. udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
  277. udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
  278. udata/migrations/__init__.py +123 -105
  279. udata/models/__init__.py +4 -4
  280. udata/mongo/__init__.py +13 -11
  281. udata/mongo/badges_field.py +3 -2
  282. udata/mongo/datetime_fields.py +13 -12
  283. udata/mongo/document.py +17 -16
  284. udata/mongo/engine.py +15 -16
  285. udata/mongo/errors.py +2 -1
  286. udata/mongo/extras_fields.py +30 -20
  287. udata/mongo/queryset.py +12 -12
  288. udata/mongo/slug_fields.py +38 -28
  289. udata/mongo/taglist_field.py +1 -2
  290. udata/mongo/url_field.py +5 -5
  291. udata/mongo/uuid_fields.py +4 -3
  292. udata/notifications/__init__.py +1 -1
  293. udata/notifications/mattermost.py +10 -9
  294. udata/rdf.py +167 -188
  295. udata/routing.py +40 -45
  296. udata/search/__init__.py +18 -19
  297. udata/search/adapter.py +17 -16
  298. udata/search/commands.py +44 -51
  299. udata/search/fields.py +13 -20
  300. udata/search/query.py +23 -18
  301. udata/search/result.py +9 -10
  302. udata/sentry.py +21 -19
  303. udata/settings.py +262 -198
  304. udata/sitemap.py +8 -6
  305. udata/storage/s3.py +20 -13
  306. udata/tags.py +4 -5
  307. udata/tasks.py +43 -42
  308. udata/tests/__init__.py +9 -6
  309. udata/tests/api/__init__.py +8 -6
  310. udata/tests/api/test_auth_api.py +395 -321
  311. udata/tests/api/test_base_api.py +33 -35
  312. udata/tests/api/test_contact_points.py +7 -9
  313. udata/tests/api/test_dataservices_api.py +211 -158
  314. udata/tests/api/test_datasets_api.py +823 -812
  315. udata/tests/api/test_follow_api.py +13 -15
  316. udata/tests/api/test_me_api.py +95 -112
  317. udata/tests/api/test_organizations_api.py +301 -339
  318. udata/tests/api/test_reports_api.py +35 -25
  319. udata/tests/api/test_reuses_api.py +134 -139
  320. udata/tests/api/test_swagger.py +5 -5
  321. udata/tests/api/test_tags_api.py +18 -25
  322. udata/tests/api/test_topics_api.py +94 -94
  323. udata/tests/api/test_transfer_api.py +53 -48
  324. udata/tests/api/test_user_api.py +128 -141
  325. udata/tests/apiv2/test_datasets.py +290 -198
  326. udata/tests/apiv2/test_me_api.py +10 -11
  327. udata/tests/apiv2/test_organizations.py +56 -74
  328. udata/tests/apiv2/test_swagger.py +5 -5
  329. udata/tests/apiv2/test_topics.py +69 -87
  330. udata/tests/cli/test_cli_base.py +8 -8
  331. udata/tests/cli/test_db_cli.py +21 -19
  332. udata/tests/dataservice/test_dataservice_tasks.py +8 -12
  333. udata/tests/dataset/test_csv_adapter.py +44 -35
  334. udata/tests/dataset/test_dataset_actions.py +2 -3
  335. udata/tests/dataset/test_dataset_commands.py +7 -8
  336. udata/tests/dataset/test_dataset_events.py +36 -29
  337. udata/tests/dataset/test_dataset_model.py +224 -217
  338. udata/tests/dataset/test_dataset_rdf.py +142 -131
  339. udata/tests/dataset/test_dataset_tasks.py +15 -15
  340. udata/tests/dataset/test_resource_preview.py +10 -13
  341. udata/tests/features/territories/__init__.py +9 -13
  342. udata/tests/features/territories/test_territories_api.py +71 -91
  343. udata/tests/forms/test_basic_fields.py +7 -7
  344. udata/tests/forms/test_current_user_field.py +39 -66
  345. udata/tests/forms/test_daterange_field.py +31 -39
  346. udata/tests/forms/test_dict_field.py +28 -26
  347. udata/tests/forms/test_extras_fields.py +102 -76
  348. udata/tests/forms/test_form_field.py +8 -8
  349. udata/tests/forms/test_image_field.py +33 -26
  350. udata/tests/forms/test_model_field.py +134 -123
  351. udata/tests/forms/test_model_list_field.py +7 -7
  352. udata/tests/forms/test_nested_model_list_field.py +117 -79
  353. udata/tests/forms/test_publish_as_field.py +36 -65
  354. udata/tests/forms/test_reference_field.py +34 -53
  355. udata/tests/forms/test_user_forms.py +23 -21
  356. udata/tests/forms/test_uuid_field.py +6 -10
  357. udata/tests/frontend/__init__.py +9 -6
  358. udata/tests/frontend/test_auth.py +7 -6
  359. udata/tests/frontend/test_csv.py +81 -96
  360. udata/tests/frontend/test_hooks.py +43 -43
  361. udata/tests/frontend/test_markdown.py +211 -191
  362. udata/tests/helpers.py +32 -37
  363. udata/tests/models.py +2 -2
  364. udata/tests/organization/test_csv_adapter.py +21 -16
  365. udata/tests/organization/test_notifications.py +11 -18
  366. udata/tests/organization/test_organization_model.py +13 -13
  367. udata/tests/organization/test_organization_rdf.py +29 -22
  368. udata/tests/organization/test_organization_tasks.py +16 -17
  369. udata/tests/plugin.py +79 -73
  370. udata/tests/reuse/test_reuse_model.py +21 -21
  371. udata/tests/reuse/test_reuse_task.py +11 -13
  372. udata/tests/search/__init__.py +11 -12
  373. udata/tests/search/test_adapter.py +60 -70
  374. udata/tests/search/test_query.py +16 -16
  375. udata/tests/search/test_results.py +10 -7
  376. udata/tests/site/test_site_api.py +11 -16
  377. udata/tests/site/test_site_metrics.py +20 -30
  378. udata/tests/site/test_site_model.py +4 -5
  379. udata/tests/site/test_site_rdf.py +94 -78
  380. udata/tests/test_activity.py +17 -17
  381. udata/tests/test_cors.py +62 -0
  382. udata/tests/test_discussions.py +292 -299
  383. udata/tests/test_i18n.py +37 -40
  384. udata/tests/test_linkchecker.py +91 -85
  385. udata/tests/test_mail.py +13 -17
  386. udata/tests/test_migrations.py +219 -180
  387. udata/tests/test_model.py +164 -157
  388. udata/tests/test_notifications.py +17 -17
  389. udata/tests/test_owned.py +14 -14
  390. udata/tests/test_rdf.py +25 -23
  391. udata/tests/test_routing.py +89 -93
  392. udata/tests/test_storages.py +137 -128
  393. udata/tests/test_tags.py +44 -46
  394. udata/tests/test_topics.py +7 -7
  395. udata/tests/test_transfer.py +42 -49
  396. udata/tests/test_uris.py +160 -161
  397. udata/tests/test_utils.py +79 -71
  398. udata/tests/user/test_user_rdf.py +5 -9
  399. udata/tests/workers/test_jobs_commands.py +57 -58
  400. udata/tests/workers/test_tasks_routing.py +23 -29
  401. udata/tests/workers/test_workers_api.py +125 -131
  402. udata/tests/workers/test_workers_helpers.py +6 -6
  403. udata/tracking.py +4 -6
  404. udata/uris.py +45 -46
  405. udata/utils.py +68 -66
  406. udata/wsgi.py +1 -1
  407. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/METADATA +7 -3
  408. udata-9.1.2.dev30454.dist-info/RECORD +706 -0
  409. udata-9.1.2.dev30355.dist-info/RECORD +0 -704
  410. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/LICENSE +0 -0
  411. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/WHEEL +0 -0
  412. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/entry_points.txt +0 -0
  413. {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/top_level.txt +0 -0
@@ -1,122 +1,133 @@
1
1
  import datetime
2
- from udata.models import (
3
- Dataset, Organization, User, GeoZone, License, Topic
4
- )
2
+
3
+ from udata.core.dataset.api import DEFAULT_SORTING, DatasetApiParser
4
+ from udata.core.spatial.constants import ADMIN_LEVEL_MAX
5
+ from udata.core.spatial.models import admin_levels
6
+ from udata.models import Dataset, GeoZone, License, Organization, Topic, User
5
7
  from udata.search import (
6
- ModelSearchAdapter, register,
7
- ModelTermsFilter, BoolFilter, Filter,
8
- TemporalCoverageFilter
8
+ BoolFilter,
9
+ Filter,
10
+ ModelSearchAdapter,
11
+ ModelTermsFilter,
12
+ TemporalCoverageFilter,
13
+ register,
9
14
  )
10
- from udata.core.spatial.models import admin_levels
11
- from udata.core.spatial.constants import ADMIN_LEVEL_MAX
12
- from udata.core.dataset.api import DatasetApiParser, DEFAULT_SORTING
13
15
  from udata.utils import to_iso_datetime
14
16
 
15
- __all__ = ('DatasetSearch', )
17
+ __all__ = ("DatasetSearch",)
16
18
 
17
19
 
18
20
  @register
19
21
  class DatasetSearch(ModelSearchAdapter):
20
22
  model = Dataset
21
- search_url = 'datasets/'
23
+ search_url = "datasets/"
22
24
 
23
25
  sorts = {
24
- 'created': 'created_at_internal',
25
- 'last_update': 'last_modified_internal',
26
- 'reuses': 'metrics.reuses',
27
- 'followers': 'metrics.followers',
28
- 'views': 'metrics.views',
26
+ "created": "created_at_internal",
27
+ "last_update": "last_modified_internal",
28
+ "reuses": "metrics.reuses",
29
+ "followers": "metrics.followers",
30
+ "views": "metrics.views",
29
31
  }
30
32
 
31
33
  filters = {
32
- 'tag': Filter(),
33
- 'badge': Filter(),
34
- 'organization': ModelTermsFilter(model=Organization),
35
- 'owner': ModelTermsFilter(model=User),
36
- 'license': ModelTermsFilter(model=License),
37
- 'geozone': ModelTermsFilter(model=GeoZone),
38
- 'granularity': Filter(),
39
- 'format': Filter(),
40
- 'schema': Filter(),
41
- 'temporal_coverage': TemporalCoverageFilter(),
42
- 'featured': BoolFilter(),
43
- 'topic': ModelTermsFilter(model=Topic),
34
+ "tag": Filter(),
35
+ "badge": Filter(),
36
+ "organization": ModelTermsFilter(model=Organization),
37
+ "owner": ModelTermsFilter(model=User),
38
+ "license": ModelTermsFilter(model=License),
39
+ "geozone": ModelTermsFilter(model=GeoZone),
40
+ "granularity": Filter(),
41
+ "format": Filter(),
42
+ "schema": Filter(),
43
+ "temporal_coverage": TemporalCoverageFilter(),
44
+ "featured": BoolFilter(),
45
+ "topic": ModelTermsFilter(model=Topic),
44
46
  }
45
47
 
46
48
  @classmethod
47
49
  def is_indexable(cls, dataset):
48
- return (dataset.deleted is None and dataset.archived is None and
49
- not dataset.private)
50
+ return dataset.deleted is None and dataset.archived is None and not dataset.private
50
51
 
51
52
  @classmethod
52
53
  def mongo_search(cls, args):
53
54
  datasets = Dataset.objects(archived=None, deleted=None, private=False)
54
55
  datasets = DatasetApiParser.parse_filters(datasets, args)
55
56
 
56
- sort = cls.parse_sort(args['sort']) or ('$text_score' if args['q'] else None) or DEFAULT_SORTING
57
- offset = (args['page'] - 1) * args['page_size']
58
- return datasets.order_by(sort).skip(offset).limit(args['page_size']), datasets.count()
57
+ sort = (
58
+ cls.parse_sort(args["sort"])
59
+ or ("$text_score" if args["q"] else None)
60
+ or DEFAULT_SORTING
61
+ )
62
+ offset = (args["page"] - 1) * args["page_size"]
63
+ return datasets.order_by(sort).skip(offset).limit(args["page_size"]), datasets.count()
59
64
 
60
65
  @classmethod
61
66
  def serialize(cls, dataset):
62
67
  organization = None
63
68
  owner = None
64
69
 
65
- topics = Topic.objects(datasets=dataset).only('id')
70
+ topics = Topic.objects(datasets=dataset).only("id")
66
71
 
67
72
  if dataset.organization:
68
73
  org = Organization.objects(id=dataset.organization.id).first()
69
74
  organization = {
70
- 'id': str(org.id),
71
- 'name': org.name,
72
- 'public_service': 1 if org.public_service else 0,
73
- 'followers': org.metrics.get('followers', 0)
75
+ "id": str(org.id),
76
+ "name": org.name,
77
+ "public_service": 1 if org.public_service else 0,
78
+ "followers": org.metrics.get("followers", 0),
74
79
  }
75
80
  elif dataset.owner:
76
81
  owner = User.objects(id=dataset.owner.id).first()
77
82
 
78
83
  document = {
79
- 'id': str(dataset.id),
80
- 'title': dataset.title,
81
- 'description': dataset.description,
82
- 'acronym': dataset.acronym or None,
83
- 'url': dataset.display_url,
84
- 'tags': dataset.tags,
85
- 'license': getattr(dataset.license, 'id', None),
86
- 'badges': [badge.kind for badge in dataset.badges],
87
- 'frequency': dataset.frequency,
88
- 'created_at': to_iso_datetime(dataset.created_at),
89
- 'last_update': to_iso_datetime(dataset.last_update),
90
- 'views': dataset.metrics.get('views', 0),
91
- 'followers': dataset.metrics.get('followers', 0),
92
- 'reuses': dataset.metrics.get('reuses', 0),
93
- 'featured': 1 if dataset.featured else 0,
94
- 'resources_count': len(dataset.resources),
95
- 'organization': organization,
96
- 'owner': str(owner.id) if owner else None,
97
- 'format': [r.format.lower() for r in dataset.resources if r.format],
98
- 'schema': [r.schema.name for r in dataset.resources if r.schema],
99
- 'topics': [str(t.id) for t in topics if topics],
84
+ "id": str(dataset.id),
85
+ "title": dataset.title,
86
+ "description": dataset.description,
87
+ "acronym": dataset.acronym or None,
88
+ "url": dataset.display_url,
89
+ "tags": dataset.tags,
90
+ "license": getattr(dataset.license, "id", None),
91
+ "badges": [badge.kind for badge in dataset.badges],
92
+ "frequency": dataset.frequency,
93
+ "created_at": to_iso_datetime(dataset.created_at),
94
+ "last_update": to_iso_datetime(dataset.last_update),
95
+ "views": dataset.metrics.get("views", 0),
96
+ "followers": dataset.metrics.get("followers", 0),
97
+ "reuses": dataset.metrics.get("reuses", 0),
98
+ "featured": 1 if dataset.featured else 0,
99
+ "resources_count": len(dataset.resources),
100
+ "organization": organization,
101
+ "owner": str(owner.id) if owner else None,
102
+ "format": [r.format.lower() for r in dataset.resources if r.format],
103
+ "schema": [r.schema.name for r in dataset.resources if r.schema],
104
+ "topics": [str(t.id) for t in topics if topics],
100
105
  }
101
106
  extras = {}
102
107
  for key, value in dataset.extras.items():
103
108
  extras[key] = to_iso_datetime(value) if isinstance(value, datetime.datetime) else value
104
- document.update({'extras': extras})
109
+ document.update({"extras": extras})
105
110
  if dataset.harvest:
106
111
  harvest = {}
107
112
  for key, value in dataset.harvest._data.items():
108
- harvest[key] = to_iso_datetime(value) if isinstance(value, datetime.datetime) else value
109
- document.update({'harvest': harvest})
113
+ harvest[key] = (
114
+ to_iso_datetime(value) if isinstance(value, datetime.datetime) else value
115
+ )
116
+ document.update({"harvest": harvest})
110
117
 
111
- if (dataset.temporal_coverage is not None and
112
- dataset.temporal_coverage.start and
113
- dataset.temporal_coverage.end):
118
+ if (
119
+ dataset.temporal_coverage is not None
120
+ and dataset.temporal_coverage.start
121
+ and dataset.temporal_coverage.end
122
+ ):
114
123
  start = to_iso_datetime(dataset.temporal_coverage.start)
115
124
  end = to_iso_datetime(dataset.temporal_coverage.end)
116
- document.update({
117
- 'temporal_coverage_start': start,
118
- 'temporal_coverage_end': end,
119
- })
125
+ document.update(
126
+ {
127
+ "temporal_coverage_start": start,
128
+ "temporal_coverage_end": end,
129
+ }
130
+ )
120
131
 
121
132
  if dataset.spatial is not None:
122
133
  # Index precise zone labels to allow fast filtering.
@@ -125,13 +136,17 @@ class DatasetSearch(ModelSearchAdapter):
125
136
  geozones = []
126
137
  coverage_level = ADMIN_LEVEL_MAX
127
138
  for zone in zones:
128
- geozones.append({
129
- 'id': zone.id,
130
- 'name': zone.name,
131
- })
139
+ geozones.append(
140
+ {
141
+ "id": zone.id,
142
+ "name": zone.name,
143
+ }
144
+ )
132
145
  coverage_level = min(coverage_level, admin_levels[zone.level])
133
- document.update({
134
- 'geozones': geozones,
135
- 'granularity': dataset.spatial.granularity,
136
- })
146
+ document.update(
147
+ {
148
+ "geozones": geozones,
149
+ "granularity": dataset.spatial.granularity,
150
+ }
151
+ )
137
152
  return document
@@ -3,4 +3,4 @@ from blinker import Namespace
3
3
  namespace = Namespace()
4
4
 
5
5
  #: Trigerred when a dataset is published
6
- on_dataset_published = namespace.signal('on-dataset-published')
6
+ on_dataset_published = namespace.signal("on-dataset-published")
@@ -1,6 +1,5 @@
1
1
  import collections
2
2
  import os
3
-
4
3
  from datetime import datetime, timedelta
5
4
  from tempfile import NamedTemporaryFile
6
5
 
@@ -13,28 +12,29 @@ from udata.core import storages
13
12
  from udata.frontend import csv
14
13
  from udata.harvest.models import HarvestJob
15
14
  from udata.i18n import lazy_gettext as _
16
- from udata.models import (Follow, Discussion, Activity, Topic,
17
- Organization, Transfer, db)
15
+ from udata.models import Activity, Discussion, Follow, Organization, Topic, Transfer, db
18
16
  from udata.tasks import job
19
17
 
20
- from .models import Dataset, Resource, CommunityResource, Checksum
21
18
  from .constants import UPDATE_FREQUENCIES
19
+ from .models import Checksum, CommunityResource, Dataset, Resource
22
20
 
23
21
  log = get_task_logger(__name__)
24
22
 
25
23
 
26
24
  def flatten(iterable):
27
25
  for el in iterable:
28
- if isinstance(el, collections.Iterable) and not (isinstance(el, str) or isinstance(el, db.Document)):
26
+ if isinstance(el, collections.Iterable) and not (
27
+ isinstance(el, str) or isinstance(el, db.Document)
28
+ ):
29
29
  yield from flatten(el)
30
30
  else:
31
31
  yield el
32
32
 
33
33
 
34
- @job('purge-datasets')
34
+ @job("purge-datasets")
35
35
  def purge_datasets(self):
36
36
  for dataset in Dataset.objects(deleted__ne=None):
37
- log.info(f'Purging dataset {dataset}')
37
+ log.info(f"Purging dataset {dataset}")
38
38
  # Remove followers
39
39
  Follow.objects(following=dataset).delete()
40
40
  # Remove discussions
@@ -72,19 +72,23 @@ def purge_datasets(self):
72
72
  dataset.delete()
73
73
 
74
74
 
75
- @job('send-frequency-reminder')
75
+ @job("send-frequency-reminder")
76
76
  def send_frequency_reminder(self):
77
77
  # We exclude irrelevant frequencies.
78
- frequencies = [f for f in UPDATE_FREQUENCIES.keys()
79
- if f not in ('unknown', 'realtime', 'punctual', 'irregular', 'continuous')]
78
+ frequencies = [
79
+ f
80
+ for f in UPDATE_FREQUENCIES.keys()
81
+ if f not in ("unknown", "realtime", "punctual", "irregular", "continuous")
82
+ ]
80
83
  now = datetime.utcnow()
81
84
  reminded_orgs = {}
82
85
  reminded_people = []
83
- allowed_delay = current_app.config['DELAY_BEFORE_REMINDER_NOTIFICATION']
86
+ allowed_delay = current_app.config["DELAY_BEFORE_REMINDER_NOTIFICATION"]
84
87
  for org in Organization.objects.visible():
85
88
  outdated_datasets = []
86
89
  for dataset in Dataset.objects.filter(
87
- frequency__in=frequencies, organization=org).visible():
90
+ frequency__in=frequencies, organization=org
91
+ ).visible():
88
92
  if dataset.next_update + timedelta(days=allowed_delay) < now:
89
93
  dataset.outdated = now - dataset.next_update
90
94
  dataset.frequency_str = UPDATE_FREQUENCIES[dataset.frequency]
@@ -92,23 +96,28 @@ def send_frequency_reminder(self):
92
96
  if outdated_datasets:
93
97
  reminded_orgs[org] = outdated_datasets
94
98
  for reminded_org, datasets in reminded_orgs.items():
95
- print('{org.name} will be emailed for {datasets_nb} datasets'.format(
96
- org=reminded_org, datasets_nb=len(datasets)))
99
+ print(
100
+ "{org.name} will be emailed for {datasets_nb} datasets".format(
101
+ org=reminded_org, datasets_nb=len(datasets)
102
+ )
103
+ )
97
104
  recipients = [m.user for m in reminded_org.members]
98
105
  reminded_people.append(recipients)
99
- subject = _('You need to update some frequency-based datasets')
100
- mail.send(subject, recipients, 'frequency_reminder',
101
- org=reminded_org, datasets=datasets)
106
+ subject = _("You need to update some frequency-based datasets")
107
+ mail.send(subject, recipients, "frequency_reminder", org=reminded_org, datasets=datasets)
102
108
 
103
- print('{nb_orgs} orgs concerned'.format(nb_orgs=len(reminded_orgs)))
109
+ print("{nb_orgs} orgs concerned".format(nb_orgs=len(reminded_orgs)))
104
110
  reminded_people = list(flatten(reminded_people))
105
- print('{nb_emails} people contacted ({nb_emails_twice} twice)'.format(
106
- nb_emails=len(reminded_people),
107
- nb_emails_twice=len(reminded_people) - len(set(reminded_people))))
108
- print('Done')
111
+ print(
112
+ "{nb_emails} people contacted ({nb_emails_twice} twice)".format(
113
+ nb_emails=len(reminded_people),
114
+ nb_emails_twice=len(reminded_people) - len(set(reminded_people)),
115
+ )
116
+ )
117
+ print("Done")
109
118
 
110
119
 
111
- @job('update-datasets-reuses-metrics')
120
+ @job("update-datasets-reuses-metrics")
112
121
  def update_datasets_reuses_metrics(self):
113
122
  all_datasets = Dataset.objects.visible().timeout(False)
114
123
  for dataset in all_datasets:
@@ -117,10 +126,10 @@ def update_datasets_reuses_metrics(self):
117
126
 
118
127
  def get_queryset(model_cls):
119
128
  # special case for resources
120
- if model_cls.__name__ == 'Resource':
121
- model_cls = getattr(udata_models, 'Dataset')
129
+ if model_cls.__name__ == "Resource":
130
+ model_cls = getattr(udata_models, "Dataset")
122
131
  params = {}
123
- attrs = ('private', 'deleted')
132
+ attrs = ("private", "deleted")
124
133
  for attr in attrs:
125
134
  if getattr(model_cls, attr, None):
126
135
  params[attr] = False
@@ -131,7 +140,7 @@ def get_queryset(model_cls):
131
140
  def get_or_create_resource(r_info, model, dataset):
132
141
  resource = None
133
142
  for r in dataset.resources:
134
- if r.extras.get('csv-export:model', '') == model:
143
+ if r.extras.get("csv-export:model", "") == model:
135
144
  resource = r
136
145
  break
137
146
  if resource:
@@ -140,45 +149,45 @@ def get_or_create_resource(r_info, model, dataset):
140
149
  resource.save()
141
150
  return False, resource
142
151
  else:
143
- r_info['extras'] = {'csv-export:model': model}
152
+ r_info["extras"] = {"csv-export:model": model}
144
153
  return True, Resource(**r_info)
145
154
 
146
155
 
147
156
  def store_resource(csvfile, model, dataset):
148
- timestr = datetime.utcnow().strftime('%Y%m%d-%H%M%S')
149
- filename = 'export-%s-%s.csv' % (model, timestr)
150
- prefix = '/'.join((dataset.slug, timestr))
157
+ timestr = datetime.utcnow().strftime("%Y%m%d-%H%M%S")
158
+ filename = "export-%s-%s.csv" % (model, timestr)
159
+ prefix = "/".join((dataset.slug, timestr))
151
160
  storage = storages.resources
152
- with open(csvfile.name, 'rb') as infile:
161
+ with open(csvfile.name, "rb") as infile:
153
162
  stored_filename = storage.save(infile, prefix=prefix, filename=filename)
154
163
  r_info = storage.metadata(stored_filename)
155
- r_info['last_modified_internal'] = r_info.pop('modified')
156
- r_info['fs_filename'] = stored_filename
157
- checksum = r_info.pop('checksum')
158
- algo, checksum = checksum.split(':', 1)
159
- r_info['format'] = storages.utils.extension(stored_filename)
160
- r_info['checksum'] = Checksum(type=algo, value=checksum)
161
- r_info['filesize'] = r_info.pop('size')
162
- del r_info['filename']
163
- r_info['title'] = filename
164
+ r_info["last_modified_internal"] = r_info.pop("modified")
165
+ r_info["fs_filename"] = stored_filename
166
+ checksum = r_info.pop("checksum")
167
+ algo, checksum = checksum.split(":", 1)
168
+ r_info["format"] = storages.utils.extension(stored_filename)
169
+ r_info["checksum"] = Checksum(type=algo, value=checksum)
170
+ r_info["filesize"] = r_info.pop("size")
171
+ del r_info["filename"]
172
+ r_info["title"] = filename
164
173
  return get_or_create_resource(r_info, model, dataset)
165
174
 
166
175
 
167
176
  def export_csv_for_model(model, dataset):
168
177
  model_cls = getattr(udata_models, model.capitalize(), None)
169
178
  if not model_cls:
170
- log.error('Unknow model %s' % model)
179
+ log.error("Unknow model %s" % model)
171
180
  return
172
181
  queryset = get_queryset(model_cls)
173
182
  adapter = csv.get_adapter(model_cls)
174
183
  if not adapter:
175
- log.error('No adapter found for %s' % model)
184
+ log.error("No adapter found for %s" % model)
176
185
  return
177
186
  adapter = adapter(queryset)
178
187
 
179
- log.info('Exporting CSV for %s...' % model)
188
+ log.info("Exporting CSV for %s..." % model)
180
189
 
181
- csvfile = NamedTemporaryFile(mode='w', encoding='utf8', delete=False)
190
+ csvfile = NamedTemporaryFile(mode="w", encoding="utf8", delete=False)
182
191
  try:
183
192
  # write adapter results into a tmp file
184
193
  writer = csv.get_writer(csvfile)
@@ -198,27 +207,27 @@ def export_csv_for_model(model, dataset):
198
207
  os.unlink(csvfile.name)
199
208
 
200
209
 
201
- @job('export-csv')
210
+ @job("export-csv")
202
211
  def export_csv(self, model=None):
203
- '''
212
+ """
204
213
  Generates a CSV export of all model objects as a resource of a dataset
205
- '''
206
- ALLOWED_MODELS = current_app.config.get('EXPORT_CSV_MODELS', [])
207
- DATASET_ID = current_app.config.get('EXPORT_CSV_DATASET_ID')
214
+ """
215
+ ALLOWED_MODELS = current_app.config.get("EXPORT_CSV_MODELS", [])
216
+ DATASET_ID = current_app.config.get("EXPORT_CSV_DATASET_ID")
208
217
 
209
218
  if model and model not in ALLOWED_MODELS:
210
- log.error('Unknown or unallowed model: %s' % model)
219
+ log.error("Unknown or unallowed model: %s" % model)
211
220
  return
212
221
 
213
222
  if not DATASET_ID:
214
- log.error('EXPORT_CSV_DATASET_ID setting value not set')
223
+ log.error("EXPORT_CSV_DATASET_ID setting value not set")
215
224
  return
216
225
  try:
217
226
  dataset = Dataset.objects.get(id=DATASET_ID)
218
227
  except Dataset.DoesNotExist:
219
- log.error('EXPORT_CSV_DATASET_ID points to a non existent dataset')
228
+ log.error("EXPORT_CSV_DATASET_ID points to a non existent dataset")
220
229
  return
221
230
 
222
- models = (model, ) if model else ALLOWED_MODELS
231
+ models = (model,) if model else ALLOWED_MODELS
223
232
  for model in models:
224
233
  export_csv_for_model(model, dataset)
@@ -1,20 +1,20 @@
1
- from udata.models import Reuse, Dataset
1
+ from udata.models import Dataset, Reuse
2
2
 
3
3
  from .models import Discussion
4
4
 
5
5
 
6
6
  def discussions_for(user, only_open=True):
7
- '''
7
+ """
8
8
  Build a queryset to query discussions related to a given user's assets.
9
9
 
10
10
  It includes discussions coming from the user's organizations
11
11
 
12
12
  :param bool only_open: whether to include closed discussions or not.
13
- '''
13
+ """
14
14
  # Only fetch required fields for discussion filtering (id and slug)
15
15
  # Greatly improve performances and memory usage
16
- datasets = Dataset.objects.owned_by(user.id, *user.organizations).only('id', 'slug')
17
- reuses = Reuse.objects.owned_by(user.id, *user.organizations).only('id', 'slug')
16
+ datasets = Dataset.objects.owned_by(user.id, *user.organizations).only("id", "slug")
17
+ reuses = Reuse.objects.owned_by(user.id, *user.organizations).only("id", "slug")
18
18
 
19
19
  qs = Discussion.objects(subject__in=list(datasets) + list(reuses))
20
20
  if only_open: