udata 9.0.1.dev29468__py2.py3-none-any.whl → 9.0.1.dev29504__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/api_fields.py +31 -0
- udata/core/dataservices/models.py +4 -1
- udata/harvest/api.py +5 -0
- udata/tests/api/test_dataservices_api.py +26 -1
- {udata-9.0.1.dev29468.dist-info → udata-9.0.1.dev29504.dist-info}/METADATA +3 -1
- {udata-9.0.1.dev29468.dist-info → udata-9.0.1.dev29504.dist-info}/RECORD +10 -10
- {udata-9.0.1.dev29468.dist-info → udata-9.0.1.dev29504.dist-info}/LICENSE +0 -0
- {udata-9.0.1.dev29468.dist-info → udata-9.0.1.dev29504.dist-info}/WHEEL +0 -0
- {udata-9.0.1.dev29468.dist-info → udata-9.0.1.dev29504.dist-info}/entry_points.txt +0 -0
- {udata-9.0.1.dev29468.dist-info → udata-9.0.1.dev29504.dist-info}/top_level.txt +0 -0
udata/api_fields.py
CHANGED
|
@@ -96,6 +96,7 @@ def generate_fields(**kwargs):
|
|
|
96
96
|
read_fields = {}
|
|
97
97
|
write_fields = {}
|
|
98
98
|
sortables = []
|
|
99
|
+
filterables = []
|
|
99
100
|
|
|
100
101
|
read_fields['id'] = restx_fields.String(required=True)
|
|
101
102
|
|
|
@@ -106,6 +107,23 @@ def generate_fields(**kwargs):
|
|
|
106
107
|
if info.get('sortable', False):
|
|
107
108
|
sortables.append(key)
|
|
108
109
|
|
|
110
|
+
filterable = info.get('filterable', None)
|
|
111
|
+
if filterable is not None:
|
|
112
|
+
if 'key' not in filterable:
|
|
113
|
+
filterable['key'] = key
|
|
114
|
+
if 'column' not in filterable:
|
|
115
|
+
filterable['column'] = key
|
|
116
|
+
|
|
117
|
+
if 'constraints' not in filterable:
|
|
118
|
+
filterable['constraints'] = []
|
|
119
|
+
if isinstance(field, mongo_fields.ReferenceField) or (isinstance(field, mongo_fields.ListField) and isinstance(field.field, mongo_fields.ReferenceField)):
|
|
120
|
+
filterable['constraints'].append('objectid')
|
|
121
|
+
|
|
122
|
+
# We may add more information later here:
|
|
123
|
+
# - type of mongo query to execute (right now only simple =)
|
|
124
|
+
|
|
125
|
+
filterables.append(filterable)
|
|
126
|
+
|
|
109
127
|
read, write = convert_db_to_field(key, field)
|
|
110
128
|
|
|
111
129
|
if read:
|
|
@@ -159,6 +177,9 @@ def generate_fields(**kwargs):
|
|
|
159
177
|
choices = sortables + ['-' + k for k in sortables]
|
|
160
178
|
parser.add_argument('sort', type=str, location='args', choices=choices, help='The field (and direction) on which sorting apply')
|
|
161
179
|
|
|
180
|
+
for filterable in filterables:
|
|
181
|
+
parser.add_argument(filterable['key'], type=str, location='args')
|
|
182
|
+
|
|
162
183
|
cls.__index_parser__ = parser
|
|
163
184
|
def apply_sort_filters_and_pagination(base_query):
|
|
164
185
|
args = cls.__index_parser__.parse_args()
|
|
@@ -166,6 +187,16 @@ def generate_fields(**kwargs):
|
|
|
166
187
|
if sortables and args['sort']:
|
|
167
188
|
base_query = base_query.order_by(args['sort'])
|
|
168
189
|
|
|
190
|
+
for filterable in filterables:
|
|
191
|
+
if args.get(filterable['key']):
|
|
192
|
+
for constraint in filterable['constraints']:
|
|
193
|
+
if constraint == 'objectid' and not ObjectId.is_valid(args[filterable['key']]):
|
|
194
|
+
api.abort(400, f'`{filterable["key"]}` must be an identifier')
|
|
195
|
+
|
|
196
|
+
base_query = base_query.filter(**{
|
|
197
|
+
filterable['column']: args[filterable['key']],
|
|
198
|
+
})
|
|
199
|
+
|
|
169
200
|
if paginable:
|
|
170
201
|
base_query = base_query.paginate(args['page'], args['page_size'])
|
|
171
202
|
|
|
@@ -113,7 +113,10 @@ class Dataservice(WithMetrics, Owned, db.Document):
|
|
|
113
113
|
db.ReferenceField(Dataset),
|
|
114
114
|
nested_fields=datasets_api_fields.dataset_fields,
|
|
115
115
|
)
|
|
116
|
-
)
|
|
116
|
+
),
|
|
117
|
+
filterable={
|
|
118
|
+
'key': 'dataset',
|
|
119
|
+
},
|
|
117
120
|
)
|
|
118
121
|
|
|
119
122
|
@function_field(description="Link to the API endpoint for this dataservice")
|
udata/harvest/api.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from bson import ObjectId
|
|
1
2
|
from werkzeug.exceptions import BadRequest
|
|
2
3
|
from flask import request
|
|
3
4
|
|
|
@@ -190,6 +191,10 @@ class SourcesAPI(API):
|
|
|
190
191
|
def get(self):
|
|
191
192
|
'''List all harvest sources'''
|
|
192
193
|
args = source_parser.parse_args()
|
|
194
|
+
|
|
195
|
+
if args.get('owner') and not ObjectId.is_valid(args.get('owner')):
|
|
196
|
+
api.abort(400, '`owner` arg must be an identifier')
|
|
197
|
+
|
|
193
198
|
return actions.paginate_sources(args.get('owner'),
|
|
194
199
|
page=args['page'],
|
|
195
200
|
page_size=args['page_size'],
|
|
@@ -85,23 +85,30 @@ class DataserviceAPITest(APITestCase):
|
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
def test_dataservice_api_index(self):
|
|
88
|
+
dataset_a = DatasetFactory()
|
|
89
|
+
dataset_b = DatasetFactory()
|
|
90
|
+
|
|
88
91
|
self.login()
|
|
89
92
|
self.post(url_for('api.dataservices'), {
|
|
90
93
|
'title': 'B',
|
|
91
94
|
'base_api_url': 'https://example.org/B',
|
|
95
|
+
'datasets': [dataset_b.id],
|
|
92
96
|
})
|
|
93
97
|
self.post(url_for('api.dataservices'), {
|
|
94
98
|
'title': 'C',
|
|
95
99
|
'base_api_url': 'https://example.org/C',
|
|
100
|
+
'datasets': [dataset_a.id, dataset_b.id],
|
|
96
101
|
})
|
|
97
102
|
self.post(url_for('api.dataservices'), {
|
|
98
103
|
'title': 'A',
|
|
99
104
|
'base_api_url': 'https://example.org/A',
|
|
105
|
+
'datasets': [dataset_a.id],
|
|
100
106
|
})
|
|
101
|
-
|
|
107
|
+
self.post(url_for('api.dataservices'), {
|
|
102
108
|
'title': 'X',
|
|
103
109
|
'base_api_url': 'https://example.org/X',
|
|
104
110
|
'private': True,
|
|
111
|
+
'datasets': [dataset_a.id],
|
|
105
112
|
})
|
|
106
113
|
|
|
107
114
|
self.assertEqual(Dataservice.objects.count(), 4)
|
|
@@ -115,8 +122,12 @@ class DataserviceAPITest(APITestCase):
|
|
|
115
122
|
self.assertEqual(response.json['total'], 3)
|
|
116
123
|
self.assertEqual(len(response.json['data']), 3)
|
|
117
124
|
self.assertEqual(response.json['data'][0]['title'], 'B')
|
|
125
|
+
self.assertEqual(response.json['data'][0]['datasets'][0]['id'], str(dataset_b.id))
|
|
118
126
|
self.assertEqual(response.json['data'][1]['title'], 'C')
|
|
127
|
+
self.assertEqual(response.json['data'][1]['datasets'][0]['id'], str(dataset_a.id))
|
|
128
|
+
self.assertEqual(response.json['data'][1]['datasets'][1]['id'], str(dataset_b.id))
|
|
119
129
|
self.assertEqual(response.json['data'][2]['title'], 'A')
|
|
130
|
+
self.assertEqual(response.json['data'][2]['datasets'][0]['id'], str(dataset_a.id))
|
|
120
131
|
|
|
121
132
|
response = self.get(url_for('api.dataservices', sort='title'))
|
|
122
133
|
self.assert200(response)
|
|
@@ -153,6 +164,20 @@ class DataserviceAPITest(APITestCase):
|
|
|
153
164
|
self.assertEqual(len(response.json['data']), 1)
|
|
154
165
|
self.assertEqual(response.json['data'][0]['title'], 'B')
|
|
155
166
|
|
|
167
|
+
response = self.get(url_for('api.dataservices', sort='title', dataset=str(dataset_a.id)))
|
|
168
|
+
self.assert200(response)
|
|
169
|
+
|
|
170
|
+
self.assertEqual(response.json['total'], 2)
|
|
171
|
+
self.assertEqual(response.json['data'][0]['title'], 'A')
|
|
172
|
+
self.assertEqual(response.json['data'][0]['datasets'][0]['id'], str(dataset_a.id))
|
|
173
|
+
self.assertEqual(response.json['data'][1]['title'], 'C')
|
|
174
|
+
self.assertEqual(response.json['data'][1]['datasets'][0]['id'], str(dataset_a.id))
|
|
175
|
+
self.assertEqual(response.json['data'][1]['datasets'][1]['id'], str(dataset_b.id))
|
|
176
|
+
|
|
177
|
+
def test_dataservice_api_index_with_wrong_dataset_id(self):
|
|
178
|
+
response = self.get(url_for('api.dataservices', sort='title', dataset=str("xxx")))
|
|
179
|
+
self.assert400(response)
|
|
180
|
+
|
|
156
181
|
def test_dataservice_api_create_with_validation_error(self):
|
|
157
182
|
self.login()
|
|
158
183
|
response = self.post(url_for('api.dataservices'), {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: udata
|
|
3
|
-
Version: 9.0.1.
|
|
3
|
+
Version: 9.0.1.dev29504
|
|
4
4
|
Summary: Open data portal
|
|
5
5
|
Home-page: https://github.com/opendatateam/udata
|
|
6
6
|
Author: Opendata Team
|
|
@@ -138,8 +138,10 @@ It is collectively taken care of by members of the
|
|
|
138
138
|
## Current (in progress)
|
|
139
139
|
|
|
140
140
|
- Refactor catalog exports [#3052](https://github.com/opendatateam/udata/pull/3052)
|
|
141
|
+
- Add a filter to filter dataservices by dataset [#3056](https://github.com/opendatateam/udata/pull/3056)
|
|
141
142
|
- Fix reuses' datasets references [#3057](https://github.com/opendatateam/udata/pull/3057)
|
|
142
143
|
- Save and show harvest logs [#3053](https://github.com/opendatateam/udata/pull/3053)
|
|
144
|
+
- Fix missing `ObjectId` validation on `/sources` endpoint [#3060](https://github.com/opendatateam/udata/pull/3060)
|
|
143
145
|
|
|
144
146
|
## 9.0.0 (2024-06-07)
|
|
145
147
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
tasks/__init__.py,sha256=CnVhb_TV-6nMhxVR6itnBmvuU2OSCs02AfNB4irVBTE,8132
|
|
2
2
|
tasks/helpers.py,sha256=k_HiuiEJNgQLvWdeHqczPOAcrYpFjEepBeKo7EQzY8M,994
|
|
3
3
|
udata/__init__.py,sha256=SslifvQytly1ztvHCc4bdlP3Va1F5UV60hSJC6a_guk,101
|
|
4
|
-
udata/api_fields.py,sha256=
|
|
4
|
+
udata/api_fields.py,sha256=PMD206aJmntO4wizzL5Bv9_qU3HivAHRNO9ypV4nlXg,12959
|
|
5
5
|
udata/app.py,sha256=6upwrImLaWrSYtsXPW1zH84_oRxp3B6XFuocMe2D6NU,7329
|
|
6
6
|
udata/assets.py,sha256=aMa-MnAEXVSTavptSn2V8sUE6jL_N0MrYCQ6_QpsuHs,645
|
|
7
7
|
udata/entrypoints.py,sha256=8bZUvZ8ICO3kZxa8xDUaen6YS_OAGNKHFvaD7d8BOk0,2687
|
|
@@ -80,7 +80,7 @@ udata/core/contact_point/forms.py,sha256=ggLhSJ1IRn5MclrhydckjAxwr4fFZxgAD4huSSu
|
|
|
80
80
|
udata/core/contact_point/models.py,sha256=NlNKureCpzgTLJuGviZPjNx-ABYRp4j2L-ur9Gmixao,324
|
|
81
81
|
udata/core/dataservices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
82
82
|
udata/core/dataservices/api.py,sha256=rjCU55NNGgCDRlurfhJUT2byBGJWN5coM8b7AApzEew,3090
|
|
83
|
-
udata/core/dataservices/models.py,sha256=
|
|
83
|
+
udata/core/dataservices/models.py,sha256=rCJW_Bh9Ovc5oL_RH69nyexRYH_JPpudCT_L-RKzsvc,4512
|
|
84
84
|
udata/core/dataservices/permissions.py,sha256=X9Bh8e0pnx6OgeEf6NowXZUiwyreUa6UY479B16cCqs,175
|
|
85
85
|
udata/core/dataservices/tasks.py,sha256=NOWcTPoLasMrrvq9EkwQMGlUbQQmi_l3s815K-mtZTM,971
|
|
86
86
|
udata/core/dataset/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -267,7 +267,7 @@ udata/frontend/csv.py,sha256=SDV8GMhNpHyE9NYy9cfHulsFUzwclfc7roWAAm-PZ9M,8257
|
|
|
267
267
|
udata/frontend/markdown.py,sha256=41bOiU6AKng4U-5v3otBed3dyCu63NI9fnznUQThbIk,4377
|
|
268
268
|
udata/harvest/__init__.py,sha256=C4y5w4vGb_F9Opy62lzV3eHo4DkNyRgPCq-wsarPXiQ,28
|
|
269
269
|
udata/harvest/actions.py,sha256=6f9bkIITLHes0vGU-yioRGfFOghAS3nXThXEVKaHFpA,10082
|
|
270
|
-
udata/harvest/api.py,sha256=
|
|
270
|
+
udata/harvest/api.py,sha256=GDFqjAyKh4a22QDniyZZC9AyTFi4ndhS94VzW6plBZE,14545
|
|
271
271
|
udata/harvest/commands.py,sha256=Vo1KxO7GAOgQ87khqlGIyjZIlwAOlQ8ztadqmWdMvwk,4922
|
|
272
272
|
udata/harvest/csv.py,sha256=c2fPnMB6q99wRxLncl8L0ILcdF4SI8Lsl8tViNrcW6A,396
|
|
273
273
|
udata/harvest/exceptions.py,sha256=YaXw0ESmSCcibfUmP5uc1uRedKD2mvUBXUOnbaSXtNw,299
|
|
@@ -590,7 +590,7 @@ udata/tests/api/__init__.py,sha256=gIIB9OPiSs1A-u1bEIHlOesaCQzJ08SOLPuJd34LesE,1
|
|
|
590
590
|
udata/tests/api/test_auth_api.py,sha256=3Zhn2A29poZIcCJ_R9_-LkR3xOFUTw1aTquiZVXQ2F0,20306
|
|
591
591
|
udata/tests/api/test_base_api.py,sha256=DRX5nuFIj51GFmMIAxUzoW1yiq1apNgr1vS4U4agzeg,2319
|
|
592
592
|
udata/tests/api/test_contact_points.py,sha256=MJm8B06iaUqIZCqxll3NViFwUCxwqZZ4u9e9s1h8MgU,1056
|
|
593
|
-
udata/tests/api/test_dataservices_api.py,sha256=
|
|
593
|
+
udata/tests/api/test_dataservices_api.py,sha256=51NU5d4iqfJT8iiyi0aLdWXDqZLvmyD-qh-Sflk9Lsg,11122
|
|
594
594
|
udata/tests/api/test_datasets_api.py,sha256=zqRtC61NvYUNum4kBTjA1W87JAIobA4k564rm-U6c98,81704
|
|
595
595
|
udata/tests/api/test_fields.py,sha256=OW85Z5MES5HeWOpapeem8OvR1cIcrqW-xMWpdZO4LZ8,1033
|
|
596
596
|
udata/tests/api/test_follow_api.py,sha256=0h54P_Dfbo07u6tg0Rbai1WWgWb19ZLN2HGv4oLCWfg,3383
|
|
@@ -685,9 +685,9 @@ udata/translations/pt/LC_MESSAGES/udata.mo,sha256=uttB2K8VsqzkEQG-5HfTtFms_3LtV9
|
|
|
685
685
|
udata/translations/pt/LC_MESSAGES/udata.po,sha256=8Ql1Lp7Z9KLnvp-qRxw-NhFu1p35Xj-q6Jg9JHsYhcw,43733
|
|
686
686
|
udata/translations/sr/LC_MESSAGES/udata.mo,sha256=US8beNIMPxP5h-zD_jfP1TheDDd4DdRVS5UIiY5XVZ8,28553
|
|
687
687
|
udata/translations/sr/LC_MESSAGES/udata.po,sha256=TM0yMDvKRljyOzgZZMlTX6OfpF6OC4Ngf_9Zc8n6ayA,50313
|
|
688
|
-
udata-9.0.1.
|
|
689
|
-
udata-9.0.1.
|
|
690
|
-
udata-9.0.1.
|
|
691
|
-
udata-9.0.1.
|
|
692
|
-
udata-9.0.1.
|
|
693
|
-
udata-9.0.1.
|
|
688
|
+
udata-9.0.1.dev29504.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
|
|
689
|
+
udata-9.0.1.dev29504.dist-info/METADATA,sha256=4pr78Vn0p9iHB9HmYAtWWp6mw6yOqRuvdlFg40cmuWg,124496
|
|
690
|
+
udata-9.0.1.dev29504.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
|
|
691
|
+
udata-9.0.1.dev29504.dist-info/entry_points.txt,sha256=3SKiqVy4HUqxf6iWspgMqH8d88Htk6KoLbG1BU-UddQ,451
|
|
692
|
+
udata-9.0.1.dev29504.dist-info/top_level.txt,sha256=39OCg-VWFWOq4gCKnjKNu-s3OwFlZIu_dVH8Gl6ndHw,12
|
|
693
|
+
udata-9.0.1.dev29504.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|