1 # Main logic block for data-related interactions.
2 data_logic
<- function(input
, output
, react_values
) {
3 # Initialize a data frame to hold the datasets.
4 react_values$data_table
<- data.frame(
6 `Time units`
= character(0),
7 `Case counts`
= character(0),
11 render_plot(input
, output
)
12 single_entry(input
, output
, react_values
)
13 bulk_entry(input
, output
, react_values
)
14 render_data(output
, react_values
)
15 delete_data(input
, react_values
)
16 export_data(output
, react_values
)
19 # Convert the input case counts string to an integer vector.
20 tokenize_counts
<- function(counts_str
) {
21 suppressWarnings(as.integer(unlist(strsplit(trimws(counts_str
), ","))))
24 # Render the preview plot for single entry data.
25 render_plot
<- function(input
, output
) {
27 counts
<- tokenize_counts(input$data_counts
)
28 if (length(counts
) > 0 && !anyNA(counts
) && all(counts
>= 0)) {
29 output$data_plot
<- renderPlot(
30 plot(seq_along(counts
) - 1, counts
, type
= "o", pch
= 16, col
= "black",
31 xlab
= input$data_units
, ylab
= "Cases", cex.lab
= 1.5,
32 xlim
= c(0, max(length(counts
) - 1, 1)), ylim
= c(0, max(counts
, 1))
36 output$data_plot
<- renderPlot(
37 plot(NULL, xlim
= c(0, 10), ylim
= c(0, 10),
38 xlab
= input$data_units
, ylab
= "Cases", cex.lab
= 1.5
45 # Add a single dataset to the existing table.
46 single_entry
<- function(input
, output
, react_values
) {
47 observeEvent(input$data_single
, {
50 # Ensure the dataset name is neither blank nor a duplicate.
51 name
<- trimws(input$data_name
)
53 output$data_name_warn
<- renderText("The dataset name cannot be blank.")
55 } else if (name
%in% react_values$data_table
[, 1]) {
56 output$data_name_warn
<- renderText(
57 "There is already a dataset with the specified name."
61 output$data_name_warn
<- renderText("")
64 # Ensure the case counts are specified as a comma-separated of one or more
65 # non-negative integers.
66 counts
<- tokenize_counts(input$data_counts
)
67 if (length(counts
) == 0) {
68 output$data_counts_warn
<- renderText("Case counts cannot be blank.")
70 } else if (anyNA(counts
) || any(counts
< 0)) {
71 output$data_counts_warn
<- renderText(
72 "Case counts can only contain non-negative integers."
76 output$data_counts_warn
<- renderText("")
80 # Add the new dataset to the data table.
81 new_row
<- data.frame(name
, input$data_units
, toString(counts
))
82 colnames(new_row
) <- c("Name", "Time units", "Case counts")
83 react_values$data_table
<- rbind(react_values$data_table
, new_row
)
85 # Evaluate all existing estimators on the new dataset and update the
86 # corresponding row in the estimates table.
87 update_estimates_rows(new_row
, react_values
)
89 showNotification("Dataset added successfully.",
90 duration
= 3, id
= "notify-success"
96 # Add multiple datasets to the existing table.
97 bulk_entry
<- function(input
, output
, react_values
) {
98 observeEvent(input$data_bulk
, {
101 datasets
<- read.csv(text
= input$data_area
, header
= FALSE, sep
= ",")
103 names
<- trimws(datasets
[, 1])
104 units
<- trimws(datasets
[, 2])
105 counts
<- apply(datasets
[, 3:ncol(datasets
)], 1,
107 row
<- suppressWarnings(as.integer(row
))
108 toString(row
[!is.na(row
) & row
>= 0])
114 # Ensure the dataset names are neither blank nor duplicates.
115 if (anyNA(names
) || any(names
== "")) {
116 warning_text
<- paste0(warning_text
, sep
= "<br>",
117 "Each row must begin with a non-blank dataset name."
120 if (length(unique(names
)) != length(names
)) {
121 warning_text
<- paste0(warning_text
, sep
= "<br>",
122 "The rows contain duplicate dataset names."
125 if (any(names
%in% react_values$data_table
[, 1])) {
126 warning_text
<- paste0(warning_text
, sep
= "<br>",
127 "The rows contain dataset names which already exist."
132 # Ensure the second entry in each row is a time unit equal to
134 if (!all(units
%in% c("Days", "Weeks"))) {
135 warning_text
<- paste0(warning_text
, sep
= "<br>",
136 "The second entry in each row must be either 'Days' or 'Weeks'."
140 # Ensure the counts in each row have at least one non-negative integer.
141 if (any(counts
== "")) {
142 warning_text
<- paste0(warning_text
, sep
= "<br>",
143 "Each row must contain at least one non-negative integer."
147 output$data_area_warn
<- renderUI(HTML(warning_text
))
149 if (warning_text
== "") {
150 # Add the new datasets to the data table.
151 new_rows
<- data.frame(names
, units
, counts
)
152 colnames(new_rows
) <- c("Name", "Time units", "Case counts")
153 react_values$data_table
<- rbind(react_values$data_table
, new_rows
)
155 # Evaluate all existing estimators on the new dataset and update the
156 # corresponding row in the estimates table.
157 update_estimates_rows(new_rows
, react_values
)
159 showNotification("Datasets added successfully.",
160 duration
= 3, id
= "notify-success"
164 error
= function(e
) {
165 output$data_area_warn
<- renderText(
166 "The input does not match the required format."
173 # Render the data table when new datasets are added.
174 render_data
<- function(output
, react_values
) {
176 output$data_table
<- DT
::renderDataTable(react_values$data_table
)
180 # Delete rows in the data table,
181 # and the corresponding rows in the estimates table.
182 delete_data
<- function(input
, react_values
) {
183 observeEvent(input$data_delete
, {
184 new_table
<- react_values$data_table
[-input$data_table_rows_selected
, ]
185 if (nrow(new_table
) > 0) {
186 rownames(new_table
) <- seq_len(nrow(new_table
))
188 react_values$data_table
<- new_table
190 if (ncol(react_values$estimates_table
) == 1) {
191 react_values$estimates_table
<- data.frame(
192 Datasets
= react_values$data_table
[, 1]
195 react_values$estimates_table
<-
196 react_values$estimates_table
[-input$data_table_rows_selected
, ]
201 # Export data table as a CSV file.
202 export_data
<- function(output
, react_values
) {
203 output$data_export
<- downloadHandler(
204 filename
= function() {
205 paste0("Rnaught_data_", format(Sys.time(), "%y-%m-%d_%H-%M-%S"), ".csv")
207 content
= function(file
) {
208 write.csv(react_values$data_table
, file
, row.names
= FALSE)
213 # When new datasets are added, evaluate all existing estimators on them and
214 # add new rows to the estimates table.
215 update_estimates_rows
<- function(datasets
, react_values
) {
216 new_rows
<- data.frame(
217 matrix(nrow
= nrow(datasets
), ncol
= ncol(react_values$estimates_table
))
219 colnames(new_rows
) <- colnames(react_values$estimates_table
)
221 for (row
in seq_len(nrow(datasets
))) {
222 new_rows
[row
, 1] <- datasets
[row
, 1]
224 if (length(react_values$estimators
) > 0) {
225 for (col
in 2:ncol(react_values$estimates_table
)) {
226 new_rows
[row
, col
] <- eval_estimator(
227 react_values$estimators
[[col
- 1]], datasets
[row
, ]
233 react_values$estimates_table
<- rbind(
234 react_values$estimates_table
, new_rows