udata 10.0.8.dev33663__py2.py3-none-any.whl → 10.0.8.dev33704__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/api_fields.py +27 -6
- udata/core/dataservices/rdf.py +21 -0
- udata/core/dataset/rdf.py +14 -0
- udata/core/owned.py +9 -4
- udata/core/reuse/models.py +2 -2
- udata/forms/fields.py +21 -1
- udata/static/chunks/{10.8ca60413647062717b1e.js → 10.471164b2a9fe15614797.js} +3 -3
- udata/static/chunks/{10.8ca60413647062717b1e.js.map → 10.471164b2a9fe15614797.js.map} +1 -1
- udata/static/chunks/{11.b6f741fcc366abfad9c4.js → 11.83535504cd650ea08f65.js} +3 -3
- udata/static/chunks/{11.b6f741fcc366abfad9c4.js.map → 11.83535504cd650ea08f65.js.map} +1 -1
- udata/static/chunks/{13.2d06442dd9a05d9777b5.js → 13.d9c1735d14038b94c17e.js} +2 -2
- udata/static/chunks/{13.2d06442dd9a05d9777b5.js.map → 13.d9c1735d14038b94c17e.js.map} +1 -1
- udata/static/chunks/{17.e8e4caaad5cb0cc0bacc.js → 17.81c57c0dedf812e43013.js} +2 -2
- udata/static/chunks/{17.e8e4caaad5cb0cc0bacc.js.map → 17.81c57c0dedf812e43013.js.map} +1 -1
- udata/static/chunks/{19.f03a102365af4315f9db.js → 19.df16abde17a42033a7f8.js} +3 -3
- udata/static/chunks/{19.f03a102365af4315f9db.js.map → 19.df16abde17a42033a7f8.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/chunks/{9.033d7e190ca9e226a5d0.js → 9.07515e5187f475bce828.js} +3 -3
- udata/static/chunks/{9.033d7e190ca9e226a5d0.js.map → 9.07515e5187f475bce828.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/tests/api/test_dataservices_api.py +17 -0
- udata/tests/api/test_datasets_api.py +17 -0
- udata/tests/api/test_reuses_api.py +1 -1
- udata/tests/dataset/test_dataset_rdf.py +34 -0
- udata/tests/forms/test_publish_as_field.py +7 -5
- udata/tests/test_api_fields.py +2 -2
- {udata-10.0.8.dev33663.dist-info → udata-10.0.8.dev33704.dist-info}/METADATA +3 -1
- {udata-10.0.8.dev33663.dist-info → udata-10.0.8.dev33704.dist-info}/RECORD +34 -34
- {udata-10.0.8.dev33663.dist-info → udata-10.0.8.dev33704.dist-info}/LICENSE +0 -0
- {udata-10.0.8.dev33663.dist-info → udata-10.0.8.dev33704.dist-info}/WHEEL +0 -0
- {udata-10.0.8.dev33663.dist-info → udata-10.0.8.dev33704.dist-info}/entry_points.txt +0 -0
- {udata-10.0.8.dev33663.dist-info → udata-10.0.8.dev33704.dist-info}/top_level.txt +0 -0
udata/api_fields.py
CHANGED
|
@@ -42,7 +42,7 @@ from typing import Any, Callable, Iterable
|
|
|
42
42
|
import flask_restx.fields as restx_fields
|
|
43
43
|
import mongoengine
|
|
44
44
|
import mongoengine.fields as mongo_fields
|
|
45
|
-
from bson import ObjectId
|
|
45
|
+
from bson import DBRef, ObjectId
|
|
46
46
|
from flask_restx.inputs import boolean
|
|
47
47
|
from flask_restx.reqparse import RequestParser
|
|
48
48
|
from flask_storage.mongo import ImageField as FlaskStorageImageField
|
|
@@ -446,7 +446,7 @@ def generate_fields(**kwargs) -> Callable:
|
|
|
446
446
|
if constraint == "objectid" and not ObjectId.is_valid(
|
|
447
447
|
args[filterable["key"]]
|
|
448
448
|
):
|
|
449
|
-
api.abort(400, f
|
|
449
|
+
api.abort(400, f"`{filterable['key']}` must be an identifier")
|
|
450
450
|
|
|
451
451
|
query = filterable.get("query", None)
|
|
452
452
|
if query:
|
|
@@ -534,17 +534,37 @@ def patch(obj, request) -> type:
|
|
|
534
534
|
|
|
535
535
|
info = getattr(model_attribute, "__additional_field_info__", {})
|
|
536
536
|
|
|
537
|
-
# `
|
|
537
|
+
# `checks` field attribute allows to do validation from the request before setting
|
|
538
538
|
# the attribute
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
539
|
+
checks = info.get("checks", [])
|
|
540
|
+
|
|
541
|
+
if is_value_modified(getattr(obj, key), value):
|
|
542
|
+
for check in checks:
|
|
543
|
+
check(
|
|
544
|
+
value,
|
|
545
|
+
**{
|
|
546
|
+
"is_creation": obj._created,
|
|
547
|
+
"is_update": not obj._created,
|
|
548
|
+
"field": key,
|
|
549
|
+
},
|
|
550
|
+
) # TODO add other model attributes in function parameters
|
|
542
551
|
|
|
543
552
|
setattr(obj, key, value)
|
|
544
553
|
|
|
545
554
|
return obj
|
|
546
555
|
|
|
547
556
|
|
|
557
|
+
def is_value_modified(old_value, new_value) -> bool:
|
|
558
|
+
# If we want to modify a reference, the new_value may be a DBRef.
|
|
559
|
+
# `wrap_primary_key` can also return the `foreign_document` (see :WrapToForeignDocument)
|
|
560
|
+
# and it is not currently taken into account here…
|
|
561
|
+
# Maybe we can do another type of check to check if the reference changes in the future…
|
|
562
|
+
if isinstance(new_value, DBRef):
|
|
563
|
+
return not old_value or new_value.id != old_value.id
|
|
564
|
+
|
|
565
|
+
return new_value != old_value
|
|
566
|
+
|
|
567
|
+
|
|
548
568
|
def patch_and_save(obj, request) -> type:
|
|
549
569
|
obj = patch(obj, request)
|
|
550
570
|
|
|
@@ -587,6 +607,7 @@ def wrap_primary_key(
|
|
|
587
607
|
raise FieldValidationError(field=field_name, message=f"Unknown reference '{value}'")
|
|
588
608
|
|
|
589
609
|
# GenericReferenceField only accepts document (not dbref / objectid)
|
|
610
|
+
# :WrapToForeignDocument
|
|
590
611
|
if isinstance(
|
|
591
612
|
foreign_field,
|
|
592
613
|
(
|
udata/core/dataservices/rdf.py
CHANGED
|
@@ -181,3 +181,24 @@ def dataservice_to_rdf(dataservice: Dataservice, graph=None):
|
|
|
181
181
|
d.set(DCAT.contactPoint, contact_point)
|
|
182
182
|
|
|
183
183
|
return d
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def dataservice_as_distribution_to_rdf(
|
|
187
|
+
dataservice: Dataservice, graph: Graph = None, is_hvd: bool = True
|
|
188
|
+
):
|
|
189
|
+
"""
|
|
190
|
+
Create a blank distribution pointing towards a dataservice with DCAT.accessService property
|
|
191
|
+
"""
|
|
192
|
+
id = BNode()
|
|
193
|
+
distribution = graph.resource(id)
|
|
194
|
+
distribution.set(RDF.type, DCAT.Distribution)
|
|
195
|
+
distribution.add(DCT.title, Literal(dataservice.title))
|
|
196
|
+
distribution.add(DCAT.accessURL, URIRef(dataservice.base_api_url))
|
|
197
|
+
|
|
198
|
+
if is_hvd:
|
|
199
|
+
# DCAT-AP HVD applicable legislation is also expected at the distribution level
|
|
200
|
+
distribution.add(DCATAP.applicableLegislation, URIRef(HVD_LEGISLATION))
|
|
201
|
+
|
|
202
|
+
distribution.add(DCAT.accessService, dataservice_to_rdf(dataservice, graph))
|
|
203
|
+
|
|
204
|
+
return distribution
|
udata/core/dataset/rdf.py
CHANGED
|
@@ -340,6 +340,20 @@ def dataset_to_rdf(dataset: Dataset, graph: Optional[Graph] = None) -> RdfResour
|
|
|
340
340
|
for resource in dataset.resources:
|
|
341
341
|
d.add(DCAT.distribution, resource_to_rdf(resource, dataset, graph, is_hvd))
|
|
342
342
|
|
|
343
|
+
if is_hvd:
|
|
344
|
+
from udata.core.dataservices.models import Dataservice
|
|
345
|
+
from udata.core.dataservices.rdf import dataservice_as_distribution_to_rdf
|
|
346
|
+
|
|
347
|
+
# Add a blank distribution pointing to a DataService using the distribution DCAT.accessService.
|
|
348
|
+
# Useful for HVD reporting since DataService are not currently harvested by
|
|
349
|
+
# data.europa.eu as first class entities.
|
|
350
|
+
# Should be removed once supported by data.europa.eu harvesting.
|
|
351
|
+
for service in Dataservice.objects.filter(datasets=dataset, tags="hvd"):
|
|
352
|
+
d.add(
|
|
353
|
+
DCAT.distribution,
|
|
354
|
+
dataservice_as_distribution_to_rdf(service, graph),
|
|
355
|
+
)
|
|
356
|
+
|
|
343
357
|
if dataset.temporal_coverage:
|
|
344
358
|
d.set(DCT.temporal, temporal_to_rdf(dataset.temporal_coverage, graph))
|
|
345
359
|
|
udata/core/owned.py
CHANGED
|
@@ -41,7 +41,12 @@ class OwnedQuerySet(UDataQuerySet):
|
|
|
41
41
|
return self(visible_query | owned_qs._query_obj)
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
def
|
|
44
|
+
def only_creation(_value, is_update, field, **_kwargs):
|
|
45
|
+
if is_update:
|
|
46
|
+
raise FieldValidationError(_(f"Cannot modify {field} after creation"), field=field)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def check_owner_is_current_user(owner, **_kwargs):
|
|
45
50
|
from udata.auth import admin_permission, current_user
|
|
46
51
|
|
|
47
52
|
if (
|
|
@@ -53,7 +58,7 @@ def check_owner_is_current_user(owner):
|
|
|
53
58
|
raise FieldValidationError(_("You can only set yourself as owner"), field="owner")
|
|
54
59
|
|
|
55
60
|
|
|
56
|
-
def check_organization_is_valid_for_current_user(organization):
|
|
61
|
+
def check_organization_is_valid_for_current_user(organization, **_kwargs):
|
|
57
62
|
from udata.auth import current_user
|
|
58
63
|
from udata.models import Organization
|
|
59
64
|
|
|
@@ -76,7 +81,7 @@ class Owned(object):
|
|
|
76
81
|
ReferenceField(User, reverse_delete_rule=NULLIFY),
|
|
77
82
|
nested_fields=user_ref_fields,
|
|
78
83
|
description="Only present if organization is not set. Can only be set to the current authenticated user.",
|
|
79
|
-
|
|
84
|
+
checks=[check_owner_is_current_user, only_creation],
|
|
80
85
|
allow_null=True,
|
|
81
86
|
filterable={},
|
|
82
87
|
)
|
|
@@ -84,7 +89,7 @@ class Owned(object):
|
|
|
84
89
|
ReferenceField(Organization, reverse_delete_rule=NULLIFY),
|
|
85
90
|
nested_fields=org_ref_fields,
|
|
86
91
|
description="Only present if owner is not set. Can only be set to an organization of the current authenticated user.",
|
|
87
|
-
|
|
92
|
+
checks=[check_organization_is_valid_for_current_user, only_creation],
|
|
88
93
|
allow_null=True,
|
|
89
94
|
filterable={},
|
|
90
95
|
)
|
udata/core/reuse/models.py
CHANGED
|
@@ -30,7 +30,7 @@ class ReuseQuerySet(OwnedQuerySet):
|
|
|
30
30
|
return self(db.Q(private=True) | db.Q(datasets__0__exists=False) | db.Q(deleted__ne=None))
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def check_url_does_not_exists(url):
|
|
33
|
+
def check_url_does_not_exists(url, **_kwargs):
|
|
34
34
|
"""Ensure a reuse URL is not yet registered"""
|
|
35
35
|
if url and Reuse.url_exists(url):
|
|
36
36
|
raise FieldValidationError(_("This URL is already registered"), field="url")
|
|
@@ -82,7 +82,7 @@ class Reuse(db.Datetimed, WithMetrics, ReuseBadgeMixin, Owned, db.Document):
|
|
|
82
82
|
url = field(
|
|
83
83
|
db.URLField(required=True),
|
|
84
84
|
description="The remote URL (website)",
|
|
85
|
-
|
|
85
|
+
checks=[check_url_does_not_exists],
|
|
86
86
|
)
|
|
87
87
|
urlhash = db.StringField(required=True, unique=True)
|
|
88
88
|
image_url = db.StringField()
|
udata/forms/fields.py
CHANGED
|
@@ -418,7 +418,7 @@ class TagField(Field):
|
|
|
418
418
|
for tag in self.data:
|
|
419
419
|
if not tags.MIN_TAG_LENGTH <= len(tag) <= tags.MAX_TAG_LENGTH:
|
|
420
420
|
message = _(
|
|
421
|
-
'Tag "%(tag)s" must be between %(min)d
|
|
421
|
+
'Tag "%(tag)s" must be between %(min)d and %(max)d characters long.',
|
|
422
422
|
min=tags.MIN_TAG_LENGTH,
|
|
423
423
|
max=tags.MAX_TAG_LENGTH,
|
|
424
424
|
tag=tag,
|
|
@@ -728,6 +728,16 @@ class CurrentUserField(ModelFieldMixin, Field):
|
|
|
728
728
|
return super(CurrentUserField, self).process(formdata, data, **kwargs)
|
|
729
729
|
|
|
730
730
|
def pre_validate(self, form):
|
|
731
|
+
if (
|
|
732
|
+
isinstance(form, ModelForm) # Some forms (like HarvestSourceForm) are not model forms
|
|
733
|
+
and form.instance
|
|
734
|
+
and self.name in form.instance
|
|
735
|
+
and getattr(form.instance, self.name).id != self.data.id
|
|
736
|
+
):
|
|
737
|
+
raise validators.ValidationError(
|
|
738
|
+
_("Cannot change owner after creation. Please use transfer feature.")
|
|
739
|
+
)
|
|
740
|
+
|
|
731
741
|
if self.data:
|
|
732
742
|
if current_user.is_anonymous:
|
|
733
743
|
raise validators.ValidationError(_("You must be authenticated"))
|
|
@@ -749,6 +759,16 @@ class PublishAsField(ModelFieldMixin, Field):
|
|
|
749
759
|
return len(current_user.organizations) <= 0
|
|
750
760
|
|
|
751
761
|
def pre_validate(self, form):
|
|
762
|
+
if (
|
|
763
|
+
isinstance(form, ModelForm) # Some forms (like HarvestSourceForm) are not model forms
|
|
764
|
+
and form.instance
|
|
765
|
+
and self.name in form.instance
|
|
766
|
+
and getattr(form.instance, self.name).id != self.data.id
|
|
767
|
+
):
|
|
768
|
+
raise validators.ValidationError(
|
|
769
|
+
_("Cannot change owner after creation. Please use transfer feature.")
|
|
770
|
+
)
|
|
771
|
+
|
|
752
772
|
if self.data:
|
|
753
773
|
if not current_user.is_authenticated:
|
|
754
774
|
raise validators.ValidationError(_("You must be authenticated"))
|