From 1ad4601fc1f5fa2fa893fa613197899f92d0d378 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:32:35 +0000 Subject: [PATCH 01/16] Initial plan From 43083dfca90eae71904da187457d967f101e2d3b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:40:43 +0000 Subject: [PATCH 02/16] Fix tm_outliers to work without join keys Co-authored-by: m7pr <133694481+m7pr@users.noreply.github.com> --- R/tm_outliers.R | 44 ++++++++----- tests/testthat/test-shinytest2-tm_outliers.R | 66 ++++++++++++++++++++ 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index e15ba7635..e7af7640e 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -616,25 +616,37 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, ) # ANL_OUTLIER_EXTENDED is the base table - qenv <- teal.code::eval_code( - qenv, - substitute( - expr = { - ANL_OUTLIER_EXTENDED <- dplyr::left_join( - ANL_OUTLIER, - dplyr::select( - dataname, - dplyr::setdiff(names(dataname), dplyr::setdiff(names(ANL_OUTLIER), join_keys)) - ), - by = join_keys + join_keys <- as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) + + if (length(join_keys) == 0) { + # No join keys defined - working with single dataset, no join needed + # ANL_OUTLIER already contains all necessary columns + qenv <- teal.code::eval_code( + qenv, + quote(ANL_OUTLIER_EXTENDED <- ANL_OUTLIER) + ) + } else { + # Join keys exist - perform left join as before + qenv <- teal.code::eval_code( + qenv, + substitute( + expr = { + ANL_OUTLIER_EXTENDED <- dplyr::left_join( + ANL_OUTLIER, + dplyr::select( + dataname, + dplyr::setdiff(names(dataname), dplyr::setdiff(names(ANL_OUTLIER), join_keys)) + ), + by = join_keys + ) + }, + env = list( + dataname = as.name(dataname_first), + join_keys = join_keys ) - }, - env = list( - dataname = as.name(dataname_first), - join_keys = as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) ) ) - ) + } qenv <- if (length(categorical_var) > 0) { qenv <- teal.code::eval_code( diff --git a/tests/testthat/test-shinytest2-tm_outliers.R b/tests/testthat/test-shinytest2-tm_outliers.R index 9468f7653..4bb1166bd 100644 --- a/tests/testthat/test-shinytest2-tm_outliers.R +++ b/tests/testthat/test-shinytest2-tm_outliers.R @@ -319,3 +319,69 @@ testthat::test_that("e2e - tm_outliers: Outlier table is displayed with proper c app_driver$stop() }) + +testthat::test_that("e2e - tm_outliers: works without join keys", { + # Test for issue where tm_outliers fails if data has no joining keys + # This test reproduces the exact scenario described in the issue + testthat::skip("chromium") + skip_if_too_deep(5) + + # Create data without join keys (as per issue description) + data <- teal.data::teal_data() + data <- within(data, { + CO2 <- CO2 # nolint: object_name + }) + # Note: No join_keys defined here, which should NOT cause the issue after fix + + vars <- teal.transform::choices_selected( + teal.transform::variable_choices( + data[["CO2"]], + c("Plant", "Type", "Treatment") + ) + ) + + # This should NOT fail after the fix + app_driver <- init_teal_app_driver( + data = data, + modules = tm_outliers( + outlier_var = list( + teal.transform::data_extract_spec( + dataname = "CO2", + select = teal.transform::select_spec( + label = "Select variable:", + choices = teal.transform::variable_choices(data[["CO2"]], c("conc", "uptake")), + selected = "uptake", + multiple = FALSE, + fixed = FALSE + ) + ) + ), + categorical_var = list( + teal.transform::data_extract_spec( + dataname = "CO2", + filter = teal.transform::filter_spec( + vars = vars, + choices = teal.transform::value_choices(data[["CO2"]], vars$selected), + selected = teal.transform::value_choices(data[["CO2"]], vars$selected), + multiple = TRUE + ) + ) + ), + ggplot2_args = list( + teal.widgets::ggplot2_args( + labs = list(subtitle = "Plot generated by Outliers Module") + ) + ) + ) + ) + + app_driver$expect_no_shiny_error() + + # Verify the module loads correctly + testthat::expect_equal( + app_driver$get_text("#teal-teal_modules-active_tab .active"), + "Outliers Module" + ) + + app_driver$stop() +}) From d9f67240445f5aeb9ba40671b11186a4b2ae1e45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:44:07 +0000 Subject: [PATCH 03/16] Add documentation example for tm_outliers without join keys Co-authored-by: m7pr <133694481+m7pr@users.noreply.github.com> --- R/tm_outliers.R | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index e7af7640e..2fd22d15a 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -51,6 +51,54 @@ #' {{ next_example }} #' @examples #' +#' # simple data example without join keys +#' data <- teal_data() +#' data <- within(data, { +#' CO2 <- CO2 +#' }) +#' +#' vars <- choices_selected(variable_choices(data[["CO2"]], c("Plant", "Type", "Treatment"))) +#' +#' app <- init( +#' data = data, +#' modules = modules( +#' tm_outliers( +#' outlier_var = list( +#' data_extract_spec( +#' dataname = "CO2", +#' select = select_spec( +#' label = "Select variable:", +#' choices = variable_choices(data[["CO2"]], c("conc", "uptake")), +#' selected = "uptake", +#' multiple = FALSE, +#' fixed = FALSE +#' ) +#' ) +#' ), +#' categorical_var = list( +#' data_extract_spec( +#' dataname = "CO2", +#' filter = filter_spec( +#' vars = vars, +#' choices = value_choices(data[["CO2"]], vars$selected), +#' selected = value_choices(data[["CO2"]], vars$selected), +#' multiple = TRUE +#' ) +#' ) +#' ) +#' ) +#' ) +#' ) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +#' +#' @examplesShinylive +#' library(teal.modules.general) +#' interactive <- function() TRUE +#' {{ next_example }} +#' @examples +#' #' # general data example #' data <- teal_data() #' data <- within(data, { From 4de5d68852bfd1198c6e012462084882c5f3d048 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:51:53 +0000 Subject: [PATCH 04/16] [skip style] [skip vbump] Restyle files --- R/tm_outliers.R | 2 +- tests/testthat/test-shinytest2-tm_outliers.R | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index 2fd22d15a..eb03dc7e5 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -665,7 +665,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, # ANL_OUTLIER_EXTENDED is the base table join_keys <- as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) - + if (length(join_keys) == 0) { # No join keys defined - working with single dataset, no join needed # ANL_OUTLIER already contains all necessary columns diff --git a/tests/testthat/test-shinytest2-tm_outliers.R b/tests/testthat/test-shinytest2-tm_outliers.R index 4bb1166bd..5cdc8923a 100644 --- a/tests/testthat/test-shinytest2-tm_outliers.R +++ b/tests/testthat/test-shinytest2-tm_outliers.R @@ -325,21 +325,21 @@ testthat::test_that("e2e - tm_outliers: works without join keys", { # This test reproduces the exact scenario described in the issue testthat::skip("chromium") skip_if_too_deep(5) - + # Create data without join keys (as per issue description) data <- teal.data::teal_data() data <- within(data, { CO2 <- CO2 # nolint: object_name }) # Note: No join_keys defined here, which should NOT cause the issue after fix - + vars <- teal.transform::choices_selected( teal.transform::variable_choices( - data[["CO2"]], + data[["CO2"]], c("Plant", "Type", "Treatment") ) ) - + # This should NOT fail after the fix app_driver <- init_teal_app_driver( data = data, @@ -374,14 +374,14 @@ testthat::test_that("e2e - tm_outliers: works without join keys", { ) ) ) - + app_driver$expect_no_shiny_error() - + # Verify the module loads correctly testthat::expect_equal( app_driver$get_text("#teal-teal_modules-active_tab .active"), "Outliers Module" ) - + app_driver$stop() }) From 8063a70e8b7e1ebacc109994938fe73f5c178981 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:57:56 +0000 Subject: [PATCH 05/16] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/tm_outliers.Rd | 50 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/man/tm_outliers.Rd b/man/tm_outliers.Rd index f8c15278d..1e3683fcb 100644 --- a/man/tm_outliers.Rd +++ b/man/tm_outliers.Rd @@ -102,6 +102,49 @@ To learn more please refer to the vignette \examples{ +# simple data example without join keys +data <- teal_data() +data <- within(data, { + CO2 <- CO2 +}) + +vars <- choices_selected(variable_choices(data[["CO2"]], c("Plant", "Type", "Treatment"))) + +app <- init( + data = data, + modules = modules( + tm_outliers( + outlier_var = list( + data_extract_spec( + dataname = "CO2", + select = select_spec( + label = "Select variable:", + choices = variable_choices(data[["CO2"]], c("conc", "uptake")), + selected = "uptake", + multiple = FALSE, + fixed = FALSE + ) + ) + ), + categorical_var = list( + data_extract_spec( + dataname = "CO2", + filter = filter_spec( + vars = vars, + choices = value_choices(data[["CO2"]], vars$selected), + selected = value_choices(data[["CO2"]], vars$selected), + multiple = TRUE + ) + ) + ) + ) + ) +) +if (interactive()) { + shinyApp(app$ui, app$server) +} + + # general data example data <- teal_data() data <- within(data, { @@ -197,11 +240,16 @@ if (interactive()) { \section{Examples in Shinylive}{ \describe{ \item{example-1}{ + \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQAxLtG0YqEbr8UKRQunAAHrD+egDutKQAFkSCpLoAVkQqugDWcKyiSkEhBsZc1AD6RVA2hcGhRrpxiSrsVbi6IEq6ugDCAPIATCW9g0oAvooQSlososMESbQEYuWicCIacPzsM4y0UPQi5QuZy6KtdcDACmD9AzcAug-tBOw3AArUUGQ37Td2rFQcF+un+jDgwXgPzAclh7jQqGGKnibwg3SqugAvIE6rguro+EIRHNsYThGJUd1uqQYOVkqQ6DJzviqbp6YzGOVdljdHRRKRKayqVVyhFSMwNKsga8WUL0XVoPAeTc7r9ZXK1htUtjNXBJaJpYK5ayvvR1sqwABldZ61K7faHOCINVo42sk5LMQ8+0HI4es4XEJXFWjMBPF5vQgkAggm6CVAhXI3WF411u7q6zb8C3xxPAvDq40wYSaaI8gBiAEEADKW5yp9NUky0cJbCs1uuF1mTN09oVyBtCgjBOASIh7YcVbnYvkCrs4kKi8Li9SkKV6o3GqqKvTYkP3Atp43N6jkRg8k9n9cyo9u3Yk3T3weN-1e7EzaiCODHRYBqrB25Q3DR9ZgAEkzch+AHedWQgtt324L8f1OCl-2uQCD2A+9wJtLNoNvIsS1oMtsQcFwYN0Ps5SoqkaL7SZJloExdHYFQz1XbQ4BsWxOldUQEhUVhK3QdgEVAwRaHaMS1kYHRGEmMYlDAMYHiAA}{Open in Shinylive} + \if{html}{\out{}} + \if{html}{\out{}} + } + \item{example-2}{ \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQAxLqkQ11XfxQpFC6cAAesKgiSoHBBsZc1AD6sVA2MUEhRroA7rSkABYq7Km4uiBKuroAwgDyAEzxNQ2VzfXAwApgqIy0MCysSQDWcKxdALrjTaJwAI5JIhDsEIxEOex19XKKEAC+OwBWRCrDo6IlmbbZRycjrOc3EKccXZtdZa8teLpdPX0Dzy623cWhYoiaBAKxwIYiSMxEGjg-HYoN6UHoIiSkOhYguwQ6n3qE3GZQI7C6AAVqFAyO8fmA7KxUHA6V07Iw4EF4LSwNsdko0Kgmip8uSIFVUroALwBTK4Vp8IQicEyxXCXGtKqkGBJIiCUh0GTnTVVXR6g20GRJVHS3R0USkMWm52y4JJcKkZgaOHMskml2ummwPQywnvf0u+FwDS2qPe0S+p0Bl3U+hwfyhsAAZXT0dIulRtHRIkQ4fFyed2NoMJVBZYRYxcCxUOruNSBLAbzAk1J5MIJAIrLAglQwRGQLk8vLFaqcfI-FtXRHY5ZeAjAZgwk0URDugAYgBBAAyWecU5nppMtDCSNth5PrmnyZ2FZfLsnEYIQTgEiIvS-yQ2jK9qOuugbumEnrqKQPrRkmFapNA8CLp2XznjOV7UOQjC2ph2GwX6T4VqitYkehF5VjWtqgtQghNpRbaZB2XY9nWjCiAAJHOSIfkRybcQuMo0XRzY4uc7adKhRLdiSbGcQJvEXlUm5YbQO62g4LhgVUb4Brppr6W+Ow7LQJi6OwKjYdB2hwDYtgVOWohFBArAHug7CChxgi0GUnkzIwOiMDsuxKGAuzjEAA}{Open in Shinylive} \if{html}{\out{}} \if{html}{\out{}} } - \item{example-2}{ + \item{example-3}{ \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQAxLoDCAEQCSAMreuvxQpFC6cAAesKgiSmERBsZc1AD6SVA2ieGRRroA7rSkABYq7Fm4uiBKuroAgr6BADIpumkYWYiIjE2tSgC+ihAAVkQq6QDWcKyilXm2BfxwJlDCpOkE-LSiBOnjkzNzwNDw81lyALruaxrpWiyi6VD8otTtZ2LsAGK01ORGOxdg4XNVRGh4hwssBgAowP0WvCrldqrsMHdSERGHJcUpHoxRO0CKUJgQxOlRHARBo4Px2ATaFB6CItqTaOSLnlYfDEcjUaZ1JsCc9Xu88RB3EpIe0VCV2HVQnldABeJURXCKvhCEREtXa4TfRX1UgwdJEQSkOgyebG+q6C1W2gyB4sVW6OiiUgKiD2v3qqDpGKkZj3USoOAEH3+-1ZL7u3nNJF4O0xqk00ju9ORzbhyPRmP+6jM6kJsCBak53SM5kiRDwzW+wt+klksTumssuBsttciI8hFJ-nVKO8gDirjwunhACEALJYADSWAAjPDcY3m37s7T+GWGhOG6nCzANrR4no1T8Gi1As5N1v6iZaNE6e7r7fXE3myMf8e5A+LbhHAEjYhy3Cuow7qet6x71FkQbRCGQqUhGo7foWcawJe06DgMKYYTGz4AjI7rEYCqH5nB-oih2TyAVurYcu2aqPNQgjdkxnILP2cJ4cmKLVCKAAkO7kPwAHUdula7nR7Gcey3EwnxfJgIJ1ZPKJMniZJhEnmeF7uqCX6Pr+hZmX6Fm6GZIwjLQJi6MCqihpoOg2LYtRNqI5QQKwDToOwkLCYItDVEFVKMDoOJDEoYCDFcQA}{Open in Shinylive} \if{html}{\out{}} \if{html}{\out{}} From 113d0d8c69a550a8d440a0d63417d97973ec3b83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:16:29 +0000 Subject: [PATCH 06/16] Fix merge_expression_srv to handle single datasets without join keys Co-authored-by: m7pr <133694481+m7pr@users.noreply.github.com> --- R/tm_outliers.R | 65 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index eb03dc7e5..4f09e7651 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -486,11 +486,66 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, } }) - anl_merged_input <- teal.transform::merge_expression_srv( - selector_list = reactive_select_input, - datasets = data, - merge_function = "dplyr::inner_join" - ) + anl_merged_input <- reactive({ + # Check if we have join keys available + dataname_first <- names(data())[[1]] + join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] + + if (length(join_keys) == 0) { + # No join keys available - create a simple data extract without merging + # This handles the case where we have only one dataset without join keys + selectors <- reactive_select_input() + + # Get the first (primary) selector for the outlier variable + outlier_selector <- selectors$outlier_var + + if (!is.null(outlier_selector)) { + sel_result <- outlier_selector() + if (!is.null(sel_result)) { + # Create a simple ANL assignment without merging + dataname <- sel_result$dataname + + # Handle categorical variable if present + categorical_selector <- selectors$categorical_var + categorical_cols <- character(0) + if (!is.null(categorical_selector)) { + cat_result <- categorical_selector() + if (!is.null(cat_result) && !is.null(cat_result$filter)) { + # Extract filter variable names + filter_vars <- cat_result$filter + if (length(filter_vars) > 0) { + categorical_cols <- names(filter_vars) + } + } + } + + return(list( + expr = substitute(ANL <- dataname, list(dataname = as.name(dataname))), + columns_source = list( + outlier_var = sel_result$select, + categorical_var = categorical_cols + ) + )) + } + } + + # Fallback - return first dataset + return(list( + expr = substitute(ANL <- dataname, list(dataname = as.name(dataname_first))), + columns_source = list( + outlier_var = character(0), + categorical_var = character(0) + ) + )) + } else { + # Join keys exist - use the standard merge approach + teal.transform::merge_expression_srv( + selector_list = reactive_select_input, + datasets = data, + merge_function = "dplyr::inner_join" + )() + } + }) anl_merged_q <- reactive({ req(anl_merged_input()) From 322a7170411c5f76ecdbeb5606ee9dd38a1d75ad Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:25:04 +0000 Subject: [PATCH 07/16] [skip style] [skip vbump] Restyle files --- R/tm_outliers.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index 4f09e7651..e88e1d558 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -490,21 +490,21 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, # Check if we have join keys available dataname_first <- names(data())[[1]] join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] - + if (length(join_keys) == 0) { # No join keys available - create a simple data extract without merging # This handles the case where we have only one dataset without join keys selectors <- reactive_select_input() - + # Get the first (primary) selector for the outlier variable outlier_selector <- selectors$outlier_var - + if (!is.null(outlier_selector)) { sel_result <- outlier_selector() if (!is.null(sel_result)) { # Create a simple ANL assignment without merging dataname <- sel_result$dataname - + # Handle categorical variable if present categorical_selector <- selectors$categorical_var categorical_cols <- character(0) @@ -518,7 +518,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, } } } - + return(list( expr = substitute(ANL <- dataname, list(dataname = as.name(dataname))), columns_source = list( @@ -528,7 +528,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, )) } } - + # Fallback - return first dataset return(list( expr = substitute(ANL <- dataname, list(dataname = as.name(dataname_first))), From 0e6b8024d96762de0ceae5027c72e5324c3d8219 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:39:33 +0000 Subject: [PATCH 08/16] Simplify tm_outliers fix for datasets without join keys Co-authored-by: m7pr <133694481+m7pr@users.noreply.github.com> --- R/tm_outliers.R | 116 +++++++----------------------------------------- 1 file changed, 15 insertions(+), 101 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index e88e1d558..54fad2527 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -51,54 +51,6 @@ #' {{ next_example }} #' @examples #' -#' # simple data example without join keys -#' data <- teal_data() -#' data <- within(data, { -#' CO2 <- CO2 -#' }) -#' -#' vars <- choices_selected(variable_choices(data[["CO2"]], c("Plant", "Type", "Treatment"))) -#' -#' app <- init( -#' data = data, -#' modules = modules( -#' tm_outliers( -#' outlier_var = list( -#' data_extract_spec( -#' dataname = "CO2", -#' select = select_spec( -#' label = "Select variable:", -#' choices = variable_choices(data[["CO2"]], c("conc", "uptake")), -#' selected = "uptake", -#' multiple = FALSE, -#' fixed = FALSE -#' ) -#' ) -#' ), -#' categorical_var = list( -#' data_extract_spec( -#' dataname = "CO2", -#' filter = filter_spec( -#' vars = vars, -#' choices = value_choices(data[["CO2"]], vars$selected), -#' selected = value_choices(data[["CO2"]], vars$selected), -#' multiple = TRUE -#' ) -#' ) -#' ) -#' ) -#' ) -#' ) -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } -#' -#' @examplesShinylive -#' library(teal.modules.general) -#' interactive <- function() TRUE -#' {{ next_example }} -#' @examples -#' #' # general data example #' data <- teal_data() #' data <- within(data, { @@ -487,58 +439,24 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, }) anl_merged_input <- reactive({ - # Check if we have join keys available dataname_first <- names(data())[[1]] join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] - + if (length(join_keys) == 0) { - # No join keys available - create a simple data extract without merging - # This handles the case where we have only one dataset without join keys - selectors <- reactive_select_input() - - # Get the first (primary) selector for the outlier variable - outlier_selector <- selectors$outlier_var - - if (!is.null(outlier_selector)) { - sel_result <- outlier_selector() - if (!is.null(sel_result)) { - # Create a simple ANL assignment without merging - dataname <- sel_result$dataname - - # Handle categorical variable if present - categorical_selector <- selectors$categorical_var - categorical_cols <- character(0) - if (!is.null(categorical_selector)) { - cat_result <- categorical_selector() - if (!is.null(cat_result) && !is.null(cat_result$filter)) { - # Extract filter variable names - filter_vars <- cat_result$filter - if (length(filter_vars) > 0) { - categorical_cols <- names(filter_vars) - } - } - } - - return(list( - expr = substitute(ANL <- dataname, list(dataname = as.name(dataname))), - columns_source = list( - outlier_var = sel_result$select, - categorical_var = categorical_cols - ) - )) - } - } - - # Fallback - return first dataset - return(list( + # No join keys - create simple assignment expression + list( expr = substitute(ANL <- dataname, list(dataname = as.name(dataname_first))), columns_source = list( - outlier_var = character(0), - categorical_var = character(0) + outlier_var = reactive_select_input()$outlier_var()$select, + categorical_var = if (!is.null(reactive_select_input()$categorical_var)) { + names(reactive_select_input()$categorical_var()$filter) + } else { + character(0) + } ) - )) + ) } else { - # Join keys exist - use the standard merge approach + # Join keys exist - use standard merge teal.transform::merge_expression_srv( selector_list = reactive_select_input, datasets = data, @@ -720,16 +638,12 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, # ANL_OUTLIER_EXTENDED is the base table join_keys <- as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) - + if (length(join_keys) == 0) { - # No join keys defined - working with single dataset, no join needed - # ANL_OUTLIER already contains all necessary columns - qenv <- teal.code::eval_code( - qenv, - quote(ANL_OUTLIER_EXTENDED <- ANL_OUTLIER) - ) + # No join keys - no join needed + qenv <- teal.code::eval_code(qenv, quote(ANL_OUTLIER_EXTENDED <- ANL_OUTLIER)) } else { - # Join keys exist - perform left join as before + # Join keys exist - perform left join qenv <- teal.code::eval_code( qenv, substitute( From c0360c6477c4751e4586e4ea7ce5a2bc7c3240ca Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:06:15 +0000 Subject: [PATCH 09/16] [skip style] [skip vbump] Restyle files --- R/tm_outliers.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index 54fad2527..bcfde2215 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -441,7 +441,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, anl_merged_input <- reactive({ dataname_first <- names(data())[[1]] join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] - + if (length(join_keys) == 0) { # No join keys - create simple assignment expression list( @@ -638,7 +638,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, # ANL_OUTLIER_EXTENDED is the base table join_keys <- as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) - + if (length(join_keys) == 0) { # No join keys - no join needed qenv <- teal.code::eval_code(qenv, quote(ANL_OUTLIER_EXTENDED <- ANL_OUTLIER)) From 93b1674cee35af942d3217e708b03e9f816fe9f3 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:13:34 +0000 Subject: [PATCH 10/16] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/tm_outliers.Rd | 50 +--------------------------------------------- 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/man/tm_outliers.Rd b/man/tm_outliers.Rd index 1e3683fcb..f8c15278d 100644 --- a/man/tm_outliers.Rd +++ b/man/tm_outliers.Rd @@ -102,49 +102,6 @@ To learn more please refer to the vignette \examples{ -# simple data example without join keys -data <- teal_data() -data <- within(data, { - CO2 <- CO2 -}) - -vars <- choices_selected(variable_choices(data[["CO2"]], c("Plant", "Type", "Treatment"))) - -app <- init( - data = data, - modules = modules( - tm_outliers( - outlier_var = list( - data_extract_spec( - dataname = "CO2", - select = select_spec( - label = "Select variable:", - choices = variable_choices(data[["CO2"]], c("conc", "uptake")), - selected = "uptake", - multiple = FALSE, - fixed = FALSE - ) - ) - ), - categorical_var = list( - data_extract_spec( - dataname = "CO2", - filter = filter_spec( - vars = vars, - choices = value_choices(data[["CO2"]], vars$selected), - selected = value_choices(data[["CO2"]], vars$selected), - multiple = TRUE - ) - ) - ) - ) - ) -) -if (interactive()) { - shinyApp(app$ui, app$server) -} - - # general data example data <- teal_data() data <- within(data, { @@ -240,16 +197,11 @@ if (interactive()) { \section{Examples in Shinylive}{ \describe{ \item{example-1}{ - \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQAxLtG0YqEbr8UKRQunAAHrD+egDutKQAFkSCpLoAVkQqugDWcKyiSkEhBsZc1AD6RVA2hcGhRrpxiSrsVbi6IEq6ugDCAPIATCW9g0oAvooQSlososMESbQEYuWicCIacPzsM4y0UPQi5QuZy6KtdcDACmD9AzcAug-tBOw3AArUUGQ37Td2rFQcF+un+jDgwXgPzAclh7jQqGGKnibwg3SqugAvIE6rguro+EIRHNsYThGJUd1uqQYOVkqQ6DJzviqbp6YzGOVdljdHRRKRKayqVVyhFSMwNKsga8WUL0XVoPAeTc7r9ZXK1htUtjNXBJaJpYK5ayvvR1sqwABldZ61K7faHOCINVo42sk5LMQ8+0HI4es4XEJXFWjMBPF5vQgkAggm6CVAhXI3WF411u7q6zb8C3xxPAvDq40wYSaaI8gBiAEEADKW5yp9NUky0cJbCs1uuF1mTN09oVyBtCgjBOASIh7YcVbnYvkCrs4kKi8Li9SkKV6o3GqqKvTYkP3Atp43N6jkRg8k9n9cyo9u3Yk3T3weN-1e7EzaiCODHRYBqrB25Q3DR9ZgAEkzch+AHedWQgtt324L8f1OCl-2uQCD2A+9wJtLNoNvIsS1oMtsQcFwYN0Ps5SoqkaL7SZJloExdHYFQz1XbQ4BsWxOldUQEhUVhK3QdgEVAwRaHaMS1kYHRGEmMYlDAMYHiAA}{Open in Shinylive} - \if{html}{\out{}} - \if{html}{\out{}} - } - \item{example-2}{ \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQAxLqkQ11XfxQpFC6cAAesKgiSoHBBsZc1AD6sVA2MUEhRroA7rSkABYq7Km4uiBKuroAwgDyAEzxNQ2VzfXAwApgqIy0MCysSQDWcKxdALrjTaJwAI5JIhDsEIxEOex19XKKEAC+OwBWRCrDo6IlmbbZRycjrOc3EKccXZtdZa8teLpdPX0Dzy623cWhYoiaBAKxwIYiSMxEGjg-HYoN6UHoIiSkOhYguwQ6n3qE3GZQI7C6AAVqFAyO8fmA7KxUHA6V07Iw4EF4LSwNsdko0Kgmip8uSIFVUroALwBTK4Vp8IQicEyxXCXGtKqkGBJIiCUh0GTnTVVXR6g20GRJVHS3R0USkMWm52y4JJcKkZgaOHMskml2ummwPQywnvf0u+FwDS2qPe0S+p0Bl3U+hwfyhsAAZXT0dIulRtHRIkQ4fFyed2NoMJVBZYRYxcCxUOruNSBLAbzAk1J5MIJAIrLAglQwRGQLk8vLFaqcfI-FtXRHY5ZeAjAZgwk0URDugAYgBBAAyWecU5nppMtDCSNth5PrmnyZ2FZfLsnEYIQTgEiIvS-yQ2jK9qOuugbumEnrqKQPrRkmFapNA8CLp2XznjOV7UOQjC2ph2GwX6T4VqitYkehF5VjWtqgtQghNpRbaZB2XY9nWjCiAAJHOSIfkRybcQuMo0XRzY4uc7adKhRLdiSbGcQJvEXlUm5YbQO62g4LhgVUb4Brppr6W+Ow7LQJi6OwKjYdB2hwDYtgVOWohFBArAHug7CChxgi0GUnkzIwOiMDsuxKGAuzjEAA}{Open in Shinylive} \if{html}{\out{}} \if{html}{\out{}} } - \item{example-3}{ + \item{example-2}{ \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQAxLoDCAEQCSAMreuvxQpFC6cAAesKgiSmERBsZc1AD6SVA2ieGRRroA7rSkABYq7Fm4uiBKuroAgr6BADIpumkYWYiIjE2tSgC+ihAAVkQq6QDWcKyilXm2BfxwJlDCpOkE-LSiBOnjkzNzwNDw81lyALruaxrpWiyi6VD8otTtZ2LsAGK01ORGOxdg4XNVRGh4hwssBgAowP0WvCrldqrsMHdSERGHJcUpHoxRO0CKUJgQxOlRHARBo4Px2ATaFB6CItqTaOSLnlYfDEcjUaZ1JsCc9Xu88RB3EpIe0VCV2HVQnldABeJURXCKvhCEREtXa4TfRX1UgwdJEQSkOgyebG+q6C1W2gyB4sVW6OiiUgKiD2v3qqDpGKkZj3USoOAEH3+-1ZL7u3nNJF4O0xqk00ju9ORzbhyPRmP+6jM6kJsCBak53SM5kiRDwzW+wt+klksTumssuBsttciI8hFJ-nVKO8gDirjwunhACEALJYADSWAAjPDcY3m37s7T+GWGhOG6nCzANrR4no1T8Gi1As5N1v6iZaNE6e7r7fXE3myMf8e5A+LbhHAEjYhy3Cuow7qet6x71FkQbRCGQqUhGo7foWcawJe06DgMKYYTGz4AjI7rEYCqH5nB-oih2TyAVurYcu2aqPNQgjdkxnILP2cJ4cmKLVCKAAkO7kPwAHUdula7nR7Gcey3EwnxfJgIJ1ZPKJMniZJhEnmeF7uqCX6Pr+hZmX6Fm6GZIwjLQJi6MCqihpoOg2LYtRNqI5QQKwDToOwkLCYItDVEFVKMDoOJDEoYCDFcQA}{Open in Shinylive} \if{html}{\out{}} \if{html}{\out{}} From f3a88e49d2ef9b00f571379f5a9b688f2251da5e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:25:04 +0000 Subject: [PATCH 11/16] Simplify tm_outliers fix - use minimal conditional logic to avoid merge_expression_srv when no join keys exist Co-authored-by: m7pr <133694481+m7pr@users.noreply.github.com> --- R/tm_outliers.R | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index bcfde2215..0bb69ebee 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -441,9 +441,10 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, anl_merged_input <- reactive({ dataname_first <- names(data())[[1]] join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] - + if (length(join_keys) == 0) { - # No join keys - create simple assignment expression + # No join keys - single dataset, no merging needed + # Return the same structure as merge_expression_srv but with simple assignment list( expr = substitute(ANL <- dataname, list(dataname = as.name(dataname_first))), columns_source = list( @@ -456,7 +457,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, ) ) } else { - # Join keys exist - use standard merge + # Join keys exist - use merge_expression_srv teal.transform::merge_expression_srv( selector_list = reactive_select_input, datasets = data, @@ -637,33 +638,25 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, ) # ANL_OUTLIER_EXTENDED is the base table - join_keys <- as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) - - if (length(join_keys) == 0) { - # No join keys - no join needed - qenv <- teal.code::eval_code(qenv, quote(ANL_OUTLIER_EXTENDED <- ANL_OUTLIER)) - } else { - # Join keys exist - perform left join - qenv <- teal.code::eval_code( - qenv, - substitute( - expr = { - ANL_OUTLIER_EXTENDED <- dplyr::left_join( - ANL_OUTLIER, - dplyr::select( - dataname, - dplyr::setdiff(names(dataname), dplyr::setdiff(names(ANL_OUTLIER), join_keys)) - ), - by = join_keys - ) - }, - env = list( - dataname = as.name(dataname_first), - join_keys = join_keys + qenv <- teal.code::eval_code( + qenv, + substitute( + expr = { + ANL_OUTLIER_EXTENDED <- dplyr::left_join( + ANL_OUTLIER, + dplyr::select( + dataname, + dplyr::setdiff(names(dataname), dplyr::setdiff(names(ANL_OUTLIER), join_keys)) + ), + by = join_keys ) + }, + env = list( + dataname = as.name(dataname_first), + join_keys = as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) ) ) - } + ) qenv <- if (length(categorical_var) > 0) { qenv <- teal.code::eval_code( From e8589def13ea131dc6eafd77a88c8ef89ef85e52 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:27:15 +0000 Subject: [PATCH 12/16] Fix ANL_OUTLIER_EXTENDED join logic to handle missing join keys Co-authored-by: m7pr <133694481+m7pr@users.noreply.github.com> --- R/tm_outliers.R | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index 0bb69ebee..8ba1ffe4f 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -638,25 +638,33 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, ) # ANL_OUTLIER_EXTENDED is the base table - qenv <- teal.code::eval_code( - qenv, - substitute( - expr = { - ANL_OUTLIER_EXTENDED <- dplyr::left_join( - ANL_OUTLIER, - dplyr::select( - dataname, - dplyr::setdiff(names(dataname), dplyr::setdiff(names(ANL_OUTLIER), join_keys)) - ), - by = join_keys + join_keys <- as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) + + if (length(join_keys) == 0) { + # No join keys - single dataset, no join needed + qenv <- teal.code::eval_code(qenv, quote(ANL_OUTLIER_EXTENDED <- ANL_OUTLIER)) + } else { + # Join keys exist - perform left join + qenv <- teal.code::eval_code( + qenv, + substitute( + expr = { + ANL_OUTLIER_EXTENDED <- dplyr::left_join( + ANL_OUTLIER, + dplyr::select( + dataname, + dplyr::setdiff(names(dataname), dplyr::setdiff(names(ANL_OUTLIER), join_keys)) + ), + by = join_keys + ) + }, + env = list( + dataname = as.name(dataname_first), + join_keys = join_keys ) - }, - env = list( - dataname = as.name(dataname_first), - join_keys = as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) ) ) - ) + } qenv <- if (length(categorical_var) > 0) { qenv <- teal.code::eval_code( From 8390f9c1bdd35f49bbc3bb1e29e7535d5faccc05 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 09:51:58 +0000 Subject: [PATCH 13/16] [skip style] [skip vbump] Restyle files --- R/tm_outliers.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index 8ba1ffe4f..b26ef24ae 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -441,7 +441,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, anl_merged_input <- reactive({ dataname_first <- names(data())[[1]] join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] - + if (length(join_keys) == 0) { # No join keys - single dataset, no merging needed # Return the same structure as merge_expression_srv but with simple assignment @@ -639,7 +639,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, # ANL_OUTLIER_EXTENDED is the base table join_keys <- as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) - + if (length(join_keys) == 0) { # No join keys - single dataset, no join needed qenv <- teal.code::eval_code(qenv, quote(ANL_OUTLIER_EXTENDED <- ANL_OUTLIER)) From be9ceff3f6214bee9aa24f9339f4550c2d9c381e Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 9 Jul 2025 13:16:50 +0200 Subject: [PATCH 14/16] simplify approach to create dummy join_keys --- R/tm_outliers.R | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index b26ef24ae..dd7a08b34 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -441,29 +441,20 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, anl_merged_input <- reactive({ dataname_first <- names(data())[[1]] join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] + obj <- data() if (length(join_keys) == 0) { - # No join keys - single dataset, no merging needed - # Return the same structure as merge_expression_srv but with simple assignment - list( - expr = substitute(ANL <- dataname, list(dataname = as.name(dataname_first))), - columns_source = list( - outlier_var = reactive_select_input()$outlier_var()$select, - categorical_var = if (!is.null(reactive_select_input()$categorical_var)) { - names(reactive_select_input()$categorical_var()$filter) - } else { - character(0) - } - ) - ) - } else { - # Join keys exist - use merge_expression_srv - teal.transform::merge_expression_srv( - selector_list = reactive_select_input, - datasets = data, - merge_function = "dplyr::inner_join" - )() + if (!".row_id" %in% names(obj[[dataname_first]])) { + obj[[dataname_first]]$.row_id <- seq_len(nrow(obj[[dataname_first]])) + } + join_keys(obj) <- join_keys(join_key(dataname_first, dataname_first, ".row_id")) } + + teal.transform::merge_expression_srv( + selector_list = reactive_select_input, + datasets = reactive(obj), + merge_function = "dplyr::inner_join" + )() }) anl_merged_q <- reactive({ From db981e45831d326aa039be02100888e33f04462b Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 9 Jul 2025 13:27:31 +0200 Subject: [PATCH 15/16] fix second join --- R/tm_outliers.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index dd7a08b34..0ea4bfdc5 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -442,7 +442,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, dataname_first <- names(data())[[1]] join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] obj <- data() - + if (length(join_keys) == 0) { if (!".row_id" %in% names(obj[[dataname_first]])) { obj[[dataname_first]]$.row_id <- seq_len(nrow(obj[[dataname_first]])) @@ -631,8 +631,8 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, # ANL_OUTLIER_EXTENDED is the base table join_keys <- as.character(teal.data::join_keys(data())[dataname_first, dataname_first]) - if (length(join_keys) == 0) { - # No join keys - single dataset, no join needed + if (length(join_keys) == 1 && join_keys == ".row_id") { + # Dummy join key - single dataset, no join needed qenv <- teal.code::eval_code(qenv, quote(ANL_OUTLIER_EXTENDED <- ANL_OUTLIER)) } else { # Join keys exist - perform left join From 31362b9ae00493c36e98420ad5446cc8d41b2fdd Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 11:30:07 +0000 Subject: [PATCH 16/16] [skip style] [skip vbump] Restyle files --- R/tm_outliers.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/tm_outliers.R b/R/tm_outliers.R index 0ea4bfdc5..586e3f330 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -442,7 +442,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, dataname_first <- names(data())[[1]] join_keys <- teal.data::join_keys(data())[dataname_first, dataname_first] obj <- data() - + if (length(join_keys) == 0) { if (!".row_id" %in% names(obj[[dataname_first]])) { obj[[dataname_first]]$.row_id <- seq_len(nrow(obj[[dataname_first]]))