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.
- tasks/__init__.py +109 -107
- tasks/helpers.py +18 -18
- udata/__init__.py +4 -4
- udata/admin/views.py +5 -5
- udata/api/__init__.py +111 -134
- udata/api/commands.py +45 -37
- udata/api/errors.py +5 -4
- udata/api/fields.py +23 -21
- udata/api/oauth2.py +55 -74
- udata/api/parsers.py +15 -15
- udata/api/signals.py +1 -1
- udata/api_fields.py +137 -89
- udata/app.py +58 -55
- udata/assets.py +5 -5
- udata/auth/__init__.py +37 -26
- udata/auth/forms.py +23 -15
- udata/auth/helpers.py +1 -1
- udata/auth/mails.py +3 -3
- udata/auth/password_validation.py +19 -15
- udata/auth/views.py +94 -68
- udata/commands/__init__.py +71 -69
- udata/commands/cache.py +7 -7
- udata/commands/db.py +201 -140
- udata/commands/dcat.py +36 -30
- udata/commands/fixtures.py +100 -84
- udata/commands/images.py +21 -20
- udata/commands/info.py +17 -20
- udata/commands/init.py +10 -10
- udata/commands/purge.py +12 -13
- udata/commands/serve.py +41 -29
- udata/commands/static.py +16 -18
- udata/commands/test.py +20 -20
- udata/commands/tests/fixtures.py +26 -24
- udata/commands/worker.py +31 -33
- udata/core/__init__.py +12 -12
- udata/core/activity/__init__.py +0 -1
- udata/core/activity/api.py +59 -49
- udata/core/activity/models.py +28 -26
- udata/core/activity/signals.py +1 -1
- udata/core/activity/tasks.py +16 -10
- udata/core/badges/api.py +6 -6
- udata/core/badges/commands.py +14 -13
- udata/core/badges/fields.py +8 -5
- udata/core/badges/forms.py +7 -4
- udata/core/badges/models.py +16 -31
- udata/core/badges/permissions.py +1 -3
- udata/core/badges/signals.py +2 -2
- udata/core/badges/tasks.py +3 -2
- udata/core/badges/tests/test_commands.py +10 -10
- udata/core/badges/tests/test_model.py +24 -31
- udata/core/contact_point/api.py +19 -18
- udata/core/contact_point/api_fields.py +21 -14
- udata/core/contact_point/factories.py +2 -2
- udata/core/contact_point/forms.py +7 -6
- udata/core/contact_point/models.py +3 -5
- udata/core/dataservices/api.py +26 -21
- udata/core/dataservices/factories.py +13 -11
- udata/core/dataservices/models.py +35 -40
- udata/core/dataservices/permissions.py +4 -4
- udata/core/dataservices/rdf.py +40 -17
- udata/core/dataservices/tasks.py +4 -3
- udata/core/dataset/actions.py +10 -10
- udata/core/dataset/activities.py +21 -23
- udata/core/dataset/api.py +321 -298
- udata/core/dataset/api_fields.py +443 -271
- udata/core/dataset/apiv2.py +305 -229
- udata/core/dataset/commands.py +38 -36
- udata/core/dataset/constants.py +61 -54
- udata/core/dataset/csv.py +70 -74
- udata/core/dataset/events.py +39 -32
- udata/core/dataset/exceptions.py +8 -4
- udata/core/dataset/factories.py +57 -65
- udata/core/dataset/forms.py +87 -63
- udata/core/dataset/models.py +336 -280
- udata/core/dataset/permissions.py +9 -6
- udata/core/dataset/preview.py +15 -17
- udata/core/dataset/rdf.py +156 -122
- udata/core/dataset/search.py +92 -77
- udata/core/dataset/signals.py +1 -1
- udata/core/dataset/tasks.py +63 -54
- udata/core/discussions/actions.py +5 -5
- udata/core/discussions/api.py +124 -120
- udata/core/discussions/factories.py +2 -2
- udata/core/discussions/forms.py +9 -7
- udata/core/discussions/metrics.py +1 -3
- udata/core/discussions/models.py +25 -24
- udata/core/discussions/notifications.py +18 -14
- udata/core/discussions/permissions.py +3 -3
- udata/core/discussions/signals.py +4 -4
- udata/core/discussions/tasks.py +24 -28
- udata/core/followers/api.py +32 -33
- udata/core/followers/models.py +9 -9
- udata/core/followers/signals.py +3 -3
- udata/core/jobs/actions.py +7 -7
- udata/core/jobs/api.py +99 -92
- udata/core/jobs/commands.py +48 -49
- udata/core/jobs/forms.py +11 -11
- udata/core/jobs/models.py +6 -6
- udata/core/metrics/__init__.py +2 -2
- udata/core/metrics/commands.py +34 -30
- udata/core/metrics/models.py +2 -4
- udata/core/metrics/signals.py +1 -1
- udata/core/metrics/tasks.py +3 -3
- udata/core/organization/activities.py +12 -15
- udata/core/organization/api.py +167 -174
- udata/core/organization/api_fields.py +183 -124
- udata/core/organization/apiv2.py +32 -32
- udata/core/organization/commands.py +20 -22
- udata/core/organization/constants.py +11 -11
- udata/core/organization/csv.py +17 -15
- udata/core/organization/factories.py +8 -11
- udata/core/organization/forms.py +32 -26
- udata/core/organization/metrics.py +2 -1
- udata/core/organization/models.py +87 -67
- udata/core/organization/notifications.py +18 -14
- udata/core/organization/permissions.py +10 -11
- udata/core/organization/rdf.py +14 -14
- udata/core/organization/search.py +30 -28
- udata/core/organization/signals.py +7 -7
- udata/core/organization/tasks.py +42 -61
- udata/core/owned.py +38 -27
- udata/core/post/api.py +82 -81
- udata/core/post/constants.py +8 -5
- udata/core/post/factories.py +4 -4
- udata/core/post/forms.py +13 -14
- udata/core/post/models.py +20 -22
- udata/core/post/tests/test_api.py +30 -32
- udata/core/reports/api.py +8 -7
- udata/core/reports/constants.py +1 -3
- udata/core/reports/models.py +10 -10
- udata/core/reuse/activities.py +15 -19
- udata/core/reuse/api.py +123 -126
- udata/core/reuse/api_fields.py +120 -85
- udata/core/reuse/apiv2.py +11 -10
- udata/core/reuse/constants.py +23 -23
- udata/core/reuse/csv.py +18 -18
- udata/core/reuse/factories.py +5 -9
- udata/core/reuse/forms.py +24 -21
- udata/core/reuse/models.py +55 -51
- udata/core/reuse/permissions.py +2 -2
- udata/core/reuse/search.py +49 -46
- udata/core/reuse/signals.py +1 -1
- udata/core/reuse/tasks.py +4 -5
- udata/core/site/api.py +47 -50
- udata/core/site/factories.py +2 -2
- udata/core/site/forms.py +4 -5
- udata/core/site/models.py +94 -63
- udata/core/site/rdf.py +14 -14
- udata/core/spam/api.py +16 -9
- udata/core/spam/constants.py +4 -4
- udata/core/spam/fields.py +13 -7
- udata/core/spam/models.py +27 -20
- udata/core/spam/signals.py +1 -1
- udata/core/spam/tests/test_spam.py +6 -5
- udata/core/spatial/api.py +72 -80
- udata/core/spatial/api_fields.py +73 -58
- udata/core/spatial/commands.py +67 -64
- udata/core/spatial/constants.py +3 -3
- udata/core/spatial/factories.py +37 -54
- udata/core/spatial/forms.py +27 -26
- udata/core/spatial/geoids.py +17 -17
- udata/core/spatial/models.py +43 -47
- udata/core/spatial/tasks.py +2 -1
- udata/core/spatial/tests/test_api.py +115 -130
- udata/core/spatial/tests/test_fields.py +74 -77
- udata/core/spatial/tests/test_geoid.py +22 -22
- udata/core/spatial/tests/test_models.py +5 -7
- udata/core/spatial/translations.py +16 -16
- udata/core/storages/__init__.py +16 -18
- udata/core/storages/api.py +66 -64
- udata/core/storages/tasks.py +7 -7
- udata/core/storages/utils.py +15 -15
- udata/core/storages/views.py +5 -6
- udata/core/tags/api.py +17 -14
- udata/core/tags/csv.py +4 -4
- udata/core/tags/models.py +8 -5
- udata/core/tags/tasks.py +11 -13
- udata/core/tags/views.py +4 -4
- udata/core/topic/api.py +84 -73
- udata/core/topic/apiv2.py +157 -127
- udata/core/topic/factories.py +3 -4
- udata/core/topic/forms.py +12 -14
- udata/core/topic/models.py +14 -19
- udata/core/topic/parsers.py +26 -26
- udata/core/user/activities.py +30 -29
- udata/core/user/api.py +151 -152
- udata/core/user/api_fields.py +132 -100
- udata/core/user/apiv2.py +7 -7
- udata/core/user/commands.py +38 -38
- udata/core/user/factories.py +8 -9
- udata/core/user/forms.py +14 -11
- udata/core/user/metrics.py +2 -2
- udata/core/user/models.py +68 -69
- udata/core/user/permissions.py +4 -5
- udata/core/user/rdf.py +7 -8
- udata/core/user/tasks.py +2 -2
- udata/core/user/tests/test_user_model.py +24 -16
- udata/cors.py +99 -0
- udata/db/tasks.py +2 -1
- udata/entrypoints.py +35 -31
- udata/errors.py +2 -1
- udata/event/values.py +6 -6
- udata/factories.py +2 -2
- udata/features/identicon/api.py +5 -6
- udata/features/identicon/backends.py +48 -55
- udata/features/identicon/tests/test_backends.py +4 -5
- udata/features/notifications/__init__.py +0 -1
- udata/features/notifications/actions.py +9 -9
- udata/features/notifications/api.py +17 -13
- udata/features/territories/__init__.py +12 -10
- udata/features/territories/api.py +14 -15
- udata/features/territories/models.py +23 -28
- udata/features/transfer/actions.py +8 -11
- udata/features/transfer/api.py +84 -77
- udata/features/transfer/factories.py +2 -1
- udata/features/transfer/models.py +11 -12
- udata/features/transfer/notifications.py +19 -15
- udata/features/transfer/permissions.py +5 -5
- udata/forms/__init__.py +5 -2
- udata/forms/fields.py +164 -172
- udata/forms/validators.py +19 -22
- udata/forms/widgets.py +9 -13
- udata/frontend/__init__.py +31 -26
- udata/frontend/csv.py +68 -58
- udata/frontend/markdown.py +40 -44
- udata/harvest/actions.py +89 -77
- udata/harvest/api.py +294 -238
- udata/harvest/backends/__init__.py +4 -4
- udata/harvest/backends/base.py +128 -111
- udata/harvest/backends/dcat.py +80 -66
- udata/harvest/commands.py +56 -60
- udata/harvest/csv.py +8 -8
- udata/harvest/exceptions.py +6 -3
- udata/harvest/filters.py +24 -23
- udata/harvest/forms.py +27 -28
- udata/harvest/models.py +88 -80
- udata/harvest/notifications.py +15 -10
- udata/harvest/signals.py +13 -13
- udata/harvest/tasks.py +11 -10
- udata/harvest/tests/factories.py +23 -24
- udata/harvest/tests/test_actions.py +136 -166
- udata/harvest/tests/test_api.py +220 -214
- udata/harvest/tests/test_base_backend.py +117 -112
- udata/harvest/tests/test_dcat_backend.py +380 -308
- udata/harvest/tests/test_filters.py +33 -22
- udata/harvest/tests/test_models.py +11 -14
- udata/harvest/tests/test_notifications.py +6 -7
- udata/harvest/tests/test_tasks.py +7 -6
- udata/i18n.py +237 -78
- udata/linkchecker/backends.py +5 -11
- udata/linkchecker/checker.py +23 -22
- udata/linkchecker/commands.py +4 -6
- udata/linkchecker/models.py +6 -6
- udata/linkchecker/tasks.py +18 -20
- udata/mail.py +21 -21
- udata/migrations/2020-07-24-remove-s-from-scope-oauth.py +9 -8
- udata/migrations/2020-08-24-add-fs-filename.py +9 -8
- udata/migrations/2020-09-28-update-reuses-datasets-metrics.py +5 -4
- udata/migrations/2020-10-16-migrate-ods-resources.py +9 -10
- udata/migrations/2021-04-08-update-schema-with-new-structure.py +8 -7
- udata/migrations/2021-05-27-fix-default-schema-name.py +7 -6
- udata/migrations/2021-07-05-remove-unused-badges.py +17 -15
- udata/migrations/2021-07-07-update-schema-for-community-resources.py +7 -6
- udata/migrations/2021-08-17-follow-integrity.py +5 -4
- udata/migrations/2021-08-17-harvest-integrity.py +13 -12
- udata/migrations/2021-08-17-oauth2client-integrity.py +5 -4
- udata/migrations/2021-08-17-transfer-integrity.py +5 -4
- udata/migrations/2021-08-17-users-integrity.py +9 -8
- udata/migrations/2021-12-14-reuse-topics.py +7 -6
- udata/migrations/2022-04-21-improve-extension-detection.py +8 -7
- udata/migrations/2022-09-22-clean-inactive-harvest-datasets.py +16 -14
- udata/migrations/2022-10-10-add-fs_uniquifier-to-user-model.py +6 -6
- udata/migrations/2022-10-10-migrate-harvest-extras.py +36 -26
- udata/migrations/2023-02-08-rename-internal-dates.py +46 -28
- udata/migrations/2024-01-29-fix-reuse-and-dataset-with-private-None.py +10 -8
- udata/migrations/2024-03-22-migrate-activity-kwargs-to-extras.py +6 -4
- udata/migrations/2024-06-11-fix-reuse-datasets-references.py +7 -6
- udata/migrations/__init__.py +123 -105
- udata/models/__init__.py +4 -4
- udata/mongo/__init__.py +13 -11
- udata/mongo/badges_field.py +3 -2
- udata/mongo/datetime_fields.py +13 -12
- udata/mongo/document.py +17 -16
- udata/mongo/engine.py +15 -16
- udata/mongo/errors.py +2 -1
- udata/mongo/extras_fields.py +30 -20
- udata/mongo/queryset.py +12 -12
- udata/mongo/slug_fields.py +38 -28
- udata/mongo/taglist_field.py +1 -2
- udata/mongo/url_field.py +5 -5
- udata/mongo/uuid_fields.py +4 -3
- udata/notifications/__init__.py +1 -1
- udata/notifications/mattermost.py +10 -9
- udata/rdf.py +167 -188
- udata/routing.py +40 -45
- udata/search/__init__.py +18 -19
- udata/search/adapter.py +17 -16
- udata/search/commands.py +44 -51
- udata/search/fields.py +13 -20
- udata/search/query.py +23 -18
- udata/search/result.py +9 -10
- udata/sentry.py +21 -19
- udata/settings.py +262 -198
- udata/sitemap.py +8 -6
- udata/storage/s3.py +20 -13
- udata/tags.py +4 -5
- udata/tasks.py +43 -42
- udata/tests/__init__.py +9 -6
- udata/tests/api/__init__.py +8 -6
- udata/tests/api/test_auth_api.py +395 -321
- udata/tests/api/test_base_api.py +33 -35
- udata/tests/api/test_contact_points.py +7 -9
- udata/tests/api/test_dataservices_api.py +211 -158
- udata/tests/api/test_datasets_api.py +823 -812
- udata/tests/api/test_follow_api.py +13 -15
- udata/tests/api/test_me_api.py +95 -112
- udata/tests/api/test_organizations_api.py +301 -339
- udata/tests/api/test_reports_api.py +35 -25
- udata/tests/api/test_reuses_api.py +134 -139
- udata/tests/api/test_swagger.py +5 -5
- udata/tests/api/test_tags_api.py +18 -25
- udata/tests/api/test_topics_api.py +94 -94
- udata/tests/api/test_transfer_api.py +53 -48
- udata/tests/api/test_user_api.py +128 -141
- udata/tests/apiv2/test_datasets.py +290 -198
- udata/tests/apiv2/test_me_api.py +10 -11
- udata/tests/apiv2/test_organizations.py +56 -74
- udata/tests/apiv2/test_swagger.py +5 -5
- udata/tests/apiv2/test_topics.py +69 -87
- udata/tests/cli/test_cli_base.py +8 -8
- udata/tests/cli/test_db_cli.py +21 -19
- udata/tests/dataservice/test_dataservice_tasks.py +8 -12
- udata/tests/dataset/test_csv_adapter.py +44 -35
- udata/tests/dataset/test_dataset_actions.py +2 -3
- udata/tests/dataset/test_dataset_commands.py +7 -8
- udata/tests/dataset/test_dataset_events.py +36 -29
- udata/tests/dataset/test_dataset_model.py +224 -217
- udata/tests/dataset/test_dataset_rdf.py +142 -131
- udata/tests/dataset/test_dataset_tasks.py +15 -15
- udata/tests/dataset/test_resource_preview.py +10 -13
- udata/tests/features/territories/__init__.py +9 -13
- udata/tests/features/territories/test_territories_api.py +71 -91
- udata/tests/forms/test_basic_fields.py +7 -7
- udata/tests/forms/test_current_user_field.py +39 -66
- udata/tests/forms/test_daterange_field.py +31 -39
- udata/tests/forms/test_dict_field.py +28 -26
- udata/tests/forms/test_extras_fields.py +102 -76
- udata/tests/forms/test_form_field.py +8 -8
- udata/tests/forms/test_image_field.py +33 -26
- udata/tests/forms/test_model_field.py +134 -123
- udata/tests/forms/test_model_list_field.py +7 -7
- udata/tests/forms/test_nested_model_list_field.py +117 -79
- udata/tests/forms/test_publish_as_field.py +36 -65
- udata/tests/forms/test_reference_field.py +34 -53
- udata/tests/forms/test_user_forms.py +23 -21
- udata/tests/forms/test_uuid_field.py +6 -10
- udata/tests/frontend/__init__.py +9 -6
- udata/tests/frontend/test_auth.py +7 -6
- udata/tests/frontend/test_csv.py +81 -96
- udata/tests/frontend/test_hooks.py +43 -43
- udata/tests/frontend/test_markdown.py +211 -191
- udata/tests/helpers.py +32 -37
- udata/tests/models.py +2 -2
- udata/tests/organization/test_csv_adapter.py +21 -16
- udata/tests/organization/test_notifications.py +11 -18
- udata/tests/organization/test_organization_model.py +13 -13
- udata/tests/organization/test_organization_rdf.py +29 -22
- udata/tests/organization/test_organization_tasks.py +16 -17
- udata/tests/plugin.py +79 -73
- udata/tests/reuse/test_reuse_model.py +21 -21
- udata/tests/reuse/test_reuse_task.py +11 -13
- udata/tests/search/__init__.py +11 -12
- udata/tests/search/test_adapter.py +60 -70
- udata/tests/search/test_query.py +16 -16
- udata/tests/search/test_results.py +10 -7
- udata/tests/site/test_site_api.py +11 -16
- udata/tests/site/test_site_metrics.py +20 -30
- udata/tests/site/test_site_model.py +4 -5
- udata/tests/site/test_site_rdf.py +94 -78
- udata/tests/test_activity.py +17 -17
- udata/tests/test_cors.py +62 -0
- udata/tests/test_discussions.py +292 -299
- udata/tests/test_i18n.py +37 -40
- udata/tests/test_linkchecker.py +91 -85
- udata/tests/test_mail.py +13 -17
- udata/tests/test_migrations.py +219 -180
- udata/tests/test_model.py +164 -157
- udata/tests/test_notifications.py +17 -17
- udata/tests/test_owned.py +14 -14
- udata/tests/test_rdf.py +25 -23
- udata/tests/test_routing.py +89 -93
- udata/tests/test_storages.py +137 -128
- udata/tests/test_tags.py +44 -46
- udata/tests/test_topics.py +7 -7
- udata/tests/test_transfer.py +42 -49
- udata/tests/test_uris.py +160 -161
- udata/tests/test_utils.py +79 -71
- udata/tests/user/test_user_rdf.py +5 -9
- udata/tests/workers/test_jobs_commands.py +57 -58
- udata/tests/workers/test_tasks_routing.py +23 -29
- udata/tests/workers/test_workers_api.py +125 -131
- udata/tests/workers/test_workers_helpers.py +6 -6
- udata/tracking.py +4 -6
- udata/uris.py +45 -46
- udata/utils.py +68 -66
- udata/wsgi.py +1 -1
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/METADATA +7 -3
- udata-9.1.2.dev30454.dist-info/RECORD +706 -0
- udata-9.1.2.dev30355.dist-info/RECORD +0 -704
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30355.dist-info → udata-9.1.2.dev30454.dist-info}/entry_points.txt +0 -0
- {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
|
|
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
|
|
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
|
-
|
|
20
|
-
|
|
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(
|
|
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(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
description=
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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(
|
|
240
|
+
@ns.route("/sources/", endpoint="harvest_sources")
|
|
193
241
|
class SourcesAPI(API):
|
|
194
|
-
@api.doc(
|
|
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
|
-
|
|
246
|
+
"""List all harvest sources"""
|
|
199
247
|
args = source_parser.parse_args()
|
|
200
248
|
|
|
201
|
-
if args.get(
|
|
202
|
-
api.abort(400,
|
|
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(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
223
|
-
@api.param(
|
|
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(
|
|
275
|
+
@api.doc("get_harvest_source")
|
|
226
276
|
@api.marshal_with(source_fields)
|
|
227
277
|
def get(self, ident):
|
|
228
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
253
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
269
|
-
@
|
|
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(
|
|
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(
|
|
325
|
+
enabled = current_app.config.get("HARVEST_ENABLE_MANUAL_RUN")
|
|
276
326
|
if not enabled:
|
|
277
|
-
api.abort(
|
|
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,
|
|
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(
|
|
291
|
-
|
|
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(
|
|
346
|
+
@api.doc("schedule_harvest_source")
|
|
295
347
|
@api.secure(admin_permission)
|
|
296
|
-
@api.expect((str,
|
|
348
|
+
@api.expect((str, "A cron expression"))
|
|
297
349
|
@api.marshal_with(source_fields)
|
|
298
350
|
def post(self, ident):
|
|
299
|
-
|
|
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(
|
|
356
|
+
data = request.data.decode("utf-8")
|
|
305
357
|
return actions.schedule(ident, data)
|
|
306
358
|
|
|
307
|
-
@api.doc(
|
|
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
|
-
|
|
363
|
+
"""Unschedule an harvest source"""
|
|
312
364
|
return actions.unschedule(ident), 204
|
|
313
365
|
|
|
314
366
|
|
|
315
|
-
@ns.route(
|
|
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(
|
|
371
|
+
@api.doc("preview_harvest_source_config")
|
|
320
372
|
@api.marshal_with(preview_job_fields)
|
|
321
373
|
def post(self):
|
|
322
|
-
|
|
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(
|
|
330
|
-
@api.param(
|
|
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(
|
|
385
|
+
@api.doc("preview_harvest_source")
|
|
334
386
|
@api.marshal_with(preview_job_fields)
|
|
335
387
|
def get(self, ident):
|
|
336
|
-
|
|
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(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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(
|
|
399
|
+
@ns.route("/source/<string:ident>/jobs/", endpoint="harvest_jobs")
|
|
348
400
|
class JobsAPI(API):
|
|
349
|
-
@api.doc(
|
|
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
|
-
|
|
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(
|
|
357
|
-
return qs.paginate(args[
|
|
408
|
+
qs = qs.order_by("-created")
|
|
409
|
+
return qs.paginate(args["page"], args["page_size"])
|
|
358
410
|
|
|
359
411
|
|
|
360
|
-
@ns.route(
|
|
412
|
+
@ns.route("/job/<string:ident>/", endpoint="harvest_job")
|
|
361
413
|
class JobAPI(API):
|
|
362
|
-
@api.doc(
|
|
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
|
-
|
|
418
|
+
"""List all jobs for a given source"""
|
|
367
419
|
return actions.get_job(ident)
|
|
368
420
|
|
|
369
421
|
|
|
370
|
-
@ns.route(
|
|
422
|
+
@ns.route("/backends", endpoint="harvest_backends")
|
|
371
423
|
class ListBackendsAPI(API):
|
|
372
|
-
@api.doc(
|
|
424
|
+
@api.doc("harvest_backends")
|
|
373
425
|
@api.marshal_with(backend_fields)
|
|
374
426
|
def get(self):
|
|
375
|
-
|
|
376
|
-
return sorted(
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
446
|
+
"""List all available harvesters"""
|
|
391
447
|
return actions.list_backends()
|