udata 10.4.1.dev35201__py2.py3-none-any.whl → 10.4.2__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- udata/__init__.py +1 -1
- udata/core/activity/__init__.py +2 -0
- udata/core/activity/api.py +10 -2
- udata/core/activity/models.py +28 -1
- udata/core/activity/tasks.py +19 -4
- udata/core/dataservices/activities.py +53 -0
- udata/core/dataservices/api.py +43 -0
- udata/core/dataservices/models.py +16 -20
- udata/core/dataset/activities.py +52 -5
- udata/core/dataset/api.py +44 -0
- udata/core/dataset/csv.py +0 -1
- udata/core/dataset/models.py +49 -47
- udata/core/dataset/rdf.py +1 -1
- udata/core/metrics/commands.py +1 -0
- udata/core/metrics/helpers.py +102 -0
- udata/core/metrics/models.py +1 -0
- udata/core/metrics/tasks.py +1 -0
- udata/core/organization/activities.py +3 -2
- udata/core/organization/api.py +11 -0
- udata/core/organization/api_fields.py +6 -5
- udata/core/organization/models.py +31 -31
- udata/core/owned.py +1 -1
- udata/core/post/api.py +34 -0
- udata/core/reuse/activities.py +6 -5
- udata/core/reuse/api.py +42 -1
- udata/core/reuse/models.py +8 -16
- udata/core/site/models.py +33 -0
- udata/core/topic/activities.py +36 -0
- udata/core/topic/models.py +23 -15
- udata/core/user/activities.py +17 -6
- udata/core/user/api.py +1 -0
- udata/core/user/api_fields.py +6 -1
- udata/core/user/models.py +39 -32
- udata/migrations/2025-05-22-purge-duplicate-activities.py +101 -0
- udata/mongo/datetime_fields.py +1 -0
- udata/settings.py +4 -0
- udata/tests/api/test_activities_api.py +29 -1
- udata/tests/api/test_dataservices_api.py +53 -0
- udata/tests/api/test_datasets_api.py +61 -0
- udata/tests/api/test_organizations_api.py +27 -2
- udata/tests/api/test_reuses_api.py +54 -0
- udata/tests/dataset/test_csv_adapter.py +6 -3
- udata/tests/dataset/test_dataset_model.py +49 -0
- udata/tests/test_topics.py +19 -0
- {udata-10.4.1.dev35201.dist-info → udata-10.4.2.dist-info}/METADATA +17 -2
- {udata-10.4.1.dev35201.dist-info → udata-10.4.2.dist-info}/RECORD +50 -46
- {udata-10.4.1.dev35201.dist-info → udata-10.4.2.dist-info}/LICENSE +0 -0
- {udata-10.4.1.dev35201.dist-info → udata-10.4.2.dist-info}/WHEEL +0 -0
- {udata-10.4.1.dev35201.dist-info → udata-10.4.2.dist-info}/entry_points.txt +0 -0
- {udata-10.4.1.dev35201.dist-info → udata-10.4.2.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from xml.etree.ElementTree import XML
|
|
3
3
|
|
|
4
|
+
import feedparser
|
|
4
5
|
import pytest
|
|
5
6
|
from flask import url_for
|
|
6
7
|
from werkzeug.test import TestResponse
|
|
@@ -627,3 +628,55 @@ class DataserviceRdfViewsTest:
|
|
|
627
628
|
response = client.get(url, headers={"Accept": mime})
|
|
628
629
|
assert200(response)
|
|
629
630
|
assert response.content_type == mime
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
class DataservicesFeedAPItest(APITestCase):
|
|
634
|
+
def test_recent_feed(self):
|
|
635
|
+
dataservices = [DataserviceFactory() for i in range(3)]
|
|
636
|
+
|
|
637
|
+
response = self.get(url_for("api.recent_dataservices_atom_feed"))
|
|
638
|
+
|
|
639
|
+
self.assert200(response)
|
|
640
|
+
|
|
641
|
+
feed = feedparser.parse(response.data)
|
|
642
|
+
|
|
643
|
+
self.assertEqual(len(feed.entries), len(dataservices))
|
|
644
|
+
for i in range(1, len(feed.entries)):
|
|
645
|
+
published_date = feed.entries[i].published_parsed
|
|
646
|
+
prev_published_date = feed.entries[i - 1].published_parsed
|
|
647
|
+
self.assertGreaterEqual(prev_published_date, published_date)
|
|
648
|
+
|
|
649
|
+
def test_recent_feed_owner(self):
|
|
650
|
+
owner = UserFactory()
|
|
651
|
+
DataserviceFactory(owner=owner)
|
|
652
|
+
|
|
653
|
+
response = self.get(url_for("api.recent_dataservices_atom_feed"))
|
|
654
|
+
|
|
655
|
+
self.assert200(response)
|
|
656
|
+
|
|
657
|
+
feed = feedparser.parse(response.data)
|
|
658
|
+
|
|
659
|
+
self.assertEqual(len(feed.entries), 1)
|
|
660
|
+
entry = feed.entries[0]
|
|
661
|
+
self.assertEqual(len(entry.authors), 1)
|
|
662
|
+
author = entry.authors[0]
|
|
663
|
+
self.assertEqual(author.name, owner.fullname)
|
|
664
|
+
self.assertEqual(author.href, owner.external_url)
|
|
665
|
+
|
|
666
|
+
def test_recent_feed_org(self):
|
|
667
|
+
owner = UserFactory()
|
|
668
|
+
org = OrganizationFactory()
|
|
669
|
+
DataserviceFactory(owner=owner, organization=org)
|
|
670
|
+
|
|
671
|
+
response = self.get(url_for("api.recent_dataservices_atom_feed"))
|
|
672
|
+
|
|
673
|
+
self.assert200(response)
|
|
674
|
+
|
|
675
|
+
feed = feedparser.parse(response.data)
|
|
676
|
+
|
|
677
|
+
self.assertEqual(len(feed.entries), 1)
|
|
678
|
+
entry = feed.entries[0]
|
|
679
|
+
self.assertEqual(len(entry.authors), 1)
|
|
680
|
+
author = entry.authors[0]
|
|
681
|
+
self.assertEqual(author.name, org.name)
|
|
682
|
+
self.assertEqual(author.href, org.external_url)
|
|
@@ -3,6 +3,7 @@ from datetime import datetime
|
|
|
3
3
|
from io import BytesIO
|
|
4
4
|
from uuid import uuid4
|
|
5
5
|
|
|
6
|
+
import feedparser
|
|
6
7
|
import pytest
|
|
7
8
|
import pytz
|
|
8
9
|
import requests_mock
|
|
@@ -977,6 +978,14 @@ class DatasetAPITest(APITestCase):
|
|
|
977
978
|
dataset = Dataset.objects.first()
|
|
978
979
|
self.assertEqual(dataset.contact_points[0].name, contact_point_data["name"])
|
|
979
980
|
|
|
981
|
+
data["contact_points"] = []
|
|
982
|
+
response = self.put(url_for("api.dataset", dataset=dataset), data)
|
|
983
|
+
self.assert200(response)
|
|
984
|
+
|
|
985
|
+
dataset = Dataset.objects.first()
|
|
986
|
+
# This is weird, we should have no contact point if sending an empty array… :RemoveAllContactPoints (in cdata)
|
|
987
|
+
self.assertEqual(dataset.contact_points[0].name, contact_point_data["name"])
|
|
988
|
+
|
|
980
989
|
data["contact_points"] = None
|
|
981
990
|
response = self.put(url_for("api.dataset", dataset=dataset), data)
|
|
982
991
|
self.assert200(response)
|
|
@@ -1242,6 +1251,58 @@ class DatasetAPITest(APITestCase):
|
|
|
1242
1251
|
}
|
|
1243
1252
|
|
|
1244
1253
|
|
|
1254
|
+
class DatasetsFeedAPItest(APITestCase):
|
|
1255
|
+
def test_recent_feed(self):
|
|
1256
|
+
datasets = [DatasetFactory(resources=[ResourceFactory()]) for i in range(3)]
|
|
1257
|
+
|
|
1258
|
+
response = self.get(url_for("api.recent_datasets_atom_feed"))
|
|
1259
|
+
|
|
1260
|
+
self.assert200(response)
|
|
1261
|
+
|
|
1262
|
+
feed = feedparser.parse(response.data)
|
|
1263
|
+
|
|
1264
|
+
self.assertEqual(len(feed.entries), len(datasets))
|
|
1265
|
+
for i in range(1, len(feed.entries)):
|
|
1266
|
+
published_date = feed.entries[i].published_parsed
|
|
1267
|
+
prev_published_date = feed.entries[i - 1].published_parsed
|
|
1268
|
+
self.assertGreaterEqual(prev_published_date, published_date)
|
|
1269
|
+
|
|
1270
|
+
def test_recent_feed_owner(self):
|
|
1271
|
+
owner = UserFactory()
|
|
1272
|
+
DatasetFactory(owner=owner, resources=[ResourceFactory()])
|
|
1273
|
+
|
|
1274
|
+
response = self.get(url_for("api.recent_datasets_atom_feed"))
|
|
1275
|
+
|
|
1276
|
+
self.assert200(response)
|
|
1277
|
+
|
|
1278
|
+
feed = feedparser.parse(response.data)
|
|
1279
|
+
|
|
1280
|
+
self.assertEqual(len(feed.entries), 1)
|
|
1281
|
+
entry = feed.entries[0]
|
|
1282
|
+
self.assertEqual(len(entry.authors), 1)
|
|
1283
|
+
author = entry.authors[0]
|
|
1284
|
+
self.assertEqual(author.name, owner.fullname)
|
|
1285
|
+
self.assertEqual(author.href, owner.external_url)
|
|
1286
|
+
|
|
1287
|
+
def test_recent_feed_org(self):
|
|
1288
|
+
owner = UserFactory()
|
|
1289
|
+
org = OrganizationFactory()
|
|
1290
|
+
DatasetFactory(owner=owner, organization=org, resources=[ResourceFactory()])
|
|
1291
|
+
|
|
1292
|
+
response = self.get(url_for("api.recent_datasets_atom_feed"))
|
|
1293
|
+
|
|
1294
|
+
self.assert200(response)
|
|
1295
|
+
|
|
1296
|
+
feed = feedparser.parse(response.data)
|
|
1297
|
+
|
|
1298
|
+
self.assertEqual(len(feed.entries), 1)
|
|
1299
|
+
entry = feed.entries[0]
|
|
1300
|
+
self.assertEqual(len(entry.authors), 1)
|
|
1301
|
+
author = entry.authors[0]
|
|
1302
|
+
self.assertEqual(author.name, org.name)
|
|
1303
|
+
self.assertEqual(author.href, org.external_url)
|
|
1304
|
+
|
|
1305
|
+
|
|
1245
1306
|
class DatasetBadgeAPITest(APITestCase):
|
|
1246
1307
|
@classmethod
|
|
1247
1308
|
def setUpClass(cls):
|
|
@@ -8,6 +8,7 @@ import udata.core.organization.constants as org_constants
|
|
|
8
8
|
from udata.core import csv
|
|
9
9
|
from udata.core.badges.factories import badge_factory
|
|
10
10
|
from udata.core.badges.signals import on_badge_added, on_badge_removed
|
|
11
|
+
from udata.core.dataservices.factories import DataserviceFactory
|
|
11
12
|
from udata.core.dataset.factories import DatasetFactory, ResourceFactory
|
|
12
13
|
from udata.core.discussions.factories import DiscussionFactory
|
|
13
14
|
from udata.core.organization.factories import OrganizationFactory
|
|
@@ -362,10 +363,10 @@ class MembershipAPITest:
|
|
|
362
363
|
assert len(members) == 2
|
|
363
364
|
assert members[0]["role"] == "admin"
|
|
364
365
|
assert members[0]["since"] == "2024-04-14T00:00:00+00:00"
|
|
365
|
-
assert members[0]["user"]["email"]
|
|
366
|
+
assert members[0]["user"]["email"] == "***@example.org"
|
|
366
367
|
|
|
367
368
|
assert members[1]["role"] == "editor"
|
|
368
|
-
assert members[1]["user"]["email"]
|
|
369
|
+
assert members[1]["user"]["email"] == "***@example.org"
|
|
369
370
|
|
|
370
371
|
# Super admin of udata can see emails
|
|
371
372
|
api.login(AdminFactory())
|
|
@@ -1042,6 +1043,30 @@ class OrganizationCsvExportsTest:
|
|
|
1042
1043
|
assert str(hidden_dataset.id) not in dataset_ids
|
|
1043
1044
|
assert str(not_org_dataset.id) not in dataset_ids
|
|
1044
1045
|
|
|
1046
|
+
def test_dataservices_csv(self, api):
|
|
1047
|
+
org = OrganizationFactory()
|
|
1048
|
+
[DataserviceFactory(organization=org) for _ in range(3)]
|
|
1049
|
+
|
|
1050
|
+
response = api.get(url_for("api.organization_dataservices_csv", org=org))
|
|
1051
|
+
|
|
1052
|
+
assert200(response)
|
|
1053
|
+
assert response.mimetype == "text/csv"
|
|
1054
|
+
assert response.charset == "utf-8"
|
|
1055
|
+
|
|
1056
|
+
csvfile = StringIO(response.data.decode("utf-8"))
|
|
1057
|
+
reader = csv.get_reader(csvfile)
|
|
1058
|
+
header = next(reader)
|
|
1059
|
+
|
|
1060
|
+
assert header[0] == "id"
|
|
1061
|
+
assert "title" in header
|
|
1062
|
+
assert "url" in header
|
|
1063
|
+
assert "description" in header
|
|
1064
|
+
assert "created_at" in header
|
|
1065
|
+
assert "metadata_modified_at" in header
|
|
1066
|
+
assert "tags" in header
|
|
1067
|
+
assert "metric.views" in header
|
|
1068
|
+
assert "datasets" in header
|
|
1069
|
+
|
|
1045
1070
|
def test_discussions_csv_content_empty(self, api):
|
|
1046
1071
|
organization = OrganizationFactory()
|
|
1047
1072
|
response = api.get(url_for("api.organization_discussions_csv", org=organization))
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
|
+
import feedparser
|
|
3
4
|
import pytest
|
|
4
5
|
from flask import url_for
|
|
5
6
|
from werkzeug.test import TestResponse
|
|
@@ -12,6 +13,7 @@ from udata.core.reuse.constants import REUSE_TOPICS, REUSE_TYPES
|
|
|
12
13
|
from udata.core.reuse.factories import ReuseFactory
|
|
13
14
|
from udata.core.user.factories import AdminFactory, UserFactory
|
|
14
15
|
from udata.models import Follow, Member, Reuse
|
|
16
|
+
from udata.tests.api import APITestCase
|
|
15
17
|
from udata.tests.helpers import (
|
|
16
18
|
assert200,
|
|
17
19
|
assert201,
|
|
@@ -569,6 +571,58 @@ class ReuseAPITest:
|
|
|
569
571
|
assert len(response.json) == 0
|
|
570
572
|
|
|
571
573
|
|
|
574
|
+
class ReusesFeedAPItest(APITestCase):
|
|
575
|
+
def test_recent_feed(self):
|
|
576
|
+
datasets = [ReuseFactory(datasets=[DatasetFactory()]) for i in range(3)]
|
|
577
|
+
|
|
578
|
+
response = self.get(url_for("api.recent_reuses_atom_feed"))
|
|
579
|
+
|
|
580
|
+
self.assert200(response)
|
|
581
|
+
|
|
582
|
+
feed = feedparser.parse(response.data)
|
|
583
|
+
|
|
584
|
+
self.assertEqual(len(feed.entries), len(datasets))
|
|
585
|
+
for i in range(1, len(feed.entries)):
|
|
586
|
+
published_date = feed.entries[i].published_parsed
|
|
587
|
+
prev_published_date = feed.entries[i - 1].published_parsed
|
|
588
|
+
self.assertGreaterEqual(prev_published_date, published_date)
|
|
589
|
+
|
|
590
|
+
def test_recent_feed_owner(self):
|
|
591
|
+
owner = UserFactory()
|
|
592
|
+
ReuseFactory(owner=owner, datasets=[DatasetFactory()])
|
|
593
|
+
|
|
594
|
+
response = self.get(url_for("api.recent_reuses_atom_feed"))
|
|
595
|
+
|
|
596
|
+
self.assert200(response)
|
|
597
|
+
|
|
598
|
+
feed = feedparser.parse(response.data)
|
|
599
|
+
|
|
600
|
+
self.assertEqual(len(feed.entries), 1)
|
|
601
|
+
entry = feed.entries[0]
|
|
602
|
+
self.assertEqual(len(entry.authors), 1)
|
|
603
|
+
author = entry.authors[0]
|
|
604
|
+
self.assertEqual(author.name, owner.fullname)
|
|
605
|
+
self.assertEqual(author.href, owner.external_url)
|
|
606
|
+
|
|
607
|
+
def test_recent_feed_org(self):
|
|
608
|
+
owner = UserFactory()
|
|
609
|
+
org = OrganizationFactory()
|
|
610
|
+
ReuseFactory(owner=owner, organization=org, datasets=[DatasetFactory()])
|
|
611
|
+
|
|
612
|
+
response = self.get(url_for("api.recent_reuses_atom_feed"))
|
|
613
|
+
|
|
614
|
+
self.assert200(response)
|
|
615
|
+
|
|
616
|
+
feed = feedparser.parse(response.data)
|
|
617
|
+
|
|
618
|
+
self.assertEqual(len(feed.entries), 1)
|
|
619
|
+
entry = feed.entries[0]
|
|
620
|
+
self.assertEqual(len(entry.authors), 1)
|
|
621
|
+
author = entry.authors[0]
|
|
622
|
+
self.assertEqual(author.name, org.name)
|
|
623
|
+
self.assertEqual(author.href, org.external_url)
|
|
624
|
+
|
|
625
|
+
|
|
572
626
|
class ReuseBadgeAPITest:
|
|
573
627
|
modules = []
|
|
574
628
|
|
|
@@ -21,7 +21,10 @@ class DatasetCSVAdapterTest:
|
|
|
21
21
|
"created_at": date_created,
|
|
22
22
|
"modified_at": date_modified,
|
|
23
23
|
"uri": "http://domain.gouv.fr/dataset/uri",
|
|
24
|
-
}
|
|
24
|
+
},
|
|
25
|
+
metrics={
|
|
26
|
+
"views": 42,
|
|
27
|
+
},
|
|
25
28
|
)
|
|
26
29
|
],
|
|
27
30
|
harvest={
|
|
@@ -41,6 +44,8 @@ class DatasetCSVAdapterTest:
|
|
|
41
44
|
assert date_modified.isoformat() in d_row
|
|
42
45
|
# dataset harvest dates should not be here
|
|
43
46
|
assert another_date.isoformat() not in d_row
|
|
47
|
+
# assert resource metrics downloads
|
|
48
|
+
assert 42 in d_row
|
|
44
49
|
|
|
45
50
|
def test_datasets_csv_adapter(self):
|
|
46
51
|
date_created = datetime(2022, 12, 31)
|
|
@@ -87,10 +92,8 @@ class DatasetCSVAdapterTest:
|
|
|
87
92
|
assert harvest_dataset_values["harvest.domain"] == "example.com"
|
|
88
93
|
assert harvest_dataset_values["harvest.remote_url"] == "https://www.example.com/"
|
|
89
94
|
assert harvest_dataset_values["resources_count"] == 0
|
|
90
|
-
assert harvest_dataset_values["downloads"] == 0
|
|
91
95
|
|
|
92
96
|
resources_dataset_values = csv[str(resources_dataset.id)]
|
|
93
97
|
assert resources_dataset_values["resources_count"] == 3
|
|
94
98
|
assert resources_dataset_values["main_resources_count"] == 1
|
|
95
99
|
assert set(resources_dataset_values["resources_formats"].split(",")) == set(["csv", "json"])
|
|
96
|
-
assert resources_dataset_values["downloads"] == 1337 + 42
|
|
@@ -9,6 +9,14 @@ from mongoengine import ValidationError as MongoEngineValidationError
|
|
|
9
9
|
from mongoengine import post_save
|
|
10
10
|
|
|
11
11
|
from udata.app import cache
|
|
12
|
+
from udata.core.dataset.activities import (
|
|
13
|
+
UserAddedResourceToDataset,
|
|
14
|
+
UserCreatedDataset,
|
|
15
|
+
UserDeletedDataset,
|
|
16
|
+
UserRemovedResourceFromDataset,
|
|
17
|
+
UserUpdatedDataset,
|
|
18
|
+
UserUpdatedResource,
|
|
19
|
+
)
|
|
12
20
|
from udata.core.dataset.constants import LEGACY_FREQUENCIES, UPDATE_FREQUENCIES
|
|
13
21
|
from udata.core.dataset.exceptions import (
|
|
14
22
|
SchemasCacheUnavailableException,
|
|
@@ -350,6 +358,47 @@ class DatasetModelTest:
|
|
|
350
358
|
|
|
351
359
|
assert dataset_without_resources.resources_len == 0
|
|
352
360
|
|
|
361
|
+
def test_dataset_activities(self, api, mocker):
|
|
362
|
+
# A user must be authenticated for activities to be emitted
|
|
363
|
+
user = api.login()
|
|
364
|
+
|
|
365
|
+
mock_created = mocker.patch.object(UserCreatedDataset, "emit")
|
|
366
|
+
mock_updated = mocker.patch.object(UserUpdatedDataset, "emit")
|
|
367
|
+
mock_deleted = mocker.patch.object(UserDeletedDataset, "emit")
|
|
368
|
+
mock_resource_added = mocker.patch.object(UserAddedResourceToDataset, "emit")
|
|
369
|
+
mock_resouce_updated = mocker.patch.object(UserUpdatedResource, "emit")
|
|
370
|
+
mock_resouce_removed = mocker.patch.object(UserRemovedResourceFromDataset, "emit")
|
|
371
|
+
|
|
372
|
+
with assert_emit(Dataset.on_create):
|
|
373
|
+
dataset = DatasetFactory(owner=user)
|
|
374
|
+
mock_created.assert_called()
|
|
375
|
+
|
|
376
|
+
with assert_emit(Dataset.on_update):
|
|
377
|
+
dataset.title = "new title"
|
|
378
|
+
dataset.save()
|
|
379
|
+
mock_updated.assert_called()
|
|
380
|
+
|
|
381
|
+
with assert_emit(Dataset.on_resource_added):
|
|
382
|
+
dataset.add_resource(ResourceFactory())
|
|
383
|
+
mock_resource_added.assert_called()
|
|
384
|
+
|
|
385
|
+
dataset.reload()
|
|
386
|
+
|
|
387
|
+
with assert_emit(Dataset.on_resource_updated):
|
|
388
|
+
resource = dataset.resources[0]
|
|
389
|
+
resource.description = "New description"
|
|
390
|
+
dataset.update_resource(resource)
|
|
391
|
+
mock_resouce_updated.assert_called()
|
|
392
|
+
|
|
393
|
+
with assert_emit(Dataset.on_resource_removed):
|
|
394
|
+
dataset.remove_resource(dataset.resources[-1])
|
|
395
|
+
mock_resouce_removed.assert_called()
|
|
396
|
+
|
|
397
|
+
with assert_emit(Dataset.on_delete):
|
|
398
|
+
dataset.deleted = datetime.utcnow()
|
|
399
|
+
dataset.save()
|
|
400
|
+
mock_deleted.assert_called()
|
|
401
|
+
|
|
353
402
|
|
|
354
403
|
class ResourceModelTest:
|
|
355
404
|
def test_url_is_required(self):
|
udata/tests/test_topics.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
3
|
from udata.core.dataset.factories import DatasetFactory
|
|
4
|
+
from udata.core.topic.activities import UserCreatedTopic, UserUpdatedTopic
|
|
4
5
|
from udata.core.topic.factories import TopicFactory
|
|
6
|
+
from udata.core.topic.models import Topic
|
|
5
7
|
from udata.search import reindex
|
|
8
|
+
from udata.tests.helpers import assert_emit
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
@pytest.fixture
|
|
@@ -45,3 +48,19 @@ class TopicModelTest:
|
|
|
45
48
|
# creates a topic with datasets, thus calls reindex
|
|
46
49
|
TopicFactory()
|
|
47
50
|
job_reindex_undelayed.assert_called()
|
|
51
|
+
|
|
52
|
+
def test_topic_activities(self, api, mocker):
|
|
53
|
+
# A user must be authenticated for activities to be emitted
|
|
54
|
+
user = api.login()
|
|
55
|
+
|
|
56
|
+
mock_created = mocker.patch.object(UserCreatedTopic, "emit")
|
|
57
|
+
mock_updated = mocker.patch.object(UserUpdatedTopic, "emit")
|
|
58
|
+
|
|
59
|
+
with assert_emit(Topic.on_create):
|
|
60
|
+
topic = TopicFactory(owner=user)
|
|
61
|
+
mock_created.assert_called()
|
|
62
|
+
|
|
63
|
+
with assert_emit(Topic.on_update):
|
|
64
|
+
topic.name = "new name"
|
|
65
|
+
topic.save()
|
|
66
|
+
mock_updated.assert_called()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: udata
|
|
3
|
-
Version: 10.4.
|
|
3
|
+
Version: 10.4.2
|
|
4
4
|
Summary: Open data portal
|
|
5
5
|
Home-page: https://github.com/opendatateam/udata
|
|
6
6
|
Author: Opendata Team
|
|
@@ -48,6 +48,7 @@ Requires-Dist: dnspython==2.7.0
|
|
|
48
48
|
Requires-Dist: email-validator==2.2.0
|
|
49
49
|
Requires-Dist: factory-boy==3.3.3
|
|
50
50
|
Requires-Dist: faker==37.0.2
|
|
51
|
+
Requires-Dist: feedgenerator==2.1.0
|
|
51
52
|
Requires-Dist: filelock==3.18.0
|
|
52
53
|
Requires-Dist: flask==2.1.2
|
|
53
54
|
Requires-Dist: flask-babel==4.0.0
|
|
@@ -139,8 +140,22 @@ It is collectively taken care of by members of the
|
|
|
139
140
|
|
|
140
141
|
# Changelog
|
|
141
142
|
|
|
142
|
-
##
|
|
143
|
+
## 10.4.2 (2025-06-05)
|
|
143
144
|
|
|
145
|
+
- :warning: Add migration to clean duplicate activities [#3327](https://github.com/opendatateam/udata/pull/3327)
|
|
146
|
+
- Add test for removing last contact point [#3322](https://github.com/opendatateam/udata/pull/3322)
|
|
147
|
+
- Add activities to dataservices, topics and resources, add Auditable class to refactor improve code [#3308](https://github.com/opendatateam/udata/pull/3308) [#3333](https://github.com/opendatateam/udata/pull/3333)
|
|
148
|
+
- Store activities for private objects [#3328](https://github.com/opendatateam/udata/pull/3328)
|
|
149
|
+
- Do not crash if file doesn't exists during resource deletion [#3323](https://github.com/opendatateam/udata/pull/3323)
|
|
150
|
+
- Show user domain in suggest [#3324](https://github.com/opendatateam/udata/pull/3324)
|
|
151
|
+
- Add new global site metrics [#3325](https://github.com/opendatateam/udata/pull/3325)
|
|
152
|
+
- Keep the existing frequency if not found during harvesting [#3330](https://github.com/opendatateam/udata/pull/3330)
|
|
153
|
+
- Migrate atom feeds from udata-front to udata [#3326](https://github.com/opendatateam/udata/pull/3326)
|
|
154
|
+
- Add organization dataservices catalog route [#3332](https://github.com/opendatateam/udata/pull/3332)
|
|
155
|
+
|
|
156
|
+
## 10.4.1 (2025-05-20)
|
|
157
|
+
|
|
158
|
+
- Remove duplicate `downloads` in dataset csv adapter [#3319](https://github.com/opendatateam/udata/pull/3319)
|
|
144
159
|
- Add missing default for license full object [#3317](https://github.com/opendatateam/udata/pull/3317/)
|
|
145
160
|
- Check for slugs in followers API [#3320](https://github.com/opendatateam/udata/pull/3320)
|
|
146
161
|
- Fix missing ID in dataset reuses mask [#3321](https://github.com/opendatateam/udata/pull/3321)
|