From 190cbed757b62c8f1a52e61e7df65b280090467a Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:11:04 -0500 Subject: [PATCH 01/29] Write function to read a pins board from EDAV --- R/get_edav_pins_board.R | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 R/get_edav_pins_board.R diff --git a/R/get_edav_pins_board.R b/R/get_edav_pins_board.R new file mode 100644 index 00000000..bc0b476f --- /dev/null +++ b/R/get_edav_pins_board.R @@ -0,0 +1,24 @@ +#' Obtain a pins board from EDAV +#' +#' @description +#' This is a convenience function to obtain a pins board in EDAV. After the pins +#' board is loaded, several functions from the pins package are available to explore +#' the board. Please see the pins package [get started](https://pins.rstudio.com/articles/pins.html) page. +#' +#' @param board_loc `str` Location of the pins board in Azure. +#' @param azcontainer `azcontainer` An Azure container. +#' +#' @returns `pins board` An Azure pins board. +#' @export +#' +#' @examples +#' \dontrun{ +#' edav_board <- get_edav_pins_board() +#' } +get_edav_pins_board <- function(board_loc = "GID/PEB/SIR/pins_board", + azcontainer = get_azure_storage_connection()) { + + azboard <- pins::board_azure(azcontainer, board_loc) + + return(azboard) +} From 860ccce85114b3c291ae47c856914641371caf71 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:11:43 -0500 Subject: [PATCH 02/29] Ensure pins dependency is reflected on the description and package level import from --- DESCRIPTION | 3 ++- R/sirfunctions-package.R | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0357100a..2f11ddda 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -47,7 +47,8 @@ Imports: stats, rappdirs, rmarkdown, - grDevices + grDevices, + pins Suggests: arrow, qs2, diff --git a/R/sirfunctions-package.R b/R/sirfunctions-package.R index 41c0aa3b..f44b942c 100644 --- a/R/sirfunctions-package.R +++ b/R/sirfunctions-package.R @@ -44,6 +44,7 @@ #' @importFrom lifecycle deprecate_warn #' @importFrom lifecycle deprecated #' @importFrom lifecycle is_present +#' @importFrom pins board_azure #' @importFrom purrr map #' @importFrom rappdirs user_data_dir #' @importFrom readr read_csv From f2f41c95f3ea9528f8599c6b428718736cfb4086 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:11:58 -0500 Subject: [PATCH 03/29] Generate documentation for the new function --- NAMESPACE | 2 ++ man/get_edav_pins_board.Rd | 29 +++++++++++++++++++++++++++++ man/sirfunctions-package.Rd | 5 ++--- 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 man/get_edav_pins_board.Rd diff --git a/NAMESPACE b/NAMESPACE index 8dff4417..50753d80 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -104,6 +104,7 @@ export(get_cdc_childvaxview_data) export(get_constant) export(get_ctry_abbrev) export(get_diff_cols) +export(get_edav_pins_board) export(get_lab_date_col_missingness) export(get_lab_locs) export(get_ppt_template) @@ -171,6 +172,7 @@ importFrom(lifecycle,badge) importFrom(lifecycle,deprecate_warn) importFrom(lifecycle,deprecated) importFrom(lifecycle,is_present) +importFrom(pins,board_azure) importFrom(purrr,map) importFrom(rappdirs,user_data_dir) importFrom(readr,read_csv) diff --git a/man/get_edav_pins_board.Rd b/man/get_edav_pins_board.Rd new file mode 100644 index 00000000..f41c080e --- /dev/null +++ b/man/get_edav_pins_board.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_edav_pins_board.R +\name{get_edav_pins_board} +\alias{get_edav_pins_board} +\title{Obtain a pins board from EDAV} +\usage{ +get_edav_pins_board( + board_loc = "GID/PEB/SIR/pins_board", + azcontainer = get_azure_storage_connection() +) +} +\arguments{ +\item{board_loc}{\code{str} Location of the pins board in Azure.} + +\item{azcontainer}{\code{azcontainer} An Azure container.} +} +\value{ +\verb{pins board} An Azure pins board. +} +\description{ +This is a convenience function to obtain a pins board in EDAV. After the pins +board is loaded, several functions from the pins package are available to explore +the board. Please see the pins package \href{https://pins.rstudio.com/articles/pins.html}{get started} page. +} +\examples{ +\dontrun{ +edav_board <- get_edav_pins_board() +} +} diff --git a/man/sirfunctions-package.Rd b/man/sirfunctions-package.Rd index 8201e132..5bbb787d 100644 --- a/man/sirfunctions-package.Rd +++ b/man/sirfunctions-package.Rd @@ -13,17 +13,16 @@ The sirfunctions package contains key functions used by the Surveillance, Innova \seealso{ Useful links: \itemize{ - \item \url{https://github.com/nish-kishore/sirfunctions} \item \url{https://cdcgov.github.io/sirfunctions/} } } \author{ -\strong{Maintainer}: Nishant Kishore \email{ynm2@cdc.gov} (\href{https://orcid.org/0000-0003-0408-2747}{ORCID}) +\strong{Maintainer}: Mervin Keith Cuadera \email{xrg9@cdc.gov} (\href{https://orcid.org/0000-0003-4898-2659}{ORCID}) Authors: \itemize{ - \item Mervin Keith Cuadera \email{xrg9@cdc.gov} (\href{https://orcid.org/0000-0003-4898-2659}{ORCID}) + \item Nishant Kishore \email{ynm2@cdc.gov} (\href{https://orcid.org/0000-0003-0408-2747}{ORCID}) \item Nicholas Heaghney \email{uic3@cdc.gov} \item Elizabeth Krow-Lucal \email{yxn9@cdc.gov} \item Smita Chavan \email{wsy2@cdc.gov} From 94edab7f5b05b77b5e7fbc8ef0ab6d246453e2a4 Mon Sep 17 00:00:00 2001 From: chadhunt2 Date: Fri, 16 Jan 2026 13:42:42 -0500 Subject: [PATCH 04/29] Update dr.main.functions.R Updated to allow countries with commas in the name to pass as one country. --- R/dr.main.functions.R | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/R/dr.main.functions.R b/R/dr.main.functions.R index a29f74ca..dfd521f6 100644 --- a/R/dr.main.functions.R +++ b/R/dr.main.functions.R @@ -251,9 +251,14 @@ update_data <- polis_folder = .polis_folder, use_edav = .use_edav ) - country_name <- stringr::str_replace_all(country_name, ", ", ",") |> - stringr::str_split(",") |> - unlist() + country_name <- stringr::str_squish(country_name) + + if (length(country_name) == 1) { + + if (stringr::str_detect(country_name, "\\|")) { + country_name <- stringr::str_split(country_name, "\\s*\\|\\s*") |> unlist() + } + } country_data <- sirfunctions::extract_country_data(country_name, raw_data) readr::write_rds(country_data, path_to_save) message(paste0("Data saved at:\n", dr_data_path)) @@ -521,19 +526,19 @@ init_dr <- if (is.null(end_date)) { end_date <- Sys.Date() - lubridate::weeks(6) - assign("end_date", end_date, envir = .GlobalEnv) + end_date <<- end_date } else { end_date <- lubridate::as_date(end_date) - assign("end_date", end_date, envir = .GlobalEnv) + end_date <<- end_date } if (is.null(start_date)) { start_date <- (end_date - lubridate::years(3)) |> lubridate::floor_date("year") - assign("start_date", start_date, envir = .GlobalEnv) + start_date <<- start_date } else { start_date <- lubridate::as_date(start_date) - assign("start_date", start_date, envir = .GlobalEnv) + start_date <<- start_date } year <- lubridate::year(end_date) @@ -641,8 +646,7 @@ init_dr <- (stringr::str_detect(data_folder_files, "_lab_data_") |> sum() != 0)) { cli::cli_alert_info("Loading cached lab data") lab_files <- data_folder_files[stringr::str_detect(data_folder_files, "lab_data")] - lab_data <- readRDS(file.path(data_path, lab_files[1])) - assign("lab_data", lab_data, envir = .GlobalEnv) + lab_data <<- readRDS(file.path(data_path, lab_files[1])) Sys.setenv(DR_LAB_PATH = file.path(data_path, lab_files[1])) } @@ -670,12 +674,8 @@ init_dr <- start_date, end_date, country_name ) - end_date <- lubridate::as_date(end_date) - assign("end_date", end_date, envir = .GlobalEnv) - - start_date <- lubridate::as_date(start_date) - assign("start_date", start_date, envir = .GlobalEnv) - + end_date <<- lubridate::as_date(end_date) + start_date <<- lubridate::as_date(start_date) # Setting environmental variables Sys.setenv(DR_PATH = file.path(country_dir_path)) Sys.setenv(DR_DATA_PATH = file.path(country_dir_path, "data")) @@ -705,7 +705,7 @@ init_dr <- cli::cli_process_done() } - assign("ctry.data", country_data, envir = .GlobalEnv) + ctry.data <<- country_data cli::cli_alert_success("ctry.data loaded to the global environment") cli::cli_alert_success("Desk review analysis set up complete.") cli::cli_text(paste0("Click here to access the template file: ", From 73c3c151d5aa5624fa532be45e89cf7f825e8a01 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:30:21 -0500 Subject: [PATCH 05/29] get rid of <<- assignments <<- will be a warning when devtools::check() runs. assign() is a way to circumvent that --- R/dr.main.functions.R | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/R/dr.main.functions.R b/R/dr.main.functions.R index dfd521f6..88d1007d 100644 --- a/R/dr.main.functions.R +++ b/R/dr.main.functions.R @@ -526,19 +526,19 @@ init_dr <- if (is.null(end_date)) { end_date <- Sys.Date() - lubridate::weeks(6) - end_date <<- end_date + assign("end_date", end_date, envir = .GlobalEnv) } else { end_date <- lubridate::as_date(end_date) - end_date <<- end_date + assign("end_date", end_date, envir = .GlobalEnv) } if (is.null(start_date)) { start_date <- (end_date - lubridate::years(3)) |> lubridate::floor_date("year") - start_date <<- start_date + assign("start_date", start_date, envir = .GlobalEnv) } else { start_date <- lubridate::as_date(start_date) - start_date <<- start_date + assign("start_date", start_date, envir = .GlobalEnv) } year <- lubridate::year(end_date) @@ -646,7 +646,8 @@ init_dr <- (stringr::str_detect(data_folder_files, "_lab_data_") |> sum() != 0)) { cli::cli_alert_info("Loading cached lab data") lab_files <- data_folder_files[stringr::str_detect(data_folder_files, "lab_data")] - lab_data <<- readRDS(file.path(data_path, lab_files[1])) + lab_data <- readRDS(file.path(data_path, lab_files[1])) + assign("lab_data", lab_data, envir = .GlobalEnv) Sys.setenv(DR_LAB_PATH = file.path(data_path, lab_files[1])) } @@ -674,8 +675,12 @@ init_dr <- start_date, end_date, country_name ) - end_date <<- lubridate::as_date(end_date) - start_date <<- lubridate::as_date(start_date) + end_date <- lubridate::as_date(end_date) + assign("end_date", end_date, envir = .GlobalEnv) + + start_date <- lubridate::as_date(start_date) + assign("start_date", start_date, envir = .GlobalEnv) + # Setting environmental variables Sys.setenv(DR_PATH = file.path(country_dir_path)) Sys.setenv(DR_DATA_PATH = file.path(country_dir_path, "data")) @@ -705,7 +710,7 @@ init_dr <- cli::cli_process_done() } - ctry.data <<- country_data + assign("ctry.data", country_data, envir = .GlobalEnv) cli::cli_alert_success("ctry.data loaded to the global environment") cli::cli_alert_success("Desk review analysis set up complete.") cli::cli_text(paste0("Click here to access the template file: ", From 7efac946ee62a043a4feb413df5a3980520e6e33 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:41:23 -0500 Subject: [PATCH 06/29] add doc for init_dr --- R/dr.main.functions.R | 3 ++- man/init_dr.Rd | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/R/dr.main.functions.R b/R/dr.main.functions.R index 88d1007d..420bc1bb 100644 --- a/R/dr.main.functions.R +++ b/R/dr.main.functions.R @@ -484,7 +484,7 @@ fetch_dr_data <- function(country, year, local_dr_repo) { #' environmental variables (i.e., [Sys.getenv()], where values for `x` related to #' the desk review is prefixed with `"DR"`) . The function only supports running one country at a time. #' -#' @param country_name `str` Name of the country. +#' @param country_name `str` Name of the country. You may pass multiple countries as a vector of strings. #' @param start_date `str` Start date of the desk review. If `NULL`, defaults to four years #' from when the function was ran on January 1st. #' @param end_date `str` End date of the desk review. If `NULL`, defaults to six weeks from when @@ -505,6 +505,7 @@ fetch_dr_data <- function(country, year, local_dr_repo) { #' \dontrun{ #' ctry.data <- init_dr("algeria", source = F) # Sets up folder in the current working directory #' ctry.data <- init_dr("algeria", branch = "dev") # Use functions from the dev branch +#' ctry.data <- init_dr(c("algeria", "nigeria"), source = F) # Multiple countries. #' } #' #' @export diff --git a/man/init_dr.Rd b/man/init_dr.Rd index 14ebb26e..b411f3a7 100644 --- a/man/init_dr.Rd +++ b/man/init_dr.Rd @@ -20,7 +20,7 @@ init_dr( ) } \arguments{ -\item{country_name}{\code{str} Name of the country.} +\item{country_name}{\code{str} Name of the country. You may pass multiple countries as a vector of strings.} \item{start_date}{\code{str} Start date of the desk review. If \code{NULL}, defaults to four years from when the function was ran on January 1st.} @@ -63,6 +63,7 @@ the desk review is prefixed with \code{"DR"}) . The function only supports runni \dontrun{ ctry.data <- init_dr("algeria", source = F) # Sets up folder in the current working directory ctry.data <- init_dr("algeria", branch = "dev") # Use functions from the dev branch +ctry.data <- init_dr(c("algeria", "nigeria"), source = F) # Multiple countries. } } From fc270c569704be248532e4c0c07c5eed701ea905 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:45:18 -0500 Subject: [PATCH 07/29] fix spatial_data to spatial.data --- R/dal.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/dal.R b/R/dal.R index 1016c22f..e254a9f4 100644 --- a/R/dal.R +++ b/R/dal.R @@ -3655,7 +3655,7 @@ force_load_polio_data_cache <- function(attach.spatial.data, output_format = ".r # Check if spatial data exists in the cache if (sirfunctions_io("exists.file", NULL, spatial_data_path, edav = FALSE)) { cli::cli_alert_info("Loading spatial data from cache") - spatial_data <- sirfunctions_io("read", NULL, spatial_data_path, edav = FALSE) + spatial.data <- sirfunctions_io("read", NULL, spatial_data_path, edav = FALSE) raw.data$global.ctry <- spatial.data$global.ctry raw.data$global.prov <- spatial.data$global.prov From 312191d9d6f445b486c10a799e355f7a4afa5914 Mon Sep 17 00:00:00 2001 From: chadhunt2 Date: Tue, 10 Feb 2026 16:00:28 -0500 Subject: [PATCH 08/29] Update dr.main.functions.R --- R/dr.main.functions.R | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/R/dr.main.functions.R b/R/dr.main.functions.R index 420bc1bb..3be9875a 100644 --- a/R/dr.main.functions.R +++ b/R/dr.main.functions.R @@ -251,14 +251,8 @@ update_data <- polis_folder = .polis_folder, use_edav = .use_edav ) - country_name <- stringr::str_squish(country_name) - if (length(country_name) == 1) { - if (stringr::str_detect(country_name, "\\|")) { - country_name <- stringr::str_split(country_name, "\\s*\\|\\s*") |> unlist() - } - } country_data <- sirfunctions::extract_country_data(country_name, raw_data) readr::write_rds(country_data, path_to_save) message(paste0("Data saved at:\n", dr_data_path)) @@ -546,13 +540,14 @@ init_dr <- # Set up local directory for storing for data and metadata df_name <- - stringr::str_c(stringr::str_to_lower(country_name)) + stringr::str_c(stringr::str_to_lower(country_name)) |> + paste(collapse = ", ") # Relative path of where data and metadata is stored dr_path <- file.path( local_dr_folder, - stringr::str_to_lower(country_name), + stringr::str_to_lower(df_name), year ) From 9f0f212500f8a72e225a1f89296090894501601c Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:42:38 -0400 Subject: [PATCH 09/29] update geo names to reflect new pop file names --- R/kpi.table.functions.R | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/R/kpi.table.functions.R b/R/kpi.table.functions.R index 4c1b311c..535dbd09 100644 --- a/R/kpi.table.functions.R +++ b/R/kpi.table.functions.R @@ -718,7 +718,7 @@ generate_c1_table <- function(raw_data, start_date, end_date, # Calculate meeting indicators dist_lookup_table <- raw_data$dist.pop |> - dplyr::select(ctry = ADM0_NAME, prov = ADM1_NAME, dist = ADM2_NAME, adm2guid) |> + dplyr::select(ctry, prov, dist, adm2guid) |> dplyr::distinct() # Flag any inconsistent GUIDs to say any calculations are invalid @@ -1012,9 +1012,6 @@ generate_c2_table <- function(afp_data, pop_data, start_date, end_date, cdc.classification.all = "cdc.class" ) pop_data <- dplyr::rename_with(pop_data, recode, - ADM0_NAME = "ctry", - ADM1_NAME = "prov", - ADM2_NAME = "dist", ADM0_GUID = "adm0guid", u15pop.prov = "u15pop", WHO_REGION = "who_region" From b4d76f8d6383eb8fb1532fb3c445cec80421e30c Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:45:20 -0400 Subject: [PATCH 10/29] update who region col name to reflect name in the pop data --- R/kpi.table.functions.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/kpi.table.functions.R b/R/kpi.table.functions.R index 535dbd09..b3c33bb1 100644 --- a/R/kpi.table.functions.R +++ b/R/kpi.table.functions.R @@ -1014,7 +1014,7 @@ generate_c2_table <- function(afp_data, pop_data, start_date, end_date, pop_data <- dplyr::rename_with(pop_data, recode, ADM0_GUID = "adm0guid", u15pop.prov = "u15pop", - WHO_REGION = "who_region" + WHO_REGION = "who.region" ) cli::cli_progress_update() @@ -1375,13 +1375,13 @@ generate_c2_table <- function(afp_data, pop_data, start_date, end_date, # Add region and risk levels region_lookup_table <- pop_data |> - dplyr::select(dplyr::any_of(c("ctry", "prov", "dist", "who_region"))) |> + dplyr::select(dplyr::any_of(c("ctry", "prov", "dist", "who.region"))) |> dplyr::distinct() results <- add_risk_category(results, risk_table) |> dplyr::left_join(region_lookup_table) |> dplyr::select(-Region) |> - dplyr::rename(Region = who_region) |> + dplyr::rename(Region = who.region) |> tidyr::replace_na(list(`SG Priority Level` = "LOW")) cli::cli_progress_done() From 8bd6fe2560c8cb95aded7f9bc5f5512d1f2c99f1 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:16:22 -0400 Subject: [PATCH 11/29] manual edit to reflect indonesia region change in 2025 --- R/kpi.table.functions.R | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/R/kpi.table.functions.R b/R/kpi.table.functions.R index b3c33bb1..e0b4de35 100644 --- a/R/kpi.table.functions.R +++ b/R/kpi.table.functions.R @@ -886,6 +886,11 @@ generate_c1_table <- function(raw_data, start_date, end_date, dplyr::mutate(dplyr::across(dplyr::ends_with("label"), \(x) tidyr::replace_na(x, "0/0"))) |> dplyr::rename(Region = whoregion) + # Manual edit to reflect Indonesia WPRO change in May 23, 2025 + combine <- combine |> + dplyr::mutate(Region = ifelse(ctry == "INDONESIA" & + analysis_year_start >= lubridate::as_date("2025-05-23"), "WPRO", Region)) + cli::cli_progress_update() cli::cli_progress_done() @@ -1384,6 +1389,11 @@ generate_c2_table <- function(afp_data, pop_data, start_date, end_date, dplyr::rename(Region = who.region) |> tidyr::replace_na(list(`SG Priority Level` = "LOW")) + # Manual edit to reflect Indonesia WPRO change in May 23, 2025 + results <- results |> + dplyr::mutate(Region = ifelse(ctry == "INDONESIA" & + analysis_year_start >= lubridate::as_date("2025-05-23"), "WPRO", Region)) + cli::cli_progress_done() return(results) From 06d497008c3a4c0c10cdedbdb28c7bebde1a0d0c Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:27:29 -0400 Subject: [PATCH 12/29] reflect seq lab changes in 2025 when generating the positives timeliness data --- R/kpi.table.functions.R | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/R/kpi.table.functions.R b/R/kpi.table.functions.R index e0b4de35..c1127b82 100644 --- a/R/kpi.table.functions.R +++ b/R/kpi.table.functions.R @@ -158,6 +158,26 @@ generate_pos_timeliness <- function(raw_data, start_date, end_date, pos <- add_rolling_years(pos, start_date, end_date, "dateonset") pos <- add_seq_capacity(pos, ctry_col = "place.admin.0", lab_locs) + # Manual edit based on changes to the sequencing lab list in Feb 2025 + # There is no collection date, so will use dateonset to classify + pos <- pos |> + mutate(seq.lab = case_when( + seq.lab == "CDC-Atlanta" & dateonset >= as_date("2025-02-01") & culture.itd.lab == "Cameroon" ~ "NICD-South Africa", + seq.lab == "CDC-Atlanta" & dateonset >= as_date("2025-02-01") & culture.itd.lab == "ETHIOPIA/ KEMRI-Kenya" ~ "UVRI-Uganda", + seq.lab == "CDC-Atlanta" & dateonset >= as_date("2025-02-01") & culture.itd.lab %in% c("Ibadan-Nigeria, Maiduguri-Nigeria", "Nigeria") ~ "Ibadan-Nigeria", + seq.lab == "CDC-Atlanta" & dateonset >= as_date("2025-02-01") & culture.itd.lab == "KEMRI-Kenya" ~ "UVRI-Uganda", + place.admin.0 == "UGANDA" & dateonset >= as_date("2025-02-01") ~ "UVRI-Uganda", + seq.lab == "CDC-Atlanta" & dateonset >= as_date("2025-02-01") & culture.itd.lab == "Senegal" ~ "NICD-South Africa", + seq.lab == "CDC-Atlanta" & dateonset >= as_date("2025-02-01") & culture.itd.lab == "Varied (KEMRI-Kenya/ Oman/ Jordan)" ~ "Varied (UVRI/ Oman/ Jordan)", + .default = seq.lab + )) |> + mutate(seq.cat = case_when( + dateonset >= as_date("2025-02-01") & culture.itd.lab %in% c("Ibadan-Nigeria, Maiduguri-Nigeria", "Nigeria") & seq.lab == "Ibadan-Nigeria" ~ "Not shipped for sequencing", + place.admin.0 == "UGANDA" & dateonset >= as_date("2025-02-01") ~ "Not shipped for sequencing", + .default = seq.cat + )) |> + mutate(seq.capacity = if_else(place.admin.0 %in% c("NIGERIA", "UGANDA") & dateonset >= as_date("2025-02-01"), "yes", seq.capacity)) + pos_summary <- pos |> dplyr::mutate( ontonothq = as.numeric(lubridate::as_date(.data$datenotificationtohq) - From 4009622dc2b78154044f557d5a36c1d353522663 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:30:37 -0400 Subject: [PATCH 13/29] add missing param for timely det violin --- R/kpi.figure.functions.R | 9 ++++++++- man/generate_timely_det_violin.Rd | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/R/kpi.figure.functions.R b/R/kpi.figure.functions.R index 4e82d4e3..624816eb 100644 --- a/R/kpi.figure.functions.R +++ b/R/kpi.figure.functions.R @@ -736,6 +736,11 @@ generate_kpi_violin <- function( #' @param rolling `logical` Using rolling periods or year-to-year? Defaults to `TRUE`. #' @param who_region `list` Regions to display. Defaults to `NULL`, which shows #' all of the regions. +#' @param risk_table `tibble` Priority level of each country. Defaults to `NULL`, +#' which will download the information directly from EDAV. +#' @param lab_locs `tibble` Summary of the sequencing capacities of labs. +#' Output of [get_lab_locs()]. Defaults to `NULL`, which will download the information +#' directly from EDAV. #' @param output_path `str` Where to output the figure to. #' @param y_max `num` The maximum y-axis value. #' @@ -752,12 +757,14 @@ generate_timely_det_violin <- function(raw_data, priority_level = c("HIGH", "MEDIUM", "LOW (WATCHLIST)", "LOW"), who_region = NULL, rolling = TRUE, + risk_table = NULL, + lab_locs = NULL, output_path = Sys.getenv("KPI_FIGURES"), y_max = 250) { start_date <- lubridate::as_date(start_date) end_date <- lubridate::as_date(end_date) - pos <- generate_pos_timeliness(raw_data, start_date, end_date) + pos <- generate_pos_timeliness(raw_data, start_date, end_date, risk_table, lab_locs) ctry_abbrev <- get_ctry_abbrev(raw_data$afp) color.risk.cat <- c( "HIGH" = "#d73027", diff --git a/man/generate_timely_det_violin.Rd b/man/generate_timely_det_violin.Rd index 69eba8ba..a78d0cf5 100644 --- a/man/generate_timely_det_violin.Rd +++ b/man/generate_timely_det_violin.Rd @@ -11,6 +11,8 @@ generate_timely_det_violin( priority_level = c("HIGH", "MEDIUM", "LOW (WATCHLIST)", "LOW"), who_region = NULL, rolling = TRUE, + risk_table = NULL, + lab_locs = NULL, output_path = Sys.getenv("KPI_FIGURES"), y_max = 250 ) @@ -30,6 +32,13 @@ all of the regions.} \item{rolling}{\code{logical} Using rolling periods or year-to-year? Defaults to \code{TRUE}.} +\item{risk_table}{\code{tibble} Priority level of each country. Defaults to \code{NULL}, +which will download the information directly from EDAV.} + +\item{lab_locs}{\code{tibble} Summary of the sequencing capacities of labs. +Output of \code{\link[=get_lab_locs]{get_lab_locs()}}. Defaults to \code{NULL}, which will download the information +directly from EDAV.} + \item{output_path}{\code{str} Where to output the figure to.} \item{y_max}{\code{num} The maximum y-axis value.} From 0585544d31e12f279ff3acc5d7ba83f74fa66686 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:40:29 -0400 Subject: [PATCH 14/29] use get_region inside get_ctry_abbrev for consistency --- R/kpi.main.functions.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/R/kpi.main.functions.R b/R/kpi.main.functions.R index 63b638e3..156579a4 100644 --- a/R/kpi.main.functions.R +++ b/R/kpi.main.functions.R @@ -196,7 +196,7 @@ get_ctry_abbrev <- function(afp_data) { .data$whoregion != "AFRO" ~ .data$country.iso3 ) ) |> - dplyr::select("ctry.short", "place.admin.0", "whoregion") |> + dplyr::select("ctry.short", "place.admin.0") |> # fixing bad abbreviation in Gabon and turning all to upper case to eliminate dupes dplyr::mutate( ctry.short = ifelse(.data$place.admin.0 == "GABON" & .data$ctry.short == "BUU", @@ -224,6 +224,9 @@ get_ctry_abbrev <- function(afp_data) { ctry.short == "IND" & place.admin.0 == "INDONESIA" ~ "IDN", .default = ctry.short )) + + ctry_abbrev <- ctry_abbrev |> + dplyr::mutate(whoregion = get_region(place.admin.0)) summarize_ctry_abbrev <- ctry_abbrev |> dplyr::group_by(place.admin.0) |> From 559416b1690daed35c110c8efe835e82d0ae5cea Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:40:52 -0400 Subject: [PATCH 15/29] manually correct regions --- R/kpi.figure.functions.R | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/R/kpi.figure.functions.R b/R/kpi.figure.functions.R index 624816eb..a0846565 100644 --- a/R/kpi.figure.functions.R +++ b/R/kpi.figure.functions.R @@ -781,10 +781,18 @@ generate_timely_det_violin <- function(raw_data, } pos_filtered <- pos |> + dplyr::mutate(whoregion = get_region(place.admin.0)) |> + # Remove other who region columns because it's confusing + dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> dplyr::filter(.data$`SG Priority Level` %in% priority_level, .data$whoregion %in% who_region) |> dplyr::left_join(ctry_abbrev, by = c("place.admin.0", "whoregion")) |> + # Manually change the region of Indonesia based on change + # since get_region() defaults to WPRO now, we have to do the opposite + # to assign Indonesia samples to SEARO prior to May 23, 2025 + dplyr::mutate(whoregion = ifelse(place.admin.0 == "INDONESIA" & + analysis_year_start < lubridate::as_date("2025-05-23"), "SEARO", whoregion)) |> dplyr::mutate(seq_lab = case_when( .data$seq.capacity == "no" ~ "No sequencing capacity", .data$seq.capacity == "yes" ~ "Sequencing capacity" @@ -796,12 +804,12 @@ generate_timely_det_violin <- function(raw_data, ) if (rolling) { - facets <- ggh4x::facet_nested(rolling_period~seq_lab+who.region, + facets <- ggh4x::facet_nested(rolling_period~seq_lab+whoregion, scales = "free", space = "free", labeller = ggplot2::label_wrap_gen(13), switch = "y") } else { - facets <- ggh4x::facet_nested(year~seq_lab+who.region, + facets <- ggh4x::facet_nested(year~seq_lab+whoregion, scales = "free", space = "free", labeller = ggplot2::label_wrap_gen(13), switch = "y") From 71d2280a332e4a8eac0073a95acb8dc41cedf2ea Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:13:26 -0400 Subject: [PATCH 16/29] fix regions for the lab violin plots --- R/kpi.figure.functions.R | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/R/kpi.figure.functions.R b/R/kpi.figure.functions.R index a0846565..70fbd20b 100644 --- a/R/kpi.figure.functions.R +++ b/R/kpi.figure.functions.R @@ -529,8 +529,11 @@ generate_kpi_npafp_bar <- function(c1, afp_data, ctry_abbrev <- get_ctry_abbrev(afp_data) priority_ctry <- c1 |> dplyr::left_join(ctry_abbrev, - by = c("ctry" = "place.admin.0", "Region" = "whoregion")) |> + by = c("ctry" = "place.admin.0")) |> dplyr::filter(.data$`SG Priority Level` == "HIGH") |> + # Remove whoregion from ctry_abbrev and use Region since + # c1 already accounts for Indonesia change and it's not used here + dplyr::select(-whoregion) |> dplyr::mutate(prop_met_npafp = .data$prop_met_npafp) bar_plot <- generate_kpi_barchart(priority_ctry, @@ -587,7 +590,10 @@ generate_kpi_evdetect_bar <- function(c1, afp_data, ctry_abbrev <- get_ctry_abbrev(afp_data) priority_ctry <- c1 |> dplyr::left_join(ctry_abbrev, - by = c("ctry" = "place.admin.0", "Region" = "whoregion")) |> + by = c("ctry" = "place.admin.0")) |> + # Remove whoregion from ctry_abbrev and use Region since + # c1 already accounts for Indonesia change and it's not used here + dplyr::select(-whoregion) |> dplyr::mutate(prop_met_ev = .data$prop_met_ev) if (!is.null(who_region)) { @@ -635,7 +641,10 @@ generate_kpi_stoolad_bar <- function(c1, afp_data, ctry_abbrev <- get_ctry_abbrev(afp_data) priority_ctry <- c1 |> dplyr::left_join(ctry_abbrev, - by = c("ctry" = "place.admin.0", "Region" = "whoregion")) |> + by = c("ctry" = "place.admin.0")) |> + # Remove whoregion from ctry_abbrev and use Region since + # c1 already accounts for Indonesia change and it's not used here + dplyr::select(-whoregion) |> dplyr::filter(.data$`SG Priority Level` == "HIGH") |> dplyr::mutate(prop_met_stool = .data$prop_met_stool) @@ -932,6 +941,9 @@ generate_timely_ship_violin <- function(afp_data, afp_filtered <- afp_data |> dplyr::filter(.data$`SG Priority Level` %in% priority_level, .data$whoregion %in% who_region) |> + dplyr::mutate(whoregion = get_region(place.admin.0)) |> + # Remove other who region columns because it's confusing + dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> dplyr::left_join(ctry_abbrev, by = c("ctry" = "place.admin.0", "whoregion")) |> dplyr::mutate( @@ -1053,6 +1065,9 @@ generate_lab_culture_violin <- function(lab_data, afp_data, lab_filtered <- lab_data |> dplyr::left_join(ctry_abbrev, by = c("country" = "place.admin.0", "whoregion")) |> + dplyr::mutate(whoregion = get_region(country)) |> + # Remove other who region columns because it's confusing + dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) @@ -1139,6 +1154,9 @@ generate_lab_itd_violin <- function(lab_data, afp_data, lab_filtered <- lab_data |> dplyr::left_join(ctry_abbrev, by = c("country" = "place.admin.0", "whoregion")) |> + dplyr::mutate(whoregion = get_region(country)) |> + # Remove other who region columns because it's confusing + dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) @@ -1225,6 +1243,9 @@ generate_lab_seqship_violin <- function(lab_data, afp_data, lab_filtered <- lab_data |> dplyr::left_join(ctry_abbrev, by = c("country" = "place.admin.0", "whoregion")) |> + dplyr::mutate(whoregion = get_region(country)) |> + # Remove other who region columns because it's confusing + dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) @@ -1312,6 +1333,9 @@ generate_lab_seqres_violin <- function(lab_data, afp_data, lab_filtered <- lab_data |> dplyr::left_join(ctry_abbrev, by = c("country" = "place.admin.0", "whoregion")) |> + dplyr::mutate(whoregion = get_region(country)) |> + # Remove other who region columns because it's confusing + dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) @@ -1425,6 +1449,9 @@ generate_lab_itdres_seqres_violin <- function(lab_data, afp_data, lab_filtered <- lab_data |> dplyr::left_join(ctry_abbrev, by = c("country" = "place.admin.0", "whoregion")) |> + dplyr::mutate(whoregion = get_region(country)) |> + # Remove other who region columns because it's confusing + dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) From 6b9accc99ac437483e0a33ea4791d852d06c4392 Mon Sep 17 00:00:00 2001 From: Mervin Keith Cuadera <40894971+mcuadera@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:31:44 -0400 Subject: [PATCH 17/29] get rid of incorrectly assigned dates some date collections are more than a year ahead of the date onset --- R/kpi.figure.functions.R | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/R/kpi.figure.functions.R b/R/kpi.figure.functions.R index 70fbd20b..1e8fb7ba 100644 --- a/R/kpi.figure.functions.R +++ b/R/kpi.figure.functions.R @@ -1072,6 +1072,9 @@ generate_lab_culture_violin <- function(lab_data, afp_data, dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) ) |> + # Filter out "incorrect" lab data where the date onset doesn't match + # the stool collection date + dplyr::filter(dplyr::between(DateStoolCollected, analysis_year_start, analysis_year_end)) |> dplyr::filter(.data$`SG Priority Level` %in% priority_level, !is.na(.data$culture.itd.cat), dplyr::between(DateStoolCollected, start_date, end_date)) |> @@ -1158,6 +1161,9 @@ generate_lab_itd_violin <- function(lab_data, afp_data, # Remove other who region columns because it's confusing dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> + # Filter out "incorrect" lab data where the date onset doesn't match + # the stool collection date + dplyr::filter(dplyr::between(DateStoolCollected, analysis_year_start, analysis_year_end)) |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) ) |> @@ -1247,6 +1253,9 @@ generate_lab_seqship_violin <- function(lab_data, afp_data, # Remove other who region columns because it's confusing dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> + # Filter out "incorrect" lab data where the date onset doesn't match + # the stool collection date + dplyr::filter(dplyr::between(DateStoolCollected, analysis_year_start, analysis_year_end)) |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) ) |> @@ -1337,6 +1346,9 @@ generate_lab_seqres_violin <- function(lab_data, afp_data, # Remove other who region columns because it's confusing dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> + # Filter out "incorrect" lab data where the date onset doesn't match + # the stool collection date + dplyr::filter(dplyr::between(DateStoolCollected, analysis_year_start, analysis_year_end)) |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) ) |> @@ -1453,6 +1465,9 @@ generate_lab_itdres_seqres_violin <- function(lab_data, afp_data, # Remove other who region columns because it's confusing dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> + # Filter out "incorrect" lab data where the date onset doesn't match + # the stool collection date + dplyr::filter(dplyr::between(DateStoolCollected, analysis_year_start, analysis_year_end)) |> dplyr::mutate( year = lubridate::year(.data$DateStoolCollected) ) |> From 6a6499941e574da13b80f84e7a92c76025f8768a Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Mon, 13 Apr 2026 12:27:00 -0400 Subject: [PATCH 18/29] Update kpi.table.functions.R Removed the pop data renaming code because it was not doing anything with the new names in pop dataset. --- R/kpi.table.functions.R | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/R/kpi.table.functions.R b/R/kpi.table.functions.R index c1127b82..6bf768b5 100644 --- a/R/kpi.table.functions.R +++ b/R/kpi.table.functions.R @@ -908,7 +908,7 @@ generate_c1_table <- function(raw_data, start_date, end_date, # Manual edit to reflect Indonesia WPRO change in May 23, 2025 combine <- combine |> - dplyr::mutate(Region = ifelse(ctry == "INDONESIA" & + dplyr::mutate(Region = ifelse(ctry == "INDONESIA" & analysis_year_start >= lubridate::as_date("2025-05-23"), "WPRO", Region)) cli::cli_progress_update() @@ -1036,11 +1036,7 @@ generate_c2_table <- function(afp_data, pop_data, start_date, end_date, dateinvest = "date.invest", cdc.classification.all = "cdc.class" ) - pop_data <- dplyr::rename_with(pop_data, recode, - ADM0_GUID = "adm0guid", - u15pop.prov = "u15pop", - WHO_REGION = "who.region" - ) + cli::cli_progress_update() # Add required columns @@ -1411,7 +1407,7 @@ generate_c2_table <- function(afp_data, pop_data, start_date, end_date, # Manual edit to reflect Indonesia WPRO change in May 23, 2025 results <- results |> - dplyr::mutate(Region = ifelse(ctry == "INDONESIA" & + dplyr::mutate(Region = ifelse(ctry == "INDONESIA" & analysis_year_start >= lubridate::as_date("2025-05-23"), "WPRO", Region)) cli::cli_progress_done() From 657ae513b3fc50c8da932f347c687bda33b432a8 Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Mon, 13 Apr 2026 14:33:23 -0400 Subject: [PATCH 19/29] corrections for PR 430 - correct region lookup to ensure WPRO for Indonesia - correctly get_ctry_abbrev function to address Paraguay --- R/kpi.main.functions.R | 7 ++++--- R/kpi.table.functions.R | 9 ++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/R/kpi.main.functions.R b/R/kpi.main.functions.R index 156579a4..aaf68d2b 100644 --- a/R/kpi.main.functions.R +++ b/R/kpi.main.functions.R @@ -218,13 +218,14 @@ get_ctry_abbrev <- function(afp_data) { !(place.admin.0 == "GHANA" & ctry.short == "TOG"), !(place.admin.0 == "INDIA" & ctry.short == "PAK"), !(place.admin.0 == "PAKISTAN" & ctry.short == "IND"), - !(place.admin.0 == "ZAMBIA" & ctry.short == "TAN") + !(place.admin.0 == "ZAMBIA" & ctry.short == "TAN"), + !(place.admin.0 == "PARAGUAY" & ctry.short == "TTO") ) |> dplyr::mutate(ctry.short = dplyr::case_when( ctry.short == "IND" & place.admin.0 == "INDONESIA" ~ "IDN", .default = ctry.short )) - + ctry_abbrev <- ctry_abbrev |> dplyr::mutate(whoregion = get_region(place.admin.0)) @@ -235,7 +236,7 @@ get_ctry_abbrev <- function(afp_data) { dplyr::ungroup() if (nrow(summarize_ctry_abbrev) > 0) { - cli::cli_alert_warning("Some values in the country lookup table are not unique for the following countries: ") + cli::cli_alert_warning("Some values in the country lookup table are not unique for the following countries, please updated code in get_ctry_abbrev: ") non_unique <- ctry_abbrev |> dplyr::filter(place.admin.0 %in% summarize_ctry_abbrev$place.admin.0) diff --git a/R/kpi.table.functions.R b/R/kpi.table.functions.R index 6bf768b5..5c4a882c 100644 --- a/R/kpi.table.functions.R +++ b/R/kpi.table.functions.R @@ -1394,15 +1394,10 @@ generate_c2_table <- function(afp_data, pop_data, start_date, end_date, # NPAFP is only NA if it's missing population dplyr::mutate(npafp_rate = dplyr::if_else(npafp_cat != "Missing Pop" & is.nan(npafp_rate), 0, npafp_rate)) - # Add region and risk levels - region_lookup_table <- pop_data |> - dplyr::select(dplyr::any_of(c("ctry", "prov", "dist", "who.region"))) |> - dplyr::distinct() - results <- add_risk_category(results, risk_table) |> - dplyr::left_join(region_lookup_table) |> + dplyr::mutate(whoregion = get_region(ctry)) |> dplyr::select(-Region) |> - dplyr::rename(Region = who.region) |> + dplyr::rename(Region = whoregion) |> tidyr::replace_na(list(`SG Priority Level` = "LOW")) # Manual edit to reflect Indonesia WPRO change in May 23, 2025 From 61c38bd3432e3a40650d7e500f9f4a3fdb16e9d6 Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Mon, 13 Apr 2026 17:14:14 -0400 Subject: [PATCH 20/29] Update kpi.figure.functions.R -add filter to the image to only show High Priority countries --- R/kpi.figure.functions.R | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/R/kpi.figure.functions.R b/R/kpi.figure.functions.R index 1e8fb7ba..52c401e1 100644 --- a/R/kpi.figure.functions.R +++ b/R/kpi.figure.functions.R @@ -531,7 +531,7 @@ generate_kpi_npafp_bar <- function(c1, afp_data, dplyr::left_join(ctry_abbrev, by = c("ctry" = "place.admin.0")) |> dplyr::filter(.data$`SG Priority Level` == "HIGH") |> - # Remove whoregion from ctry_abbrev and use Region since + # Remove whoregion from ctry_abbrev and use Region since # c1 already accounts for Indonesia change and it's not used here dplyr::select(-whoregion) |> dplyr::mutate(prop_met_npafp = .data$prop_met_npafp) @@ -591,10 +591,11 @@ generate_kpi_evdetect_bar <- function(c1, afp_data, priority_ctry <- c1 |> dplyr::left_join(ctry_abbrev, by = c("ctry" = "place.admin.0")) |> - # Remove whoregion from ctry_abbrev and use Region since + # Remove whoregion from ctry_abbrev and use Region since # c1 already accounts for Indonesia change and it's not used here dplyr::select(-whoregion) |> - dplyr::mutate(prop_met_ev = .data$prop_met_ev) + dplyr::mutate(prop_met_ev = .data$prop_met_ev) |> + dplyr::filter(`SG Priority Level` == "HIGH") if (!is.null(who_region)) { priority_ctry <- priority_ctry |> dplyr::filter(Region %in% who_region) @@ -642,7 +643,7 @@ generate_kpi_stoolad_bar <- function(c1, afp_data, priority_ctry <- c1 |> dplyr::left_join(ctry_abbrev, by = c("ctry" = "place.admin.0")) |> - # Remove whoregion from ctry_abbrev and use Region since + # Remove whoregion from ctry_abbrev and use Region since # c1 already accounts for Indonesia change and it's not used here dplyr::select(-whoregion) |> dplyr::filter(.data$`SG Priority Level` == "HIGH") |> @@ -766,7 +767,7 @@ generate_timely_det_violin <- function(raw_data, priority_level = c("HIGH", "MEDIUM", "LOW (WATCHLIST)", "LOW"), who_region = NULL, rolling = TRUE, - risk_table = NULL, + risk_table = NULL, lab_locs = NULL, output_path = Sys.getenv("KPI_FIGURES"), y_max = 250) { @@ -800,7 +801,7 @@ generate_timely_det_violin <- function(raw_data, # Manually change the region of Indonesia based on change # since get_region() defaults to WPRO now, we have to do the opposite # to assign Indonesia samples to SEARO prior to May 23, 2025 - dplyr::mutate(whoregion = ifelse(place.admin.0 == "INDONESIA" & + dplyr::mutate(whoregion = ifelse(place.admin.0 == "INDONESIA" & analysis_year_start < lubridate::as_date("2025-05-23"), "SEARO", whoregion)) |> dplyr::mutate(seq_lab = case_when( .data$seq.capacity == "no" ~ "No sequencing capacity", From 0a33bb6e76553f342e9ac1652282b1c5dc37fea0 Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Tue, 14 Apr 2026 13:02:55 -0400 Subject: [PATCH 21/29] Update kpi.figure.functions.R various updates to correct violin plot outputs: - some cleanup of unnecessary code - change y_max for two violin plots to be max of indicator - correct a lab indicator to ensure all seq.capcity are considered --- R/kpi.figure.functions.R | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/R/kpi.figure.functions.R b/R/kpi.figure.functions.R index 52c401e1..f7e19ee5 100644 --- a/R/kpi.figure.functions.R +++ b/R/kpi.figure.functions.R @@ -533,8 +533,7 @@ generate_kpi_npafp_bar <- function(c1, afp_data, dplyr::filter(.data$`SG Priority Level` == "HIGH") |> # Remove whoregion from ctry_abbrev and use Region since # c1 already accounts for Indonesia change and it's not used here - dplyr::select(-whoregion) |> - dplyr::mutate(prop_met_npafp = .data$prop_met_npafp) + dplyr::select(-whoregion) bar_plot <- generate_kpi_barchart(priority_ctry, "prop_met_npafp", @@ -594,7 +593,6 @@ generate_kpi_evdetect_bar <- function(c1, afp_data, # Remove whoregion from ctry_abbrev and use Region since # c1 already accounts for Indonesia change and it's not used here dplyr::select(-whoregion) |> - dplyr::mutate(prop_met_ev = .data$prop_met_ev) |> dplyr::filter(`SG Priority Level` == "HIGH") if (!is.null(who_region)) { @@ -646,8 +644,7 @@ generate_kpi_stoolad_bar <- function(c1, afp_data, # Remove whoregion from ctry_abbrev and use Region since # c1 already accounts for Indonesia change and it's not used here dplyr::select(-whoregion) |> - dplyr::filter(.data$`SG Priority Level` == "HIGH") |> - dplyr::mutate(prop_met_stool = .data$prop_met_stool) + dplyr::filter(.data$`SG Priority Level` == "HIGH") bar_plot <- generate_kpi_barchart(priority_ctry, "prop_met_stool", @@ -770,7 +767,7 @@ generate_timely_det_violin <- function(raw_data, risk_table = NULL, lab_locs = NULL, output_path = Sys.getenv("KPI_FIGURES"), - y_max = 250) { + y_max = NULL) { start_date <- lubridate::as_date(start_date) end_date <- lubridate::as_date(end_date) @@ -801,18 +798,21 @@ generate_timely_det_violin <- function(raw_data, # Manually change the region of Indonesia based on change # since get_region() defaults to WPRO now, we have to do the opposite # to assign Indonesia samples to SEARO prior to May 23, 2025 - dplyr::mutate(whoregion = ifelse(place.admin.0 == "INDONESIA" & - analysis_year_start < lubridate::as_date("2025-05-23"), "SEARO", whoregion)) |> - dplyr::mutate(seq_lab = case_when( - .data$seq.capacity == "no" ~ "No sequencing capacity", - .data$seq.capacity == "yes" ~ "Sequencing capacity" - )) |> + # dplyr::mutate(whoregion = ifelse(place.admin.0 == "INDONESIA" & + # analysis_year_start < lubridate::as_date("2025-05-23"), "SEARO", whoregion)) |> + dplyr::mutate(seq_lab = stringr::str_to_lower(seq.capacity), + seq_lab = ifelse(seq_lab == "yes", + "Sequencing capacity", + "No sequencing capacity" + )) |> dplyr::filter(!is.na(.data$seq_lab)) |> dplyr::mutate( sg_priority_level = factor(.data$`SG Priority Level`, levels = c( "LOW", "LOW (WATCHLIST)", "MEDIUM", "HIGH")) ) + y_max <- max(pos_filtered$ontonothq, na.rm = T) + if (rolling) { facets <- ggh4x::facet_nested(rolling_period~seq_lab+whoregion, scales = "free", space = "free", @@ -836,7 +836,7 @@ generate_timely_det_violin <- function(raw_data, plot_mock_legend <- ggpubr::get_legend(plot_mock) plot_1 <- generate_kpi_violin(pos_filtered |> - filter(seq.capacity == "no"), "ctry.short", "ontonothq", + filter(seq_lab == "No sequencing capacity"), "ctry.short", "ontonothq", "sg_priority_level", facets, 46, y.max = y_max) @@ -845,7 +845,7 @@ generate_timely_det_violin <- function(raw_data, name = "Priority Level", na.value = "white") - plot_2 <- generate_kpi_violin(pos_filtered |> filter(seq.capacity == "yes"), "ctry.short", "ontonothq", + plot_2 <- generate_kpi_violin(pos_filtered |> filter(seq_lab == "Sequencing capacity"), "ctry.short", "ontonothq", "sg_priority_level", facets, 35, y.max = y_max) @@ -1326,7 +1326,7 @@ generate_lab_seqres_violin <- function(lab_data, afp_data, who_region = NULL, rolling = TRUE, output_path = Sys.getenv("KPI_FIGURES"), - y_max = 60) { + y_max = NULL) { start_date <- lubridate::as_date(start_date) end_date <- lubridate::as_date(end_date) @@ -1361,6 +1361,8 @@ generate_lab_seqres_violin <- function(lab_data, afp_data, "LOW", "LOW (WATCHLIST)", "MEDIUM", "HIGH")) ) + y_max <- as.numeric(max(lab_filtered$days.seq.rec.res, na.rm = T)) + if (!is.null(who_region)) { lab_filtered <- lab_filtered |> dplyr::filter(.data$whoregion %in% who_region) From 42838c8275b495bc62a5c0d304bd6959b9157cf0 Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Tue, 14 Apr 2026 15:02:30 -0400 Subject: [PATCH 22/29] Update dr.lab.functions.R correct issue with return df --- R/dr.lab.functions.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/dr.lab.functions.R b/R/dr.lab.functions.R index 14d6bf9d..36df2c88 100644 --- a/R/dr.lab.functions.R +++ b/R/dr.lab.functions.R @@ -1232,7 +1232,7 @@ clean_lab_data <- function(lab_data, start_date, end_date, )) |> mutate(seq.capacity = if_else(country %in% c("NIGERIA", "UGANDA") & DateStoolCollected >= as_date("2025-02-01"), "Sequencing capacity", seq.capacity)) - return(lab_data) + return(lab_data_man) } From 2803f5580e433bb5d6892fb2422e66174632826f Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Wed, 15 Apr 2026 15:13:29 -0400 Subject: [PATCH 23/29] Update kpi.figure.functions.R correction to added y_max to allow user to enter a number as a function argument --- R/kpi.figure.functions.R | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/R/kpi.figure.functions.R b/R/kpi.figure.functions.R index f7e19ee5..23212f87 100644 --- a/R/kpi.figure.functions.R +++ b/R/kpi.figure.functions.R @@ -749,7 +749,8 @@ generate_kpi_violin <- function( #' Output of [get_lab_locs()]. Defaults to `NULL`, which will download the information #' directly from EDAV. #' @param output_path `str` Where to output the figure to. -#' @param y_max `num` The maximum y-axis value. +#' @param y_max `num` The maximum y-axis value. Defaults to `NULL`, which will compute the +#' y_max as the max value of days to notify hq. #' #' @returns `ggplot` A violin plot showing timeliness of detection. #' @export @@ -811,7 +812,9 @@ generate_timely_det_violin <- function(raw_data, "LOW", "LOW (WATCHLIST)", "MEDIUM", "HIGH")) ) - y_max <- max(pos_filtered$ontonothq, na.rm = T) + if (is.null(y_max)) { + y_max <- max(pos_filtered$ontonothq, na.rm = TRUE) + } if (rolling) { facets <- ggh4x::facet_nested(rolling_period~seq_lab+whoregion, @@ -1343,7 +1346,6 @@ generate_lab_seqres_violin <- function(lab_data, afp_data, lab_filtered <- lab_data |> dplyr::left_join(ctry_abbrev, by = c("country" = "place.admin.0", "whoregion")) |> - dplyr::mutate(whoregion = get_region(country)) |> # Remove other who region columns because it's confusing dplyr::select(-dplyr::any_of(c("who.region", "Region"))) |> add_risk_category(ctry_col = "country") |> @@ -1361,13 +1363,16 @@ generate_lab_seqres_violin <- function(lab_data, afp_data, "LOW", "LOW (WATCHLIST)", "MEDIUM", "HIGH")) ) - y_max <- as.numeric(max(lab_filtered$days.seq.rec.res, na.rm = T)) if (!is.null(who_region)) { lab_filtered <- lab_filtered |> dplyr::filter(.data$whoregion %in% who_region) } + if (is.null(y_max)) { + y_max <- as.numeric(max(lab_filtered$days.seq.rec.res, na.rm = TRUE)) + } + if (rolling) { facets <- ggh4x::facet_nested(rolling_period ~ seq.cat + seq.lab, scales = "free", space = "free", From 8a9d986b8c2fb36b8b7ea46e1812614d7b4efeba Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Wed, 15 Apr 2026 15:13:54 -0400 Subject: [PATCH 24/29] Update kpi.main.functions.R update to kpi template to add code for users to check violin ranges --- R/kpi.main.functions.R | 45 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/R/kpi.main.functions.R b/R/kpi.main.functions.R index aaf68d2b..20721e66 100644 --- a/R/kpi.main.functions.R +++ b/R/kpi.main.functions.R @@ -305,11 +305,12 @@ generate_kpi_template <- function(output_path, name, edav) { stool_bar <- "generate_kpi_stoolad_bar(c1, raw_data$afp)" ev_bar <- "generate_kpi_evdetect_bar(c1, raw_data$afp)" - timely_violin <- 'generate_timely_det_violin(raw_data, start_date, end_date)' + culture_violin <- 'generate_lab_culture_violin(lab_data, raw_data$afp, start_date, end_date)' itd_violin <- 'generate_lab_itd_violin(lab_data, raw_data$afp, start_date, end_date)' seqship_violin <- 'generate_lab_seqship_violin(lab_data, raw_data$afp, start_date, end_date)' seqres_violin <- 'generate_lab_seqres_violin(lab_data, raw_data$afp, start_date, end_date)' + timely_violin <- 'generate_timely_det_violin(raw_data, start_date, end_date)' kpi_tile <- 'generate_kpi_tile(c1)' @@ -341,9 +342,44 @@ generate_kpi_template <- function(output_path, name, edav) { "# and may take a while to complete.", sg_priority_map, npafp_kpi_loop, stool_kpi_loop, ev_kpi_loop, "\n", npafp_bar, stool_bar, ev_bar, "\n", - "# Adjust the y_max as needed via the 'y_max' parameter", - timely_violin, culture_violin, itd_violin, seqship_violin, - seqres_violin, "\n", + "# Violin plots ----", + "# Check the indicator ranges below before setting y_max manually.", + "# Note these are approximations; ranges reflect the full dataset before additional filtering in plots.", + "# Examine plots carefully to finalize y_max.", + "# Example to change y_max: generate_lab_seqres_violin(lab_data, raw_data$afp, start_date, end_date, y_max = 250)", + "", + "# Build base dataset for lab violin date checks", + 'lab_kpi_check <- generate_kpi_lab_timeliness(lab_data, start_date, end_date, raw_data$afp)', + "", + "# For generate_lab_culture_violin()", + "# Default y_max is 60", + 'range(lab_kpi_check$days.lab.culture, na.rm = TRUE)', + 'summary(lab_kpi_check$days.lab.culture)', + "", + "# For generate_lab_itd_violin()", + "# Default y_max is 30", + 'range(lab_kpi_check$days.culture.itd, na.rm = TRUE)', + 'summary(lab_kpi_check$days.culture.itd)', + "", + "# For generate_lab_seqship_violin()", + "# Default y_max is 50", + 'range(lab_kpi_check$days.seq.ship, na.rm = TRUE)', + 'summary(lab_kpi_check$days.seq.ship)', + "", + "# For generate_lab_seqres_violin()", + "# Default y_max is dynamic to the max of days.seq.rec.res", + 'range(as.numeric(lab_kpi_check$days.seq.rec.res), na.rm = TRUE)', + 'summary(as.numeric(lab_kpi_check$days.seq.rec.res))', + "", + "# For generate_timely_det_violin()", + "# Default y_max is dynamic to the max of ontonothq", + "# Uses internal processing; this check approximates the plotted data.", + 'pos_timeliness_check <- sirfunctions:::generate_pos_timeliness(raw_data, start_date, end_date)', + 'range(pos_timeliness_check$ontonothq, na.rm = TRUE)', + 'summary(pos_timeliness_check$ontonothq)', + "", + "", + culture_violin, itd_violin, seqship_violin, seqres_violin, timely_violin, "\n", "# Generate C1 KPI tile ----", kpi_tile, "\n", "# Export GPSAP tables ----", @@ -351,4 +387,5 @@ generate_kpi_template <- function(output_path, name, edav) { ) writeLines(output_string, conn) + close(conn) } From 9ea11675f727acaf79f317f4d29f5c9033370dcc Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Wed, 15 Apr 2026 15:27:12 -0400 Subject: [PATCH 25/29] documentation update update documentation with document() --- man/generate_lab_seqres_violin.Rd | 2 +- man/generate_timely_det_violin.Rd | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/man/generate_lab_seqres_violin.Rd b/man/generate_lab_seqres_violin.Rd index 3f0d5d40..b17efe67 100644 --- a/man/generate_lab_seqres_violin.Rd +++ b/man/generate_lab_seqres_violin.Rd @@ -13,7 +13,7 @@ generate_lab_seqres_violin( who_region = NULL, rolling = TRUE, output_path = Sys.getenv("KPI_FIGURES"), - y_max = 60 + y_max = NULL ) } \arguments{ diff --git a/man/generate_timely_det_violin.Rd b/man/generate_timely_det_violin.Rd index a78d0cf5..72ae188a 100644 --- a/man/generate_timely_det_violin.Rd +++ b/man/generate_timely_det_violin.Rd @@ -14,7 +14,7 @@ generate_timely_det_violin( risk_table = NULL, lab_locs = NULL, output_path = Sys.getenv("KPI_FIGURES"), - y_max = 250 + y_max = NULL ) } \arguments{ @@ -41,7 +41,8 @@ directly from EDAV.} \item{output_path}{\code{str} Where to output the figure to.} -\item{y_max}{\code{num} The maximum y-axis value.} +\item{y_max}{\code{num} The maximum y-axis value. Defaults to \code{NULL}, which will compute the +y_max as the max value of days to notify hq.} } \value{ \code{ggplot} A violin plot showing timeliness of detection. From 74c3b3c8e9ad9ef9a2f568f39abb2851cafb8434 Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Wed, 15 Apr 2026 15:52:47 -0400 Subject: [PATCH 26/29] Update kpi.main.functions.R additional changes to kpi template --- R/kpi.main.functions.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/R/kpi.main.functions.R b/R/kpi.main.functions.R index 20721e66..2557b0ab 100644 --- a/R/kpi.main.functions.R +++ b/R/kpi.main.functions.R @@ -343,8 +343,9 @@ generate_kpi_template <- function(output_path, name, edav) { sg_priority_map, npafp_kpi_loop, stool_kpi_loop, ev_kpi_loop, "\n", npafp_bar, stool_bar, ev_bar, "\n", "# Violin plots ----", - "# Check the indicator ranges below before setting y_max manually.", - "# Note these are approximations; ranges reflect the full dataset before additional filtering in plots.", + "", + "# Before running plots, check the indicator ranges below to determine if you need to change y_max.", + "# Note these data are approximations; ranges reflect the full dataset before additional filtering in plots.", "# Examine plots carefully to finalize y_max.", "# Example to change y_max: generate_lab_seqres_violin(lab_data, raw_data$afp, start_date, end_date, y_max = 250)", "", @@ -379,6 +380,7 @@ generate_kpi_template <- function(output_path, name, edav) { 'summary(pos_timeliness_check$ontonothq)', "", "", + "# Build plots:", culture_violin, itd_violin, seqship_violin, seqres_violin, timely_violin, "\n", "# Generate C1 KPI tile ----", kpi_tile, "\n", From 678df67216c30e45b6b5973a9eac418307f39a94 Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Wed, 15 Apr 2026 15:53:04 -0400 Subject: [PATCH 27/29] Update R-CMD-check.yaml change to what is an error in github actions check --- .github/workflows/R-CMD-check.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index d935b2a0..871aa21d 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -49,3 +49,4 @@ jobs: with: upload-snapshots: true build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' + error-on: '"error"' From a5c6c1afee74ec78d43861e957d5c0af8da746ff Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Wed, 15 Apr 2026 16:10:34 -0400 Subject: [PATCH 28/29] Update kpi.main.functions.R changes to kpi template --- R/kpi.main.functions.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/kpi.main.functions.R b/R/kpi.main.functions.R index 2557b0ab..4bbd522e 100644 --- a/R/kpi.main.functions.R +++ b/R/kpi.main.functions.R @@ -346,11 +346,12 @@ generate_kpi_template <- function(output_path, name, edav) { "", "# Before running plots, check the indicator ranges below to determine if you need to change y_max.", "# Note these data are approximations; ranges reflect the full dataset before additional filtering in plots.", + "# Note: some range checks use internal helper functions from sirfunctions.", "# Examine plots carefully to finalize y_max.", "# Example to change y_max: generate_lab_seqres_violin(lab_data, raw_data$afp, start_date, end_date, y_max = 250)", "", - "# Build base dataset for lab violin date checks", - 'lab_kpi_check <- generate_kpi_lab_timeliness(lab_data, start_date, end_date, raw_data$afp)', + "# Build base dataset for lab violin range checks", + 'lab_kpi_check <- sirfunctions:::generate_kpi_lab_timeliness(lab_data, start_date, end_date, raw_data$afp)', "", "# For generate_lab_culture_violin()", "# Default y_max is 60", @@ -374,13 +375,12 @@ generate_kpi_template <- function(output_path, name, edav) { "", "# For generate_timely_det_violin()", "# Default y_max is dynamic to the max of ontonothq", - "# Uses internal processing; this check approximates the plotted data.", 'pos_timeliness_check <- sirfunctions:::generate_pos_timeliness(raw_data, start_date, end_date)', 'range(pos_timeliness_check$ontonothq, na.rm = TRUE)', 'summary(pos_timeliness_check$ontonothq)', "", "", - "# Build plots:", + "# Build plots ----", culture_violin, itd_violin, seqship_violin, seqres_violin, timely_violin, "\n", "# Generate C1 KPI tile ----", kpi_tile, "\n", From b0bca89c9aa74039d4f4e61bebcd1b69be85128b Mon Sep 17 00:00:00 2001 From: Michelle Sloan Date: Wed, 15 Apr 2026 16:49:57 -0400 Subject: [PATCH 29/29] Increment version number to 2.1.7 --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 33b28cc9..ab11f694 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: sirfunctions Title: Key Functions to Analyze Global Polio Surveillance Data -Version: 2.1.6 +Version: 2.1.7 Authors@R: c( person("Nishant", "Kishore", , "ynm2@cdc.gov", role = c("aut"), comment = c(ORCID = "0000-0003-0408-2747")),