From 2d0d7044b3d2e2ff06b28efda75632f096f58779 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 21 Nov 2024 15:44:37 -0500
Subject: [PATCH 01/86] Add very basic checkbox column to tables
---
R/fct_tables.R | 12 ++++++++++++
R/mod_common_forms.R | 38 +++++++++++++++++++++++++++++++++-----
R/mod_study_forms.R | 16 +++++++++++++++-
3 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/R/fct_tables.R b/R/fct_tables.R
index 6d9337a1..cbe45ddf 100644
--- a/R/fct_tables.R
+++ b/R/fct_tables.R
@@ -51,6 +51,17 @@ create_table.default <- function(
stopifnot(is.character(keep_vars))
stopifnot(is.character(name_column))
stopifnot(is.character(value_column))
+ if ("reviewed" %in% names(data)) {
+ data <- dplyr::mutate(
+ data,
+ o_reviewed = dplyr::case_when(
+ any(reviewed == "No") & any(reviewed == "Yes") ~ NA,
+ any(reviewed == "Yes") ~ TRUE,
+ .default = FALSE
+ ),
+ .by = keep_vars)
+ keep_vars <- c("o_reviewed", keep_vars)
+ }
df <- data[c(keep_vars, name_column, value_column)] |>
tidyr::pivot_wider(
names_from = {{name_column}},
@@ -298,6 +309,7 @@ create_table.medication <- function(
) |>
dplyr::arrange(dplyr::desc(in_use), dplyr::desc(`Start Date`)) |>
dplyr::select(
+ dplyr::any_of("o_reviewed"),
dplyr::all_of(c(keep_vars, "Name")),
dplyr::everything(),
-dplyr::all_of(c("in_use", "Active Ingredient", "Trade Name",
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 09f0f833..ed46e279 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -125,15 +125,29 @@ mod_common_forms_server <- function(
SAE_data <- data_active() |>
dplyr::filter(grepl("Yes", `Serious Adverse Event`)) |>
dplyr::select(dplyr::any_of(
- c("subject_id","form_repeat", "Name", "AESI", "SAE Start date",
+ c("o_reviewed", "subject_id","form_repeat", "Name", "AESI", "SAE Start date",
"SAE End date", "CTCAE severity", "Treatment related",
"Treatment action", "Other action", "SAE Category",
"SAE Awareness date", "SAE Date of death", "SAE Death reason")
)) |>
adjust_colnames("^SAE ")
if(!input$show_all_data) SAE_data$subject_id <- NULL
- datatable_custom(SAE_data, rename_vars = table_names, rownames= FALSE,
- title = "Serious Adverse Events", escape = FALSE)
+ datatable_custom(
+ SAE_data,
+ rename_vars = table_names,
+ rownames= FALSE,
+ title = "Serious Adverse Events",
+ escape = FALSE,
+ options = list(
+ columnDefs = list(list(
+ targets = 0,
+ render = DT::JS(
+ "function(data, type, row, meta) {",
+ "return ``;",
+ "}"
+ )
+ ))
+ ))
})
output[["common_form_table"]] <- DT::renderDT({
@@ -145,8 +159,22 @@ mod_common_forms_server <- function(
dplyr::select(-dplyr::starts_with("SAE"))
}
if(!input$show_all_data) df$subject_id <- NULL
- datatable_custom(df, rename_vars = table_names, rownames= FALSE,
- title = form, escape = FALSE)
+ datatable_custom(
+ df,
+ rename_vars = table_names,
+ rownames= FALSE,
+ title = form,
+ escape = FALSE,
+ options = list(
+ columnDefs = list(list(
+ targets = 0,
+ render = DT::JS(
+ "function(data, type, row, meta) {",
+ "return ``;",
+ "}"
+ )
+ ))
+ ))
})
})
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index bbf45905..8bfef958 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -223,7 +223,21 @@ mod_study_forms_server <- function(
output[["table"]] <- DT::renderDT({
req(table_data_active())
- datatable_custom(table_data_active(), table_names, escape = FALSE)
+ datatable_custom(
+ table_data_active(),
+ table_names,
+ rownames= FALSE,
+ escape = FALSE,
+ options = list(
+ columnDefs = list(list(
+ targets = 0,
+ render = DT::JS(
+ "function(data, type, row, meta) {",
+ "return ``;",
+ "}"
+ )
+ ))
+ ))
})
if(form %in% c("Vital signs", "Vitals adjusted")){
From 67d817f7fc37ed362ce3c3bd6e78dc846646c87d Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 22 Nov 2024 14:20:21 -0500
Subject: [PATCH 02/86] "Highlight" rows when check status changed
---
R/mod_common_forms.R | 12 ++++++++++--
R/mod_study_forms.R | 12 ++++++++++--
inst/app/www/custom.css | 14 ++++++++++++++
3 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index ed46e279..5bff4e79 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -170,10 +170,18 @@ mod_common_forms_server <- function(
targets = 0,
render = DT::JS(
"function(data, type, row, meta) {",
- "return ``;",
+ "return ``;",
"}"
)
- ))
+ )),
+ createdRow = DT::JS(
+ "function(row, data, dataIndex) {",
+ "if (data[0] == null) {",
+ "let cb = row.cells[0].getElementsByTagName('input')[0]",
+ "cb.indeterminate = cb.readOnly = true;",
+ "}",
+ "}"
+ )
))
})
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 8bfef958..78998f97 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -233,10 +233,18 @@ mod_study_forms_server <- function(
targets = 0,
render = DT::JS(
"function(data, type, row, meta) {",
- "return ``;",
+ "return ``;",
"}"
)
- ))
+ )),
+ createdRow = DT::JS(
+ "function(row, data, dataIndex) {",
+ "if (data[0] == null) {",
+ "let cb = row.cells[0].getElementsByTagName('input')[0]",
+ "cb.indeterminate = cb.readOnly = true;",
+ "}",
+ "}"
+ )
))
})
diff --git a/inst/app/www/custom.css b/inst/app/www/custom.css
index 5232a222..d1125951 100644
--- a/inst/app/www/custom.css
+++ b/inst/app/www/custom.css
@@ -61,3 +61,17 @@ div.datatables div.header {
font-weight: bold;
}
+tr:has(td input[type="checkbox"].checked:not(:checked))>td {
+ background-color: aquamarine;
+ border-color: aquamarine;
+}
+
+tr:has(td input[type="checkbox"].unchecked:checked)>td {
+ background-color: aquamarine;
+ border-color: aquamarine;
+}
+
+tr:has(td input[type="checkbox"].indeterminate:not(:indeterminate))>td {
+ background-color: aquamarine;
+ border-color: aquamarine;
+}
From 7939b3ca6016d28af5931750a2c74d27b1d771e7 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 22 Nov 2024 15:44:54 -0500
Subject: [PATCH 03/86] Grab id's for `o_reviewed`
---
R/fct_tables.R | 6 +++---
R/mod_common_forms.R | 3 ++-
R/mod_study_forms.R | 3 ++-
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/R/fct_tables.R b/R/fct_tables.R
index cbe45ddf..8a31bafe 100644
--- a/R/fct_tables.R
+++ b/R/fct_tables.R
@@ -55,9 +55,9 @@ create_table.default <- function(
data <- dplyr::mutate(
data,
o_reviewed = dplyr::case_when(
- any(reviewed == "No") & any(reviewed == "Yes") ~ NA,
- any(reviewed == "Yes") ~ TRUE,
- .default = FALSE
+ any(reviewed == "No") & any(reviewed == "Yes") ~ list(list(reviewed = NA, ids = id)),
+ any(reviewed == "Yes") ~ list(list(reviewed = TRUE, ids = id)),
+ .default = list(list(reviewed = FALSE, ids = id))
),
.by = keep_vars)
keep_vars <- c("o_reviewed", keep_vars)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 5bff4e79..58dc179b 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -170,7 +170,8 @@ mod_common_forms_server <- function(
targets = 0,
render = DT::JS(
"function(data, type, row, meta) {",
- "return ``;",
+ "var reviewed = data.reviewed;",
+ "return ``;",
"}"
)
)),
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 78998f97..ee9c8568 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -233,7 +233,8 @@ mod_study_forms_server <- function(
targets = 0,
render = DT::JS(
"function(data, type, row, meta) {",
- "return ``;",
+ "var reviewed = data.reviewed;",
+ "return ``;",
"}"
)
)),
From e3bb3ed5fa33ac5c6be9588342a839e92e6d8437 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 22 Nov 2024 16:27:55 -0500
Subject: [PATCH 04/86] Add input handler
---
R/mod_common_forms.R | 10 ++++++++++
R/mod_study_forms.R | 10 ++++++++++
R/shiny.R | 4 ++++
3 files changed, 24 insertions(+)
create mode 100644 R/shiny.R
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 58dc179b..e2f36b1a 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -165,6 +165,16 @@ mod_common_forms_server <- function(
rownames= FALSE,
title = form,
escape = FALSE,
+ callback = DT::JS(
+ "table.on('click', 'input[type=\"checkbox\"]', function(){",
+ "var id = $(this).closest('.datatables').attr('id');",
+ "var cell = table.cell($(this).closest('td'));",
+ "var ids = cell.data().ids;",
+ "var review = $(this).is(':checked');",
+ "var info = {review: review, ids: ids};",
+ "Shiny.setInputValue(id + '_review_selection:CS.reviewInfo', info);",
+ "})"
+ ),
options = list(
columnDefs = list(list(
targets = 0,
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index ee9c8568..5450ab7f 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -228,6 +228,16 @@ mod_study_forms_server <- function(
table_names,
rownames= FALSE,
escape = FALSE,
+ callback = DT::JS(
+ "table.on('click', 'input[type=\"checkbox\"]', function(){",
+ "var id = $(this).closest('.datatables').attr('id');",
+ "var cell = table.cell($(this).closest('td'));",
+ "var ids = cell.data().ids;",
+ "var review = $(this).is(':checked');",
+ "var info = {review: review, ids: ids};",
+ "Shiny.setInputValue(id + '_review_selection:CS.reviewInfo', info);",
+ "})"
+ ),
options = list(
columnDefs = list(list(
targets = 0,
diff --git a/R/shiny.R b/R/shiny.R
new file mode 100644
index 00000000..43964b72
--- /dev/null
+++ b/R/shiny.R
@@ -0,0 +1,4 @@
+shiny::registerInputHandler('CS.reviewInfo', function(val, ...) {
+ val[["ids"]] <- unlist(val[["ids"]])
+ val
+}, TRUE)
\ No newline at end of file
From 6e258a21a17efbc7c1f82fca99e071aba886fdb3 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 09:45:14 -0500
Subject: [PATCH 05/86] Capture row index info
---
R/app_server.R | 2 ++
R/mod_common_forms.R | 22 +++++++++++++++++++---
R/shiny.R | 9 ++++++---
3 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/R/app_server.R b/R/app_server.R
index 0135aacb..af0d25bb 100644
--- a/R/app_server.R
+++ b/R/app_server.R
@@ -30,6 +30,8 @@ app_server <- function(
})
check_appdata(app_data, meta)
+ session$userData$review_records <- reactiveValues()
+
res_auth <- authenticate_server(
all_sites = app_vars$Sites$site_code,
credentials_db = credentials_db,
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index e2f36b1a..c25deb33 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -94,6 +94,7 @@ mod_common_forms_server <- function(
ns <- session$ns
data_active <- reactive({
+ req(!is.null(input$show_all_data))
shiny::validate(need(
!is.null(r$filtered_data[[form]]),
paste0("Warning: no data found in the database for the form '", form, "'.")
@@ -150,6 +151,20 @@ mod_common_forms_server <- function(
))
})
+ observeEvent(data_active(), {
+ session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character(), row_index = character())
+ })
+
+ observeEvent(input$common_form_table_review_selection, {
+ session$userData$review_records[[form]] <-
+ dplyr::rows_upsert(
+ session$userData$review_records[[form]],
+ input$common_form_table_review_selection,
+ by = "id"
+ ) |>
+ dplyr::arrange(id)
+ })
+
output[["common_form_table"]] <- DT::renderDT({
df <- data_active()
if(form == "Adverse events") {
@@ -167,12 +182,13 @@ mod_common_forms_server <- function(
escape = FALSE,
callback = DT::JS(
"table.on('click', 'input[type=\"checkbox\"]', function(){",
- "var id = $(this).closest('.datatables').attr('id');",
+ "var tblId = $(this).closest('.datatables').attr('id');",
"var cell = table.cell($(this).closest('td'));",
+ "var rowIdx = table.row($(this).closest('tr')).index();",
"var ids = cell.data().ids;",
"var review = $(this).is(':checked');",
- "var info = {review: review, ids: ids};",
- "Shiny.setInputValue(id + '_review_selection:CS.reviewInfo', info);",
+ "var info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};",
+ "Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
"})"
),
options = list(
diff --git a/R/shiny.R b/R/shiny.R
index 43964b72..4189c23e 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -1,4 +1,7 @@
shiny::registerInputHandler('CS.reviewInfo', function(val, ...) {
- val[["ids"]] <- unlist(val[["ids"]])
- val
-}, TRUE)
\ No newline at end of file
+ with(val, data.frame(
+ id = unlist(ids),
+ reviewed = ifelse(review, "Yes", "No"),
+ row_index = row
+ ))
+}, TRUE)
From a42049b6a41f4e95fa4a71dcf7b8e3c5b0fe747e Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 09:52:17 -0500
Subject: [PATCH 06/86] Standardize callbacks and renders
---
R/mod_common_forms.R | 13 +++++++------
R/mod_study_forms.R | 27 +++------------------------
R/shiny.R | 28 ++++++++++++++++++++++++++++
3 files changed, 38 insertions(+), 30 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index c25deb33..12ad93c2 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -139,15 +139,13 @@ mod_common_forms_server <- function(
rownames= FALSE,
title = "Serious Adverse Events",
escape = FALSE,
+ callback = checkbox_callback,
options = list(
columnDefs = list(list(
targets = 0,
- render = DT::JS(
- "function(data, type, row, meta) {",
- "return ``;",
- "}"
- )
- ))
+ render = checkbox_render
+ )),
+ createdRow = checkbox_create_callback
))
})
@@ -191,6 +189,7 @@ mod_common_forms_server <- function(
"Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
"})"
),
+ callback = checkbox_callback,
options = list(
columnDefs = list(list(
targets = 0,
@@ -200,6 +199,7 @@ mod_common_forms_server <- function(
"return ``;",
"}"
)
+ render = checkbox_render
)),
createdRow = DT::JS(
"function(row, data, dataIndex) {",
@@ -209,6 +209,7 @@ mod_common_forms_server <- function(
"}",
"}"
)
+ createdRow = checkbox_create_callback
))
})
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 5450ab7f..aa60f058 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -228,34 +228,13 @@ mod_study_forms_server <- function(
table_names,
rownames= FALSE,
escape = FALSE,
- callback = DT::JS(
- "table.on('click', 'input[type=\"checkbox\"]', function(){",
- "var id = $(this).closest('.datatables').attr('id');",
- "var cell = table.cell($(this).closest('td'));",
- "var ids = cell.data().ids;",
- "var review = $(this).is(':checked');",
- "var info = {review: review, ids: ids};",
- "Shiny.setInputValue(id + '_review_selection:CS.reviewInfo', info);",
- "})"
- ),
+ callback = checkbox_callback,
options = list(
columnDefs = list(list(
targets = 0,
- render = DT::JS(
- "function(data, type, row, meta) {",
- "var reviewed = data.reviewed;",
- "return ``;",
- "}"
- )
+ render = checkbox_render
)),
- createdRow = DT::JS(
- "function(row, data, dataIndex) {",
- "if (data[0] == null) {",
- "let cb = row.cells[0].getElementsByTagName('input')[0]",
- "cb.indeterminate = cb.readOnly = true;",
- "}",
- "}"
- )
+ createdRow = checkbox_create_callback
))
})
diff --git a/R/shiny.R b/R/shiny.R
index 4189c23e..500b1acf 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -5,3 +5,31 @@ shiny::registerInputHandler('CS.reviewInfo', function(val, ...) {
row_index = row
))
}, TRUE)
+
+checkbox_callback <- DT::JS(
+ "table.on('click', 'input[type=\"checkbox\"]', function(){",
+ "var tblId = $(this).closest('.datatables').attr('id');",
+ "var cell = table.cell($(this).closest('td'));",
+ "var rowIdx = table.row($(this).closest('tr')).index();",
+ "var ids = cell.data().ids;",
+ "var review = $(this).is(':checked');",
+ "var info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};",
+ "Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
+ "})"
+)
+
+checkbox_render <- DT::JS(
+ "function(data, type, row, meta) {",
+ "var reviewed = data.reviewed;",
+ "return ``;",
+ "}"
+)
+
+checkbox_create_callback <- DT::JS(
+ "function(row, data, dataIndex) {",
+ "if (data[0] == null) {",
+ "let cb = row.cells[0].getElementsByTagName('input')[0]",
+ "cb.indeterminate = cb.readOnly = true;",
+ "}",
+ "}"
+)
From 7ba17d9f3837b541940d7c0779c7d003670a0517 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 10:45:09 -0500
Subject: [PATCH 07/86] Add missing observers
---
R/mod_common_forms.R | 35 ++++++++++-------------------------
1 file changed, 10 insertions(+), 25 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 12ad93c2..3343f761 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -163,6 +163,16 @@ mod_common_forms_server <- function(
dplyr::arrange(id)
})
+ observeEvent(input$SAE_table_review_selection, {
+ session$userData$review_records[[form]] <-
+ dplyr::rows_upsert(
+ session$userData$review_records[[form]],
+ input$SAE_table_review_selection,
+ by = "id"
+ ) |>
+ dplyr::arrange(id)
+ })
+
output[["common_form_table"]] <- DT::renderDT({
df <- data_active()
if(form == "Adverse events") {
@@ -178,37 +188,12 @@ mod_common_forms_server <- function(
rownames= FALSE,
title = form,
escape = FALSE,
- callback = DT::JS(
- "table.on('click', 'input[type=\"checkbox\"]', function(){",
- "var tblId = $(this).closest('.datatables').attr('id');",
- "var cell = table.cell($(this).closest('td'));",
- "var rowIdx = table.row($(this).closest('tr')).index();",
- "var ids = cell.data().ids;",
- "var review = $(this).is(':checked');",
- "var info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};",
- "Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
- "})"
- ),
callback = checkbox_callback,
options = list(
columnDefs = list(list(
targets = 0,
- render = DT::JS(
- "function(data, type, row, meta) {",
- "var reviewed = data.reviewed;",
- "return ``;",
- "}"
- )
render = checkbox_render
)),
- createdRow = DT::JS(
- "function(row, data, dataIndex) {",
- "if (data[0] == null) {",
- "let cb = row.cells[0].getElementsByTagName('input')[0]",
- "cb.indeterminate = cb.readOnly = true;",
- "}",
- "}"
- )
createdRow = checkbox_create_callback
))
})
From f0dfcc31588a6a6deccc523ff3eb53221d7d2ef5 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 10:46:48 -0500
Subject: [PATCH 08/86] Add study form observers
---
R/mod_study_forms.R | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index aa60f058..56e5eea2 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -163,6 +163,7 @@ mod_study_forms_server <- function(
})
table_data_active <- reactive({
+ req(!is.null(input$show_all_data))
validate(need(
r$filtered_data[[form]],
paste0("Warning: no data found in database for the form '", form, "'")
@@ -193,6 +194,20 @@ mod_study_forms_server <- function(
lapply(add_missing_columns(item_info, cols)[1, cols], isTRUE)
})
+ observeEvent(table_data_active(), {
+ session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character(), row_index = character())
+ })
+
+ observeEvent(input$table_review_selection, {
+ session$userData$review_records[[form]] <-
+ dplyr::rows_upsert(
+ session$userData$review_records[[form]],
+ input$table_review_selection,
+ by = "id"
+ ) |>
+ dplyr::arrange(id)
+ })
+
############################### Outputs: ###################################
dynamic_figure <- reactive({
req(nrow(fig_data()) > 0, scaling_data())
From 9f0e4de79e76aa3cc3ffd137a0bdcf2f4ca6030e Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 10:54:01 -0500
Subject: [PATCH 09/86] Add `anti_join()`s back
---
R/mod_common_forms.R | 8 ++++++++
R/mod_study_forms.R | 4 ++++
2 files changed, 12 insertions(+)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 3343f761..06ff5890 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -160,6 +160,10 @@ mod_common_forms_server <- function(
input$common_form_table_review_selection,
by = "id"
) |>
+ dplyr::anti_join(
+ subset(r$review_data, item_group == form),
+ by = c("id", "reviewed")
+ ) |>
dplyr::arrange(id)
})
@@ -170,6 +174,10 @@ mod_common_forms_server <- function(
input$SAE_table_review_selection,
by = "id"
) |>
+ dplyr::anti_join(
+ subset(r$review_data, item_group == form),
+ by = c("id", "reviewed")
+ ) |>
dplyr::arrange(id)
})
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 56e5eea2..676cc84f 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -205,6 +205,10 @@ mod_study_forms_server <- function(
input$table_review_selection,
by = "id"
) |>
+ dplyr::anti_join(
+ subset(r$review_data, item_group == form),
+ by = c("id", "reviewed")
+ ) |>
dplyr::arrange(id)
})
From b703beca663dcf5bef46e64816ad471afc5db40b Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 10:54:25 -0500
Subject: [PATCH 10/86] First pass at integration in review module
---
R/mod_review_forms.R | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index ac31f7dd..ad921b42 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -180,15 +180,13 @@ mod_review_forms_server <- function(
enable_save_review <- reactive({
req(
- review_data_active(),
+ active_form(),
+ session$userData$review_records[[active_form()]],
is.logical(input$form_reviewed),
is.logical(enable_any_review())
)
if(!enable_any_review()) return(FALSE)
- any(c(
- unique(with(review_data_active(), reviewed[edit_date_time == max(as.POSIXct(edit_date_time))])) == "No" & input$form_reviewed,
- unique(with(review_data_active(), reviewed[edit_date_time == max(as.POSIXct(edit_date_time))])) == "Yes" & !input$form_reviewed
- ))
+ nrow(session$userData$review_records[[active_form()]]) != 0
})
observeEvent(c(enable_any_review(), enable_save_review()), {
@@ -226,9 +224,9 @@ mod_review_forms_server <- function(
review_save_error(FALSE)
golem::cat_dev("Save review status reviewed:", input$form_reviewed, "\n")
- review_records <- review_data_active()["id"] |>
+ review_records <- session$userData$review_records[[active_form()]][c("id", "reviewed")] |>
dplyr::mutate(
- reviewed = if(input$form_reviewed) "Yes" else "No",
+ # reviewed = if(input$form_reviewed) "Yes" else "No",
comment = ifelse(is.null(input$review_comment), "", input$review_comment),
reviewer = paste0(r$user_name, " (", r$user_role, ")"),
timestamp = time_stamp(),
From 5fd57472efb6e0282d26f1ea466c2fbb5874a8f0 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 12:26:29 -0500
Subject: [PATCH 11/86] Fix couple of issues
---
R/mod_review_forms.R | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index ad921b42..3e576d18 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -226,11 +226,10 @@ mod_review_forms_server <- function(
review_records <- session$userData$review_records[[active_form()]][c("id", "reviewed")] |>
dplyr::mutate(
- # reviewed = if(input$form_reviewed) "Yes" else "No",
comment = ifelse(is.null(input$review_comment), "", input$review_comment),
reviewer = paste0(r$user_name, " (", r$user_role, ")"),
timestamp = time_stamp(),
- status = if(input$form_reviewed) "old" else "new"
+ status = ifelse(reviewed == "Yes", "old", "new")
)
golem::cat_dev("review records to add:\n")
@@ -266,7 +265,7 @@ mod_review_forms_server <- function(
review_save_error(any(
!isTRUE(all.equal(review_records_db, review_records, check.attributes = FALSE)),
- !isTRUE(all.equal(updated_items_memory, review_records_db, check.attributes = FALSE))
+ !isTRUE(all.equal(updated_items_memory[,names(review_records_db)], review_records_db, check.attributes = FALSE))
))
if(review_save_error()){
From 59562699c6eaaf45e6405edb6a021d18148b545a Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 12:27:58 -0500
Subject: [PATCH 12/86] Remove row selection
---
R/mod_common_forms.R | 2 ++
R/mod_study_forms.R | 1 +
2 files changed, 3 insertions(+)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 06ff5890..5a11dce3 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -139,6 +139,7 @@ mod_common_forms_server <- function(
rownames= FALSE,
title = "Serious Adverse Events",
escape = FALSE,
+ selection = "none",
callback = checkbox_callback,
options = list(
columnDefs = list(list(
@@ -196,6 +197,7 @@ mod_common_forms_server <- function(
rownames= FALSE,
title = form,
escape = FALSE,
+ selection = "none",
callback = checkbox_callback,
options = list(
columnDefs = list(list(
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 676cc84f..2469c886 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -247,6 +247,7 @@ mod_study_forms_server <- function(
table_names,
rownames= FALSE,
escape = FALSE,
+ selection = "none",
callback = checkbox_callback,
options = list(
columnDefs = list(list(
From d9f49e8afb5d31db75a0349b970edf0055294752 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 25 Nov 2024 12:30:40 -0500
Subject: [PATCH 13/86] Fix bug on study forms
---
R/mod_study_forms.R | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 2469c886..b007e2b7 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -163,7 +163,7 @@ mod_study_forms_server <- function(
})
table_data_active <- reactive({
- req(!is.null(input$show_all_data))
+ req(!is.null(input$show_all))
validate(need(
r$filtered_data[[form]],
paste0("Warning: no data found in database for the form '", form, "'")
From 2493779ab2ee03b3da27764e85ff8054a39a03a4 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 2 Dec 2024 11:17:57 -0500
Subject: [PATCH 14/86] Integrate "form_reviewed" checkbox
---
R/mod_common_forms.R | 2 +-
R/mod_review_forms.R | 49 +++++++++++++++++++++++++++++++++++++++-----
R/shiny.R | 3 +--
3 files changed, 46 insertions(+), 8 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 5a11dce3..01f5e944 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -151,7 +151,7 @@ mod_common_forms_server <- function(
})
observeEvent(data_active(), {
- session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character(), row_index = character())
+ session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character())
})
observeEvent(input$common_form_table_review_selection, {
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index 90b7ede1..fbcbf65c 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -113,13 +113,49 @@ mod_review_forms_server <- function(
with(r$review_data, r$review_data[
subject_id == r$subject_id & item_group == active_form(),
])
- })
+ })
+
+ review_indeterminate <- reactiveVal()
+
+ observeEvent(review_indeterminate(), {
+ shinyjs::runjs(sprintf("$('#%s').prop('indeterminate', %s)", ns("form_reviewed"), tolower(review_indeterminate())))
+ })
+
+ observe({
+ req(session$userData$review_records[[active_form()]])
+ review_status <-
+ review_data_active()[,c("id", "reviewed")] |>
+ dplyr::rows_update(session$userData$review_records[[active_form()]][,c("id", "reviewed")], by = "id") |>
+ dplyr::distinct(reviewed) |>
+ dplyr::pull()
+
+ if (length(review_status) == 1)
+ updateCheckboxInput(
+ inputId = "form_reviewed",
+ value = identical(review_status, "Yes")
+ )
+ review_indeterminate(length(review_status) > 1)
+ }) |>
+ bindEvent(active_form(), session$userData$review_records[[active_form()]])
+
+ observeEvent(input$form_reviewed, {
+ session$userData$review_records[[active_form()]] <-
+ review_data_active() |>
+ dplyr::mutate(reviewed = ifelse(input$form_reviewed, "Yes", "No")) |>
+ dplyr::select(id, reviewed) |>
+ dplyr::anti_join(
+ subset(r$review_data, item_group == active_form()),
+ by = c("id", "reviewed")
+ ) |>
+ dplyr::arrange(id)
+ })
observeEvent(c(active_form(), r$subject_id), {
cat("Update confirm review button\n\n\n")
req(r$review_data)
golem::cat_dev("review_data_active:\n")
golem::print_dev(review_data_active())
+ review_indeterminate(FALSE)
if(nrow(review_data_active()) == 0){
cat("No review data found for Subject id: ", r$subject_id,
" and group: ", active_form(), "\n")
@@ -131,16 +167,19 @@ mod_review_forms_server <- function(
# it will give a warning. This would be rare since it would mean a datapoint with the same edit date-time was reviewed but another one was not.
# probably better to use defensive coding here to ensure the app does not crash in that case. However we need to define which review status we need to select
# in this case get the reviewed = "No"
- review_status <- with(review_data_active(), reviewed[edit_date_time == max(as.POSIXct(edit_date_time))]) |> unique()
- review_comment <- with(review_data_active(), comment[edit_date_time == max(as.POSIXct(edit_date_time))]) |> unique()
- if(length(review_status) != 1) warning("multiple variables in review_status, namely: ",
- review_status, "Verify data.")
+ review_status <- unique(review_data_active()[["reviewed"]])
+ review_comment <- unique(review_data_active()[["comment"]])
+ if(length(review_status) != 1) {
+ review_indeterminate(TRUE)
+ review_status <- "No"
+ }
}
updateCheckboxInput(
inputId = "form_reviewed",
value = identical(review_status, "Yes")
)
+ shinyjs::runjs(sprintf("$('#%s').prop('indeterminate', %s)", ns("form_reviewed"), tolower(review_indeterminate())))
shinyWidgets::updatePrettySwitch(
session = session,
diff --git a/R/shiny.R b/R/shiny.R
index 500b1acf..90484d36 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -1,8 +1,7 @@
shiny::registerInputHandler('CS.reviewInfo', function(val, ...) {
with(val, data.frame(
id = unlist(ids),
- reviewed = ifelse(review, "Yes", "No"),
- row_index = row
+ reviewed = ifelse(review, "Yes", "No")
))
}, TRUE)
From 0cb8710b69c191beef7735133fc187c39325fcae Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 2 Dec 2024 13:43:41 -0500
Subject: [PATCH 15/86] Update tables from overall checkbox
---
R/app_server.R | 1 +
R/mod_common_forms.R | 7 +++++++
R/mod_review_forms.R | 32 +++++++++++++++-----------------
R/mod_study_forms.R | 6 ++++++
4 files changed, 29 insertions(+), 17 deletions(-)
diff --git a/R/app_server.R b/R/app_server.R
index af0d25bb..3d0ff4f0 100644
--- a/R/app_server.R
+++ b/R/app_server.R
@@ -31,6 +31,7 @@ app_server <- function(
check_appdata(app_data, meta)
session$userData$review_records <- reactiveValues()
+ session$userData$update_checkboxes <- reactiveValues()
res_auth <- authenticate_server(
all_sites = app_vars$Sites$site_code,
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 01f5e944..ffaf57be 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -168,6 +168,13 @@ mod_common_forms_server <- function(
dplyr::arrange(id)
})
+ observeEvent(session$userData$update_checkboxes[[form]], {
+ checked <- session$userData$update_checkboxes[[form]]
+
+ shinyjs::runjs(sprintf("$(':checkbox', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s)", ns("common_form_table"), tolower(checked)))
+ shinyjs::runjs(sprintf("$(':checkbox', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s)", ns("SAE_table"), tolower(checked)))
+ })
+
observeEvent(input$SAE_table_review_selection, {
session$userData$review_records[[form]] <-
dplyr::rows_upsert(
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index fbcbf65c..46c32d48 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -20,7 +20,8 @@ mod_review_forms_ui <- function(id){
inputId = ns("form_reviewed"),
label = "Reviewed",
value = FALSE
- ),
+ ) |>
+ shiny::tagAppendAttributes(class = "cs_checkbox", .cssSelector = "input"),
"Mark as reviewed",
placement = "bottom"
),
@@ -129,16 +130,19 @@ mod_review_forms_server <- function(
dplyr::distinct(reviewed) |>
dplyr::pull()
- if (length(review_status) == 1)
- updateCheckboxInput(
- inputId = "form_reviewed",
- value = identical(review_status, "Yes")
- )
+ shinyjs::runjs(sprintf("$('#%s').prop('checked', %s)", ns("form_reviewed"), tolower(identical(review_status, "Yes"))))
review_indeterminate(length(review_status) > 1)
}) |>
bindEvent(active_form(), session$userData$review_records[[active_form()]])
+ observeEvent(r$subject_id, {
+ session$userData$update_checkboxes[[active_form()]] <- NULL
+ session$userData$review_records[[active_form()]] <- NULL
+ })
+
observeEvent(input$form_reviewed, {
+ session$userData$update_checkboxes[[active_form()]] <- input$form_reviewed
+
session$userData$review_records[[active_form()]] <-
review_data_active() |>
dplyr::mutate(reviewed = ifelse(input$form_reviewed, "Yes", "No")) |>
@@ -148,7 +152,7 @@ mod_review_forms_server <- function(
by = c("id", "reviewed")
) |>
dplyr::arrange(id)
- })
+ }, ignoreInit = TRUE)
observeEvent(c(active_form(), r$subject_id), {
cat("Update confirm review button\n\n\n")
@@ -169,16 +173,11 @@ mod_review_forms_server <- function(
# in this case get the reviewed = "No"
review_status <- unique(review_data_active()[["reviewed"]])
review_comment <- unique(review_data_active()[["comment"]])
- if(length(review_status) != 1) {
+ if(length(review_status) != 1)
review_indeterminate(TRUE)
- review_status <- "No"
- }
}
- updateCheckboxInput(
- inputId = "form_reviewed",
- value = identical(review_status, "Yes")
- )
+ shinyjs::runjs(sprintf("$('#%s').prop('checked', %s)", ns("form_reviewed"), tolower(identical(review_status, "Yes"))))
shinyjs::runjs(sprintf("$('#%s').prop('indeterminate', %s)", ns("form_reviewed"), tolower(review_indeterminate())))
shinyWidgets::updatePrettySwitch(
@@ -221,7 +220,6 @@ mod_review_forms_server <- function(
req(
active_form(),
session$userData$review_records[[active_form()]],
- is.logical(input$form_reviewed),
is.logical(enable_any_review())
)
if(!enable_any_review()) return(FALSE)
@@ -258,10 +256,10 @@ mod_review_forms_server <- function(
review_save_error <- reactiveVal(FALSE)
observeEvent(input$save_review, {
- req(is.logical(input$form_reviewed), review_data_active())
+ req(review_data_active())
req(enable_save_review())
review_save_error(FALSE)
- golem::cat_dev("Save review status reviewed:", input$form_reviewed, "\n")
+ # golem::cat_dev("Save review status reviewed:", input$form_reviewed, "\n")
review_records <- session$userData$review_records[[active_form()]][c("id", "reviewed")] |>
dplyr::mutate(
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index b007e2b7..a3a9830d 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -212,6 +212,12 @@ mod_study_forms_server <- function(
dplyr::arrange(id)
})
+ observeEvent(session$userData$update_checkboxes[[form]], {
+ checked <- session$userData$update_checkboxes[[form]]
+
+ shinyjs::runjs(sprintf("$(':checkbox', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s)", ns("table"), tolower(checked)))
+ })
+
############################### Outputs: ###################################
dynamic_figure <- reactive({
req(nrow(fig_data()) > 0, scaling_data())
From 28943513bde6415e18663c988922ffbda86da7a6 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 2 Dec 2024 13:59:03 -0500
Subject: [PATCH 16/86] Update `app-feature-1` snapshots
---
.../app_feature_01/app-feature-1-002.json | 92 +++++++++-----
.../app_feature_01/app-feature-1-003.json | 92 +++++++++-----
.../app_feature_01/app-feature-1-004.json | 113 +++++++++++-------
.../app_feature_01/app-feature-1-005.json | 113 +++++++++++-------
4 files changed, 262 insertions(+), 148 deletions(-)
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
index 2796a847..e02389e1 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
@@ -9,7 +9,7 @@
"Scroller",
"ColReorder"
],
- "container": "
\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -18,66 +18,75 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "Start date",
- "targets": 3
+ "targets": 4
},
{
"name": "End date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Category",
- "targets": 9
+ "targets": 10
},
{
"name": "Awareness date",
- "targets": 10
+ "targets": 11
},
{
"name": "Date of death",
- "targets": 11
+ "targets": 12
},
{
"name": "Death reason",
- "targets": 12
+ "targets": 13
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -90,16 +99,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -242,7 +255,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -251,54 +264,63 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "start date",
- "targets": 3
+ "targets": 4
},
{
"name": "end date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Serious Adverse Event",
- "targets": 9
+ "targets": 10
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -311,16 +333,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
index 76c45761..12f5431a 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
@@ -9,7 +9,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -18,66 +18,75 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "Start date",
- "targets": 3
+ "targets": 4
},
{
"name": "End date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Category",
- "targets": 9
+ "targets": 10
},
{
"name": "Awareness date",
- "targets": 10
+ "targets": 11
},
{
"name": "Date of death",
- "targets": 11
+ "targets": 12
},
{
"name": "Death reason",
- "targets": 12
+ "targets": 13
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -90,16 +99,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -242,7 +255,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -251,54 +264,63 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "start date",
- "targets": 3
+ "targets": 4
},
{
"name": "end date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Serious Adverse Event",
- "targets": 9
+ "targets": 10
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -311,16 +333,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
index 76db2408..a0447bc5 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
@@ -9,7 +9,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -18,66 +18,75 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "Start date",
- "targets": 3
+ "targets": 4
},
{
"name": "End date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Category",
- "targets": 9
+ "targets": 10
},
{
"name": "Awareness date",
- "targets": 10
+ "targets": 11
},
{
"name": "Date of death",
- "targets": 11
+ "targets": 12
},
{
"name": "Death reason",
- "targets": 12
+ "targets": 13
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -90,16 +99,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -242,7 +255,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -251,54 +264,63 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "start date",
- "targets": 3
+ "targets": 4
},
{
"name": "end date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Serious Adverse Event",
- "targets": 9
+ "targets": 10
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -311,16 +333,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -8270,7 +8296,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n <\/th>\n | Event<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | Event<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -8279,15 +8305,13 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
{
- "orderable": false,
- "targets": 0
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
- "name": " ",
+ "name": "o_reviewed",
"targets": 0
},
{
@@ -8327,6 +8351,9 @@
"targets": 9
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -8339,16 +8366,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
index e683af83..df20d098 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
@@ -9,7 +9,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -18,66 +18,75 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "Start date",
- "targets": 3
+ "targets": 4
},
{
"name": "End date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Category",
- "targets": 9
+ "targets": 10
},
{
"name": "Awareness date",
- "targets": 10
+ "targets": 11
},
{
"name": "Date of death",
- "targets": 11
+ "targets": 12
},
{
"name": "Death reason",
- "targets": 12
+ "targets": 13
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -90,16 +99,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -242,7 +255,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -251,54 +264,63 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "start date",
- "targets": 3
+ "targets": 4
},
{
"name": "end date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Serious Adverse Event",
- "targets": 9
+ "targets": 10
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -311,16 +333,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -8271,7 +8297,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n <\/th>\n | Event<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | Event<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -8280,15 +8306,13 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
{
- "orderable": false,
- "targets": 0
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
- "name": " ",
+ "name": "o_reviewed",
"targets": 0
},
{
@@ -8328,6 +8352,9 @@
"targets": 9
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -8340,16 +8367,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
From b27d95dd1dbfd00c63de7f216f729b278460baa9 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 2 Dec 2024 14:01:58 -0500
Subject: [PATCH 17/86] Update app-feature-3-002.json
---
.../app_feature_03/app-feature-3-002.json | 116 ++++++++++++------
1 file changed, 81 insertions(+), 35 deletions(-)
diff --git a/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json b/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
index 3a9093e2..7a2a525a 100644
--- a/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
+++ b/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
@@ -146,6 +146,15 @@
"caseInsensitive": true
}
},
+ {
+ "visible": true,
+ "search": {
+ "search": "",
+ "smart": true,
+ "regex": false,
+ "caseInsensitive": true
+ }
+ },
{
"visible": true,
"search": {
@@ -169,7 +178,8 @@
9,
10,
11,
- 12
+ 12,
+ 13
],
"scroller": {
"topRow": 0,
@@ -295,6 +305,15 @@
"caseInsensitive": true
}
},
+ {
+ "visible": true,
+ "search": {
+ "search": "",
+ "smart": true,
+ "regex": false,
+ "caseInsensitive": true
+ }
+ },
{
"visible": true,
"search": {
@@ -315,7 +334,8 @@
6,
7,
8,
- 9
+ 9,
+ 10
],
"scroller": {
"topRow": 0,
@@ -463,7 +483,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -472,66 +492,75 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "Start date",
- "targets": 3
+ "targets": 4
},
{
"name": "End date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Category",
- "targets": 9
+ "targets": 10
},
{
"name": "Awareness date",
- "targets": 10
+ "targets": 11
},
{
"name": "Date of death",
- "targets": 11
+ "targets": 12
},
{
"name": "Death reason",
- "targets": 12
+ "targets": 13
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -544,16 +573,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -696,7 +729,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -705,54 +738,63 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
+ {
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ },
{
"className": "dt-right",
+ "targets": 1
+ },
+ {
+ "name": "o_reviewed",
"targets": 0
},
{
"name": "N",
- "targets": 0
+ "targets": 1
},
{
"name": "Name",
- "targets": 1
+ "targets": 2
},
{
"name": "AESI",
- "targets": 2
+ "targets": 3
},
{
"name": "start date",
- "targets": 3
+ "targets": 4
},
{
"name": "end date",
- "targets": 4
+ "targets": 5
},
{
"name": "CTCAE severity",
- "targets": 5
+ "targets": 6
},
{
"name": "Treatment related",
- "targets": 6
+ "targets": 7
},
{
"name": "Treatment action",
- "targets": 7
+ "targets": 8
},
{
"name": "Other action",
- "targets": 8
+ "targets": 9
},
{
"name": "Serious Adverse Event",
- "targets": 9
+ "targets": 10
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -765,16 +807,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
From 36f843ab96d019d13a3e6938a4adfd023fc3218e Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 2 Dec 2024 14:03:23 -0500
Subject: [PATCH 18/86] Update app_feature_04.md
---
tests/testthat/_snaps/app_feature_04.md | 30 ++++++++++++++-----------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/tests/testthat/_snaps/app_feature_04.md b/tests/testthat/_snaps/app_feature_04.md
index 5ecee994..65ed6907 100644
--- a/tests/testthat/_snaps/app_feature_04.md
+++ b/tests/testthat/_snaps/app_feature_04.md
@@ -25,17 +25,21 @@
Code
print(table_data, width = Inf)
Output
- # A tibble: 2 x 9
- event_name `Systolic blood pressure` `Diastolic blood pressure`
-
- 1 Screening 99* mmHg 77* mmHg
- 2 Visit 2 99* mmHg 77* mmHg
- Pulse Resp Temperature
-
- 1 77* beats/min 9* breaths/min 37.5* °C
- 2 77* beats/min 9* breaths/min 37.5* °C
- `Weight change since screening` BMI Weight
-
- 1 22.09* kg/m2 70* kg
- 2
+ # A tibble: 2 x 10
+ o_reviewed event_name `Systolic blood pressure`
+
+ 1 Screening 99* mmHg
+ 2 Visit 2 99* mmHg
+ `Diastolic blood pressure` Pulse Resp
+
+ 1 77* mmHg 77* beats/min 9* breaths/min
+ 2 77* mmHg 77* beats/min 9* breaths/min
+ Temperature `Weight change since screening` BMI
+
+ 1 37.5* °C 22.09* kg/m2
+ 2 37.5* °C
+ Weight
+
+ 1 70* kg
+ 2
From e98afa047a8010daf05fb57fb01a85d5ba49a2c5 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 2 Dec 2024 14:14:10 -0500
Subject: [PATCH 19/86] Create custom.js
---
inst/app/www/custom.js | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 inst/app/www/custom.js
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
new file mode 100644
index 00000000..c810c0bc
--- /dev/null
+++ b/inst/app/www/custom.js
@@ -0,0 +1,25 @@
+function ts(cb) {
+ if (cb.readOnly) cb.checked=cb.readOnly=false;
+ else if (!cb.checked) cb.readOnly=cb.indeterminate=true;
+}
+
+var customCheckbox = new Shiny.InputBinding();
+
+$.extend(customCheckbox, {
+ find: function(scope) {
+ return $(scope).find("input[type='checkbox'].cs_checkbox");
+ },
+ getValue: function(el) {
+ return el.checked;
+ },
+ subscribe: function(el, callback) {
+ $(el).on("change.checkboxInputBinding", function() {
+ Shiny.onInputChange($(this).attr('id'), this.checked, {priority: 'event'});
+ });
+ },
+ unsubscribe: function(el) {
+ $(el).off(".checkboxInputBinding");
+ }
+});
+
+Shiny.inputBindings.register(customCheckbox);
From a353f5d5062c3721a0cb947692558efafb185fc3 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 2 Dec 2024 14:34:30 -0500
Subject: [PATCH 20/86] Fix warnings and errors in `mod_common_forms` test
---
R/fct_tables.R | 2 +-
tests/testthat/test-mod_common_forms.R | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/R/fct_tables.R b/R/fct_tables.R
index 8a31bafe..e54a425b 100644
--- a/R/fct_tables.R
+++ b/R/fct_tables.R
@@ -59,7 +59,7 @@ create_table.default <- function(
any(reviewed == "Yes") ~ list(list(reviewed = TRUE, ids = id)),
.default = list(list(reviewed = FALSE, ids = id))
),
- .by = keep_vars)
+ .by = dplyr::all_of(keep_vars))
keep_vars <- c("o_reviewed", keep_vars)
}
df <- data[c(keep_vars, name_column, value_column)] |>
diff --git a/tests/testthat/test-mod_common_forms.R b/tests/testthat/test-mod_common_forms.R
index 3762094e..909a001e 100644
--- a/tests/testthat/test-mod_common_forms.R
+++ b/tests/testthat/test-mod_common_forms.R
@@ -52,6 +52,7 @@ describe(
})
rev_data <- get_review_data(bind_rows_custom(appdata)) |>
dplyr::mutate(
+ id = dplyr::row_number(),
reviewed = sample(c("Yes", "No"), dplyr::n(), replace = TRUE),
status = sample(c("new", "old", "updated"), dplyr::n(), replace = TRUE)
)
From 579d80e370ce11a13671cdf5ecb2c7f35543c21b Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 08:01:43 -0500
Subject: [PATCH 21/86] Repair part of `mod_review_form` tests
---
R/mod_review_forms.R | 6 +++---
tests/testthat/test-mod_review_forms.R | 7 +++++++
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index 46c32d48..fa3206bb 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -137,7 +137,7 @@ mod_review_forms_server <- function(
observeEvent(r$subject_id, {
session$userData$update_checkboxes[[active_form()]] <- NULL
- session$userData$review_records[[active_form()]] <- NULL
+ session$userData$review_records[[active_form()]] <- data.frame(id = integer(), reviewed = character())
})
observeEvent(input$form_reviewed, {
@@ -172,7 +172,7 @@ mod_review_forms_server <- function(
# probably better to use defensive coding here to ensure the app does not crash in that case. However we need to define which review status we need to select
# in this case get the reviewed = "No"
review_status <- unique(review_data_active()[["reviewed"]])
- review_comment <- unique(review_data_active()[["comment"]])
+ review_comment <- with(review_data_active(), comment[edit_date_time == max(as.POSIXct(edit_date_time))]) |> unique() |> paste(collapse = "; ")
if(length(review_status) != 1)
review_indeterminate(TRUE)
}
@@ -233,7 +233,7 @@ mod_review_forms_server <- function(
shinyjs::enable("save_review")
shinyjs::enable("add_comment")
shinyjs::enable("review_comment")
- } else{
+ } else {
shinyjs::disable("save_review")
shinyjs::disable("add_comment")
shinyjs::disable("review_comment")
diff --git a/tests/testthat/test-mod_review_forms.R b/tests/testthat/test-mod_review_forms.R
index 4ef10412..198d1a4e 100644
--- a/tests/testthat/test-mod_review_forms.R
+++ b/tests/testthat/test-mod_review_forms.R
@@ -79,6 +79,9 @@ describe(
mod_review_forms_server, args = testargs, {
ns <- session$ns
+ session$userData$review_records <- reactiveValues()
+ session$userData$update_checkboxes <- reactiveValues()
+
## patient has two rows: AF and Cystitis. AF is already reviewed by someone else:
expect_equal(
data.frame(
@@ -94,6 +97,7 @@ describe(
})
)
+ session$setInputs(form_reviewed = FALSE) # Needs to be initialized to work
session$setInputs(form_reviewed = TRUE, save_review = 1)
db_reviewdata <- db_get_table(db_path)
db_reviewlogdata <- db_get_table(db_path, "all_review_data_log")
@@ -183,6 +187,9 @@ describe(
)
}
test_server <- function(input, output, session){
+ session$userData$review_records <- reactiveValues()
+ session$userData$update_checkboxes <- reactiveValues()
+
mod_review_forms_server(
id = "test",
r = reactiveValues(
From a7fda2180d9f1633180bf815882326ba40db05e0 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 09:03:49 -0500
Subject: [PATCH 22/86] Properly handle partially reviewed rows
---
R/mod_common_forms.R | 2 ++
R/mod_study_forms.R | 1 +
R/shiny.R | 8 ++++----
inst/app/www/custom.js | 9 +++++++--
4 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index ffaf57be..6fd8128b 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -161,6 +161,7 @@ mod_common_forms_server <- function(
input$common_form_table_review_selection,
by = "id"
) |>
+ dplyr::filter(!is.na(reviewed)) |>
dplyr::anti_join(
subset(r$review_data, item_group == form),
by = c("id", "reviewed")
@@ -182,6 +183,7 @@ mod_common_forms_server <- function(
input$SAE_table_review_selection,
by = "id"
) |>
+ dplyr::filter(!is.na(reviewed)) |>
dplyr::anti_join(
subset(r$review_data, item_group == form),
by = c("id", "reviewed")
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index a3a9830d..f45d81d7 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -205,6 +205,7 @@ mod_study_forms_server <- function(
input$table_review_selection,
by = "id"
) |>
+ dplyr::filter(!is.na(reviewed)) |>
dplyr::anti_join(
subset(r$review_data, item_group == form),
by = c("id", "reviewed")
diff --git a/R/shiny.R b/R/shiny.R
index 90484d36..22b673c8 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -1,7 +1,7 @@
shiny::registerInputHandler('CS.reviewInfo', function(val, ...) {
with(val, data.frame(
id = unlist(ids),
- reviewed = ifelse(review, "Yes", "No")
+ reviewed = ifelse(isTRUE(review), "Yes", ifelse(isFALSE(review), "No", NA_character_))
))
}, TRUE)
@@ -11,7 +11,7 @@ checkbox_callback <- DT::JS(
"var cell = table.cell($(this).closest('td'));",
"var rowIdx = table.row($(this).closest('tr')).index();",
"var ids = cell.data().ids;",
- "var review = $(this).is(':checked');",
+ "var review = $(this).is(':indeterminate') ? null : $(this).is(':checked');",
"var info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};",
"Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
"})"
@@ -26,9 +26,9 @@ checkbox_render <- DT::JS(
checkbox_create_callback <- DT::JS(
"function(row, data, dataIndex) {",
- "if (data[0] == null) {",
+ "if (data[0].reviewed == null) {",
"let cb = row.cells[0].getElementsByTagName('input')[0]",
- "cb.indeterminate = cb.readOnly = true;",
+ "cb.indeterminate = true;",
"}",
"}"
)
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index c810c0bc..bedc0976 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -1,6 +1,11 @@
function ts(cb) {
- if (cb.readOnly) cb.checked=cb.readOnly=false;
- else if (!cb.checked) cb.readOnly=cb.indeterminate=true;
+ if (cb.readOnly) {
+ cb.indeterminate=true;
+ cb.readOnly=cb.checked=false;
+ } else if (!cb.checked) {
+ cb.readOnly=true;
+ cb.indeterminate=false;
+ }
}
var customCheckbox = new Shiny.InputBinding();
From 310eff22cbccac5c92d437d77610a1adeb653d1c Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 10:13:22 -0500
Subject: [PATCH 23/86] Repair interactivity between overall checkbox and
tables
---
R/mod_common_forms.R | 9 +++++++--
R/mod_review_forms.R | 2 +-
R/mod_study_forms.R | 5 ++++-
R/shiny.R | 9 +++++++++
4 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 6fd8128b..83af3dc7 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -151,10 +151,13 @@ mod_common_forms_server <- function(
})
observeEvent(data_active(), {
+ session$userData$update_checkboxes[[form]] <- NULL
session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character())
})
observeEvent(input$common_form_table_review_selection, {
+ session$userData$update_checkboxes[[form]] <- NULL
+
session$userData$review_records[[form]] <-
dplyr::rows_upsert(
session$userData$review_records[[form]],
@@ -172,11 +175,13 @@ mod_common_forms_server <- function(
observeEvent(session$userData$update_checkboxes[[form]], {
checked <- session$userData$update_checkboxes[[form]]
- shinyjs::runjs(sprintf("$(':checkbox', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s)", ns("common_form_table"), tolower(checked)))
- shinyjs::runjs(sprintf("$(':checkbox', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s)", ns("SAE_table"), tolower(checked)))
+ update_cbs(ns("common_form_table"), checked)
+ update_cbs(ns("SAE_table"), checked)
})
observeEvent(input$SAE_table_review_selection, {
+ session$userData$update_checkboxes[[form]] <- NULL
+
session$userData$review_records[[form]] <-
dplyr::rows_upsert(
session$userData$review_records[[form]],
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index fa3206bb..801e937b 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -142,7 +142,7 @@ mod_review_forms_server <- function(
observeEvent(input$form_reviewed, {
session$userData$update_checkboxes[[active_form()]] <- input$form_reviewed
-
+
session$userData$review_records[[active_form()]] <-
review_data_active() |>
dplyr::mutate(reviewed = ifelse(input$form_reviewed, "Yes", "No")) |>
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index f45d81d7..96313d30 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -195,10 +195,13 @@ mod_study_forms_server <- function(
})
observeEvent(table_data_active(), {
+ session$userData$update_checkboxes[[form]] <- NULL
session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character(), row_index = character())
})
observeEvent(input$table_review_selection, {
+ session$userData$update_checkboxes[[form]] <- NULL
+
session$userData$review_records[[form]] <-
dplyr::rows_upsert(
session$userData$review_records[[form]],
@@ -216,7 +219,7 @@ mod_study_forms_server <- function(
observeEvent(session$userData$update_checkboxes[[form]], {
checked <- session$userData$update_checkboxes[[form]]
- shinyjs::runjs(sprintf("$(':checkbox', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s)", ns("table"), tolower(checked)))
+ update_cbs(ns("table"), checked)
})
############################### Outputs: ###################################
diff --git a/R/shiny.R b/R/shiny.R
index 22b673c8..ad75318b 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -32,3 +32,12 @@ checkbox_create_callback <- DT::JS(
"}",
"}"
)
+
+update_cbs <- function(tblId, checked) {
+ "$(':checkbox:not(.indeterminate)', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s)" |>
+ sprintf(tblId, tolower(checked)) |>
+ shinyjs::runjs()
+ "$(':checkbox.indeterminate', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s).prop('indeterminate', false).prop('readOnly', %s)" |>
+ sprintf(tblId, tolower(checked), tolower(!checked)) |>
+ shinyjs::runjs()
+}
From 5a78643a337e0172492e7cb08256b31e7da19a4b Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 11:28:01 -0500
Subject: [PATCH 24/86] Clean up table name
---
R/mod_common_forms.R | 4 ++--
R/mod_study_forms.R | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 83af3dc7..e41428f2 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -135,7 +135,7 @@ mod_common_forms_server <- function(
if(!input$show_all_data) SAE_data$subject_id <- NULL
datatable_custom(
SAE_data,
- rename_vars = table_names,
+ rename_vars = c("Review Status" = "o_reviewed", table_names),
rownames= FALSE,
title = "Serious Adverse Events",
escape = FALSE,
@@ -207,7 +207,7 @@ mod_common_forms_server <- function(
if(!input$show_all_data) df$subject_id <- NULL
datatable_custom(
df,
- rename_vars = table_names,
+ rename_vars = c("Review Status" = "o_reviewed", table_names),
rownames= FALSE,
title = form,
escape = FALSE,
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 96313d30..e1a4133f 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -254,7 +254,7 @@ mod_study_forms_server <- function(
req(table_data_active())
datatable_custom(
table_data_active(),
- table_names,
+ rename_vars = c("Review Status" = "o_reviewed", table_names),
rownames= FALSE,
escape = FALSE,
selection = "none",
From 365206d7f84894471eead58a3f8ce8e45f8ac64d Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 11:34:47 -0500
Subject: [PATCH 25/86] Update `app_feature_01` JSONs
---
.../app_feature_01/app-feature-1-002.json | 16 ++++++-------
.../app_feature_01/app-feature-1-003.json | 16 ++++++-------
.../app_feature_01/app-feature-1-004.json | 24 +++++++++----------
.../app_feature_01/app-feature-1-005.json | 24 +++++++++----------
4 files changed, 40 insertions(+), 40 deletions(-)
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
index e02389e1..8afc3e9c 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
@@ -9,7 +9,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -28,7 +28,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -84,7 +84,7 @@
"targets": 13
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -99,7 +99,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
@@ -255,7 +255,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -274,7 +274,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -318,7 +318,7 @@
"targets": 10
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -333,7 +333,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
index 12f5431a..9824f6b7 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
@@ -9,7 +9,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -28,7 +28,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -84,7 +84,7 @@
"targets": 13
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -99,7 +99,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
@@ -255,7 +255,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -274,7 +274,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -318,7 +318,7 @@
"targets": 10
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -333,7 +333,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
index a0447bc5..8e278c98 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
@@ -9,7 +9,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -28,7 +28,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -84,7 +84,7 @@
"targets": 13
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -99,7 +99,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
@@ -255,7 +255,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -274,7 +274,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -318,7 +318,7 @@
"targets": 10
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -333,7 +333,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
@@ -8296,7 +8296,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | Event<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | Event<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -8311,7 +8311,7 @@
"render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -8351,7 +8351,7 @@
"targets": 9
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -8366,7 +8366,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
index df20d098..2468a1f6 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
@@ -9,7 +9,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -28,7 +28,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -84,7 +84,7 @@
"targets": 13
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -99,7 +99,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
@@ -255,7 +255,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -274,7 +274,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -318,7 +318,7 @@
"targets": 10
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -333,7 +333,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
@@ -8297,7 +8297,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | Event<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | Event<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -8312,7 +8312,7 @@
"render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -8352,7 +8352,7 @@
"targets": 9
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -8367,7 +8367,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
From d10e475da272ea21f9940a77115787e1c4b41348 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 11:48:36 -0500
Subject: [PATCH 26/86] Fix issue with setting input for checkbox
---
inst/app/www/custom.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index bedc0976..2f26fac2 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -17,6 +17,9 @@ $.extend(customCheckbox, {
getValue: function(el) {
return el.checked;
},
+ setValue: function(el, value) {
+ el.checked = value;
+ },
subscribe: function(el, callback) {
$(el).on("change.checkboxInputBinding", function() {
Shiny.onInputChange($(this).attr('id'), this.checked, {priority: 'event'});
From 19826507c6e6f81f1e3317e3a3ef5bfff068d310 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 11:51:17 -0500
Subject: [PATCH 27/86] Update `app_feature_03` JSONs
---
.../_snaps/app_feature_03/app-feature-3-002.json | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json b/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
index 7a2a525a..2ecd7042 100644
--- a/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
+++ b/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
@@ -483,7 +483,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -502,7 +502,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -558,7 +558,7 @@
"targets": 13
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Serious Adverse Events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -573,7 +573,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
@@ -729,7 +729,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n o_reviewed<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | start date<\/th>\n | end date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Serious Adverse Event<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -748,7 +748,7 @@
"targets": 1
},
{
- "name": "o_reviewed",
+ "name": "Review Status",
"targets": 0
},
{
@@ -792,7 +792,7 @@
"targets": 10
}
],
- "createdRow": "function(row, data, dataIndex) {\nif (data[0] == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = cb.readOnly = true;\n}\n}",
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"Adverse events\")\n}",
"dom": "f<\"header h5\">ti",
"order": [
@@ -807,7 +807,7 @@
"serverSide": true,
"processing": true
},
- "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
"mode": "none",
"selected": null,
From 5d454316866a8fb8db348ff424030a2457c047ec Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 12:30:13 -0500
Subject: [PATCH 28/86] Reset review reactiveValues after save
---
R/mod_review_forms.R | 3 +++
1 file changed, 3 insertions(+)
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index 801e937b..dce5a4a2 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -294,6 +294,9 @@ mod_review_forms_server <- function(
names(review_records_db)
]
+ session$userData$update_checkboxes[[active_form()]] <- NULL
+ session$userData$review_records[[active_form()]] <- data.frame(id = integer(), reviewed = character())
+
review_save_error(any(
!isTRUE(all.equal(review_records_db, review_records, check.attributes = FALSE)),
!isTRUE(all.equal(updated_records_memory, review_records_db, check.attributes = FALSE))
From 96e795efd8e3a8936505fe31a5e2c633eed56937 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 12:48:15 -0500
Subject: [PATCH 29/86] Repair study form test
---
.../mod_study_forms/study_forms-001.json | 59 ++++++++++++++++---
.../mod_study_forms/study_forms-002.json | 59 ++++++++++++++++---
tests/testthat/test-mod_study_forms.R | 4 +-
3 files changed, 105 insertions(+), 17 deletions(-)
diff --git a/tests/testthat/_snaps/mod_study_forms/study_forms-001.json b/tests/testthat/_snaps/mod_study_forms/study_forms-001.json
index c05b3cb7..65e613dd 100644
--- a/tests/testthat/_snaps/mod_study_forms/study_forms-001.json
+++ b/tests/testthat/_snaps/mod_study_forms/study_forms-001.json
@@ -1243,7 +1243,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n <\/th>\n | event_name<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | event_name<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -1252,15 +1252,13 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
{
- "orderable": false,
- "targets": 0
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
- "name": " ",
+ "name": "Review Status",
"targets": 0
},
{
@@ -1300,6 +1298,9 @@
"targets": 9
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -1312,16 +1313,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -3490,6 +3495,44 @@
]
},
"test-table_data": {
+ "o_reviewed": [
+ {
+ "reviewed": null,
+ "ids": [
+ 78,
+ 79,
+ 80,
+ 81,
+ 82,
+ 83,
+ 84,
+ 440
+ ]
+ },
+ {
+ "reviewed": null,
+ "ids": [
+ 85,
+ 86,
+ 87,
+ 88,
+ 89,
+ 90,
+ 91,
+ 441
+ ]
+ },
+ {
+ "reviewed": false,
+ "ids": [
+ 92,
+ 93,
+ 94,
+ 95,
+ 96
+ ]
+ }
+ ],
"event_name": [
"Screening",
"Visit 1",
diff --git a/tests/testthat/_snaps/mod_study_forms/study_forms-002.json b/tests/testthat/_snaps/mod_study_forms/study_forms-002.json
index c05b3cb7..65e613dd 100644
--- a/tests/testthat/_snaps/mod_study_forms/study_forms-002.json
+++ b/tests/testthat/_snaps/mod_study_forms/study_forms-002.json
@@ -1243,7 +1243,7 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n <\/th>\n | event_name<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | event_name<\/th>\n | Systolic blood pressure<\/th>\n | Diastolic blood pressure<\/th>\n | Pulse<\/th>\n | Resp<\/th>\n | Temperature<\/th>\n | Weight change since screening<\/th>\n | BMI<\/th>\n | Weight<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
@@ -1252,15 +1252,13 @@
"scrollResize": true,
"scrollCollapse": true,
"colReorder": true,
- "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
- "dom": "f<\"header h5\">ti",
"columnDefs": [
{
- "orderable": false,
- "targets": 0
+ "targets": 0,
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
- "name": " ",
+ "name": "Review Status",
"targets": 0
},
{
@@ -1300,6 +1298,9 @@
"targets": 9
}
],
+ "createdRow": "function(row, data, dataIndex) {\nif (data[0].reviewed == null) {\nlet cb = row.cells[0].getElementsByTagName('input')[0]\ncb.indeterminate = true;\n}\n}",
+ "initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
+ "dom": "f<\"header h5\">ti",
"order": [
],
@@ -1312,16 +1313,20 @@
"serverSide": true,
"processing": true
},
+ "callback": "function(table) {\ntable.on('click', 'input[type=\"checkbox\"]', function(){\nvar tblId = $(this).closest('.datatables').attr('id');\nvar cell = table.cell($(this).closest('td'));\nvar rowIdx = table.row($(this).closest('tr')).index();\nvar ids = cell.data().ids;\nvar review = $(this).is(':indeterminate') ? null : $(this).is(':checked');\nvar info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};\nShiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);\n})\n}",
"selection": {
- "mode": "single",
+ "mode": "none",
"selected": null,
"target": "row",
"selectable": null
}
},
"evals": [
+ "options.columnDefs.0.render",
+ "options.createdRow",
"options.initComplete",
- "options.ajax.data"
+ "options.ajax.data",
+ "callback"
],
"jsHooks": [
@@ -3490,6 +3495,44 @@
]
},
"test-table_data": {
+ "o_reviewed": [
+ {
+ "reviewed": null,
+ "ids": [
+ 78,
+ 79,
+ 80,
+ 81,
+ 82,
+ 83,
+ 84,
+ 440
+ ]
+ },
+ {
+ "reviewed": null,
+ "ids": [
+ 85,
+ 86,
+ 87,
+ 88,
+ 89,
+ 90,
+ 91,
+ 441
+ ]
+ },
+ {
+ "reviewed": false,
+ "ids": [
+ 92,
+ 93,
+ 94,
+ 95,
+ 96
+ ]
+ }
+ ],
"event_name": [
"Screening",
"Visit 1",
diff --git a/tests/testthat/test-mod_study_forms.R b/tests/testthat/test-mod_study_forms.R
index c4b868f0..b2a715d7 100644
--- a/tests/testthat/test-mod_study_forms.R
+++ b/tests/testthat/test-mod_study_forms.R
@@ -45,6 +45,7 @@ describe(
appdata <- get_appdata(clinsightful_data)
rev_data <- get_review_data(appdata[["Vital signs"]]) |>
dplyr::mutate(
+ id = dplyr::row_number(),
reviewed = sample(c("Yes", "No"), dplyr::n(), replace = TRUE),
status = sample(c("new", "old", "updated"), dplyr::n(), replace = TRUE)
)
@@ -98,7 +99,7 @@ describe(
# only difference between the the data frame is some html tags around
# some not yet reviewed data. However, because of these tags, we cannot
# compare expected with actual directly.
- expect_equal(names(table_data_active()), names(df_expected) )
+ expect_equal(names(table_data_active()), c("o_reviewed", names(df_expected)) )
expect_equal(table_data_active()$event_name, df_expected$event_name )
expect_true(is.data.frame(table_data_active()))
@@ -142,6 +143,7 @@ describe(
appdata <- get_appdata(clinsightful_data)
rev_data <- get_review_data(appdata[["Vital signs"]]) |>
dplyr::mutate(
+ id = dplyr::row_number(),
reviewed = sample(c("Yes", "No"), dplyr::n(), replace = TRUE),
status = sample(c("new", "old", "updated"), dplyr::n(), replace = TRUE)
)
From f4a71ade0560a66120bde478f00d3ca80ebeb30e Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 3 Dec 2024 13:55:39 -0500
Subject: [PATCH 30/86] Update test-mod_review_forms.R
---
tests/testthat/test-mod_review_forms.R | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tests/testthat/test-mod_review_forms.R b/tests/testthat/test-mod_review_forms.R
index 198d1a4e..e8c797ad 100644
--- a/tests/testthat/test-mod_review_forms.R
+++ b/tests/testthat/test-mod_review_forms.R
@@ -496,10 +496,15 @@ describe(
testServer(
mod_review_forms_server, args = testargs, {
ns <- session$ns
+
+ session$userData$review_records <- reactiveValues()
+ session$userData$update_checkboxes <- reactiveValues()
+
+ session$setInputs(form_reviewed = NULL)
db_before_saving <- db_get_table(db_path)
session$setInputs(form_reviewed = TRUE, save_review = 1)
db_after_saving <- db_get_table(db_path)
-
+
expect_true(review_save_error())
expect_equal(r$review_data, rev_data)
expect_equal(db_after_saving, db_before_saving)
From 54f7f553870a69b3d9de8e0370d17d7fb0ba09ce Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Wed, 4 Dec 2024 09:15:58 -0500
Subject: [PATCH 31/86] Add rudimentary progress bar
---
R/mod_review_forms.R | 16 ++++++++++++++++
R/shiny.R | 18 ++++++++++++++++++
inst/app/www/custom.css | 26 ++++++++++++++++++++++++++
inst/app/www/custom.js | 18 ++++++++++++++++++
4 files changed, 78 insertions(+)
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index dce5a4a2..8e61766d 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -45,6 +45,7 @@ mod_review_forms_ui <- function(id){
label = NULL
)
),
+ progress_bar(ns("progress_bar")),
bslib::layout_columns(
col_widths = c(11, 12),
shiny::actionButton(
@@ -319,6 +320,21 @@ mod_review_forms_server <- function(
showNotification("Input saved successfully", duration = 1, type = "message")
})
+ output[["progress_bar"]] <- render_progress_bar({
+ req(
+ review_data_active(),
+ active_form(),
+ session$userData$review_records[[active_form()]]
+ )
+
+ list(
+ completed = sum(review_data_active()$reviewed == "Yes"),
+ unmarking = sum(session$userData$review_records[[active_form()]]$reviewed == "No"),
+ marking = sum(session$userData$review_records[[active_form()]]$reviewed == "Yes"),
+ total = nrow(review_data_active())
+ )
+ })
+
output[["review_header"]] <- renderText({active_form()})
output[["save_review_error"]] <- renderPrint({
diff --git a/R/shiny.R b/R/shiny.R
index ad75318b..b6dac821 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -41,3 +41,21 @@ update_cbs <- function(tblId, checked) {
sprintf(tblId, tolower(checked), tolower(!checked)) |>
shinyjs::runjs()
}
+
+progress_bar <- function(outputId) {
+ div(
+ id = outputId,
+ class = "cs-progress-bar",
+ div(class = c("cs-progress", "completed")),
+ div(class = c("cs-progress", "unmarking")),
+ div(class = c("cs-progress", "marking"))
+ )
+}
+
+render_progress_bar <- function(expr, env = parent.frame(), quoted = FALSE) {
+ func <- exprToFunction(expr, env, quoted)
+
+ function(){
+ func()
+ }
+}
diff --git a/inst/app/www/custom.css b/inst/app/www/custom.css
index d1125951..65b5ee8a 100644
--- a/inst/app/www/custom.css
+++ b/inst/app/www/custom.css
@@ -75,3 +75,29 @@ tr:has(td input[type="checkbox"].indeterminate:not(:indeterminate))>td {
background-color: aquamarine;
border-color: aquamarine;
}
+
+div.cs-progress-bar {
+ width: 100%;
+ background-color: #b3b3b3;
+ color: #ffffff;
+ border-radius: 10px;
+ height: 10px;
+ overflow: hidden;
+ display: flex;
+}
+
+div.cs-progress-bar>div.cs-progress {
+ height: 100%;
+}
+
+div.cs-progress-bar>div.cs-progress.completed {
+ background-color: green;
+}
+
+div.cs-progress-bar>div.cs-progress.unmarking {
+ background-color: red;
+}
+
+div.cs-progress-bar>div.cs-progress.marking {
+ background-color: blue;
+}
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index 2f26fac2..d51187d6 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -31,3 +31,21 @@ $.extend(customCheckbox, {
});
Shiny.inputBindings.register(customCheckbox);
+
+var customProgressBar = new Shiny.OutputBinding();
+
+$.extend(customProgressBar, {
+ find: function(scope) {
+ return $(scope).find("div.cs-progress-bar");
+ },
+ renderValue: function(el, data) {
+ let cmp_pct = (data.completed-data.unmarking)/data.total*100;
+ let um_pct = data.unmarking/data.total*100;
+ let m_pct = data.marking/data.total*100;
+ $('#' + el.id + " .cs-progress.completed").width(cmp_pct.toFixed(2) + "%")
+ $('#' + el.id + " .cs-progress.unmarking").width(um_pct.toFixed(2) + "%")
+ $('#' + el.id + " .cs-progress.marking").width(m_pct.toFixed(2) + "%")
+ }
+});
+
+Shiny.outputBindings.register(customProgressBar)
From 2473133bbc9dae01080f1cb528601c929b11d866 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Wed, 4 Dec 2024 12:51:44 -0500
Subject: [PATCH 32/86] Add transition to progress bar
---
inst/app/www/custom.css | 1 +
1 file changed, 1 insertion(+)
diff --git a/inst/app/www/custom.css b/inst/app/www/custom.css
index 65b5ee8a..086ba87b 100644
--- a/inst/app/www/custom.css
+++ b/inst/app/www/custom.css
@@ -88,6 +88,7 @@ div.cs-progress-bar {
div.cs-progress-bar>div.cs-progress {
height: 100%;
+ transition: width 1s;
}
div.cs-progress-bar>div.cs-progress.completed {
From 3123136739dcad39b8c13177bc0f31ac58105329 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 07:29:02 -0500
Subject: [PATCH 33/86] Wrap Shiny bindings
---
inst/app/www/custom.js | 86 +++++++++++++++++++++++-------------------
1 file changed, 47 insertions(+), 39 deletions(-)
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index d51187d6..b5f56bed 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -8,44 +8,52 @@ function ts(cb) {
}
}
-var customCheckbox = new Shiny.InputBinding();
+$(document).ready(function() {
+
+ /* Define custom Shiny input binding for overall review checkbox.
+ This is needed to assign an event priority to the checkbox.*/
+ var customCheckbox = new Shiny.InputBinding();
+
+ $.extend(customCheckbox, {
+ find: function(scope) {
+ return $(scope).find("input[type='checkbox'].cs_checkbox");
+ },
+ getValue: function(el) {
+ return el.checked;
+ },
+ setValue: function(el, value) {
+ el.checked = value;
+ },
+ subscribe: function(el, callback) {
+ $(el).on("change.checkboxInputBinding", function() {
+ Shiny.onInputChange($(this).attr('id'), this.checked, {priority: 'event'});
+ });
+ },
+ unsubscribe: function(el) {
+ $(el).off(".checkboxInputBinding");
+ }
+ });
+
+ Shiny.inputBindings.register(customCheckbox);
+
+ /* Define custom Shiny output binding for review progress bar.
+ It expects 4 values: completed, unmarking, marking, and total.*/
+ var customProgressBar = new Shiny.OutputBinding();
+
+ $.extend(customProgressBar, {
+ find: function(scope) {
+ return $(scope).find("div.cs-progress-bar");
+ },
+ renderValue: function(el, data) {
+ let cmp_pct = (data.completed-data.unmarking)/data.total*100;
+ let um_pct = data.unmarking/data.total*100;
+ let m_pct = data.marking/data.total*100;
+ $('#' + el.id + " .cs-progress.completed").width(cmp_pct.toFixed(2) + "%")
+ $('#' + el.id + " .cs-progress.unmarking").width(um_pct.toFixed(2) + "%")
+ $('#' + el.id + " .cs-progress.marking").width(m_pct.toFixed(2) + "%")
+ }
+ });
+
+ Shiny.outputBindings.register(customProgressBar)
-$.extend(customCheckbox, {
- find: function(scope) {
- return $(scope).find("input[type='checkbox'].cs_checkbox");
- },
- getValue: function(el) {
- return el.checked;
- },
- setValue: function(el, value) {
- el.checked = value;
- },
- subscribe: function(el, callback) {
- $(el).on("change.checkboxInputBinding", function() {
- Shiny.onInputChange($(this).attr('id'), this.checked, {priority: 'event'});
- });
- },
- unsubscribe: function(el) {
- $(el).off(".checkboxInputBinding");
- }
});
-
-Shiny.inputBindings.register(customCheckbox);
-
-var customProgressBar = new Shiny.OutputBinding();
-
-$.extend(customProgressBar, {
- find: function(scope) {
- return $(scope).find("div.cs-progress-bar");
- },
- renderValue: function(el, data) {
- let cmp_pct = (data.completed-data.unmarking)/data.total*100;
- let um_pct = data.unmarking/data.total*100;
- let m_pct = data.marking/data.total*100;
- $('#' + el.id + " .cs-progress.completed").width(cmp_pct.toFixed(2) + "%")
- $('#' + el.id + " .cs-progress.unmarking").width(um_pct.toFixed(2) + "%")
- $('#' + el.id + " .cs-progress.marking").width(m_pct.toFixed(2) + "%")
- }
-});
-
-Shiny.outputBindings.register(customProgressBar)
From b9877083fc9716790e5067faa60de34f25ab9411 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 07:56:52 -0500
Subject: [PATCH 34/86] Clean up readability of checkbox render function
---
R/shiny.R | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/R/shiny.R b/R/shiny.R
index b6dac821..28cd074f 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -20,7 +20,10 @@ checkbox_callback <- DT::JS(
checkbox_render <- DT::JS(
"function(data, type, row, meta) {",
"var reviewed = data.reviewed;",
- "return ``;",
+ "return ``;",
"}"
)
From f637dd061b7e5b1d4a9d7dd854e0a77e9efd545a Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 08:01:27 -0500
Subject: [PATCH 35/86] Update `app-feature-01` JSONs
---
.../_snaps/app_feature_01/app-feature-1-002.json | 10 ++++++++--
.../_snaps/app_feature_01/app-feature-1-003.json | 10 ++++++++--
.../_snaps/app_feature_01/app-feature-1-004.json | 12 +++++++++---
.../_snaps/app_feature_01/app-feature-1-005.json | 12 +++++++++---
4 files changed, 34 insertions(+), 10 deletions(-)
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
index 8afc3e9c..f0813be6 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
@@ -21,7 +21,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -267,7 +267,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -799,6 +799,12 @@
]
},
"main_sidebar_1-navigate_forms_1-form_name": "Adverse events<\/b><\/center>",
+ "main_sidebar_1-review_forms_1-progress_bar": {
+ "completed": 0,
+ "unmarking": 0,
+ "marking": 0,
+ "total": 45
+ },
"main_sidebar_1-review_forms_1-save_review_error": {
"message": "Requires review",
"call": "NULL",
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
index 9824f6b7..cdbe742d 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-003.json
@@ -21,7 +21,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -267,7 +267,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -799,6 +799,12 @@
]
},
"main_sidebar_1-navigate_forms_1-form_name": "Vital signs<\/b><\/center>",
+ "main_sidebar_1-review_forms_1-progress_bar": {
+ "completed": 0,
+ "unmarking": 0,
+ "marking": 0,
+ "total": 47
+ },
"main_sidebar_1-review_forms_1-save_review_error": {
"message": "Requires review",
"call": "NULL",
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
index 8e278c98..a11077a3 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-004.json
@@ -21,7 +21,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -267,7 +267,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -799,6 +799,12 @@
]
},
"main_sidebar_1-navigate_forms_1-form_name": "Vital signs<\/b><\/center>",
+ "main_sidebar_1-review_forms_1-progress_bar": {
+ "completed": 0,
+ "unmarking": 0,
+ "marking": 0,
+ "total": 47
+ },
"main_sidebar_1-review_forms_1-save_review_error": {
"message": "Requires review",
"call": "NULL",
@@ -8308,7 +8314,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"name": "Review Status",
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
index 2468a1f6..3da1e3eb 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-005.json
@@ -21,7 +21,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -267,7 +267,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -799,6 +799,12 @@
]
},
"main_sidebar_1-navigate_forms_1-form_name": "Vital signs<\/b><\/center>",
+ "main_sidebar_1-review_forms_1-progress_bar": {
+ "completed": 0,
+ "unmarking": 0,
+ "marking": 0,
+ "total": 15
+ },
"main_sidebar_1-review_forms_1-save_review_error": {
"message": "Requires review",
"call": "NULL",
@@ -8309,7 +8315,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"name": "Review Status",
From 7144bd8b3482e8d5e6ad8563a077836e8f86dc97 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 08:03:01 -0500
Subject: [PATCH 36/86] Update `app-feature-02` JSON
---
tests/testthat/_snaps/app_feature_02/app-feature-2-001.json | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/tests/testthat/_snaps/app_feature_02/app-feature-2-001.json b/tests/testthat/_snaps/app_feature_02/app-feature-2-001.json
index b0c72096..b7c12cbd 100644
--- a/tests/testthat/_snaps/app_feature_02/app-feature-2-001.json
+++ b/tests/testthat/_snaps/app_feature_02/app-feature-2-001.json
@@ -83,6 +83,12 @@
]
},
"main_sidebar_1-navigate_forms_1-form_name": "Vital signs<\/center>",
+ "main_sidebar_1-review_forms_1-progress_bar": {
+ "completed": 8,
+ "unmarking": 0,
+ "marking": 0,
+ "total": 8
+ },
"main_sidebar_1-review_forms_1-save_review_error": {
"message": "Form already reviewed",
"call": "NULL",
From e6fe7eeffdf71bdb11e1a0de3b79d786744ad2cd Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 08:04:21 -0500
Subject: [PATCH 37/86] Update `app-feature-03` JSONs
---
.../_snaps/app_feature_03/app-feature-3-001.json | 6 ++++++
.../_snaps/app_feature_03/app-feature-3-002.json | 10 ++++++++--
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/tests/testthat/_snaps/app_feature_03/app-feature-3-001.json b/tests/testthat/_snaps/app_feature_03/app-feature-3-001.json
index b0f0134a..335abaa1 100644
--- a/tests/testthat/_snaps/app_feature_03/app-feature-3-001.json
+++ b/tests/testthat/_snaps/app_feature_03/app-feature-3-001.json
@@ -188,6 +188,12 @@
]
},
"main_sidebar_1-navigate_forms_1-form_name": "Vital signs<\/b><\/center>",
+ "main_sidebar_1-review_forms_1-progress_bar": {
+ "completed": 0,
+ "unmarking": 0,
+ "marking": 0,
+ "total": 8
+ },
"main_sidebar_1-review_forms_1-save_review_error": {
"message": "Requires review",
"call": "NULL",
diff --git a/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json b/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
index 2ecd7042..312fa8f8 100644
--- a/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
+++ b/tests/testthat/_snaps/app_feature_03/app-feature-3-002.json
@@ -495,7 +495,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -741,7 +741,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"className": "dt-right",
@@ -1273,6 +1273,12 @@
]
},
"main_sidebar_1-navigate_forms_1-form_name": "Adverse events<\/b><\/center>",
+ "main_sidebar_1-review_forms_1-progress_bar": {
+ "completed": 0,
+ "unmarking": 0,
+ "marking": 0,
+ "total": 45
+ },
"main_sidebar_1-review_forms_1-save_review_error": {
"message": "Requires review",
"call": "NULL",
From d6a2ff9d599ecc2ce3cf71f05c508e47561dd23b Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 08:14:20 -0500
Subject: [PATCH 38/86] Add bottom margin to progress bar
---
inst/app/www/custom.css | 1 +
1 file changed, 1 insertion(+)
diff --git a/inst/app/www/custom.css b/inst/app/www/custom.css
index 086ba87b..c132f215 100644
--- a/inst/app/www/custom.css
+++ b/inst/app/www/custom.css
@@ -84,6 +84,7 @@ div.cs-progress-bar {
height: 10px;
overflow: hidden;
display: flex;
+ margin-bottom: 1rem;
}
div.cs-progress-bar>div.cs-progress {
From f0a19e7a8f7cd5f722edac7a06ff9ecc843220e2 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 08:15:44 -0500
Subject: [PATCH 39/86] Update `mod_study_forms` JSONs
---
tests/testthat/_snaps/mod_study_forms/study_forms-001.json | 2 +-
tests/testthat/_snaps/mod_study_forms/study_forms-002.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/testthat/_snaps/mod_study_forms/study_forms-001.json b/tests/testthat/_snaps/mod_study_forms/study_forms-001.json
index 65e613dd..3abdfbb7 100644
--- a/tests/testthat/_snaps/mod_study_forms/study_forms-001.json
+++ b/tests/testthat/_snaps/mod_study_forms/study_forms-001.json
@@ -1255,7 +1255,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"name": "Review Status",
diff --git a/tests/testthat/_snaps/mod_study_forms/study_forms-002.json b/tests/testthat/_snaps/mod_study_forms/study_forms-002.json
index 65e613dd..3abdfbb7 100644
--- a/tests/testthat/_snaps/mod_study_forms/study_forms-002.json
+++ b/tests/testthat/_snaps/mod_study_forms/study_forms-002.json
@@ -1255,7 +1255,7 @@
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
+ "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ``;\n}"
},
{
"name": "Review Status",
From bfc864f8684647b50aecfd0b1086d23bd6141428 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 08:40:54 -0500
Subject: [PATCH 40/86] Spruce up progress bar output object
---
R/shiny.R | 14 ++++++++++----
inst/app/www/custom.css | 22 ++++++++++++++++------
inst/app/www/custom.js | 4 +++-
3 files changed, 29 insertions(+), 11 deletions(-)
diff --git a/R/shiny.R b/R/shiny.R
index 28cd074f..487b1501 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -48,10 +48,16 @@ update_cbs <- function(tblId, checked) {
progress_bar <- function(outputId) {
div(
id = outputId,
- class = "cs-progress-bar",
- div(class = c("cs-progress", "completed")),
- div(class = c("cs-progress", "unmarking")),
- div(class = c("cs-progress", "marking"))
+ class = "cs-progress-container",
+ div(
+ class = "cs-progress-bar",
+ div(class = c("cs-progress", "completed")),
+ div(class = c("cs-progress", "unmarking")),
+ div(class = c("cs-progress", "marking"))
+ ),
+ div(
+ class = "cs-completed"
+ )
)
}
diff --git a/inst/app/www/custom.css b/inst/app/www/custom.css
index c132f215..0ccc5a83 100644
--- a/inst/app/www/custom.css
+++ b/inst/app/www/custom.css
@@ -76,7 +76,14 @@ tr:has(td input[type="checkbox"].indeterminate:not(:indeterminate))>td {
border-color: aquamarine;
}
-div.cs-progress-bar {
+div.cs-progress-container {
+ display: flex;
+ margin-bottom: 1rem;
+ align-items: center;
+ gap: 5px;
+}
+
+div.cs-progress-container>.cs-progress-bar {
width: 100%;
background-color: #b3b3b3;
color: #ffffff;
@@ -84,22 +91,25 @@ div.cs-progress-bar {
height: 10px;
overflow: hidden;
display: flex;
- margin-bottom: 1rem;
}
-div.cs-progress-bar>div.cs-progress {
+div.cs-progress-container>.cs-completed {
+ cursor: default;
+}
+
+div.cs-progress-container>.cs-progress-bar>.cs-progress {
height: 100%;
transition: width 1s;
}
-div.cs-progress-bar>div.cs-progress.completed {
+div.cs-progress-container>.cs-progress-bar>.cs-progress.completed {
background-color: green;
}
-div.cs-progress-bar>div.cs-progress.unmarking {
+div.cs-progress-container>.cs-progress-bar>.cs-progress.unmarking {
background-color: red;
}
-div.cs-progress-bar>div.cs-progress.marking {
+div.cs-progress-container>.cs-progress-bar>.cs-progress.marking {
background-color: blue;
}
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index b5f56bed..c2a61bed 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -42,15 +42,17 @@ $(document).ready(function() {
$.extend(customProgressBar, {
find: function(scope) {
- return $(scope).find("div.cs-progress-bar");
+ return $(scope).find("div.cs-progress-container");
},
renderValue: function(el, data) {
let cmp_pct = (data.completed-data.unmarking)/data.total*100;
let um_pct = data.unmarking/data.total*100;
let m_pct = data.marking/data.total*100;
+ let true_cmp_pct = data.completed/data.total*100;
$('#' + el.id + " .cs-progress.completed").width(cmp_pct.toFixed(2) + "%")
$('#' + el.id + " .cs-progress.unmarking").width(um_pct.toFixed(2) + "%")
$('#' + el.id + " .cs-progress.marking").width(m_pct.toFixed(2) + "%")
+ $('#' + el.id + " .cs-completed").html(true_cmp_pct.toFixed(1) + "%")
}
});
From e9ab76d4353a6c5f0b00a050002432d3d5bcfab4 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 10:21:07 -0500
Subject: [PATCH 41/86] Update custom.css
---
inst/app/www/custom.css | 37 ++++++++++++++++++++++---------------
1 file changed, 22 insertions(+), 15 deletions(-)
diff --git a/inst/app/www/custom.css b/inst/app/www/custom.css
index 0ccc5a83..3ee6d67c 100644
--- a/inst/app/www/custom.css
+++ b/inst/app/www/custom.css
@@ -1,3 +1,8 @@
+:root {
+ --cs-completed: #91C483;
+ --cs-unmarking: #FF6464;
+ --cs-marking: #97b0f8;
+}
.bslib-value-box .value-box-area {
padding: 0.1rem 0rem 0.1rem 1rem;
@@ -61,19 +66,18 @@ div.datatables div.header {
font-weight: bold;
}
-tr:has(td input[type="checkbox"].checked:not(:checked))>td {
- background-color: aquamarine;
- border-color: aquamarine;
-}
-
-tr:has(td input[type="checkbox"].unchecked:checked)>td {
- background-color: aquamarine;
- border-color: aquamarine;
+tr:has(td input[type="checkbox"].checked:not(:checked))>td,
+ tr:has(td input[type="checkbox"].indeterminate:not(:indeterminate):not(:checked))>td {
+ background-color: var(--cs-unmarking, #FF6464);
+ border-color: var(--cs-unmarking, #FF6464);
+ color: #000000;
}
-tr:has(td input[type="checkbox"].indeterminate:not(:indeterminate))>td {
- background-color: aquamarine;
- border-color: aquamarine;
+tr:has(td input[type="checkbox"].unchecked:checked)>td,
+ tr:has(td input[type="checkbox"].indeterminate:not(:indeterminate):checked)>td {
+ background-color: var(--cs-marking, #97b0f8);
+ border-color: var(--cs-marking, #97b0f8);
+ color: #000000;
}
div.cs-progress-container {
@@ -85,7 +89,10 @@ div.cs-progress-container {
div.cs-progress-container>.cs-progress-bar {
width: 100%;
- background-color: #b3b3b3;
+ background-color: #eeeeee;
+ border-color: #b3b3b3;
+ border-style: solid;
+ border-width: thin;
color: #ffffff;
border-radius: 10px;
height: 10px;
@@ -103,13 +110,13 @@ div.cs-progress-container>.cs-progress-bar>.cs-progress {
}
div.cs-progress-container>.cs-progress-bar>.cs-progress.completed {
- background-color: green;
+ background-color: var(--cs-completed, #91C483);
}
div.cs-progress-container>.cs-progress-bar>.cs-progress.unmarking {
- background-color: red;
+ background-color: var(--cs-unmarking, #FF6464);
}
div.cs-progress-container>.cs-progress-bar>.cs-progress.marking {
- background-color: blue;
+ background-color: var(--cs-marking, #97b0f8);
}
From c6ef036a9479ca93a12ab413299e6ae229714c27 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 10:31:38 -0500
Subject: [PATCH 42/86] Fix "form already reviewed" indicator
---
R/mod_review_forms.R | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index 8e61766d..b9c5c147 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -355,7 +355,7 @@ mod_review_forms_server <- function(
"No user name found. Cannot save review"
))
validate(need(
- !unique(with(review_data_active(), reviewed[edit_date_time == max(as.POSIXct(edit_date_time))])) == "Yes",
+ any(review_data_active()[["reviewed"]] != "Yes"),
"Form already reviewed"
))
validate(need(input$form_reviewed, "Requires review"))
From a36d7cface12d8bb99a8a8a1bcc9c5001ceb6fb5 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 10:58:30 -0500
Subject: [PATCH 43/86] Update version
---
DESCRIPTION | 2 +-
inst/golem-config.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index 744bcd2e..6e7b8ac4 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: clinsight
Title: ClinSight
-Version: 0.1.1.9011
+Version: 0.1.1.9012
Authors@R: c(
person("Leonard Daniël", "Samson", , "lsamson@gcp-service.com", role = c("cre", "aut"),
comment = c(ORCID = "0000-0002-6252-7639")),
diff --git a/inst/golem-config.yml b/inst/golem-config.yml
index 3d15e651..19beab51 100644
--- a/inst/golem-config.yml
+++ b/inst/golem-config.yml
@@ -1,6 +1,6 @@
default:
golem_name: clinsight
- golem_version: 0.1.1.9011
+ golem_version: 0.1.1.9012
app_prod: no
user_identification: test_user
study_data: !expr clinsight::clinsightful_data
From a746b2ee3515a29b658cb80c3a6ba04127962b52 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 15:02:55 -0500
Subject: [PATCH 44/86] Only review selected subject
---
R/mod_common_forms.R | 12 ++++++++++--
R/mod_review_forms.R | 1 +
R/mod_study_forms.R | 6 +++++-
3 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index e41428f2..9ca04e0e 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -165,8 +165,12 @@ mod_common_forms_server <- function(
by = "id"
) |>
dplyr::filter(!is.na(reviewed)) |>
+ dplyr::semi_join(
+ subset(r$review_data, subject_id == r$subject_id & item_group == form),
+ by = "id"
+ ) |>
dplyr::anti_join(
- subset(r$review_data, item_group == form),
+ subset(r$review_data, subject_id == r$subject_id & item_group == form),
by = c("id", "reviewed")
) |>
dplyr::arrange(id)
@@ -189,8 +193,12 @@ mod_common_forms_server <- function(
by = "id"
) |>
dplyr::filter(!is.na(reviewed)) |>
+ dplyr::semi_join(
+ subset(r$review_data, subject_id == r$subject_id & item_group == form),
+ by = "id"
+ ) |>
dplyr::anti_join(
- subset(r$review_data, item_group == form),
+ subset(r$review_data, subject_id == r$subject_id & item_group == form),
by = c("id", "reviewed")
) |>
dplyr::arrange(id)
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index b9c5c147..2275a1e4 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -125,6 +125,7 @@ mod_review_forms_server <- function(
observe({
req(session$userData$review_records[[active_form()]])
+ # browser()
review_status <-
review_data_active()[,c("id", "reviewed")] |>
dplyr::rows_update(session$userData$review_records[[active_form()]][,c("id", "reviewed")], by = "id") |>
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index e1a4133f..70105a89 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -209,8 +209,12 @@ mod_study_forms_server <- function(
by = "id"
) |>
dplyr::filter(!is.na(reviewed)) |>
+ dplyr::semi_join(
+ subset(r$review_data, subject_id == r$subject_id & item_group == form),
+ by = "id"
+ ) |>
dplyr::anti_join(
- subset(r$review_data, item_group == form),
+ subset(r$review_data, subject_id == r$subject_id & item_group == form),
by = c("id", "reviewed")
) |>
dplyr::arrange(id)
From 546b4e4b57f00389bcc5c010980b84127c056d4a Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 15:04:06 -0500
Subject: [PATCH 45/86] Save updated status in DOM
---
R/shiny.R | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/R/shiny.R b/R/shiny.R
index 487b1501..4a697a9d 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -12,6 +12,7 @@ checkbox_callback <- DT::JS(
"var rowIdx = table.row($(this).closest('tr')).index();",
"var ids = cell.data().ids;",
"var review = $(this).is(':indeterminate') ? null : $(this).is(':checked');",
+ "cell.data().updated = review;",
"var info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};",
"Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
"})"
@@ -20,9 +21,10 @@ checkbox_callback <- DT::JS(
checkbox_render <- DT::JS(
"function(data, type, row, meta) {",
"var reviewed = data.reviewed;",
+ "var updated = data.updated;",
"return ``;",
"}"
)
From c07540a17d763ffae8cec7b0da2195fc0d404657 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Thu, 5 Dec 2024 15:14:12 -0500
Subject: [PATCH 46/86] Set `server=FALSE` for the moment
---
R/mod_common_forms.R | 4 ++--
R/mod_study_forms.R | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 9ca04e0e..4a3b55d8 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -148,7 +148,7 @@ mod_common_forms_server <- function(
)),
createdRow = checkbox_create_callback
))
- })
+ }, server = FALSE)
observeEvent(data_active(), {
session$userData$update_checkboxes[[form]] <- NULL
@@ -228,7 +228,7 @@ mod_common_forms_server <- function(
)),
createdRow = checkbox_create_callback
))
- })
+ }, server = FALSE)
})
}
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 70105a89..a552b3bc 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -270,7 +270,7 @@ mod_study_forms_server <- function(
)),
createdRow = checkbox_create_callback
))
- })
+ }, server = FALSE)
if(form %in% c("Vital signs", "Vitals adjusted")){
shiny::exportTestValues(
From 39238a3c099b4190c8eb6695cf82d6375387dfac Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 6 Dec 2024 08:27:10 -0500
Subject: [PATCH 47/86] Switch back to using server for datatables
---
R/mod_common_forms.R | 4 ++--
R/mod_review_forms.R | 1 -
R/mod_study_forms.R | 2 +-
3 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 4a3b55d8..9ca04e0e 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -148,7 +148,7 @@ mod_common_forms_server <- function(
)),
createdRow = checkbox_create_callback
))
- }, server = FALSE)
+ })
observeEvent(data_active(), {
session$userData$update_checkboxes[[form]] <- NULL
@@ -228,7 +228,7 @@ mod_common_forms_server <- function(
)),
createdRow = checkbox_create_callback
))
- }, server = FALSE)
+ })
})
}
diff --git a/R/mod_review_forms.R b/R/mod_review_forms.R
index 2275a1e4..b9c5c147 100644
--- a/R/mod_review_forms.R
+++ b/R/mod_review_forms.R
@@ -125,7 +125,6 @@ mod_review_forms_server <- function(
observe({
req(session$userData$review_records[[active_form()]])
- # browser()
review_status <-
review_data_active()[,c("id", "reviewed")] |>
dplyr::rows_update(session$userData$review_records[[active_form()]][,c("id", "reviewed")], by = "id") |>
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index a552b3bc..70105a89 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -270,7 +270,7 @@ mod_study_forms_server <- function(
)),
createdRow = checkbox_create_callback
))
- }, server = FALSE)
+ })
if(form %in% c("Vital signs", "Vitals adjusted")){
shiny::exportTestValues(
From 70ad8632a75bf2e745bce2f5c9cdb86ee990abc6 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 6 Dec 2024 16:04:43 -0500
Subject: [PATCH 48/86] Improve `update_cbs()`
---
R/mod_common_forms.R | 4 ++--
R/mod_study_forms.R | 2 +-
R/shiny.R | 11 ++++-------
inst/app/www/custom.js | 11 +++++++++++
4 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 9ca04e0e..30121888 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -179,8 +179,8 @@ mod_common_forms_server <- function(
observeEvent(session$userData$update_checkboxes[[form]], {
checked <- session$userData$update_checkboxes[[form]]
- update_cbs(ns("common_form_table"), checked)
- update_cbs(ns("SAE_table"), checked)
+ update_cbs("common_form_table", checked)
+ update_cbs("SAE_table", checked)
})
observeEvent(input$SAE_table_review_selection, {
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 70105a89..87be1a54 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -223,7 +223,7 @@ mod_study_forms_server <- function(
observeEvent(session$userData$update_checkboxes[[form]], {
checked <- session$userData$update_checkboxes[[form]]
- update_cbs(ns("table"), checked)
+ update_cbs("table", checked)
})
############################### Outputs: ###################################
diff --git a/R/shiny.R b/R/shiny.R
index 4a697a9d..61ff0f6e 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -38,13 +38,10 @@ checkbox_create_callback <- DT::JS(
"}"
)
-update_cbs <- function(tblId, checked) {
- "$(':checkbox:not(.indeterminate)', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s)" |>
- sprintf(tblId, tolower(checked)) |>
- shinyjs::runjs()
- "$(':checkbox.indeterminate', $('#%s .table').DataTable().rows().nodes()).prop('checked', %s).prop('indeterminate', false).prop('readOnly', %s)" |>
- sprintf(tblId, tolower(checked), tolower(!checked)) |>
- shinyjs::runjs()
+update_cbs <- function(id, checked, session = getDefaultReactiveDomain()) {
+ tblId <- session$ns(id)
+ params <- list(id = tblId, checked = checked)
+ session$sendCustomMessage('update_checkboxes', params)
}
progress_bar <- function(outputId) {
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index c2a61bed..e4da91d2 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -10,6 +10,17 @@ function ts(cb) {
$(document).ready(function() {
+ Shiny.addCustomMessageHandler("update_checkboxes", function(params) {
+ var table = $('#' + params.id + " .table").DataTable()
+ table.column(0).data().each(function(data){data.updated = params.checked})
+ $(':checkbox:not(.indeterminate)', table.rows().nodes()).
+ prop('checked', params.checked);
+ $(':checkbox.indeterminate', table.rows().nodes()).
+ prop('checked', params.checked).
+ prop('indeterminate', false).
+ prop('readOnly', params.checked);
+ });
+
/* Define custom Shiny input binding for overall review checkbox.
This is needed to assign an event priority to the checkbox.*/
var customCheckbox = new Shiny.InputBinding();
From 5c64e0866b1555a686fefb9845115c63293fac92 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Mon, 9 Dec 2024 13:27:04 -0500
Subject: [PATCH 49/86] Resolve issue for partially reviewed rows
---
R/fct_data_helpers.R | 6 ++++-
R/mod_common_forms.R | 4 +--
R/mod_study_forms.R | 2 +-
R/shiny.R | 60 +++++++++++++++++++++++++++---------------
inst/app/www/custom.js | 2 +-
5 files changed, 48 insertions(+), 26 deletions(-)
diff --git a/R/fct_data_helpers.R b/R/fct_data_helpers.R
index 6ff84fcb..e7b6c1e5 100644
--- a/R/fct_data_helpers.R
+++ b/R/fct_data_helpers.R
@@ -565,7 +565,11 @@ datatable_custom <- function(
deferRender = TRUE,
scrollResize = TRUE,
scrollCollapse = TRUE,
- colReorder = TRUE
+ colReorder = list(
+ enable = TRUE,
+ realtime = FALSE,
+ fixedColumnsLeft = 1
+ )
)
fixed_opts <- list(
initComplete = DT::JS(
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 30121888..1f4a524a 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -146,7 +146,7 @@ mod_common_forms_server <- function(
targets = 0,
render = checkbox_render
)),
- createdRow = checkbox_create_callback
+ rowCallback = row_callback
))
})
@@ -226,7 +226,7 @@ mod_common_forms_server <- function(
targets = 0,
render = checkbox_render
)),
- createdRow = checkbox_create_callback
+ rowCallback = row_callback
))
})
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 87be1a54..b2d8e480 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -268,7 +268,7 @@ mod_study_forms_server <- function(
targets = 0,
render = checkbox_render
)),
- createdRow = checkbox_create_callback
+ rowCallback = row_callback
))
})
diff --git a/R/shiny.R b/R/shiny.R
index 61ff0f6e..764896b9 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -6,35 +6,53 @@ shiny::registerInputHandler('CS.reviewInfo', function(val, ...) {
}, TRUE)
checkbox_callback <- DT::JS(
+ "table.on('column-reorder', function() {",
+ "table.rows().every(function() {",
+ "if (this.data()[0].reviewed == null) {",
+ "$(':checkbox', this.node()).",
+ "addClass('indeterminate').",
+ "prop('indeterminate', this.data()[0].updated == null).",
+ "prop('readOnly', this.data()[0].updated == false)",
+ "}",
+ "})",
+ "});",
"table.on('click', 'input[type=\"checkbox\"]', function(){",
- "var tblId = $(this).closest('.datatables').attr('id');",
- "var cell = table.cell($(this).closest('td'));",
- "var rowIdx = table.row($(this).closest('tr')).index();",
- "var ids = cell.data().ids;",
- "var review = $(this).is(':indeterminate') ? null : $(this).is(':checked');",
- "cell.data().updated = review;",
- "var info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};",
- "Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
- "})"
+ "var tblId = $(this).closest('.datatables').attr('id');",
+ "var cell = table.cell($(this).closest('td'));",
+ "var rowIdx = table.row($(this).closest('tr')).index();",
+ "var ids = cell.data().ids;",
+ "var review = $(this).is(':indeterminate') ? null : $(this).is(':checked');",
+ "cell.data().updated = review;",
+ "var info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};",
+ "Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
+ "});"
)
checkbox_render <- DT::JS(
"function(data, type, row, meta) {",
- "var reviewed = data.reviewed;",
- "var updated = data.updated;",
- "return ``;",
+ "var reviewed = data.reviewed;",
+ "var updated = data.updated;",
+ "var cb_class = ''",
+ "if (reviewed == null) {",
+ "cb_class = updated == null ? '' : 'indeterminate'",
+ "} else {",
+ "cb_class = reviewed ? 'checked' : 'unchecked'",
+ "}",
+ "return ``;",
"}"
)
-checkbox_create_callback <- DT::JS(
- "function(row, data, dataIndex) {",
- "if (data[0].reviewed == null) {",
- "let cb = row.cells[0].getElementsByTagName('input')[0]",
- "cb.indeterminate = true;",
- "}",
+row_callback <- DT::JS(
+ "function(row, data) {",
+ "if (data[0].reviewed == null) {",
+ "$(':checkbox', row).",
+ "addClass('indeterminate').",
+ "prop('indeterminate', data[0].updated == null).",
+ "prop('readOnly', data[0].updated == false)",
+ "}",
"}"
)
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index e4da91d2..809b65b7 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -18,7 +18,7 @@ $(document).ready(function() {
$(':checkbox.indeterminate', table.rows().nodes()).
prop('checked', params.checked).
prop('indeterminate', false).
- prop('readOnly', params.checked);
+ prop('readOnly', !params.checked);
});
/* Define custom Shiny input binding for overall review checkbox.
From 02e65df6c3144c9a4eed5a25972da9adcb9511d2 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 10 Dec 2024 08:01:01 -0500
Subject: [PATCH 50/86] Return row IDs as well
---
R/fct_tables.R | 6 ++++--
R/mod_common_forms.R | 4 ++--
R/mod_study_forms.R | 2 +-
R/shiny.R | 7 +++----
4 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/R/fct_tables.R b/R/fct_tables.R
index e54a425b..f2576442 100644
--- a/R/fct_tables.R
+++ b/R/fct_tables.R
@@ -67,7 +67,9 @@ create_table.default <- function(
names_from = {{name_column}},
values_from = {{value_column}},
values_fn = ~paste0(., collapse = "; ")
- )
+ )
+ if ("o_reviewed" %in% names(df))
+ df <- dplyr::mutate(df, o_reviewed = lapply(dplyr::row_number(), \(x) append(o_reviewed[[x]], list(row_id = x))))
expected_columns <- na.omit(expected_columns) %||% character(0)
if(length(expected_columns) == 0) return(df)
add_missing_columns(df, expected_columns)[
@@ -238,7 +240,7 @@ create_table.adverse_events <- function(
keep_vars, expected_columns) |>
adjust_colnames("^AE ")
df[["Number"]] <- NULL
-
+
# create new row when an AE gets worse:
df_worsening <- df[!is.na(df[[worsening_start_column]]), ] |>
dplyr::mutate(
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 1f4a524a..306ad333 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -161,7 +161,7 @@ mod_common_forms_server <- function(
session$userData$review_records[[form]] <-
dplyr::rows_upsert(
session$userData$review_records[[form]],
- input$common_form_table_review_selection,
+ input$common_form_table_review_selection[c("id", "reviewed")],
by = "id"
) |>
dplyr::filter(!is.na(reviewed)) |>
@@ -189,7 +189,7 @@ mod_common_forms_server <- function(
session$userData$review_records[[form]] <-
dplyr::rows_upsert(
session$userData$review_records[[form]],
- input$SAE_table_review_selection,
+ input$SAE_table_review_selection[c("id", "reviewed")],
by = "id"
) |>
dplyr::filter(!is.na(reviewed)) |>
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index b2d8e480..68b444c4 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -205,7 +205,7 @@ mod_study_forms_server <- function(
session$userData$review_records[[form]] <-
dplyr::rows_upsert(
session$userData$review_records[[form]],
- input$table_review_selection,
+ input$table_review_selection[, c("id", "reviewed")],
by = "id"
) |>
dplyr::filter(!is.na(reviewed)) |>
diff --git a/R/shiny.R b/R/shiny.R
index 764896b9..92ceef79 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -1,7 +1,8 @@
shiny::registerInputHandler('CS.reviewInfo', function(val, ...) {
with(val, data.frame(
id = unlist(ids),
- reviewed = ifelse(isTRUE(review), "Yes", ifelse(isFALSE(review), "No", NA_character_))
+ reviewed = ifelse(isTRUE(review), "Yes", ifelse(isFALSE(review), "No", NA_character_)),
+ row_id = row_id
))
}, TRUE)
@@ -19,11 +20,9 @@ checkbox_callback <- DT::JS(
"table.on('click', 'input[type=\"checkbox\"]', function(){",
"var tblId = $(this).closest('.datatables').attr('id');",
"var cell = table.cell($(this).closest('td'));",
- "var rowIdx = table.row($(this).closest('tr')).index();",
- "var ids = cell.data().ids;",
"var review = $(this).is(':indeterminate') ? null : $(this).is(':checked');",
"cell.data().updated = review;",
- "var info = {review: review, ids: ids, row: tblId + '_row_' + rowIdx};",
+ "var info = {review: review, ids: cell.data().ids, row_id: cell.data().row_id};",
"Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
"});"
)
From a82bec907b85ff6884e329d1447c2c48dc6db289 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Tue, 10 Dec 2024 09:16:19 -0500
Subject: [PATCH 51/86] Use `colnames` instead of renaming data frame
---
R/fct_data_helpers.R | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/R/fct_data_helpers.R b/R/fct_data_helpers.R
index e7b6c1e5..255013b7 100644
--- a/R/fct_data_helpers.R
+++ b/R/fct_data_helpers.R
@@ -550,9 +550,11 @@ datatable_custom <- function(
...
){
stopifnot(is.data.frame(data))
+ colnames <- names(data)
if(!is.null(rename_vars)){
stopifnot(is.character(rename_vars))
- data <- dplyr::rename(data, dplyr::any_of(rename_vars))
+ colnames <- dplyr::rename(data[0,], dplyr::any_of(rename_vars)) |>
+ names()
}
stopifnot(is.null(title) | is.character(title))
stopifnot(grepl("t", dom, fixed = TRUE))
@@ -593,6 +595,7 @@ datatable_custom <- function(
options = opts,
extensions = extensions,
plugins = plugins,
+ colnames = colnames,
...
)
}
From f3f82477f3110600a99d36604f17808085bfcd55 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 13 Dec 2024 08:56:05 -0500
Subject: [PATCH 52/86] Remove `plugin = "scrollResize"` from custom datatable
---
R/fct_data_helpers.R | 4 ----
man/datatable_custom.Rd | 3 ---
2 files changed, 7 deletions(-)
diff --git a/R/fct_data_helpers.R b/R/fct_data_helpers.R
index 255013b7..3b4724c6 100644
--- a/R/fct_data_helpers.R
+++ b/R/fct_data_helpers.R
@@ -516,7 +516,6 @@ add_missing_columns <- function(
#' @param title Optional. Character string with the title of the table.
#' @param selection See [DT::datatable()]. Default set to 'single'.
#' @param extensions See [DT::datatable()]. Default set to 'Scroller'.
-#' @param plugins See [DT::datatable()]. Default set to 'scrollResize'.
#' @param dom See \url{https://datatables.net/reference/option/dom}. A div
#' element will be inserted before the table for the table title. Default set
#' to 'fti' resulting in 'f<"header h5">ti'.
@@ -544,7 +543,6 @@ datatable_custom <- function(
title = NULL,
selection = "single",
extensions = c("Scroller", "ColReorder"),
- plugins = "scrollResize",
dom = "fti",
options = list(),
...
@@ -565,7 +563,6 @@ datatable_custom <- function(
scrollX = TRUE,
scroller = TRUE,
deferRender = TRUE,
- scrollResize = TRUE,
scrollCollapse = TRUE,
colReorder = list(
enable = TRUE,
@@ -594,7 +591,6 @@ datatable_custom <- function(
selection = selection,
options = opts,
extensions = extensions,
- plugins = plugins,
colnames = colnames,
...
)
diff --git a/man/datatable_custom.Rd b/man/datatable_custom.Rd
index f87c8e9c..f46efb80 100644
--- a/man/datatable_custom.Rd
+++ b/man/datatable_custom.Rd
@@ -10,7 +10,6 @@ datatable_custom(
title = NULL,
selection = "single",
extensions = c("Scroller", "ColReorder"),
- plugins = "scrollResize",
dom = "fti",
options = list(),
...
@@ -28,8 +27,6 @@ rename any column names found in this vector to the provided name.}
\item{extensions}{See \code{\link[DT:datatable]{DT::datatable()}}. Default set to 'Scroller'.}
-\item{plugins}{See \code{\link[DT:datatable]{DT::datatable()}}. Default set to 'scrollResize'.}
-
\item{dom}{See \url{https://datatables.net/reference/option/dom}. A div
element will be inserted before the table for the table title. Default set
to 'fti' resulting in 'f<"header h5">ti'.}
From 0bb7ee655a22e4a437d7ad68af555a471000d04c Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 13 Dec 2024 14:09:32 -0500
Subject: [PATCH 53/86] Larger rewrite to use datatable proxy objects
---
R/fct_tables.R | 2 -
R/mod_common_forms.R | 273 ++++++++++++++++++++++++++---------------
R/mod_study_forms.R | 117 ++++++++++++------
R/shiny.R | 10 +-
inst/app/www/custom.js | 11 --
5 files changed, 259 insertions(+), 154 deletions(-)
diff --git a/R/fct_tables.R b/R/fct_tables.R
index f2576442..83070689 100644
--- a/R/fct_tables.R
+++ b/R/fct_tables.R
@@ -68,8 +68,6 @@ create_table.default <- function(
values_from = {{value_column}},
values_fn = ~paste0(., collapse = "; ")
)
- if ("o_reviewed" %in% names(df))
- df <- dplyr::mutate(df, o_reviewed = lapply(dplyr::row_number(), \(x) append(o_reviewed[[x]], list(row_id = x))))
expected_columns <- na.omit(expected_columns) %||% character(0)
if(length(expected_columns) == 0) return(df)
add_missing_columns(df, expected_columns)[
diff --git a/R/mod_common_forms.R b/R/mod_common_forms.R
index 306ad333..993f02d8 100644
--- a/R/mod_common_forms.R
+++ b/R/mod_common_forms.R
@@ -93,48 +93,62 @@ mod_common_forms_server <- function(
moduleServer( id, function(input, output, session){
ns <- session$ns
- data_active <- reactive({
- req(!is.null(input$show_all_data))
- shiny::validate(need(
- !is.null(r$filtered_data[[form]]),
- paste0("Warning: no data found in the database for the form '", form, "'.")
- ))
- df <- dplyr::left_join(
- r$filtered_data[[form]],
- with(r$review_data, r$review_data[item_group == form, ]) |>
- dplyr::select(-dplyr::all_of(c("edit_date_time", "event_date"))),
- by = id_item
- ) |>
- dplyr::mutate(
- item_value = ifelse(
- reviewed == "No",
- paste0("", htmltools::htmlEscape(item_value), "*"),
- htmltools::htmlEscape(item_value)
- )
+ common_form_data <- reactiveVal()
+ SAE_data <- reactiveVal()
+ observe({
+ df <- {
+ shiny::validate(need(
+ !is.null(r$filtered_data[[form]]),
+ paste0("Warning: no data found in the database for the form '", form, "'.")
+ ))
+ dplyr::left_join(
+ r$filtered_data[[form]],
+ with(r$review_data, r$review_data[item_group == form, ]) |>
+ dplyr::select(-dplyr::all_of(c("edit_date_time", "event_date"))),
+ by = id_item
) |>
- create_table(expected_columns = names(form_items))
- if(!input$show_all_data){
- df <- with(df, df[subject_id == r$subject_id, ])
+ dplyr::mutate(
+ item_value = ifelse(
+ reviewed == "No",
+ paste0("", htmltools::htmlEscape(item_value), "*"),
+ htmltools::htmlEscape(item_value)
+ )
+ ) |>
+ create_table(expected_columns = names(form_items)) |>
+ dplyr::mutate(o_reviewed = Map(\(x, y) append(x, list(row_id = y)),
+ o_reviewed,
+ dplyr::row_number()))
}
- df
+ common_form_data({
+ if(form == "Adverse events") {
+ df |>
+ dplyr::filter(!grepl("Yes", `Serious Adverse Event`)
+ ) |>
+ dplyr::select(-dplyr::starts_with("SAE"))
+ } else {
+ df
+ }
+ })
+ if (form == "Adverse events")
+ SAE_data({
+ df |>
+ dplyr::filter(grepl("Yes", `Serious Adverse Event`)) |>
+ dplyr::select(dplyr::any_of(
+ c("o_reviewed", "subject_id","form_repeat", "Name", "AESI", "SAE Start date",
+ "SAE End date", "CTCAE severity", "Treatment related",
+ "Treatment action", "Other action", "SAE Category",
+ "SAE Awareness date", "SAE Date of death", "SAE Death reason")
+ )) |>
+ adjust_colnames("^SAE ")
+ })
})
mod_timeline_server("timeline_fig", r = r, form = form)
output[["SAE_table"]] <- DT::renderDT({
req(form == "Adverse events")
- SAE_data <- data_active() |>
- dplyr::filter(grepl("Yes", `Serious Adverse Event`)) |>
- dplyr::select(dplyr::any_of(
- c("o_reviewed", "subject_id","form_repeat", "Name", "AESI", "SAE Start date",
- "SAE End date", "CTCAE severity", "Treatment related",
- "Treatment action", "Other action", "SAE Category",
- "SAE Awareness date", "SAE Date of death", "SAE Death reason")
- )) |>
- adjust_colnames("^SAE ")
- if(!input$show_all_data) SAE_data$subject_id <- NULL
datatable_custom(
- SAE_data,
+ isolate(subset(SAE_data(), input$show_all_data | subject_id == r$subject_id)),
rename_vars = c("Review Status" = "o_reviewed", table_names),
rownames= FALSE,
title = "Serious Adverse Events",
@@ -142,92 +156,153 @@ mod_common_forms_server <- function(
selection = "none",
callback = checkbox_callback,
options = list(
- columnDefs = list(list(
- targets = 0,
+ columnDefs = list(
+ list(
+ targets = "o_reviewed",
+ orderable = FALSE,
render = checkbox_render
+ ),
+ list(
+ targets = "subject_id",
+ visible = isolate(input$show_all_data)
)),
rowCallback = row_callback
))
})
+ SAE_proxy <- DT::dataTableProxy("SAE_table")
- observeEvent(data_active(), {
- session$userData$update_checkboxes[[form]] <- NULL
- session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character())
+ output[["common_form_table"]] <- DT::renderDT({
+ datatable_custom(
+ isolate(subset(common_form_data(), input$show_all_data | subject_id == r$subject_id)),
+ rename_vars = c("Review Status" = "o_reviewed", table_names),
+ rownames= FALSE,
+ title = form,
+ escape = FALSE,
+ selection = "none",
+ callback = checkbox_callback,
+ options = list(
+ columnDefs = list(
+ list(
+ targets = "o_reviewed",
+ orderable = FALSE,
+ render = checkbox_render
+ ),
+ list(
+ targets = "subject_id",
+ visible = isolate(input$show_all_data)
+ )),
+ rowCallback = row_callback
+ ))
})
+ common_form_proxy <- DT::dataTableProxy('common_form_table')
- observeEvent(input$common_form_table_review_selection, {
+ observe({
session$userData$update_checkboxes[[form]] <- NULL
-
- session$userData$review_records[[form]] <-
- dplyr::rows_upsert(
- session$userData$review_records[[form]],
- input$common_form_table_review_selection[c("id", "reviewed")],
- by = "id"
- ) |>
- dplyr::filter(!is.na(reviewed)) |>
- dplyr::semi_join(
- subset(r$review_data, subject_id == r$subject_id & item_group == form),
- by = "id"
- ) |>
- dplyr::anti_join(
- subset(r$review_data, subject_id == r$subject_id & item_group == form),
- by = c("id", "reviewed")
- ) |>
- dplyr::arrange(id)
- })
+ session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character())
+ }) |>
+ bindEvent(r$subject_id, r$review_data)
observeEvent(session$userData$update_checkboxes[[form]], {
checked <- session$userData$update_checkboxes[[form]]
- update_cbs("common_form_table", checked)
- update_cbs("SAE_table", checked)
+ df <- common_form_data() |>
+ dplyr::mutate(o_reviewed = dplyr::if_else(subject_id == r$subject_id,
+ lapply(o_reviewed, modifyList, list(updated = checked)),
+ o_reviewed))
+ common_form_data(df)
+ if (form == "Adverse events") {
+ df <- SAE_data() |>
+ dplyr::mutate(o_reviewed = dplyr::if_else(subject_id == r$subject_id,
+ lapply(o_reviewed, modifyList, list(updated = checked)),
+ o_reviewed))
+ SAE_data(df)
+ }
+ })
+
+ lapply(c("common_form", if (form == "Adverse events") "SAE"), \(x) {
+ observeEvent(input[[sprintf("%s_table_review_selection", x)]], {
+ session$userData$update_checkboxes[[form]] <- NULL
+
+ session$userData$review_records[[form]] <-
+ dplyr::rows_upsert(
+ session$userData$review_records[[form]],
+ input[[sprintf("%s_table_review_selection", x)]][c("id", "reviewed")],
+ by = "id"
+ ) |>
+ dplyr::filter(!is.na(reviewed)) |>
+ dplyr::semi_join(
+ subset(r$review_data, subject_id == r$subject_id & item_group == form),
+ by = "id"
+ ) |>
+ dplyr::anti_join(
+ subset(r$review_data, subject_id == r$subject_id & item_group == form),
+ by = c("id", "reviewed")
+ ) |>
+ dplyr::arrange(id)
+ })
+ })
+
+ observe({
+ req(!is.null(input$show_all_data))
+ DT::replaceData(common_form_proxy,
+ subset(common_form_data(), input$show_all_data | subject_id == r$subject_id),
+ rownames = FALSE, resetPaging = FALSE)
+ req(form == "Adverse events")
+ DT::replaceData(SAE_proxy,
+ subset(SAE_data(), input$show_all_data | subject_id == r$subject_id),
+ rownames = FALSE, resetPaging = FALSE)
+ })
+
+ observeEvent(input$common_form_table_review_selection, {
+ df <- common_form_data()
+
+ update_row <- dplyr::distinct(input$common_form_table_review_selection, reviewed, row_id)
+ row_ids <- df$o_reviewed |> lapply(\(x) x[["row_id"]]) |> unlist()
+ df[row_ids == update_row$row_id, "o_reviewed"] <- list(list(
+ modifyList(df[row_ids == update_row$row_id,]$o_reviewed[[1]],
+ list(updated = switch(update_row$reviewed, "Yes" = TRUE, "No" = FALSE, NA)))
+ ))
+ common_form_data(df)
})
observeEvent(input$SAE_table_review_selection, {
- session$userData$update_checkboxes[[form]] <- NULL
+ df <- SAE_data()
- session$userData$review_records[[form]] <-
- dplyr::rows_upsert(
- session$userData$review_records[[form]],
- input$SAE_table_review_selection[c("id", "reviewed")],
- by = "id"
- ) |>
- dplyr::filter(!is.na(reviewed)) |>
- dplyr::semi_join(
- subset(r$review_data, subject_id == r$subject_id & item_group == form),
- by = "id"
- ) |>
- dplyr::anti_join(
- subset(r$review_data, subject_id == r$subject_id & item_group == form),
- by = c("id", "reviewed")
- ) |>
- dplyr::arrange(id)
+ update_row <- dplyr::distinct(input$SAE_table_review_selection, reviewed, row_id)
+ row_ids <- df$o_reviewed |> lapply(\(x) x[["row_id"]]) |> unlist()
+ df[row_ids == update_row$row_id, "o_reviewed"] <- list(list(
+ modifyList(df[row_ids == update_row$row_id,]$o_reviewed[[1]],
+ list(updated = switch(update_row$reviewed, "Yes" = TRUE, "No" = FALSE, NA)))
+ ))
+ SAE_data(df)
})
- output[["common_form_table"]] <- DT::renderDT({
- df <- data_active()
- if(form == "Adverse events") {
- df <- df |>
- dplyr::filter(!grepl("Yes", `Serious Adverse Event`)
- ) |>
- dplyr::select(-dplyr::starts_with("SAE"))
+ observeEvent(r$subject_id, {
+ df <- common_form_data() |>
+ dplyr::mutate(o_reviewed = Map(\(x, y) modifyList(x, list(updated = NULL, disabled = y)), o_reviewed, subject_id != r$subject_id))
+ common_form_data(df)
+ if (form == "Adverse events") {
+ df <- SAE_data() |>
+ dplyr::mutate(o_reviewed = Map(\(x, y) modifyList(x, list(updated = NULL, disabled = y)), o_reviewed, subject_id != r$subject_id))
+ SAE_data(df)
+ }
+ })
+
+ observeEvent(input$show_all_data, {
+ index <- match("subject_id", colnames(common_form_data())) - 1
+ if (input$show_all_data) {
+ DT::showCols(common_form_proxy, index)
+ } else {
+ DT::hideCols(common_form_proxy, index)
+ }
+ if (form == "Adverse events") {
+ index <- match("subject_id", colnames(SAE_data())) - 1
+ if (input$show_all_data) {
+ DT::showCols(SAE_proxy, index)
+ } else {
+ DT::hideCols(SAE_proxy, index)
+ }
}
- if(!input$show_all_data) df$subject_id <- NULL
- datatable_custom(
- df,
- rename_vars = c("Review Status" = "o_reviewed", table_names),
- rownames= FALSE,
- title = form,
- escape = FALSE,
- selection = "none",
- callback = checkbox_callback,
- options = list(
- columnDefs = list(list(
- targets = 0,
- render = checkbox_render
- )),
- rowCallback = row_callback
- ))
})
})
diff --git a/R/mod_study_forms.R b/R/mod_study_forms.R
index 68b444c4..5e982983 100644
--- a/R/mod_study_forms.R
+++ b/R/mod_study_forms.R
@@ -162,30 +162,32 @@ mod_study_forms_server <- function(
dplyr::mutate(item_name = factor(item_name, levels = names(form_items)))
})
- table_data_active <- reactive({
- req(!is.null(input$show_all))
- validate(need(
- r$filtered_data[[form]],
- paste0("Warning: no data found in database for the form '", form, "'")
- ))
- df <- dplyr::left_join(
- r$filtered_data[[form]],
- with(r$review_data, r$review_data[item_group == form, ]) |>
- dplyr::select(-dplyr::all_of(c("edit_date_time", "event_date"))),
- by = id_item
- ) |>
- dplyr::mutate(
- item_value = ifelse(
- reviewed == "No",
- paste0("", htmltools::htmlEscape(item_value), "*"),
- htmltools::htmlEscape(item_value)
- )
+ table_data <- reactiveVal()
+ observe({
+ df <- {
+ validate(need(
+ r$filtered_data[[form]],
+ paste0("Warning: no data found in database for the form '", form, "'")
+ ))
+ dplyr::left_join(
+ r$filtered_data[[form]],
+ with(r$review_data, r$review_data[item_group == form, ]) |>
+ dplyr::select(-dplyr::all_of(c("edit_date_time", "event_date"))),
+ by = id_item
) |>
- create_table(expected_columns = names(form_items))
- req(nrow(df) != 0)
- if(input$show_all) return(df)
- with(df, df[subject_id == r$subject_id, ]) |>
- dplyr::select(-dplyr::all_of("subject_id"))
+ dplyr::mutate(
+ item_value = ifelse(
+ reviewed == "No",
+ paste0("", htmltools::htmlEscape(item_value), "*"),
+ htmltools::htmlEscape(item_value)
+ )
+ ) |>
+ create_table(expected_columns = names(form_items)) |>
+ dplyr::mutate(o_reviewed = Map(\(x, y) append(x, list(row_id = y)),
+ o_reviewed,
+ dplyr::row_number()))
+ }
+ table_data(df)
})
scaling_data <- reactive({
@@ -194,11 +196,22 @@ mod_study_forms_server <- function(
lapply(add_missing_columns(item_info, cols)[1, cols], isTRUE)
})
- observeEvent(table_data_active(), {
+ observe({
session$userData$update_checkboxes[[form]] <- NULL
- session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character(), row_index = character())
+ session$userData$review_records[[form]] <- data.frame(id = integer(), reviewed = character())
+ }) |>
+ bindEvent(r$subject_id, r$review_data)
+
+ observeEvent(session$userData$update_checkboxes[[form]], {
+ checked <- session$userData$update_checkboxes[[form]]
+
+ df <- table_data() |>
+ dplyr::mutate(o_reviewed = dplyr::if_else(subject_id == r$subject_id,
+ lapply(o_reviewed, modifyList, list(updated = checked)),
+ o_reviewed))
+ table_data(df)
})
-
+
observeEvent(input$table_review_selection, {
session$userData$update_checkboxes[[form]] <- NULL
@@ -220,10 +233,38 @@ mod_study_forms_server <- function(
dplyr::arrange(id)
})
- observeEvent(session$userData$update_checkboxes[[form]], {
- checked <- session$userData$update_checkboxes[[form]]
+ observe({
+ req(!is.null(input$show_all))
+ DT::replaceData(table_proxy,
+ subset(table_data(), input$show_all | subject_id == r$subject_id),
+ rownames = FALSE, resetPaging = FALSE)
+ })
+
+ observeEvent(input$table_review_selection, {
+ df <- table_data()
- update_cbs("table", checked)
+ update_row <- dplyr::distinct(input$table_review_selection, reviewed, row_id)
+ row_ids <- df$o_reviewed |> lapply(\(x) x[["row_id"]]) |> unlist()
+ df[row_ids == update_row$row_id, "o_reviewed"] <- list(list(
+ modifyList(df[row_ids == update_row$row_id,]$o_reviewed[[1]],
+ list(updated = switch(update_row$reviewed, "Yes" = TRUE, "No" = FALSE, NA)))
+ ))
+ table_data(df)
+ })
+
+ observeEvent(r$subject_id, {
+ df <- table_data() |>
+ dplyr::mutate(o_reviewed = Map(\(x, y) modifyList(x, list(updated = NULL, disabled = y)), o_reviewed, subject_id != r$subject_id))
+ table_data(df)
+ })
+
+ observeEvent(input$show_all, {
+ index <- match("subject_id", colnames(table_data())) - 1
+ if (input$show_all) {
+ DT::showCols(table_proxy, index)
+ } else {
+ DT::hideCols(table_proxy, index)
+ }
})
############################### Outputs: ###################################
@@ -255,22 +296,28 @@ mod_study_forms_server <- function(
})
output[["table"]] <- DT::renderDT({
- req(table_data_active())
datatable_custom(
- table_data_active(),
+ isolate(subset(table_data(), input$show_all | subject_id == r$subject_id)),
rename_vars = c("Review Status" = "o_reviewed", table_names),
rownames= FALSE,
escape = FALSE,
selection = "none",
callback = checkbox_callback,
options = list(
- columnDefs = list(list(
- targets = 0,
- render = checkbox_render
- )),
+ columnDefs = list(
+ list(
+ targets = "o_reviewed",
+ orderable = FALSE,
+ render = checkbox_render
+ ),
+ list(
+ targets = "subject_id",
+ visible = isolate(input$show_all)
+ )),
rowCallback = row_callback
))
})
+ table_proxy <- DT::dataTableProxy("table")
if(form %in% c("Vital signs", "Vitals adjusted")){
shiny::exportTestValues(
diff --git a/R/shiny.R b/R/shiny.R
index 92ceef79..20233f49 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -23,7 +23,7 @@ checkbox_callback <- DT::JS(
"var review = $(this).is(':indeterminate') ? null : $(this).is(':checked');",
"cell.data().updated = review;",
"var info = {review: review, ids: cell.data().ids, row_id: cell.data().row_id};",
- "Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info);",
+ "Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info, {priority: 'event'});",
"});"
)
@@ -31,6 +31,7 @@ checkbox_render <- DT::JS(
"function(data, type, row, meta) {",
"var reviewed = data.reviewed;",
"var updated = data.updated;",
+ "var disabled = data.disabled;",
"var cb_class = ''",
"if (reviewed == null) {",
"cb_class = updated == null ? '' : 'indeterminate'",
@@ -38,6 +39,7 @@ checkbox_render <- DT::JS(
"cb_class = reviewed ? 'checked' : 'unchecked'",
"}",
"return ``;",
@@ -55,12 +57,6 @@ row_callback <- DT::JS(
"}"
)
-update_cbs <- function(id, checked, session = getDefaultReactiveDomain()) {
- tblId <- session$ns(id)
- params <- list(id = tblId, checked = checked)
- session$sendCustomMessage('update_checkboxes', params)
-}
-
progress_bar <- function(outputId) {
div(
id = outputId,
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index 809b65b7..c2a61bed 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -10,17 +10,6 @@ function ts(cb) {
$(document).ready(function() {
- Shiny.addCustomMessageHandler("update_checkboxes", function(params) {
- var table = $('#' + params.id + " .table").DataTable()
- table.column(0).data().each(function(data){data.updated = params.checked})
- $(':checkbox:not(.indeterminate)', table.rows().nodes()).
- prop('checked', params.checked);
- $(':checkbox.indeterminate', table.rows().nodes()).
- prop('checked', params.checked).
- prop('indeterminate', false).
- prop('readOnly', !params.checked);
- });
-
/* Define custom Shiny input binding for overall review checkbox.
This is needed to assign an event priority to the checkbox.*/
var customCheckbox = new Shiny.InputBinding();
From 05084db2ddef8f020e28e651ef7e37f6888527d6 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 13 Dec 2024 14:35:54 -0500
Subject: [PATCH 54/86] Simplify DT callbacks
---
R/shiny.R | 53 +++---------------------------------------
inst/app/www/custom.js | 48 ++++++++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+), 50 deletions(-)
diff --git a/R/shiny.R b/R/shiny.R
index 20233f49..525ebe53 100644
--- a/R/shiny.R
+++ b/R/shiny.R
@@ -6,56 +6,9 @@ shiny::registerInputHandler('CS.reviewInfo', function(val, ...) {
))
}, TRUE)
-checkbox_callback <- DT::JS(
- "table.on('column-reorder', function() {",
- "table.rows().every(function() {",
- "if (this.data()[0].reviewed == null) {",
- "$(':checkbox', this.node()).",
- "addClass('indeterminate').",
- "prop('indeterminate', this.data()[0].updated == null).",
- "prop('readOnly', this.data()[0].updated == false)",
- "}",
- "})",
- "});",
- "table.on('click', 'input[type=\"checkbox\"]', function(){",
- "var tblId = $(this).closest('.datatables').attr('id');",
- "var cell = table.cell($(this).closest('td'));",
- "var review = $(this).is(':indeterminate') ? null : $(this).is(':checked');",
- "cell.data().updated = review;",
- "var info = {review: review, ids: cell.data().ids, row_id: cell.data().row_id};",
- "Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info, {priority: 'event'});",
- "});"
-)
-
-checkbox_render <- DT::JS(
- "function(data, type, row, meta) {",
- "var reviewed = data.reviewed;",
- "var updated = data.updated;",
- "var disabled = data.disabled;",
- "var cb_class = ''",
- "if (reviewed == null) {",
- "cb_class = updated == null ? '' : 'indeterminate'",
- "} else {",
- "cb_class = reviewed ? 'checked' : 'unchecked'",
- "}",
- "return ``;",
- "}"
-)
-
-row_callback <- DT::JS(
- "function(row, data) {",
- "if (data[0].reviewed == null) {",
- "$(':checkbox', row).",
- "addClass('indeterminate').",
- "prop('indeterminate', data[0].updated == null).",
- "prop('readOnly', data[0].updated == false)",
- "}",
- "}"
-)
+checkbox_callback <- DT::JS("checkboxCallback(table);")
+checkbox_render <- DT::JS("checkboxRender")
+row_callback <- DT::JS("rowCallback")
progress_bar <- function(outputId) {
div(
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
index c2a61bed..a593fe76 100644
--- a/inst/app/www/custom.js
+++ b/inst/app/www/custom.js
@@ -8,6 +8,54 @@ function ts(cb) {
}
}
+function checkboxCallback(table) {
+ table.on('column-reorder', function() {
+ table.rows().every(function() {
+ if (this.data()[0].reviewed == null) {
+ $(':checkbox', this.node())
+ .addClass('indeterminate')
+ .prop('indeterminate', this.data()[0].updated == null)
+ .prop('readOnly', this.data()[0].updated == false)
+ }
+ })
+ });
+ table.on('click', 'input[type="checkbox"]', function(){
+ var tblId = $(this).closest('.datatables').attr('id');
+ var cell = table.cell($(this).closest('td'));
+ var review = $(this).is(':indeterminate') ? null : $(this).is(':checked');
+ cell.data().updated = review;
+ var info = {review: review, ids: cell.data().ids, row_id: cell.data().row_id};
+ Shiny.setInputValue(tblId + '_review_selection:CS.reviewInfo', info, {priority: 'event'});
+ })
+ return table;
+}
+
+function checkboxRender(data, type, row, meta) {
+ var reviewed = data.reviewed;
+ var updated = data.updated;
+ var disabled = data.disabled;
+ var cb_class = ''
+ if (reviewed == null) {
+ cb_class = updated == null ? '' : 'indeterminate'
+ } else {
+ cb_class = reviewed ? 'checked' : 'unchecked'
+ }
+ return ``;
+}
+
+function rowCallback(row, data) {
+ if (data[0].reviewed == null) {
+ $(':checkbox', row)
+ .addClass('indeterminate')
+ .prop('indeterminate', data[0].updated == null)
+ .prop('readOnly', data[0].updated == false)
+ }
+}
+
$(document).ready(function() {
/* Define custom Shiny input binding for overall review checkbox.
From ebfd724eb9c2887d926bf4def4508537d42dc426 Mon Sep 17 00:00:00 2001
From: Jeff Thompson <160783290+jthompson-arcus@users.noreply.github.com>
Date: Fri, 13 Dec 2024 14:54:09 -0500
Subject: [PATCH 55/86] Update app feature JSONs
---
.../app_feature_01/app-feature-1-001.json | 31 +--
.../app_feature_01/app-feature-1-002.json | 169 ++++++-------
.../app_feature_01/app-feature-1-003.json | 169 ++++++-------
.../app_feature_01/app-feature-1-004.json | 231 ++++++++----------
.../app_feature_01/app-feature-1-005.json | 231 ++++++++----------
.../app_feature_02/app-feature-2-001.json | 31 +--
.../app_feature_03/app-feature-3-001.json | 31 +--
.../app_feature_03/app-feature-3-002.json | 203 +++++++--------
.../app_feature_03/app-feature-3-003.json | 62 ++---
.../app_feature_03/app-feature-3-004.json | 62 ++---
.../app_feature_03/app-feature-3-005.json | 64 ++---
11 files changed, 540 insertions(+), 744 deletions(-)
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-001.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-001.json
index e436c373..7068b035 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-001.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-001.json
@@ -181,9 +181,12 @@
"scrollX": true,
"scroller": true,
"deferRender": true,
- "scrollResize": true,
"scrollCollapse": true,
- "colReorder": true,
+ "colReorder": {
+ "enable": true,
+ "realtime": false,
+ "fixedColumnsLeft": 1
+ },
"initComplete": "function() {\n$(this.api().table().container()).find('.header').html(\"\")\n}",
"dom": "f<\"header h5\">ti",
"columnDefs": [
@@ -196,15 +199,15 @@
"targets": 0
},
{
- "name": "Subject",
+ "name": "subject_id",
"targets": 1
},
{
- "name": "Status",
+ "name": "status",
"targets": 2
},
{
- "name": "Dx",
+ "name": "WHO.classification",
"targets": 3
},
{
@@ -216,7 +219,7 @@
"targets": 5
},
{
- "name": "Event",
+ "name": "event_name",
"targets": 6
}
],
@@ -348,22 +351,6 @@
"package": null,
"all_files": false
},
- {
- "name": "dt-plugin-scrollresize",
- "version": "1.13.6",
- "src": {
- "href": "dt-plugin-scrollresize-1.13.6"
- },
- "meta": null,
- "script": "source.min.js",
- "stylesheet": [
-
- ],
- "head": null,
- "attachment": null,
- "package": null,
- "all_files": true
- },
{
"name": "crosstalk",
"version": "1.2.1",
diff --git a/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json b/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
index f0813be6..7e5d7f06 100644
--- a/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
+++ b/tests/testthat/_snaps/app_feature_01/app-feature-1-002.json
@@ -9,82 +9,94 @@
"Scroller",
"ColReorder"
],
- "container": "\n \n \n Review Status<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
+ "container": "\n \n \n Review Status<\/th>\n | Subject<\/th>\n | N<\/th>\n | Name<\/th>\n | AESI<\/th>\n | Start date<\/th>\n | End date<\/th>\n | CTCAE severity<\/th>\n | Treatment related<\/th>\n | Treatment action<\/th>\n | Other action<\/th>\n | Category<\/th>\n | Awareness date<\/th>\n | Date of death<\/th>\n | Death reason<\/th>\n <\/tr>\n <\/thead>\n<\/table>",
"options": {
"scrollY": 400,
"scrollX": true,
"scroller": true,
"deferRender": true,
- "scrollResize": true,
"scrollCollapse": true,
- "colReorder": true,
+ "colReorder": {
+ "enable": true,
+ "realtime": false,
+ "fixedColumnsLeft": 1
+ },
"columnDefs": [
{
"targets": 0,
- "render": "function(data, type, row, meta) {\nvar reviewed = data.reviewed;\nreturn ` | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |