udata 10.8.1.dev36652__py2.py3-none-any.whl → 10.8.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/app.py +0 -2
- udata/commands/db.py +22 -9
- udata/core/dataset/models.py +5 -3
- udata/core/discussions/api.py +2 -2
- udata/core/jobs/api.py +3 -3
- udata/core/metrics/helpers.py +10 -0
- udata/core/metrics/tasks.py +144 -1
- udata/core/organization/api.py +2 -2
- udata/core/post/api.py +1 -1
- udata/core/user/api.py +1 -1
- udata/features/identicon/api.py +1 -1
- udata/harvest/actions.py +24 -28
- udata/harvest/api.py +28 -36
- udata/harvest/backends/ckan/__init__.py +3 -0
- udata/harvest/backends/ckan/harvesters.py +274 -0
- udata/harvest/backends/ckan/schemas/__init__.py +0 -0
- udata/harvest/backends/ckan/schemas/ckan.py +86 -0
- udata/harvest/backends/ckan/schemas/dkan.py +98 -0
- udata/harvest/commands.py +7 -7
- udata/harvest/tasks.py +1 -1
- udata/harvest/tests/ckan/conftest.py +67 -0
- udata/harvest/tests/ckan/data/dkan-french-w-license.json +226 -0
- udata/harvest/tests/ckan/test_ckan_backend.py +697 -0
- udata/harvest/tests/ckan/test_ckan_backend_errors.py +140 -0
- udata/harvest/tests/ckan/test_ckan_backend_filters.py +130 -0
- udata/harvest/tests/ckan/test_dkan_backend.py +68 -0
- udata/harvest/tests/test_actions.py +27 -32
- udata/harvest/tests/test_api.py +23 -18
- udata/harvest/tests/test_dcat_backend.py +29 -29
- udata/migrations/2025-07-30-purge-old-harvest-dynamic-fields.py +29 -0
- udata/mongo/slug_fields.py +25 -8
- udata/routing.py +6 -0
- udata/static/chunks/{11.b6f741fcc366abfad9c4.js → 11.51d706fb9521c16976bc.js} +3 -3
- udata/static/chunks/{11.b6f741fcc366abfad9c4.js.map → 11.51d706fb9521c16976bc.js.map} +1 -1
- udata/static/chunks/{13.2d06442dd9a05d9777b5.js → 13.39e106d56f794ebd06a0.js} +2 -2
- udata/static/chunks/{13.2d06442dd9a05d9777b5.js.map → 13.39e106d56f794ebd06a0.js.map} +1 -1
- udata/static/chunks/{17.e8e4caaad5cb0cc0bacc.js → 17.70cbb4a91b002338007e.js} +2 -2
- udata/static/chunks/{17.e8e4caaad5cb0cc0bacc.js.map → 17.70cbb4a91b002338007e.js.map} +1 -1
- udata/static/chunks/{19.f03a102365af4315f9db.js → 19.a348a5fff8fe2801e52a.js} +3 -3
- udata/static/chunks/{19.f03a102365af4315f9db.js.map → 19.a348a5fff8fe2801e52a.js.map} +1 -1
- udata/static/chunks/{5.0fa1408dae4e76b87b2e.js → 5.343ca020a2d38cec1a14.js} +3 -3
- udata/static/chunks/{5.0fa1408dae4e76b87b2e.js.map → 5.343ca020a2d38cec1a14.js.map} +1 -1
- udata/static/chunks/{6.d663709d877baa44a71e.js → 6.a3b07de9dd2ca2d24e85.js} +3 -3
- udata/static/chunks/{6.d663709d877baa44a71e.js.map → 6.a3b07de9dd2ca2d24e85.js.map} +1 -1
- udata/static/chunks/{8.778091d55cd8ea39af6b.js → 8.462bb3029de008497675.js} +2 -2
- udata/static/chunks/{8.778091d55cd8ea39af6b.js.map → 8.462bb3029de008497675.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/tests/api/test_datasets_api.py +0 -46
- udata/tests/api/test_organizations_api.py +5 -0
- udata/tests/cli/test_db_cli.py +12 -0
- udata/tests/dataset/test_dataset_model.py +0 -16
- udata/tests/metrics/__init__.py +0 -0
- udata/tests/metrics/conftest.py +15 -0
- udata/tests/metrics/helpers.py +58 -0
- udata/tests/metrics/test_metrics.py +67 -0
- udata/tests/metrics/test_tasks.py +171 -0
- udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
- udata/translations/ar/LC_MESSAGES/udata.po +72 -65
- udata/translations/de/LC_MESSAGES/udata.mo +0 -0
- udata/translations/de/LC_MESSAGES/udata.po +72 -65
- udata/translations/es/LC_MESSAGES/udata.mo +0 -0
- udata/translations/es/LC_MESSAGES/udata.po +72 -65
- udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/fr/LC_MESSAGES/udata.po +72 -65
- udata/translations/it/LC_MESSAGES/udata.mo +0 -0
- udata/translations/it/LC_MESSAGES/udata.po +72 -65
- udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
- udata/translations/pt/LC_MESSAGES/udata.po +72 -65
- udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/sr/LC_MESSAGES/udata.po +72 -65
- udata/translations/udata.pot +74 -70
- {udata-10.8.1.dev36652.dist-info → udata-10.8.2.dist-info}/METADATA +16 -2
- {udata-10.8.1.dev36652.dist-info → udata-10.8.2.dist-info}/RECORD +79 -62
- {udata-10.8.1.dev36652.dist-info → udata-10.8.2.dist-info}/entry_points.txt +2 -0
- {udata-10.8.1.dev36652.dist-info → udata-10.8.2.dist-info}/LICENSE +0 -0
- {udata-10.8.1.dev36652.dist-info → udata-10.8.2.dist-info}/WHEEL +0 -0
- {udata-10.8.1.dev36652.dist-info → udata-10.8.2.dist-info}/top_level.txt +0 -0
udata/harvest/tests/test_api.py
CHANGED
|
@@ -395,7 +395,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
395
395
|
"url": new_url,
|
|
396
396
|
"backend": "factory",
|
|
397
397
|
}
|
|
398
|
-
api_url = url_for("api.harvest_source",
|
|
398
|
+
api_url = url_for("api.harvest_source", source=source)
|
|
399
399
|
response = api.put(api_url, data)
|
|
400
400
|
assert200(response)
|
|
401
401
|
assert response.json["url"] == new_url
|
|
@@ -403,7 +403,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
403
403
|
# Source is now owned by orga, with user as member
|
|
404
404
|
source.organization = OrganizationFactory(members=[Member(user=user)])
|
|
405
405
|
source.save()
|
|
406
|
-
api_url = url_for("api.harvest_source",
|
|
406
|
+
api_url = url_for("api.harvest_source", source=source)
|
|
407
407
|
response = api.put(api_url, data)
|
|
408
408
|
assert200(response)
|
|
409
409
|
|
|
@@ -418,7 +418,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
418
418
|
"url": new_url,
|
|
419
419
|
"backend": "factory",
|
|
420
420
|
}
|
|
421
|
-
api_url: str = url_for("api.harvest_source",
|
|
421
|
+
api_url: str = url_for("api.harvest_source", source=source)
|
|
422
422
|
response = api.put(api_url, data)
|
|
423
423
|
|
|
424
424
|
assert403(response)
|
|
@@ -429,7 +429,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
429
429
|
source = HarvestSourceFactory()
|
|
430
430
|
|
|
431
431
|
data = {"state": VALIDATION_ACCEPTED}
|
|
432
|
-
url = url_for("api.validate_harvest_source",
|
|
432
|
+
url = url_for("api.validate_harvest_source", source=source)
|
|
433
433
|
response = api.post(url, data)
|
|
434
434
|
assert200(response)
|
|
435
435
|
|
|
@@ -443,7 +443,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
443
443
|
source = HarvestSourceFactory()
|
|
444
444
|
|
|
445
445
|
data = {"state": VALIDATION_REFUSED, "comment": "Not valid"}
|
|
446
|
-
url = url_for("api.validate_harvest_source",
|
|
446
|
+
url = url_for("api.validate_harvest_source", source=source)
|
|
447
447
|
response = api.post(url, data)
|
|
448
448
|
assert200(response)
|
|
449
449
|
|
|
@@ -458,22 +458,27 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
458
458
|
source = HarvestSourceFactory()
|
|
459
459
|
|
|
460
460
|
data = {"validate": True}
|
|
461
|
-
url = url_for("api.validate_harvest_source",
|
|
461
|
+
url = url_for("api.validate_harvest_source", source=source)
|
|
462
462
|
response = api.post(url, data)
|
|
463
463
|
assert403(response)
|
|
464
464
|
|
|
465
465
|
def test_get_source(self, api):
|
|
466
466
|
source = HarvestSourceFactory()
|
|
467
467
|
|
|
468
|
-
url = url_for("api.harvest_source",
|
|
468
|
+
url = url_for("api.harvest_source", source=source)
|
|
469
469
|
response = api.get(url)
|
|
470
470
|
assert200(response)
|
|
471
471
|
|
|
472
|
+
def test_get_missing_source(self, api):
|
|
473
|
+
url = url_for("api.harvest_source", source="685bb38b9cb9284b93fd9e72")
|
|
474
|
+
response = api.get(url)
|
|
475
|
+
assert404(response)
|
|
476
|
+
|
|
472
477
|
def test_source_preview(self, api):
|
|
473
478
|
api.login()
|
|
474
479
|
source = HarvestSourceFactory(backend="factory")
|
|
475
480
|
|
|
476
|
-
url = url_for("api.preview_harvest_source",
|
|
481
|
+
url = url_for("api.preview_harvest_source", source=source)
|
|
477
482
|
response = api.get(url)
|
|
478
483
|
assert200(response)
|
|
479
484
|
|
|
@@ -488,7 +493,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
488
493
|
validation=HarvestSourceValidation(state=VALIDATION_ACCEPTED),
|
|
489
494
|
)
|
|
490
495
|
|
|
491
|
-
url = url_for("api.run_harvest_source",
|
|
496
|
+
url = url_for("api.run_harvest_source", source=source)
|
|
492
497
|
response = api.post(url)
|
|
493
498
|
assert200(response)
|
|
494
499
|
|
|
@@ -505,7 +510,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
505
510
|
validation=HarvestSourceValidation(state=VALIDATION_ACCEPTED),
|
|
506
511
|
)
|
|
507
512
|
|
|
508
|
-
url = url_for("api.run_harvest_source",
|
|
513
|
+
url = url_for("api.run_harvest_source", source=source)
|
|
509
514
|
response = api.post(url)
|
|
510
515
|
assert400(response)
|
|
511
516
|
|
|
@@ -523,7 +528,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
523
528
|
validation=HarvestSourceValidation(state=VALIDATION_ACCEPTED),
|
|
524
529
|
)
|
|
525
530
|
|
|
526
|
-
url = url_for("api.run_harvest_source",
|
|
531
|
+
url = url_for("api.run_harvest_source", source=source)
|
|
527
532
|
response = api.post(url)
|
|
528
533
|
assert403(response)
|
|
529
534
|
|
|
@@ -540,7 +545,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
540
545
|
validation=HarvestSourceValidation(state=VALIDATION_PENDING),
|
|
541
546
|
)
|
|
542
547
|
|
|
543
|
-
url = url_for("api.run_harvest_source",
|
|
548
|
+
url = url_for("api.run_harvest_source", source=source)
|
|
544
549
|
response = api.post(url)
|
|
545
550
|
assert400(response)
|
|
546
551
|
|
|
@@ -556,7 +561,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
556
561
|
user = api.login()
|
|
557
562
|
source = HarvestSourceFactory(owner=user)
|
|
558
563
|
|
|
559
|
-
url = url_for("api.harvest_source",
|
|
564
|
+
url = url_for("api.harvest_source", source=source)
|
|
560
565
|
response = api.delete(url)
|
|
561
566
|
assert204(response)
|
|
562
567
|
|
|
@@ -568,7 +573,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
568
573
|
api.login()
|
|
569
574
|
source = HarvestSourceFactory()
|
|
570
575
|
|
|
571
|
-
url = url_for("api.harvest_source",
|
|
576
|
+
url = url_for("api.harvest_source", source=source)
|
|
572
577
|
response = api.delete(url)
|
|
573
578
|
|
|
574
579
|
assert403(response)
|
|
@@ -579,7 +584,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
579
584
|
source = HarvestSourceFactory()
|
|
580
585
|
|
|
581
586
|
data = "0 0 * * *"
|
|
582
|
-
url = url_for("api.schedule_harvest_source",
|
|
587
|
+
url = url_for("api.schedule_harvest_source", source=source)
|
|
583
588
|
response = api.post(url, data)
|
|
584
589
|
assert200(response)
|
|
585
590
|
|
|
@@ -601,7 +606,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
601
606
|
source = HarvestSourceFactory()
|
|
602
607
|
|
|
603
608
|
data = "0 0 * * *"
|
|
604
|
-
url = url_for("api.schedule_harvest_source",
|
|
609
|
+
url = url_for("api.schedule_harvest_source", source=source)
|
|
605
610
|
response = api.post(url, data)
|
|
606
611
|
assert403(response)
|
|
607
612
|
|
|
@@ -620,7 +625,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
620
625
|
)
|
|
621
626
|
source = HarvestSourceFactory(periodic_task=periodic_task)
|
|
622
627
|
|
|
623
|
-
url = url_for("api.schedule_harvest_source",
|
|
628
|
+
url = url_for("api.schedule_harvest_source", source=source)
|
|
624
629
|
response = api.delete(url)
|
|
625
630
|
assert204(response)
|
|
626
631
|
|
|
@@ -639,7 +644,7 @@ class HarvestAPITest(MockBackendsMixin):
|
|
|
639
644
|
)
|
|
640
645
|
source = HarvestSourceFactory(periodic_task=periodic_task)
|
|
641
646
|
|
|
642
|
-
url = url_for("api.schedule_harvest_source",
|
|
647
|
+
url = url_for("api.schedule_harvest_source", source=source)
|
|
643
648
|
response = api.delete(url)
|
|
644
649
|
assert403(response)
|
|
645
650
|
|
|
@@ -74,7 +74,7 @@ class DcatBackendTest:
|
|
|
74
74
|
org = OrganizationFactory()
|
|
75
75
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
76
76
|
|
|
77
|
-
actions.run(source
|
|
77
|
+
actions.run(source)
|
|
78
78
|
|
|
79
79
|
source.reload()
|
|
80
80
|
|
|
@@ -123,7 +123,7 @@ class DcatBackendTest:
|
|
|
123
123
|
org = OrganizationFactory()
|
|
124
124
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
125
125
|
|
|
126
|
-
actions.run(source
|
|
126
|
+
actions.run(source)
|
|
127
127
|
|
|
128
128
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
129
129
|
|
|
@@ -154,7 +154,7 @@ class DcatBackendTest:
|
|
|
154
154
|
org = OrganizationFactory()
|
|
155
155
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
156
156
|
|
|
157
|
-
actions.run(source
|
|
157
|
+
actions.run(source)
|
|
158
158
|
|
|
159
159
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
160
160
|
|
|
@@ -171,7 +171,7 @@ class DcatBackendTest:
|
|
|
171
171
|
org = OrganizationFactory()
|
|
172
172
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
173
173
|
|
|
174
|
-
actions.run(source
|
|
174
|
+
actions.run(source)
|
|
175
175
|
|
|
176
176
|
dataservices = Dataservice.objects
|
|
177
177
|
|
|
@@ -192,7 +192,7 @@ class DcatBackendTest:
|
|
|
192
192
|
org = OrganizationFactory()
|
|
193
193
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
194
194
|
|
|
195
|
-
actions.run(source
|
|
195
|
+
actions.run(source)
|
|
196
196
|
|
|
197
197
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
198
198
|
assert len(datasets) == 8
|
|
@@ -244,7 +244,7 @@ class DcatBackendTest:
|
|
|
244
244
|
org = OrganizationFactory()
|
|
245
245
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
246
246
|
|
|
247
|
-
actions.run(source
|
|
247
|
+
actions.run(source)
|
|
248
248
|
|
|
249
249
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
250
250
|
|
|
@@ -306,7 +306,7 @@ class DcatBackendTest:
|
|
|
306
306
|
org = OrganizationFactory()
|
|
307
307
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
308
308
|
|
|
309
|
-
actions.run(source
|
|
309
|
+
actions.run(source)
|
|
310
310
|
|
|
311
311
|
assert Dataset.objects.count() == 2
|
|
312
312
|
assert HarvestJob.objects.first().status == "done"
|
|
@@ -320,7 +320,7 @@ class DcatBackendTest:
|
|
|
320
320
|
org = OrganizationFactory()
|
|
321
321
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
322
322
|
|
|
323
|
-
actions.run(source
|
|
323
|
+
actions.run(source)
|
|
324
324
|
|
|
325
325
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
326
326
|
|
|
@@ -345,7 +345,7 @@ class DcatBackendTest:
|
|
|
345
345
|
org = OrganizationFactory()
|
|
346
346
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
347
347
|
|
|
348
|
-
actions.run(source
|
|
348
|
+
actions.run(source)
|
|
349
349
|
|
|
350
350
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
351
351
|
|
|
@@ -388,7 +388,7 @@ class DcatBackendTest:
|
|
|
388
388
|
url = mock_dcat(rmock, filename)
|
|
389
389
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=OrganizationFactory())
|
|
390
390
|
|
|
391
|
-
actions.run(source
|
|
391
|
+
actions.run(source)
|
|
392
392
|
|
|
393
393
|
source.reload()
|
|
394
394
|
|
|
@@ -416,8 +416,8 @@ class DcatBackendTest:
|
|
|
416
416
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
417
417
|
|
|
418
418
|
# Run the same havester twice
|
|
419
|
-
actions.run(source
|
|
420
|
-
actions.run(source
|
|
419
|
+
actions.run(source)
|
|
420
|
+
actions.run(source)
|
|
421
421
|
|
|
422
422
|
datasets = {d.harvest.dct_identifier: d for d in Dataset.objects}
|
|
423
423
|
|
|
@@ -431,7 +431,7 @@ class DcatBackendTest:
|
|
|
431
431
|
org = OrganizationFactory()
|
|
432
432
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
433
433
|
|
|
434
|
-
actions.run(source
|
|
434
|
+
actions.run(source)
|
|
435
435
|
|
|
436
436
|
source.reload()
|
|
437
437
|
|
|
@@ -443,7 +443,7 @@ class DcatBackendTest:
|
|
|
443
443
|
org = OrganizationFactory()
|
|
444
444
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
445
445
|
|
|
446
|
-
actions.run(source
|
|
446
|
+
actions.run(source)
|
|
447
447
|
|
|
448
448
|
source.reload()
|
|
449
449
|
|
|
@@ -456,7 +456,7 @@ class DcatBackendTest:
|
|
|
456
456
|
org = OrganizationFactory()
|
|
457
457
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
458
458
|
|
|
459
|
-
actions.run(source
|
|
459
|
+
actions.run(source)
|
|
460
460
|
|
|
461
461
|
source.reload()
|
|
462
462
|
|
|
@@ -470,7 +470,7 @@ class DcatBackendTest:
|
|
|
470
470
|
org = OrganizationFactory()
|
|
471
471
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
472
472
|
|
|
473
|
-
actions.run(source
|
|
473
|
+
actions.run(source)
|
|
474
474
|
|
|
475
475
|
source.reload()
|
|
476
476
|
|
|
@@ -493,7 +493,7 @@ class DcatBackendTest:
|
|
|
493
493
|
org = OrganizationFactory()
|
|
494
494
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
495
495
|
|
|
496
|
-
actions.run(source
|
|
496
|
+
actions.run(source)
|
|
497
497
|
|
|
498
498
|
# test dct:license support
|
|
499
499
|
dataset = Dataset.objects.get(harvest__dct_identifier="3")
|
|
@@ -587,7 +587,7 @@ class DcatBackendTest:
|
|
|
587
587
|
url = mock_dcat(rmock, "geonetwork.xml", path="catalog.xml")
|
|
588
588
|
org = OrganizationFactory()
|
|
589
589
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
590
|
-
actions.run(source
|
|
590
|
+
actions.run(source)
|
|
591
591
|
dataset = Dataset.objects.filter(organization=org).first()
|
|
592
592
|
assert dataset is not None
|
|
593
593
|
assert dataset.harvest is not None
|
|
@@ -616,7 +616,7 @@ class DcatBackendTest:
|
|
|
616
616
|
url = mock_dcat(rmock, "sig.oreme.rdf")
|
|
617
617
|
org = OrganizationFactory()
|
|
618
618
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
619
|
-
actions.run(source
|
|
619
|
+
actions.run(source)
|
|
620
620
|
dataset = Dataset.objects.filter(organization=org).first()
|
|
621
621
|
|
|
622
622
|
assert dataset is not None
|
|
@@ -646,7 +646,7 @@ class DcatBackendTest:
|
|
|
646
646
|
url = mock_dcat(rmock, "udata.xml")
|
|
647
647
|
org = OrganizationFactory()
|
|
648
648
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
649
|
-
actions.run(source
|
|
649
|
+
actions.run(source)
|
|
650
650
|
|
|
651
651
|
source.reload()
|
|
652
652
|
job = source.get_last_job()
|
|
@@ -712,7 +712,7 @@ class DcatBackendTest:
|
|
|
712
712
|
get_mock = rmock.get(url)
|
|
713
713
|
org = OrganizationFactory()
|
|
714
714
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
715
|
-
actions.run(source
|
|
715
|
+
actions.run(source)
|
|
716
716
|
|
|
717
717
|
assert "User-Agent" in get_mock.last_request.headers
|
|
718
718
|
assert get_mock.last_request.headers["User-Agent"] == "uData/0.1 dcat"
|
|
@@ -723,7 +723,7 @@ class DcatBackendTest:
|
|
|
723
723
|
org = OrganizationFactory()
|
|
724
724
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
725
725
|
|
|
726
|
-
actions.run(source
|
|
726
|
+
actions.run(source)
|
|
727
727
|
|
|
728
728
|
source.reload()
|
|
729
729
|
|
|
@@ -741,7 +741,7 @@ class DcatBackendTest:
|
|
|
741
741
|
org = OrganizationFactory()
|
|
742
742
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
743
743
|
|
|
744
|
-
actions.run(source
|
|
744
|
+
actions.run(source)
|
|
745
745
|
|
|
746
746
|
source.reload()
|
|
747
747
|
|
|
@@ -773,7 +773,7 @@ class DcatBackendTest:
|
|
|
773
773
|
rmock.head(url, headers={"Content-Type": "application/json"})
|
|
774
774
|
org = OrganizationFactory()
|
|
775
775
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
776
|
-
actions.run(source
|
|
776
|
+
actions.run(source)
|
|
777
777
|
|
|
778
778
|
source.reload()
|
|
779
779
|
|
|
@@ -787,7 +787,7 @@ class DcatBackendTest:
|
|
|
787
787
|
rmock.get(url, status_code=404)
|
|
788
788
|
|
|
789
789
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=OrganizationFactory())
|
|
790
|
-
actions.run(source
|
|
790
|
+
actions.run(source)
|
|
791
791
|
source.reload()
|
|
792
792
|
|
|
793
793
|
job = source.get_last_job()
|
|
@@ -800,7 +800,7 @@ class DcatBackendTest:
|
|
|
800
800
|
rmock.head(url, status_code=404)
|
|
801
801
|
|
|
802
802
|
source = HarvestSourceFactory(backend="dcat", url=url, organization=OrganizationFactory())
|
|
803
|
-
actions.run(source
|
|
803
|
+
actions.run(source)
|
|
804
804
|
source.reload()
|
|
805
805
|
|
|
806
806
|
job = source.get_last_job()
|
|
@@ -817,7 +817,7 @@ class CswDcatBackendTest:
|
|
|
817
817
|
org = OrganizationFactory()
|
|
818
818
|
source = HarvestSourceFactory(backend="csw-dcat", url=url, organization=org)
|
|
819
819
|
|
|
820
|
-
actions.run(source
|
|
820
|
+
actions.run(source)
|
|
821
821
|
|
|
822
822
|
source.reload()
|
|
823
823
|
|
|
@@ -863,7 +863,7 @@ class CswDcatBackendTest:
|
|
|
863
863
|
org = OrganizationFactory()
|
|
864
864
|
source = HarvestSourceFactory(backend="csw-dcat", url=url, organization=org)
|
|
865
865
|
|
|
866
|
-
actions.run(source
|
|
866
|
+
actions.run(source)
|
|
867
867
|
|
|
868
868
|
assert "User-Agent" in get_mock.last_request.headers
|
|
869
869
|
assert get_mock.last_request.headers["User-Agent"] == "uData/0.1 csw-dcat"
|
|
@@ -902,7 +902,7 @@ class CswIso19139DcatBackendTest:
|
|
|
902
902
|
},
|
|
903
903
|
)
|
|
904
904
|
|
|
905
|
-
actions.run(source
|
|
905
|
+
actions.run(source)
|
|
906
906
|
|
|
907
907
|
source.reload()
|
|
908
908
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This migration removes legacy harvest dynamic fields
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from mongoengine.connection import get_db
|
|
8
|
+
|
|
9
|
+
log = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def migrate(db):
|
|
13
|
+
# Remove legacy fields (`ods_has_records`, `ods_url`, ...) from old harvested datasets and resources
|
|
14
|
+
dataset_legacy_fields = ["ods_has_records", "ods_url", "ods_geo"]
|
|
15
|
+
for field in dataset_legacy_fields:
|
|
16
|
+
result = get_db().dataset.update_many({}, {"$unset": {f"harvest.{field}": 1}})
|
|
17
|
+
log.info(
|
|
18
|
+
f"Harvest Dataset dynamic legacy fields ({field}) removed from {result.modified_count} objects"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
resource_legacy_fields = ["ods_type"]
|
|
22
|
+
for field in resource_legacy_fields:
|
|
23
|
+
result = get_db().dataset.update_many(
|
|
24
|
+
{"resources": {"$exists": True, "$type": "array"}},
|
|
25
|
+
{"$unset": {f"resources.$[].harvest.{field}": 1}},
|
|
26
|
+
)
|
|
27
|
+
log.info(
|
|
28
|
+
f"Harvest Resource dynamic legacy fields ({field}) removed from {result.modified_count} objects"
|
|
29
|
+
)
|
udata/mongo/slug_fields.py
CHANGED
|
@@ -172,21 +172,38 @@ def populate_slug(instance, field):
|
|
|
172
172
|
# Ensure uniqueness
|
|
173
173
|
if field.unique:
|
|
174
174
|
base_slug = slug
|
|
175
|
-
|
|
176
|
-
qs = instance.__class__.objects
|
|
175
|
+
qs = instance.__class__.objects.no_cache()
|
|
177
176
|
if previous:
|
|
178
177
|
qs = qs(id__ne=previous.id)
|
|
179
178
|
|
|
180
|
-
def exists(
|
|
181
|
-
return qs(**{field.db_field:
|
|
179
|
+
def exists(slug):
|
|
180
|
+
return qs(**{field.db_field: slug}).clear_cls_query().limit(1).count(True) > 0
|
|
182
181
|
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
def get_existing_slug_suffixes(slug):
|
|
183
|
+
qs_suffix = qs(slug__regex=f"^{slug}-\d*$").clear_cls_query().only(field.db_field)
|
|
184
|
+
return [getattr(obj, field.db_field) for obj in qs_suffix]
|
|
185
|
+
|
|
186
|
+
def trim_base_slug(base_slug, index):
|
|
185
187
|
slug_overflow = len("{0}-{1}".format(base_slug, index)) - field.max_length
|
|
186
188
|
if slug_overflow >= 1:
|
|
187
189
|
base_slug = base_slug[:-slug_overflow]
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
return base_slug
|
|
191
|
+
|
|
192
|
+
if exists(base_slug):
|
|
193
|
+
# We'll iterate to get the first free slug suffix
|
|
194
|
+
index = 1
|
|
195
|
+
existing_slugs = None
|
|
196
|
+
while True:
|
|
197
|
+
# Keep space for index suffix, trim slug if needed
|
|
198
|
+
trimmed_slug = trim_base_slug(base_slug, index)
|
|
199
|
+
# Find all existing slugs with suffixes
|
|
200
|
+
if existing_slugs is None or trimmed_slug != base_slug:
|
|
201
|
+
base_slug = trimmed_slug
|
|
202
|
+
existing_slugs = set(sorted(get_existing_slug_suffixes(base_slug)))
|
|
203
|
+
slug = "{0}-{1}".format(base_slug, index)
|
|
204
|
+
if slug not in existing_slugs:
|
|
205
|
+
break
|
|
206
|
+
index += 1
|
|
190
207
|
|
|
191
208
|
if is_uuid(slug):
|
|
192
209
|
slug = "{0}-uuid".format(slug)
|
udata/routing.py
CHANGED
|
@@ -10,6 +10,7 @@ from werkzeug.urls import url_quote
|
|
|
10
10
|
from udata import models
|
|
11
11
|
from udata.core.dataservices.models import Dataservice
|
|
12
12
|
from udata.core.spatial.models import GeoZone
|
|
13
|
+
from udata.harvest.models import HarvestSource
|
|
13
14
|
from udata.i18n import ISO_639_1_CODES
|
|
14
15
|
from udata.mongo import db
|
|
15
16
|
from udata.uris import cdata_url, homepage_url
|
|
@@ -136,6 +137,10 @@ class DataserviceConverter(ModelConverter):
|
|
|
136
137
|
model = Dataservice
|
|
137
138
|
|
|
138
139
|
|
|
140
|
+
class HarvestSourceConverter(ModelConverter):
|
|
141
|
+
model = HarvestSource
|
|
142
|
+
|
|
143
|
+
|
|
139
144
|
class CommunityResourceConverter(ModelConverter):
|
|
140
145
|
model = models.CommunityResource
|
|
141
146
|
|
|
@@ -239,6 +244,7 @@ def init_app(app):
|
|
|
239
244
|
app.url_map.converters["dataset"] = DatasetConverter
|
|
240
245
|
app.url_map.converters["dataset_without_resources"] = DatasetWithoutResourcesConverter
|
|
241
246
|
app.url_map.converters["dataservice"] = DataserviceConverter
|
|
247
|
+
app.url_map.converters["harvest_source"] = HarvestSourceConverter
|
|
242
248
|
app.url_map.converters["crid"] = CommunityResourceConverter
|
|
243
249
|
app.url_map.converters["org"] = OrganizationConverter
|
|
244
250
|
app.url_map.converters["reuse"] = ReuseConverter
|