From d5c51e132e79112b4f704ed52ae99a70dda6a36d Mon Sep 17 00:00:00 2001 From: nmdefries <42820733+nmdefries@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:52:02 -0500 Subject: [PATCH 01/12] Fetch new `flusurv` fields (#210) * fetch new flusurv fields * add a couple new fields * docs: document (GHA) * style: styler (GHA) * style: styler (GHA) * docs: document (GHA) --------- Co-authored-by: nmdefries --- DESCRIPTION | 2 +- R/endpoints.R | 303 ++++++++++++++++++++++----------------- R/epidatacall.R | 23 +-- man/covidcast_epidata.Rd | 4 +- 4 files changed, 190 insertions(+), 142 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index ad1d2f35..c32a6132 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -68,7 +68,7 @@ Encoding: UTF-8 Language: en-US LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 +RoxygenNote: 7.3.3 Collate: 'auth.R' 'avail_endpoints.R' diff --git a/R/endpoints.R b/R/endpoints.R index 1f789d18..e82a9928 100644 --- a/R/endpoints.R +++ b/R/endpoints.R @@ -22,10 +22,11 @@ #' @keywords endpoint #' @export pvt_cdc <- function( - auth, - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + auth, + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("auth", auth, len = 1) @@ -88,13 +89,14 @@ pvt_cdc <- function( #' @keywords endpoint #' @export pub_covid_hosp_facility_lookup <- function( - ..., - state = NULL, - ccn = NULL, - city = NULL, - zip = NULL, - fips_code = NULL, - fetch_args = fetch_args_list()) { + ..., + state = NULL, + ccn = NULL, + city = NULL, + zip = NULL, + fips_code = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() assert_character_param("state", state, len = 1, required = FALSE) @@ -182,11 +184,12 @@ pub_covid_hosp_facility_lookup <- function( #' @export # pub_covid_hosp_facility <- function( - hospital_pks, - collection_weeks = "*", - ..., - publication_dates = NULL, - fetch_args = fetch_args_list()) { + hospital_pks, + collection_weeks = "*", + ..., + publication_dates = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() collection_weeks <- get_wildcard_equivalent_dates(collection_weeks, "day") @@ -567,12 +570,13 @@ pub_covid_hosp_facility <- function( #' @export # pub_covid_hosp_state_timeseries <- function( - states, - dates = "*", - ..., - as_of = NULL, - issues = NULL, - fetch_args = fetch_args_list()) { + states, + dates = "*", + ..., + as_of = NULL, + issues = NULL, + fetch_args = fetch_args_list() +) { # Check parameters rlang::check_dots_empty() @@ -982,17 +986,18 @@ pub_covidcast_meta <- function(fetch_args = fetch_args_list()) { #' @keywords endpoint #' @export pub_covidcast <- function( - source, - signals, - geo_type, - time_type, - geo_values = "*", - time_values = "*", - ..., - as_of = NULL, - issues = NULL, - lag = NULL, - fetch_args = fetch_args_list()) { + source, + signals, + geo_type, + time_type, + geo_values = "*", + time_values = "*", + ..., + as_of = NULL, + issues = NULL, + lag = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() # Check parameters @@ -1097,9 +1102,10 @@ pub_covidcast <- function( #' @keywords endpoint #' @export pub_delphi <- function( - system, - epiweek, - fetch_args = fetch_args_list()) { + system, + epiweek, + fetch_args = fetch_args_list() +) { assert_character_param("system", system) assert_timeset_param("epiweek", epiweek, len = 1) epiweek <- parse_timeset_input(epiweek) @@ -1134,9 +1140,10 @@ pub_delphi <- function( #' @keywords endpoint #' @export pub_dengue_nowcast <- function( - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("locations", locations) @@ -1177,11 +1184,12 @@ pub_dengue_nowcast <- function( #' @keywords endpoint #' @export pvt_dengue_sensors <- function( - auth, - names, - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + auth, + names, + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("auth", auth, len = 1) @@ -1234,12 +1242,13 @@ pvt_dengue_sensors <- function( #' @keywords endpoint #' @export pub_ecdc_ili <- function( - regions, - epiweeks = "*", - ..., - issues = NULL, - lag = NULL, - fetch_args = fetch_args_list()) { + regions, + epiweeks = "*", + ..., + issues = NULL, + lag = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") @@ -1301,12 +1310,13 @@ pub_ecdc_ili <- function( #' @keywords endpoint #' @export pub_flusurv <- function( - locations, - epiweeks = "*", - ..., - issues = NULL, - lag = NULL, - fetch_args = fetch_args_list()) { + locations, + epiweeks = "*", + ..., + issues = NULL, + lag = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") @@ -1340,7 +1350,30 @@ pub_flusurv <- function( create_epidata_field_info("rate_age_2", "float"), create_epidata_field_info("rate_age_3", "float"), create_epidata_field_info("rate_age_4", "float"), - create_epidata_field_info("rate_overall", "float") + create_epidata_field_info("rate_overall", "float"), + create_epidata_field_info("rate_age_5", "float"), + create_epidata_field_info("rate_age_6", "float"), + create_epidata_field_info("rate_age_7", "float"), + create_epidata_field_info("rate_age_18t29", "float"), + create_epidata_field_info("rate_age_30t39", "float"), + create_epidata_field_info("rate_age_40t49", "float"), + create_epidata_field_info("rate_age_5t11", "float"), + create_epidata_field_info("rate_age_12t17", "float"), + create_epidata_field_info("rate_age_lt18", "float"), + create_epidata_field_info("rate_age_gte18", "float"), + create_epidata_field_info("rate_age_0tlt1", "float"), + create_epidata_field_info("rate_age_1t4", "float"), + create_epidata_field_info("rate_age_gte75", "float"), + create_epidata_field_info("rate_race_white", "float"), + create_epidata_field_info("rate_race_black", "float"), + create_epidata_field_info("rate_race_hisp", "float"), + create_epidata_field_info("rate_race_asian", "float"), + create_epidata_field_info("rate_race_natamer", "float"), + create_epidata_field_info("rate_sex_male", "float"), + create_epidata_field_info("rate_sex_female", "float"), + create_epidata_field_info("rate_flu_a", "float"), + create_epidata_field_info("rate_flu_b", "float"), + create_epidata_field_info("season", "text") ) ) %>% fetch(fetch_args = fetch_args) } @@ -1367,12 +1400,13 @@ pub_flusurv <- function( #' @keywords endpoint #' @export pub_fluview_clinical <- function( - regions, - epiweeks = "*", - ..., - issues = NULL, - lag = NULL, - fetch_args = fetch_args_list()) { + regions, + epiweeks = "*", + ..., + issues = NULL, + lag = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") @@ -1474,13 +1508,14 @@ pub_fluview_meta <- function(fetch_args = fetch_args_list()) { #' @keywords endpoint #' @export pub_fluview <- function( - regions, - epiweeks = "*", - ..., - issues = NULL, - lag = NULL, - auth = NULL, - fetch_args = fetch_args_list()) { + regions, + epiweeks = "*", + ..., + issues = NULL, + lag = NULL, + auth = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") @@ -1554,9 +1589,10 @@ pub_fluview <- function( #' @keywords endpoint #' @export pub_gft <- function( - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("locations", locations) @@ -1599,11 +1635,12 @@ pub_gft <- function( #' @keywords endpoint #' @export pvt_ght <- function( - auth, - locations, - epiweeks = "*", - query, - fetch_args = fetch_args_list()) { + auth, + locations, + epiweeks = "*", + query, + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("auth", auth, len = 1) @@ -1648,12 +1685,13 @@ pvt_ght <- function( #' @keywords endpoint #' @export pub_kcdc_ili <- function( - regions, - epiweeks = "*", - ..., - issues = NULL, - lag = NULL, - fetch_args = fetch_args_list()) { + regions, + epiweeks = "*", + ..., + issues = NULL, + lag = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") @@ -1749,9 +1787,10 @@ pub_meta <- function(fetch_args = fetch_args_list()) { #' @keywords endpoint #' @export pub_nidss_dengue <- function( - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("locations", locations) @@ -1793,12 +1832,13 @@ pub_nidss_dengue <- function( #' @keywords endpoint #' @export pub_nidss_flu <- function( - regions, - epiweeks = "*", - ..., - issues = NULL, - lag = NULL, - fetch_args = fetch_args_list()) { + regions, + epiweeks = "*", + ..., + issues = NULL, + lag = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") @@ -1860,10 +1900,11 @@ pub_nidss_flu <- function( #' @keywords endpoint #' @export pvt_norostat <- function( - auth, - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + auth, + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("auth", auth, len = 1) @@ -1906,9 +1947,10 @@ pvt_norostat <- function( #' @keywords endpoint #' @export pub_nowcast <- function( - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("locations", locations) @@ -1947,12 +1989,13 @@ pub_nowcast <- function( #' @keywords endpoint #' @export pub_paho_dengue <- function( - regions, - epiweeks = "*", - ..., - issues = NULL, - lag = NULL, - fetch_args = fetch_args_list()) { + regions, + epiweeks = "*", + ..., + issues = NULL, + lag = NULL, + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") @@ -2010,10 +2053,11 @@ pub_paho_dengue <- function( #' @keywords endpoint #' @export pvt_quidel <- function( - auth, - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + auth, + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("auth", auth, len = 1) @@ -2069,11 +2113,12 @@ pvt_quidel <- function( #' @keywords endpoint #' @export pvt_sensors <- function( - auth, - names, - locations, - epiweeks = "*", - fetch_args = fetch_args_list()) { + auth, + names, + locations, + epiweeks = "*", + fetch_args = fetch_args_list() +) { epiweeks <- get_wildcard_equivalent_dates(epiweeks, "week") assert_character_param("auth", auth, len = 1) @@ -2128,12 +2173,13 @@ pvt_sensors <- function( #' @keywords endpoint #' @export pvt_twitter <- function( - auth, - locations, - ..., - time_type = c("day", "week"), - time_values = "*", - fetch_args = fetch_args_list()) { + auth, + locations, + ..., + time_type = c("day", "week"), + time_values = "*", + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() time_type <- match.arg(time_type) @@ -2212,13 +2258,14 @@ pvt_twitter <- function( #' @keywords endpoint #' @export pub_wiki <- function( - articles, - ..., - time_type = c("day", "week"), - time_values = "*", - hours = NULL, - language = "en", - fetch_args = fetch_args_list()) { + articles, + ..., + time_type = c("day", "week"), + time_values = "*", + hours = NULL, + language = "en", + fetch_args = fetch_args_list() +) { rlang::check_dots_empty() time_type <- match.arg(time_type) diff --git a/R/epidatacall.R b/R/epidatacall.R index c97a6d16..cd3c06b4 100644 --- a/R/epidatacall.R +++ b/R/epidatacall.R @@ -161,17 +161,18 @@ print.epidata_call <- function(x, ...) { #' @aliases fetch_args #' @importFrom checkmate assert_character assert_logical assert_numeric fetch_args_list <- function( - ..., - fields = NULL, - disable_date_parsing = FALSE, - disable_data_frame_parsing = FALSE, - return_empty = FALSE, - timeout_seconds = 15 * 60, - base_url = NULL, - dry_run = FALSE, - debug = FALSE, - format_type = c("json", "classic", "csv"), - refresh_cache = FALSE) { + ..., + fields = NULL, + disable_date_parsing = FALSE, + disable_data_frame_parsing = FALSE, + return_empty = FALSE, + timeout_seconds = 15 * 60, + base_url = NULL, + dry_run = FALSE, + debug = FALSE, + format_type = c("json", "classic", "csv"), + refresh_cache = FALSE +) { rlang::check_dots_empty() assert_character(fields, null.ok = TRUE, any.missing = FALSE) diff --git a/man/covidcast_epidata.Rd b/man/covidcast_epidata.Rd index fbf3f1cc..e3a997a8 100644 --- a/man/covidcast_epidata.Rd +++ b/man/covidcast_epidata.Rd @@ -27,7 +27,7 @@ an object containing fields for every signal: \if{html}{\out{
}}\preformatted{epidata <- covidcast_epidata() epidata$signals -#> # A tibble: 468 x 3 +#> # A tibble: 508 x 3 #> source signal short_description #> #> 1 chng smoothed_outpatient_cli Estimated percentage of outpatie~ @@ -40,7 +40,7 @@ epidata$signals #> 8 chng 7dav_outpatient_covid Ratio of outpatient doctor visit~ #> 9 covid-act-now pcr_specimen_positivity_rate Proportion of PCR specimens test~ #> 10 covid-act-now pcr_specimen_total_tests Total number of PCR specimens te~ -#> # i 458 more rows +#> # i 498 more rows }\if{html}{\out{
}} If you use an editor that supports tab completion, such as RStudio, type From bc2c22fa137c05a40e49cfa5312462dadec8877c Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Thu, 13 Nov 2025 13:10:14 -0800 Subject: [PATCH 02/12] ci: same pr-commands as other epi packages (#325) * ci: same pr-commands as other epi packages * document, style now PR commands * docs preview is now available * ci: be a bit more judicious when triggering ci --- .github/workflows/R-CMD-check.yaml | 8 ++ .github/workflows/document.yaml | 52 ---------- .github/workflows/lint.yaml | 7 ++ .github/workflows/pkgdown.yaml | 8 ++ .github/workflows/pr-commands.yaml | 149 ++++++++++++++++++++++++++++ .github/workflows/style.yaml | 82 --------------- .github/workflows/test-coverage.yml | 9 +- 7 files changed, 180 insertions(+), 135 deletions(-) delete mode 100644 .github/workflows/document.yaml create mode 100644 .github/workflows/pr-commands.yaml delete mode 100644 .github/workflows/style.yaml diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index a0b910bd..320729f2 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -5,6 +5,14 @@ on: branches: [main, dev] pull_request: branches: [main, dev] + paths: + - '**.R' + - '**.Rmd' + - DESCRIPTION + - NAMESPACE + - man/** + - R/** + - tests/** workflow_dispatch: name: R-CMD-check diff --git a/.github/workflows/document.yaml b/.github/workflows/document.yaml deleted file mode 100644 index 17a85104..00000000 --- a/.github/workflows/document.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples -# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help -on: - push: - paths: ["R/**", "README.Rmd"] - workflow_dispatch: - -name: Document - -jobs: - document: - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - DELPHI_EPIDATA_KEY: ${{ secrets.DELPHI_GITHUB_ACTIONS_EPIDATA_API_KEY }} - steps: - - name: Checkout repo - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Setup R - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true - - - name: Install dependencies - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: | - any::devtools - any::roxygen2 - needs: | - devtools - roxygen2 - - - name: Document - run: roxygen2::roxygenise() - shell: Rscript {0} - - - name: Build README.md from README.Rmd - run: Rscript -e 'devtools::build_readme()' - - - name: Commit and push changes - run: | - git config --local user.name "$GITHUB_ACTOR" - git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" - git add README.md - git add man/\* NAMESPACE DESCRIPTION - git commit -m "docs: document (GHA)" || echo "No changes to commit" - git pull --rebase - git push origin diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 4eda2fd3..baa43519 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -5,6 +5,13 @@ on: branches: [main, dev] pull_request: branches: [main, dev] + paths: + - '**.R' + - '**.Rmd' + - DESCRIPTION + - NAMESPACE + - man/** + - R/** workflow_dispatch: name: lint diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index f5e78c25..5a6bafd9 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -8,6 +8,14 @@ on: branches: [main, dev] pull_request: branches: [main, dev] + paths: + - '**.R' + - '**.Rmd' + - DESCRIPTION + - NAMESPACE + - man/** + - R/** + - _pkgdown.yml release: types: [published] workflow_dispatch: diff --git a/.github/workflows/pr-commands.yaml b/.github/workflows/pr-commands.yaml new file mode 100644 index 00000000..6cf91e87 --- /dev/null +++ b/.github/workflows/pr-commands.yaml @@ -0,0 +1,149 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +# Modifications: +# - Allow more roles to trigger each PR command +# - Document builds README.md from README.Rmd with devtools::build_readme() +# - Include a doc-preview command (uses Netlify to preview the docs) +on: + issue_comment: + types: [created] + +name: pr-commands.yaml + +permissions: read-all + +jobs: + document: + if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'CONTRIBUTOR' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} + name: document + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/pr-fetch@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::roxygen2 + needs: pr-document + + - name: Document + run: roxygen2::roxygenise() + shell: Rscript {0} + + - name: Build README.md from README.Rmd + run: Rscript -e 'if (file.exists("README.Rmd")) devtools::build_readme()' + + - name: commit + run: | + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git add man/\* NAMESPACE + git commit -m 'Document' + + - uses: r-lib/actions/pr-push@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + style: + if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'CONTRIBUTOR' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} + name: style + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/pr-fetch@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - uses: r-lib/actions/setup-r@v2 + + - name: Install dependencies + run: install.packages("styler") + shell: Rscript {0} + + - name: Style + run: styler::style_pkg() + shell: Rscript {0} + + - name: commit + run: | + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + git add \*.R + git commit -m 'Style' + + - uses: r-lib/actions/pr-push@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + preview: + if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'CONTRIBUTOR' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/preview-docs') }} + + runs-on: ubuntu-latest + permissions: + # Needed to write a comment on the PR + pull-requests: write + # Needed to read the PR branch + contents: read + steps: + - uses: actions/checkout@v4 + with: + # Checkout the PR branch + ref: refs/pull/${{ github.event.issue.number }}/head + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::pkgdown, local::. + needs: website + + - name: Build site + run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) + shell: Rscript {0} + + - name: Deploy to Netlify + uses: nwtgck/actions-netlify@v3.0 + with: + # Standard config + github-token: ${{ secrets.GITHUB_TOKEN }} + deploy-message: "Deploy from GitHub Actions" + # 'docs/' is the default directory for pkgdown::build_site() + # we add 'dev' because _pkgdown.yml has 'development: mode: devel' + publish-dir: './docs/dev' + # Development deploys only + production-deploy: false + # Enable pull request comment (default) + enable-pull-request-comment: true + # Overwrite the pull request comment with updated link (default) + overwrites-pull-request-comment: true + # Don't deploy to GitHub + enable-github-deployment: false + # Don't update the status of the commit + enable-commit-status: false + # Don't comment on the commit + enable-commit-comment: false + env: + # Netlify credentials (currently from Dmitry's account) + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + timeout-minutes: 1 diff --git a/.github/workflows/style.yaml b/.github/workflows/style.yaml deleted file mode 100644 index acdc0470..00000000 --- a/.github/workflows/style.yaml +++ /dev/null @@ -1,82 +0,0 @@ -# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples -# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help -on: - push: - paths: - [ - "**.[rR]", - "**.[qrR]md", - "**.[rR]markdown", - "**.[rR]nw", - "**.[rR]profile", - ] - workflow_dispatch: - -name: Style - -jobs: - style: - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - DELPHI_EPIDATA_KEY: ${{ secrets.DELPHI_GITHUB_ACTIONS_EPIDATA_API_KEY }} - steps: - - name: Checkout repo - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Setup R - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true - - - name: Install dependencies - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: any::styler, any::roxygen2 - needs: styler - - - name: Enable styler cache - run: styler::cache_activate() - shell: Rscript {0} - - - name: Determine cache location - id: styler-location - run: | - cat( - "location=", - styler::cache_info(format = "tabular")$location, - "\n", - file = Sys.getenv("GITHUB_OUTPUT"), - append = TRUE, - sep = "" - ) - shell: Rscript {0} - - - name: Cache styler - uses: actions/cache@v3 - with: - path: ${{ steps.styler-location.outputs.location }} - key: ${{ runner.os }}-styler-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-styler- - ${{ runner.os }}- - - - name: Style - run: styler::style_pkg() - shell: Rscript {0} - - - name: Commit and push changes - run: | - if FILES_TO_COMMIT=($(git diff-index --name-only ${{ github.sha }} \ - | egrep --ignore-case '\.(R|[qR]md|Rmarkdown|Rnw|Rprofile)$')) - then - git config --local user.name "$GITHUB_ACTOR" - git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" - git commit ${FILES_TO_COMMIT[*]} -m "style: styler (GHA)" - git pull --rebase - git push origin - else - echo "No changes to commit." - fi diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml index 74ccb483..e3b3ffda 100644 --- a/.github/workflows/test-coverage.yml +++ b/.github/workflows/test-coverage.yml @@ -5,7 +5,14 @@ on: push: branches: [main, dev] pull_request: - + paths: + - '**.R' + - '**.Rmd' + - DESCRIPTION + - NAMESPACE + - man/** + - R/** + - tests/** name: test-coverage jobs: From 4be1c3082fec0d7d7c3b24d602d54cdb9216df79 Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Fri, 14 Nov 2025 16:47:32 -0800 Subject: [PATCH 03/12] enh: pub_covidcast errors when time_type not day or week * nssp errors when time_type not week --- R/endpoints.R | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/R/endpoints.R b/R/endpoints.R index e82a9928..a682178b 100644 --- a/R/endpoints.R +++ b/R/endpoints.R @@ -1040,6 +1040,17 @@ pub_covidcast <- function( ) } + if (source == "nssp" && time_type != "week") { + cli::cli_abort( + "{source} data is only available at the week level", + class = "epidatr__nchs_week_only" + ) + } + + # TODO: This should probably be done in the create_epidata_call function. But + # this is a quick fix for now. + checkmate::assert_subset(time_type, c("day", "week")) + create_epidata_call( "covidcast/", list( From 3b0e01cb4a0521bb0829f9b98536fe3ffc435884 Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Fri, 14 Nov 2025 16:49:04 -0800 Subject: [PATCH 04/12] fix: pub_covidcast_meta doesn't null hsa_nci, doesn't cast time to avoid NA for week types * pub_covidcast doen't null hsa_nci --- R/endpoints.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/endpoints.R b/R/endpoints.R index a682178b..2c3a3148 100644 --- a/R/endpoints.R +++ b/R/endpoints.R @@ -910,17 +910,17 @@ pub_covidcast_meta <- function(fetch_args = fetch_args_list()) { create_epidata_field_info( "geo_type", "categorical", - categories = c("nation", "msa", "hrr", "hhs", "state", "county", "dma") + categories = c("nation", "msa", "hrr", "hhs", "state", "county", "dma", "hsa_nci") ), - create_epidata_field_info("min_time", "date"), - create_epidata_field_info("max_time", "date"), + create_epidata_field_info("min_time", "int"), + create_epidata_field_info("max_time", "int"), create_epidata_field_info("num_locations", "int"), create_epidata_field_info("min_value", "float"), create_epidata_field_info("max_value", "float"), create_epidata_field_info("mean_value", "float"), create_epidata_field_info("stdev_value", "float"), create_epidata_field_info("last_update", "int"), - create_epidata_field_info("max_issue", "date"), + create_epidata_field_info("max_issue", "int"), create_epidata_field_info("min_lag", "int"), create_epidata_field_info("max_lag", "int") ) @@ -1070,7 +1070,7 @@ pub_covidcast <- function( create_epidata_field_info( "geo_type", "categorical", - categories = c("nation", "msa", "hrr", "hhs", "state", "county") + categories = c("nation", "msa", "hrr", "hhs", "state", "county", "dma", "hsa_nci") ), create_epidata_field_info("time_type", "categorical", categories = From a9a038572915e7355dd74a9d0d42f33d5fd38336 Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Fri, 14 Nov 2025 16:49:51 -0800 Subject: [PATCH 05/12] enh: checkmate some checks --- R/epidatacall.R | 19 ++++++++++++++----- R/model.R | 27 ++++++++++++--------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/R/epidatacall.R b/R/epidatacall.R index cd3c06b4..402b3a5c 100644 --- a/R/epidatacall.R +++ b/R/epidatacall.R @@ -44,11 +44,11 @@ #' @importFrom purrr map_chr map_lgl create_epidata_call <- function(endpoint, params, meta = NULL, only_supports_classic = FALSE) { - stopifnot(is.character(endpoint), length(endpoint) == 1) - stopifnot(is.list(params)) - stopifnot(is.null(meta) || is.list(meta)) - stopifnot(all(map_lgl(meta, ~ inherits(.x, "EpidataFieldInfo")))) - stopifnot(is.logical(only_supports_classic), length(only_supports_classic) == 1) + checkmate::assert_character(endpoint, len = 1) + checkmate::assert_list(params) + checkmate::assert_list(meta, null.ok = TRUE) + checkmate::assert_logical(only_supports_classic, len = 1) + checkmate::assert_true(all(map_lgl(meta, ~ inherits(.x, "EpidataFieldInfo")))) if (length(unique(meta)) != length(meta)) { cli::cli_abort( @@ -73,6 +73,15 @@ create_epidata_call <- function(endpoint, params, meta = NULL, ) } + # TODO: Something like this in the future? We set up the categories + # but we don't actually validate them yet? + # for (field in names(params)) { + # value <- params[[field]] + # if (meta[[field]]$type == "categorical") { + # checkmate::assert_subset(value, meta[[field]]$categories) + # } + # } + if (is.null(meta)) { meta <- list() } diff --git a/R/model.R b/R/model.R index 383c6ab7..015ec8cf 100644 --- a/R/model.R +++ b/R/model.R @@ -132,21 +132,18 @@ create_epidata_field_info <- function(name, type, description = "", categories = c()) { - stopifnot(is.character(name) && length(name) == 1) - stopifnot( - is.character(type) && - length(type) == 1 && - type %in% c( - "text", - "int", - "float", - "date", - "epiweek", - "categorical", - "bool" - ) - ) - stopifnot(is.character(description) && length(description) == 1) + checkmate::assert_character(name, len = 1) + checkmate::assert_character(type, len = 1) + checkmate::assert_subset(type, c( + "text", + "int", + "float", + "date", + "epiweek", + "categorical", + "bool" + )) + checkmate::assert_character(description, len = 1) structure( list( name = name, From 0240e9d13b8244869bb96bd3d1f48fb8222098cd Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Fri, 14 Nov 2025 17:06:49 -0800 Subject: [PATCH 06/12] enh: add reference_week_day arg to fetch_args_list --- R/epidatacall.R | 14 ++++++++++---- R/model.R | 13 +++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/R/epidatacall.R b/R/epidatacall.R index 402b3a5c..21925c10 100644 --- a/R/epidatacall.R +++ b/R/epidatacall.R @@ -164,7 +164,10 @@ print.epidata_call <- function(x, ...) { #' @param format_type the format to request from the API, one of classic, json, #' csv; this is only used by `fetch_debug`, and by default is `"json"` #' @param refresh_cache if `TRUE`, ignore the cache, fetch the data from the -#' API, and update the cache, if it is enabled +#' API, and update the cache, if it is enabled +#' @param reference_week_day the day of the week to use as the reference day +#' when parsing epiweeks to dates (happens if `disable_date_parsing` is `FALSE`) +#' Defaults to 1 Sunday (the first day of the week). #' @return A `fetch_args` object containing all the specified options #' @export #' @aliases fetch_args @@ -180,7 +183,8 @@ fetch_args_list <- function( dry_run = FALSE, debug = FALSE, format_type = c("json", "classic", "csv"), - refresh_cache = FALSE + refresh_cache = FALSE, + reference_week_day = 1 ) { rlang::check_dots_empty() @@ -194,6 +198,7 @@ fetch_args_list <- function( assert_logical(debug, null.ok = FALSE, len = 1L, any.missing = FALSE) format_type <- match.arg(format_type) assert_logical(refresh_cache, null.ok = FALSE, len = 1L, any.missing = FALSE) + assert_numeric(reference_week_day, null.ok = FALSE, len = 1L, any.missing = FALSE) structure( list( @@ -206,7 +211,8 @@ fetch_args_list <- function( dry_run = dry_run, debug = debug, format_type = format_type, - refresh_cache = refresh_cache + refresh_cache = refresh_cache, + reference_week_day = reference_week_day ), class = "fetch_args" ) @@ -279,7 +285,7 @@ fetch <- function(epidata_call, fetch_args = fetch_args_list()) { if (fetch_args$return_empty && length(response_content) == 0) { fetched <- tibble() } else { - fetched <- parse_data_frame(epidata_call, response_content, fetch_args$disable_date_parsing) %>% as_tibble() + fetched <- parse_data_frame(epidata_call, response_content, fetch_args$disable_date_parsing, fetch_args$reference_week_day) %>% as_tibble() } }) diff --git a/R/model.R b/R/model.R index 015ec8cf..0322cc75 100644 --- a/R/model.R +++ b/R/model.R @@ -163,7 +163,7 @@ print.EpidataFieldInfo <- function(x, ...) { } #' @importFrom stats na.omit -parse_value <- function(info, value, disable_date_parsing = FALSE) { +parse_value <- function(info, value, disable_date_parsing = FALSE, reference_week_day = 1) { stopifnot(inherits(info, "EpidataFieldInfo")) if (is.null(value)) { @@ -171,7 +171,7 @@ parse_value <- function(info, value, disable_date_parsing = FALSE) { } else if (info$type == "date" && !disable_date_parsing && !inherits(value, "Date")) { return(parse_api_date(value)) } else if (info$type == "epiweek" && !disable_date_parsing && !inherits(value, "Date")) { - return(parse_api_week(value)) + return(parse_api_week(value, reference_week_day = reference_week_day)) } else if (info$type == "bool") { return(as.logical(value)) } else if (info$type == "int") { @@ -197,7 +197,7 @@ parse_value <- function(info, value, disable_date_parsing = FALSE) { } #' @importFrom purrr map_chr -parse_data_frame <- function(epidata_call, df, disable_date_parsing = FALSE) { +parse_data_frame <- function(epidata_call, df, disable_date_parsing = FALSE, reference_week_day = 1) { stopifnot(inherits(epidata_call, "epidata_call")) meta <- epidata_call$meta df <- as.data.frame(df) @@ -224,7 +224,7 @@ parse_data_frame <- function(epidata_call, df, disable_date_parsing = FALSE) { for (i in seq_len(length(meta))) { info <- meta[[i]] if (info$name %in% columns) { - df[[info$name]] <- parse_value(info, df[[info$name]], disable_date_parsing = disable_date_parsing) + df[[info$name]] <- parse_value(info, df[[info$name]], disable_date_parsing = disable_date_parsing, reference_week_day = reference_week_day) } } df @@ -251,14 +251,15 @@ parse_api_date <- function(value) { #' parse_api_week converts an integer to a date #' @param value value to be converted to an epiweek +#' @param reference_week_day the day of the week to use as the reference day. Defaults to Saturday. #' @return a date #' @importFrom MMWRweek MMWRweek2Date #' @keywords internal -parse_api_week <- function(value) { +parse_api_week <- function(value, reference_week_day = 1) { v <- as.integer(value) years <- floor(v / 100) weeks <- v - (years * 100) - MMWRweek::MMWRweek2Date(years, weeks) + MMWRweek::MMWRweek2Date(years, weeks, MMWRday = reference_week_day) } #' @importFrom checkmate test_character test_class test_date test_integerish test_list From 781eecd4452bc0e4218df4169557e1edac863e7d Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Fri, 14 Nov 2025 17:08:49 -0800 Subject: [PATCH 07/12] doc: bump version and add NEWS entry --- DESCRIPTION | 2 +- NEWS.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index c32a6132..f818e50d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: epidatr Title: Client for Delphi's 'Epidata' API -Version: 1.2.1 +Version: 1.2.2 Authors@R: c( person("Logan", "Brooks", , "lcbrooks@andrew.cmu.edu", role = "aut"), person("Dmitry", "Shemetov", , "dshemeto@andrew.cmu.edu", role = "aut"), diff --git a/NEWS.md b/NEWS.md index 38c3c8b5..c2630125 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,21 @@ +# epidatr 1.2.2 + +## Changes + +- Add `reference_week_day` argument to `fetch_args_list` and `fetch` functions. + +## Patches + +- Validate that `time_type` is one of "day" or "week" in `pub_covidcast`. +- Validate that `time_type` is "week" when source is "nssp" in `pub_covidcast`. +- Allow `hsa_nci` as a `geo_type` in `pub_covidcast`. +- Allow `hsa_nci` as a `geo_type` in `pub_covidcast_meta`. +- `pub_covidcast_meta` now returns `min_time`, `max_time`, `max_issue` as + integers rather than Dates. Because these fields can mix YYYYMMDD and YYYYWW + values, we recommend you parse them yourself. + # epidatr 1.2.1 + ## Patches - Fix so that `covidcast_epidata()` will still print if fields are missing. From 42b78677ba1d14c891ed72931c9722f902b041ae Mon Sep 17 00:00:00 2001 From: Dmitry Shemetov Date: Fri, 14 Nov 2025 17:16:21 -0800 Subject: [PATCH 08/12] fix: tests --- tests/testthat/test-epidatacall.R | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-epidatacall.R b/tests/testthat/test-epidatacall.R index 69f885b4..e9096afa 100644 --- a/tests/testthat/test-epidatacall.R +++ b/tests/testthat/test-epidatacall.R @@ -43,7 +43,8 @@ test_that("fetch_args", { dry_run = FALSE, debug = FALSE, format_type = "json", - refresh_cache = FALSE + refresh_cache = FALSE, + reference_week_day = 1 ), class = "fetch_args" ) @@ -59,7 +60,8 @@ test_that("fetch_args", { dry_run = TRUE, debug = TRUE, format_type = "classic", - refresh_cache = TRUE + refresh_cache = TRUE, + reference_week_day = 1 ), structure( list( @@ -72,7 +74,8 @@ test_that("fetch_args", { dry_run = TRUE, debug = TRUE, format_type = "classic", - refresh_cache = TRUE + refresh_cache = TRUE, + reference_week_day = 1 ), class = "fetch_args" ) From c9f19fd5f0a0bdf3ea0c7591414dc75c7b6d2378 Mon Sep 17 00:00:00 2001 From: dsweber2 Date: Sun, 16 Nov 2025 13:48:16 -0600 Subject: [PATCH 09/12] docs update, fix linting warning --- R/epidatacall.R | 16 ++++++++-------- R/model.R | 7 ++++++- man/fetch_args_list.Rd | 7 ++++++- man/parse_api_week.Rd | 4 +++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/R/epidatacall.R b/R/epidatacall.R index 21925c10..c01066ad 100644 --- a/R/epidatacall.R +++ b/R/epidatacall.R @@ -73,14 +73,9 @@ create_epidata_call <- function(endpoint, params, meta = NULL, ) } - # TODO: Something like this in the future? We set up the categories + # TODO: Check the categories in the future? We set up the categories # but we don't actually validate them yet? - # for (field in names(params)) { - # value <- params[[field]] - # if (meta[[field]]$type == "categorical") { - # checkmate::assert_subset(value, meta[[field]]$categories) - # } - # } + # use checkmate::assert_subset or something like that if (is.null(meta)) { meta <- list() @@ -285,7 +280,12 @@ fetch <- function(epidata_call, fetch_args = fetch_args_list()) { if (fetch_args$return_empty && length(response_content) == 0) { fetched <- tibble() } else { - fetched <- parse_data_frame(epidata_call, response_content, fetch_args$disable_date_parsing, fetch_args$reference_week_day) %>% as_tibble() + fetched <- parse_data_frame( + epidata_call, + response_content, + fetch_args$disable_date_parsing, + fetch_args$reference_week_day + ) %>% as_tibble() } }) diff --git a/R/model.R b/R/model.R index 0322cc75..4177fa88 100644 --- a/R/model.R +++ b/R/model.R @@ -224,7 +224,12 @@ parse_data_frame <- function(epidata_call, df, disable_date_parsing = FALSE, ref for (i in seq_len(length(meta))) { info <- meta[[i]] if (info$name %in% columns) { - df[[info$name]] <- parse_value(info, df[[info$name]], disable_date_parsing = disable_date_parsing, reference_week_day = reference_week_day) + df[[info$name]] <- parse_value( + info, + df[[info$name]], + disable_date_parsing = disable_date_parsing, + reference_week_day = reference_week_day + ) } } df diff --git a/man/fetch_args_list.Rd b/man/fetch_args_list.Rd index 1701288c..2275d421 100644 --- a/man/fetch_args_list.Rd +++ b/man/fetch_args_list.Rd @@ -16,7 +16,8 @@ fetch_args_list( dry_run = FALSE, debug = FALSE, format_type = c("json", "classic", "csv"), - refresh_cache = FALSE + refresh_cache = FALSE, + reference_week_day = 1 ) } \arguments{ @@ -52,6 +53,10 @@ csv; this is only used by \code{fetch_debug}, and by default is \code{"json"}} \item{refresh_cache}{if \code{TRUE}, ignore the cache, fetch the data from the API, and update the cache, if it is enabled} + +\item{reference_week_day}{the day of the week to use as the reference day +when parsing epiweeks to dates (happens if \code{disable_date_parsing} is \code{FALSE}) +Defaults to 1 Sunday (the first day of the week).} } \value{ A \code{fetch_args} object containing all the specified options diff --git a/man/parse_api_week.Rd b/man/parse_api_week.Rd index a877d210..1cf5e83f 100644 --- a/man/parse_api_week.Rd +++ b/man/parse_api_week.Rd @@ -4,10 +4,12 @@ \alias{parse_api_week} \title{parse_api_week converts an integer to a date} \usage{ -parse_api_week(value) +parse_api_week(value, reference_week_day = 1) } \arguments{ \item{value}{value to be converted to an epiweek} + +\item{reference_week_day}{the day of the week to use as the reference day. Defaults to Saturday.} } \value{ a date From 9b53a5e9352a0bd0d71b44d86ee8dc61f4bbff88 Mon Sep 17 00:00:00 2001 From: dsweber2 Date: Sun, 16 Nov 2025 14:17:24 -0600 Subject: [PATCH 10/12] test day of week, correct docs --- R/model.R | 2 +- man/parse_api_week.Rd | 2 +- tests/testthat/test-model.R | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/R/model.R b/R/model.R index 4177fa88..3de76db3 100644 --- a/R/model.R +++ b/R/model.R @@ -256,7 +256,7 @@ parse_api_date <- function(value) { #' parse_api_week converts an integer to a date #' @param value value to be converted to an epiweek -#' @param reference_week_day the day of the week to use as the reference day. Defaults to Saturday. +#' @param reference_week_day the day of the week to use as the reference day. Defaults to Sunday. #' @return a date #' @importFrom MMWRweek MMWRweek2Date #' @keywords internal diff --git a/man/parse_api_week.Rd b/man/parse_api_week.Rd index 1cf5e83f..9a78ef56 100644 --- a/man/parse_api_week.Rd +++ b/man/parse_api_week.Rd @@ -9,7 +9,7 @@ parse_api_week(value, reference_week_day = 1) \arguments{ \item{value}{value to be converted to an epiweek} -\item{reference_week_day}{the day of the week to use as the reference day. Defaults to Saturday.} +\item{reference_week_day}{the day of the week to use as the reference day. Defaults to Sunday.} } \value{ a date diff --git a/tests/testthat/test-model.R b/tests/testthat/test-model.R index 5444eb5e..10d2b48f 100644 --- a/tests/testthat/test-model.R +++ b/tests/testthat/test-model.R @@ -148,6 +148,12 @@ test_that("parse_api_date handles missing values appropriately", { expect_identical(parse_api_date(NA), as.Date(NA)) }) +test_that("parse_api_week returns the expected day of the week", { + expect_identical(parse_api_week(202005) %>% weekdays(), "Sunday") + expect_identical(parse_api_week(202005, 4) %>% weekdays(), "Wednesday") + expect_identical(parse_api_week(202005, 7) %>% weekdays(), "Saturday") +}) + test_that("date_to_epiweek accepts str and int input", { expect_identical(date_to_epiweek("20200101"), 202001) expect_identical(date_to_epiweek(20200101), 202001) From a22b060eb457ecea8e1362542d1e63e6499b8561 Mon Sep 17 00:00:00 2001 From: David Weber Date: Sun, 16 Nov 2025 12:45:33 -0800 Subject: [PATCH 11/12] Apply suggestion from @dsweber2 --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index c2630125..b3ebd162 100644 --- a/NEWS.md +++ b/NEWS.md @@ -13,7 +13,8 @@ - `pub_covidcast_meta` now returns `min_time`, `max_time`, `max_issue` as integers rather than Dates. Because these fields can mix YYYYMMDD and YYYYWW values, we recommend you parse them yourself. - + - add new fields for `flusurv` endpoint. + # epidatr 1.2.1 ## Patches From 63624a26b6b162d1bf52dd0f8bf7a2e7a5ec9428 Mon Sep 17 00:00:00 2001 From: David Weber Date: Sun, 16 Nov 2025 12:46:06 -0800 Subject: [PATCH 12/12] Apply suggestion from @dsweber2 --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index b3ebd162..193725ee 100644 --- a/NEWS.md +++ b/NEWS.md @@ -13,7 +13,7 @@ - `pub_covidcast_meta` now returns `min_time`, `max_time`, `max_issue` as integers rather than Dates. Because these fields can mix YYYYMMDD and YYYYWW values, we recommend you parse them yourself. - - add new fields for `flusurv` endpoint. +- add new fields for `flusurv` endpoint. # epidatr 1.2.1