udata 12.0.2.dev10__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 -4
- 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 +10 -12
- 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.py +15 -24
- 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 +24 -42
- 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 +20 -14
- udata/core/organization/mails.py +144 -0
- udata/core/organization/models.py +2 -1
- udata/core/organization/rdf.py +3 -3
- 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 +29 -3
- 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/api.py +27 -19
- udata/core/site/models.py +2 -6
- udata/core/site/rdf.py +2 -2
- 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 -6
- 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/bnodes.xml +17 -1
- 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 +72 -16
- 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/models/__init__.py +0 -2
- udata/mongo/extras_fields.py +4 -3
- udata/mongo/taglist_field.py +3 -3
- udata/rdf.py +65 -20
- udata/sentry.py +3 -4
- udata/settings.py +15 -13
- 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 +65 -97
- udata/tests/api/test_datasets_api.py +171 -56
- 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 +99 -23
- 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 +64 -4
- 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 +205 -16
- 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 +6 -12
- 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 +85 -29
- 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 +66 -4
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/METADATA +1 -4
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/RECORD +212 -256
- udata/linkchecker/__init__.py +0 -0
- udata/linkchecker/backends.py +0 -31
- udata/linkchecker/checker.py +0 -75
- udata/linkchecker/commands.py +0 -21
- udata/linkchecker/models.py +0 -9
- udata/linkchecker/tasks.py +0 -55
- 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/tests/test_linkchecker.py +0 -277
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/WHEEL +0 -0
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/entry_points.txt +0 -0
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/licenses/LICENSE +0 -0
- {udata-12.0.2.dev10.dist-info → udata-13.0.1.dev21.dist-info}/top_level.txt +0 -0
udata/core/badges/tasks.py
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from mongoengine.base import TopLevelDocumentMetaclass
|
|
4
|
+
|
|
5
|
+
from udata.tasks import get_logger, job, task
|
|
2
6
|
|
|
3
7
|
from .signals import on_badge_added
|
|
4
8
|
|
|
5
9
|
log = get_logger(__name__)
|
|
6
10
|
|
|
11
|
+
_badge_jobs: dict[tuple[TopLevelDocumentMetaclass, str], Any] = {}
|
|
12
|
+
|
|
7
13
|
|
|
8
14
|
def notify_new_badge(cls, kind):
|
|
9
15
|
def wrapper(func):
|
|
@@ -17,3 +23,31 @@ def notify_new_badge(cls, kind):
|
|
|
17
23
|
return t
|
|
18
24
|
|
|
19
25
|
return wrapper
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def register(model: TopLevelDocumentMetaclass, badge: str):
|
|
29
|
+
"""Register a job to update some badge"""
|
|
30
|
+
|
|
31
|
+
def inner(func):
|
|
32
|
+
_badge_jobs[(model, badge)] = func
|
|
33
|
+
return func
|
|
34
|
+
|
|
35
|
+
return inner
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_badge_job(model, badge):
|
|
39
|
+
return _badge_jobs.get((model, badge))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@job(name="update-badges")
|
|
43
|
+
def update_badges(self, badges: list[str] = []) -> None:
|
|
44
|
+
from udata.core.dataservices.models import Dataservice
|
|
45
|
+
from udata.models import Dataset, Organization, Reuse
|
|
46
|
+
|
|
47
|
+
for model in [Dataset, Reuse, Organization, Dataservice]:
|
|
48
|
+
for badge in model.__badges__:
|
|
49
|
+
if badges and badge not in badges:
|
|
50
|
+
continue
|
|
51
|
+
if adapter := get_badge_job(model, badge):
|
|
52
|
+
log.info(f"Running {model.__name__} {badge} job")
|
|
53
|
+
adapter()
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
from tempfile import NamedTemporaryFile
|
|
2
2
|
|
|
3
|
-
import pytest
|
|
4
|
-
|
|
5
3
|
from udata.core.organization.constants import CERTIFIED, PUBLIC_SERVICE
|
|
6
4
|
from udata.core.organization.factories import OrganizationFactory
|
|
5
|
+
from udata.tests.api import PytestOnlyDBTestCase
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
class BadgeCommandTest:
|
|
8
|
+
class BadgeCommandTest(PytestOnlyDBTestCase):
|
|
11
9
|
def toggle(self, path_or_id, kind):
|
|
12
10
|
return self.cli("badges", "toggle", path_or_id, kind)
|
|
13
11
|
|
|
@@ -2,7 +2,7 @@ from udata.api_fields import field
|
|
|
2
2
|
from udata.auth import login_user
|
|
3
3
|
from udata.core.user.factories import UserFactory
|
|
4
4
|
from udata.mongo import db
|
|
5
|
-
from udata.tests import
|
|
5
|
+
from udata.tests.api import DBTestCase
|
|
6
6
|
|
|
7
7
|
from ..models import Badge, BadgeMixin, BadgesList
|
|
8
8
|
|
|
@@ -33,7 +33,7 @@ class Fake(db.Document, FakeBadgeMixin):
|
|
|
33
33
|
pass
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
class BadgeMixinTest(
|
|
36
|
+
class BadgeMixinTest(DBTestCase):
|
|
37
37
|
def test_attributes(self):
|
|
38
38
|
"""It should have a badge list"""
|
|
39
39
|
fake = Fake.objects.create()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import udata.core.dataservices.tasks # noqa
|
|
2
|
+
import udata.core.dataset.tasks # noqa
|
|
3
|
+
from udata.core.badges.tasks import update_badges
|
|
4
|
+
from udata.core.constants import HVD
|
|
5
|
+
from udata.core.dataservices.factories import DataserviceFactory
|
|
6
|
+
from udata.core.dataservices.models import Dataservice
|
|
7
|
+
from udata.core.dataset.factories import DatasetFactory
|
|
8
|
+
from udata.core.dataset.models import Dataset
|
|
9
|
+
from udata.core.organization.constants import CERTIFIED, PUBLIC_SERVICE
|
|
10
|
+
from udata.core.organization.factories import OrganizationFactory
|
|
11
|
+
from udata.tests.api import DBTestCase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BadgeTasksTest(DBTestCase):
|
|
15
|
+
def test_update_badges(self):
|
|
16
|
+
"""
|
|
17
|
+
Test update_badges run the appropriate badge update jobs.
|
|
18
|
+
In particular, test that the two following registered jobs run and work as expected:
|
|
19
|
+
- update_dataset_hvd_badge
|
|
20
|
+
- update_dataservice_hvd_badge
|
|
21
|
+
"""
|
|
22
|
+
org = OrganizationFactory()
|
|
23
|
+
org.add_badge(PUBLIC_SERVICE)
|
|
24
|
+
org.add_badge(CERTIFIED)
|
|
25
|
+
|
|
26
|
+
datasets = [
|
|
27
|
+
DatasetFactory(organization=org, tags=["hvd"]), # Should be badged HVD
|
|
28
|
+
DatasetFactory(organization=org, tags=["random"]), # Should not be badged HVD
|
|
29
|
+
DatasetFactory(
|
|
30
|
+
organization=org,
|
|
31
|
+
tags=[],
|
|
32
|
+
badges=[Dataset.badges.field.document_type(kind=HVD)],
|
|
33
|
+
), # Badge should be remove
|
|
34
|
+
]
|
|
35
|
+
dataservices = [
|
|
36
|
+
DataserviceFactory(organization=org, tags=["hvd"]), # Should be badged HVD
|
|
37
|
+
DataserviceFactory(organization=org, tags=["random"]), # Should not be badged HVD
|
|
38
|
+
DataserviceFactory(
|
|
39
|
+
organization=org,
|
|
40
|
+
tags=[],
|
|
41
|
+
badges=[Dataservice.badges.field.document_type(kind=HVD)],
|
|
42
|
+
), # Badge should be remove
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
update_badges.run()
|
|
46
|
+
|
|
47
|
+
[model.reload() for model in (*datasets, *dataservices)]
|
|
48
|
+
|
|
49
|
+
assert datasets[0].badges[0].kind == HVD
|
|
50
|
+
assert datasets[1].badges == []
|
|
51
|
+
assert datasets[2].badges == []
|
|
52
|
+
|
|
53
|
+
assert dataservices[0].badges[0].kind == HVD
|
|
54
|
+
assert dataservices[1].badges == []
|
|
55
|
+
assert dataservices[2].badges == []
|
udata/core/constants.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
HVD = "hvd"
|
|
@@ -9,6 +9,14 @@ CONTACT_ROLES = {
|
|
|
9
9
|
"contact": _("Contact"),
|
|
10
10
|
"creator": _("Creator"),
|
|
11
11
|
"publisher": _("Publisher"),
|
|
12
|
+
"rightsHolder": _("Rights Holder"),
|
|
13
|
+
"custodian": _("Custodian"),
|
|
14
|
+
"distributor": _("Distributor"),
|
|
15
|
+
"originator": _("Originator"),
|
|
16
|
+
"principalInvestigator": _("Principal Investigator"),
|
|
17
|
+
"processor": _("Processor"),
|
|
18
|
+
"resourceProvider": _("Resource Provider"),
|
|
19
|
+
"user": _("User"),
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
|
udata/core/dataservices/api.py
CHANGED
|
@@ -9,13 +9,13 @@ from flask_login import current_user
|
|
|
9
9
|
from udata.api import API, api, fields
|
|
10
10
|
from udata.api_fields import patch
|
|
11
11
|
from udata.auth import admin_permission
|
|
12
|
-
from udata.core.
|
|
12
|
+
from udata.core.access_type.constants import AccessType
|
|
13
13
|
from udata.core.dataset.models import Dataset
|
|
14
14
|
from udata.core.followers.api import FollowAPI
|
|
15
|
-
from udata.core.site.models import current_site
|
|
16
15
|
from udata.frontend.markdown import md
|
|
17
16
|
from udata.i18n import gettext as _
|
|
18
17
|
from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
|
|
18
|
+
from udata.utils import get_rss_feed_list
|
|
19
19
|
|
|
20
20
|
from .models import Dataservice
|
|
21
21
|
from .rdf import dataservice_to_rdf
|
|
@@ -48,7 +48,7 @@ class DataservicesAPI(API):
|
|
|
48
48
|
dataservice = patch(Dataservice(), request)
|
|
49
49
|
if not dataservice.owner and not dataservice.organization:
|
|
50
50
|
dataservice.owner = current_user._get_current_object()
|
|
51
|
-
if dataservice.access_type !=
|
|
51
|
+
if dataservice.access_type != AccessType.RESTRICTED:
|
|
52
52
|
dataservice.access_audiences = []
|
|
53
53
|
dataservice.save()
|
|
54
54
|
return dataservice, 201
|
|
@@ -62,9 +62,7 @@ class DataservicesAtomFeedAPI(API):
|
|
|
62
62
|
_("Latest APIs"), description=None, feed_url=request.url, link=request.url_root
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
-
dataservices
|
|
66
|
-
Dataservice.objects.visible().order_by("-created_at").limit(current_site.feed_size)
|
|
67
|
-
)
|
|
65
|
+
dataservices = get_rss_feed_list(Dataservice.objects.visible(), "created_at")
|
|
68
66
|
for dataservice in dataservices:
|
|
69
67
|
author_name = None
|
|
70
68
|
author_uri = None
|
|
@@ -117,7 +115,7 @@ class DataserviceAPI(API):
|
|
|
117
115
|
|
|
118
116
|
patch(dataservice, request)
|
|
119
117
|
dataservice.metadata_modified_at = datetime.utcnow()
|
|
120
|
-
if dataservice.access_type !=
|
|
118
|
+
if dataservice.access_type != AccessType.RESTRICTED:
|
|
121
119
|
dataservice.access_audiences = []
|
|
122
120
|
|
|
123
121
|
dataservice.save()
|
|
@@ -238,19 +236,19 @@ class DataserviceDatasetAPI(API):
|
|
|
238
236
|
class DataserviceRdfAPI(API):
|
|
239
237
|
@api.doc("rdf_dataservice")
|
|
240
238
|
def get(self, dataservice):
|
|
241
|
-
|
|
242
|
-
url = url_for("api.dataservice_rdf_format", dataservice=dataservice.id,
|
|
239
|
+
_format = RDF_EXTENSIONS[negociate_content()]
|
|
240
|
+
url = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, _format=_format)
|
|
243
241
|
return redirect(url)
|
|
244
242
|
|
|
245
243
|
|
|
246
244
|
@ns.route(
|
|
247
|
-
"/<dataservice:dataservice>/rdf.<
|
|
245
|
+
"/<dataservice:dataservice>/rdf.<_format>", endpoint="dataservice_rdf_format", doc=common_doc
|
|
248
246
|
)
|
|
249
247
|
@api.response(404, "Dataservice not found")
|
|
250
248
|
@api.response(410, "Dataservice has been deleted")
|
|
251
249
|
class DataserviceRdfFormatAPI(API):
|
|
252
250
|
@api.doc("rdf_dataservice_format")
|
|
253
|
-
def get(self, dataservice: Dataservice,
|
|
251
|
+
def get(self, dataservice: Dataservice, _format):
|
|
254
252
|
if not dataservice.permissions["edit"].can():
|
|
255
253
|
if dataservice.private:
|
|
256
254
|
api.abort(404)
|
|
@@ -260,7 +258,7 @@ class DataserviceRdfFormatAPI(API):
|
|
|
260
258
|
resource = dataservice_to_rdf(dataservice)
|
|
261
259
|
# bypass flask-restplus make_response, since graph_response
|
|
262
260
|
# is handling the content negociation directly
|
|
263
|
-
return make_response(*graph_response(resource,
|
|
261
|
+
return make_response(*graph_response(resource, _format))
|
|
264
262
|
|
|
265
263
|
|
|
266
264
|
@ns.route("/<id>/followers/", endpoint="dataservice_followers")
|
udata/core/dataservices/apiv2.py
CHANGED
|
@@ -2,7 +2,8 @@ from flask import request
|
|
|
2
2
|
|
|
3
3
|
from udata import search
|
|
4
4
|
from udata.api import API, apiv2
|
|
5
|
-
from udata.core.
|
|
5
|
+
from udata.core.access_type.models import AccessAudience
|
|
6
|
+
from udata.core.dataservices.models import Dataservice, HarvestMetadata
|
|
6
7
|
from udata.utils import multi_to_dict
|
|
7
8
|
|
|
8
9
|
from .models import dataservice_permissions_fields
|
|
@@ -11,6 +12,7 @@ from .search import DataserviceSearch
|
|
|
11
12
|
apiv2.inherit("DataservicePermissions", dataservice_permissions_fields)
|
|
12
13
|
apiv2.inherit("DataservicePage", Dataservice.__page_fields__)
|
|
13
14
|
apiv2.inherit("Dataservice (read)", Dataservice.__read_fields__)
|
|
15
|
+
apiv2.inherit("DataserviceReference", Dataservice.__ref_fields__)
|
|
14
16
|
apiv2.inherit("HarvestMetadata (read)", HarvestMetadata.__read_fields__)
|
|
15
17
|
apiv2.inherit("AccessAudience (read)", AccessAudience.__read_fields__)
|
|
16
18
|
|
|
@@ -1,30 +1 @@
|
|
|
1
1
|
DATASERVICE_FORMATS = ["REST", "WMS", "WSL"]
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
DATASERVICE_ACCESS_TYPE_OPEN = "open"
|
|
5
|
-
DATASERVICE_ACCESS_TYPE_OPEN_WITH_ACCOUNT = "open_with_account"
|
|
6
|
-
DATASERVICE_ACCESS_TYPE_RESTRICTED = "restricted"
|
|
7
|
-
DATASERVICE_ACCESS_TYPES = [
|
|
8
|
-
DATASERVICE_ACCESS_TYPE_OPEN,
|
|
9
|
-
DATASERVICE_ACCESS_TYPE_OPEN_WITH_ACCOUNT,
|
|
10
|
-
DATASERVICE_ACCESS_TYPE_RESTRICTED,
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION = "local_authority_and_administration"
|
|
14
|
-
DATASERVICE_ACCESS_AUDIENCE_COMPANY = "company_and_association"
|
|
15
|
-
DATASERVICE_ACCESS_AUDIENCE_PRIVATE = "private"
|
|
16
|
-
|
|
17
|
-
DATASERVICE_ACCESS_AUDIENCE_TYPES = [
|
|
18
|
-
DATASERVICE_ACCESS_AUDIENCE_ADMINISTRATION,
|
|
19
|
-
DATASERVICE_ACCESS_AUDIENCE_COMPANY,
|
|
20
|
-
DATASERVICE_ACCESS_AUDIENCE_PRIVATE,
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
DATASERVICE_ACCESS_AUDIENCE_YES = "yes"
|
|
24
|
-
DATASERVICE_ACCESS_AUDIENCE_NO = "no"
|
|
25
|
-
DATASERVICE_ACCESS_AUDIENCE_UNDER_CONDITIONS = "under_condition"
|
|
26
|
-
DATASERVICE_ACCESS_AUDIENCE_CONDITIONS = [
|
|
27
|
-
DATASERVICE_ACCESS_AUDIENCE_YES,
|
|
28
|
-
DATASERVICE_ACCESS_AUDIENCE_NO,
|
|
29
|
-
DATASERVICE_ACCESS_AUDIENCE_UNDER_CONDITIONS,
|
|
30
|
-
]
|
|
@@ -2,19 +2,17 @@ from datetime import datetime
|
|
|
2
2
|
|
|
3
3
|
from blinker import Signal
|
|
4
4
|
from flask import url_for
|
|
5
|
+
from flask_babel import LazyString
|
|
5
6
|
from mongoengine import Q
|
|
6
7
|
from mongoengine.signals import post_save
|
|
7
8
|
|
|
8
9
|
import udata.core.contact_point.api_fields as contact_api_fields
|
|
9
10
|
from udata.api import api, fields
|
|
10
11
|
from udata.api_fields import field, generate_fields
|
|
12
|
+
from udata.core.access_type.models import WithAccessType
|
|
11
13
|
from udata.core.activity.models import Auditable
|
|
12
|
-
from udata.core.
|
|
13
|
-
|
|
14
|
-
DATASERVICE_ACCESS_AUDIENCE_TYPES,
|
|
15
|
-
DATASERVICE_ACCESS_TYPES,
|
|
16
|
-
DATASERVICE_FORMATS,
|
|
17
|
-
)
|
|
14
|
+
from udata.core.constants import HVD
|
|
15
|
+
from udata.core.dataservices.constants import DATASERVICE_FORMATS
|
|
18
16
|
from udata.core.dataset.api_fields import dataset_ref_fields
|
|
19
17
|
from udata.core.dataset.models import Dataset
|
|
20
18
|
from udata.core.linkable import Linkable
|
|
@@ -22,19 +20,12 @@ from udata.core.metrics.helpers import get_stock_metrics
|
|
|
22
20
|
from udata.core.metrics.models import WithMetrics
|
|
23
21
|
from udata.core.owned import Owned, OwnedQuerySet
|
|
24
22
|
from udata.i18n import lazy_gettext as _
|
|
25
|
-
from udata.models import Discussion, Follow, db
|
|
26
|
-
from udata.mongo.errors import FieldValidationError
|
|
23
|
+
from udata.models import Badge, BadgeMixin, BadgesList, Discussion, Follow, db
|
|
27
24
|
from udata.uris import cdata_url
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# "page"
|
|
33
|
-
# "quality" # Peut-être pas dans une v1 car la qualité sera probablement calculé différemment
|
|
34
|
-
# "datasets" # objet : liste de datasets liés à une API
|
|
35
|
-
# "spatial"
|
|
36
|
-
# "temporal_coverage"
|
|
37
|
-
|
|
26
|
+
BADGES: dict[str, LazyString] = {
|
|
27
|
+
HVD: _("Dataservice serving high value datasets"),
|
|
28
|
+
}
|
|
38
29
|
|
|
39
30
|
dataservice_permissions_fields = api.model(
|
|
40
31
|
"DataservicePermissions",
|
|
@@ -89,6 +80,20 @@ class DataserviceQuerySet(OwnedQuerySet):
|
|
|
89
80
|
return self(dataservices_filter)
|
|
90
81
|
|
|
91
82
|
|
|
83
|
+
def validate_badge(value):
|
|
84
|
+
if value not in Dataservice.__badges__.keys():
|
|
85
|
+
raise db.ValidationError("Unknown badge type")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class DataserviceBadge(Badge):
|
|
89
|
+
kind = db.StringField(required=True, validation=validate_badge)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class DataserviceBadgeMixin(BadgeMixin):
|
|
93
|
+
badges = field(BadgesList(DataserviceBadge), **BadgeMixin.default_badges_list_params)
|
|
94
|
+
__badges__ = BADGES
|
|
95
|
+
|
|
96
|
+
|
|
92
97
|
@generate_fields()
|
|
93
98
|
class HarvestMetadata(db.EmbeddedDocument):
|
|
94
99
|
backend = field(db.StringField())
|
|
@@ -119,28 +124,13 @@ class HarvestMetadata(db.EmbeddedDocument):
|
|
|
119
124
|
archived_reason = field(db.StringField())
|
|
120
125
|
|
|
121
126
|
|
|
122
|
-
@generate_fields()
|
|
123
|
-
class AccessAudience(db.EmbeddedDocument):
|
|
124
|
-
role = field(db.StringField(choices=DATASERVICE_ACCESS_AUDIENCE_TYPES), filterable={})
|
|
125
|
-
condition = field(db.StringField(choices=DATASERVICE_ACCESS_AUDIENCE_CONDITIONS), filterable={})
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def check_only_one_condition_per_role(access_audiences, **_kwargs):
|
|
129
|
-
roles = set(e["role"] for e in access_audiences)
|
|
130
|
-
if len(roles) != len(access_audiences):
|
|
131
|
-
raise FieldValidationError(
|
|
132
|
-
_("You can only set one condition for a given access audience role"),
|
|
133
|
-
field="access_audiences",
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
|
|
137
127
|
def filter_by_topic(base_query, filter_value):
|
|
138
128
|
from udata.core.topic.models import Topic
|
|
139
129
|
|
|
140
130
|
try:
|
|
141
131
|
topic = Topic.objects.get(id=filter_value)
|
|
142
132
|
except Topic.DoesNotExist:
|
|
143
|
-
|
|
133
|
+
return base_query
|
|
144
134
|
else:
|
|
145
135
|
return base_query.filter(
|
|
146
136
|
id__in=[
|
|
@@ -150,18 +140,32 @@ def filter_by_topic(base_query, filter_value):
|
|
|
150
140
|
)
|
|
151
141
|
|
|
152
142
|
|
|
143
|
+
def filter_by_reuse(base_query, filter_value):
|
|
144
|
+
from udata.core.reuse.models import Reuse
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
reuse = Reuse.objects.get(id=filter_value)
|
|
148
|
+
except Reuse.DoesNotExist:
|
|
149
|
+
return base_query
|
|
150
|
+
else:
|
|
151
|
+
return base_query.filter(id__in=[dataservice.id for dataservice in reuse.dataservices])
|
|
152
|
+
|
|
153
|
+
|
|
153
154
|
@generate_fields(
|
|
154
155
|
searchable=True,
|
|
155
156
|
nested_filters={"organization_badge": "organization.badges"},
|
|
156
157
|
standalone_filters=[
|
|
157
|
-
{"key": "topic", "constraints": "objectid", "query": filter_by_topic, "type": str}
|
|
158
|
+
{"key": "topic", "constraints": ["objectid"], "query": filter_by_topic, "type": str},
|
|
159
|
+
{"key": "reuse", "constraints": ["objectid"], "query": filter_by_reuse, "type": str},
|
|
158
160
|
],
|
|
159
161
|
additional_sorts=[
|
|
160
162
|
{"key": "followers", "value": "metrics.followers"},
|
|
161
163
|
{"key": "views", "value": "metrics.views"},
|
|
162
164
|
],
|
|
163
165
|
)
|
|
164
|
-
class Dataservice(
|
|
166
|
+
class Dataservice(
|
|
167
|
+
Auditable, WithMetrics, WithAccessType, DataserviceBadgeMixin, Linkable, Owned, db.Document
|
|
168
|
+
):
|
|
165
169
|
meta = {
|
|
166
170
|
"indexes": [
|
|
167
171
|
"$title",
|
|
@@ -212,14 +216,6 @@ class Dataservice(Auditable, WithMetrics, Linkable, Owned, db.Document):
|
|
|
212
216
|
availability = field(db.FloatField(min=0, max=100), example="99.99")
|
|
213
217
|
availability_url = field(db.URLField())
|
|
214
218
|
|
|
215
|
-
access_type = field(db.StringField(choices=DATASERVICE_ACCESS_TYPES), filterable={})
|
|
216
|
-
access_audiences = field(
|
|
217
|
-
db.EmbeddedDocumentListField(AccessAudience),
|
|
218
|
-
checks=[check_only_one_condition_per_role],
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
authorization_request_url = field(db.URLField())
|
|
222
|
-
|
|
223
219
|
format = field(db.StringField(choices=DATASERVICE_FORMATS))
|
|
224
220
|
|
|
225
221
|
license = field(
|
|
@@ -303,7 +299,7 @@ class Dataservice(Auditable, WithMetrics, Linkable, Owned, db.Document):
|
|
|
303
299
|
auditable=False,
|
|
304
300
|
)
|
|
305
301
|
|
|
306
|
-
@field(description="Link to the API endpoint for this dataservice")
|
|
302
|
+
@field(description="Link to the API endpoint for this dataservice", show_as_ref=True)
|
|
307
303
|
def self_api_url(self, **kwargs):
|
|
308
304
|
return url_for(
|
|
309
305
|
"api.dataservice",
|
|
@@ -323,6 +319,10 @@ class Dataservice(Auditable, WithMetrics, Linkable, Owned, db.Document):
|
|
|
323
319
|
"views",
|
|
324
320
|
]
|
|
325
321
|
|
|
322
|
+
@property
|
|
323
|
+
def is_visible(self):
|
|
324
|
+
return not self.is_hidden
|
|
325
|
+
|
|
326
326
|
@property
|
|
327
327
|
def is_hidden(self):
|
|
328
328
|
return self.private or self.deleted_at or self.archived_at
|
udata/core/dataservices/rdf.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from flask import current_app
|
|
2
2
|
from rdflib import RDF, BNode, Graph, Literal, URIRef
|
|
3
3
|
|
|
4
|
+
from udata.core.constants import HVD
|
|
4
5
|
from udata.core.dataservices.models import Dataservice
|
|
5
6
|
from udata.core.dataservices.models import HarvestMetadata as HarvestDataserviceMetadata
|
|
6
7
|
from udata.core.dataset.models import Dataset, License
|
|
@@ -146,7 +147,7 @@ def dataservice_to_rdf(dataservice: Dataservice, graph=None):
|
|
|
146
147
|
|
|
147
148
|
# Add DCAT-AP HVD properties if the dataservice is tagged hvd.
|
|
148
149
|
# See https://semiceu.github.io/DCAT-AP/releases/2.2.0-hvd/
|
|
149
|
-
is_hvd = current_app.config["HVD_SUPPORT"] and
|
|
150
|
+
is_hvd = current_app.config["HVD_SUPPORT"] and any(b.kind == HVD for b in dataservice.badges)
|
|
150
151
|
if is_hvd:
|
|
151
152
|
d.add(DCATAP.applicableLegislation, URIRef(HVD_LEGISLATION))
|
|
152
153
|
|
|
@@ -5,11 +5,7 @@ from flask_restx.inputs import boolean
|
|
|
5
5
|
|
|
6
6
|
from udata.api import api
|
|
7
7
|
from udata.api.parsers import ModelApiParser
|
|
8
|
-
from udata.core.
|
|
9
|
-
DATASERVICE_ACCESS_TYPE_OPEN,
|
|
10
|
-
DATASERVICE_ACCESS_TYPE_OPEN_WITH_ACCOUNT,
|
|
11
|
-
DATASERVICE_ACCESS_TYPE_RESTRICTED,
|
|
12
|
-
)
|
|
8
|
+
from udata.core.access_type.constants import AccessType
|
|
13
9
|
from udata.models import Dataservice, Organization, User
|
|
14
10
|
from udata.search import (
|
|
15
11
|
BoolFilter,
|
|
@@ -54,9 +50,9 @@ class DataserviceApiParser(ModelApiParser):
|
|
|
54
50
|
dataservices = dataservices.filter(organization=args["organization"])
|
|
55
51
|
if "is_restricted" in args:
|
|
56
52
|
dataservices = dataservices.filter(
|
|
57
|
-
access_type__in=[
|
|
53
|
+
access_type__in=[AccessType.RESTRICTED]
|
|
58
54
|
if boolean(args["is_restricted"])
|
|
59
|
-
else [
|
|
55
|
+
else [AccessType.OPEN, AccessType.OPEN_WITH_ACCOUNT]
|
|
60
56
|
)
|
|
61
57
|
if args.get("featured"):
|
|
62
58
|
dataservices = dataservices.filter(featured=args["featured"])
|
|
@@ -79,7 +75,7 @@ class DataserviceSearch(ModelSearchAdapter):
|
|
|
79
75
|
|
|
80
76
|
@classmethod
|
|
81
77
|
def is_indexable(cls, dataservice: Dataservice) -> bool:
|
|
82
|
-
return dataservice.
|
|
78
|
+
return dataservice.is_visible
|
|
83
79
|
|
|
84
80
|
@classmethod
|
|
85
81
|
def mongo_search(cls, args):
|
|
@@ -126,6 +122,6 @@ class DataserviceSearch(ModelSearchAdapter):
|
|
|
126
122
|
"tags": dataservice.tags,
|
|
127
123
|
"extras": extras,
|
|
128
124
|
"followers": dataservice.metrics.get("followers", 0),
|
|
129
|
-
"is_restricted": dataservice.access_type ==
|
|
125
|
+
"is_restricted": dataservice.access_type == AccessType.RESTRICTED,
|
|
130
126
|
"views": dataservice.metrics.get("views", 0),
|
|
131
127
|
}
|
udata/core/dataservices/tasks.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
from celery.utils.log import get_task_logger
|
|
2
|
+
from flask import current_app
|
|
2
3
|
|
|
4
|
+
from udata.core.badges import tasks as badge_tasks
|
|
5
|
+
from udata.core.constants import HVD
|
|
3
6
|
from udata.core.dataservices.models import Dataservice
|
|
7
|
+
from udata.core.organization.constants import CERTIFIED, PUBLIC_SERVICE
|
|
8
|
+
from udata.core.organization.models import Organization
|
|
4
9
|
from udata.core.topic.models import TopicElement
|
|
5
10
|
from udata.harvest.models import HarvestJob
|
|
6
11
|
from udata.models import Discussion, Follow, Transfer
|
|
@@ -25,3 +30,31 @@ def purge_dataservices(self):
|
|
|
25
30
|
TopicElement.objects(element=dataservice).update(element=None)
|
|
26
31
|
# Remove dataservice
|
|
27
32
|
dataservice.delete()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@badge_tasks.register(model=Dataservice, badge=HVD)
|
|
36
|
+
def update_dataservice_hvd_badge() -> None:
|
|
37
|
+
"""
|
|
38
|
+
Update HVD badges to candidate dataservices, based on the hvd tag.
|
|
39
|
+
Only dataservices owned by certified and public service organizations are candidate to have a HVD badge.
|
|
40
|
+
"""
|
|
41
|
+
if not current_app.config["HVD_SUPPORT"]:
|
|
42
|
+
log.error("You need to set HVD_SUPPORT if you want to update dataservice hvd badge")
|
|
43
|
+
return
|
|
44
|
+
public_certified_orgs = (
|
|
45
|
+
Organization.objects(badges__kind=PUBLIC_SERVICE).filter(badges__kind=CERTIFIED).only("id")
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
dataservices = Dataservice.objects(
|
|
49
|
+
tags="hvd", badges__kind__ne="hvd", organization__in=public_certified_orgs
|
|
50
|
+
)
|
|
51
|
+
log.info(f"Adding HVD badge to {dataservices.count()} dataservices")
|
|
52
|
+
for dataservice in dataservices:
|
|
53
|
+
dataservice.add_badge(HVD)
|
|
54
|
+
|
|
55
|
+
dataservices = Dataservice.objects(
|
|
56
|
+
tags__nin=["hvd"], badges__kind="hvd", organization__in=public_certified_orgs
|
|
57
|
+
)
|
|
58
|
+
log.info(f"Remove HVD badge from {dataservices.count()} dataservices")
|
|
59
|
+
for dataservice in dataservices:
|
|
60
|
+
dataservice.remove_badge(HVD)
|
udata/core/dataset/api.py
CHANGED
|
@@ -41,14 +41,12 @@ from udata.core.followers.api import FollowAPI
|
|
|
41
41
|
from udata.core.followers.models import Follow
|
|
42
42
|
from udata.core.organization.models import Organization
|
|
43
43
|
from udata.core.reuse.models import Reuse
|
|
44
|
-
from udata.core.site.models import current_site
|
|
45
44
|
from udata.core.storages.api import handle_upload, upload_parser
|
|
46
45
|
from udata.core.topic.models import Topic
|
|
47
46
|
from udata.frontend.markdown import md
|
|
48
47
|
from udata.i18n import gettext as _
|
|
49
|
-
from udata.linkchecker.checker import check_resource
|
|
50
48
|
from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
|
|
51
|
-
from udata.utils import get_by
|
|
49
|
+
from udata.utils import get_by, get_rss_feed_list
|
|
52
50
|
|
|
53
51
|
from .api_fields import (
|
|
54
52
|
catalog_schema_fields,
|
|
@@ -291,6 +289,12 @@ community_parser.add_argument(
|
|
|
291
289
|
|
|
292
290
|
common_doc = {"params": {"dataset": "The dataset ID or slug"}}
|
|
293
291
|
|
|
292
|
+
# Build catalog_parser from DatasetApiParser parser with a default page_size of 100
|
|
293
|
+
catalog_parser = DatasetApiParser().parser
|
|
294
|
+
catalog_parser.replace_argument(
|
|
295
|
+
"page_size", type=int, location="args", default=100, help="The page size"
|
|
296
|
+
)
|
|
297
|
+
|
|
294
298
|
|
|
295
299
|
@ns.route("/", endpoint="datasets")
|
|
296
300
|
class DatasetListAPI(API):
|
|
@@ -331,9 +335,10 @@ class DatasetsAtomFeedAPI(API):
|
|
|
331
335
|
link=request.url_root,
|
|
332
336
|
)
|
|
333
337
|
|
|
334
|
-
datasets: list[Dataset] = (
|
|
335
|
-
Dataset.objects.visible()
|
|
338
|
+
datasets: list[Dataset] = get_rss_feed_list(
|
|
339
|
+
Dataset.objects.visible(), "created_at_internal"
|
|
336
340
|
)
|
|
341
|
+
|
|
337
342
|
for dataset in datasets:
|
|
338
343
|
author_name = None
|
|
339
344
|
author_uri = None
|
|
@@ -432,17 +437,17 @@ class DatasetFeaturedAPI(API):
|
|
|
432
437
|
class DatasetRdfAPI(API):
|
|
433
438
|
@api.doc("rdf_dataset")
|
|
434
439
|
def get(self, dataset):
|
|
435
|
-
|
|
436
|
-
url = url_for("api.dataset_rdf_format", dataset=dataset.id,
|
|
440
|
+
_format = RDF_EXTENSIONS[negociate_content()]
|
|
441
|
+
url = url_for("api.dataset_rdf_format", dataset=dataset.id, _format=_format)
|
|
437
442
|
return redirect(url)
|
|
438
443
|
|
|
439
444
|
|
|
440
|
-
@ns.route("/<dataset:dataset>/rdf.<
|
|
445
|
+
@ns.route("/<dataset:dataset>/rdf.<_format>", endpoint="dataset_rdf_format", doc=common_doc)
|
|
441
446
|
@api.response(404, "Dataset not found")
|
|
442
447
|
@api.response(410, "Dataset has been deleted")
|
|
443
448
|
class DatasetRdfFormatAPI(API):
|
|
444
449
|
@api.doc("rdf_dataset_format")
|
|
445
|
-
def get(self, dataset,
|
|
450
|
+
def get(self, dataset, _format):
|
|
446
451
|
if not dataset.permissions["edit"].can():
|
|
447
452
|
if dataset.private:
|
|
448
453
|
api.abort(404)
|
|
@@ -452,7 +457,7 @@ class DatasetRdfFormatAPI(API):
|
|
|
452
457
|
resource = dataset_to_rdf(dataset)
|
|
453
458
|
# bypass flask-restplus make_response, since graph_response
|
|
454
459
|
# is handling the content negociation directly
|
|
455
|
-
return make_response(*graph_response(resource,
|
|
460
|
+
return make_response(*graph_response(resource, _format))
|
|
456
461
|
|
|
457
462
|
|
|
458
463
|
@ns.route("/badges/", endpoint="available_dataset_badges")
|
|
@@ -902,20 +907,6 @@ class AllowedExtensionsAPI(API):
|
|
|
902
907
|
return sorted(current_app.config["ALLOWED_RESOURCES_EXTENSIONS"])
|
|
903
908
|
|
|
904
909
|
|
|
905
|
-
@ns.route(
|
|
906
|
-
"/<dataset:dataset>/resources/<uuid:rid>/check/",
|
|
907
|
-
endpoint="check_dataset_resource",
|
|
908
|
-
doc=common_doc,
|
|
909
|
-
)
|
|
910
|
-
@api.param("rid", "The resource unique identifier")
|
|
911
|
-
class CheckDatasetResource(API, ResourceMixin):
|
|
912
|
-
@api.doc("check_dataset_resource")
|
|
913
|
-
def get(self, dataset, rid):
|
|
914
|
-
"""Checks that a resource's URL exists and returns metadata."""
|
|
915
|
-
resource = self.get_resource_or_404(dataset, rid)
|
|
916
|
-
return check_resource(resource)
|
|
917
|
-
|
|
918
|
-
|
|
919
910
|
@ns.route("/resource_types/", endpoint="resource_types")
|
|
920
911
|
class ResourceTypesAPI(API):
|
|
921
912
|
@api.doc("resource_types")
|