|
1 | 1 | from typing import List, Optional, Union, Tuple, Dict, Any, Set
|
2 | 2 | from itertools import groupby
|
3 |
| -from datetime import date, datetime |
| 3 | +from datetime import date, datetime, timedelta |
4 | 4 | from flask import Blueprint, request
|
5 | 5 | from flask.json import loads, jsonify
|
6 | 6 | from bisect import bisect_right
|
7 | 7 | from sqlalchemy import text
|
| 8 | +from pandas import read_csv |
8 | 9 |
|
9 | 10 | from .._common import is_compatibility_mode, db
|
10 | 11 | from .._exceptions import ValidationFailedException, DatabaseErrorException
|
|
32 | 33 | require_any,
|
33 | 34 | )
|
34 | 35 | from .._db import sql_table_has_columns
|
35 |
| -from .._pandas import as_pandas |
| 36 | +from .._pandas import as_pandas, print_pandas |
36 | 37 | from .covidcast_utils import compute_trend, compute_trends, compute_correlations, compute_trend_value, CovidcastMetaEntry, AllSignalsMap
|
37 |
| -from ..utils import shift_time_value, date_to_time_value, time_value_to_iso |
| 38 | +from ..utils import shift_time_value, date_to_time_value, time_value_to_iso, time_value_to_date |
38 | 39 |
|
39 | 40 | # first argument is the endpoint name
|
40 | 41 | bp = Blueprint("covidcast", __name__)
|
@@ -150,7 +151,6 @@ def handle():
|
150 | 151 | q.set_order("source", "signal", "time_type", "time_value", "geo_type", "geo_value", "issue")
|
151 | 152 | q.set_fields(fields_string, fields_int, fields_float)
|
152 | 153 |
|
153 |
| - |
154 | 154 | # basic query info
|
155 | 155 | # data type of each field
|
156 | 156 | # build the source, signal, time, and location (type and id) filters
|
@@ -244,8 +244,8 @@ def handle_trendseries():
|
244 | 244 | def gen(rows):
|
245 | 245 | for key, group in groupby((parse_row(row, fields_string, fields_int, fields_float) for row in rows), lambda row: (row["geo_type"], row["geo_value"], row["source"], row["signal"])):
|
246 | 246 | trends = compute_trends(key[0], key[1], key[2], key[3], shifter, ((row["time_value"], row["value"]) for row in group))
|
247 |
| - for t in trends: |
248 |
| - yield t |
| 247 | + for trend in trends: |
| 248 | + yield trend.asdict() |
249 | 249 |
|
250 | 250 | # execute first query
|
251 | 251 | try:
|
@@ -493,3 +493,61 @@ def handle_meta():
|
493 | 493 | entry.intergrate(row)
|
494 | 494 |
|
495 | 495 | return jsonify([r.asdict() for r in out.values()])
|
| 496 | + |
| 497 | + |
| 498 | +@bp.route("/coverage", methods=("GET", "POST")) |
| 499 | +def handle_coverage(): |
| 500 | + """ |
| 501 | + similar to /signal_dashboard_coverage for a specific signal returns the coverage (number of locations for a given geo_type) |
| 502 | + """ |
| 503 | + |
| 504 | + signal = parse_source_signal_pairs() |
| 505 | + geo_type = request.args.get("geo_type", "county") |
| 506 | + if "window" in request.values: |
| 507 | + time_window = parse_day_range_arg("window") |
| 508 | + else: |
| 509 | + now_time = extract_date("latest") |
| 510 | + now = date.today() if now_time is None else time_value_to_date(now_time) |
| 511 | + last = extract_integer("days") |
| 512 | + if last is None: |
| 513 | + last = 30 |
| 514 | + time_window = (date_to_time_value(now - timedelta(days=last)), date_to_time_value(now)) |
| 515 | + |
| 516 | + q = QueryBuilder("covidcast", "c") |
| 517 | + fields_string = ["source", "signal"] |
| 518 | + fields_int = ["time_value"] |
| 519 | + |
| 520 | + q.set_fields(fields_string, fields_int) |
| 521 | + |
| 522 | + # manually append the count column because of grouping |
| 523 | + fields_int.append("count") |
| 524 | + q.fields.append(f"count({q.alias}.geo_value) as count") |
| 525 | + |
| 526 | + if geo_type == "only-county": |
| 527 | + q.where(geo_type="county") |
| 528 | + q.conditions.append('geo_value not like "%000"') |
| 529 | + else: |
| 530 | + q.where(geo_type=geo_type) |
| 531 | + q.where_source_signal_pairs("source", "signal", signal) |
| 532 | + q.where_time_pairs("time_type", "time_value", [TimePair("day", [time_window])]) |
| 533 | + q.group_by = "c.source, c.signal, c.time_value" |
| 534 | + q.set_order("source", "signal", "time_value") |
| 535 | + |
| 536 | + _handle_lag_issues_as_of(q, None, None, None) |
| 537 | + |
| 538 | + return execute_query(q.query, q.params, fields_string, fields_int, []) |
| 539 | + |
| 540 | + |
| 541 | +@bp.route("/anomalies", methods=("GET", "POST")) |
| 542 | +def handle_anomalies(): |
| 543 | + """ |
| 544 | + proxy to the excel sheet about data anomalies |
| 545 | + """ |
| 546 | + |
| 547 | + signal = parse_source_signal_arg("signal") |
| 548 | + |
| 549 | + df = read_csv( |
| 550 | + "https://docs.google.com/spreadsheets/d/e/2PACX-1vToGcf9x5PNJg-eSrxadoR5b-LM2Cqs9UML97587OGrIX0LiQDcU1HL-L2AA8o5avbU7yod106ih0_n/pub?gid=0&single=true&output=csv", skip_blank_lines=True |
| 551 | + ) |
| 552 | + df = df[df["source"].notnull() & df["published"]] |
| 553 | + return print_pandas(df) |
0 commit comments