udata 12.0.2.dev12__py3-none-any.whl → 12.0.2.dev13__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.

@@ -238,19 +238,19 @@ class DataserviceDatasetAPI(API):
238
238
  class DataserviceRdfAPI(API):
239
239
  @api.doc("rdf_dataservice")
240
240
  def get(self, dataservice):
241
- format = RDF_EXTENSIONS[negociate_content()]
242
- url = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, format=format)
241
+ _format = RDF_EXTENSIONS[negociate_content()]
242
+ url = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, _format=_format)
243
243
  return redirect(url)
244
244
 
245
245
 
246
246
  @ns.route(
247
- "/<dataservice:dataservice>/rdf.<format>", endpoint="dataservice_rdf_format", doc=common_doc
247
+ "/<dataservice:dataservice>/rdf.<_format>", endpoint="dataservice_rdf_format", doc=common_doc
248
248
  )
249
249
  @api.response(404, "Dataservice not found")
250
250
  @api.response(410, "Dataservice has been deleted")
251
251
  class DataserviceRdfFormatAPI(API):
252
252
  @api.doc("rdf_dataservice_format")
253
- def get(self, dataservice: Dataservice, format):
253
+ def get(self, dataservice: Dataservice, _format):
254
254
  if not dataservice.permissions["edit"].can():
255
255
  if dataservice.private:
256
256
  api.abort(404)
@@ -260,7 +260,7 @@ class DataserviceRdfFormatAPI(API):
260
260
  resource = dataservice_to_rdf(dataservice)
261
261
  # bypass flask-restplus make_response, since graph_response
262
262
  # is handling the content negociation directly
263
- return make_response(*graph_response(resource, format))
263
+ return make_response(*graph_response(resource, _format))
264
264
 
265
265
 
266
266
  @ns.route("/<id>/followers/", endpoint="dataservice_followers")
udata/core/dataset/api.py CHANGED
@@ -290,6 +290,12 @@ community_parser.add_argument(
290
290
 
291
291
  common_doc = {"params": {"dataset": "The dataset ID or slug"}}
292
292
 
293
+ # Build catalog_parser from DatasetApiParser parser with a default page_size of 100
294
+ catalog_parser = DatasetApiParser().parser
295
+ catalog_parser.replace_argument(
296
+ "page_size", type=int, location="args", default=100, help="The page size"
297
+ )
298
+
293
299
 
294
300
  @ns.route("/", endpoint="datasets")
295
301
  class DatasetListAPI(API):
@@ -431,17 +437,17 @@ class DatasetFeaturedAPI(API):
431
437
  class DatasetRdfAPI(API):
432
438
  @api.doc("rdf_dataset")
433
439
  def get(self, dataset):
434
- format = RDF_EXTENSIONS[negociate_content()]
435
- url = url_for("api.dataset_rdf_format", dataset=dataset.id, format=format)
440
+ _format = RDF_EXTENSIONS[negociate_content()]
441
+ url = url_for("api.dataset_rdf_format", dataset=dataset.id, _format=_format)
436
442
  return redirect(url)
437
443
 
438
444
 
439
- @ns.route("/<dataset:dataset>/rdf.<format>", endpoint="dataset_rdf_format", doc=common_doc)
445
+ @ns.route("/<dataset:dataset>/rdf.<_format>", endpoint="dataset_rdf_format", doc=common_doc)
440
446
  @api.response(404, "Dataset not found")
441
447
  @api.response(410, "Dataset has been deleted")
442
448
  class DatasetRdfFormatAPI(API):
443
449
  @api.doc("rdf_dataset_format")
444
- def get(self, dataset, format):
450
+ def get(self, dataset, _format):
445
451
  if not dataset.permissions["edit"].can():
446
452
  if dataset.private:
447
453
  api.abort(404)
@@ -451,7 +457,7 @@ class DatasetRdfFormatAPI(API):
451
457
  resource = dataset_to_rdf(dataset)
452
458
  # bypass flask-restplus make_response, since graph_response
453
459
  # is handling the content negociation directly
454
- return make_response(*graph_response(resource, format))
460
+ return make_response(*graph_response(resource, _format))
455
461
 
456
462
 
457
463
  @ns.route("/badges/", endpoint="available_dataset_badges")
@@ -13,7 +13,7 @@ from udata.core.contact_point.api import ContactPointApiParser
13
13
  from udata.core.contact_point.api_fields import contact_point_fields, contact_point_page_fields
14
14
  from udata.core.dataservices.csv import DataserviceCsvAdapter
15
15
  from udata.core.dataservices.models import Dataservice
16
- from udata.core.dataset.api import DatasetApiParser
16
+ from udata.core.dataset.api import DatasetApiParser, catalog_parser
17
17
  from udata.core.dataset.api_fields import dataset_page_fields
18
18
  from udata.core.dataset.csv import DatasetCsvAdapter, ResourcesCsvAdapter
19
19
  from udata.core.dataset.models import Dataset
@@ -29,7 +29,6 @@ from udata.core.storages.api import (
29
29
  )
30
30
  from udata.models import ContactPoint
31
31
  from udata.rdf import RDF_EXTENSIONS, graph_response, negociate_content
32
- from udata.utils import multi_to_dict
33
32
 
34
33
  from .api_fields import (
35
34
  member_fields,
@@ -234,32 +233,37 @@ class DatasetsResourcesCsvAPI(API):
234
233
  class OrganizationRdfAPI(API):
235
234
  @api.doc("rdf_organization")
236
235
  def get(self, org):
237
- format = RDF_EXTENSIONS[negociate_content()]
238
- url = url_for("api.organization_rdf_format", org=org.id, format=format)
236
+ _format = RDF_EXTENSIONS[negociate_content()]
237
+ # We sanitize the args used as kwargs in url_for
238
+ params = catalog_parser.parse_args()
239
+ url = url_for("api.organization_rdf_format", org=org.id, _format=_format, **params)
239
240
  return redirect(url)
240
241
 
241
242
 
242
- @ns.route("/<org:org>/catalog.<format>", endpoint="organization_rdf_format", doc=common_doc)
243
+ @ns.route("/<org:org>/catalog.<_format>", endpoint="organization_rdf_format", doc=common_doc)
243
244
  @api.response(404, "Organization not found")
244
245
  @api.response(410, "Organization has been deleted")
245
246
  class OrganizationRdfFormatAPI(API):
246
247
  @api.doc("rdf_organization_format")
247
- def get(self, org, format):
248
+ @api.expect(catalog_parser)
249
+ def get(self, org, _format):
248
250
  if org.deleted:
249
251
  api.abort(410)
250
- params = multi_to_dict(request.args)
251
- page = int(params.get("page", 1))
252
- page_size = int(params.get("page_size", 100))
253
- datasets = Dataset.objects(organization=org).visible().paginate(page, page_size)
252
+ params = catalog_parser.parse_args()
253
+ datasets = DatasetApiParser.parse_filters(
254
+ Dataset.objects(organization=org).visible(), params
255
+ )
256
+ datasets = datasets.paginate(params["page"], params["page_size"])
257
+
254
258
  dataservices = (
255
259
  Dataservice.objects(organization=org)
256
260
  .visible()
257
- .filter_by_dataset_pagination(datasets, page)
261
+ .filter_by_dataset_pagination(datasets, params["page"])
258
262
  )
259
- catalog = build_org_catalog(org, datasets, dataservices, format=format)
263
+ catalog = build_org_catalog(org, datasets, dataservices, _format=_format, **params)
260
264
  # bypass flask-restplus make_response, since graph_response
261
265
  # is handling the content negociation directly
262
- return make_response(*graph_response(catalog, format))
266
+ return make_response(*graph_response(catalog, _format))
263
267
 
264
268
 
265
269
  @ns.route("/badges/", endpoint="available_organization_badges")
@@ -32,7 +32,7 @@ def organization_to_rdf(org, graph=None):
32
32
  return o
33
33
 
34
34
 
35
- def build_org_catalog(org, datasets, dataservices, format=None):
35
+ def build_org_catalog(org, datasets, dataservices, _format=None, **kwargs):
36
36
  graph = Graph(namespace_manager=namespace_manager)
37
37
  org_catalog_url = url_for("api.organization_rdf", org=org.id, _external=True)
38
38
 
@@ -47,9 +47,9 @@ def build_org_catalog(org, datasets, dataservices, format=None):
47
47
  for dataservice in dataservices:
48
48
  catalog.add(DCAT.service, dataservice_to_rdf(dataservice, graph))
49
49
 
50
- values = {"org": org.id}
50
+ values = {**kwargs, "org": org.id}
51
51
 
52
52
  if isinstance(datasets, Paginable):
53
- paginate_catalog(catalog, graph, datasets, format, "api.organization_rdf_format", **values)
53
+ paginate_catalog(catalog, graph, datasets, _format, "api.organization_rdf_format", **values)
54
54
 
55
55
  return catalog
udata/core/site/api.py CHANGED
@@ -6,7 +6,7 @@ from udata.auth import admin_permission
6
6
  from udata.core import csv
7
7
  from udata.core.dataservices.csv import DataserviceCsvAdapter
8
8
  from udata.core.dataservices.models import Dataservice
9
- from udata.core.dataset.api import DatasetApiParser
9
+ from udata.core.dataset.api import DatasetApiParser, catalog_parser
10
10
  from udata.core.dataset.csv import ResourcesCsvAdapter
11
11
  from udata.core.dataset.search import DatasetSearch
12
12
  from udata.core.dataset.tasks import get_queryset as get_csv_queryset
@@ -47,39 +47,47 @@ class SiteAPI(API):
47
47
  return current_site
48
48
 
49
49
 
50
- @api.route("/site/data.<format>", endpoint="site_dataportal")
50
+ @api.route("/site/data.<_format>", endpoint="site_dataportal")
51
51
  class SiteDataPortal(API):
52
- def get(self, format):
52
+ def get(self, _format):
53
53
  """Root RDF endpoint with content negociation handling"""
54
- url = url_for("api.site_rdf_catalog_format", format=format)
54
+ url = url_for("api.site_rdf_catalog_format", _format=_format)
55
55
  return redirect(url)
56
56
 
57
57
 
58
58
  @api.route("/site/catalog", endpoint="site_rdf_catalog")
59
59
  class SiteRdfCatalog(API):
60
+ @api.expect(catalog_parser)
60
61
  def get(self):
61
62
  """Root RDF endpoint with content negociation handling"""
62
- format = RDF_EXTENSIONS[negociate_content()]
63
- url = url_for("api.site_rdf_catalog_format", format=format)
63
+ _format = RDF_EXTENSIONS[negociate_content()]
64
+ # We sanitize the args used as kwargs in url_for
65
+ params = catalog_parser.parse_args()
66
+ url = url_for("api.site_rdf_catalog_format", _format=_format, **params)
64
67
  return redirect(url)
65
68
 
66
69
 
67
- @api.route("/site/catalog.<format>", endpoint="site_rdf_catalog_format")
70
+ @api.route("/site/catalog.<_format>", endpoint="site_rdf_catalog_format")
68
71
  class SiteRdfCatalogFormat(API):
69
- def get(self, format):
70
- params = multi_to_dict(request.args)
71
- page = int(params.get("page", 1))
72
- page_size = int(params.get("page_size", 100))
73
- datasets = Dataset.objects.visible()
74
- if "tag" in params:
75
- datasets = datasets.filter(tags=params.get("tag", ""))
76
- datasets = datasets.paginate(page, page_size)
77
- dataservices = Dataservice.objects.visible().filter_by_dataset_pagination(datasets, page)
78
-
79
- catalog = build_catalog(current_site, datasets, dataservices=dataservices, format=format)
72
+ @api.expect(catalog_parser)
73
+ def get(self, _format):
74
+ """
75
+ Return the RDF catalog in the requested format.
76
+ Filtering, sorting and paginating abilities apply to the datasets elements.
77
+ """
78
+ params = catalog_parser.parse_args()
79
+ datasets = DatasetApiParser.parse_filters(Dataset.objects.visible(), params)
80
+ datasets = datasets.paginate(params["page"], params["page_size"])
81
+ dataservices = Dataservice.objects.visible().filter_by_dataset_pagination(
82
+ datasets, params["page"]
83
+ )
84
+
85
+ catalog = build_catalog(
86
+ current_site, datasets, dataservices=dataservices, _format=_format, **params
87
+ )
80
88
  # bypass flask-restplus make_response, since graph_response
81
89
  # is handling the content negociation directly
82
- return make_response(*graph_response(catalog, format))
90
+ return make_response(*graph_response(catalog, _format))
83
91
 
84
92
 
85
93
  @api.route("/site/datasets.csv", endpoint="site_datasets_csv")
udata/core/site/rdf.py CHANGED
@@ -15,7 +15,7 @@ from udata.uris import homepage_url
15
15
  from udata.utils import Paginable
16
16
 
17
17
 
18
- def build_catalog(site, datasets, dataservices=[], format=None):
18
+ def build_catalog(site, datasets, dataservices=[], _format=None, **kwargs):
19
19
  """Build the DCAT catalog for this site"""
20
20
  catalog_url = url_for("api.site_rdf_catalog", _external=True)
21
21
  graph = Graph(namespace_manager=namespace_manager)
@@ -45,6 +45,6 @@ def build_catalog(site, datasets, dataservices=[], format=None):
45
45
  catalog.add(DCAT.service, rdf_dataservice)
46
46
 
47
47
  if isinstance(datasets, Paginable):
48
- paginate_catalog(catalog, graph, datasets, format, "api.site_rdf_catalog_format")
48
+ paginate_catalog(catalog, graph, datasets, _format, "api.site_rdf_catalog_format", **kwargs)
49
49
 
50
50
  return catalog
udata/rdf.py CHANGED
@@ -490,17 +490,18 @@ def escape_xml_illegal_chars(val, replacement="?"):
490
490
  return illegal_xml_chars_RE.sub(replacement, val)
491
491
 
492
492
 
493
- def paginate_catalog(catalog, graph, datasets, format, rdf_catalog_endpoint, **values):
493
+ def paginate_catalog(catalog, graph, datasets, _format, rdf_catalog_endpoint, **values):
494
494
  if not format:
495
495
  raise ValueError("Pagination requires format")
496
496
  catalog.add(RDF.type, HYDRA.Collection)
497
497
  catalog.set(HYDRA.totalItems, Literal(datasets.total))
498
498
  kwargs = {
499
- "format": format,
499
+ "_format": _format,
500
500
  "page_size": datasets.page_size,
501
501
  "_external": True,
502
502
  }
503
-
503
+ values.pop("page", None)
504
+ values.pop("page_size", None)
504
505
  kwargs.update(values)
505
506
 
506
507
  first_url = url_for(rdf_catalog_endpoint, page=1, **kwargs)
@@ -1,8 +1,6 @@
1
1
  from datetime import datetime, timedelta
2
- from xml.etree.ElementTree import XML
3
2
 
4
3
  import feedparser
5
- import pytest
6
4
  from flask import url_for
7
5
  from werkzeug.test import TestResponse
8
6
 
@@ -24,7 +22,7 @@ from udata.core.organization.models import Member
24
22
  from udata.core.topic.factories import TopicElementFactory, TopicFactory
25
23
  from udata.core.user.factories import AdminFactory, UserFactory
26
24
  from udata.i18n import gettext as _
27
- from udata.tests.helpers import assert200, assert400, assert410, assert_redirects
25
+ from udata.tests.helpers import assert200, assert400, assert410
28
26
 
29
27
  from . import APITestCase
30
28
 
@@ -664,60 +662,6 @@ class DataserviceAPITest(APITestCase):
664
662
  self.assertEqual(Dataservice.objects.first().organization.id, new_org.id)
665
663
 
666
664
 
667
- @pytest.mark.frontend
668
- class DataserviceRdfViewsTest:
669
- def test_rdf_default_to_jsonld(self, client):
670
- dataservice = DataserviceFactory()
671
- expected = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, format="json")
672
- response = client.get(url_for("api.dataservice_rdf", dataservice=dataservice))
673
- assert_redirects(response, expected)
674
-
675
- def test_rdf_perform_content_negociation(self, client):
676
- dataservice = DataserviceFactory()
677
- expected = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, format="xml")
678
- url = url_for("api.dataservice_rdf", dataservice=dataservice)
679
- headers = {"accept": "application/xml"}
680
- response = client.get(url, headers=headers)
681
- assert_redirects(response, expected)
682
-
683
- def test_rdf_perform_content_negociation_response(self, client):
684
- """Check we have valid XML as output"""
685
- dataservice = DataserviceFactory()
686
- url = url_for("api.dataservice_rdf", dataservice=dataservice)
687
- headers = {"accept": "application/xml"}
688
- response = client.get(url, headers=headers, follow_redirects=True)
689
- element = XML(response.data)
690
- assert element.tag == "{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF"
691
-
692
- def test_dataservice_rdf_json_ld(self, client):
693
- dataservice = DataserviceFactory()
694
- for fmt in "json", "jsonld":
695
- url = url_for("api.dataservice_rdf_format", dataservice=dataservice, format=fmt)
696
- response = client.get(url, headers={"Accept": "application/ld+json"})
697
- assert200(response)
698
- assert response.content_type == "application/ld+json"
699
- assert response.json["@context"]["@vocab"] == "http://www.w3.org/ns/dcat#"
700
-
701
- @pytest.mark.parametrize(
702
- "fmt,mime",
703
- [
704
- ("n3", "text/n3"),
705
- ("nt", "application/n-triples"),
706
- ("ttl", "application/x-turtle"),
707
- ("xml", "application/rdf+xml"),
708
- ("rdf", "application/rdf+xml"),
709
- ("owl", "application/rdf+xml"),
710
- ("trig", "application/trig"),
711
- ],
712
- )
713
- def test_dataservice_rdf_formats(self, client, fmt, mime):
714
- dataservice = DataserviceFactory()
715
- url = url_for("api.dataservice_rdf_format", dataservice=dataservice, format=fmt)
716
- response = client.get(url, headers={"Accept": mime})
717
- assert200(response)
718
- assert response.content_type == mime
719
-
720
-
721
665
  class DataservicesFeedAPItest(APITestCase):
722
666
  def test_recent_feed(self):
723
667
  DataserviceFactory(title="A", created_at=datetime.utcnow())
@@ -1,4 +1,7 @@
1
+ from xml.etree.ElementTree import XML
2
+
1
3
  import pytest
4
+ from flask import url_for
2
5
  from rdflib import BNode, Literal, URIRef
3
6
  from rdflib.namespace import RDF
4
7
  from rdflib.resource import Resource as RdfResource
@@ -13,6 +16,7 @@ from udata.rdf import (
13
16
  HVD_LEGISLATION,
14
17
  TAG_TO_EU_HVD_CATEGORIES,
15
18
  )
19
+ from udata.tests.helpers import assert200, assert_redirects
16
20
 
17
21
  pytestmark = pytest.mark.usefixtures("app")
18
22
 
@@ -77,3 +81,57 @@ class DataserviceToRdfTest:
77
81
  assert URIRef(TAG_TO_EU_HVD_CATEGORIES["meteorologiques"]) not in hvd_categories
78
82
  for distrib in d.objects(DCAT.distribution):
79
83
  assert distrib.value(DCATAP.applicableLegislation).identifier == URIRef(HVD_LEGISLATION)
84
+
85
+
86
+ @pytest.mark.frontend
87
+ class DataserviceRdfViewsTest:
88
+ def test_rdf_default_to_jsonld(self, client):
89
+ dataservice = DataserviceFactory()
90
+ expected = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, _format="json")
91
+ response = client.get(url_for("api.dataservice_rdf", dataservice=dataservice))
92
+ assert_redirects(response, expected)
93
+
94
+ def test_rdf_perform_content_negociation(self, client):
95
+ dataservice = DataserviceFactory()
96
+ expected = url_for("api.dataservice_rdf_format", dataservice=dataservice.id, _format="xml")
97
+ url = url_for("api.dataservice_rdf", dataservice=dataservice)
98
+ headers = {"accept": "application/xml"}
99
+ response = client.get(url, headers=headers)
100
+ assert_redirects(response, expected)
101
+
102
+ def test_rdf_perform_content_negociation_response(self, client):
103
+ """Check we have valid XML as output"""
104
+ dataservice = DataserviceFactory()
105
+ url = url_for("api.dataservice_rdf", dataservice=dataservice)
106
+ headers = {"accept": "application/xml"}
107
+ response = client.get(url, headers=headers, follow_redirects=True)
108
+ element = XML(response.data)
109
+ assert element.tag == "{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF"
110
+
111
+ def test_dataservice_rdf_json_ld(self, client):
112
+ dataservice = DataserviceFactory()
113
+ for fmt in "json", "jsonld":
114
+ url = url_for("api.dataservice_rdf_format", dataservice=dataservice, _format=fmt)
115
+ response = client.get(url, headers={"Accept": "application/ld+json"})
116
+ assert200(response)
117
+ assert response.content_type == "application/ld+json"
118
+ assert response.json["@context"]["@vocab"] == "http://www.w3.org/ns/dcat#"
119
+
120
+ @pytest.mark.parametrize(
121
+ "fmt,mime",
122
+ [
123
+ ("n3", "text/n3"),
124
+ ("nt", "application/n-triples"),
125
+ ("ttl", "application/x-turtle"),
126
+ ("xml", "application/rdf+xml"),
127
+ ("rdf", "application/rdf+xml"),
128
+ ("owl", "application/rdf+xml"),
129
+ ("trig", "application/trig"),
130
+ ],
131
+ )
132
+ def test_dataservice_rdf_formats(self, client, fmt, mime):
133
+ dataservice = DataserviceFactory()
134
+ url = url_for("api.dataservice_rdf_format", dataservice=dataservice, _format=fmt)
135
+ response = client.get(url, headers={"Accept": mime})
136
+ assert200(response)
137
+ assert response.content_type == mime
@@ -1126,13 +1126,13 @@ class RdfToDatasetTest:
1126
1126
  class DatasetRdfViewsTest:
1127
1127
  def test_rdf_default_to_jsonld(self, client):
1128
1128
  dataset = DatasetFactory()
1129
- expected = url_for("api.dataset_rdf_format", dataset=dataset.id, format="json")
1129
+ expected = url_for("api.dataset_rdf_format", dataset=dataset.id, _format="json")
1130
1130
  response = client.get(url_for("api.dataset_rdf", dataset=dataset))
1131
1131
  assert_redirects(response, expected)
1132
1132
 
1133
1133
  def test_rdf_perform_content_negociation(self, client):
1134
1134
  dataset = DatasetFactory()
1135
- expected = url_for("api.dataset_rdf_format", dataset=dataset.id, format="xml")
1135
+ expected = url_for("api.dataset_rdf_format", dataset=dataset.id, _format="xml")
1136
1136
  url = url_for("api.dataset_rdf", dataset=dataset)
1137
1137
  headers = {"accept": "application/xml"}
1138
1138
  response = client.get(url, headers=headers)
@@ -1150,7 +1150,7 @@ class DatasetRdfViewsTest:
1150
1150
  def test_dataset_rdf_json_ld(self, client):
1151
1151
  dataset = DatasetFactory()
1152
1152
  for fmt in "json", "jsonld":
1153
- url = url_for("api.dataset_rdf_format", dataset=dataset, format=fmt)
1153
+ url = url_for("api.dataset_rdf_format", dataset=dataset, _format=fmt)
1154
1154
  response = client.get(url, headers={"Accept": "application/ld+json"})
1155
1155
  assert200(response)
1156
1156
  assert response.content_type == "application/ld+json"
@@ -1170,7 +1170,7 @@ class DatasetRdfViewsTest:
1170
1170
  )
1171
1171
  def test_dataset_rdf_formats(self, client, fmt, mime):
1172
1172
  dataset = DatasetFactory()
1173
- url = url_for("api.dataset_rdf_format", dataset=dataset, format=fmt)
1173
+ url = url_for("api.dataset_rdf_format", dataset=dataset, _format=fmt)
1174
1174
  response = client.get(url, headers={"Accept": mime})
1175
1175
  assert200(response)
1176
1176
  assert response.content_type == mime
@@ -95,7 +95,7 @@ class OrganizationToRdfTest(DBTestMixin, TestCase):
95
95
  uri_first = url_for(
96
96
  "api.organization_rdf_format",
97
97
  org=origin_org.id,
98
- format="json",
98
+ _format="json",
99
99
  page=1,
100
100
  page_size=page_size,
101
101
  _external=True,
@@ -103,7 +103,7 @@ class OrganizationToRdfTest(DBTestMixin, TestCase):
103
103
  uri_last = url_for(
104
104
  "api.organization_rdf_format",
105
105
  org=origin_org.id,
106
- format="json",
106
+ _format="json",
107
107
  page=2,
108
108
  page_size=page_size,
109
109
  _external=True,
@@ -124,7 +124,7 @@ class OrganizationToRdfTest(DBTestMixin, TestCase):
124
124
  # First page
125
125
  datasets = Dataset.objects.paginate(1, page_size)
126
126
  dataservices = Dataservice.objects.filter_by_dataset_pagination(datasets, 1)
127
- catalog = build_org_catalog(origin_org, datasets, dataservices, format="json")
127
+ catalog = build_org_catalog(origin_org, datasets, dataservices, _format="json")
128
128
  graph = catalog.graph
129
129
 
130
130
  self.assertIsInstance(catalog, RdfResource)
@@ -152,7 +152,7 @@ class OrganizationToRdfTest(DBTestMixin, TestCase):
152
152
  # Second page
153
153
  datasets = Dataset.objects.paginate(2, page_size)
154
154
  dataservices = Dataservice.objects.filter_by_dataset_pagination(datasets, 2)
155
- catalog = build_org_catalog(origin_org, datasets, dataservices, format="json")
155
+ catalog = build_org_catalog(origin_org, datasets, dataservices, _format="json")
156
156
  graph = catalog.graph
157
157
 
158
158
  self.assertIsInstance(catalog, RdfResource)
@@ -3,8 +3,10 @@ from flask import url_for
3
3
  from rdflib import Graph, Literal, URIRef
4
4
  from rdflib.namespace import FOAF, RDF
5
5
  from rdflib.resource import Resource
6
+ from werkzeug.datastructures import ImmutableMultiDict
6
7
 
7
8
  from udata.core.dataservices.factories import DataserviceFactory, HarvestMetadataFactory
9
+ from udata.core.dataset.constants import HVD
8
10
  from udata.core.dataset.factories import DatasetFactory
9
11
  from udata.core.dataset.models import Dataset
10
12
  from udata.core.organization.factories import OrganizationFactory
@@ -77,14 +79,14 @@ class CatalogTest:
77
79
  uri = url_for("api.site_rdf_catalog", _external=True)
78
80
  uri_first = url_for(
79
81
  "api.site_rdf_catalog_format",
80
- format="json",
82
+ _format="json",
81
83
  page=1,
82
84
  page_size=page_size,
83
85
  _external=True,
84
86
  )
85
87
  uri_last = url_for(
86
88
  "api.site_rdf_catalog_format",
87
- format="json",
89
+ _format="json",
88
90
  page=2,
89
91
  page_size=page_size,
90
92
  _external=True,
@@ -93,7 +95,7 @@ class CatalogTest:
93
95
 
94
96
  # First page
95
97
  datasets = Dataset.objects.paginate(1, page_size)
96
- catalog = build_catalog(site, datasets, format="json")
98
+ catalog = build_catalog(site, datasets, _format="json")
97
99
  graph = catalog.graph
98
100
 
99
101
  assert isinstance(catalog, Resource)
@@ -117,7 +119,7 @@ class CatalogTest:
117
119
 
118
120
  # Second page
119
121
  datasets = Dataset.objects.paginate(2, page_size)
120
- catalog = build_catalog(site, datasets, format="json")
122
+ catalog = build_catalog(site, datasets, _format="json")
121
123
  graph = catalog.graph
122
124
 
123
125
  assert isinstance(catalog, Resource)
@@ -152,12 +154,39 @@ class SiteRdfViewsTest:
152
154
  assert response.json == CONTEXT
153
155
 
154
156
  def test_catalog_default_to_jsonld(self, client):
155
- expected = url_for("api.site_rdf_catalog_format", format="json")
157
+ expected = url_for("api.site_rdf_catalog_format", _format="json", page=1, page_size=100)
156
158
  response = client.get(url_for("api.site_rdf_catalog"))
157
159
  assert_redirects(response, expected)
158
160
 
161
+ def test_catalog_redirect_with_filters_sanitizing(self, client):
162
+ """
163
+ It should filter out unknown params (including url_for keywords).
164
+ Repeated keywords should be kept as expected.
165
+ """
166
+ expected_params = ImmutableMultiDict(
167
+ [("page", 3), ("tag", "hvd"), ("tag", "other"), ("page_size", 100)]
168
+ )
169
+ expected = url_for(
170
+ "api.site_rdf_catalog_format", _format="xml", **expected_params.to_dict(flat=False)
171
+ )
172
+ params = [
173
+ ("page", 3),
174
+ ("tag", "hvd"),
175
+ ("tag", "other"),
176
+ ("unknown_param", 5),
177
+ ("_external", True),
178
+ ]
179
+ url = (
180
+ url_for("api.site_rdf_catalog")
181
+ + "?"
182
+ + "&".join(f"{arg}={value}" for arg, value in params)
183
+ )
184
+ headers = {"accept": "application/xml"}
185
+ response = client.get(url, headers=headers)
186
+ assert_redirects(response, expected)
187
+
159
188
  def test_rdf_perform_content_negociation(self, client):
160
- expected = url_for("api.site_rdf_catalog_format", format="xml")
189
+ expected = url_for("api.site_rdf_catalog_format", _format="xml", page=1, page_size=100)
161
190
  url = url_for("api.site_rdf_catalog")
162
191
  headers = {"accept": "application/xml"}
163
192
  response = client.get(url, headers=headers)
@@ -165,57 +194,65 @@ class SiteRdfViewsTest:
165
194
 
166
195
  @pytest.mark.parametrize("fmt", ("json", "jsonld"))
167
196
  def test_catalog_rdf_json_ld(self, fmt, client):
168
- url = url_for("api.site_rdf_catalog_format", format=fmt)
197
+ url = url_for("api.site_rdf_catalog_format", _format=fmt)
169
198
  response = client.get(url, headers={"Accept": "application/ld+json"})
170
199
  assert200(response)
171
200
  assert response.content_type == "application/ld+json"
172
201
  assert response.json["@context"]["@vocab"] == "http://www.w3.org/ns/dcat#"
173
202
 
174
203
  def test_catalog_rdf_n3(self, client):
175
- url = url_for("api.site_rdf_catalog_format", format="n3")
204
+ url = url_for("api.site_rdf_catalog_format", _format="n3")
176
205
  response = client.get(url, headers={"Accept": "text/n3"})
177
206
  assert200(response)
178
207
  assert response.content_type == "text/n3"
179
208
 
180
209
  def test_catalog_rdf_turtle(self, client):
181
- url = url_for("api.site_rdf_catalog_format", format="ttl")
210
+ url = url_for("api.site_rdf_catalog_format", _format="ttl")
182
211
  response = client.get(url, headers={"Accept": "application/x-turtle"})
183
212
  assert200(response)
184
213
  assert response.content_type == "application/x-turtle"
185
214
 
186
215
  @pytest.mark.parametrize("fmt", ("xml", "rdf", "owl"))
187
216
  def test_catalog_rdf_rdfxml(self, fmt, client):
188
- url = url_for("api.site_rdf_catalog_format", format=fmt)
217
+ url = url_for("api.site_rdf_catalog_format", _format=fmt)
189
218
  response = client.get(url, headers={"Accept": "application/rdf+xml"})
190
219
  assert200(response)
191
220
  assert response.content_type == "application/rdf+xml"
192
221
 
193
222
  def test_catalog_rdf_n_triples(self, client):
194
- url = url_for("api.site_rdf_catalog_format", format="nt")
223
+ url = url_for("api.site_rdf_catalog_format", _format="nt")
195
224
  response = client.get(url, headers={"Accept": "application/n-triples"})
196
225
  assert200(response)
197
226
  assert response.content_type == "application/n-triples"
198
227
 
199
228
  def test_catalog_rdf_trig(self, client):
200
- url = url_for("api.site_rdf_catalog_format", format="trig")
229
+ url = url_for("api.site_rdf_catalog_format", _format="trig")
201
230
  response = client.get(url, headers={"Accept": "application/trig"})
202
231
  assert200(response)
203
232
  assert response.content_type == "application/trig"
204
233
 
205
234
  @pytest.mark.parametrize("fmt", ("json", "xml", "ttl"))
206
235
  def test_dataportal_compliance(self, fmt, client):
207
- url = url_for("api.site_dataportal", format=fmt)
236
+ url = url_for("api.site_dataportal", _format=fmt)
208
237
  assert url == "/api/1/site/data.{0}".format(fmt)
209
- expected_url = url_for("api.site_rdf_catalog_format", format=fmt)
238
+ expected_url = url_for("api.site_rdf_catalog_format", _format=fmt)
210
239
 
211
240
  response = client.get(url)
212
241
  assert_redirects(response, expected_url)
213
242
 
214
243
  def test_catalog_rdf_paginate(self, client):
215
- DatasetFactory.create_batch(4)
216
- url = url_for("api.site_rdf_catalog_format", format="n3", page_size=3)
244
+ DatasetFactory.create_batch(4, tags=["my-tag"])
245
+ DatasetFactory.create_batch(
246
+ 3, tags=["other-tag"]
247
+ ) # Shouldn't be returned because of the filter on tag="my-tag"
248
+ url = url_for("api.site_rdf_catalog_format", _format="n3", page_size=3, tag="my-tag")
217
249
  next_url = url_for(
218
- "api.site_rdf_catalog_format", format="n3", page=2, page_size=3, _external=True
250
+ "api.site_rdf_catalog_format",
251
+ _format="n3",
252
+ page=2,
253
+ page_size=3,
254
+ _external=True,
255
+ tag="my-tag",
219
256
  )
220
257
 
221
258
  response = client.get(url, headers={"Accept": "text/n3"})
@@ -227,16 +264,27 @@ class SiteRdfViewsTest:
227
264
  pagination = graph.resource(pagination)
228
265
  assert not pagination.value(HYDRA.previous)
229
266
  assert pagination.value(HYDRA.next).identifier == URIRef(next_url)
267
+ assert pagination.value(HYDRA.last).identifier == URIRef(next_url)
268
+ catalog = graph.value(predicate=RDF.type, object=HYDRA.Collection)
269
+ assert catalog is not None
270
+ catalog = graph.resource(catalog)
271
+ assert catalog.value(HYDRA.totalItems) == Literal(4)
230
272
 
231
273
  def test_catalog_format_unknown(self, client):
232
- url = url_for("api.site_rdf_catalog_format", format="unknown")
274
+ url = url_for("api.site_rdf_catalog_format", _format="unknown")
233
275
  response = client.get(url)
234
276
  assert404(response)
235
277
 
236
- def test_catalog_rdf_filter_tag(self, client):
278
+ def test_catalog_rdf_filter(self, client):
279
+ """
280
+ Test catalog RDF filter on tags and badges
281
+ """
237
282
  DatasetFactory.create_batch(4, tags=["my-tag"])
283
+ [dat.add_badge(HVD) for dat in DatasetFactory.create_batch(5)]
238
284
  DatasetFactory.create_batch(3)
239
- url = url_for("api.site_rdf_catalog_format", format="xml", tag="my-tag")
285
+
286
+ # Filter on tags
287
+ url = url_for("api.site_rdf_catalog_format", _format="xml", tag="my-tag")
240
288
 
241
289
  response = client.get(url, headers={"Accept": "application/xml"})
242
290
  assert200(response)
@@ -249,6 +297,17 @@ class SiteRdfViewsTest:
249
297
  for dat in datasets:
250
298
  assert graph.value(dat, DCAT.keyword) == Literal("my-tag")
251
299
 
300
+ # Filter on badge
301
+ url = url_for("api.site_rdf_catalog_format", _format="xml", badge=HVD)
302
+
303
+ response = client.get(url, headers={"Accept": "application/xml"})
304
+ assert200(response)
305
+
306
+ graph = Graph().parse(data=response.data, format="xml")
307
+
308
+ datasets = list(graph.subjects(RDF.type, DCAT.Dataset))
309
+ assert len(datasets) == 5
310
+
252
311
  def test_catalog_rdf_dataservices(self, client):
253
312
  dataset_a = DatasetFactory.create()
254
313
  dataset_b = DatasetFactory.create()
@@ -260,7 +319,7 @@ class SiteRdfViewsTest:
260
319
  dataservice_y = DataserviceFactory.create(datasets=[])
261
320
 
262
321
  response = client.get(
263
- url_for("api.site_rdf_catalog_format", format="xml"),
322
+ url_for("api.site_rdf_catalog_format", _format="xml"),
264
323
  headers={"Accept": "application/xml"},
265
324
  )
266
325
  assert200(response)
@@ -280,7 +339,7 @@ class SiteRdfViewsTest:
280
339
 
281
340
  # Test first page contains the dataservice without dataset
282
341
  response = client.get(
283
- url_for("api.site_rdf_catalog_format", format="xml", page_size=1),
342
+ url_for("api.site_rdf_catalog_format", _format="xml", page_size=1),
284
343
  headers={"Accept": "application/xml"},
285
344
  )
286
345
  assert200(response)
@@ -299,7 +358,7 @@ class SiteRdfViewsTest:
299
358
 
300
359
  # Test second page doesn't contains the dataservice without dataset
301
360
  response = client.get(
302
- url_for("api.site_rdf_catalog_format", format="xml", page_size=1, page=2),
361
+ url_for("api.site_rdf_catalog_format", _format="xml", page_size=1, page=2),
303
362
  headers={"Accept": "application/xml"},
304
363
  )
305
364
  assert200(response)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: udata
3
- Version: 12.0.2.dev12
3
+ Version: 12.0.2.dev13
4
4
  Summary: Open data portal
5
5
  Author-email: Opendata Team <opendatateam@data.gouv.fr>
6
6
  Maintainer-email: Opendata Team <opendatateam@data.gouv.fr>
@@ -7,7 +7,7 @@ udata/errors.py,sha256=E8W7b4PH7c5B85g_nsUMt8fHqMVpDFOZFkO6wMPl6bA,117
7
7
  udata/factories.py,sha256=MoklZnU8iwNL25dm3JsoXhoQs1PQWSVYL1WvcUBtJqM,492
8
8
  udata/i18n.py,sha256=bC9ajf66YgcYoJffvresLZLa32rb6NsY-JGMtFiVsG4,8163
9
9
  udata/mail.py,sha256=Huhx_1QthJkLvuRUuP6jqb5Qq5R4iSmqeEpLVO9ZkQ4,2671
10
- udata/rdf.py,sha256=aJKmnE1r6YyMKXLo-VRlUvOXoZSJuvNeNbMCqEl0kdY,19370
10
+ udata/rdf.py,sha256=4SruNYSJzqwureb8no6ghh0ZXypG7wCahqSpSrBo8K0,19435
11
11
  udata/routing.py,sha256=Hnc1ktmKVS-RUHNKw2zYTft2HJ903FhjtlcenQ9igwI,8044
12
12
  udata/sentry.py,sha256=ekcxqUSqxfM98TtvCsPaOoX5i2l6PEcYt7kb4l3od-Q,3223
13
13
  udata/settings.py,sha256=1gDu1fnorsgzRzkMIEzFjWiVZ_7UPe1kRW-GQulb7O0,21686
@@ -79,7 +79,7 @@ udata/core/contact_point/forms.py,sha256=oBe1agSJFyx2QRgYzPRg2A7qVscaBTaKG4V-AyI
79
79
  udata/core/contact_point/models.py,sha256=Xqmqg7S13gcaKxiQT52WHeQEHTaUDDGIXInXyqNh4Po,854
80
80
  udata/core/dataservices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  udata/core/dataservices/activities.py,sha256=wcYQCyYpKciCz99VqQqKti72a5Fyhc-AvDZBWdF0KUc,1763
82
- udata/core/dataservices/api.py,sha256=MUMOANkmkpQHF5gepdbhEZqjipVHSczK-2-t7jEc1Io,10052
82
+ udata/core/dataservices/api.py,sha256=_-ShQz9ijnFwy5xM7nJffA0YpHyXyXknFnsEuV1YvRY,10058
83
83
  udata/core/dataservices/apiv2.py,sha256=pd4UWF6m7pQ5nmBRFmeiDyedhZSngz211to6qTscVsA,1205
84
84
  udata/core/dataservices/constants.py,sha256=SxetyqvbWQlb-T9Bqn7mdfzV3vS-is7s56rzmGzTXY0,1029
85
85
  udata/core/dataservices/csv.py,sha256=HWI2JrN_Vuw0te9FHlJ6eyqcRcKHOKXuzg45D4Ti6F0,1106
@@ -92,7 +92,7 @@ udata/core/dataservices/tasks.py,sha256=fHG1r5ymfJRXJ_Lug6je3VKZoK30XKXE2rQ8x0R-
92
92
  udata/core/dataset/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
93
  udata/core/dataset/actions.py,sha256=mX6xox0PiMrbcAPZ3VZsI26rfM-ciYfEXxN6sqqImKA,1222
94
94
  udata/core/dataset/activities.py,sha256=eGxMUnC47YHxTgcls6igQ3qP7cYgwFtPfj0asCylGsI,3315
95
- udata/core/dataset/api.py,sha256=JBdYH2RXac1WVOF67ZzBSvImVxixMfN0rxEknnfthRU,35338
95
+ udata/core/dataset/api.py,sha256=ct3GNQjoAxZSYqb98rQkx85o-pj2qO5E5DMzCDGauN8,35585
96
96
  udata/core/dataset/api_fields.py,sha256=p7ZnmGNImZ4sgZTpoyHpI0CgOukpEIx8QdGnxlmgl2I,18032
97
97
  udata/core/dataset/apiv2.py,sha256=1H4557ZMi6rwEyrwB1Ha20m0bf3Avhg_vDLiDQt5Fi0,21030
98
98
  udata/core/dataset/commands.py,sha256=3mKSdJ-M7ggdG29AVn77C4ouZanbYoqkTaGQoBKOp3s,3471
@@ -145,7 +145,7 @@ udata/core/metrics/signals.py,sha256=9mdJW__gR2GJT3huBr6HN2SDhKYJRgNbW9dnh48cAnU
145
145
  udata/core/metrics/tasks.py,sha256=5hVSvBFF2-k_MZGn1XrzEWLgRp3TlrZJtPjX1Y_niS4,4963
146
146
  udata/core/organization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
147
  udata/core/organization/activities.py,sha256=Mw4-R8Q6G745IZnCDgrj7h2ax2crGYRhZtcewSK6_Ok,1213
148
- udata/core/organization/api.py,sha256=rfEGVsr64cfUEEN8zFA6xvDod8Chh-GOqRbuJ8cbaIM,22139
148
+ udata/core/organization/api.py,sha256=mjfo19OeLijo_0HyPElyYcxIiz_OqNYzjcREsUgheg4,22304
149
149
  udata/core/organization/api_fields.py,sha256=UlDYS62Wt1capSO0rYf6-m_rza3lCmpTuA93R-sV9w8,7776
150
150
  udata/core/organization/apiv2.py,sha256=fLMN5T7s8yF4u7c1WWHXMqQAlyH2OCSyAfmleiFlf7M,3037
151
151
  udata/core/organization/commands.py,sha256=DsRAtFDZvTciYNsUWumQWdn0jnNmKW-PwfIHUUZoBb8,1591
@@ -157,7 +157,7 @@ udata/core/organization/metrics.py,sha256=CEhkZLUufDyWi2XyizMoXkuddz7xDJvmdkPTwe
157
157
  udata/core/organization/models.py,sha256=QYneMUDA99NsMHn4_pGn1dnnDK4dESVUy5ogo8Tm7ZI,10962
158
158
  udata/core/organization/notifications.py,sha256=i_36-l2y7fOGmnBmr5NDWmGGmrGRaCWbU-6XS4c2wQs,917
159
159
  udata/core/organization/permissions.py,sha256=GD-9TMtRppVCPaC1ysXYrONvGJV-ArzAOXm2XMKf9yo,1256
160
- udata/core/organization/rdf.py,sha256=fFyxN0szeiQjQyVJbSN_4VqHxOE0PdSLg9A-K66Gdng,1785
160
+ udata/core/organization/rdf.py,sha256=KYJXTE5Yxhp3Cb7GZsRT1BY3Bd7rcRfwFSK9dWG2xQ4,1807
161
161
  udata/core/organization/search.py,sha256=5XMZn7xehkTV4X9NvGys_1WyHDGlUVpCQ_uJ3oyWdsE,2227
162
162
  udata/core/organization/signals.py,sha256=Ft1MBU9S41uxFN-rUrKHhiwedR1OYGv-ncuroKg8orY,488
163
163
  udata/core/organization/tasks.py,sha256=2Pd9ixeKNd7BGxBHrHYbR9AelhqkflX3ocnGgtoa8Uc,5641
@@ -194,11 +194,11 @@ udata/core/reuse/search.py,sha256=y1DwXYkBMBwuhn62CULkU1NNo89IYp0Ae7U01jcnjBY,31
194
194
  udata/core/reuse/signals.py,sha256=nDrEUpYKN0AdYiEbrR0z3nzXzjaRcD8SAMutwIDsQPM,155
195
195
  udata/core/reuse/tasks.py,sha256=cc6f06HMFCDHRsdgsG09FBYsITNQ8xzdZjE-pMY61a0,1688
196
196
  udata/core/site/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
197
- udata/core/site/api.py,sha256=ekZmQs5U3ywPy4vqpgd0mB28O__HZXnrmg5AXaGApnI,7838
197
+ udata/core/site/api.py,sha256=GhSZfdv0rQgtmqG3h0uGcXrzbX9Zx3GuypgaxcAClsg,8139
198
198
  udata/core/site/factories.py,sha256=O0nLFmjrFyemzcs-YwNukq5nqd383KBGrgL5bOQsGbA,378
199
199
  udata/core/site/forms.py,sha256=avEZ4rJhibF7sKexqMlKt1qMFiUHNrmJGKjZqcAyjrc,459
200
200
  udata/core/site/models.py,sha256=mlSsnXUoMmIZ26-mvotWpICaoL1ScbYMsc85Dt-28cI,8322
201
- udata/core/site/rdf.py,sha256=Dk1lnszASh_2v9tcI4uoN5yQ2DxCxUt4r4YY3pkWqEc,2015
201
+ udata/core/site/rdf.py,sha256=CT4TIu9DMiYHaNugW873UAJsQLBtzqQIr_a24CMyEJ8,2037
202
202
  udata/core/spam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
203
203
  udata/core/spam/api.py,sha256=eqmDB4_VF5EM92675FqFmk4Be5TAnu87dmSVtgXKF4A,1740
204
204
  udata/core/spam/constants.py,sha256=dTj0GyPH2zdxhurxva2CGnL-3WwqZETyt5vYQhmVpow,144
@@ -516,7 +516,7 @@ udata/tests/api/test_activities_api.py,sha256=GzyznB1rHrWfPH-te5RKmA3Ezlo5jlP3z7
516
516
  udata/tests/api/test_auth_api.py,sha256=Mue5EneRp3MbD-ahcXI3YaRNYPcxzHMw-hYpY0RtGVU,25569
517
517
  udata/tests/api/test_base_api.py,sha256=2w_vz0eEuq3P3aN-ByvxGc3VZAo7XtgatFfcrzf2uEU,2244
518
518
  udata/tests/api/test_contact_points.py,sha256=Sbb486RTN7HVycna_XB60OnURPSNc7xUity26XsYA4k,8766
519
- udata/tests/api/test_dataservices_api.py,sha256=vQZc8gey1zTckaWuqodXHJ95jp0O5ksVLllE1n3iqoE,31496
519
+ udata/tests/api/test_dataservices_api.py,sha256=scju3nQVO51NyNxeCagi0znjMQozPBbp5BQKP-BuhyY,28961
520
520
  udata/tests/api/test_datasets_api.py,sha256=EkMJndRMNHpVVC7ar-F8i_PXdGyvd-bCoVbnsaatH3s,104911
521
521
  udata/tests/api/test_fields.py,sha256=OW85Z5MES5HeWOpapeem8OvR1cIcrqW-xMWpdZO4LZ8,1033
522
522
  udata/tests/api/test_follow_api.py,sha256=4nFXG5pZ_Hf2PJ4KEdHJX_uggjc9RpB8v0fidkAcw9I,5792
@@ -540,7 +540,7 @@ udata/tests/contact_point/test_contact_point_models.py,sha256=b8vraZPPrs9LeQMWLn
540
540
  udata/tests/data/image.jpg,sha256=hdmpaCjOhmAAfNGuTqWKEjv7IC4GXJx-nP_rT274hc8,337049
541
541
  udata/tests/data/image.png,sha256=GAqXz7w_u7CapODIUF45UpVddmqelnGQkcrwKZq3448,266488
542
542
  udata/tests/dataservice/test_csv_adapter.py,sha256=UyDPiqKIDwe0AdXLKLpSsBqe4ZJsfkzcZ6SxMns2WnY,1984
543
- udata/tests/dataservice/test_dataservice_rdf.py,sha256=qN-vzaVK8d-Hq-NKBl9teGnnyWFyO3cVqVnC2f-EOqY,3623
543
+ udata/tests/dataservice/test_dataservice_rdf.py,sha256=a2ptzGaNUYOYCwC-uUAmg6bdYJeGeI7zPohJY0QEHl0,6217
544
544
  udata/tests/dataservice/test_dataservice_tasks.py,sha256=9XUoYLb_p3dzmgLfswaXjMG2civP0i1kzzP4VdJWE0k,1614
545
545
  udata/tests/dataset/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
546
546
  udata/tests/dataset/test_csv_adapter.py,sha256=Qy3dKg7OVz7paEOrRzHe-plBGsZgkalfW30SJK1JB0g,3905
@@ -548,7 +548,7 @@ udata/tests/dataset/test_dataset_actions.py,sha256=bgDjVYjOvu3sX_FCTCzf2snZYSprs
548
548
  udata/tests/dataset/test_dataset_commands.py,sha256=zMPJG2wYwKBee2zI65kmboxf59Zqa84DDjT8V5wj9uo,801
549
549
  udata/tests/dataset/test_dataset_events.py,sha256=hlrpoOiBbnX_COUI9Pzdqlp45GZZDqu5piwupbnPiTI,3601
550
550
  udata/tests/dataset/test_dataset_model.py,sha256=plzvIaEqvxQpcYS8YlDBltjuTgtT7xgNVEzkmlPZBC0,33766
551
- udata/tests/dataset/test_dataset_rdf.py,sha256=8Gv_v1nRIvopfKJ2R7KAwv63GnzGXr_yFR-qQ1_Kwx8,45945
551
+ udata/tests/dataset/test_dataset_rdf.py,sha256=aug8GJLvIjiutVVklQY8Oej6le5o_n8r6GVV05pLWv8,45949
552
552
  udata/tests/dataset/test_dataset_recommendations.py,sha256=K52HXGXi9DuUSiSRQWpqTrsFCkYbv7K3upNp0fFp5v8,7068
553
553
  udata/tests/dataset/test_dataset_tasks.py,sha256=SwSv-hDSXgs1xXtvtaePQ6fa6_d4ZpHo-oahDRinvPg,3271
554
554
  udata/tests/dataset/test_resource_preview.py,sha256=AxSF8cnG6bhy2pz9Fi3PfEzdLlNhZ0xrp9dr6yrt31A,2051
@@ -586,7 +586,7 @@ udata/tests/organization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
586
586
  udata/tests/organization/test_csv_adapter.py,sha256=61XIVDYdZxnWUyysN_wApWwghM7nT_Id-74IMtFd3Zg,1263
587
587
  udata/tests/organization/test_notifications.py,sha256=PGmME5BbYVtcPffeSE1sx47J6iAfm4RDAZIRv_MXDFE,1366
588
588
  udata/tests/organization/test_organization_model.py,sha256=ZEUHX7lvGMYMlHDpPiVUPeZeJeDDTp5owLEpFDSihjc,4311
589
- udata/tests/organization/test_organization_rdf.py,sha256=Uf-OgXV615wLUHfcBTmp9QFuV8VCFmjI4wgvShve-mc,8074
589
+ udata/tests/organization/test_organization_rdf.py,sha256=mAaMQHofgJ_sCPuE2v_3qDc3IO2EPiYSVu8HrP6E1Zk,8078
590
590
  udata/tests/organization/test_organization_tasks.py,sha256=9Ta-f4R3RoKdARYUFWHO4pE1nijceTHzyEvI34lYBJo,2876
591
591
  udata/tests/reuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
592
592
  udata/tests/reuse/test_reuse_model.py,sha256=PtdC39JrDCtF9iSfYSH98115309Q-E8aKn4s6rZCqCY,4662
@@ -600,7 +600,7 @@ udata/tests/site/test_site_api.py,sha256=pGisx3jtxTWNL9-R-tcCPBPYoQZM8oz8BJ4-seK
600
600
  udata/tests/site/test_site_csv_exports.py,sha256=hrUthkJw8yoSpEZNDHfzZIMEoXSBFosjhNVd5OxQDOE,17487
601
601
  udata/tests/site/test_site_metrics.py,sha256=KxlilOUeAbBta7HwD4mB4ybDWBmRB-3UVrLunvRGoGg,2362
602
602
  udata/tests/site/test_site_model.py,sha256=CNE-jP48guJpRlh4D3rQGruu2hSRliQaLOnBmzkHMnI,2181
603
- udata/tests/site/test_site_rdf.py,sha256=zlw7leYc7azfrC6JwDjKiy3wixQzTpo-lDOm-Dg_kXw,13092
603
+ udata/tests/site/test_site_rdf.py,sha256=8xGGd68WgjNj9mw52E8XTx1Byh3zDq2OgM2xg9RyMAU,15308
604
604
  udata/tests/topic/test_topic_tasks.py,sha256=6rotG7ZtRffpfaO-KFu4i5MRqHitbwvMwqXg1jFT6XU,1199
605
605
  udata/tests/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
606
606
  udata/tests/user/test_user_rdf.py,sha256=BRdv9ivLLUQoImFOuIohDoZAQXMXCruaNJ37siKul5o,1771
@@ -625,9 +625,9 @@ udata/translations/pt/LC_MESSAGES/udata.mo,sha256=nv80xZLfIfUsSOMBcr29L268FDc_Gt
625
625
  udata/translations/pt/LC_MESSAGES/udata.po,sha256=bUp-7Ray8t8ALgJk3Icw1jmiGIc9_pEJQHiGw_2EU2o,50989
626
626
  udata/translations/sr/LC_MESSAGES/udata.mo,sha256=Y_XpUxD074wXc63oJTnoVOyOQ2lmBxl-MrgluZ0Qdw4,27961
627
627
  udata/translations/sr/LC_MESSAGES/udata.po,sha256=qh8mrz9AFuVQtXYSSP4QWsXLM_Lv3EHVifHT1NflWXY,57529
628
- udata-12.0.2.dev12.dist-info/licenses/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
629
- udata-12.0.2.dev12.dist-info/METADATA,sha256=Q5Wk0TmHufgkuNdrCwlb4ROur_K-z2warRq0MeXgAgk,5174
630
- udata-12.0.2.dev12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
631
- udata-12.0.2.dev12.dist-info/entry_points.txt,sha256=v2u12qO11i2lyLNIp136WmLJ-NHT-Kew3Duu8J-AXPM,614
632
- udata-12.0.2.dev12.dist-info/top_level.txt,sha256=EF6CE6YSHd_og-8LCEA4q25ALUpWVe8D0okOLdMAE3A,6
633
- udata-12.0.2.dev12.dist-info/RECORD,,
628
+ udata-12.0.2.dev13.dist-info/licenses/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
629
+ udata-12.0.2.dev13.dist-info/METADATA,sha256=0p4YaygMwNbbY_fnRD4PmOzXTqOb40rc6-mwEAzrgls,5174
630
+ udata-12.0.2.dev13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
631
+ udata-12.0.2.dev13.dist-info/entry_points.txt,sha256=v2u12qO11i2lyLNIp136WmLJ-NHT-Kew3Duu8J-AXPM,614
632
+ udata-12.0.2.dev13.dist-info/top_level.txt,sha256=EF6CE6YSHd_og-8LCEA4q25ALUpWVe8D0okOLdMAE3A,6
633
+ udata-12.0.2.dev13.dist-info/RECORD,,