udata 10.8.4.dev37339__py2.py3-none-any.whl → 10.8.4.dev37405__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 (30) hide show
  1. udata/core/dataservices/api.py +23 -0
  2. udata/core/dataservices/csv.py +1 -0
  3. udata/core/dataservices/models.py +7 -0
  4. udata/core/dataservices/rdf.py +4 -1
  5. udata/core/dataservices/search.py +5 -0
  6. udata/core/dataset/rdf.py +3 -2
  7. udata/rdf.py +19 -2
  8. udata/static/chunks/{10.8ca60413647062717b1e.js → 10.471164b2a9fe15614797.js} +3 -3
  9. udata/static/chunks/{10.8ca60413647062717b1e.js.map → 10.471164b2a9fe15614797.js.map} +1 -1
  10. udata/static/chunks/{11.b6f741fcc366abfad9c4.js → 11.51d706fb9521c16976bc.js} +3 -3
  11. udata/static/chunks/{11.b6f741fcc366abfad9c4.js.map → 11.51d706fb9521c16976bc.js.map} +1 -1
  12. udata/static/chunks/{13.2d06442dd9a05d9777b5.js → 13.f29411b06be1883356a3.js} +2 -2
  13. udata/static/chunks/{13.2d06442dd9a05d9777b5.js.map → 13.f29411b06be1883356a3.js.map} +1 -1
  14. udata/static/chunks/{17.e8e4caaad5cb0cc0bacc.js → 17.3bd0340930d4a314ce9c.js} +2 -2
  15. udata/static/chunks/{17.e8e4caaad5cb0cc0bacc.js.map → 17.3bd0340930d4a314ce9c.js.map} +1 -1
  16. udata/static/chunks/{19.f03a102365af4315f9db.js → 19.8da42e8359d72afc2618.js} +3 -3
  17. udata/static/chunks/{19.f03a102365af4315f9db.js.map → 19.8da42e8359d72afc2618.js.map} +1 -1
  18. udata/static/chunks/{8.778091d55cd8ea39af6b.js → 8.54e44b102164ae5e7a67.js} +2 -2
  19. udata/static/chunks/{8.778091d55cd8ea39af6b.js.map → 8.54e44b102164ae5e7a67.js.map} +1 -1
  20. udata/static/chunks/{9.033d7e190ca9e226a5d0.js → 9.07515e5187f475bce828.js} +3 -3
  21. udata/static/chunks/{9.033d7e190ca9e226a5d0.js.map → 9.07515e5187f475bce828.js.map} +1 -1
  22. udata/static/common.js +1 -1
  23. udata/static/common.js.map +1 -1
  24. udata/tests/dataset/test_dataset_rdf.py +19 -0
  25. {udata-10.8.4.dev37339.dist-info → udata-10.8.4.dev37405.dist-info}/METADATA +3 -1
  26. {udata-10.8.4.dev37339.dist-info → udata-10.8.4.dev37405.dist-info}/RECORD +30 -30
  27. {udata-10.8.4.dev37339.dist-info → udata-10.8.4.dev37405.dist-info}/LICENSE +0 -0
  28. {udata-10.8.4.dev37339.dist-info → udata-10.8.4.dev37405.dist-info}/WHEEL +0 -0
  29. {udata-10.8.4.dev37339.dist-info → udata-10.8.4.dev37405.dist-info}/entry_points.txt +0 -0
  30. {udata-10.8.4.dev37339.dist-info → udata-10.8.4.dev37405.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,7 @@ from flask_login import current_user
9
9
 
10
10
  from udata.api import API, api, fields
11
11
  from udata.api_fields import patch
12
+ from udata.auth import admin_permission
12
13
  from udata.core.dataservices.constants import DATASERVICE_ACCESS_TYPE_RESTRICTED
13
14
  from udata.core.dataset.models import Dataset
14
15
  from udata.core.followers.api import FollowAPI
@@ -138,6 +139,28 @@ class DataserviceAPI(API):
138
139
  return "", 204
139
140
 
140
141
 
142
+ @ns.route("/<dataservice:dataservice>/featured/", endpoint="dataservice_featured")
143
+ @api.doc(**common_doc)
144
+ class ReuseFeaturedAPI(API):
145
+ @api.doc("feature_dataservice")
146
+ @api.secure(admin_permission)
147
+ @api.marshal_with(Dataservice.__read_fields__)
148
+ def post(self, dataservice):
149
+ """Mark a dataservice as featured"""
150
+ dataservice.featured = True
151
+ dataservice.save()
152
+ return dataservice
153
+
154
+ @api.doc("unfeature_dataservice")
155
+ @api.secure(admin_permission)
156
+ @api.marshal_with(Dataservice.__read_fields__)
157
+ def delete(self, dataservice):
158
+ """Unmark a dataservice as featured"""
159
+ dataservice.featured = False
160
+ dataservice.save()
161
+ return dataservice
162
+
163
+
141
164
  dataservice_add_datasets_fields = api.model(
142
165
  "DataserviceDatasetsAdd",
143
166
  {
@@ -25,6 +25,7 @@ class DataserviceCsvAdapter(csv.Adapter):
25
25
  ("organization_id", "organization.id"),
26
26
  ("owner", "owner.slug"), # in case it's owned by a user
27
27
  ("owner_id", "owner.id"),
28
+ ("featured", lambda d: d.featured or False),
28
29
  "created_at",
29
30
  "metadata_modified_at",
30
31
  ("archived", lambda d: d.archived_at or False),
@@ -224,6 +224,13 @@ class Dataservice(Auditable, WithMetrics, Linkable, Owned, db.Document):
224
224
  auditable=False,
225
225
  )
226
226
 
227
+ featured = field(
228
+ db.BooleanField(),
229
+ filterable={},
230
+ readonly=True,
231
+ auditable=False,
232
+ )
233
+
227
234
  contact_points = field(
228
235
  db.ListField(
229
236
  field(
@@ -14,6 +14,7 @@ from udata.rdf import (
14
14
  TAG_TO_EU_HVD_CATEGORIES,
15
15
  contact_points_from_rdf,
16
16
  contact_points_to_rdf,
17
+ default_lang_value,
17
18
  namespace_manager,
18
19
  rdf_value,
19
20
  remote_url_from_rdf,
@@ -38,7 +39,9 @@ def dataservice_from_rdf(
38
39
  d = graph.resource(node)
39
40
 
40
41
  dataservice.title = rdf_value(d, DCT.title)
41
- dataservice.description = sanitize_html(d.value(DCT.description) or d.value(DCT.abstract))
42
+ dataservice.description = sanitize_html(
43
+ default_lang_value(d, DCT.description) or default_lang_value(d, DCT.abstract)
44
+ )
42
45
 
43
46
  dataservice.base_api_url = url_from_rdf(d, DCAT.endpointURL)
44
47
 
@@ -35,6 +35,7 @@ class DataserviceApiParser(ModelApiParser):
35
35
  self.parser.add_argument("tag", type=str, location="args")
36
36
  self.parser.add_argument("organization", type=str, location="args")
37
37
  self.parser.add_argument("is_restricted", type=bool, location="args")
38
+ self.parser.add_argument("featured", type=bool, location="args")
38
39
 
39
40
  @staticmethod
40
41
  def parse_filters(dataservices, args):
@@ -57,6 +58,8 @@ class DataserviceApiParser(ModelApiParser):
57
58
  if boolean(args["is_restricted"])
58
59
  else [DATASERVICE_ACCESS_TYPE_OPEN, DATASERVICE_ACCESS_TYPE_OPEN_WITH_ACCOUNT]
59
60
  )
61
+ if args.get("featured"):
62
+ dataservices = dataservices.filter(featured=args["featured"])
60
63
  return dataservices
61
64
 
62
65
 
@@ -71,6 +74,7 @@ class DataserviceSearch(ModelSearchAdapter):
71
74
  "tag": Filter(),
72
75
  "organization": ModelTermsFilter(model=Organization),
73
76
  "archived": BoolFilter(),
77
+ "featured": BoolFilter(),
74
78
  }
75
79
 
76
80
  @classmethod
@@ -116,6 +120,7 @@ class DataserviceSearch(ModelSearchAdapter):
116
120
  "archived": to_iso_datetime(dataservice.archived_at)
117
121
  if dataservice.archived_at
118
122
  else None,
123
+ "featured": 1 if dataservice.featured else 0,
119
124
  "organization": organization,
120
125
  "owner": str(owner.id) if owner else None,
121
126
  "tags": dataservice.tags,
udata/core/dataset/rdf.py CHANGED
@@ -40,6 +40,7 @@ from udata.rdf import (
40
40
  TAG_TO_EU_HVD_CATEGORIES,
41
41
  contact_points_from_rdf,
42
42
  contact_points_to_rdf,
43
+ default_lang_value,
43
44
  namespace_manager,
44
45
  rdf_unique_values,
45
46
  rdf_value,
@@ -656,7 +657,7 @@ def resource_from_rdf(graph_or_distrib, dataset=None, is_additionnal=False):
656
657
  resource.filetype = "remote"
657
658
  resource.title = title_from_rdf(distrib, url)
658
659
  resource.url = url
659
- resource.description = sanitize_html(distrib.value(DCT.description))
660
+ resource.description = sanitize_html(default_lang_value(distrib, DCT.description))
660
661
  resource.filesize = rdf_value(distrib, DCAT.byteSize)
661
662
  resource.mime = mime_from_rdf(distrib)
662
663
  resource.format = format_from_rdf(distrib)
@@ -731,7 +732,7 @@ def dataset_from_rdf(graph: Graph, dataset=None, node=None, remote_url_prefix: s
731
732
  raise HarvestSkipException("missing title on dataset")
732
733
 
733
734
  # Support dct:abstract if dct:description is missing (sometimes used instead)
734
- description = d.value(DCT.description) or d.value(DCT.abstract)
735
+ description = default_lang_value(d, DCT.description) or default_lang_value(d, DCT.abstract)
735
736
  dataset.description = sanitize_html(description)
736
737
  dataset.frequency = frequency_from_rdf(d.value(DCT.accrualPeriodicity)) or dataset.frequency
737
738
  roles = [ # Imbricated list of contact points for each role
udata/rdf.py CHANGED
@@ -239,11 +239,28 @@ def rdf_unique_values(resource, predicate, parse_label=False) -> set[str]:
239
239
 
240
240
 
241
241
  def rdf_value(obj, predicate, default=None, parse_label=False):
242
- """Serialize the value for a predicate on a RdfResource"""
243
- value = obj.value(predicate)
242
+ """
243
+ Serialize the value for a predicate on a RdfResource,
244
+ expecting one value only or (at most) one per language for Literals.
245
+ """
246
+ value = default_lang_value(obj, predicate)
244
247
  return serialize_value(value, parse_label=parse_label) if value else default
245
248
 
246
249
 
250
+ def default_lang_value(obj, predicate):
251
+ """
252
+ Return the value with the default language if multiple Literal values exist in different languages.
253
+ """
254
+ candidate_values = list(obj.objects(predicate))
255
+ if not candidate_values:
256
+ return None
257
+ for val in candidate_values:
258
+ if isinstance(val, Literal) and val.language == current_app.config["DEFAULT_LANGUAGE"]:
259
+ return val
260
+ # Defaulting to the first value found
261
+ return candidate_values[0]
262
+
263
+
247
264
  class HTMLDetector(HTMLParser):
248
265
  def __init__(self, *args, **kwargs):
249
266
  HTMLParser.__init__(self, *args, **kwargs)