udata 14.0.0__py3-none-any.whl → 14.4.1.dev7__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_fields.py +35 -4
- udata/app.py +18 -20
- udata/auth/__init__.py +29 -6
- udata/auth/forms.py +2 -2
- udata/auth/views.py +6 -3
- udata/commands/serve.py +3 -11
- udata/commands/tests/test_fixtures.py +9 -9
- udata/core/access_type/api.py +1 -1
- udata/core/access_type/constants.py +12 -8
- udata/core/activity/api.py +5 -6
- udata/core/badges/tests/test_commands.py +6 -6
- udata/core/csv.py +5 -0
- udata/core/dataservices/models.py +1 -1
- udata/core/dataservices/tasks.py +7 -0
- udata/core/dataset/api.py +2 -0
- udata/core/dataset/models.py +2 -2
- udata/core/dataset/permissions.py +31 -0
- udata/core/dataset/tasks.py +17 -5
- udata/core/discussions/models.py +1 -0
- udata/core/organization/api.py +8 -5
- udata/core/organization/mails.py +1 -1
- udata/core/organization/models.py +9 -1
- udata/core/organization/notifications.py +84 -0
- udata/core/organization/permissions.py +1 -1
- udata/core/organization/tasks.py +3 -0
- udata/core/pages/tests/test_api.py +32 -0
- udata/core/post/api.py +24 -69
- udata/core/post/models.py +84 -16
- udata/core/post/tests/test_api.py +24 -1
- udata/core/reports/api.py +18 -0
- udata/core/reports/models.py +42 -2
- udata/core/reuse/models.py +1 -1
- udata/core/reuse/tasks.py +7 -0
- udata/core/spatial/forms.py +2 -2
- udata/core/user/models.py +5 -1
- udata/features/notifications/api.py +7 -18
- udata/features/notifications/models.py +56 -0
- udata/features/notifications/tasks.py +25 -0
- udata/flask_mongoengine/engine.py +0 -4
- udata/frontend/markdown.py +2 -1
- udata/harvest/actions.py +21 -1
- udata/harvest/api.py +25 -8
- udata/harvest/backends/base.py +27 -1
- udata/harvest/backends/ckan/harvesters.py +11 -2
- 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 +58 -5
- udata/harvest/tests/test_api.py +276 -122
- udata/harvest/tests/test_base_backend.py +86 -1
- udata/harvest/tests/test_dcat_backend.py +57 -10
- udata/harvest/tests/test_filters.py +6 -0
- udata/i18n.py +1 -4
- udata/mail.py +5 -1
- 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/mongo/slug_fields.py +1 -1
- udata/rdf.py +45 -6
- udata/routing.py +2 -2
- udata/settings.py +7 -0
- udata/tasks.py +1 -0
- udata/templates/mail/message.html +5 -31
- udata/tests/__init__.py +27 -2
- udata/tests/api/__init__.py +108 -21
- udata/tests/api/test_activities_api.py +36 -0
- udata/tests/api/test_auth_api.py +121 -95
- udata/tests/api/test_base_api.py +7 -4
- udata/tests/api/test_datasets_api.py +44 -19
- udata/tests/api/test_organizations_api.py +192 -197
- udata/tests/api/test_reports_api.py +157 -0
- udata/tests/api/test_reuses_api.py +147 -147
- udata/tests/api/test_security_api.py +12 -12
- udata/tests/api/test_swagger.py +4 -4
- udata/tests/api/test_tags_api.py +8 -8
- udata/tests/api/test_user_api.py +1 -1
- udata/tests/apiv2/test_swagger.py +4 -4
- udata/tests/cli/test_cli_base.py +8 -9
- udata/tests/dataset/test_dataset_commands.py +4 -4
- udata/tests/dataset/test_dataset_model.py +66 -26
- udata/tests/dataset/test_dataset_rdf.py +99 -5
- udata/tests/frontend/test_auth.py +24 -1
- udata/tests/frontend/test_csv.py +0 -3
- udata/tests/helpers.py +25 -27
- udata/tests/organization/test_notifications.py +67 -2
- udata/tests/plugin.py +6 -261
- udata/tests/site/test_site_csv_exports.py +22 -10
- udata/tests/test_activity.py +9 -9
- udata/tests/test_dcat_commands.py +2 -2
- udata/tests/test_discussions.py +5 -5
- udata/tests/test_migrations.py +21 -21
- 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_storages.py +25 -19
- udata/tests/test_topics.py +77 -61
- udata/tests/test_uris.py +33 -0
- udata/tests/workers/test_jobs_commands.py +23 -23
- udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
- udata/translations/ar/LC_MESSAGES/udata.po +187 -108
- udata/translations/de/LC_MESSAGES/udata.mo +0 -0
- udata/translations/de/LC_MESSAGES/udata.po +187 -108
- udata/translations/es/LC_MESSAGES/udata.mo +0 -0
- udata/translations/es/LC_MESSAGES/udata.po +187 -108
- udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/fr/LC_MESSAGES/udata.po +188 -109
- udata/translations/it/LC_MESSAGES/udata.mo +0 -0
- udata/translations/it/LC_MESSAGES/udata.po +187 -108
- udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
- udata/translations/pt/LC_MESSAGES/udata.po +187 -108
- udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/sr/LC_MESSAGES/udata.po +187 -108
- udata/translations/udata.pot +215 -106
- udata/uris.py +0 -2
- udata-14.4.1.dev7.dist-info/METADATA +109 -0
- {udata-14.0.0.dist-info → udata-14.4.1.dev7.dist-info}/RECORD +121 -123
- 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.0.dist-info/METADATA +0 -132
- {udata-14.0.0.dist-info → udata-14.4.1.dev7.dist-info}/WHEEL +0 -0
- {udata-14.0.0.dist-info → udata-14.4.1.dev7.dist-info}/entry_points.txt +0 -0
- {udata-14.0.0.dist-info → udata-14.4.1.dev7.dist-info}/licenses/LICENSE +0 -0
- {udata-14.0.0.dist-info → udata-14.4.1.dev7.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
1
3
|
from flask import url_for
|
|
2
4
|
|
|
3
5
|
from udata.core.dataset.factories import DatasetFactory
|
|
@@ -148,6 +150,28 @@ class ReportsAPITest(APITestCase):
|
|
|
148
150
|
|
|
149
151
|
self.assertEqual(payload["data"][1]["subject"]["id"], str(spam_reuse.id))
|
|
150
152
|
|
|
153
|
+
def test_reports_api_list_sort_by_reported_at(self):
|
|
154
|
+
user = UserFactory()
|
|
155
|
+
|
|
156
|
+
dataset1 = DatasetFactory.create(owner=user)
|
|
157
|
+
dataset2 = DatasetFactory.create(owner=user)
|
|
158
|
+
dataset3 = DatasetFactory.create(owner=user)
|
|
159
|
+
|
|
160
|
+
# Create reports with different reported_at times
|
|
161
|
+
report1 = Report(subject=dataset1, reason="spam", reported_at=datetime(2024, 1, 1)).save()
|
|
162
|
+
report2 = Report(subject=dataset2, reason="spam", reported_at=datetime(2024, 1, 3)).save()
|
|
163
|
+
report3 = Report(subject=dataset3, reason="spam", reported_at=datetime(2024, 1, 2)).save()
|
|
164
|
+
|
|
165
|
+
self.login(AdminFactory())
|
|
166
|
+
|
|
167
|
+
# Sort by -reported_at (most recent first)
|
|
168
|
+
response = self.get(url_for("api.reports", sort="-reported_at"))
|
|
169
|
+
self.assert200(response)
|
|
170
|
+
payload = response.json
|
|
171
|
+
self.assertEqual(payload["data"][0]["id"], str(report2.id))
|
|
172
|
+
self.assertEqual(payload["data"][1]["id"], str(report3.id))
|
|
173
|
+
self.assertEqual(payload["data"][2]["id"], str(report1.id))
|
|
174
|
+
|
|
151
175
|
def test_reports_api_get(self):
|
|
152
176
|
user = UserFactory()
|
|
153
177
|
|
|
@@ -165,3 +189,136 @@ class ReportsAPITest(APITestCase):
|
|
|
165
189
|
|
|
166
190
|
payload = response.json
|
|
167
191
|
self.assertEqual(payload["subject"]["id"], str(spam_dataset.id))
|
|
192
|
+
|
|
193
|
+
def test_reports_api_dismiss(self):
|
|
194
|
+
user = UserFactory()
|
|
195
|
+
admin = AdminFactory()
|
|
196
|
+
|
|
197
|
+
spam_dataset = DatasetFactory.create(owner=user)
|
|
198
|
+
report = Report(subject=spam_dataset, reason="spam").save()
|
|
199
|
+
|
|
200
|
+
dismiss_time = datetime.utcnow().isoformat()
|
|
201
|
+
|
|
202
|
+
# Should require admin
|
|
203
|
+
response = self.patch(url_for("api.report", report=report), {"dismissed_at": dismiss_time})
|
|
204
|
+
self.assert401(response)
|
|
205
|
+
|
|
206
|
+
self.login(user)
|
|
207
|
+
response = self.patch(url_for("api.report", report=report), {"dismissed_at": dismiss_time})
|
|
208
|
+
self.assert403(response)
|
|
209
|
+
|
|
210
|
+
self.login(admin)
|
|
211
|
+
response = self.patch(url_for("api.report", report=report), {"dismissed_at": dismiss_time})
|
|
212
|
+
self.assert200(response)
|
|
213
|
+
|
|
214
|
+
payload = response.json
|
|
215
|
+
self.assertIsNotNone(payload["dismissed_at"])
|
|
216
|
+
self.assertEqual(payload["dismissed_by"]["id"], str(admin.id))
|
|
217
|
+
|
|
218
|
+
report.reload()
|
|
219
|
+
self.assertIsNotNone(report.dismissed_at)
|
|
220
|
+
self.assertEqual(report.dismissed_by.id, admin.id)
|
|
221
|
+
|
|
222
|
+
def test_reports_api_undismiss(self):
|
|
223
|
+
user = UserFactory()
|
|
224
|
+
admin = AdminFactory()
|
|
225
|
+
|
|
226
|
+
spam_dataset = DatasetFactory.create(owner=user)
|
|
227
|
+
report = Report(
|
|
228
|
+
subject=spam_dataset,
|
|
229
|
+
reason="spam",
|
|
230
|
+
dismissed_at=datetime.utcnow(),
|
|
231
|
+
dismissed_by=admin,
|
|
232
|
+
).save()
|
|
233
|
+
|
|
234
|
+
# Verify report is dismissed
|
|
235
|
+
self.assertIsNotNone(report.dismissed_at)
|
|
236
|
+
self.assertIsNotNone(report.dismissed_by)
|
|
237
|
+
|
|
238
|
+
# Should require admin
|
|
239
|
+
response = self.patch(url_for("api.report", report=report), {"dismissed_at": None})
|
|
240
|
+
self.assert401(response)
|
|
241
|
+
|
|
242
|
+
self.login(user)
|
|
243
|
+
response = self.patch(url_for("api.report", report=report), {"dismissed_at": None})
|
|
244
|
+
self.assert403(response)
|
|
245
|
+
|
|
246
|
+
self.login(admin)
|
|
247
|
+
response = self.patch(url_for("api.report", report=report), {"dismissed_at": None})
|
|
248
|
+
self.assert200(response)
|
|
249
|
+
|
|
250
|
+
payload = response.json
|
|
251
|
+
self.assertIsNone(payload["dismissed_at"])
|
|
252
|
+
self.assertIsNone(payload["dismissed_by"])
|
|
253
|
+
|
|
254
|
+
report.reload()
|
|
255
|
+
self.assertIsNone(report.dismissed_at)
|
|
256
|
+
self.assertIsNone(report.dismissed_by)
|
|
257
|
+
|
|
258
|
+
def test_reports_api_filter_by_handled(self):
|
|
259
|
+
user = UserFactory()
|
|
260
|
+
admin = AdminFactory()
|
|
261
|
+
|
|
262
|
+
dataset1 = DatasetFactory.create(owner=user)
|
|
263
|
+
dataset2 = DatasetFactory.create(owner=user)
|
|
264
|
+
|
|
265
|
+
# Unhandled report (not dismissed)
|
|
266
|
+
ongoing_report = Report(subject=dataset1, reason="spam").save()
|
|
267
|
+
|
|
268
|
+
# Handled report (dismissed)
|
|
269
|
+
dismissed_report = Report(
|
|
270
|
+
subject=dataset2, reason="spam", dismissed_at=datetime.utcnow(), dismissed_by=admin
|
|
271
|
+
).save()
|
|
272
|
+
|
|
273
|
+
self.login(admin)
|
|
274
|
+
|
|
275
|
+
# Filter by unhandled
|
|
276
|
+
response = self.get(url_for("api.reports", handled="false"))
|
|
277
|
+
self.assert200(response)
|
|
278
|
+
payload = response.json
|
|
279
|
+
self.assertEqual(payload["total"], 1)
|
|
280
|
+
self.assertEqual(payload["data"][0]["id"], str(ongoing_report.id))
|
|
281
|
+
|
|
282
|
+
# Filter by handled
|
|
283
|
+
response = self.get(url_for("api.reports", handled="true"))
|
|
284
|
+
self.assert200(response)
|
|
285
|
+
payload = response.json
|
|
286
|
+
self.assertEqual(payload["total"], 1)
|
|
287
|
+
self.assertEqual(payload["data"][0]["id"], str(dismissed_report.id))
|
|
288
|
+
|
|
289
|
+
# No filter (all reports)
|
|
290
|
+
response = self.get(url_for("api.reports"))
|
|
291
|
+
self.assert200(response)
|
|
292
|
+
payload = response.json
|
|
293
|
+
self.assertEqual(payload["total"], 2)
|
|
294
|
+
|
|
295
|
+
def test_reports_api_filter_handled_with_deleted_subject(self):
|
|
296
|
+
"""Reports with deleted subjects should appear when handled="true", not handled="false"."""
|
|
297
|
+
user = UserFactory()
|
|
298
|
+
admin = AdminFactory()
|
|
299
|
+
|
|
300
|
+
dataset1 = DatasetFactory.create(owner=user)
|
|
301
|
+
dataset2 = DatasetFactory.create(owner=user)
|
|
302
|
+
|
|
303
|
+
# Unhandled report (not dismissed, subject exists)
|
|
304
|
+
ongoing_report = Report(subject=dataset1, reason="spam").save()
|
|
305
|
+
|
|
306
|
+
# Report with deleted subject (should appear in "handled", not "unhandled")
|
|
307
|
+
deleted_subject_report = Report(subject=dataset2, reason="spam").save()
|
|
308
|
+
dataset2.delete()
|
|
309
|
+
|
|
310
|
+
self.login(admin)
|
|
311
|
+
|
|
312
|
+
# Filter by unhandled - should only return the report with existing subject
|
|
313
|
+
response = self.get(url_for("api.reports", handled="false"))
|
|
314
|
+
self.assert200(response)
|
|
315
|
+
payload = response.json
|
|
316
|
+
self.assertEqual(payload["total"], 1)
|
|
317
|
+
self.assertEqual(payload["data"][0]["id"], str(ongoing_report.id))
|
|
318
|
+
|
|
319
|
+
# Filter by handled - should return the report with deleted subject
|
|
320
|
+
response = self.get(url_for("api.reports", handled="true"))
|
|
321
|
+
self.assert200(response)
|
|
322
|
+
payload = response.json
|
|
323
|
+
self.assertEqual(payload["total"], 1)
|
|
324
|
+
self.assertEqual(payload["data"][0]["id"], str(deleted_subject_report.id))
|