Skip to content

Commit d0c7da0

Browse files
committed
move extract methods
1 parent 2f509db commit d0c7da0

29 files changed

+241
-169
lines changed

src/server/_params.py

+132-2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,142 @@
99
from ._exceptions import ValidationFailedException
1010
from .utils import days_in_range, weeks_in_range, guess_time_value_is_day, guess_time_value_is_week, TimeValues, days_to_ranges, weeks_to_ranges
1111
from ._validate import (
12-
extract_dates,
13-
extract_strings,
1412
require_all,
1513
require_any,
1614
)
1715

16+
17+
def _extract_value(key: Union[str, Sequence[str]]) -> Optional[str]:
18+
if isinstance(key, str):
19+
return request.values.get(key)
20+
for k in key:
21+
if k in request.values:
22+
return request.values[k]
23+
return None
24+
25+
26+
def _extract_list_value(key: Union[str, Sequence[str]]) -> List[str]:
27+
if isinstance(key, str):
28+
return request.values.getlist(key)
29+
for k in key:
30+
if k in request.values:
31+
return request.values.getlist(k)
32+
return []
33+
34+
35+
def extract_strings(key: Union[str, Sequence[str]]) -> Optional[List[str]]:
36+
s = _extract_list_value(key)
37+
if not s:
38+
# nothing to do
39+
return None
40+
# we can have multiple values
41+
return [v for vs in s for v in vs.split(",")]
42+
43+
44+
IntRange = Union[Tuple[int, int], int]
45+
46+
47+
def extract_integer(key: Union[str, Sequence[str]]) -> Optional[int]:
48+
s = _extract_value(key)
49+
if not s:
50+
# nothing to do
51+
return None
52+
try:
53+
return int(s)
54+
except ValueError:
55+
raise ValidationFailedException(f"{key}: not a number: {s}")
56+
57+
58+
def extract_integers(key: Union[str, Sequence[str]]) -> Optional[List[IntRange]]:
59+
parts = extract_strings(key)
60+
if not parts:
61+
# nothing to do
62+
return None
63+
64+
def _parse_range(part: str):
65+
if "-" not in part:
66+
return int(part)
67+
r = part.split("-", 2)
68+
first = int(r[0])
69+
last = int(r[1])
70+
if first == last:
71+
# the first and last numbers are the same, just treat it as a singe value
72+
return first
73+
elif last > first:
74+
# add the range as an array
75+
return (first, last)
76+
# the range is inverted, this is an error
77+
raise ValidationFailedException(f"{key}: the given range is inverted")
78+
79+
try:
80+
values = [_parse_range(part) for part in parts]
81+
# check for invalid values
82+
return None if any(v is None for v in values) else values
83+
except ValueError as e:
84+
raise ValidationFailedException(f"{key}: not a number: {str(e)}")
85+
86+
87+
def parse_date(s: str) -> int:
88+
# parses a given string in format YYYYMMDD or YYYY-MM-DD to a number in the form YYYYMMDD
89+
try:
90+
return int(s.replace("-", ""))
91+
except ValueError:
92+
raise ValidationFailedException(f"not a valid date: {s}")
93+
94+
95+
def extract_date(key: Union[str, Sequence[str]]) -> Optional[int]:
96+
s = _extract_value(key)
97+
if not s:
98+
return None
99+
return parse_date(s)
100+
101+
102+
def extract_dates(key: Union[str, Sequence[str]]) -> Optional[TimeValues]:
103+
parts = extract_strings(key)
104+
if not parts:
105+
return None
106+
values: TimeValues = []
107+
108+
def push_range(first: str, last: str):
109+
first_d = parse_date(first)
110+
last_d = parse_date(last)
111+
if first_d == last_d:
112+
# the first and last numbers are the same, just treat it as a singe value
113+
return first_d
114+
if last_d > first_d:
115+
# add the range as an array
116+
return (first_d, last_d)
117+
# the range is inverted, this is an error
118+
raise ValidationFailedException(f"{key}: the given range is inverted")
119+
120+
for part in parts:
121+
if "-" not in part and ":" not in part:
122+
# YYYYMMDD
123+
values.append(parse_date(part))
124+
continue
125+
if ":" in part:
126+
# YYYY-MM-DD:YYYY-MM-DD
127+
range_part = part.split(":", 2)
128+
r = push_range(range_part[0], range_part[1])
129+
if r is None:
130+
return None
131+
values.append(r)
132+
continue
133+
# YYYY-MM-DD or YYYYMMDD-YYYYMMDD
134+
# split on the dash
135+
range_part = part.split("-")
136+
if len(range_part) == 2:
137+
# YYYYMMDD-YYYYMMDD
138+
r = push_range(range_part[0], range_part[1])
139+
if r is None:
140+
return None
141+
values.append(r)
142+
continue
143+
# YYYY-MM-DD
144+
values.append(parse_date(part))
145+
# success, return the list
146+
return values
147+
18148
def _parse_common_multi_arg(key: str) -> List[Tuple[str, Union[bool, Sequence[str]]]]:
19149
# support multiple request parameter with the same name
20150
line = ";".join(request.values.getlist(key))

src/server/_query.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
from ._common import db
1919
from ._printer import create_printer, APrinter
2020
from ._exceptions import DatabaseErrorException
21-
from ._validate import extract_strings
22-
from ._params import GeoFilter, SourceSignalFilter, TimeFilter
21+
from ._params import extract_strings, GeoFilter, SourceSignalFilter, TimeFilter
2322
from .utils import time_values_to_ranges, TimeValues
2423

2524

src/server/_validate.py

+1-134
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
from typing import List, Optional, Sequence, Tuple, Union
1+
from typing import Optional
22

33
from flask import request
44

55
from ._exceptions import UnAuthenticatedException, ValidationFailedException
6-
from .utils import TimeValues
76

87

98
def resolve_auth_token() -> Optional[str]:
@@ -55,135 +54,3 @@ def require_any(*values: str, empty=False) -> bool:
5554
if request.values.get(value) or (empty and value in request.values):
5655
return True
5756
raise ValidationFailedException(f"missing parameter: need one of [{', '.join(values)}]")
58-
59-
60-
def _extract_value(key: Union[str, Sequence[str]]) -> Optional[str]:
61-
if isinstance(key, str):
62-
return request.values.get(key)
63-
for k in key:
64-
if k in request.values:
65-
return request.values[k]
66-
return None
67-
68-
69-
def _extract_list_value(key: Union[str, Sequence[str]]) -> List[str]:
70-
if isinstance(key, str):
71-
return request.values.getlist(key)
72-
for k in key:
73-
if k in request.values:
74-
return request.values.getlist(k)
75-
return []
76-
77-
78-
def extract_strings(key: Union[str, Sequence[str]]) -> Optional[List[str]]:
79-
s = _extract_list_value(key)
80-
if not s:
81-
# nothing to do
82-
return None
83-
# we can have multiple values
84-
return [v for vs in s for v in vs.split(",")]
85-
86-
87-
IntRange = Union[Tuple[int, int], int]
88-
89-
90-
def extract_integer(key: Union[str, Sequence[str]]) -> Optional[int]:
91-
s = _extract_value(key)
92-
if not s:
93-
# nothing to do
94-
return None
95-
try:
96-
return int(s)
97-
except ValueError:
98-
raise ValidationFailedException(f"{key}: not a number: {s}")
99-
100-
101-
def extract_integers(key: Union[str, Sequence[str]]) -> Optional[List[IntRange]]:
102-
parts = extract_strings(key)
103-
if not parts:
104-
# nothing to do
105-
return None
106-
107-
def _parse_range(part: str):
108-
if "-" not in part:
109-
return int(part)
110-
r = part.split("-", 2)
111-
first = int(r[0])
112-
last = int(r[1])
113-
if first == last:
114-
# the first and last numbers are the same, just treat it as a singe value
115-
return first
116-
elif last > first:
117-
# add the range as an array
118-
return (first, last)
119-
# the range is inverted, this is an error
120-
raise ValidationFailedException(f"{key}: the given range is inverted")
121-
122-
try:
123-
values = [_parse_range(part) for part in parts]
124-
# check for invalid values
125-
return None if any(v is None for v in values) else values
126-
except ValueError as e:
127-
raise ValidationFailedException(f"{key}: not a number: {str(e)}")
128-
129-
130-
def parse_date(s: str) -> int:
131-
# parses a given string in format YYYYMMDD or YYYY-MM-DD to a number in the form YYYYMMDD
132-
try:
133-
return int(s.replace("-", ""))
134-
except ValueError:
135-
raise ValidationFailedException(f"not a valid date: {s}")
136-
137-
138-
def extract_date(key: Union[str, Sequence[str]]) -> Optional[int]:
139-
s = _extract_value(key)
140-
if not s:
141-
return None
142-
return parse_date(s)
143-
144-
145-
def extract_dates(key: Union[str, Sequence[str]]) -> Optional[TimeValues]:
146-
parts = extract_strings(key)
147-
if not parts:
148-
return None
149-
values: TimeValues = []
150-
151-
def push_range(first: str, last: str):
152-
first_d = parse_date(first)
153-
last_d = parse_date(last)
154-
if first_d == last_d:
155-
# the first and last numbers are the same, just treat it as a singe value
156-
return first_d
157-
if last_d > first_d:
158-
# add the range as an array
159-
return (first_d, last_d)
160-
# the range is inverted, this is an error
161-
raise ValidationFailedException(f"{key}: the given range is inverted")
162-
163-
for part in parts:
164-
if "-" not in part and ":" not in part:
165-
# YYYYMMDD
166-
values.append(parse_date(part))
167-
continue
168-
if ":" in part:
169-
# YYYY-MM-DD:YYYY-MM-DD
170-
range_part = part.split(":", 2)
171-
r = push_range(range_part[0], range_part[1])
172-
if r is None:
173-
return None
174-
values.append(r)
175-
continue
176-
# YYYY-MM-DD or YYYYMMDD-YYYYMMDD
177-
# split on the dash
178-
range_part = part.split("-")
179-
if len(range_part) == 2:
180-
# YYYYMMDD-YYYYMMDD
181-
r = push_range(range_part[0], range_part[1])
182-
if r is None:
183-
return None
184-
values.append(r)
185-
continue
186-
# YYYY-MM-DD
187-
values.append(parse_date(part))
188-
# success, return the list
189-
return values

src/server/endpoints/afhsb.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
from .._config import AUTH
66
from .._query import execute_queries, filter_integers, filter_strings
7-
from .._validate import check_auth_token, extract_integers, extract_strings, require_all
7+
from .._params import extract_integers, extract_strings
8+
from .._validate import check_auth_token, require_all
89

910
# first argument is the endpoint name
1011
bp = Blueprint("afhsb", __name__)

src/server/endpoints/cdc.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from flask import Blueprint
22

33
from .._config import AUTH, NATION_REGION, REGION_TO_STATE
4-
from .._validate import require_all, extract_strings, extract_integers, check_auth_token
4+
from .._params import extract_strings, extract_integers
5+
from .._validate import require_all, check_auth_token
56
from .._query import filter_strings, execute_queries, filter_integers
67

78
# first argument is the endpoint name

src/server/endpoints/covid_hosp_facility.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from flask import Blueprint
22

33
from .._query import execute_query, QueryBuilder
4-
from .._validate import extract_integers, extract_strings, require_all
4+
from .._params import extract_integers, extract_strings
5+
from .._validate import require_all
56

67
# first argument is the endpoint name
78
bp = Blueprint("covid_hosp_facility", __name__)

src/server/endpoints/covid_hosp_facility_lookup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from flask import Blueprint
22

33
from .._query import execute_query, QueryBuilder
4-
from .._validate import extract_strings, require_any
4+
from .._params import extract_strings
5+
from .._validate import require_any
56

67
# first argument is the endpoint name
78
bp = Blueprint("covid_hosp_facility_lookup", __name__)

src/server/endpoints/covid_hosp_state_timeseries.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from flask import Blueprint
22

33
from .._query import execute_query, QueryBuilder
4-
from .._validate import extract_integers, extract_strings, extract_date, require_all
4+
from .._params import extract_integers, extract_strings, extract_date
5+
from .._validate import require_all
56

67
# first argument is the endpoint name
78
bp = Blueprint("covid_hosp_state_timeseries", __name__)

src/server/endpoints/covidcast.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@
2828
)
2929
from .._query import QueryBuilder, execute_query, run_query, parse_row, filter_fields
3030
from .._printer import create_printer, CSVPrinter
31-
from .._validate import (
31+
from .._params import (
3232
extract_date,
3333
extract_dates,
3434
extract_integer,
35-
extract_strings,
35+
)
36+
from .._validate import (
3637
require_all,
37-
require_any,
3838
)
3939
from .._pandas import as_pandas, print_pandas
4040
from .covidcast_utils import compute_trend, compute_trends, compute_correlations, compute_trend_value, CovidcastMetaEntry

src/server/endpoints/covidcast_meta.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from .._common import db
99
from .._printer import create_printer
1010
from .._query import filter_fields
11-
from .._validate import extract_strings
11+
from .._params import extract_strings
1212
from ..utils.logger import get_structured_logger
1313

1414
bp = Blueprint("covidcast_meta", __name__)

src/server/endpoints/covidcast_nowcast.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from flask import Blueprint, request
22

33
from .._query import execute_query, filter_integers, filter_strings
4-
from .._validate import (
4+
from .._params import (
55
extract_date,
66
extract_dates,
77
extract_integer,
88
extract_strings,
9+
)
10+
from .._validate import (
911
require_all,
1012
require_any,
1113
)

0 commit comments

Comments
 (0)