udata 6.2.1.dev26892__py2.py3-none-any.whl → 6.2.1.dev26916__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 (32) hide show
  1. udata/core/dataset/rdf.py +1 -0
  2. udata/core/topic/api.py +18 -3
  3. udata/core/topic/apiv2.py +19 -35
  4. udata/core/topic/models.py +3 -3
  5. udata/harvest/tests/test_dcat_backend.py +1 -0
  6. udata/models/__init__.py +0 -1
  7. udata/settings.py +2 -0
  8. udata/static/chunks/{11.a23c110811a9ac943478.js → 11.c0ccea08914b6b41568e.js} +3 -3
  9. udata/static/chunks/{11.a23c110811a9ac943478.js.map → 11.c0ccea08914b6b41568e.js.map} +1 -1
  10. udata/static/chunks/{13.0889e093f8664e38568c.js → 13.526a25163ababaa44409.js} +2 -2
  11. udata/static/chunks/{13.0889e093f8664e38568c.js.map → 13.526a25163ababaa44409.js.map} +1 -1
  12. udata/static/chunks/{16.f41599478d3e97ad9a30.js → 16.7901839b4227881947f6.js} +2 -2
  13. udata/static/chunks/{16.f41599478d3e97ad9a30.js.map → 16.7901839b4227881947f6.js.map} +1 -1
  14. udata/static/chunks/{19.2b534a26af8b17e9170b.js → 19.471d5a2a08eef6e5338a.js} +3 -3
  15. udata/static/chunks/{19.2b534a26af8b17e9170b.js.map → 19.471d5a2a08eef6e5338a.js.map} +1 -1
  16. udata/static/chunks/{5.da4db938a6ecf99fccbc.js → 5.6b73b3d83a8ff187132d.js} +3 -3
  17. udata/static/chunks/{5.da4db938a6ecf99fccbc.js.map → 5.6b73b3d83a8ff187132d.js.map} +1 -1
  18. udata/static/chunks/{6.16bb24fb8240f2746488.js → 6.e56975229e6065f68d2a.js} +3 -3
  19. udata/static/chunks/{6.16bb24fb8240f2746488.js.map → 6.e56975229e6065f68d2a.js.map} +1 -1
  20. udata/static/chunks/{9.3e752966ff14e47e11f2.js → 9.534426728626f11f4571.js} +2 -2
  21. udata/static/chunks/{9.3e752966ff14e47e11f2.js.map → 9.534426728626f11f4571.js.map} +1 -1
  22. udata/static/common.js +1 -1
  23. udata/static/common.js.map +1 -1
  24. udata/tests/api/test_reuses_api.py +3 -3
  25. udata/tests/api/test_topics_api.py +24 -1
  26. udata/tests/test_topics.py +14 -1
  27. {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26916.dist-info}/METADATA +3 -2
  28. {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26916.dist-info}/RECORD +32 -32
  29. {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26916.dist-info}/LICENSE +0 -0
  30. {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26916.dist-info}/WHEEL +0 -0
  31. {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26916.dist-info}/entry_points.txt +0 -0
  32. {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26916.dist-info}/top_level.txt +0 -0
udata/core/dataset/rdf.py CHANGED
@@ -424,6 +424,7 @@ def resource_from_rdf(graph_or_distrib, dataset=None):
424
424
  resource = Resource()
425
425
  if dataset:
426
426
  dataset.resources.append(resource)
427
+ resource.filetype = 'remote'
427
428
  resource.title = title_from_rdf(distrib, url)
428
429
  resource.url = url
429
430
  resource.description = sanitize_html(distrib.value(DCT.description))
udata/core/topic/api.py CHANGED
@@ -23,9 +23,15 @@ topic_fields = api.model('Topic', {
23
23
  'tags': fields.List(
24
24
  fields.String, description='Some keywords to help in search', required=True),
25
25
  'datasets': fields.List(
26
- fields.Nested(dataset_fields), description='The topic datasets'),
26
+ fields.Nested(dataset_fields),
27
+ description='The topic datasets',
28
+ attribute=lambda o: [d.fetch() for d in o.datasets],
29
+ ),
27
30
  'reuses': fields.List(
28
- fields.Nested(reuse_fields), description='The topic reuses'),
31
+ fields.Nested(reuse_fields),
32
+ description='The topic reuses',
33
+ attribute=lambda o: [r.fetch() for r in o.reuses],
34
+ ),
29
35
  'featured': fields.Boolean(description='Is the topic featured'),
30
36
  'private': fields.Boolean(description='Is the topic private'),
31
37
  'created_at': fields.ISODateTime(
@@ -43,7 +49,7 @@ topic_fields = api.model('Topic', {
43
49
  'topics.display', lambda o: {'topic': o},
44
50
  description='The topic page URL', readonly=True, fallback_endpoint='api.topic'),
45
51
  'extras': fields.Raw(description='Extras attributes as key-value pairs'),
46
- }, mask='*,datasets{id,title,uri,page},reuses{id,title, image, image_thumbnail,uri,page}')
52
+ }, mask='*,datasets{id,title,uri,page},reuses{id,title,image,image_thumbnail,uri,page}')
47
53
 
48
54
  topic_page_fields = api.model('TopicPage', fields.pager(topic_fields))
49
55
 
@@ -52,6 +58,10 @@ topic_parser = TopicApiParser()
52
58
 
53
59
  @ns.route('/', endpoint='topics')
54
60
  class TopicsAPI(API):
61
+ """
62
+ Warning: querying a list with a topic containing a lot of related objects (datasets, reuses)
63
+ will fail/take a lot of time because every object is dereferenced. Use api v2 if you can.
64
+ """
55
65
 
56
66
  @api.doc('list_topics')
57
67
  @api.expect(topic_parser.parser)
@@ -79,6 +89,11 @@ class TopicsAPI(API):
79
89
  @api.param('topic', 'The topic ID or slug')
80
90
  @api.response(404, 'Object not found')
81
91
  class TopicAPI(API):
92
+ """
93
+ Warning: querying a topic containing a lot of related objects (datasets, reuses)
94
+ will fail/take a lot of time because every object is dereferenced. Use api v2 if you can.
95
+ """
96
+
82
97
  @api.doc('get_topic')
83
98
  @api.marshal_with(topic_fields)
84
99
  def get(self, topic):
udata/core/topic/apiv2.py CHANGED
@@ -2,6 +2,7 @@ import logging
2
2
 
3
3
  import mongoengine
4
4
 
5
+ from bson import ObjectId
5
6
  from flask import url_for, request
6
7
 
7
8
  from udata.api import apiv2, API, fields
@@ -131,22 +132,8 @@ class TopicDatasetsAPI(API):
131
132
  @apiv2.response(400, 'Expecting a list')
132
133
  @apiv2.response(400, 'Expecting a list of dicts with id attribute')
133
134
  @apiv2.response(404, 'Topic not found')
134
- @apiv2.response(404, 'Dataset(s) not found')
135
135
  @apiv2.response(403, 'Forbidden')
136
136
  def post(self, topic):
137
- '''Add datasets to a given topic from a list of dataset ids'''
138
- def add_dataset(topic, dataset):
139
- if dataset not in topic.datasets:
140
- topic.datasets.append(dataset)
141
- return topic
142
-
143
- def get_dataset(dataset_id):
144
- try:
145
- dataset = Dataset.objects.get_or_404(id=dataset_id)
146
- except mongoengine.errors.ValidationError:
147
- apiv2.abort(400, 'Malformed object id(s) in request')
148
- return dataset
149
-
150
137
  if not TopicEditPermission(topic).can():
151
138
  apiv2.abort(403, 'Forbidden')
152
139
 
@@ -157,10 +144,15 @@ class TopicDatasetsAPI(API):
157
144
  if not all(isinstance(d, dict) and d.get('id') for d in data):
158
145
  apiv2.abort(400, 'Expecting a list of dicts with id attribute')
159
146
 
160
- datasets = (get_dataset(d['id']) for d in data)
161
- for dataset in datasets:
162
- topic = add_dataset(topic, dataset)
163
- topic.save()
147
+ try:
148
+ datasets = Dataset.objects.filter(id__in=[d['id'] for d in data]).only('id')
149
+ diff = set(d.id for d in datasets) - set(d.id for d in topic.datasets)
150
+ except mongoengine.errors.ValidationError:
151
+ apiv2.abort(400, 'Malformed object id(s) in request')
152
+
153
+ if diff:
154
+ topic.datasets += [ObjectId(did) for did in diff]
155
+ topic.save()
164
156
 
165
157
  return topic, 201
166
158
 
@@ -210,22 +202,9 @@ class TopicReusesAPI(API):
210
202
  @apiv2.response(400, 'Expecting a list')
211
203
  @apiv2.response(400, 'Expecting a list of dicts with id attribute')
212
204
  @apiv2.response(404, 'Topic not found')
213
- @apiv2.response(404, 'Reuse(s) not found')
214
205
  @apiv2.response(403, 'Forbidden')
215
206
  def post(self, topic):
216
207
  '''Add reuses to a given topic from a list of reuses ids'''
217
- def add_reuse(topic, reuse):
218
- if reuse.id not in (r.id for r in topic.reuses):
219
- topic.reuses.append(reuse)
220
- return topic
221
-
222
- def get_reuse(reuse_id):
223
- try:
224
- reuse = Reuse.objects.get_or_404(id=reuse_id)
225
- except mongoengine.errors.ValidationError:
226
- apiv2.abort(400, 'Malformed object id(s) in request')
227
- return reuse
228
-
229
208
  if not TopicEditPermission(topic).can():
230
209
  apiv2.abort(403, 'Forbidden')
231
210
 
@@ -236,10 +215,15 @@ class TopicReusesAPI(API):
236
215
  if not all(isinstance(d, dict) and d.get('id') for d in data):
237
216
  apiv2.abort(400, 'Expecting a list of dicts with id attribute')
238
217
 
239
- reuses = (get_reuse(d['id']) for d in data)
240
- for reuse in reuses:
241
- topic = add_reuse(topic, reuse)
242
- topic.save()
218
+ try:
219
+ reuses = Reuse.objects.filter(id__in=[r['id'] for r in data]).only('id')
220
+ diff = set(d.id for d in reuses) - set(d.id for d in topic.reuses)
221
+ except mongoengine.errors.ValidationError:
222
+ apiv2.abort(400, 'Malformed object id(s) in request')
223
+
224
+ if diff:
225
+ topic.reuses += [ObjectId(rid) for rid in diff]
226
+ topic.save()
243
227
 
244
228
  return topic, 201
245
229
 
@@ -20,9 +20,9 @@ class Topic(db.Document, db.Owned):
20
20
 
21
21
  tags = db.ListField(db.StringField())
22
22
  datasets = db.ListField(
23
- db.ReferenceField('Dataset', reverse_delete_rule=db.PULL))
23
+ db.LazyReferenceField('Dataset', reverse_delete_rule=db.PULL))
24
24
  reuses = db.ListField(
25
- db.ReferenceField('Reuse', reverse_delete_rule=db.PULL))
25
+ db.LazyReferenceField('Reuse', reverse_delete_rule=db.PULL))
26
26
 
27
27
  featured = db.BooleanField()
28
28
  private = db.BooleanField()
@@ -54,7 +54,7 @@ class Topic(db.Document, db.Owned):
54
54
  except cls.DoesNotExist:
55
55
  datasets_list_dif = document.datasets
56
56
  for dataset in datasets_list_dif:
57
- reindex.delay(*as_task_param(dataset))
57
+ reindex.delay(*as_task_param(dataset.fetch()))
58
58
 
59
59
  @property
60
60
  def display_url(self):
@@ -300,6 +300,7 @@ class DcatBackendTest:
300
300
  assert len(dataset.resources) == 2
301
301
 
302
302
  resource_1 = next(res for res in dataset.resources if res.title == 'Resource 1-1')
303
+ assert resource_1.filetype == 'remote'
303
304
  # Format is a IANA URI
304
305
  assert resource_1.format == 'json'
305
306
  assert resource_1.mime == 'application/json'
udata/models/__init__.py CHANGED
@@ -1,4 +1,3 @@
1
- import importlib
2
1
  import logging
3
2
  import warnings
4
3
 
udata/settings.py CHANGED
@@ -461,6 +461,8 @@ class Testing(object):
461
461
  WTF_CSRF_ENABLED = False
462
462
  AUTO_INDEX = False
463
463
  CELERY_TASK_ALWAYS_EAGER = True
464
+ # TODO: ideally, this should be set to True in order to reveal exceptions in delayed tasks
465
+ CELERY_TASK_EAGER_PROPAGATES = False
464
466
  TEST_WITH_PLUGINS = False
465
467
  PLUGINS = []
466
468
  TEST_WITH_THEME = False