udata 10.1.3.dev34359__py2.py3-none-any.whl → 10.1.4__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/dataset/api.py +15 -1
- udata/core/dataset/apiv2.py +26 -4
- udata/core/dataset/models.py +40 -3
- udata/core/user/models.py +1 -1
- udata/core/user/tasks.py +6 -6
- udata/harvest/tests/dcat/udata.xml +180 -0
- udata/harvest/tests/test_dcat_backend.py +65 -0
- udata/rdf.py +6 -1
- udata/routing.py +13 -2
- udata/settings.py +4 -0
- udata/static/chunks/{11.51d706fb9521c16976bc.js → 11.55ab79044cda0271b595.js} +3 -3
- udata/static/chunks/{11.51d706fb9521c16976bc.js.map → 11.55ab79044cda0271b595.js.map} +1 -1
- udata/static/chunks/{13.f29411b06be1883356a3.js → 13.2d06442dd9a05d9777b5.js} +2 -2
- udata/static/chunks/{13.f29411b06be1883356a3.js.map → 13.2d06442dd9a05d9777b5.js.map} +1 -1
- udata/static/chunks/{17.3bd0340930d4a314ce9c.js → 17.e8e4caaad5cb0cc0bacc.js} +2 -2
- udata/static/chunks/{17.3bd0340930d4a314ce9c.js.map → 17.e8e4caaad5cb0cc0bacc.js.map} +1 -1
- udata/static/chunks/{19.8da42e8359d72afc2618.js → 19.f03a102365af4315f9db.js} +3 -3
- udata/static/chunks/{19.8da42e8359d72afc2618.js.map → 19.f03a102365af4315f9db.js.map} +1 -1
- udata/static/chunks/{5.0fa1408dae4e76b87b2e.js → 5.5660483641193b7f8295.js} +3 -3
- udata/static/chunks/{5.0fa1408dae4e76b87b2e.js.map → 5.5660483641193b7f8295.js.map} +1 -1
- udata/static/chunks/{6.d663709d877baa44a71e.js → 6.30dce49d17db07600b06.js} +3 -3
- udata/static/chunks/{6.d663709d877baa44a71e.js.map → 6.30dce49d17db07600b06.js.map} +1 -1
- udata/static/chunks/{8.54e44b102164ae5e7a67.js → 8.b58fcd977fcaf3415571.js} +2 -2
- udata/static/chunks/{8.54e44b102164ae5e7a67.js.map → 8.b58fcd977fcaf3415571.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/templates/mail/account_inactivity.html +1 -1
- udata/templates/mail/account_inactivity.txt +1 -1
- udata/tests/api/test_datasets_api.py +49 -8
- udata/tests/apiv2/test_datasets.py +21 -0
- udata/tests/dataset/test_dataset_model.py +25 -0
- udata/tests/user/test_user_tasks.py +7 -7
- {udata-10.1.3.dev34359.dist-info → udata-10.1.4.dist-info}/METADATA +10 -2
- {udata-10.1.3.dev34359.dist-info → udata-10.1.4.dist-info}/RECORD +39 -38
- {udata-10.1.3.dev34359.dist-info → udata-10.1.4.dist-info}/LICENSE +0 -0
- {udata-10.1.3.dev34359.dist-info → udata-10.1.4.dist-info}/WHEEL +0 -0
- {udata-10.1.3.dev34359.dist-info → udata-10.1.4.dist-info}/entry_points.txt +0 -0
- {udata-10.1.3.dev34359.dist-info → udata-10.1.4.dist-info}/top_level.txt +0 -0
udata/__init__.py
CHANGED
udata/core/dataset/api.py
CHANGED
|
@@ -402,7 +402,21 @@ class ResourcesAPI(API):
|
|
|
402
402
|
def put(self, dataset):
|
|
403
403
|
"""Reorder resources"""
|
|
404
404
|
ResourceEditPermission(dataset).test()
|
|
405
|
-
|
|
405
|
+
resources = request.json
|
|
406
|
+
if len(dataset.resources) != len(resources):
|
|
407
|
+
api.abort(
|
|
408
|
+
400,
|
|
409
|
+
f"All resources must be reordered, you provided {len(resources)} "
|
|
410
|
+
f"out of {len(dataset.resources)}",
|
|
411
|
+
)
|
|
412
|
+
if set(r["id"] if isinstance(r, dict) else r for r in resources) != set(
|
|
413
|
+
str(r.id) for r in dataset.resources
|
|
414
|
+
):
|
|
415
|
+
api.abort(
|
|
416
|
+
400,
|
|
417
|
+
f"Resource ids must match existing ones in dataset, ie: {set(str(r.id) for r in dataset.resources)}",
|
|
418
|
+
)
|
|
419
|
+
data = {"resources": resources}
|
|
406
420
|
form = ResourcesListForm.from_json(
|
|
407
421
|
data, obj=dataset, instance=dataset, meta={"csrf": False}
|
|
408
422
|
)
|
udata/core/dataset/apiv2.py
CHANGED
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
import mongoengine
|
|
4
4
|
from flask import abort, request, url_for
|
|
5
|
+
from flask_login import current_user
|
|
5
6
|
from flask_restx import marshal
|
|
6
7
|
|
|
7
8
|
from udata import search
|
|
@@ -11,7 +12,7 @@ from udata.core.organization.api_fields import member_user_with_email_fields
|
|
|
11
12
|
from udata.core.spatial.api_fields import geojson
|
|
12
13
|
from udata.utils import get_by
|
|
13
14
|
|
|
14
|
-
from .api import ResourceMixin
|
|
15
|
+
from .api import DEFAULT_SORTING, DatasetApiParser, ResourceMixin
|
|
15
16
|
from .api_fields import (
|
|
16
17
|
badge_fields,
|
|
17
18
|
catalog_schema_fields,
|
|
@@ -128,7 +129,7 @@ dataset_fields = apiv2.model(
|
|
|
128
129
|
_external=True,
|
|
129
130
|
),
|
|
130
131
|
"type": "GET",
|
|
131
|
-
"total":
|
|
132
|
+
"total": o.resources_len, # :ResourcesLengthProperty may call MongoDB to fetch the length if resources were not fetched
|
|
132
133
|
},
|
|
133
134
|
description="Link to the dataset resources",
|
|
134
135
|
),
|
|
@@ -155,7 +156,7 @@ dataset_fields = apiv2.model(
|
|
|
155
156
|
),
|
|
156
157
|
"frequency_date": fields.ISODateTime(
|
|
157
158
|
description=(
|
|
158
|
-
"Next expected update date, you will be notified
|
|
159
|
+
"Next expected update date, you will be notified once that date is reached."
|
|
159
160
|
)
|
|
160
161
|
),
|
|
161
162
|
"harvest": fields.Nested(
|
|
@@ -276,7 +277,28 @@ class DatasetSearchAPI(API):
|
|
|
276
277
|
abort(500, "Internal search service error")
|
|
277
278
|
|
|
278
279
|
|
|
279
|
-
|
|
280
|
+
dataset_parser = DatasetApiParser()
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
@ns.route("/", endpoint="datasets")
|
|
284
|
+
class DatasetListAPI(API):
|
|
285
|
+
"""Datasets collection endpoint"""
|
|
286
|
+
|
|
287
|
+
@apiv2.doc("list_datasets")
|
|
288
|
+
@apiv2.expect(dataset_parser.parser)
|
|
289
|
+
@apiv2.marshal_with(dataset_page_fields)
|
|
290
|
+
def get(self):
|
|
291
|
+
"""List or search all datasets"""
|
|
292
|
+
args = dataset_parser.parse()
|
|
293
|
+
datasets = Dataset.objects.exclude("resources").visible_by_user(
|
|
294
|
+
current_user, mongoengine.Q(private__ne=True, archived=None, deleted=None)
|
|
295
|
+
)
|
|
296
|
+
datasets = dataset_parser.parse_filters(datasets, args)
|
|
297
|
+
sort = args["sort"] or ("$text_score" if args["q"] else None) or DEFAULT_SORTING
|
|
298
|
+
return datasets.order_by(sort).paginate(args["page"], args["page_size"])
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@ns.route("/<dataset_without_resources:dataset>/", endpoint="dataset", doc=common_doc)
|
|
280
302
|
@apiv2.response(404, "Dataset not found")
|
|
281
303
|
@apiv2.response(410, "Dataset has been deleted")
|
|
282
304
|
class DatasetAPI(API):
|
udata/core/dataset/models.py
CHANGED
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
import re
|
|
3
3
|
from datetime import datetime, timedelta
|
|
4
4
|
from pydoc import locate
|
|
5
|
+
from typing import Self
|
|
5
6
|
from urllib.parse import urlparse
|
|
6
7
|
|
|
7
8
|
import requests
|
|
@@ -11,7 +12,7 @@ from flask import current_app
|
|
|
11
12
|
from mongoengine import DynamicEmbeddedDocument
|
|
12
13
|
from mongoengine import ValidationError as MongoEngineValidationError
|
|
13
14
|
from mongoengine.fields import DateTimeField
|
|
14
|
-
from mongoengine.signals import post_save, pre_save
|
|
15
|
+
from mongoengine.signals import post_save, pre_init, pre_save
|
|
15
16
|
from stringdist import rdlevenshtein
|
|
16
17
|
from werkzeug.utils import cached_property
|
|
17
18
|
|
|
@@ -617,6 +618,33 @@ class Dataset(WithMetrics, DatasetBadgeMixin, Owned, db.Document):
|
|
|
617
618
|
|
|
618
619
|
verbose_name = _("dataset")
|
|
619
620
|
|
|
621
|
+
missing_resources = False
|
|
622
|
+
|
|
623
|
+
@cached_property
|
|
624
|
+
def resources_len(self):
|
|
625
|
+
# :ResourcesLengthProperty
|
|
626
|
+
# If we've excluded the resources from the Mongo request we need
|
|
627
|
+
# to do a new Mongo request to fetch the resources length manually.
|
|
628
|
+
# If the resources are already present, just return the `len()` of the array.
|
|
629
|
+
if not self.missing_resources:
|
|
630
|
+
return len(self.resources)
|
|
631
|
+
|
|
632
|
+
pipeline = [
|
|
633
|
+
{"$project": {"_id": 1, "resources_len": {"$size": {"$ifNull": ["$resources", []]}}}}
|
|
634
|
+
]
|
|
635
|
+
data = Dataset.objects(id=self.id).aggregate(pipeline)
|
|
636
|
+
|
|
637
|
+
return next(data)["resources_len"]
|
|
638
|
+
|
|
639
|
+
@classmethod
|
|
640
|
+
def pre_init(cls, sender, document: Self, values, **kwargs):
|
|
641
|
+
# MongoEngine loses the information about raw values during the __init__ function
|
|
642
|
+
# Here we catch the raw values from the database (or from the creation of the object)
|
|
643
|
+
# and we check if resources were returned (sometimes we exclude `resources` from the
|
|
644
|
+
# Mongo request to improve perfs)
|
|
645
|
+
# This is used in :ResourcesLengthProperty
|
|
646
|
+
document.missing_resources = "resources" not in values
|
|
647
|
+
|
|
620
648
|
@classmethod
|
|
621
649
|
def pre_save(cls, sender, document, **kwargs):
|
|
622
650
|
cls.before_save.send(document)
|
|
@@ -941,9 +969,17 @@ class Dataset(WithMetrics, DatasetBadgeMixin, Owned, db.Document):
|
|
|
941
969
|
"url": endpoint_for("datasets.show", "api.dataset", dataset=self, _external=True),
|
|
942
970
|
"name": self.title,
|
|
943
971
|
"keywords": ",".join(self.tags),
|
|
944
|
-
"distribution": [
|
|
972
|
+
"distribution": [
|
|
973
|
+
resource.json_ld
|
|
974
|
+
for resource in self.resources[: current_app.config["MAX_RESOURCES_IN_JSON_LD"]]
|
|
975
|
+
],
|
|
945
976
|
# Theses values are not standard
|
|
946
|
-
"contributedDistribution": [
|
|
977
|
+
"contributedDistribution": [
|
|
978
|
+
resource.json_ld
|
|
979
|
+
for resource in self.community_resources[
|
|
980
|
+
: current_app.config["MAX_RESOURCES_IN_JSON_LD"]
|
|
981
|
+
]
|
|
982
|
+
],
|
|
947
983
|
"extras": [get_json_ld_extra(*item) for item in self.extras.items()],
|
|
948
984
|
}
|
|
949
985
|
|
|
@@ -995,6 +1031,7 @@ class Dataset(WithMetrics, DatasetBadgeMixin, Owned, db.Document):
|
|
|
995
1031
|
self.save()
|
|
996
1032
|
|
|
997
1033
|
|
|
1034
|
+
pre_init.connect(Dataset.pre_init, sender=Dataset)
|
|
998
1035
|
pre_save.connect(Dataset.pre_save, sender=Dataset)
|
|
999
1036
|
post_save.connect(Dataset.post_save, sender=Dataset)
|
|
1000
1037
|
|
udata/core/user/models.py
CHANGED
|
@@ -83,7 +83,7 @@ class User(WithMetrics, UserMixin, db.Document):
|
|
|
83
83
|
extras = db.ExtrasField()
|
|
84
84
|
|
|
85
85
|
# Used to track notification for automatic inactive users deletion
|
|
86
|
-
# when
|
|
86
|
+
# when YEARS_OF_INACTIVITY_BEFORE_DELETION is set
|
|
87
87
|
inactive_deletion_notified_at = db.DateTimeField()
|
|
88
88
|
|
|
89
89
|
before_save = Signal()
|
udata/core/user/tasks.py
CHANGED
|
@@ -21,14 +21,14 @@ def send_test_mail(email):
|
|
|
21
21
|
|
|
22
22
|
@job("notify-inactive-users")
|
|
23
23
|
def notify_inactive_users(self):
|
|
24
|
-
if not current_app.config["
|
|
24
|
+
if not current_app.config["YEARS_OF_INACTIVITY_BEFORE_DELETION"]:
|
|
25
25
|
logging.warning(
|
|
26
|
-
"
|
|
26
|
+
"YEARS_OF_INACTIVITY_BEFORE_DELETION setting is not set, no deletion planned"
|
|
27
27
|
)
|
|
28
28
|
return
|
|
29
29
|
notification_comparison_date = (
|
|
30
30
|
datetime.utcnow()
|
|
31
|
-
- timedelta(days=current_app.config["
|
|
31
|
+
- timedelta(days=current_app.config["YEARS_OF_INACTIVITY_BEFORE_DELETION"] * 365)
|
|
32
32
|
+ timedelta(days=current_app.config["DAYS_BEFORE_ACCOUNT_INACTIVITY_NOTIFY_DELAY"])
|
|
33
33
|
)
|
|
34
34
|
|
|
@@ -56,9 +56,9 @@ def notify_inactive_users(self):
|
|
|
56
56
|
|
|
57
57
|
@job("delete-inactive-users")
|
|
58
58
|
def delete_inactive_users(self):
|
|
59
|
-
if not current_app.config["
|
|
59
|
+
if not current_app.config["YEARS_OF_INACTIVITY_BEFORE_DELETION"]:
|
|
60
60
|
logging.warning(
|
|
61
|
-
"
|
|
61
|
+
"YEARS_OF_INACTIVITY_BEFORE_DELETION setting is not set, no deletion planned"
|
|
62
62
|
)
|
|
63
63
|
return
|
|
64
64
|
|
|
@@ -70,7 +70,7 @@ def delete_inactive_users(self):
|
|
|
70
70
|
|
|
71
71
|
# Delete inactive users upon notification delay if user still hasn't logged in
|
|
72
72
|
deletion_comparison_date = datetime.utcnow() - timedelta(
|
|
73
|
-
days=current_app.config["
|
|
73
|
+
days=current_app.config["YEARS_OF_INACTIVITY_BEFORE_DELETION"] * 365
|
|
74
74
|
)
|
|
75
75
|
notified_at = datetime.utcnow() - timedelta(
|
|
76
76
|
days=current_app.config["DAYS_BEFORE_ACCOUNT_INACTIVITY_NOTIFY_DELAY"]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<rdf:RDF
|
|
3
|
+
xmlns:adms="http://www.w3.org/ns/adms#"
|
|
4
|
+
xmlns:dcat="http://www.w3.org/ns/dcat#"
|
|
5
|
+
xmlns:dct="http://purl.org/dc/terms/"
|
|
6
|
+
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
|
7
|
+
xmlns:hydra="http://www.w3.org/ns/hydra/core#"
|
|
8
|
+
xmlns:ns1="http://data.europa.eu/930/"
|
|
9
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
10
|
+
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
|
11
|
+
xmlns:skos="http://www.w3.org/2004/02/skos/core#"
|
|
12
|
+
xmlns:vcard="http://www.w3.org/2006/vcard/ns#"
|
|
13
|
+
>
|
|
14
|
+
<rdf:Description rdf:about="https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/">
|
|
15
|
+
<dct:identifier>https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/</dct:identifier>
|
|
16
|
+
<adms:identifier rdf:nodeID="N7cbef9d8b574438f86f859e81e04e4c0"/>
|
|
17
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Dataset"/>
|
|
18
|
+
<dct:title>Bureaux de vote - Vanves</dct:title>
|
|
19
|
+
<dct:description>La liste des 23 bureaux de vote à Vanves</dct:description>
|
|
20
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:issued>
|
|
21
|
+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:modified>
|
|
22
|
+
<dcat:landingPage rdf:resource="https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/"/>
|
|
23
|
+
<dcat:keyword>administration-finances-publiques</dcat:keyword>
|
|
24
|
+
<dcat:keyword>bureaux-de-vote</dcat:keyword>
|
|
25
|
+
<dcat:keyword>elections</dcat:keyword>
|
|
26
|
+
<dcat:distribution rdf:resource="https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/#resource-5dd4e0b2-4d96-4f36-b73e-b78ec993703c"/>
|
|
27
|
+
<dcat:distribution rdf:resource="https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/#resource-0f80d285-72f8-49f8-a691-1dad6bd2f6db"/>
|
|
28
|
+
<dcat:distribution rdf:resource="https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/#resource-d78d1245-6e8e-44d8-88cd-87b1dd66034f"/>
|
|
29
|
+
<dcat:distribution rdf:resource="https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/#resource-b18fb2bd-6c8b-47a8-84fb-cdc8e29f4f1a"/>
|
|
30
|
+
<ns1:distributor rdf:resource="https://www.data.gouv.fr/organizations/54884a24c751df7226a3fc16/"/>
|
|
31
|
+
<dct:publisher rdf:resource="https://www.data.gouv.fr/api/1/contacts/67b7c3b0e2714e0e9de260a3/"/>
|
|
32
|
+
<dct:creator rdf:resource="https://www.data.gouv.fr/api/1/contacts/67b7c3b0e2714e0e9de260a4/"/>
|
|
33
|
+
</rdf:Description>
|
|
34
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/#resource-5dd4e0b2-4d96-4f36-b73e-b78ec993703c">
|
|
35
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Distribution"/>
|
|
36
|
+
<dct:identifier>5dd4e0b2-4d96-4f36-b73e-b78ec993703c</dct:identifier>
|
|
37
|
+
<dct:title>bureau-de-vote-vanves.csv</dct:title>
|
|
38
|
+
<dct:description>Bureaux de vote - Vanves (csv)</dct:description>
|
|
39
|
+
<dcat:downloadURL rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/catalog/datasets/bureau-de-vote-vanves/exports/csv?use_labels=true"/>
|
|
40
|
+
<dcat:accessURL rdf:resource="https://www.data.gouv.fr/fr/datasets/r/5dd4e0b2-4d96-4f36-b73e-b78ec993703c"/>
|
|
41
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:issued>
|
|
42
|
+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:modified>
|
|
43
|
+
<dct:rights>License Not Specified</dct:rights>
|
|
44
|
+
<dcat:mediaType>text/csv</dcat:mediaType>
|
|
45
|
+
<dct:format>csv</dct:format>
|
|
46
|
+
</rdf:Description>
|
|
47
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/api/1/organizations/54884a24c751df7226a3fc16/catalog">
|
|
48
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Catalog"/>
|
|
49
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/hydra/core#Collection"/>
|
|
50
|
+
<dct:publisher rdf:resource="https://www.data.gouv.fr/organizations/54884a24c751df7226a3fc16/"/>
|
|
51
|
+
<dct:title>Ville de Vanves</dct:title>
|
|
52
|
+
<dct:description>Ville de Vanves</dct:description>
|
|
53
|
+
<dcat:dataset rdf:resource="https://vanves-seineouest.opendatasoft.com/explore/dataset/vfe_public_219200755_20240506/"/>
|
|
54
|
+
<dcat:dataset rdf:resource="https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/"/>
|
|
55
|
+
<dcat:service rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/"/>
|
|
56
|
+
<hydra:totalItems rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">2</hydra:totalItems>
|
|
57
|
+
<hydra:view rdf:resource="https://www.data.gouv.fr/api/1/organizations/54884a24c751df7226a3fc16/catalog.rdf?page=1&page_size=100"/>
|
|
58
|
+
</rdf:Description>
|
|
59
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/#resource-d78d1245-6e8e-44d8-88cd-87b1dd66034f">
|
|
60
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Distribution"/>
|
|
61
|
+
<dct:identifier>d78d1245-6e8e-44d8-88cd-87b1dd66034f</dct:identifier>
|
|
62
|
+
<dct:title>bureau-de-vote-vanves.geojson</dct:title>
|
|
63
|
+
<dct:description>Bureaux de vote - Vanves (geojson)</dct:description>
|
|
64
|
+
<dcat:downloadURL rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/catalog/datasets/bureau-de-vote-vanves/exports/geojson"/>
|
|
65
|
+
<dcat:accessURL rdf:resource="https://www.data.gouv.fr/fr/datasets/r/d78d1245-6e8e-44d8-88cd-87b1dd66034f"/>
|
|
66
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:issued>
|
|
67
|
+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:modified>
|
|
68
|
+
<dct:rights>License Not Specified</dct:rights>
|
|
69
|
+
<dcat:mediaType>application/json</dcat:mediaType>
|
|
70
|
+
<dct:format>json</dct:format>
|
|
71
|
+
</rdf:Description>
|
|
72
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/api/1/organizations/54884a24c751df7226a3fc16/catalog.rdf?page=1&page_size=100">
|
|
73
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/hydra/core#PartialCollectionView"/>
|
|
74
|
+
<hydra:first rdf:resource="https://www.data.gouv.fr/api/1/organizations/54884a24c751df7226a3fc16/catalog.rdf?page=1&page_size=100"/>
|
|
75
|
+
<hydra:last rdf:resource="https://www.data.gouv.fr/api/1/organizations/54884a24c751df7226a3fc16/catalog.rdf?page=1&page_size=100"/>
|
|
76
|
+
</rdf:Description>
|
|
77
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/organizations/54884a24c751df7226a3fc16/">
|
|
78
|
+
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Organization"/>
|
|
79
|
+
<foaf:name>Ville de Vanves</foaf:name>
|
|
80
|
+
<rdfs:label>Ville de Vanves</rdfs:label>
|
|
81
|
+
<foaf:homepage rdf:resource="http://www.vanves.fr"/>
|
|
82
|
+
</rdf:Description>
|
|
83
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/datasets/66397176ff2a31840e29304c/#resource-482677b8-379c-45a5-83ef-0361fecb4cc3">
|
|
84
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Distribution"/>
|
|
85
|
+
<dct:identifier>482677b8-379c-45a5-83ef-0361fecb4cc3</dct:identifier>
|
|
86
|
+
<dct:title>vfe_public_219200755_20240506.json</dct:title>
|
|
87
|
+
<dct:description>Ville de Vanves - Part des véhicules à faibles émissions dans le renouvellement du parc (json)</dct:description>
|
|
88
|
+
<dcat:downloadURL rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/catalog/datasets/vfe_public_219200755_20240506/exports/json"/>
|
|
89
|
+
<dcat:accessURL rdf:resource="https://www.data.gouv.fr/fr/datasets/r/482677b8-379c-45a5-83ef-0361fecb4cc3"/>
|
|
90
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-05-06T15:15:18</dct:issued>
|
|
91
|
+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-05-06T15:15:18</dct:modified>
|
|
92
|
+
<dct:rights>License Not Specified</dct:rights>
|
|
93
|
+
<dcat:mediaType>application/json</dcat:mediaType>
|
|
94
|
+
<dct:format>json</dct:format>
|
|
95
|
+
</rdf:Description>
|
|
96
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/#resource-b18fb2bd-6c8b-47a8-84fb-cdc8e29f4f1a">
|
|
97
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Distribution"/>
|
|
98
|
+
<dct:identifier>b18fb2bd-6c8b-47a8-84fb-cdc8e29f4f1a</dct:identifier>
|
|
99
|
+
<dct:title>bureau-de-vote-vanves.zip</dct:title>
|
|
100
|
+
<dct:description>Bureaux de vote - Vanves (shp)</dct:description>
|
|
101
|
+
<dcat:downloadURL rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/catalog/datasets/bureau-de-vote-vanves/exports/shp"/>
|
|
102
|
+
<dcat:accessURL rdf:resource="https://www.data.gouv.fr/fr/datasets/r/b18fb2bd-6c8b-47a8-84fb-cdc8e29f4f1a"/>
|
|
103
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:issued>
|
|
104
|
+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:modified>
|
|
105
|
+
<dct:rights>License Not Specified</dct:rights>
|
|
106
|
+
<dcat:mediaType>application/zip</dcat:mediaType>
|
|
107
|
+
<dct:format>zip</dct:format>
|
|
108
|
+
</rdf:Description>
|
|
109
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/datasets/66397176ff2a31840e29304c/#resource-aab4d337-a617-4c18-a045-291d68787a7d">
|
|
110
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Distribution"/>
|
|
111
|
+
<dct:identifier>aab4d337-a617-4c18-a045-291d68787a7d</dct:identifier>
|
|
112
|
+
<dct:title>vfe_public_219200755_20240506.csv</dct:title>
|
|
113
|
+
<dct:description>Ville de Vanves - Part des véhicules à faibles émissions dans le renouvellement du parc (csv)</dct:description>
|
|
114
|
+
<dcat:downloadURL rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/catalog/datasets/vfe_public_219200755_20240506/exports/csv?use_labels=true"/>
|
|
115
|
+
<dcat:accessURL rdf:resource="https://www.data.gouv.fr/fr/datasets/r/aab4d337-a617-4c18-a045-291d68787a7d"/>
|
|
116
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-05-06T15:15:18</dct:issued>
|
|
117
|
+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-05-06T15:15:18</dct:modified>
|
|
118
|
+
<dct:rights>License Not Specified</dct:rights>
|
|
119
|
+
<dcat:mediaType>text/csv</dcat:mediaType>
|
|
120
|
+
<dct:format>csv</dct:format>
|
|
121
|
+
</rdf:Description>
|
|
122
|
+
<rdf:Description rdf:about="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/">
|
|
123
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#DataService"/>
|
|
124
|
+
<dct:identifier>https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/</dct:identifier>
|
|
125
|
+
<dct:title>Explore API v2 https://vanves-seineouest.opendatasoft.com</dct:title>
|
|
126
|
+
<dct:description></dct:description>
|
|
127
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-07-12T00:03:38.764000</dct:issued>
|
|
128
|
+
<dcat:endpointURL rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/"/>
|
|
129
|
+
<dcat:landingPage rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/console"/>
|
|
130
|
+
<dcat:endpointDescription rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/swagger.json"/>
|
|
131
|
+
<dcat:servesDataset rdf:resource="https://vanves-seineouest.opendatasoft.com/explore/dataset/vfe_public_219200755_20240506/"/>
|
|
132
|
+
<dcat:servesDataset rdf:resource="https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/"/>
|
|
133
|
+
</rdf:Description>
|
|
134
|
+
<rdf:Description rdf:nodeID="N25911b8ad0604e9f8de4d7e1965eb81a">
|
|
135
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/adms#Identifier"/>
|
|
136
|
+
<dct:creator>data.gouv.fr</dct:creator>
|
|
137
|
+
<skos:notation>https://www.data.gouv.fr/datasets/66397176ff2a31840e29304c/</skos:notation>
|
|
138
|
+
</rdf:Description>
|
|
139
|
+
<rdf:Description rdf:about="https://vanves-seineouest.opendatasoft.com/explore/dataset/vfe_public_219200755_20240506/">
|
|
140
|
+
<dct:identifier>https://vanves-seineouest.opendatasoft.com/explore/dataset/vfe_public_219200755_20240506/</dct:identifier>
|
|
141
|
+
<adms:identifier rdf:nodeID="N25911b8ad0604e9f8de4d7e1965eb81a"/>
|
|
142
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Dataset"/>
|
|
143
|
+
<dct:title>Ville de Vanves - Part des véhicules à faibles émissions dans le renouvellement du parc</dct:title>
|
|
144
|
+
<dct:description>Ville de Vanves - Part des véhicules à faibles émissions dans le renouvellement du parc</dct:description>
|
|
145
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-05-06T15:15:18</dct:issued>
|
|
146
|
+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2024-05-06T15:15:18</dct:modified>
|
|
147
|
+
<dcat:landingPage rdf:resource="https://vanves-seineouest.opendatasoft.com/explore/dataset/vfe_public_219200755_20240506/"/>
|
|
148
|
+
<dcat:distribution rdf:resource="https://www.data.gouv.fr/datasets/66397176ff2a31840e29304c/#resource-aab4d337-a617-4c18-a045-291d68787a7d"/>
|
|
149
|
+
<dcat:distribution rdf:resource="https://www.data.gouv.fr/datasets/66397176ff2a31840e29304c/#resource-482677b8-379c-45a5-83ef-0361fecb4cc3"/>
|
|
150
|
+
<ns1:distributor rdf:resource="https://www.data.gouv.fr/organizations/54884a24c751df7226a3fc16/"/>
|
|
151
|
+
<dct:publisher rdf:resource="https://www.data.gouv.fr/api/1/contacts/67b7c3b0e2714e0e9de260a3/"/>
|
|
152
|
+
<dct:creator rdf:resource="https://www.data.gouv.fr/api/1/contacts/67b7c3b0e2714e0e9de260a4/"/>
|
|
153
|
+
</rdf:Description>
|
|
154
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/api/1/contacts/67b7c3b0e2714e0e9de260a4/">
|
|
155
|
+
<rdf:type rdf:resource="http://www.w3.org/2006/vcard/ns#Kind"/>
|
|
156
|
+
<vcard:fn>Vanves</vcard:fn>
|
|
157
|
+
</rdf:Description>
|
|
158
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/#resource-0f80d285-72f8-49f8-a691-1dad6bd2f6db">
|
|
159
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/dcat#Distribution"/>
|
|
160
|
+
<dct:identifier>0f80d285-72f8-49f8-a691-1dad6bd2f6db</dct:identifier>
|
|
161
|
+
<dct:title>bureau-de-vote-vanves.json</dct:title>
|
|
162
|
+
<dct:description>Bureaux de vote - Vanves (json)</dct:description>
|
|
163
|
+
<dcat:downloadURL rdf:resource="https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/catalog/datasets/bureau-de-vote-vanves/exports/json"/>
|
|
164
|
+
<dcat:accessURL rdf:resource="https://www.data.gouv.fr/fr/datasets/r/0f80d285-72f8-49f8-a691-1dad6bd2f6db"/>
|
|
165
|
+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:issued>
|
|
166
|
+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2019-04-19T12:21:56</dct:modified>
|
|
167
|
+
<dct:rights>License Not Specified</dct:rights>
|
|
168
|
+
<dcat:mediaType>application/json</dcat:mediaType>
|
|
169
|
+
<dct:format>json</dct:format>
|
|
170
|
+
</rdf:Description>
|
|
171
|
+
<rdf:Description rdf:about="https://www.data.gouv.fr/api/1/contacts/67b7c3b0e2714e0e9de260a3/">
|
|
172
|
+
<rdf:type rdf:resource="http://www.w3.org/2006/vcard/ns#Kind"/>
|
|
173
|
+
<vcard:fn>Vanves</vcard:fn>
|
|
174
|
+
</rdf:Description>
|
|
175
|
+
<rdf:Description rdf:nodeID="N7cbef9d8b574438f86f859e81e04e4c0">
|
|
176
|
+
<rdf:type rdf:resource="http://www.w3.org/ns/adms#Identifier"/>
|
|
177
|
+
<dct:creator>data.gouv.fr</dct:creator>
|
|
178
|
+
<skos:notation>https://www.data.gouv.fr/datasets/61892c9d076a4d62434a1318/</skos:notation>
|
|
179
|
+
</rdf:Description>
|
|
180
|
+
</rdf:RDF>
|
|
@@ -624,6 +624,71 @@ class DcatBackendTest:
|
|
|
624
624
|
) # noqa
|
|
625
625
|
assert dataset.harvest.last_update.date() == date.today()
|
|
626
626
|
|
|
627
|
+
def test_udata_xml_catalog(self, rmock):
|
|
628
|
+
LicenseFactory(id="fr-lo", title="Licence ouverte / Open Licence")
|
|
629
|
+
url = mock_dcat(rmock, "udata.xml")
|
|
630
|
+
org = OrganizationFactory()
|
|
631
|
+
source = HarvestSourceFactory(backend="dcat", url=url, organization=org)
|
|
632
|
+
actions.run(source.slug)
|
|
633
|
+
|
|
634
|
+
source.reload()
|
|
635
|
+
job = source.get_last_job()
|
|
636
|
+
assert len(job.items) == 3
|
|
637
|
+
|
|
638
|
+
assert Dataset.objects.filter(organization=org).count() == 2
|
|
639
|
+
dataset = Dataset.objects.filter(organization=org, title="Bureaux de vote - Vanves").first()
|
|
640
|
+
|
|
641
|
+
assert dataset is not None
|
|
642
|
+
assert "bureaux-de-vote" in dataset.tags # support dcat:keyword
|
|
643
|
+
assert len(dataset.resources) == 4
|
|
644
|
+
assert dataset.description == "La liste des 23 bureaux de vote à Vanves"
|
|
645
|
+
assert dataset.harvest is not None
|
|
646
|
+
assert (
|
|
647
|
+
dataset.harvest.dct_identifier
|
|
648
|
+
== "https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/"
|
|
649
|
+
)
|
|
650
|
+
assert (
|
|
651
|
+
dataset.harvest.remote_id
|
|
652
|
+
== "https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/"
|
|
653
|
+
)
|
|
654
|
+
assert dataset.harvest.created_at.isoformat() == "2019-04-19T12:21:56"
|
|
655
|
+
assert dataset.harvest.modified_at.isoformat() == "2019-04-19T12:21:56"
|
|
656
|
+
assert (
|
|
657
|
+
dataset.harvest.uri
|
|
658
|
+
== "https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/"
|
|
659
|
+
)
|
|
660
|
+
assert (
|
|
661
|
+
dataset.harvest.remote_url
|
|
662
|
+
== "https://vanves-seineouest.opendatasoft.com/explore/dataset/bureau-de-vote-vanves/"
|
|
663
|
+
)
|
|
664
|
+
assert dataset.harvest.last_update.date() == date.today()
|
|
665
|
+
|
|
666
|
+
assert Dataservice.objects(organization=org).count() == 1
|
|
667
|
+
service = Dataservice.objects(organization=org).first()
|
|
668
|
+
|
|
669
|
+
assert service is not None
|
|
670
|
+
assert len(service.datasets) == 2
|
|
671
|
+
assert service.title == "Explore API v2 https://vanves-seineouest.opendatasoft.com"
|
|
672
|
+
assert service.description == ""
|
|
673
|
+
assert (
|
|
674
|
+
service.machine_documentation_url
|
|
675
|
+
== "https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/swagger.json"
|
|
676
|
+
)
|
|
677
|
+
assert (
|
|
678
|
+
service.base_api_url == "https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/"
|
|
679
|
+
)
|
|
680
|
+
assert service.harvest is not None
|
|
681
|
+
assert (
|
|
682
|
+
service.harvest.remote_id
|
|
683
|
+
== "https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/"
|
|
684
|
+
)
|
|
685
|
+
assert service.harvest.created_at.isoformat() == "2024-07-12T00:03:38.764000"
|
|
686
|
+
assert (
|
|
687
|
+
service.harvest.remote_url
|
|
688
|
+
== "https://vanves-seineouest.opendatasoft.com/api/explore/v2.1/console"
|
|
689
|
+
)
|
|
690
|
+
assert service.harvest.last_update.date() == date.today()
|
|
691
|
+
|
|
627
692
|
def test_user_agent_get(self, rmock):
|
|
628
693
|
url = mock_dcat(rmock, "catalog.xml", path="without/extension")
|
|
629
694
|
rmock.head(url, headers={"Content-Type": "application/xml; charset=utf-8"})
|
udata/rdf.py
CHANGED
|
@@ -312,7 +312,12 @@ def themes_from_rdf(rdf):
|
|
|
312
312
|
def contact_points_from_rdf(rdf, prop, role, dataset):
|
|
313
313
|
for contact_point in rdf.objects(prop):
|
|
314
314
|
# Read contact point information
|
|
315
|
-
if
|
|
315
|
+
if isinstance(contact_point, Literal):
|
|
316
|
+
log.warning(f"Found a `Literal` inside {prop}, `foaf:Agent` or `vcard:Kind` expected.")
|
|
317
|
+
name = contact_point.toPython()
|
|
318
|
+
email = None
|
|
319
|
+
contact_form = None
|
|
320
|
+
elif prop == DCAT.contactPoint: # Could be split on the type of contact_point instead
|
|
316
321
|
name = rdf_value(contact_point, VCARD.fn) or ""
|
|
317
322
|
email = (
|
|
318
323
|
rdf_value(contact_point, VCARD.hasEmail)
|
udata/routing.py
CHANGED
|
@@ -77,6 +77,9 @@ class ModelConverter(BaseConverter):
|
|
|
77
77
|
def has_redirected_slug(self):
|
|
78
78
|
return self.has_slug and self.model.slug.follow
|
|
79
79
|
|
|
80
|
+
def get_excludes(self):
|
|
81
|
+
return []
|
|
82
|
+
|
|
80
83
|
def quote(self, value):
|
|
81
84
|
if self.has_slug:
|
|
82
85
|
return self.model.slug.slugify(value)
|
|
@@ -85,13 +88,13 @@ class ModelConverter(BaseConverter):
|
|
|
85
88
|
|
|
86
89
|
def to_python(self, value):
|
|
87
90
|
try:
|
|
88
|
-
return self.model.objects.get_or_404(id=value)
|
|
91
|
+
return self.model.objects.exclude(*self.get_excludes()).get_or_404(id=value)
|
|
89
92
|
except (NotFound, ValidationError):
|
|
90
93
|
pass
|
|
91
94
|
try:
|
|
92
95
|
quoted = self.quote(value)
|
|
93
96
|
query = db.Q(slug=value) | db.Q(slug=quoted)
|
|
94
|
-
obj = self.model.objects(query).get()
|
|
97
|
+
obj = self.model.objects(query).exclude(*self.get_excludes()).get()
|
|
95
98
|
except (InvalidQueryError, self.model.DoesNotExist):
|
|
96
99
|
# If the model doesn't have a slug or matching slug doesn't exist.
|
|
97
100
|
if self.has_redirected_slug:
|
|
@@ -121,6 +124,13 @@ class DatasetConverter(ModelConverter):
|
|
|
121
124
|
model = models.Dataset
|
|
122
125
|
|
|
123
126
|
|
|
127
|
+
class DatasetWithoutResourcesConverter(ModelConverter):
|
|
128
|
+
model = models.Dataset
|
|
129
|
+
|
|
130
|
+
def get_excludes(self):
|
|
131
|
+
return ["resources"]
|
|
132
|
+
|
|
133
|
+
|
|
124
134
|
class DataserviceConverter(ModelConverter):
|
|
125
135
|
model = Dataservice
|
|
126
136
|
|
|
@@ -226,6 +236,7 @@ def init_app(app):
|
|
|
226
236
|
app.url_map.converters["pathlist"] = PathListConverter
|
|
227
237
|
app.url_map.converters["uuid"] = UUIDConverter
|
|
228
238
|
app.url_map.converters["dataset"] = DatasetConverter
|
|
239
|
+
app.url_map.converters["dataset_without_resources"] = DatasetWithoutResourcesConverter
|
|
229
240
|
app.url_map.converters["dataservice"] = DataserviceConverter
|
|
230
241
|
app.url_map.converters["crid"] = CommunityResourceConverter
|
|
231
242
|
app.url_map.converters["org"] = OrganizationConverter
|
udata/settings.py
CHANGED
|
@@ -570,6 +570,10 @@ class Defaults(object):
|
|
|
570
570
|
###########################################################################
|
|
571
571
|
TABULAR_API_DATASERVICE_ID = None
|
|
572
572
|
|
|
573
|
+
# JSON-LD settings
|
|
574
|
+
###########################################################################
|
|
575
|
+
MAX_RESOURCES_IN_JSON_LD = 20
|
|
576
|
+
|
|
573
577
|
|
|
574
578
|
class Testing(object):
|
|
575
579
|
"""Sane values for testing. Should be applied as override"""
|