Skip to content

Commit 5350d2c

Browse files
korlaxxalrokeujingvishakha1812krivardhuisaddison
authored
Add AWS credentials to vault (#220)
* Fix test affected by backfill * Set target python version * Update: added new use case in safegraph * change in params.json.template * update in params.json.template: wip_prefix to wip_signal * Changed func name from epidata_signal() -- > public_signal(), signal_name() --> add_prefix() for better readability * cleaner code, * handling wip signal in python template * Minor changes in process.py, updated test cases * signal naming using wip_signal parameter * Supply missing params.json.template in tests * Switch from raw Epidata client to COVIDcast client * Move signal name constants to their own file, referenced from both run.py and process.py * Corrected logic to comply with #113 * Also converted unit test to test the process.py logic instead of the validity of the params file. * Corrected errors I'd introduced in the conversion to covidcast client * changes in compliance with #205 * Missing comma in setup.py * Linter suggested changes, added HOME_DWELL signal computation * changes in compliance with #205 * default prefix value * Apply suggestions from code review Co-authored-by: krivard <[email protected]> * Removed unnecessary comment * Updated constants.py * Updated README.md * Updated update_sensor.py * updated params.json.template * updated tests/params.json.template * New test case file for handling wip signal * Updated test_update_sensor.py * Update constants.py * Update update_sensor.py * Update test_update_sensor.py * Update test_update_sensor.py * Update update_sensor.py * Update update_sensor.py * Set up initial google_health-deploy branch - Add new google_health Jenkins pipeline stage scripts - Add the abilty for Ansible to write either a file or a template depending on which has been configured for the indicator - Add Ansible template directory (special tall bookshelf) - Add the ability to keep sensitive variables in `vault.yaml` - Add google_health production params template Encrypt vault.yaml - Use templates dir - Configure start_date and end_date * Finalize production config for google_health deployment (#165) * Switch to midas export dir * Properly rename Jenkins pipeline stage scripts * Handle google_health's testing needs - Add: Ansible playbook for securely handling placing the `params.json` template we need for testing. This will happen during the Jenkins build stage when we are setting up the venv in the workspace on the Jenkins server. - Add: Test `params.json` template. - Add: Jenkins user variable. - Fix: Was incorrectly trying to use a file instead of a template in `ansible-deploy.yaml`. - Add: Call the small Ansible playbook from the Jenkins build wrapper. * Change to the Ansible root dir before trying to do Ansible things * Delegate to localhost * Tell Ansible we want to connect locally for this playbook * Remove set -x from Bash scripts * used set instead of list to compare dict.keys() (#186) Co-authored-by: Vishakha Srivastava <[email protected]> * Use brace instead of bracket (#189) * Add path parameter (#190) * Allow `""` for `start_date` to get "latest" data (#194) - We can treat this like we do `end_date` and default to a date to some number of days in the past. The result is that we only process updates for one day, instead of of the whole shebang. - Add new cache files, a temporary necessity. * Set start date to empty string (#203) * Add a cache set that this identical to production (#211) * Sync cache * Remove end date from params * Add AWS credentials to vault Co-authored-by: Chua Eu Jing <[email protected]> Co-authored-by: Vishakha <[email protected]> Co-authored-by: Kathryn M Mazaitis <[email protected]> Co-authored-by: Addison Hu <[email protected]>
1 parent bae28ab commit 5350d2c

File tree

315 files changed

+90335
-121
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

315 files changed

+90335
-121
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
Registry for signal names
3+
"""
4+
5+
6+
## example:
7+
# FULL_TIME = "full_time_work_prop"
8+
# PART_TIME = "part_time_work_prop"
9+
# COVIDNET = "covidnet"
10+
#
11+
# SIGNALS = [
12+
# FULL_TIME,
13+
# PART_TIME,
14+
# COVIDNET
15+
# ]
16+
17+
SIGNALS = []
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""
2+
Handle signal names.
3+
4+
Author: Vishakha
5+
Created: 2020-08-07
6+
"""
7+
8+
import covidcast
9+
10+
11+
def add_prefix(signal_names, wip_signal, prefix="wip_"):
12+
"""Adds prefix to signal if there is a WIP signal
13+
Parameters
14+
----------
15+
signal_names: List[str]
16+
Names of signals to be exported
17+
prefix : 'wip_'
18+
prefix for new/non public signals
19+
wip_signal : List[str] or bool
20+
a list of wip signals: [], OR
21+
all signals in the registry: True OR
22+
only signals that have never been published: False
23+
Returns
24+
-------
25+
List of signal names
26+
wip/non wip signals for further computation
27+
"""
28+
29+
if wip_signal is True:
30+
return [prefix + signal for signal in signal_names]
31+
if isinstance(wip_signal, list):
32+
make_wip = set(wip_signal)
33+
return[
34+
prefix + signal if signal in make_wip else signal
35+
for signal in signal_names
36+
]
37+
if wip_signal in {False, ""}:
38+
return [
39+
signal if public_signal(signal)
40+
else prefix + signal
41+
for signal in signal_names
42+
]
43+
raise ValueError("Supply True | False or '' or [] | list()")
44+
45+
46+
def public_signal(signal_):
47+
"""Checks if the signal name is already public using COVIDcast
48+
Parameters
49+
----------
50+
signal_ : str
51+
Name of the signal
52+
Returns
53+
-------
54+
bool
55+
True if the signal is present
56+
False if the signal is not present
57+
"""
58+
epidata_df = covidcast.metadata()
59+
for index in range(len(epidata_df)):
60+
if epidata_df['signal'][index] == signal_:
61+
return True
62+
return False

_template_python/delphi_NAME/run.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@
44
This module should contain a function called `run_module`, that is executed
55
when the module is run with `python -m MODULE_NAME`.
66
"""
7-
import numpy as np
8-
import pandas as pd
97
from delphi_utils import read_params
10-
8+
from .handle_wip_signal import add_prefix
9+
from .constants import SIGNALS
1110

1211
def run_module():
13-
12+
"""
13+
Calls the method for handling the wip signals
14+
Returns
15+
-------
16+
prints the updated signal names
17+
"""
1418
params = read_params()
19+
wip_signal = params["wip_signal"]
20+
signal_names = add_prefix(SIGNALS, wip_signal, prefix="wip_")
21+
print(signal_names)

_template_python/params.json.template

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"static_file_dir": "./static",
33
"export_dir": "./receiving",
4-
"cache_dir": "./cache"
4+
"cache_dir": "./cache",
5+
"wip_signal": ""
56
}

_template_python/setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"pytest",
88
"pytest-cov",
99
"pylint",
10-
"delphi-utils"
10+
"delphi-utils",
11+
"covidcast"
1112
]
1213

1314
setup(
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"static_file_dir": "../static",
33
"export_dir": "./receiving",
4-
"cache_dir": "./cache"
4+
"cache_dir": "./cache",
5+
"wip_signal": ""
56
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import unittest
2+
from delphi_NAME.handle_wip_signal import add_prefix
3+
from delphi_NAME.run import SIGNALS
4+
from delphi_utils import read_params
5+
6+
7+
8+
def test_handle_wip_signal():
9+
# Test wip_signal = True (all signals should receive prefix)
10+
signal_names = add_prefix(SIGNALS, True, prefix="wip_")
11+
assert all(s.startswith("wip_") for s in signal_names)
12+
# Test wip_signal = list (only listed signals should receive prefix)
13+
signal_names = add_prefix(SIGNALS, [SIGNALS[0]], prefix="wip_")
14+
assert signal_names[0].startswith("wip_")
15+
assert all(not s.startswith("wip_") for s in signal_names[1:])
16+
# Test wip_signal = False (only unpublished signals should receive prefix)
17+
signal_names = add_prefix(["xyzzy", SIGNALS[0]], False, prefix="wip_")
18+
assert signal_names[0].startswith("wip_")
19+
assert all(not s.startswith("wip_") for s in signal_names[1:])
20+
21+
22+
class MyTestCase(unittest.TestCase):
23+
pass
24+
25+
26+
if __name__ == '__main__':
27+
unittest.main()

ansible/google_health-build.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
- hosts: localhost
3+
connection: local
4+
vars_files:
5+
- vars.yaml
6+
- vault.yaml
7+
tasks:
8+
- name: Set test params template.
9+
template:
10+
src: templates/{{ indicator }}-params-test.json.j2
11+
dest: "{{ workspace }}/{{ indicator }}/tests/params.json"
12+
owner: "{{ jenkins_user }}"
13+
group: "{{ jenkins_user }}"
14+
mode: "0644"
15+
delegate_to: localhost
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"static_file_dir": "./static",
3+
"export_dir": "/common/covidcast/receiving/ght",
4+
"cache_dir": "./cache",
5+
"start_date": "",
6+
"end_date": "",
7+
"ght_key": "{{ google_health_api_key }}"
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"static_file_dir": "../static",
3+
"export_dir": "./receiving",
4+
"cache_dir": "../cache",
5+
"start_date": "2020-02-11",
6+
"end_date": "2020-04-30",
7+
"ght_key": "{{ google_health_api_key }}"
8+
}

ansible/vars.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
---
22
runtime_user: "indicators"
3+
jenkins_user: "jenkins"
34
jenkins_artifact_dir: "/var/lib/jenkins/artifacts"
45
indicators_runtime_dir: "/home/{{ runtime_user }}/runtime"
5-
package: "{{ indicator }}.tar.gz" # This is passed in the Ansible invocation.
6+
package: "{{ indicator }}.tar.gz" # {{ indicator }} is passed in from the Jenkins shell script wrapper.
67
python_version: "3.8.2"
78
pyenv_python_path: "/home/{{ runtime_user }}/.pyenv/versions/{{ python_version }}/bin/python"
89

910
# Indicators variables.
11+
google_health_api_key: "{{ vault_google_health_api_key }}"
1012
delphi_aws_access_key_id: "{{ vault_delphi_aws_access_key_id }}"
1113
delphi_aws_secret_access_key: "{{ vault_delphi_aws_secret_access_key }}"

ansible/vault.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
$ANSIBLE_VAULT;1.1;AES256
2+
66386163643862646634343162646465663762643034303563333833633661333932646164656462
3+
6166646131623132393238336263623562373065643633310a663232373237396361623462613333
4+
62373663383565623263306539636431623230633065626363666531366662363065343066363031
5+
3738616663336665340a326138333634306137363837396366303861663064326333613662656630
6+
62306331646637326637363766366237663037306665343761643263646663316535343561623137
7+
63313365653535393639626465343232396261643239303430383138633135346466323834336665
8+
33633064353034613836313265613466623961373565363835343430373138376336363966316365
9+
35663664396436313432376264316663326130306134326231303234393561643436623039613136
10+
63366638396262383762383336643930343661636461646162653734336334306239383132643435
11+
39333665643738643966356431333830646561353263353063326330643731616130396466343339
12+
39346437653063303336626663623835613938633834396430353634383366386237353862643766
13+
37393738353231666565303031393839306463373461393761653866653330646534393832303264
14+
30323038646166366465396235623731343539313633326539663966333437623733626131653437
15+
62326632656462383835656235373664366566343866383938343639613737623631616231616135
16+
633863383761366461363532353137323936

cdc_covidnet/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ should be raised and they should be manually checked (or better, fixed).
5454

5555
Unit tests are also included in the module. To execute these, run the following
5656
command from this directory:
57-
57+
(Note: the following command requires python 3.8, having any version less than 3.8 might
58+
fail some test cases. Please install it before running.)
5859
```
5960
(cd tests && ../env/bin/pytest --cov=delphi_cdc_covidnet --cov-report=term-missing)
6061
```
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
Registry for signal names
3+
"""
4+
COVIDNET = "covidnet"
5+
SIGNALS = [COVIDNET]

cdc_covidnet/delphi_cdc_covidnet/covidnet.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,9 @@ def download_all_hosp_data(
134134
for nid, cid in args.itertuples(index=False, name=None):
135135
outfile = os.path.join(cache_path, f"networkid_{nid}_catchmentid_{cid}.json")
136136
state_files.append(outfile)
137-
args = (nid, cid, age_groups, APIConfig.SEASONS, outfile, APIConfig.HOSP_URL)
138-
state_args.append(args)
137+
if not os.path.exists(outfile):
138+
args = (nid, cid, age_groups, APIConfig.SEASONS, outfile, APIConfig.HOSP_URL)
139+
state_args.append(args)
139140

140141
# Download all state files
141142
if parallel:

cdc_covidnet/delphi_cdc_covidnet/run.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77
import logging
88
from datetime import datetime
9+
from os import remove
910
from os.path import join
1011

1112
from delphi_utils import read_params
@@ -57,4 +58,9 @@ def run_module():
5758
start_date,
5859
end_date)
5960

61+
# Cleanup cache dir
62+
remove(mappings_file)
63+
for state_file in state_files:
64+
remove(state_file)
65+
6066
logging.info("finished all")

cdc_covidnet/delphi_cdc_covidnet/update_sensor.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
import numpy as np
1313
import pandas as pd
1414

15+
from delphi_utils import read_params
16+
import covidcast
1517
from .api_config import APIConfig
1618
from .covidnet import CovidNet
1719
from .geo_maps import GeoMaps
20+
from .constants import SIGNALS
1821

1922
def write_to_csv(data: pd.DataFrame, out_name: str, output_path: str):
2023
"""
@@ -93,7 +96,62 @@ def update_sensor(
9396
hosp_df["sample_size"] = np.nan
9497

9598
# Write results
96-
out_name = "wip_covidnet"
97-
write_to_csv(hosp_df, out_name, output_path)
99+
signals = add_prefix(SIGNALS, wip_signal=read_params()["wip_signal"], prefix="wip_")
100+
for signal in signals:
101+
write_to_csv(hosp_df, signal, output_path)
98102

99103
return hosp_df
104+
105+
106+
def add_prefix(signal_names, wip_signal, prefix):
107+
"""Adds prefix to signal if there is a WIP signal
108+
Parameters
109+
----------
110+
signal_names: List[str]
111+
Names of signals to be exported
112+
prefix : 'wip_'
113+
prefix for new/non public signals
114+
wip_signal : List[str] or bool
115+
a list of wip signals: [], OR
116+
all signals in the registry: True OR
117+
only signals that have never been published: False
118+
Returns
119+
-------
120+
List of signal names
121+
wip/non wip signals for further computation
122+
"""
123+
124+
if wip_signal is True:
125+
return [prefix + signal for signal in signal_names]
126+
if isinstance(wip_signal, list):
127+
make_wip = set(wip_signal)
128+
return [
129+
(prefix if signal in make_wip else "") + signal
130+
for signal in signal_names
131+
]
132+
if wip_signal in {False, ""}:
133+
return [
134+
signal if public_signal(signal)
135+
else prefix + signal
136+
for signal in signal_names
137+
]
138+
raise ValueError("Supply True | False or '' or [] | list()")
139+
140+
141+
def public_signal(signal_):
142+
"""Checks if the signal name is already public using COVIDcast
143+
Parameters
144+
----------
145+
signal_ : str
146+
Name of the signal
147+
Returns
148+
-------
149+
bool
150+
True if the signal is present
151+
False if the signal is not present
152+
"""
153+
epidata_df = covidcast.metadata()
154+
for index in range(len(epidata_df)):
155+
if epidata_df['signal'][index] == signal_:
156+
return True
157+
return False

cdc_covidnet/params.json.template

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"cache_dir": "./cache",
55
"start_date": "2020-03-07",
66
"end_date": "",
7-
"parallel": false
7+
"parallel": false,
8+
"wip_signal": ""
89
}

cdc_covidnet/setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"pytest-cov",
99
"pylint",
1010
"delphi-utils",
11-
"requests"
11+
"requests",
12+
"covidcast"
1213
]
1314

1415
setup(
@@ -22,7 +23,7 @@
2223
classifiers=[
2324
"Development Status :: 5 - Production/Stable",
2425
"Intended Audience :: Developers",
25-
"Programming Language :: Python :: 3.7",
26+
"Programming Language :: Python :: 3.8",
2627
],
2728
packages=find_packages(),
2829
)

cdc_covidnet/tests/cache_test/init.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"datadownload": [{"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "10", "age_category": "Overall", "cumulative-rate": 0.2, "weekly-rate": 0.2}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "11", "age_category": "Overall", "cumulative-rate": 1, "weekly-rate": 0.8}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "12", "age_category": "Overall", "cumulative-rate": 3.2, "weekly-rate": 2.2}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "13", "age_category": "Overall", "cumulative-rate": 6.8, "weekly-rate": 3.6}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "14", "age_category": "Overall", "cumulative-rate": 10.8, "weekly-rate": 3.9}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "15", "age_category": "Overall", "cumulative-rate": 14.7, "weekly-rate": 3.9}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "16", "age_category": "Overall", "cumulative-rate": 17.8, "weekly-rate": 3.1}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "17", "age_category": "Overall", "cumulative-rate": 20, "weekly-rate": 2.2}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "18", "age_category": "Overall", "cumulative-rate": 22.5, "weekly-rate": 2.5}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "19", "age_category": "Overall", "cumulative-rate": 25, "weekly-rate": 2.5}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "20", "age_category": "Overall", "cumulative-rate": 27, "weekly-rate": 2}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "21", "age_category": "Overall", "cumulative-rate": 29.1, "weekly-rate": 2.1}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "22", "age_category": "Overall", "cumulative-rate": 31.4, "weekly-rate": 2.4}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "23", "age_category": "Overall", "cumulative-rate": 34.2, "weekly-rate": 2.8}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "24", "age_category": "Overall", "cumulative-rate": 36.5, "weekly-rate": 2.2}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "25", "age_category": "Overall", "cumulative-rate": 39.1, "weekly-rate": 2.6}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "26", "age_category": "Overall", "cumulative-rate": 42.3, "weekly-rate": 3.2}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "27", "age_category": "Overall", "cumulative-rate": 45.9, "weekly-rate": 3.7}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "28", "age_category": "Overall", "cumulative-rate": 50, "weekly-rate": 4.1}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "29", "age_category": "Overall", "cumulative-rate": 52, "weekly-rate": 2}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "30", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "31", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "32", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "33", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "34", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "35", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "36", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "37", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "38", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}, {"catchment": "California", "network": "EIP", "year": "2020", "mmwr-year": "2020", "mmwr-week": "39", "age_category": "Overall", "cumulative-rate": null, "weekly-rate": null}]}

0 commit comments

Comments
 (0)