udata 9.1.4.dev31191__py2.py3-none-any.whl → 9.1.4.dev31242__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/core/dataservices/api.py +37 -1
- udata/core/dataservices/models.py +38 -0
- udata/core/dataset/events.py +4 -1
- udata/core/organization/api.py +7 -1
- udata/core/organization/rdf.py +4 -1
- udata/core/site/api.py +1 -26
- udata/settings.py +1 -0
- udata/static/chunks/{11.18f1cc16362b76373c40.js → 11.8910c81ce1e1b0f4a71f.js} +3 -3
- udata/static/chunks/{11.18f1cc16362b76373c40.js.map → 11.8910c81ce1e1b0f4a71f.js.map} +1 -1
- udata/static/chunks/{19.2061dca8438f415029a3.js → 19.28f355064e529318c9b1.js} +3 -3
- udata/static/chunks/{19.2061dca8438f415029a3.js.map → 19.28f355064e529318c9b1.js.map} +1 -1
- udata/static/chunks/{5.d17ff266ecc1ad0331fb.js → 5.270f2cb840d87976aecd.js} +3 -3
- udata/static/chunks/{5.d17ff266ecc1ad0331fb.js.map → 5.270f2cb840d87976aecd.js.map} +1 -1
- udata/static/chunks/{6.0db5d3ff22944de7edd5.js → 6.c433506a3004b8f4d5fb.js} +3 -3
- udata/static/chunks/{6.0db5d3ff22944de7edd5.js.map → 6.c433506a3004b8f4d5fb.js.map} +1 -1
- udata/static/chunks/{8.292c6a157d71f0b21b6f.js → 8.4aa47633f437db19feb5.js} +2 -2
- udata/static/chunks/{8.292c6a157d71f0b21b6f.js.map → 8.4aa47633f437db19feb5.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/tests/api/test_dataservices_api.py +59 -0
- udata/tests/dataset/test_dataset_events.py +28 -0
- udata/tests/organization/test_organization_rdf.py +31 -6
- {udata-9.1.4.dev31191.dist-info → udata-9.1.4.dev31242.dist-info}/METADATA +3 -1
- {udata-9.1.4.dev31191.dist-info → udata-9.1.4.dev31242.dist-info}/RECORD +28 -28
- {udata-9.1.4.dev31191.dist-info → udata-9.1.4.dev31242.dist-info}/LICENSE +0 -0
- {udata-9.1.4.dev31191.dist-info → udata-9.1.4.dev31242.dist-info}/WHEEL +0 -0
- {udata-9.1.4.dev31191.dist-info → udata-9.1.4.dev31242.dist-info}/entry_points.txt +0 -0
- {udata-9.1.4.dev31191.dist-info → udata-9.1.4.dev31242.dist-info}/top_level.txt +0 -0
udata/core/dataservices/api.py
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
3
|
import mongoengine
|
|
4
|
-
from flask import request
|
|
4
|
+
from flask import make_response, redirect, request, url_for
|
|
5
5
|
from flask_login import current_user
|
|
6
6
|
|
|
7
7
|
from udata.api import API, api
|
|
8
8
|
from udata.api_fields import patch
|
|
9
9
|
from udata.core.dataset.permissions import OwnablePermission
|
|
10
10
|
from udata.core.followers.api import FollowAPI
|
|
11
|
+
from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
|
|
11
12
|
|
|
12
13
|
from .models import Dataservice
|
|
14
|
+
from .permissions import DataserviceEditPermission
|
|
15
|
+
from .rdf import dataservice_to_rdf
|
|
13
16
|
|
|
14
17
|
ns = api.namespace("dataservices", "Dataservices related operations (beta)")
|
|
15
18
|
|
|
19
|
+
common_doc = {"params": {"dataservice": "The dataservice ID or slug"}}
|
|
20
|
+
|
|
16
21
|
|
|
17
22
|
@ns.route("/", endpoint="dataservices")
|
|
18
23
|
class DataservicesAPI(API):
|
|
@@ -87,6 +92,37 @@ class DataserviceAPI(API):
|
|
|
87
92
|
return "", 204
|
|
88
93
|
|
|
89
94
|
|
|
95
|
+
@ns.route("/<dataservice:dataservice>/rdf", endpoint="dataservice_rdf", doc=common_doc)
|
|
96
|
+
@api.response(404, "Dataservice not found")
|
|
97
|
+
@api.response(410, "Dataservice has been deleted")
|
|
98
|
+
class DataserviceRdfAPI(API):
|
|
99
|
+
@api.doc("rdf_dataservice")
|
|
100
|
+
def get(self, dataservice):
|
|
101
|
+
format = RDF_EXTENSIONS[negociate_content()]
|
|
102
|
+
url = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, format=format)
|
|
103
|
+
return redirect(url)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@ns.route(
|
|
107
|
+
"/<dataservice:dataservice>/rdf.<format>", endpoint="dataservice_rdf_format", doc=common_doc
|
|
108
|
+
)
|
|
109
|
+
@api.response(404, "Dataservice not found")
|
|
110
|
+
@api.response(410, "Dataservice has been deleted")
|
|
111
|
+
class DataserviceRdfFormatAPI(API):
|
|
112
|
+
@api.doc("rdf_dataservice_format")
|
|
113
|
+
def get(self, dataservice, format):
|
|
114
|
+
if not DataserviceEditPermission(dataservice).can():
|
|
115
|
+
if dataservice.private:
|
|
116
|
+
api.abort(404)
|
|
117
|
+
elif dataservice.deleted_at:
|
|
118
|
+
api.abort(410)
|
|
119
|
+
|
|
120
|
+
resource = dataservice_to_rdf(dataservice)
|
|
121
|
+
# bypass flask-restplus make_response, since graph_response
|
|
122
|
+
# is handling the content negociation directly
|
|
123
|
+
return make_response(*graph_response(resource, format))
|
|
124
|
+
|
|
125
|
+
|
|
90
126
|
@ns.route("/<id>/followers/", endpoint="dataservice_followers")
|
|
91
127
|
@ns.doc(
|
|
92
128
|
get={"id": "list_dataservice_followers"},
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
|
+
from mongoengine import Q
|
|
4
|
+
|
|
3
5
|
import udata.core.contact_point.api_fields as contact_api_fields
|
|
4
6
|
import udata.core.dataset.api_fields as datasets_api_fields
|
|
5
7
|
from udata.api_fields import field, function_field, generate_fields
|
|
@@ -29,6 +31,42 @@ class DataserviceQuerySet(OwnedQuerySet):
|
|
|
29
31
|
def hidden(self):
|
|
30
32
|
return self(db.Q(private=True) | db.Q(deleted_at__ne=None) | db.Q(archived_at__ne=None))
|
|
31
33
|
|
|
34
|
+
def filter_by_dataset_pagination(self, datasets: list[Dataset], page: int):
|
|
35
|
+
"""Paginate the dataservices on the datasets provided.
|
|
36
|
+
|
|
37
|
+
This is a workaround, used (at least) in the catalogs for sites and organizations.
|
|
38
|
+
We paginate those kinda weirdly, on their datasets. So a given organization or site
|
|
39
|
+
catalog will only list a `page_size` number of datasets, but we'd still want to display
|
|
40
|
+
the site's or org's dataservices.
|
|
41
|
+
We can't "double paginate", so instead:
|
|
42
|
+
- only if it's the first page, list all the dataservices that serve no dataset
|
|
43
|
+
- list all the dataservices that serve the datasets in this page
|
|
44
|
+
"""
|
|
45
|
+
# We need to add Dataservice to the catalog.
|
|
46
|
+
# In the best world, we want:
|
|
47
|
+
# - Keep the correct number of datasets on the page (if the requested page size is 100, we should have 100 datasets)
|
|
48
|
+
# - Have simple MongoDB queries
|
|
49
|
+
# - Do not duplicate the datasets (each dataset is present once in the catalog)
|
|
50
|
+
# - Do not duplicate the dataservices (each dataservice is present once in the catalog)
|
|
51
|
+
# - Every referenced dataset for one dataservices present on the page (hard to do)
|
|
52
|
+
#
|
|
53
|
+
# Multiple solutions are possible but none check all the constraints.
|
|
54
|
+
# The selected one is to put all the dataservices referencing at least one of the dataset on
|
|
55
|
+
# the page at the end of it. It means dataservices could be duplicated (present on multiple pages)
|
|
56
|
+
# and these dataservices may referenced some datasets not present in the current page. It's working
|
|
57
|
+
# if somebody is doing the same thing as us (keeping the list of all the datasets IDs for the entire catalog then
|
|
58
|
+
# listing all dataservices in a second pass)
|
|
59
|
+
# Another option is to do some tricky Mongo requests to order/group datasets by their presence in some dataservices but
|
|
60
|
+
# it could be really hard to do with a n..n relation.
|
|
61
|
+
# Let's keep this solution simple right now and iterate on it in the future.
|
|
62
|
+
dataservices_filter = Q(datasets__in=[d.id for d in datasets])
|
|
63
|
+
|
|
64
|
+
# On the first page, add all dataservices without datasets
|
|
65
|
+
if page == 1:
|
|
66
|
+
dataservices_filter = dataservices_filter | Q(datasets__size=0)
|
|
67
|
+
|
|
68
|
+
return self(dataservices_filter)
|
|
69
|
+
|
|
32
70
|
|
|
33
71
|
@generate_fields()
|
|
34
72
|
class HarvestMetadata(db.EmbeddedDocument):
|
udata/core/dataset/events.py
CHANGED
|
@@ -49,7 +49,10 @@ def publish(url, document, resource_id, action):
|
|
|
49
49
|
"dataset_id": str(document.id),
|
|
50
50
|
"document": resource,
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
headers = {}
|
|
53
|
+
if current_app.config["RESOURCES_ANALYSER_API_KEY"]:
|
|
54
|
+
headers = {"Authorization": f"Bearer {current_app.config['RESOURCES_ANALYSER_API_KEY']}"}
|
|
55
|
+
r = requests.post(url, json=payload, headers=headers)
|
|
53
56
|
r.raise_for_status()
|
|
54
57
|
|
|
55
58
|
|
udata/core/organization/api.py
CHANGED
|
@@ -10,6 +10,7 @@ from udata.core.badges import api as badges_api
|
|
|
10
10
|
from udata.core.badges.fields import badge_fields
|
|
11
11
|
from udata.core.contact_point.api import ContactPointApiParser
|
|
12
12
|
from udata.core.contact_point.api_fields import contact_point_page_fields
|
|
13
|
+
from udata.core.dataservices.models import Dataservice
|
|
13
14
|
from udata.core.dataset.api import DatasetApiParser
|
|
14
15
|
from udata.core.dataset.api_fields import dataset_page_fields
|
|
15
16
|
from udata.core.dataset.models import Dataset
|
|
@@ -175,7 +176,12 @@ class OrganizationRdfFormatAPI(API):
|
|
|
175
176
|
page = int(params.get("page", 1))
|
|
176
177
|
page_size = int(params.get("page_size", 100))
|
|
177
178
|
datasets = Dataset.objects(organization=org).visible().paginate(page, page_size)
|
|
178
|
-
|
|
179
|
+
dataservices = (
|
|
180
|
+
Dataservice.objects(organization=org)
|
|
181
|
+
.visible()
|
|
182
|
+
.filter_by_dataset_pagination(datasets, page)
|
|
183
|
+
)
|
|
184
|
+
catalog = build_org_catalog(org, datasets, dataservices, format=format)
|
|
179
185
|
# bypass flask-restplus make_response, since graph_response
|
|
180
186
|
# is handling the content negociation directly
|
|
181
187
|
return make_response(*graph_response(catalog, format))
|
udata/core/organization/rdf.py
CHANGED
|
@@ -7,6 +7,7 @@ from flask import url_for
|
|
|
7
7
|
from rdflib import BNode, Graph, Literal, URIRef
|
|
8
8
|
from rdflib.namespace import FOAF, RDF, RDFS
|
|
9
9
|
|
|
10
|
+
from udata.core.dataservices.rdf import dataservice_to_rdf
|
|
10
11
|
from udata.core.dataset.rdf import dataset_to_rdf
|
|
11
12
|
from udata.rdf import DCAT, DCT, namespace_manager, paginate_catalog
|
|
12
13
|
from udata.uris import endpoint_for
|
|
@@ -35,7 +36,7 @@ def organization_to_rdf(org, graph=None):
|
|
|
35
36
|
return o
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def build_org_catalog(org, datasets, format=None):
|
|
39
|
+
def build_org_catalog(org, datasets, dataservices, format=None):
|
|
39
40
|
graph = Graph(namespace_manager=namespace_manager)
|
|
40
41
|
org_catalog_url = url_for("api.organization_rdf", org=org.id, _external=True)
|
|
41
42
|
|
|
@@ -47,6 +48,8 @@ def build_org_catalog(org, datasets, format=None):
|
|
|
47
48
|
|
|
48
49
|
for dataset in datasets:
|
|
49
50
|
catalog.add(DCAT.dataset, dataset_to_rdf(dataset, graph))
|
|
51
|
+
for dataservice in dataservices:
|
|
52
|
+
catalog.add(DCAT.dataservice, dataservice_to_rdf(dataservice, graph))
|
|
50
53
|
|
|
51
54
|
values = {"org": org.id}
|
|
52
55
|
|
udata/core/site/api.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from bson import ObjectId
|
|
2
2
|
from flask import json, make_response, redirect, request, url_for
|
|
3
|
-
from mongoengine import Q
|
|
4
3
|
|
|
5
4
|
from udata.api import API, api, fields
|
|
6
5
|
from udata.auth import admin_permission
|
|
@@ -107,31 +106,7 @@ class SiteRdfCatalogFormat(API):
|
|
|
107
106
|
if "tag" in params:
|
|
108
107
|
datasets = datasets.filter(tags=params.get("tag", ""))
|
|
109
108
|
datasets = datasets.paginate(page, page_size)
|
|
110
|
-
|
|
111
|
-
# We need to add Dataservice to the catalog.
|
|
112
|
-
# In the best world, we want:
|
|
113
|
-
# - Keep the correct number of datasets on the page (if the requested page size is 100, we should have 100 datasets)
|
|
114
|
-
# - Have simple MongoDB queries
|
|
115
|
-
# - Do not duplicate the datasets (each dataset is present once in the catalog)
|
|
116
|
-
# - Do not duplicate the dataservices (each dataservice is present once in the catalog)
|
|
117
|
-
# - Every referenced dataset for one dataservices present on the page (hard to do)
|
|
118
|
-
#
|
|
119
|
-
# Multiple solutions are possible but none check all the constraints.
|
|
120
|
-
# The selected one is to put all the dataservices referencing at least one of the dataset on
|
|
121
|
-
# the page at the end of it. It means dataservices could be duplicated (present on multiple pages)
|
|
122
|
-
# and these dataservices may referenced some datasets not present in the current page. It's working
|
|
123
|
-
# if somebody is doing the same thing as us (keeping the list of all the datasets IDs for the entire catalog then
|
|
124
|
-
# listing all dataservices in a second pass)
|
|
125
|
-
# Another option is to do some tricky Mongo requests to order/group datasets by their presence in some dataservices but
|
|
126
|
-
# it could be really hard to do with a n..n relation.
|
|
127
|
-
# Let's keep this solution simple right now and iterate on it in the future.
|
|
128
|
-
dataservices_filter = Q(datasets__in=[d.id for d in datasets])
|
|
129
|
-
|
|
130
|
-
# On the first page, add all dataservices without datasets
|
|
131
|
-
if page == 1:
|
|
132
|
-
dataservices_filter = dataservices_filter | Q(datasets__size=0)
|
|
133
|
-
|
|
134
|
-
dataservices = Dataservice.objects.visible().filter(dataservices_filter)
|
|
109
|
+
dataservices = Dataservice.objects.visible().filter_by_dataset_pagination(datasets, page)
|
|
135
110
|
|
|
136
111
|
catalog = build_catalog(current_site, datasets, dataservices=dataservices, format=format)
|
|
137
112
|
# bypass flask-restplus make_response, since graph_response
|
udata/settings.py
CHANGED
|
@@ -522,6 +522,7 @@ class Defaults(object):
|
|
|
522
522
|
FIXTURE_DATASET_SLUGS = []
|
|
523
523
|
PUBLISH_ON_RESOURCE_EVENTS = False
|
|
524
524
|
RESOURCES_ANALYSER_URI = "http://localhost:8000"
|
|
525
|
+
RESOURCES_ANALYSER_API_KEY = None
|
|
525
526
|
|
|
526
527
|
# Datasets quality settings
|
|
527
528
|
###########################################################################
|