From 2420b77ddc389f6ace790951efdd2686609f0fc8 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Tue, 24 Feb 2026 16:19:38 +0200 Subject: [PATCH 01/15] Closes #36 --- R/module-helpers-prevalence.R | 28 ++++++++++++------------- tests/testthat/test-module-prevalence.R | 14 ++++++++----- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/R/module-helpers-prevalence.R b/R/module-helpers-prevalence.R index 1f2ed30..749b9c5 100644 --- a/R/module-helpers-prevalence.R +++ b/R/module-helpers-prevalence.R @@ -47,7 +47,20 @@ mod_prevalence_display_input_variables <- function( ) ), choices = c("", vars) - ) + ), + shiny::selectInput( + inputId = ns("wts"), + label = shiny::tagList( + htmltools::tags$span("Survey weights", + style = "font-size: 14px; font-weight: bold;" + ), + htmltools::tags$div( + style = "font-size: 0.85em; color: #6c7574;", + "Final survey weights for weighted analysis" + ) + ), + choices = c("", vars) + ) ) #### Conditional inputs depending on source of data ---- @@ -75,19 +88,6 @@ mod_prevalence_display_input_variables <- function( htmltools::tags$span("*", style = "color: red;") ), choices = c("", vars) - ), - shiny::selectInput( - inputId = ns("wts"), - label = shiny::tagList( - htmltools::tags$span("Survey weights", - style = "font-size: 14px; font-weight: bold;" - ), - htmltools::tags$div( - style = "font-size: 0.85em; color: #6c7574;", - "Final survey weights for weighted analysis" - ) - ), - choices = c("", vars) ) ) } diff --git a/tests/testthat/test-module-prevalence.R b/tests/testthat/test-module-prevalence.R index 36a05cf..3cbf3d4 100644 --- a/tests/testthat/test-module-prevalence.R +++ b/tests/testthat/test-module-prevalence.R @@ -90,8 +90,8 @@ testthat::test_that( ### Test check ---- testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) - testthat::expect_equal(as.numeric(weighted_pop), 243016) - testthat::expect_equal(as.numeric(gam_prev), 3.7) + testthat::expect_equal(as.numeric(weighted_pop), 230478) + testthat::expect_equal(as.numeric(gam_prev), 4.3) ### Stop the app ---- app$stop() @@ -287,12 +287,16 @@ testthat::test_that( ### Capture prevalence results of Nampula Province, Urban Strata ---- glued_results <- app$get_js(js_values)[[2]] - prev <- stringr::str_extract_all(glued_results, "\\d{2}\\.\\d")[[1]] # not all are prevs + prev <- stringr::str_extract_all(glued_results, "\\d+(?:\\.\\d+)")[[1]] # not all are prevs ### Test check ---- + testthat::expect_equal( + round(as.numeric(stringr::str_extract_all(glued_results, "\\d{6}\\.\\d")[[1]][1]) + ), 574022 + ) testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) - testthat::expect_equal(as.numeric(prev[1]), 10.1) # GAM - testthat::expect_equal(as.numeric(prev[2]), 13.3) # GAM's upper CI + testthat::expect_equal(as.numeric(prev[9]), 10.6) # MAM's upper CI + testthat::expect_equal(as.numeric(prev[3]), 12.5) # GAM's upper CI ### Stop the app ---- app$stop() From 7885b60fe55c846ad792bdbbcac63a48bfb33d19 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Tue, 24 Feb 2026 17:26:30 +0200 Subject: [PATCH 02/15] recode sex to 'm' and 'f', then update unit tests --- tests/testthat/test-module-prevalence.R | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/testthat/test-module-prevalence.R b/tests/testthat/test-module-prevalence.R index 3cbf3d4..ddf7a96 100644 --- a/tests/testthat/test-module-prevalence.R +++ b/tests/testthat/test-module-prevalence.R @@ -35,6 +35,7 @@ testthat::test_that( file = testthat::test_path("fixtures", "anthro-01.csv"), check.names = FALSE ) + data["sex"] <- ifelse(data$sex == 1, "m", "f") tempfile <- tempfile(fileext = ".csv") write.csv(data, tempfile, row.names = FALSE) @@ -84,14 +85,14 @@ testthat::test_that( {return $(this).text();}).get();" ### Capture prevalence results of Nampula Province, Rural Strata ---- - glued_results <- app$get_js(js_values)[[1]] - weighted_pop <- stringr::str_extract(glued_results, "\\d{6}") + glued_results <- app$get_js(js_values)[[3]] + weighted_pop <- sub("1", "", stringr::str_extract(glued_results, "\\d{7}(?:)")) gam_prev <- stringr::str_extract(glued_results, "\\d\\.\\d") ### Test check ---- testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) - testthat::expect_equal(as.numeric(weighted_pop), 230478) - testthat::expect_equal(as.numeric(gam_prev), 4.3) + testthat::expect_equal(as.numeric(weighted_pop), 292611) + testthat::expect_equal(as.numeric(gam_prev), 6.1) ### Stop the app ---- app$stop() @@ -127,6 +128,7 @@ testthat::test_that( file = testthat::test_path("fixtures", "anthro-01.csv"), check.names = FALSE ) + data["sex"] <- ifelse(data$sex == 1, "m", "f") tempfile <- tempfile(fileext = ".csv") write.csv(data, tempfile, row.names = FALSE) @@ -181,22 +183,22 @@ testthat::test_that( {return $(this).text();}).get();" ### Get a JS expression ---- - js_result <- app$get_js(js_values) + js_result <- app$get_js(js_values)[[3]] ### Wait/validate that we have at least 3 values if (length(js_result) < 4) { #### Add a small delay and retry Sys.sleep(3) - js_result <- app$get_js(js_values) + js_result <- app$get_js(js_values)[[3]] } ### Capture prevalence results of Zambezia Province, Rural Strata ---- - prev <- stringr::str_extract_all(js_result[[3]][1], "\\d\\.\\d")[[1]] + prev <- stringr::str_extract_all(js_result, "\\d\\.\\d")[[1]] + weighted_pop <- sub("1", "", stringr::str_extract(js_result, "\\d{7}(?:)")) ### Test check ---- testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) - testthat::expect_equal(as.numeric(prev[2]), 5.3) # GAM - testthat::expect_equal(as.numeric(prev[6]), 1.3) # SAM - testthat::expect_equal(as.numeric(prev[10]), 4.0) # MAM + testthat::expect_equal(as.numeric(prev[2]), 7.7) # GAM + testthat::expect_equal(as.numeric(weighted_pop), 307395) ### Stop the app ---- app$stop() @@ -232,6 +234,7 @@ testthat::test_that( file = testthat::test_path("fixtures", "anthro-01.csv"), check.names = FALSE ) + data["sex"] <- ifelse(data$sex == 1, "m", "f") tempfile <- tempfile(fileext = ".csv") write.csv(data, tempfile, row.names = FALSE) @@ -286,17 +289,14 @@ testthat::test_that( {return $(this).text();}).get();" ### Capture prevalence results of Nampula Province, Urban Strata ---- - glued_results <- app$get_js(js_values)[[2]] - prev <- stringr::str_extract_all(glued_results, "\\d+(?:\\.\\d+)")[[1]] # not all are prevs + glued_results <- app$get_js(js_values)[[3]] + prev <- stringr::str_extract_all(glued_results, "\\d{2}\\.\\d")[[1]] + weighted_pop <- sub("1", "", stringr::str_extract(glued_results, "\\d{7}(?:)")) ### Test check ---- - testthat::expect_equal( - round(as.numeric(stringr::str_extract_all(glued_results, "\\d{6}\\.\\d")[[1]][1]) - ), 574022 - ) testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) - testthat::expect_equal(as.numeric(prev[9]), 10.6) # MAM's upper CI - testthat::expect_equal(as.numeric(prev[3]), 12.5) # GAM's upper CI + testthat::expect_equal(as.numeric(prev[2]), 10.8) # GAM + testthat::expect_equal(as.numeric(weighted_pop), 288534) ### Stop the app ---- app$stop() From 1f878741e16198a79218e56d37b58c7d31eb58ce Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Tue, 24 Feb 2026 17:47:52 +0200 Subject: [PATCH 03/15] add unit test for when survey weights are NULL --- tests/testthat/test-module-prevalence.R | 318 +++++++++++++++++++++--- 1 file changed, 286 insertions(+), 32 deletions(-) diff --git a/tests/testthat/test-module-prevalence.R b/tests/testthat/test-module-prevalence.R index ddf7a96..92672bf 100644 --- a/tests/testthat/test-module-prevalence.R +++ b/tests/testthat/test-module-prevalence.R @@ -6,15 +6,9 @@ ## ---- Survey data ------------------------------------------------------------ -### WFHZ Prevalence ---- - -#### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } - +### WFHZ Prevalence: weighted prevalence ---- testthat::test_that( - desc = "Module works well to estimate prevalence of AMN by WFHZ from survey", + desc = "Module works well to estimate weighted-WFHZ prevalence from survey", code = { ### Initialise mwana app ---- app <- shinytest2::AppDriver$new( @@ -99,15 +93,98 @@ testthat::test_that( } ) -### MUAC prevalence ---- -#### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } +### WFHZ Prevalence: unweighted prevalence ---- +testthat::test_that( + desc = "Module works well to estimate unweighted-WFHZ prevalence from survey", + code = { + ### Initialise mwana app ---- + app <- shinytest2::AppDriver$new( + app_dir = testthat::test_path("fixtures"), + timeout = 120000, + wait = TRUE + ) + + ### Wait the app to idle ---- + app$wait_for_idle(timeout = 40000) + + ### Click in the Data Upload tab ---- + app$click(selector = "a[data-value='Data Upload']") + app$wait_for_idle(timeout = 40000) + + #### Read data ---- + data <- read.csv( + file = testthat::test_path("fixtures", "anthro-01.csv"), + check.names = FALSE + ) + data["sex"] <- ifelse(data$sex == 1, "m", "f") + tempfile <- tempfile(fileext = ".csv") + write.csv(data, tempfile, row.names = FALSE) + + #### Upload onto the app ---- + app$upload_file(`upload_data-upload` = tempfile, wait_ = TRUE) + + ### Click on the data wrangling tab ---- + app$click(selector = "a[data-value='Data Wrangling']") + app$wait_for_idle(timeout = 40000) + + ## App defaults to WFHZ ---- + ### Input variables ---- + app$set_inputs(`wrangle_data-dos` = "", wait_ = FALSE) + app$set_inputs(`wrangle_data-dob` = "", wait_ = FALSE) + app$set_inputs(`wrangle_data-sex` = "sex", wait_ = FALSE) + app$set_inputs(`wrangle_data-weight` = "weight", wait_ = FALSE) + app$set_inputs(`wrangle_data-height` = "height", wait_ = FALSE) + + ### Click wrangle button and wait the app to idle ---- + app$click(input = "wrangle_data-apply_wrangle") + Sys.sleep(3) + + ### Click on the Prevalence tab and wait the app to idle ---- + app$click(selector = "a[data-value='Prevalence Analysis']") + app$wait_for_idle(timeout = 40000) + + ### Select source of data ---- + app$set_inputs(`prevalence-source` = "survey", wait_ = FALSE) + + ### Select the method ---- + app$set_inputs(`prevalence-amn_method_survey` = "wfhz", wait_ = FALSE) + app$set_inputs(`prevalence-area1` = "province", wait_ = FALSE) + app$set_inputs(`prevalence-area2` = "strata", wait_ = FALSE) ## Assume sex as grouping var + app$set_inputs(`prevalence-area3` = "sex", wait_ = FALSE) + app$set_inputs(`prevalence-wts` = "", wait_ = FALSE) + app$set_inputs(`prevalence-oedema` = "oedema", wait_ = FALSE) + + ### Click on Estime Prevalence button ---- + app$click(input = "prevalence-estimate") + app$wait_for_value(output = "prevalence-results", timeout = 40000) + + ### Capture JavaScript expressions to return results's cols and values ---- + js_cols <- "$('#prevalence-results thead th').map(function() + {return $(this).text();}).get();" + js_values <- "$('#prevalence-results tbody tr').map(function() + {return $(this).text();}).get();" + + ### Capture prevalence results of Nampula Province, Rural Strata ---- + glued_results <- app$get_js(js_values)[[3]] + pop <- sub("1", "", stringr::str_extract(glued_results, "\\d{4}")) + gam_prev <- stringr::str_extract(glued_results, "\\d{1}\\.\\d") + + ### Test check ---- + testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) + testthat::expect_equal(as.numeric(pop), 280) + testthat::expect_equal(as.numeric(gam_prev), 6.1) + + ### Stop the app ---- + app$stop() + } +) + + +### MUAC prevalence: weighted prevalence ---- testthat::test_that( - desc = "Module works well to estimate prevalence of AMN by MUAC from survey", + desc = "Module works well to estimate weighted-MUAC prevalence from survey", code = { ### Initialise mwana app ---- app <- shinytest2::AppDriver$new( @@ -205,15 +282,112 @@ testthat::test_that( } ) -### Combined prevalence ---- -#### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } +### MUAC prevalence: unweighted prevalence ---- +testthat::test_that( + desc = "Module works well to estimate unweighted-MUAC prevalence from survey", + code = { + ### Initialise mwana app ---- + app <- shinytest2::AppDriver$new( + app_dir = testthat::test_path("fixtures"), + timeout = 120000, + wait = TRUE + ) + + ### Wait the app to idle ---- + app$wait_for_idle(timeout = 40000) + + ### Click in the Data Upload tab ---- + app$click(selector = "a[data-value='Data Upload']") + app$wait_for_idle(timeout = 40000) + + #### Read data ---- + data <- read.csv( + file = testthat::test_path("fixtures", "anthro-01.csv"), + check.names = FALSE + ) + data["sex"] <- ifelse(data$sex == 1, "m", "f") + tempfile <- tempfile(fileext = ".csv") + write.csv(data, tempfile, row.names = FALSE) + + #### Upload onto the app ---- + app$upload_file(`upload_data-upload` = tempfile, wait_ = TRUE) + + ### Click on the data wrangling tab ---- + app$click(selector = "a[data-value='Data Wrangling']") + app$wait_for_idle(timeout = 40000) + + ### Select data wrangling method and wait the app till idles ---- + app$set_inputs(`wrangle_data-wrangle` = "mfaz", wait_ = TRUE) + app$wait_for_idle(timeout = 40000) + + ### Input variables ---- + app$set_inputs(`wrangle_data-dos` = "", wait_ = FALSE) + app$set_inputs(`wrangle_data-dob` = "", wait_ = FALSE) + app$set_inputs(`wrangle_data-age` = "age", wait_ = FALSE) + app$set_inputs(`wrangle_data-sex` = "sex", wait_ = FALSE) + app$set_inputs(`wrangle_data-muac` = "muac", wait_ = FALSE) + + ### Click wrangle button and wait the app to idle ---- + app$click(input = "wrangle_data-apply_wrangle") + app$wait_for_idle(timeout = 40000) + + ### Click on the Prevalence tab and wait the app to idle ---- + app$click(selector = "a[data-value='Prevalence Analysis']") + app$wait_for_idle(timeout = 40000) + + ### Select source of data ---- + app$set_inputs(`prevalence-source` = "survey", wait_ = FALSE) + + ### Select the method ---- + app$set_inputs(`prevalence-amn_method_survey` = "muac", wait_ = TRUE) + app$set_inputs(`prevalence-area1` = "province", wait_ = FALSE) + app$set_inputs(`prevalence-area2` = "strata", wait_ = FALSE) ## Assume sex as grouping var + app$set_inputs(`prevalence-area3` = "sex", wait_ = FALSE) + app$set_inputs(`prevalence-muac` = "muac", wait_ = FALSE) + app$set_inputs(`prevalence-age` = "age", wait_ = FALSE) + app$set_inputs(`prevalence-wts` = "", wait_ = FALSE) + app$set_inputs(`prevalence-oedema` = "oedema", wait_ = FALSE) + + ### Click on Estime Prevalence button ---- + app$click(input = "prevalence-estimate") + app$wait_for_value(output = "prevalence-results", timeout = 40000) + + ### Capture JavaScript expressions to return results's cols and values ---- + js_cols <- "$('#prevalence-results thead th').map(function() + {return $(this).text();}).get();" + + js_values <- "$('#prevalence-results tbody tr').map(function() + {return $(this).text();}).get();" + + ### Get a JS expression ---- + js_result <- app$get_js(js_values)[[3]] + + ### Wait/validate that we have at least 3 values + if (length(js_result) < 4) { + #### Add a small delay and retry + Sys.sleep(3) + js_result <- app$get_js(js_values)[[3]] + } + ### Capture prevalence results of Zambezia Province, Rural Strata ---- + pop <- sub("1", "", stringr::str_extract(js_result, "\\d{4}")) + prev <- stringr::str_extract(js_result, "\\d{1}\\.\\d") + + ### Test check ---- + testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) + testthat::expect_equal(as.numeric(prev), 8.0) # GAM + testthat::expect_equal(as.numeric(pop), 300) + + ### Stop the app ---- + app$stop() + } +) + + +### Combined prevalence: weighted prevalence ---- testthat::test_that( - desc = "Module works well to estimate prevalence of combined AMN from survey", + desc = "Module works well to estimate weighted-combined prevalence from survey", code = { ### Initialise mwana app ---- app <- shinytest2::AppDriver$new( @@ -304,16 +478,102 @@ testthat::test_that( ) -## ---- Screening data --------------------------------------------------------- +### Combined prevalence: unweighted prevalence ---- +testthat::test_that( + desc = "Module works well to estimate unweighted-combined prevalence from survey", + code = { + ### Initialise mwana app ---- + app <- shinytest2::AppDriver$new( + app_dir = testthat::test_path("fixtures"), + timeout = 120000, + wait = TRUE + ) + ### Wait the app to idle ---- + app$wait_for_idle(timeout = 40000) -### When age is available ---- + ### Click in the Data Upload tab ---- + app$click(selector = "a[data-value='Data Upload']") + app$wait_for_idle(timeout = 40000) -### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } + #### Read data ---- + data <- read.csv( + file = testthat::test_path("fixtures", "anthro-01.csv"), + check.names = FALSE + ) + data["sex"] <- ifelse(data$sex == 1, "m", "f") + tempfile <- tempfile(fileext = ".csv") + write.csv(data, tempfile, row.names = FALSE) + #### Upload onto the app ---- + app$upload_file(`upload_data-upload` = tempfile, wait_ = TRUE) + + ### Click on the data wrangling tab ---- + app$click(selector = "a[data-value='Data Wrangling']") + app$wait_for_idle(timeout = 40000) + + ### Select data wrangling method and wait the app till idles ---- + app$set_inputs(`wrangle_data-wrangle` = "combined", wait_ = TRUE) + app$wait_for_idle(timeout = 40000) + + ### Input variables ---- + app$set_inputs(`wrangle_data-dos` = "", wait_ = FALSE) + app$set_inputs(`wrangle_data-dob` = "", wait_ = FALSE) + app$set_inputs(`wrangle_data-age` = "age", wait_ = FALSE) + app$set_inputs(`wrangle_data-sex` = "sex", wait_ = FALSE) + app$set_inputs(`wrangle_data-weight` = "weight", wait_ = FALSE) + app$set_inputs(`wrangle_data-height` = "height", wait_ = FALSE) + app$set_inputs(`wrangle_data-muac` = "muac", wait_ = FALSE) + + ### Click wrangle button and wait the app to idle ---- + app$click(input = "wrangle_data-apply_wrangle") + app$wait_for_idle(timeout = 40000) + + ### Click on the Prevalence tab and wait the app to idle ---- + app$click(selector = "a[data-value='Prevalence Analysis']") + app$wait_for_idle(timeout = 40000) + + ### Select source of data ---- + app$set_inputs(`prevalence-source` = "survey", wait_ = FALSE) + + ### Select the method ---- + app$set_inputs(`prevalence-amn_method_survey` = "combined", wait_ = TRUE) + app$set_inputs(`prevalence-area1` = "province", wait_ = FALSE) + app$set_inputs(`prevalence-area2` = "strata", wait_ = FALSE) ## Assume sex as grouping var + app$set_inputs(`prevalence-area3` = "sex", wait_ = FALSE) + app$set_inputs(`prevalence-wts` = "", wait_ = FALSE) + app$set_inputs(`prevalence-oedema` = "oedema", wait_ = FALSE) + + ### Click on Estime Prevalence button ---- + app$click(input = "prevalence-estimate") + app$wait_for_value(output = "prevalence-results", timeout = 40000) + + ### Capture JavaScript expressions to return results's cols and values ---- + js_cols <- "$('#prevalence-results thead th').map(function() + {return $(this).text();}).get();" + + js_values <- "$('#prevalence-results tbody tr').map(function() + {return $(this).text();}).get();" + + ### Capture prevalence results of Nampula Province, Urban Strata ---- + glued_results <- app$get_js(js_values)[[3]] + pop <- sub("1", "", stringr::str_extract(glued_results, "\\d{4}")) + prev <- stringr::str_extract(glued_results, "\\d{2}\\.\\d") + + ### Test check ---- + testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) + testthat::expect_equal(as.numeric(prev), 10.5) # GAM + testthat::expect_equal(as.numeric(pop), 276) + + ### Stop the app ---- + app$stop() + } +) + +## ---- Screening data --------------------------------------------------------- + + +### When age is available ---- testthat::test_that( desc = "Module works well to estimate prevalence from screening", code = { @@ -419,12 +679,6 @@ testthat::test_that( ) ### When age is given in categories ---- - -#### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } - testthat::test_that( desc = "Prevalence tab works as expected when age is given in categories", code = { From 6fd9b4b4f79912d2b6f5f3b4700c851b58c9a433 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Tue, 24 Feb 2026 18:03:21 +0200 Subject: [PATCH 04/15] improve code annotation --- tests/testthat/test-module-prevalence.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/testthat/test-module-prevalence.R b/tests/testthat/test-module-prevalence.R index 92672bf..ca14d3b 100644 --- a/tests/testthat/test-module-prevalence.R +++ b/tests/testthat/test-module-prevalence.R @@ -6,7 +6,7 @@ ## ---- Survey data ------------------------------------------------------------ -### WFHZ Prevalence: weighted prevalence ---- +### WFHZ weighted prevalence ---- testthat::test_that( desc = "Module works well to estimate weighted-WFHZ prevalence from survey", code = { @@ -94,7 +94,7 @@ testthat::test_that( ) -### WFHZ Prevalence: unweighted prevalence ---- +### WFHZ unweighted prevalence ---- testthat::test_that( desc = "Module works well to estimate unweighted-WFHZ prevalence from survey", code = { @@ -182,7 +182,7 @@ testthat::test_that( ) -### MUAC prevalence: weighted prevalence ---- +### MUAC weighted prevalence ---- testthat::test_that( desc = "Module works well to estimate weighted-MUAC prevalence from survey", code = { @@ -283,7 +283,7 @@ testthat::test_that( ) -### MUAC prevalence: unweighted prevalence ---- +### MUAC unweighted prevalence ---- testthat::test_that( desc = "Module works well to estimate unweighted-MUAC prevalence from survey", code = { @@ -385,7 +385,7 @@ testthat::test_that( -### Combined prevalence: weighted prevalence ---- +### Combined weighted prevalence ---- testthat::test_that( desc = "Module works well to estimate weighted-combined prevalence from survey", code = { @@ -478,7 +478,7 @@ testthat::test_that( ) -### Combined prevalence: unweighted prevalence ---- +### Combined unweighted prevalence ---- testthat::test_that( desc = "Module works well to estimate unweighted-combined prevalence from survey", code = { From 5840c0825d07df00c0cdd4168b492a3ada77eee7 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Tue, 24 Feb 2026 19:17:10 +0200 Subject: [PATCH 05/15] do not subset when geting js; fix error in ubuntu CI --- tests/testthat/test-module-prevalence.R | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-module-prevalence.R b/tests/testthat/test-module-prevalence.R index ca14d3b..a618553 100644 --- a/tests/testthat/test-module-prevalence.R +++ b/tests/testthat/test-module-prevalence.R @@ -260,17 +260,17 @@ testthat::test_that( {return $(this).text();}).get();" ### Get a JS expression ---- - js_result <- app$get_js(js_values)[[3]] + js_result <- app$get_js(js_values) ### Wait/validate that we have at least 3 values if (length(js_result) < 4) { #### Add a small delay and retry Sys.sleep(3) - js_result <- app$get_js(js_values)[[3]] + js_result <- app$get_js(js_values) } ### Capture prevalence results of Zambezia Province, Rural Strata ---- - prev <- stringr::str_extract_all(js_result, "\\d\\.\\d")[[1]] - weighted_pop <- sub("1", "", stringr::str_extract(js_result, "\\d{7}(?:)")) + prev <- stringr::str_extract_all(js_result[[3]], "\\d\\.\\d")[[1]] + weighted_pop <- sub("1", "", stringr::str_extract(js_result[[3]], "\\d{7}(?:)")) ### Test check ---- testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) @@ -361,17 +361,17 @@ testthat::test_that( {return $(this).text();}).get();" ### Get a JS expression ---- - js_result <- app$get_js(js_values)[[3]] + js_result <- app$get_js(js_values) ### Wait/validate that we have at least 3 values if (length(js_result) < 4) { #### Add a small delay and retry Sys.sleep(3) - js_result <- app$get_js(js_values)[[3]] + js_result <- app$get_js(js_values) } ### Capture prevalence results of Zambezia Province, Rural Strata ---- - pop <- sub("1", "", stringr::str_extract(js_result, "\\d{4}")) - prev <- stringr::str_extract(js_result, "\\d{1}\\.\\d") + pop <- sub("1", "", stringr::str_extract(js_result[[3]], "\\d{4}")) + prev <- stringr::str_extract(js_result[[3]], "\\d{1}\\.\\d") ### Test check ---- testthat::expect_equal(length(app$get_js(js_cols)[1:19]), 19) From 67d2e49c766c0f05c9a41fb62e376e1d0a25de13 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Wed, 25 Feb 2026 10:51:47 +0200 Subject: [PATCH 06/15] make DoS and DoB work in mfaz wrangling --- R/module-data-wrangling.R | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/R/module-data-wrangling.R b/R/module-data-wrangling.R index fe2e5db..56a8335 100644 --- a/R/module-data-wrangling.R +++ b/R/module-data-wrangling.R @@ -193,13 +193,18 @@ module_server_wrangling <- function(id, data) { data() |> dplyr::mutate( - muac = mwana::recode_muac(x = !!rlang::sym(input$muac), .to = "cm") - ) |> - dplyr::rename( - age = !!rlang::sym(input$age), + muac = mwana::recode_muac(!!rlang::sym(input$muac), "cm"), + dos = as.Date(!!rlang::sym(input$dos), format = "%d/%m/%Y"), + dob = as.Date(!!rlang::sym(input$dob), format = "%d/%m/%Y"), + age = as.numeric(!!rlang::sym(input$age)), sex = !!rlang::sym(input$sex) ) |> - mwana::mw_wrangle_age(dos = NULL, dob = NULL, age = .data$age) |> + mwana::mw_wrangle_age( + dos = .data$dos, + dob = .data$dob, + age = .data$age, + .decimals = 2 + ) |> mwana::mw_wrangle_muac( sex = .data$sex, .recode_sex = TRUE, @@ -259,7 +264,7 @@ module_server_wrangling <- function(id, data) { ) } ) - +print(w) dataset$wrangled <- w }, error = function(e) { From 2f1fa4cdcfede1d8fce74513ecdeafc300189f88 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Wed, 25 Feb 2026 10:56:05 +0200 Subject: [PATCH 07/15] make DoS and DoB work in wrangling for combined prev --- R/module-data-wrangling.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/module-data-wrangling.R b/R/module-data-wrangling.R index 56a8335..73f62fd 100644 --- a/R/module-data-wrangling.R +++ b/R/module-data-wrangling.R @@ -239,10 +239,10 @@ module_server_wrangling <- function(id, data) { data() |> dplyr::mutate( - muac = mwana::recode_muac(x = !!rlang::sym(input$muac), .to = "cm") - ) |> - dplyr::rename( - age = !!rlang::sym(input$age), + muac = mwana::recode_muac(!!rlang::sym(input$muac), "cm"), + dos = as.Date(!!rlang::sym(input$dos), format = "%d/%m/%Y"), + dob = as.Date(!!rlang::sym(input$dob), format = "%d/%m/%Y"), + age = as.numeric(!!rlang::sym(input$age)), sex = !!rlang::sym(input$sex), weight = !!rlang::sym(input$weight), height = !!rlang::sym(input$height) @@ -253,7 +253,7 @@ module_server_wrangling <- function(id, data) { weight = .data$weight, height = .data$height ) |> - mwana::mw_wrangle_age(dos = NULL, dob = NULL, age = .data$age) |> + mwana::mw_wrangle_age(.data$dos, .data$dob, .data$age) |> mwana::mw_wrangle_muac( sex = .data$sex, .recode_sex = FALSE, From 6c20e5258bf82abdb5755e6aa5befdfad3e57ace Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Wed, 25 Feb 2026 13:07:40 +0200 Subject: [PATCH 08/15] Closes #37 --- R/module-data-wrangling.R | 70 +++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/R/module-data-wrangling.R b/R/module-data-wrangling.R index 73f62fd..f4f645b 100644 --- a/R/module-data-wrangling.R +++ b/R/module-data-wrangling.R @@ -175,12 +175,32 @@ module_server_wrangling <- function(id, data) { w <- switch(EXPR = input$wrangle, "wfhz" = { shiny::req(input$sex, input$weight, input$height) - data() |> - dplyr::rename( + + ### Wrangle age dynamically ---- + df <- if (nzchar(input$dos)) { + dplyr::mutate( + .data = data(), + dos = as.Date(!!rlang::sym(input$dos), format = "%d/%m/%Y"), + dob = as.Date(!!rlang::sym(input$dob), format = "%d/%m/%Y"), + age = as.numeric(!!rlang::sym(input$age)), sex = !!rlang::sym(input$sex), weight = !!rlang::sym(input$weight), height = !!rlang::sym(input$height) ) |> + mwana::mw_wrangle_age(.data$dos, .data$dob, .data$age) + } else { + dplyr::mutate( + .data = data(), + dos = NULL, + dob = NULL, + sex = !!rlang::sym(input$sex), + weight = !!rlang::sym(input$weight), + height = !!rlang::sym(input$height) + ) + } + + ### Wrangle WFHZ data ---- + df |> mwana::mw_wrangle_wfhz( sex = .data$sex, .recode_sex = TRUE, @@ -188,23 +208,34 @@ module_server_wrangling <- function(id, data) { height = .data$height ) }, + "mfaz" = { shiny::req(input$age, input$sex, input$muac) - data() |> + ### Wrangle age dynamically ---- + df <- if (nzchar(input$dos)) { dplyr::mutate( + .data = data(), muac = mwana::recode_muac(!!rlang::sym(input$muac), "cm"), dos = as.Date(!!rlang::sym(input$dos), format = "%d/%m/%Y"), dob = as.Date(!!rlang::sym(input$dob), format = "%d/%m/%Y"), age = as.numeric(!!rlang::sym(input$age)), sex = !!rlang::sym(input$sex) ) |> - mwana::mw_wrangle_age( - dos = .data$dos, - dob = .data$dob, - age = .data$age, - .decimals = 2 + mwana::mw_wrangle_age(.data$dos, .data$dob, .data$age) + } else { + dplyr::mutate( + .data = data(), + muac = mwana::recode_muac(!!rlang::sym(input$muac), "cm"), + dos = NULL, + dob = NULL, + sex = !!rlang::sym(input$sex) ) |> + mwana::mw_wrangle_age(NULL, NULL, .data$age) + } + + ### Wrangle MUAC ---- + df |> mwana::mw_wrangle_muac( sex = .data$sex, .recode_sex = TRUE, @@ -237,8 +268,10 @@ module_server_wrangling <- function(id, data) { input$muac ) - data() |> + ### Wrangle age dynamically ---- + df <- if (nzchar(input$dos)) { dplyr::mutate( + .data = data(), muac = mwana::recode_muac(!!rlang::sym(input$muac), "cm"), dos = as.Date(!!rlang::sym(input$dos), format = "%d/%m/%Y"), dob = as.Date(!!rlang::sym(input$dob), format = "%d/%m/%Y"), @@ -247,13 +280,28 @@ module_server_wrangling <- function(id, data) { weight = !!rlang::sym(input$weight), height = !!rlang::sym(input$height) ) |> + mwana::mw_wrangle_age(.data$dos, .data$dob, .data$age) + } else { + dplyr::mutate( + .data = data(), + muac = mwana::recode_muac(!!rlang::sym(input$muac), "cm"), + dos = NULL, + dob = NULL, + sex = !!rlang::sym(input$sex), + weight = !!rlang::sym(input$weight), + height = !!rlang::sym(input$height) + ) |> + mwana::mw_wrangle_age(NULL, NULL, .data$age) + } + + ### Wrangle WHZ and MUAC data ---- + df |> mwana::mw_wrangle_wfhz( sex = .data$sex, .recode_sex = TRUE, weight = .data$weight, height = .data$height ) |> - mwana::mw_wrangle_age(.data$dos, .data$dob, .data$age) |> mwana::mw_wrangle_muac( sex = .data$sex, .recode_sex = FALSE, @@ -264,7 +312,7 @@ module_server_wrangling <- function(id, data) { ) } ) -print(w) + dataset$wrangled <- w }, error = function(e) { From ca086360b3eda1e310a7b0cfb2602a10f8c4d07c Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Thu, 26 Feb 2026 09:55:14 +0200 Subject: [PATCH 09/15] Closes #1 --- R/module-data-upload.R | 10 ++-- R/module-data-wrangling.R | 21 +++++---- R/module-helpers-data-wrangling.R | 13 ++++- R/module-helpers-ipc-check.R | 21 ++++++++- R/module-helpers-plausibility-check.R | 14 ++++-- R/module-helpers-prevalence.R | 47 +++++++++++++++++-- R/module-ipc-aman-check.R | 20 +++----- R/module-plausibility-check.R | 17 +++---- R/module-prevalence.R | 9 ++-- ..._data_wrangling_display_input_variables.Rd | 12 +++++ man/mod_ipccheck_call_checker.Rd | 12 +++++ man/mod_ipccheck_display_input_variables.Rd | 13 +++++ man/mod_plausibility_call_checker.Rd | 9 ++++ ...od_plausibility_display_input_variables.Rd | 9 +++- ...prevalence_call_combined_prev_estimator.Rd | 5 +- ...mod_prevalence_call_muac_prev_estimator.Rd | 8 ++++ ...revalence_call_prev_estimator_screening.Rd | 9 ++++ ...evalence_call_prev_estimator_screening2.Rd | 1 - ...mod_prevalence_call_wfhz_prev_estimator.Rd | 7 +++ man/mod_prevalence_display_input_variables.Rd | 6 +++ man/mod_prevalence_neat_output_screening.Rd | 19 ++++++++ man/mod_prevalence_neat_output_survey.Rd | 21 +++++++++ man/module-id.Rd | 28 +++++++++++ man/module_server_ipccheck.Rd | 3 +- man/module_server_plausibility_check.Rd | 3 +- man/module_server_prevalence.Rd | 3 +- man/module_server_upload.Rd | 15 ------ man/module_server_wrangling.Rd | 6 +++ man/module_ui_upload.Rd | 15 ------ man/module_ui_wrangling.Rd | 15 ------ 30 files changed, 286 insertions(+), 105 deletions(-) create mode 100644 man/mod_prevalence_neat_output_screening.Rd create mode 100644 man/mod_prevalence_neat_output_survey.Rd create mode 100644 man/module-id.Rd delete mode 100644 man/module_server_upload.Rd delete mode 100644 man/module_ui_upload.Rd delete mode 100644 man/module_ui_wrangling.Rd diff --git a/R/module-data-upload.R b/R/module-data-upload.R index f58a443..7399beb 100644 --- a/R/module-data-upload.R +++ b/R/module-data-upload.R @@ -5,9 +5,10 @@ #' #' #' @param id Module ID -#' +#' @rdname module-id +#' #' @keywords internal -#' +#' #' module_ui_upload <- function(id) { ## Namespace ID's ---- @@ -94,9 +95,10 @@ module_ui_upload <- function(id) { #' #' Module server for data upload #' +#' @rdname module-id #' -#' @param id Module ID -#' +#' @returns A reactive data object to be used in data wrangling tab. +#' #' @keywords internal #' #' diff --git a/R/module-data-wrangling.R b/R/module-data-wrangling.R index f4f645b..d1000a6 100644 --- a/R/module-data-wrangling.R +++ b/R/module-data-wrangling.R @@ -6,8 +6,7 @@ #' #' Module UI for data wrangling #' -#' @param id Module ID -#' +#' @rdname module-id #' #' @keywords internal #' @@ -64,7 +63,7 @@ module_ui_wrangling <- function(id) { style = "font-size: 15px; font-weight: bold;" )), - #### A Placehoder for wrangled data and embed user feedback ---- + #### A Placeholder for wrangled data and embed user feedback ---- shinycssloaders::withSpinner( ui_element = DT::DTOutput(outputId = ns("wrangled")), type = 8, @@ -82,7 +81,7 @@ module_ui_wrangling <- function(id) { ) ), - #### Placeholder for donwload button ---- + #### Placeholder for download button ---- shiny::uiOutput(outputId = ns("download_wrangled_data")) ) ) @@ -98,7 +97,11 @@ module_ui_wrangling <- function(id) { #' Module server for data wrangling #' #' @param id Module ID -#' +#' +#' @param data An output and reactive data object from previous tab. +#' +#' @returns A reactive data object wrangled and ready to be used in plausibility +#' checks and prevalence analysis tabs. #' #' @keywords internal #' @@ -145,22 +148,22 @@ module_server_wrangling <- function(id, data) { if (input$wrangle == "wfhz") { if (any(!nzchar(c(input$sex, input$weight, input$height)))) { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } else if (input$wrangle == "mfaz") { if (any(!nzchar(c(input$age, input$sex, input$muac)))) { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } else if (input$wrangle == "muac") { if (any(!nzchar(c(input$sex, input$muac)))) { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } else { if (any(!nzchar(c(input$age, input$sex, input$weight, input$height, input$muac)))) { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } diff --git a/R/module-helpers-data-wrangling.R b/R/module-helpers-data-wrangling.R index 84e7275..c4d0fb5 100644 --- a/R/module-helpers-data-wrangling.R +++ b/R/module-helpers-data-wrangling.R @@ -5,8 +5,17 @@ #' #' #' Display input variables dynamically, according to UI for screening -#' -#' +#' +#' @param vars An object holding the data variable names. This is used to display +#' all the variables in the input variable of the ui. +#' +#' @param method User-selected method for data wrangling. +#' +#' @param ns A placeholder for Shiny module namespace. +#' +#' @returns A set of input variables specific for the user-selected data-wrangling +#' method. +#' #' @keywords internal #' #' diff --git a/R/module-helpers-ipc-check.R b/R/module-helpers-ipc-check.R index cca67a0..eaf96c2 100644 --- a/R/module-helpers-ipc-check.R +++ b/R/module-helpers-ipc-check.R @@ -8,8 +8,18 @@ #' #' #' Display input variables dynamically, according to UI for screening +#' +#' @param vars An object holding the data variable names. This is used to display +#' all the variables in the input variable of the UI. +#' +#' @param source User-selected source of data. As in the underlying method +#' used to collect the data. Choices are survey, screening and sentinel. #' -#' +#' @param ns A placeholder for Shiny module namespace. +#' +#' @returns A set of input variables specific for the user-selected source of +#' data. +#' #' @keywords internal #' #' @@ -103,7 +113,14 @@ mod_ipccheck_display_input_variables <- function(vars, source, ns) { #' Invoke mwana's IPC Acute Malnutrition minimum sample size requirement checker #' from within the module server #' -#' +#' @param df,cluster,source,area1,area2 Input variables collected from the UI +#' and required to pass to mwana::mw_check_ipcamn_ssreq() function. +#' +#' @returns A summary tibble containing check results for: +#' + n_clusters - the total number of unique clusters or screening or site identifiers; +#' + n_obs - the corresponding total number of children in the dataset; and, +#' + meet_ipc - whether the IPC AMN requirements were met. +#' #' @keywords internal #' #' diff --git a/R/module-helpers-plausibility-check.R b/R/module-helpers-plausibility-check.R index 0ef444b..cfe9265 100644 --- a/R/module-helpers-plausibility-check.R +++ b/R/module-helpers-plausibility-check.R @@ -6,7 +6,8 @@ #' #' Display input variables dynamically, according to UI for screening #' -#' @keywords internal +#' @inheritParams mod_data_wrangling_display_input_variables +#' #' mod_plausibility_display_input_variables <- function(vars, method, ns) { ### Base inputs always shown @@ -132,16 +133,21 @@ mod_plausibility_display_input_variables <- function(vars, method, ns) { inputs_vars } + #' #' #' Invoke mwana's plausibility checkers dynamically from within module server, #' according to user specifications in the UI #' -#' +#' @param df,age,sex,muac,weight,height,flags,area1,area2,area3,.for Input +#' variables collected from the UI and required to pass to +#' mwana::mw_plausibility_check_wfhz(). +#' +#' @returns A single-row summary tibble with columns containing the plausibility +#' check results. +#' #' @keywords internal #' -#' -#' mod_plausibility_call_checker <- function( df, age = NULL, sex, muac = NULL, weight = NULL, height = NULL, flags, area1, area2, area3, .for = c("wfhz", "muac", "mfaz")) { diff --git a/R/module-helpers-prevalence.R b/R/module-helpers-prevalence.R index 749b9c5..85bd830 100644 --- a/R/module-helpers-prevalence.R +++ b/R/module-helpers-prevalence.R @@ -5,6 +5,12 @@ #' #' #' Display input variables dynamically, according to UI for screening +#' +#' +#' @param df,vars,source,indicator_surv,has_age Input variables collected +#' from the UI and required to pass to `{mwana}` prevalence functions. +#' +#' @param ns A placeholder for Shiny module namespace. #' #' @keywords internal #' @@ -154,6 +160,11 @@ mod_prevalence_display_input_variables <- function( #' #' Invoke mwana's prevalence functions from within module server according to #' user specifications in the UI +#' +#' @param df,wts,oedema,area1,area2,area3 Input variables collected from the UI +#' and required to pass to mwana::mw_estimate_prevalence_wfhz(). +#' +#' @returns A summary tibble for the descriptive statistics about wasting. #' #' @keywords internal #' @@ -180,12 +191,19 @@ mod_prevalence_call_wfhz_prev_estimator <- function( ) } + #' #' #' #' Invoke mwana's prevalence functions from within module server according to #' user specifications in the UI #' +#' @param df,age,muac,wts,oedema,area1,area2,area3 Input variables collected +#' from the UI and required to pass to mwana::mw_estimate_prevalence_muac(). +#' +#' @returns A summary tibble for the descriptive statistics about wasting based +#' on MUAC, with confidence intervals. +#' #' @keywords internal #' #' @@ -213,13 +231,14 @@ mod_prevalence_call_muac_prev_estimator <- function( ) } + #' #' #' #' Invoke mwana's prevalence functions from within module server according to #' user specifications in the UI #' -#' @keywords internal +#' @inheritParams mod_prevalence_call_wfhz_prev_estimator #' #' mod_prevalence_call_combined_prev_estimator <- function( @@ -250,11 +269,17 @@ mod_prevalence_call_combined_prev_estimator <- function( #' #' Invoke mwana's prevalence functions from within module server according to #' user specifications in the UI +#' +#' @param df,age,age_cat,muac,oedema,area1,area2,area3 Input variables collected +#' from the UI and required to pass to mwana::mw_estimate_prevalence_screening() +#' and mwana::mw_estimate_prevalence_screening2() +#' +#' @returns A summary tibble for the descriptive statistics about wasting based +#' on MUAC, with no confidence intervals. #' #' @keywords internal #' #' -#' mod_prevalence_call_prev_estimator_screening <- function( df, age, muac, oedema = NULL, area1, area2, area3) { @@ -293,8 +318,7 @@ mod_prevalence_call_prev_estimator_screening <- function( #' Invoke mwana's prevalence functions from within module server according to #' user specifications in the UI #' -#' -#' @keywords internal +#' @inheritParams mod_prevalence_call_prev_estimator_screening #' #' mod_prevalence_call_prev_estimator_screening2 <- function( @@ -329,6 +353,14 @@ mod_prevalence_call_prev_estimator_screening2 <- function( #' #' +#' Neat prevalence output from survey +#' +#' @param df data.frame containing the prevalence results. +#' @param .type A choice from which the prevalence is derived. +#' +#' @returns A tibble object of the same length and width as df, with column +#' names and values formatted for clarity and readability. +#' #' @keywords internal #' #' @@ -393,6 +425,13 @@ mod_prevalence_neat_output_survey <- function( #' +#' +#' Neat prevalence output from survey +#' +#' @param df data.frame containing the prevalence results. +#' +#' @returns A tibble object of the same length and width as df, with column +#' names and values formatted for clarity and readability. #' #' @keywords internal #' diff --git a/R/module-ipc-aman-check.R b/R/module-ipc-aman-check.R index c066086..853f494 100644 --- a/R/module-ipc-aman-check.R +++ b/R/module-ipc-aman-check.R @@ -8,8 +8,6 @@ #' #' @keywords internal #' -#' -#' module_ui_ipccheck <- function(id) { ## Namespace ID ---- ns <- shiny::NS(id) @@ -79,7 +77,7 @@ module_ui_ipccheck <- function(id) { ) ), - #### Placeholder for donwload button ---- + #### Placeholder for download button ---- shiny::uiOutput(outputId = ns("download_ipccheck")) ) ) @@ -87,17 +85,13 @@ module_ui_ipccheck <- function(id) { } -## ---- Module: Sever ---------------------------------------------------------- +## ---- Module: Server ---------------------------------------------------------- #' #' #' Module server for IPC Acute Malnutrition sample size requirements check #' -#' @param id Module ID -#' -#' -#' @keywords internal -#' +#' @inheritParams module_server_wrangling #' #' module_server_ipccheck <- function(id, data) { @@ -131,17 +125,17 @@ module_server_ipccheck <- function(id, data) { if (input$ipccheck == "survey") { if (is.null(input$area1) || is.null(input$psu) || input$area1 == "" || input$psu == "") { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } else if (input$ipccheck == "screening") { if (is.null(input$area1) || is.null(input$sites) || input$area1 == "" || input$sites == "") { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } else if (input$ipccheck == "sentinel") { if (is.null(input$area1) || is.null(input$ssites) || input$area1 == "" || input$ssites == "") { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } @@ -204,7 +198,7 @@ module_server_ipccheck <- function(id, data) { pageLength = 20, scrollX = FALSE, scrollY = "800px", - columDefs = list(list(className = "dt-center", targets = "_all")) + columnDefs = list(list(className = "dt-center", targets = "_all")) ), caption = if (nrow(dataset$checked) > 20) { paste( diff --git a/R/module-plausibility-check.R b/R/module-plausibility-check.R index f095439..b14faef 100644 --- a/R/module-plausibility-check.R +++ b/R/module-plausibility-check.R @@ -6,7 +6,6 @@ #' #' @param id Module ID #' -#' #' @keywords internal #' #' @@ -59,7 +58,7 @@ module_ui_plausibility_check <- function(id) { style = "font-size: 15px; font-weight: bold;" )), - #### A Placehoder for wrangled data and embed user feedback ---- + #### A Placeholder for wrangled data and embed user feedback ---- shinycssloaders::withSpinner( ui_element = DT::DTOutput(outputId = ns("checked")), type = 8, @@ -77,7 +76,7 @@ module_ui_plausibility_check <- function(id) { ) ), - #### Placeholder for donwload button ---- + #### Placeholder for download button ---- shiny::uiOutput(outputId = ns("download_plausibility")) ) ) @@ -90,10 +89,8 @@ module_ui_plausibility_check <- function(id) { #' #' Module server for plausibility check #' -#' @param id Module ID -#' -#' -#' @keywords internal +#' +#' @inheritParams module_server_wrangling #' #' module_server_plausibility_check <- function(id, data) { @@ -133,19 +130,19 @@ module_server_plausibility_check <- function(id, data) { required_vars <- c(input$sex, input$weight, input$height, input$age, input$flags) if (any(required_vars == "" | is.null(required_vars))) { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } else if (input$method == "mfaz") { required_vars <- c(input$age, input$sex, input$muac, input$flags) if (any(required_vars == "" | is.null(required_vars))) { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } else if (input$method == "muac") { required_vars <- c(input$sex, input$muac, input$flags) if (any(required_vars == "" | is.null(required_vars))) { valid <- FALSE - message <- "Please select all required variables." + message <- "Please select all required variables for the chosen method (see the UI input list)." } } else { valid <- FALSE diff --git a/R/module-prevalence.R b/R/module-prevalence.R index 5973813..6b066ff 100644 --- a/R/module-prevalence.R +++ b/R/module-prevalence.R @@ -9,7 +9,6 @@ #' @keywords internal #' #' -#' module_ui_prevalence <- function(id) { ## Namespace ID ---- ns <- shiny::NS(id) @@ -71,7 +70,7 @@ module_ui_prevalence <- function(id) { ) ), - #### A Placehoder for wrangled data and embed user feedback ---- + #### A Placeholder for wrangled data and embed user feedback ---- shinycssloaders::withSpinner( ui_element = DT::DTOutput(outputId = ns("results")), type = 8, @@ -89,7 +88,7 @@ module_ui_prevalence <- function(id) { ) ), - #### Placeholder for donwload button ---- + #### Placeholder for download button ---- shiny::uiOutput(outputId = ns("download_prevalence")) ) ) @@ -103,9 +102,7 @@ module_ui_prevalence <- function(id) { #' #' Module server for prevalence analysis #' -#' @param id Module ID -#' -#' @keywords internal +#' @inheritParams module_server_wrangling #' #' module_server_prevalence <- function(id, data) { diff --git a/man/mod_data_wrangling_display_input_variables.Rd b/man/mod_data_wrangling_display_input_variables.Rd index ddb1809..c9c7f62 100644 --- a/man/mod_data_wrangling_display_input_variables.Rd +++ b/man/mod_data_wrangling_display_input_variables.Rd @@ -6,6 +6,18 @@ \usage{ mod_data_wrangling_display_input_variables(vars, method, ns) } +\arguments{ +\item{vars}{An object holding the data variable names. This is used to display +all the variables in the input variable of the ui.} + +\item{method}{User-selected method for data wrangling.} + +\item{ns}{A placeholder for Shiny module namespace.} +} +\value{ +A set of input variables specific for the user-selected data-wrangling +method. +} \description{ Display input variables dynamically, according to UI for screening } diff --git a/man/mod_ipccheck_call_checker.Rd b/man/mod_ipccheck_call_checker.Rd index 329f09d..83a7f65 100644 --- a/man/mod_ipccheck_call_checker.Rd +++ b/man/mod_ipccheck_call_checker.Rd @@ -7,6 +7,18 @@ from within the module server} \usage{ mod_ipccheck_call_checker(df, cluster, source = character(), area1, area2) } +\arguments{ +\item{df, cluster, source, area1, area2}{Input variables collected from the UI +and required to pass to mwana::mw_check_ipcamn_ssreq() function.} +} +\value{ +A summary tibble containing check results for: +\itemize{ +\item n_clusters - the total number of unique clusters or screening or site identifiers; +\item n_obs - the corresponding total number of children in the dataset; and, +\item meet_ipc - whether the IPC AMN requirements were met. +} +} \description{ Invoke mwana's IPC Acute Malnutrition minimum sample size requirement checker from within the module server diff --git a/man/mod_ipccheck_display_input_variables.Rd b/man/mod_ipccheck_display_input_variables.Rd index 046c980..e1e3319 100644 --- a/man/mod_ipccheck_display_input_variables.Rd +++ b/man/mod_ipccheck_display_input_variables.Rd @@ -6,6 +6,19 @@ \usage{ mod_ipccheck_display_input_variables(vars, source, ns) } +\arguments{ +\item{vars}{An object holding the data variable names. This is used to display +all the variables in the input variable of the UI.} + +\item{source}{User-selected source of data. As in the underlying method +used to collect the data. Choices are survey, screening and sentinel.} + +\item{ns}{A placeholder for Shiny module namespace.} +} +\value{ +A set of input variables specific for the user-selected source of +data. +} \description{ Display input variables dynamically, according to UI for screening } diff --git a/man/mod_plausibility_call_checker.Rd b/man/mod_plausibility_call_checker.Rd index 3617ff3..2b5939e 100644 --- a/man/mod_plausibility_call_checker.Rd +++ b/man/mod_plausibility_call_checker.Rd @@ -19,6 +19,15 @@ mod_plausibility_call_checker( .for = c("wfhz", "muac", "mfaz") ) } +\arguments{ +\item{df, age, sex, muac, weight, height, flags, area1, area2, area3, .for}{Input +variables collected from the UI and required to pass to +mwana::mw_plausibility_check_wfhz().} +} +\value{ +A single-row summary tibble with columns containing the plausibility +check results. +} \description{ Invoke mwana's plausibility checkers dynamically from within module server, according to user specifications in the UI diff --git a/man/mod_plausibility_display_input_variables.Rd b/man/mod_plausibility_display_input_variables.Rd index 876edc5..6e1b9ca 100644 --- a/man/mod_plausibility_display_input_variables.Rd +++ b/man/mod_plausibility_display_input_variables.Rd @@ -6,7 +6,14 @@ \usage{ mod_plausibility_display_input_variables(vars, method, ns) } +\arguments{ +\item{vars}{An object holding the data variable names. This is used to display +all the variables in the input variable of the ui.} + +\item{method}{User-selected method for data wrangling.} + +\item{ns}{A placeholder for Shiny module namespace.} +} \description{ Display input variables dynamically, according to UI for screening } -\keyword{internal} diff --git a/man/mod_prevalence_call_combined_prev_estimator.Rd b/man/mod_prevalence_call_combined_prev_estimator.Rd index ee6420a..619a8bd 100644 --- a/man/mod_prevalence_call_combined_prev_estimator.Rd +++ b/man/mod_prevalence_call_combined_prev_estimator.Rd @@ -14,8 +14,11 @@ mod_prevalence_call_combined_prev_estimator( area3 ) } +\arguments{ +\item{df, wts, oedema, area1, area2, area3}{Input variables collected from the UI +and required to pass to mwana::mw_estimate_prevalence_wfhz().} +} \description{ Invoke mwana's prevalence functions from within module server according to user specifications in the UI } -\keyword{internal} diff --git a/man/mod_prevalence_call_muac_prev_estimator.Rd b/man/mod_prevalence_call_muac_prev_estimator.Rd index b1c4b56..cf18dcc 100644 --- a/man/mod_prevalence_call_muac_prev_estimator.Rd +++ b/man/mod_prevalence_call_muac_prev_estimator.Rd @@ -16,6 +16,14 @@ mod_prevalence_call_muac_prev_estimator( area3 ) } +\arguments{ +\item{df, age, muac, wts, oedema, area1, area2, area3}{Input variables collected +from the UI and required to pass to mwana::mw_estimate_prevalence_muac().} +} +\value{ +A summary tibble for the descriptive statistics about wasting based +on MUAC, with confidence intervals. +} \description{ Invoke mwana's prevalence functions from within module server according to user specifications in the UI diff --git a/man/mod_prevalence_call_prev_estimator_screening.Rd b/man/mod_prevalence_call_prev_estimator_screening.Rd index b6613c9..7b32940 100644 --- a/man/mod_prevalence_call_prev_estimator_screening.Rd +++ b/man/mod_prevalence_call_prev_estimator_screening.Rd @@ -15,6 +15,15 @@ mod_prevalence_call_prev_estimator_screening( area3 ) } +\arguments{ +\item{df, age, age_cat, muac, oedema, area1, area2, area3}{Input variables collected +from the UI and required to pass to mwana::mw_estimate_prevalence_screening() +and mwana::mw_estimate_prevalence_screening2()} +} +\value{ +A summary tibble for the descriptive statistics about wasting based +on MUAC, with no confidence intervals. +} \description{ Invoke mwana's prevalence functions from within module server according to user specifications in the UI diff --git a/man/mod_prevalence_call_prev_estimator_screening2.Rd b/man/mod_prevalence_call_prev_estimator_screening2.Rd index 769d342..1dd3d22 100644 --- a/man/mod_prevalence_call_prev_estimator_screening2.Rd +++ b/man/mod_prevalence_call_prev_estimator_screening2.Rd @@ -19,4 +19,3 @@ mod_prevalence_call_prev_estimator_screening2( Invoke mwana's prevalence functions from within module server according to user specifications in the UI } -\keyword{internal} diff --git a/man/mod_prevalence_call_wfhz_prev_estimator.Rd b/man/mod_prevalence_call_wfhz_prev_estimator.Rd index 26ca9df..2c8759d 100644 --- a/man/mod_prevalence_call_wfhz_prev_estimator.Rd +++ b/man/mod_prevalence_call_wfhz_prev_estimator.Rd @@ -14,6 +14,13 @@ mod_prevalence_call_wfhz_prev_estimator( area3 ) } +\arguments{ +\item{df, wts, oedema, area1, area2, area3}{Input variables collected from the UI +and required to pass to mwana::mw_estimate_prevalence_wfhz().} +} +\value{ +A summary tibble for the descriptive statistics about wasting. +} \description{ Invoke mwana's prevalence functions from within module server according to user specifications in the UI diff --git a/man/mod_prevalence_display_input_variables.Rd b/man/mod_prevalence_display_input_variables.Rd index 9875fcc..659ef51 100644 --- a/man/mod_prevalence_display_input_variables.Rd +++ b/man/mod_prevalence_display_input_variables.Rd @@ -12,6 +12,12 @@ mod_prevalence_display_input_variables( ns ) } +\arguments{ +\item{ns}{A placeholder for Shiny module namespace.} + +\item{df, vars, source, indicator_surv, has_age}{Input variables collected +from the UI and required to pass to \code{{mwana}} prevalence functions.} +} \description{ Display input variables dynamically, according to UI for screening } diff --git a/man/mod_prevalence_neat_output_screening.Rd b/man/mod_prevalence_neat_output_screening.Rd new file mode 100644 index 0000000..853a188 --- /dev/null +++ b/man/mod_prevalence_neat_output_screening.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/module-helpers-prevalence.R +\name{mod_prevalence_neat_output_screening} +\alias{mod_prevalence_neat_output_screening} +\title{Neat prevalence output from survey} +\usage{ +mod_prevalence_neat_output_screening(df) +} +\arguments{ +\item{df}{data.frame containing the prevalence results.} +} +\value{ +A tibble object of the same length and width as df, with column +names and values formatted for clarity and readability. +} +\description{ +Neat prevalence output from survey +} +\keyword{internal} diff --git a/man/mod_prevalence_neat_output_survey.Rd b/man/mod_prevalence_neat_output_survey.Rd new file mode 100644 index 0000000..7e004ba --- /dev/null +++ b/man/mod_prevalence_neat_output_survey.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/module-helpers-prevalence.R +\name{mod_prevalence_neat_output_survey} +\alias{mod_prevalence_neat_output_survey} +\title{Neat prevalence output from survey} +\usage{ +mod_prevalence_neat_output_survey(df, .type = c("wfhz", "muac", "combined")) +} +\arguments{ +\item{df}{data.frame containing the prevalence results.} + +\item{.type}{A choice from which the prevalence is derived.} +} +\value{ +A tibble object of the same length and width as df, with column +names and values formatted for clarity and readability. +} +\description{ +Neat prevalence output from survey +} +\keyword{internal} diff --git a/man/module-id.Rd b/man/module-id.Rd new file mode 100644 index 0000000..9de5a4b --- /dev/null +++ b/man/module-id.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/module-data-upload.R, R/module-data-wrangling.R +\name{module_ui_upload} +\alias{module_ui_upload} +\alias{module_server_upload} +\alias{module_ui_wrangling} +\title{Module UI for data upload} +\usage{ +module_ui_upload(id) + +module_server_upload(id) + +module_ui_wrangling(id) +} +\arguments{ +\item{id}{Module ID} +} +\value{ +A reactive data object to be used in data wrangling tab. +} +\description{ +Module UI for data upload + +Module server for data upload + +Module UI for data wrangling +} +\keyword{internal} diff --git a/man/module_server_ipccheck.Rd b/man/module_server_ipccheck.Rd index 34ba2f2..845e09a 100644 --- a/man/module_server_ipccheck.Rd +++ b/man/module_server_ipccheck.Rd @@ -8,8 +8,9 @@ module_server_ipccheck(id, data) } \arguments{ \item{id}{Module ID} + +\item{data}{An output and reactive data object from previous tab.} } \description{ Module server for IPC Acute Malnutrition sample size requirements check } -\keyword{internal} diff --git a/man/module_server_plausibility_check.Rd b/man/module_server_plausibility_check.Rd index c6bf93c..769aa34 100644 --- a/man/module_server_plausibility_check.Rd +++ b/man/module_server_plausibility_check.Rd @@ -8,8 +8,9 @@ module_server_plausibility_check(id, data) } \arguments{ \item{id}{Module ID} + +\item{data}{An output and reactive data object from previous tab.} } \description{ Module server for plausibility check } -\keyword{internal} diff --git a/man/module_server_prevalence.Rd b/man/module_server_prevalence.Rd index 6129ff3..0737f08 100644 --- a/man/module_server_prevalence.Rd +++ b/man/module_server_prevalence.Rd @@ -8,8 +8,9 @@ module_server_prevalence(id, data) } \arguments{ \item{id}{Module ID} + +\item{data}{An output and reactive data object from previous tab.} } \description{ Module server for prevalence analysis } -\keyword{internal} diff --git a/man/module_server_upload.Rd b/man/module_server_upload.Rd deleted file mode 100644 index c8f4be2..0000000 --- a/man/module_server_upload.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/module-data-upload.R -\name{module_server_upload} -\alias{module_server_upload} -\title{Module server for data upload} -\usage{ -module_server_upload(id) -} -\arguments{ -\item{id}{Module ID} -} -\description{ -Module server for data upload -} -\keyword{internal} diff --git a/man/module_server_wrangling.Rd b/man/module_server_wrangling.Rd index 5d5e840..11d433a 100644 --- a/man/module_server_wrangling.Rd +++ b/man/module_server_wrangling.Rd @@ -8,6 +8,12 @@ module_server_wrangling(id, data) } \arguments{ \item{id}{Module ID} + +\item{data}{An output and reactive data object from previous tab.} +} +\value{ +A reactive data object wrangled and ready to be used in plausibility +checks and prevalence analysis tabs. } \description{ Module server for data wrangling diff --git a/man/module_ui_upload.Rd b/man/module_ui_upload.Rd deleted file mode 100644 index 6883bf6..0000000 --- a/man/module_ui_upload.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/module-data-upload.R -\name{module_ui_upload} -\alias{module_ui_upload} -\title{Module UI for data upload} -\usage{ -module_ui_upload(id) -} -\arguments{ -\item{id}{Module ID} -} -\description{ -Module UI for data upload -} -\keyword{internal} diff --git a/man/module_ui_wrangling.Rd b/man/module_ui_wrangling.Rd deleted file mode 100644 index 62acf81..0000000 --- a/man/module_ui_wrangling.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/module-data-wrangling.R -\name{module_ui_wrangling} -\alias{module_ui_wrangling} -\title{Module UI for data wrangling} -\usage{ -module_ui_wrangling(id) -} -\arguments{ -\item{id}{Module ID} -} -\description{ -Module UI for data wrangling -} -\keyword{internal} From f34e478dbdfa18721b827c3e2fa3d8e5f627ed93 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Thu, 26 Feb 2026 10:53:18 +0200 Subject: [PATCH 10/15] add test suite for age data wrangling --- tests/testthat/test-module-wrangling.R | 245 ++++++++++++++++++++++--- 1 file changed, 221 insertions(+), 24 deletions(-) diff --git a/tests/testthat/test-module-wrangling.R b/tests/testthat/test-module-wrangling.R index f803c3d..ab3afe2 100644 --- a/tests/testthat/test-module-wrangling.R +++ b/tests/testthat/test-module-wrangling.R @@ -6,11 +6,6 @@ ## ---- Data Wrangling: WFHZ --------------------------------------------------- -### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } - testthat::test_that(desc = "Server data wrangling works as expected for WFHZ", { ## Initialise app ---- app <- shinytest2::AppDriver$new( @@ -70,13 +65,80 @@ testthat::test_that(desc = "Server data wrangling works as expected for WFHZ", { }) -## ---- Data Wrangling: MFAZ --------------------------------------------------- +### When user supplies dos and dob for age wrangling ---- + +testthat::test_that(desc = "Server data wrangling wrangles age correctly in WFHZ", { + ## Initialise app ---- + app <- shinytest2::AppDriver$new( + app_dir = testthat::test_path("fixtures"), + load_timeout = 120000, + wait = TRUE + ) + + ### Let the app load ---- + app$wait_for_idle(timeout = 40000) + + ### Click on the Data uploading navbar ---- + app$click(selector = "a[data-value='Data Upload']") + app$wait_for_idle(timeout = 40000) + + #### Create a toy dataset ---- + data <- data.frame( + dos = c("26/02/2026", "26/02/2026", "26/02/2026", "26/02/2026", "26/02/2026"), + dob = c("12/05/2024", "14/07/2025", "22/03/2024", "20/07/2024", "20/09/2021"), + age = c(NA, NA, NA, NA, 28), + sex = c("m", "f", "m", "f", "f"), + muac = c(121, 124, 132, 117, 100), + weight = c(12.3, 13.3, 14.5, 15.5, 10.0), + height = c(110.3, 117.2, 119.2, 80.6, 98.9) + ) + tempfile <- tempfile(fileext = ".csv") + write.csv(data, tempfile, row.names = FALSE) + #### Upload onto the app ---- + app$upload_file(`upload_data-upload` = tempfile, wait_ = TRUE) + + ### Click on the data wrangling tab ---- + app$click(selector = "a[data-value='Data Wrangling']") + app$wait_for_idle(timeout = 40000) -### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } + ### Select input variables ---- + app$set_inputs(`wrangle_data-wrangle` = "wfhz", wait_ = FALSE) + app$set_inputs(`wrangle_data-dos` = "dos", wait_ = FALSE) + app$set_inputs(`wrangle_data-dob` = "dob", wait_ = FALSE) + app$set_inputs(`wrangle_data-age` = "age", wait_ = FALSE) + app$set_inputs(`wrangle_data-sex` = "sex", wait_ = FALSE) + app$set_inputs(`wrangle_data-weight` = "weight", wait_ = FALSE) + app$set_inputs(`wrangle_data-height` = "height", wait_ = FALSE) + + ### Click wrangle button ---- + app$click(input = "wrangle_data-apply_wrangle") + app$wait_for_value(output = "wrangle_data-wrangled", timeout = 40000) + + ### Get wrangled values ---- + column_names <- app$get_js(" + $('#wrangle_data-wrangled thead th').map(function() { + return $(this).text(); + }).get(); + ") |> as.character() + + ### Capture JavaScript expressions to return results ---- + js_results <- app$get_js("$('#wrangle_data-wrangled tbody tr').map(function() + {return $(this).text();}).get();") + + age_mo <- stringr::str_extract(js_results[[1]], "\\d{2}\\.\\d{2}") + age_28 <- stringr::str_extract(js_results[[5]], stringr::fixed("28")) + + testthat::expect_true("age_days" %in% column_names) + testthat::expect_equal(as.numeric(age_mo), 21.51) + testthat::expect_equal(as.numeric(age_28), 28) + + #### Stop the app ---- + app$stop() +}) + + +## ---- Data Wrangling: MFAZ --------------------------------------------------- testthat::test_that(desc = "Server data wrangling works as expected for MFAZ", { @@ -111,8 +173,7 @@ testthat::test_that(desc = "Server data wrangling works as expected for MFAZ", { app$wait_for_idle(timeout = 40000) ### Set the wrangling method to MFAZ ---- - app$set_inputs(`wrangle_data-wrangle` = "mfaz", wait_ = FALSE) - Sys.sleep(3) + app$set_inputs(`wrangle_data-wrangle` = "mfaz", wait_ = TRUE) ### Select variables ---- app$set_inputs(`wrangle_data-dos` = "", wait_ = FALSE) app$set_inputs(`wrangle_data-dob` = "", wait_ = FALSE) @@ -141,13 +202,78 @@ testthat::test_that(desc = "Server data wrangling works as expected for MFAZ", { }) -## ---- Data Wrangling: MUAC --------------------------------------------------- +### When user supplies dos and dob for age wrangling ---- +testthat::test_that(desc = "Server data wrangling wrangles age correctly in MFAZ", { + ## Initialise app ---- + app <- shinytest2::AppDriver$new( + app_dir = testthat::test_path("fixtures"), + load_timeout = 120000, + wait = TRUE + ) + ### Let the app load ---- + app$wait_for_idle(timeout = 40000) + + ### Click on the Data uploading navbar ---- + app$click(selector = "a[data-value='Data Upload']") + app$wait_for_idle(timeout = 40000) + + #### Create a toy dataset ---- + data <- data.frame( + dos = c("26/02/2026", "26/02/2026", "26/02/2026", "26/02/2026", "26/02/2026"), + dob = c("12/05/2024", "14/07/2025", "22/03/2024", "20/07/2024", "20/09/2021"), + age = c(NA, NA, NA, NA, 28), + sex = c("m", "f", "m", "f", "f"), + muac = c(121, 124, 132, 117, 100) + ) + tempfile <- tempfile(fileext = ".csv") + write.csv(data, tempfile, row.names = FALSE) + + #### Upload onto the app ---- + app$upload_file(`upload_data-upload` = tempfile, wait_ = TRUE) + + ### Click on the data wrangling tab ---- + app$click(selector = "a[data-value='Data Wrangling']") + app$wait_for_idle(timeout = 40000) + + ### Set the wrangling method to MFAZ ---- + app$set_inputs(`wrangle_data-wrangle` = "mfaz", wait_ = TRUE) + ### Select variables ---- + app$set_inputs(`wrangle_data-dos` = "dos", wait_ = FALSE) + app$set_inputs(`wrangle_data-dob` = "dob", wait_ = FALSE) + app$set_inputs(`wrangle_data-age` = "age", wait_ = FALSE) + app$set_inputs(`wrangle_data-sex` = "sex", wait_ = FALSE) + app$set_inputs(`wrangle_data-muac` = "muac", wait_ = FALSE) + + ### Click wrangle button ---- + app$click(input = "wrangle_data-apply_wrangle", wait_ = TRUE, timeout_ = 15000) + app$wait_for_value(output = "wrangle_data-wrangled", timeout = 40000) + + ### Get wrangled values ---- + column_names <- app$get_js(" + $('#wrangle_data-wrangled thead th').map(function() { + return $(this).text(); + }).get(); + ") |> as.character() + + ### Capture JavaScript expressions to return results ---- + js_results <- app$get_js("$('#wrangle_data-wrangled tbody tr').map(function() + {return $(this).text();}).get();") + + age_mo <- stringr::str_extract(js_results[[1]], "\\d{2}\\.\\d{2}") + age_28 <- stringr::str_extract(js_results[[5]], stringr::fixed("28")) + + testthat::expect_true("age_days" %in% column_names) + testthat::expect_equal(as.numeric(age_mo), 21.51) + testthat::expect_equal(as.numeric(age_28), 28) + + #### Stop the app ---- + app$stop() +}) + + +## ---- Data Wrangling: MUAC --------------------------------------------------- -### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } ### When age is given in categories ---- testthat::test_that( @@ -216,11 +342,6 @@ testthat::test_that( ## ---- Data Wrangling: WFHZ and MFAZ ------------------------------------------ -### Skip test on windows ---- -# if (identical(Sys.getenv("CI"), "true") && Sys.info()[["sysname"]] == "Windows") { -# skip("Skipping shinytest2 integration tests on Windows CI to reduce runtime") -# } - testthat::test_that( desc = "Server data wrangling works as expected for combined wrangling", { @@ -255,7 +376,7 @@ testthat::test_that( app$wait_for_idle(timeout = 40000) ### Select input variables ---- - app$set_inputs(`wrangle_data-wrangle` = "combined", wait_ = FALSE) + app$set_inputs(`wrangle_data-wrangle` = "combined", wait_ = TRUE) app$set_inputs(`wrangle_data-dos` = "", wait_ = FALSE) app$set_inputs(`wrangle_data-dob` = "", wait_ = FALSE) app$set_inputs(`wrangle_data-age` = "age", wait_ = FALSE) @@ -269,10 +390,86 @@ testthat::test_that( app$wait_for_value(output = "wrangle_data-wrangled", timeout = 40000) app$wait_for_idle(timeout = 15000) - + testthat::expect_true(app$get_js("$('#wrangle_data-wrangled').length > 0")) #### Stop the app ---- app$stop() } ) + + +### When user supplies dos and dob for age wrangling ---- +testthat::test_that(desc = "Server data wrangling wrangles age correctly in +combined data wrangling", { + ## Initialise app ---- + app <- shinytest2::AppDriver$new( + app_dir = testthat::test_path("fixtures"), + load_timeout = 120000, + wait = TRUE + ) + + ### Let the app load ---- + app$wait_for_idle(timeout = 40000) + + ### Click on the Data uploading navbar ---- + app$click(selector = "a[data-value='Data Upload']") + app$wait_for_idle(timeout = 40000) + + #### Create a toy dataset ---- + data <- data.frame( + dos = c("26/02/2026", "26/02/2026", "26/02/2026", "26/02/2026", "26/02/2026"), + dob = c("12/05/2024", "14/07/2025", "22/03/2024", "20/07/2024", "20/09/2021"), + age = c(NA, NA, NA, NA, 28), + sex = c("m", "f", "m", "f", "f"), + muac = c(121, 124, 132, 117, 100), + weight = c(12.3, 13.3, 14.5, 15.5, 10.0), + height = c(110.3, 117.2, 119.2, 80.6, 98.9) + ) + tempfile <- tempfile(fileext = ".csv") + write.csv(data, tempfile, row.names = FALSE) + + #### Upload onto the app ---- + app$upload_file(`upload_data-upload` = tempfile, wait_ = TRUE) + + ### Click on the data wrangling tab ---- + app$click(selector = "a[data-value='Data Wrangling']") + app$wait_for_idle(timeout = 40000) + + ### Select input variables ---- + app$set_inputs(`wrangle_data-wrangle` = "combined", wait_ = TRUE) + app$set_inputs(`wrangle_data-dos` = "dos", wait_ = FALSE) + app$set_inputs(`wrangle_data-dob` = "dob", wait_ = FALSE) + app$set_inputs(`wrangle_data-age` = "age", wait_ = FALSE) + app$set_inputs(`wrangle_data-sex` = "sex", wait_ = FALSE) + app$set_inputs(`wrangle_data-weight` = "weight", wait_ = FALSE) + app$set_inputs(`wrangle_data-height` = "height", wait_ = FALSE) + app$set_inputs(`wrangle_data-muac` = "muac", wait_ = FALSE) + + ### Click wrangle button ---- + app$click(input = "wrangle_data-apply_wrangle") + app$wait_for_value(output = "wrangle_data-wrangled", timeout = 40000) + + app$wait_for_idle(timeout = 15000) + + ## Get wrangled values ---- + column_names <- app$get_js(" + $('#wrangle_data-wrangled thead th').map(function() { + return $(this).text(); + }).get(); + ") |> as.character() + + ### Capture JavaScript expressions to return results ---- + js_results <- app$get_js("$('#wrangle_data-wrangled tbody tr').map(function() + {return $(this).text();}).get();") + + age_mo <- stringr::str_extract(js_results[[1]], "\\d{2}\\.\\d{2}") + age_28 <- stringr::str_extract(js_results[[5]], stringr::fixed("28")) + + testthat::expect_true("age_days" %in% column_names) + testthat::expect_equal(as.numeric(age_mo), 21.51) + testthat::expect_equal(as.numeric(age_28), 28) + + #### Stop the app ---- + app$stop() +}) From ee5ffe9b79ab8e9f231da131446a7ca090a29f73 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Thu, 26 Feb 2026 11:08:03 +0200 Subject: [PATCH 11/15] correct issues in man directory --- R/module-helpers-prevalence.R | 16 ++++++++++------ ...d_prevalence_call_prev_estimator_screening.Rd | 5 ++--- ..._prevalence_call_prev_estimator_screening2.Rd | 9 +++++++++ man/mod_prevalence_display_input_variables.Rd | 6 +++--- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/R/module-helpers-prevalence.R b/R/module-helpers-prevalence.R index 85bd830..631e21e 100644 --- a/R/module-helpers-prevalence.R +++ b/R/module-helpers-prevalence.R @@ -7,7 +7,7 @@ #' Display input variables dynamically, according to UI for screening #' #' -#' @param df,vars,source,indicator_surv,has_age Input variables collected +#' @param vars,source,indicator_surv,has_age Input variables collected #' from the UI and required to pass to `{mwana}` prevalence functions. #' #' @param ns A placeholder for Shiny module namespace. @@ -270,10 +270,9 @@ mod_prevalence_call_combined_prev_estimator <- function( #' Invoke mwana's prevalence functions from within module server according to #' user specifications in the UI #' -#' @param df,age,age_cat,muac,oedema,area1,area2,area3 Input variables collected -#' from the UI and required to pass to mwana::mw_estimate_prevalence_screening() -#' and mwana::mw_estimate_prevalence_screening2() -#' +#' @param df,age,muac,oedema,area1,area2,area3 Input variables collected +#' from the UI and required to pass to mwana::mw_estimate_prevalence_screening(). +# #' @returns A summary tibble for the descriptive statistics about wasting based #' on MUAC, with no confidence intervals. #' @@ -318,8 +317,13 @@ mod_prevalence_call_prev_estimator_screening <- function( #' Invoke mwana's prevalence functions from within module server according to #' user specifications in the UI #' -#' @inheritParams mod_prevalence_call_prev_estimator_screening +#' @param df,age_cat,muac,oedema,area1,area2,area3 Input variables collected +#' from the UI and required to pass to mwana::mw_estimate_prevalence_screening2() +#' +#' @returns A summary tibble for the descriptive statistics about wasting based +#' on MUAC, with no confidence intervals. #' +#' @keywords internal #' mod_prevalence_call_prev_estimator_screening2 <- function( df, age_cat, muac, oedema = NULL, diff --git a/man/mod_prevalence_call_prev_estimator_screening.Rd b/man/mod_prevalence_call_prev_estimator_screening.Rd index 7b32940..a29132b 100644 --- a/man/mod_prevalence_call_prev_estimator_screening.Rd +++ b/man/mod_prevalence_call_prev_estimator_screening.Rd @@ -16,9 +16,8 @@ mod_prevalence_call_prev_estimator_screening( ) } \arguments{ -\item{df, age, age_cat, muac, oedema, area1, area2, area3}{Input variables collected -from the UI and required to pass to mwana::mw_estimate_prevalence_screening() -and mwana::mw_estimate_prevalence_screening2()} +\item{df, age, muac, oedema, area1, area2, area3}{Input variables collected +from the UI and required to pass to mwana::mw_estimate_prevalence_screening().} } \value{ A summary tibble for the descriptive statistics about wasting based diff --git a/man/mod_prevalence_call_prev_estimator_screening2.Rd b/man/mod_prevalence_call_prev_estimator_screening2.Rd index 1dd3d22..1c48446 100644 --- a/man/mod_prevalence_call_prev_estimator_screening2.Rd +++ b/man/mod_prevalence_call_prev_estimator_screening2.Rd @@ -15,7 +15,16 @@ mod_prevalence_call_prev_estimator_screening2( area3 ) } +\arguments{ +\item{df, age_cat, muac, oedema, area1, area2, area3}{Input variables collected +from the UI and required to pass to mwana::mw_estimate_prevalence_screening2()} +} +\value{ +A summary tibble for the descriptive statistics about wasting based +on MUAC, with no confidence intervals. +} \description{ Invoke mwana's prevalence functions from within module server according to user specifications in the UI } +\keyword{internal} diff --git a/man/mod_prevalence_display_input_variables.Rd b/man/mod_prevalence_display_input_variables.Rd index 659ef51..5ab673d 100644 --- a/man/mod_prevalence_display_input_variables.Rd +++ b/man/mod_prevalence_display_input_variables.Rd @@ -13,10 +13,10 @@ mod_prevalence_display_input_variables( ) } \arguments{ -\item{ns}{A placeholder for Shiny module namespace.} - -\item{df, vars, source, indicator_surv, has_age}{Input variables collected +\item{vars, source, indicator_surv, has_age}{Input variables collected from the UI and required to pass to \code{{mwana}} prevalence functions.} + +\item{ns}{A placeholder for Shiny module namespace.} } \description{ Display input variables dynamically, according to UI for screening From 6ea2b18915b64a5e624762e3e1f88b47bf173acf Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Thu, 26 Feb 2026 11:11:04 +0200 Subject: [PATCH 12/15] Increment version number to 0.2.0 --- DESCRIPTION | 2 +- NEWS.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0df4763..61ca0f4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: mwanaApp Title: mwana GUI -Version: 0.1.0 +Version: 0.2.0 Authors@R: person(given = "Tomás", family = "Zaba", diff --git a/NEWS.md b/NEWS.md index 9959cda..c13c4e7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,5 @@ +# mwanaApp 0.2.0 + ## mwanaApp v0.1.0 -+ First release of a feature-complete app. \ No newline at end of file ++ First release of a feature-complete app. From 6bebcb9c5e77443727ed8c60562054d8f7421bf1 Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Thu, 26 Feb 2026 11:15:10 +0200 Subject: [PATCH 13/15] update CITATION and README.md --- README.md | 8 ++++---- inst/CITATION | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 074635d..118ca03 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ citation("mwanaApp") Tomás Zaba (2026). _mwanaApp: A seamless graphical interface to the mwana R package for data wrangling, plausibility checks, and - prevalence estimation_. R package version 0.1.0, - . + prevalence estimation_. R package version 0.2.0, + . A BibTeX entry for LaTeX users is @@ -63,6 +63,6 @@ citation("mwanaApp") title = {mwanaApp: A seamless graphical interface to the mwana R package for data wrangling, plausibility checks, and prevalence estimation}, author = {{Tomás Zaba}}, year = {2026}, - note = {R package version 0.1.0}, - url = {https://github.com/mphimo/mwanaApp.git}, + note = {R package version 0.2.0}, + url = {https://github.com/mphimo/mwanaApp}, } diff --git a/inst/CITATION b/inst/CITATION index 0cf8e7f..56e6169 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -4,6 +4,6 @@ bibentry( title = "mwanaApp: A seamless graphical interface to the mwana R package for data wrangling, plausibility checks, and prevalence estimation", author = person("Tomás Zaba"), year = 2026, - note = "R package version 0.1.0", - url = "https://github.com/mphimo/mwanaApp.git" + note = "R package version 0.2.0", + url = "https://github.com/mphimo/mwanaApp" ) From 8b6d92efcac561fba74b1d3b06e4a19f4cdae40f Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Thu, 26 Feb 2026 11:32:52 +0200 Subject: [PATCH 14/15] update NEWS.md --- NEWS.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/NEWS.md b/NEWS.md index c13c4e7..5b7bcc2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,23 @@ # mwanaApp 0.2.0 +### New features ✨ + ++ Enabled estimation of age in months from the date of data collection and the +child’s date of birth, and added a corresponding note in the user guide. + +### General updates + ++ Revised the documentation for the app’s user‑interface and server modules. + +### Bug fixes + ++ Resolved an issue in which the app’s user interface failed to display the input + field for supplying survey weights in the weight‑for‑height- and combined‑prevalence + tabs. + ++ Resolved an issue where hyperlinks in the app were directing users to a +non‑existent page. + ## mwanaApp v0.1.0 + First release of a feature-complete app. From 08882e50ae3c7a97fe1effe643b123be428f054e Mon Sep 17 00:00:00 2001 From: tomaszaba Date: Thu, 26 Feb 2026 12:26:28 +0200 Subject: [PATCH 15/15] revert user feedback message to its former to fix codecov patch failure --- R/module-data-wrangling.R | 8 ++++---- R/module-ipc-aman-check.R | 6 +++--- R/module-plausibility-check.R | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/R/module-data-wrangling.R b/R/module-data-wrangling.R index d1000a6..dcf1294 100644 --- a/R/module-data-wrangling.R +++ b/R/module-data-wrangling.R @@ -148,22 +148,22 @@ module_server_wrangling <- function(id, data) { if (input$wrangle == "wfhz") { if (any(!nzchar(c(input$sex, input$weight, input$height)))) { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } else if (input$wrangle == "mfaz") { if (any(!nzchar(c(input$age, input$sex, input$muac)))) { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } else if (input$wrangle == "muac") { if (any(!nzchar(c(input$sex, input$muac)))) { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } else { if (any(!nzchar(c(input$age, input$sex, input$weight, input$height, input$muac)))) { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } diff --git a/R/module-ipc-aman-check.R b/R/module-ipc-aman-check.R index 853f494..47bf934 100644 --- a/R/module-ipc-aman-check.R +++ b/R/module-ipc-aman-check.R @@ -125,17 +125,17 @@ module_server_ipccheck <- function(id, data) { if (input$ipccheck == "survey") { if (is.null(input$area1) || is.null(input$psu) || input$area1 == "" || input$psu == "") { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } else if (input$ipccheck == "screening") { if (is.null(input$area1) || is.null(input$sites) || input$area1 == "" || input$sites == "") { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } else if (input$ipccheck == "sentinel") { if (is.null(input$area1) || is.null(input$ssites) || input$area1 == "" || input$ssites == "") { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } diff --git a/R/module-plausibility-check.R b/R/module-plausibility-check.R index b14faef..95578b8 100644 --- a/R/module-plausibility-check.R +++ b/R/module-plausibility-check.R @@ -130,19 +130,19 @@ module_server_plausibility_check <- function(id, data) { required_vars <- c(input$sex, input$weight, input$height, input$age, input$flags) if (any(required_vars == "" | is.null(required_vars))) { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } else if (input$method == "mfaz") { required_vars <- c(input$age, input$sex, input$muac, input$flags) if (any(required_vars == "" | is.null(required_vars))) { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } else if (input$method == "muac") { required_vars <- c(input$sex, input$muac, input$flags) if (any(required_vars == "" | is.null(required_vars))) { valid <- FALSE - message <- "Please select all required variables for the chosen method (see the UI input list)." + message <- "Please select all required variables." } } else { valid <- FALSE