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/harvest/filters.py CHANGED
@@ -1,18 +1,17 @@
1
1
  import dateutil.parser
2
-
3
2
  from voluptuous import Invalid
4
3
 
5
4
  from udata import tags, uris
6
5
 
7
6
 
8
7
  def boolean(value):
9
- '''
8
+ """
10
9
  Convert the content of a string (or a number) to a boolean.
11
10
  Do nothing when input value is already a boolean.
12
11
 
13
12
  This filter accepts usual values for ``True`` and ``False``:
14
13
  "0", "f", "false", "n", etc.
15
- '''
14
+ """
16
15
  if value is None or isinstance(value, bool):
17
16
  return value
18
17
 
@@ -22,29 +21,29 @@ def boolean(value):
22
21
  lower_value = value.strip().lower()
23
22
  if not lower_value:
24
23
  return None
25
- if lower_value in ('f', 'false', 'n', 'no', 'off'):
24
+ if lower_value in ("f", "false", "n", "no", "off"):
26
25
  return False
27
- if lower_value in ('on', 't', 'true', 'y', 'yes'):
26
+ if lower_value in ("on", "t", "true", "y", "yes"):
28
27
  return True
29
- raise Invalid('Unable to parse boolean {0}'.format(value))
28
+ raise Invalid("Unable to parse boolean {0}".format(value))
30
29
 
31
30
 
32
31
  def to_date(value):
33
- '''Parse a date'''
32
+ """Parse a date"""
34
33
  return dateutil.parser.parse(value).date()
35
34
 
36
35
 
37
36
  def email(value):
38
- '''Validate an email'''
39
- if '@' not in value:
40
- raise Invalid('This email is invalid.')
37
+ """Validate an email"""
38
+ if "@" not in value:
39
+ raise Invalid("This email is invalid.")
41
40
  return value
42
41
 
43
42
 
44
43
  def force_list(value):
45
- '''
44
+ """
46
45
  Ensure single elements are wrapped into list
47
- '''
46
+ """
48
47
  if not isinstance(value, (list, tuple)):
49
48
  return [value]
50
49
  return value
@@ -63,12 +62,12 @@ def taglist(value):
63
62
 
64
63
 
65
64
  def empty_none(value):
66
- '''Replace falsy values with None'''
65
+ """Replace falsy values with None"""
67
66
  return value if value else None
68
67
 
69
68
 
70
69
  def strip(value):
71
- '''Strip spaces from a string and remove it when empty.'''
70
+ """Strip spaces from a string and remove it when empty."""
72
71
  return empty_none(value.strip())
73
72
 
74
73
 
@@ -76,37 +75,39 @@ def line_endings(value):
76
75
  """Replaces CR + LF or CR to LF in a string,
77
76
  then strip spaces and remove it when empty.
78
77
  """
79
- return value.replace('\r\n', '\n').replace('\r', '\n')
78
+ return value.replace("\r\n", "\n").replace("\r", "\n")
80
79
 
81
80
 
82
81
  def normalize_string(value):
83
82
  return strip(line_endings(value))
84
83
 
85
84
 
86
- def is_url(default_scheme='http', **kwargs):
85
+ def is_url(default_scheme="http", **kwargs):
87
86
  """Return a converter that converts a clean string to an URL."""
87
+
88
88
  def converter(value):
89
89
  if value is None:
90
90
  return value
91
- if '://' not in value and default_scheme:
92
- value = '://'.join((default_scheme, value.strip()))
91
+ if "://" not in value and default_scheme:
92
+ value = "://".join((default_scheme, value.strip()))
93
93
  try:
94
94
  return uris.validate(value)
95
95
  except uris.ValidationError as e:
96
96
  raise Invalid(str(e))
97
+
97
98
  return converter
98
99
 
99
100
 
100
101
  def hash(value):
101
- '''Detect an hash type'''
102
+ """Detect an hash type"""
102
103
  if not value:
103
104
  return
104
105
  elif len(value) == 32:
105
- type = 'md5'
106
+ type = "md5"
106
107
  elif len(value) == 40:
107
- type = 'sha1'
108
+ type = "sha1"
108
109
  elif len(value) == 64:
109
- type = 'sha256'
110
+ type = "sha256"
110
111
  else:
111
112
  return None
112
- return {'type': type, 'value': value}
113
+ return {"type": type, "value": value}
udata/harvest/forms.py CHANGED
@@ -1,18 +1,18 @@
1
- from udata.utils import safe_unicode
2
1
  from udata.forms import Form, fields, validators
3
2
  from udata.i18n import lazy_gettext as _
4
-
3
+ from udata.utils import safe_unicode
5
4
 
6
5
  from .actions import list_backends
7
- from .models import VALIDATION_STATES, VALIDATION_REFUSED
6
+ from .models import VALIDATION_REFUSED, VALIDATION_STATES
8
7
 
9
- __all__ = 'HarvestSourceForm', 'HarvestSourceValidationForm'
8
+ __all__ = "HarvestSourceForm", "HarvestSourceValidationForm"
10
9
 
11
10
 
12
11
  class HarvestConfigField(fields.DictField):
13
- '''
12
+ """
14
13
  A DictField with extras validations on known configurations
15
- '''
14
+ """
15
+
16
16
  def get_backend(self, form):
17
17
  return next(b for b in list_backends() if b.name == form.backend.data)
18
18
 
@@ -28,27 +28,27 @@ class HarvestConfigField(fields.DictField):
28
28
  if self.data:
29
29
  backend = self.get_backend(form)
30
30
  # Validate filters
31
- for f in (self.data.get('filters') or []):
32
- if not ('key' in f and 'value' in f):
33
- msg = 'A field should have both key and value properties'
31
+ for f in self.data.get("filters") or []:
32
+ if not ("key" in f and "value" in f):
33
+ msg = "A field should have both key and value properties"
34
34
  raise validators.ValidationError(msg)
35
- specs = self.get_filter_specs(backend, f['key'])
35
+ specs = self.get_filter_specs(backend, f["key"])
36
36
  if not specs:
37
37
  msg = 'Unknown filter key "{0}" for "{1}" backend'
38
- msg = msg.format(f['key'], backend.name)
38
+ msg = msg.format(f["key"], backend.name)
39
39
  raise validators.ValidationError(msg)
40
40
 
41
- if isinstance(f['value'], str):
42
- f['value'] = safe_unicode(f['value']) # Fix encoding error
41
+ if isinstance(f["value"], str):
42
+ f["value"] = safe_unicode(f["value"]) # Fix encoding error
43
43
 
44
- if not isinstance(f['value'], specs.type):
44
+ if not isinstance(f["value"], specs.type):
45
45
  msg = '"{0}" filter should of type "{1}"'
46
46
  msg = msg.format(specs.key, specs.type.__name__)
47
47
  raise validators.ValidationError(msg)
48
48
  # Validate features
49
- for key, value in (self.data.get('features') or {}).items():
49
+ for key, value in (self.data.get("features") or {}).items():
50
50
  if not isinstance(value, bool):
51
- msg = 'A feature should be a boolean'
51
+ msg = "A feature should be a boolean"
52
52
  raise validators.ValidationError(msg)
53
53
  if not self.get_feature_specs(backend, key):
54
54
  msg = 'Unknown feature "{0}" for "{1}" backend'
@@ -57,16 +57,16 @@ class HarvestConfigField(fields.DictField):
57
57
 
58
58
 
59
59
  class HarvestSourceForm(Form):
60
- name = fields.StringField(_('Name'), [validators.DataRequired()])
60
+ name = fields.StringField(_("Name"), [validators.DataRequired()])
61
61
  description = fields.MarkdownField(
62
- _('Description'),
63
- description=_('Some optional details about this harvester'))
64
- url = fields.URLField(_('URL'), [validators.DataRequired()])
65
- backend = fields.SelectField(_('Backend'), choices=lambda: [
66
- (b.name, b.display_name) for b in list_backends()
67
- ])
62
+ _("Description"), description=_("Some optional details about this harvester")
63
+ )
64
+ url = fields.URLField(_("URL"), [validators.DataRequired()])
65
+ backend = fields.SelectField(
66
+ _("Backend"), choices=lambda: [(b.name, b.display_name) for b in list_backends()]
67
+ )
68
68
  owner = fields.CurrentUserField()
69
- organization = fields.PublishAsField(_('Publish as'))
69
+ organization = fields.PublishAsField(_("Publish as"))
70
70
  active = fields.BooleanField()
71
71
  autoarchive = fields.BooleanField()
72
72
 
@@ -75,7 +75,6 @@ class HarvestSourceForm(Form):
75
75
 
76
76
  class HarvestSourceValidationForm(Form):
77
77
  state = fields.SelectField(choices=list(VALIDATION_STATES.items()))
78
- comment = fields.StringField(_('Comment'),
79
- [validators.RequiredIfVal('state',
80
- VALIDATION_REFUSED
81
- )])
78
+ comment = fields.StringField(
79
+ _("Comment"), [validators.RequiredIfVal("state", VALIDATION_REFUSED)]
80
+ )
udata/harvest/models.py CHANGED
@@ -1,65 +1,75 @@
1
+ import logging
1
2
  from collections import OrderedDict
2
3
  from datetime import datetime
3
- import logging
4
4
  from urllib.parse import urlparse
5
5
 
6
- from udata.core.dataservices.models import Dataservice
7
6
  from werkzeug.utils import cached_property
8
7
 
8
+ from udata.core.dataservices.models import Dataservice
9
9
  from udata.core.dataset.models import HarvestDatasetMetadata
10
- from udata.models import db, Dataset
11
- from udata.i18n import lazy_gettext as _
12
10
  from udata.core.owned import Owned, OwnedQuerySet
11
+ from udata.i18n import lazy_gettext as _
12
+ from udata.models import Dataset, db
13
13
 
14
14
  log = logging.getLogger(__name__)
15
15
 
16
- HARVEST_FREQUENCIES = OrderedDict((
17
- ('manual', _('Manual')),
18
- ('monthly', _('Monthly')),
19
- ('weekly', _('Weekly')),
20
- ('daily', _('Daily')),
21
- ))
22
-
23
- HARVEST_JOB_STATUS = OrderedDict((
24
- ('pending', _('Pending')),
25
- ('initializing', _('Initializing')),
26
- ('initialized', _('Initialized')),
27
- ('processing', _('Processing')),
28
- ('done', _('Done')),
29
- ('done-errors', _('Done with errors')),
30
- ('failed', _('Failed')),
31
- ))
32
-
33
- HARVEST_ITEM_STATUS = OrderedDict((
34
- ('pending', _('Pending')),
35
- ('started', _('Started')),
36
- ('done', _('Done')),
37
- ('failed', _('Failed')),
38
- ('skipped', _('Skipped')),
39
- ('archived', _('Archived'))
40
- ))
41
-
42
- DEFAULT_HARVEST_FREQUENCY = 'manual'
43
- DEFAULT_HARVEST_JOB_STATUS = 'pending'
44
- DEFAULT_HARVEST_ITEM_STATUS = 'pending'
16
+ HARVEST_FREQUENCIES = OrderedDict(
17
+ (
18
+ ("manual", _("Manual")),
19
+ ("monthly", _("Monthly")),
20
+ ("weekly", _("Weekly")),
21
+ ("daily", _("Daily")),
22
+ )
23
+ )
24
+
25
+ HARVEST_JOB_STATUS = OrderedDict(
26
+ (
27
+ ("pending", _("Pending")),
28
+ ("initializing", _("Initializing")),
29
+ ("initialized", _("Initialized")),
30
+ ("processing", _("Processing")),
31
+ ("done", _("Done")),
32
+ ("done-errors", _("Done with errors")),
33
+ ("failed", _("Failed")),
34
+ )
35
+ )
36
+
37
+ HARVEST_ITEM_STATUS = OrderedDict(
38
+ (
39
+ ("pending", _("Pending")),
40
+ ("started", _("Started")),
41
+ ("done", _("Done")),
42
+ ("failed", _("Failed")),
43
+ ("skipped", _("Skipped")),
44
+ ("archived", _("Archived")),
45
+ )
46
+ )
47
+
48
+ DEFAULT_HARVEST_FREQUENCY = "manual"
49
+ DEFAULT_HARVEST_JOB_STATUS = "pending"
50
+ DEFAULT_HARVEST_ITEM_STATUS = "pending"
45
51
 
46
52
 
47
53
  class HarvestError(db.EmbeddedDocument):
48
- '''Store harvesting errors'''
54
+ """Store harvesting errors"""
55
+
49
56
  created_at = db.DateTimeField(default=datetime.utcnow, required=True)
50
57
  message = db.StringField()
51
58
  details = db.StringField()
52
59
 
60
+
53
61
  class HarvestLog(db.EmbeddedDocument):
54
62
  level = db.StringField()
55
63
  message = db.StringField()
56
64
 
65
+
57
66
  class HarvestItem(db.EmbeddedDocument):
58
67
  remote_id = db.StringField()
59
68
  dataset = db.ReferenceField(Dataset)
60
69
  dataservice = db.ReferenceField(Dataservice)
61
- status = db.StringField(choices=list(HARVEST_ITEM_STATUS),
62
- default=DEFAULT_HARVEST_ITEM_STATUS, required=True)
70
+ status = db.StringField(
71
+ choices=list(HARVEST_ITEM_STATUS), default=DEFAULT_HARVEST_ITEM_STATUS, required=True
72
+ )
63
73
  created = db.DateTimeField(default=datetime.utcnow, required=True)
64
74
  started = db.DateTimeField()
65
75
  ended = db.DateTimeField()
@@ -69,23 +79,24 @@ class HarvestItem(db.EmbeddedDocument):
69
79
  kwargs = db.DictField()
70
80
 
71
81
 
72
- VALIDATION_ACCEPTED = 'accepted'
73
- VALIDATION_REFUSED = 'refused'
74
- VALIDATION_PENDING = 'pending'
82
+ VALIDATION_ACCEPTED = "accepted"
83
+ VALIDATION_REFUSED = "refused"
84
+ VALIDATION_PENDING = "pending"
75
85
 
76
86
  VALIDATION_STATES = {
77
- VALIDATION_PENDING: _('Pending'),
78
- VALIDATION_ACCEPTED: _('Accepted'),
79
- VALIDATION_REFUSED: _('Refused'),
87
+ VALIDATION_PENDING: _("Pending"),
88
+ VALIDATION_ACCEPTED: _("Accepted"),
89
+ VALIDATION_REFUSED: _("Refused"),
80
90
  }
81
91
 
82
92
 
83
93
  class HarvestSourceValidation(db.EmbeddedDocument):
84
- '''Store harvest source validation details'''
85
- state = db.StringField(choices=list(VALIDATION_STATES),
86
- default=VALIDATION_PENDING,
87
- required=True)
88
- by = db.ReferenceField('User')
94
+ """Store harvest source validation details"""
95
+
96
+ state = db.StringField(
97
+ choices=list(VALIDATION_STATES), default=VALIDATION_PENDING, required=True
98
+ )
99
+ by = db.ReferenceField("User")
89
100
  on = db.DateTimeField()
90
101
  comment = db.StringField()
91
102
 
@@ -97,29 +108,28 @@ class HarvestSourceQuerySet(OwnedQuerySet):
97
108
 
98
109
  class HarvestSource(Owned, db.Document):
99
110
  name = db.StringField(max_length=255)
100
- slug = db.SlugField(max_length=255, required=True, unique=True,
101
- populate_from='name', update=True)
111
+ slug = db.SlugField(
112
+ max_length=255, required=True, unique=True, populate_from="name", update=True
113
+ )
102
114
  description = db.StringField()
103
115
  url = db.StringField(required=True)
104
116
  backend = db.StringField(required=True)
105
117
  config = db.DictField()
106
- periodic_task = db.ReferenceField('PeriodicTask',
107
- reverse_delete_rule=db.NULLIFY)
118
+ periodic_task = db.ReferenceField("PeriodicTask", reverse_delete_rule=db.NULLIFY)
108
119
  created_at = db.DateTimeField(default=datetime.utcnow, required=True)
109
- frequency = db.StringField(choices=list(HARVEST_FREQUENCIES),
110
- default=DEFAULT_HARVEST_FREQUENCY,
111
- required=True)
120
+ frequency = db.StringField(
121
+ choices=list(HARVEST_FREQUENCIES), default=DEFAULT_HARVEST_FREQUENCY, required=True
122
+ )
112
123
  active = db.BooleanField(default=True)
113
124
  autoarchive = db.BooleanField(default=True)
114
- validation = db.EmbeddedDocumentField(HarvestSourceValidation,
115
- default=HarvestSourceValidation)
125
+ validation = db.EmbeddedDocumentField(HarvestSourceValidation, default=HarvestSourceValidation)
116
126
 
117
127
  deleted = db.DateTimeField()
118
128
 
119
129
  @property
120
130
  def domain(self):
121
131
  parsed = urlparse(self.url)
122
- return parsed.netloc.split(':')[0]
132
+ return parsed.netloc.split(":")[0]
123
133
 
124
134
  @classmethod
125
135
  def get(cls, ident):
@@ -128,9 +138,9 @@ class HarvestSource(Owned, db.Document):
128
138
  def get_last_job(self, reduced=False):
129
139
  qs = HarvestJob.objects(source=self)
130
140
  if reduced:
131
- qs = qs.exclude('source', 'items', 'errors', 'data')
141
+ qs = qs.exclude("source", "items", "errors", "data")
132
142
  qs = qs.no_dereference()
133
- return qs.order_by('-created').first()
143
+ return qs.order_by("-created").first()
134
144
 
135
145
  @cached_property
136
146
  def last_job(self):
@@ -143,48 +153,46 @@ class HarvestSource(Owned, db.Document):
143
153
  return self.periodic_task.schedule_display
144
154
 
145
155
  meta = {
146
- 'indexes': [
147
- '-created_at',
148
- 'slug',
149
- ('deleted', '-created_at'),
150
- ] + Owned.meta['indexes'],
151
- 'ordering': ['-created_at'],
152
- 'queryset_class': HarvestSourceQuerySet,
156
+ "indexes": [
157
+ "-created_at",
158
+ "slug",
159
+ ("deleted", "-created_at"),
160
+ ]
161
+ + Owned.meta["indexes"],
162
+ "ordering": ["-created_at"],
163
+ "queryset_class": HarvestSourceQuerySet,
153
164
  }
154
165
 
155
166
  def __str__(self):
156
- return self.name or ''
167
+ return self.name or ""
157
168
 
158
169
 
159
170
  class HarvestJob(db.Document):
160
- '''Keep track of harvestings'''
171
+ """Keep track of harvestings"""
172
+
161
173
  created = db.DateTimeField(default=datetime.utcnow, required=True)
162
174
  started = db.DateTimeField()
163
175
  ended = db.DateTimeField()
164
- status = db.StringField(choices=list(HARVEST_JOB_STATUS),
165
- default=DEFAULT_HARVEST_JOB_STATUS, required=True)
176
+ status = db.StringField(
177
+ choices=list(HARVEST_JOB_STATUS), default=DEFAULT_HARVEST_JOB_STATUS, required=True
178
+ )
166
179
  errors = db.ListField(db.EmbeddedDocumentField(HarvestError))
167
180
  items = db.ListField(db.EmbeddedDocumentField(HarvestItem))
168
181
  source = db.ReferenceField(HarvestSource, reverse_delete_rule=db.CASCADE)
169
182
  data = db.DictField()
170
183
 
171
184
  meta = {
172
- 'indexes': [
173
- '-created',
174
- 'source',
175
- ('source', '-created'),
176
- 'items.dataset'
177
- ],
178
- 'ordering': ['-created'],
185
+ "indexes": ["-created", "source", ("source", "-created"), "items.dataset"],
186
+ "ordering": ["-created"],
179
187
  }
180
188
 
181
189
 
182
190
  def archive_harvested_dataset(dataset, reason, dryrun=False):
183
- '''
191
+ """
184
192
  Archive an harvested dataset, setting extras accordingly.
185
193
  If `dryrun` is True, the dataset is not saved but validated only.
186
- '''
187
- log.debug('Archiving dataset %s', dataset.id)
194
+ """
195
+ log.debug("Archiving dataset %s", dataset.id)
188
196
  archival_date = datetime.utcnow()
189
197
  dataset.archived = archival_date
190
198
  if not dataset.harvest:
@@ -1,15 +1,15 @@
1
- from udata.features.notifications.actions import notifier
1
+ import logging
2
2
 
3
- from .models import HarvestSource, VALIDATION_PENDING
3
+ from udata.features.notifications.actions import notifier
4
4
 
5
- import logging
5
+ from .models import VALIDATION_PENDING, HarvestSource
6
6
 
7
7
  log = logging.getLogger(__name__)
8
8
 
9
9
 
10
- @notifier('validate_harvester')
10
+ @notifier("validate_harvester")
11
11
  def validate_harvester_notifications(user):
12
- '''Notify admins about pending harvester validation'''
12
+ """Notify admins about pending harvester validation"""
13
13
  if not user.sysadmin:
14
14
  return []
15
15
 
@@ -18,12 +18,17 @@ def validate_harvester_notifications(user):
18
18
  # Only fetch required fields for notification serialization
19
19
  # Greatly improve performances and memory usage
20
20
  qs = HarvestSource.objects(validation__state=VALIDATION_PENDING)
21
- qs = qs.only('id', 'created_at', 'name')
21
+ qs = qs.only("id", "created_at", "name")
22
22
 
23
23
  for source in qs:
24
- notifications.append((source.created_at, {
25
- 'id': source.id,
26
- 'name': source.name,
27
- }))
24
+ notifications.append(
25
+ (
26
+ source.created_at,
27
+ {
28
+ "id": source.id,
29
+ "name": source.name,
30
+ },
31
+ )
32
+ )
28
33
 
29
34
  return notifications
udata/harvest/signals.py CHANGED
@@ -7,40 +7,40 @@ log = logging.getLogger(__name__)
7
7
  ns = Namespace()
8
8
 
9
9
  #: Sent when a new HarvestSource is created
10
- harvest_source_created = ns.signal('harvest:source-created')
10
+ harvest_source_created = ns.signal("harvest:source-created")
11
11
 
12
12
  #: Sent when a HarvestSource is updated
13
- harvest_source_updated = ns.signal('harvest:source-updated')
13
+ harvest_source_updated = ns.signal("harvest:source-updated")
14
14
 
15
15
  #: Sent when a HarvestSource is deleted
16
- harvest_source_deleted = ns.signal('harvest:source-deleted')
16
+ harvest_source_deleted = ns.signal("harvest:source-deleted")
17
17
 
18
18
  #: Run before each harvest job
19
- before_harvest_job = ns.signal('harvest:before-job')
19
+ before_harvest_job = ns.signal("harvest:before-job")
20
20
 
21
21
  #: Run before each harvest job
22
- after_harvest_job = ns.signal('harvest:after-job')
22
+ after_harvest_job = ns.signal("harvest:after-job")
23
23
 
24
24
  #: Sent when a new HarvestJob started
25
- harvest_job_started = ns.signal('harvest:job-started')
25
+ harvest_job_started = ns.signal("harvest:job-started")
26
26
 
27
27
  #: Sent when a HarvestJob is done
28
- harvest_job_done = ns.signal('harvest:job-done')
28
+ harvest_job_done = ns.signal("harvest:job-done")
29
29
 
30
30
  #: Sent when a HarvestJob failed
31
- harvest_job_failed = ns.signal('harvest:job-failed')
31
+ harvest_job_failed = ns.signal("harvest:job-failed")
32
32
 
33
33
  #: Sent when a HarvestItem start
34
- harvest_item_started = ns.signal('harvest:item-started')
34
+ harvest_item_started = ns.signal("harvest:item-started")
35
35
 
36
36
  #: Sent when a HarvestItem is done
37
- harvest_item_done = ns.signal('harvest:item-done')
37
+ harvest_item_done = ns.signal("harvest:item-done")
38
38
 
39
39
  #: Sent when a HarvestItem failed
40
- harvest_item_failed = ns.signal('harvest:item-failed')
40
+ harvest_item_failed = ns.signal("harvest:item-failed")
41
41
 
42
42
  #: Sent when a HarvestSource is scheduled
43
- harvest_source_scheduled = ns.signal('harvest:source-scheduled')
43
+ harvest_source_scheduled = ns.signal("harvest:source-scheduled")
44
44
 
45
45
  #: Sent when a HarvestSource is unscheduled
46
- harvest_source_unscheduled = ns.signal('harvest:source-unscheduled')
46
+ harvest_source_unscheduled = ns.signal("harvest:source-unscheduled")
udata/harvest/tasks.py CHANGED
@@ -1,15 +1,15 @@
1
1
  from celery import chord
2
2
  from flask import current_app
3
3
 
4
- from udata.tasks import job, get_logger, task
4
+ from udata.tasks import get_logger, job, task
5
5
 
6
6
  from . import backends
7
- from .models import HarvestSource, HarvestJob
7
+ from .models import HarvestJob, HarvestSource
8
8
 
9
9
  log = get_logger(__name__)
10
10
 
11
11
 
12
- @job('harvest', route='low.harvest')
12
+ @job("harvest", route="low.harvest")
13
13
  def harvest(self, ident):
14
14
  log.info('Launching harvest job for source "%s"', ident)
15
15
 
@@ -21,9 +21,8 @@ def harvest(self, ident):
21
21
 
22
22
  backend.harvest()
23
23
 
24
-
25
24
 
26
- @task(ignore_result=False, route='low.harvest')
25
+ @task(ignore_result=False, route="low.harvest")
27
26
  def harvest_job_item(job_id, item_id):
28
27
  log.info('Harvesting item %s for job "%s"', item_id, job_id)
29
28
 
@@ -37,7 +36,7 @@ def harvest_job_item(job_id, item_id):
37
36
  return item_id
38
37
 
39
38
 
40
- @task(ignore_result=False, route='low.harvest')
39
+ @task(ignore_result=False, route="low.harvest")
41
40
  def harvest_job_finalize(results, job_id):
42
41
  log.info('Finalize harvesting for job "%s"', job_id)
43
42
  job = HarvestJob.objects.get(pk=job_id)
@@ -46,15 +45,17 @@ def harvest_job_finalize(results, job_id):
46
45
  backend.finalize()
47
46
 
48
47
 
49
- @job('purge-harvesters', route='low.harvest')
48
+ @job("purge-harvesters", route="low.harvest")
50
49
  def purge_harvest_sources(self):
51
- log.info('Purging HarvestSources flagged as deleted')
50
+ log.info("Purging HarvestSources flagged as deleted")
52
51
  from .actions import purge_sources
52
+
53
53
  purge_sources()
54
54
 
55
55
 
56
- @job('purge-harvest-jobs', route='low.harvest')
56
+ @job("purge-harvest-jobs", route="low.harvest")
57
57
  def purge_harvest_jobs(self):
58
- log.info('Purging HarvestJobs older than retention policy')
58
+ log.info("Purging HarvestJobs older than retention policy")
59
59
  from .actions import purge_jobs
60
+
60
61
  purge_jobs()