Skip to content

Commit aad54e0

Browse files
committed
feat: support week in /csv
1 parent c86131b commit aad54e0

File tree

2 files changed

+24
-25
lines changed

2 files changed

+24
-25
lines changed

src/server/_params.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
from flask import request
77

8+
89
from ._exceptions import ValidationFailedException
9-
from .utils import days_in_range, weeks_in_range
10+
from .utils import days_in_range, weeks_in_range, guess_time_value_is_day
1011

1112

1213
def _parse_common_multi_arg(key: str) -> List[Tuple[str, Union[bool, Sequence[str]]]]:
@@ -254,9 +255,11 @@ def parse_week_range_arg(key: str) -> Tuple[int, int]:
254255
raise ValidationFailedException(f"{key} must match YYYYWW-YYYYWW")
255256
return r
256257

257-
def parse_day_or_week_arg(key: str) -> Tuple[int, bool]:
258+
def parse_day_or_week_arg(key: str, default_value: Optional[int] = None) -> Tuple[int, bool]:
258259
v = request.values.get(key)
259260
if not v:
261+
if default_value is not None:
262+
return default_value, guess_time_value_is_day(default_value)
260263
raise ValidationFailedException(f"{key} param is required")
261264
# format is either YYYY-MM-DD or YYYYMMDD or YYYYMM
262265
is_week = len(v) == 6

src/server/endpoints/covidcast.py

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import List, Optional, Union, Tuple, Dict, Any
22
from itertools import groupby
3-
from datetime import date, datetime, timedelta
3+
from datetime import date, timedelta
44
from epiweeks import Week
55
from flask import Blueprint, request
66
from flask.json import loads, jsonify
@@ -366,43 +366,39 @@ def gen():
366366
def handle_export():
367367
source, signal = request.args.get("signal", "jhu-csse:confirmed_incidence_num").split(":")
368368
source_signal_pairs = [SourceSignalPair(source, [signal])]
369-
_, weekly_signals = count_signal_time_types(source_signal_pairs)
370-
if weekly_signals > 0:
371-
raise ValidationFailedException('weekly signals are not supported')
369+
daily_signals, weekly_signals = count_signal_time_types(source_signal_pairs)
372370
source_signal_pairs, alias_mapper = create_source_signal_alias_mapper(source_signal_pairs)
373-
start_day = request.args.get("start_day", "2020-04-01")
374-
end_day = request.args.get("end_day", "2020-09-01")
371+
start_day, is_day = parse_day_or_week_arg("start_day", 202001 if weekly_signals > 0 else 20200401)
372+
end_day , is_end_day = parse_day_or_week_arg("end_day", 202020 if weekly_signals > 0 else 20200901)
373+
if is_day != is_end_day:
374+
raise ValidationFailedException("mixing weeks with day arguments")
375+
_verify_argument_time_type_matches(is_day, daily_signals, weekly_signals)
376+
375377
geo_type = request.args.get("geo_type", "county")
376378
geo_values = request.args.get("geo_values", "*")
377379

378380
if geo_values != "*":
379381
geo_values = geo_values.split(",")
380382

381-
as_of = request.args.get("as_of", None)
382-
383-
start_day = datetime.strptime(start_day, "%Y-%m-%d").date()
384-
end_day = datetime.strptime(end_day, "%Y-%m-%d").date()
385-
386-
if as_of is not None:
387-
as_of = datetime.strptime(as_of, "%Y-%m-%d").date()
383+
as_of, is_as_of_day = parse_day_or_week_arg('as_of') if 'as_of' in request.args else None, is_day
384+
if is_day != is_as_of_day:
385+
raise ValidationFailedException("mixing weeks with day arguments")
388386

389387
# build query
390388
q = QueryBuilder("covidcast", "t")
391389

392390
q.set_fields(["geo_value", "signal", "time_value", "issue", "lag", "value", "stderr", "sample_size", "geo_type", "source"], [], [])
393391
q.set_order("time_value", "geo_value")
394-
q.where(time_type="day")
395392
q.where_source_signal_pairs("source", "signal", source_signal_pairs)
396-
q.conditions.append("time_value BETWEEN :start_day AND :end_day")
397-
q.params["start_day"] = date_to_time_value(start_day)
398-
q.params["end_day"] = date_to_time_value(end_day)
393+
q.where_time_pairs("time_type", "time_value", [TimePair('day' if is_day else 'week', [(start_day, end_day)])])
399394
q.where_geo_pairs("geo_type", "geo_value", [GeoPair(geo_type, True if geo_values == "*" else geo_values)])
400395

401-
_handle_lag_issues_as_of(q, None, None, date_to_time_value(as_of) if as_of is not None else None)
396+
_handle_lag_issues_as_of(q, None, None, as_of)
402397

398+
format_date = time_value_to_iso if is_day else lambda x: week_value_to_week(x).cdcformat()
403399
# tag as_of in filename, if it was specified
404-
as_of_str = "-asof-{as_of}".format(as_of=as_of.isoformat()) if as_of is not None else ""
405-
filename = "covidcast-{source}-{signal}-{start_day}-to-{end_day}{as_of}".format(source=source, signal=signal, start_day=start_day.isoformat(), end_day=end_day.isoformat(), as_of=as_of_str)
400+
as_of_str = "-asof-{as_of}".format(as_of=format_date(as_of)) if as_of is not None else ""
401+
filename = "covidcast-{source}-{signal}-{start_day}-to-{end_day}{as_of}".format(source=source, signal=signal, start_day=format_date(start_day), end_day=format_date(end_day), as_of=as_of_str)
406402
p = CSVPrinter(filename)
407403

408404
def parse_row(i, row):
@@ -411,8 +407,8 @@ def parse_row(i, row):
411407
"": i,
412408
"geo_value": row["geo_value"],
413409
"signal": row["signal"],
414-
"time_value": time_value_to_iso(row["time_value"]),
415-
"issue": time_value_to_iso(row["issue"]),
410+
"time_value": time_value_to_iso(row["time_value"]) if is_day else row["time_value"],
411+
"issue": time_value_to_iso(row["issue"]) if is_day else row["issue"],
416412
"lag": row["lag"],
417413
"value": row["value"],
418414
"stderr": row["stderr"],
@@ -436,7 +432,7 @@ def gen(first_row, rows):
436432
first_row = next(r, None)
437433
if not first_row:
438434
return "No matching data found for signal {source}:{signal} " "at {geo} level from {start_day} to {end_day}, as of {as_of}.".format(
439-
source=source, signal=signal, geo=geo_type, start_day=start_day.isoformat(), end_day=end_day.isoformat(), as_of=(date.today().isoformat() if as_of is None else as_of.isoformat())
435+
source=source, signal=signal, geo=geo_type, start_day=format_date(start_day), end_day=format_date(end_day), as_of=(date.today().isoformat() if as_of is None else format_date(as_of))
440436
)
441437

442438
# now use a generator for sending the rows and execute all the other queries

0 commit comments

Comments
 (0)