Skip to content

Commit dd8c5c8

Browse files
committed
1. add geo_value validator in _params
2. modified test_covidcast and test_delphi_epidata for server geo_value validation passing 3. add TODO for patch and mock part
1 parent c21b940 commit dd8c5c8

File tree

4 files changed

+69
-38
lines changed

4 files changed

+69
-38
lines changed

integrations/client/test_delphi_epidata.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
from delphi.epidata.acquisition.covidcast.test_utils import CovidcastBase
2020
import delphi.operations.secrets as secrets
2121

22+
# TODO replace these real geo_values with fake values, and use patch and mock to mock the return values of
23+
# delphi_utils.geomap.GeoMapper().get_geo_values(geo_type) in parse_geo_sets() of _params.py
24+
25+
global fips, msa # add two global lists of valid geo_values of these types to pass the validation in parse_geo_sets() of _params.py
26+
27+
fips = ['04019', '19143', '29063'] # Example list of valid FIPS codes as strings
28+
msa = ['40660', '44180', '48620'] # Example list of valid MSAs as strings
29+
2230
# py3tester coverage target
2331
__test_target__ = 'delphi.epidata.client.delphi_epidata'
2432

@@ -54,12 +62,12 @@ def test_covidcast(self):
5462

5563
# insert placeholder data: three issues of one signal, one issue of another
5664
rows = [
57-
self._make_placeholder_row(issue=self.DEFAULT_ISSUE + i, value=i, lag=i)[0]
65+
self._make_placeholder_row(geo_type='msa', geo_value=msa[0], issue=self.DEFAULT_ISSUE + i, value=i, lag=i)[0]
5866
for i in range(3)
5967
]
6068
row_latest_issue = rows[-1]
6169
rows.append(
62-
self._make_placeholder_row(signal="sig2")[0]
70+
self._make_placeholder_row(geo_type='msa', geo_value=msa[0], signal="sig2")[0]
6371
)
6472
self._insert_rows(rows)
6573

@@ -74,6 +82,7 @@ def test_covidcast(self):
7482
self.expected_from_row(rows[-1])
7583
]
7684

85+
self.maxDiff = None
7786
# check result
7887
self.assertEqual(response, {
7988
'result': 1,
@@ -223,10 +232,10 @@ def test_geo_value(self):
223232
# insert placeholder data: three counties, three MSAs
224233
N = 3
225234
rows = [
226-
self._make_placeholder_row(geo_type="county", geo_value=str(i)*5, value=i)[0]
235+
self._make_placeholder_row(geo_type="fips", geo_value=fips[i], value=i)[0]
227236
for i in range(N)
228237
] + [
229-
self._make_placeholder_row(geo_type="msa", geo_value=str(i)*5, value=i*10)[0]
238+
self._make_placeholder_row(geo_type="msa", geo_value=msa[i], value=i*10)[0]
230239
for i in range(N)
231240
]
232241
self._insert_rows(rows)
@@ -240,32 +249,32 @@ def fetch(geo):
240249
**self.params_from_row(rows[0], geo_value=geo)
241250
)
242251

252+
self.maxDiff = None
243253
# test fetch all
244254
r = fetch('*')
245255
self.assertEqual(r['message'], 'success')
246256
self.assertEqual(r['epidata'], counties)
247257
# test fetch a specific region
248-
r = fetch('11111')
258+
r = fetch('{}'.format(fips[0]))
249259
self.assertEqual(r['message'], 'success')
250-
self.assertEqual(r['epidata'], [counties[1]])
260+
self.assertEqual(r['epidata'], [counties[0]])
251261
# test fetch a specific yet not existing region
252262
r = fetch('55555')
253-
self.assertEqual(r['message'], 'no results')
263+
self.assertEqual(r['message'], 'invalid geo_value for the requested geo_type')
254264
# test fetch a multiple regions
255-
r = fetch(['11111', '22222'])
265+
r = fetch(['{}'.format(fips[0]), '{}'.format(fips[1])])
256266
self.assertEqual(r['message'], 'success')
257-
self.assertEqual(r['epidata'], [counties[1], counties[2]])
267+
self.assertEqual(r['epidata'], [counties[0], counties[1]])
258268
# test fetch a multiple regions in another variant
259-
r = fetch(['00000', '22222'])
269+
r = fetch(['{}'.format(fips[0]), '{}'.format(fips[2])])
260270
self.assertEqual(r['message'], 'success')
261271
self.assertEqual(r['epidata'], [counties[0], counties[2]])
262272
# test fetch a multiple regions but one is not existing
263-
r = fetch(['11111', '55555'])
264-
self.assertEqual(r['message'], 'success')
265-
self.assertEqual(r['epidata'], [counties[1]])
273+
r = fetch(['{}'.format(fips[0]), '55555'])
274+
self.assertEqual(r['message'], 'invalid geo_value for the requested geo_type')
266275
# test fetch a multiple regions but specify no region
267276
r = fetch([])
268-
self.assertEqual(r['message'], 'no results')
277+
self.assertEqual(r['message'], 'invalid geo_value for the requested geo_type')
269278

270279
def test_covidcast_meta(self):
271280
"""Test that the covidcast_meta endpoint returns expected data."""
@@ -322,10 +331,10 @@ def test_async_epidata(self):
322331
# insert placeholder data: three counties, three MSAs
323332
N = 3
324333
rows = [
325-
self._make_placeholder_row(geo_type="county", geo_value=str(i)*5, value=i)[0]
334+
self._make_placeholder_row(geo_type="fips", geo_value=fips[i-1], value=i)[0]
326335
for i in range(N)
327336
] + [
328-
self._make_placeholder_row(geo_type="msa", geo_value=str(i)*5, value=i*10)[0]
337+
self._make_placeholder_row(geo_type="msa", geo_value=msa[i-1], value=i*10)[0]
329338
for i in range(N)
330339
]
331340
self._insert_rows(rows)

integrations/server/test_covidcast.py

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# standard library
44
import json
55
import unittest
6+
# from unittest.mock import MagicMock
7+
# from unittest.mock import patch
68

79
# third party
810
import mysql.connector
@@ -15,6 +17,13 @@
1517
# use the local instance of the Epidata API
1618
BASE_URL = 'http://delphi_web_epidata/epidata/api.php'
1719

20+
# TODO replace these real geo_values with fake values, and use patch and mock to mock the return values of
21+
# delphi_utils.geomap.GeoMapper().get_geo_values(geo_type) in parse_geo_sets() of _params.py
22+
23+
global fips, msa # add two global lists of valid geo_values of these types to pass the validation in parse_geo_sets() of _params.py
24+
25+
fips = ['04019', '19143', '29063'] # Example list of valid FIPS codes as strings
26+
msa = ['40660', '44180', '48620'] # Example list of valid MSAs as strings
1827

1928

2029
class CovidcastTests(CovidcastBase):
@@ -35,53 +44,52 @@ def request_based_on_row(self, row, extract_response=lambda x: x.json(), **kwarg
3544
return response, expected
3645

3746
def _insert_placeholder_set_one(self):
38-
row, settings = self._make_placeholder_row()
47+
row, settings = self._make_placeholder_row(geo_type='msa', geo_value=msa[0])
3948
self._insert_rows([row])
4049
return row
4150

4251
def _insert_placeholder_set_two(self):
4352
rows = [
44-
self._make_placeholder_row(geo_type='county', geo_value=str(i)*5, value=i*1., stderr=i*10., sample_size=i*100.)[0]
53+
self._make_placeholder_row(geo_type='msa', geo_value=msa[i-1], value=i*1., stderr=i*10., sample_size=i*100.)[0]
4554
for i in [1, 2, 3]
4655
] + [
47-
# geo value intended to overlap with counties above
48-
self._make_placeholder_row(geo_type='msa', geo_value=str(i-3)*5, value=i*1., stderr=i*10., sample_size=i*100.)[0]
56+
self._make_placeholder_row(geo_type='fips', geo_value=fips[i-4], value=i*1., stderr=i*10., sample_size=i*100.)[0]
4957
for i in [4, 5, 6]
5058
]
5159
self._insert_rows(rows)
5260
return rows
5361

5462
def _insert_placeholder_set_three(self):
5563
rows = [
56-
self._make_placeholder_row(geo_type='county', geo_value='11111', time_value=2000_01_01+i, value=i*1., stderr=i*10., sample_size=i*100., issue=2000_01_03, lag=2-i)[0]
64+
self._make_placeholder_row(geo_type='msa', geo_value=msa[0], time_value=2000_01_01+i, value=i*1., stderr=i*10., sample_size=i*100., issue=2000_01_03, lag=2-i)[0]
5765
for i in [1, 2, 3]
5866
] + [
5967
# time value intended to overlap with 11111 above, with disjoint geo values
60-
self._make_placeholder_row(geo_type='county', geo_value=str(i)*5, time_value=2000_01_01+i-3, value=i*1., stderr=i*10., sample_size=i*100., issue=2000_01_03, lag=5-i)[0]
68+
self._make_placeholder_row(geo_type='msa', geo_value=str(i)*5, time_value=2000_01_01+i-3, value=i*1., stderr=i*10., sample_size=i*100., issue=2000_01_03, lag=5-i)[0]
6169
for i in [4, 5, 6]
6270
]
6371
self._insert_rows(rows)
6472
return rows
6573

6674
def _insert_placeholder_set_four(self):
6775
rows = [
68-
self._make_placeholder_row(source='src1', signal=str(i)*5, value=i*1., stderr=i*10., sample_size=i*100.)[0]
76+
self._make_placeholder_row(geo_type='msa', geo_value=msa[0], source='src1', signal=str(i)*5, value=i*1., stderr=i*10., sample_size=i*100.)[0]
6977
for i in [1, 2, 3]
7078
] + [
7179
# signal intended to overlap with the signal above
72-
self._make_placeholder_row(source='src2', signal=str(i-3)*5, value=i*1., stderr=i*10., sample_size=i*100.)[0]
80+
self._make_placeholder_row(geo_type='msa', geo_value=msa[0], source='src2', signal=str(i-3)*5, value=i*1., stderr=i*10., sample_size=i*100.)[0]
7381
for i in [4, 5, 6]
7482
]
7583
self._insert_rows(rows)
7684
return rows
7785

7886
def _insert_placeholder_set_five(self):
7987
rows = [
80-
self._make_placeholder_row(time_value=2000_01_01, value=i*1., stderr=i*10., sample_size=i*100., issue=2000_01_03+i)[0]
88+
self._make_placeholder_row(geo_type='msa', geo_value=msa[0], time_value=2000_01_01, value=i*1., stderr=i*10., sample_size=i*100., issue=2000_01_03+i)[0]
8189
for i in [1, 2, 3]
8290
] + [
8391
# different time_values, same issues
84-
self._make_placeholder_row(time_value=2000_01_01+i-3, value=i*1., stderr=i*10., sample_size=i*100., issue=2000_01_03+i-3)[0]
92+
self._make_placeholder_row(geo_type='msa', geo_value=msa[0], time_value=2000_01_01+i-3, value=i*1., stderr=i*10., sample_size=i*100., issue=2000_01_03+i-3)[0]
8593
for i in [4, 5, 6]
8694
]
8795
self._insert_rows(rows)
@@ -310,7 +318,7 @@ def test_signal_wildcard(self):
310318
})
311319

312320
def test_geo_value(self):
313-
"""test different variants of geo types: single, *, multi."""
321+
"""test whether geo values are valid for specific geo types"""
314322

315323
# insert placeholder data
316324
rows = self._insert_placeholder_set_two()
@@ -324,28 +332,33 @@ def fetch(geo_value):
324332

325333
return response
326334

335+
self.maxDiff = None
327336
# test fetch a specific region
328-
r = fetch('11111')
337+
r = fetch(msa[0])
329338
self.assertEqual(r['message'], 'success')
330339
self.assertEqual(r['epidata'], [expected_counties[0]])
340+
331341
# test fetch a specific yet not existing region
332-
r = fetch('55555')
333-
self.assertEqual(r['message'], 'no results')
342+
r = fetch('11111')
343+
self.assertEqual(r['message'], 'invalid geo_value for the requested geo_type')
344+
334345
# test fetch multiple regions
335-
r = fetch('11111,22222')
346+
r = fetch('{},{}'.format(msa[0], msa[1]))
336347
self.assertEqual(r['message'], 'success')
337348
self.assertEqual(r['epidata'], [expected_counties[0], expected_counties[1]])
349+
338350
# test fetch multiple noncontiguous regions
339-
r = fetch('11111,33333')
351+
r = fetch('{},{}'.format(msa[0], msa[2]))
340352
self.assertEqual(r['message'], 'success')
341353
self.assertEqual(r['epidata'], [expected_counties[0], expected_counties[2]])
354+
342355
# test fetch multiple regions but one is not existing
343-
r = fetch('11111,55555')
344-
self.assertEqual(r['message'], 'success')
345-
self.assertEqual(r['epidata'], [expected_counties[0]])
356+
r = fetch('{},11111'.format(msa[0]))
357+
self.assertEqual(r['message'], 'invalid geo_value for the requested geo_type')
358+
346359
# test fetch empty region
347360
r = fetch('')
348-
self.assertEqual(r['message'], 'no results')
361+
self.assertEqual(r['message'], 'invalid geo_value for the requested geo_type')
349362

350363
def test_location_timeline(self):
351364
"""Select a timeline for a particular location."""
@@ -384,7 +397,7 @@ def test_nullable_columns(self):
384397
"""Missing values should be surfaced as null."""
385398

386399
row, _ = self._make_placeholder_row(
387-
stderr=None, sample_size=None,
400+
geo_type='msa', geo_value=msa[0], stderr=None, sample_size=None,
388401
missing_stderr=Nans.OTHER.value, missing_sample_size=Nans.OTHER.value
389402
)
390403
self._insert_rows([row])
@@ -405,7 +418,7 @@ def test_temporal_partitioning(self):
405418

406419
# insert placeholder data
407420
rows = [
408-
self._make_placeholder_row(time_type=tt)[0]
421+
self._make_placeholder_row(geo_type='msa', geo_value=msa[0], time_type=tt)[0]
409422
for tt in "hour day week month year".split()
410423
]
411424
self._insert_rows(rows)

requirements.api.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
delphi_utils==0.3.6
12
epiweeks==2.1.2
23
Flask==2.2.2
34
itsdangerous<2.1

src/server/_params.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import re
33
from dataclasses import dataclass
44
from typing import List, Optional, Sequence, Tuple, Union
5+
import delphi_utils
56

67
from flask import request
78

@@ -460,14 +461,21 @@ def parse_source_signal_sets() -> List[SourceSignalSet]:
460461

461462
def parse_geo_sets() -> List[GeoSet]:
462463
geo_type = request.values.get("geo_type")
464+
463465
if geo_type:
464466
# old version
465467
require_any(request, "geo_value", "geo_values", empty=True)
466468
geo_values = extract_strings(("geo_values", "geo_value"))
467469
if len(geo_values) == 1 and geo_values[0] == "*":
468470
return [GeoSet(geo_type, True)]
471+
472+
for geo_value in geo_values:
473+
if geo_value not in delphi_utils.geomap.GeoMapper().get_geo_values(geo_type):
474+
raise ValidationFailedException("invalid geo_value for the requested geo_type")
475+
469476
return [GeoSet(geo_type, geo_values)]
470477

478+
471479
if ":" not in request.values.get("geo", ""):
472480
raise ValidationFailedException("missing parameter: geo or (geo_type and geo_value[s])")
473481

0 commit comments

Comments
 (0)