/
Shiny.R
258 lines (234 loc) · 12 KB
/
Shiny.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# Copyright 2023 Observational Health Data Sciences and Informatics
#
# This file is part of CohortDiagnostics
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#' Launch the Diagnostics Explorer Shiny app
#' @param connectionDetails An object of type \code{connectionDetails} as created using the
#' \code{\link[DatabaseConnector]{createConnectionDetails}} function in the
#' DatabaseConnector package, specifying how to connect to the server where
#' the CohortDiagnostics results have been uploaded using the
#' \code{\link{uploadResults}} function.
#' @param resultsDatabaseSchema The schema on the database server where the CohortDiagnostics results
#' have been uploaded.
#' @param vocabularyDatabaseSchema (Deprecated) Please use vocabularyDatabaseSchemas.
#' @param vocabularyDatabaseSchemas (optional) A list of one or more schemas on the database server where the vocabulary tables are located.
#' The default value is the value of the resultsDatabaseSchema. We can provide a list of vocabulary schema
#' that might represent different versions of the OMOP vocabulary tables. It allows us to compare the impact
#' of vocabulary changes on Diagnostics. Not supported with an sqlite database.
#' @param sqliteDbPath Path to merged sqlite file. See \code{\link{createMergedResultsFile}} to create file.
#' @param shinyConfigPath Path to shiny yml configuration file (use instead of sqliteDbPath or connectionDetails object)
#' @param runOverNetwork (optional) Do you want the app to run over your network?
#' @param port (optional) Only used if \code{runOverNetwork} = TRUE.
#' @param launch.browser Should the app be launched in your default browser, or in a Shiny window.
#' Note: copying to clipboard will not work in a Shiny window.
#' @param enableAnnotation Enable annotation functionality in shiny app
#' @param aboutText Text (using HTML markup) that will be displayed in an About tab in the Shiny app.
#' If not provided, no About tab will be shown.
#' @param tablePrefix (Optional) string to insert before table names (e.g. "cd_") for database table names
#' @param cohortTableName (Optional) if cohort table name differs from the standard - cohort (ignores prefix if set)
#' @param databaseTableName (Optional) if database table name differs from the standard - database (ignores prefix if set)
#'
#' @param makePublishable (Optional) copy data files to make app publishable to posit connect/shinyapp.io
#' @param publishDir If make publishable is true - the directory that the shiny app is copied to
#' @param overwritePublishDir (Optional) If make publishable is true - overwrite the directory for publishing
#'
#' @details
#' Launches a Shiny app that allows the user to explore the diagnostics
#'
#' @export
launchDiagnosticsExplorer <- function(sqliteDbPath = "MergedCohortDiagnosticsData.sqlite",
connectionDetails = NULL,
shinyConfigPath = NULL,
resultsDatabaseSchema = NULL,
vocabularyDatabaseSchema = NULL,
vocabularyDatabaseSchemas = resultsDatabaseSchema,
tablePrefix = "",
cohortTableName = "cohort",
databaseTableName = "database",
aboutText = NULL,
runOverNetwork = FALSE,
port = 80,
makePublishable = FALSE,
publishDir = file.path(getwd(), "DiagnosticsExplorer"),
overwritePublishDir = FALSE,
launch.browser = FALSE,
enableAnnotation = TRUE) {
useShinyPublishFile <- FALSE
if (is.null(shinyConfigPath)) {
if (is.null(connectionDetails)) {
sqliteDbPath <- normalizePath(sqliteDbPath)
if (!file.exists(sqliteDbPath)) {
stop("Sqlite database", sqliteDbPath, "not found. Please see createMergedSqliteResults")
}
resultsDatabaseSchema <- "main"
vocabularyDatabaseSchemas <- "main"
connectionDetails <- DatabaseConnector::createConnectionDetails(dbms = "sqlite", server = sqliteDbPath)
useShinyPublishFile <- TRUE
}
if (is.null(resultsDatabaseSchema)) {
stop("resultsDatabaseSchema is required to connect to the database.")
}
if (!is.null(vocabularyDatabaseSchema) &
is.null(vocabularyDatabaseSchemas)) {
vocabularyDatabaseSchemas <- vocabularyDatabaseSchema
warning(
"vocabularyDatabaseSchema option is deprecated. Please use vocabularyDatabaseSchemas."
)
}
if (cohortTableName == "cohort") {
cohortTableName <- paste0(tablePrefix, cohortTableName)
}
if (databaseTableName == "database") {
databaseTableName <- paste0(tablePrefix, databaseTableName)
}
.GlobalEnv$shinySettings <- list(
connectionDetails = connectionDetails,
resultsDatabaseSchema = resultsDatabaseSchema,
vocabularyDatabaseSchemas = vocabularyDatabaseSchemas,
aboutText = aboutText,
tablePrefix = tablePrefix,
cohortTableName = cohortTableName,
databaseTableName = databaseTableName,
enableAnnotation = enableAnnotation,
enableAuthorization = FALSE
)
options("enableCdAnnotation" = enableAnnotation)
on.exit(rm("shinySettings", envir = .GlobalEnv))
} else {
checkmate::assertFileExists(shinyConfigPath)
options("CD-shiny-config" = normalizePath(shinyConfigPath))
on.exit(options("CD-shiny-config" = NULL))
}
if (!"OhdsiShinyModules" %in% as.data.frame(installed.packages())$Package) {
remotes::install_github("OHDSI/OhdsiShinyModules")
}
appDir <-
system.file("shiny", "DiagnosticsExplorer", package = utils::packageName())
if (makePublishable) {
if (dir.exists(publishDir) && !overwritePublishDir) {
warning("Directory for publishing exists, use overwritePublishDir to overwrite")
} else {
if (getwd() == publishDir) {
stop("Publishable dir should not be current working directory")
}
dir.create(publishDir, showWarnings = FALSE)
filesToCopy <- file.path(appDir, list.files(appDir))
file.copy(filesToCopy, publishDir, recursive = TRUE, overwrite = TRUE)
if (useShinyPublishFile) {
file.copy(sqliteDbPath, file.path(publishDir, "data", "MergedCohortDiagnosticsData.sqlite"), overwrite = TRUE)
} else if (is.null(shinyConfigPath)) {
stop("Cannot make publishable shiny app when using connectionDetails object. Please create a config file")
} else {
file.copy(shinyConfigPath, file.path(publishDir, "config.yml"))
}
}
appDir <- publishDir
}
if (launch.browser) {
options(shiny.launch.browser = TRUE)
}
if (runOverNetwork) {
options(shiny.port = port)
options(shiny.host = "0.0.0.0")
}
shiny::runApp(appDir = appDir)
}
#' Merge Shiny diagnostics files into sqlite database
#'
#' @description
#' This function combines diagnostics results from one or more databases into a single file. The result is an
#' sqlite database that can be used as input for the Diagnostics Explorer Shiny app.
#'
#' It also checks whether the results conform to the results data model specifications.
#'
#' @param dataFolder folder where the exported zip files for the diagnostics are stored. Use
#' the \code{\link{executeDiagnostics}} function to generate these zip files.
#' Zip files containing results from multiple databases may be placed in the same
#' folder.
#' @param sqliteDbPath Output path where sqlite database is placed
#' @param overwrite (Optional) overwrite existing sqlite lite db if it exists.
#' @param tablePrefix (Optional) string to insert before table names (e.g. "cd_") for database table names
#' @export
createMergedResultsFile <-
function(dataFolder,
sqliteDbPath = "MergedCohortDiagnosticsData.sqlite",
overwrite = FALSE,
tablePrefix = "") {
if (file.exists(sqliteDbPath) & !overwrite) {
stop("File ", sqliteDbPath, " already exists. Set overwrite = TRUE to replace")
} else if (file.exists(sqliteDbPath)) {
unlink(sqliteDbPath)
}
connectionDetails <- DatabaseConnector::createConnectionDetails(dbms = "sqlite", server = sqliteDbPath)
connection <- DatabaseConnector::connect(connectionDetails = connectionDetails)
on.exit(DatabaseConnector::disconnect(connection))
createResultsDataModel(
connectionDetails = connectionDetails,
databaseSchema = "main",
tablePrefix = tablePrefix
)
listOfZipFilesToUpload <-
list.files(
path = dataFolder,
pattern = ".zip",
full.names = TRUE,
recursive = TRUE
)
for (zipFileName in listOfZipFilesToUpload) {
uploadResults(
connectionDetails = connectionDetails,
schema = "main",
zipFileName = zipFileName,
tablePrefix = tablePrefix
)
}
DatabaseConnector::renderTranslateExecuteSql(connection, "VACUUM;")
}
#' Create publishable shiny zip
#' @description
#' A utility designed for creating a published zip of a shiny app with an sqlite database.
#' Designed for sharing projects on servers like data.ohdsi.org.
#'
#' Takes the shiny code from the R project and adds an sqlite file to a zip archive.
#' Uncompressed cohort diagnostics sqlite databases can become large very quickly.
#'
#' @param outputZipfile The output path for the zip file
#' @param sqliteDbPath Merged Cohort Diagnostics sqlitedb created with \code{\link{createMergedResultsFile}}
#' @param shinyDirectory (optional) Path to the location where the shiny code is stored. By default,
#' this is the package root
#' @param overwrite If the zip file already exists, overwrite it?
#'
#' @export
createDiagnosticsExplorerZip <- function(outputZipfile = file.path(getwd(), "DiagnosticsExplorer.zip"),
sqliteDbPath = "MergedCohortDiagnosticsData.sqlite",
shinyDirectory = system.file(file.path("shiny", "DiagnosticsExplorer"),
package = "CohortDiagnostics"
),
overwrite = FALSE) {
outputZipfile <- normalizePath(outputZipfile, mustWork = FALSE)
if (file.exists(outputZipfile) & !overwrite) {
stop(outputZipfile, " already exists. Set overwrite = TRUE to continue")
}
stopifnot(dir.exists(shinyDirectory))
stopifnot(file.exists(sqliteDbPath))
sqliteDbPath <- normalizePath(sqliteDbPath)
message("Creating zip archive")
tmpDir <- tempfile()
dir.create(tmpDir)
on.exit(unlink(tmpDir, recursive = TRUE, force = TRUE), add = TRUE)
file.copy(shinyDirectory, tmpDir, recursive = TRUE)
dir.create(file.path(tmpDir, "DiagnosticsExplorer", "data"))
file.copy(sqliteDbPath, file.path(tmpDir, "DiagnosticsExplorer", "data", "MergedCohortDiagnosticsData.sqlite"))
DatabaseConnector::createZipFile(outputZipfile, file.path(tmpDir, "DiagnosticsExplorer"), rootFolder = tmpDir)
}