udata 14.0.3.dev1__py3-none-any.whl → 14.7.3.dev4__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.
- udata/api/__init__.py +2 -0
- udata/api_fields.py +120 -19
- udata/app.py +18 -20
- udata/auth/__init__.py +4 -7
- udata/auth/forms.py +3 -3
- udata/auth/views.py +13 -6
- udata/commands/dcat.py +1 -1
- udata/commands/serve.py +3 -11
- udata/core/activity/api.py +5 -6
- udata/core/badges/tests/test_tasks.py +0 -2
- udata/core/csv.py +5 -0
- udata/core/dataservices/api.py +8 -1
- udata/core/dataservices/apiv2.py +3 -6
- udata/core/dataservices/models.py +5 -2
- udata/core/dataservices/rdf.py +2 -1
- udata/core/dataservices/tasks.py +6 -2
- udata/core/dataset/api.py +30 -4
- udata/core/dataset/api_fields.py +1 -1
- udata/core/dataset/apiv2.py +1 -1
- udata/core/dataset/constants.py +2 -9
- udata/core/dataset/models.py +21 -9
- udata/core/dataset/permissions.py +31 -0
- udata/core/dataset/rdf.py +18 -16
- udata/core/dataset/tasks.py +16 -7
- udata/core/discussions/api.py +15 -1
- udata/core/discussions/models.py +6 -0
- udata/core/legal/__init__.py +0 -0
- udata/core/legal/mails.py +128 -0
- udata/core/organization/api.py +16 -5
- udata/core/organization/api_fields.py +3 -3
- udata/core/organization/apiv2.py +3 -4
- udata/core/organization/mails.py +1 -1
- udata/core/organization/models.py +40 -7
- udata/core/organization/notifications.py +84 -0
- udata/core/organization/permissions.py +1 -1
- udata/core/organization/tasks.py +3 -0
- udata/core/pages/models.py +49 -0
- udata/core/pages/tests/test_api.py +165 -1
- udata/core/post/api.py +25 -70
- udata/core/post/constants.py +8 -0
- udata/core/post/models.py +109 -17
- udata/core/post/tests/test_api.py +140 -3
- udata/core/post/tests/test_models.py +24 -0
- udata/core/reports/api.py +18 -0
- udata/core/reports/models.py +42 -2
- udata/core/reuse/api.py +8 -0
- udata/core/reuse/apiv2.py +3 -6
- udata/core/reuse/models.py +1 -1
- udata/core/spatial/forms.py +2 -2
- udata/core/topic/models.py +8 -2
- udata/core/user/api.py +10 -3
- udata/core/user/api_fields.py +3 -3
- udata/core/user/models.py +33 -8
- udata/features/notifications/api.py +7 -18
- udata/features/notifications/models.py +59 -0
- udata/features/notifications/tasks.py +25 -0
- udata/features/transfer/actions.py +2 -0
- udata/features/transfer/models.py +17 -0
- udata/features/transfer/notifications.py +96 -0
- udata/flask_mongoengine/engine.py +0 -4
- udata/flask_mongoengine/pagination.py +1 -1
- udata/frontend/markdown.py +2 -1
- udata/harvest/actions.py +20 -0
- udata/harvest/api.py +24 -7
- udata/harvest/backends/base.py +27 -1
- udata/harvest/backends/ckan/harvesters.py +21 -4
- udata/harvest/backends/dcat.py +4 -1
- udata/harvest/commands.py +33 -0
- udata/harvest/filters.py +17 -6
- udata/harvest/models.py +16 -0
- udata/harvest/permissions.py +27 -0
- udata/harvest/tests/ckan/test_ckan_backend.py +33 -0
- udata/harvest/tests/test_actions.py +46 -2
- udata/harvest/tests/test_api.py +161 -6
- udata/harvest/tests/test_base_backend.py +86 -1
- udata/harvest/tests/test_dcat_backend.py +68 -3
- udata/harvest/tests/test_filters.py +6 -0
- udata/i18n.py +1 -4
- udata/mail.py +14 -0
- udata/migrations/2021-08-17-harvest-integrity.py +23 -16
- udata/migrations/2025-10-31-create-membership-request-notifications.py +55 -0
- udata/migrations/2025-12-04-add-uuid-to-discussion-messages.py +28 -0
- udata/migrations/2025-12-16-create-transfer-request-notifications.py +69 -0
- udata/migrations/2026-01-14-add-default-kind-to-posts.py +17 -0
- udata/mongo/slug_fields.py +1 -1
- udata/rdf.py +65 -11
- udata/routing.py +2 -2
- udata/settings.py +11 -0
- udata/tasks.py +2 -0
- udata/templates/mail/message.html +3 -1
- udata/tests/api/__init__.py +7 -17
- udata/tests/api/test_activities_api.py +36 -0
- udata/tests/api/test_datasets_api.py +69 -0
- udata/tests/api/test_organizations_api.py +0 -3
- udata/tests/api/test_reports_api.py +157 -0
- udata/tests/api/test_user_api.py +1 -1
- udata/tests/apiv2/test_dataservices.py +14 -0
- udata/tests/apiv2/test_organizations.py +9 -0
- udata/tests/apiv2/test_reuses.py +11 -0
- udata/tests/cli/test_cli_base.py +0 -1
- udata/tests/dataservice/test_dataservice_tasks.py +29 -0
- udata/tests/dataset/test_dataset_model.py +13 -1
- udata/tests/dataset/test_dataset_rdf.py +164 -5
- udata/tests/dataset/test_dataset_tasks.py +25 -0
- udata/tests/frontend/test_auth.py +58 -1
- udata/tests/frontend/test_csv.py +0 -3
- udata/tests/helpers.py +31 -27
- udata/tests/organization/test_notifications.py +67 -2
- udata/tests/search/test_search_integration.py +70 -0
- udata/tests/site/test_site_csv_exports.py +22 -10
- udata/tests/test_activity.py +9 -9
- udata/tests/test_api_fields.py +10 -0
- udata/tests/test_discussions.py +5 -5
- udata/tests/test_legal_mails.py +359 -0
- udata/tests/test_notifications.py +15 -57
- udata/tests/test_notifications_task.py +43 -0
- udata/tests/test_owned.py +81 -1
- udata/tests/test_transfer.py +181 -2
- udata/tests/test_uris.py +33 -0
- udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
- udata/translations/ar/LC_MESSAGES/udata.po +309 -158
- udata/translations/de/LC_MESSAGES/udata.mo +0 -0
- udata/translations/de/LC_MESSAGES/udata.po +313 -160
- udata/translations/es/LC_MESSAGES/udata.mo +0 -0
- udata/translations/es/LC_MESSAGES/udata.po +312 -160
- udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/fr/LC_MESSAGES/udata.po +475 -202
- udata/translations/it/LC_MESSAGES/udata.mo +0 -0
- udata/translations/it/LC_MESSAGES/udata.po +317 -162
- udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
- udata/translations/pt/LC_MESSAGES/udata.po +315 -161
- udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/sr/LC_MESSAGES/udata.po +323 -164
- udata/translations/udata.pot +169 -124
- udata/uris.py +0 -2
- udata/utils.py +23 -0
- udata-14.7.3.dev4.dist-info/METADATA +109 -0
- {udata-14.0.3.dev1.dist-info → udata-14.7.3.dev4.dist-info}/RECORD +142 -135
- udata/core/post/forms.py +0 -30
- udata/flask_mongoengine/json.py +0 -38
- udata/templates/mail/base.html +0 -105
- udata/templates/mail/base.txt +0 -6
- udata/templates/mail/button.html +0 -3
- udata/templates/mail/layouts/1-column.html +0 -19
- udata/templates/mail/layouts/2-columns.html +0 -20
- udata/templates/mail/layouts/center-panel.html +0 -16
- udata-14.0.3.dev1.dist-info/METADATA +0 -132
- {udata-14.0.3.dev1.dist-info → udata-14.7.3.dev4.dist-info}/WHEEL +0 -0
- {udata-14.0.3.dev1.dist-info → udata-14.7.3.dev4.dist-info}/entry_points.txt +0 -0
- {udata-14.0.3.dev1.dist-info → udata-14.7.3.dev4.dist-info}/licenses/LICENSE +0 -0
- {udata-14.0.3.dev1.dist-info → udata-14.7.3.dev4.dist-info}/top_level.txt +0 -0
udata/tests/test_transfer.py
CHANGED
|
@@ -10,11 +10,13 @@ from udata.core.user.factories import UserFactory
|
|
|
10
10
|
from udata.core.user.metrics import (
|
|
11
11
|
update_owner_metrics, # noqa needed to register signals
|
|
12
12
|
)
|
|
13
|
-
from udata.features.
|
|
13
|
+
from udata.features.notifications.models import Notification
|
|
14
|
+
from udata.features.transfer.actions import accept_transfer, refuse_transfer, request_transfer
|
|
14
15
|
from udata.features.transfer.factories import TransferFactory
|
|
15
16
|
from udata.features.transfer.notifications import transfer_request_notifications
|
|
16
17
|
from udata.models import Member
|
|
17
|
-
from udata.tests.api import PytestOnlyDBTestCase
|
|
18
|
+
from udata.tests.api import DBTestCase, PytestOnlyDBTestCase
|
|
19
|
+
from udata.tests.helpers import assert_equal_dates
|
|
18
20
|
from udata.utils import faker
|
|
19
21
|
|
|
20
22
|
|
|
@@ -218,3 +220,180 @@ class TransferNotificationsTest(PytestOnlyDBTestCase):
|
|
|
218
220
|
transfer = transfers[details["id"]]
|
|
219
221
|
assert details["subject"]["class"] == "dataset"
|
|
220
222
|
assert details["subject"]["id"] == transfer.subject.id
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class TransferRequestNotificationTest(DBTestCase):
|
|
226
|
+
def test_notification_created_for_user_recipient(self):
|
|
227
|
+
"""Notification is created for user recipient when transfer is requested"""
|
|
228
|
+
owner = UserFactory()
|
|
229
|
+
recipient = UserFactory()
|
|
230
|
+
dataset = DatasetFactory(owner=owner)
|
|
231
|
+
|
|
232
|
+
login_user(owner)
|
|
233
|
+
transfer = request_transfer(dataset, recipient, faker.sentence())
|
|
234
|
+
|
|
235
|
+
notifications = Notification.objects.all()
|
|
236
|
+
assert len(notifications) == 1
|
|
237
|
+
|
|
238
|
+
notification = notifications[0]
|
|
239
|
+
assert notification.user == recipient
|
|
240
|
+
assert notification.details.transfer_owner == owner
|
|
241
|
+
assert notification.details.transfer_recipient == recipient
|
|
242
|
+
assert notification.details.transfer_subject == dataset
|
|
243
|
+
assert_equal_dates(notification.created_at, transfer.created)
|
|
244
|
+
|
|
245
|
+
def test_notification_created_for_org_admins_only(self):
|
|
246
|
+
"""Notifications are created for all admin users of recipient org, not editors"""
|
|
247
|
+
owner = UserFactory()
|
|
248
|
+
admin1 = UserFactory()
|
|
249
|
+
admin2 = UserFactory()
|
|
250
|
+
editor = UserFactory()
|
|
251
|
+
members = [
|
|
252
|
+
Member(user=editor, role="editor"),
|
|
253
|
+
Member(user=admin1, role="admin"),
|
|
254
|
+
Member(user=admin2, role="admin"),
|
|
255
|
+
]
|
|
256
|
+
org = OrganizationFactory(members=members)
|
|
257
|
+
dataset = DatasetFactory(owner=owner)
|
|
258
|
+
|
|
259
|
+
login_user(owner)
|
|
260
|
+
transfer = request_transfer(dataset, org, faker.sentence())
|
|
261
|
+
|
|
262
|
+
notifications = Notification.objects.all()
|
|
263
|
+
assert len(notifications) == 2
|
|
264
|
+
|
|
265
|
+
admin_users = [notif.user for notif in notifications]
|
|
266
|
+
self.assertIn(admin1, admin_users)
|
|
267
|
+
self.assertIn(admin2, admin_users)
|
|
268
|
+
|
|
269
|
+
for notification in notifications:
|
|
270
|
+
assert notification.details.transfer_owner == owner
|
|
271
|
+
assert notification.details.transfer_recipient == org
|
|
272
|
+
assert notification.details.transfer_subject == dataset
|
|
273
|
+
assert_equal_dates(notification.created_at, transfer.created)
|
|
274
|
+
|
|
275
|
+
def test_no_duplicate_notifications(self):
|
|
276
|
+
"""Duplicate notifications are not created for same transfer"""
|
|
277
|
+
owner = UserFactory()
|
|
278
|
+
recipient = UserFactory()
|
|
279
|
+
dataset = DatasetFactory(owner=owner)
|
|
280
|
+
|
|
281
|
+
login_user(owner)
|
|
282
|
+
request_transfer(dataset, recipient, faker.sentence())
|
|
283
|
+
request_transfer(dataset, recipient, faker.sentence())
|
|
284
|
+
|
|
285
|
+
assert Notification.objects.count() == 1
|
|
286
|
+
|
|
287
|
+
def test_multiple_transfers_create_separate_notifications(self):
|
|
288
|
+
"""Multiple transfer requests create separate notifications"""
|
|
289
|
+
owner = UserFactory()
|
|
290
|
+
recipient = UserFactory()
|
|
291
|
+
dataset1 = DatasetFactory(owner=owner)
|
|
292
|
+
dataset2 = DatasetFactory(owner=owner)
|
|
293
|
+
|
|
294
|
+
login_user(owner)
|
|
295
|
+
request_transfer(dataset1, recipient, faker.sentence())
|
|
296
|
+
request_transfer(dataset2, recipient, faker.sentence())
|
|
297
|
+
|
|
298
|
+
notifications = Notification.objects.all()
|
|
299
|
+
assert len(notifications) == 2
|
|
300
|
+
|
|
301
|
+
subjects = [notif.details.transfer_subject for notif in notifications]
|
|
302
|
+
self.assertIn(dataset1, subjects)
|
|
303
|
+
self.assertIn(dataset2, subjects)
|
|
304
|
+
|
|
305
|
+
def test_notification_not_created_if_previous_exists(self):
|
|
306
|
+
"""Notification is created when transferring from org to user"""
|
|
307
|
+
admin = UserFactory()
|
|
308
|
+
org = OrganizationFactory(members=[Member(user=admin, role="admin")])
|
|
309
|
+
dataset = DatasetFactory(organization=org)
|
|
310
|
+
recipient = UserFactory()
|
|
311
|
+
|
|
312
|
+
login_user(admin)
|
|
313
|
+
request_transfer(dataset, recipient, faker.sentence())
|
|
314
|
+
|
|
315
|
+
notifications = Notification.objects.all()
|
|
316
|
+
assert len(notifications) == 1
|
|
317
|
+
|
|
318
|
+
request_transfer(dataset, recipient, faker.sentence())
|
|
319
|
+
|
|
320
|
+
notifications = Notification.objects.all()
|
|
321
|
+
assert len(notifications) == 1
|
|
322
|
+
|
|
323
|
+
def test_notification_created_if_previous_handled(self):
|
|
324
|
+
"""Notification is created when transferring from org to user"""
|
|
325
|
+
admin = UserFactory()
|
|
326
|
+
org = OrganizationFactory(members=[Member(user=admin, role="admin")])
|
|
327
|
+
dataset = DatasetFactory(organization=org)
|
|
328
|
+
recipient = UserFactory()
|
|
329
|
+
|
|
330
|
+
login_user(admin)
|
|
331
|
+
transfer = request_transfer(dataset, recipient, faker.sentence())
|
|
332
|
+
|
|
333
|
+
login_user(recipient)
|
|
334
|
+
refuse_transfer(transfer)
|
|
335
|
+
|
|
336
|
+
notifications = Notification.objects.all()
|
|
337
|
+
assert len(notifications) == 1
|
|
338
|
+
|
|
339
|
+
login_user(admin)
|
|
340
|
+
request_transfer(dataset, recipient, faker.sentence())
|
|
341
|
+
|
|
342
|
+
notifications = Notification.objects.all()
|
|
343
|
+
assert len(notifications) == 2
|
|
344
|
+
|
|
345
|
+
def test_notification_created_for_org_to_user_transfer(self):
|
|
346
|
+
"""Notification is created when transferring from org to user"""
|
|
347
|
+
admin = UserFactory()
|
|
348
|
+
org = OrganizationFactory(members=[Member(user=admin, role="admin")])
|
|
349
|
+
dataset = DatasetFactory(organization=org)
|
|
350
|
+
recipient = UserFactory()
|
|
351
|
+
|
|
352
|
+
login_user(admin)
|
|
353
|
+
transfer = request_transfer(dataset, recipient, faker.sentence())
|
|
354
|
+
|
|
355
|
+
notifications = Notification.objects.all()
|
|
356
|
+
assert len(notifications) == 1
|
|
357
|
+
|
|
358
|
+
notification = notifications[0]
|
|
359
|
+
assert notification.user == recipient
|
|
360
|
+
assert notification.details.transfer_owner == org
|
|
361
|
+
assert notification.details.transfer_recipient == recipient
|
|
362
|
+
assert notification.details.transfer_subject == dataset
|
|
363
|
+
assert_equal_dates(notification.created_at, transfer.created)
|
|
364
|
+
|
|
365
|
+
def test_notification_handled_when_transfer_accepted(self):
|
|
366
|
+
"""Notification's handled_at is updated when transfer is accepted"""
|
|
367
|
+
owner = UserFactory()
|
|
368
|
+
recipient = UserFactory()
|
|
369
|
+
dataset = DatasetFactory(owner=owner)
|
|
370
|
+
|
|
371
|
+
login_user(owner)
|
|
372
|
+
# First create the notification by requesting the transfer
|
|
373
|
+
transfer = request_transfer(dataset, recipient, faker.sentence())
|
|
374
|
+
# Then accept the transfer
|
|
375
|
+
login_user(recipient)
|
|
376
|
+
accept_transfer(transfer)
|
|
377
|
+
|
|
378
|
+
# Check that the notification has been handled
|
|
379
|
+
notifications = Notification.objects.all()
|
|
380
|
+
assert len(notifications) == 1
|
|
381
|
+
assert notifications[0].handled_at is not None
|
|
382
|
+
|
|
383
|
+
def test_notification_handled_when_transfer_refused(self):
|
|
384
|
+
"""Notification's handled_at is updated when transfer is refused"""
|
|
385
|
+
owner = UserFactory()
|
|
386
|
+
recipient = UserFactory()
|
|
387
|
+
dataset = DatasetFactory(owner=owner)
|
|
388
|
+
|
|
389
|
+
login_user(owner)
|
|
390
|
+
# First create the notification by requesting the transfer
|
|
391
|
+
transfer = request_transfer(dataset, recipient, faker.sentence())
|
|
392
|
+
# Then refuse the transfer
|
|
393
|
+
login_user(recipient)
|
|
394
|
+
refuse_transfer(transfer)
|
|
395
|
+
|
|
396
|
+
# Check that the notification has been handled
|
|
397
|
+
notifications = Notification.objects.all()
|
|
398
|
+
assert len(notifications) == 1
|
|
399
|
+
assert notifications[0].handled_at is not None
|
udata/tests/test_uris.py
CHANGED
|
@@ -2,6 +2,7 @@ import pytest
|
|
|
2
2
|
|
|
3
3
|
from udata import uris
|
|
4
4
|
from udata.settings import Defaults
|
|
5
|
+
from udata.tests import PytestOnlyTestCase
|
|
5
6
|
|
|
6
7
|
PUBLIC_HOSTS = [
|
|
7
8
|
"http://foo.com/blah_blah",
|
|
@@ -289,3 +290,35 @@ def test_with_credentials(url):
|
|
|
289
290
|
def test_with_credentials_disabled(url):
|
|
290
291
|
with pytest.raises(uris.ValidationError, match="Credentials in URL are not allowed"):
|
|
291
292
|
uris.validate(url, credentials=False)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@pytest.mark.options(CDATA_BASE_URL="http://localhost:3000/")
|
|
296
|
+
class CdataUrlTest(PytestOnlyTestCase):
|
|
297
|
+
def test_cdata_url_without_base_url(self, app):
|
|
298
|
+
app.config["CDATA_BASE_URL"] = None
|
|
299
|
+
assert uris.cdata_url("test") is None
|
|
300
|
+
|
|
301
|
+
def test_cdata_url_with_simple_uri(self):
|
|
302
|
+
assert uris.cdata_url("test") == "http://localhost:3000/test"
|
|
303
|
+
|
|
304
|
+
@pytest.mark.options(MAIL_CAMPAIGN="mail")
|
|
305
|
+
def test_cdata_url_with_mail_campaign(self):
|
|
306
|
+
assert (
|
|
307
|
+
uris.cdata_url("test", _mailCampaign=True)
|
|
308
|
+
== "http://localhost:3000/test?mtm_campaign=mail"
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
def test_cdata_url_with_trailing_slash(self):
|
|
312
|
+
assert uris.cdata_url("test/") == "http://localhost:3000/test"
|
|
313
|
+
|
|
314
|
+
def test_cdata_url_with_append(self):
|
|
315
|
+
assert (
|
|
316
|
+
uris.cdata_url("test/", append="/discussions")
|
|
317
|
+
== "http://localhost:3000/test/discussions"
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
def test_cdata_url_with_append_and_kwargs(self):
|
|
321
|
+
assert (
|
|
322
|
+
uris.cdata_url("test/", append="/discussions", discussion_id="disc_id")
|
|
323
|
+
== "http://localhost:3000/test/discussions?discussion_id=disc_id"
|
|
324
|
+
)
|
|
Binary file
|