Skip to content

Commit cf8e05f

Browse files
authored
Merge pull request #211 from ecmwf-projects/COPDS-2006-format-api-request
Format API request
2 parents 9ec2197 + 0e80610 commit cf8e05f

File tree

5 files changed

+102
-87
lines changed

5 files changed

+102
-87
lines changed

cads_processing_api_service/config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@
3131
client.retrieve(dataset, request).download()
3232
"""
3333

34+
API_REQUEST_MAX_LIST_LENGTH: dict[str, int] = {
35+
"year": 3,
36+
"month": 3,
37+
"day": 3,
38+
"time": 3,
39+
"area": 4,
40+
"pressure_level": 3,
41+
}
42+
3443
ANONYMOUS_LICENCES_MESSAGE = (
3544
"The job has been submitted as an anonymous user. "
3645
"Please consider the following licences implicitly accepted: "
@@ -71,6 +80,7 @@ class Settings(pydantic_settings.BaseSettings):
7180
cache_resources_ttl: int = 10
7281

7382
api_request_template: str = API_REQUEST_TEMPLATE
83+
api_request_max_list_length: dict[str, int] = API_REQUEST_MAX_LIST_LENGTH
7484
missing_dataset_title: str = "Dataset not available"
7585
anonymous_licences_message: str = ANONYMOUS_LICENCES_MESSAGE
7686
deprecation_warning_message: str = DEPRECATION_WARNING_MESSAGE

cads_processing_api_service/translators.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,38 @@ def translate_request_ids_into_labels(
249249
return request_labels
250250

251251

252+
def format_list(
253+
value_list: list[int | float | str], max_items_per_line: int = 1
254+
) -> str:
255+
if len(value_list) > max_items_per_line:
256+
formatted = "[\n"
257+
for i in range(0, len(value_list), max_items_per_line):
258+
line = ", ".join(
259+
f"'{item}'" if isinstance(item, str) else f"{item}"
260+
for item in value_list[i : i + max_items_per_line]
261+
)
262+
formatted += f" {line},\n"
263+
formatted = formatted.rstrip(",\n") + "\n ]"
264+
else:
265+
formatted = str(value_list)
266+
return formatted
267+
268+
252269
def format_request_value(
253-
request_value: str | list[str],
270+
request_value: int | float | str | list[int | float | str],
271+
key: str | None = None,
254272
) -> str:
255-
if isinstance(request_value, str):
256-
formatted_request_value = f"'{request_value}'"
273+
if isinstance(request_value, list):
274+
if key is None:
275+
formatted_request_value = format_list(request_value)
276+
else:
277+
api_request_max_list_length = (
278+
config.ensure_settings().api_request_max_list_length
279+
)
280+
max_items_per_line = api_request_max_list_length.get(key, 1)
281+
formatted_request_value = format_list(request_value, max_items_per_line)
282+
elif isinstance(request_value, str):
283+
formatted_request_value = f'"{request_value}"'
257284
else:
258285
formatted_request_value = str(request_value)
259286
return formatted_request_value
@@ -285,15 +312,15 @@ def format_api_request(
285312
"{"
286313
+ ",".join(
287314
[
288-
f"\n '{key}': {format_request_value(value)}"
315+
f'\n "{key}": {format_request_value(value, key)}'
289316
for key, value in request_inputs.items()
290317
]
291318
)
292319
+ "\n}"
293320
)
294321
api_request = api_request_template.format(
295322
process_id=process_id, api_request_kwargs=api_request_kwargs
296-
)
323+
).replace("'", '"')
297324

298325
return api_request
299326

environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ channels:
1010
dependencies:
1111
- attrs
1212
- asyncpg
13+
- black
1314
- cachetools
1415
- fastapi>=0.109.0
1516
- pip

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ classifiers = [
1515
]
1616
dependencies = [
1717
"attrs",
18+
"black",
1819
"cacholote",
1920
"cads-adaptors@git+https://github.com/ecmwf-projects/cads-adaptors.git",
2021
"cads-broker@git+https://github.com/ecmwf-projects/cads-broker.git",

tests/test_10_translators.py

Lines changed: 58 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17+
# mypy: ignore-errors
18+
1719
from typing import Any
1820

19-
import cads_processing_api_service.translators
21+
from cads_processing_api_service import config, translators
2022

2123
TEST_INPUT_CDS_SCHEMAS: dict[str, Any] = {
2224
"string_list": {
@@ -115,16 +117,12 @@ def test_extract_groups_labels() -> None:
115117
"val2": "Val2",
116118
"val3": "Val3",
117119
}
118-
res_output = cads_processing_api_service.translators.extract_groups_labels(
119-
test_groups, test_values
120-
)
120+
res_output = translators.extract_groups_labels(test_groups, test_values)
121121
assert res_output == exp_output
122122

123123
test_groups = TEST_INPUT_CDS_SCHEMAS["string_list_array"]["details"]["groups"]
124124
exp_output = {"val1": "Val1", "val2": "Val2", "val3": "Val3"}
125-
res_output = cads_processing_api_service.translators.extract_groups_labels(
126-
test_groups
127-
)
125+
res_output = translators.extract_groups_labels(test_groups)
128126
assert res_output == exp_output
129127

130128
test_groups = TEST_INPUT_CDS_SCHEMAS["string_list_array_groups"]["details"][
@@ -138,32 +136,24 @@ def test_extract_groups_labels() -> None:
138136
"val5": "Val5",
139137
"val6": "Val6",
140138
}
141-
res_output = cads_processing_api_service.translators.extract_groups_labels(
142-
test_groups
143-
)
139+
res_output = translators.extract_groups_labels(test_groups)
144140
assert res_output == exp_output
145141

146142

147143
def test_extract_labels() -> None:
148144
test_inputs_cds_schema = TEST_INPUT_CDS_SCHEMAS["string_list_array"]
149145
exp_output = {"val1": "Val1", "val2": "Val2", "val3": "Val3"}
150-
res_output = cads_processing_api_service.translators.extract_labels(
151-
test_inputs_cds_schema
152-
)
146+
res_output = translators.extract_labels(test_inputs_cds_schema)
153147
assert res_output == exp_output
154148

155149
test_inputs_cds_schema = TEST_INPUT_CDS_SCHEMAS["string_list"]
156150
exp_output = {"val1": "Val1", "val2": "Val2", "val3": "Val3"}
157-
res_output = cads_processing_api_service.translators.extract_labels(
158-
test_inputs_cds_schema
159-
)
151+
res_output = translators.extract_labels(test_inputs_cds_schema)
160152
assert res_output == exp_output
161153

162154
test_inputs_cds_schema = TEST_INPUT_CDS_SCHEMAS["free_edition_widget"]
163155
exp_output = {}
164-
res_output = cads_processing_api_service.translators.extract_labels(
165-
test_inputs_cds_schema
166-
)
156+
res_output = translators.extract_labels(test_inputs_cds_schema)
167157
assert res_output == exp_output
168158

169159

@@ -173,9 +163,7 @@ def test_translate_string_list() -> None:
173163
"type": "array",
174164
"items": {"type": "string", "enum": ["val1", "val2", "val3"]},
175165
}
176-
res_output = cads_processing_api_service.translators.translate_string_list(
177-
test_input
178-
)
166+
res_output = translators.translate_string_list(test_input)
179167
assert res_output == exp_ouput
180168

181169

@@ -185,9 +173,7 @@ def test_translate_string_list_array() -> None:
185173
"type": "array",
186174
"items": {"type": "string", "enum": ["val1", "val2", "val3"]},
187175
}
188-
res_output = cads_processing_api_service.translators.translate_string_list_array(
189-
test_input
190-
)
176+
res_output = translators.translate_string_list_array(test_input)
191177
assert res_output == exp_ouput
192178

193179
test_input = TEST_INPUT_CDS_SCHEMAS["string_list_array_groups"]
@@ -198,18 +184,14 @@ def test_translate_string_list_array() -> None:
198184
"enum": ["val1", "val2", "val3", "val4", "val5", "val6"],
199185
},
200186
}
201-
res_output = cads_processing_api_service.translators.translate_string_list_array(
202-
test_input
203-
)
187+
res_output = translators.translate_string_list_array(test_input)
204188
assert res_output == exp_ouput
205189

206190

207191
def test_translate_string_choice() -> None:
208192
test_input = TEST_INPUT_CDS_SCHEMAS["string_choice"]
209193
exp_ouput = {"type": "string", "enum": ["val1", "val2", "val3"], "default": "val1"}
210-
res_output = cads_processing_api_service.translators.translate_string_choice(
211-
test_input
212-
)
194+
res_output = translators.translate_string_choice(test_input)
213195
assert res_output == exp_ouput
214196

215197

@@ -222,43 +204,39 @@ def test_translate_geographic_extent_map() -> None:
222204
"items": {"type": "number"},
223205
"default": [1, 2, 3, 4],
224206
}
225-
res_output = (
226-
cads_processing_api_service.translators.translate_geographic_extent_map(
227-
test_input
228-
)
229-
)
207+
res_output = translators.translate_geographic_extent_map(test_input)
230208
assert res_output == exp_ouput
231209

232210

233211
def test_make_request_labels() -> None:
234212
test_input_value_ids = ["1", "1", "1", "1"]
235213
test_input_cds_schema = TEST_INPUT_CDS_SCHEMAS["geographic_extent_map"]
236214
exp_output = ["North: 1°", "West: 1°", "South: 1°", "East: 1°"]
237-
res_output = cads_processing_api_service.translators.make_request_labels(
215+
res_output = translators.make_request_labels(
238216
test_input_value_ids, test_input_cds_schema
239217
)
240218
assert res_output == exp_output
241219

242220
test_input_value_ids = [{"latitude": 10, "longitude": 10}]
243221
test_input_cds_schema = TEST_INPUT_CDS_SCHEMAS["geographic_location"]
244222
exp_output = ["Latitude: 10°", "Longitude: 10°"]
245-
res_output = cads_processing_api_service.translators.make_request_labels(
223+
res_output = translators.make_request_labels(
246224
test_input_value_ids, test_input_cds_schema
247225
)
248226
assert res_output == exp_output
249227

250228
test_input_value_ids = ["val1", "val2"]
251229
test_input_cds_schema = TEST_INPUT_CDS_SCHEMAS["string_list"]
252230
exp_output = ["Val1", "Val2"]
253-
res_output = cads_processing_api_service.translators.make_request_labels(
231+
res_output = translators.make_request_labels(
254232
test_input_value_ids, test_input_cds_schema
255233
)
256234
assert res_output == exp_output
257235

258236
test_input_value_ids = ["val1", "val4"]
259237
test_input_cds_schema = TEST_INPUT_CDS_SCHEMAS["string_list"]
260238
exp_output = ["Val1", "val4"]
261-
res_output = cads_processing_api_service.translators.make_request_labels(
239+
res_output = translators.make_request_labels(
262240
test_input_value_ids, test_input_cds_schema
263241
)
264242
assert res_output == exp_output
@@ -268,11 +246,7 @@ def test_translate_request_ids_into_labels() -> None:
268246
request = {"key1": "val1", "key2": "val2"}
269247
cds_schema = None
270248
exp_output = {"key1": "val1", "key2": "val2"}
271-
res_output = (
272-
cads_processing_api_service.translators.translate_request_ids_into_labels(
273-
request, cds_schema
274-
)
275-
)
249+
res_output = translators.translate_request_ids_into_labels(request, cds_schema)
276250
assert res_output == exp_output
277251

278252
request = {
@@ -289,11 +263,7 @@ def test_translate_request_ids_into_labels() -> None:
289263
"String Choice": ["Val1"],
290264
"unknown_key": "unknown_value",
291265
}
292-
res_output = (
293-
cads_processing_api_service.translators.translate_request_ids_into_labels(
294-
request, cds_schema
295-
)
296-
)
266+
res_output = translators.translate_request_ids_into_labels(request, cds_schema)
297267
assert res_output == exp_output
298268

299269
request = {}
@@ -309,52 +279,58 @@ def test_translate_request_ids_into_labels() -> None:
309279
}
310280

311281

282+
def test_format_list() -> None:
283+
value_list = ["test_value_1", "test_value_2"]
284+
max_items_per_line = 1
285+
exp_output = "[\n 'test_value_1',\n 'test_value_2'\n ]"
286+
res_output = translators.format_list(value_list, max_items_per_line)
287+
assert res_output == exp_output
288+
289+
max_items_per_line = 2
290+
exp_output = "['test_value_1', 'test_value_2']"
291+
res_output = translators.format_list(value_list, max_items_per_line)
292+
assert res_output == exp_output
293+
294+
312295
def test_format_request_value() -> None:
313-
test_value_1 = "test_value"
314-
exp_output_1 = "'test_value'"
315-
res_output_1 = cads_processing_api_service.translators.format_request_value(
316-
test_value_1
317-
)
318-
assert res_output_1 == exp_output_1
296+
test_value = "test_value"
297+
exp_output = '"test_value"'
298+
res_output = translators.format_request_value(test_value)
299+
assert res_output == exp_output
319300

320-
test_value_2 = ["test_value_1", "test_value_2"]
321-
exp_output_2 = "['test_value_1', 'test_value_2']"
322-
res_output_2 = cads_processing_api_service.translators.format_request_value(
323-
test_value_2
324-
)
325-
assert res_output_2 == exp_output_2
301+
test_value = 1
302+
exp_output = "1"
303+
res_output = translators.format_request_value(test_value)
304+
assert res_output == exp_output
305+
306+
test_value = ["test_value_1", "test_value_2"]
307+
exp_output = "[\n 'test_value_1',\n 'test_value_2'\n ]"
308+
res_output = translators.format_request_value(test_value)
309+
assert res_output == exp_output
326310

327311

328312
def test_format_api_request() -> None:
329-
test_api_request_template = (
330-
"import cads_api_client\n\n"
331-
"request = {api_request_kwargs}\n\n"
332-
"client = cads_api_client.ApiClient()\n"
333-
"client.retrieve(\n\t"
334-
"collection_id='{process_id}',\n\t"
335-
"**request\n"
336-
")\n"
337-
)
313+
test_api_request_template = config.API_REQUEST_TEMPLATE
338314
test_process_id = "test_process_id"
339315
test_request = {
340316
"inputs": {
341-
"variable": "test_variable_1",
342-
"year": ["2000", "2001"],
317+
"variable_1": "value_1",
318+
"variable_2": ["value_1", "value_2"],
319+
"variable_3": 1,
343320
}
344321
}
345322
exp_output = (
346-
"import cads_api_client\n\n"
323+
"import cdsapi\n\n"
324+
'dataset = "test_process_id"\n'
347325
"request = {\n"
348-
" 'variable': 'test_variable_1',\n"
349-
" 'year': ['2000', '2001']\n"
326+
' "variable_1": "value_1",\n'
327+
' "variable_2": [\n "value_1",\n "value_2"\n ],\n'
328+
' "variable_3": 1\n'
350329
"}\n\n"
351-
"client = cads_api_client.ApiClient()\n"
352-
"client.retrieve(\n\t"
353-
"collection_id='test_process_id',\n\t"
354-
"**request\n"
355-
")\n"
330+
"client = cdsapi.Client()\n"
331+
"client.retrieve(dataset, request).download()\n"
356332
)
357-
res_output = cads_processing_api_service.translators.format_api_request(
333+
res_output = translators.format_api_request(
358334
test_api_request_template, test_process_id, test_request
359335
)
360336
assert res_output == exp_output

0 commit comments

Comments
 (0)