Nothing
#-------------------------------------------------------------------------
# This application is a R-Shiny implementation of a calcium and phosphate
# homeostasis model. It aims at being used by medical students but also
# researchers. See https://divadnojnarg.github.io for more informations
#
# David Granjon, the Interface Group, Zurich
# June 12th, 2017
#-------------------------------------------------------------------------
server <- function(input, output, session) {
#-------------------------------------------------------------------------
# useful datas: initialization. These data are not in global.R since
# they are some time reloaded by the program. In global.R they would not
# be reloaded, which would corrupt the new session
#
#-------------------------------------------------------------------------
# all students names for the session
students_names <- paste(rep("Jane Doe", 5), c(1:5))
# load all questions
questions <- generate_questions()
# load patient files
patient_datas <- patient_selector()
# Load state values based on files previously created for each case (php1, hypopara, hypoD3)
patient_state_0 <- patient_datas$initial_conditions
# patient disease
patient_disease <- patient_datas$disease_id
# game answers
if (patient_disease == "php1") {
answer <- c("primary hyperparathyroidism")
} else if (patient_disease == "hypopara") {
answer <- c("hypoparathyroidism")
} else if (patient_disease == "hypoD3") {
answer <- c("vitamin D3 deficiency")
} else if (patient_disease == "hyperD3") {
answer <- c("vitamin D3 intoxication")
}
# disease answer list for students
diseases_list <- c(
"nephrolithiasis",
"primary hyperparathroidism",
"vitamin D3 intoxication",
"hypoparathyroidism",
"ricket",
"oncogenic osteomalacia",
"FGF23 deficiency",
"vitamin D3 deficiency",
"nephrocalcinosis",
"depression",
"nonalcoholic fatty liver disease"
)
# below is needed to handle treatments events
treatment_choices <- c(
#"PTX",
#"D3_inject",
#"Ca_food",
#"Ca_inject",
#"P_food",
#"P_inject",
"D3_intake_reduction",
"cinacalcet",
"bisphosphonate",
"furosemide"
)
# plot summary list
summary_plot_names <- c(
"Ca_p",
"PO4_p",
"PTH_p",
"D3_p",
"FGF_p"
)
# initialization of the patient feedback observer
patient_feedback <- NULL
# # inititalization of the timer
# minutes_time <- 60 # the application will stop in 60 minutes
start_time <- Sys.time()
# end_time <- start_time + minutes_time * 60
# store the app url
app_url <- reactive({
paste0(
session$clientData$url_protocol, "//",
session$clientData$url_hostname, ":",
session$clientData$url_port
)
})
# store the current user folder
user_folder <- reactive({
paste0(
users_logs, "/",
input$register_user, "-", start_time,
"/")
})
#-------------------------------------------------------------------------
# Store times, state and parameters in reactive values that can
# react to user inputs
#
#-------------------------------------------------------------------------
# Basic reactive expressions needed by the solver
times <- reactive({
seq(0, ifelse(parameters()[["t_stop"]] != 0, parameters()[["t_stop"]], input$tmax), by = 1)
})
# initial conditions
states <- reactiveValues(
val = list(),
counter = 1,
name = "start_case"
)
# storing parameters event from the data frame to a reactive list
parameters_event <- reactive({
c(
# static event parameters
"PTX_coeff" = ifelse(isTRUE(events$PTX), 0, 1),
# dynamic event parameters
generate_event_parms(events$current)
)
})
# Create parameters sets for all diseases and treatments
parameters_disease <- reactive({
c("k_prod_PTHg" = ifelse(
patient_disease == "php1", 300*4.192,
ifelse(patient_disease == "hypopara", 0, 4.192)
),
"D3_inact" = ifelse(
patient_disease == "hypoD3", 0,
ifelse(patient_disease == "hyperD3", 5e-004, 2.5e-005)
)
)
})
# make a vector of disease related parameters,
# fixed_parameters and parameters related to events
parameters <- reactive({
c(parameters_disease(), parameters_fixed, parameters_event())
})
#-------------------------------------------------------------------------
# Render Patient boxes: patient_info,
# medical_history, timeline events as well
# as the graph and CaPO4 network box
#
#-------------------------------------------------------------------------
# patient info box
output$patient_info <- renderUI({
medical_history <- patient_datas$medical_history
len <- length(medical_history$pathologies)
f7SocialCard(
author = f7Flex(
patient_datas$name,
uiOutput("user_game_status"),
f7Badge(len, color = "red")#,
#fullScreenUI()
),
author_img = patient_datas$picture,
f7Align(side = "center", h4("Patient Infos")),
# patient details
f7List(
f7ListItem(
title = HTML(paste("Age:", f7Badge(patient_datas$age, color = "lightblue")))
),
f7ListItem(
title = HTML(paste("Height:", f7Badge(patient_datas$height, label = "pink")))
),
f7ListItem(
title = HTML(paste("Weight:", f7Badge(patient_datas$weight, color = "orange")))
)
),
br(),
f7Align(side = "center", h4("Patient Medical History")),
f7Accordion(
lapply(1:len, FUN = function(i){
f7AccordionItem(
title = medical_history$doctors[[i]],
f7Block(
f7Flex(
tags$img(src = medical_history$doctors_avatars[[i]], height = "20px", width = "20px"),
medical_history$doctors[[i]]
),
strong(medical_history$pathologies[[i]]), br(),
HTML(paste(medical_history$disease_description[[i]])),
if (!is.null(medical_history$disease_image[[i]])) {
tagAppendAttributes(
img(src = medical_history$disease_image[[i]]),
style = "display: block;
margin-left: auto;
margin-right: auto;
width: 50%;"
)
}
)
)
})
)
)
})
# the user notebook
output$user_notebook <- renderUI({
if (events$logged) {
comments <- comments$history
len <- nrow(comments)
f7SocialCard(
author_img = "https://image.flaticon.com/icons/svg/305/305983.svg",
author = paste0(input$register_user, "'s notebook."),
date = start_time,
if (events$animation >= 8) {
tagList(
f7Button(
inputId = "diagnosis",
label = "Diagnosis",
color = "blue"
),
br()
)
},
if (events$animation < 8) {
tagList(
questions[[events$animation + 1]],
f7Button(
inputId = "user_add_comment",
label = "Next",
color = "green"
)
)
},
footer = NULL
)
}
})
# Event to be added in the timeLine
output$recent_events <- renderUI({
validate(need(input$current_node_id, "Select one node on the graph!"))
if (events$logged) {
if (events$animation_started) {
len <- nrow(events$history)
name <- events$history$event
start_time <- events$history$real_time
rate <- events$history$rate
plasma_values <- plasma_analysis$history
withMathJax(
f7Card(
title = tagList("Events History", f7Badge(len, color = "red")),
# treatments input are
# in the event box
if (!is.null(events$answered)) {
tagList(
f7checkBoxGroup(
inputId = "treatment_selected",
label = "Select a new treatment:",
choices = c(
#"parathyroid surgery" = "PTX",
#"D3 iv injection" = "D3_inject",
#"Ca supplementation" = "Ca_food",
#"Ca iv injection" = "Ca_inject",
#"Pi iv injection" = "P_inject",
#"Pi supplementation" = "P_food",
"D3 intake reduction" = "D3_intake_reduction",
"Cinacalcet" = "cinacalcet",
"Bisphosphonate" = "bisphosphonate",
"Furosemide" = "furosemide"
)
),
uiOutput(outputId = "sliderInject"),
hr()
)
},
if (len > 0) {
items <- lapply(1:len, FUN = function(i){
item_side <- if (i %% 2 == 0) "left" else "right"
items <- f7TimelineItem(
title = name[[i]],
card = TRUE,
date = f7Badge(
color = "yellow",
start_time[[i]]
),
subtitle = if (name[[i]] %in% c("D3_inject", "Ca_inject", "P_inject")) {
img(src = "treatments_img/syringe.svg", height = "20px", width = "20px")
} else if (name[[i]] %in% c("Ca_food", "P_food", "D3_intake_reduction")) {
img(src = "treatments_img/medicine.svg", height = "20px", width = "20px")
} else if (name[[i]] == "PTX") {
img(src = "treatments_img/surgery.svg", height = "20px", width = "20px")
} else if (name[[i]] %in% c("cinacalcet", "furosemide", "bisphosphonate")) {
img(src = "treatments_img/pills.svg", height = "20px", width = "20px")
} else if (name[[i]] == "plasma analysis") {
img(src = "treatments_img/test-tube.svg", height = "20px", width = "20px")
},
# in case of plasma analysis, display the results next to the logo
if (name[[i]] == "plasma analysis") {
tagList(
paste0("$$[Ca^{2+}_p] = ", round(plasma_values[i, 'Ca_p'], 2), " mM [1.1-1.4 mM]$$"),
paste0("$$[P_i] = ", round(plasma_values[i, "PO4_p"], 2), " mM [0.8-1.6 mM]$$"),
paste0("$$[PTH_p] = ", round(plasma_values[i, "PTH_p"] * 100) * 1.33, " pM [3-16 pM]$$"),
# scale D3
paste0("$$[1,25D3_p] = ", round(plasma_values[i, "D3_p"]) / 4, " pM [36-150 pM]$$"),
# scale FGF23
paste0("$$[FGF23_p] = ", round(plasma_values[i, "FGF_p"] / 25, 2), " pM [0.3-2.1 pM]$$")
)
},
side = item_side
)
})
f7Timeline(
sides = TRUE,
items
)
}
)
)
}
}
})
# graph box
output$graphs_box <- renderUI({
validate(need(input$current_node_id, "Select one node on the graph!"))
if (events$logged) {
if (events$animation_started) {
f7Card(
withSpinner(
plotlyOutput(
"plot_node",
#height = "300px",
width = "100%"
),
size = 2,
type = 8,
color = "#000000"
)
)
}
}
})
# network box
output$network_box <- renderUI({
validate(need(
expr = isTRUE(events$animation_started),
message = "Please click on the next button in the first tab"))
if (events$logged) {
if (events$animation_started) {
f7Card(
title = tagList(
f7Button(
inputId = "run",
size = "large",
label = "Run",
color = "blue"
),
f7Button(
inputId = "summary",
size = "large",
label = "Summary",
color = "purple"
)
),
div(
id = "network_cap",
withSpinner(
visNetworkOutput(
"network_Ca"#,
#height = input$screenSize$height
),
size = 2,
type = 8,
color = "#000000"
)
),
footer = NULL
)
}
}
})
#-------------------------------------------------------------------------
# Javascript alerts: to give instructions to users, handle when the
# game ends
#
#-------------------------------------------------------------------------
# time <- reactiveValues(switcher = FALSE)
#
# # set up a timer during which user have to finish the game
# # and generate the related progress bar
# countdown <- reactive({
# invalidateLater(1000, session)
# countdown <- end_time - Sys.time()
# })
#
# # switch between minutes and seconds when coutdown < 1 minute
# observe({
# if (countdown()<= 1.02) {
# time$switcher <- TRUE
# }
# })
#
# # convert in percentage for the progress bar
# percent_countdown <- reactive({
# countdown <- countdown()
# if (!time$switcher) {
# countdown / minutes_time * 100
# } else {
# countdown / 60 * 100
# }
# })
#
# # render the progress bar for countdown
# output$currentTime <- renderUI({
# if (!events$stop) {
# countdown <- countdown()
# percent_countdown <- percent_countdown()
# statusClass <- if (!time$switcher) {
# if (66 < percent_countdown & percent_countdown <= 100) {
# "success"
# } else if (30 < percent_countdown & percent_countdown <= 66) {
# "warning"
# } else {
# "danger"
# }
# } else {
# "danger"
# }
# progressBar(
# id = "countdown",
# value = percent_countdown,
# status = statusClass,
# striped = TRUE,
# size = "xs",
# title = paste0("End in ", round(countdown), if (!time$switcher) " min" else " sec")
# )
# }
# })
# When the counter is equal to 0, each time the session is opened,
# show the how to start sweetAlert
# I set up a delay of 5 seconds so that the alert is not displayed before
# the page is fully loaded (in case we use preloaders in the dashboardPagePlus
# the preloader lasts around 3s...)
observe({
if (!events$logged) {
shinyjs::delay(
1000,
f7Dialog(
session,
inputId = "register_user",
title = "How to start?",
type = "prompt",
text = tagList(
img(src = "interface_img/start.svg", width = "100px", height = "100px"),
br(),
HTML(
"You will be presented with a patient case-study related
to CaPO4 homeostasis. The goal of this activity is to
<b>establish</b> a diagnosis and <b>treat</b>
the patient correctly:
<ol>
<li> To establish your diagnostic, you can click on any compartment e.g.
click on plasma to conduct blood plasma analyses. </li>
<li> After having established an initial diagnostic you will be
offered multiple treatment options. </li>
</ol>"
),
hr(),
column(
align = "center",
width = 12,
h4("Enter your name below")
)
)#,
#btn_labels = c(NULL, "Confirm"),
#type = "warning",
#html = TRUE
)
)
}
})
# disable the confirm button if the user name is missing
observe({
if (!is.null(input$register_user)) {
shinyjs::toggleState(
selector = "button.swal-button.swal-button--confirm",
condition = input$register_user != ""
)
}
})
# when the user is registered, set logged to TRUE
observeEvent(input$register_user,{
if (input$register_user != "") {
events$logged <- TRUE
}
})
# # shift stop when countdown is 0
# observe({
# if (countdown() <= 0)
# events$stop <- TRUE
# })
#
# # When the timer is 0 the game is over if the user has no diagnosis
# # and treatment
# observe({
# if (is.null(input$close_app)) {
# if (events$stop) {
# confirmSweetAlert(
# inputId = "close_app",
# danger_mode = TRUE,
# session,
# title = "This is the end!",
# text = tagList(
# img(src = "interface_img/finish.svg", width = "100px", height = "100px"),
# br(),
# HTML(
# paste(
# "It seems that the game is finished.
# You can restart or close the game."
# )
# )
# ),
# btn_labels = c("Restart", "Stop"),
# type = "error",
# html = TRUE
# )
# }
# }
# })
#
# # Handle what happens when the user close or restart the app
# observeEvent(input$close_app, {
# if (input$close_app) {
# sendSweetAlert(
# session,
# title = "Stop in 5 seconds...",
# type = "error"
# )
# shinyjs::delay(5000, {
# js$closeWindow()
# stopApp()
# })
# } else {
# session$reload()
# }
# })
# init the directory where user datas will be saved
observeEvent(input$register_user, {
if (input$register_user != "") {
# create the new folder
dir.create(user_folder())
}
})
# # give the user the opportunity to load a previous session
# observeEvent(input$register_user, {
# user_folder <- paste0(getwd(), "/www/users_datas/")
# file_list <- as.vector(list.files(user_folder))
#
# confirmSweetAlert(
# session,
# danger_mode = TRUE,
# inputId = "load_previous_session",
# title = "Want to load an older session?",
# text = tagList(
# column(
# width = 12,
# align = "center",
# prettyRadioButtons(
# inputId = "old_session",
# label = "Choose a saved session:",
# choices = file_list,
# animation = "pulse",
# status = "info"
# )
# )
# ),
# btn_labels = c("Cancel", "Load"),
# type = "warning",
# html = TRUE
# )
# })
#
# # load the previous session
# observeEvent(input$load_previous_session, {
# if (input$load_previous_session) {
# user_folder <- paste0(getwd(), "/www/users_datas/")
# temp_folder <- paste0(user_folder, input$old_session)
# file_list <- list.files(temp_folder)
# lapply(1:length(file_list), FUN = function(i) {
# print(paste0(temp_folder, "/", file_list[[i]]))
# readRDS(file = paste0(temp_folder, "/", file_list[[i]]))
# })
#
# # replace start_time by the value of when the folder was first created
# start_time <- unlist(str_split(input$old_session, "-", n = 2))[[2]]
# }
# })
# handle case when the use press the diagnosis button
observeEvent(input$diagnosis, {
f7Dialog(
session,
inputId = "diagnosis_answer",
type = "prompt",
title = "Diagnosis",
text = "What is the disease of this patient?"
)
})
# treat the diagnosis answer
observeEvent(input$diagnosis_answer, {
user_answer <- input$diagnosis_answer
if (user_answer != "") {
test <- str_detect(answer, regex(paste0("\\b", user_answer, "\\b"), ignore_case = TRUE))
if (test) {
events$answered <- TRUE
f7Dialog(
session,
type = "alert",
title = paste0("Congratulations ", input$register_user, " !"),
text = HTML(
paste0(
"This patient has,", answer,
"It would be better to treat him now. Remember you have
<b>15 minutes</b> to complete this activity."
)
)
)
} else {
events$answered <- FALSE
f7Dialog(
session = session,
type = "alert",
title = "Wasted!",
text = paste0(input$register_user, ", it seems that your answer is wrong!")
)
}
# save the answer status
saveRDS(
object = c(events$answered, user_answer),
file = paste0(user_folder(), "/user_answer.rds")
)
} else {
f7Dialog(
session,
type = "alert",
title = "Missing diagnosis!",
text = paste0(input$register_user, ", it seems that your answer is empty!")
)
}
})
# prevent the user from resubmitting an answer if he correctly guessed
# the patient disease
observe({
if (!is.null(events$answered)) {
if (events$answered) {
shinyjs::disable("diagnosis")
}
}
})
# a label to indicate the user whether the diagnosis is ok or not
# in the header
output$user_game_status <- renderUI({
game_status <- if (!is.null(events$answered)) {
if (events$answered) "green" else "red"
} else {
"orange"
}
game_text <- if (!is.null(events$answered)) {
if (events$answered)
paste0(input$diagnosis_answer, ": successful diagnosis")
else paste0(input$diagnosis_answer, ": unsuccessful diagnosis")
} else {
"No diagnosis yet"
}
div(
style = "margin-top: 7.5px; margin-left: 10px;",
class = "diagnosis-badge",
f7Badge(
game_text,
color = game_status
)
)
})
# Give users the opportunity to save data
output$download_logs <- downloadHandler(
filename = function() paste0(input$register_user, "_logs.rds"),
content = function(file) {
saveRDS(
list(
my_events = events$history,
my_comments = comments$history,
my_answer = c(events$answered, input$diagnosis_answer)
),
file
)
}
)
# clean all empty folders when the application starts
observe({
dir_list <- list.dirs(users_logs)
if (length(dir_list) > 1) {
lapply(2:length(dir_list), FUN = function(i) {
temp_dir <- dir_list[[i]]
temp_file_list <- list.files(temp_dir)
if (length(temp_file_list) == 0) unlink(x = temp_dir, recursive = TRUE)
})
}
})
#-------------------------------------------------------------------------
# Calcium/PTH/D3/FGF3 feedback: give the user some feedback
# regarding the current state of the app
#
#-------------------------------------------------------------------------
# how to use the notebook
observe({
if (!is_empty(input$register_user)) {
shinyjs::delay(
1000,
f7Dialog(
session,
inputId = "diagnosis_intro",
title = "How to use the notebook?",
type = "confirm",
text = tagList(
img(src = "interface_img/notebook.svg", width = "100px", height = "100px"),
br(),
HTML("A serie of questions will help you during
the diagnostic process. Click on <img src='interface_img/next.svg' height='50' width='50'>
to go through the questions. Once you completed all questions,
submit your diagnosis by clicking on
<img src='interface_img/diagnosis.svg' height='70' width='70'>.")
)
)
)
}
})
# Introduction to plasma analysis
observeEvent(input$user_add_comment, {
if (events$animation == 3) {
f7Dialog(
session,
inputId = "plasma_analysis_intro",
title = "How to deal with plasma analysis?",
type = "confirm",
text = tagList(
img(src = "CaPO4_network/plasma.svg", width = "100px", height = "100px"),
br(),
"You can access any plasma concentration by clicking on the",
img(src = "CaPO4_network/plasma.svg", width = "50px", height = "50px"),
" node. Besides, other compartments are available such as",
img(src = "CaPO4_network/parathyroid_gland_human.svg", width = "50px", height = "50px"),
img(src = "CaPO4_network/cells.svg", width = "50px", height = "50px"),
img(src = "CaPO4_network/bone.svg", width = "50px", height = "50px"),
"and", img(src = "CaPO4_network/rapid-bone.svg", width = "50px", height = "50px")
)
)
}
})
# Introduction to treatments
observeEvent(input$diagnosis_answer, {
if (events$animation == 8) {
shinyjs::delay(
1000,
f7Dialog(
session,
type = "confirm",
inputId = "treatments_intro",
title = "How to deal with treatments?",
text = tagList(
img(src = "treatments_img/pills.svg", width = "100px", height = "100px"),
br(),
column(
width = 12,
align = "center",
HTML(
"Now that you have posed your initial diagnostic, you may explore different treatment options.
For each:
<ol>
<li> Select the treatment in the timeline </li>
<li> Specify dosage and duration (if relevant) </li>
<li> Click on <img src='interface_img/add_treatment.svg' height='50' width='50'>
to add the treatment</li>
<li> Click on <img src='interface_img/run.svg' height='50' width='50'></li>
<li> You may visualize changes due to your last intervention in the top right panel </li>
<li> To visualize the entire simulation history, click on
<img src='interface_img/summary.svg' height='70' width='70'></li>
</ol>
You can perform several treatments. Note that interventions cannot
be erased from the timeline (i.e. you cannot go back in time).
But you can always start over and explore a different approach.
"
)
)
)
)
)
# increament by 1 to prevent this alert
# from being displayed each time since
# the button is hidden when equal to 8
events$animation <- events$animation + 1
}
})
# increase the animation counter by 1 each time a new comment
# is added by the user
observeEvent(input$user_add_comment, {
events$animation <- events$animation + 1
})
# say that the animation is started when the user has clicked on next
observeEvent(events$animation , {
if (events$animation == 1) {
events$animation_started <- TRUE
}
})
# # warn the user when Calcium, PTH, vitamin D3 are above their physiological ranges
# observe({
# out <- out()
# # event only triggered if the user is logged in
# if (events$logged) {
#
# # Calcium conditions
# Cap_range <- (out[, "Ca_p"] > 1.1 && out[, "Ca_p"] < 1.3)
# # Pi conditions
# PO4p_range <- (out[, "PO4_p"] > 0.8 && out[, "PO4_p"] < 1.5)
# # PTH conditions
# PTHp_range <- (out[, "PTH_p"] > 8 && out[, "PTH_p"] < 51)
# # D3 conditions
# D3p_range <- (out[, "D3_p"] > 80 && out[, "D3_p"] < 700)
# # FGF23 conditions
# FGFp_range <- (out[, "FGF_p"] > 12 && out[, "FGF_p"] < 21)
#
# if (!Cap_range) {
# patient_feedback <- paste0(
# patient_feedback, p(" [Ca2+]p is out of bounds", class = "text-danger")
# )
# }
# if (!PO4p_range) {
# patient_feedback <- paste0(
# patient_feedback, p(" [Pi]p is out of bounds", class = "text-danger")
# )
# }
# if (!PTHp_range) {
# patient_feedback <- paste0(
# patient_feedback, p(" [PTH]p is out of bounds", class = "text-danger")
# )
# }
# if (!D3p_range) {
# patient_feedback <- paste0(
# patient_feedback, p(" [D3]p is out of bounds", class = "text-danger")
# )
# }
# if (!FGFp_range) {
# patient_feedback <- paste0(
# patient_feedback, p(" [FGF23]p is out of bounds", class = "text-danger")
# )
# }
#
# # send the alert message with all feedbacks
# sendSweetAlert(
# session,
# title = paste0("Oups ", input$register_user, " !"),
# text = HTML(paste0(
# "It seems that: ", patient_feedback,
# "You should do something!")
# ),
# type = "warning",
# html = TRUE
# )
# }
# })
# output$current_calcium <- renderUI({
# Ca_p <- round(out()[, "Ca_p"], 2)
# if (Ca_p > 1.1 && Ca_p < 1.3) {
# p(Ca_p)
# } else if (Ca_p < 1.1) {
# p(class = "text-danger", paste0("$$[Ca]$$"))
# } else {
# p(class = "text-success", Ca_p)
# }
# })
#-------------------------------------------------------------------------
# sidebar User panel: print name and date
#
#-------------------------------------------------------------------------
output$user_panel <- renderUI({
# use invalidate later to simulate a clock
invalidateLater(1000)
f7Icon("person_round_fill", tags$small(paste(input$register_user, Sys.time())))
})
#-------------------------------------------------------------------------
# Handle user comments
#
#-------------------------------------------------------------------------
# create the comment dataframe to store all comments
comments <- reactiveValues(
history = data.frame(
description = NULL,
date = NULL,
stringsAsFactors = FALSE
)
)
# each time the user add a new comment, add it to the table
observeEvent(input$user_add_comment, {
if (!is.null(input$user_comment)) {
temp_comment <- data.frame(
description = input$user_comment,
date = Sys.time(),
stringsAsFactors = FALSE
)
comments$history <- rbind(comments$history, temp_comment)
}
})
#-------------------------------------------------------------------------
# This part handle events, plasma analysis, triggered by the user
# as well as the export function to save the timeline Event
#
#-------------------------------------------------------------------------
# Set events parameters in reactiveValues so as to modify them later
# history stores all events whereas current correspond to the last called
# event in the stack
events <- reactiveValues(
history = data.frame(
id = NULL,
real_time = NULL,
event = NULL,
rate = NULL,
start_time = NULL,
stop_time = NULL,
status = NULL,
stringsAsFactors = FALSE
),
current = data.frame(
id = NULL,
real_time = NULL,
event = NULL,
rate = NULL,
start_time = NULL,
stop_time = NULL,
status = NULL,
stringsAsFactors = FALSE
),
counter = 1,
stop = FALSE,
answered = NULL,
PTX = FALSE,
logged = FALSE,
animation = 0,
animation_started = FALSE
)
# handle plasma analysis history
plasma_analysis <- reactiveValues(history = data.frame(stringsAsFactors = FALSE))
observeEvent(input$current_node_id, {
node_id <- input$current_node_id
if (node_id == 2) {
temp_plasma_analysis <- out()[nrow(out()), -1]
plasma_analysis$history <- rbind(plasma_analysis$history, temp_plasma_analysis)
}
})
observeEvent(input$add_treatment, {
if (!is.null(input$add_treatment)) {
# prevent plasma analysis from being done when PTX was already
# performed before
if (input$treatment_selected == "PTX" && isTRUE(events$PTX)) {
NULL
} else {
temp_plasma_analysis <- out()[nrow(out()), -1]
plasma_analysis$history <- rbind(plasma_analysis$history, temp_plasma_analysis)
}
}
})
# generate the slider corresponding to a given treatment
output$sliderInject <- renderUI({
req(input$treatment_selected)
generate_slider_events(input)
})
# plasma analysis events
observeEvent(input$current_node_id, {
node_id <- input$current_node_id
if (node_id == 2) {
if (nrow(events$history) == 0) {
temp_event <- data.frame(
id = events$counter,
real_time = Sys.time(),
event = "plasma analysis",
rate = "undefined",
start_time = "undefined",
stop_time = "undefined",
status = "active",
stringsAsFactors = FALSE
)
} else {
temp_event <- data.frame(
id = events$counter,
real_time = if (events$history[nrow(events$history), "event"] == "PTX" ||
events$history[nrow(events$history), "event"] == "plasma analysis") {
events$history[nrow(events$history), "real_time"]
# need to wait before the end of the previous event
} else {
# calculate the time difference between the previous event
# end and when the user press the add event button
dt <- difftime(
time1 = Sys.time(),
time2 = events$history[nrow(events$history), "real_time"] +
as.numeric(events$history[nrow(events$history), "stop_time"]),
units = c("mins"),
tz = Sys.timezone(location = TRUE)
)
# if the user press before the previous event is finished
# we consider that the next event happens just after
if (dt <= 0) {
events$history[nrow(events$history), "real_time"] +
as.numeric(events$history[nrow(events$history), "stop_time"])
# otherwise, we consider the elapsed time plus the time
# that takes the event (t_stop)
} else {
Sys.time()
}
},
event = "plasma analysis",
rate = "undefined",
start_time = "undefined",
stop_time = "undefined",
status = "active",
stringsAsFactors = FALSE
)
}
events$history <- rbind(events$history, temp_event)
events$counter <- events$counter + 1
}
})
# Add treatments to the event list
observeEvent(input$add_treatment, {
# the same treatment can be added
# multiple times. However, parathyroidectomy
# cannot be performed more than once
if (input$treatment_selected != "PTX") {
if (nrow(events$history) == 0) {
temp_event <- data.frame(
id = events$counter,
real_time = Sys.time(),
event = input$treatment_selected,
rate = if (!(input$treatment_selected %in%
c("bisphosphonate", "furosemide", "cinacalcet"))) {
input[[paste(input$treatment_selected)]]
} else {
"undefined"
},
start_time = 0,
stop_time = input$t_stop,
status = "active",
stringsAsFactors = FALSE
)
} else {
temp_event <- data.frame(
id = events$counter,
# if PTX was performed before, we do not need to wait
real_time = if (events$history[nrow(events$history), "event"] == "PTX" ||
events$history[nrow(events$history), "event"] == "plasma analysis") {
events$history[nrow(events$history), "real_time"]
# need to wait before the end of the previous event
} else {
# calculate the time difference between the previous event
# end and when the user press the add event button
dt <- difftime(
time1 = Sys.time(),
time2 = events$history[nrow(events$history), "real_time"] +
as.numeric(events$history[nrow(events$history), "stop_time"]),
units = c("mins"),
tz = Sys.timezone(location = TRUE)
)
# if the user press before the previous event is finished
# we consider that the next event happens just after
if (dt <= 0) {
events$history[nrow(events$history), "real_time"] +
as.numeric(events$history[nrow(events$history), "stop_time"]) +
input$t_stop
# otherwise, we consider the elapsed time plus the time
# that takes the event (t_stop)
} else {
Sys.time() + input$t_stop
}
},
event = input$treatment_selected,
rate = if (!(input$treatment_selected %in%
c("bisphosphonate", "furosemide", "cinacalcet"))) {
input[[paste(input$treatment_selected)]]
} else {
"undefined"
},
start_time = 0,
stop_time = input$t_stop,
status = "active",
stringsAsFactors = FALSE
)
}
events$history <- rbind(events$history, temp_event)
events$counter <- events$counter + 1
events$current <- temp_event
} else {
if (!isTRUE(events$PTX)) {
if (nrow(events$history) == 0) {
temp_event <- data.frame(
id = events$counter,
real_time = Sys.time(),
event = input$treatment_selected,
rate = "undefined",
start_time = "undefined",
stop_time = "undefined",
status = "active",
stringsAsFactors = FALSE
)
} else {
temp_event <- data.frame(
id = events$counter,
# if PTX was performed before, we do not need to wait
real_time = if (events$history[nrow(events$history), "event"] == "plasma analysis") {
events$history[nrow(events$history), "real_time"]
# need to wait before the end of the previous event
} else {
# calculate the time difference between the previous event
# end and when the user press the add event button
dt <- difftime(
time1 = Sys.time(),
time2 = events$history[nrow(events$history), "real_time"] +
as.numeric(events$history[nrow(events$history), "stop_time"]),
units = c("mins"),
tz = Sys.timezone(location = TRUE)
)
# if the user press before the previous event is finished
# we consider that the next event happens just after
if (dt < 0) {
events$history[nrow(events$history), "real_time"] +
as.numeric(events$history[nrow(events$history), "stop_time"])
# otherwise, we consider the elapsed time plus the time
# that takes the event (t_stop)
} else {
Sys.time()
}
},
event = input$treatment_selected,
rate = "undefined",
start_time = "undefined",
stop_time = "undefined",
status = "active",
stringsAsFactors = FALSE
)
}
events$history <- rbind(events$history, temp_event)
events$counter <- events$counter + 1
events$PTX <- TRUE
} else {
showNotification(
"Cannot perform parathyroidectomy more than once!",
type = "error",
closeButton = TRUE
)
}
}
})
# flush the stack of current events
# 5 seconds after the user click on run
observeEvent(input$run, {
shinyjs::delay(1000, {
events$current <- data.frame(
id = NULL,
real_time = NULL,
event = NULL,
rate = NULL,
start_time = NULL,
stop_time = NULL,
status = NULL,
stringsAsFactors = FALSE
)
})
})
#-------------------------------------------------------------------------
#
# Integrate equations using deSolve package to generate table
# out is a reactive intermediate component that is called by
# to make plots or other stuffs. We used the compiled version of
# the code, to make computations faster
#
#-------------------------------------------------------------------------
# will be used the save all out elements
out_history <- reactiveValues(
item = list(),
counter = 0,
summary = data.frame()
)
out <- reactive({
input$run
isolate({
parameters <- parameters()
times <- times()
# always solve from the last state
as.data.frame(
ode(
# when opening the application, y will be state_0 since states$val
# is an empty list. However, for the next runs, states$val is
# populated with the last simulated final state and so on
# each time the user press run
y = if (is_empty(states$val)) {
patient_state_0
} else {
states$val[[length(states$val)]]
},
times = times,
func = "derivs",
parms = parameters,
dllname = "compiled_core",
initfunc = "initmod",
nout = 33,
outnames = c(
"U_Ca", "U_PO4", "Abs_int_Ca",
"Abs_int_PO4", "Res_Ca", "Res_PO4",
"Ac_Ca", "Ac_PO4", "Reabs_Ca", "Reabs_PO4",
"Ca_pf", "Ca_fp", "PO4_pf", "PO4_fp",
"PO4_pc", "PO4_cp", "PTHg_synth",
"PTHg_synth_D3", "PTHg_synth_PO4",
"PTHg_exo_CaSR", "PTHg_deg", "PTHg_exo",
"PTHp_deg", "Reabs_PT_PTH",
"Reabs_TAL_CaSR", "Reabs_TAL_PTH",
"Reabs_DCT_PTH", "Reabs_DCT_D3",
"Abs_int_D3", "Res_PTH", "Res_D3",
"Reabs_PT_PO4_PTH", "Reabs_PT_PO4_FGF"
)
)
)
})
})
# update initial conditions to the last state of the system each time an event
# has occured. Need to delayed by the time needed for computation before updating
# which is not really obvious since we don't know exactly what time it will take.
observe({
input$run
shinyjs::delay(1000, {
out <- out()
temp_state <- c(
"PTH_g" = out[nrow(out),"PTH_g"],
"PTH_p" = out[nrow(out),"PTH_p"],
"D3_p" = out[nrow(out),"D3_p"],
"FGF_p" = out[nrow(out),"FGF_p"],
"Ca_p" = out[nrow(out),"Ca_p"],
"Ca_f" = out[nrow(out),"Ca_f"],
"Ca_b" = out[nrow(out),"Ca_b"],
"PO4_p" = out[nrow(out),"PO4_p"],
"PO4_f" = out[nrow(out),"PO4_f"],
"PO4_b" = out[nrow(out),"PO4_b"],
"PO4_c" = out[nrow(out),"PO4_c"],
"CaHPO4_p" = out[nrow(out),"CaHPO4_p"],
"CaH2PO4_p" = out[nrow(out),"CaH2PO4_p"],
"CPP_p" = out[nrow(out),"CPP_p"],
"CaHPO4_f" = out[nrow(out),"CaHPO4_f"],
"CaH2PO4_f" = out[nrow(out),"CaH2PO4_f"],
"CaProt_p" = out[nrow(out),"CaProt_p"],
"NaPO4_p" = out[nrow(out),"NaPO4_p"],
"Ca_tot" = out[nrow(out),"Ca_tot"],
"PO4_tot" = out[nrow(out),"PO4_tot"],
"EGTA_p" = out[nrow(out),"EGTA_p"],
"CaEGTA_p" = out[nrow(out),"CaEGTA_p"]
)
states$counter <- states$counter + 1
states$val[[states$counter]] <- temp_state
states$name <- input$treatment_selected
})
})
# when the user clicks on summary rerun the simulation with all events
observeEvent(input$summary, {
showModal(
modalDialog(
title = fluidRow(
column(
width = 9,
align = "left",
p(style = "text-align: center;", "Overview of your patient")
),
column(
width = 3,
align = "right",
tags$button(
type = "button",
class = "btn btn-default float-right",
`data-dismiss` = "modal",
icon("close"),
"Dismiss"
)
)
),
f7Swiper(
id = "tabset1",
lapply(1:length(summary_plot_names), FUN = function(i) {
name <- summary_plot_names[[i]]
f7Slide(
withSpinner(
plotlyOutput(paste0("plot_summary_", name)),
size = 2,
type = 8,
color = "#000000"
)
)
})
),
size = "m",
footer = NULL
)
)
})
# out_summary <- eventReactive(input$summary, {
# if (nrow(events$history) >= 2) {
# times <- as.list(events$history[, "real_time"])
# len <- length(times)
# delta_t <- lapply(2:len, FUN = function(i) {
# difftime(
# time1 = times[[i]],
# time2 = times[[i - 1]],
# units = c("secs"),
# tz = Sys.timezone(location = TRUE)
# )
# })
#
#
#
# }
#
# })
# cumulative datas
datas_summary <- reactive({
datas <- out_history$summary %>%
filter(time %% 50 == 0) %>%
accumulate_by(~time)
# add bounds for each variable
low_norm_Ca_p <- data.frame(low_norm_Ca_p = rep(1.1, length(datas[, "time"])))
high_norm_Ca_p <- data.frame(high_norm_Ca_p = rep(1.3, length(datas[, "time"])))
low_norm_PO4_p <- data.frame(low_norm_PO4_p = rep(0.8, length(datas[, "time"])))
high_norm_PO4_p <- data.frame(high_norm_PO4_p = rep(1.5, length(datas[, "time"])))
low_norm_PTH_p <- data.frame(low_norm_PTH_p = rep(1.5, length(datas[, "time"])))
high_norm_PTH_p <- data.frame(high_norm_PTH_p = rep(7, length(datas[, "time"])))
low_norm_D3_p <- data.frame(low_norm_D3_p = rep(50, length(datas[, "time"])))
high_norm_D3_p <- data.frame(high_norm_D3_p = rep(180, length(datas[, "time"])))
low_norm_FGF_p <- data.frame(low_norm_FGF_p = rep(8, length(datas[, "time"])))
high_norm_FGF_p <- data.frame(high_norm_FGF_p = rep(51, length(datas[, "time"])))
# bind all values
datas <- cbind(
datas,
low_norm_Ca_p,
high_norm_Ca_p,
low_norm_PO4_p,
high_norm_PO4_p,
low_norm_PTH_p,
high_norm_PTH_p,
low_norm_D3_p,
high_norm_D3_p,
low_norm_FGF_p,
high_norm_FGF_p
)
})
# cumulative plot (5 plots)
lapply(1:length(summary_plot_names), FUN = function(i) {
name <- summary_plot_names[[i]]
output[[paste0("plot_summary_", name)]] <- renderPlotly({
if (nrow(out_history$summary) >= 1) {
plot_ly(
datas_summary(),
x = datas_summary()[, "time"],
y = if (name == "PTH_p") {
datas_summary()[, name] * 100
} else if (name == "D3_p") {
datas_summary()[, name] / 4
} else if (name == "FGF_p") {
datas_summary()[, name] / 16.8 * 32
} else {
datas_summary()[, name]
},
name = if (name %in% c("Ca_p", "PO4_p")) {
paste0(name, " (mM)")
} else if (name == "FGF_p") {
paste0(name, " (pg/mL)")
} else {
paste0(name, " (pM)")
},
frame = ~frame,
type = 'scatter',
mode = 'lines',
line = list(
simplify = FALSE,
color = if (name == "Ca_p") {
'rgb(27, 102, 244)'
} else if (name == "PO4_p") {
'rgb(244, 27, 27)'
} else {
'black'
}
)
) %>%
add_lines(
y = datas_summary()[, paste0("low_norm_", name)],
frame = ~frame,
name = if (name %in% c("Ca_p", "PO4_p")) {
paste0("Low ", name, " bound (mM)")
} else {
paste0("Low ", name, " bound (pM)")
},
line = list(
color = 'rgb(169,169,169)',
width = 4,
dash = 'dash'
)
) %>%
add_lines(
y = datas_summary()[, paste0("high_norm_", name)],
frame = ~frame,
name = if (name %in% c("Ca_p", "PO4_p")) {
paste0("High ", name, " bound (mM)")
} else {
paste0("High ", name, " bound (pM)")
},
line = list(
color = 'rgb(169,169,169)',
width = 4,
dash = 'dot'
)
) %>%
layout(
xaxis = list(
title = "time (min)",
zeroline = FALSE
),
yaxis = list(
title = if (name %in% c("Ca_p", "PO4_p")) {
paste0(name, " (mM)")
} else {
paste0(name, " (pM)")
},
zeroline = FALSE
),
showlegend = TRUE
) %>%
animation_opts(
# animation speed (the lower, the faster)
frame = 5,
transition = 0,
redraw = FALSE
) %>%
animation_slider(
hide = FALSE
) %>%
config(displayModeBar = FALSE)
}
})
})
# each time the user click on run, the history is saved
observeEvent(input$run, {
out <- out()
len <- length(out_history$item)
if (len >= 1) {
# translate all time by the number of time points
# in the previous run + 1
out_history$counter <- out_history$counter + nrow(out_history$item[[len]])
out[, "time"] <- out[, "time"] + out_history$counter
}
out_history$item[[len + 1]] <- out
# merge all dataframe into a big one
out_history$summary <- bind_rows(out_history$item)
})
#-------------------------------------------------------------------------
#
# The network part: make interactive diagramms of Ca and PO4 homeostasis
# as well as regulation by hormones such as PTH, vitamin D3 and FGF23
#
#-------------------------------------------------------------------------
# Generate the CaP Graph network
nodes_Ca <- reactive({generate_nodes_Ca(input)})
edges_Ca <- reactive({generate_edges_Ca(input)})
# Generate the output of the Ca graph to be used in body
output$network_Ca <- renderVisNetwork({
nodes_Ca <- nodes_Ca()
edges_Ca <- edges_Ca()
input$network_hormonal_choice
generate_network(
input,
nodes = nodes_Ca,
edges = edges_Ca,
usephysics = TRUE
) %>%
# simple click event to allow graph ploting
visEvents(
selectNode = "
function(nodes) {
Shiny.onInputChange('current_node_id', nodes.nodes);
}"
) %>%
# unselect node event
visEvents(
deselectNode = "
function(nodes) {
Shiny.onInputChange('current_node_id', 'null');
}"
) %>%
# add the doubleclick function to handle zoom views
visEvents(
doubleClick = "
function(nodes) {
Shiny.onInputChange('current_node_bis_id', nodes.nodes);
}"
) %>%
visEvents(
selectEdge = "
function(edges) {
Shiny.onInputChange('current_edge_id', edges.edges);
}"
) %>%
visEvents(
deselectEdge = "
function(edges) {
Shiny.onInputChange('current_edge_id', 'null');
}"
) %>%
# very important: change the whole graph position after drawing
visEvents(
type = "on",
stabilized = "
function() {
this.moveTo({
position: {x:0, y:-13.43},
offset: {x: 0, y:0}
});
}"
) %>%
# very important: allow to detect the web browser used by client
# use before drawing the network. Works with find_navigator.js
visEvents(
type = "on",
initRedraw = paste0("
function() {
this.moveTo({scale:", 0.8 * input$screenSize$width / 1000, "});
}")
) # to set the initial zoom (1 by default)
})
observe(print(input$screenSize))
observeEvent(input$screenSize$height, {print( 0.8 * input$screenSize$width / 1000)})
# Events for the CaPO4 Homeostasis diagramm whenever a flux change
# Change arrow color relatively to the value of fluxes for Ca injection/PO4
# injection as well as PO4 gavage
observe({
out <- out()
edges_Ca <- edges_Ca()
arrow_lighting_live(
out,
edges = edges_Ca,
session,
t_target = input$t_now
)
})
# change the selected node size to better highlight it
last <- reactiveValues(selected_node = NULL, selected_edge = NULL)
observeEvent(input$current_node_id, {
req(input$current_node_id)
selected_node <- input$current_node_id
nodes_Ca <- nodes_Ca()
# javascript return null instead of NULL
# cannot use is.null
if (!identical(selected_node, "null")) {
last$selected_node <- selected_node
# organ nodes
if (selected_node %in% c(1:5, 7:8, 11)) {
nodes_Ca$size[selected_node] <- 100
# Kidney zoom node
} else if (selected_node == 6) {
nodes_Ca$size[selected_node] <- 214
# regulation nodes
} else {
nodes_Ca$size[selected_node] <- 57
}
visNetworkProxy("network_Ca") %>%
visUpdateNodes(nodes = nodes_Ca)
# reset the node size when unselected
} else {
if (last$selected_node %in% c(1:5, 7:8, 11)) {
nodes_Ca$size[last$selected_node] <- 70
} else if (last$selected_node == 6) {
nodes_Ca$size[last$selected_node] <- 150
} else {
nodes_Ca$size[last$selected_node] <- 40
}
visNetworkProxy("network_Ca") %>%
visUpdateNodes(nodes = nodes_Ca)
}
})
# change the selected edge size to
# better highlight it
observeEvent(input$current_edge_id,{
req(input$current_edge_id)
selected_edge <- input$current_edge_id
edges_Ca <- edges_Ca()
edge_id <- match(selected_edge, edges_Ca$id)
if (!identical(selected_edge, "null")) {
last$selected_edge <- edge_id
# organs edges
if (edge_id %in% c(1:12)) {
edges_Ca$width[edge_id] <- 24
# regulations edges
} else {
edges_Ca$width[edge_id] <- 12
}
visNetworkProxy("network_Ca") %>%
visUpdateEdges(edges = edges_Ca)
# reset the edge size when unselected
} else {
if (edge_id %in% c(1:12)) {
edges_Ca$width[edge_id] <- 8
} else {
edges_Ca$width[edge_id] <- 4
}
visNetworkProxy("network_Ca") %>%
visUpdateEdges(edges = edges_Ca)
}
})
#-------------------------------------------------------------------------
#
# The graph part: calls out(), parameters_bis()
# Interactive graph as a result of click on the diagram
#
#-------------------------------------------------------------------------
# Generate a graph when node is clicked.
# The graph corresponds to the node clicked
output$plot_node <- renderPlotly({
validate(need(input$current_node_id, "Select one node on the graph!"))
out <- out()
plot_node(input, node = input$current_node_id , out, parameters_fixed)
})
output$plot_edge <- renderPlotly({
validate(need(input$current_edge_id, "Select one edge on the graph!"))
out <- out()
plot_edge(edge = input$current_edge_id , out)
})
#-------------------------------------------------------------------------
#
# Handle dangerous parameter values by the user
#
#-------------------------------------------------------------------------
# prevent the user to put infinite value in the max time of integration
# With compiled code, tmax = 100000 min is a correct value
observeEvent(input$tmax,{
# check if input tmax does not exists or is not numeric
if (is.na(input$tmax)) {
f7Toast(
session,
position = "bottom",
text = "Invalid value: tmax should be set correctly."
)
reset("tmax") # value is reset
} else {
# if yes, check it is negative
if (input$tmax <= 0) {
f7Toast(
session,
text = "Invalid value: tmax must be higher than 0.",
position = "bottom"
)
reset("tmax") # value is reset
# check whether it is too high
} else if (input$tmax > 100000) {
f7Toast(
session,
text = "Invalid value: the maximum
time of simulation is too high!",
position = "bottom"
)
reset("tmax") # value is reset
}
}
})
#-------------------------------------------------------------------------
#
# Useful tasks such as save, reset, load ...
#
#-------------------------------------------------------------------------
# reset parameters individually
button_states <- reactiveValues(values = list())
observeEvent(input$reset_t_now,{
# call the function to reset the given slider
sliders_reset(button_states, input)
})
# disable the summary button as long as input$run is lower than 1
observe({
if (!is.null(input$run)) {
toggleState(id = "summary", condition = input$run >= 1)
}
})
# make the run button blinking when a new event is added
# but remove it when run is pressed
observeEvent(input$add_treatment, {
addClass(id = "run", class = "run_glowing_blue")
})
observeEvent(input$run, {
removeClass(id = "run", class = "run_glowing_blue")
})
# make the Summary button blinking when run was pressed at least once
observeEvent(input$run, {
addClass(id = "summary", class = "run_glowing_purple")
})
observeEvent(input$summary, {
removeClass(id = "summary", class = "run_glowing_purple")
})
# make the run button glowing when not clicked
observeEvent(input$diagnosis_intro, {
addClass(id = "user_add_comment", class = "run_glowing_green")
})
observeEvent(input$add_user_comment, {
removeClass(id = "user_add_comment", class = "run_glowing_green")
})
# make diagnosis blinking when there remains 5 min
# before the app close, only if it exists (if the user
# never clicked on next, diagmosis does not exist!!!)
# observe({
# if (countdown() <= 5) {
# if (!is_empty(input$diagnosis)) {
# if (input$diagnosis == 0) {
# addClass(id = "diagnosis", class = "run_glowing_blue")
# }
# }
# }
# })
observe({
if (!is_empty(input$diagnosis)) {
if (input$diagnosis > 0) {
removeClass(id = "diagnosis", class = "run_glowing_blue")
}
}
})
# prevent user from selecting multiple treatments as the same time
observe({
if (!is.null(input$treatment_selected)) {
treatment <- match.arg(input$treatment_selected, treatment_choices)
idx <- match(input$treatment_selected, treatment_choices)
other_treatments <- treatment_choices[-idx]
lapply(seq_along(other_treatments), FUN = function(j) {
disable(selector = paste0("#treatment_selected input[value='", other_treatments[[j]], "']"))
})
} else {
enable(id = "treatment_selected")
}
})
# display or not display the network background
observe({
# add invalidate later so that the background class is
# applied after the application startup
invalidateLater(1000, session)
if (!is_empty(input$background_choice)) {
if (input$background_choice == "rat") {
addClass(id = "network_cap", class = "network_caprat")
removeClass(id = "network_cap", class = "network_caphuman")
} else {
removeClass(id = "network_cap", class = "network_caprat")
addClass(id = "network_cap", class = "network_caphuman")
}
} else {
addClass(id = "network_cap", class = "network_capnone")
removeClass(id = "network_cap", class = "network_caphuman")
removeClass(id = "network_cap", class = "network_caprat")
}
})
# prevent user from selecting multiple background
observe({
if (is.element("rat", input$background_choice) &&
!is.element("human", input$background_choice)) {
disable(selector = "#background_choice input[value='human']")
} else {
enable(selector = "#background_choice input[value='human']")
}
if (is.element("human", input$background_choice) &&
!is.element("rat", input$background_choice)) {
disable(selector = "#background_choice input[value='rat']")
} else {
enable(selector = "#background_choice input[value='rat']")
}
})
# when enable regulation is selected, activates all the checkboxes
# the reverse case does not work for unknow reason
observeEvent(input$network_hormonal_choice, {
if (input$network_hormonal_choice == TRUE) {
updatePrettyCheckboxGroup(
session,
inputId = "network_Ca_choice",
selected = c("Ca","PO4", "PTH", "D3", "FGF23")
)
}
})
# delete compiled files right after session is closed...
session$onSessionEnded(function() {
if (.Platform$OS.type == "unix") {
file.remove("compiled_core.o")
file.remove("compiled_core.so")
} else if (.Platform$OS.type == "windows") {
file.remove("compiled_core.dll")
}
})
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.