udata 9.0.1.dev29687__py2.py3-none-any.whl → 9.0.1.dev29716__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 (29) hide show
  1. udata/core/dataservices/factories.py +19 -0
  2. udata/core/dataservices/rdf.py +55 -4
  3. udata/core/dataset/rdf.py +14 -8
  4. udata/core/site/api.py +29 -1
  5. udata/core/site/rdf.py +6 -1
  6. udata/routing.py +1 -1
  7. udata/static/chunks/{11.ae54612e36c6d46f85db.js → 11.7266fef2dddc1db403d9.js} +3 -3
  8. udata/static/chunks/{11.ae54612e36c6d46f85db.js.map → 11.7266fef2dddc1db403d9.js.map} +1 -1
  9. udata/static/chunks/{13.d8ccb992a49875966313.js → 13.91b177d7d531fd55cf5d.js} +2 -2
  10. udata/static/chunks/{13.d8ccb992a49875966313.js.map → 13.91b177d7d531fd55cf5d.js.map} +1 -1
  11. udata/static/chunks/{16.4565605e68bab129a471.js → 16.e866757bab9f6b0a3f1b.js} +2 -2
  12. udata/static/chunks/{16.4565605e68bab129a471.js.map → 16.e866757bab9f6b0a3f1b.js.map} +1 -1
  13. udata/static/chunks/{19.f993a75d5bfe2382548d.js → 19.619b83ac597516dcd03e.js} +3 -3
  14. udata/static/chunks/{19.f993a75d5bfe2382548d.js.map → 19.619b83ac597516dcd03e.js.map} +1 -1
  15. udata/static/chunks/{5.cc2e7bf65ef32f9c8604.js → 5.48417db6b33328fa9d6a.js} +3 -3
  16. udata/static/chunks/{5.cc2e7bf65ef32f9c8604.js.map → 5.48417db6b33328fa9d6a.js.map} +1 -1
  17. udata/static/chunks/{6.cad898a38692eda28965.js → 6.f84539bd4c419b36cc19.js} +3 -3
  18. udata/static/chunks/{6.cad898a38692eda28965.js.map → 6.f84539bd4c419b36cc19.js.map} +1 -1
  19. udata/static/chunks/{9.d5b992e9ef51921aeb57.js → 9.07503e7f7ec02919f696.js} +2 -2
  20. udata/static/chunks/{9.d5b992e9ef51921aeb57.js.map → 9.07503e7f7ec02919f696.js.map} +1 -1
  21. udata/static/common.js +1 -1
  22. udata/static/common.js.map +1 -1
  23. udata/tests/site/test_site_rdf.py +50 -0
  24. {udata-9.0.1.dev29687.dist-info → udata-9.0.1.dev29716.dist-info}/METADATA +3 -1
  25. {udata-9.0.1.dev29687.dist-info → udata-9.0.1.dev29716.dist-info}/RECORD +29 -28
  26. {udata-9.0.1.dev29687.dist-info → udata-9.0.1.dev29716.dist-info}/LICENSE +0 -0
  27. {udata-9.0.1.dev29687.dist-info → udata-9.0.1.dev29716.dist-info}/WHEEL +0 -0
  28. {udata-9.0.1.dev29687.dist-info → udata-9.0.1.dev29716.dist-info}/entry_points.txt +0 -0
  29. {udata-9.0.1.dev29687.dist-info → udata-9.0.1.dev29716.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,19 @@
1
+ import factory
2
+
3
+ from udata.core.dataservices.models import Dataservice
4
+ from udata.core.organization.factories import OrganizationFactory
5
+ from udata.factories import ModelFactory
6
+
7
+
8
+ class DataserviceFactory(ModelFactory):
9
+ class Meta:
10
+ model = Dataservice
11
+
12
+ title = factory.Faker('sentence')
13
+ description = factory.Faker('text')
14
+ base_api_url = factory.Faker('url')
15
+
16
+ class Params:
17
+ org = factory.Trait(
18
+ organization=factory.SubFactory(OrganizationFactory),
19
+ )
@@ -1,13 +1,14 @@
1
1
 
2
2
  from datetime import datetime
3
3
  from typing import List, Optional
4
- from rdflib import RDF, Graph, URIRef
4
+ from rdflib import RDF, BNode, Graph, Literal, URIRef
5
5
 
6
6
  from udata.core.dataservices.models import Dataservice, HarvestMetadata as HarvestDataserviceMetadata
7
7
  from udata.core.dataset.models import Dataset, License
8
- from udata.core.dataset.rdf import sanitize_html
8
+ from udata.core.dataset.rdf import dataset_to_graph_id, sanitize_html
9
9
  from udata.harvest.models import HarvestSource
10
- from udata.rdf import DCAT, DCT, contact_point_from_rdf, rdf_value, remote_url_from_rdf, theme_labels_from_rdf, themes_from_rdf, url_from_rdf
10
+ from udata.rdf import DCATAP, TAG_TO_EU_HVD_CATEGORIES, namespace_manager, DCAT, DCT, contact_point_from_rdf, rdf_value, remote_url_from_rdf, themes_from_rdf, url_from_rdf
11
+ from udata.uris import endpoint_for
11
12
 
12
13
  def dataservice_from_rdf(graph: Graph, dataservice: Dataservice, node, all_datasets: List[Dataset]) -> Dataservice :
13
14
  '''
@@ -55,4 +56,54 @@ def dataservice_from_rdf(graph: Graph, dataservice: Dataservice, node, all_datas
55
56
 
56
57
  dataservice.tags = themes_from_rdf(d)
57
58
 
58
- return dataservice
59
+ return dataservice
60
+
61
+
62
+ def dataservice_to_rdf(dataservice, graph=None):
63
+ '''
64
+ Map a dataservice domain model to a DCAT/RDF graph
65
+ '''
66
+ # Use the unlocalized permalink to the dataset as URI when available
67
+ # unless there is already an upstream URI
68
+ if dataservice.harvest and dataservice.harvest.rdf_node_id_as_url:
69
+ id = URIRef(dataservice.harvest.rdf_node_id_as_url)
70
+ elif dataservice.id:
71
+ id = URIRef(endpoint_for('dataservices.show_redirect', 'api.dataservice',
72
+ dataservice=dataservice.id, _external=True))
73
+ else:
74
+ # Should not happen in production. Some test only
75
+ # `build()` a dataset without saving it to the DB.
76
+ id = BNode()
77
+
78
+ # Expose upstream identifier if present
79
+ if dataservice.harvest and dataservice.harvest.dct_identifier:
80
+ identifier = dataservice.harvest.dct_identifier
81
+ else:
82
+ identifier = dataservice.id
83
+ graph = graph or Graph(namespace_manager=namespace_manager)
84
+
85
+ d = graph.resource(id)
86
+ d.set(RDF.type, DCAT.DataService)
87
+ d.set(DCT.identifier, Literal(identifier))
88
+ d.set(DCT.title, Literal(dataservice.title))
89
+ d.set(DCT.description, Literal(dataservice.description))
90
+ d.set(DCT.issued, Literal(dataservice.created_at))
91
+
92
+ if dataservice.base_api_url:
93
+ d.set(DCAT.endpointURL, Literal(dataservice.base_api_url))
94
+
95
+ if dataservice.endpoint_description_url:
96
+ d.set(DCAT.endpointDescription, Literal(dataservice.endpoint_description_url))
97
+
98
+ for tag in dataservice.tags:
99
+ d.add(DCAT.keyword, Literal(tag))
100
+
101
+ # `dataset_to_graph_id(dataset)` URIRef may not exist in the current page
102
+ # but should exists in the catalog somewhere. Maybe we should create a Node
103
+ # with some basic information about this dataset (but this will return a page
104
+ # with more datasets than the page size… and could be problematic when processing the
105
+ # correct Node with all the information in a future page)
106
+ for dataset in dataservice.datasets:
107
+ d.add(DCAT.servesDataset, dataset_to_graph_id(dataset))
108
+
109
+ return d
udata/core/dataset/rdf.py CHANGED
@@ -6,7 +6,7 @@ import json
6
6
  import logging
7
7
 
8
8
  from datetime import date
9
- from typing import Optional
9
+ from typing import Optional, Union
10
10
  from dateutil.parser import parse as parse_dt
11
11
  from flask import current_app
12
12
  from geomet import wkt
@@ -149,19 +149,25 @@ def resource_to_rdf(resource, dataset=None, graph=None, is_hvd=False):
149
149
  return r
150
150
 
151
151
 
152
+ def dataset_to_graph_id(dataset: Dataset) -> Union[URIRef, BNode]:
153
+ if dataset.harvest and dataset.harvest.uri:
154
+ return URIRef(dataset.harvest.uri)
155
+ elif dataset.id:
156
+ return URIRef(endpoint_for('datasets.show_redirect', 'api.dataset',
157
+ dataset=dataset.id, _external=True))
158
+ else:
159
+ # Should not happen in production. Some test only
160
+ # `build()` a dataset without saving it to the DB.
161
+ return BNode()
162
+
152
163
  def dataset_to_rdf(dataset, graph=None):
153
164
  '''
154
165
  Map a dataset domain model to a DCAT/RDF graph
155
166
  '''
156
167
  # Use the unlocalized permalink to the dataset as URI when available
157
168
  # unless there is already an upstream URI
158
- if dataset.harvest and dataset.harvest.uri:
159
- id = URIRef(dataset.harvest.uri)
160
- elif dataset.id:
161
- id = URIRef(endpoint_for('datasets.show_redirect', 'api.dataset',
162
- dataset=dataset.id, _external=True))
163
- else:
164
- id = BNode()
169
+ id = dataset_to_graph_id(dataset)
170
+
165
171
  # Expose upstream identifier if present
166
172
  if dataset.harvest and dataset.harvest.dct_identifier:
167
173
  identifier = dataset.harvest.dct_identifier
udata/core/site/api.py CHANGED
@@ -1,9 +1,11 @@
1
1
  from bson import ObjectId
2
2
 
3
3
  from flask import request, redirect, url_for, json, make_response
4
+ from mongoengine import Q
4
5
 
5
6
  from udata.api import api, API, fields
6
7
  from udata.auth import admin_permission
8
+ from udata.core.dataservices.models import Dataservice
7
9
  from udata.models import Dataset, Reuse
8
10
  from udata.utils import multi_to_dict
9
11
  from udata.rdf import (
@@ -109,7 +111,33 @@ class SiteRdfCatalogFormat(API):
109
111
  if 'tag' in params:
110
112
  datasets = datasets.filter(tags=params.get('tag', ''))
111
113
  datasets = datasets.paginate(page, page_size)
112
- catalog = build_catalog(current_site, datasets, format=format)
114
+
115
+ # We need to add Dataservice to the catalog.
116
+ # In the best world, we want:
117
+ # - Keep the correct number of datasets on the page (if the requested page size is 100, we should have 100 datasets)
118
+ # - Have simple MongoDB queries
119
+ # - Do not duplicate the datasets (each dataset is present once in the catalog)
120
+ # - Do not duplicate the dataservices (each dataservice is present once in the catalog)
121
+ # - Every referenced dataset for one dataservices present on the page (hard to do)
122
+ #
123
+ # Multiple solutions are possible but none check all the constraints.
124
+ # The selected one is to put all the dataservices referencing at least one of the dataset on
125
+ # the page at the end of it. It means dataservices could be duplicated (present on multiple pages)
126
+ # and these dataservices may referenced some datasets not present in the current page. It's working
127
+ # if somebody is doing the same thing as us (keeping the list of all the datasets IDs for the entire catalog then
128
+ # listing all dataservices in a second pass)
129
+ # Another option is to do some tricky Mongo requests to order/group datasets by their presence in some dataservices but
130
+ # it could be really hard to do with a n..n relation.
131
+ # Let's keep this solution simple right now and iterate on it in the future.
132
+ dataservices_filter = Q(datasets__in=[d.id for d in datasets])
133
+
134
+ # On the first page, add all dataservices without datasets
135
+ if page == 1:
136
+ dataservices_filter = dataservices_filter | Q(datasets__size=0)
137
+
138
+ dataservices = Dataservice.objects.visible().filter(dataservices_filter)
139
+
140
+ catalog = build_catalog(current_site, datasets, dataservices=dataservices, format=format)
113
141
  # bypass flask-restplus make_response, since graph_response
114
142
  # is handling the content negociation directly
115
143
  return make_response(*graph_response(catalog, format))
udata/core/site/rdf.py CHANGED
@@ -5,6 +5,7 @@ from flask import url_for, current_app
5
5
  from rdflib import Graph, URIRef, Literal, BNode
6
6
  from rdflib.namespace import RDF, FOAF
7
7
 
8
+ from udata.core.dataservices.rdf import dataservice_to_rdf
8
9
  from udata.core.dataset.rdf import dataset_to_rdf
9
10
  from udata.core.organization.rdf import organization_to_rdf
10
11
  from udata.core.user.rdf import user_to_rdf
@@ -13,7 +14,7 @@ from udata.utils import Paginable
13
14
  from udata.uris import endpoint_for
14
15
 
15
16
 
16
- def build_catalog(site, datasets, format=None):
17
+ def build_catalog(site, datasets, dataservices = [], format=None):
17
18
  '''Build the DCAT catalog for this site'''
18
19
  site_url = endpoint_for('site.home_redirect', 'api.site', _external=True)
19
20
  catalog_url = url_for('api.site_rdf_catalog', _external=True)
@@ -40,6 +41,10 @@ def build_catalog(site, datasets, format=None):
40
41
  rdf_dataset.add(DCT.publisher, organization_to_rdf(dataset.organization, graph))
41
42
  catalog.add(DCAT.dataset, rdf_dataset)
42
43
 
44
+ for dataservice in dataservices:
45
+ rdf_dataservice = dataservice_to_rdf(dataservice, graph)
46
+ catalog.add(DCAT.DataService, rdf_dataservice)
47
+
43
48
  if isinstance(datasets, Paginable):
44
49
  paginate_catalog(catalog, graph, datasets, format, 'api.site_rdf_catalog_format')
45
50
 
udata/routing.py CHANGED
@@ -217,7 +217,7 @@ def lazy_raise_or_redirect():
217
217
  new_args = request.view_args
218
218
  new_args[name] = value.arg
219
219
  new_url = url_for(request.endpoint, **new_args)
220
- return redirect(new_url, code=308)
220
+ return redirect(new_url, code=204 if request.method == 'OPTIONS' else 308)
221
221
 
222
222
 
223
223
  def init_app(app):