Skip to content

Deploy validation params to production #966

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 70 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
51d9de8
remove unnecessary second loop of signals
sgsmob Feb 5, 2021
cd3d308
Merge branch 'main' of github.com:cmu-delphi/covidcast-indicators int…
sgsmob Feb 8, 2021
e7ab5ea
outline of new screening var
nmdefries Feb 19, 2021
16134d3
Combo bug fix
dshemetov Mar 17, 2021
726bd62
Combo indicators: remove unused import
dshemetov Mar 17, 2021
a7e824e
Combo: make linter happy
dshemetov Mar 17, 2021
bd4d70e
check group sample size before aggregating
nmdefries Mar 17, 2021
e4ab067
simplify batching
nmdefries Mar 17, 2021
328df25
update usafacts to use new lag style
sgsmob Mar 19, 2021
6d172a0
adapt validation for safegraph_patterns
sgsmob Mar 19, 2021
16ce2e9
create screening testing positive var
nmdefries Mar 11, 2021
a3c69cb
Merge branch 'main' into fb-package-screening-positive-test
nmdefries Mar 19, 2021
88236f0
Create preliminary validation parameters for quidel_covidtest
sgsmob Mar 19, 2021
605ba68
Combo bug fix:
dshemetov Mar 19, 2021
125bca7
Combo bug: make linter happy
dshemetov Mar 19, 2021
cad316b
parens issues
sgsmob Mar 19, 2021
5ec0a59
allow today for validator
sgsmob Mar 19, 2021
a69cb02
decrease number of county batches. add batching test
nmdefries Mar 22, 2021
9d73d2f
Combo bug: add issue_days param for reissuing
dshemetov Mar 22, 2021
b82f38b
Combo: fix bare except
dshemetov Mar 22, 2021
860ccc0
Merge pull request #932 from dshemetov/fix_combo_bug
krivard Mar 22, 2021
5c577bb
Merge pull request #943 from sgsmob/hotfix
krivard Mar 22, 2021
45a7fae
remove megacounty creation for contingency tables
nmdefries Mar 22, 2021
394a864
Merge branch 'main' into fb-package-filter-groups-preagg
nmdefries Mar 22, 2021
2477337
R-ify case_when check
nmdefries Mar 22, 2021
0424c30
combo cases: If JHU data extends farther into the future than USAFact…
krivard Mar 22, 2021
dc16589
add test for screening test positivity
nmdefries Mar 22, 2021
555875e
Add preliminary validation parameters to hhs_hosp
sgsmob Mar 23, 2021
40f5655
add missing quote
sgsmob Mar 23, 2021
dffbe07
combo cases: drop debug line
krivard Mar 23, 2021
c38e780
Merge pull request #953 from cmu-delphi/combo-no-jhu-only
krivard Mar 23, 2021
38e41e8
CI: Drop cdc_covidnet
krivard Mar 23, 2021
ab18ee0
Add preliminary validation parameters to google_symptoms
sgsmob Mar 23, 2021
ecfafc5
Add preliminary validation parameters for changehc
sgsmob Mar 23, 2021
7708a38
Merge pull request #954 from sgsmob/hhs
krivard Mar 23, 2021
e937125
Merge pull request #940 from cmu-delphi/fb-package-screening-positive…
krivard Mar 23, 2021
3ffa281
Merge pull request #956 from cmu-delphi/ci-drop-covidnet
krivard Mar 23, 2021
548906e
Merge pull request #957 from sgsmob/dv
krivard Mar 23, 2021
51a1048
Merge pull request #958 from sgsmob/google_symptoms
krivard Mar 23, 2021
bcbb2cc
Merge branch 'main' of github.com:cmu-delphi/covidcast-indicators int…
sgsmob Mar 23, 2021
12bd240
Use geomapper to validate geo value correctness
sgsmob Mar 23, 2021
476a73f
no print
sgsmob Mar 23, 2021
18261ab
Merge pull request #959 from sgsmob/validator
krivard Mar 23, 2021
fdec6b1
remove unused library
sgsmob Mar 23, 2021
a3f1704
remove unused import
nmdefries Mar 24, 2021
ead4818
Merge pull request #952 from cmu-delphi/fb-package-filter-groups-preagg
krivard Mar 24, 2021
f6a14f0
Merge pull request #960 from sgsmob/validator_combos
krivard Mar 24, 2021
620707e
Update ansible/templates/quidel_covidtest-params-prod.json.j2
sgsmob Mar 24, 2021
e722def
Update ansible/templates/safegraph_patterns-params-prod.json.j2
sgsmob Mar 24, 2021
7bb8930
Update safegraph_patterns/params.json.template
sgsmob Mar 24, 2021
1b9537c
Update ansible/templates/usafacts-params-prod.json.j2
sgsmob Mar 24, 2021
15b2328
Update usafacts/params.json.template
sgsmob Mar 24, 2021
bbbb633
Update quidel_covidtest/params.json.template
sgsmob Mar 24, 2021
0f49c39
Merge pull request #939 from sgsmob/safegraph_patterns
krivard Mar 24, 2021
f877bc3
Merge pull request #942 from sgsmob/quidel_covidtest
krivard Mar 24, 2021
a422152
Merge pull request #938 from sgsmob/usafacts
krivard Mar 24, 2021
0dd84f0
Update contribution guide with current branch configuration and devel…
krivard Jan 12, 2021
8db66dc
Merge pull request #686 from cmu-delphi/update-contribution-guide
krivard Mar 24, 2021
26747f5
add ability to have separate geo_values
sgsmob Mar 24, 2021
3b19472
Merge branch 'main' of github.com:cmu-delphi/covidcast-indicators int…
sgsmob Mar 24, 2021
f5fd5b1
update documentation
sgsmob Mar 24, 2021
ecfa98a
allow state level fips in county validation
sgsmob Mar 25, 2021
c1da4fe
include territories in fips test
sgsmob Mar 25, 2021
b8c04c4
Update _delphi_utils_python/tests/validator/test_static.py
sgsmob Mar 25, 2021
73aa447
name changing
sgsmob Mar 25, 2021
a2f5bb0
name change 2
sgsmob Mar 25, 2021
b1ef84e
Merge pull request #961 from sgsmob/hotfix
krivard Mar 25, 2021
dc35c40
validator allows no check name specified
sgsmob Mar 25, 2021
00e85f7
documentation updated
sgsmob Mar 25, 2021
67a10e0
Merge pull request #965 from sgsmob/validator
krivard Mar 25, 2021
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
30 changes: 9 additions & 21 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

* `main`

The primary/authoritative branch of this repository is called `main`, and contains up-to-date code and supporting libraries. This should be your starting point when creating a new indicator. It is protected so that only reviewed pull requests can be merged in.
The primary branch of this repository is called `main`, and contains the version of the code and supporting libraries currently under development. This should be your starting point when creating a new indicator. It is protected so that only reviewed pull requests can be merged in. The main branch is configured to deploy to our staging environment on push. CI is set up to build and test all indicators on PR.

* `deploy-*`
* `prod`

Each automated pipeline has a corresponding branch which automatically deploys to a runtime host which runs the pipeline at a designated time each day. New features and bugfixes are merged into this branch using a pull request, so that our CI system can run the lint and test cycles and make sure the package will run correctly on the runtime host. If an indicator does not have a branch named after it starting with `deploy-`, that means the indicator has not yet been automated, and has a designated human keeper who is responsible for making sure the indicator runs each day -- whether that is manually or using a scheduler like cron is the keeper's choice.
The production branch is configured to automatically deploy to our production environment on push, and is protected so that only administrators can push or merge. CI is set up to build and test all indicators on PR.

* everything else

Expand All @@ -22,15 +22,6 @@ If you ensure that each issue deals with a single topic (ie a single new propose

Admins will assign issues to one or more people based on balancing expediency, expertise, and team robustness. It may be faster for one person to fix something, but we can reduce the risk of having too many single points of failure if two people work on it together.

## Project Boards

The Delphi Engineering team uses project boards to structure its weekly calls and track active tasks.

Immediate work is tracked on [Release Planning](https://github.com/cmu-delphi/covidcast-indicators/projects/2)

Long-term work and modeling collaborations are tracked on [Refactoring](https://github.com/cmu-delphi/covidcast-indicators/projects/3)


## General workflow for indicators creation and deployment

So, how does one go about developing a pipeline for a new data source?
Expand All @@ -40,13 +31,11 @@ So, how does one go about developing a pipeline for a new data source?
1. Create your new indicator branch from `main`.
2. Build it using the appropriate template, following the guidelines in the included README.md and REVIEW.md files.
3. Make some stuff!
4. When your stuff works, push your `dev-*` branch to remote for review.
5. Consult with a platform engineer for the remaining production setup needs. They will create a branch called `deploy-*` for your indicator.
6. Initiate a pull request against this new branch.
7. Following [the source documentation template](https://github.com/cmu-delphi/delphi-epidata/blob/main/docs/api/covidcast-signals/_source-template.md), create public API documentation for the source. You can submit this as a pull request against the delphi-epidata repository.
8. If your peers like the code, the documentation is ready, and Jenkins approves, deploy your changes by merging the PR.
9. An admin will propagate your successful changes to `main`.
10. Rejoice!
4. When your stuff works, push your development branch to remote, and open a PR against `main` for review.
5. Once your PR has been merged, consult with a platform engineer for the remaining production setup needs. They will create a deployment workflow for your indicator including any necessary production parameters. Production secrets are encrypted in the Ansible vault. This workflow will be tested in staging by admins, who will consult you about any problems they encounter.
6. Following [the source documentation template](https://github.com/cmu-delphi/delphi-epidata/blob/main/docs/api/covidcast-signals/_source-template.md), create public API documentation for the source. You can submit this as a pull request against the delphi-epidata repository.
7. If your peers like the code, the documentation is ready, and the staging runs are successful, work with admins to schedule your indicator in production, merge the documentation, and announce the new indicator to the mailing list.
8. Rejoice!

### Starting out

Expand Down Expand Up @@ -86,12 +75,11 @@ becomes available to the public.

Once you have your branch set up you should get in touch with a platform engineer to pair up on the remaining production needs. These include:

- Creating the corresponding `deploy-*` branch in the repo.
- Adding the necessary Jenkins scripts for your indicator.
- Preparing the runtime host with any Automation configuration necessities.
- Reviewing the workflow to make sure it meets the general guidelines and will run as expected on the runtime host.

Once all the last mile configuration is in place you can create a pull request against the correct `deploy-*` branch to initiate the CI/CD pipeline which will build, test, and package your indicator for deployment.
Once all the last mile configuration is in place you can create a pull request against `prod` to initiate the CI/CD pipeline which will build, test, and package your indicator for deployment.

If everything looks ok, you've drafted source documentation, platform engineering has validated the last mile, and the pull request is accepted, you can merge the PR. Deployment will start automatically.

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
if: github.event.pull_request.draft == false
strategy:
matrix:
packages: [_delphi_utils_python, cdc_covidnet, changehc, claims_hosp, combo_cases_and_deaths, covid_act_now, doctor_visits, google_symptoms, hhs_hosp, hhs_facilities, jhu, nchs_mortality, nowcast, quidel, quidel_covidtest, safegraph, safegraph_patterns, usafacts]
packages: [_delphi_utils_python, changehc, claims_hosp, combo_cases_and_deaths, covid_act_now, doctor_visits, google_symptoms, hhs_hosp, hhs_facilities, jhu, nchs_mortality, nowcast, quidel, quidel_covidtest, safegraph, safegraph_patterns, usafacts]
defaults:
run:
working-directory: ${{ matrix.packages }}
Expand Down
7 changes: 4 additions & 3 deletions _delphi_utils_python/delphi_utils/validator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,19 @@ Please update the follow settings:

* `common`: global validation settings
* `data_source`: should match the [formatting](https://cmu-delphi.github.io/delphi-epidata/api/covidcast_signals.html) as used in COVIDcast API calls
* `end_date`: specifies the last date to be checked; this can be specified as `YYYY-MM-DD` or as `today-{num}`. The latter is interpretted as `num` days before the current date (with `today-0` being today).
* `end_date`: specifies the last date to be checked; this can be specified as `YYYY-MM-DD`, `today`, or `today-{num}`. The latter is interpretted as `num` days before the current date.
* `span_length`: specifies the number of days before the `end_date` to check. `span_length` should be long enough to contain all recent source data that is still in the process of being updated (i.e. in the backfill period), for example, if the data source of interest has a 2-week lag before all reports are in for a given date, `span_length` should be 14 days
* `suppressed_errors`: list of objects specifying errors that have been manually verified as false positives or acceptable deviations from expected. These errors can be specified with the following variables, where omitted values are interpreted as a wildcard, i.e., not specifying a date applies to all dates:
* `check_name` (required): name of the check, as specified in the validation output
* `check_name`: name of the check, as specified in the validation output
* `date`: date in `YYYY-MM-DD` format
* `geo_type`: geo resolution of the data
* `signal`: name of COVIDcast API signal
* `test_mode`: boolean; `true` checks only a small number of data files
* `static`: settings for validations that don't require comparison with external COVIDcast API data
* `minimum_sample_size` (default: 100): threshold for flagging small sample sizes as invalid
* `missing_se_allowed` (default: False): whether signals with missing standard errors are valid
* `misisng_sample_size_allowed` (default: False): whether signals with missing sample sizes are valid
* `missing_sample_size_allowed` (default: False): whether signals with missing sample sizes are valid
* `additional_valid_geo_values` (default: `{}`): map of geo type names to lists of geo values that are not recorded in the GeoMapper but are nonetheless valid for this indicator
* `dynamic`: settings for validations that require comparison with external COVIDcast API data
* `ref_window_size` (default: 7): number of days over which to look back for comparison
* `smoothed_signals`: list of the names of the signals that are smoothed (e.g. 7-day average)
Expand Down
38 changes: 17 additions & 21 deletions _delphi_utils_python/delphi_utils/validator/dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,27 +119,23 @@ def validate(self, all_frames, report):
continue

# Outlier dataframe
if (signal_type in ["confirmed_7dav_cumulative_num", "confirmed_7dav_incidence_num",
"confirmed_cumulative_num", "confirmed_incidence_num",
"deaths_7dav_cumulative_num",
"deaths_cumulative_num"]):
earliest_available_date = geo_sig_df["time_value"].min()
source_df = geo_sig_df.query(
'time_value <= @self.params.time_window.end_date & '
'time_value >= @self.params.time_window.start_date'
)

# These variables are interpolated into the call to `api_df_or_error.query()`
# below but pylint doesn't recognize that.
# pylint: disable=unused-variable
outlier_start_date = earliest_available_date - outlier_lookbehind
outlier_end_date = earliest_available_date - timedelta(days=1)
outlier_api_df = api_df_or_error.query(
'time_value <= @outlier_end_date & time_value >= @outlier_start_date')
# pylint: enable=unused-variable

self.check_positive_negative_spikes(
source_df, outlier_api_df, geo_type, signal_type, report)
earliest_available_date = geo_sig_df["time_value"].min()
source_df = geo_sig_df.query(
'time_value <= @self.params.time_window.end_date & '
'time_value >= @self.params.time_window.start_date'
)

# These variables are interpolated into the call to `api_df_or_error.query()`
# below but pylint doesn't recognize that.
# pylint: disable=unused-variable
outlier_start_date = earliest_available_date - outlier_lookbehind
outlier_end_date = earliest_available_date - timedelta(days=1)
outlier_api_df = api_df_or_error.query(
'time_value <= @outlier_end_date & time_value >= @outlier_start_date')
# pylint: enable=unused-variable

self.check_positive_negative_spikes(
source_df, outlier_api_df, geo_type, signal_type, report)

# Check data from a group of dates against recent (previous 7 days,
# by default) data from the API.
Expand Down
7 changes: 4 additions & 3 deletions _delphi_utils_python/delphi_utils/validator/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ValidationFailure:
"""Structured report of single validation failure."""

def __init__(self,
check_name: str,
check_name: Optional[str]=None,
date: Optional[Union[str, dt.date]]=None,
geo_type: Optional[str]=None,
signal: Optional[str]=None,
Expand All @@ -33,8 +33,9 @@ def __init__(self,

Parameters
----------
check_name: str
Name of check at which the failure happened.
check_name: Optional[str]
Name of check at which the failure happened. A value of `None` is used to express all
possible checks with a given `date`, `geo_type`, and/or `signal`.
date: Optional[Union[str, dt.date]]
Date corresponding to the data over which the failure happened.
Strings are interpretted in ISO format ("YYYY-MM-DD").
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"minimum_sample_size": 100,
"missing_sample_size_allowed": true,
"missing_se_allowed": true,
"validator_static_file_dir": "../validator/static"
"additional_valid_geo_values": {
"state": ["xyz"]
}
},
"dynamic": {
"expected_lag": {
Expand Down
32 changes: 23 additions & 9 deletions _delphi_utils_python/delphi_utils/validator/static.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Static file checks."""
from os.path import join
import re
from datetime import datetime
from dataclasses import dataclass
from typing import Dict, List
import pandas as pd
from .datafetcher import FILENAME_REGEX
from .errors import ValidationFailure
from .utils import GEO_REGEX_DICT, TimeWindow
from ..geomap import GeoMapper

class StaticValidator:
"""Class for validation of static properties of individual datasets."""
Expand All @@ -15,8 +16,6 @@ class StaticValidator:
class Parameters:
"""Configuration parameters."""

# Place to find the data files
validator_static_file_dir: str
# Span of time over which to perform checks
time_window: TimeWindow
# Threshold for reporting small sample sizes
Expand All @@ -25,6 +24,8 @@ class Parameters:
missing_se_allowed: bool
# Whether to report missing sample sizes
missing_sample_size_allowed: bool
# Valid geo values not found in the GeoMapper
additional_valid_geo_values: Dict[str, List[str]]

def __init__(self, params):
"""
Expand All @@ -37,13 +38,12 @@ def __init__(self, params):
static_params = params.get("static", dict())

self.params = self.Parameters(
validator_static_file_dir = static_params.get('validator_static_file_dir',
'../validator/static'),
time_window = TimeWindow.from_params(common_params["end_date"],
common_params["span_length"]),
minimum_sample_size = static_params.get('minimum_sample_size', 100),
missing_se_allowed = static_params.get('missing_se_allowed', False),
missing_sample_size_allowed = static_params.get('missing_sample_size_allowed', False)
missing_sample_size_allowed = static_params.get('missing_sample_size_allowed', False),
additional_valid_geo_values = static_params.get('additional_valid_geo_values', {})
)


Expand Down Expand Up @@ -134,6 +134,22 @@ def check_df_format(self, df_to_test, nameformat, report):

report.increment_total_checks()

def _get_valid_geo_values(self, geo_type):
# geomapper uses slightly different naming conventions for geo_types
if geo_type == "state":
geomap_type = "state_id"
elif geo_type == "county":
geomap_type = "fips"
else:
geomap_type = geo_type

gmpr = GeoMapper()
valid_geos = gmpr.get_geo_values(geomap_type)
valid_geos |= set(self.params.additional_valid_geo_values.get(geo_type, []))
if geo_type == "county":
valid_geos |= set(x + "000" for x in gmpr.get_geo_values("state_code"))
return valid_geos

def check_bad_geo_id_value(self, df_to_test, filename, geo_type, report):
"""
Check for bad geo_id values, by comparing to a list of known historical values.
Expand All @@ -143,9 +159,7 @@ def check_bad_geo_id_value(self, df_to_test, filename, geo_type, report):
- geo_type: string from CSV name specifying geo type (state, county, msa, etc.) of data
- report: ValidationReport; report where results are added
"""
file_path = join(self.params.validator_static_file_dir, geo_type + '_geo.csv')
valid_geo_df = pd.read_csv(file_path, dtype={'geo_id': str})
valid_geos = valid_geo_df['geo_id'].values
valid_geos = self._get_valid_geo_values(geo_type)
unexpected_geos = [geo for geo in df_to_test['geo_id']
if geo.lower() not in valid_geos]
if len(unexpected_geos) > 0:
Expand Down
Loading