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/api.py CHANGED
@@ -1,217 +1,267 @@
1
1
  from bson import ObjectId
2
- from werkzeug.exceptions import BadRequest
3
2
  from flask import current_app, request
3
+ from werkzeug.exceptions import BadRequest
4
4
 
5
- from udata.api import api, API, fields
5
+ from udata.api import API, api, fields
6
6
  from udata.auth import admin_permission
7
-
8
7
  from udata.core.dataservices.models import Dataservice
9
- from udata.core.dataset.api_fields import dataset_ref_fields, dataset_fields
8
+ from udata.core.dataset.api_fields import dataset_fields, dataset_ref_fields
10
9
  from udata.core.dataset.permissions import OwnablePermission
11
10
  from udata.core.organization.api_fields import org_ref_fields
12
- from udata.core.dataset.permissions import OwnablePermission
13
11
  from udata.core.organization.permissions import EditOrganizationPermission
14
12
  from udata.core.user.api_fields import user_ref_fields
15
13
 
16
14
  from . import actions
17
15
  from .forms import HarvestSourceForm, HarvestSourceValidationForm
18
16
  from .models import (
19
- HARVEST_JOB_STATUS, HARVEST_ITEM_STATUS, HarvestJob, HarvestSource,
20
- VALIDATION_STATES, VALIDATION_ACCEPTED
17
+ HARVEST_ITEM_STATUS,
18
+ HARVEST_JOB_STATUS,
19
+ VALIDATION_ACCEPTED,
20
+ VALIDATION_STATES,
21
+ HarvestJob,
22
+ HarvestSource,
21
23
  )
22
24
 
23
- ns = api.namespace('harvest', 'Harvest related operations')
25
+ ns = api.namespace("harvest", "Harvest related operations")
24
26
 
25
27
 
26
28
  def backends_ids():
27
29
  return [b.name for b in actions.list_backends()]
28
30
 
29
31
 
30
- error_fields = api.model('HarvestError', {
31
- 'created_at': fields.ISODateTime(description='The error creation date',
32
- required=True, readonly=True),
33
- 'message': fields.String(description='The error short message',
34
- required=True),
35
- 'details': fields.String(description='Optional details (ie. stacktrace)'),
36
- })
37
-
38
-
39
- log_fields = api.model('HarvestError', {
40
- 'level': fields.String(required=True),
41
- 'message': fields.String(required=True),
42
- })
43
-
44
-
45
- item_fields = api.model('HarvestItem', {
46
- 'remote_id': fields.String(description='The item remote ID to process',
47
- required=True),
48
- 'dataset': fields.Nested(dataset_ref_fields,
49
- description='The processed dataset',
50
- allow_null=True),
51
- 'dataservice': fields.Nested(Dataservice.__read_fields__,
52
- description='The processed dataservice',
53
- allow_null=True),
54
- 'status': fields.String(description='The item status',
55
- required=True,
56
- enum=list(HARVEST_ITEM_STATUS)),
57
- 'created': fields.ISODateTime(description='The item creation date',
58
- required=True),
59
- 'started': fields.ISODateTime(description='The item start date'),
60
- 'ended': fields.ISODateTime(description='The item end date'),
61
- 'errors': fields.List(fields.Nested(error_fields),
62
- description='The item errors'),
63
- 'logs': fields.List(fields.Nested(log_fields),
64
- description='The item logs'),
65
- 'args': fields.List(fields.String,
66
- description='The item positional arguments',
67
- default=[]),
68
- 'kwargs': fields.Raw(description='The item keyword arguments',
69
- default={}),
70
- })
71
-
72
- job_fields = api.model('HarvestJob', {
73
- 'id': fields.String(description='The job execution ID', required=True),
74
- 'created': fields.ISODateTime(description='The job creation date',
75
- required=True),
76
- 'started': fields.ISODateTime(description='The job start date'),
77
- 'ended': fields.ISODateTime(description='The job end date'),
78
- 'status': fields.String(description='The job status',
79
- required=True, enum=list(HARVEST_JOB_STATUS)),
80
- 'errors': fields.List(fields.Nested(error_fields),
81
- description='The job initialization errors'),
82
- 'items': fields.List(fields.Nested(item_fields),
83
- description='The job collected items'),
84
- 'source': fields.String(description='The source owning the job',
85
- required=True),
86
- })
87
-
88
- job_page_fields = api.model('HarvestJobPage', fields.pager(job_fields))
89
-
90
- validation_fields = api.model('HarvestSourceValidation', {
91
- 'state': fields.String(description='Is it validated or not',
92
- enum=list(VALIDATION_STATES),
93
- required=True),
94
- 'by': fields.Nested(user_ref_fields, allow_null=True, readonly=True,
95
- description='Who performed the validation'),
96
- 'on': fields.ISODateTime(
97
- readonly=True,
98
- description='Date date on which validation was performed'
99
- ),
100
- 'comment': fields.String(
101
- description='A comment about the validation. Required on rejection'
102
- )
103
- })
104
-
105
- source_fields = api.model('HarvestSource', {
106
- 'id': fields.String(description='The source unique identifier',
107
- readonly=True),
108
- 'name': fields.String(description='The source display name',
109
- required=True),
110
- 'description': fields.Markdown(description='The source description'),
111
- 'url': fields.String(description='The source base URL',
112
- required=True),
113
- 'backend': fields.String(description='The source backend',
114
- enum=backends_ids,
115
- required=True),
116
- 'config': fields.Raw(description='The configuration as key-value pairs'),
117
- 'created_at': fields.ISODateTime(description='The source creation date',
118
- required=True, readonly=True),
119
- 'active': fields.Boolean(description='Is this source active',
120
- required=True, default=False),
121
- 'autoarchive': fields.Boolean(
122
- description='If enabled, datasets not present on the remote source will be automatically archived', # noqa
123
- required=True, default=True),
124
- 'validation': fields.Nested(validation_fields, readonly=True,
125
- description='Has the source been validated'),
126
- 'last_job': fields.Nested(job_fields,
127
- description='The last job for this source',
128
- allow_null=True, readonly=True),
129
- 'owner': fields.Nested(user_ref_fields, allow_null=True,
130
- description='The owner information'),
131
- 'organization': fields.Nested(org_ref_fields, allow_null=True,
132
- description='The producer organization'),
133
- 'deleted': fields.ISODateTime(description='The source deletion date', readonly=True),
134
- 'schedule': fields.String(description='The source schedule (interval or cron expression)',
135
- readonly=True),
136
- })
137
-
138
- source_page_fields = api.model('HarvestSourcePage',
139
- fields.pager(source_fields))
140
-
141
-
142
- filter_fields = api.model('HarvestFilter', {
143
- 'label': fields.String(description='A localized human-readable label'),
144
- 'key': fields.String(description='The filter key'),
145
- 'type': fields.String(description='The filter expected type'),
146
- 'description': fields.String(description='The filter details'),
147
- })
148
-
149
- feature_fields = api.model('HarvestFeature', {
150
- 'label': fields.String(description='A localized human-readable and descriptive label'),
151
- 'key': fields.String(description='The feature key'),
152
- 'description': fields.String(description='Some details about the behavior'),
153
- 'default': fields.String(description='The feature default state (true is enabled)'),
154
- })
155
-
156
- backend_fields = api.model('HarvestBackend', {
157
- 'id': fields.String(description='The backend identifier'),
158
- 'label': fields.String(description='The backend display name'),
159
- 'filters': fields.List(fields.Nested(filter_fields),
160
- description='The backend supported filters'),
161
- 'features': fields.List(fields.Nested(feature_fields),
162
- description='The backend optional features'),
163
- })
164
-
165
- preview_dataset_fields = api.clone('DatasetPreview', dataset_fields, {
166
- 'uri': fields.UrlFor(
167
- 'api.dataset', lambda o: {'dataset': 'not-available'},
168
- description='The dataset API URI (fake)'),
169
- 'page': fields.UrlFor(
170
- 'datasets.show', lambda o: {'dataset': 'not-available'},
171
- description='The dataset page URL (fake)', fallback_endpoint='api.dataset'),
172
- })
173
-
174
- preview_item_fields = api.clone('HarvestItemPreview', item_fields, {
175
- 'dataset': fields.Nested(preview_dataset_fields,
176
- description='The processed dataset',
177
- allow_null=True),
178
- })
179
-
180
- preview_job_fields = api.clone('HarvestJobPreview', job_fields, {
181
- 'items': fields.List(fields.Nested(preview_item_fields),
182
- description='The job collected items'),
183
- })
32
+ error_fields = api.model(
33
+ "HarvestError",
34
+ {
35
+ "created_at": fields.ISODateTime(
36
+ description="The error creation date", required=True, readonly=True
37
+ ),
38
+ "message": fields.String(description="The error short message", required=True),
39
+ "details": fields.String(description="Optional details (ie. stacktrace)"),
40
+ },
41
+ )
42
+
43
+
44
+ log_fields = api.model(
45
+ "HarvestError",
46
+ {
47
+ "level": fields.String(required=True),
48
+ "message": fields.String(required=True),
49
+ },
50
+ )
51
+
52
+
53
+ item_fields = api.model(
54
+ "HarvestItem",
55
+ {
56
+ "remote_id": fields.String(description="The item remote ID to process", required=True),
57
+ "dataset": fields.Nested(
58
+ dataset_ref_fields, description="The processed dataset", allow_null=True
59
+ ),
60
+ "dataservice": fields.Nested(
61
+ Dataservice.__read_fields__, description="The processed dataservice", allow_null=True
62
+ ),
63
+ "status": fields.String(
64
+ description="The item status", required=True, enum=list(HARVEST_ITEM_STATUS)
65
+ ),
66
+ "created": fields.ISODateTime(description="The item creation date", required=True),
67
+ "started": fields.ISODateTime(description="The item start date"),
68
+ "ended": fields.ISODateTime(description="The item end date"),
69
+ "errors": fields.List(fields.Nested(error_fields), description="The item errors"),
70
+ "logs": fields.List(fields.Nested(log_fields), description="The item logs"),
71
+ "args": fields.List(fields.String, description="The item positional arguments", default=[]),
72
+ "kwargs": fields.Raw(description="The item keyword arguments", default={}),
73
+ },
74
+ )
75
+
76
+ job_fields = api.model(
77
+ "HarvestJob",
78
+ {
79
+ "id": fields.String(description="The job execution ID", required=True),
80
+ "created": fields.ISODateTime(description="The job creation date", required=True),
81
+ "started": fields.ISODateTime(description="The job start date"),
82
+ "ended": fields.ISODateTime(description="The job end date"),
83
+ "status": fields.String(
84
+ description="The job status", required=True, enum=list(HARVEST_JOB_STATUS)
85
+ ),
86
+ "errors": fields.List(
87
+ fields.Nested(error_fields), description="The job initialization errors"
88
+ ),
89
+ "items": fields.List(fields.Nested(item_fields), description="The job collected items"),
90
+ "source": fields.String(description="The source owning the job", required=True),
91
+ },
92
+ )
93
+
94
+ job_page_fields = api.model("HarvestJobPage", fields.pager(job_fields))
95
+
96
+ validation_fields = api.model(
97
+ "HarvestSourceValidation",
98
+ {
99
+ "state": fields.String(
100
+ description="Is it validated or not", enum=list(VALIDATION_STATES), required=True
101
+ ),
102
+ "by": fields.Nested(
103
+ user_ref_fields,
104
+ allow_null=True,
105
+ readonly=True,
106
+ description="Who performed the validation",
107
+ ),
108
+ "on": fields.ISODateTime(
109
+ readonly=True, description="Date date on which validation was performed"
110
+ ),
111
+ "comment": fields.String(
112
+ description="A comment about the validation. Required on rejection"
113
+ ),
114
+ },
115
+ )
116
+
117
+ source_fields = api.model(
118
+ "HarvestSource",
119
+ {
120
+ "id": fields.String(description="The source unique identifier", readonly=True),
121
+ "name": fields.String(description="The source display name", required=True),
122
+ "description": fields.Markdown(description="The source description"),
123
+ "url": fields.String(description="The source base URL", required=True),
124
+ "backend": fields.String(
125
+ description="The source backend", enum=backends_ids, required=True
126
+ ),
127
+ "config": fields.Raw(description="The configuration as key-value pairs"),
128
+ "created_at": fields.ISODateTime(
129
+ description="The source creation date", required=True, readonly=True
130
+ ),
131
+ "active": fields.Boolean(description="Is this source active", required=True, default=False),
132
+ "autoarchive": fields.Boolean(
133
+ description="If enabled, datasets not present on the remote source will be automatically archived", # noqa
134
+ required=True,
135
+ default=True,
136
+ ),
137
+ "validation": fields.Nested(
138
+ validation_fields, readonly=True, description="Has the source been validated"
139
+ ),
140
+ "last_job": fields.Nested(
141
+ job_fields, description="The last job for this source", allow_null=True, readonly=True
142
+ ),
143
+ "owner": fields.Nested(
144
+ user_ref_fields, allow_null=True, description="The owner information"
145
+ ),
146
+ "organization": fields.Nested(
147
+ org_ref_fields, allow_null=True, description="The producer organization"
148
+ ),
149
+ "deleted": fields.ISODateTime(description="The source deletion date", readonly=True),
150
+ "schedule": fields.String(
151
+ description="The source schedule (interval or cron expression)", readonly=True
152
+ ),
153
+ },
154
+ )
155
+
156
+ source_page_fields = api.model("HarvestSourcePage", fields.pager(source_fields))
157
+
158
+
159
+ filter_fields = api.model(
160
+ "HarvestFilter",
161
+ {
162
+ "label": fields.String(description="A localized human-readable label"),
163
+ "key": fields.String(description="The filter key"),
164
+ "type": fields.String(description="The filter expected type"),
165
+ "description": fields.String(description="The filter details"),
166
+ },
167
+ )
168
+
169
+ feature_fields = api.model(
170
+ "HarvestFeature",
171
+ {
172
+ "label": fields.String(description="A localized human-readable and descriptive label"),
173
+ "key": fields.String(description="The feature key"),
174
+ "description": fields.String(description="Some details about the behavior"),
175
+ "default": fields.String(description="The feature default state (true is enabled)"),
176
+ },
177
+ )
178
+
179
+ backend_fields = api.model(
180
+ "HarvestBackend",
181
+ {
182
+ "id": fields.String(description="The backend identifier"),
183
+ "label": fields.String(description="The backend display name"),
184
+ "filters": fields.List(
185
+ fields.Nested(filter_fields), description="The backend supported filters"
186
+ ),
187
+ "features": fields.List(
188
+ fields.Nested(feature_fields), description="The backend optional features"
189
+ ),
190
+ },
191
+ )
192
+
193
+ preview_dataset_fields = api.clone(
194
+ "DatasetPreview",
195
+ dataset_fields,
196
+ {
197
+ "uri": fields.UrlFor(
198
+ "api.dataset",
199
+ lambda o: {"dataset": "not-available"},
200
+ description="The dataset API URI (fake)",
201
+ ),
202
+ "page": fields.UrlFor(
203
+ "datasets.show",
204
+ lambda o: {"dataset": "not-available"},
205
+ description="The dataset page URL (fake)",
206
+ fallback_endpoint="api.dataset",
207
+ ),
208
+ },
209
+ )
210
+
211
+ preview_item_fields = api.clone(
212
+ "HarvestItemPreview",
213
+ item_fields,
214
+ {
215
+ "dataset": fields.Nested(
216
+ preview_dataset_fields, description="The processed dataset", allow_null=True
217
+ ),
218
+ },
219
+ )
220
+
221
+ preview_job_fields = api.clone(
222
+ "HarvestJobPreview",
223
+ job_fields,
224
+ {
225
+ "items": fields.List(
226
+ fields.Nested(preview_item_fields), description="The job collected items"
227
+ ),
228
+ },
229
+ )
184
230
 
185
231
  source_parser = api.page_parser()
186
- source_parser.add_argument('owner', type=str, location='args',
187
- help='The organization or user ID to filter on')
188
- source_parser.add_argument('deleted', type=bool, location='args', default=False,
189
- help='Include sources flaggued as deleted')
232
+ source_parser.add_argument(
233
+ "owner", type=str, location="args", help="The organization or user ID to filter on"
234
+ )
235
+ source_parser.add_argument(
236
+ "deleted", type=bool, location="args", default=False, help="Include sources flaggued as deleted"
237
+ )
190
238
 
191
239
 
192
- @ns.route('/sources/', endpoint='harvest_sources')
240
+ @ns.route("/sources/", endpoint="harvest_sources")
193
241
  class SourcesAPI(API):
194
- @api.doc('list_harvest_sources')
242
+ @api.doc("list_harvest_sources")
195
243
  @api.expect(source_parser)
196
244
  @api.marshal_list_with(source_page_fields)
197
245
  def get(self):
198
- '''List all harvest sources'''
246
+ """List all harvest sources"""
199
247
  args = source_parser.parse_args()
200
248
 
201
- if args.get('owner') and not ObjectId.is_valid(args.get('owner')):
202
- api.abort(400, '`owner` arg must be an identifier')
249
+ if args.get("owner") and not ObjectId.is_valid(args.get("owner")):
250
+ api.abort(400, "`owner` arg must be an identifier")
203
251
 
204
- return actions.paginate_sources(args.get('owner'),
205
- page=args['page'],
206
- page_size=args['page_size'],
207
- deleted=args['deleted'])
252
+ return actions.paginate_sources(
253
+ args.get("owner"),
254
+ page=args["page"],
255
+ page_size=args["page_size"],
256
+ deleted=args["deleted"],
257
+ )
208
258
 
209
259
  @api.secure
210
- @api.doc('create_harvest_source')
260
+ @api.doc("create_harvest_source")
211
261
  @api.expect(source_fields)
212
262
  @api.marshal_with(source_fields)
213
263
  def post(self):
214
- '''Create a new harvest source'''
264
+ """Create a new harvest source"""
215
265
  form = api.validate(HarvestSourceForm)
216
266
  if form.organization.data:
217
267
  EditOrganizationPermission(form.organization.data).test()
@@ -219,21 +269,21 @@ class SourcesAPI(API):
219
269
  return source, 201
220
270
 
221
271
 
222
- @ns.route('/source/<string:ident>', endpoint='harvest_source')
223
- @api.param('ident', 'A source ID or slug')
272
+ @ns.route("/source/<string:ident>", endpoint="harvest_source")
273
+ @api.param("ident", "A source ID or slug")
224
274
  class SourceAPI(API):
225
- @api.doc('get_harvest_source')
275
+ @api.doc("get_harvest_source")
226
276
  @api.marshal_with(source_fields)
227
277
  def get(self, ident):
228
- '''Get a single source given an ID or a slug'''
278
+ """Get a single source given an ID or a slug"""
229
279
  return actions.get_source(ident)
230
280
 
231
281
  @api.secure
232
- @api.doc('update_harvest_source')
282
+ @api.doc("update_harvest_source")
233
283
  @api.expect(source_fields)
234
284
  @api.marshal_with(source_fields)
235
285
  def put(self, ident):
236
- '''Update a harvest source'''
286
+ """Update a harvest source"""
237
287
  source = actions.get_source(ident)
238
288
  OwnablePermission(source).test()
239
289
  form = api.validate(HarvestSourceForm, source)
@@ -241,7 +291,7 @@ class SourceAPI(API):
241
291
  return source
242
292
 
243
293
  @api.secure
244
- @api.doc('delete_harvest_source')
294
+ @api.doc("delete_harvest_source")
245
295
  @api.marshal_with(source_fields)
246
296
  def delete(self, ident):
247
297
  source: HarvestSource = actions.get_source(ident)
@@ -249,143 +299,149 @@ class SourceAPI(API):
249
299
  return actions.delete_source(ident), 204
250
300
 
251
301
 
252
- @ns.route('/source/<string:ident>/validate',
253
- endpoint='validate_harvest_source')
254
- @api.param('ident', 'A source ID or slug')
302
+ @ns.route("/source/<string:ident>/validate", endpoint="validate_harvest_source")
303
+ @api.param("ident", "A source ID or slug")
255
304
  class ValidateSourceAPI(API):
256
- @api.doc('validate_harvest_source')
305
+ @api.doc("validate_harvest_source")
257
306
  @api.secure(admin_permission)
258
307
  @api.expect(validation_fields)
259
308
  @api.marshal_with(source_fields)
260
309
  def post(self, ident):
261
- '''Validate or reject an harvest source'''
310
+ """Validate or reject an harvest source"""
262
311
  form = api.validate(HarvestSourceValidationForm)
263
312
  if form.state.data == VALIDATION_ACCEPTED:
264
313
  return actions.validate_source(ident, form.comment.data)
265
314
  else:
266
315
  return actions.reject_source(ident, form.comment.data)
267
316
 
268
- @ns.route('/source/<string:ident>/run', endpoint='run_harvest_source')
269
- @api.param('ident', 'A source ID or slug')
317
+
318
+ @ns.route("/source/<string:ident>/run", endpoint="run_harvest_source")
319
+ @api.param("ident", "A source ID or slug")
270
320
  class RunSourceAPI(API):
271
- @api.doc('run_harvest_source')
321
+ @api.doc("run_harvest_source")
272
322
  @api.secure
273
323
  @api.marshal_with(source_fields)
274
324
  def post(self, ident):
275
- enabled = current_app.config.get('HARVEST_ENABLE_MANUAL_RUN')
325
+ enabled = current_app.config.get("HARVEST_ENABLE_MANUAL_RUN")
276
326
  if not enabled:
277
- api.abort(400, 'Cannot run source manually. Please contact the platform if you need to reschedule the harvester.')
327
+ api.abort(
328
+ 400,
329
+ "Cannot run source manually. Please contact the platform if you need to reschedule the harvester.",
330
+ )
278
331
 
279
332
  source: HarvestSource = actions.get_source(ident)
280
333
  OwnablePermission(source).test()
281
334
 
282
335
  if source.validation.state != VALIDATION_ACCEPTED:
283
- api.abort(400, 'Source is not validated. Please validate the source before running.')
336
+ api.abort(400, "Source is not validated. Please validate the source before running.")
284
337
 
285
338
  actions.launch(ident)
286
339
 
287
340
  return source
288
341
 
289
342
 
290
- @ns.route('/source/<string:ident>/schedule',
291
- endpoint='schedule_harvest_source')
292
- @api.param('ident', 'A source ID or slug')
343
+ @ns.route("/source/<string:ident>/schedule", endpoint="schedule_harvest_source")
344
+ @api.param("ident", "A source ID or slug")
293
345
  class ScheduleSourceAPI(API):
294
- @api.doc('schedule_harvest_source')
346
+ @api.doc("schedule_harvest_source")
295
347
  @api.secure(admin_permission)
296
- @api.expect((str, 'A cron expression'))
348
+ @api.expect((str, "A cron expression"))
297
349
  @api.marshal_with(source_fields)
298
350
  def post(self, ident):
299
- '''Schedule an harvest source'''
351
+ """Schedule an harvest source"""
300
352
  # Handle both syntax: quoted and unquoted
301
353
  try:
302
354
  data = request.json
303
355
  except BadRequest:
304
- data = request.data.decode('utf-8')
356
+ data = request.data.decode("utf-8")
305
357
  return actions.schedule(ident, data)
306
358
 
307
- @api.doc('unschedule_harvest_source')
359
+ @api.doc("unschedule_harvest_source")
308
360
  @api.secure(admin_permission)
309
361
  @api.marshal_with(source_fields)
310
362
  def delete(self, ident):
311
- '''Unschedule an harvest source'''
363
+ """Unschedule an harvest source"""
312
364
  return actions.unschedule(ident), 204
313
365
 
314
366
 
315
- @ns.route('/source/preview', endpoint='preview_harvest_source_config')
367
+ @ns.route("/source/preview", endpoint="preview_harvest_source_config")
316
368
  class PreviewSourceConfigAPI(API):
317
369
  @api.secure
318
370
  @api.expect(source_fields)
319
- @api.doc('preview_harvest_source_config')
371
+ @api.doc("preview_harvest_source_config")
320
372
  @api.marshal_with(preview_job_fields)
321
373
  def post(self):
322
- '''Preview an harvesting from a source created with the given payload'''
374
+ """Preview an harvesting from a source created with the given payload"""
323
375
  form = api.validate(HarvestSourceForm)
324
376
  if form.organization.data:
325
377
  EditOrganizationPermission(form.organization.data).test()
326
378
  return actions.preview_from_config(**form.data)
327
379
 
328
380
 
329
- @ns.route('/source/<string:ident>/preview', endpoint='preview_harvest_source')
330
- @api.param('ident', 'A source ID or slug')
381
+ @ns.route("/source/<string:ident>/preview", endpoint="preview_harvest_source")
382
+ @api.param("ident", "A source ID or slug")
331
383
  class PreviewSourceAPI(API):
332
384
  @api.secure
333
- @api.doc('preview_harvest_source')
385
+ @api.doc("preview_harvest_source")
334
386
  @api.marshal_with(preview_job_fields)
335
387
  def get(self, ident):
336
- '''Preview a single harvest source given an ID or a slug'''
388
+ """Preview a single harvest source given an ID or a slug"""
337
389
  return actions.preview(ident)
338
390
 
339
391
 
340
392
  parser = api.parser()
341
- parser.add_argument('page', type=int, default=1, location='args',
342
- help='The page to fetch')
343
- parser.add_argument('page_size', type=int, default=20, location='args',
344
- help='The page size to fetch')
393
+ parser.add_argument("page", type=int, default=1, location="args", help="The page to fetch")
394
+ parser.add_argument(
395
+ "page_size", type=int, default=20, location="args", help="The page size to fetch"
396
+ )
345
397
 
346
398
 
347
- @ns.route('/source/<string:ident>/jobs/', endpoint='harvest_jobs')
399
+ @ns.route("/source/<string:ident>/jobs/", endpoint="harvest_jobs")
348
400
  class JobsAPI(API):
349
- @api.doc('list_harvest_jobs')
401
+ @api.doc("list_harvest_jobs")
350
402
  @api.expect(parser)
351
403
  @api.marshal_with(job_page_fields)
352
404
  def get(self, ident):
353
- '''List all jobs for a given source'''
405
+ """List all jobs for a given source"""
354
406
  args = parser.parse_args()
355
407
  qs = HarvestJob.objects(source=ident)
356
- qs = qs.order_by('-created')
357
- return qs.paginate(args['page'], args['page_size'])
408
+ qs = qs.order_by("-created")
409
+ return qs.paginate(args["page"], args["page_size"])
358
410
 
359
411
 
360
- @ns.route('/job/<string:ident>/', endpoint='harvest_job')
412
+ @ns.route("/job/<string:ident>/", endpoint="harvest_job")
361
413
  class JobAPI(API):
362
- @api.doc('get_harvest_job')
414
+ @api.doc("get_harvest_job")
363
415
  @api.expect(parser)
364
416
  @api.marshal_with(job_fields)
365
417
  def get(self, ident):
366
- '''List all jobs for a given source'''
418
+ """List all jobs for a given source"""
367
419
  return actions.get_job(ident)
368
420
 
369
421
 
370
- @ns.route('/backends', endpoint='harvest_backends')
422
+ @ns.route("/backends", endpoint="harvest_backends")
371
423
  class ListBackendsAPI(API):
372
- @api.doc('harvest_backends')
424
+ @api.doc("harvest_backends")
373
425
  @api.marshal_with(backend_fields)
374
426
  def get(self):
375
- '''List all available harvest backends'''
376
- return sorted([
377
- {
378
- 'id': b.name,
379
- 'label': b.display_name,
380
- 'filters': [f.as_dict() for f in b.filters],
381
- 'features': [f.as_dict() for f in b.features],
382
- } for b in actions.list_backends()
383
- ], key=lambda b: b['label'])
384
-
385
-
386
- @ns.route('/job_status', endpoint='havest_job_status')
427
+ """List all available harvest backends"""
428
+ return sorted(
429
+ [
430
+ {
431
+ "id": b.name,
432
+ "label": b.display_name,
433
+ "filters": [f.as_dict() for f in b.filters],
434
+ "features": [f.as_dict() for f in b.features],
435
+ }
436
+ for b in actions.list_backends()
437
+ ],
438
+ key=lambda b: b["label"],
439
+ )
440
+
441
+
442
+ @ns.route("/job_status", endpoint="havest_job_status")
387
443
  class ListHarvesterAPI(API):
388
444
  @api.doc(model=[str])
389
445
  def get(self):
390
- '''List all available harvesters'''
446
+ """List all available harvesters"""
391
447
  return actions.list_backends()