diff --git a/quidel_covidtest/.pylintrc b/quidel_covidtest/.pylintrc index 58c6edbba..854cf38d2 100644 --- a/quidel_covidtest/.pylintrc +++ b/quidel_covidtest/.pylintrc @@ -4,6 +4,7 @@ disable=logging-format-interpolation, too-many-locals, too-many-arguments, + too-many-branches, # Allow pytest functions to be part of a class. no-self-use, # Allow pytest classes to have one test. diff --git a/quidel_covidtest/delphi_quidel_covidtest/constants.py b/quidel_covidtest/delphi_quidel_covidtest/constants.py index abe88c7b1..8e4d37cb2 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/constants.py +++ b/quidel_covidtest/delphi_quidel_covidtest/constants.py @@ -3,7 +3,7 @@ MIN_OBS = 50 # minimum number of observations in order to compute a proportion. POOL_DAYS = 7 # number of days in the past (including today) to pool over END_FROM_TODAY_MINUS = 5 # report data until - X days -# Signal names +# Signal Types SMOOTHED_POSITIVE = "covid_ag_smoothed_pct_positive" RAW_POSITIVE = "covid_ag_raw_pct_positive" SMOOTHED_TEST_PER_DEVICE = "covid_ag_smoothed_test_per_device" @@ -22,6 +22,7 @@ HRR, ] +# state should be last one NONPARENT_GEO_RESOLUTIONS = [ HHS, NATION, @@ -39,3 +40,12 @@ # SMOOTHED_TEST_PER_DEVICE: (True, True), # RAW_TEST_PER_DEVICE: (True, False) } +AGE_GROUPS = [ + "total", + "age_0_4", + "age_5_17", + "age_18_49", + "age_50_64", + "age_65plus", + "age_0_17", +] diff --git a/quidel_covidtest/delphi_quidel_covidtest/data_tools.py b/quidel_covidtest/delphi_quidel_covidtest/data_tools.py index f89a353ed..e8958ecfd 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/data_tools.py +++ b/quidel_covidtest/delphi_quidel_covidtest/data_tools.py @@ -67,15 +67,14 @@ def _slide_window_sum(arr, k): sarr = np.convolve(temp, np.ones(k, dtype=int), 'valid') return sarr - def _geographical_pooling(tpooled_tests, tpooled_ptests, min_obs): """ Determine how many samples from the parent geography must be borrowed. - If there are no samples available in the parent, the borrow_prop is 0. If - the parent does not have enough samples, we return a borrow_prop of 1, and - the fact that the pooled samples are insufficient are handled in the - statistic fitting step. + If there are no samples available in the parent, the borrow_prop is 0. + If the parent does not have enough samples, we return a borrow_prop of 1. + No more samples borrowed from the parent compared to the number of samples + we currently have. Args: tpooled_tests: np.ndarray[float] @@ -93,10 +92,12 @@ def _geographical_pooling(tpooled_tests, tpooled_ptests, min_obs): """ if (np.any(np.isnan(tpooled_tests)) or np.any(np.isnan(tpooled_ptests))): raise ValueError('[parent] tests should be non-negative ' - 'with no np.nan') + 'with no np.nan') # STEP 1: "TOP UP" USING PARENT LOCATION # Number of observations we need to borrow to "top up" + # Can't borrow more than total no. observations. borrow_tests = np.maximum(min_obs - tpooled_tests, 0) + borrow_tests = np.minimum(borrow_tests, tpooled_tests) # There are many cases (a, b > 0): # Case 1: a / b => no problem # Case 2: a / 0 => np.inf => borrow_prop becomes 1 @@ -108,13 +109,14 @@ def _geographical_pooling(tpooled_tests, tpooled_ptests, min_obs): with np.errstate(divide='ignore', invalid='ignore'): borrow_prop = borrow_tests / tpooled_ptests # If there's nothing to borrow, then ya can't borrow - borrow_prop[np.isnan(borrow_prop)] = 0 - # Can't borrow more than total no. observations. + borrow_prop[(np.isnan(borrow_prop)) + | (tpooled_tests == 0) + | (tpooled_ptests == 0)] = 0 + # Can't borrow more than total no. observations in the parent state # Relies on the fact that np.inf > 1 borrow_prop[borrow_prop > 1] = 1 return borrow_prop - def raw_positive_prop(positives, tests, min_obs): """ Calculate the proportion of positive tests without any temporal smoothing. diff --git a/quidel_covidtest/delphi_quidel_covidtest/generate_sensor.py b/quidel_covidtest/delphi_quidel_covidtest/generate_sensor.py index ba6b443c6..5f44a519b 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/generate_sensor.py +++ b/quidel_covidtest/delphi_quidel_covidtest/generate_sensor.py @@ -6,11 +6,12 @@ smoothed_tests_per_device, raw_tests_per_device, remove_null_samples) +from .geo_maps import add_megacounties -MIN_OBS = 50 # minimum number of observations in order to compute a proportion. -POOL_DAYS = 7 +from .constants import (MIN_OBS, POOL_DAYS) -def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, first_date, last_date): +def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, + first_date, last_date, suffix): """ Fit over geo resolutions that don't use a parent state (nation/hhs/state). @@ -21,6 +22,8 @@ def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, fir Consider raw or smooth device: bool Consider test_per_device or pct_positive + suffix: str + Indicate the age group Returns: df: pd.DataFrame """ @@ -35,27 +38,27 @@ def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, fir # smoothed test per device if device & smooth: stat, se, sample_size = smoothed_tests_per_device( - devices=state_group["numUniqueDevices"].values, - tests=state_group['totalTest'].values, + devices=state_group[f"numUniqueDevices_{suffix}"].values, + tests=state_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS) # raw test per device elif device & (not smooth): stat, se, sample_size = raw_tests_per_device( - devices=state_group["numUniqueDevices"].values, - tests=state_group['totalTest'].values, + devices=state_group[f"numUniqueDevices_{suffix}"].values, + tests=state_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS) # smoothed pct positive elif (not device) & smooth: stat, se, sample_size = smoothed_positive_prop( - tests=state_group['totalTest'].values, - positives=state_group['positiveTest'].values, + tests=state_group[f'totalTest_{suffix}'].values, + positives=state_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS) stat = stat * 100 # raw pct positive else: stat, se, sample_size = raw_positive_prop( - tests=state_group['totalTest'].values, - positives=state_group['positiveTest'].values, + tests=state_group[f'totalTest_{suffix}'].values, + positives=state_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS) stat = stat * 100 @@ -68,7 +71,7 @@ def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, fir return remove_null_samples(state_df) def generate_sensor_for_parent_geo(state_groups, data, res_key, smooth, - device, first_date, last_date): + device, first_date, last_date, suffix): """ Fit over geo resolutions that use a parent state (county/hrr/msa). @@ -79,15 +82,16 @@ def generate_sensor_for_parent_geo(state_groups, data, res_key, smooth, Consider raw or smooth device: bool Consider test_per_device or pct_positive + suffix: str + Indicate the age group Returns: df: pd.DataFrame """ has_parent = True res_df = pd.DataFrame(columns=["geo_id", "val", "se", "sample_size"]) - res_groups = data.groupby(res_key) - loc_list = list(res_groups.groups.keys()) - for loc in loc_list: - res_group = res_groups.get_group(loc) + if res_key == "fips": # Add rest-of-state report for county level + data = add_megacounties(data, smooth) + for loc, res_group in data.groupby(res_key): parent_state = res_group['state_id'].values[0] try: parent_group = state_groups.get_group(parent_state) @@ -104,41 +108,41 @@ def generate_sensor_for_parent_geo(state_groups, data, res_key, smooth, if has_parent: if device: stat, se, sample_size = smoothed_tests_per_device( - devices=res_group["numUniqueDevices"].values, - tests=res_group['totalTest'].values, + devices=res_group[f"numUniqueDevices_{suffix}"].values, + tests=res_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS, - parent_devices=res_group["numUniqueDevices_parent"].values, - parent_tests=res_group["totalTest_parent"].values) + parent_devices=res_group[f"numUniqueDevices_{suffix}_parent"].values, + parent_tests=res_group[f"totalTest_{suffix}_parent"].values) else: stat, se, sample_size = smoothed_positive_prop( - tests=res_group['totalTest'].values, - positives=res_group['positiveTest'].values, + tests=res_group[f'totalTest_{suffix}'].values, + positives=res_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS, - parent_tests=res_group["totalTest_parent"].values, - parent_positives=res_group['positiveTest_parent'].values) + parent_tests=res_group[f"totalTest_{suffix}_parent"].values, + parent_positives=res_group[f'positiveTest_{suffix}_parent'].values) stat = stat * 100 else: if device: stat, se, sample_size = smoothed_tests_per_device( - devices=res_group["numUniqueDevices"].values, - tests=res_group['totalTest'].values, + devices=res_group[f"numUniqueDevices_{suffix}"].values, + tests=res_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS) else: stat, se, sample_size = smoothed_positive_prop( - tests=res_group['totalTest'].values, - positives=res_group['positiveTest'].values, + tests=res_group[f'totalTest_{suffix}'].values, + positives=res_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS) stat = stat * 100 else: if device: stat, se, sample_size = raw_tests_per_device( - devices=res_group["numUniqueDevices"].values, - tests=res_group['totalTest'].values, + devices=res_group[f"numUniqueDevices_{suffix}"].values, + tests=res_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS) else: stat, se, sample_size = raw_positive_prop( - tests=res_group['totalTest'].values, - positives=res_group['positiveTest'].values, + tests=res_group[f'totalTest_{suffix}'].values, + positives=res_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS) stat = stat * 100 diff --git a/quidel_covidtest/delphi_quidel_covidtest/geo_maps.py b/quidel_covidtest/delphi_quidel_covidtest/geo_maps.py index 7ebdcc834..9cefc0f9e 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/geo_maps.py +++ b/quidel_covidtest/delphi_quidel_covidtest/geo_maps.py @@ -1,7 +1,13 @@ """Contains geographic mapping tools.""" +from itertools import product +from functools import reduce + +import pandas as pd + from delphi_utils import GeoMapper +from .constants import (AGE_GROUPS, MIN_OBS) -DATA_COLS = ['totalTest', 'numUniqueDevices', 'positiveTest', "population"] +DATA_COLS = ['totalTest', 'numUniqueDevices', 'positiveTest'] GMPR = GeoMapper() # Use geo utils GEO_KEY_DICT = { "county": "fips", @@ -12,7 +18,6 @@ "hhs": "hhs" } - def geo_map(geo_res, df): """Map a geocode to a new value.""" data = df.copy() @@ -20,13 +25,45 @@ def geo_map(geo_res, df): # Add population for each zipcode data = GMPR.add_population_column(data, "zip") # zip -> geo_res - data = GMPR.replace_geocode(data, "zip", geo_key, data_cols=DATA_COLS) + data_cols = ["population"] + for col, agegroup in product(DATA_COLS, AGE_GROUPS): + data_cols.append("_".join([col, agegroup])) + + data = GMPR.replace_geocode( + data, from_code="zip", new_code=geo_key, date_col = "timestamp", + data_cols=data_cols) if geo_res in ["state", "hhs", "nation"]: return data, geo_key # Add parent state data = add_parent_state(data, geo_res, geo_key) return data, geo_key +def add_megacounties(data, smooth=False): + """Add megacounties to county level report.""" + assert "fips" in data.columns # Make sure the data is at county level + + # For raw signals, the threshold is MIN_OBS + # For smoothed signals, the threshold is MIN_OBS/2 + if smooth: + threshold_visits = MIN_OBS/2 + else: + threshold_visits = MIN_OBS + pdList = [] + for agegroup in AGE_GROUPS: + data_cols = [f"{col}_{agegroup}" for col in DATA_COLS] + df = GMPR.fips_to_megacounty(data[data_cols + ["timestamp", "fips"]], + threshold_visits, 1, fips_col="fips", + thr_col=f"totalTest_{agegroup}", + date_col="timestamp") + df.rename({"megafips": "fips"}, axis=1, inplace=True) + megacounties = df[df.fips.str.endswith("000")] + pdList.append(megacounties) + mega_df = reduce(lambda x, y: pd.merge( + x, y, on = ["timestamp", "fips"]), pdList) + mega_df = GMPR.add_geocode(mega_df, from_code="fips", new_code="state_id", + from_col="fips", new_col="state_id") + + return pd.concat([data, mega_df]) def add_parent_state(data, geo_res, geo_key): """ diff --git a/quidel_covidtest/delphi_quidel_covidtest/pull.py b/quidel_covidtest/delphi_quidel_covidtest/pull.py index 3efa9ed23..ab397ce47 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/pull.py +++ b/quidel_covidtest/delphi_quidel_covidtest/pull.py @@ -8,6 +8,8 @@ import pandas as pd import numpy as np +from .constants import AGE_GROUPS + def get_from_s3(start_date, end_date, bucket, logger): """ Get raw data from aws s3 bucket. @@ -163,21 +165,21 @@ def preprocess_new_data(start_date, end_date, params, test_mode, logger): overall_pos = df[df["OverallResult"] == "positive"].groupby( by=["timestamp", "zip"], as_index=False)['OverallResult'].count() - overall_pos["positiveTest"] = overall_pos["OverallResult"] + overall_pos["positiveTest_total"] = overall_pos["OverallResult"] overall_pos.drop(labels="OverallResult", axis="columns", inplace=True) # Compute overallTotal overall_total = df.groupby( by=["timestamp", "zip"], as_index=False)['OverallResult'].count() - overall_total["totalTest"] = overall_total["OverallResult"] + overall_total["totalTest_total"] = overall_total["OverallResult"] overall_total.drop(labels="OverallResult", axis="columns", inplace=True) # Compute numUniqueDevices numUniqueDevices = df.groupby( by=["timestamp", "zip"], as_index=False)["SofiaSerNum"].agg({"SofiaSerNum": "nunique"}).rename( - columns={"SofiaSerNum": "numUniqueDevices"} + columns={"SofiaSerNum": "numUniqueDevices_total"} ) df_merged = overall_total.merge( @@ -186,6 +188,55 @@ def preprocess_new_data(start_date, end_date, params, test_mode, logger): overall_pos, on=["timestamp", "zip"], how="left" ).fillna(0).drop_duplicates() + # Compute Summary info for age groups + df["PatientAge"] = df["PatientAge"].fillna(-1) + df.loc[df["PatientAge"] == "<1", "PatientAge"] = 0.5 + df.loc[df["PatientAge"] == ">85", "PatientAge"] = 100 + df["PatientAge"] = df["PatientAge"] .astype(float) + + # Should match the suffixes of signal names + df["label"] = None + df.loc[df["PatientAge"] < 5, "label"] = "age_0_4" + df.loc[((df["PatientAge"] >= 5)) & (df["PatientAge"] < 18), "label"] = "age_5_17" + df.loc[((df["PatientAge"] >= 18)) & (df["PatientAge"] < 50), "label"] = "age_18_49" + df.loc[((df["PatientAge"] >= 50)) & (df["PatientAge"] < 65), "label"] = "age_50_64" + df.loc[(df["PatientAge"] >= 65), "label"] = "age_65plus" + df.loc[df["PatientAge"] == -1, "label"] = "NA" + + for agegroup in AGE_GROUPS[1:]: # Exclude total + if agegroup == "age_0_17": + ages = ["age_0_4", "age_5_17"] + else: + ages = [agegroup] + # Compute overallPositive + group_pos = df.loc[(df["OverallResult"] == "positive") + & (df["label"].isin(ages))].groupby( + by=["timestamp", "zip"], + as_index=False)['OverallResult'].count() + group_pos[f"positiveTest_{agegroup}"] = group_pos["OverallResult"] + group_pos.drop(labels="OverallResult", axis="columns", inplace=True) + + # Compute overallTotal + group_total = df.loc[df["label"].isin(ages)].groupby( + by=["timestamp", "zip"], + as_index=False)['OverallResult'].count() + group_total[f"totalTest_{agegroup}"] = group_total["OverallResult"] + group_total.drop(labels="OverallResult", axis="columns", inplace=True) + + # Compute numUniqueDevices + group_numUniqueDevices = df.loc[df["label"].isin(ages)].groupby( + by=["timestamp", "zip"], + as_index=False)["SofiaSerNum"].agg({"SofiaSerNum": "nunique"}).rename( + columns={"SofiaSerNum": f"numUniqueDevices_{agegroup}"} + ) + + df_merged = df_merged.merge( + group_numUniqueDevices, on=["timestamp", "zip"], how="left" + ).merge( + group_pos, on=["timestamp", "zip"], how="left" + ).merge( + group_total, on=["timestamp", "zip"], how="left" + ).fillna(0).drop_duplicates() return df_merged, time_flag @@ -295,7 +346,7 @@ def check_export_start_date(export_start_date, export_end_date, export_start_date = datetime(2020, 5, 26) else: export_start_date = datetime.strptime(export_start_date, '%Y-%m-%d') - # Only export data from -45 days to -5 days + # Only export data from -50 days to -5 days if (export_end_date - export_start_date).days > export_day_range: export_start_date = export_end_date - timedelta(days=export_day_range) diff --git a/quidel_covidtest/delphi_quidel_covidtest/run.py b/quidel_covidtest/delphi_quidel_covidtest/run.py index 5f084440c..fb1a69ac2 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/run.py +++ b/quidel_covidtest/delphi_quidel_covidtest/run.py @@ -18,7 +18,8 @@ from .constants import (END_FROM_TODAY_MINUS, SMOOTHED_POSITIVE, RAW_POSITIVE, SMOOTHED_TEST_PER_DEVICE, RAW_TEST_PER_DEVICE, - PARENT_GEO_RESOLUTIONS, SENSORS, SMOOTHERS, NONPARENT_GEO_RESOLUTIONS) + PARENT_GEO_RESOLUTIONS, SENSORS, SMOOTHERS, NONPARENT_GEO_RESOLUTIONS, + AGE_GROUPS) from .generate_sensor import generate_sensor_for_parent_geo, generate_sensor_for_nonparent_geo from .geo_maps import geo_map from .pull import (pull_quidel_covidtest, @@ -40,6 +41,20 @@ def log_exit(start_time, stats, logger): max_lag_in_days = max_lag_in_days, oldest_final_export_date = formatted_min_max_date) +def get_smooth_info(sensors, _SMOOTHERS): + """Get smooth info from SMOOTHERS.""" + smoothers = _SMOOTHERS.copy() + for sensor in sensors: + if sensor.endswith(SMOOTHED_POSITIVE): + smoothers[sensor] = smoothers.pop(SMOOTHED_POSITIVE) + elif sensor.endswith(RAW_POSITIVE): + smoothers[sensor] = smoothers.pop(RAW_POSITIVE) + elif sensor.endswith(SMOOTHED_TEST_PER_DEVICE): + smoothers[sensor] = smoothers.pop(SMOOTHED_TEST_PER_DEVICE) + else: + smoothers[sensor] = smoothers.pop(RAW_TEST_PER_DEVICE) + return smoothers + def run_module(params: Dict[str, Any]): """Run the quidel_covidtest indicator. @@ -80,65 +95,68 @@ def run_module(params: Dict[str, Any]): if _end_date is None: logger.info("The data is up-to-date. Currently, no new data to be ingested.") return - export_end_date = check_export_end_date(export_end_date, _end_date, - END_FROM_TODAY_MINUS) - export_start_date = check_export_start_date(export_start_date, - export_end_date, export_day_range) + export_end_date = check_export_end_date( + export_end_date, _end_date, END_FROM_TODAY_MINUS) + export_start_date = check_export_start_date( + export_start_date, export_end_date, export_day_range) first_date, last_date = df["timestamp"].min(), df["timestamp"].max() - # State Level data = df.copy() # Add prefix, if required sensors = add_prefix(SENSORS, wip_signal=params["indicator"]["wip_signal"], prefix="wip_") - smoothers = SMOOTHERS.copy() + smoothers = get_smooth_info(sensors, SMOOTHERS) for geo_res in NONPARENT_GEO_RESOLUTIONS: geo_data, res_key = geo_map(geo_res, data) geo_groups = geo_data.groupby(res_key) - for sensor in sensors: - logger.info("Generating signal and exporting to CSV", - geo_res=geo_res, - sensor=sensor) - if sensor.endswith(SMOOTHED_POSITIVE): - smoothers[sensor] = smoothers.pop(SMOOTHED_POSITIVE) - elif sensor.endswith(RAW_POSITIVE): - smoothers[sensor] = smoothers.pop(RAW_POSITIVE) - elif sensor.endswith(SMOOTHED_TEST_PER_DEVICE): - smoothers[sensor] = smoothers.pop(SMOOTHED_TEST_PER_DEVICE) - else: - smoothers[sensor] = smoothers.pop(RAW_TEST_PER_DEVICE) - state_df = generate_sensor_for_nonparent_geo( - geo_groups, res_key, smooth=smoothers[sensor][1], - device=smoothers[sensor][0], first_date=first_date, - last_date=last_date) - dates = create_export_csv( - state_df, - geo_res=geo_res, - sensor=sensor, - export_dir=export_dir, - start_date=export_start_date, - end_date=export_end_date) - if len(dates) > 0: - stats.append((max(dates), len(dates))) - + for agegroup in AGE_GROUPS: + for sensor in sensors: + if agegroup == "total": + sensor_name = sensor + else: + sensor_name = "_".join([sensor, agegroup]) + logger.info("Generating signal and exporting to CSV", + geo_res=geo_res, + sensor=sensor_name) + state_df = generate_sensor_for_nonparent_geo( + geo_groups, res_key, smooth=smoothers[sensor][1], + device=smoothers[sensor][0], first_date=first_date, + last_date=last_date, suffix=agegroup) + dates = create_export_csv( + state_df, + geo_res=geo_res, + sensor=sensor_name, + export_dir=export_dir, + start_date=export_start_date, + end_date=export_end_date) + if len(dates) > 0: + stats.append((max(dates), len(dates))) + assert geo_res == "state" # Make sure geo_groups is for state level # County/HRR/MSA level for geo_res in PARENT_GEO_RESOLUTIONS: geo_data, res_key = geo_map(geo_res, data) - for sensor in sensors: - logger.info("Generating signal and exporting to CSV", - geo_res=geo_res, - sensor=sensor) - res_df = generate_sensor_for_parent_geo( - geo_groups, geo_data, res_key, smooth=smoothers[sensor][1], - device=smoothers[sensor][0], first_date=first_date, - last_date=last_date) - dates = create_export_csv(res_df, geo_res=geo_res, sensor=sensor, export_dir=export_dir, - start_date=export_start_date, end_date=export_end_date, - remove_null_samples=True) - if len(dates) > 0: - stats.append((max(dates), len(dates))) + for agegroup in AGE_GROUPS: + for sensor in sensors: + if agegroup == "total": + sensor_name = sensor + else: + sensor_name = "_".join([sensor, agegroup]) + logger.info("Generating signal and exporting to CSV", + geo_res=geo_res, + sensor=sensor_name) + res_df = generate_sensor_for_parent_geo( + geo_groups, geo_data, res_key, smooth=smoothers[sensor][1], + device=smoothers[sensor][0], first_date=first_date, + last_date=last_date, suffix=agegroup) + dates = create_export_csv(res_df, geo_res=geo_res, + sensor=sensor_name, export_dir=export_dir, + start_date=export_start_date, + end_date=export_end_date, + remove_null_samples=True) + if len(dates) > 0: + stats.append((max(dates), len(dates))) # Export the cache file if the pipeline runs successfully. # Otherwise, don't update the cache file diff --git a/quidel_covidtest/params.json.template b/quidel_covidtest/params.json.template index a2996b032..e96d2ccbc 100644 --- a/quidel_covidtest/params.json.template +++ b/quidel_covidtest/params.json.template @@ -20,6 +20,15 @@ "wip_signal": [""], "test_mode": false }, + "archive": { + "aws_credentials": { + "aws_access_key_id": "{{ delphi_aws_access_key_id }}", + "aws_secret_access_key": "{{ delphi_aws_secret_access_key }}" + }, + "bucket_name": "delphi-covidcast-indicator-output", + "cache_dir": "./archivediffer_cache", + "indicator_prefix": "quidel" + }, "validation": { "common": { "data_source": "quidel", diff --git a/quidel_covidtest/tests/test_data/msa_data.csv b/quidel_covidtest/tests/test_data/msa_data.csv index 39593b937..8ff61dd69 100644 --- a/quidel_covidtest/tests/test_data/msa_data.csv +++ b/quidel_covidtest/tests/test_data/msa_data.csv @@ -1,4 +1,4 @@ -timestamp,cbsa_id,state_id,totalTest,numUniqueDevices,positiveTest +timestamp,cbsa_id,state_id,totalTest_total,numUniqueDevices_total,positiveTest_total 2020-06-14,10580,ny,1,1,0.0 2020-06-14,11100,tx,5,2,0.0 2020-06-14,12020,ga,8,1,0.0 diff --git a/quidel_covidtest/tests/test_data/state_data.csv b/quidel_covidtest/tests/test_data/state_data.csv index 6326c60d3..38e22141c 100644 --- a/quidel_covidtest/tests/test_data/state_data.csv +++ b/quidel_covidtest/tests/test_data/state_data.csv @@ -1,4 +1,4 @@ -timestamp,state_id,totalTest,numUniqueDevices,positiveTest +timestamp,state_id,totalTest_total,numUniqueDevices_total,positiveTest_total 2020-06-14,al,6,2,1.0 2020-06-14,ar,4,1,1.0 2020-06-14,ca,53,6,2.0 diff --git a/quidel_covidtest/tests/test_data/test_data.csv b/quidel_covidtest/tests/test_data/test_data.csv index 8b9d18c4f..e93a109d4 100644 --- a/quidel_covidtest/tests/test_data/test_data.csv +++ b/quidel_covidtest/tests/test_data/test_data.csv @@ -1,425 +1,538 @@ SofiaSerNum,TestDate,Facility,City,State,Zip,PatientAge,Result1,Result2,OverallResult,County,FacilityType,Assay,SCO1,SCO2,CLN,CSN,InstrType,StorageDate,ResultId,SarsTestNumber -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,invalid,,invalid,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,invalid,,invalid,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -4,7/20/20,,McLean,VA,22101,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/21/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/21/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -7,7/22/20,,Columbia,SC,29229,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, \ No newline at end of file +2,7/18/20,,Lorton,VA,22079,39,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,9,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,13,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,77,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,35,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,7,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,76,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,28,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,34,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,13,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,52,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,34,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,61,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,31,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,67,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,13,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,75,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,58,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,29,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,60,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,22,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,15,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,41,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,43,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,81,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,75,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,40,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,82,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,59,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,45,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,25,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,59,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,46,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,84,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,33,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,24,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,17,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,78,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,4,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,43,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,55,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,9,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,22,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,46,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,66,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,36,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,46,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,51,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,5,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,22,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24534,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24534,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24534,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24534,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,39,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,51,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,73,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,28,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,2,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,35,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,12,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,7,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,37,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,80,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,8,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,43,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,3,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,57,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,38,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,29,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,1,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,2,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,22,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,40,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,59,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,25,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,74,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,41,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,71,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,29,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,54,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,39,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,15,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,48,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,77,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,12,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,18,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,59,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,38,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,1,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,73,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,52,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,3,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,30,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,40,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,77,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,56,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,75,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,73,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,2,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,9,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,44,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,54,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,14,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,78,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,80,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,56,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,31,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,76,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,84,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,69,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,79,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,42,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,22,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,72,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,54,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,59,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,18,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,34,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,25,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,17,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,16,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,39,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,17,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,20,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,79,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,83,invalid,,invalid,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,27,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,33,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,55,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,39,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,14,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,57,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,5,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,45,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,44,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,83,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,38,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,39,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,58,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,9,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,48,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,32,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,77,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,26,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,50,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,64,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,40,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,8,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,55,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,11,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,7,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,53,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,38,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,19,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,82,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,22,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,50,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,45,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,52,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,12,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,71,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,39,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,49,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,75,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,79,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,84,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,72,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,10,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,54,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,31,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,67,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,49,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,1,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,78,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,56,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,5,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,73,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,36,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,66,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,38,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,11,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,42,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,24,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,10,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,74,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,59,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,11,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,77,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,51,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,72,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,62,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,31,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,19,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,8,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,30,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,30,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,62,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,82,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,5,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,20,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,8,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,63,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,80,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,66,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,23,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,61,positive,,positive,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,46,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,37,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,57,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,<1,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,54,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,62,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,71,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,16,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,51,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,63,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,66,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,24,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,9,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,2,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,44,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,72,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,31,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,59,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,<1,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,25,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,69,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,23,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,44,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,51,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,37,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,1,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,80,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,49,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,45,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,16,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,16,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,80,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,18,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,65,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,76,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,76,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,77,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,29,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,26,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,36,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,17,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,80,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,61,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,16,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,79,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,1,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,42,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,60,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,62,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,52,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,2,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,53,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,41,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,66,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,72,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,77,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,38,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,77,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,46,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,42,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,42,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,12,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,75,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,82,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,9,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,58,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,80,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,58,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,84,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,67,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,19,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,54,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,68,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,1,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,48,negative,,negative,,,,,,,,,8/17/20,, +4,7/20/20,,McLean,VA,22101,73,negative,,negative,,,,,,,,,8/17/20,, +5,7/21/20,,Lorton,VA,22079,2,negative,,negative,,,,,,,,,8/17/20,, +5,7/21/20,,Lorton,VA,22079,29,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,69,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,76,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,58,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,66,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,19,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,70,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,50,negative,,negative,,,,,,,,,8/17/20,, +7,7/22/20,,Columbia,SC,29229,>85,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,20,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,68,positive,,positive,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,46,positive,,positive,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,19,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,26,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,56,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,53,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,14,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,38,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,72,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,5,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,17,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,35,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,47,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,62,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,74,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,4,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,60,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,81,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,7,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,66,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,55,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,82,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,29,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,25,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,83,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,5,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,77,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,58,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,6,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,36,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,24,negative,,negative,,,,,,,,,8/17/20,, \ No newline at end of file diff --git a/quidel_covidtest/tests/test_data_tools.py b/quidel_covidtest/tests/test_data_tools.py index cb611b8f5..7125c8eef 100644 --- a/quidel_covidtest/tests/test_data_tools.py +++ b/quidel_covidtest/tests/test_data_tools.py @@ -76,8 +76,8 @@ def test__slide_window_sum(self, k, expected): @pytest.mark.parametrize("min_obs, expected", [ (1, np.array([0, 0, 0, 0])), - (2, np.array([1/2, 0, 0, 0])), - (8, np.array([1, 1, 5/6, 4/8])), + (2, np.array([1/2, 0, 0, 0])), #(2, np.array([1/2, 0, 0, 0])), + (8, np.array([1/2, 2/4, 3/6, 4/8])), #(8, np.array([1, 1, 5/6, 4/8])), ]) def test__geographical_pooling(self, min_obs, expected): tpooled_tests = np.array([1, 2, 3, 4]) @@ -135,10 +135,19 @@ def test_raw_positive_prop(self, min_obs, expected_pos_prop, expected_se, expect np.array([3, 7, 9, 11]), np.array([5, 10, 15, 20]), np.array([(1 + 0.6 + 0.5)/(2 + 1 + 1), 3.5/7, 5.5/11, 7.5/17]), - np.array([np.sqrt(2.1*(4-2.1)/4/4/3), np.sqrt(3.5*(7-3.5)/7/7/6), - np.sqrt(5.5*(11-5.5)/11/11/10), np.sqrt(7.5*(17-7.5)/17/17/16)]), + np.array([np.sqrt(2.1*(4-2.1)/4/4/3), np.sqrt(3.5*(7-3.5)/7/7/6), + np.sqrt(5.5*(11-5.5)/11/11/10), np.sqrt(7.5*(17-7.5)/17/17/16)]), np.array([3, 6, 10, 16]), ), + (5, # parents case, borrow too much + 2, + np.array([3, 7, 9, 11]), + np.array([5, 10, 15, 20]), + np.array([np.nan, 3.5/7, 5.5/11, 7.5/17]), + np.array([np.nan, np.sqrt(3.5*(7-3.5)/7/7/6), + np.sqrt(5.5*(11-5.5)/11/11/10), np.sqrt(7.5*(17-7.5)/17/17/16)]), + np.array([np.nan, 6, 10, 16]), + ), ]) def test_smoothed_positive_prop(self, min_obs, pool_days, parent_positives, parent_tests, expected_prop, expected_se, expected_sample_sz): diff --git a/quidel_covidtest/tests/test_generate_sensor.py b/quidel_covidtest/tests/test_generate_sensor.py index 0b34919c6..0ef82de83 100644 --- a/quidel_covidtest/tests/test_generate_sensor.py +++ b/quidel_covidtest/tests/test_generate_sensor.py @@ -21,7 +21,9 @@ def test_generate_sensor(self): # raw pct_positive state_pct_positive = generate_sensor_for_nonparent_geo( state_groups, "state_id", smooth = False, device = False, - first_date = datetime(2020, 6, 14), last_date = datetime(2020, 6, 20)) + first_date = datetime(2020, 6, 14), + last_date = datetime(2020, 6, 20), + suffix="total") assert (state_pct_positive.dropna()["val"] < 100).all() assert set(state_pct_positive.columns) ==\ @@ -31,7 +33,9 @@ def test_generate_sensor(self): # raw test_per_device state_test_per_device = generate_sensor_for_nonparent_geo( state_groups, "state_id", smooth = False, device = True, - first_date = datetime(2020, 6, 14), last_date = datetime(2020, 6, 20)) + first_date = datetime(2020, 6, 14), + last_date = datetime(2020, 6, 20), + suffix="total") assert state_test_per_device["se"].isnull().all() assert set(state_test_per_device.columns) ==\ @@ -45,7 +49,9 @@ def test_generate_sensor(self): parse_dates=['timestamp']) msa_pct_positive = generate_sensor_for_parent_geo( state_groups, msa_data, "cbsa_id", smooth = True, device = False, - first_date = datetime(2020, 6, 14), last_date = datetime(2020, 6, 20)) + first_date = datetime(2020, 6, 14), + last_date = datetime(2020, 6, 20), + suffix="total") assert (msa_pct_positive.dropna()["val"] < 100).all() assert set(msa_pct_positive.columns) ==\ @@ -55,7 +61,9 @@ def test_generate_sensor(self): # smoothed test_per_device msa_test_per_device = generate_sensor_for_parent_geo( state_groups, msa_data, "cbsa_id", smooth = True, device = True, - first_date = datetime(2020, 6, 14), last_date = datetime(2020, 6, 20)) + first_date = datetime(2020, 6, 14), + last_date = datetime(2020, 6, 20), + suffix="total") assert msa_test_per_device["se"].isnull().all() assert set(msa_test_per_device.columns) ==\ diff --git a/quidel_covidtest/tests/test_geo_maps.py b/quidel_covidtest/tests/test_geo_maps.py index eb1be3796..b1c74af22 100644 --- a/quidel_covidtest/tests/test_geo_maps.py +++ b/quidel_covidtest/tests/test_geo_maps.py @@ -1,29 +1,64 @@ +from datetime import datetime + import pandas as pd -from delphi_quidel_covidtest.geo_maps import geo_map +from delphi_quidel_covidtest.geo_maps import geo_map, add_megacounties +from delphi_quidel_covidtest.constants import AGE_GROUPS +DATA_COLS = ['totalTest', 'numUniqueDevices', 'positiveTest'] class TestGeoMap: def test_county(self): df = pd.DataFrame( { - "zip": [1607, 1740, 98661, 76010, 76012, 76016], - "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", - "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "zip": [1607, 1740, 1001, 1003, 98661, 76010, 76012, 76016], + "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", + "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], + "totalTest_total": [10, 16, 10, 15, 20, 200, 250, 500], + "positiveTest_total": [6, 8, 6, 8, 5, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 2, 1, 1, 1, 1, 1], } ) + + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [2, 3, 2, 2, 4, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [1, 1, 1, 1, 1, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [4, 6, 4, 4, 8, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [2, 2, 2, 2, 2, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 2, 1, 1, 1, 1, 1] new_df, res_key = geo_map("county", df) assert res_key == 'fips' - assert set(new_df["fips"].values) == set(['25027', '53011', '48439']) + assert set(new_df["fips"].values) == set(['25027', '25013', '25015', '53011', '48439']) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([150, 200, 950]) - assert set(new_df["positiveTest"].values) == set([18, 15, 75]) + assert set(new_df["totalTest_total"].values) == set([26, 10, 15, 20, 950]) + assert set(new_df["positiveTest_total"].values) == set([14, 6, 8, 5, 75]) + + assert set(new_df["totalTest_age_0_4"].values) == set([5, 2, 2, 85, 4]) + assert set(new_df["positiveTest_age_0_4"].values) == set([2, 1, 1, 12, 1]) + + assert set(new_df["totalTest_age_0_17"].values) == set([10, 4, 4, 170, 8]) + assert set(new_df["positiveTest_age_0_17"].values) == set([4, 2, 2, 24, 2]) + + # Test Megacounties + new_df["timestamp"] = [datetime.strptime(x, "%Y-%m-%d") for x in new_df["timestamp"]] + mega_df = add_megacounties(new_df, True) + + assert set(mega_df["totalTest_total"].values) == set([26, 10, 15, 20, 950, 25, 20]) + assert set(mega_df["positiveTest_total"].values) == set([14, 6, 8, 5, 75, 14, 5]) + + assert set(mega_df["totalTest_age_0_4"].values) == set([5, 2, 2, 85, 4, 4, 9, 4]) + assert set(mega_df["positiveTest_age_0_4"].values) == set([2, 1, 1, 12, 1, 4, 1]) + + assert set(mega_df["totalTest_age_0_17"].values) == set([10, 4, 4, 170, 8, 8, 18, 8]) + assert set(mega_df["positiveTest_age_0_17"].values) == set([4, 2, 2, 24, 2, 8, 2]) + + assert set(mega_df["state_id"].values) == set(["ma", "tx", "wa"]) + def test_state(self): @@ -32,18 +67,33 @@ def test_state(self): "zip": [1607, 1740, 98661, 76010, 76012, 76016], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } - ) + ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] + new_df, res_key = geo_map("state", df) assert set(new_df["state_id"].values) == set(['ma', 'tx', 'wa']) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([150, 200, 950]) - assert set(new_df["positiveTest"].values) == set([18, 15, 75]) + assert set(new_df["totalTest_total"].values) == set([150, 200, 950]) + assert set(new_df["positiveTest_total"].values) == set([18, 15, 75]) + + assert set(new_df["totalTest_age_0_4"].values) == set([30, 85, 20]) + assert set(new_df["positiveTest_age_0_4"].values) == set([3, 12, 3]) + + assert set(new_df["totalTest_age_0_17"].values) == set([60, 170, 40]) + assert set(new_df["positiveTest_age_0_17"].values) == set([6, 24, 6]) def test_hrr(self): @@ -52,18 +102,32 @@ def test_hrr(self): "zip": [1607, 98661, 76010, 76012, 74435, 74936], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] new_df, _ = geo_map("hrr", df) assert set(new_df["hrr"].values) == set(["16", "231", "340", "344", "394"]) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([500, 100, 250, 50, 400]) - assert set(new_df["positiveTest"].values) == set([50, 10, 20, 8, 20]) + assert set(new_df["totalTest_total"].values) == set([500, 100, 250, 50, 400]) + assert set(new_df["positiveTest_total"].values) == set([50, 10, 20, 8, 20]) + + assert set(new_df["totalTest_age_0_4"].values) == set([25, 20 ,20, 10, 60]) + assert set(new_df["positiveTest_age_0_4"].values) == set([8, 2, 3, 1, 4]) + + assert set(new_df["totalTest_age_0_17"].values) == set([50, 40, 40, 20, 120]) + assert set(new_df["positiveTest_age_0_17"].values) == set([16, 4, 6, 2, 8]) def test_msa(self): @@ -72,18 +136,32 @@ def test_msa(self): "zip": [1607, 73716, 73719, 76010, 74945, 74936], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] new_df, res_key = geo_map("msa", df) assert set(new_df["msa"].values) == set(['19100', '22900', '49340']) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([200, 750, 100]) - assert set(new_df["positiveTest"].values) == set([5, 70, 10]) + assert set(new_df["totalTest_total"].values) == set([200, 750, 100]) + assert set(new_df["positiveTest_total"].values) == set([5, 70, 10]) + + assert set(new_df["totalTest_age_0_4"].values) == set([40, 45, 20]) + assert set(new_df["positiveTest_age_0_4"].values) == set([1, 11, 2]) + + assert set(new_df["totalTest_age_0_17"].values) == set([80, 90, 40]) + assert set(new_df["positiveTest_age_0_17"].values) == set([2, 22, 4]) def test_nation(self): df = pd.DataFrame( @@ -91,18 +169,32 @@ def test_nation(self): "zip": [1607, 73716, 73719, 76010, 74945, 74936], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] new_df, res_key = geo_map("nation", df) assert set(new_df["nation"].values) == set(["us"]) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([1300]) - assert set(new_df["positiveTest"].values) == set([108]) + assert set(new_df["totalTest_total"].values) == set([1300]) + assert set(new_df["positiveTest_total"].values) == set([108]) + + assert set(new_df["totalTest_age_0_4"].values) == set([135]) + assert set(new_df["positiveTest_age_0_4"].values) == set([18]) + + assert set(new_df["totalTest_age_0_17"].values) == set([270]) + assert set(new_df["positiveTest_age_0_17"].values) == set([36]) def test_hhs(self): df = pd.DataFrame( @@ -110,15 +202,30 @@ def test_hhs(self): "zip": [1607, 1740, 98661, 76010, 76012, 76016], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] new_df, res_key = geo_map("hhs", df) assert set(new_df["hhs"].values) == set(["1", "6", "10"]) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([150, 200, 950]) - assert set(new_df["positiveTest"].values) == set([18, 15, 75]) + assert set(new_df["totalTest_total"].values) == set([150, 200, 950]) + assert set(new_df["positiveTest_total"].values) == set([18, 15, 75]) + + assert set(new_df["totalTest_age_0_4"].values) == set([30, 20, 85]) + assert set(new_df["positiveTest_age_0_4"].values) == set([3, 3, 12]) + + assert set(new_df["totalTest_age_0_17"].values) == set([60, 40, 170]) + assert set(new_df["positiveTest_age_0_17"].values) == set([6, 6, 24]) + diff --git a/quidel_covidtest/tests/test_pull.py b/quidel_covidtest/tests/test_pull.py index 17ddbb6fd..ae35fc68f 100644 --- a/quidel_covidtest/tests/test_pull.py +++ b/quidel_covidtest/tests/test_pull.py @@ -11,6 +11,7 @@ check_export_end_date, check_export_start_date ) +from delphi_quidel_covidtest.constants import AGE_GROUPS END_FROM_TODAY_MINUS = 5 EXPORT_DAY_RANGE = 40 @@ -60,8 +61,10 @@ def test_pull_quidel_covidtest(self): assert [first_date.month, first_date.day] == [7, 18] assert [last_date.month, last_date.day] == [7, 23] - assert (df.columns ==\ - ['timestamp', 'zip', 'totalTest', 'numUniqueDevices', 'positiveTest']).all() + assert set(['timestamp', 'zip']).issubset(set(df.columns)) + for agegroup in AGE_GROUPS: + set([f'totalTest_{agegroup}', f'numUniqueDevices_{agegroup}', + f'positiveTest_{agegroup}']).issubset(set(df.columns)) def test_check_intermediate_file(self): diff --git a/quidel_covidtest/tests/test_run.py b/quidel_covidtest/tests/test_run.py index 89c59a24f..d9e28108a 100644 --- a/quidel_covidtest/tests/test_run.py +++ b/quidel_covidtest/tests/test_run.py @@ -3,8 +3,9 @@ from os.path import join import pandas as pd +import numpy as np -from delphi_utils import read_params, add_prefix +from delphi_utils import add_prefix from delphi_quidel_covidtest.constants import PARENT_GEO_RESOLUTIONS, NONPARENT_GEO_RESOLUTIONS, \ SENSORS from delphi_quidel_covidtest.run import run_module @@ -68,6 +69,34 @@ def test_output_files(self, clean_receiving_dir): ) assert (df.columns.values == ["geo_id", "val", "se", "sample_size"]).all() + df = pd.read_csv(join("./receiving", "20200718_county_covid_ag_raw_pct_positive.csv")) + #ZIP 24534, FIPS 51083 has 4 counts, 2 positives + #ZIP 24529, FIPS 51117 has 24 counts, 12 positives + #ZIP 24526, FIPS 51019 has 49 counts, 26 positives + #MEGAFIPS 51000, should have 4+24+49 = 77 counts, 2+12+26 = 40 positives + #ZIP 24527, FIPS 51143 has 64 counts, 32 positives + #ZIP 22079, FIPS 51059 has 60 counts, 24 positives + assert set(df.geo_id) == set([51143, 51059, 51000]) + assert set(df.sample_size) == set([64, 60, 77]) + assert np.allclose(df.val.values, [(40+0.5)/(77+1)*100, (24+0.5)/(60+1)*100, + (32+0.5)/(64+1)*100], equal_nan=True) + + + df = pd.read_csv(join("./receiving", "20200718_county_covid_ag_smoothed_pct_positive.csv")) + assert set(df.geo_id) == set([51000, 51019, 51143, 51059]) + assert set(df.sample_size) == set([50, 50, 64, 60]) + parent_test = 4+24+49+64+60 + parent_pos = 2+12+26+32+24 + #ZIP 24526, FIPS 51019 has 49 counts, 26 positives, borrow 1 pseudo counts from VA + #MEGAFIPS 51000, should have 4+24 = 28 counts, 2+12 = 14 positives, borrow 22 pseudo counts + #ZIP 24527, FIPS 51143 has 64 counts, 32 positives, do not borrow + #ZIP 22079, FIPS 51059 has 60 counts, 24 positives, do not borrow + assert np.allclose(df.val.values, + [(14+parent_pos*22/parent_test+0.5)/(50+1)*100, + (26+parent_pos*1/parent_test+0.5)/(50+1)*100, + (24+0.5)/(60+1)*100, + (32+0.5)/(64+1)*100], equal_nan=True) + # test_intermediate_file flag = None for fname in listdir("./cache"):