Skip to content

Release Delphi Epidata 4.1.5 #1229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 4.1.4
current_version = 4.1.5
commit = False
tag = False

Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/dependabot-assignments.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Dependabot auto-assign reviewer
on: pull_request

permissions:
pull-requests: write

jobs:
dependabot:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Assign team to PR
run: gh pr edit "$PR_URL" --add-reviewer "cmu-delphi/code-reviewers"
env:
PR_URL: ${{github.event.pull_request.html_url}}
2 changes: 1 addition & 1 deletion dev/local/setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = Delphi Development
version = 4.1.4
version = 4.1.5

[options]
packages =
Expand Down
2 changes: 1 addition & 1 deletion src/client/delphi_epidata.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Epidata <- (function() {
# API base url
BASE_URL <- getOption('epidata.url', default = 'https://api.delphi.cmu.edu/epidata/')

client_version <- '4.1.4'
client_version <- '4.1.5'

auth <- getOption("epidata.auth", default = NA)

Expand Down
2 changes: 1 addition & 1 deletion src/client/delphi_epidata.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}
})(this, function (exports, fetchImpl, jQuery) {
const BASE_URL = "https://api.delphi.cmu.edu/epidata/";
const client_version = "4.1.4";
const client_version = "4.1.5";

// Helper function to cast values and/or ranges to strings
function _listitem(value) {
Expand Down
2 changes: 1 addition & 1 deletion src/client/packaging/npm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "delphi_epidata",
"description": "Delphi Epidata API Client",
"authors": "Delphi Group",
"version": "4.1.4",
"version": "4.1.5",
"license": "MIT",
"homepage": "https://github.com/cmu-delphi/delphi-epidata",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion src/client/packaging/pypi/delphi_epidata/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .delphi_epidata import Epidata

name = 'delphi_epidata'
__version__ = '4.1.4'
__version__ = '4.1.5'
2 changes: 1 addition & 1 deletion src/client/packaging/pypi/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="delphi_epidata",
version="4.1.4",
version="4.1.5",
author="David Farrow",
author_email="[email protected]",
description="A programmatic interface to Delphi's Epidata API.",
Expand Down
44 changes: 31 additions & 13 deletions src/common/covidcast_row.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"value_updated_timestamp": "Int64",
}


@dataclass
class CovidcastRow:
"""A container for the values of a single covidcast database row.
Expand All @@ -35,7 +36,8 @@ class CovidcastRow:
- converting from and to formats (dict, csv, df, kwargs)
- creating consistent views, with consistent data types (dict, csv, df)

The rows are specified in 'v4_schema.sql'. The datatypes are made to match database. When writing to Pandas, the dtypes match the JIT model.py schema.
The rows are specified in 'v4_schema.sql'. The datatypes are made to match
database. When writing to Pandas, the dtypes match the JIT model.py schema.
"""

# Arguments.
Expand All @@ -54,15 +56,20 @@ class CovidcastRow:
missing_sample_size: int
issue: int
lag: int
# The following three fields are only the database, but are not ingested at acquisition and not returned by the API.
# The following three fields are only in the database, but are not ingested at
# acquisition and not returned by the API.
epimetric_id: Optional[int] = None
direction: Optional[int] = None
value_updated_timestamp: Optional[int] = 0

# Classvars.
_db_row_ignore_fields: ClassVar = []
_api_row_ignore_fields: ClassVar = ["epimetric_id", "value_updated_timestamp"]
_api_row_compatibility_ignore_fields: ClassVar = _api_row_ignore_fields + ["source", "time_type", "geo_type"]
_api_row_compatibility_ignore_fields: ClassVar = _api_row_ignore_fields + [
"source",
"time_type",
"geo_type",
]

_pandas_dtypes: ClassVar = PANDAS_DTYPES

Expand All @@ -72,17 +79,22 @@ def as_dict(self, ignore_fields: Optional[List[str]] = None) -> dict:
for key in ignore_fields:
del d[key]
return d

def as_api_row_dict(self, ignore_fields: Optional[List[str]] = None) -> dict:
"""Returns a dict view into the row with the fields returned by the API server."""
"""Returns a dict view into the row with the fields returned by the API
server."""
return self.as_dict(ignore_fields=self._api_row_ignore_fields + (ignore_fields or []))

def as_api_compatibility_row_dict(self, ignore_fields: Optional[List[str]] = None) -> dict:
"""Returns a dict view into the row with the fields returned by the old API server (the PHP server)."""
return self.as_dict(ignore_fields=self._api_row_compatibility_ignore_fields + (ignore_fields or []))
"""Returns a dict view into the row with the fields returned by the old
API server (the PHP server)."""
return self.as_dict(
ignore_fields=self._api_row_compatibility_ignore_fields + (ignore_fields or [])
)

def as_db_row_dict(self, ignore_fields: Optional[List[str]] = None) -> dict:
"""Returns a dict view into the row with the fields returned by the database."""
"""Returns a dict view into the row with the fields returned by the
database."""
return self.as_dict(ignore_fields=self._db_row_ignore_fields + (ignore_fields or []))

def as_dataframe(self, ignore_fields: Optional[List[str]] = None) -> pd.DataFrame:
Expand All @@ -92,15 +104,22 @@ def as_dataframe(self, ignore_fields: Optional[List[str]] = None) -> pd.DataFram
return df

def as_api_row_df(self, ignore_fields: Optional[List[str]] = None) -> pd.DataFrame:
"""Returns a dataframe view into the row with the fields returned by the API server."""
"""Returns a dataframe view into the row with the fields returned by the
API server."""
return self.as_dataframe(ignore_fields=self._api_row_ignore_fields + (ignore_fields or []))

# fmt: off
def as_api_compatibility_row_df(self, ignore_fields: Optional[List[str]] = None) -> pd.DataFrame:
"""Returns a dataframe view into the row with the fields returned by the old API server (the PHP server)."""
return self.as_dataframe(ignore_fields=self._api_row_compatibility_ignore_fields + (ignore_fields or []))
"""Returns a dataframe view into the row with the fields returned by the
old API server (the PHP server)."""
# fmt: on
return self.as_dataframe(
ignore_fields=self._api_row_compatibility_ignore_fields + (ignore_fields or [])
)

def as_db_row_df(self, ignore_fields: Optional[List[str]] = None) -> pd.DataFrame:
"""Returns a dataframe view into the row with the fields returned by an all-field database query."""
"""Returns a dataframe view into the row with the fields returned by an
all-field database query."""
return self.as_dataframe(ignore_fields=self._db_row_ignore_fields + (ignore_fields or []))

def signal_pair(self):
Expand All @@ -113,7 +132,6 @@ def time_pair(self):
return f"{self.time_type}:{self.time_value}"



def check_valid_dtype(dtype):
try:
pd.api.types.pandas_dtype(dtype)
Expand Down
17 changes: 6 additions & 11 deletions src/common/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import threading
import structlog


def handle_exceptions(logger):
"""Handle exceptions using the provided logger."""

def exception_handler(etype, value, traceback):
logger.exception("Top-level exception occurred",
exc_info=(etype, value, traceback))
logger.exception("Top-level exception occurred", exc_info=(etype, value, traceback))

def multithread_exception_handler(args):
exception_handler(args.exc_type, args.exc_value, args.exc_traceback)
Expand All @@ -17,9 +18,7 @@ def multithread_exception_handler(args):
threading.excepthook = multithread_exception_handler


def get_structured_logger(name=__name__,
filename=None,
log_exceptions=True):
def get_structured_logger(name=__name__, filename=None, log_exceptions=True):
"""Create a new structlog logger.

Use the logger returned from this in indicator code using the standard
Expand Down Expand Up @@ -49,11 +48,7 @@ def get_structured_logger(name=__name__,
else:
log_level = logging.INFO

logging.basicConfig(
format="%(message)s",
level=log_level,
handlers=handlers
)
logging.basicConfig(format="%(message)s", level=log_level, handlers=handlers)

def add_pid(logger, method_name, event_dict):
"""
Expand Down Expand Up @@ -85,7 +80,7 @@ def add_pid(logger, method_name, event_dict):
# Decode unicode characters
structlog.processors.UnicodeDecoder(),
# Render as JSON
structlog.processors.JSONRenderer()
structlog.processors.JSONRenderer(),
],
# Use a dict class for keeping track of data.
context_class=dict,
Expand Down
2 changes: 1 addition & 1 deletion src/server/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

load_dotenv()

VERSION = "4.1.4"
VERSION = "4.1.5"

MAX_RESULTS = int(10e6)
MAX_COMPATIBILITY_RESULTS = int(3650)
Expand Down
18 changes: 11 additions & 7 deletions src/server/_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ def __init__(self):
self.result: int = -1
self._max_results: int = MAX_COMPATIBILITY_RESULTS if is_compatibility_mode() else MAX_RESULTS

def make_response(self, gen):
def make_response(self, gen, headers=None):
return Response(
gen,
mimetype="application/json",
headers=headers,
)

def __call__(self, generator: Iterable[Dict[str, Any]]) -> Response:
def __call__(self, generator: Iterable[Dict[str, Any]], headers=None) -> Response:
def gen():
self.result = -2 # no result, default response
began = False
Expand Down Expand Up @@ -84,7 +85,7 @@ def gen():
if r is not None:
yield r

return self.make_response(stream_with_context(gen()))
return self.make_response(stream_with_context(gen()), headers=headers)

@property
def remaining_rows(self) -> int:
Expand Down Expand Up @@ -223,8 +224,11 @@ def __init__(self, filename: Optional[str] = "epidata"):
super(CSVPrinter, self).__init__()
self._filename = filename

def make_response(self, gen):
headers = {"Content-Disposition": f"attachment; filename={self._filename}.csv"} if self._filename else {}
def make_response(self, gen, headers=None):
if headers is None:
headers = {}
if self._filename:
headers["Content-Disposition"] = f"attachment; filename={self._filename}.csv"
return Response(gen, mimetype="text/csv; charset=utf8", headers=headers)

def _begin(self):
Expand Down Expand Up @@ -296,8 +300,8 @@ class JSONLPrinter(APrinter):
a printer class writing in JSONLines format
"""

def make_response(self, gen):
return Response(gen, mimetype=" text/plain; charset=utf8")
def make_response(self, gen, headers=None):
return Response(gen, mimetype=" text/plain; charset=utf8", headers=headers)

def _begin(self):
if show_hard_api_key_warning():
Expand Down
Loading