udata 10.9.1.dev37462__py2.py3-none-any.whl → 10.9.1.dev37604__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.

Files changed (45) hide show
  1. udata/api/__init__.py +0 -1
  2. udata/core/dataset/api.py +1 -1
  3. udata/core/dataset/search.py +5 -2
  4. udata/core/dataset/tasks.py +2 -5
  5. udata/core/reuse/tasks.py +3 -0
  6. udata/core/topic/__init__.py +1 -0
  7. udata/core/topic/api_fields.py +87 -0
  8. udata/core/topic/apiv2.py +116 -194
  9. udata/core/topic/factories.py +69 -8
  10. udata/core/topic/forms.py +58 -4
  11. udata/core/topic/models.py +65 -20
  12. udata/core/topic/parsers.py +40 -0
  13. udata/core/topic/tasks.py +11 -0
  14. udata/forms/fields.py +8 -1
  15. udata/harvest/backends/dcat.py +41 -20
  16. udata/harvest/tests/test_dcat_backend.py +89 -0
  17. udata/migrations/2025-05-26-migrate-topics-to-elements.py +59 -0
  18. udata/migrations/2025-06-02-delete-topic-name-index.py +19 -0
  19. udata/static/chunks/{11.51d706fb9521c16976bc.js → 11.822f6ccb39c92c796d13.js} +3 -3
  20. udata/static/chunks/{11.51d706fb9521c16976bc.js.map → 11.822f6ccb39c92c796d13.js.map} +1 -1
  21. udata/static/chunks/{13.f29411b06be1883356a3.js → 13.d9c1735d14038b94c17e.js} +2 -2
  22. udata/static/chunks/{13.f29411b06be1883356a3.js.map → 13.d9c1735d14038b94c17e.js.map} +1 -1
  23. udata/static/chunks/{17.3bd0340930d4a314ce9c.js → 17.81c57c0dedf812e43013.js} +2 -2
  24. udata/static/chunks/{17.3bd0340930d4a314ce9c.js.map → 17.81c57c0dedf812e43013.js.map} +1 -1
  25. udata/static/chunks/{8.b966402f5d680d4bdf4a.js → 8.0f42630e6d8ff782928e.js} +2 -2
  26. udata/static/chunks/{8.b966402f5d680d4bdf4a.js.map → 8.0f42630e6d8ff782928e.js.map} +1 -1
  27. udata/static/common.js +1 -1
  28. udata/static/common.js.map +1 -1
  29. udata/tasks.py +1 -0
  30. udata/tests/api/test_datasets_api.py +3 -2
  31. udata/tests/apiv2/test_me_api.py +2 -2
  32. udata/tests/apiv2/test_topics.py +457 -127
  33. udata/tests/dataset/test_dataset_tasks.py +7 -2
  34. udata/tests/reuse/test_reuse_task.py +9 -0
  35. udata/tests/search/test_adapter.py +43 -0
  36. udata/tests/test_topics.py +19 -8
  37. udata/tests/topic/test_topic_tasks.py +27 -0
  38. {udata-10.9.1.dev37462.dist-info → udata-10.9.1.dev37604.dist-info}/METADATA +4 -2
  39. {udata-10.9.1.dev37462.dist-info → udata-10.9.1.dev37604.dist-info}/RECORD +43 -40
  40. udata/core/topic/api.py +0 -145
  41. udata/tests/api/test_topics_api.py +0 -284
  42. {udata-10.9.1.dev37462.dist-info → udata-10.9.1.dev37604.dist-info}/LICENSE +0 -0
  43. {udata-10.9.1.dev37462.dist-info → udata-10.9.1.dev37604.dist-info}/WHEEL +0 -0
  44. {udata-10.9.1.dev37462.dist-info → udata-10.9.1.dev37604.dist-info}/entry_points.txt +0 -0
  45. {udata-10.9.1.dev37462.dist-info → udata-10.9.1.dev37604.dist-info}/top_level.txt +0 -0
@@ -1,284 +0,0 @@
1
- from flask import url_for
2
-
3
- from udata.core.organization.factories import OrganizationFactory
4
- from udata.core.spatial.factories import SpatialCoverageFactory
5
- from udata.core.spatial.models import spatial_granularities
6
- from udata.core.topic.factories import TopicFactory
7
- from udata.core.topic.models import Topic
8
- from udata.core.user.factories import UserFactory
9
- from udata.models import Discussion, Member
10
- from udata.tests.api.test_datasets_api import SAMPLE_GEOM
11
- from udata.tests.features.territories import create_geozones_fixtures
12
-
13
- from . import APITestCase
14
-
15
-
16
- class TopicsAPITest(APITestCase):
17
- modules = []
18
-
19
- def test_topic_api_list(self):
20
- """It should fetch a topic list from the API"""
21
- owner = UserFactory()
22
- org = OrganizationFactory()
23
- paca, _, _ = create_geozones_fixtures()
24
-
25
- tag_topic_1 = TopicFactory(tags=["my-tag-shared", "my-tag-1"])
26
- tag_topic_2 = TopicFactory(tags=["my-tag-shared", "my-tag-2"])
27
- name_topic = TopicFactory(name="topic-for-query")
28
- private_topic = TopicFactory(private=True)
29
- geozone_topic = TopicFactory(spatial=SpatialCoverageFactory(zones=[paca.id]))
30
- granularity_topic = TopicFactory(spatial=SpatialCoverageFactory(granularity="country"))
31
- featured_topic = TopicFactory(featured=True)
32
- owner_topic = TopicFactory(owner=owner)
33
- org_topic = TopicFactory(organization=org)
34
-
35
- response = self.get(url_for("api.topics"))
36
- self.assert200(response)
37
- self.assertEqual(len(response.json["data"]), 8)
38
-
39
- response = self.get(url_for("api.topics", q="topic-for"))
40
- self.assert200(response)
41
- self.assertEqual(len(response.json["data"]), 1)
42
- self.assertEqual(response.json["data"][0]["id"], str(name_topic.id))
43
-
44
- response = self.get(url_for("api.topics", tag=["my-tag-shared", "my-tag-1"]))
45
- self.assert200(response)
46
- self.assertEqual(len(response.json["data"]), 1)
47
- self.assertEqual(response.json["data"][0]["id"], str(tag_topic_1.id))
48
- datasets = response.json["data"][0]["datasets"]
49
- self.assertEqual(len(datasets), 3)
50
- for dataset, expected in zip(datasets, [d.fetch() for d in tag_topic_1.datasets]):
51
- self.assertEqual(dataset["id"], str(expected.id))
52
- self.assertEqual(dataset["title"], str(expected.title))
53
- self.assertIsNone(dataset["page"]) # we don't have cdata in tests
54
- self.assertIsNotNone(dataset["uri"])
55
- reuses = response.json["data"][0]["reuses"]
56
- for reuse, expected in zip(reuses, [r.fetch() for r in tag_topic_1.reuses]):
57
- self.assertEqual(reuse["id"], str(expected.id))
58
- self.assertEqual(reuse["title"], str(expected.title))
59
- self.assertIsNone(reuse["page"]) # we don't have cdata in tests
60
- self.assertIsNotNone(reuse["uri"])
61
- self.assertEqual(len(reuses), 3)
62
-
63
- response = self.get(url_for("api.topics", tag="my-tag-shared"))
64
- self.assert200(response)
65
- self.assertEqual(len(response.json["data"]), 2)
66
- self.assertEqual(
67
- set([str(tag_topic_1.id), str(tag_topic_2.id)]),
68
- set([t["id"] for t in response.json["data"]]),
69
- )
70
-
71
- response = self.get(url_for("api.topics", include_private="true"))
72
- self.assert200(response)
73
- self.assertEqual(len(response.json["data"]), 8)
74
- # we're not logged in, so the private topic does not appear
75
- self.assertNotIn(str(private_topic.id), [t["id"] for t in response.json["data"]])
76
-
77
- response = self.get(url_for("api.topics", geozone=paca.id))
78
- self.assert200(response)
79
- self.assertEqual(len(response.json["data"]), 1)
80
- self.assertIn(str(geozone_topic.id), [t["id"] for t in response.json["data"]])
81
-
82
- response = self.get(url_for("api.topics", granularity="country"))
83
- self.assert200(response)
84
- self.assertEqual(len(response.json["data"]), 1)
85
- self.assertIn(str(granularity_topic.id), [t["id"] for t in response.json["data"]])
86
-
87
- response = self.get(url_for("api.topics", featured="true"))
88
- self.assert200(response)
89
- self.assertEqual(len(response.json["data"]), 1)
90
- self.assertIn(str(featured_topic.id), [t["id"] for t in response.json["data"]])
91
-
92
- response = self.get(url_for("api.topics", featured="false"))
93
- self.assert200(response)
94
- self.assertEqual(len(response.json["data"]), 7)
95
- self.assertNotIn(str(featured_topic.id), [t["id"] for t in response.json["data"]])
96
-
97
- response = self.get(url_for("api.topics", owner=owner.id))
98
- self.assert200(response)
99
- self.assertEqual(len(response.json["data"]), 1)
100
- self.assertIn(str(owner_topic.id), [t["id"] for t in response.json["data"]])
101
-
102
- response = self.get(url_for("api.topics", organization=org.id))
103
- self.assert200(response)
104
- self.assertEqual(len(response.json["data"]), 1)
105
- self.assertIn(str(org_topic.id), [t["id"] for t in response.json["data"]])
106
-
107
- def test_topic_api_list_authenticated(self):
108
- owner = self.login()
109
-
110
- private_topic = TopicFactory(private=True)
111
- private_topic_owner = TopicFactory(private=True, owner=owner)
112
-
113
- response = self.get(url_for("api.topics"))
114
- self.assert200(response)
115
- self.assertEqual(len(response.json["data"]), 0)
116
-
117
- response = self.get(url_for("api.topics", include_private="true"))
118
- self.assert200(response)
119
- self.assertEqual(len(response.json["data"]), 1)
120
- self.assertNotIn(str(private_topic.id), [t["id"] for t in response.json["data"]])
121
- self.assertIn(str(private_topic_owner.id), [t["id"] for t in response.json["data"]])
122
-
123
- def test_topic_api_get(self):
124
- """It should fetch a topic from the API"""
125
- topic = TopicFactory()
126
- response = self.get(url_for("api.topic", topic=topic))
127
- self.assert200(response)
128
-
129
- data = response.json
130
- self.assertIn("spatial", data)
131
-
132
- for dataset, expected in zip(data["datasets"], [d.fetch() for d in topic.datasets]):
133
- self.assertEqual(dataset["id"], str(expected.id))
134
- self.assertEqual(dataset["title"], str(expected.title))
135
- self.assertIsNone(dataset["page"]) # we don't have cdata by default
136
- self.assertIsNotNone(dataset["uri"])
137
-
138
- for reuse, expected in zip(data["reuses"], [r.fetch() for r in topic.reuses]):
139
- self.assertEqual(reuse["id"], str(expected.id))
140
- self.assertEqual(reuse["title"], str(expected.title))
141
- self.assertIsNone(reuse["page"]) # we don't have cdata by default
142
- self.assertIsNotNone(reuse["uri"])
143
-
144
- self.assertIsNotNone(data.get("created_at"))
145
- self.assertIsNotNone(data.get("last_modified"))
146
-
147
- def test_topic_api_create(self):
148
- """It should create a topic from the API"""
149
- data = TopicFactory.as_dict()
150
- data["datasets"] = [str(d.id) for d in data["datasets"]]
151
- data["reuses"] = [str(r.id) for r in data["reuses"]]
152
- self.login()
153
- response = self.post(url_for("api.topics"), data)
154
- self.assert201(response)
155
- self.assertEqual(Topic.objects.count(), 1)
156
- topic = Topic.objects.first()
157
- for dataset, expected in zip(topic.datasets, data["datasets"]):
158
- self.assertEqual(str(dataset.id), expected)
159
- for reuse, expected in zip(topic.reuses, data["reuses"]):
160
- self.assertEqual(str(reuse.id), expected)
161
-
162
- def test_topic_api_create_as_org(self):
163
- """It should create a topic as organization from the API"""
164
- data = TopicFactory.as_dict()
165
- data["datasets"] = [str(d.id) for d in data["datasets"]]
166
- data["reuses"] = [str(r.id) for r in data["reuses"]]
167
- user = self.login()
168
- member = Member(user=user, role="editor")
169
- org = OrganizationFactory(members=[member])
170
- data["organization"] = str(org.id)
171
- response = self.post(url_for("api.topics"), data)
172
- self.assert201(response)
173
- self.assertEqual(Topic.objects.count(), 1)
174
-
175
- topic = Topic.objects.first()
176
- assert topic.owner is None
177
- assert topic.organization == org
178
-
179
- def test_topic_api_create_spatial_zone(self):
180
- paca, _, _ = create_geozones_fixtures()
181
- granularity = spatial_granularities[0][0]
182
- data = TopicFactory.as_dict()
183
- data["datasets"] = [str(d.id) for d in data["datasets"]]
184
- data["reuses"] = [str(r.id) for r in data["reuses"]]
185
- data["spatial"] = {
186
- "zones": [paca.id],
187
- "granularity": granularity,
188
- }
189
- self.login()
190
- response = self.post(url_for("api.topics"), data)
191
- self.assert201(response)
192
- self.assertEqual(Topic.objects.count(), 1)
193
- topic = Topic.objects.first()
194
- self.assertEqual([str(z) for z in topic.spatial.zones], [paca.id])
195
- self.assertEqual(topic.spatial.granularity, granularity)
196
-
197
- def test_topic_api_create_spatial_geom(self):
198
- granularity = spatial_granularities[0][0]
199
- data = TopicFactory.as_dict()
200
- data["datasets"] = [str(d.id) for d in data["datasets"]]
201
- data["reuses"] = [str(r.id) for r in data["reuses"]]
202
- data["spatial"] = {
203
- "geom": SAMPLE_GEOM,
204
- "granularity": granularity,
205
- }
206
- self.login()
207
- response = self.post(url_for("api.topics"), data)
208
- self.assert201(response)
209
- self.assertEqual(Topic.objects.count(), 1)
210
- topic = Topic.objects.first()
211
- self.assertEqual(topic.spatial.geom, SAMPLE_GEOM)
212
- self.assertEqual(topic.spatial.granularity, granularity)
213
-
214
- def test_topic_api_update(self):
215
- """It should update a topic from the API"""
216
- owner = self.login()
217
- topic = TopicFactory(owner=owner)
218
- data = topic.to_dict()
219
- data["description"] = "new description"
220
- response = self.put(url_for("api.topic", topic=topic), data)
221
- self.assert200(response)
222
- self.assertEqual(Topic.objects.count(), 1)
223
- topic = Topic.objects.first()
224
- self.assertEqual(topic.description, "new description")
225
- self.assertGreater(topic.last_modified, topic.created_at)
226
-
227
- def test_topic_api_update_perm(self):
228
- """It should not update a topic from the API"""
229
- owner = UserFactory()
230
- topic = TopicFactory(owner=owner)
231
- user = self.login()
232
- data = topic.to_dict()
233
- data["owner"] = user.to_dict()
234
- response = self.put(url_for("api.topic", topic=topic), data)
235
- self.assert403(response)
236
-
237
- def test_topic_api_clear_datasets(self):
238
- """It should remove all datasets if set to None"""
239
- owner = self.login()
240
- topic = TopicFactory(owner=owner)
241
- self.assertGreater(len(topic.datasets), 0)
242
- data = topic.to_dict()
243
- data["datasets"] = None
244
- response = self.put(url_for("api.topic", topic=topic), data)
245
- self.assert200(response)
246
- topic.reload()
247
- self.assertEqual(len(topic.datasets), 0)
248
-
249
- def test_topic_api_delete(self):
250
- """It should delete a topic from the API"""
251
- owner = self.login()
252
- topic = TopicFactory(owner=owner)
253
-
254
- with self.api_user():
255
- response = self.post(
256
- url_for("api.discussions"),
257
- {
258
- "title": "test title",
259
- "comment": "bla bla",
260
- "subject": {
261
- "class": "Topic",
262
- "id": topic.id,
263
- },
264
- },
265
- )
266
- self.assert201(response)
267
-
268
- discussions = Discussion.objects(subject=topic)
269
- self.assertEqual(len(discussions), 1)
270
-
271
- with self.api_user():
272
- response = self.delete(url_for("api.topic", topic=topic))
273
- self.assertStatus(response, 204)
274
-
275
- self.assertEqual(Topic.objects.count(), 0)
276
- self.assertEqual(Discussion.objects.count(), 0)
277
-
278
- def test_topic_api_delete_perm(self):
279
- """It should not delete a topic from the API"""
280
- owner = UserFactory()
281
- topic = TopicFactory(owner=owner)
282
- with self.api_user():
283
- response = self.delete(url_for("api.topic", topic=topic))
284
- self.assertStatus(response, 403)