udata 13.0.1.dev12__py3-none-any.whl → 14.0.0__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 +2 -8
- udata/app.py +12 -30
- udata/auth/forms.py +6 -4
- udata/commands/__init__.py +2 -14
- udata/commands/db.py +13 -25
- udata/commands/info.py +0 -16
- udata/core/avatars/api.py +43 -0
- udata/core/avatars/test_avatar_api.py +30 -0
- udata/core/dataservices/models.py +14 -2
- udata/core/dataset/tasks.py +36 -8
- udata/core/metrics/__init__.py +0 -6
- udata/core/site/models.py +2 -6
- udata/core/spatial/commands.py +2 -4
- udata/core/spatial/models.py +0 -10
- udata/core/spatial/tests/test_api.py +1 -36
- udata/core/user/models.py +10 -1
- udata/cors.py +2 -5
- udata/db/migrations.py +279 -0
- udata/frontend/__init__.py +3 -122
- udata/harvest/actions.py +3 -8
- udata/harvest/api.py +5 -14
- udata/harvest/backends/__init__.py +21 -9
- udata/harvest/backends/base.py +2 -2
- udata/harvest/backends/ckan/harvesters.py +2 -0
- 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 +2 -2
- udata/harvest/tests/ckan/test_ckan_backend_errors.py +1 -1
- udata/harvest/tests/ckan/test_ckan_backend_filters.py +1 -1
- udata/harvest/tests/ckan/test_dkan_backend.py +1 -1
- udata/harvest/tests/dcat/udata.xml +6 -6
- udata/harvest/tests/factories.py +1 -1
- udata/harvest/tests/test_actions.py +5 -3
- udata/harvest/tests/test_api.py +2 -1
- udata/harvest/tests/test_base_backend.py +2 -0
- udata/harvest/tests/test_dcat_backend.py +3 -3
- udata/i18n.py +14 -273
- udata/migrations/2025-11-13-delete-user-email-index.py +25 -0
- udata/models/__init__.py +0 -8
- udata/routing.py +0 -8
- udata/sentry.py +4 -10
- udata/settings.py +16 -17
- udata/tasks.py +3 -3
- udata/tests/__init__.py +1 -10
- udata/tests/api/test_dataservices_api.py +29 -1
- udata/tests/api/test_datasets_api.py +1 -2
- udata/tests/api/test_user_api.py +12 -0
- udata/tests/apiv2/test_topics.py +1 -1
- udata/tests/dataset/test_resource_preview.py +0 -1
- udata/tests/helpers.py +12 -0
- udata/tests/test_cors.py +1 -1
- udata/tests/test_migrations.py +181 -481
- udata/utils.py +5 -0
- {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/METADATA +1 -2
- {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/RECORD +62 -73
- {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/entry_points.txt +3 -5
- udata/core/followers/views.py +0 -15
- udata/entrypoints.py +0 -93
- udata/features/identicon/__init__.py +0 -0
- udata/features/identicon/api.py +0 -13
- udata/features/identicon/backends.py +0 -131
- udata/features/identicon/tests/__init__.py +0 -0
- udata/features/identicon/tests/test_backends.py +0 -18
- udata/features/territories/__init__.py +0 -49
- udata/features/territories/api.py +0 -25
- udata/features/territories/models.py +0 -51
- udata/migrations/__init__.py +0 -367
- udata/tests/cli/test_db_cli.py +0 -68
- udata/tests/features/territories/__init__.py +0 -20
- udata/tests/features/territories/test_territories_api.py +0 -185
- udata/tests/frontend/test_hooks.py +0 -149
- {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/WHEEL +0 -0
- {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/licenses/LICENSE +0 -0
- {udata-13.0.1.dev12.dist-info → udata-14.0.0.dist-info}/top_level.txt +0 -0
udata/sentry.py
CHANGED
|
@@ -2,15 +2,13 @@ import logging
|
|
|
2
2
|
import re
|
|
3
3
|
import warnings
|
|
4
4
|
|
|
5
|
-
import pkg_resources
|
|
6
5
|
from werkzeug.exceptions import HTTPException
|
|
7
6
|
|
|
8
|
-
from udata import entrypoints
|
|
9
7
|
from udata.core.storages.api import UploadProgress
|
|
8
|
+
from udata.utils import get_udata_version
|
|
10
9
|
|
|
11
10
|
from .app import UDataApp
|
|
12
11
|
from .auth import PermissionDenied
|
|
13
|
-
from .frontend import package_version
|
|
14
12
|
|
|
15
13
|
log = logging.getLogger(__name__)
|
|
16
14
|
|
|
@@ -64,7 +62,7 @@ def init_app(app: UDataApp):
|
|
|
64
62
|
dsn=app.config["SENTRY_PUBLIC_DSN"],
|
|
65
63
|
integrations=[FlaskIntegration(), CeleryIntegration()],
|
|
66
64
|
ignore_errors=list(exceptions),
|
|
67
|
-
release=f"udata@{
|
|
65
|
+
release=f"udata@{get_udata_version()}",
|
|
68
66
|
environment=app.config.get("SITE_ID", None),
|
|
69
67
|
# Set traces_sample_rate to 1.0 to capture 100%
|
|
70
68
|
# of transactions for performance monitoring.
|
|
@@ -83,9 +81,5 @@ def init_app(app: UDataApp):
|
|
|
83
81
|
tags = app.config["SENTRY_TAGS"]
|
|
84
82
|
for tag_key in tags:
|
|
85
83
|
sentry_sdk.set_tag(tag_key, tags[tag_key])
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if dist.version:
|
|
89
|
-
sentry_sdk.set_tag(dist.project_name, dist.version)
|
|
90
|
-
# Do not forget udata itself
|
|
91
|
-
sentry_sdk.set_tag("udata", pkg_resources.get_distribution("udata").version)
|
|
84
|
+
|
|
85
|
+
sentry_sdk.set_tag("udata", get_udata_version())
|
udata/settings.py
CHANGED
|
@@ -21,7 +21,6 @@ class Defaults(object):
|
|
|
21
21
|
DEFAULT_LANGUAGE = "en"
|
|
22
22
|
SECRET_KEY = "Default uData secret key"
|
|
23
23
|
CONTACT_EMAIL = "contact@example.org"
|
|
24
|
-
TERRITORIES_EMAIL = "territories@example.org"
|
|
25
24
|
|
|
26
25
|
CDATA_BASE_URL = None
|
|
27
26
|
|
|
@@ -175,7 +174,7 @@ class Defaults(object):
|
|
|
175
174
|
|
|
176
175
|
UDATA_INSTANCE_NAME = "udata"
|
|
177
176
|
|
|
178
|
-
|
|
177
|
+
HARVESTER_BACKENDS = []
|
|
179
178
|
THEME = None
|
|
180
179
|
|
|
181
180
|
STATIC_DIRS = []
|
|
@@ -332,10 +331,6 @@ class Defaults(object):
|
|
|
332
331
|
# - add inspire keyword during harvest if GEMETE INSPIRE thesaurus is used in DCAT.theme
|
|
333
332
|
INSPIRE_SUPPORT = True
|
|
334
333
|
|
|
335
|
-
ACTIVATE_TERRITORIES = False
|
|
336
|
-
# The order is important to compute parents/children, smaller first.
|
|
337
|
-
HANDLED_LEVELS = tuple()
|
|
338
|
-
|
|
339
334
|
# Ignore some endpoint from API tracking
|
|
340
335
|
# By default ignore the 3 most called APIs
|
|
341
336
|
TRACKING_BLACKLIST = [
|
|
@@ -466,19 +461,22 @@ class Defaults(object):
|
|
|
466
461
|
# if set to anything else than `None`
|
|
467
462
|
###########################################################################
|
|
468
463
|
# avatar provider used to render user avatars
|
|
469
|
-
AVATAR_PROVIDER = None
|
|
470
464
|
# Number of blocks used by the internal provider
|
|
471
|
-
AVATAR_INTERNAL_SIZE =
|
|
465
|
+
AVATAR_INTERNAL_SIZE = 7
|
|
472
466
|
# List of foreground colors used by the internal provider
|
|
473
|
-
AVATAR_INTERNAL_FOREGROUND =
|
|
467
|
+
AVATAR_INTERNAL_FOREGROUND = [
|
|
468
|
+
"rgb(45,79,255)",
|
|
469
|
+
"rgb(254,180,44)",
|
|
470
|
+
"rgb(226,121,234)",
|
|
471
|
+
"rgb(30,179,253)",
|
|
472
|
+
"rgb(232,77,65)",
|
|
473
|
+
"rgb(49,203,115)",
|
|
474
|
+
"rgb(141,69,170)",
|
|
475
|
+
]
|
|
474
476
|
# Background color used by the internal provider
|
|
475
|
-
AVATAR_INTERNAL_BACKGROUND =
|
|
477
|
+
AVATAR_INTERNAL_BACKGROUND = "rgb(224,224,224)"
|
|
476
478
|
# Padding (in percent) used by the internal provider
|
|
477
|
-
AVATAR_INTERNAL_PADDING =
|
|
478
|
-
# Skin (set) used by the robohash provider
|
|
479
|
-
AVATAR_ROBOHASH_SKIN = None
|
|
480
|
-
# The background used by the robohash provider.
|
|
481
|
-
AVATAR_ROBOHASH_BACKGROUND = None
|
|
479
|
+
AVATAR_INTERNAL_PADDING = 10
|
|
482
480
|
|
|
483
481
|
# Post settings
|
|
484
482
|
###########################################################################
|
|
@@ -530,6 +528,8 @@ class Defaults(object):
|
|
|
530
528
|
"harvest",
|
|
531
529
|
)
|
|
532
530
|
EXPORT_CSV_DATASET_ID = None
|
|
531
|
+
EXPORT_CSV_ARCHIVE_S3_BUCKET = None # If this setting is set, an archive is uploaded to the corresponding S3 bucket every first day of the month (if export-csv is scheduled to run daily)
|
|
532
|
+
EXPORT_CSV_ARCHIVE_S3_FILENAME_PREFIX = "" # Useful to store the csv archives inside a subfolder of the bucket, ie setting 'csv-catalog-archives/'`
|
|
533
533
|
|
|
534
534
|
# Autocomplete parameters
|
|
535
535
|
#########################
|
|
@@ -636,7 +636,7 @@ class Testing(object):
|
|
|
636
636
|
CELERY_TASK_ALWAYS_EAGER = True
|
|
637
637
|
CELERY_TASK_EAGER_PROPAGATES = True
|
|
638
638
|
TEST_WITH_PLUGINS = False
|
|
639
|
-
|
|
639
|
+
HARVESTER_BACKENDS = ["factory"]
|
|
640
640
|
TEST_WITH_THEME = False
|
|
641
641
|
THEME = "testing"
|
|
642
642
|
CACHE_TYPE = "flask_caching.backends.null"
|
|
@@ -644,7 +644,6 @@ class Testing(object):
|
|
|
644
644
|
DEBUG_TOOLBAR = False
|
|
645
645
|
SERVER_NAME = "local.test"
|
|
646
646
|
DEFAULT_LANGUAGE = "fr"
|
|
647
|
-
ACTIVATE_TERRITORIES = False
|
|
648
647
|
LOGGER_HANDLER_POLICY = "never"
|
|
649
648
|
CELERYD_HIJACK_ROOT_LOGGER = False
|
|
650
649
|
URLS_ALLOW_LOCAL = True # Test server URL is local.test
|
udata/tasks.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from importlib.metadata import entry_points
|
|
2
3
|
from urllib.parse import urlparse
|
|
3
4
|
|
|
4
5
|
from celery import Celery, Task
|
|
5
6
|
from celery.utils.log import get_task_logger
|
|
6
7
|
from celerybeatmongo.schedulers import MongoScheduler
|
|
7
8
|
|
|
8
|
-
from udata import entrypoints
|
|
9
|
-
|
|
10
9
|
log = logging.getLogger(__name__)
|
|
11
10
|
|
|
12
11
|
|
|
@@ -176,6 +175,7 @@ def init_app(app):
|
|
|
176
175
|
import udata.harvest.tasks # noqa
|
|
177
176
|
import udata.db.tasks # noqa
|
|
178
177
|
|
|
179
|
-
|
|
178
|
+
for ep in entry_points(group="udata.tasks"):
|
|
179
|
+
ep.load()
|
|
180
180
|
|
|
181
181
|
return celery
|
udata/tests/__init__.py
CHANGED
|
@@ -27,16 +27,7 @@ class TestCaseMixin:
|
|
|
27
27
|
Not sure if the plugin situation is still relevant now that plugins are integrated
|
|
28
28
|
into udata.
|
|
29
29
|
"""
|
|
30
|
-
|
|
31
|
-
# apply the options(plugins) marker from pytest_flask as soon as app is created
|
|
32
|
-
# https://github.com/pytest-dev/pytest-flask/blob/a62ea18cb0fe89e3f3911192ab9ea4f9b12f8a16/pytest_flask/plugin.py#L126
|
|
33
|
-
# this lets us have default settings for plugins applied while testing
|
|
34
|
-
plugins = getattr(_settings, "PLUGINS", [])
|
|
35
|
-
for options in request.node.iter_markers("options"):
|
|
36
|
-
option = options.kwargs.get("plugins", []) or options.kwargs.get("PLUGINS", [])
|
|
37
|
-
plugins += option
|
|
38
|
-
setattr(_settings, "PLUGINS", plugins)
|
|
39
|
-
return _settings
|
|
30
|
+
return settings.Testing
|
|
40
31
|
|
|
41
32
|
@pytest.fixture(autouse=True, name="app")
|
|
42
33
|
def _app(self, request):
|
|
@@ -16,7 +16,7 @@ from udata.core.dataservices.models import Dataservice
|
|
|
16
16
|
from udata.core.dataset.factories import DatasetFactory, LicenseFactory
|
|
17
17
|
from udata.core.organization.factories import OrganizationFactory
|
|
18
18
|
from udata.core.organization.models import Member
|
|
19
|
-
from udata.core.topic.factories import TopicElementFactory, TopicFactory
|
|
19
|
+
from udata.core.topic.factories import ReuseFactory, TopicElementFactory, TopicFactory
|
|
20
20
|
from udata.core.user.factories import AdminFactory, UserFactory
|
|
21
21
|
from udata.i18n import gettext as _
|
|
22
22
|
from udata.tests.helpers import assert200, assert400, assert410
|
|
@@ -99,6 +99,34 @@ class DataserviceAPITest(APITestCase):
|
|
|
99
99
|
assert len(response.json["data"]) == 1
|
|
100
100
|
assert response.json["data"][0]["id"] == str(topic_dataservice.id)
|
|
101
101
|
|
|
102
|
+
# filter on reuse
|
|
103
|
+
reuse_dataservice = DataserviceFactory()
|
|
104
|
+
reuse = ReuseFactory(dataservices=[reuse_dataservice])
|
|
105
|
+
response = self.get(url_for("api.dataservices", reuse=reuse.id))
|
|
106
|
+
assert200(response)
|
|
107
|
+
assert len(response.json["data"]) == 1
|
|
108
|
+
assert response.json["data"][0]["id"] == str(reuse_dataservice.id)
|
|
109
|
+
|
|
110
|
+
def test_dataservices_topic_filter_errors(self):
|
|
111
|
+
# non-existing
|
|
112
|
+
response = self.get(url_for("api.dataservices", topic="690c7f48ec85adaa376c1e93"))
|
|
113
|
+
assert200(response)
|
|
114
|
+
|
|
115
|
+
# not an object ID
|
|
116
|
+
response = self.get(url_for("api.dataservices", topic="xxx"))
|
|
117
|
+
assert400(response)
|
|
118
|
+
assert "`topic` must be an identifier" in response.json["message"]
|
|
119
|
+
|
|
120
|
+
def test_dataservices_reuse_filter_errors(self):
|
|
121
|
+
# non-existing
|
|
122
|
+
response = self.get(url_for("api.dataservices", reuse="690c7f48ec85adaa376c1e93"))
|
|
123
|
+
assert200(response)
|
|
124
|
+
|
|
125
|
+
# not an object ID
|
|
126
|
+
response = self.get(url_for("api.dataservices", reuse="xxx"))
|
|
127
|
+
assert400(response)
|
|
128
|
+
assert "`reuse` must be an identifier" in response.json["message"]
|
|
129
|
+
|
|
102
130
|
def test_dataservice_api_create(self):
|
|
103
131
|
user = self.login()
|
|
104
132
|
datasets = DatasetFactory.create_batch(3)
|
|
@@ -49,8 +49,7 @@ from udata.core.user.factories import AdminFactory, UserFactory
|
|
|
49
49
|
from udata.i18n import gettext as _
|
|
50
50
|
from udata.models import CommunityResource, Dataset, Follow, Member, db
|
|
51
51
|
from udata.tags import TAG_MAX_LENGTH, TAG_MIN_LENGTH
|
|
52
|
-
from udata.tests.
|
|
53
|
-
from udata.tests.helpers import assert200, assert404
|
|
52
|
+
from udata.tests.helpers import assert200, assert404, create_geozones_fixtures
|
|
54
53
|
from udata.utils import faker, unique_string
|
|
55
54
|
|
|
56
55
|
from . import APITestCase, PytestOnlyAPITestCase
|
udata/tests/api/test_user_api.py
CHANGED
|
@@ -175,6 +175,18 @@ class UserAPITest(APITestCase):
|
|
|
175
175
|
|
|
176
176
|
self.assertEqual(len(response.json["data"]), 2)
|
|
177
177
|
|
|
178
|
+
def test_user_api_full_text_search_email(self):
|
|
179
|
+
"""It should find users based on last name"""
|
|
180
|
+
self.login(AdminFactory())
|
|
181
|
+
|
|
182
|
+
UserFactory(email="john@example.org")
|
|
183
|
+
UserFactory(email="jane@example.org")
|
|
184
|
+
|
|
185
|
+
response = self.get(url_for("api.users", q="jane"))
|
|
186
|
+
self.assert200(response)
|
|
187
|
+
|
|
188
|
+
self.assertEqual(len(response.json["data"]), 1)
|
|
189
|
+
|
|
178
190
|
def test_user_api_full_text_search_unicode(self):
|
|
179
191
|
"""It should find user with special characters"""
|
|
180
192
|
self.login(AdminFactory())
|
udata/tests/apiv2/test_topics.py
CHANGED
|
@@ -23,7 +23,7 @@ from udata.core.user.factories import UserFactory
|
|
|
23
23
|
from udata.i18n import _
|
|
24
24
|
from udata.tests.api import APITestCase
|
|
25
25
|
from udata.tests.api.test_datasets_api import SAMPLE_GEOM
|
|
26
|
-
from udata.tests.
|
|
26
|
+
from udata.tests.helpers import create_geozones_fixtures
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class TopicsListAPITest(APITestCase):
|
udata/tests/helpers.py
CHANGED
|
@@ -9,6 +9,7 @@ from flask import current_app, json
|
|
|
9
9
|
from flask_security.babel import FsDomain
|
|
10
10
|
from PIL import Image
|
|
11
11
|
|
|
12
|
+
from udata.core.spatial.factories import GeoZoneFactory
|
|
12
13
|
from udata.mail import mail_sent
|
|
13
14
|
|
|
14
15
|
|
|
@@ -221,5 +222,16 @@ def create_test_image():
|
|
|
221
222
|
return file
|
|
222
223
|
|
|
223
224
|
|
|
225
|
+
def create_geozones_fixtures():
|
|
226
|
+
paca = GeoZoneFactory(
|
|
227
|
+
id="fr:region:93", level="fr:region", name="Provence Alpes Côtes dAzur", code="93"
|
|
228
|
+
)
|
|
229
|
+
bdr = GeoZoneFactory(
|
|
230
|
+
id="fr:departement:13", level="fr:departement", name="Bouches-du-Rhône", code="13"
|
|
231
|
+
)
|
|
232
|
+
arles = GeoZoneFactory(id="fr:commune:13004", level="fr:commune", name="Arles", code="13004")
|
|
233
|
+
return paca, bdr, arles
|
|
234
|
+
|
|
235
|
+
|
|
224
236
|
def security_gettext(string):
|
|
225
237
|
return FsDomain(current_app).gettext(string)
|
udata/tests/test_cors.py
CHANGED
|
@@ -27,7 +27,7 @@ class CorsTest(APITestCase):
|
|
|
27
27
|
assert "Access-Control-Allow-Origin" in response.headers
|
|
28
28
|
|
|
29
29
|
# Resource permalink
|
|
30
|
-
response = self.get(f"/
|
|
30
|
+
response = self.get(f"/datasets/r/{dataset.resources[0].id}", headers=cors_headers)
|
|
31
31
|
assert_status(response, 404) # The route is defined in udata-front
|
|
32
32
|
assert "Access-Control-Allow-Origin" in response.headers
|
|
33
33
|
|