Commit 371821e1 authored by kapsner's avatar kapsner
Browse files

initial commit; development of new shiny dqa tool

parents
No related merge requests found
Showing with 626 additions and 0 deletions
+626 -0
.gitignore 0 → 100644
/*
/*/
*.log
!/.gitignore
!/README.md
!/FAQ.md
!/docker/
/docker/addfolder/
!/DQA_Tool/
!/DQA_Tool/app/
/DQA_Tool/app/_settings/
.Rproj.user
*.Rproj
.RData
.Rhistory
\ No newline at end of file
library(shiny)
library(shinyjs)
library(shinydashboard)
library(shinyFiles)
library(DT)
library(data.table)
library(ggplot2)
library(config)
library(jsonlite)
library(RPostgres)
# app entrypoint here
shinyAppDir("app")
\ No newline at end of file
{
"dt_patient.db": ["SELECT\n\tpatient_num AS patient_id, \n birth_date::date AS birth_date, \n sex_cd AS gender, \n zip_cd AS zip_code\nFROM\n\ti2b2miracum.patient_dimension\nORDER BY \n\tpatient_num;"],
"dt_visit.db": ["SELECT\n\tpatient_num AS patient_id,\n encounter_num AS encounter_id, \n start_date::date AS encounter_start_date, \n end_date::date AS encounter_end_date\nFROM\n i2b2miracum.visit_dimension\nORDER BY \n patient_num;"],
"dt_aitaa.db": ["SELECT \n encounter_num AS encounter_id, \n nval_num AS age\nFROM \n i2b2miracum.observation_fact\nWHERE \n concept_cd LIKE 'FALL:AITAA'\nGROUP BY\n\tencounter_num, patient_num, concept_cd, nval_num\nORDER BY\n encounter_num;"],
"dt_aijaa.db": ["SELECT \n encounter_num AS encounter_id, \n nval_num AS age\nFROM \n i2b2miracum.observation_fact\nWHERE \n concept_cd LIKE 'FALL:AIJAA'\nGROUP BY\n\tencounter_num, patient_num, concept_cd, nval_num\nORDER BY\n encounter_num;"],
"dt_aufnan.db": ["SELECT\n\tencounter_num AS encounter_id, \n concept_cd AS admission_occasion\nFROM \n i2b2miracum.observation_fact\nWHERE \n concept_cd LIKE 'AUFNAN%'\nORDER BY \n encounter_num;"],
"dt_aufngr.db": ["SELECT \n encounter_num AS encounter_id, \n concept_cd AS admission_reason\nFROM \n i2b2miracum.observation_fact\nWHERE \n concept_cd LIKE 'AUFNGR%'\nORDER BY \n encounter_num;"],
"dt_entlgr.db": ["SELECT \n encounter_num AS encounter_id, \n concept_cd AS discharge_reason\nFROM \n i2b2miracum.observation_fact\nWHERE \n concept_cd LIKE 'ENTLGR%'\nORDER BY \n encounter_num;"],
"dt_beatmst.db": ["SELECT \n encounter_num AS encounter_id, \n nval_num AS ventilation_hours\nFROM \n\ti2b2miracum.observation_fact \nWHERE \n\tconcept_cd LIKE 'FALL:BEATMST'\nORDER BY \n\tencounter_num;"],
"dt_icd.db": ["SELECT \n encounter_num AS encounter_id, \n concept_cd AS icd_code,\n\tmodifier_cd AS diagnosis_type\nFROM \n i2b2miracum.observation_fact\nWHERE \n concept_cd LIKE 'ICD%'\nORDER BY\n encounter_num;"],
"dt_ops.db": ["SELECT \n encounter_num AS encounter_id, \n concept_cd AS ops_code,\n start_date::date AS ops_date\nFROM \n i2b2miracum.observation_fact\nWHERE \n concept_cd LIKE 'OPS%'\nORDER BY\n encounter_num;"],
"dt_fab.db": ["SELECT \n encounter_num AS encounter_id, \n tval_char AS department,\n start_date::date AS department_start_date, \n end_date::date AS department_end_date\nFROM \n\ti2b2miracum.observation_fact\nWHERE \n\tconcept_cd LIKE 'FACHABT%'\nORDER BY\n\tencounter_num;"],
"dt_pl_c5x.db": ["SELECT \n ob.encounter_num AS encounter_id, \n ob.patient_num AS patient_id,\n ob.concept_cd AS icd_code,\n pa.sex_cd AS gender,\n regexp_matches(ob.concept_cd, 'ICD10:C5[1-8]', 'g') AS regex\nFROM \n i2b2miracum.observation_fact AS ob\nLEFT OUTER JOIN \n i2b2miracum.patient_dimension AS pa ON ob.patient_num = pa.patient_num\nORDER BY\n ob.encounter_num;"],
"dt_pl_c6x.db": ["SELECT \n ob.encounter_num AS encounter_id, \n ob.patient_num AS patient_id,\n ob.concept_cd AS icd_code,\n pa.sex_cd AS gender,\n regexp_matches(ob.concept_cd, 'ICD10:C6[0-3]', 'g') AS regex\nFROM \n i2b2miracum.observation_fact AS ob\nLEFT OUTER JOIN \n i2b2miracum.patient_dimension AS pa ON ob.patient_num = pa.patient_num\nORDER BY\n ob.encounter_num;"],
"dt_pl_05xx.db": ["SELECT \n ob.encounter_num AS encounter_id, \n ob.patient_num AS patient_id,\n ob.concept_cd AS admission_reason,\n pa.sex_cd AS gender,\n regexp_matches(ob.concept_cd, 'AUFNGR:05', 'g') AS regex\nFROM \n i2b2miracum.observation_fact AS ob\nLEFT OUTER JOIN \n i2b2miracum.patient_dimension AS pa ON ob.patient_num = pa.patient_num\nORDER BY\n ob.encounter_num;"],
"dt_pl_o0099.db": ["SELECT \n ob.encounter_num AS encounter_id, \n ob.patient_num AS patient_id,\n ob.concept_cd AS icd_code,\n pa.sex_cd AS gender,\n regexp_matches(ob.concept_cd, 'ICD10:O[0-9]', 'g') AS regex\nFROM \n i2b2miracum.observation_fact AS ob\nLEFT OUTER JOIN \n i2b2miracum.patient_dimension AS pa ON ob.patient_num = pa.patient_num\nORDER BY\n ob.encounter_num;"]
}
\ No newline at end of file
{
"dt_patient.db": ["SELECT\n per.person_id AS patient_id, \n per.year_of_birth AS birth_date,\n per.month_of_birth AS birth_month,\n con.concept_code AS gender, \n loc.zip AS zip_code\nFROM\n\tp21_cdm.person AS per\nLEFT OUTER JOIN \n\tp21_cdm.location AS loc ON \n\tper.location_id = loc.location_id\nLEFT OUTER JOIN \n\tp21_cdm.concept AS con ON \n\tper.gender_concept_id = con.concept_id\nORDER BY \n\tper.person_id;"],
"dt_visit.db": ["SELECT\n person_id AS patient_id, \n visit_occurrence_id AS encounter_id, \n visit_start_date::DATE AS encounter_start_date, \n visit_end_date::DATE AS encounter_end_date\nFROM \n\tp21_cdm.visit_occurrence\nORDER BY \n\tperson_id;"],
"dt_aitaa.db": ["SELECT \n obs.visit_occurrence_id AS encounter_id, \n obs.value_as_number AS age\nFROM \n p21_cdm.observation AS obs\nWHERE \n obs.observation_concept_id = 4265453 AND\n obs.unit_concept_id = 8512\nORDER BY\n obs.visit_occurrence_id;"],
"dt_aijaa.db": ["SELECT \n obs.visit_occurrence_id AS encounter_id, \n obs.value_as_number AS age\nFROM \n p21_cdm.observation AS obs\nWHERE \n obs.observation_concept_id = 4265453 AND\n obs.unit_concept_id = 9448\nORDER BY\n obs.visit_occurrence_id;"],
"dt_aufnan.db": ["SELECT \n obs.visit_occurrence_id AS encounter_id, \n obs.value_as_string AS admission_occasion\nFROM \n p21_cdm.observation AS obs\nWHERE \n obs.observation_concept_id IN (4079617, 4164916, 4138807, 4123917, 4164916, 4216316, 4146925, 4194310) AND \n obs.observation_type_concept_id = 43542355\nORDER BY\n obs.visit_occurrence_id;"],
"dt_aufngr.db": ["SELECT \n obs.visit_occurrence_id AS encounter_id, \n obs.value_as_string AS admission_reason\nFROM \n p21_cdm.observation AS obs\nWHERE \n obs.observation_concept_id IN (4214577, 4010105, 4123929, 4235698, 4214577, 4216316, 4213258, 4180080) AND \n obs.observation_type_concept_id = 43542355\nORDER BY\n obs.visit_occurrence_id;"],
"dt_entlgr.db": ["SELECT \n obs.visit_occurrence_id AS encounter_id, \n obs.value_as_string AS discharge_reason\nFROM \n p21_cdm.observation AS obs\nWHERE \n obs.observation_concept_id IN (4082735, 4203130, 4021968, 4147710, 4216643, 4139566, 4143443, 4127600, 45878214, 4084686, 4213258, 4084500) AND\n obs.observation_type_concept_id = 38000280\nORDER BY\n obs.visit_occurrence_id;"],
"dt_beatmst.db": ["SELECT \n obs.visit_occurrence_id AS encounter_id, \n obs.value_as_number AS ventilation_hours\nFROM \n p21_cdm.observation AS obs\nWHERE \n obs.observation_concept_id = 4108449\nORDER BY\n obs.visit_occurrence_id;"],
"dt_icd.db": ["SELECT\n cond.visit_occurrence_id\t AS \t\tencounter_id,\n cond.condition_concept_id\t AS \t\tsnowmed,\n icd.source_code\t\t\t AS\t\ticd_code,\n cond.condition_type_concept_id AS diagnosis_type\nFROM\n(SELECT \n DISTINCT\n source_code,\n target_concept_id\nFROM\n p21_cdm.source_to_concept_map\nWHERE\n source_vocabulary_id='ICD10GM') AS icd\nJOIN \n p21_cdm.condition_occurrence AS cond ON\n cond.condition_concept_id = icd.target_concept_id\nWHERE\n cond.condition_type_concept_id IN (44786627, 44786629)\n AND cond.condition_type_concept_id != 0\nORDER BY \n cond.visit_occurrence_id;"],
"dt_ops.db": ["SELECT \n visit_occurrence_id\t\t AS\t\tencounter_id,\n procedure_concept_id\t\tAS\t\tsnowmed,\n procedure_source_value\tAS\t\tops_code,\n procedure_date::date \t\tAS \t\tops_date\nFROM\n p21_cdm.procedure_occurrence\nORDER BY \n visit_occurrence_id;"],
"dt_fab.db": ["SELECT \n visit_occurrence_id\t\tAS \t\tencounter_id,\n care_site_id\t\t\t\t AS\t\tdepartment\nFROM \n p21_cdm.visit_occurrence;"],
"dt_pl_c5x.db": ["SELECT \n cond.visit_occurrence_id\t\tAS \t\tencounter_id,\n cond.person_id\t\t\t\t AS \t\tpatient_id,\n icd.source_code\t\t\t\t AS \t\ticd_code,\n con.concept_code \t\t\t AS\t gender\nFROM\n(SELECT\n DISTINCT\n source_code,\n target_concept_id,\n regexp_matches(source_code, 'C5[1-8]', 'g')\nFROM\n p21_cdm.source_to_concept_map\nWHERE\n source_vocabulary_id='ICD10GM'\nORDER BY \n source_code) AS icd\nJOIN \n p21_cdm.condition_occurrence AS cond ON\n cond.condition_concept_id = icd.target_concept_id\nLEFT OUTER JOIN \n p21_cdm.person AS per ON \n cond.person_id = per.person_id\nLEFT OUTER JOIN \n p21_cdm.concept AS con ON \n per.gender_concept_id = con.concept_id\nORDER BY \n cond.visit_occurrence_id;"],
"dt_pl_c6x.db": ["SELECT \n cond.visit_occurrence_id\t\tAS \t\tencounter_id,\n cond.person_id\t\t\t\t AS \t\tpatient_id,\n icd.source_code\t\t\t\t AS \t\ticd_code,\n con.concept_code \t\t\t AS\t gender\nFROM\n(SELECT\n DISTINCT\n source_code,\n target_concept_id,\n regexp_matches(source_code, 'C6[0-3]', 'g')\nFROM\n p21_cdm.source_to_concept_map\nWHERE\n source_vocabulary_id='ICD10GM'\nORDER BY \n source_code) AS icd\nJOIN \n p21_cdm.condition_occurrence AS cond ON\n cond.condition_concept_id = icd.target_concept_id\nLEFT OUTER JOIN \n p21_cdm.person AS per ON \n cond.person_id = per.person_id\nLEFT OUTER JOIN \n p21_cdm.concept AS con ON \n per.gender_concept_id = con.concept_id\nORDER BY \n cond.visit_occurrence_id;"],
"dt_pl_05xx.db": ["SELECT \n obs.visit_occurrence_id\t\t AS \t\t encounter_id,\n obs.person_id\t\t\t\t\t AS \t\t patient_id,\n obs.value_as_string \t\tAS \tadmission_reason,\n obs.observation_concept_id\tAS \t\t snowmed,\n con.concept_code \t\t\t AS\t gender\nFROM \n p21_cdm.observation AS obs\nLEFT OUTER JOIN \n p21_cdm.person AS per ON \n obs.person_id = per.person_id\nLEFT OUTER JOIN \n p21_cdm.concept AS con ON \n per.gender_concept_id = con.concept_id\nWHERE \n obs.observation_concept_id = 4214577 AND\n obs.observation_type_concept_id = 43542355\nORDER BY\n obs.visit_occurrence_id;"],
"dt_pl_o0099.db": ["SELECT \n cond.visit_occurrence_id\t\tAS \t\tencounter_id,\n cond.person_id\t\t\t\t AS \t\tpatient_id,\n icd.source_code\t\t\t\t AS \t\ticd_code,\n con.concept_code \t\t\t AS\t gender\nFROM\n(SELECT\n DISTINCT\n source_code,\n target_concept_id,\n regexp_matches(source_code, 'O[0-9]', 'g')\nFROM\n p21_cdm.source_to_concept_map\nWHERE\n source_vocabulary_id='ICD10GM'\nORDER BY \n source_code) AS icd\nJOIN \n p21_cdm.condition_occurrence AS cond ON\n cond.condition_concept_id = icd.target_concept_id\nLEFT OUTER JOIN \n p21_cdm.person AS per ON \n cond.person_id = per.person_id\nLEFT OUTER JOIN \n p21_cdm.concept AS con ON \n per.gender_concept_id = con.concept_id\nORDER BY \n cond.visit_occurrence_id;"]
}
\ No newline at end of file
# get db_settings
getDBsettings <- function(input, rv){
rv$tab <- data.table("keys" = character(), "value" = character())
# create description of column selections
vec <- c("dbname", "host", "port", "user", "password")
selections <- c("config_targetdb_dbname", "config_targetdb_hostname", "config_targetdb_port", "config_targetdb_user", "config_targetdb_password")
lapply(1:length(vec), function(g) {
rv$tab <- rbind(rv$tab, cbind("keys" = vec[g], "value" = eval(parse(text=paste0("input$", selections[g])))))
})
# if one column is selected multiple times
if ("" %in% rv$tab[,value] || any(rv$tab[,grepl("\\s", value)])){
showModal(modalDialog(
title = "Invalid values",
"No empty strings or spaces allowed in database configurations"
))
return(NULL)
} else {
print(rv$tab)
outlist <- lapply(setNames(vec, vec), function(g){
rv$tab[keys==g,value]
})
return(outlist)
}
}
# test db connection
testDBcon <- function(rv){
drv <- RPostgres::Postgres()
tryCatch({
rv$db_con <- dbConnect(
drv = drv,
dbname = rv$db_settings$dbname,
host = rv$db_settings$host,
port = rv$db_settings$port,
user = rv$db_settings$user,
password = rv$db_settings$password
)
print(RPostgres::dbGetInfo(rv$db_con))
}, error = function(e){
showModal(modalDialog(
title = "Error occured during testing database connection",
"An error occured during the test of the database connection. Please check your settings and try again."
))
rv$db_con <- NULL
cat("\nDB connection error\n")
})
}
fireSQL <- function(rv, jsonobj){
withProgress(
message = paste0("Getting ", jsonobj, " data from server"), value = 0, {
# avoid sql-injection
# https://db.rstudio.com/best-practices/run-queries-safely/
sql <- DBI::sqlInterpolate(rv$db_con, rv$sql[[jsonobj]])
incProgress(1/1, detail = "... working hard to get data ...")
# get data
rv$data_objects[[jsonobj]] <- paste0("rv$", jsonobj)
return(data.table(dbGetQuery(rv$db_con, sql), stringsAsFactors = TRUE))
})
}
\ No newline at end of file
default:
i2b2:
dbname: "i2b2"
host: "i2b2-pg"
port: 5432
user: "i2b2"
password: "demouser"
omop:
dbname: "OHDSI"
host: "omop-pg"
port: 5432
user: "ohdsi_admin_user"
password: "admin1"
\ No newline at end of file
# assign global variable here
\ No newline at end of file
shinyjs.reset = function() {
console.log("Entered shinyjs.reset");
//location.reload();
history.go(0);
};
\ No newline at end of file
shinyServer(function(input, output, session) {
# if you want to source any files with functions, do it inside the server-function, so the information will not be shared across sessions
source("./_utilities/functions.R", encoding = "UTF-8")
# define reactive values here
rv <- reactiveValues(
file = NULL,
db_settings = NULL,
db_con = NULL,
db_getdata = FALSE,
data_objects = list()
)
########################
# tab_config
########################
# observe source file directory
observe({
shinyDirChoose(input, "config_sourcedir_in", updateFreq = 0, session = session, defaultPath = "", roots = c(home="/home/"), defaultRoot = "home")
output$config_sourcedir_out <- reactive({
roots = c(home="/home/")
sourcefiledir <- parseDirPath(roots, input$config_sourcedir_in)
paste(sourcefiledir)
})
})
# observe target database configuration
observeEvent(input$config_targetdb_rad, {
print(input$config_targetdb_rad)
# if "./_utilities/settings.yml" not present, read default settings list here and populate textInputs
if (!file.exists(paste0("./_settings/settings_", input$config_targetdb_rad, ".JSON"))){
cat("\nReading default settings\n")
rv$db_settings <- config::get(input$config_targetdb_rad, file = "./_utilities/settings_default.yml")
showModal(modalDialog(
"Loading default configuration",
title = "Loading default database configuration")
)
} else {
rv$db_settings <- fromJSON(paste0("./_settings/settings_", input$config_targetdb_rad, ".JSON"))
}
updateTextInput(session, "config_targetdb_dbname", value = rv$db_settings$dbname)
updateTextInput(session, "config_targetdb_hostname", value = rv$db_settings$host)
updateTextInput(session, "config_targetdb_port", value = rv$db_settings$port)
updateTextInput(session, "config_targetdb_user", value = rv$db_settings$user)
updateTextInput(session, "config_targetdb_password", value = rv$db_settings$password)
})
# observe saving of settings
observeEvent(input$config_targetdb_save_btn,{
rv$db_settings <- getDBsettings(input, rv)
if (!is.null(rv$db_settings)){
print(rv$db_settings)
if (!dir.exists("./_settings/")){
dir.create("./_settings/")
}
writeLines(toJSON(rv$db_settings,
pretty = T,
auto_unbox = F),
paste0("./_settings/settings_", input$config_targetdb_rad, ".JSON"))
}
})
# test db-connection
observeEvent(input$config_targetdb_test_btn, {
rv$db_settings <- getDBsettings(input, rv)
if (!is.null(rv$db_settings)){
testDBcon(rv)
if (!is.null(rv$db_con)){
cat("\nDB connection successfully established\n")
showModal(modalDialog(
title = "Database connection successfully tested",
"The database connection has been successfully established and tested."
))
}
# workaround to tell ui, that db_connection is there
output$dbConnection <- reactive({
if (!is.null(rv$db_con)){
shinyjs::hide("dash_instruction")
return(TRUE)
} else {
shinyjs::show("dash_instruction")
return(FALSE)
}
})
outputOptions(output, 'dbConnection', suspendWhenHidden=FALSE)
}
})
# load sql statements
observe({
req(rv$db_con)
if (is.null(rv$sql)){
if (input$config_targetdb_rad == "i2b2"){
rv$sql <- fromJSON("./_utilities/SQL_i2b2.JSON")
} else if (input$config_targetdb_rad == "omop"){
rv$sql <- fromJSON("./_utilities/SQL_omop.JSON")
}
}
})
########################
# tab_dashboard
########################
output$dash_instruction <- renderText({
paste0("Please configure and test your database connection in the settings tab.\nThen return here in order to load the data.")
})
observeEvent(input$dash_load_btn, {
testDBcon(rv)
if (!is.null(rv$sql)){
rv$db_getdata <- TRUE
output$menu <- renderMenu({
sidebarMenu(
menuItem("Raw data", tabName = "tab_rawdata1", icon = icon("table"))
)
})
updateTabItems(session, "tabs", "tab_rawdata1")
output$rawdata1_uiout <- renderUI({
selectInput("rawdata1_sel", "Data object", rv$data_objects, multiple=TRUE, selectize=FALSE)
})
shinyjs::disable("dash_load_btn")
} else {
cat("\nSQL not loaded yet\n")
}
})
# TODO overview: count encounter ids, count begleitlieger, count patient ids
########################
# tab_rawdata1
########################
# dt_patient.db
observe({
req(rv$db_getdata)
vec <- c("dt_patient.db", "dt_visit.db", "dt_aitaa.db", "dt_aijaa.db",
"dt_aufnan.db", "dt_aufngr.db", "dt_entlgr.db", "dt_beatmst.db",
"dt_icd.db", "dt_ops.db", "dt_fab.db",
"dt_pl_c5x.db", "dt_pl_c6x.db", "dt_pl_05xx.db", "dt_pl_o0099.db")
for (i in vec){
if (is.null(eval(parse(text=paste0("rv$", i))))){
rv[[i]] <- fireSQL(rv, i)
}
}
invisible(gc())
rv$db_getdata <- FALSE
})
observeEvent(input$rawdata1_sel, {
output$rawdata1_table <- DT::renderDataTable({
DT::datatable(eval(parse(text= input$rawdata1_sel)), options = list(scrollX = TRUE, pageLength = 20))
})
})
})
shinyUI(dashboardPage(skin = "black",
# Application title
dashboardHeader(title = "Shiny DQA Tool"),
dashboardSidebar(
# Include shinyjs in the UI Sidebar
shinyjs::useShinyjs(),
#Sidebar Panel
sidebarMenu(id = "tabs",
menuItem("Dashboard", tabName = "tab_dashboard", icon = icon("file")),
sidebarMenuOutput("menu"),
menuItem("Settings", tabName = "tab_config", icon = icon("file"))
)),
dashboardBody(
# Include shinyjs in the UI Body
shinyjs::useShinyjs(),
# js reset function
# https://stackoverflow.com/questions/25062422/restart-shiny-session
extendShinyjs(script = "reset.js", functions = "reset"), # Add the js code to the page
tabItems(
tabItem(tabName = "tab_dashboard",
fluidRow(
box(title = "Overview",
verbatimTextOutput("dash_instruction"),
conditionalPanel(
condition = "output.dbConnection",
actionButton("dash_load_btn", "Load data")
),
width = 6
)
)
),
tabItem(tabName = "tab_config",
fluidRow(
box(
title = "Target Database Configuration",
radioButtons(inputId = "config_targetdb_rad",
label = "Pleas select the target database",
choices = list("i2b2" = "i2b2",
"OMOP" = "omop"),
selected = NULL,
inline = TRUE),
textInput("config_targetdb_dbname", label = "Database name"),
textInput("config_targetdb_hostname", label = "Host name"),
textInput("config_targetdb_port", label = "Port"),
textInput("config_targetdb_user", label = "Username"),
textInput("config_targetdb_password", label = "Password"),
div(class = "row", style = "text-align: center;",
actionButton("config_targetdb_save_btn", "Save settings"),
actionButton("config_targetdb_test_btn", "Test connection")),
width = 6
),
box(title = "Source File Directory",
div(class = "row",
div(class="col-sm-4", shinyDirButton("config_sourcedir_in",
"Source Dir",
"Please select the source file directory",
buttonType = "default",
class = NULL,
icon = NULL,
style = NULL)),
div(class = "col-sm-8", verbatimTextOutput("config_sourcedir_out"))
),
width = 6
)
)
),
tabItem(tabName = "tab_rawdata1",
fluidRow(
box(title = "Select data",
uiOutput("rawdata1_uiout"),
width = 4
),
box(
title = "Raw data",
dataTableOutput("rawdata1_table"),
width = 8
)
)
)
)
)
))
README.md 0 → 100644
# docker-shiny_baser
- this is a basic Shiny container
- put your Shiny app to the folder "shiny_app"
- add your required R packages to the Dockerfile before building the image
- to build your image:
```
cd ./docker/
chmod +x build_image.sh
./build_image.sh
```
# More Infos:
- about Shiny: https://www.rstudio.com/products/shiny/
- RStudio and Shiny are trademarks of RStudio, Inc.
FROM ubuntu:18.04
# set environment variable to supress user interaction
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
apt-utils \
dialog \
dirmngr \
gdebi-core \
gpg-agent \
less \
libcurl4-openssl-dev \
libssh-dev \
libssl-dev \
locales \
nano \
software-properties-common \
ssh \
sudo \
tar \
unzip \
vim \
wget \
xtail
RUN apt-get clean
RUN locale-gen en_US.utf8 \
&& /usr/sbin/update-locale LANG=en_US.UTF-8
ENV LANG=en_US.UTF-8
# add user + password
# https://stackoverflow.com/questions/2150882/how-to-automatically-add-user-account-and-password-with-a-bash-script
RUN useradd -ms /bin/bash user
RUN echo user:password | chpasswd
# Add R apt repository for latest R
# https://cran.r-project.org/bin/linux/ubuntu/
RUN gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
RUN gpg -a --export E298A3A825C0D65DFD57CBB651716619E084DAB9 | sudo apt-key add -
RUN add-apt-repository "deb http://cran.r-project.org/bin/linux/ubuntu $(lsb_release -cs)-cran35/"
# install basic R packages
RUN apt-get update && apt-get install -y --no-install-recommends \
r-base-dev \
pandoc \
pandoc-citeproc \
libcairo2-dev \
libxt-dev \
libsasl2-dev \
libxml2-dev \
libgmp3-dev \
libpq-dev \
libffi-dev \
libmpfr-dev \
libgsl-dev \
libglu1-mesa-dev \
texinfo \
unixodbc-dev
RUN apt-get clean && apt-get autoclean
# install opensource shiny server
# https://www.rstudio.com/products/shiny/download-server/
# https://github.com/rocker-org/shiny/blob/master/Dockerfile
RUN wget --no-verbose https://download3.rstudio.org/ubuntu-14.04/x86_64/VERSION -O "version.txt" && \
VERSION=$(cat version.txt) && \
wget --no-verbose "https://download3.rstudio.org/ubuntu-14.04/x86_64/shiny-server-$VERSION-amd64.deb" -O ss-latest.deb && \
gdebi -n ss-latest.deb && \
rm -f version.txt ss-latest.deb
# install necessary r-packages
ARG p_base="shiny \
shinyjs \
shinydashboard \
shinyWidgets \
DT \
data.table \
ggplot2"
ARG p_add="RPostgres"
RUN for package in $p_base; do \
R -q -e "p <- \"$package\"; if (isFALSE(p %in% installed.packages()[,\"Package\"])){; cat(paste(\"Installing package:\", p, \"\n\n\")); install.packages(p, repos = \"https://ftp.fau.de/cran/\", quiet=T);} else {;cat(paste(\"Package\", p, \"is already installed\n\n\"));}"; \
done
RUN for package in $p_add; do \
R -q -e "p <- \"$package\"; if (isFALSE(p %in% installed.packages()[,\"Package\"])){; cat(paste(\"Installing package:\", p, \"\n\n\")); install.packages(p, repos = \"https://ftp.fau.de/cran/\", quiet=T);} else {;cat(paste(\"Package\", p, \"is already installed\n\n\"));}"; \
done
# server configuration
ADD shiny-server.conf /etc/shiny-server/shiny-server.conf
# copy app
ADD addfolder /srv/shiny-server/
# fix permissions
RUN chown -R shiny:shiny /srv/shiny-server/
# add log-script
ADD show-log.sh /
RUN chmod +x show-log.sh
# make app available at port 3838
EXPOSE 3838
# copy server script
# https://github.com/rocker-org/shiny/blob/master/shiny-server.sh
ADD shiny-server.sh /usr/bin/shiny-server.sh
RUN cd /usr/bin/ && chmod +x shiny-server.sh
CMD ["/usr/bin/shiny-server.sh"]
#!/bin/bash
mkdir -p addfolder/
cp -R ../shiny_app/* addfolder/
docker build -f Dockerfile -t dqa-shiny-web-app .
rm -rf addfolder/
version: '3'
services:
shiny:
container_name: dqa-app
image: dqa-shiny-web-app
restart: unless-stopped
ports:
- "3838:3838"
networks:
- shiny_app_net
networks:
shiny_app_net:
\ No newline at end of file
# https://docs.rstudio.com/shiny-server/#server-management
# Define the user we should use when spawning R Shiny processes
run_as shiny;
# Define a top-level server which will listen on a port
server {
# Instruct this server to listen on port 3838
listen 3838;
# Define the location available at the base URL
location / {
# Run this location in 'site_dir' mode, which hosts the entire directory
# tree at '/srv/shiny-server'
site_dir /srv/shiny-server;
# Define where we should put the log files for this location
log_dir /var/log/shiny-server;
# Should we list the contents of a (non-Shiny-App) directory when the user
# visits the corresponding URL?
directory_index on;
}
}
\ No newline at end of file
#!/bin/sh
# Make sure the directory for individual app logs exists
mkdir -p /var/log/shiny-server
chown shiny.shiny /var/log/shiny-server
if [ "$APPLICATION_LOGS_TO_STDOUT" = "false" ];
then
exec shiny-server 2>&1
else
# start shiny server in detached mode
exec shiny-server 2>&1 &
# push the "real" application logs to stdout with xtail
exec xtail /var/log/shiny-server/
fi
\ No newline at end of file
#!/bin/bash
cd /var/log/shiny-server/
tail -f shiny*.log
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment