Skip to content

Commit 773f6bc

Browse files
authored
Merge pull request #24 from Boehringer-Ingelheim/280738-week-labels
280738 week labels
2 parents 92dbc47 + 54dbb3b commit 773f6bc

19 files changed

Lines changed: 224 additions & 42 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010
- ready_for_review
1111
branches:
1212
- main
13+
- test
1314
push:
1415
workflow_dispatch:
1516

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: dv.papo
22
Title: Patient Profile
3-
Version: 2.0.1-9008
3+
Version: 2.0.1-9009
44
Date: 2024-08-13
55
Authors@R:
66
c(person("Boehringer-Ingelheim Pharma GmbH & Co.KG", role = c("cph", "fnd")),
@@ -21,7 +21,7 @@ License: Apache License (>= 2)
2121
Encoding: UTF-8
2222
LazyData: true
2323
Roxygen: list(markdown = TRUE)
24-
RoxygenNote: 7.3.2
24+
RoxygenNote: 7.3.1
2525
Depends:
2626
R (>= 4.1.0)
2727
Imports:

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# dv.papo 2.0.1-9009
2+
- x axis in the plots can now show `weeks` or `days` units
3+
- number of x axis breaks or an specific set of breaks can now be specified
4+
- Update to provide early error feedback if a sender_id is not available in list of modules.
5+
16
# dv.papo 2.0.1-9008
27

38
- Improves SAE label positioning.

R/check_papo_call_manual.R

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,26 @@ check_papo_call <- function(datasets, module_id, subject_level_dataset_name, sub
195195
palette <- plots[["palette"]]
196196
range_plots <- plots[["range_plots"]]
197197
value_plots <- plots[["value_plots"]]
198+
x_axis_unit <- plots[["x_axis_unit"]]
199+
x_axis_breaks <- plots[["x_axis_breaks"]]
200+
201+
assert_err(
202+
checkmate::test_subset(x_axis_unit, choices = as.character(CONST$PLOT_X_AXIS_UNITS), empty.ok = FALSE) ||
203+
is.null(x_axis_unit),
204+
sprintf("`plots$x_axis_unit` must be `NULL` or one of [%s]", paste('"', CONST$PLOT_X_AXIS_UNITS,'"', collapse = ", "))
205+
)
206+
207+
assert_err(
208+
checkmate::test_numeric(x_axis_breaks, min.len = 1, null.ok = TRUE, any.missing = FALSE),
209+
"`plots$x_axis_breaks` must NULL or a numeric vector with no NA values"
210+
)
211+
212+
if(length(x_axis_breaks) == 1) {
213+
assert_err(
214+
checkmate::test_integerish(x_axis_breaks, len = 1, tol = 0, lower = 1, null.ok = TRUE),
215+
"when a single value is passed`plots$x_axis_breaks` must NULL or an integer larger or equal than 1"
216+
)
217+
}
198218

199219
# timeline_info
200220
if (assert_err(

R/create_plots.R

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#'
99
#' @return A ggplot2 object
1010
create_ae_cm_plot <- function(data, x_limits, palette, sl_info, vline_vars, vline_day_numbers,
11-
ref_date) {
11+
x_axis_unit, x_axis_breaks, ref_date) {
1212
# set column for title banner
1313
data[["title_banner"]] <- " "
1414

@@ -104,12 +104,54 @@ create_ae_cm_plot <- function(data, x_limits, palette, sl_info, vline_vars, vlin
104104
)
105105

106106
as_CDISC_days <- function(days) days + (days >= 0)
107-
p <- p + ggplot2::scale_x_continuous(
108-
labels = function(days) {
107+
108+
if (x_axis_unit == CONST$PLOT_X_AXIS_UNITS$DAYS) {
109+
if (length(x_axis_breaks) == 1) {
110+
breaks <- base::pretty(x_limits_z, n = x_axis_breaks)
111+
} else {
112+
breaks <- x_axis_breaks
113+
}
114+
115+
lbl_fn <- function(days) {
109116
dates <- ref_date + days
110117
days <- as_CDISC_days(days)
111118
sprintf("%s\nDay %s", dates, days)
112-
},
119+
}
120+
} else if (x_axis_unit == CONST$PLOT_X_AXIS_UNITS$WEEKS) {
121+
if (length(x_axis_breaks) == 1) {
122+
# Calculates the breaks in weeks and move them back to days, round in case we incurr in a numerical error
123+
breaks <- round(base::pretty(x_limits_z / 7, n = x_axis_breaks) * 7)
124+
# Only return values within limits otherwise the appear in the labels as NA
125+
breaks <- breaks[breaks >= x_limits_z[[1]] & breaks <= x_limits_z[[2]]]
126+
} else {
127+
breaks <- x_axis_breaks * 7
128+
}
129+
130+
lbl_fn <- function(days) {
131+
dates <- ref_date + days
132+
days_z <- days
133+
days <- as_CDISC_days(days)
134+
labels <- vector(mode = "character", length = length(days))
135+
136+
for (idx in seq_along(days)) {
137+
day <- days[[idx]]
138+
day_z <- days_z[[idx]]
139+
date <- dates[[idx]]
140+
if (day_z == 0) {
141+
labels[[idx]] <- sprintf("%s\nDay %s", date, day)
142+
} else {
143+
labels[[idx]] <- sprintf("%s\nWeek %s", date, day_z / 7)
144+
}
145+
}
146+
return(labels)
147+
}
148+
} else {
149+
stop("Unknown x_axis_unit")
150+
}
151+
152+
p <- p + ggplot2::scale_x_continuous(
153+
labels = lbl_fn,
154+
breaks = breaks,
113155
limits = x_limits_z
114156
)
115157

@@ -135,7 +177,7 @@ create_ae_cm_plot <- function(data, x_limits, palette, sl_info, vline_vars, vlin
135177
#'
136178
#' @return A ggplot2 object
137179
create_lb_vs_plot <- function(data, date, val, low_limit, high_limit, param, summary_stats, x_limits,
138-
palette, sl_info, vline_vars, vline_day_numbers, ref_date) {
180+
x_axis_unit, x_axis_breaks, palette, sl_info, vline_vars, vline_day_numbers, ref_date) {
139181
# NOTE(miguel): The following song and dance courtesy of plotly::layout not supporting dates on axes
140182
# column names that end with '_z' are days that represent ref_date as zero (unlike CDISC)
141183
data[["date_z"]] <- as.numeric(as.Date(data[[date]]) - ref_date)
@@ -193,7 +235,6 @@ create_lb_vs_plot <- function(data, date, val, low_limit, high_limit, param, sum
193235
plot <- plot + ggplot2::scale_color_manual(name = "Legend", values = palette)
194236
plot <- plot + ggplot2::scale_fill_manual(name = "Legend", values = palette)
195237

196-
as_CDISC_days <- function(days) days + (days >= 0)
197238

198239
# get facet plots and set formats
199240
plot <- plot + ggplot2::facet_wrap(ggplot2::vars(.data[[param]]), ncol = 1, scales = "free_y") +
@@ -203,16 +244,60 @@ create_lb_vs_plot <- function(data, date, val, low_limit, high_limit, param, sum
203244
axis.text.y = ggplot2::element_text(size = 7), # y-axis text size
204245
strip.text = ggplot2::element_text(size = 6), # title text/banner size
205246
panel.spacing.y = ggplot2::unit(0, "lines") # distance between plots in facet_wrap
206-
) +
207-
ggplot2::xlab("") + ggplot2::ylab("") +
208-
ggplot2::scale_x_continuous(
209-
labels = function(days) {
210-
dates <- ref_date + days
211-
days <- as_CDISC_days(days)
212-
sprintf("%s\nDay %s", dates, days)
213-
},
214-
limits = x_limits_z
215247
)
248+
ggplot2::xlab("") + ggplot2::ylab("")
249+
250+
as_CDISC_days <- function(days) days + (days >= 0)
251+
252+
if (x_axis_unit == CONST$PLOT_X_AXIS_UNITS$DAYS) {
253+
if (length(x_axis_breaks) == 1) {
254+
breaks <- base::pretty(x_limits_z, n = x_axis_breaks)
255+
} else {
256+
breaks <- x_axis_breaks
257+
}
258+
259+
lbl_fn <- function(days) {
260+
dates <- ref_date + days
261+
days <- as_CDISC_days(days)
262+
sprintf("%s\nDay %s", dates, days)
263+
}
264+
} else if (x_axis_unit == CONST$PLOT_X_AXIS_UNITS$WEEKS) {
265+
if (length(x_axis_breaks) == 1) {
266+
# Calculates the breaks in weeks and move them back to days, round in case we incurr in a numerical error
267+
breaks <- round(base::pretty(x_limits_z / 7, n = x_axis_breaks) * 7)
268+
# Only return values within limits otherwise the appear in the labels as NA
269+
breaks <- breaks[breaks >= x_limits_z[[1]] & breaks <= x_limits_z[[2]]]
270+
} else {
271+
breaks <- x_axis_breaks * 7
272+
}
273+
274+
lbl_fn <- function(days) {
275+
dates <- ref_date + days
276+
days_z <- days
277+
days <- as_CDISC_days(days)
278+
labels <- vector(mode = "character", length = length(days))
279+
280+
for (idx in seq_along(days)) {
281+
day <- days[[idx]]
282+
day_z <- days_z[[idx]]
283+
date <- dates[[idx]]
284+
if (day_z == 0) {
285+
labels[[idx]] <- sprintf("%s\nDay %s", date, day)
286+
} else {
287+
labels[[idx]] <- sprintf("%s\nWeek %s", date, day_z / 7)
288+
}
289+
}
290+
return(labels)
291+
}
292+
} else {
293+
stop("Unknown x_axis_unit")
294+
}
295+
296+
p <- p + ggplot2::scale_x_continuous(
297+
labels = lbl_fn,
298+
breaks = breaks,
299+
limits = x_limits_z
300+
)
216301

217302
# HACK: Offset the limits of the y axis to account for label strips
218303
# TODO: Remove during transition away from ggplot2+plotly

R/global_params.r

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@ CONST <- poc(
1212
`MILD` = "lightgreen", `MODERATE` = "gold1", `SEVERE` = "red",
1313
`Mild` = "lightgreen", `Moderate` = "gold1", `Severe` = "red",
1414
`mild` = "lightgreen", `moderate` = "gold1", `severe` = "red"
15-
)
15+
),
16+
PLOT_X_AXIS_UNITS = poc(
17+
DAYS = "days",
18+
WEEKS = "weeks"
19+
),
20+
PLOT_X_AXIS_DEFAULT_NUMBER_OF_BREAKS = 5
1621
)

R/mock_patient_profile.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ mock_patient_profile_server <- function(input, output, session) {
7272
"Concomitant Medication" = list(dataset = "cm", default_vars = NULL)
7373
),
7474
plots = list(
75+
x_axis_unit = "weeks", # Week or Date
76+
x_axis_breaks = 20, # x axis by
7577
timeline_info = c(
7678
trt_start_date = "TRTSDT",
7779
trt_end_date = "TRTEDT",

R/mod_API.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ mod_patient_profile_API_docs <- list(
1717
),
1818
plots = list(
1919
"Plot section",
20+
x_axis_unit = "Defines the units of the x axis in the plots",
21+
x_axis_breaks = "When a single integer is passed it will use `base::pretty` to compute a set of breakpoints. If more than one value is passed it will use those breaks in the x axis",
2022
timeline_info = list(
2123
"Start and end study dates",
2224
icf_date = "Informed Consent Form signing Date",
@@ -72,6 +74,8 @@ mod_patient_profile_API <- T_group(
7274
default_vars = T_col("dataset") |> T_flag("optional", "zero_or_more", "as_array")
7375
) |> T_flag("optional", "zero_or_more", "named"),
7476
plots = T_group(
77+
x_axis_unit = T_character() |> T_flag("optional"),
78+
x_axis_breaks = T_integer(min = 1) |> T_flag("optional", "zero_or_more", "as_array"),
7579
timeline_info = T_group(
7680
icf_date = T_col("subject_level_dataset_name", T_or(T_date(), T_datetime())) |> T_flag("optional"),
7781
trt_start_date = T_col("subject_level_dataset_name", T_or(T_date(), T_datetime())),

R/mod_listing.R

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ patient_listing_UI <- function(id) { # nolint
1212
shiny::tags$head(
1313
shiny::tags$style(
1414
shiny::HTML(
15-
paste(".btn-papo_listing_data_selector_status:active,",
16-
".btn-papo_listing_data_selector_status.active,",
17-
".open>.btn-papo_listing_data_selector_status.dropdown-toggle {
15+
paste(
16+
".btn-papo_listing_data_selector_status:active,",
17+
".btn-papo_listing_data_selector_status.active,",
18+
".open>.btn-papo_listing_data_selector_status.dropdown-toggle {
1819
color: #fff;
1920
background-color: #274AB3;
2021
border-color: #274AB3;
21-
}")
22-
),
22+
}"
23+
)
24+
),
2325
)
2426
),
2527
shiny::uiOutput(ns("ui"))

R/mod_patient_profile.R

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ mod_patient_profile_server <- function(id, subject_level_dataset, extra_datasets
4848
vline_vars <- plots[["vline_vars"]]
4949
vline_day_numbers <- plots[["vline_day_numbers"]]
5050
palette <- plots[["palette"]]
51+
x_axis_unit <- if (!is.null(plots[["x_axis_unit"]])) plots[["x_axis_unit"]] else CONST$PLOT_X_AXIS_UNITS$DAYS
52+
x_axis_breaks <- if (!is.null(plots[["x_axis_breaks"]])) plots[["x_axis_breaks"]] else CONST$PLOT_X_AXIS_DEFAULT_NUMBER_OF_BREAKS
5153

5254
# NOTE: simplifies downstream code because list[[optional_missing_element]] returns NULL
5355
for (i_plot in seq_along(range_plots)) {
@@ -88,9 +90,9 @@ mod_patient_profile_server <- function(id, subject_level_dataset, extra_datasets
8890
return(res)
8991
})
9092

91-
# (#ag4hj): Without these outputOptions the update selector tries to update a selector that is not yet in the UI.
92-
# Therefore the update is lost. In practice this means that when using the receiver_ids the first subjid is lost
93-
# and the interaction is incorrect.
93+
# (ag4hj): Without these outputOptions the update selector (See: ag4hj) tries to update a selector that is not yet
94+
# in the UI. Therefore the update is lost. In practice this means that when using the receiver_ids the first
95+
# subjid is lost and the interaction is incorrect.
9496
shiny::outputOptions(output, "ui", suspendWhenHidden = FALSE)
9597

9698
output[["selector"]] <- shiny::renderUI({
@@ -103,23 +105,25 @@ mod_patient_profile_server <- function(id, subject_level_dataset, extra_datasets
103105
)
104106
})
105107

106-
# See: (#ag4hj)
108+
# See: (ag4hj)
107109
shiny::outputOptions(output, "selector", suspendWhenHidden = FALSE)
108110

109-
# See: (#ag4hj)
111+
# See: (ag4hj)
110112
# change selected patient based on sender_ids
111-
lapply(sender_ids, function(x) {
112-
shiny::observeEvent(x()[["subj_id"]](), {
113-
pid_passed <- x()[["subj_id"]]()
114-
if (!identical(pid_passed, character(0))) {
115-
shiny::updateSelectInput(
116-
session = session,
117-
inputId = "patient_selector",
118-
selected = pid_passed
119-
)
120-
}
113+
if (!is.null(sender_ids)) {
114+
lapply(sender_ids, function(x) {
115+
shiny::observeEvent(x()[["subj_id"]](), {
116+
pid_passed <- x()[["subj_id"]]()
117+
if (!identical(pid_passed, character(0))) {
118+
shiny::updateSelectInput(
119+
session = session,
120+
inputId = "patient_selector",
121+
selected = pid_passed
122+
)
123+
}
124+
})
121125
})
122-
})
126+
}
123127

124128
assert <- function(condition, message) shiny::validate(shiny::need(condition, message))
125129

@@ -201,6 +205,8 @@ mod_patient_profile_server <- function(id, subject_level_dataset, extra_datasets
201205
id = "plot_contents", subjid_var,
202206
subject_level_dataset = filtered_subject_level_dataset,
203207
timeline_info,
208+
x_axis_unit = x_axis_unit,
209+
x_axis_breaks = x_axis_breaks,
204210
extra_datasets = filtered_extra_datasets,
205211
range_plots = range_plots,
206212
value_plots = value_plots,

0 commit comments

Comments
 (0)