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
}