diff --git a/DESCRIPTION b/DESCRIPTION index 917036a923..80f1cfffc3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -43,6 +43,7 @@ Imports: magrittr, methods, rlang, + rlistings (>= 0.2.4), rmarkdown, rtables (>= 0.6.1), scales, diff --git a/NEWS.md b/NEWS.md index d947126acc..67212744f8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,8 @@ * Updated `tm_t_events` to maintain indentation after pruning. * Updated default reference/comparison arm level selection to work when arm variable levels are filtered out. * Updated `tm_t_coxreg` to drop factor covariate variable levels that are not present to avoid errors when filtering. +* Updated `tm_t_pp_basic_info`, `tm_t_pp_medical_history`, `tm_g_pp_therapy`, `tm_g_pp_adverse_events`, and `tm_t_pp_laboratory` to print patient ID above table. +* Updated `tm_t_pp_basic_info`, `tm_g_pp_therapy`, `tm_g_pp_adverse_events`, and `tm_t_pp_laboratory` to use `rlistings` to print data neatly in reports. ### Bug fixes diff --git a/R/tm_g_pp_adverse_events.R b/R/tm_g_pp_adverse_events.R index 59d3cd1b43..ea37176a7e 100644 --- a/R/tm_g_pp_adverse_events.R +++ b/R/tm_g_pp_adverse_events.R @@ -53,6 +53,14 @@ template_adverse_events <- function(dataname = "ANL", ) %>% dplyr::arrange(dplyr::desc(tox_grade)) %>% `colnames<-`(get_labels(dataname)$column_labels[vars]) + + table <- rlistings::as_listing( + table, + key_cols = NULL, + default_formatting = list(all = fmt_config(align = "left")) + ) + main_title(table) <- paste("Patient ID:", patient_id) + table }, env = list( @@ -64,7 +72,8 @@ template_adverse_events <- function(dataname = "ANL", action = as.name(action), time = as.name(time), decod = `if`(is.null(decod), NULL, as.name(decod)), - vars = c(aeterm, tox_grade, causality, outcome, action, time, decod) + vars = c(aeterm, tox_grade, causality, outcome, action, time, decod), + patient_id = patient_id ) ) ) @@ -311,6 +320,7 @@ ui_g_adverse_events <- function(id, ...) { ns <- shiny::NS(id) teal.widgets::standard_layout( output = shiny::div( + shiny::htmlOutput(ns("title")), teal.widgets::get_dt_rows(ns("table"), ns("table_rows")), DT::DTOutput(outputId = ns("table")), teal.widgets::plot_with_settings_ui(id = ns("chart")) @@ -502,8 +512,10 @@ srv_g_adverse_events <- function(id, qenv2 <- teal.code::eval_code( qenv, substitute( - expr = ANL <- ANL[ANL[[patient_col]] == patient_id, ], # nolint - env = list( + expr = { + pt_id <- patient_id + ANL <- ANL[ANL[[patient_col]] == patient_id, ] # nolint + }, env = list( patient_col = patient_col, patient_id = patient_id() ) @@ -526,6 +538,11 @@ srv_g_adverse_events <- function(id, teal.code::eval_code(qenv2, as.expression(calls)) }) + + output$title <- shiny::renderText({ + paste("
Patient ID:", all_q()[["pt_id"]], "
") + }) + output$table <- DT::renderDataTable( expr = teal.code::dev_suppress(all_q()[["table"]]), options = list(pageLength = input$table_rows) @@ -560,11 +577,13 @@ srv_g_adverse_events <- function(id, if (with_reporter) { card_fun <- function(comment) { card <- teal::TealReportCard$new() - card$set_name("Patient Profile Adverse Events Plot") - card$append_text("Patient Profile Adverse Events Plot", "header2") + card$set_name("Patient Profile Adverse Events") + card$append_text("Patient Profile Adverse Events", "header2") if (with_filter) { card$append_fs(filter_panel_api$get_filter_state()) } + card$append_text("Table", "header3") + card$append_table(teal.code::dev_suppress(all_q()[["table"]])) card$append_text("Plot", "header3") card$append_plot(plot_r(), dim = pws$dim()) if (!comment == "") { diff --git a/R/tm_g_pp_therapy.R b/R/tm_g_pp_therapy.R index c5356c8be4..adf42b587d 100644 --- a/R/tm_g_pp_therapy.R +++ b/R/tm_g_pp_therapy.R @@ -87,6 +87,14 @@ template_therapy <- function(dataname = "ANL", get_labels(dataname)$column_labels[c(cmindc_char, cmdecod_char)], "Dosage", get_labels(dataname)$column_labels[c(cmstdy_char, cmendy_char)] )) + + therapy_table <- rlistings::as_listing( + therapy_table, + key_cols = NULL, + default_formatting = list(all = fmt_config(align = "left")) + ) + main_title(therapy_table) <- paste("Patient ID:", patient_id) + therapy_table }, env = list( dataname = as.name(dataname), @@ -108,7 +116,8 @@ template_therapy <- function(dataname = "ANL", cmroute_char = cmroute, cmdosfrq_char = cmdosfrq, cmendy_char = cmendy, - cmstdy_char = cmstdy + cmstdy_char = cmstdy, + patient_id = patient_id )) ) @@ -172,10 +181,9 @@ template_therapy <- function(dataname = "ANL", ggplot2::ggplot(data = data, ggplot2::aes(fill = cmindc, color = cmindc, y = CMDECOD, x = CMSTDY)) + ggplot2::geom_segment(ggplot2::aes(xend = CMENDY, yend = CMDECOD), size = 2) + ggplot2::geom_text( - data = - data %>% - dplyr::select(CMDECOD, cmindc, CMSTDY) %>% - dplyr::distinct(), + data = data %>% + dplyr::select(CMDECOD, cmindc, CMSTDY) %>% + dplyr::distinct(), ggplot2::aes(x = CMSTDY, label = CMDECOD), color = "black", hjust = "left", vjust = "bottom", @@ -411,6 +419,7 @@ ui_g_therapy <- function(id, ...) { ns <- shiny::NS(id) teal.widgets::standard_layout( output = shiny::div( + shiny::htmlOutput(ns("title")), teal.widgets::get_dt_rows(ns("therapy_table"), ns("therapy_table_rows")), DT::DTOutput(outputId = ns("therapy_table")), teal.widgets::plot_with_settings_ui(id = ns("therapy_plot")) @@ -638,6 +647,7 @@ srv_g_therapy <- function(id, merged$anl_q(), substitute( expr = { + pt_id <- patient_id ANL <- ANL[ANL[[patient_col]] == patient_id, ] # nolint }, env = list( patient_col = patient_col, @@ -648,6 +658,10 @@ srv_g_therapy <- function(id, teal.code::eval_code(as.expression(my_calls)) }) + output$title <- shiny::renderText({ + paste("
Patient ID:", all_q()[["pt_id"]], "
") + }) + output$therapy_table <- DT::renderDataTable( expr = { teal.code::dev_suppress(all_q()[["therapy_table"]]) @@ -684,11 +698,13 @@ srv_g_therapy <- function(id, if (with_reporter) { card_fun <- function(comment) { card <- teal::TealReportCard$new() - card$set_name("Patient Profile Therapy Plot") - card$append_text("Patient Profile Therapy Plot", "header2") + card$set_name("Patient Profile Therapy") + card$append_text("Patient Profile Therapy", "header2") if (with_filter) { card$append_fs(filter_panel_api$get_filter_state()) } + card$append_text("Table", "header3") + card$append_table(teal.code::dev_suppress(all_q()[["therapy_table"]])) card$append_text("Plot", "header3") card$append_plot(plot_r(), dim = pws$dim()) if (!comment == "") { diff --git a/R/tm_t_pp_basic_info.R b/R/tm_t_pp_basic_info.R index f535393aea..d6a112f223 100644 --- a/R/tm_t_pp_basic_info.R +++ b/R/tm_t_pp_basic_info.R @@ -4,10 +4,12 @@ #' #' @inheritParams template_arguments #' @param vars (`character`)\cr variable names to be shown in Basic Info tab. +#' @param patient_id (`character`)\cr patient ID. #' @keywords internal #' template_basic_info <- function(dataname = "ANL", - vars) { + vars, + patient_id = NULL) { checkmate::assert_string(dataname) checkmate::assert_character(vars, min.len = 1) @@ -27,13 +29,21 @@ template_basic_info <- function(dataname = "ANL", key <- get_labels(dataname)$column_labels[rownames(values)] result <- - data.frame(key = key, value = values) %>% - dplyr::select(key, value) %>% - dplyr::rename(` ` = key, ` ` = value) + data.frame(var = rownames(values), key = key, value = values) %>% + dplyr::select(var, key, value) %>% + dplyr::rename(` ` = var, ` ` = key, ` ` = value) + + result <- rlistings::as_listing( + result, + default_formatting = list(all = fmt_config(align = "left")) + ) + main_title(result) <- paste("Patient ID:", patient_id) + result }, env = list( dataname = as.name(dataname), - vars = vars + vars = vars, + patient_id = patient_id ) ) ) @@ -119,6 +129,7 @@ ui_t_basic_info <- function(id, ...) { ns <- shiny::NS(id) teal.widgets::standard_layout( output = shiny::div( + shiny::htmlOutput(ns("title")), DT::DTOutput(outputId = ns("basic_info_table")) ), encoding = shiny::div( @@ -221,13 +232,15 @@ srv_t_basic_info <- function(id, teal::validate_inputs(iv_r()) my_calls <- template_basic_info( dataname = "ANL", - vars = anl_inputs()$columns_source$vars + vars = anl_inputs()$columns_source$vars, + patient_id = patient_id() ) teal.code::eval_code( anl_q(), substitute( expr = { + pt_id <- patient_id ANL <- ANL[ANL[[patient_col]] == patient_id, ] # nolint }, env = list( patient_col = patient_col, @@ -238,6 +251,10 @@ srv_t_basic_info <- function(id, teal.code::eval_code(as.expression(my_calls)) }) + output$title <- shiny::renderText({ + paste("
Patient ID:", all_q()[["pt_id"]], "
") + }) + table_r <- shiny::reactive(all_q()[["result"]]) output$basic_info_table <- DT::renderDataTable( diff --git a/R/tm_t_pp_laboratory.R b/R/tm_t_pp_laboratory.R index 84b961e776..16f7f301d1 100644 --- a/R/tm_t_pp_laboratory.R +++ b/R/tm_t_pp_laboratory.R @@ -9,6 +9,7 @@ #' @param anrind (`character`)\cr name of the analysis reference range indicator variable. #' @param aval (`character`)\cr name of the analysis value variable. #' @param avalu (`character`)\cr name of the analysis value unit variable. +#' @param patient_id (`character`)\cr patient ID. #' @param round_value (`numeric`)\cr number of decimal places to be used when rounding. #' @keywords internal #' @@ -19,6 +20,7 @@ template_laboratory <- function(dataname = "ANL", timepoints = "ADY", aval = "AVAL", avalu = "AVALU", + patient_id = NULL, round_value = 0L) { assertthat::assert_that( assertthat::is.string(dataname), @@ -49,16 +51,23 @@ template_laboratory <- function(dataname = "ANL", dplyr::mutate(aval_anrind = paste(aval, anrind)) %>% dplyr::select(-c(aval, anrind)) + labor_table_raw <- labor_table_base %>% + tidyr::pivot_wider(names_from = INDEX, values_from = aval_anrind) %>% + dplyr::mutate(param_char := clean_description(.data[[param_char]])) + + labor_table_raw <- rlistings::as_listing( + labor_table_raw, + key_cols = NULL, + default_formatting = list(all = fmt_config(align = "left")) + ) + main_title(labor_table_raw) <- paste("Patient ID:", patient_id) + labor_table_html <- labor_table_base %>% dplyr::mutate(aval_anrind_col = color_lab_values(aval_anrind)) %>% dplyr::select(-aval_anrind) %>% tidyr::pivot_wider(names_from = INDEX, values_from = aval_anrind_col) %>% dplyr::mutate(param_char := clean_description(.data[[param_char]])) - labor_table_raw <- labor_table_base %>% - tidyr::pivot_wider(names_from = INDEX, values_from = aval_anrind) %>% - dplyr::mutate(param_char := clean_description(.data[[param_char]])) - labor_table_html_dt <- DT::datatable(labor_table_html, escape = FALSE) labor_table_html_dt$dependencies <- c( labor_table_html_dt$dependencies, @@ -76,6 +85,7 @@ template_laboratory <- function(dataname = "ANL", avalu = as.name(avalu), timepoints = as.name(timepoints), anrind = as.name(anrind), + patient_id = patient_id, round_value = round_value ) ) @@ -214,6 +224,7 @@ ui_g_laboratory <- function(id, ...) { ns <- shiny::NS(id) teal.widgets::standard_layout( output = shiny::div( + shiny::htmlOutput(ns("title")), DT::DTOutput(outputId = ns("lab_values_table")) ), encoding = shiny::div( @@ -386,6 +397,7 @@ srv_g_laboratory <- function(id, param = input[[extract_input("param", dataname)]], paramcd = input[[extract_input("paramcd", dataname)]], anrind = input[[extract_input("anrind", dataname)]], + patient_id = patient_id(), round_value = as.integer(input$round_value) ) @@ -393,6 +405,7 @@ srv_g_laboratory <- function(id, anl_q(), substitute( expr = { + pt_id <- patient_id ANL <- ANL[ANL[[patient_col]] == patient_id, ] # nolint }, env = list( patient_col = patient_col, @@ -403,6 +416,10 @@ srv_g_laboratory <- function(id, teal.code::eval_code(as.expression(labor_calls)) }) + output$title <- shiny::renderText({ + paste("
Patient ID:", all_q()[["pt_id"]], "
") + }) + table_r <- shiny::reactive({ q <- all_q() list( diff --git a/R/tm_t_pp_medical_history.R b/R/tm_t_pp_medical_history.R index f77bd6f17e..917ca2127c 100644 --- a/R/tm_t_pp_medical_history.R +++ b/R/tm_t_pp_medical_history.R @@ -6,12 +6,14 @@ #' @param mhterm (`character`)\cr name of the reported name for medical history variable. #' @param mhbodsys (`character`)\cr name of the body system or organ class variable. #' @param mhdistat (`character`)\cr name of the status of the disease variable. +#' @param patient_id (`character`)\cr patient ID. #' @keywords internal #' template_medical_history <- function(dataname = "ANL", mhterm = "MHTERM", mhbodsys = "MHBODSYS", - mhdistat = "MHDISTAT") { + mhdistat = "MHDISTAT", + patient_id = NULL) { assertthat::assert_that( assertthat::is.string(dataname), assertthat::is.string(mhterm), @@ -51,6 +53,8 @@ template_medical_history <- function(dataname = "ANL", rtables::analyze_colvars(function(x) x[seq_along(x)]) %>% rtables::build_table(result_raw) + main_title(result) <- paste("Patient ID:", patient_id) + result }, env = list( dataname = as.name(dataname), @@ -59,7 +63,8 @@ template_medical_history <- function(dataname = "ANL", mhdistat = as.name(mhdistat), mhbodsys_char = mhbodsys, mhterm_char = mhterm, - mhdistat_char = mhdistat + mhdistat_char = mhdistat, + patient_id = patient_id )) ) @@ -297,7 +302,8 @@ srv_t_medical_history <- function(id, dataname = "ANL", mhterm = input[[extract_input("mhterm", dataname)]], mhbodsys = input[[extract_input("mhbodsys", dataname)]], - mhdistat = input[[extract_input("mhdistat", dataname)]] + mhdistat = input[[extract_input("mhdistat", dataname)]], + patient_id = patient_id() ) teal.code::eval_code( diff --git a/man/template_basic_info.Rd b/man/template_basic_info.Rd index 65ba3c18d2..e11bd7f154 100644 --- a/man/template_basic_info.Rd +++ b/man/template_basic_info.Rd @@ -4,13 +4,15 @@ \alias{template_basic_info} \title{Template: Basic Info} \usage{ -template_basic_info(dataname = "ANL", vars) +template_basic_info(dataname = "ANL", vars, patient_id = NULL) } \arguments{ \item{dataname}{(\code{character})\cr analysis data used in teal module.} \item{vars}{(\code{character})\cr variable names to be shown in Basic Info tab.} + +\item{patient_id}{(\code{character})\cr patient ID.} } \description{ Creates a basic info template. diff --git a/man/template_laboratory.Rd b/man/template_laboratory.Rd index aacf45c07a..cb60cd682b 100644 --- a/man/template_laboratory.Rd +++ b/man/template_laboratory.Rd @@ -12,6 +12,7 @@ template_laboratory( timepoints = "ADY", aval = "AVAL", avalu = "AVALU", + patient_id = NULL, round_value = 0L ) } @@ -32,6 +33,8 @@ the laboratory table.} \item{avalu}{(\code{character})\cr name of the analysis value unit variable.} +\item{patient_id}{(\code{character})\cr patient ID.} + \item{round_value}{(\code{numeric})\cr number of decimal places to be used when rounding.} } \description{ diff --git a/man/template_medical_history.Rd b/man/template_medical_history.Rd index 37aeaf8edd..bc6fff058a 100644 --- a/man/template_medical_history.Rd +++ b/man/template_medical_history.Rd @@ -8,7 +8,8 @@ template_medical_history( dataname = "ANL", mhterm = "MHTERM", mhbodsys = "MHBODSYS", - mhdistat = "MHDISTAT" + mhdistat = "MHDISTAT", + patient_id = NULL ) } \arguments{ @@ -20,6 +21,8 @@ analysis data used in teal module.} \item{mhbodsys}{(\code{character})\cr name of the body system or organ class variable.} \item{mhdistat}{(\code{character})\cr name of the status of the disease variable.} + +\item{patient_id}{(\code{character})\cr patient ID.} } \description{ Creates medical history template. diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index a4e5149826..affdcc2199 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -11,6 +11,9 @@ upstream_repos: insightsengineering/tern.gee: repo: insightsengineering/tern.gee host: https://github.com + insightsengineering/rlistings: + repo: insightsengineering/rlistings + host: https://github.com insightsengineering/rtables: repo: insightsengineering/rtables host: https://github.com diff --git a/tests/testthat/_snaps/tm_t_pp_medical_history.md b/tests/testthat/_snaps/tm_t_pp_medical_history.md index ff7f73dbab..7156efd45c 100644 --- a/tests/testthat/_snaps/tm_t_pp_medical_history.md +++ b/tests/testthat/_snaps/tm_t_pp_medical_history.md @@ -16,6 +16,7 @@ rtables::split_rows_by(colnames(result_raw)[2], split_fun = rtables::drop_split_levels, child_labels = "hidden") %>% rtables::analyze_colvars(function(x) x[seq_along(x)]) %>% rtables::build_table(result_raw) + main_title(result) <- paste("Patient ID:", NULL) result }