Skip to content

Commit dbb3a34

Browse files
committed
fix: handle table data that contain NaN values
1 parent ffb8b16 commit dbb3a34

3 files changed

Lines changed: 83 additions & 2 deletions

File tree

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
1.0.9
2+
- fix: handle table data that contain NaN values
13
1.0.8
24
- fix: if mimetype not available, check suffix for detecting DC data
35
- tests: own the docker image

ckanext/dc_serve/serve.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
DC_MIME_TYPES, s3cc, is_resource_private,
1414
)
1515

16+
import numpy as np
17+
1618

1719
logger = logging.getLogger(__name__)
1820

@@ -43,8 +45,18 @@ def get_dc_tables(ds, from_basins: bool = False) -> dict:
4345
tables.update(get_dc_tables(bn.ds))
4446
else:
4547
for tab in ds.tables:
48+
# A table is a 2D array
49+
tab_array = ds.tables[tab][:]
50+
tab_list = tab_array.tolist()
51+
if np.any(np.isnan(tab_array)):
52+
# NaN is not part of the JSON specification, so we convert it
53+
# to None before sending the response.
54+
tab_list = [[v if not np.isnan(v) else None for v in column]
55+
for column in tab_list]
56+
4657
tables[tab] = (ds.tables[tab].keys(),
47-
ds.tables[tab][:].tolist())
58+
tab_list
59+
)
4860

4961
return tables
5062

ckanext/dc_serve/tests/test_serve.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
import ckan.tests.factories as factories
1010
import dclab
11-
from dclab.rtdc_dataset import fmt_http
11+
from dclab.rtdc_dataset import fmt_dcor, fmt_http
1212
import h5py
13+
import numpy as np
1314

1415
import pytest
1516

@@ -606,6 +607,72 @@ def test_api_dcserv_tables(enqueue_job_mock, app):
606607
assert "brightness" in names
607608

608609

610+
@pytest.mark.ckan_config('ckan.plugins', 'dcor_schemas dc_serve')
611+
@pytest.mark.usefixtures('with_plugins', 'clean_db')
612+
@mock.patch('ckan.plugins.toolkit.enqueue_job',
613+
side_effect=synchronous_enqueue_job)
614+
def test_api_dcserv_tables_with_nan(enqueue_job_mock, app, tmp_path):
615+
"""
616+
Tables may contain NaN-data. These should be converted into None
617+
before they are encoded with JSON and sent as response.
618+
"""
619+
user = factories.UserWithToken()
620+
owner_org = factories.Organization(users=[{
621+
'name': user['id'],
622+
'capacity': 'admin'
623+
}])
624+
# Note: `call_action` bypasses authorization!
625+
create_context = {'ignore_auth': False,
626+
'user': user['name'], 'api_version': 3}
627+
628+
# generate a resource with nan-valued tables
629+
path_mod = tmp_path / 'blood_nan.rtdc'
630+
shutil.copy2(data_path / "cytoshot_blood.rtdc", path_mod)
631+
632+
# generate a table
633+
table_data = {
634+
"fox": np.arange(10),
635+
"peter": np.linspace(-1, 1, 10),
636+
"deril": np.zeros(10),
637+
}
638+
# this is the important bit for this test
639+
table_data["peter"][2] = np.nan
640+
641+
with dclab.RTDCWriter(path_mod) as hw:
642+
hw.store_table("nan-array", table_data)
643+
644+
# create a dataset
645+
ds_dict, res_dict = make_dataset_via_s3(
646+
create_context=create_context,
647+
owner_org=owner_org,
648+
resource_path=path_mod,
649+
activate=True)
650+
651+
resp = app.get(
652+
"/api/3/action/dcserv",
653+
params={"id": res_dict["id"],
654+
"query": "tables",
655+
},
656+
headers={"Authorization": user["token"]},
657+
status=200
658+
)
659+
jres = json.loads(resp.body)
660+
assert jres["success"]
661+
assert "nan-array" in jres["result"]
662+
names, data = jres["result"]["nan-array"]
663+
assert "peter" in names
664+
assert data[1][2] is None
665+
666+
# now open the same dataset with dclab
667+
host = f"http://{app.config['CKAN_HOST']}:{app.config['CKAN_PORT']}"
668+
ds = fmt_dcor.RTDC_DCOR(
669+
url=f"{host}/api/3/action/dcserv?id={res_dict['id']}&query=tables",
670+
api_key=user["token"])
671+
assert "nan-array" in ds.tables
672+
assert np.isnan(ds.tables["nan-array"]["peter"][2])
673+
assert not np.isnan(ds.tables["nan-array"]["peter"][1])
674+
675+
609676
@pytest.mark.ckan_config('ckan.plugins', 'dcor_schemas dc_serve')
610677
@pytest.mark.usefixtures('with_plugins', 'clean_db')
611678
@mock.patch('ckan.plugins.toolkit.enqueue_job',

0 commit comments

Comments
 (0)