udata 6.2.1.dev26892__py2.py3-none-any.whl → 6.2.1.dev26909__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.
- udata/core/topic/api.py +18 -3
- udata/core/topic/apiv2.py +19 -35
- udata/core/topic/models.py +3 -3
- udata/models/__init__.py +0 -1
- udata/settings.py +2 -0
- udata/static/chunks/{11.a23c110811a9ac943478.js → 11.c0ccea08914b6b41568e.js} +3 -3
- udata/static/chunks/{11.a23c110811a9ac943478.js.map → 11.c0ccea08914b6b41568e.js.map} +1 -1
- udata/static/chunks/{13.0889e093f8664e38568c.js → 13.526a25163ababaa44409.js} +2 -2
- udata/static/chunks/{13.0889e093f8664e38568c.js.map → 13.526a25163ababaa44409.js.map} +1 -1
- udata/static/chunks/{16.f41599478d3e97ad9a30.js → 16.7901839b4227881947f6.js} +2 -2
- udata/static/chunks/{16.f41599478d3e97ad9a30.js.map → 16.7901839b4227881947f6.js.map} +1 -1
- udata/static/chunks/{19.2b534a26af8b17e9170b.js → 19.471d5a2a08eef6e5338a.js} +3 -3
- udata/static/chunks/{19.2b534a26af8b17e9170b.js.map → 19.471d5a2a08eef6e5338a.js.map} +1 -1
- udata/static/chunks/{5.da4db938a6ecf99fccbc.js → 5.6b73b3d83a8ff187132d.js} +3 -3
- udata/static/chunks/{5.da4db938a6ecf99fccbc.js.map → 5.6b73b3d83a8ff187132d.js.map} +1 -1
- udata/static/chunks/{6.16bb24fb8240f2746488.js → 6.e56975229e6065f68d2a.js} +3 -3
- udata/static/chunks/{6.16bb24fb8240f2746488.js.map → 6.e56975229e6065f68d2a.js.map} +1 -1
- udata/static/chunks/{9.3e752966ff14e47e11f2.js → 9.534426728626f11f4571.js} +2 -2
- udata/static/chunks/{9.3e752966ff14e47e11f2.js.map → 9.534426728626f11f4571.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/tests/api/test_reuses_api.py +3 -3
- udata/tests/api/test_topics_api.py +24 -1
- udata/tests/test_topics.py +14 -1
- {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26909.dist-info}/METADATA +2 -2
- {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26909.dist-info}/RECORD +30 -30
- {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26909.dist-info}/LICENSE +0 -0
- {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26909.dist-info}/WHEEL +0 -0
- {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26909.dist-info}/entry_points.txt +0 -0
- {udata-6.2.1.dev26892.dist-info → udata-6.2.1.dev26909.dist-info}/top_level.txt +0 -0
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),
|
|
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),
|
|
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,
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
|
udata/core/topic/models.py
CHANGED
|
@@ -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.
|
|
23
|
+
db.LazyReferenceField('Dataset', reverse_delete_rule=db.PULL))
|
|
24
24
|
reuses = db.ListField(
|
|
25
|
-
db.
|
|
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):
|
udata/models/__init__.py
CHANGED
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
|