udata 12.0.2.dev15__py3-none-any.whl → 13.0.1.dev21__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.
- udata/api/__init__.py +1 -0
- udata/api_fields.py +10 -4
- udata/app.py +11 -10
- udata/auth/__init__.py +9 -10
- udata/auth/mails.py +137 -45
- udata/auth/views.py +5 -12
- udata/commands/__init__.py +2 -3
- udata/commands/info.py +1 -3
- udata/commands/tests/test_fixtures.py +6 -3
- udata/core/access_type/api.py +18 -0
- udata/core/access_type/constants.py +98 -0
- udata/core/access_type/models.py +44 -0
- udata/core/activity/models.py +1 -1
- udata/core/badges/models.py +1 -1
- udata/core/badges/tasks.py +35 -1
- udata/core/badges/tests/test_commands.py +2 -4
- udata/core/badges/tests/test_model.py +2 -2
- udata/core/badges/tests/test_tasks.py +55 -0
- udata/core/constants.py +1 -0
- udata/core/contact_point/models.py +8 -0
- udata/core/dataservices/api.py +3 -3
- udata/core/dataservices/apiv2.py +3 -1
- udata/core/dataservices/constants.py +0 -29
- udata/core/dataservices/models.py +44 -44
- udata/core/dataservices/rdf.py +2 -1
- udata/core/dataservices/search.py +5 -9
- udata/core/dataservices/tasks.py +33 -0
- udata/core/dataset/api_fields.py +11 -0
- udata/core/dataset/apiv2.py +11 -0
- udata/core/dataset/constants.py +0 -1
- udata/core/dataset/forms.py +29 -0
- udata/core/dataset/models.py +16 -4
- udata/core/dataset/rdf.py +2 -1
- udata/core/dataset/search.py +2 -2
- udata/core/dataset/tasks.py +86 -8
- udata/core/discussions/mails.py +63 -0
- udata/core/discussions/tasks.py +4 -18
- udata/core/metrics/__init__.py +0 -6
- udata/core/organization/api.py +3 -1
- udata/core/organization/mails.py +144 -0
- udata/core/organization/models.py +2 -1
- udata/core/organization/search.py +1 -1
- udata/core/organization/tasks.py +21 -49
- udata/core/pages/tests/test_api.py +0 -2
- udata/core/reuse/api.py +27 -1
- udata/core/reuse/mails.py +21 -0
- udata/core/reuse/models.py +10 -1
- udata/core/reuse/search.py +1 -1
- udata/core/reuse/tasks.py +2 -3
- udata/core/site/models.py +2 -6
- udata/core/spatial/tests/test_api.py +17 -20
- udata/core/spatial/tests/test_models.py +3 -3
- udata/core/user/mails.py +54 -0
- udata/core/user/models.py +2 -3
- udata/core/user/tasks.py +8 -23
- udata/core/user/tests/test_user_model.py +2 -6
- udata/entrypoints.py +0 -5
- udata/features/identicon/tests/test_backends.py +3 -13
- udata/forms/fields.py +3 -3
- udata/forms/widgets.py +2 -2
- udata/frontend/__init__.py +3 -32
- udata/harvest/actions.py +4 -9
- udata/harvest/api.py +5 -14
- udata/harvest/backends/__init__.py +20 -11
- udata/harvest/backends/base.py +2 -2
- udata/harvest/backends/ckan/harvesters.py +2 -1
- udata/harvest/backends/dcat.py +3 -0
- udata/harvest/backends/maaf.py +1 -0
- udata/harvest/commands.py +6 -4
- udata/harvest/forms.py +9 -6
- udata/harvest/tasks.py +3 -5
- udata/harvest/tests/ckan/test_ckan_backend.py +300 -337
- udata/harvest/tests/ckan/test_ckan_backend_errors.py +94 -99
- udata/harvest/tests/ckan/test_ckan_backend_filters.py +128 -122
- udata/harvest/tests/ckan/test_dkan_backend.py +39 -51
- udata/harvest/tests/dcat/datara--5a26b0f6-0ccf-46ad-ac58-734054b91977.rdf.xml +255 -0
- udata/harvest/tests/dcat/datara--f40c3860-7236-4b30-a141-23b8ae33f7b2.rdf.xml +289 -0
- udata/harvest/tests/factories.py +1 -1
- udata/harvest/tests/test_actions.py +11 -9
- udata/harvest/tests/test_api.py +4 -5
- udata/harvest/tests/test_base_backend.py +5 -4
- udata/harvest/tests/test_dcat_backend.py +50 -19
- udata/harvest/tests/test_models.py +2 -4
- udata/harvest/tests/test_notifications.py +2 -4
- udata/harvest/tests/test_tasks.py +2 -3
- udata/mail.py +90 -53
- udata/migrations/2025-01-05-dataservices-fields-changes.py +8 -14
- udata/migrations/2025-10-21-remove-ckan-harvest-modified-at.py +28 -0
- udata/migrations/2025-10-29-harvesters-sources-integrity.py +27 -0
- udata/mongo/taglist_field.py +3 -3
- udata/rdf.py +32 -15
- udata/sentry.py +3 -4
- udata/settings.py +7 -2
- udata/tags.py +5 -5
- udata/tasks.py +3 -3
- udata/templates/mail/message.html +65 -0
- udata/templates/mail/message.txt +16 -0
- udata/tests/__init__.py +40 -58
- udata/tests/api/__init__.py +87 -2
- udata/tests/api/test_activities_api.py +17 -23
- udata/tests/api/test_auth_api.py +2 -4
- udata/tests/api/test_contact_points.py +48 -54
- udata/tests/api/test_dataservices_api.py +57 -37
- udata/tests/api/test_datasets_api.py +146 -49
- udata/tests/api/test_me_api.py +4 -6
- udata/tests/api/test_organizations_api.py +19 -38
- udata/tests/api/test_reports_api.py +0 -4
- udata/tests/api/test_reuses_api.py +92 -19
- udata/tests/api/test_security_api.py +124 -0
- udata/tests/api/test_swagger.py +2 -3
- udata/tests/api/test_tags_api.py +6 -7
- udata/tests/api/test_transfer_api.py +0 -2
- udata/tests/api/test_user_api.py +8 -10
- udata/tests/apiv2/test_datasets.py +0 -4
- udata/tests/apiv2/test_me_api.py +0 -2
- udata/tests/apiv2/test_organizations.py +0 -2
- udata/tests/apiv2/test_swagger.py +2 -3
- udata/tests/apiv2/test_topics.py +0 -2
- udata/tests/cli/test_cli_base.py +14 -12
- udata/tests/cli/test_db_cli.py +51 -54
- udata/tests/contact_point/test_contact_point_models.py +2 -2
- udata/tests/dataservice/test_csv_adapter.py +2 -5
- udata/tests/dataservice/test_dataservice_rdf.py +8 -6
- udata/tests/dataservice/test_dataservice_tasks.py +36 -38
- udata/tests/dataset/test_csv_adapter.py +2 -5
- udata/tests/dataset/test_dataset_actions.py +2 -4
- udata/tests/dataset/test_dataset_commands.py +2 -4
- udata/tests/dataset/test_dataset_events.py +3 -3
- udata/tests/dataset/test_dataset_model.py +6 -7
- udata/tests/dataset/test_dataset_rdf.py +201 -12
- udata/tests/dataset/test_dataset_recommendations.py +2 -2
- udata/tests/dataset/test_dataset_tasks.py +66 -68
- udata/tests/dataset/test_resource_preview.py +39 -48
- udata/tests/dataset/test_transport_tasks.py +2 -2
- udata/tests/features/territories/__init__.py +0 -6
- udata/tests/features/territories/test_territories_api.py +25 -24
- udata/tests/forms/test_current_user_field.py +2 -2
- udata/tests/forms/test_dict_field.py +2 -4
- udata/tests/forms/test_extras_fields.py +2 -3
- udata/tests/forms/test_image_field.py +2 -2
- udata/tests/forms/test_model_field.py +2 -4
- udata/tests/forms/test_publish_as_field.py +2 -4
- udata/tests/forms/test_user_forms.py +26 -29
- udata/tests/frontend/test_auth.py +2 -3
- udata/tests/frontend/test_csv.py +5 -6
- udata/tests/frontend/test_error_handlers.py +2 -3
- udata/tests/frontend/test_hooks.py +5 -7
- udata/tests/frontend/test_markdown.py +3 -4
- udata/tests/helpers.py +2 -7
- udata/tests/metrics/test_metrics.py +52 -48
- udata/tests/metrics/test_tasks.py +154 -150
- udata/tests/organization/test_csv_adapter.py +2 -5
- udata/tests/organization/test_notifications.py +2 -4
- udata/tests/organization/test_organization_model.py +3 -4
- udata/tests/organization/test_organization_rdf.py +2 -8
- udata/tests/plugin.py +6 -110
- udata/tests/reuse/test_reuse_model.py +3 -4
- udata/tests/site/test_site_api.py +0 -2
- udata/tests/site/test_site_csv_exports.py +0 -2
- udata/tests/site/test_site_metrics.py +2 -4
- udata/tests/site/test_site_model.py +2 -2
- udata/tests/site/test_site_rdf.py +4 -7
- udata/tests/test_activity.py +3 -3
- udata/tests/test_api_fields.py +6 -9
- udata/tests/test_cors.py +0 -2
- udata/tests/test_dcat_commands.py +2 -3
- udata/tests/test_discussions.py +2 -7
- udata/tests/test_mail.py +150 -114
- udata/tests/test_migrations.py +413 -419
- udata/tests/test_model.py +10 -11
- udata/tests/test_notifications.py +2 -3
- udata/tests/test_owned.py +3 -3
- udata/tests/test_rdf.py +19 -15
- udata/tests/test_routing.py +5 -5
- udata/tests/test_storages.py +6 -5
- udata/tests/test_tags.py +2 -4
- udata/tests/test_topics.py +2 -4
- udata/tests/test_transfer.py +4 -5
- udata/tests/topic/test_topic_tasks.py +25 -27
- udata/tests/user/test_user_rdf.py +2 -8
- udata/tests/user/test_user_tasks.py +3 -5
- udata/tests/workers/test_jobs_commands.py +2 -2
- udata/tests/workers/test_tasks_routing.py +27 -27
- udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
- udata/translations/ar/LC_MESSAGES/udata.po +369 -435
- udata/translations/de/LC_MESSAGES/udata.mo +0 -0
- udata/translations/de/LC_MESSAGES/udata.po +371 -437
- udata/translations/es/LC_MESSAGES/udata.mo +0 -0
- udata/translations/es/LC_MESSAGES/udata.po +369 -435
- udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/fr/LC_MESSAGES/udata.po +381 -447
- udata/translations/it/LC_MESSAGES/udata.mo +0 -0
- udata/translations/it/LC_MESSAGES/udata.po +371 -437
- udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
- udata/translations/pt/LC_MESSAGES/udata.po +371 -437
- udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/sr/LC_MESSAGES/udata.po +372 -438
- udata/translations/udata.pot +379 -440
- udata/utils.py +14 -2
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/METADATA +1 -2
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/RECORD +205 -242
- udata/templates/mail/account_deleted.html +0 -5
- udata/templates/mail/account_deleted.txt +0 -6
- udata/templates/mail/account_inactivity.html +0 -40
- udata/templates/mail/account_inactivity.txt +0 -31
- udata/templates/mail/badge_added_association.html +0 -33
- udata/templates/mail/badge_added_association.txt +0 -11
- udata/templates/mail/badge_added_certified.html +0 -33
- udata/templates/mail/badge_added_certified.txt +0 -11
- udata/templates/mail/badge_added_company.html +0 -33
- udata/templates/mail/badge_added_company.txt +0 -11
- udata/templates/mail/badge_added_local_authority.html +0 -33
- udata/templates/mail/badge_added_local_authority.txt +0 -11
- udata/templates/mail/badge_added_public_service.html +0 -33
- udata/templates/mail/badge_added_public_service.txt +0 -11
- udata/templates/mail/discussion_closed.html +0 -47
- udata/templates/mail/discussion_closed.txt +0 -16
- udata/templates/mail/inactive_account_deleted.html +0 -5
- udata/templates/mail/inactive_account_deleted.txt +0 -6
- udata/templates/mail/membership_refused.html +0 -20
- udata/templates/mail/membership_refused.txt +0 -11
- udata/templates/mail/membership_request.html +0 -46
- udata/templates/mail/membership_request.txt +0 -12
- udata/templates/mail/new_discussion.html +0 -44
- udata/templates/mail/new_discussion.txt +0 -15
- udata/templates/mail/new_discussion_comment.html +0 -45
- udata/templates/mail/new_discussion_comment.txt +0 -16
- udata/templates/mail/new_member.html +0 -27
- udata/templates/mail/new_member.txt +0 -11
- udata/templates/mail/new_reuse.html +0 -37
- udata/templates/mail/new_reuse.txt +0 -9
- udata/templates/mail/test.html +0 -6
- udata/templates/mail/test.txt +0 -6
- udata/templates/mail/user_mail_card.html +0 -26
- udata/templates/security/email/base.html +0 -105
- udata/templates/security/email/base.txt +0 -6
- udata/templates/security/email/button.html +0 -3
- udata/templates/security/email/change_notice.html +0 -22
- udata/templates/security/email/change_notice.txt +0 -8
- udata/templates/security/email/confirmation_instructions.html +0 -20
- udata/templates/security/email/confirmation_instructions.txt +0 -7
- udata/templates/security/email/login_instructions.html +0 -19
- udata/templates/security/email/login_instructions.txt +0 -7
- udata/templates/security/email/reset_instructions.html +0 -24
- udata/templates/security/email/reset_instructions.txt +0 -9
- udata/templates/security/email/reset_notice.html +0 -11
- udata/templates/security/email/reset_notice.txt +0 -4
- udata/templates/security/email/welcome.html +0 -24
- udata/templates/security/email/welcome.txt +0 -9
- udata/templates/security/email/welcome_existing.html +0 -32
- udata/templates/security/email/welcome_existing.txt +0 -14
- udata/terms.md +0 -6
- udata/tests/frontend/__init__.py +0 -23
- udata/tests/metrics/conftest.py +0 -15
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/WHEEL +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/entry_points.txt +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/licenses/LICENSE +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from udata.core.dataset.models import Dataset
|
|
2
|
+
from udata.core.reuse.models import Reuse
|
|
3
|
+
from udata.i18n import lazy_gettext as _
|
|
4
|
+
from udata.mail import LabelledContent, MailCTA, MailMessage, ParagraphWithLinks
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def new_reuse(reuse: Reuse, dataset: Dataset) -> MailMessage:
|
|
8
|
+
return MailMessage(
|
|
9
|
+
subject=_("New reuse on your dataset"),
|
|
10
|
+
paragraphs=[
|
|
11
|
+
ParagraphWithLinks(
|
|
12
|
+
_(
|
|
13
|
+
"A new reuse has been published by %(user_or_org)s on your dataset %(dataset)s",
|
|
14
|
+
user_or_org=reuse.organization or reuse.owner,
|
|
15
|
+
dataset=dataset,
|
|
16
|
+
)
|
|
17
|
+
),
|
|
18
|
+
LabelledContent(_("Reuse title:"), str(reuse.title)),
|
|
19
|
+
MailCTA(_("View the reuse"), reuse.url_for()),
|
|
20
|
+
],
|
|
21
|
+
)
|
udata/core/reuse/models.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from blinker import Signal
|
|
2
2
|
from flask import url_for
|
|
3
|
+
from flask_babel import LazyString
|
|
3
4
|
from mongoengine.signals import post_save, pre_save
|
|
4
5
|
from werkzeug.utils import cached_property
|
|
5
6
|
|
|
@@ -22,7 +23,7 @@ from .constants import IMAGE_MAX_SIZE, IMAGE_SIZES, REUSE_TOPICS, REUSE_TYPES
|
|
|
22
23
|
|
|
23
24
|
__all__ = ("Reuse",)
|
|
24
25
|
|
|
25
|
-
BADGES: dict[str,
|
|
26
|
+
BADGES: dict[str, LazyString] = {}
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class ReuseQuerySet(OwnedQuerySet):
|
|
@@ -115,6 +116,14 @@ class Reuse(db.Datetimed, Auditable, WithMetrics, ReuseBadgeMixin, Linkable, Own
|
|
|
115
116
|
"key": "dataset",
|
|
116
117
|
},
|
|
117
118
|
)
|
|
119
|
+
dataservices = field(
|
|
120
|
+
db.ListField(
|
|
121
|
+
field(db.ReferenceField("Dataservice", reverse_delete_rule=db.PULL)),
|
|
122
|
+
),
|
|
123
|
+
filterable={
|
|
124
|
+
"key": "dataservice",
|
|
125
|
+
},
|
|
126
|
+
)
|
|
118
127
|
tags = field(
|
|
119
128
|
db.TagListField(),
|
|
120
129
|
filterable={
|
udata/core/reuse/search.py
CHANGED
|
@@ -40,7 +40,7 @@ class ReuseSearch(ModelSearchAdapter):
|
|
|
40
40
|
|
|
41
41
|
@classmethod
|
|
42
42
|
def is_indexable(cls, reuse: Reuse) -> bool:
|
|
43
|
-
return reuse.
|
|
43
|
+
return reuse.is_visible
|
|
44
44
|
|
|
45
45
|
@classmethod
|
|
46
46
|
def mongo_search(cls, args):
|
udata/core/reuse/tasks.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from udata import mail
|
|
2
1
|
from udata.core import storages
|
|
3
2
|
from udata.core.topic.models import TopicElement
|
|
4
|
-
from udata.i18n import lazy_gettext as _
|
|
5
3
|
from udata.models import Activity, Discussion, Follow, Transfer
|
|
6
4
|
from udata.tasks import get_logger, job, task
|
|
7
5
|
|
|
6
|
+
from . import mails
|
|
8
7
|
from .models import Reuse
|
|
9
8
|
|
|
10
9
|
log = get_logger(__name__)
|
|
@@ -45,4 +44,4 @@ def notify_new_reuse(reuse_id: int) -> None:
|
|
|
45
44
|
else:
|
|
46
45
|
recipients = None
|
|
47
46
|
if recipients:
|
|
48
|
-
|
|
47
|
+
mails.new_reuse(reuse, dataset).send(recipients)
|
udata/core/site/models.py
CHANGED
|
@@ -8,6 +8,7 @@ from udata.core.metrics.helpers import get_metrics_for_model, get_stock_metrics
|
|
|
8
8
|
from udata.core.organization.models import Organization
|
|
9
9
|
from udata.core.reuse.models import Reuse
|
|
10
10
|
from udata.models import WithMetrics, db
|
|
11
|
+
from udata.utils import get_udata_version
|
|
11
12
|
|
|
12
13
|
__all__ = ("Site", "SiteSettings")
|
|
13
14
|
|
|
@@ -66,12 +67,7 @@ class Site(WithMetrics, db.Document):
|
|
|
66
67
|
|
|
67
68
|
@field(description="The current version of udata")
|
|
68
69
|
def version(self):
|
|
69
|
-
|
|
70
|
-
from importlib.metadata import version
|
|
71
|
-
|
|
72
|
-
return version("udata")
|
|
73
|
-
except Exception:
|
|
74
|
-
return None
|
|
70
|
+
return get_udata_version()
|
|
75
71
|
|
|
76
72
|
def count_users(self):
|
|
77
73
|
from udata.models import User
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import pytest
|
|
1
2
|
from flask import url_for
|
|
2
3
|
|
|
3
4
|
from udata.core.dataset.factories import DatasetFactory
|
|
@@ -13,15 +14,12 @@ from udata.core.spatial.tasks import compute_geozones_metrics
|
|
|
13
14
|
from udata.tests.api import APITestCase
|
|
14
15
|
from udata.tests.api.test_datasets_api import SAMPLE_GEOM
|
|
15
16
|
from udata.tests.features.territories import (
|
|
16
|
-
TerritoriesSettings,
|
|
17
17
|
create_geozones_fixtures,
|
|
18
18
|
)
|
|
19
19
|
from udata.utils import faker
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class SpatialApiTest(APITestCase):
|
|
23
|
-
modules = []
|
|
24
|
-
|
|
25
23
|
def test_zones_api_one(self):
|
|
26
24
|
zone = GeoZoneFactory()
|
|
27
25
|
|
|
@@ -65,7 +63,7 @@ class SpatialApiTest(APITestCase):
|
|
|
65
63
|
for i in range(4):
|
|
66
64
|
GeoZoneFactory(name="name-test-{0}".format(i) if i % 2 else faker.word())
|
|
67
65
|
|
|
68
|
-
response = self.get(url_for("api.suggest_zones"
|
|
66
|
+
response = self.get(url_for("api.suggest_zones", q="name-test", size=5))
|
|
69
67
|
self.assert200(response)
|
|
70
68
|
|
|
71
69
|
self.assertEqual(len(response.json), 2)
|
|
@@ -84,7 +82,7 @@ class SpatialApiTest(APITestCase):
|
|
|
84
82
|
for _ in range(2):
|
|
85
83
|
GeoZoneFactory()
|
|
86
84
|
|
|
87
|
-
response = self.get(url_for("api.suggest_zones"
|
|
85
|
+
response = self.get(url_for("api.suggest_zones", q=zone.id))
|
|
88
86
|
self.assert200(response)
|
|
89
87
|
|
|
90
88
|
self.assertEqual(response.json[0]["id"], zone["id"])
|
|
@@ -96,7 +94,7 @@ class SpatialApiTest(APITestCase):
|
|
|
96
94
|
country_zone = GeoZoneFactory(name="name-test-country", level=country_level.id)
|
|
97
95
|
region_zone = GeoZoneFactory(name="name-test-region", level=region_level.id)
|
|
98
96
|
|
|
99
|
-
response = self.get(url_for("api.suggest_zones"
|
|
97
|
+
response = self.get(url_for("api.suggest_zones", q="name-test", size=5))
|
|
100
98
|
self.assert200(response)
|
|
101
99
|
|
|
102
100
|
self.assertEqual(len(response.json), 2)
|
|
@@ -108,7 +106,7 @@ class SpatialApiTest(APITestCase):
|
|
|
108
106
|
for i in range(4):
|
|
109
107
|
GeoZoneFactory(code="code-test-{0}".format(i) if i % 2 else faker.word())
|
|
110
108
|
|
|
111
|
-
response = self.get(url_for("api.suggest_zones"
|
|
109
|
+
response = self.get(url_for("api.suggest_zones", q="code-test", size=5))
|
|
112
110
|
self.assert200(response)
|
|
113
111
|
|
|
114
112
|
self.assertEqual(len(response.json), 2)
|
|
@@ -126,7 +124,7 @@ class SpatialApiTest(APITestCase):
|
|
|
126
124
|
for i in range(3):
|
|
127
125
|
GeoZoneFactory(name=5 * "{0}".format(i), code=3 * "{0}".format(i))
|
|
128
126
|
|
|
129
|
-
response = self.get(url_for("api.suggest_zones"
|
|
127
|
+
response = self.get(url_for("api.suggest_zones", q="xxxxxx", size=5))
|
|
130
128
|
self.assert200(response)
|
|
131
129
|
self.assertEqual(len(response.json), 0)
|
|
132
130
|
|
|
@@ -135,7 +133,7 @@ class SpatialApiTest(APITestCase):
|
|
|
135
133
|
for i in range(4):
|
|
136
134
|
GeoZoneFactory(name="name-testé-{0}".format(i) if i % 2 else faker.word())
|
|
137
135
|
|
|
138
|
-
response = self.get(url_for("api.suggest_zones"
|
|
136
|
+
response = self.get(url_for("api.suggest_zones", q="name-testé", size=5))
|
|
139
137
|
self.assert200(response)
|
|
140
138
|
|
|
141
139
|
self.assertEqual(len(response.json), 2)
|
|
@@ -150,7 +148,7 @@ class SpatialApiTest(APITestCase):
|
|
|
150
148
|
|
|
151
149
|
def test_suggest_zones_empty(self):
|
|
152
150
|
"""It should not provide zones suggestion if no data is present"""
|
|
153
|
-
response = self.get(url_for("api.suggest_zones"
|
|
151
|
+
response = self.get(url_for("api.suggest_zones", q="xxxxxx", size=5))
|
|
154
152
|
self.assert200(response)
|
|
155
153
|
self.assertEqual(len(response.json), 0)
|
|
156
154
|
|
|
@@ -194,7 +192,7 @@ class SpatialApiTest(APITestCase):
|
|
|
194
192
|
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
195
193
|
)
|
|
196
194
|
|
|
197
|
-
response = self.get(url_for("api.zone_datasets", id=paca.id
|
|
195
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id, size=2))
|
|
198
196
|
self.assert200(response)
|
|
199
197
|
self.assertEqual(len(response.json), 2)
|
|
200
198
|
|
|
@@ -206,7 +204,7 @@ class SpatialApiTest(APITestCase):
|
|
|
206
204
|
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
207
205
|
)
|
|
208
206
|
|
|
209
|
-
response = self.get(url_for("api.zone_datasets", id=paca.id
|
|
207
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id, dynamic=1))
|
|
210
208
|
self.assert200(response)
|
|
211
209
|
# No dynamic datasets given that the setting is deactivated by default.
|
|
212
210
|
self.assertEqual(len(response.json), 3)
|
|
@@ -219,7 +217,7 @@ class SpatialApiTest(APITestCase):
|
|
|
219
217
|
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
220
218
|
)
|
|
221
219
|
|
|
222
|
-
response = self.get(url_for("api.zone_datasets", id=paca.id
|
|
220
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id, dynamic=1, size=2))
|
|
223
221
|
self.assert200(response)
|
|
224
222
|
# No dynamic datasets given that the setting is deactivated by default.
|
|
225
223
|
self.assertEqual(len(response.json), 2)
|
|
@@ -260,10 +258,11 @@ class SpatialApiTest(APITestCase):
|
|
|
260
258
|
self.assertEqual(response.json["features"][1]["properties"]["datasets"], 3)
|
|
261
259
|
|
|
262
260
|
|
|
261
|
+
@pytest.mark.options(
|
|
262
|
+
ACTIVATE_TERRITORIES=True,
|
|
263
|
+
HANDLED_LEVELS=("fr:commune", "fr:departement", "fr:region", "country"),
|
|
264
|
+
)
|
|
263
265
|
class SpatialTerritoriesApiTest(APITestCase):
|
|
264
|
-
modules = []
|
|
265
|
-
settings = TerritoriesSettings
|
|
266
|
-
|
|
267
266
|
def test_zone_datasets_with_dynamic_and_setting(self):
|
|
268
267
|
paca, bdr, arles = create_geozones_fixtures()
|
|
269
268
|
organization = OrganizationFactory()
|
|
@@ -272,7 +271,7 @@ class SpatialTerritoriesApiTest(APITestCase):
|
|
|
272
271
|
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
273
272
|
)
|
|
274
273
|
|
|
275
|
-
response = self.get(url_for("api.zone_datasets", id=paca.id
|
|
274
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id, dynamic=1))
|
|
276
275
|
self.assert200(response)
|
|
277
276
|
# No dynamic datasets given that they are added by udata-front extension.
|
|
278
277
|
self.assertEqual(len(response.json), 3)
|
|
@@ -285,15 +284,13 @@ class SpatialTerritoriesApiTest(APITestCase):
|
|
|
285
284
|
organization=organization, spatial=SpatialCoverageFactory(zones=[paca.id])
|
|
286
285
|
)
|
|
287
286
|
|
|
288
|
-
response = self.get(url_for("api.zone_datasets", id=paca.id
|
|
287
|
+
response = self.get(url_for("api.zone_datasets", id=paca.id, dynamic=1, size=2))
|
|
289
288
|
self.assert200(response)
|
|
290
289
|
# No dynamic datasets given that they are added by udata-front extension.
|
|
291
290
|
self.assertEqual(len(response.json), 2)
|
|
292
291
|
|
|
293
292
|
|
|
294
293
|
class DatasetsSpatialAPITest(APITestCase):
|
|
295
|
-
modules = []
|
|
296
|
-
|
|
297
294
|
def test_create_spatial_zones(self):
|
|
298
295
|
paca, _, _ = create_geozones_fixtures()
|
|
299
296
|
granularity = spatial_granularities[0][0]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from datetime import timedelta
|
|
2
2
|
|
|
3
|
-
from udata.tests import
|
|
3
|
+
from udata.tests.api import DBTestCase
|
|
4
4
|
|
|
5
5
|
from ..factories import GeoZoneFactory
|
|
6
6
|
from ..models import GeoZone, SpatialCoverage
|
|
@@ -8,7 +8,7 @@ from ..models import GeoZone, SpatialCoverage
|
|
|
8
8
|
A_YEAR = timedelta(days=365)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class SpacialCoverageTest(
|
|
11
|
+
class SpacialCoverageTest(DBTestCase):
|
|
12
12
|
def test_top_label_empty(self):
|
|
13
13
|
coverage = SpatialCoverage()
|
|
14
14
|
self.assertIsNone(coverage.top_label)
|
|
@@ -19,7 +19,7 @@ class SpacialCoverageTest(DBTestMixin, TestCase):
|
|
|
19
19
|
self.assertEqual(coverage.top_label, "name")
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
class SpatialTemporalResolutionTest(
|
|
22
|
+
class SpatialTemporalResolutionTest(DBTestCase):
|
|
23
23
|
def test_resolve_id_only(self):
|
|
24
24
|
zone = GeoZoneFactory()
|
|
25
25
|
for i in range(3):
|
udata/core/user/mails.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from flask import current_app
|
|
2
|
+
|
|
3
|
+
from udata.i18n import lazy_gettext as _
|
|
4
|
+
from udata.mail import MailCTA, MailMessage
|
|
5
|
+
from udata.uris import homepage_url
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def account_deletion() -> MailMessage:
|
|
9
|
+
return MailMessage(
|
|
10
|
+
subject=_("Account deletion"),
|
|
11
|
+
paragraphs=[_("Your account has now been deleted")],
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def inactive_account_deleted() -> MailMessage:
|
|
16
|
+
return MailMessage(
|
|
17
|
+
subject=_(
|
|
18
|
+
"Deletion of your inactive %(site)s account", site=current_app.config["SITE_TITLE"]
|
|
19
|
+
),
|
|
20
|
+
paragraphs=[
|
|
21
|
+
_(
|
|
22
|
+
"Your account on %(site)s has been deleted due to inactivity",
|
|
23
|
+
site=current_app.config["SITE_TITLE"],
|
|
24
|
+
)
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def inactive_user(user) -> MailMessage:
|
|
30
|
+
config = current_app.config
|
|
31
|
+
|
|
32
|
+
return MailMessage(
|
|
33
|
+
subject=_("Inactivity of your {site} account").format(site=config["SITE_TITLE"]),
|
|
34
|
+
paragraphs=[
|
|
35
|
+
_(
|
|
36
|
+
"We have noticed that your account associated to (%(user_email)s) has been inactive for %(inactivity_years)d years or more on %(site)s, the open platform for public data.",
|
|
37
|
+
user_email=user.email,
|
|
38
|
+
inactivity_years=config["YEARS_OF_INACTIVITY_BEFORE_DELETION"],
|
|
39
|
+
site=config["SITE_TITLE"],
|
|
40
|
+
),
|
|
41
|
+
MailCTA(
|
|
42
|
+
label=_("If you want to keep your account, please log in with your account."),
|
|
43
|
+
link=homepage_url(),
|
|
44
|
+
),
|
|
45
|
+
_(
|
|
46
|
+
"Without logging in, your account will be deleted within %(notify_delay)d days.",
|
|
47
|
+
notify_delay=config["DAYS_BEFORE_ACCOUNT_INACTIVITY_NOTIFY_DELAY"],
|
|
48
|
+
),
|
|
49
|
+
_(
|
|
50
|
+
"This account is not tied to your other administration accounts and you can always re-create an account on the %(site)s platform if necessary.",
|
|
51
|
+
site=config["SITE_TITLE"],
|
|
52
|
+
),
|
|
53
|
+
],
|
|
54
|
+
)
|
udata/core/user/models.py
CHANGED
|
@@ -12,17 +12,16 @@ from flask_security import MongoEngineUserDatastore, RoleMixin, UserMixin
|
|
|
12
12
|
from mongoengine.signals import post_save, pre_save
|
|
13
13
|
from werkzeug.utils import cached_property
|
|
14
14
|
|
|
15
|
-
from udata import mail
|
|
16
15
|
from udata.api_fields import field
|
|
17
16
|
from udata.core import storages
|
|
18
17
|
from udata.core.discussions.models import Discussion
|
|
19
18
|
from udata.core.linkable import Linkable
|
|
20
19
|
from udata.core.storages import avatars, default_image_basename
|
|
21
20
|
from udata.frontend.markdown import mdstrip
|
|
22
|
-
from udata.i18n import lazy_gettext as _
|
|
23
21
|
from udata.models import Follow, WithMetrics, db
|
|
24
22
|
from udata.uris import cdata_url
|
|
25
23
|
|
|
24
|
+
from . import mails
|
|
26
25
|
from .constants import AVATAR_SIZES
|
|
27
26
|
|
|
28
27
|
__all__ = ("User", "Role", "datastore")
|
|
@@ -295,7 +294,7 @@ class User(WithMetrics, UserMixin, Linkable, db.Document):
|
|
|
295
294
|
ContactPoint.objects(owner=self).delete()
|
|
296
295
|
|
|
297
296
|
if notify:
|
|
298
|
-
|
|
297
|
+
mails.account_deletion().send(copied_user)
|
|
299
298
|
|
|
300
299
|
def count_datasets(self):
|
|
301
300
|
from udata.models import Dataset
|
udata/core/user/tasks.py
CHANGED
|
@@ -4,21 +4,14 @@ from datetime import datetime, timedelta
|
|
|
4
4
|
|
|
5
5
|
from flask import current_app
|
|
6
6
|
|
|
7
|
-
from udata import
|
|
8
|
-
from udata.i18n import lazy_gettext as _
|
|
9
|
-
from udata.tasks import job, task
|
|
7
|
+
from udata.tasks import job
|
|
10
8
|
|
|
11
|
-
from .
|
|
9
|
+
from . import mails
|
|
10
|
+
from .models import User
|
|
12
11
|
|
|
13
12
|
log = logging.getLogger(__name__)
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
@task(route="high.mail")
|
|
17
|
-
def send_test_mail(email):
|
|
18
|
-
user = datastore.find_user(email=email)
|
|
19
|
-
mail.send(_("Test mail"), user, "test")
|
|
20
|
-
|
|
21
|
-
|
|
22
15
|
@job("notify-inactive-users")
|
|
23
16
|
def notify_inactive_users(self):
|
|
24
17
|
if not current_app.config["YEARS_OF_INACTIVITY_BEFORE_DELETION"]:
|
|
@@ -41,12 +34,9 @@ def notify_inactive_users(self):
|
|
|
41
34
|
if i >= current_app.config["MAX_NUMBER_OF_USER_INACTIVITY_NOTIFICATIONS"]:
|
|
42
35
|
logging.warning("MAX_NUMBER_OF_USER_INACTIVITY_NOTIFICATIONS reached, stopping here.")
|
|
43
36
|
return
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"account_inactivity",
|
|
48
|
-
user=user,
|
|
49
|
-
)
|
|
37
|
+
|
|
38
|
+
mails.inactive_user(user).send(user)
|
|
39
|
+
|
|
50
40
|
logging.debug(f"Notified {user.email} of account inactivity")
|
|
51
41
|
user.inactive_deletion_notified_at = datetime.utcnow()
|
|
52
42
|
user.save()
|
|
@@ -84,11 +74,6 @@ def delete_inactive_users(self):
|
|
|
84
74
|
copied_user = copy(user)
|
|
85
75
|
user.mark_as_deleted(notify=False, delete_comments=False)
|
|
86
76
|
logging.warning(f"Deleted user {copied_user.email} due to account inactivity")
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
site=current_app.config["SITE_TITLE"]
|
|
90
|
-
),
|
|
91
|
-
copied_user,
|
|
92
|
-
"inactive_account_deleted",
|
|
93
|
-
)
|
|
77
|
+
mails.inactive_account_deleted().send(copied_user)
|
|
78
|
+
|
|
94
79
|
logging.info(f"Deleted {users_to_delete.count()} inactive users")
|
|
@@ -6,14 +6,10 @@ from udata.core.followers.models import Follow
|
|
|
6
6
|
from udata.core.organization.factories import OrganizationFactory
|
|
7
7
|
from udata.core.user.factories import UserFactory
|
|
8
8
|
from udata.core.user.models import User
|
|
9
|
+
from udata.tests.api import APITestCase
|
|
9
10
|
|
|
10
|
-
pytestmark = pytest.mark.usefixtures("clean_db")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@pytest.mark.frontend
|
|
14
|
-
class UserModelTest:
|
|
15
|
-
modules = [] # Required for mails
|
|
16
11
|
|
|
12
|
+
class UserModelTest(APITestCase):
|
|
17
13
|
def test_mark_as_deleted(self):
|
|
18
14
|
user = UserFactory()
|
|
19
15
|
other_user = UserFactory()
|
udata/entrypoints.py
CHANGED
|
@@ -4,12 +4,7 @@ import pkg_resources
|
|
|
4
4
|
ENTRYPOINTS = {
|
|
5
5
|
"udata.avatars": "Avatar rendering backends",
|
|
6
6
|
"udata.harvesters": "Harvest backends",
|
|
7
|
-
"udata.metrics": "Extra metrics",
|
|
8
7
|
"udata.models": "Models and migrations",
|
|
9
|
-
"udata.plugins": "Generic plugin",
|
|
10
|
-
"udata.tasks": "Tasks and jobs",
|
|
11
|
-
"udata.themes": "Themes",
|
|
12
|
-
"udata.views": "Extra views",
|
|
13
8
|
}
|
|
14
9
|
|
|
15
10
|
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
1
|
from udata.features.identicon.backends import internal
|
|
2
|
+
from udata.tests.api import PytestOnlyAPITestCase
|
|
4
3
|
from udata.tests.helpers import assert200
|
|
5
4
|
from udata.utils import faker
|
|
6
5
|
|
|
7
|
-
pytestmark = pytest.mark.usefixtures("app")
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def assert_stream_equal(response1, response2):
|
|
11
|
-
__tracebackhide__ = True
|
|
12
|
-
stream1 = list(response1.iter_encoded())
|
|
13
|
-
stream2 = list(response2.iter_encoded())
|
|
14
|
-
assert stream1 == stream2
|
|
15
|
-
|
|
16
6
|
|
|
17
|
-
class InternalBackendTest:
|
|
7
|
+
class InternalBackendTest(PytestOnlyAPITestCase):
|
|
18
8
|
def test_base_rendering(self):
|
|
19
9
|
response = internal(faker.word(), 32)
|
|
20
10
|
assert200(response)
|
|
@@ -25,4 +15,4 @@ class InternalBackendTest:
|
|
|
25
15
|
|
|
26
16
|
def test_render_twice_the_same(self):
|
|
27
17
|
identifier = faker.word()
|
|
28
|
-
|
|
18
|
+
self.assertStreamEqual(internal(identifier, 32), internal(identifier, 32))
|
udata/forms/fields.py
CHANGED
|
@@ -416,11 +416,11 @@ class TagField(Field):
|
|
|
416
416
|
if not self.data:
|
|
417
417
|
return
|
|
418
418
|
for tag in self.data:
|
|
419
|
-
if not tags.
|
|
419
|
+
if not tags.TAG_MIN_LENGTH <= len(tag) <= tags.TAG_MAX_LENGTH:
|
|
420
420
|
message = _(
|
|
421
421
|
'Tag "%(tag)s" must be between %(min)d and %(max)d characters long.',
|
|
422
|
-
min=tags.
|
|
423
|
-
max=tags.
|
|
422
|
+
min=tags.TAG_MIN_LENGTH,
|
|
423
|
+
max=tags.TAG_MAX_LENGTH,
|
|
424
424
|
tag=tag,
|
|
425
425
|
)
|
|
426
426
|
raise validators.ValidationError(message)
|
udata/forms/widgets.py
CHANGED
udata/frontend/__init__.py
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import inspect
|
|
2
1
|
import logging
|
|
3
|
-
from importlib import import_module
|
|
4
2
|
|
|
5
|
-
import pkg_resources
|
|
6
3
|
from jinja2 import pass_context
|
|
7
4
|
from markupsafe import Markup
|
|
8
5
|
|
|
9
|
-
from udata import entrypoints
|
|
10
6
|
from udata.i18n import I18nBlueprint
|
|
11
7
|
|
|
12
8
|
from .markdown import UdataCleaner
|
|
@@ -20,11 +16,6 @@ hook = I18nBlueprint("hook", __name__)
|
|
|
20
16
|
_template_hooks = {}
|
|
21
17
|
|
|
22
18
|
|
|
23
|
-
@hook.app_template_global()
|
|
24
|
-
def package_version(name: str) -> str:
|
|
25
|
-
return pkg_resources.get_distribution(name).version
|
|
26
|
-
|
|
27
|
-
|
|
28
19
|
@hook.app_template_filter()
|
|
29
20
|
def avatar_placeholder(url):
|
|
30
21
|
if url:
|
|
@@ -101,32 +92,12 @@ class SafeMarkup(Markup):
|
|
|
101
92
|
return super().__new__(cls, cleaner.clean(base), *args, **kwargs)
|
|
102
93
|
|
|
103
94
|
|
|
104
|
-
def
|
|
105
|
-
views
|
|
106
|
-
blueprint = getattr(views, "blueprint", None)
|
|
107
|
-
if blueprint:
|
|
108
|
-
app.register_blueprint(blueprint)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
VIEWS = ["core.storages"]
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def init_app(app, views=None):
|
|
115
|
-
views = views or VIEWS
|
|
95
|
+
def init_app(app):
|
|
96
|
+
from udata.core.storages.views import blueprint as storage_blueprint
|
|
116
97
|
|
|
117
98
|
init_markdown(app)
|
|
118
99
|
|
|
119
|
-
|
|
120
|
-
_load_views(app, "udata.{}.views".format(view))
|
|
100
|
+
app.register_blueprint(storage_blueprint)
|
|
121
101
|
|
|
122
102
|
# Load hook blueprint
|
|
123
103
|
app.register_blueprint(hook)
|
|
124
|
-
|
|
125
|
-
# Load all plugins views and blueprints
|
|
126
|
-
for module in entrypoints.get_enabled("udata.views", app).values():
|
|
127
|
-
_load_views(app, module)
|
|
128
|
-
|
|
129
|
-
# Load all plugins views and blueprints
|
|
130
|
-
for module in entrypoints.get_enabled("udata.front", app).values():
|
|
131
|
-
front_module = module if inspect.ismodule(module) else import_module(module)
|
|
132
|
-
front_module.init_app(app)
|
udata/harvest/actions.py
CHANGED
|
@@ -34,11 +34,6 @@ def get_source(ident):
|
|
|
34
34
|
return HarvestSource.get(ident)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def list_backends():
|
|
38
|
-
"""List all available backends"""
|
|
39
|
-
return backends.get_all(current_app).values()
|
|
40
|
-
|
|
41
|
-
|
|
42
37
|
def list_sources(owner=None, deleted=False):
|
|
43
38
|
"""List all harvest sources"""
|
|
44
39
|
sources = HarvestSource.objects
|
|
@@ -177,7 +172,7 @@ def purge_jobs():
|
|
|
177
172
|
|
|
178
173
|
def run(source: HarvestSource):
|
|
179
174
|
"""Launch or resume an harvesting for a given source if none is running"""
|
|
180
|
-
cls = backends.
|
|
175
|
+
cls = backends.get_backend(source.backend)
|
|
181
176
|
backend = cls(source)
|
|
182
177
|
backend.harvest()
|
|
183
178
|
|
|
@@ -189,7 +184,7 @@ def launch(source: HarvestSource):
|
|
|
189
184
|
|
|
190
185
|
def preview(source: HarvestSource):
|
|
191
186
|
"""Preview an harvesting for a given source"""
|
|
192
|
-
cls = backends.
|
|
187
|
+
cls = backends.get_backend(source.backend)
|
|
193
188
|
max_items = current_app.config["HARVEST_PREVIEW_MAX_ITEMS"]
|
|
194
189
|
backend = cls(source, dryrun=True, max_items=max_items)
|
|
195
190
|
return backend.harvest()
|
|
@@ -226,7 +221,7 @@ def preview_from_config(
|
|
|
226
221
|
active=active,
|
|
227
222
|
autoarchive=autoarchive,
|
|
228
223
|
)
|
|
229
|
-
cls = backends.
|
|
224
|
+
cls = backends.get_backend(source.backend)
|
|
230
225
|
max_items = current_app.config["HARVEST_PREVIEW_MAX_ITEMS"]
|
|
231
226
|
backend = cls(source, dryrun=True, max_items=max_items)
|
|
232
227
|
return backend.harvest()
|
|
@@ -273,7 +268,7 @@ def schedule(
|
|
|
273
268
|
def unschedule(source: HarvestSource):
|
|
274
269
|
"""Unschedule an harvesting on a source"""
|
|
275
270
|
if not source.periodic_task:
|
|
276
|
-
msg = "Harvesting on source {0} is
|
|
271
|
+
msg = "Harvesting on source {0} is not scheduled".format(source.name)
|
|
277
272
|
raise ValueError(msg)
|
|
278
273
|
|
|
279
274
|
source.periodic_task.delete()
|
udata/harvest/api.py
CHANGED
|
@@ -10,6 +10,7 @@ from udata.core.dataset.permissions import OwnablePermission
|
|
|
10
10
|
from udata.core.organization.api_fields import org_ref_fields
|
|
11
11
|
from udata.core.organization.permissions import EditOrganizationPermission
|
|
12
12
|
from udata.core.user.api_fields import user_ref_fields
|
|
13
|
+
from udata.harvest.backends import get_enabled_backends
|
|
13
14
|
|
|
14
15
|
from . import actions
|
|
15
16
|
from .forms import HarvestSourceForm, HarvestSourceValidationForm
|
|
@@ -25,10 +26,6 @@ from .models import (
|
|
|
25
26
|
ns = api.namespace("harvest", "Harvest related operations")
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
def backends_ids():
|
|
29
|
-
return [b.name for b in actions.list_backends()]
|
|
30
|
-
|
|
31
|
-
|
|
32
29
|
error_fields = api.model(
|
|
33
30
|
"HarvestError",
|
|
34
31
|
{
|
|
@@ -126,7 +123,9 @@ source_fields = api.model(
|
|
|
126
123
|
"description": fields.Markdown(description="The source description"),
|
|
127
124
|
"url": fields.String(description="The source base URL", required=True),
|
|
128
125
|
"backend": fields.String(
|
|
129
|
-
description="The source backend",
|
|
126
|
+
description="The source backend",
|
|
127
|
+
enum=lambda: list(get_enabled_backends().keys()),
|
|
128
|
+
required=True,
|
|
130
129
|
),
|
|
131
130
|
"config": fields.Raw(description="The configuration as key-value pairs"),
|
|
132
131
|
"created_at": fields.ISODateTime(
|
|
@@ -457,15 +456,7 @@ class ListBackendsAPI(API):
|
|
|
457
456
|
"features": [f.as_dict() for f in b.features],
|
|
458
457
|
"extra_configs": [f.as_dict() for f in b.extra_configs],
|
|
459
458
|
}
|
|
460
|
-
for b in
|
|
459
|
+
for b in get_enabled_backends().values()
|
|
461
460
|
],
|
|
462
461
|
key=lambda b: b["label"],
|
|
463
462
|
)
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
@ns.route("/job_status/", endpoint="havest_job_status")
|
|
467
|
-
class ListHarvesterAPI(API):
|
|
468
|
-
@api.doc(model=[str])
|
|
469
|
-
def get(self):
|
|
470
|
-
"""List all available harvesters"""
|
|
471
|
-
return actions.list_backends()
|