From 0667e5602e40fd1068fd4f540568512ecefebb7f Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Tue, 14 Oct 2025 16:01:17 +0100 Subject: [PATCH 01/18] :building_construction: Add empty inequalities module --- R/app_server.R | 2 ++ R/app_ui.R | 10 ++++++++++ R/mod_inequalities_server.R | 8 ++++++++ R/mod_inequalities_ui.R | 23 +++++++++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 R/mod_inequalities_server.R create mode 100644 R/mod_inequalities_ui.R diff --git a/R/app_server.R b/R/app_server.R index 4b2c915c..1ced97bf 100644 --- a/R/app_server.R +++ b/R/app_server.R @@ -148,6 +148,8 @@ app_server <- function(input, output, session) { mod_health_status_adjustment_server("health_status_adjustment", params) + mod_inequalities_server("inequalities", params) + mod_waiting_list_imbalances_server( "waiting_list_imbalances", wli_data(), diff --git a/R/app_ui.R b/R/app_ui.R index bfdab6af..6ff74205 100644 --- a/R/app_ui.R +++ b/R/app_ui.R @@ -63,6 +63,12 @@ app_ui <- function(request) { "Health Status Adjustment", tabName = "tab_health_status_adjustment" ), + bs4Dash::sidebarHeader("Inequalities"), + # + bs4Dash::menuItem( + "Inequalities", + tabName = "tab_inequalities" + ), # shiny::tags$hr(), bs4Dash::sidebarHeader("Demand-supply Imbalances"), @@ -183,6 +189,10 @@ app_ui <- function(request) { tabName = "tab_health_status_adjustment", mod_health_status_adjustment_ui("health_status_adjustment") ), + bs4Dash::tabItem( + tabName = "tab_inequalities", + mod_inequalities_ui("inequalities") + ), bs4Dash::tabItem( tabName = "tab_nda", mod_non_demographic_adjustment_ui("non_demographic_adjustment") diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R new file mode 100644 index 00000000..a716aa8a --- /dev/null +++ b/R/mod_inequalities_server.R @@ -0,0 +1,8 @@ +#' inequalities Server Functions +#' +#' @noRd +mod_inequalities_server <- function(id, params) { + shiny::moduleServer(id, function(input, output, session) { + + }) +} diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R new file mode 100644 index 00000000..70d443f7 --- /dev/null +++ b/R/mod_inequalities_ui.R @@ -0,0 +1,23 @@ +#' inequalities UI Function +#' +#' @description A shiny Module. +#' +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' +#' @importFrom shiny NS tagList +mod_inequalities_ui <- function(id) { + ns <- shiny::NS(id) + shiny::tagList( + shiny::tags$h1("Inequalities"), + shiny::fluidRow( + bs4Dash::box( + collapsible = FALSE, + headerBorder = FALSE, + width = 4, + md_file_to_html("app", "text", "inequalities.md") + ) + ) + ) +} From e7abf48fba0a0e500d735d74d8a1e43873a3cd6e Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Tue, 14 Oct 2025 16:49:39 +0100 Subject: [PATCH 02/18] :pencil2: Fix typo in comment --- R/fct_azure_storage.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/fct_azure_storage.R b/R/fct_azure_storage.R index 385c424d..7b4d1c31 100644 --- a/R/fct_azure_storage.R +++ b/R/fct_azure_storage.R @@ -1,6 +1,6 @@ #' Get Provider Data #' -#' Read the parquet file containing a selected tupe of provider data. +#' Read the parquet file containing a selected type of provider data. #' #' @param file The name of the file to read. #' @param inputs_data_version The version of the inputs data to use. From bf552326f20de6c1519b5b5300936149a2137133 Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Tue, 14 Oct 2025 17:41:55 +0100 Subject: [PATCH 03/18] :card_file_box: Get inequalities data from Azure --- R/fct_azure_storage.R | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/R/fct_azure_storage.R b/R/fct_azure_storage.R index 7b4d1c31..494e89dd 100644 --- a/R/fct_azure_storage.R +++ b/R/fct_azure_storage.R @@ -1,3 +1,19 @@ +#' Get inequalities data +#' +#' Read the parquet file for inequalities +#' @return A tibble. +load_inequalities_data <- function() { + + fs <- get_adls_fs() + fs |> + AzureStor::download_adls_file( + glue::glue("dev/inequalities.parquet"), + dest = NULL + ) |> + arrow::read_parquet() |> + tibble::as_tibble() +} + #' Get Provider Data #' #' Read the parquet file containing a selected type of provider data. From 3aeeea5dce35274567b292fd0f225f1ebcae938a Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Tue, 14 Oct 2025 17:42:54 +0100 Subject: [PATCH 04/18] =?UTF-8?q?=E2=AC=87=EF=B8=8FAdd=20download=20button?= =?UTF-8?q?=20for=20provider's=20inequalities=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/mod_inequalities_server.R | 19 +++++++++++++++++++ R/mod_inequalities_ui.R | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index a716aa8a..28d8dc84 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -4,5 +4,24 @@ mod_inequalities_server <- function(id, params) { shiny::moduleServer(id, function(input, output, session) { + + inequalities_data <- shiny::reactive({ + + dataset <- shiny::req(params$dataset) + + load_inequalities_data() |> + dplyr::filter( + .data[["provider"]] == .env[["dataset"]] + ) + + }) + + output$download_inequalities <- shiny::downloadHandler( + filename = \() glue::glue("{params[['dataset']]}_inequalities.csv"), + content = \(file) { + readr::write_csv(inequalities_data(), file) + } + ) + }) } diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index 70d443f7..e9c39049 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -17,6 +17,12 @@ mod_inequalities_ui <- function(id) { headerBorder = FALSE, width = 4, md_file_to_html("app", "text", "inequalities.md") + ), + bs4Dash::box( + collapsible = FALSE, + headerBorder = FALSE, + width = 8, + shiny::downloadButton(ns("download_inequalities"), "Download inequalities") ) ) ) From 8844b50ef33007abf46d17e93dd57140d8144800 Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Thu, 23 Oct 2025 10:55:31 +0100 Subject: [PATCH 05/18] =?UTF-8?q?=F0=9F=8C=AC=EF=B8=8FFormat=20with=20air?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/mod_inequalities_server.R | 7 +------ R/mod_inequalities_ui.R | 5 ++++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index 28d8dc84..2e3d4ce6 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -3,17 +3,13 @@ #' @noRd mod_inequalities_server <- function(id, params) { shiny::moduleServer(id, function(input, output, session) { - - inequalities_data <- shiny::reactive({ - - dataset <- shiny::req(params$dataset) + dataset <- shiny::req(params$dataset) # nolint: object_usage_linter load_inequalities_data() |> dplyr::filter( .data[["provider"]] == .env[["dataset"]] ) - }) output$download_inequalities <- shiny::downloadHandler( @@ -22,6 +18,5 @@ mod_inequalities_server <- function(id, params) { readr::write_csv(inequalities_data(), file) } ) - }) } diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index e9c39049..2936ee15 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -22,7 +22,10 @@ mod_inequalities_ui <- function(id) { collapsible = FALSE, headerBorder = FALSE, width = 8, - shiny::downloadButton(ns("download_inequalities"), "Download inequalities") + shiny::downloadButton( + ns("download_inequalities"), + "Download inequalities" + ) ) ) ) From c342795add5ecaace0a43fc09433470d5efdf20b Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Thu, 23 Oct 2025 15:45:40 +0100 Subject: [PATCH 06/18] :memo: Update documentation --- man/load_inequalities_data.Rd | 14 ++++++++++++++ man/load_provider_data.Rd | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 man/load_inequalities_data.Rd diff --git a/man/load_inequalities_data.Rd b/man/load_inequalities_data.Rd new file mode 100644 index 00000000..48b41151 --- /dev/null +++ b/man/load_inequalities_data.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fct_azure_storage.R +\name{load_inequalities_data} +\alias{load_inequalities_data} +\title{Get inequalities data} +\usage{ +load_inequalities_data() +} +\value{ +A tibble. +} +\description{ +Read the parquet file for inequalities +} diff --git a/man/load_provider_data.Rd b/man/load_provider_data.Rd index 3b2fc684..6f071993 100644 --- a/man/load_provider_data.Rd +++ b/man/load_provider_data.Rd @@ -18,5 +18,5 @@ load_provider_data( A tibble. } \description{ -Read the parquet file containing a selected tupe of provider data. +Read the parquet file containing a selected type of provider data. } From 9a3835e5f8bacec6e39c170ab9e6e5240a12a99f Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Thu, 23 Oct 2025 19:45:16 +0100 Subject: [PATCH 07/18] =?UTF-8?q?=F0=9F=93=87=20Add=20initial=20hrg=20tabl?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/mod_inequalities_server.R | 84 +++++++++++++++++++++++++++++++++++++ R/mod_inequalities_ui.R | 13 ++++-- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index 2e3d4ce6..70993ba0 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -3,6 +3,8 @@ #' @noRd mod_inequalities_server <- function(id, params) { shiny::moduleServer(id, function(input, output, session) { + ns <- session$ns + inequalities_data <- shiny::reactive({ dataset <- shiny::req(params$dataset) # nolint: object_usage_linter @@ -12,6 +14,88 @@ mod_inequalities_server <- function(id, params) { ) }) + # Initialize reactiveValues with NULL + hrg <- reactiveValues( + selections = NULL + ) + + # Initialise data once inequalities_data is available + observe({ + req(inequalities_data()) + + if (is.null(hrg$selections)) { + hrg$selections <- tibble::tibble( + hrg_code = unique(inequalities_data()$sushrg_trimmed), + choice = "No change" + ) + } + }) + + # Handle "Set all to zero sum" button + observeEvent(input$set_all_zero_sum, { + hrg$selections$choice <- "Zero-sum" + }) + + # Handle "Clear all" button + observeEvent(input$clear_all, { + hrg$selections$choice <- "No change" + }) + + output$hrg_table <- DT::renderDataTable({ + # Create dropdown options for Choice column + choice_options <- c("No change", "Zero-sum", "Level up", "Level down") + + # Create the dropdown HTML for each row + dropdown_html <- sapply(seq_len(nrow(hrg$selections)), function(i) { + current_choice <- hrg$selections$choice[i] + options_html <- glue::glue_collapse( + sapply(choice_options, function(option) { + selected <- if (option == current_choice) "selected" else "" + glue::glue("") + }) + ) + glue::glue( + "" + ) + }) + + # Replace the choice column with dropdown HTML + display_data <- hrg$selections + display_data$choice <- dropdown_html + + DT::datatable( + display_data, + escape = FALSE, + rownames = FALSE, + selection = "none", + filter = "top", + options = list( + pageLength = 25, + searching = FALSE, + ordering = TRUE, + info = TRUE + ), + callback = DT::JS(glue::glue( + " + table.on('change', '.choice-select', function() {{ + var row = $(this).data('row'); + var value = $(this).val(); + Shiny.setInputValue('{ns('choice_changed')}', {{row: row, value: value}}, {{priority: 'event'}}); + }}); +" + )) + ) + }) + + # Handle dropdown changes + observeEvent(input$choice_changed, { + row_index <- input$choice_changed$row + new_value <- input$choice_changed$value + + # Update the data + hrg$selections$choice[row_index] <- new_value + }) + output$download_inequalities <- shiny::downloadHandler( filename = \() glue::glue("{params[['dataset']]}_inequalities.csv"), content = \(file) { diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index 2936ee15..39898347 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -22,10 +22,15 @@ mod_inequalities_ui <- function(id) { collapsible = FALSE, headerBorder = FALSE, width = 8, - shiny::downloadButton( - ns("download_inequalities"), - "Download inequalities" - ) + div( + shiny::downloadButton( + ns("download_inequalities"), + "Download inequalities" + ), + actionButton(ns("set_all_zero_sum"), "Set all to zero sum"), + actionButton(ns("clear_all"), "Clear all", class = "btn-secondary") + ), + DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") ) ) ) From 65db284820ab8988b583b4111c53c9a263c4a0da Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Mon, 15 Dec 2025 16:11:13 +0000 Subject: [PATCH 08/18] :sparkles: Add features module to inequalities --- R/mod_inequalities_server.R | 2 ++ R/mod_inequalities_ui.R | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index 70993ba0..d98ddae7 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -5,6 +5,8 @@ mod_inequalities_server <- function(id, params) { shiny::moduleServer(id, function(input, output, session) { ns <- session$ns + mod_reasons_server(shiny::NS(id, "reasons"), params, "inequalities") + inequalities_data <- shiny::reactive({ dataset <- shiny::req(params$dataset) # nolint: object_usage_linter diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index 39898347..196ae38e 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -12,16 +12,20 @@ mod_inequalities_ui <- function(id) { shiny::tagList( shiny::tags$h1("Inequalities"), shiny::fluidRow( + col_4( bs4Dash::box( collapsible = FALSE, headerBorder = FALSE, - width = 4, + width = 12, md_file_to_html("app", "text", "inequalities.md") ), + mod_reasons_ui(ns("reasons")) + ), + col_8( bs4Dash::box( collapsible = FALSE, headerBorder = FALSE, - width = 8, + width = 12, div( shiny::downloadButton( ns("download_inequalities"), @@ -32,6 +36,7 @@ mod_inequalities_ui <- function(id) { ), DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") ) + ) ) ) } From e7493d252ddd91a3fbe363ba929183be40994e9a Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Mon, 15 Dec 2025 16:35:41 +0000 Subject: [PATCH 09/18] =?UTF-8?q?=F0=9F=92=A8=20Format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/fct_azure_storage.R | 1 - R/mod_inequalities_ui.R | 40 ++++++++++++++++++++-------------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/R/fct_azure_storage.R b/R/fct_azure_storage.R index 494e89dd..f93bc2ba 100644 --- a/R/fct_azure_storage.R +++ b/R/fct_azure_storage.R @@ -3,7 +3,6 @@ #' Read the parquet file for inequalities #' @return A tibble. load_inequalities_data <- function() { - fs <- get_adls_fs() fs |> AzureStor::download_adls_file( diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index 196ae38e..118185a6 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -13,29 +13,29 @@ mod_inequalities_ui <- function(id) { shiny::tags$h1("Inequalities"), shiny::fluidRow( col_4( - bs4Dash::box( - collapsible = FALSE, - headerBorder = FALSE, - width = 12, - md_file_to_html("app", "text", "inequalities.md") - ), - mod_reasons_ui(ns("reasons")) + bs4Dash::box( + collapsible = FALSE, + headerBorder = FALSE, + width = 12, + md_file_to_html("app", "text", "inequalities.md") + ), + mod_reasons_ui(ns("reasons")) ), col_8( - bs4Dash::box( - collapsible = FALSE, - headerBorder = FALSE, - width = 12, - div( - shiny::downloadButton( - ns("download_inequalities"), - "Download inequalities" + bs4Dash::box( + collapsible = FALSE, + headerBorder = FALSE, + width = 12, + div( + shiny::downloadButton( + ns("download_inequalities"), + "Download inequalities" + ), + actionButton(ns("set_all_zero_sum"), "Set all to zero sum"), + actionButton(ns("clear_all"), "Clear all", class = "btn-secondary") ), - actionButton(ns("set_all_zero_sum"), "Set all to zero sum"), - actionButton(ns("clear_all"), "Clear all", class = "btn-secondary") - ), - DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") - ) + DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") + ) ) ) ) From feefb9962953a0037958ede653d6dd5be7597af6 Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Mon, 15 Dec 2025 18:49:49 +0000 Subject: [PATCH 10/18] :construction: Print check inequalities list --- R/mod_inequalities_server.R | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index d98ddae7..f61da6d2 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -104,5 +104,20 @@ mod_inequalities_server <- function(id, params) { readr::write_csv(inequalities_data(), file) } ) + + shiny::observe({ + inequalities <- + hrg$selections |> + dplyr::filter(.data$choice != "No change") |> + dplyr::mutate( + choice = stringr::str_to_snake(stringr::str_to_lower(.data$choice)) + ) |> + dplyr::group_by(.data$choice) |> + dplyr::summarise(hrg_codes = list(.data$hrg_code)) |> + tibble::deframe() |> + jsonlite::toJSON(auto_unbox = FALSE, pretty = TRUE) + + print(inequalities) + }) }) } From 1f9664033a9ab226e5d5483523aa0c7ad4d0adcf Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Fri, 9 Jan 2026 17:02:26 +0000 Subject: [PATCH 11/18] =?UTF-8?q?=F0=9F=92=BE=20Save=20inequalities=20to?= =?UTF-8?q?=20params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/mod_inequalities_server.R | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index f61da6d2..a4b7954d 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -90,7 +90,7 @@ mod_inequalities_server <- function(id, params) { }) # Handle dropdown changes - observeEvent(input$choice_changed, { + shiny::observeEvent(input$choice_changed, { row_index <- input$choice_changed$row new_value <- input$choice_changed$value @@ -106,7 +106,7 @@ mod_inequalities_server <- function(id, params) { ) shiny::observe({ - inequalities <- + params$inequalities <- hrg$selections |> dplyr::filter(.data$choice != "No change") |> dplyr::mutate( @@ -115,9 +115,8 @@ mod_inequalities_server <- function(id, params) { dplyr::group_by(.data$choice) |> dplyr::summarise(hrg_codes = list(.data$hrg_code)) |> tibble::deframe() |> - jsonlite::toJSON(auto_unbox = FALSE, pretty = TRUE) + purrr::map(I) # Forces any single values to stay in a list (asis) - print(inequalities) }) }) } From 45053f2b1ddce037458dba82aa7127cf314dcd83 Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Tue, 13 Jan 2026 18:32:53 +0000 Subject: [PATCH 12/18] :pencil2: Handle editing existing scenarios --- R/mod_inequalities_server.R | 88 +++++++++++++++----------- R/mod_inequalities_utils.R | 91 +++++++++++++++++++++++++++ man/get_inequality_choice_mappings.Rd | 19 ++++++ man/inequality_choices_to_display.Rd | 22 +++++++ man/inequality_choices_to_snake.Rd | 22 +++++++ man/initialise_hrg_table.Rd | 28 +++++++++ 6 files changed, 233 insertions(+), 37 deletions(-) create mode 100644 R/mod_inequalities_utils.R create mode 100644 man/get_inequality_choice_mappings.Rd create mode 100644 man/inequality_choices_to_display.Rd create mode 100644 man/inequality_choices_to_snake.Rd create mode 100644 man/initialise_hrg_table.Rd diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index a4b7954d..c6afa736 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -7,7 +7,9 @@ mod_inequalities_server <- function(id, params) { mod_reasons_server(shiny::NS(id, "reasons"), params, "inequalities") - inequalities_data <- shiny::reactive({ + # This is the data for each HRG split by IMD for the selector provider + # load_inequalities_data() is pulling from Azure so might take some time + provider_inequalities <- shiny::reactive({ dataset <- shiny::req(params$dataset) # nolint: object_usage_linter load_inequalities_data() |> @@ -16,52 +18,61 @@ mod_inequalities_server <- function(id, params) { ) }) - # Initialize reactiveValues with NULL - hrg <- reactiveValues( + # hrg is used to track the current choice selections in table form + hrg <- shiny::reactiveValues( selections = NULL ) - # Initialise data once inequalities_data is available - observe({ - req(inequalities_data()) + # Initialisation + init <- shiny::observe( + { + # Wait for data to be available + shiny::req(provider_inequalities()) - if (is.null(hrg$selections)) { - hrg$selections <- tibble::tibble( - hrg_code = unique(inequalities_data()$sushrg_trimmed), - choice = "No change" - ) - } - }) + hrg$selections <- initialise_hrg_table(provider_inequalities(), params) - # Handle "Set all to zero sum" button - observeEvent(input$set_all_zero_sum, { + # Destroy the observer so it only runs once + init$destroy() + }, + priority = -1 # Low priority to ensure other reactives are ready + ) + + # "Set all to zero sum" button + shiny::observeEvent(input$set_all_zero_sum, { + shiny::req(hrg$selections) hrg$selections$choice <- "Zero-sum" }) - # Handle "Clear all" button - observeEvent(input$clear_all, { + # "Clear all" button + shiny::observeEvent(input$clear_all, { + shiny::req(hrg$selections) hrg$selections$choice <- "No change" }) output$hrg_table <- DT::renderDataTable({ - # Create dropdown options for Choice column - choice_options <- c("No change", "Zero-sum", "Level up", "Level down") + shiny::req(hrg$selections) + + choice_options <- unname(get_inequality_choice_mappings()) # Create the dropdown HTML for each row - dropdown_html <- sapply(seq_len(nrow(hrg$selections)), function(i) { - current_choice <- hrg$selections$choice[i] - options_html <- glue::glue_collapse( - sapply(choice_options, function(option) { - selected <- if (option == current_choice) "selected" else "" - glue::glue("") - }) - ) - glue::glue( - "" - ) - }) + dropdown_html <- purrr::map_chr( + seq_len(nrow(hrg$selections)), + function(i) { + current_choice <- hrg$selections$choice[i] + options_html <- glue::glue_collapse( + purrr::map_chr(choice_options, function(option) { + selected <- if (option == current_choice) "selected" else "" + glue::glue( + "" + ) + }) + ) + glue::glue( + "" + ) + } + ) - # Replace the choice column with dropdown HTML display_data <- hrg$selections display_data$choice <- dropdown_html @@ -91,32 +102,35 @@ mod_inequalities_server <- function(id, params) { # Handle dropdown changes shiny::observeEvent(input$choice_changed, { + shiny::req(hrg$selections) + row_index <- input$choice_changed$row - new_value <- input$choice_changed$value # Update the data - hrg$selections$choice[row_index] <- new_value + hrg$selections$choice[row_index] <- input$choice_changed$value }) + # Download inequalities data output$download_inequalities <- shiny::downloadHandler( filename = \() glue::glue("{params[['dataset']]}_inequalities.csv"), content = \(file) { - readr::write_csv(inequalities_data(), file) + readr::write_csv(provider_inequalities(), file) } ) shiny::observe({ + shiny::req(hrg$selections) + params$inequalities <- hrg$selections |> dplyr::filter(.data$choice != "No change") |> dplyr::mutate( - choice = stringr::str_to_snake(stringr::str_to_lower(.data$choice)) + choice = inequality_choices_to_snake(.data$choice) ) |> dplyr::group_by(.data$choice) |> dplyr::summarise(hrg_codes = list(.data$hrg_code)) |> tibble::deframe() |> purrr::map(I) # Forces any single values to stay in a list (asis) - }) }) } diff --git a/R/mod_inequalities_utils.R b/R/mod_inequalities_utils.R new file mode 100644 index 00000000..36b4cb09 --- /dev/null +++ b/R/mod_inequalities_utils.R @@ -0,0 +1,91 @@ +#' Initialise inequality choice table +#' +#' Creates a tibble containing unique HRG codes for selected provider and +#' a "choice" column indicating the type of adjustment to apply. +#' Set to "No change" by default +#' +#' If editing an existing scenario, previous parameters will be loaded. +#' +#' @param provider_inequalities A data frame containing provider inequality data. +#' @param params The standard params object +#' +#' @return A tibble with two columns: +#' \item{hrg_code}{Character vector of unique HRG codes from the input data} +#' \item{choice}{Character vector indicating the inequality adjustment choice +#' for each HRG code.} +#' Possible values are defined in get_inequality_choice_mappings() +#' + +initialise_hrg_table <- function(provider_inequalities, params) { + # Get unique HRG codes from the data + unique_hrg_codes <- unique(provider_inequalities$sushrg_trimmed) + + hrg_table <- tibble::tibble( + hrg_code = unique_hrg_codes, + choice = "No change" + ) + + # Check if params$inequalities has existing data + if (!is.null(params$inequalities) && length(params$inequalities) > 0) { + # Update choices based on existing params$inequalities + for (choice_type in names(params$inequalities)) { + display_choice <- inequality_choices_to_display(choice_type) + + # Get the HRG codes for this choice type + hrg_codes_for_choice <- params$inequalities[[choice_type]] + + # Update the selections for matching HRG codes + hrg_table$choice[ + hrg_table$hrg_code %in% hrg_codes_for_choice + ] <- display_choice + } + } + + hrg_table +} + +#' Get Inequality Choice Mappings +# +#' Returns the mapping between programmatic snake_case and display +#' names for inequality adjustment choices. +#' +#' @return Named character vector where names are snake_case identifiers and +#' values are display names. +get_inequality_choice_mappings <- function() { + c( + no_change = "No change", + zero_sum = "Zero-sum", + level_up = "Level up", + level_down = "Level down" + ) +} + +#' Convert inequality choices to snake case +#' +#' @param display_choice Character vector of display names e.g., Zero-sum" +#' +#' @return Character vector of snake_case names. Returns \code{NA} for +#' unrecognized values. +#' +#' @examples +#' inequality_choices_to_snake("Zero-sum") +#' +inequality_choices_to_snake <- function(display_choice) { + mappings <- get_inequality_choice_mappings() + names(mappings)[match(display_choice, mappings)] +} + +#' Convert inequality choices to display case +#' +#' @param snake_choice Character vector of snake_case names e.g., 'zero_sum' +#' +#' @return Character vector of display names. Returns \code{NA} for +#' unrecognized values. +#' +#' @examples +#' inequality_choices_to_display("zero_sum") +#' +inequality_choices_to_display <- function(snake_choice) { + mappings <- get_inequality_choice_mappings() + unname(mappings[snake_choice]) +} diff --git a/man/get_inequality_choice_mappings.Rd b/man/get_inequality_choice_mappings.Rd new file mode 100644 index 00000000..1586e135 --- /dev/null +++ b/man/get_inequality_choice_mappings.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/mod_inequalities_utils.R +\name{get_inequality_choice_mappings} +\alias{get_inequality_choice_mappings} +\title{Get Inequality Choice Mappings +Returns the mapping between programmatic snake_case and display +names for inequality adjustment choices.} +\usage{ +get_inequality_choice_mappings() +} +\value{ +Named character vector where names are snake_case identifiers and + values are display names. +} +\description{ +Get Inequality Choice Mappings +Returns the mapping between programmatic snake_case and display +names for inequality adjustment choices. +} diff --git a/man/inequality_choices_to_display.Rd b/man/inequality_choices_to_display.Rd new file mode 100644 index 00000000..ae816f58 --- /dev/null +++ b/man/inequality_choices_to_display.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/mod_inequalities_utils.R +\name{inequality_choices_to_display} +\alias{inequality_choices_to_display} +\title{Convert inequality choices to display case} +\usage{ +inequality_choices_to_display(snake_choice) +} +\arguments{ +\item{snake_choice}{Character vector of snake_case names e.g., 'zero_sum'} +} +\value{ +Character vector of display names. Returns \code{NA} for + unrecognized values. +} +\description{ +Convert inequality choices to display case +} +\examples{ +inequality_choices_to_display("zero_sum") + +} diff --git a/man/inequality_choices_to_snake.Rd b/man/inequality_choices_to_snake.Rd new file mode 100644 index 00000000..3aa24288 --- /dev/null +++ b/man/inequality_choices_to_snake.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/mod_inequalities_utils.R +\name{inequality_choices_to_snake} +\alias{inequality_choices_to_snake} +\title{Convert inequality choices to snake case} +\usage{ +inequality_choices_to_snake(display_choice) +} +\arguments{ +\item{display_choice}{Character vector of display names e.g., Zero-sum"} +} +\value{ +Character vector of snake_case names. Returns \code{NA} for + unrecognized values. +} +\description{ +Convert inequality choices to snake case +} +\examples{ +inequality_choices_to_snake("Zero-sum") + +} diff --git a/man/initialise_hrg_table.Rd b/man/initialise_hrg_table.Rd new file mode 100644 index 00000000..4e1e2ff6 --- /dev/null +++ b/man/initialise_hrg_table.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/mod_inequalities_utils.R +\name{initialise_hrg_table} +\alias{initialise_hrg_table} +\title{Initialise inequality choice table} +\usage{ +initialise_hrg_table(provider_inequalities, params) +} +\arguments{ +\item{provider_inequalities}{A data frame containing provider inequality data.} + +\item{params}{The standard params object} +} +\value{ +A tibble with two columns: + \item{hrg_code}{Character vector of unique HRG codes from the input data} + \item{choice}{Character vector indicating the inequality adjustment choice + for each HRG code.} + Possible values are defined in get_inequality_choice_mappings() +} +\description{ +Creates a tibble containing unique HRG codes for selected provider and +a "choice" column indicating the type of adjustment to apply. +Set to "No change" by default +} +\details{ +If editing an existing scenario, previous parameters will be loaded. +} From fc70bf15c70017308ff956673a7bed9f1205c76e Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Thu, 15 Jan 2026 16:45:34 +0000 Subject: [PATCH 13/18] :heavy_plus_sign: Add DT import --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index a88dbc22..ed7f0bfd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -24,6 +24,7 @@ Imports: cachem, config (>= 0.3.1), dplyr, + DT, forcats, future, ggbeeswarm, From f81a19b92d7e0b7d7f44027b9d67d3a0b342c5ff Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Thu, 15 Jan 2026 17:19:04 +0000 Subject: [PATCH 14/18] :green_heart: Fix pkg check --- R/mod_inequalities_server.R | 14 ++++++++++++++ R/mod_inequalities_ui.R | 3 +++ R/mod_inequalities_utils.R | 7 +------ man/inequality_choices_to_display.Rd | 4 ---- man/inequality_choices_to_snake.Rd | 4 ---- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index c6afa736..2a47c208 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -43,6 +43,20 @@ mod_inequalities_server <- function(id, params) { hrg$selections$choice <- "Zero-sum" }) + + # "Level up" button + shiny::observeEvent(input$level_up, { + shiny::req(hrg$selections) + hrg$selections$choice <- "Level up" + }) + + + # "Level up" button + shiny::observeEvent(input$level_down, { + shiny::req(hrg$selections) + hrg$selections$choice <- "Level down" + }) + # "Clear all" button shiny::observeEvent(input$clear_all, { shiny::req(hrg$selections) diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index 118185a6..9bc0df8e 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -32,7 +32,10 @@ mod_inequalities_ui <- function(id) { "Download inequalities" ), actionButton(ns("set_all_zero_sum"), "Set all to zero sum"), + actionButton(ns("level_up"), "Level up", class = "btn-secondary"), + actionButton(ns("level_down"), "Level down", class = "btn-secondary"), actionButton(ns("clear_all"), "Clear all", class = "btn-secondary") + ), DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") ) diff --git a/R/mod_inequalities_utils.R b/R/mod_inequalities_utils.R index 36b4cb09..30e0f8ad 100644 --- a/R/mod_inequalities_utils.R +++ b/R/mod_inequalities_utils.R @@ -66,10 +66,7 @@ get_inequality_choice_mappings <- function() { #' #' @return Character vector of snake_case names. Returns \code{NA} for #' unrecognized values. -#' -#' @examples -#' inequality_choices_to_snake("Zero-sum") -#' + inequality_choices_to_snake <- function(display_choice) { mappings <- get_inequality_choice_mappings() names(mappings)[match(display_choice, mappings)] @@ -82,8 +79,6 @@ inequality_choices_to_snake <- function(display_choice) { #' @return Character vector of display names. Returns \code{NA} for #' unrecognized values. #' -#' @examples -#' inequality_choices_to_display("zero_sum") #' inequality_choices_to_display <- function(snake_choice) { mappings <- get_inequality_choice_mappings() diff --git a/man/inequality_choices_to_display.Rd b/man/inequality_choices_to_display.Rd index ae816f58..1f3bb68a 100644 --- a/man/inequality_choices_to_display.Rd +++ b/man/inequality_choices_to_display.Rd @@ -16,7 +16,3 @@ Character vector of display names. Returns \code{NA} for \description{ Convert inequality choices to display case } -\examples{ -inequality_choices_to_display("zero_sum") - -} diff --git a/man/inequality_choices_to_snake.Rd b/man/inequality_choices_to_snake.Rd index 3aa24288..d8c0e088 100644 --- a/man/inequality_choices_to_snake.Rd +++ b/man/inequality_choices_to_snake.Rd @@ -16,7 +16,3 @@ Character vector of snake_case names. Returns \code{NA} for \description{ Convert inequality choices to snake case } -\examples{ -inequality_choices_to_snake("Zero-sum") - -} From ae91ecbffc3824155d9f20fbabbf674760f6a609 Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Thu, 15 Jan 2026 17:38:16 +0000 Subject: [PATCH 15/18] :wind-face: Format with air --- R/mod_inequalities_server.R | 2 -- R/mod_inequalities_ui.R | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index 2a47c208..8266b38e 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -43,14 +43,12 @@ mod_inequalities_server <- function(id, params) { hrg$selections$choice <- "Zero-sum" }) - # "Level up" button shiny::observeEvent(input$level_up, { shiny::req(hrg$selections) hrg$selections$choice <- "Level up" }) - # "Level up" button shiny::observeEvent(input$level_down, { shiny::req(hrg$selections) diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index 9bc0df8e..f10b8927 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -33,9 +33,12 @@ mod_inequalities_ui <- function(id) { ), actionButton(ns("set_all_zero_sum"), "Set all to zero sum"), actionButton(ns("level_up"), "Level up", class = "btn-secondary"), - actionButton(ns("level_down"), "Level down", class = "btn-secondary"), + actionButton( + ns("level_down"), + "Level down", + class = "btn-secondary" + ), actionButton(ns("clear_all"), "Clear all", class = "btn-secondary") - ), DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") ) From 94910ff9764b04b5965edd64daa68951a7b32678 Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Fri, 16 Jan 2026 16:27:54 +0000 Subject: [PATCH 16/18] :fire: Remove level-up and level-down buttons --- R/mod_inequalities_server.R | 12 ------------ R/mod_inequalities_ui.R | 14 +++++--------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/R/mod_inequalities_server.R b/R/mod_inequalities_server.R index 8266b38e..c6afa736 100644 --- a/R/mod_inequalities_server.R +++ b/R/mod_inequalities_server.R @@ -43,18 +43,6 @@ mod_inequalities_server <- function(id, params) { hrg$selections$choice <- "Zero-sum" }) - # "Level up" button - shiny::observeEvent(input$level_up, { - shiny::req(hrg$selections) - hrg$selections$choice <- "Level up" - }) - - # "Level up" button - shiny::observeEvent(input$level_down, { - shiny::req(hrg$selections) - hrg$selections$choice <- "Level down" - }) - # "Clear all" button shiny::observeEvent(input$clear_all, { shiny::req(hrg$selections) diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index f10b8927..787ace52 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -26,21 +26,17 @@ mod_inequalities_ui <- function(id) { collapsible = FALSE, headerBorder = FALSE, width = 12, - div( + shiny::div( shiny::downloadButton( ns("download_inequalities"), "Download inequalities" ), - actionButton(ns("set_all_zero_sum"), "Set all to zero sum"), - actionButton(ns("level_up"), "Level up", class = "btn-secondary"), - actionButton( - ns("level_down"), - "Level down", - class = "btn-secondary" - ), - actionButton(ns("clear_all"), "Clear all", class = "btn-secondary") + shiny::actionButton(ns("set_all_zero_sum"), "Set all to zero sum"), + shiny::actionButton(ns("clear_all"), "Clear all", class = "btn-secondary") ), + shiny::div( DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") + ) ) ) ) From 1372f9dee8de36efae7a73da8d04ffb061fbd746 Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Fri, 16 Jan 2026 16:29:04 +0000 Subject: [PATCH 17/18] :lipstick: Add padding --- R/mod_inequalities_ui.R | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/R/mod_inequalities_ui.R b/R/mod_inequalities_ui.R index 787ace52..1561c384 100644 --- a/R/mod_inequalities_ui.R +++ b/R/mod_inequalities_ui.R @@ -32,10 +32,15 @@ mod_inequalities_ui <- function(id) { "Download inequalities" ), shiny::actionButton(ns("set_all_zero_sum"), "Set all to zero sum"), - shiny::actionButton(ns("clear_all"), "Clear all", class = "btn-secondary") + shiny::actionButton( + ns("clear_all"), + "Clear all", + class = "btn-secondary" + ) ), + shiny::br(), shiny::div( - DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") + DT::dataTableOutput(ns("hrg_table"), height = "calc(100vh - 200px)") ) ) ) From 1a2c2d23d4d3b73102e8aaef0acfa5498eebcadc Mon Sep 17 00:00:00 2001 From: Rhian Davies Date: Wed, 21 Jan 2026 15:16:55 +0000 Subject: [PATCH 18/18] =?UTF-8?q?=F0=9F=AB=A3=20=20Show/hide=20inequalitie?= =?UTF-8?q?s=20based=20on=20group=20membership?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/app_server.R | 12 ++++++++++++ R/app_ui.R | 16 ++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/R/app_server.R b/R/app_server.R index 1ced97bf..d68b4aef 100644 --- a/R/app_server.R +++ b/R/app_server.R @@ -148,6 +148,18 @@ app_server <- function(input, output, session) { mod_health_status_adjustment_server("health_status_adjustment", params) + observe({ + can_set_inequalities <- any( + c("nhp_devs", "nhp_power_users", "nhp_test_inequalities") %in% + session$groups + ) + + shinyjs::toggle( + "inequalities_tab", + condition = (is_local() || can_set_inequalities) + ) + }) + mod_inequalities_server("inequalities", params) mod_waiting_list_imbalances_server( diff --git a/R/app_ui.R b/R/app_ui.R index 6ff74205..c68d1860 100644 --- a/R/app_ui.R +++ b/R/app_ui.R @@ -63,13 +63,17 @@ app_ui <- function(request) { "Health Status Adjustment", tabName = "tab_health_status_adjustment" ), - bs4Dash::sidebarHeader("Inequalities"), - # - bs4Dash::menuItem( - "Inequalities", - tabName = "tab_inequalities" + shiny::tags$hr(), + shinyjs::hidden( + shiny::div( + id = "inequalities_tab", + bs4Dash::sidebarHeader("Inequalities"), + bs4Dash::menuItem( + "Inequalities", + tabName = "tab_inequalities" + ) + ) ), - # shiny::tags$hr(), bs4Dash::sidebarHeader("Demand-supply Imbalances"), bs4Dash::menuItem(