udata 9.1.4.dev31221__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.

Files changed (25) hide show
  1. udata/core/dataservices/api.py +37 -1
  2. udata/core/dataservices/models.py +38 -0
  3. udata/core/organization/api.py +7 -1
  4. udata/core/organization/rdf.py +4 -1
  5. udata/core/site/api.py +1 -26
  6. udata/static/chunks/{11.16618d9eedd3f6a7a3c2.js → 11.8910c81ce1e1b0f4a71f.js} +3 -3
  7. udata/static/chunks/{11.16618d9eedd3f6a7a3c2.js.map → 11.8910c81ce1e1b0f4a71f.js.map} +1 -1
  8. udata/static/chunks/{13.9cfb8ee33c4d62e33f8a.js → 13.fd1ca1cf5b2c1854ed51.js} +2 -2
  9. udata/static/chunks/{13.9cfb8ee33c4d62e33f8a.js.map → 13.fd1ca1cf5b2c1854ed51.js.map} +1 -1
  10. udata/static/chunks/{16.d1de045f4bc4b5acdf6b.js → 16.6727d8aca9393baa855c.js} +2 -2
  11. udata/static/chunks/{16.d1de045f4bc4b5acdf6b.js.map → 16.6727d8aca9393baa855c.js.map} +1 -1
  12. udata/static/chunks/{19.f1ff6cd5816f2d9debc4.js → 19.28f355064e529318c9b1.js} +3 -3
  13. udata/static/chunks/{19.f1ff6cd5816f2d9debc4.js.map → 19.28f355064e529318c9b1.js.map} +1 -1
  14. udata/static/chunks/{8.b50a30118e9e2e1ab436.js → 8.4aa47633f437db19feb5.js} +2 -2
  15. udata/static/chunks/{8.b50a30118e9e2e1ab436.js.map → 8.4aa47633f437db19feb5.js.map} +1 -1
  16. udata/static/common.js +1 -1
  17. udata/static/common.js.map +1 -1
  18. udata/tests/api/test_dataservices_api.py +59 -0
  19. udata/tests/organization/test_organization_rdf.py +31 -6
  20. {udata-9.1.4.dev31221.dist-info → udata-9.1.4.dev31242.dist-info}/METADATA +2 -1
  21. {udata-9.1.4.dev31221.dist-info → udata-9.1.4.dev31242.dist-info}/RECORD +25 -25
  22. {udata-9.1.4.dev31221.dist-info → udata-9.1.4.dev31242.dist-info}/LICENSE +0 -0
  23. {udata-9.1.4.dev31221.dist-info → udata-9.1.4.dev31242.dist-info}/WHEEL +0 -0
  24. {udata-9.1.4.dev31221.dist-info → udata-9.1.4.dev31242.dist-info}/entry_points.txt +0 -0
  25. {udata-9.1.4.dev31221.dist-info → udata-9.1.4.dev31242.dist-info}/top_level.txt +0 -0
@@ -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):
@@ -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
- catalog = build_org_catalog(org, datasets, format=format)
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))
@@ -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