From 8a803f46c6608070b8113c08158ea2854a19bf45 Mon Sep 17 00:00:00 2001 From: nhall6 Date: Fri, 21 Apr 2023 12:38:56 -0400 Subject: [PATCH 01/20] added data viewer components module and modified description-cohorts.R --- R/cohortgenerator-main.R | 2 - R/components-data-viewer.R | 242 ++++++++++++++++++++++++++++ R/description-cohorts.R | 317 ++++++++++++++++++++----------------- R/sccs-main.R | 4 +- errorReportSql.txt | 51 ++++-- 5 files changed, 454 insertions(+), 162 deletions(-) create mode 100644 R/components-data-viewer.R diff --git a/R/cohortgenerator-main.R b/R/cohortgenerator-main.R index 8f8378ac..2916b237 100644 --- a/R/cohortgenerator-main.R +++ b/R/cohortgenerator-main.R @@ -43,8 +43,6 @@ cohortGeneratorViewer <- function(id) { ns <- shiny::NS(id) - - shiny::div( shinydashboard::box( diff --git a/R/components-data-viewer.R b/R/components-data-viewer.R new file mode 100644 index 00000000..b47637cd --- /dev/null +++ b/R/components-data-viewer.R @@ -0,0 +1,242 @@ + +#inputs: data, named list of colDef options, where name is name of each column, + #potentially: +#output: download buttons, table, and column selector + + + +resultTableViewer <- function(id = "result-table") { + ns <- shiny::NS(id) + shiny::div( + # UI + shinydashboard::box( + width = "100%", + title = shiny::span(shiny::icon("table"), "Table"), + shiny::fluidPage( + shiny::fluidRow( + shiny::column( + width = 8, + shiny::uiOutput(ns("columnSelector")) + ), + shiny::column( + width = 2, + shiny::downloadButton( + ns('dataFull'), + label = "Download (Full)", + icon = shiny::icon("download") + ) + ), + shiny::column( + width = 2, + shiny::actionButton( + ns('downloadDataFiltered'), + label = "Download (Filtered)", + icon = shiny::icon("download"), + onclick = paste0("Reactable.downloadDataCSV('", ns('dataFiltered'), + "', 'result-data-filtered-", Sys.Date(), ".csv')") + ) + ) + ), + shiny::fluidRow( + reactable::reactableOutput( + outputId = ns("resultData") + ) + ) + ) + ) + ) +} + + + + +#tooltip function +withTooltip <- function(value, tooltip, ...) { + shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", + tippy::tippy(value, tooltip, ...)) +} + +# customColDefs needs to be named list of colDefs +# example usage: +# Define custom colDefs for the Name and Age columns +# custom_colDefs <- list( +# mpg = colDef(align = "left", +# format = reactable::colFormat(digits = 2), +# header = withTooltip("MPG column name", "MPG tooltip")), +# disp = colDef(align = "center", +# header = withTooltip("Disp column name", "Disp tooltip")) +# ) + +create_colDefs_list <- function(df, customColDefs = NULL) { + # Get the column names of the input data frame + col_names <- colnames(df) + + # Create an empty list to store the colDefs + colDefs_list <- vector("list", length = length(col_names)) + names(colDefs_list) <- col_names + + # Define custom colDefs for each column if provided + if (!is.null(customColDefs)) { + for (col in seq_along(col_names)) { + if (col_names[col] %in% names(customColDefs)) { + colDefs_list[[col]] <- customColDefs[[col_names[col]]] + } else { + colDefs_list[[col]] <- reactable::colDef(name = col_names[col]) + } + + if (!is.null(customColDefs[[col_names[col]]]$header)) { + colDefs_list[[col]]$header <- customColDefs[[col_names[col]]]$header + } + + if (!is.null(customColDefs[[col_names[col]]]$tooltip)) { + colDefs_list[[col]]$header <- withTooltip(colDefs_list[[col]]$header, customColDefs[[col_names[col]]]$tooltip) + } + } + } else { + # Define default colDefs if customColDefs is not provided + for (col in seq_along(col_names)) { + colDefs_list[[col]] <- reactable::colDef(name = col_names[col]) + } + } + + # Return the list of colDefs + return(colDefs_list) +} + + + +resultTableServer <- function( + id, #string + df, #data.frame + colDefsInput #list of colDefs, can use checkmate::assertList, need a check that makes sure names = columns +) { + shiny::moduleServer( + id, + function(input, output, session) { + + + output$columnSelector <- shiny::renderUI({ + + # Get the column names from the data frame displayed in the reactable + # cols <- if(is.null(input$dataCols)){ + # colnames(df) + # } + # else{ + # colnames(df[,input$dataCols]) + # } + # + shinyWidgets::pickerInput( + inputId = session$ns('dataCols'), + label = 'Select Columns to Display: ', + choices = colnames(df), + selected = colnames(df), + choicesOpt = list(style = rep_len("color: black;", 999)), + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ), + width = "50%" + ) + + }) + + #need to try adding browser() to all reactives to see why selected cols isnt working + + colDefs <- shiny::reactive(create_colDefs_list(df = df[,input$dataCols], + customColDefs = colDefsInput) + ) + + + output$resultData <- + + + reactable::renderReactable({ + if(is.null(input$dataCols)){ + data = df + } + else{ + data = df[,input$dataCols] + } + reactable::reactable(data, + columns = colDefs(), #these can be turned on/off and will overwrite colDef args + sortable = TRUE, + resizable = TRUE, + filterable = TRUE, + searchable = TRUE, + showPageSizeOptions = TRUE, + selection = "multiple", + outlined = TRUE, + showSortIcon = TRUE, + striped = TRUE, + highlight = TRUE, + defaultColDef = reactable::colDef( + align = "left") + ) + }) + + } + + ) + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/R/description-cohorts.R b/R/description-cohorts.R index 7156f3f8..0d685d18 100644 --- a/R/description-cohorts.R +++ b/R/description-cohorts.R @@ -54,31 +54,33 @@ descriptionTableViewer <- function(id) { shiny::uiOutput(ns("TinputsText")), - shinydashboard::box( - status = 'info', - width = "100%", - # Title can include an icon - title = shiny::tagList(shiny::icon("gear"), "Table"), - - shiny::checkboxGroupInput( - inputId = ns("columnSelect"), - label = "Columns to show:", - inline = T, - choices = c( - "Mean" = "averageValue", - "Count" = "countValue" - ), - selected = c("averageValue", "countValue") - ), - - shiny::downloadButton( - ns('downloadCohorts'), - label = "Download" - ), - shinycssloaders::withSpinner( - reactable::reactableOutput(ns('feTable')) - ) - ) + resultTableViewer(ns("result-table")), + + # shinydashboard::box( + # status = 'info', + # width = "100%", + # # Title can include an icon + # title = shiny::tagList(shiny::icon("gear"), "Table"), + # + # shiny::checkboxGroupInput( + # inputId = ns("columnSelect"), + # label = "Columns to show:", + # inline = T, + # choices = c( + # "Mean" = "averageValue", + # "Count" = "countValue" + # ), + # selected = c("averageValue", "countValue") + # ), + # + # shiny::downloadButton( + # ns('downloadCohorts'), + # label = "Download" + # ), + # shinycssloaders::withSpinner( + # reactable::reactableOutput(ns('feTable')) + # ) + # ) ) ) } @@ -234,131 +236,148 @@ descriptionTableServer <- function( shiny::showNotification(paste0('Error: ', e)); return(NULL) }) - reactiveAllData(allData) - - if(!is.null(allData)){ - - # do the plots reactively - output$feTable <- reactable::renderReactable( - { - reactable::reactable( - data = allData, - filterable = TRUE, - showPageSizeOptions = TRUE, - pageSizeOptions = c(10, 50, 100,1000), - defaultPageSize = 50, - striped = TRUE, - highlight = TRUE, - - columns = list( - analysisName = reactable::colDef( - filterInput = function(values, name) { - shiny::tags$select( - # Set to undefined to clear the filter - onchange = sprintf("Reactable.setFilter('desc-analysis-select', '%s', event.target.value || undefined)", name), - # "All" has an empty value to clear the filter, and is the default option - shiny::tags$option(value = "", "All"), - lapply(unique(values), shiny::tags$option), - "aria-label" = sprintf("Filter %s", name), - style = "width: 100%; height: 28px;" - ) - } - ) - ), - elementId = "desc-analysis-select" - - - ) - } - ) - } else{ - shiny::showNotification('data NULL') - } + # reactiveAllData(allData) + # + # if(!is.null(allData)){ + # + # # do the plots reactively + # output$feTable <- reactable::renderReactable( + # { + # reactable::reactable( + # data = allData, + # filterable = TRUE, + # showPageSizeOptions = TRUE, + # pageSizeOptions = c(10, 50, 100,1000), + # defaultPageSize = 50, + # striped = TRUE, + # highlight = TRUE, + # + # columns = list( + # analysisName = reactable::colDef( + # filterInput = function(values, name) { + # shiny::tags$select( + # # Set to undefined to clear the filter + # onchange = sprintf("Reactable.setFilter('desc-analysis-select', '%s', event.target.value || undefined)", name), + # # "All" has an empty value to clear the filter, and is the default option + # shiny::tags$option(value = "", "All"), + # lapply(unique(values), shiny::tags$option), + # "aria-label" = sprintf("Filter %s", name), + # style = "width: 100%; height: 28px;" + # ) + # } + # ) + # ), + # elementId = "desc-analysis-select" + # + # + # ) + # } + # ) + # } else{ + # shiny::showNotification('data NULL') + # } } ) + + # custom_colDefs <- list( + # mpg = colDef(align = "left", + # format = reactable::colFormat(digits = 2), + # header = withTooltip("MPG column name", "MPG tooltip")), + # disp = colDef(align = "center", + # header = withTooltip("Disp column name", "Disp tooltip")) + # ) + + resultTableServer(id="result-table", + df=allData, + colDefsInput = custom_colDefs) + + + + + # observed the choices to update table - shiny::observeEvent( - eventExpr = input$columnSelect, - { - - if(!is.null(reactiveAllData())){ - # filter columns - columnInd <- input$columnSelect # this tells us whether to include count/mean - - inds <- c() - if(!'countValue' %in% columnInd){ - #remove counts - inds <- c(inds, grep('countValue', colnames(reactiveAllData()))) - } - if(!'averageValue' %in% columnInd){ - #remove averages - inds <- c(inds, grep('averageValue', colnames(reactiveAllData()))) - } - - if(length(inds)>0){ - allData <- reactiveAllData()[, -inds] - } else{ - allData <- reactiveAllData() - } - - # do the plots reactively - output$feTable <- reactable::renderReactable( - { - reactable::reactable( - data = allData, - filterable = TRUE, - showPageSizeOptions = TRUE, - pageSizeOptions = c(10, 50, 100,1000), - defaultPageSize = 50, - striped = TRUE, - highlight = TRUE, - - columns = list( - analysisName = reactable::colDef( - filterInput = function(values, name) { - shiny::tags$select( - # Set to undefined to clear the filter - onchange = sprintf("Reactable.setFilter('desc-analysis-select', '%s', event.target.value || undefined)", name), - # "All" has an empty value to clear the filter, and is the default option - shiny::tags$option(value = "", "All"), - lapply(unique(values), shiny::tags$option), - "aria-label" = sprintf("Filter %s", name), - style = "width: 100%; height: 28px;" - ) - } - ) - ), - elementId = "desc-analysis-select" - - ) - } - ) - } else{ - shiny::showNotification('data NULL') - } + # shiny::observeEvent( + # eventExpr = input$columnSelect, + # { + # + # if(!is.null(reactiveAllData())){ + # # filter columns + # columnInd <- input$columnSelect # this tells us whether to include count/mean + # + # inds <- c() + # if(!'countValue' %in% columnInd){ + # #remove counts + # inds <- c(inds, grep('countValue', colnames(reactiveAllData()))) + # } + # if(!'averageValue' %in% columnInd){ + # #remove averages + # inds <- c(inds, grep('averageValue', colnames(reactiveAllData()))) + # } + # + # if(length(inds)>0){ + # allData <- reactiveAllData()[, -inds] + # } else{ + # allData <- reactiveAllData() + # } + # + # # do the plots reactively + # output$feTable <- reactable::renderReactable( + # { + # reactable::reactable( + # data = allData, + # filterable = TRUE, + # showPageSizeOptions = TRUE, + # pageSizeOptions = c(10, 50, 100,1000), + # defaultPageSize = 50, + # striped = TRUE, + # highlight = TRUE, + # + # columns = list( + # analysisName = reactable::colDef( + # filterInput = function(values, name) { + # shiny::tags$select( + # # Set to undefined to clear the filter + # onchange = sprintf("Reactable.setFilter('desc-analysis-select', '%s', event.target.value || undefined)", name), + # # "All" has an empty value to clear the filter, and is the default option + # shiny::tags$option(value = "", "All"), + # lapply(unique(values), shiny::tags$option), + # "aria-label" = sprintf("Filter %s", name), + # style = "width: 100%; height: 28px;" + # ) + # } + # ) + # ), + # elementId = "desc-analysis-select" + # + # ) + # } + # ) + # } else{ + # shiny::showNotification('data NULL') + # } } ) # download button - output$downloadCohorts <- shiny::downloadHandler( - filename = function() { - paste('cohort-data-', Sys.Date(), '.csv', sep='') - }, - content = function(con) { - utils::write.csv(reactiveAllData(), con) - } - ) - - - return(invisible(NULL)) - - } - ) + # output$downloadCohorts <- shiny::downloadHandler( + # filename = function() { + # paste('cohort-data-', Sys.Date(), '.csv', sep='') + # }, + # content = function(con) { + # utils::write.csv(reactiveAllData(), con) + # } + # ) + # + # + # return(invisible(NULL)) + # + # } + # ) } @@ -372,7 +391,7 @@ getDesFEData <- function( ){ - shiny::withProgress(message = 'Getting target comparison data', value = 0, { +# shiny::withProgress(message = 'Getting target comparison data', value = 0, { sql <- "select distinct ref.covariate_id, ref.covariate_name, an.analysis_name, c.cohort_name, covs.COUNT_VALUE, covs.AVERAGE_VALUE from @@ -410,7 +429,7 @@ getDesFEData <- function( ; " - shiny::incProgress(1/3, detail = paste("Created SQL - Extracting...")) +# shiny::incProgress(1/3, detail = paste("Created SQL - Extracting...")) resultTable <- connectionHandler$queryDb( sql = sql, @@ -421,7 +440,7 @@ getDesFEData <- function( database_id = databaseId ) - shiny::incProgress(2/3, detail = paste("Formating")) +# shiny::incProgress(2/3, detail = paste("Formating")) #format resultTable$averageValue <- round(resultTable$averageValue, digits = 2) @@ -435,9 +454,9 @@ getDesFEData <- function( resultTable$analysisName <- as.factor(resultTable$analysisName) - shiny::incProgress(3/3, detail = paste("Done")) +# shiny::incProgress(3/3, detail = paste("Done")) - }) + # }) return(resultTable) } @@ -452,7 +471,7 @@ getDecCohortsInputs <- function( ){ - shiny::withProgress(message = 'Getting target comparison inputs', value = 0, { + #shiny::withProgress(message = 'Getting target comparison inputs', value = 0, { sql <- ' select distinct c.cohort_definition_id, c.cohort_name from @@ -464,7 +483,7 @@ getDecCohortsInputs <- function( on ids.id = c.cohort_definition_id ;' - shiny::incProgress(1/4, detail = paste("Extracting targetIds")) + #shiny::incProgress(1/4, detail = paste("Extracting targetIds")) idVals <- connectionHandler$queryDb( sql = sql, @@ -475,13 +494,13 @@ getDecCohortsInputs <- function( ids <- idVals$cohortDefinitionId names(ids) <- idVals$cohortName - shiny::incProgress(2/4, detail = paste("Extracted targetIds")) + #shiny::incProgress(2/4, detail = paste("Extracted targetIds")) sql <- 'select d.database_id, d.cdm_source_abbreviation as database_name from @result_schema.@database_table d;' - shiny::incProgress(3/4, detail = paste("Extracting databaseIds")) + #shiny::incProgress(3/4, detail = paste("Extracting databaseIds")) database <- connectionHandler$queryDb( sql = sql, @@ -491,9 +510,9 @@ getDecCohortsInputs <- function( databaseIds <- database$databaseId names(databaseIds) <- database$databaseName - shiny::incProgress(4/4, detail = paste("Done")) + #shiny::incProgress(4/4, detail = paste("Done")) - }) +# }) return( list( diff --git a/R/sccs-main.R b/R/sccs-main.R index 30930073..1f509da6 100644 --- a/R/sccs-main.R +++ b/R/sccs-main.R @@ -556,7 +556,7 @@ sccsView <- function(id = "sccs-module") { inputId = ns("exposuresOutcome"), label = "Exposures-outcome", choices = c() - ) + ) ), shiny::column( width = 4, @@ -566,7 +566,7 @@ sccsView <- function(id = "sccs-module") { choices = c(), choicesOpt = list(style = rep_len("color: black;", 999)), multiple = T - ) + ) ), shiny::column( width = 4, diff --git a/errorReportSql.txt b/errorReportSql.txt index 8568f701..6f2b80b6 100644 --- a/errorReportSql.txt +++ b/errorReportSql.txt @@ -2,15 +2,45 @@ DBMS: postgresql Error: -org.postgresql.util.PSQLException: ERROR: relation "ase_01.es_cohort_inclusion" does not exist - Position: 95 +org.postgresql.util.PSQLException: ERROR: syntax error at or near ")" + Position: 432 SQL: -SELECT ci.cohort_definition_id, ci.rule_sequence, ci.name as rule_name, - cd.cohort_name FROM ase_01.es_COHORT_INCLUSION ci - join ase_01.es_COHORT_DEFINITION cd - on cd.cohort_definition_id = ci.cohort_definition_id +select distinct ref.covariate_id, ref.covariate_name, an.analysis_name, c.cohort_name, covs.COUNT_VALUE, covs.AVERAGE_VALUE + from + ( + select co.RUN_ID, cd.TARGET_COHORT_ID as COHORT_DEFINITION_ID, co.COVARIATE_ID, + co.SUM_VALUE as COUNT_VALUE, co.AVERAGE_VALUE*100 as AVERAGE_VALUE from + ase_01.c_COVARIATES co + inner join + (select * from ase_01.c_cohort_details + where DATABASE_ID = '' and + TARGET_COHORT_ID in () and COHORT_TYPE = 'T' + ) as cd + on co.COHORT_DEFINITION_ID = cd.COHORT_DEFINITION_ID + and co.DATABASE_ID = cd.DATABASE_ID + union + select cc.RUN_ID, cds.TARGET_COHORT_ID as COHORT_DEFINITION_ID, cc.COVARIATE_ID, cc.COUNT_VALUE, cc.AVERAGE_VALUE from + ase_01.c_COVARIATES_continuous cc + inner join + (select * from ase_01.c_cohort_details + where DATABASE_ID = '' and + TARGET_COHORT_ID in () and COHORT_TYPE = 'T' + ) as cds + on cc.COHORT_DEFINITION_ID = cds.COHORT_DEFINITION_ID + and cc.DATABASE_ID = cds.DATABASE_ID + ) covs + inner join + ase_01.c_covariate_ref ref + on covs.RUN_ID = ref.RUN_ID and + covs.COVARIATE_ID = ref.COVARIATE_ID + inner join ase_01.c_analysis_ref an + on an.RUN_ID = ref.RUN_ID and + an.analysis_id = ref.analysis_id + inner join ase_01.cg_cohort_definition c + on c.cohort_definition_id = covs.COHORT_DEFINITION_ID ; + R version: R version 4.2.2 (2022-10-31 ucrt) @@ -28,8 +58,11 @@ Attached base packages: - base Other attached packages: +- shinyWidgets (0.7.6) +- tippy (0.1.0) +- reactable (0.4.3) - OhdsiShinyModules (1.0.4.9000) -- shiny (1.7.4) -- ShinyAppBuilder (1.1.1) - testthat (3.1.6) -- dplyr (1.1.0) \ No newline at end of file +- shiny (1.7.4) +- dplyr (1.1.0) +- ShinyAppBuilder (1.1.2) \ No newline at end of file From e6af4fdf91d74ab8a81fba23b598d99b353ca962 Mon Sep 17 00:00:00 2001 From: nhall6 Date: Fri, 21 Apr 2023 17:05:56 -0400 Subject: [PATCH 02/20] updating component functionality --- R/components-data-viewer.R | 14 ++++--- R/description-cohorts.R | 37 ++++++++++++------ errorReportSql.txt | 7 +--- tests/resources/pvDatabase/phevaluator.sqlite | Bin 0 -> 819200 bytes 4 files changed, 35 insertions(+), 23 deletions(-) create mode 100644 tests/resources/pvDatabase/phevaluator.sqlite diff --git a/R/components-data-viewer.R b/R/components-data-viewer.R index b47637cd..371c1a9c 100644 --- a/R/components-data-viewer.R +++ b/R/components-data-viewer.R @@ -128,8 +128,8 @@ resultTableServer <- function( shinyWidgets::pickerInput( inputId = session$ns('dataCols'), label = 'Select Columns to Display: ', - choices = colnames(df), - selected = colnames(df), + choices = colnames(df()), + selected = colnames(df()), choicesOpt = list(style = rep_len("color: black;", 999)), multiple = T, options = shinyWidgets::pickerOptions( @@ -147,21 +147,23 @@ resultTableServer <- function( #need to try adding browser() to all reactives to see why selected cols isnt working - colDefs <- shiny::reactive(create_colDefs_list(df = df[,input$dataCols], + colDefs <- shiny::reactive(create_colDefs_list(df = df()[,input$dataCols], customColDefs = colDefsInput) ) output$resultData <- - reactable::renderReactable({ if(is.null(input$dataCols)){ - data = df + data = df() } else{ - data = df[,input$dataCols] + data = df()[,input$dataCols] } + if(nrow(data)==0) + return(NULL) + reactable::reactable(data, columns = colDefs(), #these can be turned on/off and will overwrite colDef args sortable = TRUE, diff --git a/R/description-cohorts.R b/R/description-cohorts.R index 0d685d18..33675991 100644 --- a/R/description-cohorts.R +++ b/R/description-cohorts.R @@ -222,19 +222,14 @@ descriptionTableServer <- function( ) # hide/show columns - make allData react + + + - allData <- tryCatch({ - getDesFEData( - targetIds = input$targetIds, - databaseId = input$databaseId, - connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, - cohortTablePrefix = cohortTablePrefix - )}, - error = function(e){ - shiny::showNotification(paste0('Error: ', e)); return(NULL) - }) + #)}, + # error = function(e){ + # shiny::showNotification(paste0('Error: ', e)); return(NULL) + # }) # reactiveAllData(allData) # @@ -289,6 +284,24 @@ descriptionTableServer <- function( # header = withTooltip("Disp column name", "Disp tooltip")) # ) + allData <- shiny::reactive({ + browser() + if(is.null(input$targetIds)){ + return(data.frame()) + } + getDesFEData( + targetIds = input$targetIds, + databaseId = input$databaseId, + connectionHandler = connectionHandler, + schema = schema, + tablePrefix = tablePrefix, + cohortTablePrefix = cohortTablePrefix + ) + }) + + + custom_colDefs = NULL + #browser() resultTableServer(id="result-table", df=allData, colDefsInput = custom_colDefs) diff --git a/errorReportSql.txt b/errorReportSql.txt index 6f2b80b6..cb7e2293 100644 --- a/errorReportSql.txt +++ b/errorReportSql.txt @@ -58,11 +58,8 @@ Attached base packages: - base Other attached packages: -- shinyWidgets (0.7.6) -- tippy (0.1.0) -- reactable (0.4.3) - OhdsiShinyModules (1.0.4.9000) -- testthat (3.1.6) - shiny (1.7.4) +- ShinyAppBuilder (1.1.2) - dplyr (1.1.0) -- ShinyAppBuilder (1.1.2) \ No newline at end of file +- testthat (3.1.6) \ No newline at end of file diff --git a/tests/resources/pvDatabase/phevaluator.sqlite b/tests/resources/pvDatabase/phevaluator.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..9b4bf17c56b9b33106ba83cbba8b33014d3d3358 GIT binary patch literal 819200 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ⅅnYQm8B%Ryo707~c{BAO1csF>{4v3s7HAOTnEkQizX&A=DhLkP(Y=3AswISm9OK)beaRHOVxI z_Nu&Tc@`N>-&va7H?6#~@WCGvH2fAYO%PG0JrNy9!eE(^{ka7|+H7LwI+aZ=Ta^Nx z^hq^|rsw~yDSbv4on9C!c&1=@{yBLs=MBzXnzKFU@a&~oJF^bYY|Pk_{zdx0wCdE0 zQnn`h;4a#|QGhNZ?VS!RnDqNJd-o5y>b|e~#Xj}LnY+_A!IjbGYPMU6S02gWHHN!i zlzL4Uat%&5UQDQ?M6st*r%C>0H))r#I>uoV;X^Ih6o&sQJhUnnFW(V2d9`&$uL`t{@<`bj`u76?qOY1+2JwM{gD;b>~ zmN1b{aga@aR z7@Nv)mxfy$=~a2tvhe{TT91|{C(Cn+{8QFb3l*L8TBgowm8hIa-|*1wQ=gU3Soq(R8F*0 z_(XQIo$_&BmG!KJPijxlstn$!87-s3sIpA-8i!G@%9g-mR z6Ml8%!o*6i%FmW>!tei3dZF^a-K7DHxy(R|yP9y>~P4se&L^G3aESEuu+ocQKc z&6?@wUH--?S0*O)A0^Yeml0zVit8i4F2l>w2@Ve$bPlo;8}5zT*@`^F-ko0Ra{fQ{ z&XhjJqMHhTC^Q#r%l}utCeNSyWNx1vQ}!L%eX|y3UYqfKh8Zq^ho^b)(L-N7IRfHr zuL_L4jO97WTKDAbH_mMk#=NNcu<(;-UmEp(SG}2WJ=F(xW`aFRR!iEJ31Ss~Z*M2H z#2%MT9N<;qvIlcq!yx+^nq7D9c*1snW$cr1d`qX*wiKm3c9%HVtKwsi;t)6RjYjFg zRh8pzUh{2iad95gtq#j_kAu`4*&t9w#^w?@@*d5;y1$?CWSWU->#Y0)fA2ICL7egnj^hY47SMBW2=(cjsBnGy$TZ=J+@36 z|FBNYUyfRP?1+9RFDJI5OOW>UxKP9dpHaLft+sZ8*+kX18en$7gCG9X68b3@DtlzQ zFzmfh6=z#HU6|)=RXj^4d84>waX!aGly&!enmaCUe#GSdG%+u8t(9Q*+@AJ!4*~1} zzYAk(?B--jvPC?`8-)q8wR46CySv&sy(-gMrZx;kU$?b$PW7t%YMIJ^6^XYrY4+XT zw6^BDNr}1l4FsD=&UJEKyJ}VnOD%Q>c{5G&s$6Wj=Iz#5nuo7Pdjdi2R#Kl}K) z8@iY_2P|(*mQFVs2o74+qTK~bM*%qn+TAvb+a{KJ6?a>RW-68ni_Vn1nwJN7$GmJw zP|Hz+$K`RzXm|DUHp8n}*+MTF zbsp|jd~6|~>BOzb-`1kp+>knK$TLH`kRRv9LOsYMy^2*WRPt;ry4gs&#H$$5re%vv z9y{UD9Py>JYtiOc?j3tkSKG9>p15BGqp1t}D6ir^3$aXPD2muGq&H5Ty8PDS!i0!5 zcsgKiU?)!+6*|VN_|Iay#?;vv7OMH)_3EF7e1G486Ev|*baMXhlTw~i)L!sT{_gzT zym`6#*{@`Mp1Cmn;q(D%%TjMjc?W;2e2HVd3Pu%En<+#WzPOn$IW;dnxZ%ZP?5PJ{ zA1g#xSw9Zg&DD4nZYtz55%CncJx;`%iAS5Db^X%{0O-_nO3 zHixAZF;UWVhuIlvO^Wuj0cCCN5XX2GMk=m=XStbq>C4i+Q@NViWNdC)beb!yV1wVtAJ5ieVoFx z`w(?7(bU0yUWNV2infc~nnP&2M565)y^2X}diH9b9ps3HrP7V-Yj58_qfd-nNSuq% zJRIhTQ<9Qbz@og|)@dPe8T5A&{~tRc+7U+|tfOu2rD*(I>Oztp*nfNisd*c*Shom{ zNLF)og;(*Hg(VWtS!#sg8R@Zi-<|Qd0s4gOma5eSpn}^9t|cj$r@TJZfgF_wt#(*J zcA&5V!5SAvq?C*WjFjZVFG8Ws9%4(?L>kP(e^E);E5VH-r0{?WO-`#vE;l6*3)n7U z@Z233abm`_3RxR_8~8}_Cst4MDqgp&K2qUl_Wwg4DRHJ(G1|rJlBqN@GI)KZCrgK& zws^|-UCp>SA24joiKCgzhV#4AWu==13(UZ zg4WqayEXh!V`E7|GvI!b;1}HO+@98Uc?eL?gEm^FM2U5{XVZ2l;~u6Q`62RThWU?rBwhyRrhG zud=urF_3ryR$}mkVvMr<9ZaPX^0SpwCgMXa4lJYOkEc$USUCybAh>I!$gm>gTazZ^ zTQv?3dYt9oPb2?cLyOZ5&|C*XW4M2gWw4}?KRt2$6nwhD*|Da;8?_+5fGN$dx+CjP ztr@x4bw`Xl5?8FD>i_TiLC*jEQ~sFJXLeDb@V&yyg0u4<&o9hdoVz*a!<^D=SC*7D zD6=hNPx^Q13)5~){del|_;5FWi4(jE{5FFx5}Z_IT8URd;o=3tgh~p}?Bw9bRQy|> zAXT#PV`IH4*ey@s34*038a3-Wzc1(_{1Mms5tEjtBHQvr54w+*ruVAAwdZiW8?_&Q zBE5b0<@b)i@n_zZm{^jA_W?A^EPJ4?XaQhFki#@23kEtJ-uD)=Lx@CcMtD_}Tb_~w z?PF(2|Gau{>e3(9^BwC9NUxK?FmYZZ?{E0Nb^@oSBHi+=5!xi+)C8{zYRlA}8eY(} zr+%$@;_O@8RmTtL;*pDUM{j~nE=m|sf?4iW+0E*CCUMzG(u3PR{r=nmmnB9ctuy$Q zl;LEr%6yio`ae9ZJ-|^@Y-#a^GjR-d(m861OD(VNj0)#3AVZsQ!gDnvN+pi>Do(W+bfyaQ@HE$4vqgI4$7klx zI_&jCh&QSbfpT5~hb#pYSm9N?X`z`XY?fSPn&MOo(LC#krszp&(c9;4zJ1A`yBO#= zmub2LM$mQ-XKUgC-kDrED##FJuQ=2dJ@A(n}f5`GMc z*zUD2tMDqmr;y6?^vxPwqj}ITJ$9pzAg2N)x2&ubssfqc4I;GDEMQ0Y?T<~f^TD~D~OYR%Fr8#G1Ka_oV zR&(a#nRywfrEg1nG4+eoa(pzwUt)<*0U_hG&%`4O&rl$~x}QhpQ+UV_%aeugIUd=m zzMSx`0$#EsIA(0B{;E@?=FfM3;5hf=*!AP_6%iQZurLxjZ5{4#stdtJIjxTkMvJZ0 z>2kHmAVm`WMIAiIgyFtV_XE^3vbh6=?U-d$*-LI8?A!z&XIbv&V;8CZ)I)0S+B33tE)2W7wAlg zM=HL>&|AZ`_qhBsX?>aThljqppL@_v$fTo^hGdK<%`;b)V5W3fn(+;Hhg&?+t9Tb< zyOhr*dE?vCYhUM_@XS|RV@r;2KWOS~!;X-^Bz^%P62f~#T#M@2q|9wH_nU2QAh|_@ zS8@HtMe!v5kwSb~axVYdp8iiC7W<;&oHuh&X=-V5wzXO0XB8*`V(y4Cj$dbpx4SzD z92_MQlRR25F~j!||>APUgOhYUBJGy4`M&bqe!qt8_+r zjjGX_%tk|*L1!{F%)>Sw54}-0w|4#_p;~X!X-ztzxW-_tE&;n;ah1gl;)!^Yv~`l~ z$>{kX{QoIMOA0S7cqISl{JD7-<-VRfD#w{EW#?oq&b%(;>x}8?b!j)GrKi^P0RMlO zPsL8=+uz<`)?9Jk{R4km(w}?6)a#534D(HbX)%!Hb>dW?3Yxr_>*2h9q2{H>{{Bwx z>@T@Rl83XVRy!YWSFO-s)HP_0;a4mCYhTpdeEso1>#w^ew#GQubGiH}OuU21gJ}0fdC6>66_K^A z_ghs=@u?uoJb4H3P419xf9aU%kN;&<>{}Aww27|chB;Wa=gZqNL8~_|)Gk)mYm+$E zr{XU2bfQ4|+0tDWeaUGf7si$z=WL=eur`Yu!7bD`wp&PlIy~dV3ZII=%=cnRo+-#! zpNhrIH^y5pc(mWXOCJfo!vCJHb+$E^kxk9$h!i3wjTT#`|l`Kz)^eJZ>%-}z|ka_Naj&eHsO|5$F>rWQ~@e^u`U zUzDTFmE%d(Z*@u=3Y`_ty07NeOP@T{BDdi1>~1IB1GLA!9Tdb?HM@_F}d_Sl-^ zTb>O#*Bga7ICYFv{J>Q|{Dx6Nyeo58vM2de zCciwDF__lrnw^il{?KWKkH;SDIAu4Ww}v8l!f;;O$_^V)ElqM)5ovo7r~6bszo+so zduf`hKfdt~(>7kmEj;!FfiIA*USFlJt<@s}YV;|Nzc@=4a;fh{##8friZw9KkvvVy z$T4G;4F^=dn`z~{iYN}0p_(W@%clY?^E8g)0?IV6U27>UK0BN50%S}W@kEP? z*Dbd=JanIJa0y0RS@av&v#MCjJbPALL7}wk@Xr00ZTp_@3ba^+kUw$TEvkY413 z^CV`8XzBWW4gdMcVTtjO#H+W0GJ@kCtBf=#4vHzU%nd8sl0F4Q4~Fp@x*Xnnb-% zuMBxMqV9*Kv{o z1p?v90FyY_r}8G|E4IGjD(UK{@7>w|DgG=7DKf6nnNW^F&j0;W-c0E;tLUu4=L*{k z_7)7uKR@s7ylJ_eIdA5S%sxNs)vSJ*^D{0@e>;6v+MiN);KTnNU!uiFx!Dx$iiZbEOdSzP2b+3s-wq5&8O1=0}I zlXGP!V*7&I0*Z=!?wBFCk+g#6T9cXvBc9}=pl$ggStc00NMX!N(i6FB@^kj??dl@q zd|(c=1F%}!!G^bhwnHf|5v*0Qt+^DX{B6xPS1IcJZRqMIgF<|SmX(Od_~yx5AaZr$ zXy#iu(DRz+rq8GM|37nI?uEdz%H9D~ZEL3$r29cWO5>LGjbhi=O(6aWK1$-wI1DIK zmZ}swIW&)sxb~&tox1Mg#z(fo@;@NX@=*r2JgCc<2Q~YLW=+$l^?g6O`ZsQRTu*E} z2tCf_b{E>d6c0Xoo6KJ#q)5PZI@@hRrBH0Lc$V2ZI!aKPO<~;f6*O~PflaekJIgfx z=JwdrHgQ)l0b6(7+_H)i+yRQ>=Dq_*Xm)JN$vG!oN{0Rl=(n^W{@Ph0j`C41w|oUG zjVE;GXKi1sxo^dhPi*+XkeCg-*=6w%`zR|)%+K(+Iz_=pf!wlCmVkCx=zVWXTh`Rw zGp}=87eeEjFAMC%`%i(KSNbS*Th_<|#Sd#tvX#R?AH{IXve{pa-DKo&oR7k{Wp(Up zL{?AX`UM~5adS@G%etH=2VU6Dy!L z0GJH)QCDovNt*saiS)qM0j1XuLPEB!34gV-Z3!wv1ZJGJ)6?97wk>LSopBf=A)XFk zGWhT#eAL-mmdes$7M9w*bof(zQ}ZhN4Vp$&veYtDk;BZIhrJ(uw&C=jaYjHFfiP2; ztEcnCnHJO%od@Niq`VY*`{JQgm6Mnejz`2xI^t`C?MXjN^dZ~NR5Z)JcQ0AhZ z_LLz!IsYGqBYn{`g_HBI%FD>L=3J5ef7x@h)@QzxIX0s+T}n?&t4Uq|Uvuh@#wJh! zM+xro(#n$4rO!f=VMP;s6za~aI=QH#DL%?OmsPVQ;=*Iy1E8X5K1u|arCXQ}Ynq*} zN%uc^<5=6Sj00EAgWkY#?ZD>{loDt;09KO_kWBz576BuWe+m8$>|CJ0n{DD`9|e8O z;#s`5!sCALW75vnGgcfiz|n>HICnU;&J}nqfzHs5lZ5OH@k`dyI3J~L%gR}b+QZ6| zM~5o+QN*>Zn1xTHDBdKU_w~LN;ul9-Sl~% zE+oe}JtAC7H??iuW(ZnyG<~QIW7ny{+$oQz!FZYc( zS$e!-$uj$qXC!D@X+YD7vzdN@q`- z+eW-p%4KSR9sFHlO4aO1^o=>fM;(sk8^hAXP9N!GPdjAplR$Hy)#X0w=uB=4lkE+? zE&m^x-q8C0w^RDeE81B2P2rpZNB$r49?AVScXUp3_LW)x$eNXTe#Yw=QrN7?1FQub3sl2L9$eUxS{t7Z=mgNJpobl(T>biC=<5PJf|xm(oYeIDkXf>VRz z1ErVC64}G-G8=8jh^+dJPB*ti{jbr{M))YKTozi%@$7>)*NqUj=|0LPmu0d?pLz)4 zHr7W8<+6I71vW_lHy>q^%W_%DN$LI|Ri6}=*gPMlk;`h&;h4c2|0+Gae_L~htv>dn z7FVq4ck-)LJg(z>6jCl<3rm-eGV&YWk{jv7au15$j!mG^3BQ(&g)7X=sT&L=BqLCNInVdfof6Z&zFkS|aY&cptSym!Bl| zv_Mk5$_D$WFFH9WGnre{JJEgSRAoNub}kFlGe6ndq`CS`^igwjSujiQGab=nd-~Ax z|HhO)#-ghV|57-zpgsTg{EWPrxy?COW$(`}%sMgiii{64%G1xo`F~{Uw3LlWIrWP} z{1hB6KUC~7^KK;4HTx+pTwcsrYzvgdtoM`N%KeluF5e9HR4v#xZ)$Gc@(16)-~VUq zbvw>u=`Vr7WILzg2#xep-ncB8rDspMJ6rFPF5Fx6dG;&Ku_ed#stG$BAP?tkc67ou z)lRTurG5$JIeQLaqdV9+kz;jyOQYhsOxaYI0@^{337ss_6g*RqV zXdU&RQW;8(@lj`QSu;=AcuTglbJkt+-ul0HIW^0>U5f9fmQpH@s2U%&_LkMM^v^3V zu`OBBQ)iuX{DXse-D}ip33$AUm|hX2rnW%`FB{kf5L>Kjov}_E;Q+{XR~0^L`Ym4y z&u;Ti=Sk0xE6x2|_8pu9uLmvIvI?FIa%D;fs!`*C z&kv6+I?mJ50t>(^#C$?n<%9>PE0S|2=l>&8CZ_ahEZR|YOyRPE2lD@uKQnJ#?w;Jf zISaF|$=a6rU`AEiO{w>$e2tIEU*b$ZFkydZtV|C``~Yx?^s>6%qier8&14m-}mIw1Qo;STU$MC_EnS)XKBI- zAJu7LC^JC?ppj0fm@0IFYv7hk-cZ-J9Aq|2Tc?0OML8R(gFE2oN~xhuw4hCB>=fEt zI^k_&M`ax0L&&@3Gf^~WUup++356t%juydcL8}0+YpD&4Jz_mS12_e13;ufA6Nnl+ zrG*`TNr!VKJQd|4KD14@bwpI4#{eLwT3mote!Rw=xl7tU_}|XS4P(2yGjYAxd-;7?YIk9H$*pX|*pfm@dJhQ8iQKY-?~^h| z$lVdo^iw9eyauqp8`Es54{1)m?@V(^UrX%QOkCq_>VRoMhtuk`T0u%^Z?kz?fjY6+ zFoj@0MHmSc)3lhO?Evos@j*&73F4&5-rhncGoFYlV_n6xDP;b3*kpqz^vE}pde87! zh)zF+n#&i>9*M+7-=n!KebW@)mLsC9Yb}TJD_-Zs+E2UM3;rL zN8M!MNyNY&=PwF_axhEScQlZlBfVX8cKxY0935l4<5~&~a@!iIOHA9r>u%G;&IKGFf_8<Z#GJ-cOv!b+-%gxL=Df@=3&$1?G z{xRdrjN{WUPx~;fBK72ywTaLFizYuMQIio`%~HlS^VyNR<~ERer`}Y?W%N@xwR~AD zF@M!%^`3I4azBMu%hQ=<;f-8XvOF%u@qP-dmL;>qoTHMZi*K;*y)bxv>~$>8*M+uL zU#F`#YmJKqvk_KEvejrEjFILTYW1c%ah#v>s%6bA1yE7Vniq=uFRb`%eQeEfy`j5O zJjzdT)p7+COLGWSJm>g3r5kT>G~WLDgI)BR$EDijT-qT3wB+{K1P6fl&NgwdpYo@f zi{Gfw?kj#9;-|oAS>RZPH%3_Csb6cJIQtfN)$s$m7|A&2tjXTyam&=I34RKimKCy` zlBhyWk|8C2N|TmVvuxKD)iu)HTP~V(^J-HU;~Lit)weCB6a>so5($*is7rCyXe0;_ zr>ofl3&?Um1xw4)*_SX)5CBaE~())*Rudrq1L-E?_Bfrx$Xlf!YEVP`S(QUM4IBkrvTp#D+caH^o|FC+4AY zKR!%kOKHNg1<3{%$yOpXgSEy1su1P#l%pNBSv<;5ZLH<_HJJIC9b`X4v+K?sPuT9S zjJ@fIuTaivTiS$#ic37kPhF~IX)MjLbjEZW3i>!db*Ps0u_Q&QzMkWG`l)xdtiPQ3 z)^{tG?mD(q~4| z8HF1QJ}Q`$@5a=DOoDmFj1yZVJG|nTlOAgVH zo?v0F-ej6@)X};%v#R#)Sib_C#>8VkNF)Q0;a9lRP|8xYq0F$&dD1S=sb4KQRfv5H z;(VItSul&*L99dh0FV<7kF9ML$_M5s>3_F4)UWWUp_)B1(yf3Dzrvx0L>54XGO*xI zbT1&oKQ)>%uH<+KyBUz-SBTaaPYaLCcImSJICA?QeJl4wol|QtR12p0b4+HvdA=D7 z0V!*3&>82>H|Y)ag2_;ex>f@m%E$UAM+cb&kP(>;i#0b*9)9_?C-}M5>p`Cztrd0hE z<xDx<+* ztkKpZ`(qLferoGX1h3BLm?WDPNpHM+*^(EYx@Xpt?GX7R zef`{8Z5=7#H!dy@e?wPg)-ON{X$|&LCb7;>jh^L8%I3JFjlBh-;vW}X+ffVqyA70; zr(k2xAymluzkkZyls=P-Ru%3l>|4;7e_MV^US00hIrruqmhH^CF7rP)|KE{Ols+)+ zq|{9*|G7X(i>A zZgI3VI%cB|U+Hp<$kRp0>6w>IhupR{-je%F@xO-1DCI10Le9 zkFM%G57>3@X{IhbbIYk|+jvL)fX$jo6#NQv9D~hL^r;N?wJ&OJzW(^1_19e!TVb4m zp&Ew=T_sKzDzHg}QEc`r_-!oBJdauLXYZ8w6{Iz8hlXR5dF|QKT^4=GX(JbM-wwK@ zv{~GU@dEkYZlTR?q)J6`T!z>aIK(Co6jb6@{F6Dyck2a@_S<*qBf(er?YV26ZOvuG zUlvJv^hzy7A0OI|5jv>wD|W~b%hL0uJeyn3mnQx-`+^gU?{QBCJ*hGvw14zz{i^6O ze#IjhqP0AeV!8ChBWG#;ynigWXj2OolV4Rk-mh3DLoNHR5BQpinm>HM@_F}d_SkCU ze1prc#W|=AEvrSPsJ2c%aI0Wf6a0!{GBoqV@{`Ipn(9|flA)Tt8V^aj9stw$6@z4G zXRpJ8_D;?EyFdO)|LViMv*aMEPCJ??+d7CVMr&i({KriLr=;_;(eA|C)J@L+N2FLu z|Nqv)PYWj%Y|Q^Qe{t^PIVD*aXZ|hoxQun_FQpGjYfasodK5m!@`dti6*8RVk)o&W zqyjR>`Bn6Amh$VU<|LJ03w{M84x}eb{SH;EeV{?wwC>G2Nq#lzG|( z*ww+91#bz2VHI3BG_sFs@~EB@{0cA}f?4XE!vUk@);X8^6>vCIvp;ajEEcZtD|~QB zW_g9tfljjPn_q!~Lo`d)DJ(iE70wFo8&X*c6QWYz`R2-HPuERM2%@3tm-cjP`uP>4 zH=`FB6?({vEYX%%{rn1)ob$#U@*+!U%{EHu6{3AJ@oU3Z*$9Q^dJNSpiPraoZwer@5m!A zX1=$YXGdK7((q2*ayz=}mJn=?i+VdFv1kQK2S+#Tn}sHi+^*4N=@bsg)Q3N{gnr6p zbAUhvi)fIU;HYR@IbDb#3FLo3tn{n=&{>Lm=$K~R{hsEI%bOoDxj#KH0eX^eQ#o9NBZ`N#U!fo ziF+|Ag>6Nw8U>84w$?^?blWhMRo3Y2Q#t*E66t}h14^$SglPfCOw!`B1m@rZ>#$c5 zFd41?|0<tDrk$1gOv+a* zA%NcXGvfR}6k^U)eoSXn?<+qZ<5z*sSwgMKg(U+$iA<9UaL$rxim0vEynX$W5B~Ni zYwSfnzEG>t(s73VnDV?6svdtSMI{t-i-<5gK_ zYeV8n#sEfXlO%VgVuiEht|CuTo^(g;yT;cp$>&wtb-%#`zlsdb9+txJCbLUoxPN)H z$be_+Uiq`MyN|!mx`vy0hdPq2ikis|^{YtWEWI4cgy=@IQ@;xQ&0f~n{br}r{VL|S zg=4Y0vL~7(#`sm3Z=P*SQfxWv{87F)lNF>gZ+lN2v&to#rH(mrK|NQ;42D?LJDZ6i z7P*LC#Sk<5+agQ(Y8FmL^s=O#t_{bx?&B{{NrY*l(I55aX6p2gURN)&f-Lu|{JUA| zpCVV4EH)ZB{~t-}|A!TwUU+Z8y9N3AhP=yjKgcc5IXioO)<3dN$hf# z_2FNu{U>Mp)vf?10-Rp>Aq4pELx2Z35#Zrb|L;P82RISnk&FKCL4XH15#ZrVu1YE# zd00S&0B0{qNt-;oNP400u@ko*b3u%XlsIEMwlh6;LL;ET9oeoy6ap&TH+u|97WvSy z;LWM)w#5*6K75yi1sM0(3h-bIY!CeFZ1{#Cz=gWo>&v#p`Cj+o}5QtZffOx!g>( z?et-X#(kQ-`-fb0-&g%&k7B$TnkM9>Yy`|rzzoPgL@EjupAxw=pEwK21dxb73Hg_S z9yO{57rhmZHNCVu{^-Z@K!E0dFA5c11g3$dubKQgPqcbLTAOZ?yI@)4lTz= z!VnEUPItQ#*+L3oX~#`N^K(T&h4JQzylqXQXKQ6Z#r0;dyi2M{O|L1$o=oiO^5T}w_yL>l9iL$l5u1DPwB?A8`5&{ zu|r>sBOo3VP(jC8GP60xKMprHbQ`{OP(Z~TXQ>AZ>+7a^u+ae(aGa&on##+%p>=lV zhKn!0y7BBTjzIC9DXZ1c51|PMx=yh;prVYkbZkbYrcEiT7v6lS3#oB#;(tx+u>loU zoaIFl)p}UJW2TJ#=Y}q{#&rt*HKoS|R5Wpx_efZ2PgQ{(6Hp<>*`NQ+=VogDbjII? z7r(c!i;<3VRv0=gj%HNZ4GpNs;w(pFI=<(QHiNjM{2&lBqwm%->&OIVqBB8&f~Vi4tRylvaAiJo8pLog3x9};ZT*`jKVo3 zFgUNG-=JxPq|B0gry_@$H4l3~{A|PNKLZO2s<<#yn5(PP*6OebDl}|H(hmIxG=G&z zJJ%j~qY{fi7H0+&@-><{S(X;M!|OrPPC&t8;{sSnJE{x#_)5#A|5=;Q6U0OT5Ed0- z9(ANOc^cgvq~k%*HhNGYTSe?6EwI4VqlndN0kR%9K;dO`-GH?1XK1CT*N}5h}x14mP=C0>TKl--)+yq@f z1fvSaVHc{B)v@pPK-2}B32;QG&3_Z%Xifh>|42Ta$wfMR&4*m1EmkW6xx~LXF)q^X zK-AxREqlOEP9g7j-+RaItJ1q_QH*bo|37UbP4oX7Q;M1k|5WfE=>O;EJ(<@p*POE{ z`-AKWU;z9pb9P2B{l2t!(gvi?OWDYH{QbQ@CYA+McaThljqp zpF7SD7h7is8i3k_;z{$&l_eyhVQI!U+#PPQE)XSCGgXBtf_qI>SU|vRRUAk*i8gnor}{fI`eho2QO> zp_)_iq~^}P8};vBcFBQf9oFtHtpmweTN6y=R@mUg;AxXRp~S%f1(XfBEIE2Q#NFt9 z7*Ocg5XgQq)pnovVOc6e}mYo$9oNTkkfI`5AYL-l@qB{9zjWYuZ=^E0}FZ{c~&|&E(oTd44hj4}2 zJs~kOkP7zqk|)*#zci>A;>5ArH{$5gH>-jG}N#dw2GKioZXLSk%?% zO!_Jlvbs@FHpgiS^30km&bxo$PfPl9htcYF#s!A?Cc(5AwvBbD8dm|Xc^;;2BchiC zR8(vBf(}$)`pylgDA(-! z%AV&Ji^f2dea#gK?NtT7RRI+n+s*NY+~U-1zxsupM-I`&Ubo{ab2$hvO$uJRfJ=)t z0Tm{j=S9DzMS5+}QSaP8Y-{Yxh%>i;(1rXqC)*7U8`@s*CXHHCbG=Eskya5<`AoCW zDwL(a+nq-PDktemj@jMK&Z7a9i!=*VBQl;n0o9loh+0UCEVRFf`XpO<2BO~39I>V3 zSa}9icF)T=CUTEid6M&g-;{?^iu{E~1nC& z-~+wyYxU4Tlt{dWrDSzT>k6;AVMN1Z&HL+Lca2`RHZcVwG=5<7;JpENS^zCky-I4x zn$Uf0?GTF5<6q`%!~fYzgmy#}h=n?WiXHf@P)thX&`;acNjzWFKQD7VTcHqqOeLL% z&_uh9us>}>GRVDJ2t?VyYgl@)qeJxcv==_jeW|vqA&T=Y z+>6F&NFWO8UBi-bjE>Qd&*^tnSf1}{jN;mG^`a5V4n$eOtIuIr=I96w+qGPJ+TghVaB?9)dloK-F-!1L8KU($xe>yPup%^r1^a9KP*!% zjfqK)*4sR;14|A`o>Z<@5UT?!TVW5w%SaKupk8`pPOioMCo5mxDAFv@WG5!8tNa2&C3@6em?I;KdglkE4JY2O?|X7-SNo1QU$4Eq;8}hft6uM6 zR&ydN(W55FOqJ;28P@vIUDBaS^bQ#hM9w7#-zZBH4k{?0CY6d{$VHu<*Rof_9Ae1* z9)_IU|DWi-L#Ef|3az_WQr=Qifto&9F^;H-|!7c$3Yc+z*IeUdgVbp<}w3%^i7 zrGoUb;BDxZ2YHyJ;%!vGUY49!M0C1l=OeE_bXwu#+>!AHfV-*vO4ujTwh|7KO(-IE zw;V?f32WkJCx+Iv-|s3M?(_wvks5XzH>RS!gAy-Y=q z%B@H)Ry`19@ro4;J5NP*_hQupQL-*mqAH^DV(GpQ-syPLv4MMK(IT`GE=*qz8|zU4 z6(*M@Q5BJRk+fq(R{ch&n_HsZXw=te&HDNp!DOhNZ`K>?P2%)`%DI*$0~8V36B!_L zAnIYu)cmBV-`KmFpT-7MPPZ&AH}txa)p9c-pfbH>f7NS#+AXcS;>btt`zAqT4ft2d zog!^=zQNvXbIXCyaOLrY0H^aU9pof8jf@VcoNrkQ)5EGyyHUF4itiqqeB{kBBGvJ= z4$rf-I-4B`NVf?N(gh-rF>a+rq{jwSj<@VFMbyx1c1`k`+rC}Y)$u<*%?DYsilYOZ zHn((8lThuyre7dxbITM|p;8Z(nJ2i+95;s3)>pYP^EEF83zi(S@2#$8d%VG}He_d! zBm&bG>7W*5iHHFqez~0FgYZjYLMW3CY|AYUPc(Uilq)DEas~^@B1;=FT|}z4$%DVl z-ez@pXvYsSO<&(pRcmG8jH1h8? zv;a2TV(EbGCDN^qWflT<#-C0cKLwv|aCV3%1r_jcdNva}lim+u%uCV}xoh%s_U=v4 zLtlmAjqZJ{mUfuVTJVH5I-E`#?Xcs)7K&}nrKESS+2$%0T5LAtmu(goQq6YjvJ!E4 zP$3i}*UD1GMz1dCU7O~~+R}4wexZMYuFkL&X>6OYfVj$vLQnw`LnTWE2vwQ2`_IzD zRj(iJ**_#PDIPN-!kw6j#Nl;#Pyr8fjbzq+Z)>jo&oke(W0lk41U{Y&XS5w!SZc95P{<*U4l0mfC}-KWhle&9bkCrI1BO(VvW2MB?k`&i zDn7T+%5ubrYVGE-h4O%6TnnWv4d-E{o34=_ee%}NKECdTE~ai=C0KKm@}gg(DvSy! z?zNE2;$%uCXYQ`m{5kvT8RPtibs;&k@hajPTa(Sciah!Q1BzoUm%8q8#|AD%v8x5llchG0j@_#H(wjMJ({J#fo|t=F z-AQ&{1lu|~pt#gRAp3GO;!A1QqRp?|JNBZk_8@WHTwN>7LTqi;PQmDOwuW~i#R0{t zma#O6j^*rp*QJX)H>QVc-#Cz+^O9#C1@vNT^( z)ksCZskwE_AAJ9Q|DW86jVeRk9DO}98%SsfhPeW+qU<+N{l7BvnaqNW6VuO4+nFY$o{f+6>MwD8P{qJy-$A76HztoACIl7kE#7_X zXEW^s4brA{Zx(F2Jh0LLy61LRjLMe`s!b+Ima#o<#?~TUZfnbHmgh+2;*a z6dg{%(un9yM<@AZD;haOL$H5%nL~u6kYSltq<^N_*RN0M>c4JIhV*dHiGYD|yKx~0 zu%o5~6^<*{-OxPU1Kh5l;$jNvh=%=cZWp~9njYhJjSeb?ptu0`b953iHDXCnvGIgd z7SC@w)-(5OHM>J=|5$V1_{7ZoF!IH|3Jht*5sj-Fj7@R_IPB4Bb|y^psh%hS)FKOiKKd$A!X)9 z!jH5HyODdw2il!(+T4hoaXJ2Vs6n>a342euZxPMD1RROn9B`8K6U1JFCYvM8#;ZpM z6&G2|MfRP*h=x+l&IRW^{b~Dykx^OVv|2qb7{VgQ^!s}^nFku91S%Wts6|jutuDj=E)!i;g({gV&;bHzJ*%OC7eAcDffAR|WH;dvF#)WOz!} z{Z;ef%Zm!86*nYms7MZ0DKJ0@w$0j7Dk$&yQen9hSw5M-(&|CscDQ&TJSQwDKah(C zWG&btV7e$T*%_)*1)Bd~PV)aAH~_p}fJ98*g}HC$mgP9JcV~~tYR%l4IV7V!ePh}K zssB!`OkSlsFGCF^1=h6P89ulf&Wq zw;3^wb|)TSH_})NHW&SX?D-a4Ri%`7${XIaI}7=*%3w6>s|-M`84dbsQ>oBUtJT+w zGlL2)7gAXQoRJ$aLE5(J58pk1_khGpt=1Xqv}QxCp+;Y&trZ%Kx(2P0MzX|{g9Nww&VpphDQi-Ky`6it>CO9S<*kwQWrRq1I zDBWs$;Kr2C508CeamJDsAY*2=p#(%&g_kdE%)*^&ZgE&p;qT%xVc*h0ZM}3~;l;B` zCi}S68V@j_LOK1hL4~agk?fgBh#V^Iv|syU#(y?*i)_HYsMTr3Rskvh$Z$>wDki8< z%6^W9(mZLG=hUy3oGNfDoo7LjZhH#~5s0__QisRZw#s4?=0sHw4Jy{CP|cnMOx1TI z`YWi|qe3G4wi*(}{OGkzB-np_TIx5Bl6}wcZ=UKcqEta0}?wmlI$g&@&v2`)HqNi1FNG@P zqs;mS`E?GdiaRO>mP_gp(sLF6%$!oct-GYw1r?)IyqVdHOp$1MS#mD_+n)YUAI2?p z4taSsIosMS^5F;DR{L_C2heMUB@IW;R+Nj%=L2Dd9S00$`l+Nd4&1yO@k(6I^yAmCtiqsZ{phqMxWpYT?395!u;AQol)NT)o4v-qoK^8 zGZ`A@VY`SW$*7xKJAaW-tvBhkCY?}RV=z{ih~}W;4vQ(*kLTX>uFkabpvp;>CI7Fy z-8QCb{@7rhm$mRx-rJ?E)sv07*0>nEc5RhezEO2Lvv!W5R&S~!=l>&99!%*oxu~)5 zmV&PfO!;@@7v-Ivdt1)8Ig_)`%6cesSjMvSYt#OjHa)cyAM7n(;&DM0Tb8B5g^smc zqSF%;)I}zNXvfuOKwfqn*PBkkCr1Nd5F_=F#d=1+)-GK8U!R1 z*P<;B>5z6yIQoh`tipRD0e>rYbLf!58$dZg9iK}HvC z8Z@{Bqpd9Z4b&~EJZ?{BUyORd0cTb)>UPUfDTATUk#?5VJpRFHH}Va=0X0daVXc`~ z9K))2IYN5w*d9I=L6yBN$D5#P|A*2Gl_&r7@3%JdT|q6H1HPBa$p+UuO7ROqiU}QHs`UDk^SxDp=#BOu+nGjS= zW}%RMCxSZdbj{7%Qv-dkspXxNalp4KgNpGiRI`9Yl+z_tCy^P}5LE1EaoA_CaZS}< z)2Vsv^#A;I=^#mHyR-c_+~C^fd|RtjYQRsFWL0`e=$SE&QyouHe>! zzWJ`amq7tMBj@3qBeGA=x;iT(b7{uy8M*0=X%D8Qr=Eb1C;JO+%qjwNIm2FQ_^8@j z%?;h!#yl~ou$NJAxsYAs&i$G%E8ls*{EvIPI1k5}-mF%~J|hJrk#Z26%M=bXbhE50 z;j!Jhe|F#PsV5&jb#WKEsr}--R)^_N7* z1{I_*WV2MOga`MwEt-33N@kz8_n5@Uo{OcyB_17A$h%NClzmWd3(P*;an-KA-!;ZK zRm3$jwT*4W(8M7$Tfnb4E~wbVLK{n=Qh4O9=qKGfHox!Mzwb@V$bp{bu(aYqCuAN} z)}cC-78wGPf=Yr`TWcc<4{c(3Q1Oa|ewHX{Sbs7*+k=XEEF`n9tli()J|?L6$U+>$=MqKl^%8A-jQi%(`Q@9u3fj%izJ}FkQd}<}H=H&t2F)v$w z>OyUtOIL%(<#8aXDjcw4O$#c{u~5rWs6pp#=A(A$549J(U((Q;7`2LTdK(;+VlKA> zf{Lds#1CVh7=Q|BZgX$Evf`zoU5Jk_bz0^0G`HYgaC|t%0CDW^f^H%n9352rW1)`y zT|m@rc`>)H>{*0sQJqdIua_W1M|uX)U#hAbkMo%#BR4px7{xNu80ehrJ{rTIpkfIN zfuot{9R&U>XZV_bZF@5jV;6-*j0h^OuaL)5B}T_@#23;Vr%qjdYjI&$h3;|PTZ5+q zeY~;@8bwwW2Ng?KMo#H$)%VmPhg+oY!g|3#y7%n2F()DrqB zxs#VH?8ri(x!1N5b<`@OR+jCFDhvI9`_0?-=A0j6?-bt>Q%C#h0D*C!(8uIyB+Vgc z)X+8(W4S8yqqsV)+tJ4=S){v^cY$+($Glm2O;Ld;9(wePRqyoUgli=#U_6MWuTN zcX+w2QywJrh!E1f%zh(%HXYc(xtF3Icqv+ZWGIoMV7M{U)9ya7}rLC`BCh))<8=ziW6U5!MvXvVbEyyFW+;{o1^Ly zGEntStU1dqa^F7zl9Db_kAuV?R^r{&fq(>gdpTEjqE=RLThLC5{gtx~CYe}~9O&A# zp~*$z4U02^DiAqK8AbTPsqZU2Svu^r#Z$iT>LTM@nz0)sFlCnwZ5d9Nm9myeW22K^ zWbp!{wq*6;!6==44NLhll|O9PO3m(j_j^kR&fE|vU+({DZUhN}~FQ_(fi<^)^C z+Tm;k_!_Ovtsw8t5IV692WJ<%ETFW>7PVr6I4IZ{*))iwm@i`B8;#O~t18Fcyyn~3 zNwzrWq}5?rj!th>)58lk81%qx{C`BsGt~cY zSpK5CYjeNJotm>I`>E`svuv4nXMCG6Cq0<Fne*s>@m? ziN!$`Uzi1uOQ*!_k^40FPusKTmSZpE3qLi4L!A-AA_2TuL5%?mc|uU34I}o>UWk!w zr>LNUAm&s3^iJuX$Dh4!#)C(8@zBLNm4BI`mF=U+>jhc4CKO*QG-9Y`fkx7K(G!@$ zaX|%64CyRYz+vgvY>{61@tOIv4tu?e0gr2yGpZgxgvAhHk3NLuL4`&P#q3FN%{7~( z^S<7A2X9m-_!*aUtWl2sSpm2@ZB4w$A2E#eA=Q-m~QCJSz=oW)E%}x>fCFb zi(tLeDu+0c>_k4(A&^24hu!7|K2)*jWyFWlQ9pzaO)x$bH%4(K=Dab7{Qqp_F1vdf zyps?@B@e6|MuWqfojeS(YoW6Zq)Mc@Y0BC~a3DxglE6wNWnGS{)aHo)-zzD7EJgPf z6&5xY+>!r7zCO>Ldu7fmIhon!EHU%x%t0Avq~DPCeOguOA5&gQ;(4C=pAx5s6p}ak zGuQ)1;34${W@v0k!FWSGdtD>cFV@^NdHChmp5W(>C+ZQzz!VFoF;qwyw>UARpu8cM zy(SxS&C&}W9@pp1zYa;5T$NDDzClQoP-Lw3Cb2lAP`#m>g^@~M7ODCx%B5v>j^fM9 zR&wiJVDBJKph9)HHPHgwG!GbWnveqXhSp`w8#ZViBi)zx)a`>Cck^r2o3(ZNdO<7q zMpc)hJ&JTq{nBv6JWnT>7aGK}kb?HcCDd?S!lupA*llBWS#^83FM$}-VAH0{&7EGD%P$y+uWG#(yhTFSsnb^(4F+S4wjL-K zlUNZ_0Mro7($lO==uH9XijU2+rPmh3793Yl;9xjet+tLZJ%OX4JW0_IQi#xK56tJ8 z7K@}e-o0$e;!#UDMH7r#WQ;*f{f&z#C!dWV*OW^t3n}nr&WUsRXVUsI;|~vgb${$j zigU}1j(GTVpl(1Yo;1%~S%USY!_tgzxI5fpT}VM7;~r#l+=Jf2G}DF@Ct%zGC(i}- zApFZ5QtWdXDOt+nys>vtvWy|cI2YG-7|+dV(401TS?<^C-jDrw#I;N%f@xMBQjB&n z<=CUL#DFSE>@RVANb%i;WcILZQa!WAg%lTFXl75G$;13YasP!CpRMPdJY5jh#L*#@ zEid~vbI$R1O3!?{?}R}ochyl5?;F91EIV2h-5whSt*O$Q4d9a6dQvX80u!(Gx2z4y57uO+CLsuo2|9e@#4xIm& z6kL=4S^n6(^K&1}`5JOuZZ*?YS?pITXcluV&Bhr|PfW zr`fxI$W`}!)i3tLALnDFZG!ED%qIPHDyj8ATLDt6NlO9Azl=ByN&y|?$ZpI|n5<#U zsau>BQbE1hlR3E5JEYrRI%fLge;F0~QsbLC(YK^w4nUvtWz4Xk)f*RT7b}EM6QThs z#y3wi0K#`klk(Tz8j3AEuJt26-6bIv<(s`w00VxubeBb6a@xp+v318ew+wR4Xq&~2 z!zrA;WFQ*WX}4G&Qt`fdHXBJZ8pUQqMgHd5Y;1K(8w#Bj&$_Qp(DWaGfklgiuth4jgd zYZpIv!g;Yp$GI;vc^pffZAjEKq2ZGxTHz07fytVum88g2IVW*UNU_g`diFw_B-tGe z4Jl6AP&<}mP3cCbn~>s|4T!`TaXT8y= zuYuo6eT`r;)Xq2S;lM)A|CgooIi+ZO(NTr=f_(+0`To2Q^ZMns=4?j=fIjQe%oj3` z$v7eXlC)3K#;07CjN|{B{-G!;oC&&3m)b+n?SwifsgXoNn{Ryc3Xtl zj^jTkBRsqcRlQ#*?K-@3|7F|0=er;+MOC})me|1USs@i892*qI(%v3Kvrh=Akm2n0 z;Gmp;BmHIFS?_Ld{DyDXjh0_~P;m>YT7V807L^$-o#NDx3LVZKzkupbq-$@i%$iin z*=ckDP(IuyMQ1r44^%kF#M1TGtM3ZO(qO~WJtvw=XeE8 zd~1@l*U~V2*wL?b@$!uKRn+DLaTAb&rGl-~CY}(AlEzo3v#%i&-<+yhGyS~F-#F#U z#OSP*^OQuU8`kOM=tV5j$*;?B{&HIF=*w>sT7SXd93F}i$i)iw%Ne@yI_bK zs4IwQT>J6p#h{kgCJqUynBpbOD!b8pU|=Zf+RcGggu>{LlL9uHnArH4ZF};heO+$xYAyRVjTMicTrKzc8b~kbhC$vw6dF&&qio`G05D zQ(2=jeHou-)TZB>=1*OkQgzVzfA!E%6mGqS=`!oC|JK|v0`$B0*T3!>y>4x!C3*=A zq&+Sq*ul(EF+=0aSJO?t%|lr1XqK=q2ZhvzjC+&ah30K=*xJ0~WnmS3CPyO>U`>_=Yfq`7=0Z9@GS(2W$xd16@B^<#LB=s%U7NKGiDvH}Y z9X9uBArvKSuPJAl36UXsdfE%0=Dt+h)eyzGK=q&W!Xo+hW7l28EKqPet5RVP1;NMIY zY;+`VXq|mnzhkD1{pW^6XslL8i4ZhJEEQ!WDiAnR9T!!)b(ZGg>z(iZxaVBHAdFJD z*kmURUO@KnE|+s}t!UN(kgZwf1EShi#Rz9A7j0aV;1?lO z%DEm9QqjVhQdv}LcjLH23N@S+$1Tg8oj!Tru=md?JaE1y8pj<{(BVkBv8U)|Q|O++ zaf`==R6KEZ7-x^UT33WruG}1v#O?M)n$Oq%!!p&S*xQ9T&k}PT9;eHB zrpMlfs4_AYVtXj+w!NCYiP>nDlnoZRW)WSl&d7jW< zT3j{HP^)V&lR%<5q(G@z+X9tK=xuF*3Wu7tEiiIjy{aux!BbW;2&qWYOq&fV^??Ry)4DecHr?_e=TXqH@b@AGbcj78;hK(*8%1;2|K@31ei)N1jB!f~LQn91i zpQ_|hYA1wLz-Xr47kQ>?lFT9vsYuaGr6Xb0HPYQ%E}C@nYEu_;H?ECIgcOGahn#UI z)<_7w2#wArR0GRX~S1;7JdRNjk9a&2Z`re~f0yd(`d{gX>){@7J~F3t&4)na!vp&6IZmuTa5aY*Ic%pOUc zdFjj2y;Hv(^~@$cpRvdshtt_4SZxjmj-l|Z1LDmC6Ae=FA`T9z%$rM?C-cn4z0%gB z@6lP-E#lK!3&b)!a4m%INQ!UQ^b19Oo7oDw<;i*o%!qy&q?i+i0Hpc<>r?v7C~_2D zS@2}R5&84;w&i8!Hs;(4|G!hSZqNKKb6Un9()XnOU)rQpOUm^<5CA}Jr2=us?wGgaSIRk9S9I(4a5ty|ipOQJAB}Jsl~Ud*Z+O%0EQ~#|;@ue- zjEK7#OlFsZB@gD02RdDgR0H#z;_yUsI4LT;Iu;L+_ zsfsczdh?0Wt)>TVO!@q9?g_F0p>?t?vk3o>y)zGNqRjt)o8C8wpn$>v0xh(ZmK%ha zrlADdrY7MM71K7fku<4E0~F5zJb!D>A}A<`_pvB~C@PL}sd#@qR>cdvk9A#Fzv_DZ z-ZPUY8II8GGf7&%OaIu#Hih@|%ri63^EqDc<#s0+yiBm^%E=D8oj?`UBB`KHWJ(8eyDhiSVeiN(kg3vO)da%9|PH+CD0Y|Zt=GaEz7kTIn0LS>zGJ+Z>ZfT-Gvg15z*Ho*1RL6!2{#Dp$x7kcWDVc;j!S@nN&J7~Cq}AEZL)y%(C@FWcs_Tj4H0~3- zoa@tdkXhmUpL|kMfi}M*?@->@+{<(RCud-`BkQrOYzaHb3FyiTxAA<0Mf9bEeVp&6kJY7`x))6(25!=Sg z-4ZT5{jM(2?Y?da9G*TT)g|q|gWVENJXJqraD^ZIM7jBj+aK@^`5!q8<*-I&v{a*@ z7arzpsXGjYmr*&9$o}3Ew_NZ$nb=+29sBEk{@rSlGqH8eCUhi$+Ys^I)4L_$_ny?R zFwAkI^5cWXo~dcCMGzeA>C|p-VTLgkc9WwKvJF@aCGZGef-DDPTxw@3(B4~EV{R1a z9>|7OiM@+O_-q}%YeJn~1KZCnp}!BJ9#x$BR2g>|>(0`*%K9V`UAKhZPT!y*2u~-0Ym0+1F;hmvvd@-!dmH0 zZ}>XW#HfarJpP`sZqQS|DY@$>JoD~`R7ur{wBDIQRh8I`r(#hb7dPAnfic61R_VX;~q zE}h~>B*X%t&uay@=g?g?Ca(vCN8k#{|U`*YrN-P+dt zpIwIIU(3RYqeqW}k5#ZNO00-N#13sbI4cyYE7agHges(j@Lu@8_$E>_uo7(+T+O)^ zYO$gUGdXT_`8XZ~$Nyo1@Jsm9Xk0EGnCoHzR%ic`@R#6X0Zzm!-Gs#sLzNcl%fXCz zO?lws9ryZw__ecgx9HFqYAkjzFoKnxFb%aMeN``vufCnZe_LAlGDB2vv4h=&%Z?$? zq+G-mmHB~L`D}IZrT4ztr?VkMd8_OAK43E=_R@}b69PL_($t}fD%0<4RPKMEaaGmr z<6=|ISr-XM3IA5)3^BjM-Gs9a*EM_V*T~ni~*06<-dg1{UL{6 zpjfx=yCv*#`g*D^Y~6QD$mCS5`$F)y>st5S5@0!f+Fy~_)_u3c=$k%1!;4N->wc>{ z?Cwq16)z?U{zP5zEBm;^7U43=|4;DTW6g8t&;LD>wk757$g9rTn%ys}J@bXkK^aTa zccu4CJ1_O#l>bS|Q;bhG<0IYXi!E_WaK%;Br>bP_jR`X5u5pJ^#Z;9%;(Hj>fj2n& zlFy#$^Fq(aA-E_v&9X{g4DK+rn<+A2 zL)qGn?ap{aMUOkoJ*KMY30<#ba4_#QgBOv z$uuN|u;hf16iVC#ql|MMO}aqToVcFvGB*J$LoiMKkSI8@eXzsags%+I^x+Bg!7BHR zyVhv?X)NYIFBI=*%Q-^XT7_=RAs%hU?-s1V}|#E{(sA)%f3)DE@r z^Zt`Ao70R;SyQP1ce8-?jZZO6t{7jK6&5$KkWR^1MgRO6QhUAf!y$@GuKjLtj4q4@ zRqIGWA|cu2Fu@@U2}=j)9dJW(m>Mh%#kd?MYe}1>#VppjTUz*8ZnQVJ_~v~9hrkq; zS0bSn%$PcvnvxcjyyF)Ez-z?Z7BfqcbtM>dbEDw`7s?39d^NaFs-{0{M9Kcwbf<;(AX(A<+G!-Q<3~?B4QplC%|LWozTS1#rvQu zxays4JaL0j)$^7RYS=zF z_=lqwucrq7MS|ES;UlNY*(aTdZN@sbiB28~4VtF3S6o?%@8lWmmQbK+8Z?OTxHf2% zxn;7RLn;&L8t0bDdJc)+rF_QJ^Sh=VUqyJ@M7wG3v`M4HEnz*=hr9Vkm_&7zNVv~5 zT_vG8?oM4LL);RwGkqacvi9z3<-@nXQ+mdxn#eKls9sf~eHVYI5u*x1SL#4_*gBaC zHy}`{ZlY z8dWubva1B*bY(FB?ds}LQbxOh(M@NJGc=5Gt`4bbi3>3OyBewK_1wtKBla|Co{>ES z66N5{nfdqTU6X6hc`zqE`|PX@nO|g11pj|i z+6QT4QZG)~nR2q?qU1aA!EXM=>fHpcOo?|BkM1CdIh9+!JSESw^EBBP;3zm9A3Ad& zVhP9HR(l%`oIVL}nj||!r`%xW# zCCq}Q&oJ7v$dR>s7c1M=n~G*ST`XsLp{waH8jnMex(xdGIZ0tQi<+_yiy5o8Cq#x zsUfXh_DZdB6Am*a28j%zyA)5U+yvH)Yl1hyf1J4p4X6vNj7r>LpfgpOh&YJEm5G$Q z!!E^C{h6ZR1X}})a)*tBsrt`@s&C!Ez4_aVv#0fZ`|xDMQ6&vg1s?QfXDe(ig)}O1 zdhkmQbM?#IVH;nnavV`{qRVk4&bl<^I6=|zFUKi!OFVXI4*R0gsayLhUt40bU3=rw zP8LLTbwicXrx4rzbqTiErL~Dc#K%Zz%yTHgt8;2 zs|9f$OaRap2wn5CO zD5WWI5S8MUqx{#H_a8O>{oziOMtx&Q&a}9dk9g^@PyWr<;zVyd!4aW~wRWr<-sVyd!4$pE^bERh}U zmKYDy^qz_Og?9ee0!fQ+y*%ac1#?vBD7r|&@m%do-4f?vnijvX=0tV#SGgs|!*Vin z_xzK4@yB5kR<~+pZ)Q|guu3*Csw(&t8DN!MqtjGqRR%4-Ra?pEkgL`+b$XpqcaBa~ z%~Wa)8kIp~z_yGy5+g26Lv#^mAC*Hla~ofHcz2)Y<%1(twW{hWje++)ALu47#E>T^ zaTveCrcyUCA%;Zy^$P~z0pAj@ibdemh?N0msM1utz)OeJrZCqO-ei@vz5EyXKPlEQ}363$t$XLx=O|{yTV}9 z8fP0d;&Zr8qn|$8fZI}%&@b}HWcP;D&sTD%dheOOaA0Kh(M@vXk~pI$%0nUSY3g7`$jq)*;~mbw&8t)f zqh42{(-?GhGw|Za3tg|7UOjsrd9O2tRXTlT5j)B)69XI?RkHHc!aj4$zV^u+m8x1> zgN=$xKc6w`@kSEHQ=>7eDsM zEkyYhbJPYl%IJo{zNn4C`P%6+Ggh+`e;qs2P2j(fN>eNxmU{PH*7^tD-qDHFsP5fk z2rY3FCtV1op*x5|6PLCxbQAkr2&Q39ih|ENW54o|Nzcr?Z`f7x0^D_oupqF>AY{yg zU=cM91(OlhBDT2DN*~rraEZYLH!;M8YWncH@`WmH>juxbdsi4@v39laQZ%*j(N2_P zlhgsC$xOYy5%FqA6Z)6gsczzW3+V_rOQMdIJh8OKWU;B$D$PQ@LDlMD$|f-7qszxI zV<(T9G#RJWx*BnDC}&-){MS>=O-5&VC(@&QLL8oSBWZAc7-CoOA|9^GKQ~I<#P1dZ zpgE)m2hjTl&eHdavl_oGh|K`RkRtDADO>}-T^^%=pSu|`XX1Mc$@DR2W&X3=y!S3! z|G>gKV=1}O(u9s_Apz44FV+@bwororrvYMj3*9t@6XH}Swk}2ECU&l-JQXeAxts9z7d4RBN1t!!|Q{V$7`I&;NY@ z_bZr{zbbEY?svJ`oO`ps$)1q4D)ZCKQ!*OUA4vO0+NjhOsY6mOQE-Yu$rpA#36L%K zNQlW)?^*p1ry1)KTWP_L@koHlROus8b%N7J{XB%Ej7JbnRIpf-{nri3ZB<2QUH;r@ zv3Ued$J^e)PV)545edprM|Ip6)TKRX;oQ|%9xAG9iH$mSGtSM}tsF1C#SZZhJTq-p zOAsILtd{2#d08z%Ptg&1wn%fbzp-{eD{Z+mgetU#zoQ9tk6v2DTx-#=0=8 z<&p4`XD;$e5n=!F2ad$FQo!s(nZfM}@)=*rCY z^GK|52T|{%XA6$(xi>V$xdL}2%8y+{VmlNOsG3du-g?o(IaubrH^$$wnDk_ z^Sj-X*4!RB2pVOupaK!kl4`_1RW(N>=ZX>Tt<|cv)z!%3vi&_0KU}JfSa8ZW8o4JQ z))lX=SsGb;bn!*0liyH}#QRoGB5T7J%3CKs@YjNm(qzixGo2+Wb*%=;ZcUv^FXlk8 z2Ev}VGU8q65MHT#{M%=ikGS+WfQ+c-@u>dbrDoIO;$Tsxq`BY8>OEm+TUn`zZq?na z_DCFYoid)z8|=!*Hom(10KIP0^F z2W1XJ+k$Yxayv+Ye045Bg5rOmC5LxZMwCqG|33+7{el_!oAOigDsy}049vO)x&MlE zPufdqJyRE?Y)MH|OiuRv$#VaEodP?`Ls-_R$Dqn~2uBNW_Y;=$@p}aUybF zRUQe#`cj&^R*99$jF!owTL`k{(E$)#WcaJphN$up|6V*Id@L4b9GZ0-j@f{ot+G9}YjfNB6 z#u7~%IOa5&*{Pl|8k#O4CQf9nIxhxDr@n4izP4la zYo}R~J6SMMo-0TQQrZl#vbRU#*GmI+7sZ~)!^Aibv$B^*V%*!C`fZ8}!@YO*t!Cv4!mf+{p4JdcLx-ctUfSo2M<IwV(uB0kS2#)wg?&%LcnDSX)ZD%8nY9Urii#r%+0}KRUmE9y#(hs{`Fkn<= zwb|h^;B-0AeOk=-oq{$m9D>5lXncW4@Gk<`US!k*W*=-`J)C}D%!BRuo=#gMkJ0Ci zJS^bnI|M#HJH;b0AEpYbi&IwHgL}4k(E0Nx{Lyh{kQ)EVRK>xoR^nZ0)N9voYB`Mb_zEBKKu1@mC zw*dgrISEvnDncvFV;4o|!fa`}=p51Bo!EnFj|4wW6`doa>eA@k>7Fo@nx(;qh&x|U za?7~KFCF^vp_{kLjLyx`>QSvdN2OM4Yteo+&?DhXQ#Gpu=Q*CuDiXFdRg9lVjB~Tf zWRHX}Oij$YiM|28~?gk?^PKqi2}x1T?LT^+>SO^i^1i=Qw*L z{Av2YaAG*l9tm@LG4+lOTDuH7S>lnPsA)LPh$wV zgWu4ojoLXHbXxNb*KCbPrp+_C2Hk~JI?E%mc}|gWjn=%OT)ndX%md~unb$~`YqiQF z)809BeYz05s`P~IovDbv;Xx?x`^L*XJ?adZ*Mz?p40S3)jjj@X`nYYRbbMy6);NQy zGt5`d&{Ye~iw4%{2|GE{HI4?ik=?9ubhJmN-E-*DBqz%5S?vG6BB@|*{<^%6^U88p z<=mM4Qq~`t*Jiw#F(}=XwlVelR71*o#ixp@)&&(@6^UIs&Jzbj+kvixJ>LlL-?>b1hOh-me?%~V56ZPq{W3YZ=p*K z31yLa#|PL1G_DZEb23V zE)3D(Bpf$HK27imh1)8s*+aGw2&sl5uy&iZ9Wg}0w8l_qVu<2U;uS+I_ef~fG^uD& zUcx&@B*lR4up4QS9Tl_U^NCnpBxbe&@~+ggk25&I*J#R_)<@F#{r>9 zL-Z_Jq7&3{u)^UXc<0oNRaE`C!a|3Q>O%X(^)Lcz zZg+w}3WsxKaz!5NMM&O(po(N8@7x48T6lO1=FN-D*j3o!n>B@5WN|p#gkQmHnBS1J zBJqhVAO2Ax0erNnD$#<%p+T=%Kmb|TItq6NKGY10y5L{M7I`EV)^Y;Kw_U zZ6+u3=hzOzcwM;VoouN`VpmQ7XiaRhPS~NEj!P0eZ*NLb?%w(Kgh{#kl$nZKlYdVx#K&ZP;erwpF`mve7gXGLcS-k^ z-a`n)l)i#Qd$;Pm8R%&aw$I~=&>z*}3rHl;V@RaAlgPxci+j#lLOiC)IfsOHMb3GI zM}j+6QJffv`WxqSd#~9y{>{tA$bLs0oq!SZEqnwy3^%5VAQQFSE`lrpAk!3IhQ^h! z2r@E{gn*3DA5D{$s5x;+xv3t)Ifh^wYM3av3#nlqLPEv>=vRp{flU%)J%o%5$u#AW z!8uPjHOxcogrS=zDk17lEH!MnhqwhpG)=8YQ1k__aNK|kz6?zLt+PlBJ)iJsmjIdlXwVGYLpO0V~1u`w)rqO6YFGd?vh&Wye%u(=vMSKoUppGKd z(8k+s7P_25T(Pm;hI>i+`wZN(EG>|T?m&W> zrQf}`4eW8`8r!1vXc^p7F&bW)rizAZhBZlfqw)RQZe5h z=D@v;hX7pMoUVGwkHp?FhpENoL|WIxcamL*XAn{h&Y~&e)56p!lC$wFAt{AIzOn&q z3H-J+5*++-X`6i+g1|*gVKovaFme(?sAeQ7cx+SfZ{wd<4e+EA3;|mmmKGDbl0q|7 zwBjUB7*|c@pBJ3||6Bff=Xk=i>Oo>=0nFe6^)_GYcj$$aj)(3D5$6^-(VrM6qG z;=1AUPYtG41bF!?Dl!>D?vI7$CUhfW@t7UONNt(19+5pmd!40WaS=P%BcW*1^m~b; zi*vu%G>?Ruy_otMWc=EsZ2YRW$EO>6L=Klk^+;$%LdCws;sExH+@RQG^JbKMUuQAX z*4}Dn#xR8jlWVcLt*wYX(<6ar(^M9S^PKUkS-DCz%`p4k)})w#pqKKg&6$~(rf`YSUk3fAW^C%(i&(}ZL21)$O~osazWqIJ!^)a%GulTTUW#%Q zV=t`m>A7%*suD{2*Ag@K23gB8rvkjFAR-9zkvF%nt~t7+nOHlT$cTD zgrP+B7?d%RQV%f*#z<(2EX0eF*dhz!4h+fkCB~4P$RZ2={NFRFJt^OjdmyJG>z>RH zGbd#HCH-&d1JkUjkEQ&PqE)O-{wDcMd?dDCtkNqX$kK1$z~+0dtNK_*c_lPin#`B@ zN>3nOaF~}cWD#zsDJl~ct6JaZcKts4sUMeL6N?>Z1z7nF%@#PxJ3{-;5--8YLMi>+ zCN8U3?j=}R2&TFJgZsM|c5~Zyy>$DO9Vd4(wNakQBU-hfrnHfr;3Ys=sHQ2&A*vH^ z{xr%l=RaA;v`5QZE6-LNQI=HmEp} zd0Q`WP=#cgylqf&!t=IXVyg<>G<#7|cY^b_UShEd(KLD6py&kTZHIb^%_^i$q7Hve zgVgu`cirL_YR1Oo70CA3!|@ZHdCLj0C#1RpZo z*2)wbn=MW;TZ#Wi;-=jXc9sD3u6Ljoc}A)AdUM&4Y2=>06F@>r) z@K1&Rl|>GOh|woTN<5nF@J?()aEFl&3+Tx)rUO4g_Za_U z^r*F2_#i$~KYS+c7=P6$hA~1jfBx^2bWu`4S-unb{~@_oHyXVbm>ssv zOL();OB3oQ>tWMa?oQ7ekKX)iPGr4NzBzTq8L(H6Y|O(>^b+DLMAILeyM|KfCAe40 zP8KrZ-KvvC?Io00ToI>?W#6?PGNZi&Ba30shX}B+^0@52+rGXz=c&kdLsZ+9;8O-Y zoAnl;T!zj)WFe2Df|KLo?p}=La%13y&DB&dB44 zrt9>KQCFu`1LHN@pkb@L#D*1WY4SZGwdvgR7hgJa$LZI`P_3%Al2Oml)Cf-?Rh?#b zXbhDaJVauQsyPrtQd-%M`r>Jhp<|RW2?;7vi@xDe#4`8Cw+TTCc|XVG zz;+F_O3ZShd6`tuIN1^}v1o-_8V(>?E$e4-cl~PGd_`)hY_)oO8%bm~T1*ZH0=Z$q zBfY1DJNnczo`Vql=pC;vdENJQWWiBAK#nW-bT2V~#rzJJBR5V|-eF#e<12lj3)tX+ z+-}Qlzoq`XKJt7<^7!QQ|>frG_fPPW`Dv38{oc|vJ6x5stf_Y2QsWGkIv zGPl@U(e;6|ODG9%W-7v}2YMxjt|MhEfH+c*y%PIYnhsR*42X9J>JV?(w3Vf=oJ2RT zhg3o07=x}FFEbsCEGK&t$*j!G85vinA4ngYc5UjXsl!sPQGBVGiH~*X zuN4=239~mPgQ`|P>o~YHQtkq{v^AZ9vGA%T(a&h|mx!zLV^X=q5|1=(_B09MsxH&|?JKQ93eLmy2gtEeyE_=|FP^Yf4Y@$q}IUyt>Cn}*$nxDC`1cCajU zajLUD(@Q|WP)Ab`D$ZT{O})8?N9Occ^~H0Y!p@1HFX&3;iV& zy9`l($``vipDCq3SM*bC^am?n+AL0XxR-E!DNC6}iHRs>9^xhZUPz_c5Cx@v$Gvmj z#3lC^=EWqhq6^2+gB90ag5#yExE5t9KS@NzwO8U)OLK#XyNWKXxb_m`S{&qPDy}61 z=!%N#axZbSg?{?eQ(}vkSN8D||Tua$=toM zi9!c7=|T*jqWNA%v2T%YX$+zirNG2!yBXPwyu_v#QFi*+oWh*^xN^bvi;P7*Op&)6 zQN>{lZOBlz31I`A##@CLg3V%rOCD%_LU&l%Bo*JZ=M-8%c}gFeSdeOLZRWF!Kw7Hx zHD!|~@JY=!ILcv$Y!|wk_!MI>2Ep3B#PJt{rZ36EptmV+NV#!u$@2yAAGEw7kMR<_ zU_3YI%RW(aKZQH^m;Rb7mNiK8zKt%ykdRMBM|F{pXgvzI3aAfn6&UX&M!}FhpZ>w2 zJ~WKmSGw}5+OwaRBU@TEX&}k-5=LulHd)2oJozpnvcw@6%By53Ke&s#V}ISxzgtam zl-HqFpv8n%uZUCK&rAG)A$SnA;AHK6r*dz8`p%X&9-1lhrqG4b2nO-|zbPr-lKV<- zMvgXnW7ZE@XJuZW@mj{I>Gf$l({fW6r|e3}S4>QHCT;49<3C&D4KrO=WKhhXxQ#=V z{TrNp$!E{>d7)?I`HS-HCeMb%8z#H5b~yv;LK_mjH_U9M@;VG%l{)45;}>WBaP^_c zS0$=PU$~5;PVgCFW>>3`#pg3>RVvhV=t^`NgRX7{%zBUt)N7_!&z?uhJD9>MoxZY& z9qpCCU1`#?6=t2-MOGEIEhU zvH7cCF6(fnpI#6ALS-bi!yATKdXh2S!3!OC}+a{pTM^dk%Zeo7~Tqq_9f z!sX`c&TQs}#mGw$S5%0k*&&JRQ<><-7~*;g)if18M0MgDW5#=l(!u~G<{n%B*Q?fF zyd^ewof-)c-n%LI&fp)*(p%8k4qAT;tML+BRH$#KR^J`kWQb2HWjBVnv`@6j#JWwU zmzS8rQg&m6E)4hH*|)Y`swnI%QW@1|<-fcegFpZGPP#LxpaTAX-{t9YZ_N2Ar#k!p zWqp{{H&dNqPk$oq)wGgSPs$f58ihM~9X{FxUx=m>d@yA+Ra`uOwrDD04^u`{LxcVk zMN^9;F{aH+dsK!?sfAz%i%}${0$!#7nvIPG6+KJr3{ve3m`thQkL-&POhx$jNYZ! zl=t2-^AGLqw?+%W2ykl`WXwpq& zsGANb@896Q_Qylt$i5tET}=fn!-YymMmPP)Zl^jgv3$kV$)nz9Cu?tQS3Z5g&!3zz zTz)yRVZOnrsnM#L89IH9j<-9n(`)KfdT|iNjAov?dK6>O%nN=%V-SL>+FHZxYWxqY z_Y&_{jH#!LF?F+sB4Qbf#YNLlL>5=qHWUr_5)WAnL(b!1!x%36+;^@~Y{`qkJ)%cj ziw>(!uhUiLtFhkV;bn6PodIZu4^YzhBwfH}+DGXv2}-DQuya_{w4c z^kEfL;GfMsmUY38SO4mZJb1?>#t`G0j%L2mw>Ja_KPxdl13*_*O{$(onBF5{Ps%Jda!yV8O3|3So zwc(e0T z0e5d(Vs7WnH2838h-)r1#yU(k=OW}e1g--QW3cmP>1L4tVKvp;#oPz_Pzeth5!Xwp z$2e%u<;qv*-ZkN>Yq%CvKf4@)2RkFF8;Wv_YWTV1w~KkVQ>Uw?72t6GKY02m%D@mn zwmX_k@IG#Vm4LIsVaE3`A$GV9v*&mTE*O`KszS5d8nwaB{up#imxPGz;|x zRjY$3o4}NhE+4~;ojhjJWY`PT)rixtcx+BBx3eP4C1gCKC)IEa49|SVu8!4ofSn*(APfPVw`>Oc9EH z9B^lI^5_4Pk{%K2|8pi}+p?a{IyJK;TpgJ(KJ4q3-j=4);m8 z)KsBX;ibSAJ-_(X{#pGO@ZC(J!`(tyYcNMyCq;R`9HZ=cq)&pPrUCMayTgjIXO&xe ztkE92!5vE~?LB5S#73@kX2TVu zB|Zs}?E$(I#Tn+6D!>8%0p@XWspc>j2#xv7ZLyOWAUkyFV z8|D|&rME@pi8MF}qkZY>w?)MXH8=$%_bgkNx8v8?JnrhS)8jod z8-=hlu)+xT*-MPetl&bDLBsIPU z2Sr|q5vhv$eLeMcyYjUit6w|Kk{p`_Orj)*g`6~AiRUO)nYl2l-J#4pY(7c_UJxG} zC(6uYU1r|f8}?L{bzx4pG4T1#QisL4m_cbK`nAwE(&!Rg!EqAB*1nu#qg48w0sxPPj3|@Urmd|2|2JlM1Tym*m}&`*v<- z&fM%fvp&k2ka>B=D;X!HFGl`7OI4Fc5Ga@+H~(~8C)2c{WibU}Yu$Plpq5}$+sT}A!!hsZ?XiG33KGktVH5S$1+ zu}^}3rcaSUZk(#*hWRA8XZmd_1jZdt?30k5sY1fS)(=V(iYN9-@Xu8Bd_mQ5tmpGd zxX)Ded_Z9 zrI9W4g(=W7A{oirn@YLGHP*tlCCg;%o&(z`m;o`B!hRe9Be;gUut`w*WI8RAeb-GR zxb4}`KX7{eKKWX;M&1^PQ3(TJdAO!4*ML5)3Tg>&W-E2k()>C zY0x|)dk89=eu)2ql|g9EVGor^LNGB}Mgmlp;C0;XSKl*za$c5v!7yH`)T;FQ89V?9 z4L^FFPG6;})f#FHY>kijEhG1tA>-lPZ75ql;>V0DKtWA)6I{R?C-qgZgZ^giOE>5?ihxv%VGnO1x{QwzN!suaQ{r@_?|KE~# zUG8(a191LlvmVJh1>OHwr@xZkKdmYCxzs)>7R6=B?OqlIVXd=jLMrXf27~<3;W2vHET8cIl9>fM(pTh2lxm88xpC2Yr*w+ z`-jSjb1&L(o?>%ki6}qfS^KW0W@h|o-aFHYcp(q(MA<3aFOkVIFPkj1!)j}%YXpx) z1KZ>aW3TC&m4gbqf3tFtPXfqR%2>^}UBW$N(iWY6#@xskBg%M^=4XxOu4h)rB)bw6Jig)Qr8vFS$c-qthE&pPEe&6m+F_8JrRgjNgh$~oE> z#zD(^HEgnTtMlw-FF7~H=;}9^TLIaH>8u@*ds5S9Gq*U|GGCbHOl2=e&JBrl<{aab z$zu+QPEqdO`S#=I=k1a`(RFt&xuUNX_$(n0TE^#8U6ZGRnRQybrp!1p~j!`l(=qkI{8DB_5cH6WkSL41@; z@N8e$pn1h9a)vjZd*xsK{R5`WmpeR=z-x^`hhV)%Wvsz}^2{FIe-}|Qo=qZzCsjNX zXS&v4n5_r<=14=~$v%l?vz&av`tP_e?_M?kC0TK9@|{VaQk>zt$*-_y z`Uof+Ct(_QB5_yPWo=F6KElw3e){BTg8OL(`3OTBs_9E86l-T@C|5kwBWeD_$9bYf zd1p+wy6g`7MJ|iYZnfL6lWO%5S~hLdkMN39{E3==R`&4`&NiiOlz2Owu#E~DAH~cz zYK6l`aN?;Mt7P2hEuTKbJ$Cw!_VINiV}i-mc5gecMHYt>dlHAhBjFDmLF+E;QUs^3 zqpy?H)Qi2yM|kLX2I#2wCVw)#tdEe@F=+Z2-=E2>OX(wwcD#@l(_d%xp;_GSVe^{y zfAVI8?%*ijMyi&2a9jj01cQrj@CC zhk%zTYJZ*Zhl^-|HO)t$>=2Fk#ed$`EWTsahcda{B~`mVI{$8YqIGCnMyMMABfZ_; z!Vq(@$RjB85qi1IX+QWe_v)AhzyJ8|dU+$L4w0lHi!SvMLu`naafho9?ovKu>iJz$ zkFVs3?%XVGgpZg=Lo$6gi(=C3LglVEFTYUndUkBuSv1Rd43?cY-!a4RP2i}ojE`7K zLoGezBd*-%Sw3PcjmLZn{k=E#z(nQB$(OHv=iGI%nb9!$*|`{1OF^_6|K}1MRqYKH z(5)K5OK9LHy~W&853-sWMdZYZTGj#qAbKkI#6!dTT>SMvBA>RSi;}2NY1M=WAUgDU zA_7$M4@9)(JCT7UzSB@ECj)z5U*&y!F1t2&*6*^_q6#RSffe)r*CZ7v^8@)~^RCbR zGWX1!K=x-=Vr>Xh~DoI|O;ci`;_ERv~Y1Z&YEK8&bzQWclF>OVk268>`CU6XI6eZn0R)QkKnkWohE%mv^Q{% z-!bLQ>$5M7yLN}Lf_U*meFV`B@igscqWJX3<}0@^J$diyUv;q&Z*-Vk{2{FYkB|#p zj&}5G5J)$K(p29Cg+BNO_u)Lt-qSBv#zttJ-PvXrgAuqBoozhPcc_mTUdx))w0+ge zJ2N&;8FgLH*l3**d``eg(9Sn{<19vutRa-9*GQbyv^@-WpRV|-X?woyL}*lR9QCnp zIuz^@$7-mhIjoCn<4WZ85&LOMBBv-Z&P2|0ebckcdiS4{+EbA+lKNpg$f$hcnvcI; zeZe1yt6()TQ<>?S8dbFh_bTy;rUZV9v!MLshxS>se!cDRHHF2CxF1|+@okho^8fEb zIfI0ke%K-lerloksiO*-*{MEa8I1!zebq^q0YAn5e`ivGI{(JJzvrEa{QrA7Y1x{r z_RRg6r)Mloe>A-y&6>I|wP#9`;_2kCl1r1EG9v(}btK4b%o$Csqj-IjwVM)O>p0Oz zVB0daZ&;~({M%=ikGS+WRsV;r#7GtO5$d{*(QB#q5zsawfjy~jT+|FYm0P|%CC{_- zw8+7zD9<)Wsc%867DDr8YkQRSgM)npzKttVCSz3VPvv$^N*kxVc4y>KMHdi+r|4l! zR8$!Z^Ie!DJ~N?`exro$_X6&jH}9C;YrxBLRn`a&4zP}E;ImG_3@LvAAk=LrrAaW6 zyAyr-4_1Gmf6JdESLqBBD$QD(Emj1AaR#=!%(mqwGgA>3J=jNR+cFoyx<9ym1=kf^ zU3ZUM(G@B+{H?U89Gjz3tF^Tn19l(;xDA!`Z%#Bjd%5i&z2A1gx;nDTC~rW%i#3ST zT!M8v)JI_3kVpd)AXgtf`f-K{NJ!f>Fae3sRo{<#x1`^3@GMb1h>z)EG}$NNY15zX zx(Fsvb+wQIlT_GRC2+3cam z@wJLdEYiw|RKX{QDI7P$IHm~Etu|8=zTs?hvNL=VL^h584_Ux#KH;7?xVfp#Tq|>K zD|j4vqupjR31K+Ahb>ERhQ?z7PhISjk0MW#v=_=N!{BTgq0?mTns>P;zgvCHj1^mC zjzxtmm!VE&sL@qw)dtW7q-X(VuGTmMmA$I!Dvbd@W{p0Hy);dmb?91juQuyZzOaKd ztCg`gT9d4NwXo0JvagTR6BpGjp{myMRn{u~e8#B98&asRuF)7(6}oD02-qSYF@VM) zohJV;UJ1amxxMwutJ{CcJ^c806xTaC)yGC{mWA>6v-=4ZBKaJqR8zd^3UBCY-Mk1PJ8^uwy1p(9lZLdLpg_Xyp#Q{_^)DpIaV5 zYg8+s?l?*(`G_ktl+wTBmAl{K9@%mCNb{c5SghF!dt*DC8d`+lB04e{6bvE6=rF^a z4)cvanQTpFG5e|#vDSpLgido5j zP0mi58R_)Tp6QpM*l9u`J-^%YFJo=`O})8?N9Occ^+jj?;!*B!wKy@DS`ZDkBUgs- z51cpz+rA?!O9uKC!Tkd+BTalz)F03IU_ZYE!%ov86qNW;5%=h(zQuR+=c8aTa6M4- zZU5}4ehF!PAjR7ztZu_=Sv4gub*3(=J6F^;+(%gIh^*2)`9y6){>AN?xBktCM|wgJ zs)aZ|yBy*z6)B1sDR!f2fwKp0#&)p`5s6+)8#}~DAnKGs;Gop_+A^K%yHr*XSQL4@ zATZ&ZQwD*>e?a+*<8QYN_+|Uy>pbQ`V1hNrp_%5%C%&q>EC`JKJAs{J0Q98~UDh5| z>>~_x=+{s@dBh8#@VZ{(o(*J}Ggf|j_=2I^Hj;!HpIDdXd{_4N5ez!|Z)kcUgL+#& zeU#h!W7-{>C96Ao9z_?q?B;DALNAA@QG8>CgO;&tzj68q)*bCP^feo3jCzQB>d-Y0 z?z=hVzv9Bk?7u1Z5okN^U;3gGh5FEw+}>Lq42S6e($q6Lqpn(4rB$n{4S=f=S8He-L_Mj{cq;eir|)ce zadH)9YwLc#ECbGIH@2(@+qKt8j|T> z9gv(z@DP9g@0IjqQbB$GlliCS)#vWcJtJp&*7nRNGlr(GN_#x5N2)I6CY=7qC0~q> zb+a#alAl1uDMK&8Q$kmSUfA(|!WM^knwE#4_{6t7jPeuYIF!@eoYw8_ow`k9x_TaOw~Uy#Kq`_1<)VDkSDQ^}JPiL8D|`D1ejFV; zeJM6eqQDbwUom$3V%z)#vyP^hxzrbC&|bB2*IoB-x%jSX$z_jN6dEaj4(H*$hmW)oNdg_7kvyt@SS6zo2kp`RK8uiY9N^Z8m|R#dj!K z7n^vuZTxiX=n43AoxP1c$DbX_9j%~G%rGNf=k{i;%*lN2xz55yQC}FVHMsQ9bkbmI z1%{M=+tpj`b`d{n!9$WMG&dFVZlg_RM=|<=%;?BMu|N6|EDei`*#3S(p~v%?rqN1# z{PlaMp4+i}%&2=;{v`V_)?hU)0Xc)ewvb0)^77=xs}IaisPM>S%%{Frl`jps?e)R! znk9UaNSbuQ21$M)`(!L~6SxLQMG5}^JJ>HVSf{D46R&yt_V<*x{ruv;n{&SDWFr^l zf}Ue>;7JhN#j%WE;;mjvJ-1NFZCF{eZASa3PE;P+pVO<-0;!Lu z{BO!okbZ7V`Z8&f5CmXL{1QYsO;k=?AVRsFuSEarAl;g1eD^2_Rfey;c%d zALt(uE@z~v4gGhGx4Jn@OHaqVh{{WWu4WfHhZI1R!*yHrEDpb=Z z%tnSC<(KftwQ?3zqAE>?`w4d(yGHszPsPkF+>2lBpVfasXVpqkzLpJkG={hF6lF7- z#GSC?U`P4~hfWt%uYk3FfrIfdJ|x$n$#mwpzTxz#u0Op0Z|R{04Q90KY00jkfxQ#%?KVK#kSa`mbPYQX|o02Za@zKcIS(PBGWbtkM=ORK|`rBZu86Ikde&VkU-Sici38_Ud z_Y<3KXr;;Ricd7<@B6r`Z#sGB_W%7UHgik@j<8l;gu4z9I>Bx$n93OKCtll7P1DI4 zRQ-#O+gSM93R{w!E2sVK=vJ*Gsog*>Q4gG-IX@DDI+`@<-DmOeTr=zH>E zUGeIgrIC-;QQbCD?+kXjKg?s7kz?%+K1+TH6Ko)nmqCcqpVTd{Fq<-?^`gEz! zQk7on#43QGN-Q zyh+Zicade(2)~3%PJhw{Rpe~$e#4`8Cw+U8>}4|tFhTwlXkwP5{1`2rA+pFYzXV54 z!$2V`WBovGx8=6qQh#1ATWy`q>}olRfl}_5P{`>^uQ0FK+#c6?-!D9mk*&0I2+Bae zghoz-7a{W-N4$tX3?rw)i-aV`8!zIQV8-dwpCDg&xOn14{1U!6P3v)JK3lmPezs=y zIQ4$n3$>zJr>kTPvnvcnt#P&yj%ljeN*IRfr_VNMb+wE^S3TQ^KNt|#lo))=$;bgO za?kj|Yj2x%oow~m$bgZteu?AvY#FbMbw=*hkIyVP@X3If$vukBfw~%;^h^zwMnqa9 zHsJJ;?quyvrQG5gYvJ0GWwLe8v9!U-)W%c_s1ek0pl!tk{*cliHUy_~Xdw&wrV-rs z?B^diy?&p3ty-h11_Tk6u#hW@K@(9|kCFmK47_U~S}YjjTpc^YFERR-^HRQXKDYOp zedFJ}Y>ez7XlrpF8nwKSA>X!eIRC#-l9%uQ@5uWwuQZpOkSE3^EB`%h_L5?m48#K45{6Z$_UpQbsCJmKQl95%pDQ25YDpA z+>17xr`Q~M2BT~9KYS0sp5-S9dMIp>aWma+<@VZd`r7hUePo4E#bDLC*;O-ehN!E9 z*4C+u|Fe&urq2i&~<``(eq5amKpH3AC* z!VB*Vbu5BjgWZ8@EFnrFm}v91bpchYR-kfiXG=CEPGro z`iyU~_O=JP2VOsI@~;0G8hNPEEvQQU>?+Y2TZdzxezw}Eougq?TK!zr{P44+)IT$v z8=`L;Ok`JYk)I&$G1v52YDm8F$=>?|JOA>g{KsFl-PTls>^kq+;dEISp~C_8DL6cp z`3d13V(AmU5PJnT_LGb&XX!tTyfC7?2edZC09(wByqnuGMUU_k<~>ByZ$=?{3AcCW z#mYMmj+8Ch(2P6tm}*P>)5A-ceyj%BToXFsA%~`gW*qySt?0+GiPm|o zcwDyd>sSKSm$i<^a(8;(c=YC9kAt9&RbV zuvsO9d(>gjG%B5>dHND#ZM+Jrl}LRpBJ z5&3g%euAo}?5PZ@>@%19>%hN1nDPCd$PbLDHiHo|AN5S3o(F3=+Z-l~t%z@K!`l#D zYpr%`yVKOrfQBZ#7SWA}=DsG9t%sFMUlq=dHM)f9bEeVp&53;ZZ#XqLp3d zC&+)ura$*1k@_+B>X-$;|M>3u2(qJ`?`mL7f{iN?=X#2t(ElO2f#TQ@Oi!v0zRvBN zxGnkKBw0oUH3+qVB2#K`@xfv|d=0MFR;vXbF1)pJrFjVsdQR{`4SaKsRp9@hl+=_| za6$glc^~C|m1E3$Jacfy{PYLG{$G%KXUaDzQxw-Gzm$9`K1Bb8wkiTO$6iiLF-zhu zAaQL~<^D^9fj$VPxrKt;mP9wA5NbM9({x`D)d_DzK?gB`s6#qUav&%@kxW5CO^0Hd z%}P*l0-1ti{RET_$uw`Ypyd2#xq0thw*G;IcOJeUKZ=tc<%3q$;6fzk$PP9h^CCJ8 zm-z`W9lB`>az)*VWg!mp6PP$e)1)MWqAz$wxo6z9M%z#GI@{MpmoQMnNwE>hMZN$J z=qvPM4)+uOHk8s-2!@n$+<*(d3{3s4Go?|ki=(9u^%Lkeq|!%Nrh(7+{{OC9{6fvx z&ZI`Q#W`B2#!sl*5ZX@t#_MuABjASwzfB206c@({_+gCUhqy2VGcNPOEZ}z4EnI9l z`NGa_g(&Y@_}^OW*oN|lUkMfve_VoqfJg|$k9mlm!)#t+vbu!Fkg!8-K`n5T2<}A7 zs>z1abfaa7rO}10tjG=I)86<#A2{H*wBRf9?%}3Zh;K!|Io>0{4f4(Ad;vx18CMK3 zE}PSgGK!{B;mB@j6=CaP!DfB9tr zER;ayWhJ)$Y_L$kDG8Y#O35^H1Y(vWihYo=wZaYjN0H+VKPy`~UAtDmXuXdwx#d{MAY)1jf7db#Q<{<)O4p#!a^VRBqR#v~kL7 zcSfGd=+3Lyx3syOPB5fH7rZ<`i1AQK9}mEMCbB_yaDZUsWzOxoKe&Ab*A-k{caPk; z4Hd94b5v@zwiXrt0|SJq4wW<|_2l^!$2Q^s!Jk9o1R0AI-MLQPi&m>@YEM7$=t*KL58QlareSEADxbagXuVX*zsYo=Gvo=5sGn8GTZzOsld4Wx!k*y$53 zJYH7WQP(gdeQqG~sG@trtEy44UTak8=QBnzezmaA+_JBIk*7Jj`@EA`9v+ami_>QT5<$d0BOtLFr{4gmc8uqC z=YO9+{_(M~$z!R}&tPt37CBI+3;!YX{t?@8oc`(eQmXTQQ0~3tKBszS-`MChSZprX zNLYgWzX?eNBl0iEyFD*0SDWL{elNQyYgy)j%)*ST(mzk1p0+mixzs@^mnz;;3`{Og znt)S0dr=@vr(Z!+sv&L?VJYyqa>4eCj72?6GFSIuPB~1g!1b|NOlHPuM#(o{DqS{7 z6)qAFS@T$0oB4JWi-}R|Ysw}~U=VAKP$6y)gfaFj=%1Vz^fu)UDL3vddA=b2gO)es zae*-TUPi(=@g-1$0usVKecVDZ{;YiN)x4+A-gMfP5o(E|d&TL^7rEf`M9#2c-jxZ2 z3HDShfZ$!ki+p_0U+zu5dUFJkQN19Jsqu_}ghoG(dK07=`|ddIIaA%>L8rbIn`fBH z2CTFdAk#&Rx!ugp3Y-zz)~ra8q4U5*<;uyIuYKp-b+OS|EkN7_0PA8jo*-vcibh{2 z{#*hRFna@^vVz?lET_=u(PD0?cfd4@9T6aSdYOls`=)YFJT$z|#b5s;^0OwoC9DFa z2Z&+5o0(6%L8TKOO7kvKp&^AIfO43@}7;s9aHLo@v@6^k~Jjl=;0n}=xn%F_fl60;Kngft#b(PcmLogSIpIS42VbyeZ{wpS*q#Gq z!uAUgS~p};;Ca6sL zVmIeArS#{DVq^(Z0e)=*teD^&)h|GJ+p?Bv%11@qqnr8`-_ai>lqkmJE5;Y1aWiN} z{h!;btL?6)W_Cz`Ft%yCqlF2KcXxDPoV@O6LF93|qXPtpP1_wU`~%9pKeW%9^{emj z3l!__=zxTYP4!?A=qg><9UYL+vT1Bc#c-Q2fUf9{RtF@!Z2AH?#ba}7x%=kbvf=^v z8xg{V(FKG;@xsPX5FJz!n7<>rAcTlokEq-cqn?!k2}j#S^XL@TkuZ#%61j$=?j!qt`$VqQBlL%HIa9!c{b{#^EnXS&s8ci0hXw%M&fk~mlP4upZ)SDr#Y z*?f2T)*sXE&@5TqSuuKai|4<*xqM}xKo}EF)%G8Z{hw(22kMD$B|%WV&AtpEO>9$I zTCwUGw0+n^XQPZka_E2j-WDnA$M8;fug0XiN>wqgLo`*PkSH2GxN&b64iHu{q*4{$hor{0UAQPf*vJq{Rm3U?oqABE+;{z|j;igW z4?hdicIH>{1Z$JkvK%i^b1R4;tRRG zz^H7)R-KTLY5REvfhQWRW8Kf&J3ye%GB3=DMr$7bR_d@g7c+}cB8j~sHs&rMP2o^% z;Yr~_gSg!W_&r}@g7OmV7SZ0#_k3d;X$|q1N)d-|TG=Z=;MTN_*1KFAIB4 zpfbcxqH8!7E=zZ9I1UidH{K&u4adUh`o*c?xPO2caLbc-#x~{tnlCPV*89Hfpp>b_ zz6`hrE7%L{-~jR1hCKT0PMjIo(*ndb8!8vmM9qY2+?6co0P)qv2&jVf!4Y&73pzlI zw`E>|E@MFt4iLj_St~t#`+LgUetz-a%{kx1W|KU}g8e+nV~YL%sQ;gy@6CHRZ)ENT zIS=JzWE-+B&fK2yK}LT11!-GS|CKs3%x|{z5C$&O zbvqNGUD544G9ZBxt7SY25@VV?A|OE#%X!Wwq&d4L5GD|>$dEC)Yu@1OOFnz1&kH?e z-)0p&>!i_cvzdf5F@G9{0SJG~@d+$Rv89L?M|_T_k(*nE#tcIsOebE^OU{7yb5Gs1 zU|Hk-$7B!4&~6j^Mlhbk(CIwW&DIvpuv7twOEA)QOUzb#D<4ENSu4#}Q+o{@Vbp4s zW})7oYIQJW6PWVRX%A$z(+w($$7OL$v6jWruhUV<=P|{lfj4IEMM?0I?y4V5;^P zL9p@{AGfjax5*O+cEo0-h$m2FYFcp=2A^->-ne#6jAReT|8PAqqQe1O;xL-hjc zRjz#aX>NDRg-d(&wZ>+qtL;m{MwQMq%+ea#!t>3a|9dBWkW?@`|MI;1a{rM#F=u7= z%h`jnteIS9UPeRugK2xy2B#WQ?n}u~7?aoHqkpC^c3gk}&v7oL0-g)YDe?X569a^J z4&hYoR6*efzvOPadrbPc;;SOmdPbYB4CP+n9#Mfs#6y0q_NI1WS6X91XJfti4b))} z@HwtF@gVrpoI@?bujQCrb`ZGr6}>BJ`lbV#Nz=NE7R z@$1AV=yPbK>ckhPHU6FWgnW)hsTAsmp<-Kc`_WC(`a5lmcRr4B7PCYRFDJrJycA?2+-7&!gT~C2MbOS3Z5g&!3zze7)?a zvtho$sHp*na)wS{qpJmUvQDq5Q|ST1)zyL}s+p&*9>o|m^MW7H7zDN>pbN7F@`64< z43jaYo-)SNoto;|;Q`{Vj8!IQKek~EmwoO#*C@8+$-Yu*ogTnY9WIw%r>(@LsjF6L zYx(nk&*cA?RFIc{YMvwK*X;T%Tjre^pJWV8Z%lhMt!HXY%3Ud$3JX4R;w!#0z@F1V zV9}I59lgi-3XQehwQR0~Fs2bAqbZvcM;EuUxefx6mM1S>Wpf>b8Vz|gWpm=}#kZJ< zJ*$I&prMi`Ehnl>d!m=}>2uaq+;Za+u?d5UJP(`QRuYNnfyfZg33WNyAsqzzOvxn> z#}H>O0bAHXD9)7N08ys$!)5(`TK3zL!{<5H;D8PZzL~xju?xWg9TJu^6*xeMuXGtW zU}^^;J7YgWe*qMaU8v$-@Qqw}LHka}V*O0%Z!d;uPWamg-ESoQ)8QU#X(x5m zjqZ*B@wiUSpzF96?<&60{Y1yL03zn|}D) zo3_<+I!u;?R6NSE0C*>M+;~#a$14A9fOq2PjX}#uT>g0g@BI0{SJKN#`Ig*Qax-$Y z*&DNd$T}94no$&Q0O1p@pH*xOFD@4EELit#)Fexat^m+^H;xI z*7t@Ot>gw5s#Zz0EvS1zt}0vJK}=>Lm?k|Z3RWhzjIy+Yn9V{p&G9&>I*|aM4q`nE z$u#X`qT~bvd^(67Ekx75L#8DH;L}0;Xd#uRcqTZ%kQ!fr&*TnbNeiJ3)GxbfNvpZX z*8laY^%rl6%_Bk$t8bg7fjk-bQ>wHc*x3#`UJI+~Al9@{-%hQ5>ee;fu0PIbI)B(5 zu~B~{G+Lm6@M8d*`&gCDD|>ek_gYF;i@07+Ky+da(dpGeJbNiwE#$(SV70`M)xw|u zPfl8wR4_AtW8QapWw}@7Jf8gt{Qn%8J2SH~=A>Vp_EB1SsxxJ);*z0#k(A=%EY&a;=jM@? zXfU@nn_#TmY6l097z^0UEl#$qLt^qulcWkwb0RCJJ0zyB^r4xAS59|Gd|+uRr$cj{ z_{wR0hr}C}rplOT?^adDRUQAoy>pL_s=D`h9)!FD1q1=b0|+FL7{fy#XikSP2?I$c zotXeZCF^8nNCxJS&I1y>;u@gV)@lLI5wC#KA_}#|+g3r)+oOQsiz@eOueZL6_@Lrt zd9Cej1zq>M&pA7DSa!&`&t!sYC4WIOgx}t0?|t^~_4(=~VsByg*}>pym){sY;+_3t ze{OikeoYvz17h0#aH*bC5>h+v3H$>veg#u9N!01bvJ^~rKcL<&n2z2c>V#xjYN@(S z?JM$`sMDHdY3QK?ND29DqNvlIWsm=*0xTx#xPIiNbvfh%xBBkBc1%wCr;i` zI;tv3Lupm%9D;WaR%(}~${Pwq@K88+C)}wp7lA8cGQSGSA9r&kFv?ub+9&y?5K%y zw89F&siiCd6@C?uprw7PMg3F|P(4Kl7cyBMzsucO$#ut_l>Y#8tCR9&pW9w;P?{#{ zlzW?)=Qh=X!`F)jC4Dbb7Vf~llJuP|>Lh)cDhU<)vwr81@2>m7dnJv&TW@j*sW`kb`8dNuuz3S1r5mnk%*Y`t_#N?_4eQOEDiXFhUa z?GpKq@7~+eJfS82RX3poGC>f}vzTnQ`DRlc5D%6HhuQA15G)7zk)@%g&QxD-a#(H6 z<=QvQ)eg%dV3lgk4Gw$MF2>NkOt@(Ehz7d1k-y76^y$XuPQ)KlVz>8swx(J;S7Wtt zjS4rJYpSa@+Z2jOt;z1NS<9?uyR~sXMu1`3%r$jQOSpL!yV+znbH%k*+q{yfL!@+H z^!2bufyl7r!$E0*@5LMA4=X{(Ga2Kui4+BlaWUmJwEcp|wd}f;xqtoPd-2DW&!a6-6b%fMnUbgUL8QwYA922L`%iYoH#xxt)|1WGh?EWto0)3byKUxA6c1lq@yVw6 zHYc=rda`(usAB`)z zcRMu7#hAXXftPgEq+0&KkL71~b1QpT0tvpTX%y<8v~iimnDQ)JTZS^XmqJ^HPN{Vy z^ZC)=mXYMP4C4P^mi9r~AZyVBg>M#?7pzA9KQGUjdoXuIj*z`Sdk8py4-Yt=c{Z~w zV_o`F_~^IqD{2#C;9Wz0(j`^@RW0hsydma2h5D}me{jc%!^6gy4PM~CR{gcwEv$@J z`vtWCt@cZY>}9I`()^HKhh^dxq$Bk*Mf<3!eN_8(qZp(1GAT1t*LwR+a@(gzPkGm# zj=y9QbFDQJxvKm{svyEaw-6>;UgR#Qk>RF=^%x>HgXsT0~|DvQ+4%x!t{k;u)Tx7^Gbp3eCKj+Ksf zj~9+3Zr$F9+rLI|b8|Tju@(YHNGG_p-*er#`*tq;vEglI0eOJD3j&b)4C>zqq3Co`r@_l!h=a*?$S2;ve$bmAj7gfg zh{CiYc7(W+U#){%9`K?NP;h!&!0zhQciH1I>6ZJ;<(2i`;`_=v4cooQ6DC3~+&raB zkKB*~Z4ta9cu^<4yVAgm?EcB}zPy(nyR!ADaa%18BIv@IluOS%lAB=t%z2BWr(Du<)z+%QG?m?}9o6dvuhfBR!8bV&UcT zA3pr!uV4Sog7_9DbXl-Etaa8};)>a$qs17%IXd2eb&qV9cf9<>;o&bCFC-wIW3AZ^ z&1u|PW2j{|keYT}_MTMT7%|2ijv85k5B-UswfOrF-kh;F{_|y8fo)=p&&wpHN4?c= zfta2cgZvt*v`Af>`b05?=w;)9X-mBK#q>m-^sL3eS&=$1JyEAQ%Oa*nSAI&w^hBM= zEK~14T?nZZ(-UI7%m>nlJim?6J>`Dd^GeXH@yY>g)v3YQ^TtUzbr zp^W0YUo!Uqwg0hycrue7u8~Ab*LRP)x-C3Smxr7W}AiF zDk?;s!mOdCoI;GnR57MG%Lcxr`!AFK#liGD2mh?j=tMV?hCPqhODG{qFQ&T_@BtUP zHr1xgh%Y)#)XB%P7doXHzR@VgBxISS|LDnl2}%FW5_Mv;Hye0AKDeHL>eFLuCf_T$Co=A1|DqY_pKiGoOIrroa!A%P>A>tEJU-me?=tN73Cxy^_(Stq#1hP ztbFvXCyECDY(|m{4Fq9j0LNn}I!@A|&RIh8nvMJ3=6C+%_!sT@XOm;&B9GQN4_PQT zVgWkDIg7WgS^296`NwNZzI)pXS0u;E8k9c6qq|7|t-& z{;XGS;2&7=(YWG2y_lTYwT1<6n@7OY*CEW=pMK`468XuWk1X9ansh$9JS42&g3BYv zDw9(kh_tmw$4eAd&L|V8&R(xKGfEUs&e+VTn#sTZ*RF*N&+oi&29qrlC{ZdoYcr#A zqwK?GMv1b@F#x7!#@GN(Zx!zO$A)|(WXO8d&>%xGA8CqkJE)eep|C&z0H#*d%5c>L zH7c~ZISV$0QwRrw(HkU+FgNr7`mC8TI#r?+a}0pJ)*$22rCa&myi)q%-#gaEc>oF4 z$aL!*c2^m2t2{we9jJu{N{%r)R-(vqSj}=_&~gm-Ey(8A9UGk1{KQ3yvJy1B#v2I) z1K*E$`~h#kk7VKuiQ>CqBzxlmP`PvD$Cu69v32hxV^eGPxq}D;ID4YyR!CG;8HTev z*D$%O|GY_|^ZtwJnLZ*DA3uFIVj))pnG@6D^mh_A-oS@0!GfAL5S* z-}$U<@EK!9cLQSLf}m{@m0C8`z6z6F{QD;z-x-kgpT0Vvt0c%CMd#1gctp3ACn36r;?j7^DuQzcnq%t!e8olPDD0 z;DfnQKGfK<(lcyDl1P`05P}H6X(lDDpdz-r{UJ~z2|;od!6M?1KB6fB!Xjvx?p1;} zq85`9JqaIP2W=`F6YVVmsgHGeR(V_mo$2vAz2Ie%*M^@Xt~vP!cxAg#Ju7s;c-%$E zRixbmTw@SSQg9UIWLvAF$+R6!9+vx$slH7LNBFTO*LnVs9W|VTGJEkA1 zn@p%~xkSOxb3Phr>1n?tlvSIj_g-}j)na#BDR;qJ55=@z z_{NAgaOm$^<;Ds};ua`gWg~b6e_Mp?3Kw9M=<8QttR)(%6s_~<_a__2#km5`2^8j7^)egscN`Kd1w zaz?xY`P&MGo8S+DEbJzl^@2-{jN=s2dSJNX5GK^?CVq~-B3}D(c{tFCuDp_j@Wx7` z9u6-b0Wx&YP)YO(i6YZ+hhR@ZjEcO$@AAz~ zuXujrJI3wv1v<4NhiH{VmBe8lOM$;?UiSGm-gTy7@P|7tGJOec^$s*j1oQz;qD_lX zi{e4MO1W@Xhnt(m72Ac#N_RM1f*cx^7{>_Mb7+8w>&*heQy6dR4G3A@*piiSpsQlw!i zyZ@eAormaE5~WJRYL{GYI5q3W=1G zj%Ll$F`=906cH&Uim%4~k3B4&3L@nMiIS>eD*FYCn7aEBZ|h_KeV~VQNkaE+dtiB( zLmny=adYtf;owHEl&I`83}xvMQ&)lM>DfZ zXN=$&EtaUfF$`wuLQREF5S=+=bAHP-}t+D@1@}5sc`y5|uKB ztt`bQs;&IFP4bT7FDqwVB_?Nb)jC=rk~&aOt3-k6MEg;0MD>eda|QGA<=h3IR}RIU}S=afCaP| z!H48IiL6sPstT$ST&TgID#oyy{iT~iAT9C#3$k0&2DKFZTTy=DjDoKGL-~byOLOnc zIh8XWKj{DJpMm}v=%0c98R(yZ{uyAH0aVpfbUHk7_8doFwlz?6ItIX=B<(YermH2& zQiuI4tReL-nF7`j1*s!;mdSiEzf@R5@b%~A&!sgNjc9VWB8a|9kBKm60z zpWJ>v-*A$oM|!|CiK5kEG`p*m>X!Cd5>@7g*(?nL>aK*@8T^jZyYHU8o`IPlTlpi9V6u1m8}PsD3nzWDmX}`?^qm za@UDfuWkAy{<29Za$y7ZSnKMdSU_4s6GAYPYDB|a_6ir6TOmJn>xB2;+x}aF<}R|> P0F_>3s;;&)nC<@$JB8S2 literal 0 HcmV?d00001 From aa05c9c0f3d9e67f845364de9dfb6bfcfedc55d8 Mon Sep 17 00:00:00 2001 From: nhall6 Date: Thu, 27 Apr 2023 16:19:22 -0400 Subject: [PATCH 03/20] updating components module and functionality with target viewer from characterization --- NAMESPACE | 2 + R/components-data-viewer.R | 333 +++++----- R/description-cohorts.R | 585 +++++++----------- man/resultTableServer.Rd | 21 + man/resultTableViewer.Rd | 17 + tests/resources/pvDatabase/phevaluator.sqlite | Bin 819200 -> 1421312 bytes 6 files changed, 404 insertions(+), 554 deletions(-) create mode 100644 man/resultTableServer.Rd create mode 100644 man/resultTableViewer.Rd diff --git a/NAMESPACE b/NAMESPACE index bc01775c..88b2705f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -96,6 +96,8 @@ export(predictionSettingsViewer) export(predictionValidationServer) export(predictionValidationViewer) export(predictionViewer) +export(resultTableServer) +export(resultTableViewer) export(sccsHelperFile) export(sccsServer) export(sccsView) diff --git a/R/components-data-viewer.R b/R/components-data-viewer.R index 371c1a9c..c9762e58 100644 --- a/R/components-data-viewer.R +++ b/R/components-data-viewer.R @@ -1,50 +1,57 @@ + #inputs: data, named list of colDef options, where name is name of each column, - #potentially: +#potentially: #output: download buttons, table, and column selector +#' Result Table Viewer +#' +#' @param id string +#' +#' @return shiny module UI +#' @export +#' resultTableViewer <- function(id = "result-table") { ns <- shiny::NS(id) - shiny::div( - # UI - shinydashboard::box( - width = "100%", - title = shiny::span(shiny::icon("table"), "Table"), - shiny::fluidPage( - shiny::fluidRow( - shiny::column( - width = 8, - shiny::uiOutput(ns("columnSelector")) - ), - shiny::column( - width = 2, - shiny::downloadButton( - ns('dataFull'), - label = "Download (Full)", - icon = shiny::icon("download") - ) - ), - shiny::column( - width = 2, - shiny::actionButton( - ns('downloadDataFiltered'), - label = "Download (Filtered)", - icon = shiny::icon("download"), - onclick = paste0("Reactable.downloadDataCSV('", ns('dataFiltered'), - "', 'result-data-filtered-", Sys.Date(), ".csv')") - ) + shiny::div(# UI + shinydashboard::box( + width = "100%", + title = shiny::span(shiny::icon("table"), "Table"), + shiny::fluidPage( + shiny::fluidRow( + shiny::column(width = 7, + shiny::uiOutput(ns("columnSelector"))), + shiny::column( + width = 2, + shiny::downloadButton( + ns('downloadDataFull'), + label = "Download (Full)", + icon = shiny::icon("download") + ) + ), + shiny::column( + width = 3, + shiny::actionButton( + ns('downloadDataFiltered'), + label = "Download (Filtered)", + icon = shiny::icon("download"), + onclick = paste0( + "Reactable.downloadDataCSV('", + ns('resultData'), + "', 'result-data-filtered-", + Sys.Date(), + ".csv')" ) - ), - shiny::fluidRow( - reactable::reactableOutput( - outputId = ns("resultData") - ) + ) ) + ), + shiny::fluidRow( + shinycssloaders::withSpinner(reactable::reactableOutput(outputId = ns("resultData"))) ) ) - ) + )) } @@ -60,10 +67,10 @@ withTooltip <- function(value, tooltip, ...) { # example usage: # Define custom colDefs for the Name and Age columns # custom_colDefs <- list( -# mpg = colDef(align = "left", +# mpg = reactable::colDef(align = "left", # format = reactable::colFormat(digits = 2), # header = withTooltip("MPG column name", "MPG tooltip")), -# disp = colDef(align = "center", +# disp = reactable::colDef(align = "center", # header = withTooltip("Disp column name", "Disp tooltip")) # ) @@ -89,7 +96,8 @@ create_colDefs_list <- function(df, customColDefs = NULL) { } if (!is.null(customColDefs[[col_names[col]]]$tooltip)) { - colDefs_list[[col]]$header <- withTooltip(colDefs_list[[col]]$header, customColDefs[[col_names[col]]]$tooltip) + colDefs_list[[col]]$header <- + withTooltip(colDefs_list[[col]]$header, customColDefs[[col_names[col]]]$tooltip) } } } else { @@ -103,142 +111,115 @@ create_colDefs_list <- function(df, customColDefs = NULL) { return(colDefs_list) } - - -resultTableServer <- function( - id, #string - df, #data.frame - colDefsInput #list of colDefs, can use checkmate::assertList, need a check that makes sure names = columns -) { - shiny::moduleServer( - id, - function(input, output, session) { - - - output$columnSelector <- shiny::renderUI({ - - # Get the column names from the data frame displayed in the reactable - # cols <- if(is.null(input$dataCols)){ - # colnames(df) - # } - # else{ - # colnames(df[,input$dataCols]) - # } - # - shinyWidgets::pickerInput( - inputId = session$ns('dataCols'), - label = 'Select Columns to Display: ', - choices = colnames(df()), - selected = colnames(df()), - choicesOpt = list(style = rep_len("color: black;", 999)), - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ), - width = "50%" - ) - - }) - - #need to try adding browser() to all reactives to see why selected cols isnt working - - colDefs <- shiny::reactive(create_colDefs_list(df = df()[,input$dataCols], - customColDefs = colDefsInput) - ) - - - output$resultData <- - - reactable::renderReactable({ - if(is.null(input$dataCols)){ - data = df() - } - else{ - data = df()[,input$dataCols] - } - if(nrow(data)==0) - return(NULL) - - reactable::reactable(data, - columns = colDefs(), #these can be turned on/off and will overwrite colDef args - sortable = TRUE, - resizable = TRUE, - filterable = TRUE, - searchable = TRUE, - showPageSizeOptions = TRUE, - selection = "multiple", - outlined = TRUE, - showSortIcon = TRUE, - striped = TRUE, - highlight = TRUE, - defaultColDef = reactable::colDef( - align = "left") - ) - }) - - } - +ohdsiReactableTheme <- reactable::reactableTheme( + color = "white", + backgroundColor = "#003142", + stripedColor = "#333333", + highlightColor = "#f19119", + style = list( + fontFamily = "-apple-system, BlinkMacSystemFont, Segoe UI, + Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, + Segoe UI Emoji, Segoe UI Symbol" ) - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + #, + #headerStyle = list( + #) +) + + + +#' Result Table Server +#' +#' @param id string, table id must match resultsTableViewer function +#' @param df reactive that returns a data frame +#' @param colDefsInput named list of reactable::colDefs +#' +#' @return shiny module server +#' @export +#' +resultTableServer <- function(id, #string + df, #data.frame + colDefsInput +) #list of colDefs, can use checkmate::assertList, need a check that makes sure names = columns) { + shiny::moduleServer(id, + function(input, output, session) { + output$columnSelector <- shiny::renderUI({ + # Get the column names from the data frame displayed in the reactable + # cols <- if(is.null(input$dataCols)){ + # colnames(df) + # } + # else{ + # colnames(df[,input$dataCols]) + # } + # + shinyWidgets::pickerInput( + inputId = session$ns('dataCols'), + label = 'Select Columns to Display: ', + choices = colnames(df()), + selected = colnames(df()), + choicesOpt = list(style = rep_len("color: black;", 999)), + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ), + width = "75%" + ) + + }) + + #need to try adding browser() to all reactives to see why selected cols isnt working + + colDefs <- + shiny::reactive(create_colDefs_list(df = df()[, input$dataCols], + customColDefs = colDefsInput)) + + fullData <- shiny::reactive(df()) + + + output$resultData <- + + reactable::renderReactable({ + if (is.null(input$dataCols)) { + data = df() + } + else{ + data = df()[, input$dataCols] + } + if (nrow(data) == 0) + return(NULL) + + reactable::reactable( + data, + columns = colDefs(), + #these can be turned on/off and will overwrite colDef args + sortable = TRUE, + resizable = TRUE, + filterable = TRUE, + searchable = TRUE, + showPageSizeOptions = TRUE, + outlined = TRUE, + showSortIcon = TRUE, + striped = TRUE, + highlight = TRUE, + defaultColDef = reactable::colDef(align = "left") + #, experimental + #theme = ohdsiReactableTheme + ) + }) + + # download full data button + output$downloadDataFull <- shiny::downloadHandler( + filename = function() { + paste('data-full-', Sys.Date(), '.csv', sep = '') + }, + content = function(con) { + utils::write.csv(fullData(), con, + row.names = F) + } + ) + }) diff --git a/R/description-cohorts.R b/R/description-cohorts.R index 33675991..a8449926 100644 --- a/R/description-cohorts.R +++ b/R/description-cohorts.R @@ -23,7 +23,7 @@ #' The user specifies the id for the module #' #' @param id the unique reference id for the module -#' +#' #' @return #' The user interface to the description cohorts features #' @@ -31,7 +31,6 @@ descriptionTableViewer <- function(id) { ns <- shiny::NS(id) shiny::div( - shinydashboard::box( collapsible = TRUE, collapsed = TRUE, @@ -54,33 +53,7 @@ descriptionTableViewer <- function(id) { shiny::uiOutput(ns("TinputsText")), - resultTableViewer(ns("result-table")), - - # shinydashboard::box( - # status = 'info', - # width = "100%", - # # Title can include an icon - # title = shiny::tagList(shiny::icon("gear"), "Table"), - # - # shiny::checkboxGroupInput( - # inputId = ns("columnSelect"), - # label = "Columns to show:", - # inline = T, - # choices = c( - # "Mean" = "averageValue", - # "Count" = "countValue" - # ), - # selected = c("averageValue", "countValue") - # ), - # - # shiny::downloadButton( - # ns('downloadCohorts'), - # label = "Download" - # ), - # shinycssloaders::withSpinner( - # reactable::reactableOutput(ns('feTable')) - # ) - # ) + resultTableViewer(ns("result-table")) ) ) } @@ -93,328 +66,189 @@ descriptionTableViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database -#' @param mainPanelTab the current tab +#' @param mainPanelTab the current tab #' @param schema the database schema for the model results #' @param tablePrefix a string that appends the tables in the result schema #' @param cohortTablePrefix a string that appends the cohort table in the result schema #' @param databaseTable name of the database table -#' +#' #' @return #' The server to the cohorts features server #' #' @export -descriptionTableServer <- function( - id, - connectionHandler, - mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable = 'DATABASE_META_DATA' -) { - shiny::moduleServer( - id, - function(input, output, session) { - - #if(mainPanelTab() != 'Time To Event'){ - # return(invisible(NULL)) - #} - - inputVals <- getDecCohortsInputs( - connectionHandler, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable - ) - - # update UI - output$cohortInputs <- shiny::renderUI({ - - shiny::fluidPage( - shiny::fluidRow( - shiny::column( - width = 6, - shinyWidgets::pickerInput( - inputId = session$ns('targetIds'), - label = 'Targets: ', - choices = inputVals$cohortIds, - selected = inputVals$cohortIds, - choicesOpt = list(style = rep_len("color: black;", 999)), - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ), - shiny::column( - width = 6, - shinyWidgets::pickerInput( - inputId = session$ns('databaseId'), - label = 'Database: ', - choices = inputVals$databaseIds, - selected = 1, - choicesOpt = list(style = rep_len("color: black;", 999)), - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ) - ), - - shiny::actionButton( - inputId = session$ns('generate'), - label = 'Generate Report' - ) - ) - } - ) - - reactiveAllData <- shiny::reactiveVal(NULL) - - selectedInputs <- shiny::reactiveVal() - output$TinputsText <- shiny::renderUI(selectedInputs()) - - shiny::observeEvent( - eventExpr = input$generate, - { - if(length(input$targetIds) == 0 | is.null(input$databaseId)){ - print('Null ids value') - return(invisible(NULL)) - } - - selectedInputs( - shinydashboard::box( - status = 'warning', - width = "100%", - title = 'Selected:', - shiny::div( - shiny::fluidRow( - shiny::column( - width = 8, - shiny::tags$b("Target/s:"), - - paste( - names(inputVals$cohortIds)[inputVals$cohortIds %in% input$targetIds], - collapse = ',' - ) - - ), - shiny::column( - width = 4, - shiny::tags$b("Database:"), - names(inputVals$databaseIds)[inputVals$databaseIds == input$databaseId] - ) - ) - - ) - ) - ) - - # hide/show columns - make allData react - - - - - #)}, - # error = function(e){ - # shiny::showNotification(paste0('Error: ', e)); return(NULL) - # }) - - # reactiveAllData(allData) - # - # if(!is.null(allData)){ - # - # # do the plots reactively - # output$feTable <- reactable::renderReactable( - # { - # reactable::reactable( - # data = allData, - # filterable = TRUE, - # showPageSizeOptions = TRUE, - # pageSizeOptions = c(10, 50, 100,1000), - # defaultPageSize = 50, - # striped = TRUE, - # highlight = TRUE, - # - # columns = list( - # analysisName = reactable::colDef( - # filterInput = function(values, name) { - # shiny::tags$select( - # # Set to undefined to clear the filter - # onchange = sprintf("Reactable.setFilter('desc-analysis-select', '%s', event.target.value || undefined)", name), - # # "All" has an empty value to clear the filter, and is the default option - # shiny::tags$option(value = "", "All"), - # lapply(unique(values), shiny::tags$option), - # "aria-label" = sprintf("Filter %s", name), - # style = "width: 100%; height: 28px;" - # ) - # } - # ) - # ), - # elementId = "desc-analysis-select" - # - # - # ) - # } - # ) - # } else{ - # shiny::showNotification('data NULL') - # } - - } - ) - - - # custom_colDefs <- list( - # mpg = colDef(align = "left", - # format = reactable::colFormat(digits = 2), - # header = withTooltip("MPG column name", "MPG tooltip")), - # disp = colDef(align = "center", - # header = withTooltip("Disp column name", "Disp tooltip")) - # ) - - allData <- shiny::reactive({ - browser() - if(is.null(input$targetIds)){ - return(data.frame()) - } - getDesFEData( - targetIds = input$targetIds, - databaseId = input$databaseId, - connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, - cohortTablePrefix = cohortTablePrefix - ) - }) - - - custom_colDefs = NULL - #browser() - resultTableServer(id="result-table", - df=allData, - colDefsInput = custom_colDefs) - - - - - - - # observed the choices to update table - # shiny::observeEvent( - # eventExpr = input$columnSelect, - # { - # - # if(!is.null(reactiveAllData())){ - # # filter columns - # columnInd <- input$columnSelect # this tells us whether to include count/mean - # - # inds <- c() - # if(!'countValue' %in% columnInd){ - # #remove counts - # inds <- c(inds, grep('countValue', colnames(reactiveAllData()))) - # } - # if(!'averageValue' %in% columnInd){ - # #remove averages - # inds <- c(inds, grep('averageValue', colnames(reactiveAllData()))) - # } - # - # if(length(inds)>0){ - # allData <- reactiveAllData()[, -inds] - # } else{ - # allData <- reactiveAllData() - # } - # - # # do the plots reactively - # output$feTable <- reactable::renderReactable( - # { - # reactable::reactable( - # data = allData, - # filterable = TRUE, - # showPageSizeOptions = TRUE, - # pageSizeOptions = c(10, 50, 100,1000), - # defaultPageSize = 50, - # striped = TRUE, - # highlight = TRUE, - # - # columns = list( - # analysisName = reactable::colDef( - # filterInput = function(values, name) { - # shiny::tags$select( - # # Set to undefined to clear the filter - # onchange = sprintf("Reactable.setFilter('desc-analysis-select', '%s', event.target.value || undefined)", name), - # # "All" has an empty value to clear the filter, and is the default option - # shiny::tags$option(value = "", "All"), - # lapply(unique(values), shiny::tags$option), - # "aria-label" = sprintf("Filter %s", name), - # style = "width: 100%; height: 28px;" - # ) - # } - # ) - # ), - # elementId = "desc-analysis-select" - # - # ) - # } - # ) - # } else{ - # shiny::showNotification('data NULL') - # } - - } - ) - - - # download button - # output$downloadCohorts <- shiny::downloadHandler( - # filename = function() { - # paste('cohort-data-', Sys.Date(), '.csv', sep='') - # }, - # content = function(con) { - # utils::write.csv(reactiveAllData(), con) - # } - # ) - # - # - # return(invisible(NULL)) - # - # } - # ) +descriptionTableServer <- function(id, + connectionHandler, + mainPanelTab, + schema, + tablePrefix, + cohortTablePrefix, + databaseTable = 'DATABASE_META_DATA') { + shiny::moduleServer(id, + function(input, output, session) { + #if(mainPanelTab() != 'Time To Event'){ + # return(invisible(NULL)) + #} + + inputVals <- getDecCohortsInputs(connectionHandler, + schema, + tablePrefix, + cohortTablePrefix, + databaseTable) + + # update UI + output$cohortInputs <- shiny::renderUI({ + shiny::fluidPage( + shiny::fluidRow( + shiny::column( + width = 6, + shinyWidgets::pickerInput( + inputId = session$ns('targetIds'), + label = 'Targets: ', + choices = inputVals$cohortIds, + selected = inputVals$cohortIds, + choicesOpt = list(style = rep_len("color: black;", 999)), + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + shiny::column( + width = 6, + shinyWidgets::pickerInput( + inputId = session$ns('databaseId'), + label = 'Database: ', + choices = inputVals$databaseIds, + selected = 1, + choicesOpt = list(style = rep_len("color: black;", 999)), + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ) + ), + + shiny::actionButton(inputId = session$ns('generate'), + label = 'Generate Report') + ) + }) + + allData <- + shiny::eventReactive(#we care about returning this value, so we use eventReactive + eventExpr = input$generate, #could add complexity to event if desired + { + if (is.null(input$targetIds)) { + data.frame() + } + getDesFEData( + targetIds = input$targetIds, + databaseId = input$databaseId, + connectionHandler = connectionHandler, + schema = schema, + tablePrefix = tablePrefix, + cohortTablePrefix = cohortTablePrefix + ) + }) + + + selectedInputs <- shiny::reactiveVal() + output$TinputsText <- shiny::renderUI(selectedInputs()) + + shiny::observeEvent(eventExpr = input$generate, + { + if (length(input$targetIds) == 0 | is.null(input$databaseId)) { + print('Null ids value') + return(invisible(NULL)) + } + + selectedInputs( + shinydashboard::box( + status = 'warning', + width = "100%", + title = 'Selected:', + shiny::div(shiny::fluidRow( + shiny::column( + width = 8, + shiny::tags$b("Target/s:"), + + paste(names(inputVals$cohortIds)[inputVals$cohortIds %in% input$targetIds], + collapse = ',') + + ), + shiny::column( + width = 4, + shiny::tags$b("Database:"), + names(inputVals$databaseIds)[inputVals$databaseIds == input$databaseId] + ) + )) + ) + ) + + + + }) + + + #cols: covariateId, covariateName, analysisName, + #averageValue_"target", countValue_"target" + + custom_colDefs <- list( + covariateId = reactable::colDef( + header = withTooltip("Covariate ID", + "Unique identifier of the covariate") + ), + covariateName = reactable::colDef(header = withTooltip( + "Covariate Name", + "The name of the covariate" + )), + analysisName = reactable::colDef(header = withTooltip( + "Covariate Class", + "Class/type of the covariate" + )) + ) + + ##custom_colDefs = NULL + resultTableServer(id = "result-table", + df = allData, + colDefsInput = custom_colDefs) + + + + return(invisible(NULL)) + + }) + } -getDesFEData <- function( - targetIds, - databaseId, - connectionHandler, - schema, - tablePrefix, - cohortTablePrefix -){ - +getDesFEData <- function(targetIds, + databaseId, + connectionHandler, + schema, + tablePrefix, + cohortTablePrefix) { + # shiny::withProgress(message = 'Getting target comparison data', value = 0, { -# shiny::withProgress(message = 'Getting target comparison data', value = 0, { - - sql <- "select distinct ref.covariate_id, ref.covariate_name, an.analysis_name, c.cohort_name, covs.COUNT_VALUE, covs.AVERAGE_VALUE + sql <- + "select distinct ref.covariate_id, ref.covariate_name, an.analysis_name, c.cohort_name, covs.COUNT_VALUE, covs.AVERAGE_VALUE from ( - select co.RUN_ID, cd.TARGET_COHORT_ID as COHORT_DEFINITION_ID, co.COVARIATE_ID, + select co.RUN_ID, cd.TARGET_COHORT_ID as COHORT_DEFINITION_ID, co.COVARIATE_ID, co.SUM_VALUE as COUNT_VALUE, co.AVERAGE_VALUE*100 as AVERAGE_VALUE from @result_schema.@table_prefixCOVARIATES co - inner join - (select * from @result_schema.@table_prefixcohort_details - where DATABASE_ID = '@database_id' and + inner join + (select * from @result_schema.@table_prefixcohort_details + where DATABASE_ID = '@database_id' and TARGET_COHORT_ID in (@cohort_ids) and COHORT_TYPE = 'T' ) as cd on co.COHORT_DEFINITION_ID = cd.COHORT_DEFINITION_ID @@ -422,9 +256,9 @@ getDesFEData <- function( union select cc.RUN_ID, cds.TARGET_COHORT_ID as COHORT_DEFINITION_ID, cc.COVARIATE_ID, cc.COUNT_VALUE, cc.AVERAGE_VALUE from @result_schema.@table_prefixCOVARIATES_continuous cc - inner join - (select * from @result_schema.@table_prefixcohort_details - where DATABASE_ID = '@database_id' and + inner join + (select * from @result_schema.@table_prefixcohort_details + where DATABASE_ID = '@database_id' and TARGET_COHORT_ID in (@cohort_ids) and COHORT_TYPE = 'T' ) as cds on cc.COHORT_DEFINITION_ID = cds.COHORT_DEFINITION_ID @@ -432,20 +266,20 @@ getDesFEData <- function( ) covs inner join @result_schema.@table_prefixcovariate_ref ref - on covs.RUN_ID = ref.RUN_ID and + on covs.RUN_ID = ref.RUN_ID and covs.COVARIATE_ID = ref.COVARIATE_ID inner join @result_schema.@table_prefixanalysis_ref an - on an.RUN_ID = ref.RUN_ID and + on an.RUN_ID = ref.RUN_ID and an.analysis_id = ref.analysis_id inner join @result_schema.@cohort_table_prefixcohort_definition c on c.cohort_definition_id = covs.COHORT_DEFINITION_ID ; " -# shiny::incProgress(1/3, detail = paste("Created SQL - Extracting...")) + # shiny::incProgress(1/3, detail = paste("Created SQL - Extracting...")) resultTable <- connectionHandler$queryDb( - sql = sql, + sql = sql, result_schema = schema, table_prefix = tablePrefix, cohort_table_prefix = cohortTablePrefix, @@ -453,41 +287,41 @@ getDesFEData <- function( database_id = databaseId ) -# shiny::incProgress(2/3, detail = paste("Formating")) + # shiny::incProgress(2/3, detail = paste("Formating")) #format - resultTable$averageValue <- round(resultTable$averageValue, digits = 2) - - resultTable <- resultTable %>% + resultTable$averageValue <- + round(resultTable$averageValue, digits = 2) + + resultTable <- resultTable %>% tidyr::pivot_wider( - names_from = "cohortName", #.data$cohortName, - values_from = c("averageValue", "countValue"), #c(.data$averageValue, .data$countValue), + names_from = "cohortName", + #.data$cohortName, + values_from = c("averageValue", "countValue"), + #c(.data$averageValue, .data$countValue), id_cols = c("covariateId", "covariateName", "analysisName") #c(.data$covariateId, .data$covariateName, .data$analysisName) - ) + ) resultTable$analysisName <- as.factor(resultTable$analysisName) -# shiny::incProgress(3/3, detail = paste("Done")) + # shiny::incProgress(3/3, detail = paste("Done")) - # }) + # }) return(resultTable) } -getDecCohortsInputs <- function( - connectionHandler, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable -){ +getDecCohortsInputs <- function(connectionHandler, + schema, + tablePrefix, + cohortTablePrefix, + databaseTable) { + #shiny::withProgress(message = 'Getting target comparison inputs', value = 0, { - #shiny::withProgress(message = 'Getting target comparison inputs', value = 0, { - - - sql <- ' select distinct c.cohort_definition_id, c.cohort_name from + sql <- + ' select distinct c.cohort_definition_id, c.cohort_name from @result_schema.@cohort_table_prefixcohort_definition c inner join (select distinct TARGET_COHORT_ID as id @@ -495,11 +329,11 @@ getDecCohortsInputs <- function( ) ids on ids.id = c.cohort_definition_id ;' - + #shiny::incProgress(1/4, detail = paste("Extracting targetIds")) - + idVals <- connectionHandler$queryDb( - sql = sql, + sql = sql, result_schema = schema, table_prefix = tablePrefix, cohort_table_prefix = cohortTablePrefix @@ -510,28 +344,23 @@ getDecCohortsInputs <- function( #shiny::incProgress(2/4, detail = paste("Extracted targetIds")) - sql <- 'select d.database_id, d.cdm_source_abbreviation as database_name + sql <- + 'select d.database_id, d.cdm_source_abbreviation as database_name from @result_schema.@database_table d;' #shiny::incProgress(3/4, detail = paste("Extracting databaseIds")) - database <- connectionHandler$queryDb( - sql = sql, - result_schema = schema, - database_table = databaseTable - ) + database <- connectionHandler$queryDb(sql = sql, + result_schema = schema, + database_table = databaseTable) databaseIds <- database$databaseId names(databaseIds) <- database$databaseName #shiny::incProgress(4/4, detail = paste("Done")) -# }) + # }) - return( - list( - cohortIds = ids, - databaseIds = databaseIds - ) - ) + return(list(cohortIds = ids, + databaseIds = databaseIds)) } diff --git a/man/resultTableServer.Rd b/man/resultTableServer.Rd new file mode 100644 index 00000000..ab44eeea --- /dev/null +++ b/man/resultTableServer.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/components-data-viewer.R +\name{resultTableServer} +\alias{resultTableServer} +\title{Result Table Server} +\usage{ +resultTableServer(id, df, colDefsInput) +} +\arguments{ +\item{id}{string, table id must match resultsTableViewer function} + +\item{df}{reactive that returns a data frame} + +\item{colDefsInput}{named list of reactable::colDefs} +} +\value{ +shiny module server +} +\description{ +Result Table Server +} diff --git a/man/resultTableViewer.Rd b/man/resultTableViewer.Rd new file mode 100644 index 00000000..2b6745a4 --- /dev/null +++ b/man/resultTableViewer.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/components-data-viewer.R +\name{resultTableViewer} +\alias{resultTableViewer} +\title{Result Table Viewer} +\usage{ +resultTableViewer(id = "result-table") +} +\arguments{ +\item{id}{string} +} +\value{ +shiny module UI +} +\description{ +Result Table Viewer +} diff --git a/tests/resources/pvDatabase/phevaluator.sqlite b/tests/resources/pvDatabase/phevaluator.sqlite index 9b4bf17c56b9b33106ba83cbba8b33014d3d3358..5fff4fd096794e97ad262fa8e87b6bdc43b4da10 100644 GIT binary patch literal 1421312 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%0A&gp$ZBABJo$izu=FD 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

=3?T4ql$iAEnzfKtR=HDbf2LYmR~_h#)}s^v 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\bdh|VN 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

T?Zep>uhe}!&GUb(f6iBYS2u3_*5{WV4yIqO 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%0A&gp$ZBABJo$izu=FD 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 Date: Fri, 5 May 2023 12:50:40 -0400 Subject: [PATCH 05/20] Fixing cohort generator attrition table and plot --- R/cohortgenerator-main.R | 532 ++++++++++++++++++--------- R/helpers-cohortGeneratorDataPulls.R | 33 +- 2 files changed, 374 insertions(+), 191 deletions(-) diff --git a/R/cohortgenerator-main.R b/R/cohortgenerator-main.R index 30d5b042..5c723953 100644 --- a/R/cohortgenerator-main.R +++ b/R/cohortgenerator-main.R @@ -50,7 +50,7 @@ cohortGeneratorViewer <- function(id) { shinydashboard::box( status = 'info', width = '100%', - title = shiny::span( shiny::icon("user-gear"),'Cohort Generator Viewer'), + title = shiny::span( shiny::icon("user-gear"),'Cohorts'), solidHeader = TRUE, shinydashboard::box( @@ -71,43 +71,50 @@ cohortGeneratorViewer <- function(id) { title = "Cohort Counts", shinydashboard::box( - status = 'info', + collapsible = T, + collapsed = F, width = '100%', title = shiny::span( shiny::icon("file-arrow-down"),'Download Data'), - solidHeader = TRUE, + #solidHeader = TRUE, shiny::downloadButton( - ns('downloadCohortCounts'), - label = "Download", + ns('downloadCohortCountsFull'), + label = "Download (Full)", icon = shiny::icon("download") + ), + + shiny::actionButton( + ns('downloadCohortCountsFiltered'), + label = "Download (Filtered)", + icon = shiny::icon("download"), + onclick = paste0("Reactable.downloadDataCSV('", ns('cohortCounts'), + "', 'cohort-count-data-filtered-", Sys.Date(), ".csv')") ) ), shinydashboard::box( - status = 'info', width = '100%', title = shiny::span( shiny::icon("table"), 'Counts Table'), - solidHeader = TRUE, + #solidHeader = TRUE, + + shiny::uiOutput(ns("selectColsCohortCounts") + ), reactable::reactableOutput( outputId = ns("cohortCounts") ) ) - # , - # shiny::downloadButton( - # ns('downloadCohortCounts'), - # label = "Download" - # ) ), shiny::tabPanel( title = "Cohort Generation", shinydashboard::box( - status = 'info', + collapsible = T, + collapsed = F, width = '100%', title = shiny::span( shiny::icon("file-arrow-down"),'Download Data'), - solidHeader = TRUE, + #solidHeader = TRUE, shiny::downloadButton( ns('downloadCohortGeneration'), @@ -120,7 +127,7 @@ cohortGeneratorViewer <- function(id) { status = 'info', width = '100%', title = shiny::span( shiny::icon("table"), 'Generation Table'), - solidHeader = TRUE, + #solidHeader = TRUE, reactable::reactableOutput( outputId = ns("cohortGeneration") @@ -133,19 +140,27 @@ cohortGeneratorViewer <- function(id) { , shinydashboard::box( - status = 'info', + collapsible = T, + collapsed = F, width = '100%', title = shiny::span( shiny::icon("gear"), 'Options'), - solidHeader = TRUE, + #solidHeader = TRUE, shiny::uiOutput(ns('attritionTableSelect')) ), + shiny::conditionalPanel( + condition = "input.generate != 0", + ns = ns, + + shiny::uiOutput(ns("inputsText")), + shinydashboard::box( - status = 'info', + collapsible = T, + collapsed = F, width = '100%', title = shiny::span( shiny::icon("file-arrow-down"),'Download Data'), - solidHeader = TRUE, + #solidHeader = TRUE, shiny::downloadButton( ns('downloadAttritionTable'), @@ -154,11 +169,13 @@ cohortGeneratorViewer <- function(id) { ) ), + + shinydashboard::box( status = 'info', width = '100%', title = shiny::span( shiny::icon("table"), 'Attrition Table'), - solidHeader = TRUE, + #solidHeader = TRUE, reactable::reactableOutput(ns('attritionTable')) ), @@ -167,11 +184,12 @@ cohortGeneratorViewer <- function(id) { status = 'info', width = '100%', title = shiny::span( shiny::icon("chart-area"), 'Attrition Plot'), - solidHeader = TRUE, + #solidHeader = TRUE, plotly::plotlyOutput(ns('attritionPlot')) ) ) + ) ) ) ) @@ -214,66 +232,202 @@ cohortGeneratorServer <- function( resultsSchema <- resultDatabaseSettings$schema - output$cohortCounts <- reactable::renderReactable({ - data <- getCohortGeneratorCohortCounts( - connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix, - databaseTable = resultDatabaseSettings$databaseTable, - databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix - ) %>% - dplyr::select("cdmSourceName", - "cohortId", - "cohortName", - "cohortSubjects", - "cohortEntries") - reactable::reactable(data, - columns = list( - # Render a "show details" button in the last column of the table. - # This button won't do anything by itself, but will trigger the custom - # click action on the column. - cdmSourceName = reactable::colDef( - header = withTooltip( - "Database Name", - "The name of the database" - )), - cohortId = reactable::colDef( - header = withTooltip( - "Cohort ID", - "The unique numeric identifier of the cohort" - )), - cohortName = reactable::colDef( - header = withTooltip( - "Cohort Name", - "The name of the cohort" - )), - cohortSubjects = reactable::colDef( - header = withTooltip( - "Number of Subjects", - "The number of distinct subjects in the cohort" - ), - format = reactable::colFormat(separators = TRUE - )), - cohortEntries = reactable::colDef( - header = withTooltip( - "Number of Records", - "The number of records in the cohort" - ), - format = reactable::colFormat(separators = TRUE - )) - ), - filterable = TRUE, - sortable = TRUE, - defaultColDef = reactable::colDef( - align = "left" - ) + inputColsCohortCounts <- colnames(getCohortGeneratorCohortCounts( + connectionHandler = connectionHandler, + resultsSchema = resultsSchema, + tablePrefix = resultDatabaseSettings$tablePrefix, + databaseTable = resultDatabaseSettings$databaseTable, + databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix + ) %>% + dplyr::select("cdmSourceName", + "cohortId", + "cohortName", + "cohortSubjects", + "cohortEntries") + ) + + names(inputColsCohortCounts) <- c("Database Name", + "Cohort ID", + "Cohort Name", + "Number of Subjects", + "Number of Records") + + output$selectColsCohortCounts <- shiny::renderUI({ + + shinyWidgets::pickerInput( + inputId = session$ns('cohortCountsCols'), + label = 'Select Columns to Display: ', + choices = inputColsCohortCounts, + selected = inputColsCohortCounts, + choicesOpt = list(style = rep_len("color: black;", 999)), + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ), + width = "50%" + ) + + }) + + # output$cohortCounts <- reactable::renderReactable({ + # data <- getCohortGeneratorCohortCounts( + # connectionHandler = connectionHandler, + # resultsSchema = resultsSchema, + # tablePrefix = resultDatabaseSettings$tablePrefix, + # databaseTable = resultDatabaseSettings$databaseTable, + # databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix + # ) %>% + # dplyr::select("cdmSourceName", + # "cohortId", + # "cohortName", + # "cohortSubjects", + # "cohortEntries") + # + # data2 <- data %>% + # dplyr::select(input$cohortCountsCols) + # + # tryCatch({ + # reactable::reactable(data2, + # columns = list( + # # Render a "show details" button in the last column of the table. + # # This button won't do anything by itself, but will trigger the custom + # # click action on the column. + # cdmSourceName = reactable::colDef( + # header = withTooltip( + # "Database Name", + # "The name of the database" + # )), + # cohortId = reactable::colDef( + # header = withTooltip( + # "Cohort ID", + # "The unique numeric identifier of the cohort" + # )), + # cohortName = reactable::colDef( + # header = withTooltip( + # "Cohort Name", + # "The name of the cohort" + # )), + # cohortSubjects = reactable::colDef( + # header = withTooltip( + # "Number of Subjects", + # "The number of distinct subjects in the cohort" + # ), + # format = reactable::colFormat(separators = TRUE + # )), + # cohortEntries = reactable::colDef( + # header = withTooltip( + # "Number of Records", + # "The number of records in the cohort" + # ), + # format = reactable::colFormat(separators = TRUE + # )) + # ), + # filterable = TRUE, + # sortable = TRUE, + # resizable = T, + # searchable = T, + # striped = T, + # defaultColDef = reactable::colDef( + # align = "left" + # ) + # )}, + # error = function(e){ + # shiny::showNotification(paste0('Error: ', + # "Please select at least one column to display")); return(NULL) + # } + # ) + # }) + + # + data <- getCohortGeneratorCohortCounts( + connectionHandler = connectionHandler, + resultsSchema = resultsSchema, + tablePrefix = resultDatabaseSettings$tablePrefix, + databaseTable = resultDatabaseSettings$databaseTable, + databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix + ) %>% + dplyr::select("cdmSourceName", + "cohortId", + "cohortName", + "cohortSubjects", + "cohortEntries") + + + rtable <- shiny::reactive({ + + reactable::reactable(data %>% + dplyr::select(input$cohortCountsCols), + columns = list( + # Render a "show details" button in the last column of the table. + # This button won't do anything by itself, but will trigger the custom + # click action on the column. + cdmSourceName = reactable::colDef( + header = withTooltip( + "Database Name", + "The name of the database" + )), + cohortId = reactable::colDef( + header = withTooltip( + "Cohort ID", + "The unique numeric identifier of the cohort" + )), + cohortName = reactable::colDef( + header = withTooltip( + "Cohort Name", + "The name of the cohort" + )), + cohortSubjects = reactable::colDef( + header = withTooltip( + "Number of Subjects", + "The number of distinct subjects in the cohort" + ), + format = reactable::colFormat(separators = TRUE + )), + cohortEntries = reactable::colDef( + header = withTooltip( + "Number of Records", + "The number of records in the cohort" + ), + format = reactable::colFormat(separators = TRUE + )) + ), + filterable = TRUE, + sortable = TRUE, + resizable = T, + searchable = T, + striped = T, + defaultColDef = reactable::colDef( + align = "left" ) + ) + }) - # download button - counts - output$downloadCohortCounts <- shiny::downloadHandler( + output$cohortCounts <- reactable::renderReactable({ + + tryCatch({ + + rtable() + }, + + error = function(e){ + shiny::showNotification(paste0('Error: ', + "Please select at least one column to display")); return(NULL) + } + + ) + }) + + + # download buttons - counts + output$downloadCohortCountsFull <- shiny::downloadHandler( filename = function() { - paste('cohort-count-data-', Sys.Date(), '.csv', sep='') + paste('cohort-count-data-full', Sys.Date(), '.csv', sep='') }, content = function(con) { utils::write.csv(getCohortGeneratorCohortCounts( @@ -290,23 +444,45 @@ cohortGeneratorServer <- function( "cohortEntries"), con) } ) + + # output$downloadCohortCountsFiltered <- shiny::downloadHandler( + # filename = function() { + # paste('cohort-count-data-filtered-', Sys.Date(), '.csv', sep='') + # }, + # content = function(con) { + # filtered_data <- rtable()$reactiveData()$data() + # filtered_rows <- rtable()$reactiveData()$filteredRows() + # + # utils::write.csv(filtered_data[filtered_rows,], + # con) + # } + # ) output$cohortGeneration <- reactable::renderReactable({ data <- getCohortGeneratorCohortMeta( connectionHandler = connectionHandler, resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix + tablePrefix = resultDatabaseSettings$tablePrefix, + databaseTable = resultDatabaseSettings$databaseTable, + databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix ) %>% - dplyr::select("cohortId", + dplyr::select("cdmSourceName", + "cohortId", "cohortName", "generationStatus", "startTime", - "endTime") + "endTime", + "generationDuration") reactable::reactable(data, columns = list( # Render a "show details" button in the last column of the table. # This button won't do anything by itself, but will trigger the custom # click action on the column. + cdmSourceName = reactable::colDef( + header = withTooltip( + "Database Name", + "The name of the database" + )), cohortId = reactable::colDef( header = withTooltip( "Cohort ID", @@ -337,7 +513,14 @@ cohortGeneratorServer <- function( "The time and date the cohort finished generating" ), format = reactable::colFormat(datetime = TRUE - )) + )), + generationDuration = reactable::colDef( + header = withTooltip( + "Generation Duration (mins)", + "The time it took (in minutes) to generate the cohort" + ), + format = reactable::colFormat(digits = 2) + ) ), filterable = TRUE, sortable = TRUE, @@ -399,9 +582,9 @@ cohortGeneratorServer <- function( modeIds <- unique(inputValsClean$modeId) - cohortName <- shiny::reactiveVal(cohortNames[1]) - databaseId <- shiny::reactiveVal(databaseIds[1]) - modeId <- shiny::reactiveVal(modeIds[1]) + # cohortName <- shiny::reactiveVal(cohortNames[1]) + # databaseId <- shiny::reactiveVal(databaseIds[1]) + # modeId <- shiny::reactiveVal(modeIds[1]) #build the selector output$attritionTableSelect <- shiny::renderUI({ @@ -428,36 +611,90 @@ cohortGeneratorServer <- function( label = "Subject-level or Record-level?", choices = modeIds, selected = "Subject" + ), + shiny::actionButton( + inputId = session$ns('generate'), + label = 'Generate Report' ) ) }) - shiny::observeEvent(input$selectedCohortName,{ - cohortName(input$selectedCohortName) - }) - shiny::observeEvent(input$selectedDatabaseId,{ - databaseId(input$selectedDatabaseId) - }) - shiny::observeEvent(input$selectedModeId,{ - modeId(input$selectedModeId) - }) + reactiveData <- shiny::reactiveVal(NULL) + selectedInputs <- shiny::reactiveVal() + output$inputsText <- shiny::renderUI(selectedInputs()) + + # shiny::observeEvent(input$selectedCohortName,{ + # cohortName(input$selectedCohortName) + # }) + # shiny::observeEvent(input$selectedDatabaseId,{ + # databaseId(input$selectedDatabaseId) + # }) + # shiny::observeEvent(input$selectedModeId,{ + # modeId(input$selectedModeId) + # }) #build the reactive data - data <- shiny::reactive({ - inputValsClean %>% - dplyr::filter(.data$cdmSourceName == databaseId() & - .data$cohortName == cohortName() & - .data$modeId == modeId() + + shiny::observeEvent( + eventExpr = input$generate, + { + + # if(length(input$selectedCohortName) == 0 | is.null(input$selectedDatabaseId | + # is.null(input$selectedModeId))){ + # print('Null ids value') + # return(invisible(NULL)) + # } + + selectedInputs( + shinydashboard::box( + status = 'warning', + width = "100%", + title = 'Selected:', + collapsible = T, + collapsed = F, + shiny::div( + shiny::fluidRow( + shiny::column( + width = 8, + shiny::tags$b("Cohort:"), + #unique(inputVals$cohortName[inputVals$cohortName %in% input$selectedCohortName]) + input$selectedCohortName + ), + shiny::column( + width = 4, + shiny::tags$b("Database:"), + #unique(inputVals$cdmSourceName[inputVals$cdmSourceName == input$selectedDatabaseId]) + input$selectedDatabaseId + ), + shiny::column( + width = 4, + shiny::tags$b("Level:"), + #unique(inputValsClean$modeId)[inputValsClean$modeId == input$selectedModeId] + input$selectedModeId + ) + ) ) - }) + ) + ) + + + data <- inputValsClean %>% + dplyr::filter(cdmSourceName %in% input$selectedDatabaseId & + cohortName %in% input$selectedCohortName & + modeId %in% input$selectedModeId + ) + + reactiveData <- shiny::reactive(data) + + if(!is.null(data)){ output$attritionTable <- reactable::renderReactable( reactable::reactable( - data = data() %>% + data = reactiveData() %>% dplyr::select(c("cdmSourceName", "cohortName", "ruleName", "personCount", "dropCount", "dropPerc", "retainPerc") - ) + ) , rownames = FALSE, @@ -514,12 +751,12 @@ cohortGeneratorServer <- function( align = "left" ) ) - ) + ) #attrition plot output$attritionPlot <- plotly::renderPlotly( getCohortAttritionPlot( - data() + data ) ) @@ -533,93 +770,20 @@ cohortGeneratorServer <- function( , con) } ) + } - #testing funnel plot for attrition visualization - - - - - - - - - - - + else{ + shiny::showNotification('data NULL') + } + } + ) + - # output$inclusionSummary <- reactable::renderReactable({ - # data <- getCohortGeneratorCohortInclusionSummary( - # connectionHandler = connectionHandler, - # resultsSchema = resultsSchema, - # tablePrefix = resultDatabaseSettings$tablePrefix, - # databaseTable = resultDatabaseSettings$databaseTable, - # databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix - # ) %>% - # dplyr::select("cdmSourceName", - # "cohortDefinitionId", - # "cohortName", - # "baseCount", - # "finalCount", - # "modeId") %>% - # dplyr::mutate(modeId = - # dplyr::case_when( - # modeId == 1 ~ "Subjects", - # .default = "Records" - # ) - # ) - # reactable::reactable(data, - # columns = list( - # # Render a "show details" button in the last column of the table. - # # This button won't do anything by itself, but will trigger the custom - # # click action on the column. - # cdmSourceName = reactable::colDef( - # header = withTooltip( - # "Database Name", - # "The name of the database" - # )), - # cohortDefinitionId = reactable::colDef( - # header = withTooltip( - # "Cohort ID", - # "The unique numeric identifier of the cohort" - # )), - # cohortName = reactable::colDef( - # header = withTooltip( - # "Cohort Name", - # "The name of the cohort" - # )), - # baseCount = reactable::colDef( - # header = withTooltip( - # "Base Count", - # "The number of records before any inclusion criteria are applied (entry events)" - # ), - # format = reactable::colFormat(separators = TRUE - # )), - # finalCount = reactable::colDef( - # header = withTooltip( - # "Final Count", - # "The number of records after all inclusion criteria are applied" - # ), - # format = reactable::colFormat(separators = TRUE - # )), - # modeId = reactable::colDef( - # header = withTooltip( - # "Records or Subjects?", - # "An indicator of whether the counts shown are the number of subjects or the number of records" - # ), - # format = reactable::colFormat(separators = TRUE - # )) - # ), - # filterable = TRUE, - # sortable = TRUE, - # defaultColDef = reactable::colDef( - # align = "left" - # ) - # ) - # }) + # end of server } ) diff --git a/R/helpers-cohortGeneratorDataPulls.R b/R/helpers-cohortGeneratorDataPulls.R index d68dd52b..52df8e8b 100644 --- a/R/helpers-cohortGeneratorDataPulls.R +++ b/R/helpers-cohortGeneratorDataPulls.R @@ -29,16 +29,35 @@ getCohortGeneratorCohortCounts <- function( getCohortGeneratorCohortMeta <- function( connectionHandler, resultsSchema, - tablePrefix = 'cg_' + tablePrefix = 'cg_', + databaseTable, + databaseTablePrefix ) { - sql <- "SELECT * FROM @results_schema.@table_prefixCOHORT_GENERATION;" - return( - connectionHandler$queryDb( - sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix + sql <- "SELECT cg.cohort_id, cg.cohort_name, + cg.generation_status, cg.start_time, cg.end_time, dt.cdm_source_name + from @results_schema.@table_prefixCOHORT_GENERATION cg + join @results_schema.@database_table_prefix@database_table dt + on cg.database_id = dt.database_id + ;" + + df <- connectionHandler$queryDb( + sql = sql, + results_schema = resultsSchema, + table_prefix = tablePrefix, + database_table = databaseTable, + database_table_prefix = databaseTablePrefix + ) + + df2 <- df %>% + dplyr::mutate(generationDuration = case_when( + generationStatus == "COMPLETE" ~ difftime(endTime, startTime, units="mins"), + .default = NA ) + ) + + return( + df2 ) } From a7f48172cf960eeff57bbdbc80ba0a6e06c3599b Mon Sep 17 00:00:00 2001 From: jreps Date: Tue, 23 May 2023 16:54:08 -0400 Subject: [PATCH 06/20] test fixes - updating SCCS table to use component - fixing cohort generator test fails --- NAMESPACE | 2 - R/cohortgenerator-main.R | 95 +++--- R/components-data-viewer.R | 189 +++++------ R/description-cohorts.R | 363 +++++++++++---------- R/helpers-cohortGeneratorDataPulls.R | 11 +- R/prediction-main.R | 19 +- R/sccs-diagnosticsSummary.R | 124 +++++-- man/sccsDiagnosticsSummaryServer.Rd | 21 -- man/sccsDiagnosticsSummaryViewer.Rd | 17 - tests/testthat/test-cohortGenerator-main.R | 4 +- 10 files changed, 454 insertions(+), 391 deletions(-) delete mode 100644 man/sccsDiagnosticsSummaryServer.Rd delete mode 100644 man/sccsDiagnosticsSummaryViewer.Rd diff --git a/NAMESPACE b/NAMESPACE index b897903b..88b2705f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -96,8 +96,6 @@ export(predictionSettingsViewer) export(predictionValidationServer) export(predictionValidationViewer) export(predictionViewer) -export(sccsDiagnosticsSummaryServer) -export(sccsDiagnosticsSummaryViewer) export(resultTableServer) export(resultTableViewer) export(sccsHelperFile) diff --git a/R/cohortgenerator-main.R b/R/cohortgenerator-main.R index 8a3f5c1a..0be7698d 100644 --- a/R/cohortgenerator-main.R +++ b/R/cohortgenerator-main.R @@ -358,50 +358,51 @@ cohortGeneratorServer <- function( rtable <- shiny::reactive({ - reactable::reactable(data %>% - dplyr::select(input$cohortCountsCols), - columns = list( - # Render a "show details" button in the last column of the table. - # This button won't do anything by itself, but will trigger the custom - # click action on the column. - cdmSourceName = reactable::colDef( - header = withTooltip( - "Database Name", - "The name of the database" - )), - cohortId = reactable::colDef( - header = withTooltip( - "Cohort ID", - "The unique numeric identifier of the cohort" - )), - cohortName = reactable::colDef( - header = withTooltip( - "Cohort Name", - "The name of the cohort" - )), - cohortSubjects = reactable::colDef( - header = withTooltip( - "Number of Subjects", - "The number of distinct subjects in the cohort" - ), - format = reactable::colFormat(separators = TRUE - )), - cohortEntries = reactable::colDef( - header = withTooltip( - "Number of Records", - "The number of records in the cohort" - ), - format = reactable::colFormat(separators = TRUE - )) - ), - filterable = TRUE, - sortable = TRUE, - resizable = T, - searchable = T, - striped = T, - defaultColDef = reactable::colDef( - align = "left" - ) + reactable::reactable( + data %>% + dplyr::select(input$cohortCountsCols), + columns = list( + # Render a "show details" button in the last column of the table. + # This button won't do anything by itself, but will trigger the custom + # click action on the column. + cdmSourceName = reactable::colDef( + header = withTooltip( + "Database Name", + "The name of the database" + )), + cohortId = reactable::colDef( + header = withTooltip( + "Cohort ID", + "The unique numeric identifier of the cohort" + )), + cohortName = reactable::colDef( + header = withTooltip( + "Cohort Name", + "The name of the cohort" + )), + cohortSubjects = reactable::colDef( + header = withTooltip( + "Number of Subjects", + "The number of distinct subjects in the cohort" + ), + format = reactable::colFormat(separators = TRUE + )), + cohortEntries = reactable::colDef( + header = withTooltip( + "Number of Records", + "The number of records in the cohort" + ), + format = reactable::colFormat(separators = TRUE + )) + ), + filterable = TRUE, + sortable = TRUE, + resizable = T, + searchable = T, + striped = T, + defaultColDef = reactable::colDef( + align = "left" + ) ) }) @@ -677,9 +678,9 @@ cohortGeneratorServer <- function( data <- inputValsClean %>% - dplyr::filter(cdmSourceName %in% input$selectedDatabaseId & - cohortName %in% input$selectedCohortName & - modeId %in% input$selectedModeId + dplyr::filter(.data$cdmSourceName %in% input$selectedDatabaseId & + .data$cohortName %in% input$selectedCohortName & + .data$modeId %in% input$selectedModeId ) reactiveData <- shiny::reactive(data) diff --git a/R/components-data-viewer.R b/R/components-data-viewer.R index c9762e58..6c4d6484 100644 --- a/R/components-data-viewer.R +++ b/R/components-data-viewer.R @@ -4,8 +4,6 @@ #potentially: #output: download buttons, table, and column selector - - #' Result Table Viewer #' #' @param id string @@ -21,8 +19,10 @@ resultTableViewer <- function(id = "result-table") { title = shiny::span(shiny::icon("table"), "Table"), shiny::fluidPage( shiny::fluidRow( - shiny::column(width = 7, - shiny::uiOutput(ns("columnSelector"))), + shiny::column( + width = 7, + shiny::uiOutput(ns("columnSelector")) + ), shiny::column( width = 2, shiny::downloadButton( @@ -48,7 +48,9 @@ resultTableViewer <- function(id = "result-table") { ) ), shiny::fluidRow( - shinycssloaders::withSpinner(reactable::reactableOutput(outputId = ns("resultData"))) + shinycssloaders::withSpinner( + reactable::reactableOutput(outputId = ns("resultData")) + ) ) ) )) @@ -59,8 +61,10 @@ resultTableViewer <- function(id = "result-table") { #tooltip function withTooltip <- function(value, tooltip, ...) { - shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", - tippy::tippy(value, tooltip, ...)) + shiny::div( + style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", + tippy::tippy(value, tooltip, ...) + ) } # customColDefs needs to be named list of colDefs @@ -137,89 +141,90 @@ ohdsiReactableTheme <- reactable::reactableTheme( #' @return shiny module server #' @export #' -resultTableServer <- function(id, #string - df, #data.frame - colDefsInput +resultTableServer <- function( + id, #string + df, #data.frame + colDefsInput ) #list of colDefs, can use checkmate::assertList, need a check that makes sure names = columns) { - shiny::moduleServer(id, - function(input, output, session) { - output$columnSelector <- shiny::renderUI({ - # Get the column names from the data frame displayed in the reactable - # cols <- if(is.null(input$dataCols)){ - # colnames(df) - # } - # else{ - # colnames(df[,input$dataCols]) - # } - # - shinyWidgets::pickerInput( - inputId = session$ns('dataCols'), - label = 'Select Columns to Display: ', - choices = colnames(df()), - selected = colnames(df()), - choicesOpt = list(style = rep_len("color: black;", 999)), - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ), - width = "75%" - ) - - }) - - #need to try adding browser() to all reactives to see why selected cols isnt working - - colDefs <- - shiny::reactive(create_colDefs_list(df = df()[, input$dataCols], - customColDefs = colDefsInput)) - - fullData <- shiny::reactive(df()) - - - output$resultData <- - - reactable::renderReactable({ - if (is.null(input$dataCols)) { - data = df() - } - else{ - data = df()[, input$dataCols] - } - if (nrow(data) == 0) - return(NULL) - - reactable::reactable( - data, - columns = colDefs(), - #these can be turned on/off and will overwrite colDef args - sortable = TRUE, - resizable = TRUE, - filterable = TRUE, - searchable = TRUE, - showPageSizeOptions = TRUE, - outlined = TRUE, - showSortIcon = TRUE, - striped = TRUE, - highlight = TRUE, - defaultColDef = reactable::colDef(align = "left") - #, experimental - #theme = ohdsiReactableTheme - ) - }) - - # download full data button - output$downloadDataFull <- shiny::downloadHandler( - filename = function() { - paste('data-full-', Sys.Date(), '.csv', sep = '') - }, - content = function(con) { - utils::write.csv(fullData(), con, - row.names = F) - } - ) - }) + shiny::moduleServer( + id, + function(input, output, session) { + + if(inherits(df, 'data.frame')){ + df <- shiny::reactiveVal(df) + } + + output$columnSelector <- shiny::renderUI({ + + shinyWidgets::pickerInput( + inputId = session$ns('dataCols'), + label = 'Select Columns to Display: ', + choices = colnames(df()), + selected = colnames(df()), + choicesOpt = list(style = rep_len("color: black;", 999)), + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ), + width = "75%" + ) + + }) + + #need to try adding browser() to all reactives to see why selected cols isnt working + + colDefs <- shiny::reactive( + create_colDefs_list( + df = df()[, input$dataCols], + customColDefs = colDefsInput + ) + ) + + output$resultData <- reactable::renderReactable({ + if (is.null(input$dataCols)) { + data = df() + } + else{ + data = df()[, input$dataCols, drop = FALSE] + } + if (nrow(data) == 0) + return(NULL) + + reactable::reactable( + data, + columns = colDefs(), + #these can be turned on/off and will overwrite colDef args + sortable = TRUE, + resizable = TRUE, + filterable = TRUE, + searchable = TRUE, + showPageSizeOptions = TRUE, + outlined = TRUE, + showSortIcon = TRUE, + striped = TRUE, + highlight = TRUE, + defaultColDef = reactable::colDef(align = "left") + #, experimental + #theme = ohdsiReactableTheme + ) + }) + + # download full data button + output$downloadDataFull <- shiny::downloadHandler( + filename = function() { + paste('data-full-', Sys.Date(), '.csv', sep = '') + }, + content = function(con) { + utils::write.csv( + x = df(), + file = con, + row.names = F + ) + } + ) + }) diff --git a/R/description-cohorts.R b/R/description-cohorts.R index e2ee65a1..235d4411 100644 --- a/R/description-cohorts.R +++ b/R/description-cohorts.R @@ -76,167 +76,179 @@ descriptionTableViewer <- function(id) { #' The server to the cohorts features server #' #' @export -descriptionTableServer <- function(id, - connectionHandler, - mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable = 'DATABASE_META_DATA') { - shiny::moduleServer(id, - function(input, output, session) { - #if(mainPanelTab() != 'Time To Event'){ - # return(invisible(NULL)) - #} - - inputVals <- getDecCohortsInputs(connectionHandler, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable) - - # update UI - output$cohortInputs <- shiny::renderUI({ - shiny::fluidPage( - shiny::fluidRow( - shiny::column( - width = 6, - shinyWidgets::pickerInput( - inputId = session$ns('targetIds'), - label = 'Targets: ', - choices = inputVals$cohortIds, - selected = inputVals$cohortIds, - choicesOpt = list(style = rep_len("color: black;", 999)), - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ), - shiny::column( - width = 6, - shinyWidgets::pickerInput( - inputId = session$ns('databaseId'), - label = 'Database: ', - choices = inputVals$databaseIds, - selected = 1, - choicesOpt = list(style = rep_len("color: black;", 999)), - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ) - ), - - shiny::actionButton(inputId = session$ns('generate'), - label = 'Generate Report') - ) - }) - - allData <- - shiny::eventReactive(#we care about returning this value, so we use eventReactive - eventExpr = input$generate, #could add complexity to event if desired - { - if (is.null(input$targetIds)) { - data.frame() - } - getDesFEData( - targetIds = input$targetIds, - databaseId = input$databaseId, - connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, - cohortTablePrefix = cohortTablePrefix - ) - }) - - - selectedInputs <- shiny::reactiveVal() - output$TinputsText <- shiny::renderUI(selectedInputs()) - - shiny::observeEvent(eventExpr = input$generate, - { - if (length(input$targetIds) == 0 | is.null(input$databaseId)) { - print('Null ids value') - return(invisible(NULL)) - } - - selectedInputs( - shinydashboard::box( - status = 'warning', - width = "100%", - title = 'Selected:', - shiny::div(shiny::fluidRow( - shiny::column( - width = 8, - shiny::tags$b("Target/s:"), - - paste(names(inputVals$cohortIds)[inputVals$cohortIds %in% input$targetIds], - collapse = ',') - - ), - shiny::column( - width = 4, - shiny::tags$b("Database:"), - names(inputVals$databaseIds)[inputVals$databaseIds == input$databaseId] - ) - )) - ) - ) - - - - }) - - - #cols: covariateId, covariateName, analysisName, - #averageValue_"target", countValue_"target" - - custom_colDefs <- list( - covariateId = reactable::colDef( - header = withTooltip("Covariate ID", - "Unique identifier of the covariate") - ), - covariateName = reactable::colDef(header = withTooltip( - "Covariate Name", - "The name of the covariate" - )), - analysisName = reactable::colDef(header = withTooltip( - "Covariate Class", - "Class/type of the covariate" - )) - ) - - ##custom_colDefs = NULL - resultTableServer(id = "result-table", - df = allData, - colDefsInput = custom_colDefs) - - - - return(invisible(NULL)) - - }) +descriptionTableServer <- function( + id, + connectionHandler, + mainPanelTab, + schema, + tablePrefix, + cohortTablePrefix, + databaseTable = 'DATABASE_META_DATA' +) { + shiny::moduleServer( + id, + function(input, output, session) { + + inputVals <- getDecCohortsInputs( + connectionHandler, + schema, + tablePrefix, + cohortTablePrefix, + databaseTable + ) + + # update UI + output$cohortInputs <- shiny::renderUI({ + shiny::fluidPage( + shiny::fluidRow( + shiny::column( + width = 6, + shinyWidgets::pickerInput( + inputId = session$ns('targetIds'), + label = 'Targets: ', + choices = inputVals$cohortIds, + selected = inputVals$cohortIds, + choicesOpt = list(style = rep_len("color: black;", 999)), + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + shiny::column( + width = 6, + shinyWidgets::pickerInput( + inputId = session$ns('databaseId'), + label = 'Database: ', + choices = inputVals$databaseIds, + selected = 1, + choicesOpt = list( + style = rep_len("color: black;", 999) + ), + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ) + ), + + shiny::actionButton( + inputId = session$ns('generate'), + label = 'Generate Report' + ) + ) + }) + + allData <- + shiny::eventReactive(#we care about returning this value, so we use eventReactive + eventExpr = input$generate, #could add complexity to event if desired + { + if (is.null(input$targetIds)) { + data.frame() + } + getDesFEData( + targetIds = input$targetIds, + databaseId = input$databaseId, + connectionHandler = connectionHandler, + schema = schema, + tablePrefix = tablePrefix, + cohortTablePrefix = cohortTablePrefix + ) + }) + + + selectedInputs <- shiny::reactiveVal() + output$TinputsText <- shiny::renderUI( + selectedInputs() + ) + + shiny::observeEvent( + eventExpr = input$generate, + { + if (length(input$targetIds) == 0 | is.null(input$databaseId)) { + print('Null ids value') + return(invisible(NULL)) + } + + selectedInputs( + shinydashboard::box( + status = 'warning', + width = "100%", + title = 'Selected:', + shiny::div(shiny::fluidRow( + shiny::column( + width = 8, + shiny::tags$b("Target/s:"), + + paste(names(inputVals$cohortIds)[inputVals$cohortIds %in% input$targetIds], + collapse = ',') + + ), + shiny::column( + width = 4, + shiny::tags$b("Database:"), + names(inputVals$databaseIds)[inputVals$databaseIds == input$databaseId] + ) + )) + ) + ) + + }) + + + #cols: covariateId, covariateName, analysisName, + #averageValue_"target", countValue_"target" + + custom_colDefs <- list( + covariateId = reactable::colDef( + header = withTooltip("Covariate ID", + "Unique identifier of the covariate") + ), + covariateName = reactable::colDef( + header = withTooltip( + "Covariate Name", + "The name of the covariate" + ) + ), + analysisName = reactable::colDef( + header = withTooltip( + "Covariate Class", + "Class/type of the covariate" + ) + ) + ) + + resultTableServer( + id = "result-table", + df = allData, + colDefsInput = custom_colDefs + ) + + return(invisible(NULL)) + + }) } -getDesFEData <- function(targetIds, - databaseId, - connectionHandler, - schema, - tablePrefix, - cohortTablePrefix) { +getDesFEData <- function( + targetIds, + databaseId, + connectionHandler, + schema, + tablePrefix, + cohortTablePrefix +) { # shiny::withProgress(message = 'Getting target comparison data', value = 0, { sql <- @@ -290,8 +302,10 @@ getDesFEData <- function(targetIds, # shiny::incProgress(2/3, detail = paste("Formating")) #format - resultTable$averageValue <- - round(resultTable$averageValue, digits = 2) + resultTable$averageValue <- round( + x = resultTable$averageValue, + digits = 2 + ) resultTable <- resultTable %>% tidyr::pivot_wider( @@ -312,11 +326,13 @@ getDesFEData <- function(targetIds, } -getDecCohortsInputs <- function(connectionHandler, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable) { +getDecCohortsInputs <- function( + connectionHandler, + schema, + tablePrefix, + cohortTablePrefix, + databaseTable +) { #shiny::withProgress(message = 'Getting target comparison inputs', value = 0, { @@ -344,15 +360,16 @@ getDecCohortsInputs <- function(connectionHandler, #shiny::incProgress(2/4, detail = paste("Extracted targetIds")) - sql <- - 'select d.database_id, d.cdm_source_abbreviation as database_name + sql <- 'select d.database_id, d.cdm_source_abbreviation as database_name from @result_schema.@database_table d;' #shiny::incProgress(3/4, detail = paste("Extracting databaseIds")) - database <- connectionHandler$queryDb(sql = sql, - result_schema = schema, - database_table = databaseTable) + database <- connectionHandler$queryDb( + sql = sql, + result_schema = schema, + database_table = databaseTable + ) databaseIds <- database$databaseId names(databaseIds) <- database$databaseName @@ -360,7 +377,11 @@ getDecCohortsInputs <- function(connectionHandler, # }) - return(list(cohortIds = ids, - databaseIds = databaseIds)) + return( + list( + cohortIds = ids, + databaseIds = databaseIds + ) + ) } diff --git a/R/helpers-cohortGeneratorDataPulls.R b/R/helpers-cohortGeneratorDataPulls.R index 52df8e8b..77384137 100644 --- a/R/helpers-cohortGeneratorDataPulls.R +++ b/R/helpers-cohortGeneratorDataPulls.R @@ -50,9 +50,14 @@ getCohortGeneratorCohortMeta <- function( ) df2 <- df %>% - dplyr::mutate(generationDuration = case_when( - generationStatus == "COMPLETE" ~ difftime(endTime, startTime, units="mins"), - .default = NA + dplyr::mutate( + generationDuration = dplyr::case_when( + generationStatus == "COMPLETE" + ~ tryCatch( + {difftime(.data$endTime, .data$startTime, units="mins")}, + error = function(e){return(NA)} + ), + T ~ NA ) ) diff --git a/R/prediction-main.R b/R/prediction-main.R index 79344421..1f22ba62 100644 --- a/R/prediction-main.R +++ b/R/prediction-main.R @@ -106,21 +106,12 @@ predictionViewer <- function(id=1) { shiny::tabsetPanel( type = 'pills', id = ns('singleView'), - shiny::tabPanel( - "Design Settings", - predictionSettingsViewer(ns('settings')) - ), shiny::tabPanel( "Model", predictionCovariateSummaryViewer(ns('covariateSummary')) ), - shiny::tabPanel( - "Threshold Dependant", - predictionCutoffViewer(ns('cutoff')) - ), - shiny::tabPanel( "Discrimination", predictionDiscriminationViewer(ns('discrimination')) @@ -131,6 +122,11 @@ predictionViewer <- function(id=1) { predictionCalibrationViewer(ns('calibration')) ), + shiny::tabPanel( + "Threshold Dependant", + predictionCutoffViewer(ns('cutoff')) + ), + shiny::tabPanel( "Net Benefit", predictionNbViewer(ns('netBenefit')) @@ -140,6 +136,11 @@ predictionViewer <- function(id=1) { shiny::tabPanel( "Validation", predictionValidationViewer(ns('validation')) + ), + + shiny::tabPanel( + "Design Settings", + predictionSettingsViewer(ns('settings')) ) diff --git a/R/sccs-diagnosticsSummary.R b/R/sccs-diagnosticsSummary.R index 1158ea32..5b3579f8 100644 --- a/R/sccs-diagnosticsSummary.R +++ b/R/sccs-diagnosticsSummary.R @@ -17,34 +17,16 @@ # limitations under the License. -#' The module viewer for rendering the SCCS diagnostics results -#' -#' @param id the unique reference id for the module -#' -#' @return -#' The user interface to the estimation diagnostics viewer -#' -#' @export sccsDiagnosticsSummaryViewer <- function(id) { ns <- shiny::NS(id) shiny::div( - # div(HTML("Enhancements to come...")), - reactable::reactableOutput(outputId = ns("diagnosticsTable")) + resultTableViewer(ns("diagnosticsTable")) ) } -#' The module server for rendering the SCCS diagnostics summary -#' -#' @param id the unique reference id for the module -#' @param connectionHandler the connection to the PLE results database -#' @param resultDatabaseSettings the resultDatabaseSettings with the schemas, prefix and table names -#' -#' @return -#' the SCCS diagnostics summary results -#' -#' @export + sccsDiagnosticsSummaryServer <- function( id, connectionHandler, @@ -54,20 +36,106 @@ sccsDiagnosticsSummaryServer <- function( shiny::moduleServer( id, function(input, output, session) { - - output$diagnosticsTable <- reactable::renderReactable({ + data <- getSccsAllDiagnosticsSummary( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings ) - reactable::reactable(data, - striped = TRUE, - filterable = TRUE, - searchable = TRUE, - bordered = TRUE + customColDefs <- list( + databaseName = reactable::colDef( + header = withTooltip( + "Database", + "The database name" + ) + ), + exposure = reactable::colDef( + header = withTooltip( + "Exposure", + "The exposure of interest " + ) + ), + outcome = reactable::colDef( + header = withTooltip( + "Outcome", + "The outcome of interest " + ) + ), + analysis = reactable::colDef( + header = withTooltip( + "Analysis", + "The analysis name " + ) + ), + covariateName = reactable::colDef( + header = withTooltip( + "Time Period", + "The time period of interest" + ) + ), + mdrr = reactable::colDef( + header = withTooltip( + "mdrr", + "The minimum detectible relative risk" + ) + ), + ease = reactable::colDef( + header = withTooltip( + "ease", + "The ..." + ) + ), + timeTrendP = reactable::colDef( + header = withTooltip( + "timeTrendP", + "The ..." + ) + ), + preExposureP = reactable::colDef( + header = withTooltip( + "preExposureP", + "The ..." + ) + ), + mdrrDiagnostic = reactable::colDef( + header = withTooltip( + "mdrrDiagnostic", + "The ..." + ) + ), + easeDiagnostic = reactable::colDef( + header = withTooltip( + "easeDiagnostic", + "The ..." + ) + ), + timeTrendDiagnostic = reactable::colDef( + header = withTooltip( + "timeTrendDiagnostic", + "The ..." + ) + ), + preExposureDiagnostic = reactable::colDef( + header = withTooltip( + "preExposureDiagnostic", + "The ..." + ) + ), + + unblind = reactable::colDef( + header = withTooltip( + "unblind", + "If the value is 1 then the diagnostics passed and results can be unblinded" + ) + ) + + ) + + resultTableServer( + id = "diagnosticsTable", + df = data, + colDefsInput = customColDefs ) - }) } ) diff --git a/man/sccsDiagnosticsSummaryServer.Rd b/man/sccsDiagnosticsSummaryServer.Rd deleted file mode 100644 index 6a733aa4..00000000 --- a/man/sccsDiagnosticsSummaryServer.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/sccs-diagnosticsSummary.R -\name{sccsDiagnosticsSummaryServer} -\alias{sccsDiagnosticsSummaryServer} -\title{The module server for rendering the SCCS diagnostics summary} -\usage{ -sccsDiagnosticsSummaryServer(id, connectionHandler, resultDatabaseSettings) -} -\arguments{ -\item{id}{the unique reference id for the module} - -\item{connectionHandler}{the connection to the PLE results database} - -\item{resultDatabaseSettings}{the resultDatabaseSettings with the schemas, prefix and table names} -} -\value{ -the SCCS diagnostics summary results -} -\description{ -The module server for rendering the SCCS diagnostics summary -} diff --git a/man/sccsDiagnosticsSummaryViewer.Rd b/man/sccsDiagnosticsSummaryViewer.Rd deleted file mode 100644 index b25f1d9f..00000000 --- a/man/sccsDiagnosticsSummaryViewer.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/sccs-diagnosticsSummary.R -\name{sccsDiagnosticsSummaryViewer} -\alias{sccsDiagnosticsSummaryViewer} -\title{The module viewer for rendering the SCCS diagnostics results} -\usage{ -sccsDiagnosticsSummaryViewer(id) -} -\arguments{ -\item{id}{the unique reference id for the module} -} -\value{ -The user interface to the estimation diagnostics viewer -} -\description{ -The module viewer for rendering the SCCS diagnostics results -} diff --git a/tests/testthat/test-cohortGenerator-main.R b/tests/testthat/test-cohortGenerator-main.R index e891b592..de1c3bae 100644 --- a/tests/testthat/test-cohortGenerator-main.R +++ b/tests/testthat/test-cohortGenerator-main.R @@ -56,7 +56,9 @@ test_that("Test getCohortGeneratorCohortMeta ", { result <- getCohortGeneratorCohortMeta( connectionHandler = connectionHandlerCG, resultsSchema = 'main', - tablePrefix = 'cg_' + tablePrefix = 'cg_', + databaseTable = 'DATABASE_META_DATA', + databaseTablePrefix = '' ) testthat::expect_true( nrow(result) > 0 ) From f3fcca295db4dad2fc0889308b4693890b5c0a4f Mon Sep 17 00:00:00 2001 From: jreps Date: Tue, 23 May 2023 17:27:48 -0400 Subject: [PATCH 07/20] adding selection options to SCCS diagnostics - adding selection options to SCCS diagnostics --- R/helpers-sccsDataPulls.R | 57 +------- R/sccs-diagnosticsSummary.R | 275 +++++++++++++++++++++++++++++++++++- 2 files changed, 272 insertions(+), 60 deletions(-) diff --git a/R/helpers-sccsDataPulls.R b/R/helpers-sccsDataPulls.R index 0c4d2648..128a9e6f 100644 --- a/R/helpers-sccsDataPulls.R +++ b/R/helpers-sccsDataPulls.R @@ -542,63 +542,10 @@ getSccsDiagnosticsSummary <- function(connectionHandler, outcome_id = outcomeId, exposure_id = exposureId, snakeCaseToCamelCase = TRUE) - + } -getSccsAllDiagnosticsSummary <- function( - connectionHandler, - resultDatabaseSettings -) { - sql <- " - SELECT - d.cdm_source_abbreviation as database_name, - c.cohort_name as outcome, - c2.cohort_name as exposure, - a.description as analysis, - cov.covariate_name, - ds.* - FROM @database_schema.@table_prefixdiagnostics_summary ds - inner join - @database_schema.@table_prefixexposures_outcome_set eos - on ds.exposures_outcome_set_id = eos.exposures_outcome_set_id - inner join - @database_schema.@cg_table_prefixcohort_definition as c - on c.cohort_definition_id = eos.outcome_id - - INNER JOIN - @database_schema.@database_table_prefix@database_table d - on d.database_id = ds.database_id - - INNER JOIN - @database_schema.@table_prefixanalysis a - on a.analysis_id = ds.analysis_id - - INNER JOIN - @database_schema.@table_prefixcovariate cov - on cov.covariate_id = ds.covariate_id and - cov.exposures_outcome_set_id = ds.exposures_outcome_set_id and - cov.analysis_id = ds.analysis_id and - cov.database_id = ds.database_id - - inner join - @database_schema.@cg_table_prefixcohort_definition as c2 - on cov.era_id = c2.cohort_definition_id - ; - " - result <- connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, - table_prefix = resultDatabaseSettings$tablePrefix, - database_table_prefix = resultDatabaseSettings$databaseTablePrefix, - database_table = resultDatabaseSettings$databaseTable, - snakeCaseToCamelCase = TRUE) - - result <- result %>% - dplyr::select(-c("analysisId","exposuresOutcomeSetId","databaseId","covariateId")) - - return(result) - -} + diff --git a/R/sccs-diagnosticsSummary.R b/R/sccs-diagnosticsSummary.R index 5b3579f8..65e9a938 100644 --- a/R/sccs-diagnosticsSummary.R +++ b/R/sccs-diagnosticsSummary.R @@ -21,12 +21,18 @@ sccsDiagnosticsSummaryViewer <- function(id) { ns <- shiny::NS(id) shiny::div( - resultTableViewer(ns("diagnosticsTable")) + inputSelectionViewer(ns("input-selection")), + + shiny::conditionalPanel( + condition = 'input.generate != 0', + ns = shiny::NS(ns("input-selection")), + + resultTableViewer(ns("diagnosticsTable")) + ) ) + } - - sccsDiagnosticsSummaryServer <- function( id, connectionHandler, @@ -36,11 +42,96 @@ sccsDiagnosticsSummaryServer <- function( shiny::moduleServer( id, function(input, output, session) { + + targetIds <- getSccsDiagTargets( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + outcomeIds <- getSccsDiagOutcomes( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + analysisIds <- getSccsDiagAnalyses( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + inputSelected <- inputSelectionServer( + id = "input-selection", + inputSettingList = list( + createInputSetting( + rowNumber = 1, + columnWidth = 6, + varName = 'targetIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Target: ', + choices = targetIds, + selected = targetIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + createInputSetting( + rowNumber = 1, + columnWidth = 6, + varName = 'outcomeIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Outcome: ', + choices = outcomeIds, + selected = outcomeIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + + createInputSetting( + rowNumber = 2, + columnWidth = 12, + varName = 'analysisIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Analysis: ', + choices = analysisIds, + selected = analysisIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ) + ) + ) - data <- getSccsAllDiagnosticsSummary( + data <- shiny::reactive({ + getSccsAllDiagnosticsSummary( connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings + resultDatabaseSettings = resultDatabaseSettings, + targetIds = inputSelected()$targetIds, + outcomeIds = inputSelected()$outcomeIds, + analysisIds = inputSelected()$analysisIds ) + }) customColDefs <- list( databaseName = reactable::colDef( @@ -140,3 +231,177 @@ sccsDiagnosticsSummaryServer <- function( } ) } + + + +getSccsDiagAnalyses <- function( + connectionHandler, + resultDatabaseSettings +){ + + sql <- " + SELECT distinct + a.analysis_id, + a.description as analysis + + FROM + @database_schema.@table_prefixanalysis a + ; + " + result <- connectionHandler$queryDb( + sql, + database_schema = resultDatabaseSettings$schema, + table_prefix = resultDatabaseSettings$tablePrefix, + snakeCaseToCamelCase = TRUE + ) + + res <- result$analysisId + names(res) <- result$analysis + + return(res) +} + + +getSccsDiagOutcomes <- function( + connectionHandler, + resultDatabaseSettings +){ + + sql <- " + SELECT distinct + c.cohort_name as outcome, + c.cohort_definition_id + + FROM @database_schema.@table_prefixdiagnostics_summary ds + inner join + @database_schema.@table_prefixexposures_outcome_set eos + on ds.exposures_outcome_set_id = eos.exposures_outcome_set_id + inner join + @database_schema.@cg_table_prefixcohort_definition as c + on c.cohort_definition_id = eos.outcome_id + ; + " + result <- connectionHandler$queryDb( + sql, + database_schema = resultDatabaseSettings$schema, + table_prefix = resultDatabaseSettings$tablePrefix, + cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, + snakeCaseToCamelCase = TRUE + ) + + res <- result$cohortDefinitionId + names(res) <- result$outcome + + return(res) +} + +getSccsDiagTargets <- function( + connectionHandler, + resultDatabaseSettings +){ + + sql <- " + SELECT distinct + c2.cohort_name as target, + c2.cohort_definition_id + + FROM @database_schema.@table_prefixdiagnostics_summary ds + + INNER JOIN + @database_schema.@table_prefixcovariate cov + on cov.covariate_id = ds.covariate_id and + cov.exposures_outcome_set_id = ds.exposures_outcome_set_id and + cov.analysis_id = ds.analysis_id and + cov.database_id = ds.database_id + + inner join + @database_schema.@cg_table_prefixcohort_definition as c2 + on cov.era_id = c2.cohort_definition_id + ; + " + result <- connectionHandler$queryDb( + sql, + database_schema = resultDatabaseSettings$schema, + table_prefix = resultDatabaseSettings$tablePrefix, + cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, + snakeCaseToCamelCase = TRUE + ) + + res <- result$cohortDefinitionId + names(res) <- result$target + + return(res) +} + + +getSccsAllDiagnosticsSummary <- function( + connectionHandler, + resultDatabaseSettings, + targetIds, + outcomeIds, + analysisIds +) { + sql <- " + SELECT + d.cdm_source_abbreviation as database_name, + c.cohort_name as outcome, + c2.cohort_name as exposure, + a.description as analysis, + cov.covariate_name, + ds.* + FROM @database_schema.@table_prefixdiagnostics_summary ds + inner join + @database_schema.@table_prefixexposures_outcome_set eos + on ds.exposures_outcome_set_id = eos.exposures_outcome_set_id + inner join + @database_schema.@cg_table_prefixcohort_definition as c + on c.cohort_definition_id = eos.outcome_id + + INNER JOIN + @database_schema.@database_table_prefix@database_table d + on d.database_id = ds.database_id + + INNER JOIN + @database_schema.@table_prefixanalysis a + on a.analysis_id = ds.analysis_id + + INNER JOIN + @database_schema.@table_prefixcovariate cov + on cov.covariate_id = ds.covariate_id and + cov.exposures_outcome_set_id = ds.exposures_outcome_set_id and + cov.analysis_id = ds.analysis_id and + cov.database_id = ds.database_id + + inner join + @database_schema.@cg_table_prefixcohort_definition as c2 + on cov.era_id = c2.cohort_definition_id + + + where + + c2.cohort_definition_id in (@target_ids) and + c.cohort_definition_id in (@outcome_ids) and + a.analysis_id in (@analysis_ids) + ; + " + result <- connectionHandler$queryDb( + sql, + database_schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, + table_prefix = resultDatabaseSettings$tablePrefix, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + + target_ids = paste0(targetIds, collapse = ','), + outcome_ids = paste0(outcomeIds, collapse = ','), + analysis_ids = paste0(analysisIds, collapse = ','), + + snakeCaseToCamelCase = TRUE + ) + + result <- result %>% + dplyr::select(-c("analysisId","exposuresOutcomeSetId","databaseId","covariateId")) + + return(result) + +} From 393bc96268d69c62141480d8407dfc9f2309dd0c Mon Sep 17 00:00:00 2001 From: jreps Date: Wed, 24 May 2023 15:39:17 -0400 Subject: [PATCH 08/20] added summary diagnostic to CM and SCCS - added summary diagnostic to CM and SCCS --- R/estimation-diagnosticsSummary.R | 499 +++++++++++++++++- R/evidence-synth-main.R | 43 ++ R/helpers-estimationDataPulls.R | 41 +- R/sccs-diagnosticsSummary.R | 64 ++- .../test-estimation-DiagnosticsSummary.R | 108 ++++ .../test-helpers-estimationPlotsAndTables.R | 1 - 6 files changed, 694 insertions(+), 62 deletions(-) diff --git a/R/estimation-diagnosticsSummary.R b/R/estimation-diagnosticsSummary.R index 1396a377..1157c00f 100644 --- a/R/estimation-diagnosticsSummary.R +++ b/R/estimation-diagnosticsSummary.R @@ -29,8 +29,26 @@ estimationDiagnosticsSummaryViewer <- function(id) { ns <- shiny::NS(id) shiny::div( - # div(HTML("Enhancements to come...")), - reactable::reactableOutput(outputId = ns("diagnosticsTable")) + + inputSelectionViewer(ns("input-selection")), + + shiny::conditionalPanel( + condition = 'input.generate != 0', + ns = shiny::NS(ns("input-selection")), + + shiny::tabsetPanel( + type = 'pills', + id = ns('diagnosticsTablePanel'), + shiny::tabPanel( + title = 'Summary', + resultTableViewer(ns("diagnosticsSummaryTable")) + ), + shiny::tabPanel( + title = 'Full', + resultTableViewer(ns("diagnosticsTable")) + ) + ) + ) ) } @@ -48,32 +66,473 @@ estimationDiagnosticsSummaryViewer <- function(id) { #' the PLE diagnostics summary results #' #' @export -estimationDiagnosticsSummaryServer <- function(id, - connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, - databaseTable) { +estimationDiagnosticsSummaryServer <- function( + id, + connectionHandler, + resultsSchema, + tablePrefix, + cohortTablePrefix, + databaseTable +) { shiny::moduleServer( id, function(input, output, session) { - output$diagnosticsTable <- reactable::renderReactable({ - data <- getDiagnosticsData(connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, - databaseTable) - - reactable::reactable(data, - striped = TRUE, - filterable = TRUE, - searchable = TRUE, - bordered = TRUE + targetIds <- getCmDiagCohorts( + connectionHandler = connectionHandler, + resultsSchema = resultsSchema, + tablePrefix = tablePrefix, + cohortTablePrefix = cohortTablePrefix, + type = 'target' + ) + outcomeIds <- getCmDiagCohorts( + connectionHandler = connectionHandler, + resultsSchema = resultsSchema, + tablePrefix = tablePrefix, + cohortTablePrefix = cohortTablePrefix, + type = 'outcome' + ) + comparatorIds <- getCmDiagCohorts( + connectionHandler = connectionHandler, + resultsSchema = resultsSchema, + tablePrefix = tablePrefix, + cohortTablePrefix = cohortTablePrefix, + type = 'comparator' + ) + analysisIds <- getCmDiagAnalyses( + connectionHandler = connectionHandler, + resultsSchema = resultsSchema, + tablePrefix = tablePrefix + ) + + inputSelected <- inputSelectionServer( + id = "input-selection", + inputSettingList = list( + createInputSetting( + rowNumber = 1, + columnWidth = 6, + varName = 'targetIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Target: ', + choices = targetIds, + selected = targetIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + createInputSetting( + rowNumber = 1, + columnWidth = 6, + varName = 'outcomeIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Outcome: ', + choices = outcomeIds, + selected = outcomeIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + createInputSetting( + rowNumber = 2, + columnWidth = 6, + varName = 'comparatorIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Comparator: ', + choices = comparatorIds, + selected = comparatorIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + + createInputSetting( + rowNumber = 2, + columnWidth = 6, + varName = 'analysisIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Analysis: ', + choices = analysisIds, + selected = analysisIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ) + ) + ) + + data <- shiny::reactive({ + getCmDiagnosticsData( + connectionHandler, + resultsSchema, + tablePrefix, + cohortTablePrefix, + databaseTable, + targetIds = inputSelected()$targetIds, + outcomeIds = inputSelected()$outcomeIds, + comparatorIds = inputSelected()$comparatorIds, + analysisIds = inputSelected()$analysisIds ) }) + data2 <- shiny::reactive({ + diagnosticSummaryFormat(data) + }) + + customColDefs <- list( + databaseName = reactable::colDef( + header = withTooltip( + "Database", + "The database name" + ) + ), + target = reactable::colDef( + header = withTooltip( + "Target", + "The target cohort of interest " + ) + ), + comparator = reactable::colDef( + header = withTooltip( + "Comparator", + "The comparator cohort of interest " + ) + ), + outcome = reactable::colDef( + header = withTooltip( + "Outcome", + "The outcome of interest " + ) + ), + analysis = reactable::colDef( + header = withTooltip( + "Analysis", + "The analysis name " + ) + ), + + mdrr = reactable::colDef( + header = withTooltip( + "mdrr", + "The minimum detectible relative risk" + ) + ), + ease = reactable::colDef( + header = withTooltip( + "ease", + "The ..." + ) + ), + timeTrendP = reactable::colDef( + header = withTooltip( + "timeTrendP", + "The ..." + ) + ), + preExposureP = reactable::colDef( + header = withTooltip( + "preExposureP", + "The ..." + ) + ), + mdrrDiagnostic = reactable::colDef( + header = withTooltip( + "mdrrDiagnostic", + "The ..." + ) + ), + easeDiagnostic = reactable::colDef( + header = withTooltip( + "easeDiagnostic", + "The ..." + ) + ), + timeTrendDiagnostic = reactable::colDef( + header = withTooltip( + "timeTrendDiagnostic", + "The ..." + ) + ), + preExposureDiagnostic = reactable::colDef( + header = withTooltip( + "preExposureDiagnostic", + "The ..." + ) + ), + + unblind = reactable::colDef( + header = withTooltip( + "unblind", + "If the value is 1 then the diagnostics passed and results can be unblinded" + ) + ) + + ) + + resultTableServer( + id = "diagnosticsTable", + df = data, + colDefsInput = customColDefs + ) + + customColDefs2 <- list( + databaseName = reactable::colDef( + header = withTooltip( + "Database", + "The database name" + ) + ), + target = reactable::colDef( + header = withTooltip( + "Target", + "The target cohort of interest " + ) + ), + comparator = reactable::colDef( + header = withTooltip( + "Comparator", + "The comparator cohort of interest " + ) + ) + ) + + resultTableServer( + id = "diagnosticsSummaryTable", + df = data2, + colDefsInput = styleColumns(customColDefs2, outcomeIds, analysisIds) + ) + + + } + ) +} + +styleColumns <- function( + customColDefs, + outcomeIds, + analysisIds +){ + + colnameFormat <- merge(names(outcomeIds), names(analysisIds)) + colnameFormat <- apply(colnameFormat, 1, function(x){paste(x, collapse = '_', sep = '_')}) + + styleList <- lapply( + colnameFormat, + FUN = function(x){ + reactable::colDef( + style = function(value) { + color <- 'orange' + if(is.na(value)){ + color <- 'black' + }else if(value == 'Pass'){ + color <- '#AFE1AF' + }else if(value == 'Fail'){ + color <- '#E97451' + } + list(background = color) + } + ) } ) + names(styleList) <- colnameFormat + result <- append(customColDefs, styleList) + + return(result) +} + +diagnosticSummaryFormat <- function( + data, + idCols = c('databaseName','target', 'comparator'), + namesFrom = c('outcome','analysis') + ){ + + data2 <- tidyr::pivot_wider( + data = data(), + id_cols = idCols, + names_from = namesFrom, + values_from = c('summaryValue') + ) + + return(data2) +} + +getCmDiagCohorts <- function( + connectionHandler, + resultsSchema, + tablePrefix, + cohortTablePrefix, + type = 'target' +){ + + sql <- " + SELECT DISTINCT + cgcd1.cohort_name as names, + cgcd1.cohort_definition_id + FROM + @results_schema.@table_prefixdiagnostics_summary cmds + INNER JOIN + @results_schema.@cohort_table_prefixcohort_definition cgcd1 + ON cmds.@type_id = cgcd1.cohort_definition_id; + " + + result <- connectionHandler$queryDb( + sql = sql, + results_schema = resultsSchema, + table_prefix = tablePrefix, + cohort_table_prefix = cohortTablePrefix, + type = type + ) + + res <- result$cohortDefinitionId + names(res) <- result$names + + return( + res + ) } + +getCmDiagAnalyses <- function( + connectionHandler, + resultsSchema, + tablePrefix +){ + + sql <- " + SELECT DISTINCT + cma.analysis_id, + cma.description as names + FROM + @results_schema.@table_prefixdiagnostics_summary cmds + INNER JOIN + @results_schema.@table_prefixanalysis cma + ON cmds.analysis_id = cma.analysis_id + ; + " + + result <- connectionHandler$queryDb( + sql = sql, + results_schema = resultsSchema, + table_prefix = tablePrefix + ) + + res <- result$analysisId + names(res) <- result$names + + return( + res + ) + +} + + +getCmDiagnosticsData <- function( + connectionHandler, + resultsSchema, + tablePrefix, + cohortTablePrefix, + databaseTable, + targetIds, + outcomeIds, + comparatorIds, + analysisIds +) { + sql <- " + SELECT DISTINCT + dmd.cdm_source_abbreviation database_name, + cma.description analysis, + cgcd1.cohort_name target, + cgcd2.cohort_name comparator, + cgcd3.cohort_name outcome, + cmds.max_sdm, + cmds.shared_max_sdm, + cmds.equipoise, + cmds.mdrr, + cmds.attrition_fraction, + cmds.ease, + cmds.balance_diagnostic, + cmds.shared_balance_diagnostic, + cmds.equipoise_diagnostic, + cmds.mdrr_diagnostic, + cmds.attrition_diagnostic, + cmds.ease_diagnostic, + cmds.unblind + FROM + @results_schema.@table_prefixdiagnostics_summary cmds + INNER JOIN @results_schema.@table_prefixanalysis cma ON cmds.analysis_id = cma.analysis_id + INNER JOIN @results_schema.@database_table dmd ON dmd.database_id = cmds.database_id + INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd1 ON cmds.target_id = cgcd1.cohort_definition_id + INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd2 ON cmds.comparator_id = cgcd2.cohort_definition_id + INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd3 ON cmds.outcome_id = cgcd3.cohort_definition_id + + where cgcd1.cohort_definition_id in (@targets) + and cgcd2.cohort_definition_id in (@comparators) + and cgcd3.cohort_definition_id in (@outcomes) + and cma.analysis_id in (@analyses) + ; + " + + result <- connectionHandler$queryDb( + sql = sql, + results_schema = resultsSchema, + table_prefix = tablePrefix, + cohort_table_prefix = cohortTablePrefix, + database_table = databaseTable, + + targets = paste0(targetIds, collapse = ','), + comparators = paste0(comparatorIds, collapse = ','), + outcomes = paste0(outcomeIds, collapse = ','), + analyses = paste0(analysisIds, collapse = ',') + ) + + # adding percent fail for summary + result$summaryValue <- apply( + X = result[, grep('Diagnostic', colnames(result))], + MARGIN = 1, + FUN = function(x){ + + if(sum(x %in% c('FAIL'))>0){ + return('Fail') + } else if(sum(x %in% c('WARNING')) >0){ + return(sum(x %in% c('WARNING'))) + } else{ + return('Pass') + } + } + ) + + return( + result + ) +} \ No newline at end of file diff --git a/R/evidence-synth-main.R b/R/evidence-synth-main.R index 2092517e..d03e33e3 100644 --- a/R/evidence-synth-main.R +++ b/R/evidence-synth-main.R @@ -922,3 +922,46 @@ createPlotForSccsAnalysis <- function( return(plot) } + + +getEvidenceSynthDiagnostics <- function( + connectionHandler, + resultDatabaseSettings, + resultsSchema, + tablePrefix, + cohortTablePrefix, + databaseTable, + targetIds, + outcomeIds, + comparatorIds, + analysisIdsSccs, + analysisIdsCm + ){ + + sccsDiag <- getSccsAllDiagnosticsSummary( + connectionHandler, + resultDatabaseSettings, + targetIds, + outcomeIds, + analysisIds = analysisIdsSccs + ) + + cmDiag <- getCmDiagnosticsData( + connectionHandler, + resultsSchema, + tablePrefix, + cohortTablePrefix, + databaseTable, + targetIds, + outcomeIds, + comparatorIds, + analysisIds = analysisIdsCm + ) + + # select columns of interest and rename for consistency + + # rbind + + # return + +} diff --git a/R/helpers-estimationDataPulls.R b/R/helpers-estimationDataPulls.R index 5988c566..2cce6014 100644 --- a/R/helpers-estimationDataPulls.R +++ b/R/helpers-estimationDataPulls.R @@ -626,43 +626,4 @@ getEstimationNegativeControlEstimates <- function(cohortMethodResult, connection -getDiagnosticsData <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, databaseTable) { - sql <- " - SELECT - dmd.cdm_source_abbreviation database_name, - cma.description analysis_desc, - cgcd1.cohort_name target, - cgcd2.cohort_name comparator, - cgcd3.cohort_name outcome, - cmds.max_sdm, - cmds.shared_max_sdm, - cmds.equipoise, - cmds.mdrr, - cmds.attrition_fraction, - cmds.ease, - cmds.balance_diagnostic, - cmds.shared_balance_diagnostic, - cmds.equipoise_diagnostic, - cmds.mdrr_diagnostic, - cmds.attrition_diagnostic, - cmds.ease_diagnostic, - cmds.unblind - FROM - @results_schema.@table_prefixdiagnostics_summary cmds - JOIN @results_schema.@table_prefixanalysis cma ON cmds.analysis_id = cma.analysis_id - JOIN @results_schema.@database_table dmd ON dmd.database_id = cmds.database_id - JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd1 ON cmds.target_id = cgcd1.cohort_definition_id - JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd2 ON cmds.comparator_id = cgcd2.cohort_definition_id - JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd3 ON cmds.outcome_id = cgcd3.cohort_definition_id - " - - return( - connectionHandler$queryDb( - sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix, - database_table = databaseTable - ) - ) -} + diff --git a/R/sccs-diagnosticsSummary.R b/R/sccs-diagnosticsSummary.R index 65e9a938..68e9942d 100644 --- a/R/sccs-diagnosticsSummary.R +++ b/R/sccs-diagnosticsSummary.R @@ -27,7 +27,18 @@ sccsDiagnosticsSummaryViewer <- function(id) { condition = 'input.generate != 0', ns = shiny::NS(ns("input-selection")), - resultTableViewer(ns("diagnosticsTable")) + shiny::tabsetPanel( + type = 'pills', + id = ns('diagnosticsTablePanel'), + shiny::tabPanel( + title = 'Summary', + resultTableViewer(ns("diagnosticsSummaryTable")) + ), + shiny::tabPanel( + title = 'Full', + resultTableViewer(ns("diagnosticsTable")) + ) + ) ) ) @@ -132,6 +143,14 @@ sccsDiagnosticsSummaryServer <- function( analysisIds = inputSelected()$analysisIds ) }) + + data2 <- shiny::reactive({ # use CM diag function + diagnosticSummaryFormat( + data = data, + idCols = c('databaseName','exposure','covariateName'), + namesFrom = c('outcome','analysis') + ) + }) customColDefs <- list( databaseName = reactable::colDef( @@ -227,6 +246,35 @@ sccsDiagnosticsSummaryServer <- function( df = data, colDefsInput = customColDefs ) + + + # Summary table + customColDefs2 <- list( + databaseName = reactable::colDef( + header = withTooltip( + "Database", + "The database name" + ) + ), + exposure = reactable::colDef( + header = withTooltip( + "exposure", + "The exposure cohort of interest " + ) + ), + covariateName = reactable::colDef( + header = withTooltip( + "Time Period", + "The time period of interest" + ) + ) + ) + + resultTableServer( + id = "diagnosticsSummaryTable", + df = data2, + colDefsInput = styleColumns(customColDefs2, outcomeIds, analysisIds) + ) } ) @@ -402,6 +450,20 @@ getSccsAllDiagnosticsSummary <- function( result <- result %>% dplyr::select(-c("analysisId","exposuresOutcomeSetId","databaseId","covariateId")) + result$summaryValue <- apply( + X = result[, grep('Diagnostic', colnames(result))], + MARGIN = 1, + FUN = function(x){ + + if(sum(x %in% c('FAIL'))>0){ + return('Fail') + } else if(sum(x %in% c('WARNING')) >0){ + return(sum(x %in% c('WARNING'), na.rm = T)) + } else{ + return('Pass') + } + } + ) return(result) } diff --git a/tests/testthat/test-estimation-DiagnosticsSummary.R b/tests/testthat/test-estimation-DiagnosticsSummary.R index 4a2cbdc5..c8c20113 100644 --- a/tests/testthat/test-estimation-DiagnosticsSummary.R +++ b/tests/testthat/test-estimation-DiagnosticsSummary.R @@ -14,3 +14,111 @@ shiny::testServer( testthat::expect_true(!is.null(data)) }) + +test_that("styleColumns", { + + oid <- c(1,34) + names(oid) <- c('a','b') + aid <- c(3) + names(aid) <- c('none') + +colss <- styleColumns( + customColDefs = list(a=1), + outcomeIds = oid, + analysisIds = aid +) + +testthat::expect_is(colss, 'list') +names(colss) <- c('a', 'a_none', 'b_none') +testthat::expect_is(colss$b_none$style, 'function') +testthat::expect_equal(colss$b_none$style('Pass')$background,"#AFE1AF") +}) + +test_that("diagnosticSummaryFormat", { + + datar <- function(){ + data.frame(a=1,b=1,c=1,name ='name', summaryValue = 1) + } +val <- diagnosticSummaryFormat( + data = datar, + idCols = c('a','b', 'c'), + namesFrom = c('name') +) + +testthat::expect_true(nrow(val) == 1) +testthat::expect_true(ncol(val) == 4) + +}) + +test_that("getCmDiagCohorts", { + +cohortIds <- getCmDiagCohorts( + connectionHandler = connectionHandlerEst, + resultsSchema = 'main', + tablePrefix = 'cm_', + cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + type = 'target' + ) + +testthat::expect_true(length(cohortIds) > 0) +}) + +test_that("getCmDiagAnalyses", { + + analysisIds <- getCmDiagAnalyses( + connectionHandler = connectionHandlerEst, + resultsSchema = 'main', + tablePrefix = 'cm_' + ) + + testthat::expect_true(length(analysisIds) > 0) +}) + +test_that("getCmDiagAnalyses", { + + analysisIds <- getCmDiagAnalyses( + connectionHandler = connectionHandlerEst, + resultsSchema = 'main', + tablePrefix = 'cm_' + ) + + cohortIds <- getCmDiagCohorts( + connectionHandler = connectionHandlerEst, + resultsSchema = 'main', + tablePrefix = 'cm_', + cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + type = 'target' + ) + + outcomeIds <- getCmDiagCohorts( + connectionHandler = connectionHandlerEst, + resultsSchema = 'main', + tablePrefix = 'cm_', + cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + type = 'outcome' + ) + + comparatorIds <- getCmDiagCohorts( + connectionHandler = connectionHandlerEst, + resultsSchema = 'main', + tablePrefix = 'cm_', + cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + type = 'comparator' + ) + +diag <- getCmDiagnosticsData( + connectionHandler = connectionHandlerEst, + resultsSchema = 'main', + tablePrefix = 'cm_', + cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + databaseTable = 'database_meta_data', + targetIds = cohortIds, + outcomeIds = outcomeIds, + comparatorIds = comparatorIds, + analysisIds = analysisIds +) + +testthat::expect_true(nrow(diag) > 0) + +}) + diff --git a/tests/testthat/test-helpers-estimationPlotsAndTables.R b/tests/testthat/test-helpers-estimationPlotsAndTables.R index bf1e0440..79ba1a4b 100644 --- a/tests/testthat/test-helpers-estimationPlotsAndTables.R +++ b/tests/testthat/test-helpers-estimationPlotsAndTables.R @@ -188,4 +188,3 @@ test_that("plotEstimationScatter", { # getEstimationControlResults # getEstimationStudyPeriod # getEstimationNegativeControlEstimates -# getDiagnosticsData From c13e734ea79af18a1eaf58a0857f3ddc4ef18b86 Mon Sep 17 00:00:00 2001 From: jreps Date: Wed, 24 May 2023 21:45:43 -0400 Subject: [PATCH 09/20] added evidence synth diagnostics - added evidence synth diagnostics --- R/estimation-diagnosticsSummary.R | 17 +++- R/evidence-synth-main.R | 158 +++++++++++++++++++++++++----- R/sccs-diagnosticsSummary.R | 25 ++--- 3 files changed, 160 insertions(+), 40 deletions(-) diff --git a/R/estimation-diagnosticsSummary.R b/R/estimation-diagnosticsSummary.R index 1157c00f..fa1e3ee6 100644 --- a/R/estimation-diagnosticsSummary.R +++ b/R/estimation-diagnosticsSummary.R @@ -352,6 +352,10 @@ styleColumns <- function( colnameFormat, FUN = function(x){ reactable::colDef( + header = withTooltip( + substring(x,1,40), + x + ), style = function(value) { color <- 'orange' if(is.na(value)){ @@ -465,8 +469,8 @@ getCmDiagnosticsData <- function( databaseTable, targetIds, outcomeIds, - comparatorIds, - analysisIds + comparatorIds = NULL, + analysisIds = NULL ) { sql <- " SELECT DISTINCT @@ -497,9 +501,9 @@ getCmDiagnosticsData <- function( INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd3 ON cmds.outcome_id = cgcd3.cohort_definition_id where cgcd1.cohort_definition_id in (@targets) - and cgcd2.cohort_definition_id in (@comparators) + {@use_comparators}?{and cgcd2.cohort_definition_id in (@comparators)} and cgcd3.cohort_definition_id in (@outcomes) - and cma.analysis_id in (@analyses) + {@use_analyses}?{and cma.analysis_id in (@analyses)} ; " @@ -513,7 +517,10 @@ getCmDiagnosticsData <- function( targets = paste0(targetIds, collapse = ','), comparators = paste0(comparatorIds, collapse = ','), outcomes = paste0(outcomeIds, collapse = ','), - analyses = paste0(analysisIds, collapse = ',') + analyses = paste0(analysisIds, collapse = ','), + + use_comparators = !is.null(comparatorIds), + use_analyses = !is.null(analysisIds) ) # adding percent fail for summary diff --git a/R/evidence-synth-main.R b/R/evidence-synth-main.R index d03e33e3..cf36705c 100644 --- a/R/evidence-synth-main.R +++ b/R/evidence-synth-main.R @@ -55,6 +55,12 @@ evidenceSynthesisViewer <- function(id=1) { #title = shiny::tagList(shiny::icon("gear"), "Plot and Table"), id = ns('esCohortTabs'), + # diagnostic view + shiny::tabPanel( + title = 'Diagnostics', + resultTableViewer(ns("diagnosticsSummaryTable")) + ), + shiny::tabPanel( "Cohort Method Plot", shiny::plotOutput(ns('esCohortMethodPlot')) @@ -122,6 +128,15 @@ evidenceSynthesisServer <- function( cgTablePrefix = resultDatabaseSettings$cgTablePrefix ) + diagnosticColumnNames <- getOACcombinations( + connectionHandler = connectionHandler, + resultsSchema = resultDatabaseSettings$schema, + sccsTablePrefix = resultDatabaseSettings$sccsTablePrefix, + cmTablePrefix = resultDatabaseSettings$cmTablePrefix, + cohortTablePrefix = resultDatabaseSettings$cgTablePrefix, + databaseTable = resultDatabaseSettings$databaseMetaData + ) + inputSelected <- inputSelectionServer( id = "input-selection", inputSettingList = list( @@ -191,6 +206,40 @@ evidenceSynthesisServer <- function( ) }) + diagSumData <- shiny::reactive({ + getEvidenceSynthDiagnostics( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + resultsSchema = resultDatabaseSettings$schema, + cmTablePrefix = resultDatabaseSettings$cmTablePrefix, + cohortTablePrefix = resultDatabaseSettings$cgTablePrefix, + databaseTable = resultDatabaseSettings$databaseTable, + targetIds = inputSelected()$targetId, + outcomeIds = inputSelected()$outcomeId + ) + }) + + customColDefs2 <- list( + databaseName = reactable::colDef( + header = withTooltip( + "Database", + "The database name" + ) + ), + target = reactable::colDef( + header = withTooltip( + "Target", + "The target cohort of interest " + ) + ) + ) + + resultTableServer( + id = "diagnosticsSummaryTable", + df = diagSumData, + colDefsInput = styleColumns(customColDefs2, diagnosticColumnNames, outcomeIds) + ) + output$esCohortMethodPlot <- shiny::renderPlot( createPlotForAnalysis( unique(rbind(data(),data2())) @@ -923,45 +972,108 @@ createPlotForSccsAnalysis <- function( } +getOACcombinations <- function( + connectionHandler, + resultsSchema, + sccsTablePrefix, + cmTablePrefix, + cohortTablePrefix, + databaseTable + ){ + + sql <- "SELECT DISTINCT + CONCAT(cma.description, '_', cgcd2.cohort_name) as col_names + FROM + @results_schema.@cm_table_prefixdiagnostics_summary cmds + INNER JOIN @results_schema.@cm_table_prefixanalysis cma + ON cmds.analysis_id = cma.analysis_id + INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd2 + ON cmds.comparator_id = cgcd2.cohort_definition_id + + union + + SELECT + CONCAT(a.description, '_', cov.covariate_name) as col_names + + FROM @results_schema.@sccs_table_prefixdiagnostics_summary ds + + INNER JOIN + @results_schema.@sccs_table_prefixanalysis a + on a.analysis_id = ds.analysis_id + + INNER JOIN + @results_schema.@sccs_table_prefixcovariate cov + on cov.covariate_id = ds.covariate_id and + cov.exposures_outcome_set_id = ds.exposures_outcome_set_id and + cov.analysis_id = ds.analysis_id + ;" + + result <- connectionHandler$queryDb( + sql = sql, + results_schema = resultsSchema, + sccs_table_prefix = sccsTablePrefix, + cm_table_prefix = cmTablePrefix, + cohort_table_prefix = cohortTablePrefix, + database_table = databaseTable + ) + + res <- result$colNames + names(res) <- result$colNames + + return(res) +} getEvidenceSynthDiagnostics <- function( connectionHandler, resultDatabaseSettings, resultsSchema, - tablePrefix, + cmTablePrefix, cohortTablePrefix, databaseTable, targetIds, - outcomeIds, - comparatorIds, - analysisIdsSccs, - analysisIdsCm + outcomeIds ){ - sccsDiag <- getSccsAllDiagnosticsSummary( - connectionHandler, - resultDatabaseSettings, - targetIds, - outcomeIds, - analysisIds = analysisIdsSccs + resultDatabaseSettings$tablePrefix <- resultDatabaseSettings$sccsTablePrefix + resultDatabaseSettings$databaseTable <- resultDatabaseSettings$databaseMetaData + resultDatabaseSettings$cohortTablePrefix <- resultDatabaseSettings$cgTablePrefix + sccsDiagTemp <- getSccsAllDiagnosticsSummary( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetIds = targetIds, + outcomeIds = outcomeIds ) - cmDiag <- getCmDiagnosticsData( - connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, - databaseTable, - targetIds, - outcomeIds, - comparatorIds, - analysisIds = analysisIdsCm + cmDiagTemp <- getCmDiagnosticsData( + connectionHandler = connectionHandler, + resultsSchema = resultsSchema, + tablePrefix = cmTablePrefix, + cohortTablePrefix = cohortTablePrefix, + databaseTable = resultDatabaseSettings$databaseMetaData, + targetIds = targetIds, + outcomeIds = outcomeIds ) # select columns of interest and rename for consistency + sccsDiagTemp <- diagnosticSummaryFormat( + data = shiny::reactive({sccsDiagTemp}), + idCols = c('databaseName','target'), + namesFrom = c('analysis','covariateName','outcome') + ) + + cmDiagTemp <- diagnosticSummaryFormat( + data = shiny::reactive({cmDiagTemp}), + idCols = c('databaseName','target'), + namesFrom = c('analysis','comparator','outcome') + ) - # rbind + allResult <- merge( + x = sccsDiagTemp, + y = cmDiagTemp, + by = c('databaseName','target'), + all = T + ) # return - + return(allResult) } diff --git a/R/sccs-diagnosticsSummary.R b/R/sccs-diagnosticsSummary.R index 68e9942d..ac43867e 100644 --- a/R/sccs-diagnosticsSummary.R +++ b/R/sccs-diagnosticsSummary.R @@ -147,7 +147,7 @@ sccsDiagnosticsSummaryServer <- function( data2 <- shiny::reactive({ # use CM diag function diagnosticSummaryFormat( data = data, - idCols = c('databaseName','exposure','covariateName'), + idCols = c('databaseName','target','covariateName'), namesFrom = c('outcome','analysis') ) }) @@ -159,10 +159,10 @@ sccsDiagnosticsSummaryServer <- function( "The database name" ) ), - exposure = reactable::colDef( + target = reactable::colDef( header = withTooltip( - "Exposure", - "The exposure of interest " + "Target", + "The target cohort of interest " ) ), outcome = reactable::colDef( @@ -256,10 +256,10 @@ sccsDiagnosticsSummaryServer <- function( "The database name" ) ), - exposure = reactable::colDef( + target = reactable::colDef( header = withTooltip( - "exposure", - "The exposure cohort of interest " + "Target", + "The target cohort of interest " ) ), covariateName = reactable::colDef( @@ -387,13 +387,13 @@ getSccsAllDiagnosticsSummary <- function( resultDatabaseSettings, targetIds, outcomeIds, - analysisIds + analysisIds = NULL ) { sql <- " SELECT d.cdm_source_abbreviation as database_name, c.cohort_name as outcome, - c2.cohort_name as exposure, + c2.cohort_name as target, a.description as analysis, cov.covariate_name, ds.* @@ -427,9 +427,9 @@ getSccsAllDiagnosticsSummary <- function( where - c2.cohort_definition_id in (@target_ids) and - c.cohort_definition_id in (@outcome_ids) and - a.analysis_id in (@analysis_ids) + c2.cohort_definition_id in (@target_ids) + and c.cohort_definition_id in (@outcome_ids) + {@use_analysis}?{and a.analysis_id in (@analysis_ids)} ; " result <- connectionHandler$queryDb( @@ -443,6 +443,7 @@ getSccsAllDiagnosticsSummary <- function( target_ids = paste0(targetIds, collapse = ','), outcome_ids = paste0(outcomeIds, collapse = ','), analysis_ids = paste0(analysisIds, collapse = ','), + use_analysis = !is.null(analysisIds), snakeCaseToCamelCase = TRUE ) From b77c478f65577abeacf786ae135793547cc69704 Mon Sep 17 00:00:00 2001 From: jreps Date: Thu, 25 May 2023 08:48:17 -0400 Subject: [PATCH 10/20] adding sticky columns - adding some sticky columns to make it easier to explore results --- R/description-incidence.R | 1 + R/estimation-diagnosticsSummary.R | 9 ++++++--- R/evidence-synth-main.R | 6 ++++-- R/sccs-diagnosticsSummary.R | 9 ++++++--- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/R/description-incidence.R b/R/description-incidence.R index a36241f3..203050dd 100644 --- a/R/description-incidence.R +++ b/R/description-incidence.R @@ -235,6 +235,7 @@ descriptionIncidenceServer <- function( columns = list( cdmSourceAbbreviation = reactable::colDef( name = 'Database', + sticky = "left", filterInput = function(values, name) { shiny::tags$select( # Set to undefined to clear the filter diff --git a/R/estimation-diagnosticsSummary.R b/R/estimation-diagnosticsSummary.R index fa1e3ee6..e50eb474 100644 --- a/R/estimation-diagnosticsSummary.R +++ b/R/estimation-diagnosticsSummary.R @@ -312,19 +312,22 @@ estimationDiagnosticsSummaryServer <- function( header = withTooltip( "Database", "The database name" - ) + ), + sticky = "left" ), target = reactable::colDef( header = withTooltip( "Target", "The target cohort of interest " - ) + ), + sticky = "left" ), comparator = reactable::colDef( header = withTooltip( "Comparator", "The comparator cohort of interest " - ) + ), + sticky = "left" ) ) diff --git a/R/evidence-synth-main.R b/R/evidence-synth-main.R index cf36705c..f9113dca 100644 --- a/R/evidence-synth-main.R +++ b/R/evidence-synth-main.R @@ -224,13 +224,15 @@ evidenceSynthesisServer <- function( header = withTooltip( "Database", "The database name" - ) + ), + sticky = "left" ), target = reactable::colDef( header = withTooltip( "Target", "The target cohort of interest " - ) + ), + sticky = "left" ) ) diff --git a/R/sccs-diagnosticsSummary.R b/R/sccs-diagnosticsSummary.R index ac43867e..8717e110 100644 --- a/R/sccs-diagnosticsSummary.R +++ b/R/sccs-diagnosticsSummary.R @@ -254,19 +254,22 @@ sccsDiagnosticsSummaryServer <- function( header = withTooltip( "Database", "The database name" - ) + ), + sticky = "left" ), target = reactable::colDef( header = withTooltip( "Target", "The target cohort of interest " - ) + ), + sticky = "left" ), covariateName = reactable::colDef( header = withTooltip( "Time Period", "The time period of interest" - ) + ), + sticky = "left" ) ) From a2378cf0c583dac5eeb46ebd58e174de8fc5cec2 Mon Sep 17 00:00:00 2001 From: jreps Date: Wed, 31 May 2023 08:59:05 -0400 Subject: [PATCH 11/20] Update estimation-diagnosticsSummary.R removing shared_balance_diagnostic from diagnostics --- R/estimation-diagnosticsSummary.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/estimation-diagnosticsSummary.R b/R/estimation-diagnosticsSummary.R index e50eb474..4d57ddfb 100644 --- a/R/estimation-diagnosticsSummary.R +++ b/R/estimation-diagnosticsSummary.R @@ -489,7 +489,7 @@ getCmDiagnosticsData <- function( cmds.attrition_fraction, cmds.ease, cmds.balance_diagnostic, - cmds.shared_balance_diagnostic, + --cmds.shared_balance_diagnostic, cmds.equipoise_diagnostic, cmds.mdrr_diagnostic, cmds.attrition_diagnostic, From a32b55018272dd224f1e85888d147fedaffe3dca Mon Sep 17 00:00:00 2001 From: jreps Date: Tue, 13 Jun 2023 14:04:37 -0400 Subject: [PATCH 12/20] prediction updates - replaced plp design and model buttons with action dropdown button - updated tests - renamed tablePrefix to plpTablePrefix in Prediction to prepare for shared settings object across modules - renamed mySchema to schema in Prediction to standardized and prepare for shared settings object across modules --- R/components-data-viewer.R | 119 ++++++- R/helper-getPredictionProtocol.R | 16 +- R/helpers-getPredictionResult.R | 4 +- R/prediction-calibration.R | 20 +- R/prediction-covariateSummary.R | 32 +- R/prediction-cutoff.R | 12 +- R/prediction-designSummary.R | 292 +++++++++++------- R/prediction-diagnostics.R | 66 ++-- R/prediction-discrimination.R | 20 +- R/prediction-main.R | 150 ++++----- R/prediction-modelSummary.R | 216 ++++++------- R/prediction-netbenefit.R | 12 +- R/prediction-settings.R | 118 +++---- R/prediction-validation.R | 46 +-- errorReportSql.txt | 30 -- man/predictionCalibrationServer.Rd | 8 +- man/predictionCovariateSummaryServer.Rd | 8 +- man/predictionCutoffServer.Rd | 8 +- man/predictionDesignSummaryServer.Rd | 14 +- man/predictionDiagnosticsServer.Rd | 12 +- man/predictionDiscriminationServer.Rd | 8 +- man/predictionModelSummaryServer.Rd | 12 +- man/predictionNbServer.Rd | 8 +- man/predictionSettingsServer.Rd | 16 +- man/predictionValidationServer.Rd | 12 +- man/resultTableServer.Rd | 5 +- tests/testthat/setup.R | 2 +- tests/testthat/test-prediction-calibration.R | 4 +- .../test-prediction-covariateSummary.R | 4 +- tests/testthat/test-prediction-cutoff.R | 4 +- .../testthat/test-prediction-designSummary.R | 26 +- tests/testthat/test-prediction-diagnostics.R | 12 +- .../testthat/test-prediction-discrimination.R | 4 +- tests/testthat/test-prediction-main.R | 31 +- tests/testthat/test-prediction-modelSummary.R | 12 +- tests/testthat/test-prediction-netbenefit.R | 4 +- tests/testthat/test-prediction-settings.R | 10 +- tests/testthat/test-prediction-validation.R | 6 +- 38 files changed, 779 insertions(+), 604 deletions(-) delete mode 100644 errorReportSql.txt diff --git a/R/components-data-viewer.R b/R/components-data-viewer.R index 6c4d6484..4f94c8ed 100644 --- a/R/components-data-viewer.R +++ b/R/components-data-viewer.R @@ -78,7 +78,10 @@ withTooltip <- function(value, tooltip, ...) { # header = withTooltip("Disp column name", "Disp tooltip")) # ) -create_colDefs_list <- function(df, customColDefs = NULL) { +create_colDefs_list <- function( + df, + customColDefs = NULL + ) { # Get the column names of the input data frame col_names <- colnames(df) @@ -137,6 +140,8 @@ ohdsiReactableTheme <- reactable::reactableTheme( #' @param id string, table id must match resultsTableViewer function #' @param df reactive that returns a data frame #' @param colDefsInput named list of reactable::colDefs +#' @param addActions add a button row selector column to the table to a column called 'actions'. +#' actions must be a column in df #' #' @return shiny module server #' @export @@ -144,7 +149,8 @@ ohdsiReactableTheme <- reactable::reactableTheme( resultTableServer <- function( id, #string df, #data.frame - colDefsInput + colDefsInput, + addActions = NULL ) #list of colDefs, can use checkmate::assertList, need a check that makes sure names = columns) { shiny::moduleServer( id, @@ -154,6 +160,29 @@ resultTableServer <- function( df <- shiny::reactiveVal(df) } + # add a new entry to colDefs with an action dropdown menu + # add a onClick action + if(!is.null(addActions)){ + + onClickText <- paste0( + "function(rowInfo, column) {", + paste("if(column.id == 'actions'){ + Shiny.setInputValue('",session$ns(paste0('action_index')),"', { index: rowInfo.index + 1 }, { priority: 'event' }) + }", collapse = ' ', sep = ''), + "}" + ) + onClick <- reactable::JS(onClickText) + + colDefsInput <- addTableActions( + colDefsInput = colDefsInput, + addActions = addActions, + session = session + ) + + } else{ + onClick <- NULL + } + output$columnSelector <- shiny::renderUI({ shinyWidgets::pickerInput( @@ -198,6 +227,7 @@ resultTableServer <- function( reactable::reactable( data, columns = colDefs(), + onClick = onClick, #these can be turned on/off and will overwrite colDef args sortable = TRUE, resizable = TRUE, @@ -227,4 +257,89 @@ resultTableServer <- function( ) } ) + + + # capture the actions + actionCount <- shiny::reactiveVal(0) + actionIndex <- shiny::reactiveVal(0) + actionType <- shiny::reactiveVal('none') + shiny::observeEvent(input$action_index, { + actionIndex(input$action_index) + }) + + shiny::observeEvent(input$action_type, { + # update type + actionType(input$action_type$value) + # update count + actionCount(input$action_type$seed) + }) + + return( + list( + actionType = actionType, + actionIndex = actionIndex, + actionCount = actionCount + ) + ) }) + + + + + +# HELPERS +addTableActions <- function( + colDefsInput, + addActions, + session +){ + + args <- list( + label = "Actions", + status = "primary", + circle = FALSE, + width = "300px", + margin = "5px" + ) + + args <- append( + args, + lapply( + X = addActions, + FUN = function(x){ + shiny::actionLink( + inputId = session$ns(x), + label = paste0('View ',x), + icon = shiny::icon("play"), + onClick = reactable::JS( + paste0( + "function() { + Shiny.setInputValue('",session$ns(paste0('action_type')),"', { value: '",x,"', seed: Math.random()}) + }" + ) + ) + ) + }) + ) + + tableActionfunction <- function(){ + + cellFunction <- do.call( + args = args, + what = shinyWidgets::dropdownButton + ) + return(cellFunction) + } + + # add the actions dropdown + colDefsInput[[length(colDefsInput) + 1 ]] <- reactable::colDef( + name = "", + sortable = FALSE, + filterable = FALSE, + minWidth = 150, + cell = tableActionfunction + ) + names(colDefsInput)[length(colDefsInput)] <- 'actions' + + return(colDefsInput) +} diff --git a/R/helper-getPredictionProtocol.R b/R/helper-getPredictionProtocol.R index c4a787a4..7366e395 100644 --- a/R/helper-getPredictionProtocol.R +++ b/R/helper-getPredictionProtocol.R @@ -1,9 +1,9 @@ createPredictionProtocol <- function( connectionHandler, - mySchema, - myTableAppend, - databaseTableAppend, - cohortTableAppend, + schema, + plpTablePrefix, + databaseTablePrefix, + cohortTablePrefix, modelDesignId, output, intermediatesDir = file.path(tempdir(), 'plp-prot') @@ -24,11 +24,11 @@ createPredictionProtocol <- function( output_dir = output, params = list( connectionHandler = connectionHandler, - resultSchema = mySchema, - myTableAppend = myTableAppend, + resultSchema = schema, + myTableAppend = plpTablePrefix, modelDesignIds = modelDesignId, - databaseTableAppend = databaseTableAppend, - cohortTableAppend = cohortTableAppend + databaseTableAppend = databaseTablePrefix, + cohortTableAppend = cohortTablePrefix ) ) diff --git a/R/helpers-getPredictionResult.R b/R/helpers-getPredictionResult.R index b4a459b4..1633af11 100644 --- a/R/helpers-getPredictionResult.R +++ b/R/helpers-getPredictionResult.R @@ -2,7 +2,7 @@ getPredictionResult <- function( connectionHandler, tableName, performanceId, - mySchema + schema ){ shiny::withProgress(message = paste('Extracting PLP results from', tableName), value = 0, { @@ -15,7 +15,7 @@ getPredictionResult <- function( result <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, table_name = tableName, performance_id = performanceId() ) diff --git a/R/prediction-calibration.R b/R/prediction-calibration.R index 3c9a5b28..ebe39524 100644 --- a/R/prediction-calibration.R +++ b/R/prediction-calibration.R @@ -79,8 +79,8 @@ predictionCalibrationViewer <- function(id) { #' @param performanceId the performance id in the database #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the tables in the result schema #' #' @return #' The server to the prediction calibration module @@ -91,8 +91,8 @@ predictionCalibrationServer <- function( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend + schema, + plpTablePrefix ) { shiny::moduleServer( id, @@ -104,8 +104,8 @@ predictionCalibrationServer <- function( data <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(myTableAppend,'evaluation_statistics'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'evaluation_statistics'), + schema = schema ) } else{ data <- NULL @@ -194,16 +194,16 @@ predictionCalibrationServer <- function( value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(myTableAppend,'calibration_summary'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'calibration_summary'), + schema = schema ) calibrationSummary(value) value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(myTableAppend,'demographic_summary'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'demographic_summary'), + schema = schema ) demographicSummary(value) } diff --git a/R/prediction-covariateSummary.R b/R/prediction-covariateSummary.R index e6370949..57852df8 100644 --- a/R/prediction-covariateSummary.R +++ b/R/prediction-covariateSummary.R @@ -89,8 +89,8 @@ predictionCovariateSummaryViewer <- function(id) { #' @param performanceId unique id for the performance results #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the tables in the result schema #' #' @return #' The server to the covariate summary module @@ -103,8 +103,8 @@ predictionCovariateSummaryServer <- function( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend = '' + schema, + plpTablePrefix = '' ) { shiny::moduleServer( id, @@ -117,9 +117,9 @@ predictionCovariateSummaryServer <- function( ){ loadCovSumFromDb( performanceId = performanceId, - mySchema = mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend = myTableAppend + plpTablePrefix = plpTablePrefix ) } else{ NULL @@ -136,8 +136,8 @@ predictionCovariateSummaryServer <- function( modelDesignId = modelDesignId, databaseId = developmentDatabaseId, connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend + schema = schema, + plpTablePrefix = plpTablePrefix ) } else{ NULL @@ -341,9 +341,9 @@ plotCovariateSummary <- function(covariateSummary){ # code for database covariate extract loadCovSumFromDb <- function( performanceId, - mySchema, + schema, connectionHandler, - myTableAppend = '' + plpTablePrefix = '' ){ ParallelLogger::logInfo("starting covsum") @@ -357,9 +357,9 @@ loadCovSumFromDb <- function( covariateSummary <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, performance_id = performanceId(), - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) # format @@ -382,18 +382,18 @@ getIntercept <- function( modelDesignId, databaseId, connectionHandler, - mySchema, - myTableAppend + schema, + plpTablePrefix ){ sql <- "SELECT intercept FROM @my_schema.@my_table_appendmodels WHERE database_id = @database_id and model_design_id = @model_design_id" models <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, database_id = databaseId(), model_design_id = modelDesignId(), - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) intercept <- models$intercept diff --git a/R/prediction-cutoff.R b/R/prediction-cutoff.R index 55a3cdfa..9e8c7e91 100644 --- a/R/prediction-cutoff.R +++ b/R/prediction-cutoff.R @@ -95,8 +95,8 @@ predictionCutoffViewer <- function(id) { #' @param performanceId the performance id in the database #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the tables in the result schema #' #' @return #' The server to the prediction cut-off module @@ -107,8 +107,8 @@ predictionCutoffServer <- function( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend + schema, + plpTablePrefix ) { shiny::moduleServer( id, @@ -122,8 +122,8 @@ predictionCutoffServer <- function( value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - mySchema = mySchema, - tableName = paste0(myTableAppend, 'threshold_summary') + schema = schema, + tableName = paste0(plpTablePrefix, 'threshold_summary') ) return(value) } else{ diff --git a/R/prediction-designSummary.R b/R/prediction-designSummary.R index 324d79e1..f74f7c4a 100644 --- a/R/prediction-designSummary.R +++ b/R/prediction-designSummary.R @@ -30,7 +30,14 @@ #' @export predictionDesignSummaryViewer <- function(id) { ns <- shiny::NS(id) - reactable::reactableOutput(ns('designSummaryTable')) + shiny::div( + inputSelectionViewer(ns("input-selection")), + shiny::conditionalPanel( + condition = 'input.generate != 0', + ns = shiny::NS(ns("input-selection")), + resultTableViewer(ns('designSummaryTable')) + ) + ) } #' The module server for exploring prediction designs in the results @@ -40,8 +47,9 @@ predictionDesignSummaryViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the plp tables in the result schema +#' @param cohortTablePrefix a string that appends the cohort tables in the result schema #' #' @return #' The server to the prediction design module @@ -50,37 +58,88 @@ predictionDesignSummaryViewer <- function(id) { predictionDesignSummaryServer <- function( id, connectionHandler, - mySchema, - myTableAppend + schema, + plpTablePrefix, + cohortTablePrefix ) { shiny::moduleServer( id, function(input, output, session) { - withTooltip <- function(value, tooltip, ...) { - shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", - tippy::tippy(value, tooltip, ...)) - } + targetIds <- getPlpCohortIds( + connectionHandler = connectionHandler, + schema = schema, + plpTablePrefix = plpTablePrefix, + cohortTablePrefix = cohortTablePrefix, + type = 'target' + ) + outcomeIds <- getPlpCohortIds( + connectionHandler = connectionHandler, + schema = schema, + plpTablePrefix = plpTablePrefix, + cohortTablePrefix = cohortTablePrefix, + type = 'outcome' + ) - designSummaryTable <- getDesignSummary( - connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend + # input selection component + inputSelected <- inputSelectionServer( + id = "input-selection", + inputSettingList = list( + createInputSetting( + rowNumber = 1, + columnWidth = 6, + varName = 'targetIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Target: ', + choices = targetIds, + selected = targetIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + createInputSetting( + rowNumber = 1, + columnWidth = 6, + varName = 'outcomeIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Outcome: ', + choices = outcomeIds, + selected = outcomeIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ) + ) ) - # check if this makes drpdwn filter - designSummaryTable$target <- as.factor(designSummaryTable$target) - designSummaryTable$outcome <- as.factor(designSummaryTable$outcome) + designSummaryTable <- shiny::reactive({ + getDesignSummary( + connectionHandler = connectionHandler, + schema = schema, + plpTablePrefix = plpTablePrefix, + cohortTablePrefix = cohortTablePrefix, + targetIds = inputSelected()$targetIds, + outcomeIds = inputSelected()$outcomeIds + ) + }) - output$designSummaryTable <- reactable::renderReactable({ - reactable::reactable( - data = cbind( - designSummaryTable, - diagnostic = rep("",nrow(designSummaryTable)), - details = rep("",nrow(designSummaryTable)), - report = rep("",nrow(designSummaryTable)) - ), - columns = list( + colDefsInput = list( # Render a "show details" button in the last column of the table. # This button won't do anything by itself, but will trigger the custom # click action on the column. @@ -162,89 +221,40 @@ predictionDesignSummaryServer <- function( ), sortable = TRUE, filterable = FALSE - ), - diagnostic = reactable::colDef( - name = "", - sortable = FALSE, - filterable = FALSE, - cell = function() htmltools::tags$button("View Diagnostics") - ), - details = reactable::colDef( - name = "", - sortable = FALSE, - filterable = FALSE, - cell = function() htmltools::tags$button("View Results") - ), - report = reactable::colDef( - name = "", - sortable = FALSE, - filterable = FALSE, - cell = function() htmltools::tags$button("View Report") ) - ), - onClick = reactable::JS(paste0("function(rowInfo, column) { - // Only handle click events on the 'details' column - if (column.id !== 'details' & column.id !== 'report' & column.id !== 'diagnostic') { - return - } - - // Display an alert dialog with details for the row - //window.alert('Details for row ' + rowInfo.index + ':\\n' + JSON.stringify(rowInfo.values, null, 2)) - - // Send the click event to Shiny, which will be available in input$show_details - // Note that the row index starts at 0 in JavaScript, so we add 1 - if(column.id == 'details'){ - Shiny.setInputValue('",session$ns('show_details'),"', { index: rowInfo.index + 1 }, { priority: 'event' }) - } - if(column.id == 'report'){ - Shiny.setInputValue('",session$ns('show_report'),"', { index: rowInfo.index + 1 }, { priority: 'event' }) - } - if(column.id == 'diagnostic'){ - Shiny.setInputValue('",session$ns('show_diagnostic'),"', { index: rowInfo.index + 1 }, { priority: 'event' }) - } - }") - ), - #groupBy = c("outcome","TAR", "target"), feedback was this wasnt nice - filterable = TRUE - ) - }) + ) + tableOutputs <- resultTableServer( + id = "designSummaryTable", # how is this working without session$ns + df = designSummaryTable, + colDefsInput = colDefsInput, + addActions = c('models', 'diagnostics', 'report') + ) + # reactive of modelDesignId for exporing results modelDesignId <- shiny::reactiveVal(value = NULL) - - shiny::observeEvent(input$show_details, { - #print(designSummaryTable$modelDesignId[input$show_details$index]) - if(designSummaryTable$devDatabases[input$show_details$index] > 0){ - modelDesignId(NULL) - modelDesignId(designSummaryTable$modelDesignId[input$show_details$index]) - } else{ - shiny::showNotification("No models available for this model design.") - } - }) - reportId <- shiny::reactiveVal(NULL) - shiny::observeEvent(input$show_report, { - reportId(NULL) - idForReport <- designSummaryTable$modelDesignId[input$show_report$index] - reportId(idForReport) - #writeLines('Testing123', file.path(tempdir(), 'report.html')) - #createProtocol(connection = connection, modelDesignId = idForReport, outputLocation = file.path(tempdir(), 'report.html')) - }) - diagnosticId <- shiny::reactiveVal(value = NULL) - shiny::observeEvent(input$show_diagnostic, { - - if(designSummaryTable$diagDatabases[input$show_diagnostic$index] > 0){ - diagnosticId(NULL) - diagnosticId(designSummaryTable$modelDesignId[input$show_diagnostic$index]) - } else{ - shiny::showNotification("No diagnostic results available for this model design.") + + shiny::observeEvent(tableOutputs$actionCount(), { + if(!is.null(tableOutputs$actionType())){ + if(tableOutputs$actionType() == 'diagnostics'){ + diagnosticId(NULL) + diagnosticId(designSummaryTable()$modelDesignId[tableOutputs$actionIndex()$index]) + } + if(tableOutputs$actionType() == 'report'){ + reportId(NULL) + reportId(designSummaryTable()$modelDesignId[tableOutputs$actionIndex()$index]) + } + if(tableOutputs$actionType() == 'models'){ + modelDesignId(NULL) + modelDesignId(designSummaryTable()$modelDesignId[tableOutputs$actionIndex()$index]) + } } }) - + return( list( - designSummaryTable = designSummaryTable, modelDesignId = modelDesignId, # a reactive diagnosticId = diagnosticId, # a reactive reportId = reportId # a reactive @@ -256,13 +266,53 @@ predictionDesignSummaryServer <- function( } +getPlpCohortIds <- function( + connectionHandler, + schema, + plpTablePrefix, + cohortTablePrefix, + type = 'target' +){ + + sql <- "SELECT distinct cohorts.cohort_id, cohorts.cohort_name + + FROM + @my_schema.@my_table_appendmodel_designs as model_designs + inner join + (SELECT c.cohort_id, cd.cohort_name FROM @my_schema.@my_table_appendcohorts c + inner join @my_schema.@cohort_table_appendcohort_definition cd + on c.cohort_definition_id = cd.cohort_definition_id + ) AS cohorts + ON model_designs.@type_id = cohorts.cohort_id + ;" + + result <- connectionHandler$queryDb( + sql = sql, + my_schema = schema, + my_table_append = plpTablePrefix, + cohort_table_append = cohortTablePrefix, + type = type + ) + + res <- result$cohortId + names(res) <- result$cohortName + + return(res) +} getDesignSummary <- function( connectionHandler, - mySchema, - myTableAppend = '' + schema, + plpTablePrefix = 'plp_', + cohortTablePrefix = 'plp_', + targetIds, + outcomeIds ){ + if(length(targetIds) == 0 | length(outcomeIds) == 0){ + return(data.frame()) + } + shiny::withProgress(message = 'Generating model design summary', value = 0, { sql <- "SELECT @@ -295,14 +345,31 @@ getDesignSummary <- function( LEFT JOIN (select * from @my_schema.@my_table_appendEVALUATION_STATISTICS where EVALUATION = 'Test' and METRIC = 'AUROC') p on p.performance_id = results.performance_id - LEFT JOIN (SELECT cohort_id, cohort_name FROM @my_schema.@my_table_appendcohorts) AS targets ON model_designs.target_id = targets.cohort_id - LEFT JOIN (SELECT cohort_id, cohort_name FROM @my_schema.@my_table_appendcohorts) AS outcomes ON model_designs.outcome_id = outcomes.cohort_id - LEFT JOIN @my_schema.@my_table_appendtars AS tars ON model_designs.tar_id = tars.tar_id + LEFT JOIN + (SELECT c.cohort_id, cd.cohort_name FROM @my_schema.@my_table_appendcohorts c + inner join @my_schema.@cohort_table_appendcohort_definition cd + on c.cohort_definition_id = cd.cohort_definition_id + ) AS targets + ON model_designs.target_id = targets.cohort_id + LEFT JOIN + (SELECT c.cohort_id, cd.cohort_name FROM @my_schema.@my_table_appendcohorts c + inner join @my_schema.@cohort_table_appendcohort_definition cd + on c.cohort_definition_id = cd.cohort_definition_id + ) AS outcomes + ON model_designs.outcome_id = outcomes.cohort_id + LEFT JOIN @my_schema.@my_table_appendtars AS tars + ON model_designs.tar_id = tars.tar_id - LEFT JOIN @my_schema.@my_table_appenddatabase_details AS d ON results.development_database_id = d.database_id - LEFT JOIN @my_schema.@my_table_appenddatabase_details AS v ON results.validation_database_id = v.database_id + LEFT JOIN @my_schema.@my_table_appenddatabase_details AS d + ON results.development_database_id = d.database_id + LEFT JOIN @my_schema.@my_table_appenddatabase_details AS v + ON results.validation_database_id = v.database_id + + LEFT JOIN @my_schema.@my_table_appenddiagnostics AS diag + ON results.development_database_id = diag.database_id - LEFT JOIN @my_schema.@my_table_appenddiagnostics AS diag ON results.development_database_id = diag.database_id + WHERE targets.cohort_id in (@target_ids) + AND outcomes.cohort_id in (@outcome_ids) GROUP BY model_designs.model_design_id, model_settings.model_type, targets.cohort_name, outcomes.cohort_name, tars.tar_start_day, tars.tar_start_anchor, @@ -313,8 +380,11 @@ getDesignSummary <- function( summaryTable <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend + my_schema = schema, + my_table_append = plpTablePrefix, + cohort_table_append = cohortTablePrefix, + target_ids = paste(targetIds, collapse = ','), + outcome_ids = paste(outcomeIds, collapse = ',') ) shiny::incProgress(2/3, detail = paste("Extracted data")) @@ -324,11 +394,15 @@ getDesignSummary <- function( dplyr::relocate("devDatabases", .before = "valDatabases") %>% dplyr::relocate("diagDatabases", .before = "devDatabases") + summaryTable <- cbind( + actions = rep("",nrow(summaryTable)), + summaryTable + ) + shiny::incProgress(3/3, detail = paste("Finished")) }) - return(summaryTable) } diff --git a/R/prediction-diagnostics.R b/R/prediction-diagnostics.R index 634116ec..747c818b 100644 --- a/R/prediction-diagnostics.R +++ b/R/prediction-diagnostics.R @@ -45,10 +45,10 @@ predictionDiagnosticsViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param modelDesignId the unique id for the model design -#' @param mySchema the database schema for the model results +#' @param schema the database schema for the model results #' @param connectionHandler the connection to the prediction result database -#' @param myTableAppend a string that appends the tables in the result schema -#' @param databaseTableAppend a string that appends the database_meta_data table +#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param databaseTablePrefix a string that appends the database_meta_data table #' #' @return #' The server to the predcition diagnostic module @@ -57,10 +57,10 @@ predictionDiagnosticsViewer <- function(id) { predictionDiagnosticsServer <- function( id, modelDesignId, - mySchema, + schema, connectionHandler, - myTableAppend, - databaseTableAppend + plpTablePrefix, + databaseTablePrefix ) { shiny::moduleServer( id, @@ -76,10 +76,10 @@ predictionDiagnosticsServer <- function( diagnosticTable <- getDiagnostics( modelDesignId = modelDesignId(), - mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend, - databaseTableAppend = databaseTableAppend + plpTablePrefix = plpTablePrefix, + databaseTablePrefix = databaseTablePrefix ) # input tables output$diagnosticSummaryTable <- reactable::renderReactable({ @@ -235,9 +235,9 @@ predictionDiagnosticsServer <- function( { participants <- getDiagnosticParticipants( diagnosticId = diagnosticTable$diagnosticId[input$show_participants$index], - mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend + plpTablePrefix = plpTablePrefix ) output$participants <- reactable::renderReactable({ @@ -285,9 +285,9 @@ predictionDiagnosticsServer <- function( predTable <- getDiagnosticPredictors( diagnosticId = diagnosticTable$diagnosticId[input$show_predictors$index], - mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend + plpTablePrefix = plpTablePrefix ) output$predictorPlot <- plotly::renderPlotly({ @@ -362,9 +362,9 @@ predictionDiagnosticsServer <- function( outcomeTable <- getDiagnosticOutcomes( diagnosticId = diagnosticTable$diagnosticId[input$show_outcomes$index], - mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend + plpTablePrefix = plpTablePrefix ) #output$predictorPlot <- @@ -427,10 +427,10 @@ predictionDiagnosticsServer <- function( # get the data getDiagnostics <- function( modelDesignId, - mySchema, + schema, connectionHandler, - myTableAppend, - databaseTableAppend = myTableAppend, + plpTablePrefix, + databaseTablePrefix = plpTablePrefix, threshold1_2 = 0.9 ){ if(!is.null(modelDesignId)){ @@ -473,10 +473,10 @@ getDiagnostics <- function( summaryTable <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend, + my_schema = schema, + my_table_append = plpTablePrefix, model_design_id = modelDesignId, - database_table_append = databaseTableAppend + database_table_append = databaseTablePrefix ) if(nrow(summaryTable)==0){ @@ -510,18 +510,18 @@ getDiagnostics <- function( getDiagnosticParticipants <- function( diagnosticId, - mySchema, + schema, connectionHandler, - myTableAppend + plpTablePrefix ){ sql <- "SELECT * FROM @my_schema.@my_table_append@table_name WHERE diagnostic_id = @diagnostic_id;" participants <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, table_name = 'diagnostic_participants', - my_table_append = myTableAppend, + my_table_append = plpTablePrefix, diagnostic_id = diagnosticId ) @@ -544,18 +544,18 @@ getDiagnosticParticipants <- function( getDiagnosticPredictors <- function( diagnosticId, - mySchema, + schema, connectionHandler, - myTableAppend + plpTablePrefix ){ sql <- "SELECT * FROM @my_schema.@my_table_append@table_name WHERE diagnostic_id = @diagnostic_id;" predictors <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, table_name = 'diagnostic_predictors', - my_table_append = myTableAppend, + my_table_append = plpTablePrefix, diagnostic_id = diagnosticId ) @@ -564,18 +564,18 @@ getDiagnosticPredictors <- function( getDiagnosticOutcomes <- function( diagnosticId, - mySchema, + schema, connectionHandler, - myTableAppend + plpTablePrefix ){ sql <- "SELECT * FROM @my_schema.@my_table_append@table_name WHERE diagnostic_id = @diagnostic_id;" outcomes <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, table_name = 'diagnostic_outcomes', - my_table_append = myTableAppend, + my_table_append = plpTablePrefix, diagnostic_id = diagnosticId ) diff --git a/R/prediction-discrimination.R b/R/prediction-discrimination.R index 91203745..ea207e11 100644 --- a/R/prediction-discrimination.R +++ b/R/prediction-discrimination.R @@ -142,8 +142,8 @@ predictionDiscriminationViewer <- function(id) { #' @param performanceId the performance id in the database #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the tables in the result schema #' #' @return #' The server to the model discrimination module @@ -154,8 +154,8 @@ predictionDiscriminationServer <- function( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend + schema, + plpTablePrefix ) { shiny::moduleServer( id, @@ -167,8 +167,8 @@ predictionDiscriminationServer <- function( data <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(myTableAppend,'evaluation_statistics'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'evaluation_statistics'), + schema = schema ) print('Discrimination ended') } else{ @@ -253,16 +253,16 @@ predictionDiscriminationServer <- function( value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(myTableAppend,'prediction_distribution'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'prediction_distribution'), + schema = schema ) predictionDistribution(value) value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(myTableAppend,'threshold_summary'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'threshold_summary'), + schema = schema ) thresholdSummary(value) } diff --git a/R/prediction-main.R b/R/prediction-main.R index 1f22ba62..e9628d6f 100644 --- a/R/prediction-main.R +++ b/R/prediction-main.R @@ -189,7 +189,7 @@ predictionServer <- function( shiny::updateTabsetPanel( session = session, inputId = 'singleView', - selected = 'Design Settings' + selected = 'Model' ) # @@ -235,8 +235,9 @@ predictionServer <- function( designSummary <- predictionDesignSummaryServer( id = 'designSummaryTab', connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix + schema = resultDatabaseSettings$schema, + plpTablePrefix = resultDatabaseSettings$plpTablePrefix, + cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix ) @@ -267,13 +268,13 @@ predictionServer <- function( performance <- predictionModelSummaryServer( id = 'modelSummaryTab', connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + plpTablePrefix = resultDatabaseSettings$plpTablePrefix, modelDesignId = modelDesignId, - databaseTableAppend = ifelse( + databaseTablePrefix = ifelse( !is.null(resultDatabaseSettings$databaseTablePrefix), resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ) ) @@ -314,13 +315,13 @@ predictionServer <- function( predictionDiagnosticsServer( id = 'diagnostics', modelDesignId = designSummary$diagnosticId, - mySchema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, - myTableAppend = resultDatabaseSettings$tablePrefix, - databaseTableAppend = ifelse( + plpTablePrefix = resultDatabaseSettings$plpTablePrefix, + databaseTablePrefix = ifelse( !is.null(resultDatabaseSettings$databaseTablePrefix), resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ) ) @@ -351,19 +352,19 @@ predictionServer <- function( file.remove(file.path(protocolOutputLoc, 'main.html')) } tryCatch( - {createPredictionProtocol( # add database_table_append and cohort_table_append + {createPredictionProtocol( connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix, - databaseTableAppend = ifelse( + schema = resultDatabaseSettings$schema, + plpTablePrefix = resultDatabaseSettings$plpTablePrefix, + databaseTablePrefix = ifelse( !is.null(resultDatabaseSettings$databaseTablePrefix), resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ), - cohortTableAppend = ifelse( + cohortTablePrefix= ifelse( !is.null(resultDatabaseSettings$cohortTablePrefix), resultDatabaseSettings$cohortTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ), modelDesignId = designSummary$reportId(), output = protocolOutputLoc, @@ -415,19 +416,19 @@ predictionServer <- function( ), intermediates_dir = file.path(tempdir(), 'plp-prot'), output_dir = file.path(input$plpProtocolDownload, paste0('plp_report',designSummary$reportId())), - params = list( + params = list( #TODO UPDATE DOC connectionHandler = connectionHandler, resultSchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix, + myTableAppend = resultDatabaseSettings$plpTablePrefix, databaseTableAppend = ifelse( !is.null(resultDatabaseSettings$databaseTablePrefix), resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ), cohortTableAppend = ifelse( !is.null(resultDatabaseSettings$cohortTablePrefix), resultDatabaseSettings$cohortTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ), modelDesignIds = designSummary$reportId() ) @@ -442,19 +443,19 @@ predictionServer <- function( output$resultSelectText <- shiny::renderUI( getResultSelection( connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + plpTablePrefix = resultDatabaseSettings$plpTablePrefix, modelDesignId = modelDesignId, performanceId = performanceId, - cohortTableAppend = ifelse( + cohortTablePrefix = ifelse( !is.null(resultDatabaseSettings$cohortTablePrefix), resultDatabaseSettings$cohortTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ), - databaseTableAppend = ifelse( + databaseTablePrefix = ifelse( !is.null(resultDatabaseSettings$databaseTablePrefix), resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ) ) ) @@ -466,8 +467,8 @@ predictionServer <- function( performanceId = performanceId, # reactive connectionHandler = connectionHandler, inputSingleView = singleViewValue, - mySchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix + schema = resultDatabaseSettings$schema, + plpTablePrefix = resultDatabaseSettings$plpTablePrefix ) predictionSettingsServer( @@ -475,56 +476,56 @@ predictionServer <- function( modelDesignId = modelDesignId, # reactive developmentDatabaseId = developmentDatabaseId, # reactive performanceId = performanceId, # reactive - mySchema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - myTableAppend = resultDatabaseSettings$tablePrefix, - cohortTableAppend = ifelse( + plpTablePrefix = resultDatabaseSettings$plpTablePrefix, + cohortTablePrefix = ifelse( !is.null(resultDatabaseSettings$cohortTablePrefix), resultDatabaseSettings$cohortTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ), - databaseTableAppend = ifelse( + databaseTablePrefix = ifelse( !is.null(resultDatabaseSettings$databaseTablePrefix), resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ) ) predictionCutoffServer( id = 'cutoff', performanceId = performanceId, - mySchema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - myTableAppend = resultDatabaseSettings$tablePrefix + plpTablePrefix = resultDatabaseSettings$plpTablePrefix ) predictionDiscriminationServer( id = 'discrimination', performanceId = performanceId, - mySchema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - myTableAppend = resultDatabaseSettings$tablePrefix + plpTablePrefix = resultDatabaseSettings$plpTablePrefix ) predictionCalibrationServer( id = 'calibration', performanceId = performanceId, - mySchema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - myTableAppend = resultDatabaseSettings$tablePrefix + plpTablePrefix = resultDatabaseSettings$plpTablePrefix ) predictionNbServer( id = 'netBenefit', performanceId = performanceId, - mySchema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - myTableAppend = resultDatabaseSettings$tablePrefix + plpTablePrefix = resultDatabaseSettings$plpTablePrefix ) predictionValidationServer( @@ -534,12 +535,12 @@ predictionServer <- function( performanceId = performanceId, # reactive connectionHandler = connectionHandler, inputSingleView = singleViewValue, - mySchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix, - databaseTableAppend = ifelse( + schema = resultDatabaseSettings$schema, + plpTablePrefix = resultDatabaseSettings$plpTablePrefix, + databaseTablePrefix = ifelse( !is.null(resultDatabaseSettings$databaseTablePrefix), resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$tablePrefix + resultDatabaseSettings$plpTablePrefix ) ) @@ -551,24 +552,30 @@ predictionServer <- function( getResultSelection <- function( connectionHandler, - mySchema, - myTableAppend, + schema, + plpTablePrefix, modelDesignId, performanceId, - cohortTableAppend, - databaseTableAppend + cohortTablePrefix, + databaseTablePrefix ){ + + if(!inherits(modelDesignId, 'reactive')){ + modelDesignId <- shiny::reactiveVal(modelDesignId) + } + if(!inherits(performanceId, 'reactive')){ + performanceId <- shiny::reactiveVal(performanceId) + } + if(!is.null(modelDesignId()) & !is.null(performanceId())){ modelType <- connectionHandler$queryDb( 'select distinct model_type from @my_schema.@my_table_appendmodels where model_design_id = @model_design_id;', - my_schema = mySchema, - my_table_append = myTableAppend, + my_schema = schema, + my_table_append = plpTablePrefix, model_design_id = modelDesignId() ) - print(modelType) - developmentDb = connectionHandler$queryDb( 'select distinct d.cdm_source_abbreviation from @my_schema.@database_table_appenddatabase_meta_data d @@ -579,14 +586,12 @@ getResultSelection <- function( @my_schema.@my_table_appendperformances p on dd.database_id = p.development_database_id where p.performance_id = @performance_id;', - my_schema = mySchema, - my_table_append = myTableAppend, + my_schema = schema, + my_table_append = plpTablePrefix, performance_id = performanceId(), - database_table_append = databaseTableAppend + database_table_append = databaseTablePrefix ) - - print(developmentDb) - + validationDb = connectionHandler$queryDb( 'select distinct d.cdm_source_abbreviation from @my_schema.@database_table_appenddatabase_meta_data d @@ -597,13 +602,12 @@ getResultSelection <- function( @my_schema.@my_table_appendperformances p on dd.database_id = p.validation_database_id where p.performance_id = @performance_id;', - my_schema = mySchema, - my_table_append = myTableAppend, + my_schema = schema, + my_table_append = plpTablePrefix, performance_id = performanceId(), - database_table_append = databaseTableAppend + database_table_append = databaseTablePrefix ) - print(validationDb) - + target <- connectionHandler$queryDb( 'select distinct c.cohort_name from @my_schema.@my_table_appendcohorts c @@ -611,11 +615,10 @@ getResultSelection <- function( @my_schema.@my_table_appendperformances p on c.cohort_id = p.target_id where p.performance_id = @performance_id;', - my_schema = mySchema, - my_table_append = myTableAppend, + my_schema = schema, + my_table_append = plpTablePrefix, performance_id = performanceId() ) - print(target) outcome <- connectionHandler$queryDb( 'select distinct c.cohort_name from @my_schema.@my_table_appendcohorts c @@ -623,12 +626,11 @@ getResultSelection <- function( @my_schema.@my_table_appendperformances p on c.cohort_id = p.outcome_id where p.performance_id = @performance_id;', - my_schema = mySchema, - my_table_append = myTableAppend, + my_schema = schema, + my_table_append = plpTablePrefix, performance_id = performanceId() ) - print(outcome) - + return( shiny::fluidPage( shiny::fluidRow( diff --git a/R/prediction-modelSummary.R b/R/prediction-modelSummary.R index 12aac32b..7f009c5f 100644 --- a/R/prediction-modelSummary.R +++ b/R/prediction-modelSummary.R @@ -46,7 +46,7 @@ predictionModelSummaryViewer <- function(id) { ), shinydashboard::box( width = "100%", - reactable::reactableOutput(ns('performanceSummaryTable')) + resultTableViewer(ns('performanceSummaryTable')) ) ) } @@ -58,10 +58,10 @@ predictionModelSummaryViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the tables in the result schema #' @param modelDesignId a reactable id specifying the prediction model design identifier -#' @param databaseTableAppend a string that appends the database_meta_data table +#' @param databaseTablePrefix a string that appends the database_meta_data table #' #' @return #' The server to the summary module @@ -70,136 +70,108 @@ predictionModelSummaryViewer <- function(id) { predictionModelSummaryServer <- function( id, connectionHandler, - mySchema, - myTableAppend, + schema, + plpTablePrefix, modelDesignId, - databaseTableAppend = myTableAppend + databaseTablePrefix = plpTablePrefix ) { shiny::moduleServer( id, function(input, output, session) { - withTooltip <- function(value, tooltip, ...) { - shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", - tippy::tippy(value, tooltip, ...)) - } - selectedModelDesign <- shiny::reactive( getModelDesignInfo( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend, + schema = schema, + plpTablePrefix = plpTablePrefix, modelDesignId = modelDesignId, - databaseTableAppend = databaseTableAppend + databaseTablePrefix = databaseTablePrefix ) ) output$performanceSummaryText <- shiny::renderUI(selectedModelDesign()) - resultTable <- shiny::reactive( getModelDesignPerformanceSummary( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend, + schema = schema, + plpTablePrefix = plpTablePrefix, modelDesignId = modelDesignId, - databaseTableAppend = databaseTableAppend + databaseTablePrefix = databaseTablePrefix ) ) - shinyInput <- function(FUN,id,num,label = NULL,...) { - inputs <- character(num) - for (i in seq_len(num)) { - inputs[i] <- as.character(FUN(paste0(id,i),label=label,...)) - } - inputs - } - - output$performanceSummaryTable <- reactable::renderReactable({ - reactable::reactable( - data = cbind( - view = rep("",nrow(resultTable())), - resultTable()[,!colnames(resultTable())%in% c('performanceId', 'developmentDatabaseId', 'modelDevelopment', 'modelDesignId')] - ), - - columns = list( - Dev = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Dev Db", - "The database used to develop the model" - )), - Val = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Val Db", - "The database used to evaluate the model" - )), - T = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Target Pop", - "The patients who the risk model is applied to" - )), - O = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Outcome", - "The outcome being predicted" - )), - TAR = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "TAR", - "The time-at-risk when the outcome is being predicted relative to the target pop index" - ), - sortable = TRUE - ), - type = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Type", - "Development contains the model and internal validation; Validation contains the external validation" - ), - sortable = TRUE - ), - - view = reactable::colDef( - name = "", - sortable = FALSE, - filterable = FALSE, - cell = function() htmltools::tags$button("View Result") - ) + colDefsInput = list( + Dev = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "Dev Db", + "The database used to develop the model" + )), + Val = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "Val Db", + "The database used to evaluate the model" + )), + T = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "Target Pop", + "The patients who the risk model is applied to" + )), + O = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "Outcome", + "The outcome being predicted" + )), + TAR = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "TAR", + "The time-at-risk when the outcome is being predicted relative to the target pop index" ), - onClick = reactable::JS(paste0("function(rowInfo, column) { - // Only handle click events on the 'details' column - if (column.id !== 'view') { - return - } - - - // Send the click event to Shiny, which will be available in input$show_details - // Note that the row index starts at 0 in JavaScript, so we add 1 - // if (window.Shiny) { - if(column.id == 'view'){ - Shiny.setInputValue('",session$ns('view_details'),"', { index: rowInfo.index + 1 }, { priority: 'event' }) - } - // } - }") - ) - + sortable = TRUE + ), + type = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "Type", + "Development contains the model and internal validation; Validation contains the external validation" + ), + sortable = TRUE + ), + modelDevelopment = reactable::colDef( + show = F + ), + performanceId = reactable::colDef( + show = F + ), + modelDesignId = reactable::colDef( + show = F + ), + developmentDatabaseId = reactable::colDef( + show = F ) - - }) + ) + + modelTableOutputs <- resultTableServer( + id = "performanceSummaryTable", + df = resultTable, + colDefsInput = colDefsInput, + addActions = c('results') + ) performanceId <- shiny::reactiveVal(value = NULL) developmentDatabaseId <- shiny::reactiveVal(value = NULL) modelDevelopment <- shiny::reactiveVal(value = NULL) - shiny::observeEvent(input$view_details, { - #print('perf updated') - performanceId(NULL) - performanceId(resultTable()$performanceId[input$view_details$index]) - developmentDatabaseId(resultTable()$developmentDatabaseId[input$view_details$index]) - modelDevelopment(resultTable()$modelDevelopment[input$view_details$index]) + shiny::observeEvent(modelTableOutputs$actionCount(), { + if(modelTableOutputs$actionType() == 'results'){ + performanceId(NULL) + performanceId(resultTable()$performanceId[modelTableOutputs$actionIndex()$index]) + developmentDatabaseId(resultTable()$developmentDatabaseId[modelTableOutputs$actionIndex()$index]) + modelDevelopment(resultTable()$modelDevelopment[modelTableOutputs$actionIndex()$index]) + } }) return( @@ -218,10 +190,10 @@ predictionModelSummaryServer <- function( getModelDesignPerformanceSummary <- function( connectionHandler, - mySchema, - myTableAppend = '', + schema, + plpTablePrefix = '', modelDesignId, - databaseTableAppend + databaseTablePrefix ){ if(is.null(modelDesignId())){ @@ -286,10 +258,10 @@ getModelDesignPerformanceSummary <- function( summaryTable <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend, + my_schema = schema, + my_table_append = plpTablePrefix, model_design_id = modelDesignId(), - database_table_append = databaseTableAppend + database_table_append = databaseTablePrefix ) shiny::incProgress(2/3, detail = paste("Data extracted")) @@ -320,7 +292,13 @@ getModelDesignPerformanceSummary <- function( }) - return(summaryTable[,c('Dev', 'Val', 'T','O', 'modelDesignId', + # adding actions column to left + summaryTable <- cbind( + actions = rep("", nrow(summaryTable)), + summaryTable + ) + + return(summaryTable[,c('actions','Dev', 'Val', 'T','O', 'modelDesignId', 'TAR', 'AUROC', 'AUPRC', 'T Size', 'O Count','Val (%)', 'O Incidence (%)', 'timeStamp', 'performanceId', 'developmentDatabaseId', 'modelDevelopment', 'type')]) @@ -350,16 +328,16 @@ editColnames <- function(cnames, edits){ getModelDesignInfo <- function( connectionHandler, - mySchema, - myTableAppend, + schema, + plpTablePrefix, modelDesignId, - databaseTableAppend + databaseTablePrefix # not used? ){ modelType <- connectionHandler$queryDb( 'select distinct model_type from @my_schema.@my_table_appendmodels where model_design_id = @model_design_id;', - my_schema = mySchema, - my_table_append = myTableAppend, + my_schema = schema, + my_table_append = plpTablePrefix, model_design_id = modelDesignId() ) diff --git a/R/prediction-netbenefit.R b/R/prediction-netbenefit.R index 5027986f..ffc8ea71 100644 --- a/R/prediction-netbenefit.R +++ b/R/prediction-netbenefit.R @@ -75,8 +75,8 @@ predictionNbViewer <- function(id) { #' @param performanceId the performance id in the database #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the tables in the result schema #' #' @return #' The server to the net-benefit module @@ -87,8 +87,8 @@ predictionNbServer <- function( performanceId, # reactive connectionHandler, inputSingleView, - mySchema, - myTableAppend + schema, + plpTablePrefix ) { shiny::moduleServer( id, @@ -101,8 +101,8 @@ predictionNbServer <- function( getPredictionResult( performanceId = performanceId, connectionHandler= connectionHandler, - tableName = paste0(myTableAppend,'threshold_summary'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'threshold_summary'), + schema = schema ) } else{ NULL diff --git a/R/prediction-settings.R b/R/prediction-settings.R index 0b0874c6..efdd867a 100644 --- a/R/prediction-settings.R +++ b/R/prediction-settings.R @@ -65,10 +65,10 @@ predictionSettingsViewer <- function(id) { #' @param performanceId unique id for the performance results #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema -#' @param cohortTableAppend a string that appends the cohort_definition table -#' @param databaseTableAppend a string that appends the database_meta_data table +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param cohortTablePrefix a string that appends the cohort_definition table +#' @param databaseTablePrefix a string that appends the database_meta_data table #' #' @return #' The server to the settings module @@ -81,10 +81,10 @@ predictionSettingsServer <- function( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend, - cohortTableAppend = myTableAppend, - databaseTableAppend = myTableAppend + schema, + plpTablePrefix, + cohortTablePrefix = plpTablePrefix, + databaseTablePrefix = plpTablePrefix ) { shiny::moduleServer( @@ -97,28 +97,28 @@ predictionSettingsServer <- function( getModelDesign( inputSingleView = inputSingleView, modelDesignId = modelDesignId, - mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend, - cohortTableAppend + plpTablePrefix = plpTablePrefix, + cohortTablePrefix = cohortTablePrefix )}) hyperParamSearch <- shiny::reactive({getHyperParamSearch( inputSingleView = inputSingleView, modelDesignId = modelDesignId, databaseId = developmentDatabaseId, - mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend + plpTablePrefix = plpTablePrefix ) }) attrition <- shiny::reactive({ getAttrition( inputSingleView = inputSingleView, performanceId = performanceId, - mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend + plpTablePrefix = plpTablePrefix ) }) @@ -127,10 +127,10 @@ predictionSettingsServer <- function( getPlpSettingDatabase( inputSingleView = inputSingleView, performanceId = performanceId, - mySchema = mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend = myTableAppend, - databaseTableAppend = databaseTableAppend + plpTablePrefix = plpTablePrefix, + databaseTablePrefix = databaseTablePrefix ) }) @@ -438,10 +438,10 @@ predictionSettingsServer <- function( getPlpSettingDatabase <- function( inputSingleView, performanceId, - mySchema, + schema, connectionHandler, - myTableAppend, - databaseTableAppend = myTableAppend + plpTablePrefix, + databaseTablePrefix = plpTablePrefix ){ if(!is.null(performanceId()) & inputSingleView() == 'Design Settings'){ @@ -480,10 +480,10 @@ getPlpSettingDatabase <- function( databaseNames <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, performance_id = performanceId(), - my_table_append = myTableAppend, - database_table_append = databaseTableAppend + my_table_append = plpTablePrefix, + database_table_append = databaseTablePrefix ) return(databaseNames) @@ -497,10 +497,10 @@ getPlpSettingDatabase <- function( getModelDesign <- function( inputSingleView, modelDesignId, - mySchema, + schema, connectionHandler, - myTableAppend, - cohortTableAppend = myTableAppend + plpTablePrefix, + cohortTablePrefix = plpTablePrefix ){ if(!is.null(modelDesignId()) & inputSingleView() == 'Design Settings'){ @@ -516,9 +516,9 @@ getModelDesign <- function( ids <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, model_design_id = modelDesignId(), - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) @@ -539,9 +539,9 @@ getModelDesign <- function( tempModSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, model_setting_id = modSetId, - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) modelDesign$modelSettings <- ParallelLogger::convertJsonToSettings( @@ -554,9 +554,9 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = covSetId, - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) modelDesign$covariateSettings <- ParallelLogger::convertJsonToSettings( tempSettings$covariateSettingsJson @@ -569,9 +569,9 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = popSetId, - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) modelDesign$populationSettings <- ParallelLogger::convertJsonToSettings( @@ -584,9 +584,9 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = feSetId, - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) modelDesign$featureEngineeringSettings <- tempSettings$featureEngineeringSettingsJson @@ -596,9 +596,9 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = tidyCovariatesSettingId, - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) modelDesign$preprocessSettings <- tempSettings$tidyCovariatesSettingsJson @@ -609,9 +609,9 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = plpDataSettingId, - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) modelDesign$RestrictPlpData <- tempSettings$plpDataSettingsJson @@ -622,9 +622,9 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = sampleSetId, - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) modelDesign$sampleSettings <- tempSettings$sampleSettingsJson @@ -635,9 +635,9 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = splitId, - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) modelDesign$splitSettings <- tempSettings$splitSettingsJson @@ -652,10 +652,10 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = tId, - my_table_append = myTableAppend, - cohort_table_append = cohortTableAppend + my_table_append = plpTablePrefix, + cohort_table_append = cohortTablePrefix ) modelDesign$cohort <- tempSettings @@ -670,10 +670,10 @@ getModelDesign <- function( tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, setting_id = oId, - my_table_append = myTableAppend, - cohort_table_append = cohortTableAppend + my_table_append = plpTablePrefix, + cohort_table_append = cohortTablePrefix ) modelDesign$outcome <- tempSettings @@ -691,9 +691,9 @@ getHyperParamSearch <- function( inputSingleView, modelDesignId, databaseId, - mySchema, + schema, connectionHandler, - myTableAppend + plpTablePrefix ){ if(!is.null(modelDesignId()) & inputSingleView() == 'Design Settings'){ @@ -703,10 +703,10 @@ getHyperParamSearch <- function( models <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, database_id = databaseId(), model_design_id = modelDesignId(), - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) trainDetails <- ParallelLogger::convertJsonToSettings(models$trainDetails) @@ -718,9 +718,9 @@ getHyperParamSearch <- function( getAttrition <- function( inputSingleView, performanceId, - mySchema, + schema, connectionHandler, - myTableAppend + plpTablePrefix ){ if(!is.null(performanceId()) & inputSingleView() == 'Design Settings'){ @@ -729,9 +729,9 @@ getAttrition <- function( attrition <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, performance_id = performanceId(), - my_table_append = myTableAppend + my_table_append = plpTablePrefix ) return(attrition) diff --git a/R/prediction-validation.R b/R/prediction-validation.R index 8035eb92..09d4e8d7 100644 --- a/R/prediction-validation.R +++ b/R/prediction-validation.R @@ -65,9 +65,9 @@ predictionValidationViewer <- function(id) { #' @param performanceId identifier for the performance #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema -#' @param databaseTableAppend a string that appends the database_meta_data table +#' @param schema the database schema for the model results +#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param databaseTablePrefix a string that appends the database_meta_data table #' #' @return #' The server to the validation module @@ -80,9 +80,9 @@ predictionValidationServer <- function( performanceId, # reactive connectionHandler, inputSingleView, - mySchema, - myTableAppend = NULL, - databaseTableAppend = myTableAppend + schema, + plpTablePrefix = NULL, + databaseTablePrefix = plpTablePrefix ) { shiny::moduleServer( @@ -95,11 +95,11 @@ predictionValidationServer <- function( getValSummary( connectionHandler = connectionHandler, - mySchema, + schema = schema, modelDesignId = modelDesignId(), developmentDatabaseId = developmentDatabaseId(), - myTableAppend = myTableAppend, - databaseTableAppend = databaseTableAppend, + plpTablePrefix = plpTablePrefix, + databaseTablePrefix = databaseTablePrefix, inputSingleView = inputSingleView() ) @@ -133,8 +133,8 @@ predictionValidationServer <- function( validationTable = validationTable, validationRowIds = input$validationTable_rows_selected, connectionHandler = connectionHandler, - myTableAppend = myTableAppend, - mySchema = mySchema + plpTablePrefix = plpTablePrefix, + schema = schema ) }) @@ -172,8 +172,8 @@ getValidationResults <- function( validationTable, validationRowIds, connectionHandler, - myTableAppend, - mySchema + plpTablePrefix, + schema ){ if(!is.null(validationTable()) & !is.null(validationRowIds)){ @@ -185,14 +185,14 @@ getValidationResults <- function( thresholdSummaryList[[i]] <- getPredictionResult( performanceId = shiny::reactiveVal(valTable$performanceId[i]), connectionHandler = connectionHandler, - tableName = paste0(myTableAppend,'threshold_summary'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'threshold_summary'), + schema = schema ) calibrationSummaryList[[i]] <- getPredictionResult( performanceId = shiny::reactiveVal(valTable$performanceId[i]), connectionHandler = connectionHandler, - tableName = paste0(myTableAppend,'calibration_summary'), - mySchema = mySchema + tableName = paste0(plpTablePrefix,'calibration_summary'), + schema = schema ) } return( @@ -218,11 +218,11 @@ getValidationResults <- function( getValSummary <- function( connectionHandler, - mySchema, + schema, modelDesignId, developmentDatabaseId, - myTableAppend = '', - databaseTableAppend = myTableAppend, + plpTablePrefix = '', + databaseTablePrefix = plpTablePrefix, inputSingleView ){ @@ -283,11 +283,11 @@ getValSummary <- function( valTable <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, + my_schema = schema, model_design_id = modelDesignId, development_database_id = developmentDatabaseId, - my_table_append = myTableAppend, - database_table_append = databaseTableAppend + my_table_append = plpTablePrefix, + database_table_append = databaseTablePrefix ) valTable$target <- trimws(valTable$target) # not needed diff --git a/errorReportSql.txt b/errorReportSql.txt deleted file mode 100644 index 0baa3be8..00000000 --- a/errorReportSql.txt +++ /dev/null @@ -1,30 +0,0 @@ -DBMS: -postgresql - -Error: -org.postgresql.util.PSQLException: This connection has been closed. - -SQL: -ABORT - -R version: -R version 4.2.2 (2022-10-31 ucrt) - -Platform: -x86_64-w64-mingw32 - -Attached base packages: -- stats -- graphics -- grDevices -- utils -- datasets -- methods -- base - -Other attached packages: -- OhdsiShinyModules (1.1.0.9000) -- shiny (1.7.4) -- ShinyAppBuilder (1.1.2) -- dplyr (1.1.0) -- testthat (3.1.6) \ No newline at end of file diff --git a/man/predictionCalibrationServer.Rd b/man/predictionCalibrationServer.Rd index 61aaad16..c112edc5 100644 --- a/man/predictionCalibrationServer.Rd +++ b/man/predictionCalibrationServer.Rd @@ -9,8 +9,8 @@ predictionCalibrationServer( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend + schema, + plpTablePrefix ) } \arguments{ @@ -22,9 +22,9 @@ predictionCalibrationServer( \item{inputSingleView}{the current tab} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} } \value{ The server to the prediction calibration module diff --git a/man/predictionCovariateSummaryServer.Rd b/man/predictionCovariateSummaryServer.Rd index 9f50eeb7..6b7190be 100644 --- a/man/predictionCovariateSummaryServer.Rd +++ b/man/predictionCovariateSummaryServer.Rd @@ -11,8 +11,8 @@ predictionCovariateSummaryServer( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend = "" + schema, + plpTablePrefix = "" ) } \arguments{ @@ -28,9 +28,9 @@ predictionCovariateSummaryServer( \item{inputSingleView}{the current tab} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} } \value{ The server to the covariate summary module diff --git a/man/predictionCutoffServer.Rd b/man/predictionCutoffServer.Rd index 817a9f82..65583212 100644 --- a/man/predictionCutoffServer.Rd +++ b/man/predictionCutoffServer.Rd @@ -9,8 +9,8 @@ predictionCutoffServer( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend + schema, + plpTablePrefix ) } \arguments{ @@ -22,9 +22,9 @@ predictionCutoffServer( \item{inputSingleView}{the current tab} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} } \value{ The server to the prediction cut-off module diff --git a/man/predictionDesignSummaryServer.Rd b/man/predictionDesignSummaryServer.Rd index 7860f5e4..8610acd5 100644 --- a/man/predictionDesignSummaryServer.Rd +++ b/man/predictionDesignSummaryServer.Rd @@ -4,16 +4,24 @@ \alias{predictionDesignSummaryServer} \title{The module server for exploring prediction designs in the results} \usage{ -predictionDesignSummaryServer(id, connectionHandler, mySchema, myTableAppend) +predictionDesignSummaryServer( + id, + connectionHandler, + schema, + plpTablePrefix, + cohortTablePrefix +) } \arguments{ \item{id}{the unique reference id for the module} \item{connectionHandler}{the connection to the prediction result database} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the plp tables in the result schema} + +\item{cohortTablePrefix}{a string that appends the cohort tables in the result schema} } \value{ The server to the prediction design module diff --git a/man/predictionDiagnosticsServer.Rd b/man/predictionDiagnosticsServer.Rd index acf885a1..0fce5add 100644 --- a/man/predictionDiagnosticsServer.Rd +++ b/man/predictionDiagnosticsServer.Rd @@ -7,10 +7,10 @@ predictionDiagnosticsServer( id, modelDesignId, - mySchema, + schema, connectionHandler, - myTableAppend, - databaseTableAppend + plpTablePrefix, + databaseTablePrefix ) } \arguments{ @@ -18,13 +18,13 @@ predictionDiagnosticsServer( \item{modelDesignId}{the unique id for the model design} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} \item{connectionHandler}{the connection to the prediction result database} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} -\item{databaseTableAppend}{a string that appends the database_meta_data table} +\item{databaseTablePrefix}{a string that appends the database_meta_data table} } \value{ The server to the predcition diagnostic module diff --git a/man/predictionDiscriminationServer.Rd b/man/predictionDiscriminationServer.Rd index 3941df34..76ba9f4e 100644 --- a/man/predictionDiscriminationServer.Rd +++ b/man/predictionDiscriminationServer.Rd @@ -9,8 +9,8 @@ predictionDiscriminationServer( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend + schema, + plpTablePrefix ) } \arguments{ @@ -22,9 +22,9 @@ predictionDiscriminationServer( \item{inputSingleView}{the current tab} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} } \value{ The server to the model discrimination module diff --git a/man/predictionModelSummaryServer.Rd b/man/predictionModelSummaryServer.Rd index 166ace74..c9de790a 100644 --- a/man/predictionModelSummaryServer.Rd +++ b/man/predictionModelSummaryServer.Rd @@ -7,10 +7,10 @@ predictionModelSummaryServer( id, connectionHandler, - mySchema, - myTableAppend, + schema, + plpTablePrefix, modelDesignId, - databaseTableAppend = myTableAppend + databaseTablePrefix = plpTablePrefix ) } \arguments{ @@ -18,13 +18,13 @@ predictionModelSummaryServer( \item{connectionHandler}{the connection to the prediction result database} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} \item{modelDesignId}{a reactable id specifying the prediction model design identifier} -\item{databaseTableAppend}{a string that appends the database_meta_data table} +\item{databaseTablePrefix}{a string that appends the database_meta_data table} } \value{ The server to the summary module diff --git a/man/predictionNbServer.Rd b/man/predictionNbServer.Rd index 3ba9dc00..c4063d31 100644 --- a/man/predictionNbServer.Rd +++ b/man/predictionNbServer.Rd @@ -9,8 +9,8 @@ predictionNbServer( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend + schema, + plpTablePrefix ) } \arguments{ @@ -22,9 +22,9 @@ predictionNbServer( \item{inputSingleView}{the current tab} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} } \value{ The server to the net-benefit module diff --git a/man/predictionSettingsServer.Rd b/man/predictionSettingsServer.Rd index 4da8460e..0bf22908 100644 --- a/man/predictionSettingsServer.Rd +++ b/man/predictionSettingsServer.Rd @@ -11,10 +11,10 @@ predictionSettingsServer( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend, - cohortTableAppend = myTableAppend, - databaseTableAppend = myTableAppend + schema, + plpTablePrefix, + cohortTablePrefix = plpTablePrefix, + databaseTablePrefix = plpTablePrefix ) } \arguments{ @@ -30,13 +30,13 @@ predictionSettingsServer( \item{inputSingleView}{the current tab} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} -\item{cohortTableAppend}{a string that appends the cohort_definition table} +\item{cohortTablePrefix}{a string that appends the cohort_definition table} -\item{databaseTableAppend}{a string that appends the database_meta_data table} +\item{databaseTablePrefix}{a string that appends the database_meta_data table} } \value{ The server to the settings module diff --git a/man/predictionValidationServer.Rd b/man/predictionValidationServer.Rd index d269be9a..8fe117b6 100644 --- a/man/predictionValidationServer.Rd +++ b/man/predictionValidationServer.Rd @@ -11,9 +11,9 @@ predictionValidationServer( performanceId, connectionHandler, inputSingleView, - mySchema, - myTableAppend = NULL, - databaseTableAppend = myTableAppend + schema, + plpTablePrefix = NULL, + databaseTablePrefix = plpTablePrefix ) } \arguments{ @@ -29,11 +29,11 @@ predictionValidationServer( \item{inputSingleView}{the current tab} -\item{mySchema}{the database schema for the model results} +\item{schema}{the database schema for the model results} -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{plpTablePrefix}{a string that appends the tables in the result schema} -\item{databaseTableAppend}{a string that appends the database_meta_data table} +\item{databaseTablePrefix}{a string that appends the database_meta_data table} } \value{ The server to the validation module diff --git a/man/resultTableServer.Rd b/man/resultTableServer.Rd index ab44eeea..f13bc361 100644 --- a/man/resultTableServer.Rd +++ b/man/resultTableServer.Rd @@ -4,7 +4,7 @@ \alias{resultTableServer} \title{Result Table Server} \usage{ -resultTableServer(id, df, colDefsInput) +resultTableServer(id, df, colDefsInput, addActions = NULL) } \arguments{ \item{id}{string, table id must match resultsTableViewer function} @@ -12,6 +12,9 @@ resultTableServer(id, df, colDefsInput) \item{df}{reactive that returns a data frame} \item{colDefsInput}{named list of reactable::colDefs} + +\item{addActions}{add a button row selector column to the table to a column called 'actions'. +actions must be a column in df} } \value{ shiny module server diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 05332bf8..7727c8ad 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -37,7 +37,7 @@ connectionHandlerPlp <- ResultModelManager::ConnectionHandler$new(connectionDeta resultDatabaseSettingsPlp <- list( dbms = 'sqlite', # should this be removed - can use connection - tablePrefix = '', + plpTablePrefix = '', cohortTablePrefix = '', databaseTablePrefix = '', schema = 'main' diff --git a/tests/testthat/test-prediction-calibration.R b/tests/testthat/test-prediction-calibration.R index fc5a2af8..5467f3d6 100644 --- a/tests/testthat/test-prediction-calibration.R +++ b/tests/testthat/test-prediction-calibration.R @@ -6,8 +6,8 @@ shiny::testServer( performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Calibration"), - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { diff --git a/tests/testthat/test-prediction-covariateSummary.R b/tests/testthat/test-prediction-covariateSummary.R index 462e8aee..c3d90249 100644 --- a/tests/testthat/test-prediction-covariateSummary.R +++ b/tests/testthat/test-prediction-covariateSummary.R @@ -8,8 +8,8 @@ shiny::testServer( performanceId = shiny::reactiveVal(NULL), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Discrimination"), - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { expect_true(is.null(covariateSummary())) diff --git a/tests/testthat/test-prediction-cutoff.R b/tests/testthat/test-prediction-cutoff.R index 6be88cd5..e2a739d6 100644 --- a/tests/testthat/test-prediction-cutoff.R +++ b/tests/testthat/test-prediction-cutoff.R @@ -6,8 +6,8 @@ shiny::testServer( performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Threshold Dependant"), - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { diff --git a/tests/testthat/test-prediction-designSummary.R b/tests/testthat/test-prediction-designSummary.R index e82ca46b..4602f373 100644 --- a/tests/testthat/test-prediction-designSummary.R +++ b/tests/testthat/test-prediction-designSummary.R @@ -4,21 +4,31 @@ shiny::testServer( app = predictionDesignSummaryServer, args = list( connectionHandler = connectionHandlerPlp, - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + cohortTablePrefix = resultDatabaseSettingsPlp$cohortTablePrefix ), expr = { expect_true(is.null(modelDesignId())) - session$setInputs(show_details = list(index = 1)) - expect_true(!is.null(modelDesignId())) + #session$setInputs(show_details = list(index = 1)) + #expect_true(!is.null(modelDesignId())) expect_true(is.null(reportId())) - session$setInputs(show_report = list(index = 1)) - expect_true(!is.null(reportId())) + #session$setInputs(show_report = list(index = 1)) + #expect_true(!is.null(reportId())) expect_true(is.null(diagnosticId())) - session$setInputs(show_diagnostic = list(index = 1)) - expect_true(!is.null(diagnosticId())) + #session$setInputs(show_diagnostic = list(index = 1)) + #expect_true(!is.null(diagnosticId())) + + designSummary <- getDesignSummary( + connectionHandler = connectionHandlerPlp, + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + cohortTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + targetIds = targetIds[1], + outcomeIds = outcomeIds[1] + ) }) diff --git a/tests/testthat/test-prediction-diagnostics.R b/tests/testthat/test-prediction-diagnostics.R index 6e4481a4..037b8e63 100644 --- a/tests/testthat/test-prediction-diagnostics.R +++ b/tests/testthat/test-prediction-diagnostics.R @@ -5,18 +5,18 @@ shiny::testServer( args = list( modelDesignId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix, - databaseTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + databaseTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { diag <- getDiagnostics( modelDesignId = modelDesignId(), - mySchema = mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend = myTableAppend, - databaseTableAppend = databaseTableAppend + plpTablePrefix = plpTablePrefix, + databaseTablePrefix = databaseTablePrefix ) expect_true(nrow(diag) >0 ) diff --git a/tests/testthat/test-prediction-discrimination.R b/tests/testthat/test-prediction-discrimination.R index 48ed4830..66a275e8 100644 --- a/tests/testthat/test-prediction-discrimination.R +++ b/tests/testthat/test-prediction-discrimination.R @@ -6,8 +6,8 @@ shiny::testServer( performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Discrimination"), - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { diff --git a/tests/testthat/test-prediction-main.R b/tests/testthat/test-prediction-main.R index 8a7201b6..8882158b 100644 --- a/tests/testthat/test-prediction-main.R +++ b/tests/testthat/test-prediction-main.R @@ -9,22 +9,35 @@ shiny::testServer( expr = { expect_true(is.null(modelDesignId())) - designSummary$modelDesignId(1) + # designSummary + ##designSummary$modelDesignId(1) ##expect_true(!is.null(modelDesignId())) - performance$performanceId(1) - # check performanceId() and developmentDatabaseId() - ##expect_true(!is.null(performanceId())) - ##expect_true(!is.null(developmentDatabaseId())) + ##designSummary$diagnosticId(1) - designSummary$reportId(NULL) - designSummary$reportId(1) + ##designSummary$reportId(1) ##expect_true(file.exists(file.path(tempdir(), 'main.html'))) - designSummary$diagnosticId(1) + ##performance$performanceId(1) + # check performanceId() and developmentDatabaseId() + ##expect_true(!is.null(performanceId())) + ##expect_true(!is.null(developmentDatabaseId())) session$setInputs(allView = 'Model Designs Summary') + session$setInputs(backToModelSummary = T) + session$setInputs(backToDesignSummary = T) + + result <- getResultSelection( + connectionHandler = connectionHandlerPlp, + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + modelDesignId = 1, + performanceId = 1, + cohortTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + databaseTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + ) + testthat::expect_is(result, 'shiny.tag.list') }) @@ -36,3 +49,5 @@ test_that("Test prediction ui", { checkmate::expect_list(ui) }) + + diff --git a/tests/testthat/test-prediction-modelSummary.R b/tests/testthat/test-prediction-modelSummary.R index fb2a2f3e..c82d7c86 100644 --- a/tests/testthat/test-prediction-modelSummary.R +++ b/tests/testthat/test-prediction-modelSummary.R @@ -5,9 +5,9 @@ shiny::testServer( args = list( connectionHandler = connectionHandlerPlp, modelDesignId = shiny::reactiveVal(1), - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix, - databaseTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + databaseTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { @@ -16,8 +16,8 @@ shiny::testServer( expect_true(is.null(performanceId())) expect_true(is.null(developmentDatabaseId())) - session$setInputs(view_details = list(index = 1)) - expect_true(!is.null(performanceId())) - expect_true(!is.null(developmentDatabaseId())) + #session$setInputs(view_details = list(index = 1)) + #expect_true(!is.null(performanceId())) + #expect_true(!is.null(developmentDatabaseId())) }) diff --git a/tests/testthat/test-prediction-netbenefit.R b/tests/testthat/test-prediction-netbenefit.R index b22e3e43..c84548fd 100644 --- a/tests/testthat/test-prediction-netbenefit.R +++ b/tests/testthat/test-prediction-netbenefit.R @@ -6,8 +6,8 @@ shiny::testServer( performanceId = shiny::reactiveVal(NULL), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Discrimination"), - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { diff --git a/tests/testthat/test-prediction-settings.R b/tests/testthat/test-prediction-settings.R index b84b1665..5b81b471 100644 --- a/tests/testthat/test-prediction-settings.R +++ b/tests/testthat/test-prediction-settings.R @@ -8,8 +8,8 @@ shiny::testServer( performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal('Design Settings'), # only works with this - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { @@ -31,10 +31,10 @@ shiny::testServer( design <- getModelDesign( inputSingleView = inputSingleView, modelDesignId = modelDesignId, - mySchema = mySchema, + schema = schema, connectionHandler = connectionHandler, - myTableAppend = myTableAppend, - cohortTableAppend = '' # add as input? + plpTablePrefix = plpTablePrefix, + cohortTablePrefix = '' # add as input? ) expect_true(class(design) == 'list') expect_true(!is.null(design$RestrictPlpData)) diff --git a/tests/testthat/test-prediction-validation.R b/tests/testthat/test-prediction-validation.R index 0da81ee3..c1052b6b 100644 --- a/tests/testthat/test-prediction-validation.R +++ b/tests/testthat/test-prediction-validation.R @@ -8,9 +8,9 @@ shiny::testServer( performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal('No Validation'), - mySchema = resultDatabaseSettingsPlp$schema, - myTableAppend = resultDatabaseSettingsPlp$tablePrefix, - databaseTableAppend = resultDatabaseSettingsPlp$tablePrefix + schema = resultDatabaseSettingsPlp$schema, + plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + databaseTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix ), expr = { From 23af060f30f536edb64e67d41c8fa45d3ab961c6 Mon Sep 17 00:00:00 2001 From: jreps Date: Thu, 13 Jul 2023 09:59:53 -0400 Subject: [PATCH 13/20] restructuring - renamed description to characterization - renamed estimation to cohort-method - updated inputs to prediction --- NAMESPACE | 80 +++++------ ...R => characterization-aggregateFeatures.R} | 24 ++-- ...n-cohorts.R => characterization-cohorts.R} | 8 +- ...characterization-dechallengeRechallenge.R} | 8 +- ...cidence.R => characterization-incidence.R} | 8 +- ...ription-main.R => characterization-main.R} | 46 +++---- ...Event.R => characterization-timeToEvent.R} | 10 +- ...-attrition.R => cohort-method-attrition.R} | 12 +- ...nce.R => cohort-method-covariateBalance.R} | 30 ++-- ...y.R => cohort-method-diagnosticsSummary.R} | 8 +- ...orestPlot.R => cohort-method-forestPlot.R} | 12 +- ...inMeier.R => cohort-method-kaplainMeier.R} | 14 +- R/{estimation-main.R => cohort-method-main.R} | 86 ++++++------ ...cohort-method-populationCharacteristics.R} | 20 +-- ...timation-power.R => cohort-method-power.R} | 16 +-- ...odel.R => cohort-method-propensityModel.R} | 12 +- ...hort-method-propensityScoreDistribution.R} | 18 +-- ...tsTable.R => cohort-method-resultsTable.R} | 36 ++--- ...-subgroups.R => cohort-method-subgroups.R} | 12 +- ...rror.R => cohort-method-systematicError.R} | 18 +-- R/components-data-viewer.R | 7 +- ...lls.R => helpers-cohort-methodDataPulls.R} | 38 +++--- ... => helpers-cohort-methodPlotsAndTables.R} | 24 ++-- ...ity.R => helpers-getCohortMethodUtility.R} | 6 +- R/prediction-covariateSummary.R | 112 +++++++++++++-- R/prediction-cutoff.R | 4 +- R/prediction-discrimination.R | 77 +++++------ R/prediction-modelSummary.R | 52 ++++++- R/prediction-settings.R | 128 +----------------- .../characterization.html} | 0 .../help-OutcomeStratified.html | 0 .../help-dechallengeRechallenge.html | 0 .../help-incidenceRate.html | 0 .../help-targetViewer.html | 0 .../help-timeToEvent.html | 0 .../Table1Specs.csv | 0 .../cohort-method.html} | 0 ...haracterizationAggregateFeaturesServer.Rd} | 8 +- ...haracterizationAggregateFeaturesViewer.Rd} | 8 +- ...terizationDechallengeRechallengeServer.Rd} | 8 +- ...terizationDechallengeRechallengeViewer.Rd} | 8 +- man/characterizationHelperFile.Rd | 17 +++ ....Rd => characterizationIncidenceServer.Rd} | 8 +- ....Rd => characterizationIncidenceViewer.Rd} | 8 +- man/characterizationServer.Rd | 28 ++++ ...rver.Rd => characterizationTableServer.Rd} | 8 +- ...ewer.Rd => characterizationTableViewer.Rd} | 8 +- ...d => characterizationTimeToEventServer.Rd} | 8 +- ...d => characterizationTimeToEventViewer.Rd} | 10 +- man/characterizationViewer.Rd | 20 +++ ...rver.Rd => cohortMethodAttritionServer.Rd} | 8 +- ...ewer.Rd => cohortMethodAttritionViewer.Rd} | 10 +- ... => cohortMethodCovariateBalanceServer.Rd} | 8 +- ... => cohortMethodCovariateBalanceViewer.Rd} | 10 +- ...> cohortMethodDiagnosticsSummaryServer.Rd} | 8 +- ...> cohortMethodDiagnosticsSummaryViewer.Rd} | 10 +- ...ver.Rd => cohortMethodForestPlotServer.Rd} | 8 +- ...wer.Rd => cohortMethodForestPlotViewer.Rd} | 10 +- man/cohortMethodHelperFile.Rd | 17 +++ ...er.Rd => cohortMethodKaplanMeierServer.Rd} | 8 +- ...er.Rd => cohortMethodKaplanMeierViewer.Rd} | 8 +- ...tMethodPopulationCharacteristicsServer.Rd} | 8 +- ...rtMethodPopulationCharacteristicsViewer.Rd | 17 +++ ...erServer.Rd => cohortMethodPowerServer.Rd} | 8 +- ...erViewer.Rd => cohortMethodPowerViewer.Rd} | 10 +- ...d => cohortMethodPropensityModelServer.Rd} | 8 +- ...d => cohortMethodPropensityModelViewer.Rd} | 10 +- ... cohortMethodPropensityScoreDistServer.Rd} | 8 +- ... cohortMethodPropensityScoreDistViewer.Rd} | 10 +- ...r.Rd => cohortMethodResultsTableServer.Rd} | 8 +- ...r.Rd => cohortMethodResultsTableViewer.Rd} | 8 +- ...imationServer.Rd => cohortMethodServer.Rd} | 12 +- ...rver.Rd => cohortMethodSubgroupsServer.Rd} | 8 +- ...ewer.Rd => cohortMethodSubgroupsViewer.Rd} | 10 +- ...d => cohortMethodSystematicErrorServer.Rd} | 8 +- ...d => cohortMethodSystematicErrorViewer.Rd} | 10 +- man/cohortMethodViewer.Rd | 17 +++ man/descriptionHelperFile.Rd | 17 --- man/descriptionServer.Rd | 28 ---- man/descriptionViewer.Rd | 20 --- man/estimationHelperFile.Rd | 17 --- ...timationPopulationCharacteristicsViewer.Rd | 17 --- man/estimationViewer.Rd | 17 --- .../databaseFile.sqlite | Bin .../databaseFile.sqlite | Bin tests/testthat/setup.R | 36 +++-- ...est-characterization-aggregate-features.R} | 14 +- ...orts.R => test-characterization-cohorts.R} | 14 +- ...characterization-dechallengeRechallenge.R} | 14 +- ...ce.R => test-characterization-incidence.R} | 13 +- ...on-main.R => test-characterization-main.R} | 12 +- ....R => test-characterization-timeToEvent.R} | 14 +- ... => test-cohort-method-CovariateBalance.R} | 8 +- ...> test-cohort-method-DiagnosticsSummary.R} | 32 ++--- ...Plot.R => test-cohort-method-ForestPlot.R} | 8 +- ...ier.R => test-cohort-method-KaplanMeier.R} | 10 +- ...cohort-method-PopulationCharacteristics.R} | 6 +- ...ion-Power.R => test-cohort-method-Power.R} | 6 +- ... test-cohort-method-PropensityScoreDist.R} | 8 +- ...le.R => test-cohort-method-ResultsTable.R} | 8 +- ...roups.R => test-cohort-method-Subgroups.R} | 6 +- ...ition.R => test-cohort-method-attrition.R} | 8 +- ...ation-main.R => test-cohort-method-main.R} | 10 +- ...R => test-cohort-method-propensityModel.R} | 6 +- ...R => test-cohort-method-systematicError.R} | 6 +- ...est-helpers-cohort-methodPlotsAndTables.R} | 60 ++++---- tests/testthat/test-prediction-settings.R | 3 +- 107 files changed, 883 insertions(+), 873 deletions(-) rename R/{description-aggregateFeatures.R => characterization-aggregateFeatures.R} (97%) rename R/{description-cohorts.R => characterization-cohorts.R} (97%) rename R/{description-dechallengeRechallenge.R => characterization-dechallengeRechallenge.R} (98%) rename R/{description-incidence.R => characterization-incidence.R} (98%) rename R/{description-main.R => characterization-main.R} (76%) rename R/{description-timeToEvent.R => characterization-timeToEvent.R} (97%) rename R/{estimation-attrition.R => cohort-method-attrition.R} (91%) rename R/{estimation-covariateBalance.R => cohort-method-covariateBalance.R} (93%) rename R/{estimation-diagnosticsSummary.R => cohort-method-diagnosticsSummary.R} (98%) rename R/{estimation-forestPlot.R => cohort-method-forestPlot.R} (93%) rename R/{estimation-kaplainMeier.R => cohort-method-kaplainMeier.R} (95%) rename R/{estimation-main.R => cohort-method-main.R} (81%) rename R/{estimation-populationCharacteristics.R => cohort-method-populationCharacteristics.R} (96%) rename R/{estimation-power.R => cohort-method-power.R} (91%) rename R/{estimation-propensityModel.R => cohort-method-propensityModel.R} (86%) rename R/{estimation-propensityScoreDistribution.R => cohort-method-propensityScoreDistribution.R} (91%) rename R/{estimation-resultsTable.R => cohort-method-resultsTable.R} (83%) rename R/{estimation-subgroups.R => cohort-method-subgroups.R} (91%) rename R/{estimation-systematicError.R => cohort-method-systematicError.R} (92%) rename R/{helpers-estimationDataPulls.R => helpers-cohort-methodDataPulls.R} (88%) rename R/{helpers-estimationPlotsAndTables.R => helpers-cohort-methodPlotsAndTables.R} (97%) rename R/{helpers-getEstimationUtility.R => helpers-getCohortMethodUtility.R} (75%) rename inst/{description-www/Description.html => characterization-www/characterization.html} (100%) rename inst/{description-www => characterization-www}/help-OutcomeStratified.html (100%) rename inst/{description-www => characterization-www}/help-dechallengeRechallenge.html (100%) rename inst/{description-www => characterization-www}/help-incidenceRate.html (100%) rename inst/{description-www => characterization-www}/help-targetViewer.html (100%) rename inst/{description-www => characterization-www}/help-timeToEvent.html (100%) rename inst/{estimation-ref => cohort-method-ref}/Table1Specs.csv (100%) rename inst/{estimation-www/estimation.html => cohort-method-www/cohort-method.html} (100%) rename man/{descriptionAggregateFeaturesServer.Rd => characterizationAggregateFeaturesServer.Rd} (81%) rename man/{descriptionAggregateFeaturesViewer.Rd => characterizationAggregateFeaturesViewer.Rd} (64%) rename man/{descriptionDechallengeRechallengeServer.Rd => characterizationDechallengeRechallengeServer.Rd} (80%) rename man/{descriptionDechallengeRechallengeViewer.Rd => characterizationDechallengeRechallengeViewer.Rd} (63%) create mode 100644 man/characterizationHelperFile.Rd rename man/{descriptionIncidenceServer.Rd => characterizationIncidenceServer.Rd} (81%) rename man/{descriptionIncidenceViewer.Rd => characterizationIncidenceViewer.Rd} (67%) create mode 100644 man/characterizationServer.Rd rename man/{descriptionTableServer.Rd => characterizationTableServer.Rd} (84%) rename man/{descriptionTableViewer.Rd => characterizationTableViewer.Rd} (69%) rename man/{descriptionTimeToEventServer.Rd => characterizationTimeToEventServer.Rd} (82%) rename man/{descriptionTimeToEventViewer.Rd => characterizationTimeToEventViewer.Rd} (55%) create mode 100644 man/characterizationViewer.Rd rename man/{estimationAttritionServer.Rd => cohortMethodAttritionServer.Rd} (82%) rename man/{estimationAttritionViewer.Rd => cohortMethodAttritionViewer.Rd} (55%) rename man/{estimationCovariateBalanceServer.Rd => cohortMethodCovariateBalanceServer.Rd} (80%) rename man/{estimationCovariateBalanceViewer.Rd => cohortMethodCovariateBalanceViewer.Rd} (52%) rename man/{estimationDiagnosticsSummaryServer.Rd => cohortMethodDiagnosticsSummaryServer.Rd} (76%) rename man/{estimationDiagnosticsSummaryViewer.Rd => cohortMethodDiagnosticsSummaryViewer.Rd} (51%) rename man/{estimationForestPlotServer.Rd => cohortMethodForestPlotServer.Rd} (82%) rename man/{estimationForestPlotViewer.Rd => cohortMethodForestPlotViewer.Rd} (55%) create mode 100644 man/cohortMethodHelperFile.Rd rename man/{estimationKaplanMeierServer.Rd => cohortMethodKaplanMeierServer.Rd} (83%) rename man/{estimationKaplanMeierViewer.Rd => cohortMethodKaplanMeierViewer.Rd} (64%) rename man/{estimationPopulationCharacteristicsServer.Rd => cohortMethodPopulationCharacteristicsServer.Rd} (75%) create mode 100644 man/cohortMethodPopulationCharacteristicsViewer.Rd rename man/{estimationPowerServer.Rd => cohortMethodPowerServer.Rd} (84%) rename man/{estimationPowerViewer.Rd => cohortMethodPowerViewer.Rd} (55%) rename man/{estimationPropensityModelServer.Rd => cohortMethodPropensityModelServer.Rd} (78%) rename man/{estimationPropensityModelViewer.Rd => cohortMethodPropensityModelViewer.Rd} (54%) rename man/{estimationPropensityScoreDistServer.Rd => cohortMethodPropensityScoreDistServer.Rd} (80%) rename man/{estimationPropensityScoreDistViewer.Rd => cohortMethodPropensityScoreDistViewer.Rd} (50%) rename man/{estimationResultsTableServer.Rd => cohortMethodResultsTableServer.Rd} (80%) rename man/{estimationResultsTableViewer.Rd => cohortMethodResultsTableViewer.Rd} (63%) rename man/{estimationServer.Rd => cohortMethodServer.Rd} (55%) rename man/{estimationSubgroupsServer.Rd => cohortMethodSubgroupsServer.Rd} (80%) rename man/{estimationSubgroupsViewer.Rd => cohortMethodSubgroupsViewer.Rd} (54%) rename man/{estimationSystematicErrorServer.Rd => cohortMethodSystematicErrorServer.Rd} (80%) rename man/{estimationSystematicErrorViewer.Rd => cohortMethodSystematicErrorViewer.Rd} (53%) create mode 100644 man/cohortMethodViewer.Rd delete mode 100644 man/descriptionHelperFile.Rd delete mode 100644 man/descriptionServer.Rd delete mode 100644 man/descriptionViewer.Rd delete mode 100644 man/estimationHelperFile.Rd delete mode 100644 man/estimationPopulationCharacteristicsViewer.Rd delete mode 100644 man/estimationViewer.Rd rename tests/resources/{descDatabase => characterizationDatabase}/databaseFile.sqlite (100%) rename tests/resources/{estDatabase => cmDatabase}/databaseFile.sqlite (100%) rename tests/testthat/{test-description-aggregate-features.R => test-characterization-aggregate-features.R} (67%) rename tests/testthat/{test-description-cohorts.R => test-characterization-cohorts.R} (64%) rename tests/testthat/{test-description-dechallengeRechallenge.R => test-characterization-dechallengeRechallenge.R} (65%) rename tests/testthat/{test-description-incidence.R => test-characterization-incidence.R} (57%) rename tests/testthat/{test-description-main.R => test-characterization-main.R} (52%) rename tests/testthat/{test-description-timeToEvent.R => test-characterization-timeToEvent.R} (55%) rename tests/testthat/{test-estimation-CovariateBalance.R => test-cohort-method-CovariateBalance.R} (88%) rename tests/testthat/{test-estimation-DiagnosticsSummary.R => test-cohort-method-DiagnosticsSummary.R} (70%) rename tests/testthat/{test-estimation-ForestPlot.R => test-cohort-method-ForestPlot.R} (68%) rename tests/testthat/{test-estimation-KaplanMeier.R => test-cohort-method-KaplanMeier.R} (71%) rename tests/testthat/{test-estimation-PopulationCharacteristics.R => test-cohort-method-PopulationCharacteristics.R} (80%) rename tests/testthat/{test-estimation-Power.R => test-cohort-method-Power.R} (90%) rename tests/testthat/{test-estimation-PropensityScoreDist.R => test-cohort-method-PropensityScoreDist.R} (83%) rename tests/testthat/{test-estimation-ResultsTable.R => test-cohort-method-ResultsTable.R} (86%) rename tests/testthat/{test-estimation-Subgroups.R => test-cohort-method-Subgroups.R} (93%) rename tests/testthat/{test-estimation-attrition.R => test-cohort-method-attrition.R} (74%) rename tests/testthat/{test-estimation-main.R => test-cohort-method-main.R} (77%) rename tests/testthat/{test-estimation-propensityModel.R => test-cohort-method-propensityModel.R} (87%) rename tests/testthat/{test-estimation-systematicError.R => test-cohort-method-systematicError.R} (91%) rename tests/testthat/{test-helpers-estimationPlotsAndTables.R => test-helpers-cohort-methodPlotsAndTables.R} (68%) diff --git a/NAMESPACE b/NAMESPACE index 88b2705f..bff010a8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,7 +3,20 @@ export(aboutHelperFile) export(aboutServer) export(aboutViewer) +export(characterizationAggregateFeaturesServer) +export(characterizationAggregateFeaturesViewer) +export(characterizationDechallengeRechallengeServer) +export(characterizationDechallengeRechallengeViewer) +export(characterizationHelperFile) +export(characterizationIncidenceServer) +export(characterizationIncidenceViewer) +export(characterizationServer) +export(characterizationTableServer) +export(characterizationTableViewer) +export(characterizationTimeToEventServer) +export(characterizationTimeToEventViewer) export(characterizationView) +export(characterizationViewer) export(cohortCountsView) export(cohortDefinitionsView) export(cohortDiagnosticsHelperFile) @@ -12,6 +25,33 @@ export(cohortDiagnosticsView) export(cohortGeneratorHelperFile) export(cohortGeneratorServer) export(cohortGeneratorViewer) +export(cohortMethodAttritionServer) +export(cohortMethodAttritionViewer) +export(cohortMethodCovariateBalanceServer) +export(cohortMethodCovariateBalanceViewer) +export(cohortMethodDiagnosticsSummaryServer) +export(cohortMethodDiagnosticsSummaryViewer) +export(cohortMethodForestPlotServer) +export(cohortMethodForestPlotViewer) +export(cohortMethodHelperFile) +export(cohortMethodKaplanMeierServer) +export(cohortMethodKaplanMeierViewer) +export(cohortMethodPopulationCharacteristicsServer) +export(cohortMethodPopulationCharacteristicsViewer) +export(cohortMethodPowerServer) +export(cohortMethodPowerViewer) +export(cohortMethodPropensityModelServer) +export(cohortMethodPropensityModelViewer) +export(cohortMethodPropensityScoreDistServer) +export(cohortMethodPropensityScoreDistViewer) +export(cohortMethodResultsTableServer) +export(cohortMethodResultsTableViewer) +export(cohortMethodServer) +export(cohortMethodSubgroupsServer) +export(cohortMethodSubgroupsViewer) +export(cohortMethodSystematicErrorServer) +export(cohortMethodSystematicErrorViewer) +export(cohortMethodViewer) export(cohortOverlapView) export(compareCohortCharacterizationView) export(conceptsInDataSourceView) @@ -24,46 +64,6 @@ export(dataDiagnosticSummaryServer) export(dataDiagnosticSummaryViewer) export(dataDiagnosticViewer) export(databaseInformationView) -export(descriptionAggregateFeaturesServer) -export(descriptionAggregateFeaturesViewer) -export(descriptionDechallengeRechallengeServer) -export(descriptionDechallengeRechallengeViewer) -export(descriptionHelperFile) -export(descriptionIncidenceServer) -export(descriptionIncidenceViewer) -export(descriptionServer) -export(descriptionTableServer) -export(descriptionTableViewer) -export(descriptionTimeToEventServer) -export(descriptionTimeToEventViewer) -export(descriptionViewer) -export(estimationAttritionServer) -export(estimationAttritionViewer) -export(estimationCovariateBalanceServer) -export(estimationCovariateBalanceViewer) -export(estimationDiagnosticsSummaryServer) -export(estimationDiagnosticsSummaryViewer) -export(estimationForestPlotServer) -export(estimationForestPlotViewer) -export(estimationHelperFile) -export(estimationKaplanMeierServer) -export(estimationKaplanMeierViewer) -export(estimationPopulationCharacteristicsServer) -export(estimationPopulationCharacteristicsViewer) -export(estimationPowerServer) -export(estimationPowerViewer) -export(estimationPropensityModelServer) -export(estimationPropensityModelViewer) -export(estimationPropensityScoreDistServer) -export(estimationPropensityScoreDistViewer) -export(estimationResultsTableServer) -export(estimationResultsTableViewer) -export(estimationServer) -export(estimationSubgroupsServer) -export(estimationSubgroupsViewer) -export(estimationSystematicErrorServer) -export(estimationSystematicErrorViewer) -export(estimationViewer) export(evidenceSynthesisHelperFile) export(evidenceSynthesisServer) export(evidenceSynthesisViewer) diff --git a/R/description-aggregateFeatures.R b/R/characterization-aggregateFeatures.R similarity index 97% rename from R/description-aggregateFeatures.R rename to R/characterization-aggregateFeatures.R index 9b321df0..bf7ac954 100644 --- a/R/description-aggregateFeatures.R +++ b/R/characterization-aggregateFeatures.R @@ -1,4 +1,4 @@ -# @file description-aggregateFeatures.R +# @file characterization-aggregateFeatures.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the description aggregate feature module #' #' @export -descriptionAggregateFeaturesViewer <- function(id) { +characterizationAggregateFeaturesViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -38,7 +38,7 @@ descriptionAggregateFeaturesViewer <- function(id) { collapsed = TRUE, title = "Outcome Stratified", width = "100%", - shiny::htmlTemplate(system.file("description-www", "help-OutcomeStratified.html", package = utils::packageName())) + shiny::htmlTemplate(system.file("characterization-www", "help-OutcomeStratified.html", package = utils::packageName())) ), # summary table @@ -120,7 +120,7 @@ descriptionAggregateFeaturesViewer <- function(id) { #' The server to the description aggregate features module #' #' @export -descriptionAggregateFeaturesServer <- function( +characterizationAggregateFeaturesServer <- function( id, connectionHandler, mainPanelTab, @@ -412,7 +412,7 @@ descriptionAggregateFeaturesServer <- function( ) ) - allData <- descriptiveGetAggregateData( + allData <- characterizationGetAggregateData( connectionHandler = connectionHandler, schema = schema, tablePrefix = tablePrefix, @@ -429,20 +429,20 @@ descriptionAggregateFeaturesServer <- function( ) output$binaryPlot <- plotly::renderPlotly( - descriptiveFeaturePlot( + characterizationFeaturePlot( data = allData$binary, valueColumn = 'averageValue' ) ) output$continuousPlot <- plotly::renderPlotly( - descriptiveFeaturePlot( + characterizationFeaturePlot( data = allData$continuous, valueColumn = 'averageValue' ) ) - binaryData(descriptiveFeatureTable(data = allData$binary)) - continuousData(descriptiveFeatureTable(data = allData$continuous)) + binaryData(characterizationFeatureTable(data = allData$binary)) + continuousData(characterizationFeatureTable(data = allData$continuous)) output$binaryTable <- reactable::renderReactable({ reactable::reactable( @@ -704,7 +704,7 @@ getAggregateFeatureDatabases <- function( } # pulls all data for a target and outcome -descriptiveGetAggregateData <- function( +characterizationGetAggregateData <- function( connectionHandler, schema, tablePrefix, @@ -855,7 +855,7 @@ descriptiveGetAggregateData <- function( )) } -descriptiveFeaturePlot <- function( +characterizationFeaturePlot <- function( data, valueColumn = 'averageValue' ){ @@ -939,7 +939,7 @@ descGetTime <- function(x){ } -descriptiveFeatureTable <- function( +characterizationFeatureTable <- function( data ){ diff --git a/R/description-cohorts.R b/R/characterization-cohorts.R similarity index 97% rename from R/description-cohorts.R rename to R/characterization-cohorts.R index 235d4411..cfe89021 100644 --- a/R/description-cohorts.R +++ b/R/characterization-cohorts.R @@ -1,4 +1,4 @@ -# @file description-timeToEvent.R +# @file characterization-timeToEvent.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the description cohorts features #' #' @export -descriptionTableViewer <- function(id) { +characterizationTableViewer <- function(id) { ns <- shiny::NS(id) shiny::div( shinydashboard::box( @@ -36,7 +36,7 @@ descriptionTableViewer <- function(id) { collapsed = TRUE, title = "Target Viewer", width = "100%", - shiny::htmlTemplate(system.file("description-www", "help-targetViewer.html", package = utils::packageName())) + shiny::htmlTemplate(system.file("characterization-www", "help-targetViewer.html", package = utils::packageName())) ), shinydashboard::box( @@ -76,7 +76,7 @@ descriptionTableViewer <- function(id) { #' The server to the cohorts features server #' #' @export -descriptionTableServer <- function( +characterizationTableServer <- function( id, connectionHandler, mainPanelTab, diff --git a/R/description-dechallengeRechallenge.R b/R/characterization-dechallengeRechallenge.R similarity index 98% rename from R/description-dechallengeRechallenge.R rename to R/characterization-dechallengeRechallenge.R index 4ebf9194..44e442fe 100644 --- a/R/description-dechallengeRechallenge.R +++ b/R/characterization-dechallengeRechallenge.R @@ -1,4 +1,4 @@ -# @file description-DechallengeRechallenge.R +# @file characterization-DechallengeRechallenge.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the description Dechallenge Rechallenge module #' #' @export -descriptionDechallengeRechallengeViewer <- function(id) { +characterizationDechallengeRechallengeViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -37,7 +37,7 @@ descriptionDechallengeRechallengeViewer <- function(id) { collapsed = TRUE, title = "Dechallenge Rechallenge", width = "100%", - shiny::htmlTemplate(system.file("description-www", "help-dechallengeRechallenge.html", package = utils::packageName())) + shiny::htmlTemplate(system.file("characterization-www", "help-dechallengeRechallenge.html", package = utils::packageName())) ), shinydashboard::box( @@ -91,7 +91,7 @@ descriptionDechallengeRechallengeViewer <- function(id) { #' The server to the Dechallenge Rechallenge module #' #' @export -descriptionDechallengeRechallengeServer <- function( +characterizationDechallengeRechallengeServer <- function( id, connectionHandler, mainPanelTab, diff --git a/R/description-incidence.R b/R/characterization-incidence.R similarity index 98% rename from R/description-incidence.R rename to R/characterization-incidence.R index 203050dd..ed3e855e 100644 --- a/R/description-incidence.R +++ b/R/characterization-incidence.R @@ -1,4 +1,4 @@ -# @file description-timeToEvent.R +# @file characterization-timeToEvent.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the description incidence module #' #' @export -descriptionIncidenceViewer <- function(id) { +characterizationIncidenceViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -37,7 +37,7 @@ descriptionIncidenceViewer <- function(id) { collapsed = TRUE, title = "Incidence Rates", width = "100%", - shiny::htmlTemplate(system.file("description-www", "help-incidenceRate.html", package = utils::packageName())) + shiny::htmlTemplate(system.file("characterization-www", "help-incidenceRate.html", package = utils::packageName())) ), shinydashboard::box( @@ -90,7 +90,7 @@ descriptionIncidenceViewer <- function(id) { #' The server to the prediction incidence module #' #' @export -descriptionIncidenceServer <- function( +characterizationIncidenceServer <- function( id, connectionHandler, mainPanelTab, diff --git a/R/description-main.R b/R/characterization-main.R similarity index 76% rename from R/description-main.R rename to R/characterization-main.R index 1721ead7..ef49b632 100644 --- a/R/description-main.R +++ b/R/characterization-main.R @@ -1,4 +1,4 @@ -# @file description-main.R +# @file characterization-main.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -17,21 +17,21 @@ # limitations under the License. -#' The location of the description module helper file +#' The location of the characterization module helper file #' #' @details -#' Returns the location of the description helper file +#' Returns the location of the characterization helper file #' #' @return -#' string location of the description helper file +#' string location of the characterization helper file #' #' @export -descriptionHelperFile <- function(){ - fileLoc <- system.file('description-www', "description.html", package = "OhdsiShinyModules") +characterizationHelperFile <- function(){ + fileLoc <- system.file('characterization-www', "characterization.html", package = "OhdsiShinyModules") return(fileLoc) } -#' The module viewer for exploring description studies +#' The module viewer for exploring characterization studies #' #' @details #' The user specifies the id for the module @@ -39,10 +39,10 @@ descriptionHelperFile <- function(){ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the description viewer module +#' The user interface to the characterization viewer module #' #' @export -descriptionViewer <- function(id=1) { +characterizationViewer <- function(id=1) { ns <- shiny::NS(id) shinydashboard::box( @@ -56,47 +56,47 @@ descriptionViewer <- function(id=1) { shiny::tabPanel( "Target Viewer", - descriptionTableViewer(ns('descriptiveTableTab')) + characterizationTableViewer(ns('descriptiveTableTab')) ), shiny::tabPanel( "Outcome Stratified", - descriptionAggregateFeaturesViewer(ns('aggregateFeaturesTab')) + characterizationAggregateFeaturesViewer(ns('aggregateFeaturesTab')) ), shiny::tabPanel( "Incidence Rate", - descriptionIncidenceViewer(ns('incidenceTab')) + characterizationIncidenceViewer(ns('incidenceTab')) ), shiny::tabPanel( "Time To Event", - descriptionTimeToEventViewer(ns('timeToEventTab')) + characterizationTimeToEventViewer(ns('timeToEventTab')) ), shiny::tabPanel( "Dechallenge Rechallenge", - descriptionDechallengeRechallengeViewer(ns('dechallengeRechallengeTab')) + characterizationDechallengeRechallengeViewer(ns('dechallengeRechallengeTab')) ) ) ) } -#' The module server for exploring description studies +#' The module server for exploring characterization studies #' #' @details #' The user specifies the id for the module #' #' @param id the unique reference id for the module #' @param connectionHandler a connection to the database with the results -#' @param resultDatabaseSettings a list containing the description result schema, dbms, tablePrefix, databaseTable and cohortTablePrefix +#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cohortTablePrefix #' #' @return -#' The server for the description module +#' The server for the characterization module #' #' @export -descriptionServer <- function( +characterizationServer <- function( id, connectionHandler, resultDatabaseSettings = list(port = 1) @@ -116,7 +116,7 @@ descriptionServer <- function( # ============================= # Table of cohorts # ============================= - descriptionTableServer( + characterizationTableServer( id = 'descriptiveTableTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, @@ -131,7 +131,7 @@ descriptionServer <- function( # Aggregrate Features # ============================= - descriptionAggregateFeaturesServer( + characterizationAggregateFeaturesServer( id = 'aggregateFeaturesTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, @@ -144,7 +144,7 @@ descriptionServer <- function( # ============================= # Incidence # ============================= - descriptionIncidenceServer( + characterizationIncidenceServer( id = 'incidenceTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, @@ -158,7 +158,7 @@ descriptionServer <- function( # Time To Event # ============================= - descriptionTimeToEventServer( + characterizationTimeToEventServer( id = 'timeToEventTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, @@ -173,7 +173,7 @@ descriptionServer <- function( # Dechallenge Rechallenge # ============================= - descriptionDechallengeRechallengeServer( + characterizationDechallengeRechallengeServer( id = 'dechallengeRechallengeTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, diff --git a/R/description-timeToEvent.R b/R/characterization-timeToEvent.R similarity index 97% rename from R/description-timeToEvent.R rename to R/characterization-timeToEvent.R index a6e80827..e979ce36 100644 --- a/R/description-timeToEvent.R +++ b/R/characterization-timeToEvent.R @@ -1,4 +1,4 @@ -# @file description-timeToEvent.R +# @file characterization-timeToEvent.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -25,10 +25,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the description time to event module +#' The user interface to the characterization time to event module #' #' @export -descriptionTimeToEventViewer <- function(id) { +characterizationTimeToEventViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -37,7 +37,7 @@ descriptionTimeToEventViewer <- function(id) { collapsed = TRUE, title = "Time-to-events", width = "100%", - shiny::htmlTemplate(system.file("description-www", "help-timeToEvent.html", package = utils::packageName())) + shiny::htmlTemplate(system.file("characterization-www", "help-timeToEvent.html", package = utils::packageName())) ), shinydashboard::box( @@ -96,7 +96,7 @@ descriptionTimeToEventViewer <- function(id) { #' The server to the prediction time to event module #' #' @export -descriptionTimeToEventServer <- function( +characterizationTimeToEventServer <- function( id, connectionHandler, mainPanelTab, diff --git a/R/estimation-attrition.R b/R/cohort-method-attrition.R similarity index 91% rename from R/estimation-attrition.R rename to R/cohort-method-attrition.R index 6f0051a5..f43e49c6 100644 --- a/R/estimation-attrition.R +++ b/R/cohort-method-attrition.R @@ -1,4 +1,4 @@ -# @file estimation-attrition +# @file cohort-method-attrition # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -21,10 +21,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation attrition +#' The user interface to the cohort method attrition #' #' @export -estimationAttritionViewer <- function(id) { +cohortMethodAttritionViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -52,7 +52,7 @@ estimationAttritionViewer <- function(id) { #' the PLE attrition results content server #' #' @export -estimationAttritionServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, databaseTable) { +cohortMethodAttritionServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, databaseTable) { shiny::moduleServer( id, @@ -64,7 +64,7 @@ estimationAttritionServer <- function(id, selectedRow, inputParams, connectionHa if (is.null(row)) { return(NULL) } else { - attrition <- getEstimationAttrition(connectionHandler = connectionHandler, + attrition <- getCohortMethodAttrition(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, databaseTable = databaseTable, @@ -73,7 +73,7 @@ estimationAttritionServer <- function(id, selectedRow, inputParams, connectionHa outcomeId = inputParams()$outcome, databaseId = row$databaseId, analysisId = row$analysisId) - plot <- drawEstimationAttritionDiagram(attrition) + plot <- drawCohortMethodAttritionDiagram(attrition) return(plot) } }) diff --git a/R/estimation-covariateBalance.R b/R/cohort-method-covariateBalance.R similarity index 93% rename from R/estimation-covariateBalance.R rename to R/cohort-method-covariateBalance.R index 026fd0f5..ee2a2e7c 100644 --- a/R/estimation-covariateBalance.R +++ b/R/cohort-method-covariateBalance.R @@ -1,4 +1,4 @@ -# @file estimation-covariateBalance +# @file cohort-method-covariateBalance # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -22,10 +22,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation covariate balance results +#' The user interface to the cohort method covariate balance results #' #' @export -estimationCovariateBalanceViewer <- function(id) { +cohortMethodCovariateBalanceViewer <- function(id) { ns <- shiny::NS(id) @@ -74,7 +74,7 @@ estimationCovariateBalanceViewer <- function(id) { #' the PLE covariate balance content server #' #' @export -estimationCovariateBalanceServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { +cohortMethodCovariateBalanceServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { shiny::moduleServer( id, @@ -84,7 +84,7 @@ estimationCovariateBalanceServer <- function(id, selectedRow, inputParams, conne balance <- shiny::reactive({ row <- selectedRow() balance <- tryCatch({ - getEstimationCovariateBalanceShared( + getCohortMethodCovariateBalanceShared( connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, @@ -107,12 +107,12 @@ estimationCovariateBalanceServer <- function(id, selectedRow, inputParams, conne shiny::outputOptions(output, "isMetaAnalysis", suspendWhenHidden = FALSE) - textSearchEstimation <- shiny::reactiveVal(NULL) + textSearchCohortMethod <- shiny::reactiveVal(NULL) shiny::observeEvent( input$covariateHighlightButton,{ - textSearchEstimation(input$covariateHighlight) + textSearchCohortMethod(input$covariateHighlight) } ) @@ -121,11 +121,11 @@ estimationCovariateBalanceServer <- function(id, selectedRow, inputParams, conne if (is.null(balance()) || nrow(balance()) == 0) { return(NULL) } else { - plot <- plotEstimationCovariateBalanceScatterPlotNew( + plot <- plotCohortMethodCovariateBalanceScatterPlotNew( balance = balance(), beforeLabel = "Before propensity score adjustment", afterLabel = "After propensity score adjustment", - textsearch = textSearchEstimation + textsearch = textSearchCohortMethod ) return(plot) } @@ -199,7 +199,7 @@ estimationCovariateBalanceServer <- function(id, selectedRow, inputParams, conne if (is.null(row) || !(row$databaseId %in% metaAnalysisDbIds)) { return(NULL) } else { - balanceSummary <- getEstimationCovariateBalanceSummary(connectionHandler = connectionHandler, + balanceSummary <- getCohortMethodCovariateBalanceSummary(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, targetId = inputParams()$target, @@ -208,7 +208,7 @@ estimationCovariateBalanceServer <- function(id, selectedRow, inputParams, conne databaseId = row$analysisId, beforeLabel = paste("Before", row$psStrategy), afterLabel = paste("After", row$psStrategy)) - plot <- plotEstimationCovariateBalanceSummary(balanceSummary, + plot <- plotCohortMethodCovariateBalanceSummary(balanceSummary, threshold = 0.1, beforeLabel = paste("Before", row$psStrategy), afterLabel = paste("After", row$psStrategy)) @@ -250,7 +250,7 @@ estimationCovariateBalanceServer <- function(id, selectedRow, inputParams, conne ) } -getEstimationCovariateBalanceShared <- function( +getCohortMethodCovariateBalanceShared <- function( connectionHandler, resultsSchema, tablePrefix, @@ -307,7 +307,7 @@ getEstimationCovariateBalanceShared <- function( } -getEstimationCovariateBalanceSummary <- function(connectionHandler, +getCohortMethodCovariateBalanceSummary <- function(connectionHandler, resultsSchema, tablePrefix, databaseId, @@ -316,7 +316,7 @@ getEstimationCovariateBalanceSummary <- function(connectionHandler, beforeLabel = "Before matching", afterLabel = "After matching") { - balance <- getEstimationCovariateBalanceShared(connectionHandler = connectionHandler, + balance <- getCohortMethodCovariateBalanceShared(connectionHandler = connectionHandler, targetId = targetId, comparatorId = comparatorId, analysisId = analysisId, @@ -345,7 +345,7 @@ getEstimationCovariateBalanceSummary <- function(connectionHandler, -plotEstimationCovariateBalanceScatterPlotNew <- function( +plotCohortMethodCovariateBalanceScatterPlotNew <- function( balance, beforeLabel = "Before propensity score adjustment", afterLabel = "After propensity score adjustment", diff --git a/R/estimation-diagnosticsSummary.R b/R/cohort-method-diagnosticsSummary.R similarity index 98% rename from R/estimation-diagnosticsSummary.R rename to R/cohort-method-diagnosticsSummary.R index 4d57ddfb..20a58e4c 100644 --- a/R/estimation-diagnosticsSummary.R +++ b/R/cohort-method-diagnosticsSummary.R @@ -1,4 +1,4 @@ -# @file estimation-diagnosticsSummary +# @file cohort-method-diagnosticsSummary # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -22,10 +22,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation diagnostics viewer +#' The user interface to the cohort method diagnostics viewer #' #' @export -estimationDiagnosticsSummaryViewer <- function(id) { +cohortMethodDiagnosticsSummaryViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -66,7 +66,7 @@ estimationDiagnosticsSummaryViewer <- function(id) { #' the PLE diagnostics summary results #' #' @export -estimationDiagnosticsSummaryServer <- function( +cohortMethodDiagnosticsSummaryServer <- function( id, connectionHandler, resultsSchema, diff --git a/R/estimation-forestPlot.R b/R/cohort-method-forestPlot.R similarity index 93% rename from R/estimation-forestPlot.R rename to R/cohort-method-forestPlot.R index 5e19a360..edaa304e 100644 --- a/R/estimation-forestPlot.R +++ b/R/cohort-method-forestPlot.R @@ -1,4 +1,4 @@ -# @file estimation-forestPlot +# @file cohort-method-forestPlot # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -21,10 +21,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation forest plot +#' The user interface to the cohort method forest plot #' #' @export -estimationForestPlotViewer <- function(id) { +cohortMethodForestPlotViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -57,7 +57,7 @@ estimationForestPlotViewer <- function(id) { #' the PLE forest plot content server #' #' @export -estimationForestPlotServer <- function( +cohortMethodForestPlotServer <- function( id, connectionHandler, selectedRow, inputParams, metaAnalysisDbIds = NULL, resultsSchema, tablePrefix, @@ -72,7 +72,7 @@ estimationForestPlotServer <- function( if (is.null(row) || !(row$databaseId %in% metaAnalysisDbIds)) { return(NULL) } else { - results <- getEstimationMainResults(connectionHandler = connectionHandler, + results <- getCohortMethodMainResults(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, databaseTable = databaseTable, @@ -80,7 +80,7 @@ estimationForestPlotServer <- function( comparatorIds = row$comparatorId, outcomeIds = row$outcomeId, analysisIds = row$analysisId) - plot <- plotEstimationForest(results) + plot <- plotCohortMethodForest(results) return(plot) } }) diff --git a/R/estimation-kaplainMeier.R b/R/cohort-method-kaplainMeier.R similarity index 95% rename from R/estimation-kaplainMeier.R rename to R/cohort-method-kaplainMeier.R index 703a7644..78535a93 100644 --- a/R/estimation-kaplainMeier.R +++ b/R/cohort-method-kaplainMeier.R @@ -1,4 +1,4 @@ -# @file estimation-kaplainMeier +# @file cohort-method-kaplainMeier # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -25,7 +25,7 @@ #' The module viewer for Kaplan Meier objects #' #' @export -estimationKaplanMeierViewer <- function(id) { +cohortMethodKaplanMeierViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -55,7 +55,7 @@ estimationKaplanMeierViewer <- function(id) { #' the PLE Kaplain Meier content server #' #' @export -estimationKaplanMeierServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, databaseTable, metaAnalysisDbIds = NULL) { +cohortMethodKaplanMeierServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, databaseTable, metaAnalysisDbIds = NULL) { shiny::moduleServer( id, @@ -76,7 +76,7 @@ estimationKaplanMeierServer <- function(id, selectedRow, inputParams, connection if (is.null(row)) { return(NULL) } else { - km <- getEstimationKaplanMeier(connectionHandler = connectionHandler, + km <- getCohortMethodKaplanMeier(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, databaseTable = databaseTable, @@ -100,7 +100,7 @@ estimationKaplanMeierServer <- function(id, selectedRow, inputParams, connection cohortTablePrefix = cohortTablePrefix, cohortId = inputParams()$comparator) - plot <- plotEstimationKaplanMeier(kaplanMeier = km, + plot <- plotCohortMethodKaplanMeier(kaplanMeier = km, targetName = targetName$cohortName, comparatorName = comparatorName$cohortName) return(plot) @@ -146,8 +146,8 @@ estimationKaplanMeierServer <- function(id, selectedRow, inputParams, connection -# estimation-kaplainMeier -plotEstimationKaplanMeier <- function( +# CohortMethod-kaplainMeier +plotCohortMethodKaplanMeier <- function( kaplanMeier, targetName, comparatorName diff --git a/R/estimation-main.R b/R/cohort-method-main.R similarity index 81% rename from R/estimation-main.R rename to R/cohort-method-main.R index 0a324ad4..baefa1eb 100644 --- a/R/estimation-main.R +++ b/R/cohort-method-main.R @@ -1,4 +1,4 @@ -# @file estimation-main.R +# @file cohort-method-main.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -17,29 +17,29 @@ # limitations under the License. -#' The location of the estimation module helper file +#' The location of the cohort method module helper file #' #' @details -#' Returns the location of the estimation helper file +#' Returns the location of the cohort method helper file #' #' @return -#' string location of the estimation helper file +#' string location of the cohort method helper file #' #' @export -estimationHelperFile <- function(){ - fileLoc <- system.file('estimation-www', "estimation.html", package = "OhdsiShinyModules") +cohortMethodHelperFile <- function(){ + fileLoc <- system.file('cohort-method-www', "cohort-method.html", package = "OhdsiShinyModules") return(fileLoc) } -#' The viewer of the main estimation module +#' The viewer of the main cohort method module #' #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation results viewer +#' The user interface to the cohort method results viewer #' #' @export -estimationViewer <- function(id) { +cohortMethodViewer <- function(id) { ns <- shiny::NS(id) shinydashboard::box( @@ -80,7 +80,7 @@ estimationViewer <- function(id) { id = ns("mainTabsetPanel"), shiny::tabPanel( title = "Diagnostics", - estimationDiagnosticsSummaryViewer(ns("estimationDiganostics")) + cohortMethodDiagnosticsSummaryViewer(ns("estimationDiganostics")) ), shiny::tabPanel( title = "Results", @@ -93,38 +93,38 @@ estimationViewer <- function(id) { shiny::uiOutput(outputId = ns("analysisWidget")) ), shiny::column(width = 9, - estimationResultsTableViewer(ns("resultsTable")), + cohortMethodResultsTableViewer(ns("resultsTable")), shiny::conditionalPanel("output.rowIsSelected == true", ns = ns, shiny::tabsetPanel(id = ns("detailsTabsetPanel"), shiny::tabPanel(title = "Power", - estimationPowerViewer(ns("power")) + cohortMethodPowerViewer(ns("power")) ), shiny::tabPanel(title = "Attrition", - estimationAttritionViewer(ns("attrition")) + cohortMethodAttritionViewer(ns("attrition")) ), shiny::tabPanel(title = "Population characteristics", - estimationPopulationCharacteristicsViewer(ns("popCharacteristics")) + cohortMethodPopulationCharacteristicsViewer(ns("popCharacteristics")) ), shiny::tabPanel(title = "Propensity model", - estimationPropensityModelViewer(ns("propensityModel")) + cohortMethodPropensityModelViewer(ns("propensityModel")) ), shiny::tabPanel(title = "Propensity scores", - estimationPropensityScoreDistViewer(ns("propensityScoreDist")) + cohortMethodPropensityScoreDistViewer(ns("propensityScoreDist")) ), shiny::tabPanel(title = "Covariate balance", - estimationCovariateBalanceViewer(ns("covariateBalance")) + cohortMethodCovariateBalanceViewer(ns("covariateBalance")) ), shiny::tabPanel(title = "Systematic error", - estimationSystematicErrorViewer(ns("systematicError")) + cohortMethodSystematicErrorViewer(ns("systematicError")) ), shiny::tabPanel(title = "Forest plot", - estimationForestPlotViewer(ns("forestPlot")) + cohortMethodForestPlotViewer(ns("forestPlot")) ), shiny::tabPanel(title = "Kaplan-Meier", - estimationKaplanMeierViewer(ns("kaplanMeier")) + cohortMethodKaplanMeierViewer(ns("kaplanMeier")) ), shiny::tabPanel(title = "Subgroups", - estimationSubgroupsViewer(ns("subgroups")) + cohortMethodSubgroupsViewer(ns("subgroups")) ) ) # end tabsetPanel @@ -139,7 +139,7 @@ estimationViewer <- function(id) { } -#' The module server for the main estimation module +#' The module server for the main cohort method module #' #' @param id the unique reference id for the module #' @param connectionHandler a connection to the database with the results @@ -149,7 +149,7 @@ estimationViewer <- function(id) { #' the PLE results viewer main module server #' #' @export -estimationServer <- function( +cohortMethodServer <- function( id, connectionHandler, resultDatabaseSettings @@ -162,45 +162,45 @@ estimationServer <- function( dataFolder <- NULL output$targetWidget <- shiny::renderUI({ - targets <- getEstimationTargetChoices(connectionHandler, + targets <- getCohortMethodTargetChoices(connectionHandler, resultDatabaseSettings$schema, resultDatabaseSettings$tablePrefix, resultDatabaseSettings$cohortTablePrefix) shiny::selectInput(inputId = session$ns("target"), label = "Target", - choices = getEstimationSelectNamedChoices(targets$targetId, + choices = getCohortMethodSelectNamedChoices(targets$targetId, targets$cohortName)) }) output$comparatorWidget <- shiny::renderUI({ - comparators <- getEstimationComparatorChoices(connectionHandler, + comparators <- getCohortMethodComparatorChoices(connectionHandler, resultDatabaseSettings$schema, resultDatabaseSettings$tablePrefix, resultDatabaseSettings$cohortTablePrefix) shiny::selectInput(inputId = session$ns("comparator"), label = "Comparator", - choices = getEstimationSelectNamedChoices(comparators$comparatorId, + choices = getCohortMethodSelectNamedChoices(comparators$comparatorId, comparators$cohortName)) }) output$outcomeWidget <- shiny::renderUI({ - outcomes <- getEstimationOutcomeChoices(connectionHandler, + outcomes <- getCohortMethodOutcomeChoices(connectionHandler, resultDatabaseSettings$schema, resultDatabaseSettings$tablePrefix, resultDatabaseSettings$cohortTablePrefix) shiny::selectInput(inputId = session$ns("outcome"), label = "Outcome", - choices = getEstimationSelectNamedChoices(outcomes$outcomeId, + choices = getCohortMethodSelectNamedChoices(outcomes$outcomeId, outcomes$cohortName)) }) output$databaseWidget<- shiny::renderUI({ - databases <- getEstimationDatabaseChoices(connectionHandler, + databases <- getCohortMethodDatabaseChoices(connectionHandler, resultDatabaseSettings$schema, resultDatabaseSettings$tablePrefix, resultDatabaseSettings$databaseTable) shiny::checkboxGroupInput(inputId = session$ns("database"), label = "Data source", - choices = getEstimationSelectNamedChoices(databases$databaseId, + choices = getCohortMethodSelectNamedChoices(databases$databaseId, databases$cdmSourceAbbreviation), selected = unique(databases$databaseId)) }) @@ -210,7 +210,7 @@ estimationServer <- function( resultDatabaseSettings$tablePrefix) shiny::checkboxGroupInput(inputId = session$ns("analysis"), label = "Analysis", - choices = getEstimationSelectNamedChoices(analyses$analysisId, + choices = getCohortMethodSelectNamedChoices(analyses$analysisId, analyses$description), selected = unique(analyses$analysisId)) }) @@ -227,7 +227,7 @@ estimationServer <- function( }) - estimationDiagnosticsSummaryServer(id = "estimationDiganostics", + cohortMethodDiagnosticsSummaryServer(id = "estimationDiganostics", connectionHandler = connectionHandler, resultsSchema = resultDatabaseSettings$schema, tablePrefix = resultDatabaseSettings$tablePrefix, @@ -235,7 +235,7 @@ estimationServer <- function( databaseTable = resultDatabaseSettings$databaseTable) - selectedRow <- estimationResultsTableServer(id = "resultsTable", + selectedRow <- cohortMethodResultsTableServer(id = "resultsTable", connectionHandler = connectionHandler, inputParams = inputParams, resultsSchema = resultDatabaseSettings$schema, @@ -283,14 +283,14 @@ estimationServer <- function( shiny::outputOptions(output, "isMetaAnalysis", suspendWhenHidden = FALSE) - estimationPowerServer(id = "power", + cohortMethodPowerServer(id = "power", selectedRow = selectedRow, inputParams = inputParams, connectionHandler = connectionHandler, resultsSchema = resultDatabaseSettings$schema, resultDatabaseSettings$tablePrefix) - estimationAttritionServer(id = "attrition", + cohortMethodAttritionServer(id = "attrition", selectedRow = selectedRow, inputParams = inputParams, connectionHandler = connectionHandler, @@ -298,21 +298,21 @@ estimationServer <- function( tablePrefix = resultDatabaseSettings$tablePrefix, databaseTable = resultDatabaseSettings$cohortTablePrefix) - estimationPopulationCharacteristicsServer(id = "popCharacteristics", + cohortMethodPopulationCharacteristicsServer(id = "popCharacteristics", selectedRow = selectedRow, inputParams = inputParams, connectionHandler = connectionHandler, resultsSchema = resultDatabaseSettings$schema, tablePrefix = resultDatabaseSettings$tablePrefix) - estimationPropensityModelServer(id = "propensityModel", + cohortMethodPropensityModelServer(id = "propensityModel", selectedRow = selectedRow, inputParams = inputParams, connectionHandler = connectionHandler, resultsSchema = resultDatabaseSettings$schema, tablePrefix = resultDatabaseSettings$tablePrefix) - estimationPropensityScoreDistServer(id = "propensityScoreDist", + cohortMethodPropensityScoreDistServer(id = "propensityScoreDist", selectedRow = selectedRow, inputParams = inputParams, connectionHandler = connectionHandler, @@ -320,21 +320,21 @@ estimationServer <- function( tablePrefix = resultDatabaseSettings$tablePrefix, cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix) - estimationCovariateBalanceServer(id = "covariateBalance", + cohortMethodCovariateBalanceServer(id = "covariateBalance", selectedRow = selectedRow, inputParams = inputParams, connectionHandler = connectionHandler, resultsSchema = resultDatabaseSettings$schema, tablePrefix = resultDatabaseSettings$tablePrefix) - estimationSystematicErrorServer(id = "systematicError", + cohortMethodSystematicErrorServer(id = "systematicError", selectedRow = selectedRow, inputParams = inputParams, connectionHandler = connectionHandler, resultsSchema = resultDatabaseSettings$schema, tablePrefix = resultDatabaseSettings$tablePrefix) - estimationKaplanMeierServer(id = "kaplanMeier", + cohortMethodKaplanMeierServer(id = "kaplanMeier", selectedRow = selectedRow, inputParams = inputParams, connectionHandler = connectionHandler, @@ -347,7 +347,7 @@ estimationServer <- function( # estimationForestPlotServer("forestPlot", connection, selectedRow, inputParams) #TODO: revisit once subgroup example conducted - estimationSubgroupsServer(id = "subgroups", + cohortMethodSubgroupsServer(id = "subgroups", selectedRow = selectedRow, inputParams = inputParams) diff --git a/R/estimation-populationCharacteristics.R b/R/cohort-method-populationCharacteristics.R similarity index 96% rename from R/estimation-populationCharacteristics.R rename to R/cohort-method-populationCharacteristics.R index b7fd83b9..b22f980a 100644 --- a/R/estimation-populationCharacteristics.R +++ b/R/cohort-method-populationCharacteristics.R @@ -1,4 +1,4 @@ -# @file estimation-populationCharacteristics +# @file cohort-method-populationCharacteristics # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -22,10 +22,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation population characteristics objects +#' The user interface to the cohort method population characteristics objects #' #' @export -estimationPopulationCharacteristicsViewer <- function(id) { +cohortMethodPopulationCharacteristicsViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -48,7 +48,7 @@ estimationPopulationCharacteristicsViewer <- function(id) { #' the PLE population characteristics content server #' #' @export -estimationPopulationCharacteristicsServer <- function( +cohortMethodPopulationCharacteristicsServer <- function( id, selectedRow, inputParams, @@ -79,7 +79,7 @@ estimationPopulationCharacteristicsServer <- function( if (is.null(row)) { return(NULL) } else { - balance <- getEstimationPopChar( + balance <- getCohortMethodPopChar( connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, @@ -92,7 +92,7 @@ estimationPopulationCharacteristicsServer <- function( if (nrow(balance) == 0) { return(NULL) } - table1 <- prepareEstimationTable1( + table1 <- prepareCohortMethodTable1( balance = balance, beforeLabel = paste("Before PS adjustment"), afterLabel = paste("After PS adjustment") @@ -133,7 +133,7 @@ estimationPopulationCharacteristicsServer <- function( } -getEstimationPopChar <- function( +getCohortMethodPopChar <- function( connectionHandler, resultsSchema, tablePrefix, @@ -197,8 +197,8 @@ getEstimationPopChar <- function( } -# estimation-populationChar -prepareEstimationTable1 <- function( +# CohortMethod-populationChar +prepareCohortMethodTable1 <- function( balance, beforeLabel = "Before stratification", afterLabel = "After stratification", @@ -212,7 +212,7 @@ prepareEstimationTable1 <- function( if(is.null(pathToCsv)) { - pathToCsv <- system.file("estimation-ref", "Table1Specs.csv", package = "OhdsiShinyModules") + pathToCsv <- system.file("cohort-method-ref", "Table1Specs.csv", package = "OhdsiShinyModules") } if (output == "latex") { space <- " " diff --git a/R/estimation-power.R b/R/cohort-method-power.R similarity index 91% rename from R/estimation-power.R rename to R/cohort-method-power.R index f5152301..84e65b1d 100644 --- a/R/estimation-power.R +++ b/R/cohort-method-power.R @@ -1,4 +1,4 @@ -# @file estimation-power +# @file cohort-method-power # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -22,10 +22,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation power calculation results +#' The user interface to the cohort method power calculation results #' #' @export -estimationPowerViewer <- function(id) { +cohortMethodPowerViewer <- function(id) { ns <- shiny::NS(id) @@ -52,7 +52,7 @@ estimationPowerViewer <- function(id) { #' the PLE systematic error power server #' #' @export -estimationPowerServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { +cohortMethodPowerServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { shiny::moduleServer( id, @@ -79,12 +79,12 @@ estimationPowerServer <- function(id, selectedRow, inputParams, connectionHandle } else { #TODO: update once MA implemented if (FALSE && row$databaseId %in% metaAnalysisDbIds) { - results <- getEstimationMainResults(connectionHandler = connectionHandler, + results <- getCohortMethodMainResults(connectionHandler = connectionHandler, targetIds = row$targetId, comparatorIds = row$comparatorId, outcomeIds = row$outcomeId, analysisIds = row$analysisId) - table <- prepareEstimationPowerTable(results, connectionHandler, resultsSchema) + table <- prepareCohortMethodPowerTable(results, connectionHandler, resultsSchema) table$description <- NULL if (!row$unblind) { table$targetOutcomes <- NA @@ -104,7 +104,7 @@ estimationPowerServer <- function(id, selectedRow, inputParams, connectionHandle "Comparator IR (per 1,000 PY)", "MDRR") } else { - table <- prepareEstimationPowerTable(row, connectionHandler, resultsSchema, tablePrefix) + table <- prepareCohortMethodPowerTable(row, connectionHandler, resultsSchema, tablePrefix) table$description <- NULL table$databaseId <- NULL if (!row$unblind) { @@ -162,7 +162,7 @@ estimationPowerServer <- function(id, selectedRow, inputParams, connectionHandle databaseId = row$databaseId, analysisId = row$analysisId) } - table <- prepareEstimationFollowUpDistTable(followUpDist) + table <- prepareCohortMethodFollowUpDistTable(followUpDist) return(table) } }) diff --git a/R/estimation-propensityModel.R b/R/cohort-method-propensityModel.R similarity index 86% rename from R/estimation-propensityModel.R rename to R/cohort-method-propensityModel.R index f4a5a11c..c5752a24 100644 --- a/R/estimation-propensityModel.R +++ b/R/cohort-method-propensityModel.R @@ -1,4 +1,4 @@ -# @file estimation-propensityModel +# @file cohort-method-propensityModel # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -22,10 +22,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation propensity score model covariates/coefficients +#' The user interface to the cohort method propensity score model covariates/coefficients #' #' @export -estimationPropensityModelViewer <- function(id) { +cohortMethodPropensityModelViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -48,7 +48,7 @@ estimationPropensityModelViewer <- function(id) { #' the PLE propensity score model #' #' @export -estimationPropensityModelServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix) { +cohortMethodPropensityModelServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix) { shiny::moduleServer( id, @@ -59,7 +59,7 @@ estimationPropensityModelServer <- function(id, selectedRow, inputParams, connec if (is.null(row)) { return(NULL) } else { - model <- getEstimationPropensityModel(connectionHandler = connectionHandler, + model <- getCohortMethodPropensityModel(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, targetId = inputParams()$target, @@ -67,7 +67,7 @@ estimationPropensityModelServer <- function(id, selectedRow, inputParams, connec databaseId = row$databaseId, analysisId = row$analysisId) - table <- prepareEstimationPropensityModelTable(model) + table <- prepareCohortMethodPropensityModelTable(model) options = list(columnDefs = list(list(className = 'dt-right', targets = 0)), pageLength = 15, searching = FALSE, diff --git a/R/estimation-propensityScoreDistribution.R b/R/cohort-method-propensityScoreDistribution.R similarity index 91% rename from R/estimation-propensityScoreDistribution.R rename to R/cohort-method-propensityScoreDistribution.R index 1022c16a..b8d6b6dc 100644 --- a/R/estimation-propensityScoreDistribution.R +++ b/R/cohort-method-propensityScoreDistribution.R @@ -1,4 +1,4 @@ -# @file estimation-propensityScoreDistribution +# @file cohort-method-propensityScoreDistribution # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -22,10 +22,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation propensity score distribution +#' The user interface to the cohort method propensity score distribution #' #' @export -estimationPropensityScoreDistViewer <- function(id) { +cohortMethodPropensityScoreDistViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -57,7 +57,7 @@ estimationPropensityScoreDistViewer <- function(id) { #' the PLE propensity score distribution content server #' #' @export -estimationPropensityScoreDistServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, metaAnalysisDbIds = F) { +cohortMethodPropensityScoreDistServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, metaAnalysisDbIds = F) { shiny::moduleServer( id, @@ -70,7 +70,7 @@ estimationPropensityScoreDistServer <- function(id, selectedRow, inputParams, co } else { if (FALSE && row$databaseId %in% metaAnalysisDbIds) { #TODO: update once MA implemented - ps <- getEstimationPs(connectionHandler = connectionHandler, + ps <- getCohortMethodPs(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, #targetIds = row$targetId, @@ -79,7 +79,7 @@ estimationPropensityScoreDistServer <- function(id, selectedRow, inputParams, co comparatorId = inputParams()$comparator, analysisId = row$analysisId) } else { - ps <- getEstimationPs(connectionHandler = connectionHandler, + ps <- getCohortMethodPs(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, targetId = inputParams()$target, @@ -99,7 +99,7 @@ estimationPropensityScoreDistServer <- function(id, selectedRow, inputParams, co resultsSchema = resultsSchema, cohortTablePrefix = cohortTablePrefix, cohortId = inputParams()$comparator) - plot <- plotEstimationPs(ps, targetName$cohortName, comparatorName$cohortName) + plot <- plotCohortMethodPs(ps, targetName$cohortName, comparatorName$cohortName) return(plot) } }) @@ -126,8 +126,8 @@ estimationPropensityScoreDistServer <- function(id, selectedRow, inputParams, co -# estimation-propensityScoreDist -plotEstimationPs <- function(ps, targetName, comparatorName) { +# CohortMethod-propensityScoreDist +plotCohortMethodPs <- function(ps, targetName, comparatorName) { if (is.null(ps$databaseId)) { ps <- rbind(data.frame(x = ps$preferenceScore, y = ps$targetDensity, group = targetName), data.frame(x = ps$preferenceScore, y = ps$comparatorDensity, group = comparatorName)) diff --git a/R/estimation-resultsTable.R b/R/cohort-method-resultsTable.R similarity index 83% rename from R/estimation-resultsTable.R rename to R/cohort-method-resultsTable.R index 5ab5730b..cc5a9218 100644 --- a/R/estimation-resultsTable.R +++ b/R/cohort-method-resultsTable.R @@ -1,4 +1,4 @@ -# @file estimation-resultsTable +# @file cohort-method-resultsTable # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the PLE main results #' #' @export -estimationResultsTableViewer <- function(id) { +cohortMethodResultsTableViewer <- function(id) { ns <- shiny::NS(id) reactable::reactableOutput(outputId = ns("mainTable")) @@ -50,7 +50,7 @@ estimationResultsTableViewer <- function(id) { #' the PLE main results table server server #' #' @export -estimationResultsTableServer <- function( +cohortMethodResultsTableServer <- function( id, connectionHandler, inputParams, @@ -82,19 +82,19 @@ estimationResultsTableServer <- function( resultSubset <- shiny::reactive({ - results <- getEstimationMainResults(connectionHandler = connectionHandler, + results <- getCohortMethodMainResults(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, databaseTable = databaseTable, - targetIds = filterEstimationEmptyNullValues(inputParams()$target), - comparatorIds = filterEstimationEmptyNullValues(inputParams()$comparator), - outcomeIds = filterEstimationEmptyNullValues(inputParams()$outcome), - databaseIds = filterEstimationEmptyNullValues(inputParams()$database), - analysisIds = filterEstimationEmptyNullValues(inputParams()$analysis)) + targetIds = filterCohortMethodEmptyNullValues(inputParams()$target), + comparatorIds = filterCohortMethodEmptyNullValues(inputParams()$comparator), + outcomeIds = filterCohortMethodEmptyNullValues(inputParams()$outcome), + databaseIds = filterCohortMethodEmptyNullValues(inputParams()$database), + analysisIds = filterCohortMethodEmptyNullValues(inputParams()$analysis)) results <- results[order(results$analysisId), ] - results[which(results$unblind == 0), getEstimationColumnsToBlind(results)] <- NA + results[which(results$unblind == 0), getCohortMethodColumnsToBlind(results)] <- NA return(results) }) @@ -124,14 +124,14 @@ estimationResultsTableServer <- function( return(NULL) } table <- table[, mainColumns] - table$rr <- prettyEstimationHr(table$rr) - table$ci95Lb <- prettyEstimationHr(table$ci95Lb) - table$ci95Ub <- prettyEstimationHr(table$ci95Ub) - table$p <- prettyEstimationHr(table$p) - table$calibratedRr <- prettyEstimationHr(table$calibratedRr) - table$calibratedCi95Lb <- prettyEstimationHr(table$calibratedCi95Lb) - table$calibratedCi95Ub <- prettyEstimationHr(table$calibratedCi95Ub) - table$calibratedP <- prettyEstimationHr(table$calibratedP) + table$rr <- prettyCohortMethodHr(table$rr) + table$ci95Lb <- prettyCohortMethodHr(table$ci95Lb) + table$ci95Ub <- prettyCohortMethodHr(table$ci95Ub) + table$p <- prettyCohortMethodHr(table$p) + table$calibratedRr <- prettyCohortMethodHr(table$calibratedRr) + table$calibratedCi95Lb <- prettyCohortMethodHr(table$calibratedCi95Lb) + table$calibratedCi95Ub <- prettyCohortMethodHr(table$calibratedCi95Ub) + table$calibratedP <- prettyCohortMethodHr(table$calibratedP) #colnames(table) <- mainColumnNames reactable::reactable( # add extras diff --git a/R/estimation-subgroups.R b/R/cohort-method-subgroups.R similarity index 91% rename from R/estimation-subgroups.R rename to R/cohort-method-subgroups.R index 2ad76716..fbf04446 100644 --- a/R/estimation-subgroups.R +++ b/R/cohort-method-subgroups.R @@ -1,4 +1,4 @@ -# @file estimation-subgroups +# @file cohort-method-subgroups # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -22,10 +22,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation subgroup results module +#' The user interface to the cohort method subgroup results module #' #' @export -estimationSubgroupsViewer <- function(id) { +cohortMethodSubgroupsViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -48,7 +48,7 @@ estimationSubgroupsViewer <- function(id) { #' the PLE subgroup results server #' #' @export -estimationSubgroupsServer <- function(id, selectedRow, inputParams, exposureOfInterest, outcomeOfInterest, connectionHandler) { +cohortMethodSubgroupsServer <- function(id, selectedRow, inputParams, exposureOfInterest, outcomeOfInterest, connectionHandler) { shiny::moduleServer( id, @@ -62,7 +62,7 @@ estimationSubgroupsServer <- function(id, selectedRow, inputParams, exposureOfIn targetId <- exposureOfInterest$exposureId[exposureOfInterest$exposureName == inputParams()$target] comparatorId <- exposureOfInterest$exposureId[exposureOfInterest$exposureName == inputParams()$comparator] outcomeId <- outcomeOfInterest$outcomeId[outcomeOfInterest$outcomeName == inputParams()$outcome] - subgroupResults <- getEstimationSubgroupResults(connectionHandler = connectionHandler, + subgroupResults <- getCohortMethodSubgroupResults(connectionHandler = connectionHandler, targetIds = targetId, comparatorIds = comparatorId, outcomeIds = outcomeId, @@ -107,7 +107,7 @@ estimationSubgroupsServer <- function(id, selectedRow, inputParams, exposureOfIn if (is.null(subgroupResults)) { return(NULL) } - subgroupTable <- prepareEstimationSubgroupTable(subgroupResults, output = "html") + subgroupTable <- prepareCohortMethodSubgroupTable(subgroupResults, output = "html") colnames(subgroupTable) <- c("Subgroup", "Target subjects", "Comparator subjects", diff --git a/R/estimation-systematicError.R b/R/cohort-method-systematicError.R similarity index 92% rename from R/estimation-systematicError.R rename to R/cohort-method-systematicError.R index e869d2bc..9a199e09 100644 --- a/R/estimation-systematicError.R +++ b/R/cohort-method-systematicError.R @@ -1,4 +1,4 @@ -# @file estimation-systematicError +# @file cohort-method-systematicError # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -22,10 +22,10 @@ #' @param id the unique reference id for the module #' #' @return -#' The user interface to the estimation systematic error module +#' The user interface to the cohort method systematic error module #' #' @export -estimationSystematicErrorViewer <- function(id) { +cohortMethodSystematicErrorViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -68,7 +68,7 @@ estimationSystematicErrorViewer <- function(id) { #' the PLE systematic error content server #' #' @export -estimationSystematicErrorServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { +cohortMethodSystematicErrorServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { shiny::moduleServer( id, @@ -91,7 +91,7 @@ estimationSystematicErrorServer <- function(id, selectedRow, inputParams, connec if (is.null(row)) { return(NULL) } else { - controlResults <- getEstimationControlResults(connectionHandler = connectionHandler, + controlResults <- getCohortMethodControlResults(connectionHandler = connectionHandler, resultsSchema = resultsSchema, tablePrefix = tablePrefix, targetId = inputParams()$target, @@ -107,7 +107,7 @@ estimationSystematicErrorServer <- function(id, selectedRow, inputParams, connec controlResults$calibratedCi95Lb[controlResults$calibratedCi95Lb == 0] <- NA controlResults$calibratedCi95Ub[controlResults$calibratedCi95Ub == 0] <- NA - plot <- plotEstimationScatter(controlResults) + plot <- plotCohortMethodScatter(controlResults) return(plot) } }) @@ -133,7 +133,7 @@ estimationSystematicErrorServer <- function(id, selectedRow, inputParams, connec if (is.null(row) || !(row$databaseId %in% metaAnalysisDbIds)) { return(NULL) } else { - ##negativeControls <- getEstimationNegativeControlEstimates(connection = connection, + ##negativeControls <- getCohortMethodNegativeControlEstimates(connection = connection, ## #resultsSchema = resultsSchema, unused argument ## targetId = inputParams()$target, ## comparatorId = inputParams()$comparator, @@ -141,8 +141,8 @@ estimationSystematicErrorServer <- function(id, selectedRow, inputParams, connec ##if (is.null(negativeControls)) return(NULL) - ## plotEstimationEmpiricalNulls() not found - #plot <- plotEstimationEmpiricalNulls(negativeControls) + ## plotCohortMethodEmpiricalNulls() not found + #plot <- plotCohortMethodEmpiricalNulls(negativeControls) ##return(plot) } }) diff --git a/R/components-data-viewer.R b/R/components-data-viewer.R index 4f94c8ed..8809982e 100644 --- a/R/components-data-viewer.R +++ b/R/components-data-viewer.R @@ -238,7 +238,9 @@ resultTableServer <- function( showSortIcon = TRUE, striped = TRUE, highlight = TRUE, - defaultColDef = reactable::colDef(align = "left") + defaultColDef = reactable::colDef(align = "left"), + + rowStyle = list(height = 40*3) #, experimental #theme = ohdsiReactableTheme ) @@ -299,7 +301,8 @@ addTableActions <- function( status = "primary", circle = FALSE, width = "300px", - margin = "5px" + margin = "5px", + inline = T ) args <- append( diff --git a/R/helpers-estimationDataPulls.R b/R/helpers-cohort-methodDataPulls.R similarity index 88% rename from R/helpers-estimationDataPulls.R rename to R/helpers-cohort-methodDataPulls.R index 2cce6014..13cc7e1e 100644 --- a/R/helpers-estimationDataPulls.R +++ b/R/helpers-cohort-methodDataPulls.R @@ -19,7 +19,7 @@ getCohortNameFromId <- function(connectionHandler, resultsSchema, cohortTablePre } -getEstimationTcoChoice <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, tcoVar, sorted = TRUE) { +getCohortMethodTcoChoice <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, tcoVar, sorted = TRUE) { sql <- " SELECT DISTINCT @@ -48,28 +48,28 @@ FROM } -getEstimationTargetChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { +getCohortMethodTargetChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { return( - getEstimationTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "target_id") + getCohortMethodTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "target_id") ) } -getEstimationComparatorChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { +getCohortMethodComparatorChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { return( - getEstimationTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "comparator_id") + getCohortMethodTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "comparator_id") ) } -getEstimationOutcomeChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { +getCohortMethodOutcomeChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { return( - getEstimationTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "outcome_id") + getCohortMethodTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "outcome_id") ) } -getEstimationDatabaseChoices <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, sorted = TRUE) { +getCohortMethodDatabaseChoices <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, sorted = TRUE) { sql <- " SELECT DISTINCT @@ -122,7 +122,7 @@ FROM ) } -getAllEstimationResults <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable) { +getAllCohortMethodResults <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable) { sql <- " SELECT cma.analysis_id, @@ -167,7 +167,7 @@ FROM } -getEstimationMainResults <- function(connectionHandler, +getCohortMethodMainResults <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, @@ -273,7 +273,7 @@ getCohortMethodAnalyses <- function(connectionHandler, resultsSchema, tablePrefi } -getEstimationSubgroupResults <- function(connectionHandler, # not used? +getCohortMethodSubgroupResults <- function(connectionHandler, # not used? targetIds = c(), comparatorIds = c(), outcomeIds = c(), @@ -327,7 +327,7 @@ getEstimationSubgroupResults <- function(connectionHandler, # not used? } -getEstimationControlResults <- function(connectionHandler, resultsSchema, tablePrefix, targetId, +getCohortMethodControlResults <- function(connectionHandler, resultsSchema, tablePrefix, targetId, comparatorId, analysisId, databaseId = NULL, includePositiveControls = TRUE, emptyAsNa = TRUE) { @@ -414,7 +414,7 @@ getCmFollowUpDist <- function(connectionHandler, -getEstimationPs <- function(connectionHandler, resultsSchema, tablePrefix, targetId, comparatorId, analysisId, databaseId = NULL) { +getCohortMethodPs <- function(connectionHandler, resultsSchema, tablePrefix, targetId, comparatorId, analysisId, databaseId = NULL) { sql <- " SELECT * @@ -448,7 +448,7 @@ getEstimationPs <- function(connectionHandler, resultsSchema, tablePrefix, targe } -getEstimationKaplanMeier <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, targetId, comparatorId, outcomeId, databaseId, analysisId) { +getCohortMethodKaplanMeier <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, targetId, comparatorId, outcomeId, databaseId, analysisId) { sqlTmp <- " SELECT * @@ -491,7 +491,7 @@ getEstimationKaplanMeier <- function(connectionHandler, resultsSchema, tablePref } -getEstimationAttrition <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, targetId, comparatorId, outcomeId, analysisId, databaseId) { +getCohortMethodAttrition <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, targetId, comparatorId, outcomeId, analysisId, databaseId) { sqlTmp <- " SELECT cmat.* @@ -540,7 +540,7 @@ getEstimationAttrition <- function(connectionHandler, resultsSchema, tablePrefix } -getEstimationStudyPeriod <- function(connectionHandler, targetId, comparatorId, databaseId) { +getCohortMethodStudyPeriod <- function(connectionHandler, targetId, comparatorId, databaseId) { sql <- "SELECT min_date, max_date FROM comparison_summary @@ -558,7 +558,7 @@ getEstimationStudyPeriod <- function(connectionHandler, targetId, comparatorId, } -getEstimationPropensityModel <- function(connectionHandler, resultsSchema, tablePrefix, targetId, comparatorId, analysisId, databaseId) { +getCohortMethodPropensityModel <- function(connectionHandler, resultsSchema, tablePrefix, targetId, comparatorId, analysisId, databaseId) { sqlTmp <- " SELECT cmpm.coefficient, @@ -616,8 +616,8 @@ getEstimationPropensityModel <- function(connectionHandler, resultsSchema, table -getEstimationNegativeControlEstimates <- function(cohortMethodResult, connectionHandler, targetId, comparatorId, analysisId) { - subset <- getEstimationControlResults(cohortMethodResult, connectionHandler, targetId, comparatorId, analysisId, includePositiveControls = FALSE) +getCohortMethodNegativeControlEstimates <- function(cohortMethodResult, connectionHandler, targetId, comparatorId, analysisId) { + subset <- getCohortMethodControlResults(cohortMethodResult, connectionHandler, targetId, comparatorId, analysisId, includePositiveControls = FALSE) subset <- subset[, c("databaseId", "logRr", "seLogRr")] if(nrow(subset) == 0) return(NULL) diff --git a/R/helpers-estimationPlotsAndTables.R b/R/helpers-cohort-methodPlotsAndTables.R similarity index 97% rename from R/helpers-estimationPlotsAndTables.R rename to R/helpers-cohort-methodPlotsAndTables.R index 5bee185e..e5ebbde8 100644 --- a/R/helpers-estimationPlotsAndTables.R +++ b/R/helpers-cohort-methodPlotsAndTables.R @@ -1,5 +1,5 @@ # used in estimation-power -prepareEstimationFollowUpDistTable <- function(followUpDist) { +prepareCohortMethodFollowUpDistTable <- function(followUpDist) { targetRow <- data.frame(Database = followUpDist$databaseId, Cohort = "Target", Min = followUpDist$targetMinDays, @@ -33,7 +33,7 @@ prepareEstimationFollowUpDistTable <- function(followUpDist) { # used in estimation-power -prepareEstimationPowerTable <- function(mainResults, connectionHandler , resultsSchema, tablePrefix) { +prepareCohortMethodPowerTable <- function(mainResults, connectionHandler , resultsSchema, tablePrefix) { analyses <- getCohortMethodAnalyses(connectionHandler , resultsSchema, tablePrefix) table <- merge(mainResults, analyses) alpha <- 0.05 @@ -80,7 +80,7 @@ prepareEstimationPowerTable <- function(mainResults, connectionHandler , results } # estimation-subgroups -prepareEstimationSubgroupTable <- function(subgroupResults, output = "latex") { +prepareCohortMethodSubgroupTable <- function(subgroupResults, output = "latex") { rnd <- function(x) { ifelse(x > 10, sprintf("%.1f", x), sprintf("%.2f", x)) } @@ -137,7 +137,7 @@ prepareEstimationSubgroupTable <- function(subgroupResults, output = "latex") { # estiamtion-covariateBal -plotEstimationCovariateBalanceSummary <- function(balanceSummary, +plotCohortMethodCovariateBalanceSummary <- function(balanceSummary, threshold = 0, beforeLabel = "Before matching", afterLabel = "After matching") { @@ -214,7 +214,7 @@ plotEstimationCovariateBalanceSummary <- function(balanceSummary, } # estimation-systematicError -plotEstimationScatter <- function(controlResults) { +plotCohortMethodScatter <- function(controlResults) { if(nrow(controlResults)==0){ return(NULL) @@ -318,7 +318,7 @@ plotEstimationScatter <- function(controlResults) { } # estimation-attrition -drawEstimationAttritionDiagram <- function(attrition, +drawCohortMethodAttritionDiagram <- function(attrition, targetLabel = "Target", comparatorLabel = "Comparator") { addStep <- function(data, attrition, row) { @@ -457,7 +457,7 @@ drawEstimationAttritionDiagram <- function(attrition, } # used in helpers-estPandT -nonZeroEstimationHazardRatio <- function(hrLower, hrUpper, terms) { +nonZeroCohortMethodHazardRatio <- function(hrLower, hrUpper, terms) { if (hrUpper < 1) { return(terms[1]) } else if (hrLower > 1) { @@ -468,7 +468,7 @@ nonZeroEstimationHazardRatio <- function(hrLower, hrUpper, terms) { } # estimation-resultsTable -prettyEstimationHr <- function(x) { +prettyCohortMethodHr <- function(x) { if (!is.numeric(x)) { x <- as.numeric(x) } @@ -478,18 +478,18 @@ prettyEstimationHr <- function(x) { } # used in here -goodEstimationPropensityScore <- function(value) { +goodCohortMethodPropensityScore <- function(value) { return(value > 1) } # used in here -goodEstimationSystematicBias <- function(value) { +goodCohortMethodSystematicBias <- function(value) { return(value > 1) } # estmation-propensity -prepareEstimationPropensityModelTable <- function(model) { +prepareCohortMethodPropensityModelTable <- function(model) { rnd <- function(x) { ifelse(x > 10, sprintf("%.1f", x), sprintf("%.2f", x)) } @@ -500,7 +500,7 @@ prepareEstimationPropensityModelTable <- function(model) { } # estimation-forestPlot -plotEstimationForest <- function(results, limits = c(0.1, 10), metaAnalysisDbIds = NULL) { +plotCohortMethodForest <- function(results, limits = c(0.1, 10), metaAnalysisDbIds = NULL) { dbResults <- results[!(results$databaseId %in% metaAnalysisDbIds), ] dbResults <- dbResults[!is.na(dbResults$seLogRr), ] diff --git a/R/helpers-getEstimationUtility.R b/R/helpers-getCohortMethodUtility.R similarity index 75% rename from R/helpers-getEstimationUtility.R rename to R/helpers-getCohortMethodUtility.R index 9528f66e..a2276592 100644 --- a/R/helpers-getEstimationUtility.R +++ b/R/helpers-getCohortMethodUtility.R @@ -1,5 +1,5 @@ -getEstimationColumnsToBlind <- function(results) { +getCohortMethodColumnsToBlind <- function(results) { columnsToBlind <- c("rr", "ci95Ub", "ci95Lb", "logRr", "seLogRr", "p", "calibratedRr", "calibratedCi95Ub", @@ -11,14 +11,14 @@ getEstimationColumnsToBlind <- function(results) { } -getEstimationSelectNamedChoices <- function(v1, v2) { +getCohortMethodSelectNamedChoices <- function(v1, v2) { l <- as.list(v1) names(l) <- as.vector(v2) return(l) } -filterEstimationEmptyNullValues <- function(v, includeNull=TRUE) { +filterCohortMethodEmptyNullValues <- function(v, includeNull=TRUE) { valsToFilter <- c('') if (includeNull) valsToFilter <- c(valsToFilter, NULL) diff --git a/R/prediction-covariateSummary.R b/R/prediction-covariateSummary.R index 57852df8..e0d55c35 100644 --- a/R/prediction-covariateSummary.R +++ b/R/prediction-covariateSummary.R @@ -60,8 +60,11 @@ predictionCovariateSummaryViewer <- function(id) { width=12, shinydashboard::box( status = 'info', width = 12, - title = "Covariates", solidHeader = TRUE, - DT::dataTableOutput(ns('modelCovariateInfo')) + title = "Details", solidHeader = TRUE, + shinydashboard::infoBoxOutput(ns("covariateCount"), width = 6), + shinydashboard::infoBoxOutput(ns("nonZeroCount"), width = 6), + shinydashboard::infoBoxOutput(ns("intercept"), width = 6), + shinydashboard::infoBoxOutput(ns("hyperparameters"), width = 6) ) ), shiny::fluidRow( @@ -144,17 +147,76 @@ predictionCovariateSummaryServer <- function( } }) - output$modelView <- DT::renderDataTable( - editCovariates(covariateSummary())$table, - colnames = editCovariates(covariateSummary())$colnames + hyperParamSearch <- shiny::reactive({getHyperParamSearch( + inputSingleView = inputSingleView, + modelDesignId = modelDesignId, + databaseId = developmentDatabaseId, + schema = schema, + connectionHandler = connectionHandler, + plpTablePrefix = plpTablePrefix + ) }) + + # hyper-param + output$hyperparameters<- shinydashboard::renderInfoBox({ + shinydashboard::infoBox( + 'Hyper-parameters', + shiny::actionButton(session$ns("showHyperparameters"),"View"), + icon = shiny::icon('gear'), + color = "light-blue" + ) + }) + shiny::observeEvent( + input$showHyperparameters, { + shiny::showModal(shiny::modalDialog( + title = "Hyper-parameters", + shiny::div( + DT::renderDataTable( + DT::datatable( + as.data.frame( + hyperParamSearch() + ), + options = list(scrollX = TRUE), + colnames = 'Fold AUROC' + ) + ) + ), + easyClose = TRUE, + footer = NULL + )) + } ) - output$modelCovariateInfo <- DT::renderDataTable( - data.frame( - covariates = nrow(covariateSummary()), - nonZeroCount = sum(covariateSummary()$covariateValue!=0, na.rm = T), - intercept = intercept() + output$covariateCount <- shinydashboard::renderInfoBox({ + shinydashboard::infoBox( + '# Covariates', + nrow(covariateSummary()), + icon = shiny::icon('hashtag'), + color = "light-blue" ) + }) + + + output$nonZeroCount <- shinydashboard::renderInfoBox({ + shinydashboard::infoBox( + '# Non-zero covariates', + sum(covariateSummary()$covariateValue!=0, na.rm = T), + icon = shiny::icon('square-full'), + color = "light-blue" + ) + }) + + output$intercept <- shinydashboard::renderInfoBox({ + shinydashboard::infoBox( + 'Intercept', + format(intercept(), digits =3), + icon = shiny::icon('a'), + color = "light-blue" + ) + }) + + output$modelView <- DT::renderDataTable( + editCovariates(covariateSummary())$table, + colnames = editCovariates(covariateSummary())$colnames ) # covariate model plots @@ -209,6 +271,36 @@ editCovariates <- function(covs){ } } + +# get hyper parameters +getHyperParamSearch <- function( + inputSingleView, + modelDesignId, + databaseId, + schema, + connectionHandler, + plpTablePrefix +){ + + if(!is.null(modelDesignId()) & inputSingleView() == 'Design Settings'){ + + sql <- "SELECT train_details FROM @my_schema.@my_table_appendmodels WHERE database_id = @database_id + and model_design_id = @model_design_id;" + + models <- connectionHandler$queryDb( + sql = sql, + my_schema = schema, + database_id = databaseId(), + model_design_id = modelDesignId(), + my_table_append = plpTablePrefix + ) + trainDetails <- ParallelLogger::convertJsonToSettings(models$trainDetails) + + return(trainDetails$hyperParamSearch) + } +} + + # format covariate summary table plotCovariateSummary <- function(covariateSummary){ diff --git a/R/prediction-cutoff.R b/R/prediction-cutoff.R index 9e8c7e91..c651669d 100644 --- a/R/prediction-cutoff.R +++ b/R/prediction-cutoff.R @@ -116,9 +116,7 @@ predictionCutoffServer <- function( thresholdSummary <- shiny::reactive({ if(!is.null(performanceId()) & inputSingleView() == 'Threshold Dependant'){ - - print('getting thresholdSummary') - + value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, diff --git a/R/prediction-discrimination.R b/R/prediction-discrimination.R index ea207e11..f1169e9e 100644 --- a/R/prediction-discrimination.R +++ b/R/prediction-discrimination.R @@ -42,11 +42,14 @@ predictionDiscriminationViewer <- function(id) { title = 'Summary', solidHeader = TRUE, shiny::p('Click view to see the corresponding plots:'), - reactable::reactableOutput(ns('summaryTable')) + resultTableViewer(ns('summaryTable')) ) ), - + #shiny::conditionalPanel( + # condition = "output.generate == 1", + # ns = ns, + shiny::fluidRow( shinydashboard::box( status = 'info', @@ -129,7 +132,11 @@ predictionDiscriminationViewer <- function(id) { shiny::plotOutput(ns('prefdist')) ) ) + ) + + #) # cond panel + ) } @@ -191,50 +198,27 @@ predictionDiscriminationServer <- function( ) - tidyr::pivot_wider( + data <- tidyr::pivot_wider( data = data[ind,], names_from = 'metric', values_from = 'value' ) + cbind( + actions = rep('', nrow(data)), + data + ) + }) + modelTableOutputs <- resultTableServer( + id = "summaryTable", + colDefsInput = NULL, + df = sumTable, + addActions = c('performance') + ) - output$summaryTable <- reactable::renderReactable({ - reactable::reactable( - data = - if(is.null(sumTable())){ - NULL - } else{ - cbind( - view = rep("",nrow( sumTable())), - sumTable() - ) - }, - columns = - list( - view = reactable::colDef( - name = "", - sortable = FALSE, - cell = function() htmltools::tags$button("View") - ) - ),onClick = reactable::JS(paste0("function(rowInfo, column) { - // Only handle click events on the 'details' column - if (column.id !== 'view' ) { - return - } - // Send the click event to Shiny, which will be available in input$show_details - // Note that the row index starts at 0 in JavaScript, so we add 1 - if(column.id == 'view'){ - Shiny.setInputValue('",session$ns('show_view'),"', { index: rowInfo.index + 1 }, { priority: 'event' }) - } - }") - ), - filterable = TRUE - ) - }) - predictionDistribution <- shiny::reactiveVal(NULL) thresholdSummary <- shiny::reactiveVal(NULL) @@ -301,37 +285,42 @@ predictionDiscriminationServer <- function( } ) + #generate <- shiny::reactiveVal(0) + #output$generate <- generate() + #shiny::outputOptions(output,"generate",suspendWhenHidden=FALSE) + shiny::observeEvent( - input$show_view, { + modelTableOutputs$actionCount(), { + #generate(1) output$roc <- plotly::renderPlotly({ - type <- trimws(sumTable()$evaluation[input$show_view$index]) + type <- trimws(sumTable()$evaluation[modelTableOutputs$actionIndex()$index]) tryCatch({plots()$roc[[type]]}, error = function(err){emptyPlot(title = err)}) }) output$pr <- plotly::renderPlotly({ - type <- trimws(sumTable()$evaluation[input$show_view$index]) + type <- trimws(sumTable()$evaluation[modelTableOutputs$actionIndex()$index]) tryCatch({plots()$pr[[type]]}, error = function(err){emptyPlot(title = err)}) }) output$f1 <- plotly::renderPlotly({ - type <- trimws(sumTable()$evaluation[input$show_view$index]) + type <- trimws(sumTable()$evaluation[modelTableOutputs$actionIndex()$index]) tryCatch({plots()$f1[[type]]}, error = function(err){emptyPlot(title = err)}) }) # preference plot output$prefdist <- shiny::renderPlot({ - type <- trimws(sumTable()$evaluation[input$show_view$index]) + type <- trimws(sumTable()$evaluation[modelTableOutputs$actionIndex()$index]) tryCatch({plots()$prefpdf[[type]]}, error = function(err){emptyPlot(title = err)}) }) output$preddist <- shiny::renderPlot({ - type <- trimws(sumTable()$evaluation[input$show_view$index]) + type <- trimws(sumTable()$evaluation[modelTableOutputs$actionIndex()$index]) tryCatch({plots()$predpdf[[type]]}, error = function(err){emptyPlot(title = err)}) }) output$box <- shiny::renderPlot({ - type <- trimws(sumTable()$evaluation[input$show_view$index]) + type <- trimws(sumTable()$evaluation[modelTableOutputs$actionIndex()$index]) tryCatch({plots()$box[[type]]}, error = function(err){emptyPlot(title = err)}) }) } diff --git a/R/prediction-modelSummary.R b/R/prediction-modelSummary.R index 7f009c5f..94ff177e 100644 --- a/R/prediction-modelSummary.R +++ b/R/prediction-modelSummary.R @@ -159,7 +159,7 @@ predictionModelSummaryServer <- function( id = "performanceSummaryTable", df = resultTable, colDefsInput = colDefsInput, - addActions = c('results') + addActions = c('results','attrition') ) performanceId <- shiny::reactiveVal(value = NULL) @@ -174,6 +174,34 @@ predictionModelSummaryServer <- function( } }) + shiny::observeEvent(modelTableOutputs$actionCount(), { + if(modelTableOutputs$actionType() == 'attrition'){ + + attrition <- shiny::reactive({ + getAttrition( + performanceId = resultTable()$performanceId[modelTableOutputs$actionIndex()$index], + schema = schema, + connectionHandler = connectionHandler, + plpTablePrefix = plpTablePrefix + ) + }) + + shiny::showModal( + shiny::modalDialog( + title = "Attrition", + shiny::div( + DT::renderDataTable( + attrition() %>% dplyr::select(-c("performanceId", "outcomeId")) + ) + ), + easyClose = TRUE, + footer = NULL + ) + ) + + } + }) + return( list( developmentDatabaseId = developmentDatabaseId, @@ -186,7 +214,27 @@ predictionModelSummaryServer <- function( ) } - +getAttrition <- function( + performanceId, + schema, + connectionHandler, + plpTablePrefix +){ + + if(!is.null(performanceId)){ + + sql <- "SELECT * FROM @my_schema.@my_table_appendattrition WHERE performance_id = @performance_id;" + + attrition <- connectionHandler$queryDb( + sql = sql, + my_schema = schema, + performance_id = performanceId, + my_table_append = plpTablePrefix + ) + + return(attrition) + } +} getModelDesignPerformanceSummary <- function( connectionHandler, diff --git a/R/prediction-settings.R b/R/prediction-settings.R index efdd867a..ac3800f7 100644 --- a/R/prediction-settings.R +++ b/R/prediction-settings.R @@ -46,9 +46,7 @@ predictionSettingsViewer <- function(id) { shinydashboard::infoBoxOutput(ns("preprocess"), width = 4), shinydashboard::infoBoxOutput(ns("split"), width = 4), shinydashboard::infoBoxOutput(ns("sample"), width = 4), - shinydashboard::infoBoxOutput(ns("model"), width = 4), - shinydashboard::infoBoxOutput(ns("hyperparameters"), width = 4), - shinydashboard::infoBoxOutput(ns("attrition"), width = 4) + shinydashboard::infoBoxOutput(ns("model"), width = 4) ) ) @@ -102,26 +100,7 @@ predictionSettingsServer <- function( plpTablePrefix = plpTablePrefix, cohortTablePrefix = cohortTablePrefix )}) - - hyperParamSearch <- shiny::reactive({getHyperParamSearch( - inputSingleView = inputSingleView, - modelDesignId = modelDesignId, - databaseId = developmentDatabaseId, - schema = schema, - connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix - ) }) - - attrition <- shiny::reactive({ - getAttrition( - inputSingleView = inputSingleView, - performanceId = performanceId, - schema = schema, - connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix - ) - }) - + # databases databases <- shiny::reactive({ getPlpSettingDatabase( @@ -367,62 +346,6 @@ predictionSettingsServer <- function( } ) - # extras - - # hyper-param - output$hyperparameters<- shinydashboard::renderInfoBox({ - shinydashboard::infoBox( - 'Hyper-parameters', - shiny::actionButton(session$ns("showHyperparameters"),"View"), - icon = shiny::icon('gear'), - color = "light-blue" - ) - }) - shiny::observeEvent( - input$showHyperparameters, { - shiny::showModal(shiny::modalDialog( - title = "Hyper-parameters", - shiny::div( - DT::renderDataTable( - DT::datatable( - as.data.frame( - hyperParamSearch() - ), - options = list(scrollX = TRUE), - colnames = 'Fold AUROC' - ) - ) - ), - easyClose = TRUE, - footer = NULL - )) - } - ) - - # attrition - output$attrition <- shinydashboard::renderInfoBox({ - shinydashboard::infoBox( - 'Attrition', - shiny::actionButton(session$ns("showAttrition"),"View"), - icon = shiny::icon('magnet'), - color = "light-blue" - ) - }) - shiny::observeEvent( - input$showAttrition, { - shiny::showModal(shiny::modalDialog( - title = "Attrition", - shiny::div( - DT::renderDataTable( - attrition() %>% dplyr::select(-c("performanceId", "outcomeId")) - ) - ), - easyClose = TRUE, - footer = NULL - )) - } - ) - } ) @@ -687,56 +610,9 @@ getModelDesign <- function( } -getHyperParamSearch <- function( - inputSingleView, - modelDesignId, - databaseId, - schema, - connectionHandler, - plpTablePrefix -){ - - if(!is.null(modelDesignId()) & inputSingleView() == 'Design Settings'){ - sql <- "SELECT train_details FROM @my_schema.@my_table_appendmodels WHERE database_id = @database_id - and model_design_id = @model_design_id;" - models <- connectionHandler$queryDb( - sql = sql, - my_schema = schema, - database_id = databaseId(), - model_design_id = modelDesignId(), - my_table_append = plpTablePrefix - ) - trainDetails <- ParallelLogger::convertJsonToSettings(models$trainDetails) - - return(trainDetails$hyperParamSearch) - } -} - - -getAttrition <- function( - inputSingleView, - performanceId, - schema, - connectionHandler, - plpTablePrefix -){ - if(!is.null(performanceId()) & inputSingleView() == 'Design Settings'){ - - sql <- "SELECT * FROM @my_schema.@my_table_appendattrition WHERE performance_id = @performance_id;" - - attrition <- connectionHandler$queryDb( - sql = sql, - my_schema = schema, - performance_id = performanceId(), - my_table_append = plpTablePrefix - ) - - return(attrition) - } -} # formating formatModSettings <- function(modelSettings){ diff --git a/inst/description-www/Description.html b/inst/characterization-www/characterization.html similarity index 100% rename from inst/description-www/Description.html rename to inst/characterization-www/characterization.html diff --git a/inst/description-www/help-OutcomeStratified.html b/inst/characterization-www/help-OutcomeStratified.html similarity index 100% rename from inst/description-www/help-OutcomeStratified.html rename to inst/characterization-www/help-OutcomeStratified.html diff --git a/inst/description-www/help-dechallengeRechallenge.html b/inst/characterization-www/help-dechallengeRechallenge.html similarity index 100% rename from inst/description-www/help-dechallengeRechallenge.html rename to inst/characterization-www/help-dechallengeRechallenge.html diff --git a/inst/description-www/help-incidenceRate.html b/inst/characterization-www/help-incidenceRate.html similarity index 100% rename from inst/description-www/help-incidenceRate.html rename to inst/characterization-www/help-incidenceRate.html diff --git a/inst/description-www/help-targetViewer.html b/inst/characterization-www/help-targetViewer.html similarity index 100% rename from inst/description-www/help-targetViewer.html rename to inst/characterization-www/help-targetViewer.html diff --git a/inst/description-www/help-timeToEvent.html b/inst/characterization-www/help-timeToEvent.html similarity index 100% rename from inst/description-www/help-timeToEvent.html rename to inst/characterization-www/help-timeToEvent.html diff --git a/inst/estimation-ref/Table1Specs.csv b/inst/cohort-method-ref/Table1Specs.csv similarity index 100% rename from inst/estimation-ref/Table1Specs.csv rename to inst/cohort-method-ref/Table1Specs.csv diff --git a/inst/estimation-www/estimation.html b/inst/cohort-method-www/cohort-method.html similarity index 100% rename from inst/estimation-www/estimation.html rename to inst/cohort-method-www/cohort-method.html diff --git a/man/descriptionAggregateFeaturesServer.Rd b/man/characterizationAggregateFeaturesServer.Rd similarity index 81% rename from man/descriptionAggregateFeaturesServer.Rd rename to man/characterizationAggregateFeaturesServer.Rd index 8724cbaa..edb4d4f0 100644 --- a/man/descriptionAggregateFeaturesServer.Rd +++ b/man/characterizationAggregateFeaturesServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-aggregateFeatures.R -\name{descriptionAggregateFeaturesServer} -\alias{descriptionAggregateFeaturesServer} +% Please edit documentation in R/characterization-aggregateFeatures.R +\name{characterizationAggregateFeaturesServer} +\alias{characterizationAggregateFeaturesServer} \title{The module server for exploring aggregate features results} \usage{ -descriptionAggregateFeaturesServer( +characterizationAggregateFeaturesServer( id, connectionHandler, mainPanelTab, diff --git a/man/descriptionAggregateFeaturesViewer.Rd b/man/characterizationAggregateFeaturesViewer.Rd similarity index 64% rename from man/descriptionAggregateFeaturesViewer.Rd rename to man/characterizationAggregateFeaturesViewer.Rd index 3ab53d83..75b82348 100644 --- a/man/descriptionAggregateFeaturesViewer.Rd +++ b/man/characterizationAggregateFeaturesViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-aggregateFeatures.R -\name{descriptionAggregateFeaturesViewer} -\alias{descriptionAggregateFeaturesViewer} +% Please edit documentation in R/characterization-aggregateFeatures.R +\name{characterizationAggregateFeaturesViewer} +\alias{characterizationAggregateFeaturesViewer} \title{The module viewer for exploring aggregate feature results} \usage{ -descriptionAggregateFeaturesViewer(id) +characterizationAggregateFeaturesViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/descriptionDechallengeRechallengeServer.Rd b/man/characterizationDechallengeRechallengeServer.Rd similarity index 80% rename from man/descriptionDechallengeRechallengeServer.Rd rename to man/characterizationDechallengeRechallengeServer.Rd index 4eef475c..21fba74d 100644 --- a/man/descriptionDechallengeRechallengeServer.Rd +++ b/man/characterizationDechallengeRechallengeServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-dechallengeRechallenge.R -\name{descriptionDechallengeRechallengeServer} -\alias{descriptionDechallengeRechallengeServer} +% Please edit documentation in R/characterization-dechallengeRechallenge.R +\name{characterizationDechallengeRechallengeServer} +\alias{characterizationDechallengeRechallengeServer} \title{The module server for exploring Dechallenge Rechallenge results} \usage{ -descriptionDechallengeRechallengeServer( +characterizationDechallengeRechallengeServer( id, connectionHandler, mainPanelTab, diff --git a/man/descriptionDechallengeRechallengeViewer.Rd b/man/characterizationDechallengeRechallengeViewer.Rd similarity index 63% rename from man/descriptionDechallengeRechallengeViewer.Rd rename to man/characterizationDechallengeRechallengeViewer.Rd index 77ea9c32..f937ae7e 100644 --- a/man/descriptionDechallengeRechallengeViewer.Rd +++ b/man/characterizationDechallengeRechallengeViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-dechallengeRechallenge.R -\name{descriptionDechallengeRechallengeViewer} -\alias{descriptionDechallengeRechallengeViewer} +% Please edit documentation in R/characterization-dechallengeRechallenge.R +\name{characterizationDechallengeRechallengeViewer} +\alias{characterizationDechallengeRechallengeViewer} \title{The module viewer for exploring Dechallenge Rechallenge results} \usage{ -descriptionDechallengeRechallengeViewer(id) +characterizationDechallengeRechallengeViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/characterizationHelperFile.Rd b/man/characterizationHelperFile.Rd new file mode 100644 index 00000000..67cd8f45 --- /dev/null +++ b/man/characterizationHelperFile.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/characterization-main.R +\name{characterizationHelperFile} +\alias{characterizationHelperFile} +\title{The location of the characterization module helper file} +\usage{ +characterizationHelperFile() +} +\value{ +string location of the characterization helper file +} +\description{ +The location of the characterization module helper file +} +\details{ +Returns the location of the characterization helper file +} diff --git a/man/descriptionIncidenceServer.Rd b/man/characterizationIncidenceServer.Rd similarity index 81% rename from man/descriptionIncidenceServer.Rd rename to man/characterizationIncidenceServer.Rd index 84d9014e..327b9a87 100644 --- a/man/descriptionIncidenceServer.Rd +++ b/man/characterizationIncidenceServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-incidence.R -\name{descriptionIncidenceServer} -\alias{descriptionIncidenceServer} +% Please edit documentation in R/characterization-incidence.R +\name{characterizationIncidenceServer} +\alias{characterizationIncidenceServer} \title{The module server for exploring incidence results} \usage{ -descriptionIncidenceServer( +characterizationIncidenceServer( id, connectionHandler, mainPanelTab, diff --git a/man/descriptionIncidenceViewer.Rd b/man/characterizationIncidenceViewer.Rd similarity index 67% rename from man/descriptionIncidenceViewer.Rd rename to man/characterizationIncidenceViewer.Rd index 8f53cab8..8bb718f7 100644 --- a/man/descriptionIncidenceViewer.Rd +++ b/man/characterizationIncidenceViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-incidence.R -\name{descriptionIncidenceViewer} -\alias{descriptionIncidenceViewer} +% Please edit documentation in R/characterization-incidence.R +\name{characterizationIncidenceViewer} +\alias{characterizationIncidenceViewer} \title{The module viewer for exploring incidence results} \usage{ -descriptionIncidenceViewer(id) +characterizationIncidenceViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/characterizationServer.Rd b/man/characterizationServer.Rd new file mode 100644 index 00000000..77d64feb --- /dev/null +++ b/man/characterizationServer.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/characterization-main.R +\name{characterizationServer} +\alias{characterizationServer} +\title{The module server for exploring characterization studies} +\usage{ +characterizationServer( + id, + connectionHandler, + resultDatabaseSettings = list(port = 1) +) +} +\arguments{ +\item{id}{the unique reference id for the module} + +\item{connectionHandler}{a connection to the database with the results} + +\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cohortTablePrefix} +} +\value{ +The server for the characterization module +} +\description{ +The module server for exploring characterization studies +} +\details{ +The user specifies the id for the module +} diff --git a/man/descriptionTableServer.Rd b/man/characterizationTableServer.Rd similarity index 84% rename from man/descriptionTableServer.Rd rename to man/characterizationTableServer.Rd index 104cb8bd..6dd3fdde 100644 --- a/man/descriptionTableServer.Rd +++ b/man/characterizationTableServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-cohorts.R -\name{descriptionTableServer} -\alias{descriptionTableServer} +% Please edit documentation in R/characterization-cohorts.R +\name{characterizationTableServer} +\alias{characterizationTableServer} \title{The module server for exploring 1 or more cohorts features} \usage{ -descriptionTableServer( +characterizationTableServer( id, connectionHandler, mainPanelTab, diff --git a/man/descriptionTableViewer.Rd b/man/characterizationTableViewer.Rd similarity index 69% rename from man/descriptionTableViewer.Rd rename to man/characterizationTableViewer.Rd index f2825d50..b82f445c 100644 --- a/man/descriptionTableViewer.Rd +++ b/man/characterizationTableViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-cohorts.R -\name{descriptionTableViewer} -\alias{descriptionTableViewer} +% Please edit documentation in R/characterization-cohorts.R +\name{characterizationTableViewer} +\alias{characterizationTableViewer} \title{The module viewer for exploring 1 or more cohorts features} \usage{ -descriptionTableViewer(id) +characterizationTableViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/descriptionTimeToEventServer.Rd b/man/characterizationTimeToEventServer.Rd similarity index 82% rename from man/descriptionTimeToEventServer.Rd rename to man/characterizationTimeToEventServer.Rd index 3f877337..a24b217f 100644 --- a/man/descriptionTimeToEventServer.Rd +++ b/man/characterizationTimeToEventServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-timeToEvent.R -\name{descriptionTimeToEventServer} -\alias{descriptionTimeToEventServer} +% Please edit documentation in R/characterization-timeToEvent.R +\name{characterizationTimeToEventServer} +\alias{characterizationTimeToEventServer} \title{The module server for exploring time to event results} \usage{ -descriptionTimeToEventServer( +characterizationTimeToEventServer( id, connectionHandler, mainPanelTab, diff --git a/man/descriptionTimeToEventViewer.Rd b/man/characterizationTimeToEventViewer.Rd similarity index 55% rename from man/descriptionTimeToEventViewer.Rd rename to man/characterizationTimeToEventViewer.Rd index 1b82b5b6..f5626ffc 100644 --- a/man/descriptionTimeToEventViewer.Rd +++ b/man/characterizationTimeToEventViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-timeToEvent.R -\name{descriptionTimeToEventViewer} -\alias{descriptionTimeToEventViewer} +% Please edit documentation in R/characterization-timeToEvent.R +\name{characterizationTimeToEventViewer} +\alias{characterizationTimeToEventViewer} \title{The module viewer for exploring time to event results} \usage{ -descriptionTimeToEventViewer(id) +characterizationTimeToEventViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the description time to event module +The user interface to the characterization time to event module } \description{ The module viewer for exploring time to event results diff --git a/man/characterizationViewer.Rd b/man/characterizationViewer.Rd new file mode 100644 index 00000000..641ee31c --- /dev/null +++ b/man/characterizationViewer.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/characterization-main.R +\name{characterizationViewer} +\alias{characterizationViewer} +\title{The module viewer for exploring characterization studies} +\usage{ +characterizationViewer(id = 1) +} +\arguments{ +\item{id}{the unique reference id for the module} +} +\value{ +The user interface to the characterization viewer module +} +\description{ +The module viewer for exploring characterization studies +} +\details{ +The user specifies the id for the module +} diff --git a/man/estimationAttritionServer.Rd b/man/cohortMethodAttritionServer.Rd similarity index 82% rename from man/estimationAttritionServer.Rd rename to man/cohortMethodAttritionServer.Rd index b6bbcc42..4837ea66 100644 --- a/man/estimationAttritionServer.Rd +++ b/man/cohortMethodAttritionServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-attrition.R -\name{estimationAttritionServer} -\alias{estimationAttritionServer} +% Please edit documentation in R/cohort-method-attrition.R +\name{cohortMethodAttritionServer} +\alias{cohortMethodAttritionServer} \title{The module server for rendering the PLE attrition results} \usage{ -estimationAttritionServer( +cohortMethodAttritionServer( id, selectedRow, inputParams, diff --git a/man/estimationAttritionViewer.Rd b/man/cohortMethodAttritionViewer.Rd similarity index 55% rename from man/estimationAttritionViewer.Rd rename to man/cohortMethodAttritionViewer.Rd index c8d79da9..9b16769e 100644 --- a/man/estimationAttritionViewer.Rd +++ b/man/cohortMethodAttritionViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-attrition.R -\name{estimationAttritionViewer} -\alias{estimationAttritionViewer} +% Please edit documentation in R/cohort-method-attrition.R +\name{cohortMethodAttritionViewer} +\alias{cohortMethodAttritionViewer} \title{The module viewer for rendering the PLE attrition results} \usage{ -estimationAttritionViewer(id) +cohortMethodAttritionViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation attrition +The user interface to the cohort method attrition } \description{ The module viewer for rendering the PLE attrition results diff --git a/man/estimationCovariateBalanceServer.Rd b/man/cohortMethodCovariateBalanceServer.Rd similarity index 80% rename from man/estimationCovariateBalanceServer.Rd rename to man/cohortMethodCovariateBalanceServer.Rd index b793620e..79435083 100644 --- a/man/estimationCovariateBalanceServer.Rd +++ b/man/cohortMethodCovariateBalanceServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-covariateBalance.R -\name{estimationCovariateBalanceServer} -\alias{estimationCovariateBalanceServer} +% Please edit documentation in R/cohort-method-covariateBalance.R +\name{cohortMethodCovariateBalanceServer} +\alias{cohortMethodCovariateBalanceServer} \title{The module server for rendering the covariate balance plot} \usage{ -estimationCovariateBalanceServer( +cohortMethodCovariateBalanceServer( id, selectedRow, inputParams, diff --git a/man/estimationCovariateBalanceViewer.Rd b/man/cohortMethodCovariateBalanceViewer.Rd similarity index 52% rename from man/estimationCovariateBalanceViewer.Rd rename to man/cohortMethodCovariateBalanceViewer.Rd index 0fdb4082..5e9011c5 100644 --- a/man/estimationCovariateBalanceViewer.Rd +++ b/man/cohortMethodCovariateBalanceViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-covariateBalance.R -\name{estimationCovariateBalanceViewer} -\alias{estimationCovariateBalanceViewer} +% Please edit documentation in R/cohort-method-covariateBalance.R +\name{cohortMethodCovariateBalanceViewer} +\alias{cohortMethodCovariateBalanceViewer} \title{The module viewer for rendering the PLE covariate balance analysis} \usage{ -estimationCovariateBalanceViewer(id) +cohortMethodCovariateBalanceViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation covariate balance results +The user interface to the cohort method covariate balance results } \description{ The module viewer for rendering the PLE covariate balance analysis diff --git a/man/estimationDiagnosticsSummaryServer.Rd b/man/cohortMethodDiagnosticsSummaryServer.Rd similarity index 76% rename from man/estimationDiagnosticsSummaryServer.Rd rename to man/cohortMethodDiagnosticsSummaryServer.Rd index 6b9136d9..badcc665 100644 --- a/man/estimationDiagnosticsSummaryServer.Rd +++ b/man/cohortMethodDiagnosticsSummaryServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-diagnosticsSummary.R -\name{estimationDiagnosticsSummaryServer} -\alias{estimationDiagnosticsSummaryServer} +% Please edit documentation in R/cohort-method-diagnosticsSummary.R +\name{cohortMethodDiagnosticsSummaryServer} +\alias{cohortMethodDiagnosticsSummaryServer} \title{The module server for rendering the PLE diagnostics summary} \usage{ -estimationDiagnosticsSummaryServer( +cohortMethodDiagnosticsSummaryServer( id, connectionHandler, resultsSchema, diff --git a/man/estimationDiagnosticsSummaryViewer.Rd b/man/cohortMethodDiagnosticsSummaryViewer.Rd similarity index 51% rename from man/estimationDiagnosticsSummaryViewer.Rd rename to man/cohortMethodDiagnosticsSummaryViewer.Rd index 12252704..c14dcde7 100644 --- a/man/estimationDiagnosticsSummaryViewer.Rd +++ b/man/cohortMethodDiagnosticsSummaryViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-diagnosticsSummary.R -\name{estimationDiagnosticsSummaryViewer} -\alias{estimationDiagnosticsSummaryViewer} +% Please edit documentation in R/cohort-method-diagnosticsSummary.R +\name{cohortMethodDiagnosticsSummaryViewer} +\alias{cohortMethodDiagnosticsSummaryViewer} \title{The module viewer for rendering the PLE diagnostics results} \usage{ -estimationDiagnosticsSummaryViewer(id) +cohortMethodDiagnosticsSummaryViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation diagnostics viewer +The user interface to the cohort method diagnostics viewer } \description{ The module viewer for rendering the PLE diagnostics results diff --git a/man/estimationForestPlotServer.Rd b/man/cohortMethodForestPlotServer.Rd similarity index 82% rename from man/estimationForestPlotServer.Rd rename to man/cohortMethodForestPlotServer.Rd index b4bf929c..d489c92a 100644 --- a/man/estimationForestPlotServer.Rd +++ b/man/cohortMethodForestPlotServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-forestPlot.R -\name{estimationForestPlotServer} -\alias{estimationForestPlotServer} +% Please edit documentation in R/cohort-method-forestPlot.R +\name{cohortMethodForestPlotServer} +\alias{cohortMethodForestPlotServer} \title{The module server for rendering the PLE multiple results forest plot} \usage{ -estimationForestPlotServer( +cohortMethodForestPlotServer( id, connectionHandler, selectedRow, diff --git a/man/estimationForestPlotViewer.Rd b/man/cohortMethodForestPlotViewer.Rd similarity index 55% rename from man/estimationForestPlotViewer.Rd rename to man/cohortMethodForestPlotViewer.Rd index cd32e58b..a8678a9e 100644 --- a/man/estimationForestPlotViewer.Rd +++ b/man/cohortMethodForestPlotViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-forestPlot.R -\name{estimationForestPlotViewer} -\alias{estimationForestPlotViewer} +% Please edit documentation in R/cohort-method-forestPlot.R +\name{cohortMethodForestPlotViewer} +\alias{cohortMethodForestPlotViewer} \title{The module viewer for rendering the PLE results forest plot} \usage{ -estimationForestPlotViewer(id) +cohortMethodForestPlotViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation forest plot +The user interface to the cohort method forest plot } \description{ The module viewer for rendering the PLE results forest plot diff --git a/man/cohortMethodHelperFile.Rd b/man/cohortMethodHelperFile.Rd new file mode 100644 index 00000000..01ba84ca --- /dev/null +++ b/man/cohortMethodHelperFile.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cohort-method-main.R +\name{cohortMethodHelperFile} +\alias{cohortMethodHelperFile} +\title{The location of the cohort method module helper file} +\usage{ +cohortMethodHelperFile() +} +\value{ +string location of the cohort method helper file +} +\description{ +The location of the cohort method module helper file +} +\details{ +Returns the location of the cohort method helper file +} diff --git a/man/estimationKaplanMeierServer.Rd b/man/cohortMethodKaplanMeierServer.Rd similarity index 83% rename from man/estimationKaplanMeierServer.Rd rename to man/cohortMethodKaplanMeierServer.Rd index f5f1ab56..94758418 100644 --- a/man/estimationKaplanMeierServer.Rd +++ b/man/cohortMethodKaplanMeierServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-kaplainMeier.R -\name{estimationKaplanMeierServer} -\alias{estimationKaplanMeierServer} +% Please edit documentation in R/cohort-method-kaplainMeier.R +\name{cohortMethodKaplanMeierServer} +\alias{cohortMethodKaplanMeierServer} \title{The module server for rendering the Kaplan Meier curve} \usage{ -estimationKaplanMeierServer( +cohortMethodKaplanMeierServer( id, selectedRow, inputParams, diff --git a/man/estimationKaplanMeierViewer.Rd b/man/cohortMethodKaplanMeierViewer.Rd similarity index 64% rename from man/estimationKaplanMeierViewer.Rd rename to man/cohortMethodKaplanMeierViewer.Rd index 805828d4..a896e9b1 100644 --- a/man/estimationKaplanMeierViewer.Rd +++ b/man/cohortMethodKaplanMeierViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-kaplainMeier.R -\name{estimationKaplanMeierViewer} -\alias{estimationKaplanMeierViewer} +% Please edit documentation in R/cohort-method-kaplainMeier.R +\name{cohortMethodKaplanMeierViewer} +\alias{cohortMethodKaplanMeierViewer} \title{The module viewer for rendering the PLE Kaplan Meier curve} \usage{ -estimationKaplanMeierViewer(id) +cohortMethodKaplanMeierViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/estimationPopulationCharacteristicsServer.Rd b/man/cohortMethodPopulationCharacteristicsServer.Rd similarity index 75% rename from man/estimationPopulationCharacteristicsServer.Rd rename to man/cohortMethodPopulationCharacteristicsServer.Rd index bd31131f..41d7b101 100644 --- a/man/estimationPopulationCharacteristicsServer.Rd +++ b/man/cohortMethodPopulationCharacteristicsServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-populationCharacteristics.R -\name{estimationPopulationCharacteristicsServer} -\alias{estimationPopulationCharacteristicsServer} +% Please edit documentation in R/cohort-method-populationCharacteristics.R +\name{cohortMethodPopulationCharacteristicsServer} +\alias{cohortMethodPopulationCharacteristicsServer} \title{The module server for rendering the population characteristics} \usage{ -estimationPopulationCharacteristicsServer( +cohortMethodPopulationCharacteristicsServer( id, selectedRow, inputParams, diff --git a/man/cohortMethodPopulationCharacteristicsViewer.Rd b/man/cohortMethodPopulationCharacteristicsViewer.Rd new file mode 100644 index 00000000..9262f9e3 --- /dev/null +++ b/man/cohortMethodPopulationCharacteristicsViewer.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cohort-method-populationCharacteristics.R +\name{cohortMethodPopulationCharacteristicsViewer} +\alias{cohortMethodPopulationCharacteristicsViewer} +\title{The module viewer for rendering the PLE population characteristics} +\usage{ +cohortMethodPopulationCharacteristicsViewer(id) +} +\arguments{ +\item{id}{the unique reference id for the module} +} +\value{ +The user interface to the cohort method population characteristics objects +} +\description{ +The module viewer for rendering the PLE population characteristics +} diff --git a/man/estimationPowerServer.Rd b/man/cohortMethodPowerServer.Rd similarity index 84% rename from man/estimationPowerServer.Rd rename to man/cohortMethodPowerServer.Rd index 80cfad1b..ddaeb90f 100644 --- a/man/estimationPowerServer.Rd +++ b/man/cohortMethodPowerServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-power.R -\name{estimationPowerServer} -\alias{estimationPowerServer} +% Please edit documentation in R/cohort-method-power.R +\name{cohortMethodPowerServer} +\alias{cohortMethodPowerServer} \title{The module server for rendering the PLE power analysis results} \usage{ -estimationPowerServer( +cohortMethodPowerServer( id, selectedRow, inputParams, diff --git a/man/estimationPowerViewer.Rd b/man/cohortMethodPowerViewer.Rd similarity index 55% rename from man/estimationPowerViewer.Rd rename to man/cohortMethodPowerViewer.Rd index f8a0dde2..cea46b77 100644 --- a/man/estimationPowerViewer.Rd +++ b/man/cohortMethodPowerViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-power.R -\name{estimationPowerViewer} -\alias{estimationPowerViewer} +% Please edit documentation in R/cohort-method-power.R +\name{cohortMethodPowerViewer} +\alias{cohortMethodPowerViewer} \title{The module viewer for rendering the PLE power analysis} \usage{ -estimationPowerViewer(id) +cohortMethodPowerViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation power calculation results +The user interface to the cohort method power calculation results } \description{ The module viewer for rendering the PLE power analysis diff --git a/man/estimationPropensityModelServer.Rd b/man/cohortMethodPropensityModelServer.Rd similarity index 78% rename from man/estimationPropensityModelServer.Rd rename to man/cohortMethodPropensityModelServer.Rd index 0e1e0726..1e5e2612 100644 --- a/man/estimationPropensityModelServer.Rd +++ b/man/cohortMethodPropensityModelServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-propensityModel.R -\name{estimationPropensityModelServer} -\alias{estimationPropensityModelServer} +% Please edit documentation in R/cohort-method-propensityModel.R +\name{cohortMethodPropensityModelServer} +\alias{cohortMethodPropensityModelServer} \title{The module server for rendering the propensity score model} \usage{ -estimationPropensityModelServer( +cohortMethodPropensityModelServer( id, selectedRow, inputParams, diff --git a/man/estimationPropensityModelViewer.Rd b/man/cohortMethodPropensityModelViewer.Rd similarity index 54% rename from man/estimationPropensityModelViewer.Rd rename to man/cohortMethodPropensityModelViewer.Rd index 997df210..d05b543a 100644 --- a/man/estimationPropensityModelViewer.Rd +++ b/man/cohortMethodPropensityModelViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-propensityModel.R -\name{estimationPropensityModelViewer} -\alias{estimationPropensityModelViewer} +% Please edit documentation in R/cohort-method-propensityModel.R +\name{cohortMethodPropensityModelViewer} +\alias{cohortMethodPropensityModelViewer} \title{The module viewer for rendering the PLE propensity score model covariates/coefficients} \usage{ -estimationPropensityModelViewer(id) +cohortMethodPropensityModelViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation propensity score model covariates/coefficients +The user interface to the cohort method propensity score model covariates/coefficients } \description{ The module viewer for rendering the PLE propensity score model covariates/coefficients diff --git a/man/estimationPropensityScoreDistServer.Rd b/man/cohortMethodPropensityScoreDistServer.Rd similarity index 80% rename from man/estimationPropensityScoreDistServer.Rd rename to man/cohortMethodPropensityScoreDistServer.Rd index 4a155ebc..01f0d394 100644 --- a/man/estimationPropensityScoreDistServer.Rd +++ b/man/cohortMethodPropensityScoreDistServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-propensityScoreDistribution.R -\name{estimationPropensityScoreDistServer} -\alias{estimationPropensityScoreDistServer} +% Please edit documentation in R/cohort-method-propensityScoreDistribution.R +\name{cohortMethodPropensityScoreDistServer} +\alias{cohortMethodPropensityScoreDistServer} \title{The module server for rendering a PLE propensity score distribution} \usage{ -estimationPropensityScoreDistServer( +cohortMethodPropensityScoreDistServer( id, selectedRow, inputParams, diff --git a/man/estimationPropensityScoreDistViewer.Rd b/man/cohortMethodPropensityScoreDistViewer.Rd similarity index 50% rename from man/estimationPropensityScoreDistViewer.Rd rename to man/cohortMethodPropensityScoreDistViewer.Rd index 69cd86fe..ba8e780a 100644 --- a/man/estimationPropensityScoreDistViewer.Rd +++ b/man/cohortMethodPropensityScoreDistViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-propensityScoreDistribution.R -\name{estimationPropensityScoreDistViewer} -\alias{estimationPropensityScoreDistViewer} +% Please edit documentation in R/cohort-method-propensityScoreDistribution.R +\name{cohortMethodPropensityScoreDistViewer} +\alias{cohortMethodPropensityScoreDistViewer} \title{The module viewer for rendering the propensity score distribution} \usage{ -estimationPropensityScoreDistViewer(id) +cohortMethodPropensityScoreDistViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation propensity score distribution +The user interface to the cohort method propensity score distribution } \description{ The module viewer for rendering the propensity score distribution diff --git a/man/estimationResultsTableServer.Rd b/man/cohortMethodResultsTableServer.Rd similarity index 80% rename from man/estimationResultsTableServer.Rd rename to man/cohortMethodResultsTableServer.Rd index 3278644c..a8e0f421 100644 --- a/man/estimationResultsTableServer.Rd +++ b/man/cohortMethodResultsTableServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-resultsTable.R -\name{estimationResultsTableServer} -\alias{estimationResultsTableServer} +% Please edit documentation in R/cohort-method-resultsTable.R +\name{cohortMethodResultsTableServer} +\alias{cohortMethodResultsTableServer} \title{The module server for rendering the PLE results per current selections} \usage{ -estimationResultsTableServer( +cohortMethodResultsTableServer( id, connectionHandler, inputParams, diff --git a/man/estimationResultsTableViewer.Rd b/man/cohortMethodResultsTableViewer.Rd similarity index 63% rename from man/estimationResultsTableViewer.Rd rename to man/cohortMethodResultsTableViewer.Rd index a8e6123a..b39e34c9 100644 --- a/man/estimationResultsTableViewer.Rd +++ b/man/cohortMethodResultsTableViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-resultsTable.R -\name{estimationResultsTableViewer} -\alias{estimationResultsTableViewer} +% Please edit documentation in R/cohort-method-resultsTable.R +\name{cohortMethodResultsTableViewer} +\alias{cohortMethodResultsTableViewer} \title{The module viewer for rendering the PLE main results} \usage{ -estimationResultsTableViewer(id) +cohortMethodResultsTableViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/estimationServer.Rd b/man/cohortMethodServer.Rd similarity index 55% rename from man/estimationServer.Rd rename to man/cohortMethodServer.Rd index 0c89841b..83f734dd 100644 --- a/man/estimationServer.Rd +++ b/man/cohortMethodServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-main.R -\name{estimationServer} -\alias{estimationServer} -\title{The module server for the main estimation module} +% Please edit documentation in R/cohort-method-main.R +\name{cohortMethodServer} +\alias{cohortMethodServer} +\title{The module server for the main cohort method module} \usage{ -estimationServer(id, connectionHandler, resultDatabaseSettings) +cohortMethodServer(id, connectionHandler, resultDatabaseSettings) } \arguments{ \item{id}{the unique reference id for the module} @@ -17,5 +17,5 @@ estimationServer(id, connectionHandler, resultDatabaseSettings) the PLE results viewer main module server } \description{ -The module server for the main estimation module +The module server for the main cohort method module } diff --git a/man/estimationSubgroupsServer.Rd b/man/cohortMethodSubgroupsServer.Rd similarity index 80% rename from man/estimationSubgroupsServer.Rd rename to man/cohortMethodSubgroupsServer.Rd index 9dcd833c..7dfd6e81 100644 --- a/man/estimationSubgroupsServer.Rd +++ b/man/cohortMethodSubgroupsServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-subgroups.R -\name{estimationSubgroupsServer} -\alias{estimationSubgroupsServer} +% Please edit documentation in R/cohort-method-subgroups.R +\name{cohortMethodSubgroupsServer} +\alias{cohortMethodSubgroupsServer} \title{The module server for rendering the subgroup results} \usage{ -estimationSubgroupsServer( +cohortMethodSubgroupsServer( id, selectedRow, inputParams, diff --git a/man/estimationSubgroupsViewer.Rd b/man/cohortMethodSubgroupsViewer.Rd similarity index 54% rename from man/estimationSubgroupsViewer.Rd rename to man/cohortMethodSubgroupsViewer.Rd index f746061a..1eeb52f5 100644 --- a/man/estimationSubgroupsViewer.Rd +++ b/man/cohortMethodSubgroupsViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-subgroups.R -\name{estimationSubgroupsViewer} -\alias{estimationSubgroupsViewer} +% Please edit documentation in R/cohort-method-subgroups.R +\name{cohortMethodSubgroupsViewer} +\alias{cohortMethodSubgroupsViewer} \title{The module viewer for rendering the PLE subgroup results} \usage{ -estimationSubgroupsViewer(id) +cohortMethodSubgroupsViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation subgroup results module +The user interface to the cohort method subgroup results module } \description{ The module viewer for rendering the PLE subgroup results diff --git a/man/estimationSystematicErrorServer.Rd b/man/cohortMethodSystematicErrorServer.Rd similarity index 80% rename from man/estimationSystematicErrorServer.Rd rename to man/cohortMethodSystematicErrorServer.Rd index bb265bff..29b136e8 100644 --- a/man/estimationSystematicErrorServer.Rd +++ b/man/cohortMethodSystematicErrorServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-systematicError.R -\name{estimationSystematicErrorServer} -\alias{estimationSystematicErrorServer} +% Please edit documentation in R/cohort-method-systematicError.R +\name{cohortMethodSystematicErrorServer} +\alias{cohortMethodSystematicErrorServer} \title{The module server for rendering the systematic error objects} \usage{ -estimationSystematicErrorServer( +cohortMethodSystematicErrorServer( id, selectedRow, inputParams, diff --git a/man/estimationSystematicErrorViewer.Rd b/man/cohortMethodSystematicErrorViewer.Rd similarity index 53% rename from man/estimationSystematicErrorViewer.Rd rename to man/cohortMethodSystematicErrorViewer.Rd index b2f9bbd6..c4dea713 100644 --- a/man/estimationSystematicErrorViewer.Rd +++ b/man/cohortMethodSystematicErrorViewer.Rd @@ -1,16 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-systematicError.R -\name{estimationSystematicErrorViewer} -\alias{estimationSystematicErrorViewer} +% Please edit documentation in R/cohort-method-systematicError.R +\name{cohortMethodSystematicErrorViewer} +\alias{cohortMethodSystematicErrorViewer} \title{The module viewer for rendering the PLE systematic error objects} \usage{ -estimationSystematicErrorViewer(id) +cohortMethodSystematicErrorViewer(id) } \arguments{ \item{id}{the unique reference id for the module} } \value{ -The user interface to the estimation systematic error module +The user interface to the cohort method systematic error module } \description{ The module viewer for rendering the PLE systematic error objects diff --git a/man/cohortMethodViewer.Rd b/man/cohortMethodViewer.Rd new file mode 100644 index 00000000..ae650d1a --- /dev/null +++ b/man/cohortMethodViewer.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cohort-method-main.R +\name{cohortMethodViewer} +\alias{cohortMethodViewer} +\title{The viewer of the main cohort method module} +\usage{ +cohortMethodViewer(id) +} +\arguments{ +\item{id}{the unique reference id for the module} +} +\value{ +The user interface to the cohort method results viewer +} +\description{ +The viewer of the main cohort method module +} diff --git a/man/descriptionHelperFile.Rd b/man/descriptionHelperFile.Rd deleted file mode 100644 index fb0ab4e2..00000000 --- a/man/descriptionHelperFile.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-main.R -\name{descriptionHelperFile} -\alias{descriptionHelperFile} -\title{The location of the description module helper file} -\usage{ -descriptionHelperFile() -} -\value{ -string location of the description helper file -} -\description{ -The location of the description module helper file -} -\details{ -Returns the location of the description helper file -} diff --git a/man/descriptionServer.Rd b/man/descriptionServer.Rd deleted file mode 100644 index 6a638949..00000000 --- a/man/descriptionServer.Rd +++ /dev/null @@ -1,28 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-main.R -\name{descriptionServer} -\alias{descriptionServer} -\title{The module server for exploring description studies} -\usage{ -descriptionServer( - id, - connectionHandler, - resultDatabaseSettings = list(port = 1) -) -} -\arguments{ -\item{id}{the unique reference id for the module} - -\item{connectionHandler}{a connection to the database with the results} - -\item{resultDatabaseSettings}{a list containing the description result schema, dbms, tablePrefix, databaseTable and cohortTablePrefix} -} -\value{ -The server for the description module -} -\description{ -The module server for exploring description studies -} -\details{ -The user specifies the id for the module -} diff --git a/man/descriptionViewer.Rd b/man/descriptionViewer.Rd deleted file mode 100644 index 51938b36..00000000 --- a/man/descriptionViewer.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/description-main.R -\name{descriptionViewer} -\alias{descriptionViewer} -\title{The module viewer for exploring description studies} -\usage{ -descriptionViewer(id = 1) -} -\arguments{ -\item{id}{the unique reference id for the module} -} -\value{ -The user interface to the description viewer module -} -\description{ -The module viewer for exploring description studies -} -\details{ -The user specifies the id for the module -} diff --git a/man/estimationHelperFile.Rd b/man/estimationHelperFile.Rd deleted file mode 100644 index e87ed22b..00000000 --- a/man/estimationHelperFile.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-main.R -\name{estimationHelperFile} -\alias{estimationHelperFile} -\title{The location of the estimation module helper file} -\usage{ -estimationHelperFile() -} -\value{ -string location of the estimation helper file -} -\description{ -The location of the estimation module helper file -} -\details{ -Returns the location of the estimation helper file -} diff --git a/man/estimationPopulationCharacteristicsViewer.Rd b/man/estimationPopulationCharacteristicsViewer.Rd deleted file mode 100644 index 996635a1..00000000 --- a/man/estimationPopulationCharacteristicsViewer.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-populationCharacteristics.R -\name{estimationPopulationCharacteristicsViewer} -\alias{estimationPopulationCharacteristicsViewer} -\title{The module viewer for rendering the PLE population characteristics} -\usage{ -estimationPopulationCharacteristicsViewer(id) -} -\arguments{ -\item{id}{the unique reference id for the module} -} -\value{ -The user interface to the estimation population characteristics objects -} -\description{ -The module viewer for rendering the PLE population characteristics -} diff --git a/man/estimationViewer.Rd b/man/estimationViewer.Rd deleted file mode 100644 index 4033bbc3..00000000 --- a/man/estimationViewer.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/estimation-main.R -\name{estimationViewer} -\alias{estimationViewer} -\title{The viewer of the main estimation module} -\usage{ -estimationViewer(id) -} -\arguments{ -\item{id}{the unique reference id for the module} -} -\value{ -The user interface to the estimation results viewer -} -\description{ -The viewer of the main estimation module -} diff --git a/tests/resources/descDatabase/databaseFile.sqlite b/tests/resources/characterizationDatabase/databaseFile.sqlite similarity index 100% rename from tests/resources/descDatabase/databaseFile.sqlite rename to tests/resources/characterizationDatabase/databaseFile.sqlite diff --git a/tests/resources/estDatabase/databaseFile.sqlite b/tests/resources/cmDatabase/databaseFile.sqlite similarity index 100% rename from tests/resources/estDatabase/databaseFile.sqlite rename to tests/resources/cmDatabase/databaseFile.sqlite diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 7727c8ad..79616c99 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -46,16 +46,19 @@ resultDatabaseSettingsPlp <- list( -# =========== Desc START -serverDesc <- "../resources/descDatabase/databaseFile.sqlite" -connectionDetailsDesc <- DatabaseConnector::createConnectionDetails( +# =========== characterization START +serverCharacterization <- "../resources/characterizationDatabase/databaseFile.sqlite" +connectionDetailsCharacterization <- DatabaseConnector::createConnectionDetails( dbms = 'sqlite', - server = serverDesc + server = serverCharacterization ) -connectionHandlerDesc <- ResultModelManager::ConnectionHandler$new(connectionDetailsDesc, loadConnection = FALSE) +connectionHandlerCharacterization <- ResultModelManager::ConnectionHandler$new( + connectionDetailsCharacterization, + loadConnection = FALSE + ) -resultDatabaseSettingsDesc <- list( +resultDatabaseSettingsCharacterization <- list( dbms = 'sqlite', # should this be removed - can use connection tablePrefix = 'c_', cohortTablePrefix = 'cg_', @@ -67,18 +70,21 @@ resultDatabaseSettingsDesc <- list( ) -# =========== Desc START +# =========== Characterization END -# =========== Estimation START -connectionDetailsEst <- DatabaseConnector::createConnectionDetails( +# =========== Cohort Method START +connectionDetailsCm <- DatabaseConnector::createConnectionDetails( dbms = 'sqlite', - server = "../resources/estDatabase/databaseFile.sqlite" + server = "../resources/cmDatabase/databaseFile.sqlite" ) -connectionHandlerEst <- ResultModelManager::ConnectionHandler$new(connectionDetailsEst, loadConnection = FALSE) +connectionHandlerCm <- ResultModelManager::ConnectionHandler$new( + connectionDetailsCm, + loadConnection = FALSE + ) -resultDatabaseSettingsEst <- list( +resultDatabaseSettingsCm <- list( dbms = 'sqlite', tablePrefix = 'cm_', cohortTablePrefix = 'cg_', @@ -87,7 +93,7 @@ resultDatabaseSettingsEst <- list( tempEmulationSchema = NULL ) -# =========== Estimation END +# =========== Cohort Method END # =========== Data diag START @@ -187,9 +193,9 @@ withr::defer({ options("shiny-test-env-enabled" = FALSE) connectionHandlerCG$finalize() connectionHandlerPlp$finalize() - connectionHandlerDesc$finalize() + connectionHandlerCharacterization$finalize() connectionHandlerDataDiag$finalize() - connectionHandlerEst$finalize() + connectionHandlerCm$finalize() connectionHandlerCohortDiag$finalize() connectionHandlerSccs$finalize() connectionHandlerES$finalize() diff --git a/tests/testthat/test-description-aggregate-features.R b/tests/testthat/test-characterization-aggregate-features.R similarity index 67% rename from tests/testthat/test-description-aggregate-features.R rename to tests/testthat/test-characterization-aggregate-features.R index 0aa5dba4..d80c1b7d 100644 --- a/tests/testthat/test-description-aggregate-features.R +++ b/tests/testthat/test-characterization-aggregate-features.R @@ -1,14 +1,14 @@ -context("description-aggregateFeatures") +context("characterization-aggregateFeatures") shiny::testServer( - app = descriptionAggregateFeaturesServer, + app = characterizationAggregateFeaturesServer, args = list( - connectionHandler = connectionHandlerDesc , - schema = resultDatabaseSettingsDesc$schema, + connectionHandler = connectionHandlerCharacterization , + schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - tablePrefix = resultDatabaseSettingsDesc$tablePrefix, - cohortTablePrefix = resultDatabaseSettingsDesc$cohortTablePrefix, - databaseTable = resultDatabaseSettingsDesc$databaseTable + tablePrefix = resultDatabaseSettingsCharacterization$tablePrefix, + cohortTablePrefix = resultDatabaseSettingsCharacterization$cohortTablePrefix, + databaseTable = resultDatabaseSettingsCharacterization$databaseTable ), expr = { diff --git a/tests/testthat/test-description-cohorts.R b/tests/testthat/test-characterization-cohorts.R similarity index 64% rename from tests/testthat/test-description-cohorts.R rename to tests/testthat/test-characterization-cohorts.R index 96fccc7f..05ee64cb 100644 --- a/tests/testthat/test-description-cohorts.R +++ b/tests/testthat/test-characterization-cohorts.R @@ -1,14 +1,14 @@ -context("description-cohorts") +context("characterization-cohorts") shiny::testServer( - app = descriptionTableServer, + app = characterizationTableServer, args = list( - connectionHandler = connectionHandlerDesc , - schema = resultDatabaseSettingsDesc$schema, + connectionHandler = connectionHandlerCharacterization, + schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - tablePrefix = resultDatabaseSettingsDesc$tablePrefix, - cohortTablePrefix = resultDatabaseSettingsDesc$cohortTablePrefix, - databaseTable = resultDatabaseSettingsDesc$databaseTable + tablePrefix = resultDatabaseSettingsCharacterization$tablePrefix, + cohortTablePrefix = resultDatabaseSettingsCharacterization$cohortTablePrefix, + databaseTable = resultDatabaseSettingsCharacterization$databaseTable ), expr = { diff --git a/tests/testthat/test-description-dechallengeRechallenge.R b/tests/testthat/test-characterization-dechallengeRechallenge.R similarity index 65% rename from tests/testthat/test-description-dechallengeRechallenge.R rename to tests/testthat/test-characterization-dechallengeRechallenge.R index 16610d09..3808662d 100644 --- a/tests/testthat/test-description-dechallengeRechallenge.R +++ b/tests/testthat/test-characterization-dechallengeRechallenge.R @@ -1,14 +1,14 @@ -context("description-DechallengeRechallenge") +context("characterization-DechallengeRechallenge") shiny::testServer( - app = descriptionDechallengeRechallengeServer, + app = characterizationDechallengeRechallengeServer, args = list( - connectionHandler = connectionHandlerDesc , - schema = resultDatabaseSettingsDesc$schema, + connectionHandler = connectionHandlerCharacterization, + schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - tablePrefix = resultDatabaseSettingsDesc$tablePrefix, - cohortTablePrefix = resultDatabaseSettingsDesc$cohortTablePrefix, - databaseTable = resultDatabaseSettingsDesc$databaseTable + tablePrefix = resultDatabaseSettingsCharacterization$tablePrefix, + cohortTablePrefix = resultDatabaseSettingsCharacterization$cohortTablePrefix, + databaseTable = resultDatabaseSettingsCharacterization$databaseTable ), expr = { diff --git a/tests/testthat/test-description-incidence.R b/tests/testthat/test-characterization-incidence.R similarity index 57% rename from tests/testthat/test-description-incidence.R rename to tests/testthat/test-characterization-incidence.R index a8fe7764..907caa34 100644 --- a/tests/testthat/test-description-incidence.R +++ b/tests/testthat/test-characterization-incidence.R @@ -1,13 +1,13 @@ -context("description-incidence") +context("characterization-incidence") shiny::testServer( - app = descriptionIncidenceServer, + app = characterizationIncidenceServer, args = list( - connectionHandler = connectionHandlerDesc , - schema = resultDatabaseSettingsDesc$schema, + connectionHandler = connectionHandlerCharacterization, + schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - incidenceTablePrefix = resultDatabaseSettingsDesc$incidenceTablePrefix, - databaseTable = resultDatabaseSettingsDesc$databaseTable + incidenceTablePrefix = resultDatabaseSettingsCharacterization$incidenceTablePrefix, + databaseTable = resultDatabaseSettingsCharacterization$databaseTable ), expr = { @@ -21,5 +21,4 @@ shiny::testServer( session$setInputs(outcomeId = 3) session$setInputs(generate = T) - }) diff --git a/tests/testthat/test-description-main.R b/tests/testthat/test-characterization-main.R similarity index 52% rename from tests/testthat/test-description-main.R rename to tests/testthat/test-characterization-main.R index 1f8c49d1..f00553e1 100644 --- a/tests/testthat/test-description-main.R +++ b/tests/testthat/test-characterization-main.R @@ -1,10 +1,10 @@ -context("description-main") +context("characterization-main") shiny::testServer( - app = descriptionServer, + app = characterizationServer, args = list( - connectionHandler = connectionHandlerDesc , - resultDatabaseSettings = resultDatabaseSettingsDesc + connectionHandler = connectionHandlerCharacterization, + resultDatabaseSettings = resultDatabaseSettingsCharacterization ), expr = { @@ -16,8 +16,8 @@ shiny::testServer( }) -test_that("Test description ui", { +test_that("Test characterization ui", { # Test ui - ui <- descriptionViewer() + ui <- characterizationViewer() checkmate::expect_list(ui) }) diff --git a/tests/testthat/test-description-timeToEvent.R b/tests/testthat/test-characterization-timeToEvent.R similarity index 55% rename from tests/testthat/test-description-timeToEvent.R rename to tests/testthat/test-characterization-timeToEvent.R index b36f852e..e4f98a88 100644 --- a/tests/testthat/test-description-timeToEvent.R +++ b/tests/testthat/test-characterization-timeToEvent.R @@ -1,14 +1,14 @@ -context("description-TimeToEvent") +context("characterization-TimeToEvent") shiny::testServer( - app = descriptionTimeToEventServer, + app = characterizationTimeToEventServer, args = list( - connectionHandler = connectionHandlerDesc, - schema = resultDatabaseSettingsDesc$schema, + connectionHandler = connectionHandlerCharacterization, + schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - tablePrefix = resultDatabaseSettingsDesc$tablePrefix, - cohortTablePrefix = resultDatabaseSettingsDesc$cohortTablePrefix, - databaseTable = resultDatabaseSettingsDesc$databaseTable + tablePrefix = resultDatabaseSettingsCharacterization$tablePrefix, + cohortTablePrefix = resultDatabaseSettingsCharacterization$cohortTablePrefix, + databaseTable = resultDatabaseSettingsCharacterization$databaseTable ), expr = { diff --git a/tests/testthat/test-estimation-CovariateBalance.R b/tests/testthat/test-cohort-method-CovariateBalance.R similarity index 88% rename from tests/testthat/test-estimation-CovariateBalance.R rename to tests/testthat/test-cohort-method-CovariateBalance.R index 30f7f00b..83949381 100644 --- a/tests/testthat/test-estimation-CovariateBalance.R +++ b/tests/testthat/test-cohort-method-CovariateBalance.R @@ -1,7 +1,7 @@ -context("estimation-CovariateBalance") +context("cohort-method-CovariateBalance") shiny::testServer( - app = estimationCovariateBalanceServer, + app = cohortMethodCovariateBalanceServer, args = list( selectedRow = shiny::reactiveVal(NULL), inputParams = shiny::reactiveVal(list( @@ -9,7 +9,7 @@ shiny::testServer( comparator = 2, outcome = 3 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', metaAnalysisDbIds = '1' @@ -39,6 +39,6 @@ shiny::testServer( testthat::expect_true(!is.null(output$balanceSummaryPlotCaption)) # check textsearch - textSearchEstimation('heart') + textSearchCohortMethod('heart') }) diff --git a/tests/testthat/test-estimation-DiagnosticsSummary.R b/tests/testthat/test-cohort-method-DiagnosticsSummary.R similarity index 70% rename from tests/testthat/test-estimation-DiagnosticsSummary.R rename to tests/testthat/test-cohort-method-DiagnosticsSummary.R index c8c20113..00f150c5 100644 --- a/tests/testthat/test-estimation-DiagnosticsSummary.R +++ b/tests/testthat/test-cohort-method-DiagnosticsSummary.R @@ -1,12 +1,12 @@ -context("estimation-DiagnosticsSummary") +context("cohort-method-DiagnosticsSummary") shiny::testServer( - app = estimationDiagnosticsSummaryServer, + app = cohortMethodDiagnosticsSummaryServer, args = list( - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix + cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix ), expr = { @@ -53,10 +53,10 @@ testthat::expect_true(ncol(val) == 4) test_that("getCmDiagCohorts", { cohortIds <- getCmDiagCohorts( - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, type = 'target' ) @@ -66,7 +66,7 @@ testthat::expect_true(length(cohortIds) > 0) test_that("getCmDiagAnalyses", { analysisIds <- getCmDiagAnalyses( - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_' ) @@ -77,40 +77,40 @@ test_that("getCmDiagAnalyses", { test_that("getCmDiagAnalyses", { analysisIds <- getCmDiagAnalyses( - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_' ) cohortIds <- getCmDiagCohorts( - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, type = 'target' ) outcomeIds <- getCmDiagCohorts( - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, type = 'outcome' ) comparatorIds <- getCmDiagCohorts( - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, type = 'comparator' ) diag <- getCmDiagnosticsData( - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, databaseTable = 'database_meta_data', targetIds = cohortIds, outcomeIds = outcomeIds, diff --git a/tests/testthat/test-estimation-ForestPlot.R b/tests/testthat/test-cohort-method-ForestPlot.R similarity index 68% rename from tests/testthat/test-estimation-ForestPlot.R rename to tests/testthat/test-cohort-method-ForestPlot.R index 6849a5b8..7dbd4f85 100644 --- a/tests/testthat/test-estimation-ForestPlot.R +++ b/tests/testthat/test-cohort-method-ForestPlot.R @@ -1,7 +1,7 @@ -context("estimation-ForestPlot") +context("cohort-method-ForestPlot") shiny::testServer( - app = estimationForestPlotServer, + app = cohortMethodForestPlotServer, args = list( selectedRow = shiny::reactiveVal(NULL), inputParams = shiny::reactiveVal(list( @@ -9,11 +9,11 @@ shiny::testServer( comparator = 2, outcome = 3 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', metaAnalysisDbIds = NULL, - databaseTable = resultDatabaseSettingsEst$databaseTable + databaseTable = resultDatabaseSettingsCm$databaseTable ), expr = { diff --git a/tests/testthat/test-estimation-KaplanMeier.R b/tests/testthat/test-cohort-method-KaplanMeier.R similarity index 71% rename from tests/testthat/test-estimation-KaplanMeier.R rename to tests/testthat/test-cohort-method-KaplanMeier.R index cfc8fceb..49e89249 100644 --- a/tests/testthat/test-estimation-KaplanMeier.R +++ b/tests/testthat/test-cohort-method-KaplanMeier.R @@ -1,7 +1,7 @@ -context("estimation-KaplanMeier") +context("cohort-method-KaplanMeier") shiny::testServer( - app = estimationKaplanMeierServer, + app = cohortMethodKaplanMeierServer, args = list( selectedRow = shiny::reactiveVal(NULL), inputParams = shiny::reactiveVal(list( @@ -9,11 +9,11 @@ shiny::testServer( comparator = 2, outcome = 3 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, - databaseTable = resultDatabaseSettingsEst$databaseTable, + cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, + databaseTable = resultDatabaseSettingsCm$databaseTable, metaAnalysisDbIds = '1' ), expr = { diff --git a/tests/testthat/test-estimation-PopulationCharacteristics.R b/tests/testthat/test-cohort-method-PopulationCharacteristics.R similarity index 80% rename from tests/testthat/test-estimation-PopulationCharacteristics.R rename to tests/testthat/test-cohort-method-PopulationCharacteristics.R index 18711d1f..693b2b68 100644 --- a/tests/testthat/test-estimation-PopulationCharacteristics.R +++ b/tests/testthat/test-cohort-method-PopulationCharacteristics.R @@ -1,7 +1,7 @@ -context("estimation-PopulationCharacteristics") +context("cohort-method-PopulationCharacteristics") shiny::testServer( - app = estimationPopulationCharacteristicsServer, + app = cohortMethodPopulationCharacteristicsServer, args = list( selectedRow = shiny::reactiveVal(NULL), inputParams = shiny::reactiveVal(list( @@ -9,7 +9,7 @@ shiny::testServer( comparator = 2, outcome = 3 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_' #cohortTablePrefix = cohortTablePrefix, diff --git a/tests/testthat/test-estimation-Power.R b/tests/testthat/test-cohort-method-Power.R similarity index 90% rename from tests/testthat/test-estimation-Power.R rename to tests/testthat/test-cohort-method-Power.R index c9f8f394..40cf879f 100644 --- a/tests/testthat/test-estimation-Power.R +++ b/tests/testthat/test-cohort-method-Power.R @@ -1,7 +1,7 @@ -context("estimation-Power") +context("cohort-method-Power") shiny::testServer( - app = estimationPowerServer, + app = cohortMethodPowerServer, args = list( selectedRow = shiny::reactiveVal( data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, @@ -14,7 +14,7 @@ shiny::testServer( comparator = 2, outcome = 3 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', #cohortTablePrefix = cohortTablePrefix, diff --git a/tests/testthat/test-estimation-PropensityScoreDist.R b/tests/testthat/test-cohort-method-PropensityScoreDist.R similarity index 83% rename from tests/testthat/test-estimation-PropensityScoreDist.R rename to tests/testthat/test-cohort-method-PropensityScoreDist.R index 88491bd8..3db9e3a4 100644 --- a/tests/testthat/test-estimation-PropensityScoreDist.R +++ b/tests/testthat/test-cohort-method-PropensityScoreDist.R @@ -1,7 +1,7 @@ -context("estimation-PropensityScoreDist") +context("cohort-method-PropensityScoreDist") shiny::testServer( - app = estimationPropensityScoreDistServer, + app = cohortMethodPropensityScoreDistServer, args = list( selectedRow = shiny::reactiveVal(NULL #data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, @@ -14,10 +14,10 @@ shiny::testServer( comparator = 2, outcome = 3 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsEst$cohortTablePrefix, + cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, #databaseTable = databaseTable, metaAnalysisDbIds = NULL ), diff --git a/tests/testthat/test-estimation-ResultsTable.R b/tests/testthat/test-cohort-method-ResultsTable.R similarity index 86% rename from tests/testthat/test-estimation-ResultsTable.R rename to tests/testthat/test-cohort-method-ResultsTable.R index f5f8416f..c7c5d292 100644 --- a/tests/testthat/test-estimation-ResultsTable.R +++ b/tests/testthat/test-cohort-method-ResultsTable.R @@ -1,7 +1,7 @@ -context("estimation-ResultsTable") +context("cohort-method-ResultsTable") shiny::testServer( - app = estimationResultsTableServer, + app = cohortMethodResultsTableServer, args = list( #selectedRow = shiny::reactiveVal(NULL #data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, @@ -16,11 +16,11 @@ shiny::testServer( database = 1, analysis = 1 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', #cohortTablePrefix = cohortTablePrefix, - databaseTable = resultDatabaseSettingsEst$databaseTable + databaseTable = resultDatabaseSettingsCm$databaseTable #metaAnalysisDbIds = NULL ), expr = { diff --git a/tests/testthat/test-estimation-Subgroups.R b/tests/testthat/test-cohort-method-Subgroups.R similarity index 93% rename from tests/testthat/test-estimation-Subgroups.R rename to tests/testthat/test-cohort-method-Subgroups.R index 58991923..74a44997 100644 --- a/tests/testthat/test-estimation-Subgroups.R +++ b/tests/testthat/test-cohort-method-Subgroups.R @@ -1,10 +1,10 @@ -context("estimation-Subgroups") +context("cohort-method-Subgroups") # tests cannot be done due to getEstimationSubgroupResults() missing? if(F){ shiny::testServer( - app = estimationSubgroupsServer, + app = cohortMethodSubgroupsServer, args = list( selectedRow = shiny::reactiveVal(NULL #data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, @@ -19,7 +19,7 @@ shiny::testServer( database = 1, analysis = 1 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, exposureOfInterest = list(exposureId = c(1,2), exposureName = c(1,2)), outcomeOfInterest = list(outcomeId = 3, outcomeName = 3) #resultsSchema = 'main', diff --git a/tests/testthat/test-estimation-attrition.R b/tests/testthat/test-cohort-method-attrition.R similarity index 74% rename from tests/testthat/test-estimation-attrition.R rename to tests/testthat/test-cohort-method-attrition.R index f875b7ea..760bb09d 100644 --- a/tests/testthat/test-estimation-attrition.R +++ b/tests/testthat/test-cohort-method-attrition.R @@ -1,7 +1,7 @@ -context("estimation-attrition") +context("cohort-method-attrition") shiny::testServer( - app = estimationAttritionServer, + app = cohortMethodAttritionServer, args = list( selectedRow = shiny::reactiveVal(NULL), inputParams = shiny::reactiveVal(list( @@ -9,10 +9,10 @@ shiny::testServer( comparator = 2, outcome = 3 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', - databaseTable = resultDatabaseSettingsEst$databaseTable + databaseTable = resultDatabaseSettingsCm$databaseTable ), expr = { diff --git a/tests/testthat/test-estimation-main.R b/tests/testthat/test-cohort-method-main.R similarity index 77% rename from tests/testthat/test-estimation-main.R rename to tests/testthat/test-cohort-method-main.R index 954fa6e0..a9833a18 100644 --- a/tests/testthat/test-estimation-main.R +++ b/tests/testthat/test-cohort-method-main.R @@ -1,10 +1,10 @@ -context("estimation-main") +context("cohort-method-main") shiny::testServer( - app = estimationServer, + app = cohortMethodServer, args = list( - connectionHandler = connectionHandlerEst, - resultDatabaseSettings = resultDatabaseSettingsEst + connectionHandler = connectionHandlerCm, + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { @@ -27,6 +27,6 @@ shiny::testServer( test_that("Test estimation ui", { # Test ui - ui <- estimationViewer('test') + ui <- cohortMethodViewer('test') checkmate::expect_list(ui) }) \ No newline at end of file diff --git a/tests/testthat/test-estimation-propensityModel.R b/tests/testthat/test-cohort-method-propensityModel.R similarity index 87% rename from tests/testthat/test-estimation-propensityModel.R rename to tests/testthat/test-cohort-method-propensityModel.R index b98d46c3..c557e254 100644 --- a/tests/testthat/test-estimation-propensityModel.R +++ b/tests/testthat/test-cohort-method-propensityModel.R @@ -1,7 +1,7 @@ -context("estimation-propensityModel") +context("cohort-method-propensityModel") shiny::testServer( - app = estimationPropensityModelServer, + app = cohortMethodPropensityModelServer, args = list( selectedRow = shiny::reactiveVal( data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, @@ -14,7 +14,7 @@ shiny::testServer( comparator = 2, outcome = 3 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_' #cohortTablePrefix = cohortTablePrefix, diff --git a/tests/testthat/test-estimation-systematicError.R b/tests/testthat/test-cohort-method-systematicError.R similarity index 91% rename from tests/testthat/test-estimation-systematicError.R rename to tests/testthat/test-cohort-method-systematicError.R index bf111319..edf0e515 100644 --- a/tests/testthat/test-estimation-systematicError.R +++ b/tests/testthat/test-cohort-method-systematicError.R @@ -1,9 +1,9 @@ -context("estimation-systematicError") +context("cohort-method-systematicError") # issues with this modules! shiny::testServer( - app = estimationSystematicErrorServer, + app = cohortMethodSystematicErrorServer, args = list( selectedRow = shiny::reactiveVal(NULL #data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, @@ -18,7 +18,7 @@ shiny::testServer( database = 1, analysis = 1 )), - connectionHandler = connectionHandlerEst, + connectionHandler = connectionHandlerCm, resultsSchema = 'main', tablePrefix = 'cm_', #cohortTablePrefix = cohortTablePrefix, diff --git a/tests/testthat/test-helpers-estimationPlotsAndTables.R b/tests/testthat/test-helpers-cohort-methodPlotsAndTables.R similarity index 68% rename from tests/testthat/test-helpers-estimationPlotsAndTables.R rename to tests/testthat/test-helpers-cohort-methodPlotsAndTables.R index 79ba1a4b..c81e96cd 100644 --- a/tests/testthat/test-helpers-estimationPlotsAndTables.R +++ b/tests/testthat/test-helpers-cohort-methodPlotsAndTables.R @@ -1,9 +1,9 @@ -context('tests-helpers-estimationPlotsAndTables') +context('tests-helpers-cohort-methodPlotsAndTables') test_that("Subgroup stuff", { -subgroupRes <- getEstimationSubgroupResults( - connectionHandler = connectionHandlerEst, +subgroupRes <- getCohortMethodSubgroupResults( + connectionHandler = connectionHandlerCm, targetIds = 1, comparatorIds = 2, outcomeIds = 3, @@ -34,7 +34,7 @@ subgroupRes <- getEstimationSubgroupResults( testthat::expect_true(nrow(subgroupRes) > 0) -res <- prepareEstimationSubgroupTable(subgroupResults = subgroupRes, output = "latex") +res <- prepareCohortMethodSubgroupTable(subgroupResults = subgroupRes, output = "latex") testthat::expect_true(nrow(res) > 0) }) @@ -96,7 +96,7 @@ test_that("CovariateBalance stuff", { type = c("Before matching","After matching") ) - resP <- plotEstimationCovariateBalanceSummary( + resP <- plotCohortMethodCovariateBalanceSummary( balanceSummary = balanceSummary, threshold = 0, beforeLabel = "Before matching", @@ -107,37 +107,37 @@ test_that("CovariateBalance stuff", { }) -test_that("nonZeroEstimationHazardRatio", { +test_that("nonZeroCohortMethodHazardRatio", { testthat::expect_equal( - nonZeroEstimationHazardRatio(hrLower = 0.5, hrUpper = 0.5, terms = 'test'), + nonZeroCohortMethodHazardRatio(hrLower = 0.5, hrUpper = 0.5, terms = 'test'), 'test' ) testthat::expect_equal( - nonZeroEstimationHazardRatio(hrLower = 1.1, hrUpper = 1.1, terms = c('test','sec')), + nonZeroCohortMethodHazardRatio(hrLower = 1.1, hrUpper = 1.1, terms = c('test','sec')), 'sec' ) testthat::expect_equal( - nonZeroEstimationHazardRatio(hrLower = 0.9, hrUpper = 1.1, terms = c('test','sec','3')), + nonZeroCohortMethodHazardRatio(hrLower = 0.9, hrUpper = 1.1, terms = c('test','sec','3')), '3' ) }) -test_that("goodEstimationPropensityScore", { - testthat::expect_equal(goodEstimationPropensityScore(0.5), F) - testthat::expect_equal(goodEstimationPropensityScore(1), F) - testthat::expect_equal(goodEstimationPropensityScore(1.01), T) +test_that("goodCohortMethodPropensityScore", { + testthat::expect_equal(goodCohortMethodPropensityScore(0.5), F) + testthat::expect_equal(goodCohortMethodPropensityScore(1), F) + testthat::expect_equal(goodCohortMethodPropensityScore(1.01), T) }) -test_that("goodEstimationSystematicBias", { - testthat::expect_equal(goodEstimationSystematicBias(0.5), F) - testthat::expect_equal(goodEstimationSystematicBias(1), F) - testthat::expect_equal(goodEstimationSystematicBias(1.01), T) +test_that("goodCohortMethodSystematicBias", { + testthat::expect_equal(goodCohortMethodSystematicBias(0.5), F) + testthat::expect_equal(goodCohortMethodSystematicBias(1), F) + testthat::expect_equal(goodCohortMethodSystematicBias(1.01), T) }) -test_that("plotEstimationForest", { +test_that("plotCohortMethodForest", { results <- data.frame( databaseId = 1:10, @@ -150,12 +150,12 @@ test_that("plotEstimationForest", { calibratedCi95Ub = runif(10), i2 = runif(10) ) -resP <- plotEstimationForest(results, limits = c(0.1, 10), metaAnalysisDbIds = 2) +resP <- plotCohortMethodForest(results, limits = c(0.1, 10), metaAnalysisDbIds = 2) testthat::expect_true(inherits(resP, 'gtable')) }) -test_that("plotEstimationScatter", { +test_that("plotCohortMethodScatter", { controlResults <- data.frame( @@ -172,19 +172,19 @@ test_that("plotEstimationScatter", { trueRr = rep(1,10) ) - resP <- plotEstimationScatter(controlResults) + resP <- plotCohortMethodScatter(controlResults) testthat::expect_true(inherits(resP, 'ggplot')) }) ## Functions remaining to add tests: -# getEstimationTcoChoice -# getEstimationTargetChoices -# getEstimationComparatorChoices -# getEstimationOutcomeChoices -# getEstimationDatabaseChoices +# getCohortMethodTcoChoice +# getCohortMethodTargetChoices +# getCohortMethodComparatorChoices +# getCohortMethodOutcomeChoices +# getCohortMethodDatabaseChoices # getCmAnalysisOptions -# getAllEstimationResults -# getEstimationControlResults -# getEstimationStudyPeriod -# getEstimationNegativeControlEstimates +# getAllCohortMethodResults +# getCohortMethodControlResults +# getCohortMethodStudyPeriod +# getCohortMethodNegativeControlEstimates diff --git a/tests/testthat/test-prediction-settings.R b/tests/testthat/test-prediction-settings.R index 5b81b471..71553377 100644 --- a/tests/testthat/test-prediction-settings.R +++ b/tests/testthat/test-prediction-settings.R @@ -14,9 +14,8 @@ shiny::testServer( expr = { modelDesignId(1) - session$setInputs(showAttrition = T) - expect_true(!is.null(output$attrition)) session$setInputs(showCohort = T) + expect_true(!is.null(output$cohort)) session$setInputs(showOutcome = T) session$setInputs(showRestrictPlpData = T) session$setInputs(showPopulation = T) From c4f0a58b0ae0927a97f0f822af684450c2d256a3 Mon Sep 17 00:00:00 2001 From: jreps Date: Sat, 15 Jul 2023 22:11:34 -0400 Subject: [PATCH 14/20] standardizing modules - updating modules so they all use the same inputs in resultDatabaseSettings (this can now be shared across modules) - updating names to match analysis package names --- NAMESPACE | 50 +-- R/characterization-aggregateFeatures.R | 98 +++-- R/characterization-cohorts.R | 62 ++-- R/characterization-dechallengeRechallenge.R | 61 ++-- R/characterization-incidence.R | 36 +- R/characterization-main.R | 32 +- R/characterization-timeToEvent.R | 48 +-- R/cohort-diagnostics-characterization.R | 24 +- R/cohort-diagnostics-cohort-overlap.R | 10 +- ...hort-diagnostics-compareCharacterization.R | 8 +- R/cohort-diagnostics-conceptsInDataSource.R | 10 +- R/cohort-diagnostics-counts.R | 4 +- R/cohort-diagnostics-databaseInformation.R | 16 +- R/cohort-diagnostics-definition.R | 30 +- R/cohort-diagnostics-incidenceRates.R | 26 +- R/cohort-diagnostics-inclusionRules.R | 2 +- R/cohort-diagnostics-indexEventBreakdown.R | 6 +- R/cohort-diagnostics-main-ui.R | 2 +- R/cohort-diagnostics-main.R | 157 ++++---- R/cohort-diagnostics-orphanConcepts.R | 8 +- R/cohort-diagnostics-shared.R | 60 +-- R/cohort-diagnostics-timeDistributions.R | 2 +- R/cohort-diagnostics-visitContext.R | 6 +- R/cohort-method-attrition.R | 30 +- R/cohort-method-covariateBalance.R | 88 +++-- R/cohort-method-diagnosticsSummary.R | 82 ++--- R/cohort-method-forestPlot.R | 24 +- R/cohort-method-kaplainMeier.R | 58 +-- R/cohort-method-main.R | 178 ++++----- R/cohort-method-populationCharacteristics.R | 20 +- R/cohort-method-power.R | 73 ++-- R/cohort-method-propensityModel.R | 26 +- R/cohort-method-propensityScoreDistribution.R | 59 ++- R/cohort-method-resultsTable.R | 26 +- R/cohort-method-subgroups.R | 9 +- R/cohort-method-systematicError.R | 29 +- R/cohortgenerator-main.R | 31 +- R/data-diagnostic-drill.R | 59 ++- R/data-diagnostic-main.R | 6 +- R/data-diagnostic-summary.R | 30 +- R/evidence-synth-main.R | 247 ++++++------- R/helper-getPredictionProtocol.R | 13 +- R/helpers-cohort-methodDataPulls.R | 342 +++++++++++------- R/helpers-cohort-methodPlotsAndTables.R | 11 +- R/helpers-cohortGeneratorDataPulls.R | 85 ++--- R/helpers-getHelp.R | 2 +- R/helpers-getPredictionResult.R | 11 +- R/helpers-sccsDataPulls.R | 154 ++++---- ...=> patient-level-prediction-calibration.R} | 24 +- ...tient-level-prediction-covariateSummary.R} | 62 ++-- ...ff.R => patient-level-prediction-cutoff.R} | 16 +- ... patient-level-prediction-designSummary.R} | 80 ++-- ...=> patient-level-prediction-diagnostics.R} | 96 +++-- ...patient-level-prediction-discrimination.R} | 28 +- ...main.R => patient-level-prediction-main.R} | 215 ++++------- ...> patient-level-prediction-modelSummary.R} | 94 ++--- ... => patient-level-prediction-netbenefit.R} | 16 +- ....R => patient-level-prediction-settings.R} | 126 +++---- ... => patient-level-prediction-validation.R} | 78 ++-- R/sccs-diagnosticsSummary.R | 50 +-- _pkgdown.yml | 132 +++---- .../export-main.Rmd | 0 .../main.Rmd | 0 .../model-design.Rmd | 0 .../plp-analysis.Rmd | 0 .../plp-diagnostic.Rmd | 0 .../plp-introduction.Rmd | 0 .../plp-metrics.Rmd | 0 .../plp-outcome.Rmd | 0 .../plp-output.Rmd | 0 .../plp-participants.Rmd | 0 .../plp-plots.Rmd | 0 .../plp-predictors.Rmd | 0 .../plp-results-external.rmd | 0 .../plp-results-internal.Rmd | 0 .../plp-results-template.Rmd | 0 .../plp-results.Rmd | 0 .../DataInfo.html | 0 .../Description.html | 0 .../Help.html | 0 .../Log.html | 0 .../Model.html | 0 .../Performance.html | 0 .../Settings.html | 0 .../Summary.html | 0 .../boxHelp.html | 0 .../calHelp.html | 0 .../demoHelp.html | 0 .../f1Help.html | 0 .../help-designSummary.html | 0 .../help-fullResults.html | 0 .../main-modelSummaryHelp.html | 0 .../patient-level-prediction.html} | 0 .../prcHelp.html | 0 .../predDistHelp.html | 0 .../prefDistHelp.html | 0 .../rocHelp.html | 0 ...characterizationAggregateFeaturesServer.Rd | 13 +- ...cterizationDechallengeRechallengeServer.Rd | 13 +- man/characterizationIncidenceServer.Rd | 10 +- man/characterizationServer.Rd | 2 +- man/characterizationTableServer.Rd | 13 +- man/characterizationTimeToEventServer.Rd | 13 +- man/cohortCountsModule.Rd | 2 +- man/cohortDefinitionsModule.Rd | 2 +- ...w.Rd => cohortDiagCharacterizationView.Rd} | 6 +- ...icsSever.Rd => cohortDiagnosticsServer.Rd} | 6 +- man/cohortMethodAttritionServer.Rd | 10 +- man/cohortMethodCovariateBalanceServer.Rd | 7 +- man/cohortMethodDiagnosticsSummaryServer.Rd | 13 +- man/cohortMethodForestPlotServer.Rd | 10 +- man/cohortMethodKaplanMeierServer.Rd | 13 +- ...rtMethodPopulationCharacteristicsServer.Rd | 7 +- man/cohortMethodPowerServer.Rd | 7 +- man/cohortMethodPropensityModelServer.Rd | 7 +- man/cohortMethodPropensityScoreDistServer.Rd | 10 +- man/cohortMethodResultsTableServer.Rd | 10 +- man/cohortMethodSystematicErrorServer.Rd | 9 +- man/createCdDatabaseDataSource.Rd | 16 +- man/dataDiagnosticDrillServer.Rd | 6 +- man/dataDiagnosticSummaryServer.Rd | 6 +- man/evidenceSynthesisServer.Rd | 2 +- ...atientLevelPredictionCalibrationServer.Rd} | 15 +- ...atientLevelPredictionCalibrationViewer.Rd} | 8 +- ...tLevelPredictionCovariateSummaryServer.Rd} | 15 +- ...tLevelPredictionCovariateSummaryViewer.Rd} | 8 +- ... => patientLevelPredictionCutoffServer.Rd} | 15 +- ... => patientLevelPredictionCutoffViewer.Rd} | 8 +- ...ientLevelPredictionDesignSummaryServer.Rd} | 18 +- ...ientLevelPredictionDesignSummaryViewer.Rd} | 8 +- ...atientLevelPredictionDiagnosticsServer.Rd} | 20 +- ...atientLevelPredictionDiagnosticsViewer.Rd} | 8 +- ...entLevelPredictionDiscriminationServer.Rd} | 15 +- ...entLevelPredictionDiscriminationViewer.Rd} | 8 +- ...Rd => patientLevelPredictionHelperFile.Rd} | 8 +- ...tientLevelPredictionModelSummaryServer.Rd} | 20 +- ...tientLevelPredictionModelSummaryViewer.Rd} | 8 +- ...r.Rd => patientLevelPredictionNbServer.Rd} | 15 +- ...r.Rd => patientLevelPredictionNbViewer.Rd} | 8 +- ...ver.Rd => patientLevelPredictionServer.Rd} | 8 +- ...> patientLevelPredictionSettingsServer.Rd} | 21 +- ...> patientLevelPredictionSettingsViewer.Rd} | 8 +- ...patientLevelPredictionValidationServer.Rd} | 18 +- ...patientLevelPredictionValidationViewer.Rd} | 8 +- ...wer.Rd => patientLevelPredictionViewer.Rd} | 8 +- tests/testthat/setup.R | 45 ++- ...test-characterization-aggregate-features.R | 9 +- .../testthat/test-characterization-cohorts.R | 7 +- ...-characterization-dechallengeRechallenge.R | 5 +- .../test-characterization-incidence.R | 4 +- .../test-characterization-timeToEvent.R | 5 +- ...test-cohort-diagnostics-characterization.R | 3 +- .../testthat/test-cohort-diagnostics-counts.R | 2 +- .../test-cohort-diagnostics-definition.R | 2 +- tests/testthat/test-cohort-diagnostics-main.R | 2 +- .../test-cohort-method-CovariateBalance.R | 3 +- .../test-cohort-method-DiagnosticsSummary.R | 31 +- .../testthat/test-cohort-method-ForestPlot.R | 6 +- .../testthat/test-cohort-method-KaplanMeier.R | 5 +- ...-cohort-method-PopulationCharacteristics.R | 3 +- tests/testthat/test-cohort-method-Power.R | 3 +- .../test-cohort-method-PropensityScoreDist.R | 5 +- .../test-cohort-method-ResultsTable.R | 6 +- tests/testthat/test-cohort-method-attrition.R | 4 +- .../test-cohort-method-propensityModel.R | 3 +- .../test-cohort-method-systematicError.R | 5 +- tests/testthat/test-cohortGenerator-main.R | 32 +- tests/testthat/test-data-diagnostic-drill.R | 3 +- tests/testthat/test-data-diagnostic-summary.R | 3 +- tests/testthat/test-evidence-synth-main.R | 16 +- ...st-patient-level-prediction-calibration.R} | 7 +- ...tient-level-prediction-covariateSummary.R} | 7 +- ...=> test-patient-level-prediction-cutoff.R} | 7 +- ...-patient-level-prediction-designSummary.R} | 16 +- ...st-patient-level-prediction-diagnostics.R} | 14 +- ...patient-level-prediction-discrimination.R} | 7 +- ...R => test-patient-level-prediction-main.R} | 15 +- ...t-patient-level-prediction-modelSummary.R} | 8 +- ...est-patient-level-prediction-netbenefit.R} | 7 +- ... test-patient-level-prediction-settings.R} | 13 +- ...est-patient-level-prediction-validation.R} | 8 +- 181 files changed, 1904 insertions(+), 2374 deletions(-) rename R/{prediction-calibration.R => patient-level-prediction-calibration.R} (96%) rename R/{prediction-covariateSummary.R => patient-level-prediction-covariateSummary.R} (90%) rename R/{prediction-cutoff.R => patient-level-prediction-cutoff.R} (96%) rename R/{prediction-designSummary.R => patient-level-prediction-designSummary.R} (83%) rename R/{prediction-diagnostics.R => patient-level-prediction-diagnostics.R} (87%) rename R/{prediction-discrimination.R => patient-level-prediction-discrimination.R} (97%) rename R/{prediction-main.R => patient-level-prediction-main.R} (70%) rename R/{prediction-modelSummary.R => patient-level-prediction-modelSummary.R} (74%) rename R/{prediction-netbenefit.R => patient-level-prediction-netbenefit.R} (93%) rename R/{prediction-settings.R => patient-level-prediction-settings.R} (82%) rename R/{prediction-validation.R => patient-level-prediction-validation.R} (79%) rename inst/{prediction-document => patient-level-prediction-document}/export-main.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/main.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/model-design.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-analysis.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-diagnostic.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-introduction.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-metrics.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-outcome.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-output.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-participants.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-plots.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-predictors.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-results-external.rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-results-internal.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-results-template.Rmd (100%) rename inst/{prediction-document => patient-level-prediction-document}/plp-results.Rmd (100%) rename inst/{prediction-www => patient-level-prediction-www}/DataInfo.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/Description.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/Help.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/Log.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/Model.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/Performance.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/Settings.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/Summary.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/boxHelp.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/calHelp.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/demoHelp.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/f1Help.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/help-designSummary.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/help-fullResults.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/main-modelSummaryHelp.html (100%) rename inst/{prediction-www/prediction.html => patient-level-prediction-www/patient-level-prediction.html} (100%) rename inst/{prediction-www => patient-level-prediction-www}/prcHelp.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/predDistHelp.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/prefDistHelp.html (100%) rename inst/{prediction-www => patient-level-prediction-www}/rocHelp.html (100%) rename man/{characterizationView.Rd => cohortDiagCharacterizationView.Rd} (73%) rename man/{cohortDiagnosticsSever.Rd => cohortDiagnosticsServer.Rd} (85%) rename man/{predictionCalibrationServer.Rd => patientLevelPredictionCalibrationServer.Rd} (65%) rename man/{predictionCalibrationViewer.Rd => patientLevelPredictionCalibrationViewer.Rd} (65%) rename man/{predictionCovariateSummaryServer.Rd => patientLevelPredictionCovariateSummaryServer.Rd} (69%) rename man/{predictionCovariateSummaryViewer.Rd => patientLevelPredictionCovariateSummaryViewer.Rd} (63%) rename man/{predictionCutoffServer.Rd => patientLevelPredictionCutoffServer.Rd} (66%) rename man/{predictionCutoffViewer.Rd => patientLevelPredictionCutoffViewer.Rd} (66%) rename man/{predictionDesignSummaryServer.Rd => patientLevelPredictionDesignSummaryServer.Rd} (52%) rename man/{predictionDesignSummaryViewer.Rd => patientLevelPredictionDesignSummaryViewer.Rd} (64%) rename man/{predictionDiagnosticsServer.Rd => patientLevelPredictionDiagnosticsServer.Rd} (52%) rename man/{predictionDiagnosticsViewer.Rd => patientLevelPredictionDiagnosticsViewer.Rd} (64%) rename man/{predictionDiscriminationServer.Rd => patientLevelPredictionDiscriminationServer.Rd} (65%) rename man/{predictionDiscriminationViewer.Rd => patientLevelPredictionDiscriminationViewer.Rd} (64%) rename man/{predictionHelperFile.Rd => patientLevelPredictionHelperFile.Rd} (62%) rename man/{predictionModelSummaryServer.Rd => patientLevelPredictionModelSummaryServer.Rd} (54%) rename man/{predictionModelSummaryViewer.Rd => patientLevelPredictionModelSummaryViewer.Rd} (62%) rename man/{predictionNbServer.Rd => patientLevelPredictionNbServer.Rd} (67%) rename man/{predictionNbViewer.Rd => patientLevelPredictionNbViewer.Rd} (67%) rename man/{predictionServer.Rd => patientLevelPredictionServer.Rd} (78%) rename man/{predictionSettingsServer.Rd => patientLevelPredictionSettingsServer.Rd} (58%) rename man/{predictionSettingsViewer.Rd => patientLevelPredictionSettingsViewer.Rd} (63%) rename man/{predictionValidationServer.Rd => patientLevelPredictionValidationServer.Rd} (63%) rename man/{predictionValidationViewer.Rd => patientLevelPredictionValidationViewer.Rd} (64%) rename man/{predictionViewer.Rd => patientLevelPredictionViewer.Rd} (68%) rename tests/testthat/{test-prediction-calibration.R => test-patient-level-prediction-calibration.R} (75%) rename tests/testthat/{test-prediction-covariateSummary.R => test-patient-level-prediction-covariateSummary.R} (82%) rename tests/testthat/{test-prediction-cutoff.R => test-patient-level-prediction-cutoff.R} (77%) rename tests/testthat/{test-prediction-designSummary.R => test-patient-level-prediction-designSummary.R} (53%) rename tests/testthat/{test-prediction-diagnostics.R => test-patient-level-prediction-diagnostics.R} (55%) rename tests/testthat/{test-prediction-discrimination.R => test-patient-level-prediction-discrimination.R} (80%) rename tests/testthat/{test-prediction-main.R => test-patient-level-prediction-main.R} (72%) rename tests/testthat/{test-prediction-modelSummary.R => test-patient-level-prediction-modelSummary.R} (66%) rename tests/testthat/{test-prediction-netbenefit.R => test-patient-level-prediction-netbenefit.R} (75%) rename tests/testthat/{test-prediction-settings.R => test-patient-level-prediction-settings.R} (78%) rename tests/testthat/{test-prediction-validation.R => test-patient-level-prediction-validation.R} (76%) diff --git a/NAMESPACE b/NAMESPACE index bff010a8..d082518a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,12 +15,12 @@ export(characterizationTableServer) export(characterizationTableViewer) export(characterizationTimeToEventServer) export(characterizationTimeToEventViewer) -export(characterizationView) export(characterizationViewer) export(cohortCountsView) export(cohortDefinitionsView) +export(cohortDiagCharacterizationView) export(cohortDiagnosticsHelperFile) -export(cohortDiagnosticsSever) +export(cohortDiagnosticsServer) export(cohortDiagnosticsView) export(cohortGeneratorHelperFile) export(cohortGeneratorServer) @@ -73,29 +73,29 @@ export(incidenceRatesView) export(inclusionRulesView) export(indexEventBreakdownView) export(orpahanConceptsView) -export(predictionCalibrationServer) -export(predictionCalibrationViewer) -export(predictionCovariateSummaryServer) -export(predictionCovariateSummaryViewer) -export(predictionCutoffServer) -export(predictionCutoffViewer) -export(predictionDesignSummaryServer) -export(predictionDesignSummaryViewer) -export(predictionDiagnosticsServer) -export(predictionDiagnosticsViewer) -export(predictionDiscriminationServer) -export(predictionDiscriminationViewer) -export(predictionHelperFile) -export(predictionModelSummaryServer) -export(predictionModelSummaryViewer) -export(predictionNbServer) -export(predictionNbViewer) -export(predictionServer) -export(predictionSettingsServer) -export(predictionSettingsViewer) -export(predictionValidationServer) -export(predictionValidationViewer) -export(predictionViewer) +export(patientLevelPredictionCalibrationServer) +export(patientLevelPredictionCalibrationViewer) +export(patientLevelPredictionCovariateSummaryServer) +export(patientLevelPredictionCovariateSummaryViewer) +export(patientLevelPredictionCutoffServer) +export(patientLevelPredictionCutoffViewer) +export(patientLevelPredictionDesignSummaryServer) +export(patientLevelPredictionDesignSummaryViewer) +export(patientLevelPredictionDiagnosticsServer) +export(patientLevelPredictionDiagnosticsViewer) +export(patientLevelPredictionDiscriminationServer) +export(patientLevelPredictionDiscriminationViewer) +export(patientLevelPredictionHelperFile) +export(patientLevelPredictionModelSummaryServer) +export(patientLevelPredictionModelSummaryViewer) +export(patientLevelPredictionNbServer) +export(patientLevelPredictionNbViewer) +export(patientLevelPredictionServer) +export(patientLevelPredictionSettingsServer) +export(patientLevelPredictionSettingsViewer) +export(patientLevelPredictionValidationServer) +export(patientLevelPredictionValidationViewer) +export(patientLevelPredictionViewer) export(resultTableServer) export(resultTableViewer) export(sccsHelperFile) diff --git a/R/characterization-aggregateFeatures.R b/R/characterization-aggregateFeatures.R index bf7ac954..1d333c32 100644 --- a/R/characterization-aggregateFeatures.R +++ b/R/characterization-aggregateFeatures.R @@ -111,10 +111,7 @@ characterizationAggregateFeaturesViewer <- function(id) { #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database #' @param mainPanelTab the current tab -#' @param schema the database schema for the model results -#' @param tablePrefix a string that appends the tables in the result schema -#' @param cohortTablePrefix a string that appends the COHORT_DEFINITION table in the result schema -#' @param databaseTable The database table name +#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix #' #' @return #' The server to the description aggregate features module @@ -124,10 +121,7 @@ characterizationAggregateFeaturesServer <- function( id, connectionHandler, mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix = 'cg_', - databaseTable = 'DATABASE_META_DATA' + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -189,18 +183,14 @@ characterizationAggregateFeaturesServer <- function( # get the possible options options <- getAggregateFeatureOptions( connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, - cohortTablePrefix = cohortTablePrefix - ) + resultDatabaseSettings = resultDatabaseSettings + ) # get databases databases <- getAggregateFeatureDatabases( connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, - databaseTable = databaseTable - ) + resultDatabaseSettings = resultDatabaseSettings + ) # add buttons @@ -414,8 +404,7 @@ characterizationAggregateFeaturesServer <- function( allData <- characterizationGetAggregateData( connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, + resultDatabaseSettings = resultDatabaseSettings, targetId = input$target, outcomeId = input$outcome, riskWindowStart = options$tarList[[ind]]$riskWindowStart, @@ -584,9 +573,7 @@ characterizationAggregateFeaturesServer <- function( getAggregateFeatureOptions <- function( connectionHandler, - schema, - tablePrefix, - cohortTablePrefix + resultDatabaseSettings ){ @@ -595,12 +582,12 @@ getAggregateFeatureOptions <- function( sql <- "SELECT DISTINCT t.COHORT_NAME as TARGET, cd.TARGET_COHORT_ID, o.COHORT_NAME as outcome, cd.OUTCOME_COHORT_ID, s.RISK_WINDOW_START, s.START_ANCHOR, s.RISK_WINDOW_END, s.END_ANCHOR - FROM @result_database_schema.@table_prefixCOHORT_DETAILS cd - inner join @result_database_schema.@table_prefixSETTINGS s + FROM @schema.@c_table_prefixCOHORT_DETAILS cd + inner join @schema.@c_table_prefixSETTINGS s on cd.run_id = s.run_id and cd.database_id = s.database_id - inner join @result_database_schema.@cohort_table_prefixCOHORT_DEFINITION t + inner join @schema.@cg_table_prefixCOHORT_DEFINITION t on cd.TARGET_COHORT_ID = t.COHORT_DEFINITION_ID - inner join @result_database_schema.@cohort_table_prefixCOHORT_DEFINITION o + inner join @schema.@cg_table_prefixCOHORT_DEFINITION o on cd.OUTCOME_COHORT_ID = o.COHORT_DEFINITION_ID WHERE cd.TARGET_COHORT_ID != 0 AND cd.OUTCOME_COHORT_ID != 0;" @@ -608,9 +595,9 @@ getAggregateFeatureOptions <- function( options <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) shiny::incProgress(2/2, detail = paste("Finished")) @@ -668,17 +655,15 @@ getAggregateFeatureOptions <- function( getAggregateFeatureDatabases <- function( connectionHandler, - schema, - tablePrefix, - databaseTable + resultDatabaseSettings ){ shiny::withProgress(message = 'Finding databases', value = 0, { sql <- "SELECT DISTINCT s.DATABASE_ID, d.CDM_SOURCE_ABBREVIATION as database_name - FROM @result_database_schema.@table_prefixCOHORT_DETAILS cd - inner join @result_database_schema.@database_table d + FROM @schema.@c_table_prefixCOHORT_DETAILS cd + inner join @schema.@database_table d on cd.database_id = d.database_id - inner join @result_database_schema.@table_prefixSETTINGS s + inner join @schema.@c_table_prefixSETTINGS s on s.database_id = d.database_id and s.run_id = cd.run_id;" @@ -687,9 +672,9 @@ getAggregateFeatureDatabases <- function( databases <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, - database_table = databaseTable + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, + database_table = resultDatabaseSettings$databaseTable ) shiny::incProgress(2/2, detail = paste("Finished")) @@ -706,8 +691,7 @@ getAggregateFeatureDatabases <- function( # pulls all data for a target and outcome characterizationGetAggregateData <- function( connectionHandler, - schema, - tablePrefix, + resultDatabaseSettings, targetId, outcomeId, riskWindowStart, @@ -722,9 +706,9 @@ characterizationGetAggregateData <- function( shiny::withProgress(message = 'Getting Feature Comparison Data', value = 0, { sql <- "SELECT s.RUN_ID, cd.COHORT_DEFINITION_ID - FROM @result_database_schema.@table_prefixSETTINGS s + FROM @schema.@c_table_prefixSETTINGS s inner join - @result_database_schema.@table_prefixCOHORT_DETAILS cd + @schema.@c_table_prefixCOHORT_DETAILS cd on cd.database_id = s.database_id and cd.run_id = s.run_id WHERE cd.TARGET_COHORT_ID = @target_id and cd.OUTCOME_COHORT_ID = @outcome_id @@ -734,8 +718,8 @@ characterizationGetAggregateData <- function( settingsFirst <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, target_id = ifelse(type1 %in% c('firstO','O'), 0, targetId), outcome_id = ifelse(type1 %in% c('T', 'allT'), 0, outcomeId), risk_window_start = riskWindowStart, @@ -750,9 +734,9 @@ characterizationGetAggregateData <- function( sql <- "SELECT s.RUN_ID, cd.COHORT_DEFINITION_ID - FROM @result_database_schema.@table_prefixSETTINGS s + FROM @schema.@c_table_prefixSETTINGS s inner join - @result_database_schema.@table_prefixCOHORT_DETAILS cd + @schema.@c_table_prefixCOHORT_DETAILS cd on cd.database_id = s.database_id and cd.run_id = s.run_id WHERE cd.TARGET_COHORT_ID = @target_id and cd.OUTCOME_COHORT_ID = @outcome_id @@ -763,8 +747,8 @@ characterizationGetAggregateData <- function( settingsSecond <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, target_id = ifelse(type1 %in% c('firstO','O'), 0, targetId), outcome_id = ifelse(type1 %in% c('T', 'allT'), 0, outcomeId), risk_window_start = riskWindowStart, @@ -779,14 +763,14 @@ characterizationGetAggregateData <- function( sql <- "SELECT cov.*, cov_ref.COVARIATE_NAME, an_ref.ANALYSIS_NAME, case when (cov.DATABASE_ID = '@database_id1' and cov.COHORT_DEFINITION_ID = @cohortDef1 and cov.RUN_ID in (@run_id1)) then 'comp1' else 'comp2' end as label - FROM @result_database_schema.@table_prefixCOVARIATES cov + FROM @schema.@c_table_prefixCOVARIATES cov INNER JOIN - @result_database_schema.@table_prefixCOVARIATE_REF cov_ref + @schema.@c_table_prefixCOVARIATE_REF cov_ref ON cov.covariate_id = cov_ref.covariate_id and cov.run_id = cov_ref.run_id and cov.database_id = cov_ref.database_id INNER JOIN - @result_database_schema.@table_prefixANALYSIS_REF an_ref + @schema.@c_table_prefixANALYSIS_REF an_ref ON an_ref.analysis_id = cov_ref.analysis_id and an_ref.run_id = cov_ref.run_id and an_ref.database_id = cov_ref.database_id @@ -801,8 +785,8 @@ characterizationGetAggregateData <- function( binary <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, cohortDef1 = settingsFirst$cohortDefinitionId[1], cohortDef2 = settingsSecond$cohortDefinitionId[1], database_id1 = database1, @@ -815,14 +799,14 @@ characterizationGetAggregateData <- function( sql <- "SELECT cov.*, cov_ref.COVARIATE_NAME, an_ref.ANALYSIS_NAME, case when (cov.DATABASE_ID = '@database_id1' and cov.COHORT_DEFINITION_ID = @cohortDef1 and cov.RUN_ID in (@run_id1)) then 'comp1' else 'comp2' end as label - FROM @result_database_schema.@table_prefixCOVARIATES_CONTINUOUS cov + FROM @schema.@c_table_prefixCOVARIATES_CONTINUOUS cov INNER JOIN - @result_database_schema.@table_prefixCOVARIATE_REF cov_ref + @schema.@c_table_prefixCOVARIATE_REF cov_ref ON cov.covariate_id = cov_ref.covariate_id and cov.run_id = cov_ref.run_id and cov.database_id = cov_ref.database_id INNER JOIN - @result_database_schema.@table_prefixANALYSIS_REF an_ref + @schema.@c_table_prefixANALYSIS_REF an_ref ON an_ref.analysis_id = cov_ref.analysis_id and an_ref.run_id = cov_ref.run_id and an_ref.database_id = cov_ref.database_id @@ -835,8 +819,8 @@ characterizationGetAggregateData <- function( continuous <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, cohortDef1 = settingsFirst$cohortDefinitionId[1], cohortDef2 = settingsSecond$cohortDefinitionId[1], database_id1 = database1, diff --git a/R/characterization-cohorts.R b/R/characterization-cohorts.R index cfe89021..276504ab 100644 --- a/R/characterization-cohorts.R +++ b/R/characterization-cohorts.R @@ -67,10 +67,7 @@ characterizationTableViewer <- function(id) { #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database #' @param mainPanelTab the current tab -#' @param schema the database schema for the model results -#' @param tablePrefix a string that appends the tables in the result schema -#' @param cohortTablePrefix a string that appends the cohort table in the result schema -#' @param databaseTable name of the database table +#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix #' #' @return #' The server to the cohorts features server @@ -80,10 +77,7 @@ characterizationTableServer <- function( id, connectionHandler, mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable = 'DATABASE_META_DATA' + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -91,10 +85,7 @@ characterizationTableServer <- function( inputVals <- getDecCohortsInputs( connectionHandler, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable + resultDatabaseSettings ) # update UI @@ -160,9 +151,7 @@ characterizationTableServer <- function( targetIds = input$targetIds, databaseId = input$databaseId, connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, - cohortTablePrefix = cohortTablePrefix + resultDatabaseSettings ) }) @@ -245,9 +234,7 @@ getDesFEData <- function( targetIds, databaseId, connectionHandler, - schema, - tablePrefix, - cohortTablePrefix + resultDatabaseSettings ) { # shiny::withProgress(message = 'Getting target comparison data', value = 0, { @@ -257,9 +244,9 @@ getDesFEData <- function( ( select co.RUN_ID, cd.TARGET_COHORT_ID as COHORT_DEFINITION_ID, co.COVARIATE_ID, co.SUM_VALUE as COUNT_VALUE, co.AVERAGE_VALUE*100 as AVERAGE_VALUE from - @result_schema.@table_prefixCOVARIATES co + @schema.@c_table_prefixCOVARIATES co inner join - (select * from @result_schema.@table_prefixcohort_details + (select * from @schema.@c_table_prefixcohort_details where DATABASE_ID = '@database_id' and TARGET_COHORT_ID in (@cohort_ids) and COHORT_TYPE = 'T' ) as cd @@ -267,9 +254,9 @@ getDesFEData <- function( and co.DATABASE_ID = cd.DATABASE_ID union select cc.RUN_ID, cds.TARGET_COHORT_ID as COHORT_DEFINITION_ID, cc.COVARIATE_ID, cc.COUNT_VALUE, cc.AVERAGE_VALUE from - @result_schema.@table_prefixCOVARIATES_continuous cc + @schema.@c_table_prefixCOVARIATES_continuous cc inner join - (select * from @result_schema.@table_prefixcohort_details + (select * from @schema.@c_table_prefixcohort_details where DATABASE_ID = '@database_id' and TARGET_COHORT_ID in (@cohort_ids) and COHORT_TYPE = 'T' ) as cds @@ -277,13 +264,13 @@ getDesFEData <- function( and cc.DATABASE_ID = cds.DATABASE_ID ) covs inner join - @result_schema.@table_prefixcovariate_ref ref + @schema.@c_table_prefixcovariate_ref ref on covs.RUN_ID = ref.RUN_ID and covs.COVARIATE_ID = ref.COVARIATE_ID - inner join @result_schema.@table_prefixanalysis_ref an + inner join @schema.@c_table_prefixanalysis_ref an on an.RUN_ID = ref.RUN_ID and an.analysis_id = ref.analysis_id - inner join @result_schema.@cohort_table_prefixcohort_definition c + inner join @schema.@cg_table_prefixcohort_definition c on c.cohort_definition_id = covs.COHORT_DEFINITION_ID ; " @@ -292,9 +279,9 @@ getDesFEData <- function( resultTable <- connectionHandler$queryDb( sql = sql, - result_schema = schema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix, + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, cohort_ids = paste(as.double(targetIds), collapse = ','), database_id = databaseId ) @@ -328,20 +315,17 @@ getDesFEData <- function( getDecCohortsInputs <- function( connectionHandler, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable + resultDatabaseSettings ) { #shiny::withProgress(message = 'Getting target comparison inputs', value = 0, { sql <- ' select distinct c.cohort_definition_id, c.cohort_name from - @result_schema.@cohort_table_prefixcohort_definition c + @schema.@cg_table_prefixcohort_definition c inner join (select distinct TARGET_COHORT_ID as id - from @result_schema.@table_prefixcohort_details + from @schema.@c_table_prefixcohort_details ) ids on ids.id = c.cohort_definition_id ;' @@ -350,9 +334,9 @@ getDecCohortsInputs <- function( idVals <- connectionHandler$queryDb( sql = sql, - result_schema = schema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) ids <- idVals$cohortDefinitionId names(ids) <- idVals$cohortName @@ -367,8 +351,8 @@ getDecCohortsInputs <- function( database <- connectionHandler$queryDb( sql = sql, - result_schema = schema, - database_table = databaseTable + result_schema = resultDatabaseSettings$schema, + database_table = resultDatabaseSettings$databaseTable ) databaseIds <- database$databaseId names(databaseIds) <- database$databaseName diff --git a/R/characterization-dechallengeRechallenge.R b/R/characterization-dechallengeRechallenge.R index 44e442fe..ff8a4ae8 100644 --- a/R/characterization-dechallengeRechallenge.R +++ b/R/characterization-dechallengeRechallenge.R @@ -82,10 +82,7 @@ characterizationDechallengeRechallengeViewer <- function(id) { #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database #' @param mainPanelTab the current tab -#' @param schema the database schema for the model results -#' @param tablePrefix a string that appends the tables in the result schema -#' @param cohortTablePrefix a string that appends the cohort table in the result schema -#' @param databaseTable name of the database table +#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix #' #' @return #' The server to the Dechallenge Rechallenge module @@ -95,10 +92,7 @@ characterizationDechallengeRechallengeServer <- function( id, connectionHandler, mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix = 'cg_', - databaseTable = 'DATABASE_META_DATA' + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -111,9 +105,7 @@ characterizationDechallengeRechallengeServer <- function( # get the possible target ids bothIds <- dechalRechalGetIds( connectionHandler, - schema, - tablePrefix, - cohortTablePrefix + resultDatabaseSettings ) shiny::observeEvent( @@ -224,9 +216,7 @@ characterizationDechallengeRechallengeServer <- function( targetId = input$targetId, outcomeId = input$outcomeId, connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, - databaseTable = databaseTable + resultDatabaseSettings ) reactiveData(allData) @@ -316,9 +306,7 @@ characterizationDechallengeRechallengeServer <- function( databaseId = databases()[input$databaseRowId$index], dechallengeStopInterval = dechallengeStopInterval()[input$databaseRowId$index], dechallengeEvaluationWindow = dechallengeEvaluationWindow()[input$databaseRowId$index], - connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix + resultDatabaseSettings ) # do the plots reactively @@ -359,9 +347,7 @@ characterizationDechallengeRechallengeServer <- function( dechalRechalGetIds <- function( connectionHandler, - schema, - tablePrefix, - cohortTablePrefix + resultDatabaseSettings ){ shiny::withProgress(message = 'Getting dechal Rechal T and O ids', value = 0, { @@ -370,10 +356,10 @@ dechalRechalGetIds <- function( sql <- "SELECT DISTINCT t.COHORT_NAME as target, dr.TARGET_COHORT_DEFINITION_ID, o.COHORT_NAME as outcome, dr.OUTCOME_COHORT_DEFINITION_ID - FROM @result_database_schema.@table_prefixDECHALLENGE_RECHALLENGE dr - inner join @result_database_schema.@cohort_table_prefixCOHORT_DEFINITION t + FROM @schema.@c_table_prefixDECHALLENGE_RECHALLENGE dr + inner join @schema.@cg_table_prefixCOHORT_DEFINITION t on dr.TARGET_COHORT_DEFINITION_ID = t.COHORT_DEFINITION_ID - inner join @result_database_schema.@cohort_table_prefixCOHORT_DEFINITION o + inner join @schema.@cg_table_prefixCOHORT_DEFINITION o on dr.OUTCOME_COHORT_DEFINITION_ID = o.COHORT_DEFINITION_ID ;" @@ -381,9 +367,9 @@ dechalRechalGetIds <- function( bothIds <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) shiny::incProgress(3/4, detail = paste("Processing ids")) @@ -428,17 +414,15 @@ getDechalRechalInputsData <- function( targetId, outcomeId, connectionHandler, - schema, - tablePrefix, - databaseTable + resultDatabaseSettings ){ shiny::withProgress(message = 'Extracting DECHALLENGE_RECHALLENGE data', value = 0, { sql <- "SELECT dr.*, d.CDM_SOURCE_ABBREVIATION as database_name - FROM @result_database_schema.@table_prefixDECHALLENGE_RECHALLENGE dr - inner join @result_database_schema.@database_table d + FROM @schema.@c_table_prefixDECHALLENGE_RECHALLENGE dr + inner join @schema.@database_table d on dr.database_id = d.database_id where dr.TARGET_COHORT_DEFINITION_ID = @target_id and dr.OUTCOME_COHORT_DEFINITION_ID = @outcome_id;" @@ -448,11 +432,11 @@ getDechalRechalInputsData <- function( data <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, target_id = targetId, outcome_id = outcomeId, - database_table = databaseTable + database_table = resultDatabaseSettings$databaseTable ) shiny::incProgress(3/3, detail = paste("Finished")) @@ -470,13 +454,12 @@ getDechalRechalFailData <- function( dechallengeStopInterval, dechallengeEvaluationWindow, connectionHandler, - schema, - tablePrefix + resultDatabaseSettings ){ shiny::withProgress(message = 'Extracting FAILLED DECHALLENGE_RECHALLENGE data', value = 0, { - sql <- "SELECT * FROM @result_database_schema.@table_prefixRECHALLENGE_FAIL_CASE_SERIES + sql <- "SELECT * FROM @schema.@c_table_prefixRECHALLENGE_FAIL_CASE_SERIES where TARGET_COHORT_DEFINITION_ID = @target_id and OUTCOME_COHORT_DEFINITION_ID = @outcome_id and DATABASE_ID = '@database_id' @@ -487,8 +470,8 @@ getDechalRechalFailData <- function( data <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, target_id = targetId, outcome_id = outcomeId, database_id = databaseId, diff --git a/R/characterization-incidence.R b/R/characterization-incidence.R index ed3e855e..8a1e7fa0 100644 --- a/R/characterization-incidence.R +++ b/R/characterization-incidence.R @@ -82,9 +82,7 @@ characterizationIncidenceViewer <- function(id) { #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database #' @param mainPanelTab the current tab -#' @param schema the database schema for the model results -#' @param incidenceTablePrefix a string that appends the incidence table in the result schema -#' @param databaseTable name of the database table +#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix #' #' @return #' The server to the prediction incidence module @@ -94,9 +92,7 @@ characterizationIncidenceServer <- function( id, connectionHandler, mainPanelTab, - schema, - incidenceTablePrefix, - databaseTable = 'DATABASE_META_DATA' + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -108,8 +104,7 @@ characterizationIncidenceServer <- function( cohorts <- getTargetOutcomes( connectionHandler, - schema, - incidenceTablePrefix + resultDatabaseSettings ) @@ -205,9 +200,7 @@ characterizationIncidenceServer <- function( targetId = input$targetId, outcomeId = input$outcomeId, connectionHandler = connectionHandler, - schema = schema, - incidenceTablePrefix = incidenceTablePrefix, - databaseTable = databaseTable + resultDatabaseSettings ) allDataDownload(allData ) @@ -312,9 +305,7 @@ getIncidenceData <- function( targetId, outcomeId, connectionHandler, - schema, - incidenceTablePrefix, - databaseTable + resultDatabaseSettings ){ shiny::withProgress(message = 'Getting incidence data', value = 0, { @@ -331,11 +322,11 @@ getIncidenceData <- function( resultTable <- connectionHandler$queryDb( sql = sql, - result_schema = schema, - incidence_table_prefix = incidenceTablePrefix, + result_schema = resultDatabaseSettings$schema, + incidence_table_prefix = resultDatabaseSettings$incidenceTablePrefix, target_id = targetId, outcome_id = outcomeId, - database_table_name = databaseTable + database_table_name = resultDatabaseSettings$databaseTable ) shiny::incProgress(2/2, detail = paste("Done...")) @@ -356,8 +347,7 @@ getIncidenceData <- function( getTargetOutcomes <- function( connectionHandler, - schema, - incidenceTablePrefix + resultDatabaseSettings ){ shiny::withProgress(message = 'Getting incidence inputs', value = 0, { @@ -369,8 +359,8 @@ getTargetOutcomes <- function( targets <- connectionHandler$queryDb( sql = sql, - result_schema = schema, - incidence_table_prefix = incidenceTablePrefix + result_schema = resultDatabaseSettings$schema, + incidence_table_prefix = resultDatabaseSettings$incidenceTablePrefix ) targetIds <- targets$targetCohortDefinitionId names(targetIds) <- targets$targetName @@ -382,8 +372,8 @@ getTargetOutcomes <- function( outcomes <- connectionHandler$queryDb( sql = sql, - result_schema = schema, - incidence_table_prefix = incidenceTablePrefix + result_schema = resultDatabaseSettings$schema, + incidence_table_prefix = resultDatabaseSettings$incidenceTablePrefix ) outcomeIds <- outcomes$outcomeCohortDefinitionId diff --git a/R/characterization-main.R b/R/characterization-main.R index ef49b632..4ce63eda 100644 --- a/R/characterization-main.R +++ b/R/characterization-main.R @@ -90,7 +90,7 @@ characterizationViewer <- function(id=1) { #' #' @param id the unique reference id for the module #' @param connectionHandler a connection to the database with the results -#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cohortTablePrefix +#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix #' #' @return #' The server for the characterization module @@ -120,10 +120,7 @@ characterizationServer <- function( id = 'descriptiveTableTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, - schema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix, - databaseTable = resultDatabaseSettings$databaseTable + resultDatabaseSettings = resultDatabaseSettings ) @@ -135,11 +132,8 @@ characterizationServer <- function( id = 'aggregateFeaturesTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, - schema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix, - databaseTable = resultDatabaseSettings$databaseTable - ) + resultDatabaseSettings = resultDatabaseSettings + ) # ============================= # Incidence @@ -148,9 +142,7 @@ characterizationServer <- function( id = 'incidenceTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, - schema = resultDatabaseSettings$schema, - incidenceTablePrefix = resultDatabaseSettings$incidenceTablePrefix, - databaseTable = resultDatabaseSettings$databaseTable + resultDatabaseSettings = resultDatabaseSettings ) @@ -162,11 +154,8 @@ characterizationServer <- function( id = 'timeToEventTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, - schema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix, - databaseTable = resultDatabaseSettings$databaseTable - ) + resultDatabaseSettings = resultDatabaseSettings + ) # ============================= @@ -177,11 +166,8 @@ characterizationServer <- function( id = 'dechallengeRechallengeTab', connectionHandler = connectionHandler, mainPanelTab = mainPanelTab, - schema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix, - databaseTable = resultDatabaseSettings$databaseTable - ) + resultDatabaseSettings = resultDatabaseSettings + ) } ) diff --git a/R/characterization-timeToEvent.R b/R/characterization-timeToEvent.R index e979ce36..44b07d0a 100644 --- a/R/characterization-timeToEvent.R +++ b/R/characterization-timeToEvent.R @@ -87,10 +87,7 @@ characterizationTimeToEventViewer <- function(id) { #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database #' @param mainPanelTab the current tab -#' @param schema the database schema for the model results -#' @param tablePrefix a string that appends the tables in the result schema -#' @param cohortTablePrefix a string that appends the cohort table in the result schema -#' @param databaseTable name of the database table +#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix #' #' @return #' The server to the prediction time to event module @@ -100,10 +97,7 @@ characterizationTimeToEventServer <- function( id, connectionHandler, mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix = 'cg_', - databaseTable = 'DATABASE_META_DATA' + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -116,9 +110,7 @@ characterizationTimeToEventServer <- function( # get the possible target ids bothIds <- timeToEventGetIds( connectionHandler, - schema, - tablePrefix, - cohortTablePrefix + resultDatabaseSettings ) shiny::observeEvent( @@ -230,9 +222,7 @@ characterizationTimeToEventServer <- function( targetId = input$targetId, outcomeId = input$outcomeId, connectionHandler = connectionHandler, - schema = schema, - tablePrefix = tablePrefix, - databaseTable = databaseTable + resultDatabaseSettings ) }, error = function(e){shiny::showNotification(paste0('Error: ', e));return(NULL)} @@ -293,9 +283,7 @@ characterizationTimeToEventServer <- function( timeToEventGetIds <- function( connectionHandler, - schema, - tablePrefix, - cohortTablePrefix + resultDatabaseSettings ){ shiny::withProgress(message = 'Getting time to event T and O ids', value = 0, { @@ -303,10 +291,10 @@ timeToEventGetIds <- function( sql <- "SELECT DISTINCT t.COHORT_NAME as target, TARGET_COHORT_DEFINITION_ID, o.COHORT_NAME as outcome, OUTCOME_COHORT_DEFINITION_ID - FROM @result_database_schema.@table_prefixTIME_TO_EVENT tte - inner join @result_database_schema.@cohort_table_prefixCOHORT_DEFINITION t + FROM @schema.@c_table_prefixTIME_TO_EVENT tte + inner join @schema.@cg_table_prefixCOHORT_DEFINITION t on tte.TARGET_COHORT_DEFINITION_ID = t.COHORT_DEFINITION_ID - inner join @result_database_schema.@cohort_table_prefixCOHORT_DEFINITION o + inner join @schema.@cg_table_prefixCOHORT_DEFINITION o on tte.OUTCOME_COHORT_DEFINITION_ID = o.COHORT_DEFINITION_ID ;" @@ -315,9 +303,9 @@ timeToEventGetIds <- function( bothIds <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) shiny::incProgress(3/4, detail = paste("Processing ids")) @@ -362,17 +350,15 @@ getTimeToEventData <- function( targetId, outcomeId, connectionHandler, - schema, - tablePrefix, - databaseTable + resultDatabaseSettings ){ shiny::withProgress(message = 'Extracting time to event data', value = 0, { sql <- "SELECT tte.*, d.CDM_SOURCE_ABBREVIATION as database_name - FROM @result_database_schema.@table_prefixTIME_TO_EVENT tte - inner join @result_database_schema.@database_table d + FROM @schema.@c_table_prefixTIME_TO_EVENT tte + inner join @schema.@database_table d on tte.database_id = d.database_id where tte.TARGET_COHORT_DEFINITION_ID = @target_id and tte.OUTCOME_COHORT_DEFINITION_ID = @outcome_id;" @@ -381,11 +367,11 @@ getTimeToEventData <- function( data <- connectionHandler$queryDb( sql = sql, - result_database_schema = schema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + c_table_prefix = resultDatabaseSettings$cTablePrefix, target_id = targetId, outcome_id = outcomeId, - database_table = databaseTable + database_table = resultDatabaseSettings$databaseTable ) shiny::incProgress(3/3, detail = paste("Finished")) diff --git a/R/cohort-diagnostics-characterization.R b/R/cohort-diagnostics-characterization.R index dda8fded..141cc2ad 100644 --- a/R/cohort-diagnostics-characterization.R +++ b/R/cohort-diagnostics-characterization.R @@ -1,6 +1,6 @@ # Copyright 2022 Observational Health Data Sciences and Informatics # -# This file is part of PatientLevelPrediction +# This file is part of OhdsiShinyModules # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ #' #' @param id Namespace Id - use namespaced id ns("characterization") inside diagnosticsExplorer module #' @export -characterizationView <- function(id) { +cohortDiagCharacterizationView <- function(id) { ns <- shiny::NS(id) shiny::tagList( shinydashboard::box( @@ -426,11 +426,11 @@ prepareTable1 <- function(covariates, return(table) } -characterizationModule <- function( +cohortDiagCharacterizationModule <- function( id, dataSource, cohortTable = dataSource$cohortTable, - databaseTable = dataSource$databaseTable, + databaseTable = dataSource$dbTable, temporalAnalysisRef = dataSource$temporalAnalysisRef, analysisNameOptions = dataSource$analysisNameOptions, domainIdOptions = dataSource$domainIdOptions, @@ -661,8 +661,8 @@ characterizationModule <- function( getPrettyCharacterizationData <- shiny::reactive({ data <- dataSource$connectionHandler$queryDb( sql = "SELECT tcv.*, ref.analysis_id, ref.covariate_name - FROM @results_database_schema.@table_name tcv - INNER JOIN @results_database_schema.@ref_table_name ref ON ref.covariate_id = tcv.covariate_id + FROM @schema.@table_name tcv + INNER JOIN @schema.@ref_table_name ref ON ref.covariate_id = tcv.covariate_id WHERE ref.covariate_id IS NOT NULL {@analysis_ids != \"\"} ? { AND ref.analysis_id IN (@analysis_ids)} {@cohort_id != \"\"} ? { AND tcv.cohort_id IN (@cohort_id)} @@ -677,7 +677,7 @@ characterizationModule <- function( table_name = dataSource$prefixTable("temporal_covariate_value"), ref_table_name = dataSource$prefixTable("temporal_covariate_ref"), cohort_id = targetCohortId(), - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, filter_mean_threshold = 0.0 ) %>% dplyr::tibble() %>% @@ -776,10 +776,10 @@ characterizationModule <- function( ref.covariate_name, ref.analysis_id, ref.concept_id, aref.analysis_name, aref.is_binary, aref.domain_id, tref.start_day, tref.end_day - FROM @results_database_schema.@table_name tcv - INNER JOIN @results_database_schema.@ref_table_name ref ON ref.covariate_id = tcv.covariate_id - INNER JOIN @results_database_schema.@analysis_ref_table_name aref ON aref.analysis_id = ref.analysis_id - LEFT JOIN @results_database_schema.@temporal_time_ref tref ON tref.time_id = tcv.time_id + FROM @schema.@table_name tcv + INNER JOIN @schema.@ref_table_name ref ON ref.covariate_id = tcv.covariate_id + INNER JOIN @schema.@analysis_ref_table_name aref ON aref.analysis_id = ref.analysis_id + LEFT JOIN @schema.@temporal_time_ref tref ON tref.time_id = tcv.time_id WHERE ref.covariate_id IS NOT NULL {@analysis_ids != \"\"} ? { AND ref.analysis_id IN (@analysis_ids)} {@domain_ids != \"\"} ? { AND aref.domain_id IN (@domain_ids)} @@ -798,7 +798,7 @@ characterizationModule <- function( analysis_ref_table_name = dataSource$prefixTable("temporal_analysis_ref"), temporal_time_ref = dataSource$prefixTable("temporal_time_ref"), cohort_id = targetCohortId(), - results_database_schema = dataSource$resultsDatabaseSchema + schema = dataSource$schema ) %>% dplyr::tibble() %>% tidyr::replace_na(replace = list(timeId = -1)) %>% diff --git a/R/cohort-diagnostics-cohort-overlap.R b/R/cohort-diagnostics-cohort-overlap.R index 31793571..5e0e002d 100644 --- a/R/cohort-diagnostics-cohort-overlap.R +++ b/R/cohort-diagnostics-cohort-overlap.R @@ -1,6 +1,6 @@ # Copyright 2022 Observational Health Data Sciences and Informatics # -# This file is part of PatientLevelPrediction +# This file is part of OhdsiShinyModules # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -261,18 +261,18 @@ getResultsCohortRelationships <- function(dataSource, endDays = NULL) { data <- dataSource$connectionHandler$queryDb( sql = "SELECT cr.*, db.database_name - FROM @results_database_schema.@table_name cr - INNER JOIN @results_database_schema.@database_table db ON db.database_id = cr.database_id + FROM @schema.@table_name cr + INNER JOIN @schema.@database_table db ON db.database_id = cr.database_id WHERE cr.cohort_id IN (@cohort_id) AND cr.database_id IN (@database_id) {@comparator_cohort_id != \"\"} ? { AND cr.comparator_cohort_id IN (@comparator_cohort_id)} {@start_day != \"\"} ? { AND cr.start_day IN (@start_day)} {@end_day != \"\"} ? { AND cr.end_day IN (@end_day)};", snakeCaseToCamelCase = TRUE, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, database_id = quoteLiterals(databaseIds), table_name = dataSource$prefixTable("cohort_relationships"), - database_table = dataSource$databaseTableName, + database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), cohort_id = cohortIds, comparator_cohort_id = comparatorCohortIds, start_day = startDays, diff --git a/R/cohort-diagnostics-compareCharacterization.R b/R/cohort-diagnostics-compareCharacterization.R index 72a7c493..59fee63e 100644 --- a/R/cohort-diagnostics-compareCharacterization.R +++ b/R/cohort-diagnostics-compareCharacterization.R @@ -483,11 +483,11 @@ compareCohortCharacterizationView <- function(id, title = "Compare cohort charac # Returns data from cohort table of Cohort Diagnostics results data model getResultsCohort <- function(dataSource, cohortIds = NULL) { data <- dataSource$connectionHandler$queryDb( - sql = "SELECT * FROM @results_database_schema.@table_name + sql = "SELECT * FROM @schema.@table_name {@cohort_id != \"\"} ? { WHERE cohort_id IN (@cohort_id)};", - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, cohort_id = cohortIds, - table_name = dataSource$cohortTableName, + table_name = paste0(dataSource$cgTablePrefix,dataSource$cgTable), snakeCaseToCamelCase = TRUE ) return(data) @@ -805,7 +805,7 @@ getCharacterizationOutput <- function(dataSource, compareCohortCharacterizationModule <- function(id, dataSource, cohortTable = dataSource$cohortTable, - databaseTable = dataSource$databaseTable, + databaseTable = dataSource$dbTable, temporalAnalysisRef = dataSource$temporalAnalysisRef, analysisNameOptions = dataSource$analysisNameOptions, domainIdOptions = dataSource$domainIdOptions, diff --git a/R/cohort-diagnostics-conceptsInDataSource.R b/R/cohort-diagnostics-conceptsInDataSource.R index f2550057..ddd891e5 100644 --- a/R/cohort-diagnostics-conceptsInDataSource.R +++ b/R/cohort-diagnostics-conceptsInDataSource.R @@ -89,7 +89,7 @@ getConceptsInCohort <- 0 source_concept_id, max(concept_subjects) concept_subjects, sum(concept_count) concept_count - FROM @results_database_schema.@table_name isc + FROM @schema.@table_name isc WHERE isc.cohort_id = @cohort_id AND isc.database_id IN (@database_ids) GROUP BY isc.database_id, @@ -104,7 +104,7 @@ getConceptsInCohort <- 1 source_concept_id, max(c.concept_subjects) concept_subjects, sum(c.concept_count) concept_count - FROM @results_database_schema.@table_name c + FROM @schema.@table_name c WHERE c.cohort_id = @cohort_id AND c.database_id IN (@database_ids) GROUP BY @@ -112,12 +112,12 @@ getConceptsInCohort <- c.cohort_id, c.source_concept_id ) concepts - INNER JOIN @results_database_schema.@concept_table c ON concepts.concept_id = c.concept_id + INNER JOIN @schema.@concept_table c ON concepts.concept_id = c.concept_id WHERE c.invalid_reason IS NULL;" data <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, cohort_id = cohortId, database_ids = quoteLiterals(databaseIds), table_name = dataSource$prefixTable("included_source_concept"), @@ -135,7 +135,7 @@ conceptsInDataSourceModule <- function(id, selectedDatabaseIds, targetCohortId, selectedConceptSets, - databaseTable = dataSource$databaseTable) { + databaseTable = dataSource$dbTable) { ns <- shiny::NS(id) shiny::moduleServer(id, function(input, output, session) { output$selectedCohorts <- shiny::renderUI({ selectedCohort() }) diff --git a/R/cohort-diagnostics-counts.R b/R/cohort-diagnostics-counts.R index 733ba599..596efd8b 100644 --- a/R/cohort-diagnostics-counts.R +++ b/R/cohort-diagnostics-counts.R @@ -170,7 +170,7 @@ getInclusionRulesTable <- function( getDisplayTableGroupedByDatabaseId( data = data, - databaseTable = dataSource$databaseTable, + databaseTable = dataSource$dbTable, headerCount = countsForHeader, keyColumns = c("id", "ruleName"), countLocation = 1, @@ -194,7 +194,7 @@ getInclusionRulesTable <- function( cohortCountsModule <- function(id, dataSource, cohortTable = dataSource$cohortTable, - databaseTable = dataSource$databaseTable, + databaseTable = dataSource$dbTable, selectedCohorts, selectedDatabaseIds, cohortIds) { diff --git a/R/cohort-diagnostics-databaseInformation.R b/R/cohort-diagnostics-databaseInformation.R index d0bff6f4..9b239c03 100644 --- a/R/cohort-diagnostics-databaseInformation.R +++ b/R/cohort-diagnostics-databaseInformation.R @@ -58,14 +58,14 @@ databaseInformationView <- function(id) { getMetaDataResults <- function(dataSource, databaseId) { sql <- "SELECT * - FROM @results_database_schema.@metadata + FROM @schema.@metadata WHERE database_id = @database_id;" data <- dataSource$connectionHandler$queryDb( sql = sql, metadata = dataSource$prefixTable("metadata"), - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, database_id = quoteLiterals(databaseId) ) %>% tidyr::tibble() @@ -211,7 +211,7 @@ getExecutionMetadata <- function(dataSource, databaseId) { getDatabaseMetadata <- function(dataSource, databaseTable) { - data <- loadResultsTable(dataSource, "metadata", required = TRUE, tablePrefix = dataSource$tablePrefix) + data <- loadResultsTable(dataSource, "metadata", required = TRUE, cdTablePrefix = dataSource$cdTablePrefix) data <- data %>% tidyr::pivot_wider( id_cols = c("startTime", "databaseId"), @@ -289,10 +289,12 @@ getDatabaseMetadata <- function(dataSource, databaseTable) { } # What this module does is incredibly simple. How it does it is not. -databaseInformationModule <- function(id, - dataSource, - selectedDatabaseIds, - databaseTable = dataSource$databaseTable) { +databaseInformationModule <- function( + id, + dataSource, + selectedDatabaseIds, + databaseTable = dataSource$dbTable +) { ns <- shiny::NS(id) ## Replace this pre-loading nonsense diff --git a/R/cohort-diagnostics-definition.R b/R/cohort-diagnostics-definition.R index 6ff648b2..842979b9 100644 --- a/R/cohort-diagnostics-definition.R +++ b/R/cohort-diagnostics-definition.R @@ -210,11 +210,13 @@ getConceptSetDetailsFromCohortDefinition <- getCohortJsonSql <- function(dataSource, cohortIds) { - sql <- "SELECT * FROM @results_database_schema.@cohort_table WHERE cohort_id IN (@cohort_ids)" - dataSource$connectionHandler$queryDb(sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, - cohort_table = dataSource$cohortTableName, - cohort_ids = cohortIds) + sql <- "SELECT * FROM @schema.@cohort_table WHERE cohort_id IN (@cohort_ids)" + dataSource$connectionHandler$queryDb( + sql = sql, + schema = dataSource$schema, + cohort_table = paste0(dataSource$cgTablePrefix,dataSource$cgTable), + cohort_ids = cohortIds + ) } exportCohortDefinitionsZip <- function(cohortDefinitions, @@ -441,13 +443,13 @@ getCountForConceptIdInCohort <- cohortId, databaseIds) { sql <- "SELECT ics.* - FROM @results_database_schema.@table_name ics + FROM @schema.@table_name ics WHERE ics.cohort_id = @cohort_id AND database_id in (@database_ids);" data <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, cohort_id = cohortId, database_ids = quoteLiterals(databaseIds), table_name = dataSource$prefixTable("included_source_concept"), @@ -519,12 +521,14 @@ getCountForConceptIdInCohort <- #' @param databaseTable data.frame of databasese, databaseId, name #' @param cohortTable data.frame of cohorts, cohortId, cohortName #' @param cohortCountTable data.frame of cohortCounts, cohortId, subjects records -cohortDefinitionsModule <- function(id, - dataSource, - cohortDefinitions, - cohortTable = dataSource$cohortTable, - cohortCountTable = dataSource$cohortCountTable, - databaseTable = dataSource$databaseTable) { +cohortDefinitionsModule <- function( + id, + dataSource, + cohortDefinitions, + cohortTable = dataSource$cohortTable, + cohortCountTable = dataSource$cohortCountTable, + databaseTable = dataSource$dbTable +) { ns <- shiny::NS(id) cohortDefinitionServer <- function(input, output, session) { diff --git a/R/cohort-diagnostics-incidenceRates.R b/R/cohort-diagnostics-incidenceRates.R index 610138fe..d73b65f3 100644 --- a/R/cohort-diagnostics-incidenceRates.R +++ b/R/cohort-diagnostics-incidenceRates.R @@ -17,22 +17,22 @@ # Global ranges for IR values getIncidenceRateRanges <- function(dataSource, minPersonYears = 0) { - sql <- "SELECT DISTINCT age_group FROM @results_database_schema.@ir_table WHERE person_years >= @person_years" + sql <- "SELECT DISTINCT age_group FROM @schema.@ir_table WHERE person_years >= @person_years" ageGroups <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, ir_table = dataSource$prefixTable("incidence_rate"), person_years = minPersonYears, snakeCaseToCamelCase = TRUE ) %>% dplyr::mutate(ageGroup = dplyr::na_if(.data$ageGroup, "")) - sql <- "SELECT DISTINCT calendar_year FROM @results_database_schema.@ir_table WHERE person_years >= @person_years" + sql <- "SELECT DISTINCT calendar_year FROM @schema.@ir_table WHERE person_years >= @person_years" calendarYear <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, ir_table = dataSource$prefixTable("incidence_rate"), person_years = minPersonYears, snakeCaseToCamelCase = TRUE @@ -42,11 +42,11 @@ getIncidenceRateRanges <- function(dataSource, minPersonYears = 0) { ) %>% dplyr::mutate(calendarYear = as.integer(.data$calendarYear)) - sql <- "SELECT DISTINCT gender FROM @results_database_schema.@ir_table WHERE person_years >= @person_years" + sql <- "SELECT DISTINCT gender FROM @schema.@ir_table WHERE person_years >= @person_years" gender <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, ir_table = dataSource$prefixTable("incidence_rate"), person_years = minPersonYears, snakeCaseToCamelCase = TRUE @@ -57,14 +57,14 @@ getIncidenceRateRanges <- function(dataSource, minPersonYears = 0) { sql <- "SELECT min(incidence_rate) as min_ir, max(incidence_rate) as max_ir - FROM @results_database_schema.@ir_table + FROM @schema.@ir_table WHERE person_years >= @person_years AND incidence_rate > 0.0 " incidenceRate <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, ir_table = dataSource$prefixTable("incidence_rate"), person_years = minPersonYears, snakeCaseToCamelCase = TRUE @@ -116,9 +116,9 @@ getIncidenceRateResult <- function(dataSource, checkmate::reportAssertions(collection = errorMessage) sql <- "SELECT ir.*, dt.database_name, cc.cohort_subjects - FROM @results_database_schema.@ir_table ir - INNER JOIN @results_database_schema.@database_table dt ON ir.database_id = dt.database_id - INNER JOIN @results_database_schema.@cc_table cc ON ( + FROM @schema.@ir_table ir + INNER JOIN @schema.@database_table dt ON ir.database_id = dt.database_id + INNER JOIN @schema.@cc_table cc ON ( ir.database_id = cc.database_id AND ir.cohort_id = cc.cohort_id ) WHERE ir.cohort_id in (@cohort_ids) @@ -130,7 +130,7 @@ getIncidenceRateResult <- function(dataSource, data <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, cohort_ids = cohortIds, database_ids = quoteLiterals(databaseIds), gender = stratifyByGender, @@ -139,7 +139,7 @@ getIncidenceRateResult <- function(dataSource, personYears = minPersonYears, ir_table = dataSource$prefixTable("incidence_rate"), cc_table = dataSource$prefixTable("cohort_count"), - database_table = dataSource$databaseTableName, + database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), snakeCaseToCamelCase = TRUE ) %>% tidyr::tibble() diff --git a/R/cohort-diagnostics-inclusionRules.R b/R/cohort-diagnostics-inclusionRules.R index d2b1d9d8..1b44a5f9 100644 --- a/R/cohort-diagnostics-inclusionRules.R +++ b/R/cohort-diagnostics-inclusionRules.R @@ -77,7 +77,7 @@ inclusionRulesView <- function(id) { # inclusion Rules Module inclusionRulesModule <- function(id, dataSource, - databaseTable = dataSource$databaseTable, + databaseTable = dataSource$dbTable, selectedCohort, targetCohortId, selectedDatabaseIds) { diff --git a/R/cohort-diagnostics-indexEventBreakdown.R b/R/cohort-diagnostics-indexEventBreakdown.R index d407c0cc..e35fc847 100644 --- a/R/cohort-diagnostics-indexEventBreakdown.R +++ b/R/cohort-diagnostics-indexEventBreakdown.R @@ -101,7 +101,7 @@ getIndexEventBreakdown <- function(dataSource, concept.vocabulary_id, concept.standard_concept, concept.concept_code - FROM @results_database_schema.@table_name index_event_breakdown + FROM @schema.@table_name index_event_breakdown INNER JOIN @vocabulary_database_schema.@concept_table concept ON index_event_breakdown.concept_id = concept.concept_id WHERE database_id in (@database_id) @@ -109,7 +109,7 @@ getIndexEventBreakdown <- function(dataSource, data <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, vocabulary_database_schema = dataSource$vocabularyDatabaseSchema, cohort_ids = cohortIds, database_id = quoteLiterals(databaseIds), @@ -139,7 +139,7 @@ indexEventBreakdownModule <- function(id, targetCohortId, selectedDatabaseIds, cohortCountTable = dataSource$cohortCountTable, - databaseTable = dataSource$databaseTable) { + databaseTable = dataSource$dbTable) { ns <- shiny::NS(id) serverFunction <- function(input, output, session) { diff --git a/R/cohort-diagnostics-main-ui.R b/R/cohort-diagnostics-main-ui.R index 99409f31..2073d382 100644 --- a/R/cohort-diagnostics-main-ui.R +++ b/R/cohort-diagnostics-main-ui.R @@ -201,7 +201,7 @@ cohortDiagnosticsView <- function(id = "DiagnosticsExplorer") { shiny::conditionalPanel( ns = ns, condition = "input.tabs == 'characterization'", - characterizationView(ns("characterization")) + cohortDiagCharacterizationView(ns("characterization")) ), shiny::conditionalPanel( ns = ns, diff --git a/R/cohort-diagnostics-main.R b/R/cohort-diagnostics-main.R index bc710ede..6306ef91 100644 --- a/R/cohort-diagnostics-main.R +++ b/R/cohort-diagnostics-main.R @@ -17,11 +17,11 @@ # NOTE: here it would be nice to use dbplyr tables - this would allow lazy loading of resources # however, renaming the columns causes an error and its not obvious how it could be resolved -loadResultsTable <- function(dataSource, tableName, required = FALSE, tablePrefix = "") { - selectTableName <- paste0(tablePrefix, tableName) +loadResultsTable <- function(dataSource, tableName, required = FALSE, cdTablePrefix = "") { + selectTableName <- paste0(cdTablePrefix, tableName) resultsTablesOnServer <- tolower(DatabaseConnector::dbListTables(dataSource$connectionHandler$getConnection(), - schema = dataSource$resultsDatabaseSchema)) + schema = dataSource$schema)) if (required || selectTableName %in% resultsTablesOnServer) { if (tableIsEmpty(dataSource, selectTableName)) { @@ -32,7 +32,7 @@ loadResultsTable <- function(dataSource, tableName, required = FALSE, tablePrefi { table <- DatabaseConnector::dbReadTable( dataSource$connectionHandler$getConnection(), - paste(dataSource$resultsDatabaseSchema, selectTableName, sep = ".") + paste(dataSource$schema, selectTableName, sep = ".") ) }, error = function(err) { @@ -54,12 +54,12 @@ loadResultsTable <- function(dataSource, tableName, required = FALSE, tablePrefi # Create empty objects in memory for all other tables. This is used by the Shiny app to decide what tabs to show: tableIsEmpty <- function(dataSource, tableName) { - sql <- "SELECT * FROM @result_schema.@table LIMIT 1" + sql <- "SELECT * FROM @schema.@table LIMIT 1" row <- data.frame() tryCatch({ row <- dataSource$connectionHandler$queryDb( sql, - result_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, table = tableName ) @@ -76,7 +76,7 @@ tableIsEmpty <- function(dataSource, tableName) { getEnabledCdReports <- function(dataSource) { enabledReports <- c() resultsTables <- tolower(DatabaseConnector::dbListTables(dataSource$connectionHandler$getConnection(), - schema = dataSource$resultsDatabaseSchema)) + schema = dataSource$schema)) for (table in dataSource$dataModelSpecifications$tableName %>% unique()) { if (dataSource$prefixTable(table) %in% resultsTables) { @@ -97,51 +97,57 @@ getEnabledCdReports <- function(dataSource) { #' a shiny app. E.g. if you wanted to make a custom R markdown template #' #' @param connectionHandler An instance of a ResultModelManager::connectionHander - manages a connection to a database. -#' @param schema The schema containing the results tables in the database. -#' @param vocabularyDatabaseSchema The schema containing the vocabulary tables in the database. If not provided, defaults to `resultsDatabaseSchema`. -#' @param tablePrefix An optional prefix to add to the table names. -#' @param cohortTableName The name of the cohort table in the database. -#' @param databaseTableName The name of the database table in the database. +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param dataModelSpecificationsPath The path to a file containing specifications for the data model used by the database. #' @param displayProgress display a progress messaage (can only be used inside a shiny reactive context) #' @param dataMigrationsRef The path to a file listing all migrations for the data model that should have been applied #' @return An object of class `CdDataSource`. #' #' @export -createCdDatabaseDataSource <- function(connectionHandler, - schema, - vocabularyDatabaseSchema = schema, - tablePrefix = "", - cohortTableName = paste0(tablePrefix, "cohort"), - databaseTableName = paste0(tablePrefix, "database"), - dataModelSpecificationsPath = system.file("cohort-diagnostics-ref", - "resultsDataModelSpecification.csv", - package = utils::packageName()), - dataMigrationsRef = system.file("cohort-diagnostics-ref", - "migrations.csv", - package = utils::packageName()), - displayProgress = FALSE) { +createCdDatabaseDataSource <- function( + connectionHandler, + resultDatabaseSettings, + #schema, + #vocabularyDatabaseSchema = schema, + #cdTablePrefix = "", + #cohortTableName = paste0(tablePrefix, "cohort"), + #databaseTableName = paste0(tablePrefix, "database"), + dataModelSpecificationsPath = system.file("cohort-diagnostics-ref", + "resultsDataModelSpecification.csv", + package = utils::packageName()), + dataMigrationsRef = system.file("cohort-diagnostics-ref", + "migrations.csv", + package = utils::packageName()), + displayProgress = FALSE +) { checkmate::assertR6(connectionHandler, "ConnectionHandler") - checkmate::assertString(schema) - checkmate::assertString(vocabularyDatabaseSchema, null.ok = TRUE) - checkmate::assertString(tablePrefix, null.ok = TRUE) - checkmate::assertString(cohortTableName, null.ok = TRUE) - checkmate::assertString(databaseTableName, null.ok = TRUE) + checkmate::assertString(resultDatabaseSettings$schema) + checkmate::assertString(resultDatabaseSettings$vocabularyDatabaseSchema, null.ok = TRUE) + checkmate::assertString(resultDatabaseSettings$cdTablePrefix, null.ok = TRUE) + checkmate::assertString(resultDatabaseSettings$cgTable, null.ok = TRUE) + checkmate::assertString(resultDatabaseSettings$databaseTable, null.ok = TRUE) + checkmate::assertString(resultDatabaseSettings$databaseTablePrefix, null.ok = TRUE) checkmate::assertFileExists(dataModelSpecificationsPath) checkmate::assertFileExists(dataMigrationsRef) - if (is.null(vocabularyDatabaseSchema)) { - vocabularyDatabaseSchema <- schema + if (is.null(resultDatabaseSettings$vocabularyDatabaseSchema)) { + resultDatabaseSettings$vocabularyDatabaseSchema <- resultDatabaseSettings$schema } - if (is.null(tablePrefix)) { - tablePrefix <- "" + if (is.null(resultDatabaseSettings$cdTablePrefix)) { + resultDatabaseSettings$cdTablePrefix <- "" } - if (is.null(cohortTableName)) { - cohortTableName <- paste0(tablePrefix, "cohort") + if (is.null(resultDatabaseSettings$cgTable)) { + resultDatabaseSettings$cgTable <- "cohort" } - if (is.null(databaseTableName)) { - databaseTableName <- paste0(tablePrefix, "database") + if (is.null(resultDatabaseSettings$cgTablePrefix)) { + resultDatabaseSettings$cgTablePrefix <- resultDatabaseSettings$cdTablePrefix + } + if (is.null(resultDatabaseSettings$databaseTable)) { + resultDatabaseSettings$databaseTable <- "database" + } + if (is.null(resultDatabaseSettings$databaseTablePrefix)) { + resultDatabaseSettings$databaseTablePrefix <- resultDatabaseSettings$cdTablePrefix } if (displayProgress) { @@ -150,10 +156,10 @@ createCdDatabaseDataSource <- function(connectionHandler, migrations <- data.frame() # Check existence of migrations table - display warnings if not present or if it is out of date tryCatch({ - migrations <- connectionHandler$queryDb("SELECT * FROM @results_database_schema.@table_prefixmigration", + migrations <- connectionHandler$queryDb("SELECT * FROM @schema.@cd_table_prefixmigration", snakeCaseToCamelCase = TRUE, - results_database_schema = schema, - table_prefix = tablePrefix) + schema = resultDatabaseSettings$schema, + cd_table_prefix = resultDatabaseSettings$cdTablePrefix) }, error = function(...) { warning("CohortDiagnotics schema does not contain migrations table. Schema was likely created incorrectly") if (displayProgress) { @@ -177,22 +183,24 @@ createCdDatabaseDataSource <- function(connectionHandler, dataSource <- list( connectionHandler = connectionHandler, - resultsDatabaseSchema = schema, - vocabularyDatabaseSchema = vocabularyDatabaseSchema, + schema = resultDatabaseSettings$schema, + vocabularyDatabaseSchema = resultDatabaseSettings$vocabularyDatabaseSchema, dbms = connectionHandler$dbms(), resultsTablesOnServer = tolower(DatabaseConnector::dbListTables(connectionHandler$getConnection(), - schema = schema)), - tablePrefix = tablePrefix, - prefixTable = function(tableName) { paste0(tablePrefix, tableName) }, + schema = resultDatabaseSettings$schema)), + cdTablePrefix = resultDatabaseSettings$cdTablePrefix, + prefixTable = function(tableName) { paste0(resultDatabaseSettings$cdTablePrefix, tableName) }, prefixVocabTable = function(tableName) { # don't prexfix table if we us a dedicated vocabulary schema - if (vocabularyDatabaseSchema == schema) - return(paste0(tablePrefix, tableName)) + if (resultDatabaseSettings$vocabularyDatabaseSchema == resultDatabaseSettings$schema) + return(paste0(resultDatabaseSettings$cdTablePrefix, tableName)) return(tableName) }, - cohortTableName = cohortTableName, - databaseTableName = databaseTableName, + cgTable = resultDatabaseSettings$cgTable, + cgTablePrefix = resultDatabaseSettings$cgTablePrefix, + databaseTable = resultDatabaseSettings$databaseTable, + databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix, dataModelSpecifications = modelSpec ) @@ -203,7 +211,7 @@ createCdDatabaseDataSource <- function(connectionHandler, if (displayProgress) shiny::setProgress(value = 0.1, message = "Getting database information") - dataSource$databaseTable <- getDatabaseTable(dataSource) + dataSource$dbTable <- getDatabaseTable(dataSource) if (displayProgress) shiny::setProgress(value = 0.2, message = "Getting cohorts") @@ -213,19 +221,19 @@ createCdDatabaseDataSource <- function(connectionHandler, if (displayProgress) shiny::setProgress(value = 0.6, message = "Getting concept sets") - dataSource$conceptSets <- loadResultsTable(dataSource, "concept_sets", tablePrefix = dataSource$tablePrefix) + dataSource$conceptSets <- loadResultsTable(dataSource, "concept_sets", cdTablePrefix = dataSource$cdTablePrefix) if (displayProgress) shiny::setProgress(value = 0.7, message = "Getting counts") - dataSource$cohortCountTable <- loadResultsTable(dataSource, "cohort_count", required = TRUE, tablePrefix = dataSource$tablePrefix) + dataSource$cohortCountTable <- loadResultsTable(dataSource, "cohort_count", required = TRUE, cdTablePrefix = dataSource$cdTablePrefix) dataSource$enabledReports <- dataSource$enabledReports if (displayProgress) shiny::setProgress(value = 0.7, message = "Getting Temporal References") - dataSource$temporalAnalysisRef <- loadResultsTable(dataSource, "temporal_analysis_ref", tablePrefix = dataSource$tablePrefix) + dataSource$temporalAnalysisRef <- loadResultsTable(dataSource, "temporal_analysis_ref", cdTablePrefix = dataSource$cdTablePrefix) dataSource$temporalChoices <- getResultsTemporalTimeRef(dataSource = dataSource) dataSource$temporalCharacterizationTimeIdChoices <- dataSource$temporalChoices %>% @@ -268,7 +276,11 @@ createCdDatabaseDataSource <- function(connectionHandler, # SO much of the app requires this table in memory - it would be much better to re-write queries to not need it! getDatabaseTable <- function(dataSource) { - databaseTable <- loadResultsTable(dataSource, dataSource$databaseTableName, required = TRUE) + databaseTable <- loadResultsTable( + dataSource = dataSource, + tableName = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), + required = TRUE + ) if (nrow(databaseTable) > 0 & "vocabularyVersion" %in% colnames(databaseTable)) { @@ -283,12 +295,18 @@ getDatabaseTable <- function(dataSource) { # SO much of the app requires this table in memory - it would be much better to re-write queries to not need it! getCohortTable <- function(dataSource) { - if (tableIsEmpty(dataSource, dataSource$cohortTableName)) { + if (tableIsEmpty( + dataSource = dataSource, + tableName = paste0(dataSource$cgTablePrefix, dataSource$cgTable) + ) + ) { return(data.frame()) } - cohortTable <- dataSource$connectionHandler$queryDb("SELECT cohort_id, cohort_name FROM @schema.@table_name", - schema = dataSource$resultsDatabaseSchema, - table_name = dataSource$cohortTableName) + cohortTable <- dataSource$connectionHandler$queryDb( + "SELECT cohort_id, cohort_name FROM @schema.@table_name", + schema = dataSource$schema, + table_name = paste0(dataSource$cgTablePrefix, dataSource$cgTable) + ) # Old label if ("cohortDefinitionId" %in% names(cohortTable)) { @@ -305,11 +323,11 @@ getCohortTable <- function(dataSource) { getResultsTemporalTimeRef <- function(dataSource) { sql <- "SELECT * - FROM @results_database_schema.@table_name;" + FROM @schema.@table_name;" temporalTimeRef <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, table_name = dataSource$prefixTable("temporal_time_ref") ) @@ -373,7 +391,7 @@ getResultsTemporalTimeRef <- function(dataSource) { #' @param resultDatabaseSettings results database settings #' @param dataSource dataSource optionally created with createCdDatabaseDataSource #' @export -cohortDiagnosticsSever <- function(id, +cohortDiagnosticsServer <- function(id, connectionHandler, resultDatabaseSettings, dataSource = NULL) { @@ -385,17 +403,18 @@ cohortDiagnosticsSever <- function(id, dataSource <- createCdDatabaseDataSource( connectionHandler = connectionHandler, - schema = resultDatabaseSettings$schema, - vocabularyDatabaseSchema = resultDatabaseSettings$vocabularyDatabaseSchema, - tablePrefix = resultDatabaseSettings$tablePrefix, - cohortTableName = resultDatabaseSettings$cohortTable, - databaseTableName = resultDatabaseSettings$databaseTable, + resultDatabaseSettings = resultDatabaseSettings, + #schema = resultDatabaseSettings$schema, + #vocabularyDatabaseSchema = resultDatabaseSettings$vocabularyDatabaseSchema, # is this in results? + #cdTablePrefix = resultDatabaseSettings$cdTablePrefix, + #cgTableName = resultDatabaseSettings$cgTable, # different for CD? + #databaseTableName = paste0(resultDatabaseSettings$databaseTablePrefix,resultDatabaseSettings$databaseTable), displayProgress = TRUE ) } shiny::moduleServer(id, function(input, output, session) { - databaseTable <- dataSource$databaseTable + databaseTable <- dataSource$dbTable cohortTable <- dataSource$cohortTable conceptSets <- dataSource$conceptSets cohortCountTable <- dataSource$cohortCountTable @@ -667,7 +686,7 @@ cohortDiagnosticsSever <- function(id, cohortIds = cohortIds, selectedDatabaseIds = selectedDatabaseIds) - characterizationModule(id = "characterization", + cohortDiagCharacterizationModule(id = "characterization", dataSource = dataSource) compareCohortCharacterizationModule(id = "compareCohortCharacterization", diff --git a/R/cohort-diagnostics-orphanConcepts.R b/R/cohort-diagnostics-orphanConcepts.R index c921d0be..b816477d 100644 --- a/R/cohort-diagnostics-orphanConcepts.R +++ b/R/cohort-diagnostics-orphanConcepts.R @@ -84,8 +84,8 @@ getOrphanConceptResult <- function(dataSource, c.vocabulary_id, c.concept_code, c.standard_concept - FROM @results_database_schema.@orphan_table_name oc - INNER JOIN @results_database_schema.@cs_table_name cs + FROM @schema.@orphan_table_name oc + INNER JOIN @schema.@cs_table_name cs ON oc.cohort_id = cs.cohort_id AND oc.concept_set_id = cs.concept_set_id INNER JOIN @vocabulary_database_schema.@concept_table c @@ -96,7 +96,7 @@ getOrphanConceptResult <- function(dataSource, data <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, vocabulary_database_schema = dataSource$vocabularyDatabaseSchema, cohort_id = cohortId, database_ids = quoteLiterals(databaseIds), @@ -117,7 +117,7 @@ orphanConceptsModule <- function(id, targetCohortId, selectedConceptSets, conceptSetIds, - databaseTable = dataSource$databaseTable) { + databaseTable = dataSource$dbTable) { ns <- shiny::NS(id) shiny::moduleServer(id, function(input, output, session) { output$selectedCohorts <- shiny::renderUI({ selectedCohort() }) diff --git a/R/cohort-diagnostics-shared.R b/R/cohort-diagnostics-shared.R index 6292c503..30c5dea4 100644 --- a/R/cohort-diagnostics-shared.R +++ b/R/cohort-diagnostics-shared.R @@ -66,8 +66,8 @@ getResultsCohortCounts <- function(dataSource, cohortIds = NULL, databaseIds = NULL) { sql <- "SELECT cc.*, db.database_name - FROM @results_database_schema.@table_name cc - INNER JOIN @results_database_schema.@database_table db ON db.database_id = cc.database_id + FROM @schema.@table_name cc + INNER JOIN @schema.@database_table db ON db.database_id = cc.database_id WHERE cc.cohort_id IS NOT NULL {@use_database_ids} ? { AND cc.database_id in (@database_ids)} {@cohort_ids != ''} ? { AND cc.cohort_id in (@cohort_ids)} @@ -75,12 +75,12 @@ getResultsCohortCounts <- function(dataSource, data <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, cohort_ids = cohortIds, use_database_ids = !is.null(databaseIds), database_ids = quoteLiterals(databaseIds), table_name = dataSource$prefixTable("cohort_count"), - database_table = dataSource$databaseTableName + database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable) ) %>% tidyr::tibble() @@ -90,14 +90,14 @@ getResultsCohortCounts <- function(dataSource, getDatabaseCounts <- function(dataSource, databaseIds) { sql <- "SELECT * - FROM @results_database_schema.@database_table + FROM @schema.@database_table WHERE database_id in (@database_ids);" data <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, database_ids = quoteLiterals(databaseIds), - database_table = dataSource$databaseTableName, + database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), snakeCaseToCamelCase = TRUE ) %>% tidyr::tibble() @@ -662,8 +662,8 @@ resolvedConceptSet <- function(dataSource, c.standard_concept, c.concept_code, rc.database_id - FROM @results_database_schema.@resolved_concepts_table rc - LEFT JOIN @results_database_schema.@concept_table c + FROM @schema.@resolved_concepts_table rc + LEFT JOIN @schema.@concept_table c ON rc.concept_id = c.concept_id WHERE rc.database_id IN (@database_ids) AND rc.cohort_id = @cohortId @@ -672,7 +672,7 @@ resolvedConceptSet <- function(dataSource, resolved <- dataSource$connectionHandler$queryDb( sql = sqlResolved, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, database_ids = quoteLiterals(databaseIds), cohortId = cohortId, concept_set_id = conceptSetId, @@ -702,12 +702,12 @@ mappedConceptSet <- function(dataSource, c1.concept_code FROM ( SELECT DISTINCT concept_id - FROM @results_database_schema.@resolved_concepts + FROM @schema.@resolved_concepts WHERE database_id IN (@databaseIds) AND cohort_id = @cohort_id ) concept_sets - INNER JOIN @results_database_schema.@concept_relationship cr ON concept_sets.concept_id = cr.concept_id_2 - INNER JOIN @results_database_schema.@concept c1 ON cr.concept_id_1 = c1.concept_id + INNER JOIN @schema.@concept_relationship cr ON concept_sets.concept_id = cr.concept_id_2 + INNER JOIN @schema.@concept c1 ON cr.concept_id_1 = c1.concept_id WHERE relationship_id = 'Maps to' AND standard_concept IS NULL ) @@ -716,14 +716,14 @@ mappedConceptSet <- function(dataSource, c.cohort_id, c.concept_set_id, mapped.* - FROM (SELECT DISTINCT concept_id, database_id, cohort_id, concept_set_id FROM @results_database_schema.@resolved_concepts) c + FROM (SELECT DISTINCT concept_id, database_id, cohort_id, concept_set_id FROM @schema.@resolved_concepts) c INNER JOIN resolved_concepts_mapped mapped ON c.concept_id = mapped.resolved_concept_id {@cohort_id != ''} ? { WHERE c.cohort_id = @cohort_id}; " mapped <- dataSource$connectionHandler$queryDb( sql = sqlMapped, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, databaseIds = quoteLiterals(databaseIds), concept = dataSource$prefixTable("concept"), concept_relationship = dataSource$prefixTable("concept_relationship"), @@ -816,12 +816,12 @@ queryResultCovariateValue <- function(dataSource, temporalTimeRefData <- dataSource$connectionHandler$queryDb( sql = "SELECT * - FROM @results_database_schema.@table_name + FROM @schema.@table_name WHERE (time_id IS NOT NULL AND time_id != 0) {@start_day != \"\"} ? { AND start_day IN (@start_day)} {@end_day != \"\"} ? { AND end_day IN (@end_day)};", snakeCaseToCamelCase = TRUE, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, table_name = dataSource$prefixTable("temporal_time_ref"), start_day = startDay, end_day = endDay @@ -836,27 +836,27 @@ queryResultCovariateValue <- function(dataSource, temporalAnalysisRefData <- dataSource$connectionHandler$queryDb( sql = "SELECT * - FROM @results_database_schema.@table_name + FROM @schema.@table_name WHERE analysis_id IS NOT NULL {@analysis_ids != \"\"} ? { AND analysis_id IN (@analysis_ids)} ;", analysis_ids = analysisIds, table_name = dataSource$prefixTable("temporal_analysis_ref"), snakeCaseToCamelCase = TRUE, - results_database_schema = dataSource$resultsDatabaseSchema + schema = dataSource$schema ) %>% dplyr::tibble() temporalCovariateRefData <- dataSource$connectionHandler$queryDb( sql = "SELECT * - FROM @results_database_schema.@table_name + FROM @schema.@table_name WHERE covariate_id IS NOT NULL {@analysis_ids != \"\"} ? { AND analysis_id IN (@analysis_ids)};", snakeCaseToCamelCase = TRUE, analysis_ids = analysisIds, table_name = dataSource$prefixTable("temporal_covariate_ref"), - results_database_schema = dataSource$resultsDatabaseSchema + schema = dataSource$schema ) %>% dplyr::tibble() @@ -865,8 +865,8 @@ queryResultCovariateValue <- function(dataSource, temporalCovariateValueData <- dataSource$connectionHandler$queryDb( sql = "SELECT tcv.* - FROM @results_database_schema.@table_name tcv - INNER JOIN @results_database_schema.@ref_table_name ref ON ref.covariate_id = tcv.covariate_id + FROM @schema.@table_name tcv + INNER JOIN @schema.@ref_table_name ref ON ref.covariate_id = tcv.covariate_id WHERE ref.covariate_id IS NOT NULL {@analysis_ids != \"\"} ? { AND ref.analysis_id IN (@analysis_ids)} {@cohort_id != \"\"} ? { AND tcv.cohort_id IN (@cohort_id)} @@ -881,7 +881,7 @@ queryResultCovariateValue <- function(dataSource, table_name = dataSource$prefixTable("temporal_covariate_value"), ref_table_name = dataSource$prefixTable("temporal_covariate_ref"), cohort_id = cohortIds, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, filter_mean_threshold = meanThreshold ) %>% dplyr::tibble() %>% @@ -893,7 +893,7 @@ queryResultCovariateValue <- function(dataSource, temporalCovariateValueDistData <- dataSource$connectionHandler$queryDb( sql = "SELECT * - FROM @results_database_schema.@table_name tcv + FROM @schema.@table_name tcv WHERE covariate_id IS NOT NULL {@covariate_id != \"\"} ? { AND covariate_id IN (@covariate_id)} {@cohort_id != \"\"} ? { AND cohort_id IN (@cohort_id)} @@ -907,7 +907,7 @@ queryResultCovariateValue <- function(dataSource, database_id = quoteLiterals(databaseIds), cohort_id = cohortIds, table_name = dataSource$prefixTable("temporal_covariate_value_dist"), - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, filter_mean_threshold = meanThreshold ) %>% dplyr::tibble() %>% @@ -946,7 +946,7 @@ getInclusionRuleStats <- function(dataSource, databaseIds, modeId = 1) { sql <- "SELECT * - FROM @resultsDatabaseSchema.@table_name + FROM @schema.@table_name WHERE database_id in (@database_id) {@cohort_ids != ''} ? { AND cohort_id in (@cohort_ids)} ;" @@ -954,7 +954,7 @@ getInclusionRuleStats <- function(dataSource, inclusion <- dataSource$connectionHandler$queryDb( sql = sql, - resultsDatabaseSchema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, cohort_ids = cohortIds, database_id = quoteLiterals(databaseIds), table_name = dataSource$prefixTable("cohort_inclusion"), @@ -965,7 +965,7 @@ getInclusionRuleStats <- function(dataSource, inclusionResults <- dataSource$connectionHandler$queryDb( sql = sql, - resultsDatabaseSchema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, cohort_ids = cohortIds, database_id = quoteLiterals(databaseIds), table_name = dataSource$prefixTable("cohort_inc_result"), @@ -976,7 +976,7 @@ getInclusionRuleStats <- function(dataSource, inclusionStats <- dataSource$connectionHandler$queryDb( sql = sql, - resultsDatabaseSchema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, cohort_ids = cohortIds, database_id = quoteLiterals(databaseIds), table_name = dataSource$prefixTable("cohort_inc_stats"), diff --git a/R/cohort-diagnostics-timeDistributions.R b/R/cohort-diagnostics-timeDistributions.R index 3df92f77..f786aa46 100644 --- a/R/cohort-diagnostics-timeDistributions.R +++ b/R/cohort-diagnostics-timeDistributions.R @@ -353,7 +353,7 @@ timeDistributionsModule <- function(id, selectedDatabaseIds, cohortIds, cohortTable = dataSource$cohortTable, - databaseTable = dataSource$databaseTable) { + databaseTable = dataSource$dbTable) { shiny::moduleServer(id, function(input, output, session) { ns <- session$ns output$selectedCohorts <- shiny::renderUI({ selectedCohorts() }) diff --git a/R/cohort-diagnostics-visitContext.R b/R/cohort-diagnostics-visitContext.R index 7feee0f4..942848e2 100644 --- a/R/cohort-diagnostics-visitContext.R +++ b/R/cohort-diagnostics-visitContext.R @@ -89,7 +89,7 @@ getVisitContextResults <- function(dataSource, sql <- "SELECT visit_context.*, standard_concept.concept_name AS visit_concept_name - FROM @results_database_schema.@table_name visit_context + FROM @schema.@table_name visit_context INNER JOIN @vocabulary_database_schema.@concept_table standard_concept ON visit_context.visit_concept_id = standard_concept.concept_id WHERE database_id in (@database_id) @@ -97,7 +97,7 @@ getVisitContextResults <- function(dataSource, data <- dataSource$connectionHandler$queryDb( sql = sql, - results_database_schema = dataSource$resultsDatabaseSchema, + schema = dataSource$schema, vocabulary_database_schema = dataSource$vocabularyDatabaseSchema, cohort_ids = cohortIds, database_id = quoteLiterals(databaseIds), @@ -121,7 +121,7 @@ visitContextModule <- function(id = "visitContext", selectedDatabaseIds, targetCohortId, cohortCountTable = dataSource$cohortCountTable, - databaseTable = dataSource$databaseTable) { + databaseTable = dataSource$dbTable) { ns <- shiny::NS(id) shiny::moduleServer(id, function(input, output, session) { output$selectedCohorts <- shiny::renderUI(selectedCohort()) diff --git a/R/cohort-method-attrition.R b/R/cohort-method-attrition.R index f43e49c6..f2f71518 100644 --- a/R/cohort-method-attrition.R +++ b/R/cohort-method-attrition.R @@ -44,15 +44,19 @@ cohortMethodAttritionViewer <- function(id) { #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix -#' @param databaseTable databaseTable +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' the PLE attrition results content server #' #' @export -cohortMethodAttritionServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, databaseTable) { +cohortMethodAttritionServer <- function( + id, + selectedRow, + inputParams, + connectionHandler, + resultDatabaseSettings + ) { shiny::moduleServer( id, @@ -64,15 +68,15 @@ cohortMethodAttritionServer <- function(id, selectedRow, inputParams, connection if (is.null(row)) { return(NULL) } else { - attrition <- getCohortMethodAttrition(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - databaseTable = databaseTable, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - outcomeId = inputParams()$outcome, - databaseId = row$databaseId, - analysisId = row$analysisId) + attrition <- getCohortMethodAttrition( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetId = inputParams()$target, + comparatorId = inputParams()$comparator, + outcomeId = inputParams()$outcome, + databaseId = row$databaseId, + analysisId = row$analysisId + ) plot <- drawCohortMethodAttritionDiagram(attrition) return(plot) } diff --git a/R/cohort-method-covariateBalance.R b/R/cohort-method-covariateBalance.R index ee2a2e7c..808c25d0 100644 --- a/R/cohort-method-covariateBalance.R +++ b/R/cohort-method-covariateBalance.R @@ -66,15 +66,20 @@ cohortMethodCovariateBalanceViewer <- function(id) { #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param metaAnalysisDbIds metaAnalysisDbIds #' #' @return #' the PLE covariate balance content server #' #' @export -cohortMethodCovariateBalanceServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { +cohortMethodCovariateBalanceServer <- function( + id, + selectedRow, + inputParams, + connectionHandler, + resultDatabaseSettings, + metaAnalysisDbIds = NULL) { shiny::moduleServer( id, @@ -86,8 +91,7 @@ cohortMethodCovariateBalanceServer <- function(id, selectedRow, inputParams, con balance <- tryCatch({ getCohortMethodCovariateBalanceShared( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, + resultDatabaseSettings = resultDatabaseSettings, targetId = inputParams()$target, comparatorId = inputParams()$comparator, databaseId = row$databaseId, @@ -199,19 +203,22 @@ cohortMethodCovariateBalanceServer <- function(id, selectedRow, inputParams, con if (is.null(row) || !(row$databaseId %in% metaAnalysisDbIds)) { return(NULL) } else { - balanceSummary <- getCohortMethodCovariateBalanceSummary(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - analysisId = row$analysisId, - databaseId = row$analysisId, - beforeLabel = paste("Before", row$psStrategy), - afterLabel = paste("After", row$psStrategy)) - plot <- plotCohortMethodCovariateBalanceSummary(balanceSummary, - threshold = 0.1, - beforeLabel = paste("Before", row$psStrategy), - afterLabel = paste("After", row$psStrategy)) + balanceSummary <- getCohortMethodCovariateBalanceSummary( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetId = inputParams()$target, + comparatorId = inputParams()$comparator, + analysisId = row$analysisId, + databaseId = row$analysisId, + beforeLabel = paste("Before", row$psStrategy), + afterLabel = paste("After", row$psStrategy) + ) + plot <- plotCohortMethodCovariateBalanceSummary( + balanceSummary, + threshold = 0.1, + beforeLabel = paste("Before", row$psStrategy), + afterLabel = paste("After", row$psStrategy) + ) return(plot) } }) @@ -252,8 +259,7 @@ cohortMethodCovariateBalanceServer <- function(id, selectedRow, inputParams, con getCohortMethodCovariateBalanceShared <- function( connectionHandler, - resultsSchema, - tablePrefix, + resultDatabaseSettings, targetId, comparatorId, analysisId, @@ -276,9 +282,9 @@ getCohortMethodCovariateBalanceShared <- function( cmscb.comparator_mean_after after_matching_mean_comparator, abs(cmscb.std_diff_after) abs_after_matching_std_diff FROM - @results_schema.@table_prefixshared_covariate_balance cmscb - JOIN @results_schema.@table_prefixcovariate cmc ON cmscb.covariate_id = cmc.covariate_id AND cmscb.analysis_id = cmc.analysis_id AND cmscb.database_id = cmc.database_id -- database_id optional - -- JOIN @results_schema.@table_prefixcovariate_analysis cmca ON cmca.analysis_id = cmc.analysis_id -- question: shouldn't we have a covariate_analysis_id in @table_prefixcovariate table? + @results_schema.@cm_table_prefixshared_covariate_balance cmscb + JOIN @results_schema.@cm_table_prefixcovariate cmc ON cmscb.covariate_id = cmc.covariate_id AND cmscb.analysis_id = cmc.analysis_id AND cmscb.database_id = cmc.database_id -- database_id optional + -- JOIN @results_schema.@cm_table_prefixcovariate_analysis cmca ON cmca.analysis_id = cmc.analysis_id -- question: shouldn't we have a covariate_analysis_id in @table_prefixcovariate table? WHERE cmscb.target_id = @target_id AND cmscb.comparator_id = @comparator_id @@ -289,8 +295,8 @@ getCohortMethodCovariateBalanceShared <- function( shiny::incProgress(1/3, detail = paste("Extracting")) result <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, + results_schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, target_id = targetId, comparator_id = comparatorId, analysis_id = analysisId, @@ -307,22 +313,24 @@ getCohortMethodCovariateBalanceShared <- function( } -getCohortMethodCovariateBalanceSummary <- function(connectionHandler, - resultsSchema, - tablePrefix, - databaseId, - targetId, - comparatorId, analysisId, - beforeLabel = "Before matching", - afterLabel = "After matching") { +getCohortMethodCovariateBalanceSummary <- function( + connectionHandler, + resultDatabaseSettings, + databaseId, + targetId, + comparatorId, analysisId, + beforeLabel = "Before matching", + afterLabel = "After matching" + ) { - balance <- getCohortMethodCovariateBalanceShared(connectionHandler = connectionHandler, - targetId = targetId, - comparatorId = comparatorId, - analysisId = analysisId, - resultsSchema, - tablePrefix, - databaseId = databaseId) + balance <- getCohortMethodCovariateBalanceShared( + connectionHandler = connectionHandler, + targetId = targetId, + comparatorId = comparatorId, + analysisId = analysisId, + resultDatabaseSettings = resultDatabaseSettings, + databaseId = databaseId + ) balanceBefore <- balance %>% dplyr::group_by(.data$databaseId) %>% dplyr::summarise(covariateCount = dplyr::n(), diff --git a/R/cohort-method-diagnosticsSummary.R b/R/cohort-method-diagnosticsSummary.R index 20a58e4c..fd2cdd05 100644 --- a/R/cohort-method-diagnosticsSummary.R +++ b/R/cohort-method-diagnosticsSummary.R @@ -57,10 +57,7 @@ cohortMethodDiagnosticsSummaryViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix -#' @param cohortTablePrefix cohortTablePrefix -#' @param databaseTable databaseTable +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' the PLE diagnostics summary results @@ -69,10 +66,7 @@ cohortMethodDiagnosticsSummaryViewer <- function(id) { cohortMethodDiagnosticsSummaryServer <- function( id, connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, - databaseTable + resultDatabaseSettings ) { shiny::moduleServer( @@ -81,29 +75,22 @@ cohortMethodDiagnosticsSummaryServer <- function( targetIds <- getCmDiagCohorts( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - cohortTablePrefix = cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettings, type = 'target' ) outcomeIds <- getCmDiagCohorts( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - cohortTablePrefix = cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettings, type = 'outcome' ) comparatorIds <- getCmDiagCohorts( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - cohortTablePrefix = cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettings, type = 'comparator' ) analysisIds <- getCmDiagAnalyses( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix + resultDatabaseSettings = resultDatabaseSettings ) inputSelected <- inputSelectionServer( @@ -196,10 +183,7 @@ cohortMethodDiagnosticsSummaryServer <- function( data <- shiny::reactive({ getCmDiagnosticsData( connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, - databaseTable, + resultDatabaseSettings = resultDatabaseSettings, targetIds = inputSelected()$targetIds, outcomeIds = inputSelected()$outcomeIds, comparatorIds = inputSelected()$comparatorIds, @@ -397,9 +381,7 @@ diagnosticSummaryFormat <- function( getCmDiagCohorts <- function( connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, + resultDatabaseSettings, type = 'target' ){ @@ -408,17 +390,17 @@ getCmDiagCohorts <- function( cgcd1.cohort_name as names, cgcd1.cohort_definition_id FROM - @results_schema.@table_prefixdiagnostics_summary cmds + @schema.@cm_table_prefixdiagnostics_summary cmds INNER JOIN - @results_schema.@cohort_table_prefixcohort_definition cgcd1 + @schema.@cg_table_prefixcohort_definition cgcd1 ON cmds.@type_id = cgcd1.cohort_definition_id; " result <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, type = type ) @@ -432,8 +414,7 @@ getCmDiagCohorts <- function( getCmDiagAnalyses <- function( connectionHandler, - resultsSchema, - tablePrefix + resultDatabaseSettings ){ sql <- " @@ -441,17 +422,17 @@ getCmDiagAnalyses <- function( cma.analysis_id, cma.description as names FROM - @results_schema.@table_prefixdiagnostics_summary cmds + @schema.@cm_table_prefixdiagnostics_summary cmds INNER JOIN - @results_schema.@table_prefixanalysis cma + @schema.@cm_table_prefixanalysis cma ON cmds.analysis_id = cma.analysis_id ; " result <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix ) res <- result$analysisId @@ -466,10 +447,7 @@ getCmDiagAnalyses <- function( getCmDiagnosticsData <- function( connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, - databaseTable, + resultDatabaseSettings, targetIds, outcomeIds, comparatorIds = NULL, @@ -496,12 +474,12 @@ getCmDiagnosticsData <- function( cmds.ease_diagnostic, cmds.unblind FROM - @results_schema.@table_prefixdiagnostics_summary cmds - INNER JOIN @results_schema.@table_prefixanalysis cma ON cmds.analysis_id = cma.analysis_id - INNER JOIN @results_schema.@database_table dmd ON dmd.database_id = cmds.database_id - INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd1 ON cmds.target_id = cgcd1.cohort_definition_id - INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd2 ON cmds.comparator_id = cgcd2.cohort_definition_id - INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd3 ON cmds.outcome_id = cgcd3.cohort_definition_id + @schema.@cm_table_prefixdiagnostics_summary cmds + INNER JOIN @schema.@cm_table_prefixanalysis cma ON cmds.analysis_id = cma.analysis_id + INNER JOIN @schema.@database_table dmd ON dmd.database_id = cmds.database_id + INNER JOIN @schema.@cg_table_prefixcohort_definition cgcd1 ON cmds.target_id = cgcd1.cohort_definition_id + INNER JOIN @schema.@cg_table_prefixcohort_definition cgcd2 ON cmds.comparator_id = cgcd2.cohort_definition_id + INNER JOIN @schema.@cg_table_prefixcohort_definition cgcd3 ON cmds.outcome_id = cgcd3.cohort_definition_id where cgcd1.cohort_definition_id in (@targets) {@use_comparators}?{and cgcd2.cohort_definition_id in (@comparators)} @@ -512,11 +490,11 @@ getCmDiagnosticsData <- function( result <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix, - database_table = databaseTable, - + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + targets = paste0(targetIds, collapse = ','), comparators = paste0(comparatorIds, collapse = ','), outcomes = paste0(outcomeIds, collapse = ','), diff --git a/R/cohort-method-forestPlot.R b/R/cohort-method-forestPlot.R index edaa304e..1caa5172 100644 --- a/R/cohort-method-forestPlot.R +++ b/R/cohort-method-forestPlot.R @@ -49,9 +49,7 @@ cohortMethodForestPlotViewer <- function(id) { #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest #' @param metaAnalysisDbIds metaAnalysisDbIds -#' @param resultsSchema resultsSchema -#' @param tablePrefix tablePrefix -#' @param databaseTable databaseTable +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' the PLE forest plot content server @@ -59,9 +57,7 @@ cohortMethodForestPlotViewer <- function(id) { #' @export cohortMethodForestPlotServer <- function( id, connectionHandler, selectedRow, inputParams, metaAnalysisDbIds = NULL, - resultsSchema, - tablePrefix, - databaseTable + resultDatabaseSettings ) { shiny::moduleServer( @@ -72,14 +68,14 @@ cohortMethodForestPlotServer <- function( if (is.null(row) || !(row$databaseId %in% metaAnalysisDbIds)) { return(NULL) } else { - results <- getCohortMethodMainResults(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - databaseTable = databaseTable, - targetIds = row$targetId, - comparatorIds = row$comparatorId, - outcomeIds = row$outcomeId, - analysisIds = row$analysisId) + results <- getCohortMethodMainResults( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetIds = row$targetId, + comparatorIds = row$comparatorId, + outcomeIds = row$outcomeId, + analysisIds = row$analysisId + ) plot <- plotCohortMethodForest(results) return(plot) } diff --git a/R/cohort-method-kaplainMeier.R b/R/cohort-method-kaplainMeier.R index 78535a93..8474f9c8 100644 --- a/R/cohort-method-kaplainMeier.R +++ b/R/cohort-method-kaplainMeier.R @@ -45,17 +45,21 @@ cohortMethodKaplanMeierViewer <- function(id) { #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix -#' @param cohortTablePrefix cohortTablePrefix -#' @param databaseTable databaseTable +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param metaAnalysisDbIds metaAnalysisDbIds #' #' @return #' the PLE Kaplain Meier content server #' #' @export -cohortMethodKaplanMeierServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, databaseTable, metaAnalysisDbIds = NULL) { +cohortMethodKaplanMeierServer <- function( + id, + selectedRow, + inputParams, + connectionHandler, + resultDatabaseSettings, + metaAnalysisDbIds = NULL + ) { shiny::moduleServer( id, @@ -76,33 +80,37 @@ cohortMethodKaplanMeierServer <- function(id, selectedRow, inputParams, connecti if (is.null(row)) { return(NULL) } else { - km <- getCohortMethodKaplanMeier(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - databaseTable = databaseTable, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - outcomeId = inputParams()$outcome, - databaseId = row$databaseId, - analysisId = row$analysisId) + km <- getCohortMethodKaplanMeier( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetId = inputParams()$target, + comparatorId = inputParams()$comparator, + outcomeId = inputParams()$outcome, + databaseId = row$databaseId, + analysisId = row$analysisId + ) # hack to fix data insert replacing NA with 0 removeInd <- km$targetAtRisk == 0 & km$comparatorAtRisk == 0 km$targetAtRisk[removeInd] <- NA km$comparatorAtRisk[removeInd] <- NA - targetName <- getCohortNameFromId(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - cohortTablePrefix = cohortTablePrefix, - cohortId = inputParams()$target) - comparatorName <- getCohortNameFromId(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - cohortTablePrefix = cohortTablePrefix, - cohortId = inputParams()$comparator) + targetName <- getCohortNameFromId( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + cohortId = inputParams()$target + ) + comparatorName <- getCohortNameFromId( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + cohortId = inputParams()$comparator + ) - plot <- plotCohortMethodKaplanMeier(kaplanMeier = km, - targetName = targetName$cohortName, - comparatorName = comparatorName$cohortName) + plot <- plotCohortMethodKaplanMeier( + kaplanMeier = km, + targetName = targetName$cohortName, + comparatorName = comparatorName$cohortName + ) return(plot) } }) diff --git a/R/cohort-method-main.R b/R/cohort-method-main.R index baefa1eb..5ba555db 100644 --- a/R/cohort-method-main.R +++ b/R/cohort-method-main.R @@ -162,10 +162,10 @@ cohortMethodServer <- function( dataFolder <- NULL output$targetWidget <- shiny::renderUI({ - targets <- getCohortMethodTargetChoices(connectionHandler, - resultDatabaseSettings$schema, - resultDatabaseSettings$tablePrefix, - resultDatabaseSettings$cohortTablePrefix) + targets <- getCohortMethodTargetChoices( + connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) shiny::selectInput(inputId = session$ns("target"), label = "Target", choices = getCohortMethodSelectNamedChoices(targets$targetId, @@ -173,10 +173,10 @@ cohortMethodServer <- function( }) output$comparatorWidget <- shiny::renderUI({ - comparators <- getCohortMethodComparatorChoices(connectionHandler, - resultDatabaseSettings$schema, - resultDatabaseSettings$tablePrefix, - resultDatabaseSettings$cohortTablePrefix) + comparators <- getCohortMethodComparatorChoices( + connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) shiny::selectInput(inputId = session$ns("comparator"), label = "Comparator", choices = getCohortMethodSelectNamedChoices(comparators$comparatorId, @@ -184,20 +184,20 @@ cohortMethodServer <- function( }) output$outcomeWidget <- shiny::renderUI({ - outcomes <- getCohortMethodOutcomeChoices(connectionHandler, - resultDatabaseSettings$schema, - resultDatabaseSettings$tablePrefix, - resultDatabaseSettings$cohortTablePrefix) + outcomes <- getCohortMethodOutcomeChoices( + connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) shiny::selectInput(inputId = session$ns("outcome"), label = "Outcome", choices = getCohortMethodSelectNamedChoices(outcomes$outcomeId, outcomes$cohortName)) }) output$databaseWidget<- shiny::renderUI({ - databases <- getCohortMethodDatabaseChoices(connectionHandler, - resultDatabaseSettings$schema, - resultDatabaseSettings$tablePrefix, - resultDatabaseSettings$databaseTable) + databases <- getCohortMethodDatabaseChoices( + connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) shiny::checkboxGroupInput(inputId = session$ns("database"), label = "Data source", choices = getCohortMethodSelectNamedChoices(databases$databaseId, @@ -205,9 +205,10 @@ cohortMethodServer <- function( selected = unique(databases$databaseId)) }) output$analysisWidget <- shiny::renderUI({ - analyses <- getCmAnalysisOptions(connectionHandler, - resultDatabaseSettings$schema, - resultDatabaseSettings$tablePrefix) + analyses <- getCmAnalysisOptions( + connectionHandler, + resultDatabaseSettings + ) shiny::checkboxGroupInput(inputId = session$ns("analysis"), label = "Analysis", choices = getCohortMethodSelectNamedChoices(analyses$analysisId, @@ -227,20 +228,19 @@ cohortMethodServer <- function( }) - cohortMethodDiagnosticsSummaryServer(id = "estimationDiganostics", - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix, - databaseTable = resultDatabaseSettings$databaseTable) + cohortMethodDiagnosticsSummaryServer( + id = "estimationDiganostics", + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - selectedRow <- cohortMethodResultsTableServer(id = "resultsTable", - connectionHandler = connectionHandler, - inputParams = inputParams, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - databaseTable = resultDatabaseSettings$databaseTable) + selectedRow <- cohortMethodResultsTableServer( + id = "resultsTable", + connectionHandler = connectionHandler, + inputParams = inputParams, + resultDatabaseSettings = resultDatabaseSettings + ) output$rowIsSelected <- shiny::reactive({ return(!is.null(selectedRow())) @@ -283,73 +283,79 @@ cohortMethodServer <- function( shiny::outputOptions(output, "isMetaAnalysis", suspendWhenHidden = FALSE) - cohortMethodPowerServer(id = "power", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - resultDatabaseSettings$tablePrefix) + cohortMethodPowerServer( + id = "power", + selectedRow = selectedRow, + inputParams = inputParams, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - cohortMethodAttritionServer(id = "attrition", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - databaseTable = resultDatabaseSettings$cohortTablePrefix) + cohortMethodAttritionServer( + id = "attrition", + selectedRow = selectedRow, + inputParams = inputParams, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - cohortMethodPopulationCharacteristicsServer(id = "popCharacteristics", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix) + cohortMethodPopulationCharacteristicsServer( + id = "popCharacteristics", + selectedRow = selectedRow, + inputParams = inputParams, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - cohortMethodPropensityModelServer(id = "propensityModel", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix) + cohortMethodPropensityModelServer( + id = "propensityModel", + selectedRow = selectedRow, + inputParams = inputParams, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - cohortMethodPropensityScoreDistServer(id = "propensityScoreDist", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix) + cohortMethodPropensityScoreDistServer( + id = "propensityScoreDist", + selectedRow = selectedRow, + inputParams = inputParams, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - cohortMethodCovariateBalanceServer(id = "covariateBalance", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix) + cohortMethodCovariateBalanceServer( + id = "covariateBalance", + selectedRow = selectedRow, + inputParams = inputParams, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - cohortMethodSystematicErrorServer(id = "systematicError", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix) + cohortMethodSystematicErrorServer( + id = "systematicError", + selectedRow = selectedRow, + inputParams = inputParams, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - cohortMethodKaplanMeierServer(id = "kaplanMeier", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - tablePrefix = resultDatabaseSettings$tablePrefix, - cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix, - databaseTable = resultDatabaseSettings$databaseTable) + cohortMethodKaplanMeierServer( + id = "kaplanMeier", + selectedRow = selectedRow, + inputParams = inputParams, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) #TODO: complete once MA implemented # estimationForestPlotServer("forestPlot", connection, selectedRow, inputParams) #TODO: revisit once subgroup example conducted - cohortMethodSubgroupsServer(id = "subgroups", - selectedRow = selectedRow, - inputParams = inputParams) + cohortMethodSubgroupsServer( + id = "subgroups", + selectedRow = selectedRow, + inputParams = inputParams + ) } ) diff --git a/R/cohort-method-populationCharacteristics.R b/R/cohort-method-populationCharacteristics.R index b22f980a..56cfd621 100644 --- a/R/cohort-method-populationCharacteristics.R +++ b/R/cohort-method-populationCharacteristics.R @@ -41,8 +41,7 @@ cohortMethodPopulationCharacteristicsViewer <- function(id) { #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' the PLE population characteristics content server @@ -53,8 +52,7 @@ cohortMethodPopulationCharacteristicsServer <- function( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix + resultDatabaseSettings ) { shiny::moduleServer( @@ -81,8 +79,7 @@ cohortMethodPopulationCharacteristicsServer <- function( } else { balance <- getCohortMethodPopChar( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, + resultDatabaseSettings = resultDatabaseSettings, targetId = inputParams()$target, comparatorId = inputParams()$comparator, outcomeId = inputParams()$outcome, @@ -135,8 +132,7 @@ cohortMethodPopulationCharacteristicsServer <- function( getCohortMethodPopChar <- function( connectionHandler, - resultsSchema, - tablePrefix, + resultDatabaseSettings, targetId, comparatorId, analysisId, @@ -160,14 +156,14 @@ getCohortMethodPopChar <- function( cmcb.comparator_mean_after after_matching_mean_comparator, abs(cmcb.std_diff_after) abs_after_matching_std_diff FROM - (select * from @results_schema.@table_prefixcovariate_balance + (select * from @schema.@cm_table_prefixcovariate_balance WHERE target_id = @target_id AND comparator_id = @comparator_id AND outcome_id = @outcome_id AND analysis_id = @analysis_id AND database_id = '@database_id' ) as cmcb - INNER JOIN @results_schema.@table_prefixcovariate cmc + INNER JOIN @schema.@cm_table_prefixcovariate cmc ON cmcb.covariate_id = cmc.covariate_id AND cmcb.analysis_id = cmc.analysis_id @@ -178,8 +174,8 @@ getCohortMethodPopChar <- function( shiny::incProgress(1/3, detail = paste("Extracting")) result <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, target_id = targetId, comparator_id = comparatorId, outcome_id = outcomeId, diff --git a/R/cohort-method-power.R b/R/cohort-method-power.R index 84e65b1d..f68dabf3 100644 --- a/R/cohort-method-power.R +++ b/R/cohort-method-power.R @@ -44,15 +44,21 @@ cohortMethodPowerViewer <- function(id) { #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param metaAnalysisDbIds metaAnalysisDbIds #' #' @return #' the PLE systematic error power server #' #' @export -cohortMethodPowerServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { +cohortMethodPowerServer <- function( + id, + selectedRow, + inputParams, + connectionHandler, + resultDatabaseSettings, + metaAnalysisDbIds = NULL + ) { shiny::moduleServer( id, @@ -77,34 +83,11 @@ cohortMethodPowerServer <- function(id, selectedRow, inputParams, connectionHand if (is.null(row)) { return(NULL) } else { - #TODO: update once MA implemented - if (FALSE && row$databaseId %in% metaAnalysisDbIds) { - results <- getCohortMethodMainResults(connectionHandler = connectionHandler, - targetIds = row$targetId, - comparatorIds = row$comparatorId, - outcomeIds = row$outcomeId, - analysisIds = row$analysisId) - table <- prepareCohortMethodPowerTable(results, connectionHandler, resultsSchema) - table$description <- NULL - if (!row$unblind) { - table$targetOutcomes <- NA - table$comparatorOutcomes <- NA - table$targetIr <- NA - table$comparatorIr <- NA - } - table$databaseId[table$databaseId %in% metaAnalysisDbIds] <- "Summary" - colnames(table) <- c("Source", - "Target subjects", - "Comparator subjects", - "Target years", - "Comparator years", - "Target events", - "Comparator events", - "Target IR (per 1,000 PY)", - "Comparator IR (per 1,000 PY)", - "MDRR") - } else { - table <- prepareCohortMethodPowerTable(row, connectionHandler, resultsSchema, tablePrefix) + table <- prepareCohortMethodPowerTable( + row, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) table$description <- NULL table$databaseId <- NULL if (!row$unblind) { @@ -122,7 +105,7 @@ cohortMethodPowerServer <- function(id, selectedRow, inputParams, connectionHand "Target IR (per 1,000 PY)", "Comparator IR (per 1,000 PY)", "MDRR") - } + return(table) } }) @@ -144,24 +127,16 @@ cohortMethodPowerServer <- function(id, selectedRow, inputParams, connectionHand if (is.null(row)) { return(NULL) } else { - if (FALSE && row$databaseId %in% metaAnalysisDbIds) { - # TODO: update when MA implemented - followUpDist <- getCmFollowUpDist(#cmFollowUpDist = cmFollowUpDist, + followUpDist <- getCmFollowUpDist( connectionHandler = connectionHandler, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - outcomeId = inputParams()$outcome, - analysisId = row$analysisId) - } else { - followUpDist <- getCmFollowUpDist(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - outcomeId = inputParams()$outcome, - databaseId = row$databaseId, - analysisId = row$analysisId) - } + resultDatabaseSettings = resultDatabaseSettings, + targetId = inputParams()$target, + comparatorId = inputParams()$comparator, + outcomeId = inputParams()$outcome, + databaseId = row$databaseId, + analysisId = row$analysisId + ) + table <- prepareCohortMethodFollowUpDistTable(followUpDist) return(table) } diff --git a/R/cohort-method-propensityModel.R b/R/cohort-method-propensityModel.R index c5752a24..343a9829 100644 --- a/R/cohort-method-propensityModel.R +++ b/R/cohort-method-propensityModel.R @@ -41,14 +41,19 @@ cohortMethodPropensityModelViewer <- function(id) { #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' the PLE propensity score model #' #' @export -cohortMethodPropensityModelServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix) { +cohortMethodPropensityModelServer <- function( + id, + selectedRow, + inputParams, + connectionHandler, + resultDatabaseSettings + ) { shiny::moduleServer( id, @@ -59,13 +64,14 @@ cohortMethodPropensityModelServer <- function(id, selectedRow, inputParams, conn if (is.null(row)) { return(NULL) } else { - model <- getCohortMethodPropensityModel(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - databaseId = row$databaseId, - analysisId = row$analysisId) + model <- getCohortMethodPropensityModel( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetId = inputParams()$target, + comparatorId = inputParams()$comparator, + databaseId = row$databaseId, + analysisId = row$analysisId + ) table <- prepareCohortMethodPropensityModelTable(model) options = list(columnDefs = list(list(className = 'dt-right', targets = 0)), diff --git a/R/cohort-method-propensityScoreDistribution.R b/R/cohort-method-propensityScoreDistribution.R index b8d6b6dc..ce0e596b 100644 --- a/R/cohort-method-propensityScoreDistribution.R +++ b/R/cohort-method-propensityScoreDistribution.R @@ -48,16 +48,21 @@ cohortMethodPropensityScoreDistViewer <- function(id) { #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix -#' @param cohortTablePrefix cohortTablePrefix +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param metaAnalysisDbIds metaAnalysisDbIds #' #' @return #' the PLE propensity score distribution content server #' #' @export -cohortMethodPropensityScoreDistServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, metaAnalysisDbIds = F) { +cohortMethodPropensityScoreDistServer <- function( + id, + selectedRow, + inputParams, + connectionHandler, + resultDatabaseSettings, + metaAnalysisDbIds = F + ) { shiny::moduleServer( id, @@ -68,37 +73,29 @@ cohortMethodPropensityScoreDistServer <- function(id, selectedRow, inputParams, if (is.null(row)) { return(NULL) } else { - if (FALSE && row$databaseId %in% metaAnalysisDbIds) { - #TODO: update once MA implemented - ps <- getCohortMethodPs(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - #targetIds = row$targetId, - #comparatorIds = row$comparatorId, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - analysisId = row$analysisId) - } else { - ps <- getCohortMethodPs(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - analysisId = row$analysisId, - databaseId = row$databaseId) - } + ps <- getCohortMethodPs( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetId = inputParams()$target, + comparatorId = inputParams()$comparator, + analysisId = row$analysisId, + databaseId = row$databaseId + ) + if (nrow(ps) == 0) { return(NULL) #TODO: handle more gracefully } - targetName <- getCohortNameFromId(connectionHandler = connectionHandler , - resultsSchema = resultsSchema, - cohortTablePrefix = cohortTablePrefix, - cohortId = inputParams()$target) - comparatorName <- getCohortNameFromId(connectionHandler = connectionHandler , - resultsSchema = resultsSchema, - cohortTablePrefix = cohortTablePrefix, - cohortId = inputParams()$comparator) + targetName <- getCohortNameFromId( + connectionHandler = connectionHandler , + resultDatabaseSettings = resultDatabaseSettings, + cohortId = inputParams()$target + ) + comparatorName <- getCohortNameFromId( + connectionHandler = connectionHandler , + resultDatabaseSettings = resultDatabaseSettings, + cohortId = inputParams()$comparator + ) plot <- plotCohortMethodPs(ps, targetName$cohortName, comparatorName$cohortName) return(plot) } diff --git a/R/cohort-method-resultsTable.R b/R/cohort-method-resultsTable.R index cc5a9218..1b27fa17 100644 --- a/R/cohort-method-resultsTable.R +++ b/R/cohort-method-resultsTable.R @@ -42,9 +42,7 @@ cohortMethodResultsTableViewer <- function(id) { #' @param id the unique reference id for the module #' @param connectionHandler the connection to the PLE results database #' @param inputParams the selected study parameters of interest -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix -#' @param databaseTable databaseTable +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' the PLE main results table server server @@ -54,9 +52,7 @@ cohortMethodResultsTableServer <- function( id, connectionHandler, inputParams, - resultsSchema, - tablePrefix, - databaseTable + resultDatabaseSettings ) { shiny::moduleServer( @@ -82,15 +78,15 @@ cohortMethodResultsTableServer <- function( resultSubset <- shiny::reactive({ - results <- getCohortMethodMainResults(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - databaseTable = databaseTable, - targetIds = filterCohortMethodEmptyNullValues(inputParams()$target), - comparatorIds = filterCohortMethodEmptyNullValues(inputParams()$comparator), - outcomeIds = filterCohortMethodEmptyNullValues(inputParams()$outcome), - databaseIds = filterCohortMethodEmptyNullValues(inputParams()$database), - analysisIds = filterCohortMethodEmptyNullValues(inputParams()$analysis)) + results <- getCohortMethodMainResults( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetIds = filterCohortMethodEmptyNullValues(inputParams()$target), + comparatorIds = filterCohortMethodEmptyNullValues(inputParams()$comparator), + outcomeIds = filterCohortMethodEmptyNullValues(inputParams()$outcome), + databaseIds = filterCohortMethodEmptyNullValues(inputParams()$database), + analysisIds = filterCohortMethodEmptyNullValues(inputParams()$analysis) + ) results <- results[order(results$analysisId), ] diff --git a/R/cohort-method-subgroups.R b/R/cohort-method-subgroups.R index fbf04446..1c6d3269 100644 --- a/R/cohort-method-subgroups.R +++ b/R/cohort-method-subgroups.R @@ -48,7 +48,14 @@ cohortMethodSubgroupsViewer <- function(id) { #' the PLE subgroup results server #' #' @export -cohortMethodSubgroupsServer <- function(id, selectedRow, inputParams, exposureOfInterest, outcomeOfInterest, connectionHandler) { +cohortMethodSubgroupsServer <- function( + id, + selectedRow, + inputParams, + exposureOfInterest, + outcomeOfInterest, + connectionHandler + ) { shiny::moduleServer( id, diff --git a/R/cohort-method-systematicError.R b/R/cohort-method-systematicError.R index 9a199e09..c2a2af16 100644 --- a/R/cohort-method-systematicError.R +++ b/R/cohort-method-systematicError.R @@ -59,16 +59,22 @@ cohortMethodSystematicErrorViewer <- function(id) { #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table #' @param inputParams the selected study parameters of interest -#' @param connectionHandler the connection to the PLE results database -#' @param resultsSchema the schema with the PLE results -#' @param tablePrefix tablePrefix +#' @param connectionHandler the connection handler to the result databases +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param metaAnalysisDbIds metaAnalysisDbIds #' #' @return #' the PLE systematic error content server #' #' @export -cohortMethodSystematicErrorServer <- function(id, selectedRow, inputParams, connectionHandler, resultsSchema, tablePrefix, metaAnalysisDbIds = NULL) { +cohortMethodSystematicErrorServer <- function( + id, + selectedRow, + inputParams, + connectionHandler, + resultDatabaseSettings, + metaAnalysisDbIds = NULL + ) { shiny::moduleServer( id, @@ -91,13 +97,14 @@ cohortMethodSystematicErrorServer <- function(id, selectedRow, inputParams, conn if (is.null(row)) { return(NULL) } else { - controlResults <- getCohortMethodControlResults(connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = tablePrefix, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - analysisId = row$analysisId, - databaseId = row$databaseId) + controlResults <- getCohortMethodControlResults( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetId = inputParams()$target, + comparatorId = inputParams()$comparator, + analysisId = row$analysisId, + databaseId = row$databaseId + ) # remove the RR zeros that replace NAs during data upload controlResults$logRr[controlResults$logRr == 0] <- NA diff --git a/R/cohortgenerator-main.R b/R/cohortgenerator-main.R index 0be7698d..35d820f8 100644 --- a/R/cohortgenerator-main.R +++ b/R/cohortgenerator-main.R @@ -232,10 +232,7 @@ cohortGeneratorServer <- function( inputColsCohortCounts <- colnames(getCohortGeneratorCohortCounts( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix, - databaseTable = resultDatabaseSettings$databaseTable, - databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) %>% dplyr::select("cdmSourceName", "cohortId", @@ -344,10 +341,7 @@ cohortGeneratorServer <- function( # data <- getCohortGeneratorCohortCounts( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix, - databaseTable = resultDatabaseSettings$databaseTable, - databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) %>% dplyr::select("cdmSourceName", "cohortId", @@ -431,10 +425,7 @@ cohortGeneratorServer <- function( content = function(con) { utils::write.csv(getCohortGeneratorCohortCounts( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix, - databaseTable = resultDatabaseSettings$databaseTable, - databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) %>% dplyr::select("cdmSourceName", "cohortId", @@ -460,10 +451,7 @@ cohortGeneratorServer <- function( output$cohortGeneration <- reactable::renderReactable({ data <- getCohortGeneratorCohortMeta( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix, - databaseTable = resultDatabaseSettings$databaseTable, - databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) %>% dplyr::select("cdmSourceName", "cohortId", @@ -537,8 +525,7 @@ cohortGeneratorServer <- function( content = function(con) { utils::write.csv(getCohortGeneratorCohortMeta( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix + resultDatabaseSettings = resultDatabaseSettings ) %>% dplyr::select("cohortId", "cohortName", @@ -551,16 +538,12 @@ cohortGeneratorServer <- function( #building attrition table using inclusion rules & stats tables rules <- getCohortGeneratorInclusionRules( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix + resultDatabaseSettings = resultDatabaseSettings ) stats <- getCohortGeneratorInclusionStats( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = resultDatabaseSettings$tablePrefix, - databaseTable = resultDatabaseSettings$databaseTable, - databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) #this gets the full attrition table diff --git a/R/data-diagnostic-drill.R b/R/data-diagnostic-drill.R index 938a013d..d7cd65db 100644 --- a/R/data-diagnostic-drill.R +++ b/R/data-diagnostic-drill.R @@ -52,8 +52,7 @@ dataDiagnosticDrillViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the summary module @@ -62,8 +61,7 @@ dataDiagnosticDrillViewer <- function(id) { dataDiagnosticDrillServer <- function( id, connectionHandler, - mySchema, - myTableAppend + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -71,15 +69,13 @@ dataDiagnosticDrillServer <- function( analyses <- getAnalysisNames( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend + resultDatabaseSettings = resultDatabaseSettings ) databases <- shiny::reactiveVal({ getDbDataDiagnosticsDatabases( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend, + resultDatabaseSettings = resultDatabaseSettings, analysisName = analyses[1] ) }) @@ -115,8 +111,7 @@ dataDiagnosticDrillServer <- function( resultTable <- shiny::reactiveVal( value = getDrugStudyFail( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend, + resultDatabaseSettings = resultDatabaseSettings, analysis = analyses[1] )) @@ -126,8 +121,7 @@ dataDiagnosticDrillServer <- function( resultTableTemp <- getDrugStudyFail( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend, + resultDatabaseSettings = resultDatabaseSettings, analysis = input$analysisSelected ) resultTable(resultTableTemp) @@ -220,8 +214,7 @@ dataDiagnosticDrillServer <- function( reactable::reactable( data = getDrillDown( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend, + resultDatabaseSettings = resultDatabaseSettings, analysisName = analysisName, databaseId = databaseId ) @@ -253,19 +246,18 @@ dataDiagnosticDrillServer <- function( getDrillDown <- function( connectionHandler, - mySchema, - myTableAppend, + resultDatabaseSettings, analysisName, databaseId ){ - sql <- "SELECT * FROM @my_schema.@my_table_appenddata_diagnostics_output + sql <- "SELECT * FROM @schema.@dd_table_prefixdata_diagnostics_output WHERE analysis_name = '@analysis_name' and database_id = '@database_id';" result <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend, + schema = resultDatabaseSettings$schema, + dd_table_prefix = resultDatabaseSettings$ddTablePrefix, analysis_name = analysisName, database_id = databaseId ) @@ -277,17 +269,16 @@ getDrillDown <- function( } getDbDataDiagnostics <- function( - connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend + connectionHandler, + resultDatabaseSettings ){ - sql <- "SELECT distinct database_id FROM @my_schema.@my_table_appenddata_diagnostics_summary;" + sql <- "SELECT distinct database_id FROM @schema.@dd_table_prefixdata_diagnostics_summary;" dbNames <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend + schema = resultDatabaseSettings$schema, + dd_table_prefix = resultDatabaseSettings$ddTablePrefix ) result <- list(dbNames$databaseId) @@ -297,8 +288,7 @@ getDbDataDiagnostics <- function( getDrugStudyFail <- function( connectionHandler, - mySchema, - myTableAppend = '', + resultDatabaseSettings, analysis = NULL ){ @@ -310,13 +300,13 @@ getDrugStudyFail <- function( shiny::incProgress(1/3, detail = paste("Extracting data")) - sql <- "SELECT * FROM @my_schema.@my_table_appenddata_diagnostics_summary + sql <- "SELECT * FROM @schema.@dd_table_prefixdata_diagnostics_summary WHERE analysis_name in ('@analysis');" summaryTable <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend, + schema = resultDatabaseSettings$schema, + dd_table_prefix = resultDatabaseSettings$ddTablePrefix, analysis = analysis ) @@ -336,18 +326,17 @@ getDrugStudyFail <- function( getDbDataDiagnosticsDatabases <- function( connectionHandler, - mySchema, - myTableAppend, + resultDatabaseSettings, analysisName ){ if(!is.null(analysisName)){ - sql <- "SELECT distinct database_id FROM @my_schema.@my_table_appenddata_diagnostics_summary + sql <- "SELECT distinct database_id FROM @schema.@dd_table_prefixdata_diagnostics_summary WHERE analysis_name in ('@analysis');" res <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend, + schema = resultDatabaseSettings$schema, + dd_table_prefix = resultDatabaseSettings$ddTablePrefix, analysis = analysisName ) diff --git a/R/data-diagnostic-main.R b/R/data-diagnostic-main.R index 471911e0..814d2f05 100644 --- a/R/data-diagnostic-main.R +++ b/R/data-diagnostic-main.R @@ -93,15 +93,13 @@ dataDiagnosticServer <- function( dataDiagnosticSummaryServer( id = 'summary-tab', connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix + resultDatabaseSettings = resultDatabaseSettings ) dataDiagnosticDrillServer( id = 'drill-down-tab', connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - myTableAppend = resultDatabaseSettings$tablePrefix + resultDatabaseSettings = resultDatabaseSettings ) diff --git a/R/data-diagnostic-summary.R b/R/data-diagnostic-summary.R index cca8a58b..7e8ea0b7 100644 --- a/R/data-diagnostic-summary.R +++ b/R/data-diagnostic-summary.R @@ -46,8 +46,7 @@ dataDiagnosticSummaryViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database -#' @param mySchema the database schema for the model results -#' @param myTableAppend a string that appends the tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the summary module @@ -56,8 +55,7 @@ dataDiagnosticSummaryViewer <- function(id) { dataDiagnosticSummaryServer <- function( id, connectionHandler, - mySchema, - myTableAppend + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -65,8 +63,7 @@ dataDiagnosticSummaryServer <- function( analysisNames <- getAnalysisNames( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend + resultDatabaseSettings = resultDatabaseSettings ) # create UI for selecting analysis of interest @@ -87,8 +84,7 @@ dataDiagnosticSummaryServer <- function( if(!is.null(input$dbDiagAnalysisNameSelected[1])){ getDrugStudyFailSummary( connectionHandler = connectionHandler, - mySchema = mySchema, - myTableAppend = myTableAppend, + resultDatabaseSettings = resultDatabaseSettings, analysisNames = input$dbDiagAnalysisNameSelected ) } else{ @@ -143,17 +139,16 @@ dataDiagnosticSummaryServer <- function( getAnalysisNames <- function( connectionHandler, - mySchema, - myTableAppend + resultDatabaseSettings ){ sql <- "SELECT distinct sum.analysis_name - FROM @my_schema.@my_table_appenddata_diagnostics_summary as sum;" + FROM @schema.@dd_table_prefixdata_diagnostics_summary as sum;" result <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend + schema = resultDatabaseSettings$schema, + dd_table_prefix = resultDatabaseSettings$ddTablePrefix ) return(sort(result$analysisName)) @@ -161,8 +156,7 @@ getAnalysisNames <- function( getDrugStudyFailSummary <- function( connectionHandler, - mySchema, - myTableAppend = '', + resultDatabaseSettings, analysisNames ){ @@ -178,13 +172,13 @@ getDrugStudyFailSummary <- function( sum.database_id, sum.total_fails - FROM @my_schema.@my_table_appenddata_diagnostics_summary as sum + FROM @schema.@dd_table_prefixdata_diagnostics_summary as sum where sum.analysis_name in (@names);" summaryTable <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - my_table_append = myTableAppend, + schema = resultDatabaseSettings$schema, + dd_table_prefix = resultDatabaseSettings$ddTablePrefix, names = paste(paste0("'",analysisNames,"'"), collapse=',') ) diff --git a/R/evidence-synth-main.R b/R/evidence-synth-main.R index f9113dca..6e462e6f 100644 --- a/R/evidence-synth-main.R +++ b/R/evidence-synth-main.R @@ -94,7 +94,7 @@ evidenceSynthesisViewer <- function(id=1) { #' #' @param id the unique reference id for the module #' @param connectionHandler a connection to the database with the results -#' @param resultDatabaseSettings a list containing the prediction result schema and connection details +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server for the PatientLevelPrediction module @@ -117,24 +117,27 @@ evidenceSynthesisServer <- function( targetIds <- getESTargetIds( connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - cgTablePrefix = resultDatabaseSettings$cgTablePrefix + resultDatabaseSettings = resultDatabaseSettings + #mySchema = resultDatabaseSettings$schema, + #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, + #cgTablePrefix = resultDatabaseSettings$cgTablePrefix ) outcomeIds <- getESOutcomeIds( connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - cgTablePrefix = resultDatabaseSettings$cgTablePrefix + resultDatabaseSettings = resultDatabaseSettings + #mySchema = resultDatabaseSettings$schema, + #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, + #cgTablePrefix = resultDatabaseSettings$cgTablePrefix ) diagnosticColumnNames <- getOACcombinations( - connectionHandler = connectionHandler, - resultsSchema = resultDatabaseSettings$schema, - sccsTablePrefix = resultDatabaseSettings$sccsTablePrefix, - cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - cohortTablePrefix = resultDatabaseSettings$cgTablePrefix, - databaseTable = resultDatabaseSettings$databaseMetaData + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + #resultsSchema = resultDatabaseSettings$schema, + #sccsTablePrefix = resultDatabaseSettings$sccsTablePrefix, + #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, + #cgTablePrefix = resultDatabaseSettings$cgTablePrefix, + #databaseTable = resultDatabaseSettings$databaseMetaData ) inputSelected <- inputSelectionServer( @@ -185,10 +188,11 @@ evidenceSynthesisServer <- function( data <- shiny::reactive({ getCMEstimation( connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - cgTablePrefix = resultDatabaseSettings$cgTablePrefix, - databaseMetaData = resultDatabaseSettings$databaseMetaData, + resultDatabaseSettings = resultDatabaseSettings, + #mySchema = resultDatabaseSettings$schema, + #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, + #cgTablePrefix = resultDatabaseSettings$cgTablePrefix, + #databaseMetaData = resultDatabaseSettings$databaseMetaData, targetId = inputSelected()$targetId, outcomeId = inputSelected()$outcomeId ) @@ -197,10 +201,11 @@ evidenceSynthesisServer <- function( data2 <- shiny::reactive({ getMetaEstimation( connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - cgTablePrefix = resultDatabaseSettings$cgTablePrefix, - esTablePrefix = resultDatabaseSettings$tablePrefix, + resultDatabaseSettings = resultDatabaseSettings, + #mySchema = resultDatabaseSettings$schema, + #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, + #cgTablePrefix = resultDatabaseSettings$cgTablePrefix, + #esTablePrefix = resultDatabaseSettings$tablePrefix, targetId = inputSelected()$targetId, outcomeId = inputSelected()$outcomeId ) @@ -210,10 +215,10 @@ evidenceSynthesisServer <- function( getEvidenceSynthDiagnostics( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - resultsSchema = resultDatabaseSettings$schema, - cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - cohortTablePrefix = resultDatabaseSettings$cgTablePrefix, - databaseTable = resultDatabaseSettings$databaseTable, + #resultsSchema = resultDatabaseSettings$schema, + #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, + #cgTablePrefix = resultDatabaseSettings$cgTablePrefix, + #databaseTable = resultDatabaseSettings$databaseTable, targetIds = inputSelected()$targetId, outcomeIds = inputSelected()$outcomeId ) @@ -332,11 +337,7 @@ evidenceSynthesisServer <- function( sccsData <- shiny::reactive({ getSccsEstimation( connectionHandler = connectionHandler, - mySchema = resultDatabaseSettings$schema, - sccsTablePrefix = resultDatabaseSettings$sccsTablePrefix, - cgTablePrefix = resultDatabaseSettings$cgTablePrefix, - esTablePrefix = resultDatabaseSettings$tablePrefix, - databaseMetaData = resultDatabaseSettings$databaseMetaData, + resultDatabaseSettings = resultDatabaseSettings, targetId = inputSelected()$targetId, outcomeId = inputSelected()$outcomeId ) @@ -420,9 +421,7 @@ evidenceSynthesisServer <- function( getESTargetIds <- function( connectionHandler, - mySchema, - cmTablePrefix, - cgTablePrefix + resultDatabaseSettings ){ sql <- "select distinct @@ -430,17 +429,17 @@ getESTargetIds <- function( r.target_id from - @my_schema.@cm_table_prefixresult as r + @schema.@cm_table_prefixresult as r inner join - @my_schema.@cg_table_prefixcohort_definition as c1 + @schema.@cg_table_prefixcohort_definition as c1 on c1.cohort_definition_id = r.target_id ;" result <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - cm_table_prefix = cmTablePrefix, - cg_table_prefix = cgTablePrefix + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) output <- as.list(result$targetId) @@ -452,25 +451,23 @@ getESTargetIds <- function( getESOutcomeIds <- function( connectionHandler, - mySchema, - cmTablePrefix, - cgTablePrefix + resultDatabaseSettings ) { sql <- "select distinct c1.cohort_name as outcome, r.outcome_id from - @my_schema.@cm_table_prefixresult as r + @schema.@cm_table_prefixresult as r inner join - @my_schema.@cm_table_prefixtarget_comparator_outcome as tco + @schema.@cm_table_prefixtarget_comparator_outcome as tco on r.target_id = tco.target_id and r.comparator_id = tco.comparator_id and r.outcome_id = tco.outcome_id inner join - @my_schema.@cg_table_prefixcohort_definition as c1 + @schema.@cg_table_prefixcohort_definition as c1 on c1.cohort_definition_id = r.outcome_id where @@ -479,9 +476,9 @@ getESOutcomeIds <- function( result <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - cm_table_prefix = cmTablePrefix, - cg_table_prefix = cgTablePrefix + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) output <- as.list(result$outcomeId) @@ -494,10 +491,7 @@ getESOutcomeIds <- function( getCMEstimation <- function( connectionHandler, - mySchema, - cmTablePrefix = 'cm_', - cgTablePrefix = 'cg_', - databaseMetaData = 'database_meta_data', + resultDatabaseSettings, targetId, outcomeId ){ @@ -517,9 +511,9 @@ getCMEstimation <- function( r.calibrated_log_rr, r.calibrated_se_log_rr from - @my_schema.@cm_table_prefixresult as r + @schema.@cm_table_prefixresult as r inner join - @my_schema.@cm_table_prefixtarget_comparator_outcome as tco + @schema.@cm_table_prefixtarget_comparator_outcome as tco on r.target_id = tco.target_id and r.comparator_id = tco.comparator_id and @@ -527,7 +521,7 @@ getCMEstimation <- function( inner join - @my_schema.@cm_table_prefixdiagnostics_summary as unblind + @schema.@cm_table_prefixdiagnostics_summary as unblind on r.analysis_id = unblind.analysis_id and r.target_id = unblind.target_id and @@ -536,23 +530,23 @@ getCMEstimation <- function( r.database_id = unblind.database_id inner join - @my_schema.@database_meta_data as db + @schema.@database_table as db on db.database_id = r.database_id inner join - @my_schema.@cg_table_prefixcohort_definition as c1 + @schema.@cg_table_prefixcohort_definition as c1 on c1.cohort_definition_id = r.target_id inner join - @my_schema.@cg_table_prefixcohort_definition as c2 + @schema.@cg_table_prefixcohort_definition as c2 on c2.cohort_definition_id = r.comparator_id inner join - @my_schema.@cg_table_prefixcohort_definition as c3 + @schema.@cg_table_prefixcohort_definition as c3 on c3.cohort_definition_id = r.outcome_id inner join - @my_schema.@cm_table_prefixanalysis as a + @schema.@cm_table_prefixanalysis as a on a.analysis_id = r.analysis_id where @@ -565,10 +559,10 @@ getCMEstimation <- function( result <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - database_meta_data = databaseMetaData, - cm_table_prefix = cmTablePrefix, - cg_table_prefix = cgTablePrefix, + schema = resultDatabaseSettings$schema, + database_table = resultDatabaseSettings$databaseTable, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, outcome_id = outcomeId, target_id = targetId ) %>% @@ -589,10 +583,7 @@ getCMEstimation <- function( getMetaEstimation <- function( connectionHandler, - mySchema, - cmTablePrefix = 'cm_', - cgTablePrefix = 'cg_', - esTablePrefix = 'es_', + resultDatabaseSettings, targetId, outcomeId ){ @@ -613,9 +604,9 @@ sql <- "select r.calibrated_log_rr, r.calibrated_se_log_rr from - @my_schema.@es_table_prefixcm_result as r + @schema.@es_table_prefixcm_result as r inner join - @my_schema.@cm_table_prefixtarget_comparator_outcome as tco + @schema.@cm_table_prefixtarget_comparator_outcome as tco on r.target_id = tco.target_id and r.comparator_id = tco.comparator_id and @@ -623,7 +614,7 @@ sql <- "select inner join - @my_schema.@es_table_prefixcm_diagnostics_summary as unblind + @schema.@es_table_prefixcm_diagnostics_summary as unblind on r.analysis_id = unblind.analysis_id and r.target_id = unblind.target_id and @@ -631,23 +622,23 @@ sql <- "select r.outcome_id = unblind.outcome_id inner join - @my_schema.@cg_table_prefixcohort_definition as c1 + @schema.@cg_table_prefixcohort_definition as c1 on c1.cohort_definition_id = r.target_id inner join - @my_schema.@cg_table_prefixcohort_definition as c2 + @schema.@cg_table_prefixcohort_definition as c2 on c2.cohort_definition_id = r.comparator_id inner join - @my_schema.@cg_table_prefixcohort_definition as c3 + @schema.@cg_table_prefixcohort_definition as c3 on c3.cohort_definition_id = r.outcome_id inner join - @my_schema.@cm_table_prefixanalysis as a + @schema.@cm_table_prefixanalysis as a on a.analysis_id = r.analysis_id inner join - @my_schema.@es_table_prefixanalysis as ev + @schema.@es_table_prefixanalysis as ev on ev.evidence_synthesis_analysis_id = r.evidence_synthesis_analysis_id where @@ -660,10 +651,10 @@ sql <- "select result <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - cm_table_prefix = cmTablePrefix, - cg_table_prefix = cgTablePrefix, - es_table_prefix = esTablePrefix, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + es_table_prefix = resultDatabaseSettings$esTablePrefix, outcome_id = outcomeId, target_id = targetId ) %>% @@ -766,11 +757,7 @@ computeTraditionalP <- function( getSccsEstimation <- function( connectionHandler, - mySchema, - sccsTablePrefix, - cgTablePrefix, - esTablePrefix, - databaseMetaData, + resultDatabaseSettings, targetId, outcomeId ){ @@ -790,14 +777,14 @@ getSccsEstimation <- function( r.calibrated_se_log_rr from - @my_schema.@sccs_table_prefixresult as r + @schema.@sccs_table_prefixresult as r inner join - @my_schema.@sccs_table_prefixexposures_outcome_set as eos + @schema.@sccs_table_prefixexposures_outcome_set as eos on r.exposures_outcome_set_id = eos.exposures_outcome_set_id inner join - @my_schema.@sccs_table_prefixcovariate as cov + @schema.@sccs_table_prefixcovariate as cov on r.covariate_id = cov.covariate_id and r.database_id = cov.database_id and @@ -805,14 +792,14 @@ getSccsEstimation <- function( r.exposures_outcome_set_id = cov.exposures_outcome_set_id inner join - @my_schema.@sccs_table_prefixexposure as ex + @schema.@sccs_table_prefixexposure as ex on ex.era_id = cov.era_id and ex.exposures_outcome_set_id = cov.exposures_outcome_set_id inner join - @my_schema.@sccs_table_prefixdiagnostics_summary as unblind + @schema.@sccs_table_prefixdiagnostics_summary as unblind on r.analysis_id = unblind.analysis_id and r.exposures_outcome_set_id = unblind.exposures_outcome_set_id and @@ -820,19 +807,19 @@ getSccsEstimation <- function( r.database_id = unblind.database_id inner join - @my_schema.@database_meta_data as db + @schema.@database_table as db on db.database_id = r.database_id inner join - @my_schema.@cg_table_prefixcohort_definition as c1 + @schema.@cg_table_prefixcohort_definition as c1 on c1.cohort_definition_id = cov.era_id inner join - @my_schema.@cg_table_prefixcohort_definition as c3 + @schema.@cg_table_prefixcohort_definition as c3 on c3.cohort_definition_id = eos.outcome_id inner join - @my_schema.@sccs_table_prefixanalysis as a + @schema.@sccs_table_prefixanalysis as a on a.analysis_id = r.analysis_id where @@ -846,10 +833,10 @@ getSccsEstimation <- function( result <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - database_meta_data = databaseMetaData, - sccs_table_prefix = sccsTablePrefix, - cg_table_prefix = cgTablePrefix, + schema = resultDatabaseSettings$schema, + database_table = resultDatabaseSettings$databaseTable, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, outcome_id = outcomeId, target_id = targetId ) @@ -869,28 +856,28 @@ getSccsEstimation <- function( r.calibrated_se_log_rr from - @my_schema.@es_table_prefixsccs_result as r + @schema.@es_table_prefixsccs_result as r inner join - @my_schema.@sccs_table_prefixexposures_outcome_set as eos + @schema.@sccs_table_prefixexposures_outcome_set as eos on r.exposures_outcome_set_id = eos.exposures_outcome_set_id inner join - @my_schema.@sccs_table_prefixcovariate as cov + @schema.@sccs_table_prefixcovariate as cov on r.covariate_id = cov.covariate_id and r.analysis_id = cov.analysis_id and r.exposures_outcome_set_id = cov.exposures_outcome_set_id inner join - @my_schema.@sccs_table_prefixexposure as ex + @schema.@sccs_table_prefixexposure as ex on ex.era_id = cov.era_id and ex.exposures_outcome_set_id = cov.exposures_outcome_set_id inner join - @my_schema.@es_table_prefixsccs_diagnostics_summary as unblind + @schema.@es_table_prefixsccs_diagnostics_summary as unblind on r.analysis_id = unblind.analysis_id and r.exposures_outcome_set_id = unblind.exposures_outcome_set_id and @@ -898,19 +885,19 @@ getSccsEstimation <- function( r.evidence_synthesis_analysis_id = unblind.evidence_synthesis_analysis_id inner join - @my_schema.@cg_table_prefixcohort_definition as c1 + @schema.@cg_table_prefixcohort_definition as c1 on c1.cohort_definition_id = cov.era_id inner join - @my_schema.@cg_table_prefixcohort_definition as c3 + @schema.@cg_table_prefixcohort_definition as c3 on c3.cohort_definition_id = eos.outcome_id inner join - @my_schema.@sccs_table_prefixanalysis as a + @schema.@sccs_table_prefixanalysis as a on a.analysis_id = r.analysis_id inner join - @my_schema.@es_table_prefixanalysis as ev + @schema.@es_table_prefixanalysis as ev on ev.evidence_synthesis_analysis_id = r.evidence_synthesis_analysis_id where @@ -924,10 +911,10 @@ getSccsEstimation <- function( result2 <- connectionHandler$queryDb( sql = sql, - my_schema = mySchema, - es_table_prefix = esTablePrefix, - sccs_table_prefix = sccsTablePrefix, - cg_table_prefix = cgTablePrefix, + schema = resultDatabaseSettings$schema, + es_table_prefix = resultDatabaseSettings$esTablePrefix, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, outcome_id = outcomeId, target_id = targetId ) @@ -976,20 +963,16 @@ createPlotForSccsAnalysis <- function( getOACcombinations <- function( connectionHandler, - resultsSchema, - sccsTablePrefix, - cmTablePrefix, - cohortTablePrefix, - databaseTable + resultDatabaseSettings ){ sql <- "SELECT DISTINCT CONCAT(cma.description, '_', cgcd2.cohort_name) as col_names FROM - @results_schema.@cm_table_prefixdiagnostics_summary cmds - INNER JOIN @results_schema.@cm_table_prefixanalysis cma + @schema.@cm_table_prefixdiagnostics_summary cmds + INNER JOIN @schema.@cm_table_prefixanalysis cma ON cmds.analysis_id = cma.analysis_id - INNER JOIN @results_schema.@cohort_table_prefixcohort_definition cgcd2 + INNER JOIN @schema.@cg_table_prefixcohort_definition cgcd2 ON cmds.comparator_id = cgcd2.cohort_definition_id union @@ -997,14 +980,14 @@ getOACcombinations <- function( SELECT CONCAT(a.description, '_', cov.covariate_name) as col_names - FROM @results_schema.@sccs_table_prefixdiagnostics_summary ds + FROM @schema.@sccs_table_prefixdiagnostics_summary ds INNER JOIN - @results_schema.@sccs_table_prefixanalysis a + @schema.@sccs_table_prefixanalysis a on a.analysis_id = ds.analysis_id INNER JOIN - @results_schema.@sccs_table_prefixcovariate cov + @schema.@sccs_table_prefixcovariate cov on cov.covariate_id = ds.covariate_id and cov.exposures_outcome_set_id = ds.exposures_outcome_set_id and cov.analysis_id = ds.analysis_id @@ -1012,11 +995,11 @@ getOACcombinations <- function( result <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - sccs_table_prefix = sccsTablePrefix, - cm_table_prefix = cmTablePrefix, - cohort_table_prefix = cohortTablePrefix, - database_table = databaseTable + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable ) res <- result$colNames @@ -1028,17 +1011,10 @@ getOACcombinations <- function( getEvidenceSynthDiagnostics <- function( connectionHandler, resultDatabaseSettings, - resultsSchema, - cmTablePrefix, - cohortTablePrefix, - databaseTable, targetIds, outcomeIds ){ - resultDatabaseSettings$tablePrefix <- resultDatabaseSettings$sccsTablePrefix - resultDatabaseSettings$databaseTable <- resultDatabaseSettings$databaseMetaData - resultDatabaseSettings$cohortTablePrefix <- resultDatabaseSettings$cgTablePrefix sccsDiagTemp <- getSccsAllDiagnosticsSummary( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, @@ -1048,10 +1024,7 @@ getEvidenceSynthDiagnostics <- function( cmDiagTemp <- getCmDiagnosticsData( connectionHandler = connectionHandler, - resultsSchema = resultsSchema, - tablePrefix = cmTablePrefix, - cohortTablePrefix = cohortTablePrefix, - databaseTable = resultDatabaseSettings$databaseMetaData, + resultDatabaseSettings = resultDatabaseSettings, targetIds = targetIds, outcomeIds = outcomeIds ) diff --git a/R/helper-getPredictionProtocol.R b/R/helper-getPredictionProtocol.R index 7366e395..54eed021 100644 --- a/R/helper-getPredictionProtocol.R +++ b/R/helper-getPredictionProtocol.R @@ -1,9 +1,6 @@ createPredictionProtocol <- function( connectionHandler, - schema, - plpTablePrefix, - databaseTablePrefix, - cohortTablePrefix, + resultDatabaseSettings, modelDesignId, output, intermediatesDir = file.path(tempdir(), 'plp-prot') @@ -24,11 +21,11 @@ createPredictionProtocol <- function( output_dir = output, params = list( connectionHandler = connectionHandler, - resultSchema = schema, - myTableAppend = plpTablePrefix, + resultSchema = resultDatabaseSettings$schema, + myTableAppend = resultDatabaseSettings$plpTablePrefix, modelDesignIds = modelDesignId, - databaseTableAppend = databaseTablePrefix, - cohortTableAppend = cohortTablePrefix + databaseTableAppend = resultDatabaseSettings$databaseTablePrefix, + cohortTableAppend = resultDatabaseSettings$cgTablePrefix ) ) diff --git a/R/helpers-cohort-methodDataPulls.R b/R/helpers-cohort-methodDataPulls.R index 13cc7e1e..71c6d777 100644 --- a/R/helpers-cohort-methodDataPulls.R +++ b/R/helpers-cohort-methodDataPulls.R @@ -1,33 +1,41 @@ -getCohortNameFromId <- function(connectionHandler, resultsSchema, cohortTablePrefix, cohortId) { +getCohortNameFromId <- function( + connectionHandler, + resultDatabaseSettings, + cohortId) { sql <- " SELECT cohort_name FROM - @results_schema.@cohort_table_prefixcohort_definition cd + @schema.@cg_table_prefixcohort_definition cd WHERE cd.cohort_definition_id = @cohort_id; " return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - cohort_table_prefix = cohortTablePrefix, + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, cohort_id = cohortId ) ) } -getCohortMethodTcoChoice <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, tcoVar, sorted = TRUE) { +getCohortMethodTcoChoice <- function( + connectionHandler, + resultDatabaseSettings, + tcoVar, + sorted = TRUE) { sql <- " SELECT DISTINCT cmtco.@tco_var, cd.cohort_name FROM - @results_schema.@table_prefixtarget_comparator_outcome cmtco - join @results_schema.@cohort_table_prefixcohort_definition cd on cmtco.@tco_var = cd.cohort_definition_id + @schema.@cm_table_prefixtarget_comparator_outcome cmtco + join @schema.@cg_table_prefixcohort_definition cd + on cmtco.@tco_var = cd.cohort_definition_id " if (sorted) { @@ -39,45 +47,71 @@ FROM return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - cohort_table_prefix = cohortTablePrefix, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, tco_var = tcoVar ) ) } -getCohortMethodTargetChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { +getCohortMethodTargetChoices <- function( + connectionHandler, + resultDatabaseSettings + ) { return( - getCohortMethodTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "target_id") + getCohortMethodTcoChoice( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + "target_id" + ) ) } -getCohortMethodComparatorChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { +getCohortMethodComparatorChoices <- function( + connectionHandler, + resultDatabaseSettings + ) { return( - getCohortMethodTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "comparator_id") + getCohortMethodTcoChoice( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + "comparator_id" + ) ) } -getCohortMethodOutcomeChoices <- function(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix) { +getCohortMethodOutcomeChoices <- function( + connectionHandler, + resultDatabaseSettings + ) { return( - getCohortMethodTcoChoice(connectionHandler, resultsSchema, tablePrefix, cohortTablePrefix, "outcome_id") + getCohortMethodTcoChoice( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + "outcome_id" + ) ) } -getCohortMethodDatabaseChoices <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, sorted = TRUE) { +getCohortMethodDatabaseChoices <- function( + connectionHandler, + resultDatabaseSettings, + sorted = TRUE + ) { sql <- " SELECT DISTINCT dmd.database_id, dmd.cdm_source_abbreviation FROM - @results_schema.@table_prefixresult cmr - join @results_schema.@database_table dmd on dmd.database_id = cmr.database_id + @schema.@cm_table_prefixresult cmr + join @schema.@database_table dmd + on dmd.database_id = cmr.database_id " @@ -89,22 +123,25 @@ FROM return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + database_table = resultDatabaseSettings$databaseTable ) ) } -getCmAnalysisOptions <- function(connectionHandler, resultsSchema, tablePrefix, sorted = TRUE) { +getCmAnalysisOptions <- function( + connectionHandler, + resultDatabaseSettings, + sorted = TRUE) { sql <- " SELECT DISTINCT cma.analysis_id, cma.description FROM - @results_schema.@table_prefixanalysis cma + @schema.@cm_table_prefixanalysis cma " if (sorted) { @@ -116,13 +153,16 @@ FROM return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix ) ) } -getAllCohortMethodResults <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable) { +getAllCohortMethodResults <- function( + connectionHandler, + resultDatabaseSettings + ) { sql <- " SELECT cma.analysis_id, @@ -149,33 +189,33 @@ SELECT cmr.calibrated_se_log_rr, COALESCE(cmds.unblind, 0) unblind -- TODO: assume unblinded? (or always populated and moot) FROM - @results_schema.@table_prefixanalysis cma - JOIN @results_schema.@table_prefixresult cmr on cmr.analysis_id = cma.analysis_id - JOIN @results_schema.@database_table dmd on dmd.database_id = cmr.database_id - LEFT JOIN @results_schema.@table_prefixdiagnostics_summary cmds on cmds.analysis_id = cmr.analysis_id; + @schema.@cm_table_prefixanalysis cma + JOIN @schema.@cm_table_prefixresult cmr on cmr.analysis_id = cma.analysis_id + JOIN @schema.@database_table dmd on dmd.database_id = cmr.database_id + LEFT JOIN @schema.@cm_table_prefixdiagnostics_summary cmds on cmds.analysis_id = cmr.analysis_id; " return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + database_table = resultDatabaseSettings$databaseTable ) ) } -getCohortMethodMainResults <- function(connectionHandler, - resultsSchema, - tablePrefix, - databaseTable, - targetIds = c(), - comparatorIds = c(), - outcomeIds = c(), - databaseIds = c(), - analysisIds = c()) { +getCohortMethodMainResults <- function( + connectionHandler, + resultDatabaseSettings, + targetIds = c(), + comparatorIds = c(), + outcomeIds = c(), + databaseIds = c(), + analysisIds = c() +) { sql <- " SELECT @@ -203,10 +243,15 @@ SELECT cmr.calibrated_se_log_rr, COALESCE(cmds.unblind, 0) unblind -- TODO: assume unblinded? (or always populated and moot) FROM - @results_schema.@table_prefixanalysis cma - JOIN @results_schema.@table_prefixresult cmr on cmr.analysis_id = cma.analysis_id - JOIN @results_schema.@database_table dmd on dmd.database_id = cmr.database_id - LEFT JOIN @results_schema.@table_prefixdiagnostics_summary cmds on cmds.analysis_id = cmr.analysis_id + @schema.@cm_table_prefixanalysis cma + JOIN @schema.@cm_table_prefixresult cmr + on cmr.analysis_id = cma.analysis_id + + JOIN @schema.@database_table dmd + on dmd.database_id = cmr.database_id + + LEFT JOIN @schema.@cm_table_prefixdiagnostics_summary cmds + on cmds.analysis_id = cmr.analysis_id AND cmds.target_id = cmr.target_id AND cmds.comparator_id = cmr.comparator_id AND cmds.outcome_id = cmr.outcome_id @@ -241,9 +286,9 @@ FROM suppressWarnings( # ignoring warnings due to parameter not found connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + database_table = resultDatabaseSettings$databaseTable, target_ids = paste0("'", paste(targetIds, collapse = "', '"), "'"), comparator_ids = paste0("'", paste(comparatorIds, collapse = "', '"), "'"), outcome_ids = paste0("'", paste(outcomeIds, collapse = "', '"), "'"), @@ -256,18 +301,21 @@ FROM } -getCohortMethodAnalyses <- function(connectionHandler, resultsSchema, tablePrefix) { +getCohortMethodAnalyses <- function( + connectionHandler, + resultDatabaseSettings + ) { sql <- " SELECT cma.* FROM - @results_schema.@table_prefixanalysis cma + @schema.@cm_table_prefixanalysis cma " return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix ) ) } @@ -327,17 +375,25 @@ getCohortMethodSubgroupResults <- function(connectionHandler, # not used? } -getCohortMethodControlResults <- function(connectionHandler, resultsSchema, tablePrefix, targetId, - comparatorId, analysisId, databaseId = NULL, - includePositiveControls = TRUE, emptyAsNa = TRUE) { +getCohortMethodControlResults <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + analysisId, + databaseId = NULL, + includePositiveControls = TRUE, + emptyAsNa = TRUE + ) { sql <- " SELECT cmr.*, cmtco.true_effect_size effect_size FROM - @results_schema.@table_prefixresult cmr - JOIN @results_schema.@table_prefixtarget_comparator_outcome cmtco ON cmr.target_id = cmtco.target_id AND cmr.comparator_id = cmtco.comparator_id AND cmr.outcome_id = cmtco.outcome_id + @schema.@cm_table_prefixresult cmr + JOIN @schema.@cm_table_prefixtarget_comparator_outcome cmtco + ON cmr.target_id = cmtco.target_id AND cmr.comparator_id = cmtco.comparator_id AND cmr.outcome_id = cmtco.outcome_id WHERE cmtco.outcome_of_interest != 1 AND cmr.target_id = @target_id @@ -358,8 +414,8 @@ getCohortMethodControlResults <- function(connectionHandler, resultsSchema, tabl results <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, target_id = targetId, comparator_id = comparatorId, analysis_id = analysisId, @@ -374,20 +430,21 @@ getCohortMethodControlResults <- function(connectionHandler, resultsSchema, tabl } -getCmFollowUpDist <- function(connectionHandler, - resultsSchema, - tablePrefix, - targetId, - comparatorId, - outcomeId, - databaseId = NULL, - analysisId) { +getCmFollowUpDist <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + outcomeId, + databaseId = NULL, + analysisId +) { sql <- " SELECT * FROM - @results_schema.@table_prefixfollow_up_dist cmfud + @schema.@cm_table_prefixfollow_up_dist cmfud WHERE cmfud.target_id = @target_id AND cmfud.comparator_id = @comparator_id @@ -400,8 +457,8 @@ getCmFollowUpDist <- function(connectionHandler, return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, target_id = targetId, comparator_id = comparatorId, outcome_id = outcomeId, @@ -412,14 +469,19 @@ getCmFollowUpDist <- function(connectionHandler, } - - -getCohortMethodPs <- function(connectionHandler, resultsSchema, tablePrefix, targetId, comparatorId, analysisId, databaseId = NULL) { +getCohortMethodPs <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + analysisId, + databaseId = NULL + ) { sql <- " SELECT * FROM - @results_schema.@table_prefixpreference_score_dist cmpsd + @schema.@cm_table_prefixpreference_score_dist cmpsd WHERE cmpsd.target_id = @target_id AND cmpsd.comparator_id = @comparator_id @@ -432,8 +494,8 @@ getCohortMethodPs <- function(connectionHandler, resultsSchema, tablePrefix, tar ps <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, target_id = targetId, comparator_id = comparatorId, analysis_id = analysisId, @@ -448,25 +510,21 @@ getCohortMethodPs <- function(connectionHandler, resultsSchema, tablePrefix, tar } -getCohortMethodKaplanMeier <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, targetId, comparatorId, outcomeId, databaseId, analysisId) { - sqlTmp <- " - SELECT - * - FROM - @results_schema.@table_prefixkaplan_meier_dist cmkmd - JOIN @results_schema.@database_table dmd on dmd.database_id = cmkmd.database_id - WHERE - cmkmd.target_id = @target_id - AND cmkmd.comparator_id = @comparator_id - AND cmkmd.outcome_id = @outcome_id - AND cmkmd.analysis_id = @analysis_id - AND dmd.cdm_source_abbreviation = '@database_id'; - " +getCohortMethodKaplanMeier <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + outcomeId, + databaseId, + analysisId + ) { + sql <- " SELECT * FROM - @results_schema.@table_prefixkaplan_meier_dist cmkmd + @schema.@cm_table_prefixkaplan_meier_dist cmkmd WHERE cmkmd.target_id = @target_id AND cmkmd.comparator_id = @comparator_id @@ -478,9 +536,9 @@ getCohortMethodKaplanMeier <- function(connectionHandler, resultsSchema, tablePr return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + #database_table = resultDatabaseSettings$databaseTable, target_id = targetId, comparator_id = comparatorId, outcome_id = outcomeId, @@ -491,25 +549,20 @@ getCohortMethodKaplanMeier <- function(connectionHandler, resultsSchema, tablePr } -getCohortMethodAttrition <- function(connectionHandler, resultsSchema, tablePrefix, databaseTable, targetId, comparatorId, outcomeId, analysisId, databaseId) { - sqlTmp <- " - SELECT - cmat.* - FROM - @results_schema.@table_prefixattrition cmat - JOIN @results_schema.@database_table dmd on dmd.database_id = cmat.database_id - WHERE - cmat.target_id = @target_id - AND cmat.comparator_id = @comparator_id - AND cmat.outcome_id = @outcome_id - AND cmat.analysis_id = @analysis_id - AND dmd.cdm_source_abbreviation = '@database_id'; - " +getCohortMethodAttrition <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + outcomeId, + analysisId, + databaseId + ) { + sql <- " - SELECT - cmat.* + SELECT cmat.* FROM - @results_schema.@table_prefixattrition cmat + @schema.@cm_table_prefixattrition cmat WHERE cmat.target_id = @target_id AND cmat.comparator_id = @comparator_id @@ -519,9 +572,9 @@ getCohortMethodAttrition <- function(connectionHandler, resultsSchema, tablePref " result <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + #database_table = resultDatabaseSettings$databaseTable, target_id = targetId, comparator_id = comparatorId, outcome_id = outcomeId, @@ -540,16 +593,23 @@ getCohortMethodAttrition <- function(connectionHandler, resultsSchema, tablePref } -getCohortMethodStudyPeriod <- function(connectionHandler, targetId, comparatorId, databaseId) { - sql <- "SELECT min_date, - max_date - FROM comparison_summary +getCohortMethodStudyPeriod <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + databaseId + ) { + sql <- "SELECT min_date, max_date + FROM @schema.@cm_table_prefixcomparison_summary WHERE target_id = @target_id AND comparator_id = @comparator_id AND database_id = '@database_id';" studyPeriod <- connectionHandler$queryDb( sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, target_id = targetId, comparator_id = comparatorId, database_id = databaseId @@ -558,15 +618,24 @@ getCohortMethodStudyPeriod <- function(connectionHandler, targetId, comparatorId } -getCohortMethodPropensityModel <- function(connectionHandler, resultsSchema, tablePrefix, targetId, comparatorId, analysisId, databaseId) { +getCohortMethodPropensityModel <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + analysisId, + databaseId + ) { sqlTmp <- " SELECT cmpm.coefficient, cmc.covariate_id, cmc.covariate_name FROM - @results_schema.@table_prefixcovariate cmc - JOIN @results_schema.@table_prefixpropensity_model cmpm ON cmc.covariate_id = cmpm.covariate_id AND cmc.database_id = cmpm.database_id + @schema.@cm_table_prefixcovariate cmc + JOIN @schema.@cm_table_prefixpropensity_model cmpm + ON cmc.covariate_id = cmpm.covariate_id + AND cmc.database_id = cmpm.database_id WHERE cmpm.target_id = @target_id AND cmpm.comparator_id = @comparator_id @@ -585,7 +654,7 @@ getCohortMethodPropensityModel <- function(connectionHandler, resultsSchema, tab covariate_id, covariate_name FROM - @results_schema.@table_prefixcovariate + @schema.@cm_table_prefixcovariate WHERE analysis_id = @analysis_id AND database_id = '@database_id' @@ -593,7 +662,8 @@ getCohortMethodPropensityModel <- function(connectionHandler, resultsSchema, tab SELECT 0 as covariate_id, 'intercept' as covariate_name) cmc - JOIN @results_schema.@table_prefixpropensity_model cmpm ON cmc.covariate_id = cmpm.covariate_id + JOIN @schema.@cm_table_prefixpropensity_model cmpm + ON cmc.covariate_id = cmpm.covariate_id WHERE cmpm.target_id = @target_id AND cmpm.comparator_id = @comparator_id @@ -603,8 +673,8 @@ getCohortMethodPropensityModel <- function(connectionHandler, resultsSchema, tab model <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, target_id = targetId, comparator_id = comparatorId, analysis_id = analysisId, @@ -616,8 +686,22 @@ getCohortMethodPropensityModel <- function(connectionHandler, resultsSchema, tab -getCohortMethodNegativeControlEstimates <- function(cohortMethodResult, connectionHandler, targetId, comparatorId, analysisId) { - subset <- getCohortMethodControlResults(cohortMethodResult, connectionHandler, targetId, comparatorId, analysisId, includePositiveControls = FALSE) +getCohortMethodNegativeControlEstimates <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + analysisId + ) { + + subset <- getCohortMethodControlResults( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + targetId = targetId, + comparatorId =comparatorId, + analysisId = analysisId, + includePositiveControls = FALSE + ) subset <- subset[, c("databaseId", "logRr", "seLogRr")] if(nrow(subset) == 0) return(NULL) diff --git a/R/helpers-cohort-methodPlotsAndTables.R b/R/helpers-cohort-methodPlotsAndTables.R index e5ebbde8..e7a5122c 100644 --- a/R/helpers-cohort-methodPlotsAndTables.R +++ b/R/helpers-cohort-methodPlotsAndTables.R @@ -33,8 +33,15 @@ prepareCohortMethodFollowUpDistTable <- function(followUpDist) { # used in estimation-power -prepareCohortMethodPowerTable <- function(mainResults, connectionHandler , resultsSchema, tablePrefix) { - analyses <- getCohortMethodAnalyses(connectionHandler , resultsSchema, tablePrefix) +prepareCohortMethodPowerTable <- function( + mainResults, + connectionHandler , + resultDatabaseSettings + ) { + analyses <- getCohortMethodAnalyses( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) table <- merge(mainResults, analyses) alpha <- 0.05 power <- 0.8 diff --git a/R/helpers-cohortGeneratorDataPulls.R b/R/helpers-cohortGeneratorDataPulls.R index 77384137..07fcb0a3 100644 --- a/R/helpers-cohortGeneratorDataPulls.R +++ b/R/helpers-cohortGeneratorDataPulls.R @@ -2,51 +2,46 @@ getCohortGeneratorCohortCounts <- function( connectionHandler, - resultsSchema, - tablePrefix = 'cg_', - databaseTable, - databaseTablePrefix + resultDatabaseSettings ) { sql <- "SELECT cc.cohort_id, cc.cohort_entries, cc.cohort_subjects, - dt.cdm_source_name, cd.cohort_name FROM @results_schema.@table_prefixCOHORT_COUNT cc - join @results_schema.@database_table_prefix@database_table dt + dt.cdm_source_name, cd.cohort_name + FROM @schema.@cg_table_prefixCOHORT_COUNT cc + join @schema.@database_table_prefix@database_table dt on cc.database_id = dt.database_id - join @results_schema.@table_prefixCOHORT_DEFINITION cd + join @schema.@cg_table_prefixCOHORT_DEFINITION cd on cd.cohort_definition_id = cc.cohort_id ;" return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable, - database_table_prefix = databaseTablePrefix + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) ) } getCohortGeneratorCohortMeta <- function( connectionHandler, - resultsSchema, - tablePrefix = 'cg_', - databaseTable, - databaseTablePrefix + resultDatabaseSettings ) { sql <- "SELECT cg.cohort_id, cg.cohort_name, cg.generation_status, cg.start_time, cg.end_time, dt.cdm_source_name - from @results_schema.@table_prefixCOHORT_GENERATION cg - join @results_schema.@database_table_prefix@database_table dt + from @schema.@cg_table_prefixCOHORT_GENERATION cg + join @schema.@database_table_prefix@database_table dt on cg.database_id = dt.database_id ;" df <- connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable, - database_table_prefix = databaseTablePrefix + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) df2 <- df %>% @@ -71,26 +66,24 @@ getCohortGeneratorCohortMeta <- function( getCohortGeneratorCohortInclusionSummary <- function( connectionHandler, - resultsSchema, - tablePrefix = 'cg_', - databaseTable, - databaseTablePrefix + resultDatabaseSettings ) { sql <- "SELECT css.cohort_definition_id, css.base_count, css.final_count, css.mode_id, - dt.cdm_source_name, cd.cohort_name FROM @results_schema.@table_prefixCOHORT_SUMMARY_STATS css - join @results_schema.@database_table_prefix@database_table dt + dt.cdm_source_name, cd.cohort_name + FROM @schema.@cg_table_prefixCOHORT_SUMMARY_STATS css + join @schema.@database_table_prefix@database_table dt on css.database_id = dt.database_id - join @results_schema.@table_prefixCOHORT_DEFINITION cd + join @schema.@cg_table_prefixCOHORT_DEFINITION cd on cd.cohort_definition_id = css.cohort_definition_id ;" return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable, - database_table_prefix = databaseTablePrefix + schema =resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) ) } @@ -99,44 +92,40 @@ getCohortGeneratorCohortInclusionSummary <- function( getCohortGeneratorInclusionRules <- function( connectionHandler, - resultsSchema, - tablePrefix = 'cg_' + resultDatabaseSettings ) { sql <- "SELECT ci.cohort_definition_id, ci.rule_sequence, ci.name as rule_name, - cd.cohort_name FROM @results_schema.@table_prefixCOHORT_INCLUSION ci - join @results_schema.@table_prefixCOHORT_DEFINITION cd + cd.cohort_name FROM @schema.@cg_table_prefixCOHORT_INCLUSION ci + join @schema.@cg_table_prefixCOHORT_DEFINITION cd on cd.cohort_definition_id = ci.cohort_definition_id ;" return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) ) } getCohortGeneratorInclusionStats <- function( connectionHandler, - resultsSchema, - tablePrefix = 'cg_', - databaseTable, - databaseTablePrefix + resultDatabaseSettings ) { sql <- "SELECT cir.database_id, cir.cohort_definition_id, cir.inclusion_rule_mask, cir.person_count, cir.mode_id, - dt.cdm_source_name FROM @results_schema.@table_prefixCOHORT_INC_RESULT cir - join @results_schema.@database_table_prefix@database_table dt + dt.cdm_source_name FROM @schema.@cg_table_prefixCOHORT_INC_RESULT cir + join @schema.@database_table_prefix@database_table dt on cir.database_id = dt.database_id ;" return( connectionHandler$queryDb( sql = sql, - results_schema = resultsSchema, - table_prefix = tablePrefix, - database_table = databaseTable, - database_table_prefix = databaseTablePrefix + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) ) } diff --git a/R/helpers-getHelp.R b/R/helpers-getHelp.R index f49b1e94..a4860cd8 100644 --- a/R/helpers-getHelp.R +++ b/R/helpers-getHelp.R @@ -1,6 +1,6 @@ getPredictionHelp <- function(file){ fileLoc <- system.file( - 'prediction-www', + 'patient-level-prediction-www', file, package = "OhdsiShinyModules" ) diff --git a/R/helpers-getPredictionResult.R b/R/helpers-getPredictionResult.R index 1633af11..e68d72ac 100644 --- a/R/helpers-getPredictionResult.R +++ b/R/helpers-getPredictionResult.R @@ -1,21 +1,24 @@ getPredictionResult <- function( connectionHandler, + resultDatabaseSettings, tableName, - performanceId, - schema + performanceId ){ + # add prefix to tableName + tableName <- paste0(resultDatabaseSettings$plpTablePrefix,tableName) + shiny::withProgress(message = paste('Extracting PLP results from', tableName), value = 0, { shiny::incProgress(1/3, detail = paste("Translating and rendering ")) - sql <- "SELECT * FROM @my_schema.@table_name WHERE performance_id = @performance_id" + sql <- "SELECT * FROM @schema.@table_name WHERE performance_id = @performance_id" shiny::incProgress(2/3, detail = paste("Extracting data ")) result <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, table_name = tableName, performance_id = performanceId() ) diff --git a/R/helpers-sccsDataPulls.R b/R/helpers-sccsDataPulls.R index 128a9e6f..cc0ee126 100644 --- a/R/helpers-sccsDataPulls.R +++ b/R/helpers-sccsDataPulls.R @@ -9,12 +9,12 @@ sccsGetOutcomes <- function( eos.outcome_id from - @my_schema.@sccs_table_prefixexposures_outcome_set as eos + @schema.@sccs_table_prefixexposures_outcome_set as eos inner join - @my_schema.@cg_table_prefixcohort_definition as c + @schema.@cg_table_prefixcohort_definition as c on c.cohort_definition_id = eos.outcome_id inner join - @my_schema.@sccs_table_prefixexposure as e + @schema.@sccs_table_prefixexposure as e on e.exposures_outcome_set_id = eos.exposures_outcome_set_id --where e.true_effect_size != 1 @@ -22,9 +22,9 @@ sccsGetOutcomes <- function( " outcomes <- connectionHandler$queryDb( sql, - my_schema = resultDatabaseSettings$schema, - cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, - sccs_table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, snakeCaseToCamelCase = TRUE ) @@ -46,16 +46,16 @@ sccsGetExposures <- function( e.era_id as exposure_id from - @my_schema.@cg_table_prefixcohort_definition as c + @schema.@cg_table_prefixcohort_definition as c inner join - @my_schema.@sccs_table_prefixexposure as e + @schema.@sccs_table_prefixexposure as e on e.era_id = c.cohort_definition_id; " exposures <- connectionHandler$queryDb( sql, - my_schema = resultDatabaseSettings$schema, - cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, - sccs_table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, snakeCaseToCamelCase = TRUE ) @@ -77,17 +77,17 @@ sccsGetDatabases <- function( r.database_id from - @my_schema.@database_table_prefix@database_table ds + @schema.@database_table_prefix@database_table ds inner join - @my_schema.@sccs_table_prefixresult as r + @schema.@sccs_table_prefixresult as r on r.database_id = ds.database_id; " dbs <- connectionHandler$queryDb( sql, - my_schema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, database_table_prefix = resultDatabaseSettings$databaseTablePrefix, database_table = resultDatabaseSettings$databaseTable, - sccs_table_prefix = resultDatabaseSettings$tablePrefix, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, snakeCaseToCamelCase = TRUE ) @@ -108,16 +108,16 @@ sccsGetAnalyses <- function( r.analysis_id from - @my_schema.@sccs_table_prefixresult as r + @schema.@sccs_table_prefixresult as r inner join - @my_schema.@sccs_table_prefixanalysis as a + @schema.@sccs_table_prefixanalysis as a on r.analysis_id = a.analysis_id ; " analyses <- connectionHandler$queryDb( sql, - my_schema = resultDatabaseSettings$schema, - sccs_table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, snakeCaseToCamelCase = TRUE ) @@ -154,29 +154,29 @@ getSccsResults <- function(connectionHandler, sc.covariate_analysis_id, a.description, eos.outcome_id - FROM @database_schema.@table_prefixresult sr + FROM @schema.@sccs_table_prefixresult sr INNER JOIN - @database_schema.@database_table_prefix@database_table ds + @schema.@database_table_prefix@database_table ds ON sr.database_id = ds.database_id INNER JOIN - @database_schema.@table_prefixdiagnostics_summary sds ON ( + @schema.@sccs_table_prefixdiagnostics_summary sds ON ( sds.exposures_outcome_set_id = sr.exposures_outcome_set_id AND sds.database_id = sr.database_id AND sds.analysis_id = sr.analysis_id AND sds.covariate_id = sr.covariate_id ) INNER JOIN - @database_schema.@table_prefixcovariate sc ON ( + @schema.@sccs_table_prefixcovariate sc ON ( sc.exposures_outcome_set_id = sr.exposures_outcome_set_id AND sc.database_id = sr.database_id AND sc.analysis_id = sr.analysis_id AND sc.covariate_id = sr.covariate_id ) - INNER JOIN @database_schema.@table_prefixexposures_outcome_set eos + INNER JOIN @schema.@sccs_table_prefixexposures_outcome_set eos ON eos.exposures_outcome_set_id = sr.exposures_outcome_set_id INNER JOIN - @database_schema.@table_prefixanalysis a + @schema.@sccs_table_prefixanalysis a on a.analysis_id = sr.analysis_id WHERE sr.analysis_id IN (@analysis_ids) @@ -187,10 +187,10 @@ getSccsResults <- function(connectionHandler, results <- connectionHandler$queryDb( sql, - database_schema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, database_table_prefix = resultDatabaseSettings$databaseTablePrefix, database_table = resultDatabaseSettings$databaseTable, - table_prefix = resultDatabaseSettings$tablePrefix, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_ids = paste(quoteLiterals(databaseIds), collapse = ','), analysis_ids = analysisIds, outcome_ids = paste(outcomeIds, collapse = ','), @@ -214,22 +214,22 @@ getSccsModel <- function(connectionHandler, ELSE CONCAT(sc.covariate_name, ' : ', era.era_name) END as covariate_name, scr.covariate_id, scr.rr, scr.ci_95_lb, scr.ci_95_ub - FROM @database_schema.@table_prefixcovariate_result scr - INNER JOIN @database_schema.@table_prefixcovariate sc ON ( + FROM @schema.@sccs_table_prefixcovariate_result scr + INNER JOIN @schema.@sccs_table_prefixcovariate sc ON ( sc.exposures_outcome_set_id = scr.exposures_outcome_set_id AND sc.database_id = scr.database_id AND sc.analysis_id = scr.analysis_id AND sc.covariate_id = scr.covariate_id ) - LEFT JOIN @database_schema.@cg_table_prefixcohort_definition cd + LEFT JOIN @schema.@cg_table_prefixcohort_definition cd ON cd.cohort_definition_id = sc.era_id - LEFT JOIN @database_schema.@table_prefixera era ON ( + LEFT JOIN @schema.@sccs_table_prefixera era ON ( era.exposures_outcome_set_id = scr.exposures_outcome_set_id AND era.database_id = scr.database_id AND era.analysis_id = scr.analysis_id AND era.era_id = sc.era_id ) - INNER JOIN @database_schema.@table_prefixexposures_outcome_set eos + INNER JOIN @schema.@sccs_table_prefixexposures_outcome_set eos ON eos.exposures_outcome_set_id = scr.exposures_outcome_set_id @@ -241,9 +241,9 @@ getSccsModel <- function(connectionHandler, " connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, - cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, database_id = databaseId, analysis_id = analysisId, outcome_id = outcomeId, @@ -259,9 +259,9 @@ getSccsTimeTrend <- function(connectionHandler, analysisId) { sql <- " SELECT tt.* - FROM @database_schema.@table_prefixtime_trend tt + FROM @schema.@sccs_table_prefixtime_trend tt inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on tt.exposures_outcome_set_id = eos.exposures_outcome_set_id WHERE database_id = '@database_id' AND analysis_id = @analysis_id @@ -269,8 +269,8 @@ getSccsTimeTrend <- function(connectionHandler, " connectionHandler$queryDb( sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, analysis_id = analysisId, outcome_id = outcomeId, @@ -289,9 +289,9 @@ getSccsTimeToEvent <- function(connectionHandler, sql <- " SELECT ds.pre_exposure_p - FROM @database_schema.@table_prefixdiagnostics_summary ds + FROM @schema.@sccs_table_prefixdiagnostics_summary ds inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on ds.exposures_outcome_set_id = eos.exposures_outcome_set_id WHERE ds.database_id = '@database_id' AND ds.covariate_id = @covariate_id @@ -301,8 +301,8 @@ getSccsTimeToEvent <- function(connectionHandler, p <- connectionHandler$queryDb( sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, analysis_id = analysisId, outcome_id = outcomeId, @@ -312,9 +312,9 @@ getSccsTimeToEvent <- function(connectionHandler, sql <- " SELECT tte.*, @p as p - FROM @database_schema.@table_prefixtime_to_event tte + FROM @schema.@sccs_table_prefixtime_to_event tte inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on tte.exposures_outcome_set_id = eos.exposures_outcome_set_id WHERE tte.database_id = '@database_id' AND tte.era_id = @exposure_id @@ -324,8 +324,8 @@ getSccsTimeToEvent <- function(connectionHandler, timeToEvent <- connectionHandler$queryDb( sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, analysis_id = analysisId, outcome_id = outcomeId, @@ -348,9 +348,9 @@ getSccsAttrition <- function(connectionHandler, covariateId) { sql <- " SELECT a.* - FROM @database_schema.@table_prefixattrition a + FROM @schema.@sccs_table_prefixattrition a inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on a.exposures_outcome_set_id = eos.exposures_outcome_set_id WHERE a.database_id = '@database_id' @@ -359,8 +359,8 @@ getSccsAttrition <- function(connectionHandler, AND a.covariate_id = @covariate_id " connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, analysis_id = analysisId, outcome_id = outcomeId, @@ -375,9 +375,9 @@ getSccsEventDepObservation <- function(connectionHandler, analysisId) { sql <- " SELECT o.* - FROM @database_schema.@table_prefixevent_dep_observation o + FROM @schema.@sccs_table_prefixevent_dep_observation o inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on o.exposures_outcome_set_id = eos.exposures_outcome_set_id WHERE o.database_id = '@database_id' @@ -385,8 +385,8 @@ getSccsEventDepObservation <- function(connectionHandler, AND eos.outcome_id = @outcome_id " connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, analysis_id = analysisId, outcome_id = outcomeId, @@ -400,9 +400,9 @@ getSccsAgeSpanning <- function(connectionHandler, analysisId) { sql <- " SELECT asp.* - FROM @database_schema.@table_prefixage_spanning asp + FROM @schema.@sccs_table_prefixage_spanning asp inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on asp.exposures_outcome_set_id = eos.exposures_outcome_set_id WHERE asp.database_id = '@database_id' @@ -410,8 +410,8 @@ getSccsAgeSpanning <- function(connectionHandler, AND eos.outcome_id = @outcome_id " connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, analysis_id = analysisId, outcome_id = outcomeId, @@ -425,9 +425,9 @@ getSccsCalendarTimeSpanning <- function(connectionHandler, analysisId) { sql <- " SELECT ts.* - FROM @database_schema.@table_prefixcalendar_time_spanning ts + FROM @schema.@sccs_table_prefixcalendar_time_spanning ts inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on ts.exposures_outcome_set_id = eos.exposures_outcome_set_id WHERE ts.database_id = '@database_id' @@ -435,8 +435,8 @@ getSccsCalendarTimeSpanning <- function(connectionHandler, AND eos.outcome_id = @outcome_id " connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, analysis_id = analysisId, outcome_id = outcomeId, @@ -452,9 +452,9 @@ getSccsSpline <- function(connectionHandler, sql <- " SELECT s.* - FROM @database_schema.@table_prefixspline s + FROM @schema.@sccs_table_prefixspline s inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on s.exposures_outcome_set_id = eos.exposures_outcome_set_id WHERE s.database_id = '@database_id' @@ -463,8 +463,8 @@ getSccsSpline <- function(connectionHandler, AND s.spline_type = '@spline_type'; " connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, spline_type = splineType, analysis_id = analysisId, @@ -482,14 +482,14 @@ getSccsControlEstimates <- function(connectionHandler, sql <- " SELECT ci_95_lb, ci_95_ub, log_rr, se_log_rr, calibrated_ci_95_lb, calibrated_ci_95_ub, calibrated_log_rr, calibrated_se_log_rr, true_effect_size - FROM @database_schema.@table_prefixresult sr - INNER JOIN @database_schema.@table_prefixcovariate sc ON ( + FROM @schema.@sccs_table_prefixresult sr + INNER JOIN @schema.@sccs_table_prefixcovariate sc ON ( sc.exposures_outcome_set_id = sr.exposures_outcome_set_id AND sc.database_id = sr.database_id AND sc.analysis_id = sr.analysis_id AND sc.covariate_id = sr.covariate_id ) - INNER JOIN @database_schema.@table_prefixexposure se ON ( + INNER JOIN @schema.@sccs_table_prefixexposure se ON ( se.exposures_outcome_set_id = sr.exposures_outcome_set_id AND se.era_id = sc.era_id ) @@ -498,8 +498,8 @@ getSccsControlEstimates <- function(connectionHandler, AND sr.covariate_id = @covariate_id " connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, covariate_id = covariateId, analysis_id = analysisId, @@ -515,13 +515,13 @@ getSccsDiagnosticsSummary <- function(connectionHandler, exposureId) { sql <- " SELECT ds.* - FROM @database_schema.@table_prefixdiagnostics_summary ds + FROM @schema.@sccs_table_prefixdiagnostics_summary ds inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on ds.exposures_outcome_set_id = eos.exposures_outcome_set_id inner join - @database_schema.@table_prefixcovariate cov + @schema.@sccs_table_prefixcovariate cov on cov.covariate_id = ds.covariate_id and cov.exposures_outcome_set_id = ds.exposures_outcome_set_id and cov.analysis_id = ds.analysis_id and @@ -534,8 +534,8 @@ getSccsDiagnosticsSummary <- function(connectionHandler, AND cov.era_id = @exposure_id " connectionHandler$queryDb(sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_id = databaseId, covariate_id = covariateId, analysis_id = analysisId, diff --git a/R/prediction-calibration.R b/R/patient-level-prediction-calibration.R similarity index 96% rename from R/prediction-calibration.R rename to R/patient-level-prediction-calibration.R index ebe39524..3e3e0d97 100644 --- a/R/prediction-calibration.R +++ b/R/patient-level-prediction-calibration.R @@ -1,4 +1,4 @@ -# @file prediction-calibration.R +# @file patient-level-prediction-calibration.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the prediction model calibration module #' #' @export -predictionCalibrationViewer <- function(id) { +patientLevelPredictionCalibrationViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -79,20 +79,18 @@ predictionCalibrationViewer <- function(id) { #' @param performanceId the performance id in the database #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the prediction calibration module #' #' @export -predictionCalibrationServer <- function( +patientLevelPredictionCalibrationServer <- function( id, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -104,8 +102,8 @@ predictionCalibrationServer <- function( data <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(plpTablePrefix,'evaluation_statistics'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'evaluation_statistics' ) } else{ data <- NULL @@ -194,16 +192,16 @@ predictionCalibrationServer <- function( value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(plpTablePrefix,'calibration_summary'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'calibration_summary' ) calibrationSummary(value) value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(plpTablePrefix,'demographic_summary'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'demographic_summary' ) demographicSummary(value) } diff --git a/R/prediction-covariateSummary.R b/R/patient-level-prediction-covariateSummary.R similarity index 90% rename from R/prediction-covariateSummary.R rename to R/patient-level-prediction-covariateSummary.R index e0d55c35..a321a801 100644 --- a/R/prediction-covariateSummary.R +++ b/R/patient-level-prediction-covariateSummary.R @@ -1,4 +1,4 @@ -# @file prediction-covariateSummary.R +# @file patient-level-prediction-covariateSummary.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the covariate summary module #' #' @export -predictionCovariateSummaryViewer <- function(id) { +patientLevelPredictionCovariateSummaryViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -92,22 +92,20 @@ predictionCovariateSummaryViewer <- function(id) { #' @param performanceId unique id for the performance results #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the covariate summary module #' #' @export -predictionCovariateSummaryServer <- function( +patientLevelPredictionCovariateSummaryServer <- function( id, modelDesignId, developmentDatabaseId, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix = '' + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -118,11 +116,10 @@ predictionCovariateSummaryServer <- function( !is.null(performanceId()) & inputSingleView() == 'Model' ){ - loadCovSumFromDb( + loadPredictionCovSumFromDb( performanceId = performanceId, - schema = schema, - connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings ) } else{ NULL @@ -135,25 +132,23 @@ predictionCovariateSummaryServer <- function( !is.null(developmentDatabaseId()) & inputSingleView() == 'Model' ){ - getIntercept( + getPredictionIntercept( modelDesignId = modelDesignId, databaseId = developmentDatabaseId, connectionHandler = connectionHandler, - schema = schema, - plpTablePrefix = plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) } else{ NULL } }) - hyperParamSearch <- shiny::reactive({getHyperParamSearch( + hyperParamSearch <- shiny::reactive({getPredictionHyperParamSearch( inputSingleView = inputSingleView, modelDesignId = modelDesignId, databaseId = developmentDatabaseId, - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) }) # hyper-param @@ -273,26 +268,25 @@ editCovariates <- function(covs){ # get hyper parameters -getHyperParamSearch <- function( +getPredictionHyperParamSearch <- function( inputSingleView, modelDesignId, databaseId, - schema, connectionHandler, - plpTablePrefix + resultDatabaseSettings ){ if(!is.null(modelDesignId()) & inputSingleView() == 'Design Settings'){ - sql <- "SELECT train_details FROM @my_schema.@my_table_appendmodels WHERE database_id = @database_id + sql <- "SELECT train_details FROM @schema.@plp_table_prefixmodels WHERE database_id = @database_id and model_design_id = @model_design_id;" models <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, database_id = databaseId(), model_design_id = modelDesignId(), - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) trainDetails <- ParallelLogger::convertJsonToSettings(models$trainDetails) @@ -431,11 +425,10 @@ plotCovariateSummary <- function(covariateSummary){ # code for database covariate extract -loadCovSumFromDb <- function( +loadPredictionCovSumFromDb <- function( performanceId, - schema, connectionHandler, - plpTablePrefix = '' + resultDatabaseSettings ){ ParallelLogger::logInfo("starting covsum") @@ -443,15 +436,15 @@ loadCovSumFromDb <- function( shiny::incProgress(2/3, detail = paste("Extracting data")) - sql <- "SELECT * FROM @my_schema.@my_table_appendcovariate_summary WHERE performance_id = @performance_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixcovariate_summary WHERE performance_id = @performance_id;" shiny::incProgress(2/3, detail = paste("Data extracted")) covariateSummary <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, performance_id = performanceId(), - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) # format @@ -470,22 +463,21 @@ loadCovSumFromDb <- function( return(covariateSummary) } -getIntercept <- function( +getPredictionIntercept <- function( modelDesignId, databaseId, connectionHandler, - schema, - plpTablePrefix + resultDatabaseSettings ){ - sql <- "SELECT intercept FROM @my_schema.@my_table_appendmodels WHERE database_id = @database_id + sql <- "SELECT intercept FROM @schema.@plp_table_prefixmodels WHERE database_id = @database_id and model_design_id = @model_design_id" models <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, database_id = databaseId(), model_design_id = modelDesignId(), - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) intercept <- models$intercept diff --git a/R/prediction-cutoff.R b/R/patient-level-prediction-cutoff.R similarity index 96% rename from R/prediction-cutoff.R rename to R/patient-level-prediction-cutoff.R index c651669d..0d70a104 100644 --- a/R/prediction-cutoff.R +++ b/R/patient-level-prediction-cutoff.R @@ -1,4 +1,4 @@ -# @file prediction-cutoff.R +# @file patient-level-prediction-cutoff.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the prediction cut-off module #' #' @export -predictionCutoffViewer <- function(id) { +patientLevelPredictionCutoffViewer <- function(id) { ns <- shiny::NS(id) @@ -95,20 +95,18 @@ predictionCutoffViewer <- function(id) { #' @param performanceId the performance id in the database #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the prediction cut-off module #' #' @export -predictionCutoffServer <- function( +patientLevelPredictionCutoffServer <- function( id, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -120,8 +118,8 @@ predictionCutoffServer <- function( value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - schema = schema, - tableName = paste0(plpTablePrefix, 'threshold_summary') + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'threshold_summary' ) return(value) } else{ diff --git a/R/prediction-designSummary.R b/R/patient-level-prediction-designSummary.R similarity index 83% rename from R/prediction-designSummary.R rename to R/patient-level-prediction-designSummary.R index f74f7c4a..f88b90d5 100644 --- a/R/prediction-designSummary.R +++ b/R/patient-level-prediction-designSummary.R @@ -1,4 +1,4 @@ -# @file prediction-designSummary.R +# @file patient-level-prediction-designSummary.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the prediction design module #' #' @export -predictionDesignSummaryViewer <- function(id) { +patientLevelPredictionDesignSummaryViewer <- function(id) { ns <- shiny::NS(id) shiny::div( inputSelectionViewer(ns("input-selection")), @@ -47,20 +47,16 @@ predictionDesignSummaryViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the plp tables in the result schema -#' @param cohortTablePrefix a string that appends the cohort tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the prediction design module #' #' @export -predictionDesignSummaryServer <- function( +patientLevelPredictionDesignSummaryServer <- function( id, connectionHandler, - schema, - plpTablePrefix, - cohortTablePrefix + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -68,16 +64,12 @@ predictionDesignSummaryServer <- function( targetIds <- getPlpCohortIds( connectionHandler = connectionHandler, - schema = schema, - plpTablePrefix = plpTablePrefix, - cohortTablePrefix = cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettings, type = 'target' ) outcomeIds <- getPlpCohortIds( connectionHandler = connectionHandler, - schema = schema, - plpTablePrefix = plpTablePrefix, - cohortTablePrefix = cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettings, type = 'outcome' ) @@ -129,11 +121,9 @@ predictionDesignSummaryServer <- function( ) designSummaryTable <- shiny::reactive({ - getDesignSummary( + getPredictionDesignSummary( connectionHandler = connectionHandler, - schema = schema, - plpTablePrefix = plpTablePrefix, - cohortTablePrefix = cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettings, targetIds = inputSelected()$targetIds, outcomeIds = inputSelected()$outcomeIds ) @@ -268,19 +258,17 @@ predictionDesignSummaryServer <- function( getPlpCohortIds <- function( connectionHandler, - schema, - plpTablePrefix, - cohortTablePrefix, + resultDatabaseSettings, type = 'target' ){ sql <- "SELECT distinct cohorts.cohort_id, cohorts.cohort_name FROM - @my_schema.@my_table_appendmodel_designs as model_designs + @schema.@plp_table_prefixmodel_designs as model_designs inner join - (SELECT c.cohort_id, cd.cohort_name FROM @my_schema.@my_table_appendcohorts c - inner join @my_schema.@cohort_table_appendcohort_definition cd + (SELECT c.cohort_id, cd.cohort_name FROM @schema.@plp_table_prefixcohorts c + inner join @schema.@cg_table_prefixcohort_definition cd on c.cohort_definition_id = cd.cohort_definition_id ) AS cohorts ON model_designs.@type_id = cohorts.cohort_id @@ -288,9 +276,9 @@ getPlpCohortIds <- function( result <- connectionHandler$queryDb( sql = sql, - my_schema = schema, - my_table_append = plpTablePrefix, - cohort_table_append = cohortTablePrefix, + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, type = type ) @@ -300,11 +288,9 @@ getPlpCohortIds <- function( return(res) } -getDesignSummary <- function( +getPredictionDesignSummary <- function( connectionHandler, - schema, - plpTablePrefix = 'plp_', - cohortTablePrefix = 'plp_', + resultDatabaseSettings, targetIds, outcomeIds ){ @@ -332,40 +318,40 @@ getDesignSummary <- function( COUNT(distinct v.database_id) val_databases FROM - @my_schema.@my_table_appendmodel_designs as model_designs + @schema.@plp_table_prefixmodel_designs as model_designs inner join - @my_schema.@my_table_appendmodel_settings as model_settings + @schema.@plp_table_prefixmodel_settings as model_settings on model_designs.model_setting_id = model_settings.model_setting_id LEFT JOIN - @my_schema.@my_table_appendperformances AS results + @schema.@plp_table_prefixperformances AS results on model_designs.model_design_id = results.model_design_id - LEFT JOIN (select * from @my_schema.@my_table_appendEVALUATION_STATISTICS where EVALUATION = 'Test' and METRIC = 'AUROC') p + LEFT JOIN (select * from @schema.@plp_table_prefixEVALUATION_STATISTICS where EVALUATION = 'Test' and METRIC = 'AUROC') p on p.performance_id = results.performance_id LEFT JOIN - (SELECT c.cohort_id, cd.cohort_name FROM @my_schema.@my_table_appendcohorts c - inner join @my_schema.@cohort_table_appendcohort_definition cd + (SELECT c.cohort_id, cd.cohort_name FROM @schema.@plp_table_prefixcohorts c + inner join @schema.@cg_table_prefixcohort_definition cd on c.cohort_definition_id = cd.cohort_definition_id ) AS targets ON model_designs.target_id = targets.cohort_id LEFT JOIN - (SELECT c.cohort_id, cd.cohort_name FROM @my_schema.@my_table_appendcohorts c - inner join @my_schema.@cohort_table_appendcohort_definition cd + (SELECT c.cohort_id, cd.cohort_name FROM @schema.@plp_table_prefixcohorts c + inner join @schema.@cg_table_prefixcohort_definition cd on c.cohort_definition_id = cd.cohort_definition_id ) AS outcomes ON model_designs.outcome_id = outcomes.cohort_id - LEFT JOIN @my_schema.@my_table_appendtars AS tars + LEFT JOIN @schema.@plp_table_prefixtars AS tars ON model_designs.tar_id = tars.tar_id - LEFT JOIN @my_schema.@my_table_appenddatabase_details AS d + LEFT JOIN @schema.@plp_table_prefixdatabase_details AS d ON results.development_database_id = d.database_id - LEFT JOIN @my_schema.@my_table_appenddatabase_details AS v + LEFT JOIN @schema.@plp_table_prefixdatabase_details AS v ON results.validation_database_id = v.database_id - LEFT JOIN @my_schema.@my_table_appenddiagnostics AS diag + LEFT JOIN @schema.@plp_table_prefixdiagnostics AS diag ON results.development_database_id = diag.database_id WHERE targets.cohort_id in (@target_ids) @@ -380,9 +366,9 @@ getDesignSummary <- function( summaryTable <- connectionHandler$queryDb( sql = sql, - my_schema = schema, - my_table_append = plpTablePrefix, - cohort_table_append = cohortTablePrefix, + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, target_ids = paste(targetIds, collapse = ','), outcome_ids = paste(outcomeIds, collapse = ',') ) diff --git a/R/prediction-diagnostics.R b/R/patient-level-prediction-diagnostics.R similarity index 87% rename from R/prediction-diagnostics.R rename to R/patient-level-prediction-diagnostics.R index 747c818b..ff388595 100644 --- a/R/prediction-diagnostics.R +++ b/R/patient-level-prediction-diagnostics.R @@ -1,4 +1,4 @@ -# @file prediction-diagnostics.R +# @file patient-level-prediction-diagnostics.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the prediction diagnostic module #' #' @export -predictionDiagnosticsViewer <- function(id) { +patientLevelPredictionDiagnosticsViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -45,22 +45,18 @@ predictionDiagnosticsViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param modelDesignId the unique id for the model design -#' @param schema the database schema for the model results #' @param connectionHandler the connection to the prediction result database -#' @param plpTablePrefix a string that appends the tables in the result schema -#' @param databaseTablePrefix a string that appends the database_meta_data table +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return -#' The server to the predcition diagnostic module +#' The server to the prediction diagnostic module #' #' @export -predictionDiagnosticsServer <- function( +patientLevelPredictionDiagnosticsServer <- function( id, modelDesignId, - schema, connectionHandler, - plpTablePrefix, - databaseTablePrefix + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -74,12 +70,10 @@ predictionDiagnosticsServer <- function( shiny::observe({ if(!is.null(modelDesignId()) ){ - diagnosticTable <- getDiagnostics( + diagnosticTable <- getPredictionDiagnostics( modelDesignId = modelDesignId(), - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix, - databaseTablePrefix = databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) # input tables output$diagnosticSummaryTable <- reactable::renderReactable({ @@ -233,11 +227,10 @@ predictionDiagnosticsServer <- function( shiny::observeEvent( input$show_participants, { - participants <- getDiagnosticParticipants( + participants <- getPredictionDiagnosticParticipants( diagnosticId = diagnosticTable$diagnosticId[input$show_participants$index], - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) output$participants <- reactable::renderReactable({ @@ -283,11 +276,10 @@ predictionDiagnosticsServer <- function( input$show_predictors, { - predTable <- getDiagnosticPredictors( + predTable <- getPredictionDiagnosticPredictors( diagnosticId = diagnosticTable$diagnosticId[input$show_predictors$index], - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) output$predictorPlot <- plotly::renderPlotly({ @@ -360,11 +352,10 @@ predictionDiagnosticsServer <- function( input$show_outcomes, { - outcomeTable <- getDiagnosticOutcomes( + outcomeTable <- getPredictionDiagnosticOutcomes( diagnosticId = diagnosticTable$diagnosticId[input$show_outcomes$index], - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) #output$predictorPlot <- @@ -425,12 +416,10 @@ predictionDiagnosticsServer <- function( # get the data -getDiagnostics <- function( +getPredictionDiagnostics <- function( modelDesignId, - schema, connectionHandler, - plpTablePrefix, - databaseTablePrefix = plpTablePrefix, + resultDatabaseSettings, threshold1_2 = 0.9 ){ if(!is.null(modelDesignId)){ @@ -446,37 +435,37 @@ getDiagnostics <- function( summary.RESULT_VALUE from - (select * from @my_schema.@my_table_appendDIAGNOSTICS where MODEL_DESIGN_ID = @model_design_id) as diagnostics + (select * from @schema.@plp_table_prefixDIAGNOSTICS where MODEL_DESIGN_ID = @model_design_id) as diagnostics inner join - @my_schema.@my_table_appendMODEL_DESIGNS design + @schema.@plp_table_prefixMODEL_DESIGNS design on diagnostics.MODEL_DESIGN_ID = design.MODEL_DESIGN_ID inner join - @my_schema.@my_table_appendDIAGNOSTIC_SUMMARY summary + @schema.@plp_table_prefixDIAGNOSTIC_SUMMARY summary on diagnostics.DIAGNOSTIC_ID = summary.DIAGNOSTIC_ID inner join (select dd.database_id, md.cdm_source_abbreviation as database_name - from @my_schema.@database_table_appenddatabase_meta_data md inner join - @my_schema.@my_table_appenddatabase_details dd + from @schema.@database_table_prefixdatabase_meta_data md inner join + @schema.@plp_table_prefixdatabase_details dd on md.database_id = dd.database_meta_data_id) as database on database.database_id = diagnostics.database_id inner join - @my_schema.@my_table_appendCOHORTS cohortT + @schema.@plp_table_prefixCOHORTS cohortT on cohortT.cohort_id = design.target_id inner join - @my_schema.@my_table_appendCOHORTS cohortO + @schema.@plp_table_prefixCOHORTS cohortO on cohortO.cohort_id = design.outcome_id; " summaryTable <- connectionHandler$queryDb( sql = sql, - my_schema = schema, - my_table_append = plpTablePrefix, + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, model_design_id = modelDesignId, - database_table_append = databaseTablePrefix + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) if(nrow(summaryTable)==0){ @@ -508,20 +497,19 @@ getDiagnostics <- function( } -getDiagnosticParticipants <- function( +getPredictionDiagnosticParticipants <- function( diagnosticId, - schema, connectionHandler, - plpTablePrefix + resultDatabaseSettings ){ - sql <- "SELECT * FROM @my_schema.@my_table_append@table_name WHERE diagnostic_id = @diagnostic_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefix@table_name WHERE diagnostic_id = @diagnostic_id;" participants <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, table_name = 'diagnostic_participants', - my_table_append = plpTablePrefix, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, diagnostic_id = diagnosticId ) @@ -542,40 +530,38 @@ getDiagnosticParticipants <- function( } -getDiagnosticPredictors <- function( +getPredictionDiagnosticPredictors <- function( diagnosticId, - schema, connectionHandler, - plpTablePrefix + resultDatabaseSettings ){ - sql <- "SELECT * FROM @my_schema.@my_table_append@table_name WHERE diagnostic_id = @diagnostic_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefix@table_name WHERE diagnostic_id = @diagnostic_id;" predictors <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, table_name = 'diagnostic_predictors', - my_table_append = plpTablePrefix, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, diagnostic_id = diagnosticId ) return(predictors) } -getDiagnosticOutcomes <- function( +getPredictionDiagnosticOutcomes <- function( diagnosticId, - schema, connectionHandler, - plpTablePrefix + resultDatabaseSettings ){ - sql <- "SELECT * FROM @my_schema.@my_table_append@table_name WHERE diagnostic_id = @diagnostic_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefix@table_name WHERE diagnostic_id = @diagnostic_id;" outcomes <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, table_name = 'diagnostic_outcomes', - my_table_append = plpTablePrefix, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, diagnostic_id = diagnosticId ) diff --git a/R/prediction-discrimination.R b/R/patient-level-prediction-discrimination.R similarity index 97% rename from R/prediction-discrimination.R rename to R/patient-level-prediction-discrimination.R index f1169e9e..cddaa057 100644 --- a/R/prediction-discrimination.R +++ b/R/patient-level-prediction-discrimination.R @@ -1,4 +1,4 @@ -# @file prediction-discrimination.R +# @file patient-level-prediction-discrimination.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the model discrimination results module #' #' @export -predictionDiscriminationViewer <- function(id) { +patientLevelPredictionDiscriminationViewer <- function(id) { ns <- shiny::NS(id) @@ -149,20 +149,18 @@ predictionDiscriminationViewer <- function(id) { #' @param performanceId the performance id in the database #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the model discrimination module #' #' @export -predictionDiscriminationServer <- function( +patientLevelPredictionDiscriminationServer <- function( id, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -170,14 +168,14 @@ predictionDiscriminationServer <- function( sumTable <- shiny::reactive({ if(!is.null(performanceId()) & inputSingleView() == 'Discrimination'){ - print('Discrimination started') + data <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(plpTablePrefix,'evaluation_statistics'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'evaluation_statistics' ) - print('Discrimination ended') + } else{ data <- NULL } @@ -237,16 +235,16 @@ predictionDiscriminationServer <- function( value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(plpTablePrefix,'prediction_distribution'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'prediction_distribution' ) predictionDistribution(value) value <- getPredictionResult( performanceId = performanceId, connectionHandler = connectionHandler, - tableName = paste0(plpTablePrefix,'threshold_summary'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'threshold_summary' ) thresholdSummary(value) } diff --git a/R/prediction-main.R b/R/patient-level-prediction-main.R similarity index 70% rename from R/prediction-main.R rename to R/patient-level-prediction-main.R index e9628d6f..dd13b674 100644 --- a/R/prediction-main.R +++ b/R/patient-level-prediction-main.R @@ -1,4 +1,4 @@ -# @file prediction-main.R +# @file patient-level-prediction-main.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -26,8 +26,8 @@ #' string location of the prediction helper file #' #' @export -predictionHelperFile <- function(){ - fileLoc <- system.file('prediction-www', "prediction.html", package = "OhdsiShinyModules") +patientLevelPredictionHelperFile <- function(){ + fileLoc <- system.file('patient-level-prediction-www', "patient-level-prediction.html", package = "OhdsiShinyModules") return(fileLoc) } @@ -42,7 +42,7 @@ predictionHelperFile <- function(){ #' The user interface to the PatientLevelPrediction viewer module #' #' @export -predictionViewer <- function(id=1) { +patientLevelPredictionViewer <- function(id=1) { ns <- shiny::NS(id) shinydashboard::box( @@ -62,10 +62,10 @@ predictionViewer <- function(id=1) { collapsed = TRUE, title = "Model Designs Summary", width = "100%", - shiny::htmlTemplate(system.file("prediction-www", "help-designSummary.html", package = utils::packageName())) + shiny::htmlTemplate(system.file("patient-level-prediction-www", "help-designSummary.html", package = utils::packageName())) ), - predictionDesignSummaryViewer(ns('designSummaryTab')) + patientLevelPredictionDesignSummaryViewer(ns('designSummaryTab')) ), shiny::tabPanel( @@ -76,7 +76,7 @@ predictionViewer <- function(id=1) { shiny::icon("arrow-left"), style="color: #fff; background-color: #337ab7; border-color: #2e6da4" ), - predictionModelSummaryViewer(ns('modelSummaryTab')) + patientLevelPredictionModelSummaryViewer(ns('modelSummaryTab')) ), shiny::tabPanel( @@ -94,7 +94,7 @@ predictionViewer <- function(id=1) { collapsed = TRUE, title = "Full Result Explorer", width = "100%", - shiny::htmlTemplate(system.file("prediction-www", "help-fullResults.html", package = utils::packageName())) + shiny::htmlTemplate(system.file("patient-level-prediction-www", "help-fullResults.html", package = utils::packageName())) ), shinydashboard::box( @@ -109,38 +109,38 @@ predictionViewer <- function(id=1) { shiny::tabPanel( "Model", - predictionCovariateSummaryViewer(ns('covariateSummary')) + patientLevelPredictionCovariateSummaryViewer(ns('covariateSummary')) ), shiny::tabPanel( "Discrimination", - predictionDiscriminationViewer(ns('discrimination')) + patientLevelPredictionDiscriminationViewer(ns('discrimination')) ), shiny::tabPanel( "Calibration", - predictionCalibrationViewer(ns('calibration')) + patientLevelPredictionCalibrationViewer(ns('calibration')) ), shiny::tabPanel( "Threshold Dependant", - predictionCutoffViewer(ns('cutoff')) + patientLevelPredictionCutoffViewer(ns('cutoff')) ), shiny::tabPanel( "Net Benefit", - predictionNbViewer(ns('netBenefit')) + patientLevelPredictionNbViewer(ns('netBenefit')) ), shiny::tabPanel( "Validation", - predictionValidationViewer(ns('validation')) + patientLevelPredictionValidationViewer(ns('validation')) ), shiny::tabPanel( "Design Settings", - predictionSettingsViewer(ns('settings')) + patientLevelPredictionSettingsViewer(ns('settings')) ) @@ -166,7 +166,7 @@ predictionViewer <- function(id=1) { #' The server for the PatientLevelPrediction module #' #' @export -predictionServer <- function( +patientLevelPredictionServer <- function( id, connectionHandler, resultDatabaseSettings = list(port = 1) @@ -232,12 +232,10 @@ predictionServer <- function( # per prediction model design and lets the user # select a model design id to explore modelDesignId <- shiny::reactiveVal() - designSummary <- predictionDesignSummaryServer( + designSummary <- patientLevelPredictionDesignSummaryServer( id = 'designSummaryTab', connectionHandler = connectionHandler, - schema = resultDatabaseSettings$schema, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix, - cohortTablePrefix = resultDatabaseSettings$cohortTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) @@ -265,17 +263,11 @@ predictionServer <- function( # development database id performanceId <- shiny::reactiveVal() developmentDatabaseId <- shiny::reactiveVal() - performance <- predictionModelSummaryServer( + performance <- patientLevelPredictionModelSummaryServer( id = 'modelSummaryTab', connectionHandler = connectionHandler, - schema = resultDatabaseSettings$schema, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix, modelDesignId = modelDesignId, - databaseTablePrefix = ifelse( - !is.null(resultDatabaseSettings$databaseTablePrefix), - resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$plpTablePrefix - ) + resultDatabaseSettings = resultDatabaseSettings ) @@ -309,20 +301,14 @@ predictionServer <- function( shiny::observeEvent(designSummary$diagnosticId(), { shiny::showModal(shiny::modalDialog( title = "Diagnostic", - predictionDiagnosticsViewer(session$ns('diagnostics')) + patientLevelPredictionDiagnosticsViewer(session$ns('diagnostics')) )) }) - predictionDiagnosticsServer( + patientLevelPredictionDiagnosticsServer( id = 'diagnostics', modelDesignId = designSummary$diagnosticId, - schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix, - databaseTablePrefix = ifelse( - !is.null(resultDatabaseSettings$databaseTablePrefix), - resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$plpTablePrefix - ) + resultDatabaseSettings = resultDatabaseSettings ) # ============================= @@ -354,18 +340,7 @@ predictionServer <- function( tryCatch( {createPredictionProtocol( connectionHandler = connectionHandler, - schema = resultDatabaseSettings$schema, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix, - databaseTablePrefix = ifelse( - !is.null(resultDatabaseSettings$databaseTablePrefix), - resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$plpTablePrefix - ), - cohortTablePrefix= ifelse( - !is.null(resultDatabaseSettings$cohortTablePrefix), - resultDatabaseSettings$cohortTablePrefix, - resultDatabaseSettings$plpTablePrefix - ), + resultDatabaseSettings = resultDatabaseSettings, modelDesignId = designSummary$reportId(), output = protocolOutputLoc, intermediatesDir = file.path(protocolOutputLoc, 'plp-prot') @@ -410,26 +385,18 @@ predictionServer <- function( rmarkdown::render( input = system.file( - 'prediction-document', + 'patient-level-prediction-document', "export-main.Rmd", package = "OhdsiShinyModules" ), intermediates_dir = file.path(tempdir(), 'plp-prot'), output_dir = file.path(input$plpProtocolDownload, paste0('plp_report',designSummary$reportId())), params = list( #TODO UPDATE DOC - connectionHandler = connectionHandler, - resultSchema = resultDatabaseSettings$schema, + connectionHandler = connectionHandler, + resultSchema = resultDatabaseSettings$schema, myTableAppend = resultDatabaseSettings$plpTablePrefix, - databaseTableAppend = ifelse( - !is.null(resultDatabaseSettings$databaseTablePrefix), - resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$plpTablePrefix - ), - cohortTableAppend = ifelse( - !is.null(resultDatabaseSettings$cohortTablePrefix), - resultDatabaseSettings$cohortTablePrefix, - resultDatabaseSettings$plpTablePrefix - ), + databaseTableAppend = resultDatabaseSettings$databaseTablePrefix, + cohortTableAppend = resultDatabaseSettings$cgTablePrefix, modelDesignIds = designSummary$reportId() ) ) @@ -441,107 +408,74 @@ predictionServer <- function( # =========================================== output$resultSelectText <- shiny::renderUI( - getResultSelection( + getPlpResultSelection( connectionHandler = connectionHandler, - schema = resultDatabaseSettings$schema, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix, + resultDatabaseSettings = resultDatabaseSettings, modelDesignId = modelDesignId, - performanceId = performanceId, - cohortTablePrefix = ifelse( - !is.null(resultDatabaseSettings$cohortTablePrefix), - resultDatabaseSettings$cohortTablePrefix, - resultDatabaseSettings$plpTablePrefix - ), - databaseTablePrefix = ifelse( - !is.null(resultDatabaseSettings$databaseTablePrefix), - resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$plpTablePrefix - ) + performanceId = performanceId ) ) - predictionCovariateSummaryServer( + patientLevelPredictionCovariateSummaryServer( id = 'covariateSummary', modelDesignId = modelDesignId, # reactive developmentDatabaseId = developmentDatabaseId, # reactive performanceId = performanceId, # reactive connectionHandler = connectionHandler, inputSingleView = singleViewValue, - schema = resultDatabaseSettings$schema, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) - predictionSettingsServer( + patientLevelPredictionSettingsServer( id = 'settings', modelDesignId = modelDesignId, # reactive developmentDatabaseId = developmentDatabaseId, # reactive performanceId = performanceId, # reactive - schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix, - cohortTablePrefix = ifelse( - !is.null(resultDatabaseSettings$cohortTablePrefix), - resultDatabaseSettings$cohortTablePrefix, - resultDatabaseSettings$plpTablePrefix - ), - databaseTablePrefix = ifelse( - !is.null(resultDatabaseSettings$databaseTablePrefix), - resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$plpTablePrefix - ) + resultDatabaseSettings = resultDatabaseSettings ) - predictionCutoffServer( + patientLevelPredictionCutoffServer( id = 'cutoff', performanceId = performanceId, - schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) - predictionDiscriminationServer( + patientLevelPredictionDiscriminationServer( id = 'discrimination', performanceId = performanceId, - schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) - predictionCalibrationServer( + patientLevelPredictionCalibrationServer( id = 'calibration', performanceId = performanceId, - schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) - predictionNbServer( + patientLevelPredictionNbServer( id = 'netBenefit', performanceId = performanceId, - schema = resultDatabaseSettings$schema, connectionHandler = connectionHandler, inputSingleView = singleViewValue, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) - predictionValidationServer( + patientLevelPredictionValidationServer( id = 'validation', modelDesignId = modelDesignId, # reactive developmentDatabaseId = developmentDatabaseId, # reactive performanceId = performanceId, # reactive connectionHandler = connectionHandler, inputSingleView = singleViewValue, - schema = resultDatabaseSettings$schema, - plpTablePrefix = resultDatabaseSettings$plpTablePrefix, - databaseTablePrefix = ifelse( - !is.null(resultDatabaseSettings$databaseTablePrefix), - resultDatabaseSettings$databaseTablePrefix, - resultDatabaseSettings$plpTablePrefix - ) + resultDatabaseSettings = resultDatabaseSettings ) } @@ -549,15 +483,12 @@ predictionServer <- function( } - -getResultSelection <- function( +# name too generic +getPlpResultSelection <- function( connectionHandler, - schema, - plpTablePrefix, + resultDatabaseSettings, modelDesignId, - performanceId, - cohortTablePrefix, - databaseTablePrefix + performanceId ){ if(!inherits(modelDesignId, 'reactive')){ @@ -570,64 +501,64 @@ getResultSelection <- function( if(!is.null(modelDesignId()) & !is.null(performanceId())){ modelType <- connectionHandler$queryDb( - 'select distinct model_type from @my_schema.@my_table_appendmodels where model_design_id = @model_design_id;', - my_schema = schema, - my_table_append = plpTablePrefix, + 'select distinct model_type from @schema.@plp_table_prefixmodels where model_design_id = @model_design_id;', + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, model_design_id = modelDesignId() ) developmentDb = connectionHandler$queryDb( 'select distinct d.cdm_source_abbreviation from - @my_schema.@database_table_appenddatabase_meta_data d + @schema.@database_table_prefixdatabase_meta_data d inner join - @my_schema.@my_table_appenddatabase_details dd + @schema.@plp_table_prefixdatabase_details dd on dd.database_meta_data_id = d.database_id inner join - @my_schema.@my_table_appendperformances p + @schema.@plp_table_prefixperformances p on dd.database_id = p.development_database_id where p.performance_id = @performance_id;', - my_schema = schema, - my_table_append = plpTablePrefix, + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, performance_id = performanceId(), - database_table_append = databaseTablePrefix + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) validationDb = connectionHandler$queryDb( 'select distinct d.cdm_source_abbreviation from - @my_schema.@database_table_appenddatabase_meta_data d + @schema.@database_table_prefixdatabase_meta_data d inner join - @my_schema.@my_table_appenddatabase_details dd + @schema.@plp_table_prefixdatabase_details dd on dd.database_meta_data_id = d.database_id inner join - @my_schema.@my_table_appendperformances p + @schema.@plp_table_prefixperformances p on dd.database_id = p.validation_database_id where p.performance_id = @performance_id;', - my_schema = schema, - my_table_append = plpTablePrefix, + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, performance_id = performanceId(), - database_table_append = databaseTablePrefix + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) target <- connectionHandler$queryDb( 'select distinct c.cohort_name from - @my_schema.@my_table_appendcohorts c + @schema.@plp_table_prefixcohorts c inner join - @my_schema.@my_table_appendperformances p + @schema.@plp_table_prefixperformances p on c.cohort_id = p.target_id where p.performance_id = @performance_id;', - my_schema = schema, - my_table_append = plpTablePrefix, + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, performance_id = performanceId() ) outcome <- connectionHandler$queryDb( 'select distinct c.cohort_name from - @my_schema.@my_table_appendcohorts c + @schema.@plp_table_prefixcohorts c inner join - @my_schema.@my_table_appendperformances p + @schema.@plp_table_prefixperformances p on c.cohort_id = p.outcome_id where p.performance_id = @performance_id;', - my_schema = schema, - my_table_append = plpTablePrefix, + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, performance_id = performanceId() ) diff --git a/R/prediction-modelSummary.R b/R/patient-level-prediction-modelSummary.R similarity index 74% rename from R/prediction-modelSummary.R rename to R/patient-level-prediction-modelSummary.R index 94ff177e..78823a39 100644 --- a/R/prediction-modelSummary.R +++ b/R/patient-level-prediction-modelSummary.R @@ -1,4 +1,4 @@ -# @file prediction-modelSummary.R +# @file patient-level-prediction-modelSummary.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the summary module #' #' @export -predictionModelSummaryViewer <- function(id) { +patientLevelPredictionModelSummaryViewer <- function(id) { ns <- shiny::NS(id) shiny::tagList( @@ -37,7 +37,7 @@ predictionModelSummaryViewer <- function(id) { collapsed = TRUE, title = "All Database Results For Selected Model Design", width = "100%", - shiny::htmlTemplate(system.file("prediction-www", "main-modelSummaryHelp.html", package = utils::packageName())) + shiny::htmlTemplate(system.file("patient-level-prediction-www", "main-modelSummaryHelp.html", package = utils::packageName())) ), shinydashboard::box( status = "warning", @@ -58,22 +58,18 @@ predictionModelSummaryViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param connectionHandler the connection to the prediction result database -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param modelDesignId a reactable id specifying the prediction model design identifier -#' @param databaseTablePrefix a string that appends the database_meta_data table #' #' @return #' The server to the summary module #' #' @export -predictionModelSummaryServer <- function( +patientLevelPredictionModelSummaryServer <- function( id, connectionHandler, - schema, - plpTablePrefix, - modelDesignId, - databaseTablePrefix = plpTablePrefix + resultDatabaseSettings, + modelDesignId ) { shiny::moduleServer( id, @@ -82,10 +78,8 @@ predictionModelSummaryServer <- function( selectedModelDesign <- shiny::reactive( getModelDesignInfo( connectionHandler = connectionHandler, - schema = schema, - plpTablePrefix = plpTablePrefix, - modelDesignId = modelDesignId, - databaseTablePrefix = databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings, + modelDesignId = modelDesignId ) ) output$performanceSummaryText <- shiny::renderUI(selectedModelDesign()) @@ -93,10 +87,8 @@ predictionModelSummaryServer <- function( resultTable <- shiny::reactive( getModelDesignPerformanceSummary( connectionHandler = connectionHandler, - schema = schema, - plpTablePrefix = plpTablePrefix, - modelDesignId = modelDesignId, - databaseTablePrefix = databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings, + modelDesignId = modelDesignId ) ) @@ -180,9 +172,8 @@ predictionModelSummaryServer <- function( attrition <- shiny::reactive({ getAttrition( performanceId = resultTable()$performanceId[modelTableOutputs$actionIndex()$index], - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) }) @@ -216,20 +207,19 @@ predictionModelSummaryServer <- function( getAttrition <- function( performanceId, - schema, connectionHandler, - plpTablePrefix + resultDatabaseSettings ){ if(!is.null(performanceId)){ - sql <- "SELECT * FROM @my_schema.@my_table_appendattrition WHERE performance_id = @performance_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixattrition WHERE performance_id = @performance_id;" attrition <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, performance_id = performanceId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) return(attrition) @@ -238,10 +228,8 @@ getAttrition <- function( getModelDesignPerformanceSummary <- function( connectionHandler, - schema, - plpTablePrefix = '', - modelDesignId, - databaseTablePrefix + resultDatabaseSettings, + modelDesignId ){ if(is.null(modelDesignId())){ @@ -276,9 +264,9 @@ getModelDesignPerformanceSummary <- function( ROUND(oResult.outcome_count*100.0/nResult.population_size,4) as outcome_percent, results.model_development - FROM (select * from @my_schema.@my_table_appendperformances where model_design_id = @model_design_id) AS results + FROM (select * from @schema.@plp_table_prefixperformances where model_design_id = @model_design_id) AS results - inner join @my_schema.@my_table_appendmodel_designs as model_designs + inner join @schema.@plp_table_prefixmodel_designs as model_designs on model_designs.model_design_id = results.model_design_id -- and results.target_id = model_designs.target_id -- and results.outcome_id = model_designs.outcome_id and @@ -286,30 +274,30 @@ getModelDesignPerformanceSummary <- function( -- results.population_setting_id = model_designs.population_setting_id -- and results.plp_data_setting_id = model_designs.plp_data_setting_id - LEFT JOIN (SELECT cohort_id, cohort_name FROM @my_schema.@my_table_appendcohorts) AS targets ON results.target_id = targets.cohort_id - LEFT JOIN (SELECT cohort_id, cohort_name FROM @my_schema.@my_table_appendcohorts) AS outcomes ON results.outcome_id = outcomes.cohort_id + LEFT JOIN (SELECT cohort_id, cohort_name FROM @schema.@plp_table_prefixcohorts) AS targets ON results.target_id = targets.cohort_id + LEFT JOIN (SELECT cohort_id, cohort_name FROM @schema.@plp_table_prefixcohorts) AS outcomes ON results.outcome_id = outcomes.cohort_id LEFT JOIN (select dd.database_id, md.cdm_source_abbreviation database_acronym - from @my_schema.@database_table_appenddatabase_meta_data md inner join - @my_schema.@my_table_appenddatabase_details dd + from @schema.@database_table_prefixdatabase_meta_data md inner join + @schema.@plp_table_prefixdatabase_details dd on md.database_id = dd.database_meta_data_id) AS d ON results.development_database_id = d.database_id LEFT JOIN (select dd.database_id, md.cdm_source_abbreviation database_acronym - from @my_schema.@database_table_appenddatabase_meta_data md inner join - @my_schema.@my_table_appenddatabase_details dd + from @schema.@database_table_prefixdatabase_meta_data md inner join + @schema.@plp_table_prefixdatabase_details dd on md.database_id = dd.database_meta_data_id) AS v ON results.validation_database_id = v.database_id - LEFT JOIN @my_schema.@my_table_appendtars AS tars ON results.tar_id = tars.tar_id - LEFT JOIN (SELECT performance_id, value AS auc FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'AUROC' and evaluation in ('Test','Validation') ) AS aucResult ON results.performance_id = aucResult.performance_id - LEFT JOIN (SELECT performance_id, value AS auprc FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'AUPRC' and evaluation in ('Test','Validation') ) AS auprcResult ON results.performance_id = auprcResult.performance_id - LEFT JOIN (SELECT performance_id, sum(value) AS population_size FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'populationSize' and evaluation in ('Train','Test','Validation') group by performance_id) AS nResult ON results.performance_id = nResult.performance_id - LEFT JOIN (SELECT performance_id, sum(value) AS outcome_count FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'outcomeCount' and evaluation in ('Train','Test','Validation') group by performance_id) AS oResult ON results.performance_id = oResult.performance_id - LEFT JOIN (SELECT performance_id, value AS test_size FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'populationSize' and evaluation in ('Test', 'Validation') ) AS nTest ON results.performance_id = nTest.performance_id;" + LEFT JOIN @schema.@plp_table_prefixtars AS tars ON results.tar_id = tars.tar_id + LEFT JOIN (SELECT performance_id, value AS auc FROM @schema.@plp_table_prefixevaluation_statistics where metric = 'AUROC' and evaluation in ('Test','Validation') ) AS aucResult ON results.performance_id = aucResult.performance_id + LEFT JOIN (SELECT performance_id, value AS auprc FROM @schema.@plp_table_prefixevaluation_statistics where metric = 'AUPRC' and evaluation in ('Test','Validation') ) AS auprcResult ON results.performance_id = auprcResult.performance_id + LEFT JOIN (SELECT performance_id, sum(value) AS population_size FROM @schema.@plp_table_prefixevaluation_statistics where metric = 'populationSize' and evaluation in ('Train','Test','Validation') group by performance_id) AS nResult ON results.performance_id = nResult.performance_id + LEFT JOIN (SELECT performance_id, sum(value) AS outcome_count FROM @schema.@plp_table_prefixevaluation_statistics where metric = 'outcomeCount' and evaluation in ('Train','Test','Validation') group by performance_id) AS oResult ON results.performance_id = oResult.performance_id + LEFT JOIN (SELECT performance_id, value AS test_size FROM @schema.@plp_table_prefixevaluation_statistics where metric = 'populationSize' and evaluation in ('Test', 'Validation') ) AS nTest ON results.performance_id = nTest.performance_id;" summaryTable <- connectionHandler$queryDb( sql = sql, - my_schema = schema, - my_table_append = plpTablePrefix, + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, model_design_id = modelDesignId(), - database_table_append = databaseTablePrefix + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) shiny::incProgress(2/3, detail = paste("Data extracted")) @@ -376,16 +364,14 @@ editColnames <- function(cnames, edits){ getModelDesignInfo <- function( connectionHandler, - schema, - plpTablePrefix, - modelDesignId, - databaseTablePrefix # not used? + resultDatabaseSettings, + modelDesignId ){ modelType <- connectionHandler$queryDb( - 'select distinct model_type from @my_schema.@my_table_appendmodels where model_design_id = @model_design_id;', - my_schema = schema, - my_table_append = plpTablePrefix, + 'select distinct model_type from @schema.@plp_table_prefixmodels where model_design_id = @model_design_id;', + schema = resultDatabaseSettings$schema, + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, model_design_id = modelDesignId() ) diff --git a/R/prediction-netbenefit.R b/R/patient-level-prediction-netbenefit.R similarity index 93% rename from R/prediction-netbenefit.R rename to R/patient-level-prediction-netbenefit.R index ffc8ea71..3b19dbf0 100644 --- a/R/prediction-netbenefit.R +++ b/R/patient-level-prediction-netbenefit.R @@ -1,4 +1,4 @@ -# @file prediction-netbenefit.R +# @file patient-level-prediction-netbenefit.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the net-benefit module #' #' @export -predictionNbViewer <- function(id) { +patientLevelPredictionNbViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -75,20 +75,18 @@ predictionNbViewer <- function(id) { #' @param performanceId the performance id in the database #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the tables in the result schema +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the net-benefit module #' #' @export -predictionNbServer <- function( +patientLevelPredictionNbServer <- function( id, performanceId, # reactive connectionHandler, inputSingleView, - schema, - plpTablePrefix + resultDatabaseSettings ) { shiny::moduleServer( id, @@ -101,8 +99,8 @@ predictionNbServer <- function( getPredictionResult( performanceId = performanceId, connectionHandler= connectionHandler, - tableName = paste0(plpTablePrefix,'threshold_summary'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'threshold_summary' ) } else{ NULL diff --git a/R/prediction-settings.R b/R/patient-level-prediction-settings.R similarity index 82% rename from R/prediction-settings.R rename to R/patient-level-prediction-settings.R index ac3800f7..8eba86fe 100644 --- a/R/prediction-settings.R +++ b/R/patient-level-prediction-settings.R @@ -1,4 +1,4 @@ -# @file prediction-settings.R +# @file patient-level-prediction-settings.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the settings module #' #' @export -predictionSettingsViewer <- function(id) { +patientLevelPredictionSettingsViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -63,26 +63,20 @@ predictionSettingsViewer <- function(id) { #' @param performanceId unique id for the performance results #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the tables in the result schema -#' @param cohortTablePrefix a string that appends the cohort_definition table -#' @param databaseTablePrefix a string that appends the database_meta_data table +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the settings module #' #' @export -predictionSettingsServer <- function( +patientLevelPredictionSettingsServer <- function( id, modelDesignId, developmentDatabaseId, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix, - cohortTablePrefix = plpTablePrefix, - databaseTablePrefix = plpTablePrefix + resultDatabaseSettings ) { shiny::moduleServer( @@ -92,13 +86,11 @@ predictionSettingsServer <- function( # objects modelDesign <- shiny::reactive({ - getModelDesign( + getPredictionModelDesign( inputSingleView = inputSingleView, modelDesignId = modelDesignId, - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix, - cohortTablePrefix = cohortTablePrefix + resultDatabaseSettings = resultDatabaseSettings )}) # databases @@ -106,10 +98,8 @@ predictionSettingsServer <- function( getPlpSettingDatabase( inputSingleView = inputSingleView, performanceId = performanceId, - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix, - databaseTablePrefix = databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) }) @@ -361,10 +351,8 @@ predictionSettingsServer <- function( getPlpSettingDatabase <- function( inputSingleView, performanceId, - schema, connectionHandler, - plpTablePrefix, - databaseTablePrefix = plpTablePrefix + resultDatabaseSettings ){ if(!is.null(performanceId()) & inputSingleView() == 'Design Settings'){ @@ -377,14 +365,14 @@ getPlpSettingDatabase <- function( FROM - (select * from @my_schema.@my_table_appendperformances + (select * from @schema.@plp_table_prefixperformances WHERE performance_id = @performance_id) perf inner join (select dd.database_id, dmd.cdm_source_name as dev_db - from @my_schema.@my_table_appenddatabase_details as dd inner join - @my_schema.@database_table_appenddatabase_meta_data as dmd on + from @schema.@plp_table_prefixdatabase_details as dd inner join + @schema.@database_table_prefixdatabase_meta_data as dmd on dd.database_meta_data_id = dmd.database_id) tempD on tempD.database_id = perf.development_database_id @@ -392,8 +380,8 @@ getPlpSettingDatabase <- function( inner join (select dd.database_id, dmd.cdm_source_name as val_db - from @my_schema.@my_table_appenddatabase_details as dd inner join - @my_schema.@database_table_appenddatabase_meta_data dmd on + from @schema.@plp_table_prefixdatabase_details as dd inner join + @schema.@database_table_prefixdatabase_meta_data dmd on dd.database_meta_data_id = dmd.database_id) tempV on tempV.database_id = perf.validation_database_id @@ -403,10 +391,10 @@ getPlpSettingDatabase <- function( databaseNames <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, performance_id = performanceId(), - my_table_append = plpTablePrefix, - database_table_append = databaseTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix ) return(databaseNames) @@ -417,13 +405,11 @@ getPlpSettingDatabase <- function( # get the data -getModelDesign <- function( +getPredictionModelDesign <- function( inputSingleView, modelDesignId, - schema, connectionHandler, - plpTablePrefix, - cohortTablePrefix = plpTablePrefix + resultDatabaseSettings ){ if(!is.null(modelDesignId()) & inputSingleView() == 'Design Settings'){ @@ -434,14 +420,14 @@ getModelDesign <- function( shiny::incProgress(1/12, detail = paste("Extracting ids")) sql <- "SELECT * FROM - @my_schema.@my_table_appendmodel_designs + @schema.@plp_table_prefixmodel_designs WHERE model_design_id = @model_design_id;" ids <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, model_design_id = modelDesignId(), - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) @@ -458,13 +444,13 @@ getModelDesign <- function( shiny::incProgress(2/12, detail = paste("Extracting model settings")) - sql <- "SELECT * FROM @my_schema.@my_table_appendmodel_settings WHERE model_setting_id = @model_setting_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixmodel_settings WHERE model_setting_id = @model_setting_id;" tempModSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, model_setting_id = modSetId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) modelDesign$modelSettings <- ParallelLogger::convertJsonToSettings( @@ -473,13 +459,13 @@ getModelDesign <- function( shiny::incProgress(3/12, detail = paste("Extracting covariate settings")) - sql <- "SELECT * FROM @my_schema.@my_table_appendcovariate_settings WHERE covariate_setting_id = @setting_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixcovariate_settings WHERE covariate_setting_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = covSetId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) modelDesign$covariateSettings <- ParallelLogger::convertJsonToSettings( tempSettings$covariateSettingsJson @@ -488,13 +474,13 @@ getModelDesign <- function( shiny::incProgress(4/12, detail = paste("Extracting population settings")) - sql <- "SELECT * FROM @my_schema.@my_table_appendpopulation_settings WHERE population_setting_id = @setting_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixpopulation_settings WHERE population_setting_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = popSetId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) modelDesign$populationSettings <- ParallelLogger::convertJsonToSettings( @@ -503,64 +489,64 @@ getModelDesign <- function( shiny::incProgress(5/12, detail = paste("Extracting feature engineering settingd")) - sql <- "SELECT * FROM @my_schema.@my_table_appendfeature_engineering_settings WHERE feature_engineering_setting_id = @setting_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixfeature_engineering_settings WHERE feature_engineering_setting_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = feSetId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) modelDesign$featureEngineeringSettings <- tempSettings$featureEngineeringSettingsJson shiny::incProgress(6/12, detail = paste("Extracting tidy covariate settings")) - sql <- "SELECT * FROM @my_schema.@my_table_appendtidy_covariates_settings WHERE tidy_covariates_setting_id = @setting_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixtidy_covariates_settings WHERE tidy_covariates_setting_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = tidyCovariatesSettingId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) modelDesign$preprocessSettings <- tempSettings$tidyCovariatesSettingsJson shiny::incProgress(7/12, detail = paste("Extracting restrict plp settings")) - sql <- "SELECT * FROM @my_schema.@my_table_appendplp_data_settings WHERE plp_data_setting_id = @setting_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixplp_data_settings WHERE plp_data_setting_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = plpDataSettingId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) modelDesign$RestrictPlpData <- tempSettings$plpDataSettingsJson shiny::incProgress(8/12, detail = paste("Extracting sample settings")) - sql <- "SELECT * FROM @my_schema.@my_table_appendsample_settings WHERE sample_setting_id = @setting_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixsample_settings WHERE sample_setting_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = sampleSetId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) modelDesign$sampleSettings <- tempSettings$sampleSettingsJson shiny::incProgress(9/12, detail = paste("Extracting split settings")) - sql <- "SELECT * FROM @my_schema.@my_table_appendsplit_settings WHERE split_setting_id = @setting_id;" + sql <- "SELECT * FROM @schema.@plp_table_prefixsplit_settings WHERE split_setting_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = splitId, - my_table_append = plpTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix ) modelDesign$splitSettings <- tempSettings$splitSettingsJson @@ -568,17 +554,17 @@ getModelDesign <- function( shiny::incProgress(10/12, detail = paste("Extracting target cohort")) sql <- "SELECT c.*, cd.json as cohort_json - FROM @my_schema.@my_table_appendcohorts c inner join - @my_schema.@cohort_table_appendcohort_definition cd + FROM @schema.@plp_table_prefixcohorts c inner join + @schema.@cg_table_prefixcohort_definition cd on c.cohort_definition_id = cd.cohort_definition_id WHERE c.cohort_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = tId, - my_table_append = plpTablePrefix, - cohort_table_append = cohortTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) modelDesign$cohort <- tempSettings @@ -586,17 +572,17 @@ getModelDesign <- function( shiny::incProgress(11/12, detail = paste("Extracting outcome cohort")) sql <- "SELECT c.*, cd.json as cohort_json - FROM @my_schema.@my_table_appendcohorts c inner join - @my_schema.@cohort_table_appendcohort_definition cd + FROM @schema.@plp_table_prefixcohorts c inner join + @schema.@cg_table_prefixcohort_definition cd on c.cohort_definition_id = cd.cohort_definition_id WHERE c.cohort_id = @setting_id;" tempSettings <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, setting_id = oId, - my_table_append = plpTablePrefix, - cohort_table_append = cohortTablePrefix + plp_table_prefix = resultDatabaseSettings$plpTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix ) modelDesign$outcome <- tempSettings diff --git a/R/prediction-validation.R b/R/patient-level-prediction-validation.R similarity index 79% rename from R/prediction-validation.R rename to R/patient-level-prediction-validation.R index 09d4e8d7..6bcfa78a 100644 --- a/R/prediction-validation.R +++ b/R/patient-level-prediction-validation.R @@ -1,4 +1,4 @@ -# @file prediction-validation.R +# @file patient-level-prediction-validation.R # # Copyright 2022 Observational Health Data Sciences and Informatics # @@ -28,7 +28,7 @@ #' The user interface to the validation module #' #' @export -predictionValidationViewer <- function(id) { +patientLevelPredictionValidationViewer <- function(id) { ns <- shiny::NS(id) shiny::div( @@ -65,24 +65,20 @@ predictionValidationViewer <- function(id) { #' @param performanceId identifier for the performance #' @param connectionHandler the connection to the prediction result database #' @param inputSingleView the current tab -#' @param schema the database schema for the model results -#' @param plpTablePrefix a string that appends the tables in the result schema -#' @param databaseTablePrefix a string that appends the database_meta_data table +#' @param resultDatabaseSettings a list containing the result schema and prefixes #' #' @return #' The server to the validation module #' #' @export -predictionValidationServer <- function( +patientLevelPredictionValidationServer <- function( id, modelDesignId, # reactive developmentDatabaseId, # reactive performanceId, # reactive connectionHandler, inputSingleView, - schema, - plpTablePrefix = NULL, - databaseTablePrefix = plpTablePrefix + resultDatabaseSettings ) { shiny::moduleServer( @@ -93,13 +89,11 @@ predictionValidationServer <- function( validationTable <- shiny::eventReactive(inputSingleView(), { - getValSummary( + getPredictionValSummary( connectionHandler = connectionHandler, - schema = schema, + resultDatabaseSettings = resultDatabaseSettings, modelDesignId = modelDesignId(), developmentDatabaseId = developmentDatabaseId(), - plpTablePrefix = plpTablePrefix, - databaseTablePrefix = databaseTablePrefix, inputSingleView = inputSingleView() ) @@ -129,12 +123,11 @@ predictionValidationServer <- function( # get validation results valResult <- shiny::reactive({ - getValidationResults( + getPredictionValidationResults( validationTable = validationTable, validationRowIds = input$validationTable_rows_selected, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix, - schema = schema + resultDatabaseSettings = resultDatabaseSettings ) }) @@ -168,12 +161,11 @@ predictionValidationServer <- function( ) } -getValidationResults <- function( +getPredictionValidationResults <- function( validationTable, validationRowIds, connectionHandler, - plpTablePrefix, - schema + resultDatabaseSettings ){ if(!is.null(validationTable()) & !is.null(validationRowIds)){ @@ -185,14 +177,14 @@ getValidationResults <- function( thresholdSummaryList[[i]] <- getPredictionResult( performanceId = shiny::reactiveVal(valTable$performanceId[i]), connectionHandler = connectionHandler, - tableName = paste0(plpTablePrefix,'threshold_summary'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'threshold_summary' ) calibrationSummaryList[[i]] <- getPredictionResult( performanceId = shiny::reactiveVal(valTable$performanceId[i]), connectionHandler = connectionHandler, - tableName = paste0(plpTablePrefix,'calibration_summary'), - schema = schema + resultDatabaseSettings = resultDatabaseSettings, + tableName = 'calibration_summary' ) } return( @@ -216,13 +208,11 @@ getValidationResults <- function( } } -getValSummary <- function( +getPredictionValSummary <- function( connectionHandler, - schema, + resultDatabaseSettings, modelDesignId, developmentDatabaseId, - plpTablePrefix = '', - databaseTablePrefix = plpTablePrefix, inputSingleView ){ @@ -258,36 +248,36 @@ getValSummary <- function( FROM - (SELECT * FROM @my_schema.@my_table_appendperformances + (SELECT * FROM @schema.@plp_table_appendperformances where model_design_id = @model_design_id and development_database_id = @development_database_id ) AS results - LEFT JOIN (SELECT cohort_id, cohort_name FROM @my_schema.@my_table_appendcohorts) AS targets ON results.target_id = targets.cohort_id - LEFT JOIN (SELECT cohort_id, cohort_name FROM @my_schema.@my_table_appendcohorts) AS outcomes ON results.outcome_id = outcomes.cohort_id + LEFT JOIN (SELECT cohort_id, cohort_name FROM @schema.@plp_table_appendcohorts) AS targets ON results.target_id = targets.cohort_id + LEFT JOIN (SELECT cohort_id, cohort_name FROM @schema.@plp_table_appendcohorts) AS outcomes ON results.outcome_id = outcomes.cohort_id LEFT JOIN (select dd.database_id, md.cdm_source_abbreviation database_acronym - from @my_schema.@database_table_appenddatabase_meta_data md inner join - @my_schema.@my_table_appenddatabase_details dd + from @schema.@database_table_appenddatabase_meta_data md inner join + @schema.@plp_table_appenddatabase_details dd on md.database_id = dd.database_meta_data_id) AS d ON results.validation_database_id = d.database_id - LEFT JOIN @my_schema.@my_table_appendtars AS tars ON results.tar_id = tars.tar_id - LEFT JOIN (SELECT performance_id, value AS auc FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'AUROC' and evaluation in ('Test','Validation') ) AS aucResult ON results.performance_id = aucResult.performance_id - LEFT JOIN (SELECT performance_id, value AS auclb FROM @my_schema.@my_table_appendevaluation_statistics where metric = '95% lower AUROC' and evaluation in ('Test','Validation') ) AS auclbResult ON results.performance_id = auclbResult.performance_id - LEFT JOIN (SELECT performance_id, value AS aucub FROM @my_schema.@my_table_appendevaluation_statistics where metric = '95% upper AUROC' and evaluation in ('Test','Validation') ) AS aucubResult ON results.performance_id = aucubResult.performance_id - LEFT JOIN (SELECT performance_id, value AS auprc FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'AUPRC' and evaluation in ('Test','Validation') ) AS auprcResult ON results.performance_id = auprcResult.performance_id + LEFT JOIN @schema.@plp_table_appendtars AS tars ON results.tar_id = tars.tar_id + LEFT JOIN (SELECT performance_id, value AS auc FROM @schema.@plp_table_appendevaluation_statistics where metric = 'AUROC' and evaluation in ('Test','Validation') ) AS aucResult ON results.performance_id = aucResult.performance_id + LEFT JOIN (SELECT performance_id, value AS auclb FROM @schema.@plp_table_appendevaluation_statistics where metric = '95% lower AUROC' and evaluation in ('Test','Validation') ) AS auclbResult ON results.performance_id = auclbResult.performance_id + LEFT JOIN (SELECT performance_id, value AS aucub FROM @schema.@plp_table_appendevaluation_statistics where metric = '95% upper AUROC' and evaluation in ('Test','Validation') ) AS aucubResult ON results.performance_id = aucubResult.performance_id + LEFT JOIN (SELECT performance_id, value AS auprc FROM @schema.@plp_table_appendevaluation_statistics where metric = 'AUPRC' and evaluation in ('Test','Validation') ) AS auprcResult ON results.performance_id = auprcResult.performance_id - LEFT JOIN (SELECT performance_id, value AS calibration_in_large FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'calibrationInLarge intercept' and evaluation in ('Test','Validation') ) AS CalibrationInLargeResult ON results.performance_id = CalibrationInLargeResult.performance_id + LEFT JOIN (SELECT performance_id, value AS calibration_in_large FROM @schema.@plp_table_appendevaluation_statistics where metric = 'calibrationInLarge intercept' and evaluation in ('Test','Validation') ) AS CalibrationInLargeResult ON results.performance_id = CalibrationInLargeResult.performance_id - LEFT JOIN (SELECT performance_id, sum(value) AS population_size FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'populationSize' and evaluation in ('Test','Train','Validation') group by performance_id) AS nResult ON results.performance_id = nResult.performance_id - LEFT JOIN (SELECT performance_id, sum(value) AS outcome_count FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'outcomeCount' and evaluation in ('Test','Train','Validation') group by performance_id) AS oResult ON results.performance_id = oResult.performance_id - LEFT JOIN (SELECT performance_id, value AS test_size FROM @my_schema.@my_table_appendevaluation_statistics where metric = 'populationSize' and evaluation in ('Test','Validation')) AS nTest ON results.performance_id = nTest.performance_id;" + LEFT JOIN (SELECT performance_id, sum(value) AS population_size FROM @schema.@plp_table_appendevaluation_statistics where metric = 'populationSize' and evaluation in ('Test','Train','Validation') group by performance_id) AS nResult ON results.performance_id = nResult.performance_id + LEFT JOIN (SELECT performance_id, sum(value) AS outcome_count FROM @schema.@plp_table_appendevaluation_statistics where metric = 'outcomeCount' and evaluation in ('Test','Train','Validation') group by performance_id) AS oResult ON results.performance_id = oResult.performance_id + LEFT JOIN (SELECT performance_id, value AS test_size FROM @schema.@plp_table_appendevaluation_statistics where metric = 'populationSize' and evaluation in ('Test','Validation')) AS nTest ON results.performance_id = nTest.performance_id;" valTable <- connectionHandler$queryDb( sql = sql, - my_schema = schema, + schema = resultDatabaseSettings$schema, model_design_id = modelDesignId, development_database_id = developmentDatabaseId, - my_table_append = plpTablePrefix, - database_table_append = databaseTablePrefix + plp_table_append = resultDatabaseSettings$plpTablePrefix, + database_table_append = resultDatabaseSettings$databaseTablePrefix ) valTable$target <- trimws(valTable$target) # not needed diff --git a/R/sccs-diagnosticsSummary.R b/R/sccs-diagnosticsSummary.R index 8717e110..e605cdc6 100644 --- a/R/sccs-diagnosticsSummary.R +++ b/R/sccs-diagnosticsSummary.R @@ -296,13 +296,13 @@ getSccsDiagAnalyses <- function( a.description as analysis FROM - @database_schema.@table_prefixanalysis a + @schema.@sccs_table_prefixanalysis a ; " result <- connectionHandler$queryDb( sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, snakeCaseToCamelCase = TRUE ) @@ -323,20 +323,20 @@ getSccsDiagOutcomes <- function( c.cohort_name as outcome, c.cohort_definition_id - FROM @database_schema.@table_prefixdiagnostics_summary ds + FROM @schema.@sccs_table_prefixdiagnostics_summary ds inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on ds.exposures_outcome_set_id = eos.exposures_outcome_set_id inner join - @database_schema.@cg_table_prefixcohort_definition as c + @schema.@cg_table_prefixcohort_definition as c on c.cohort_definition_id = eos.outcome_id ; " result <- connectionHandler$queryDb( sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, - cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, snakeCaseToCamelCase = TRUE ) @@ -356,25 +356,25 @@ getSccsDiagTargets <- function( c2.cohort_name as target, c2.cohort_definition_id - FROM @database_schema.@table_prefixdiagnostics_summary ds + FROM @schema.@sccs_table_prefixdiagnostics_summary ds INNER JOIN - @database_schema.@table_prefixcovariate cov + @schema.@sccs_table_prefixcovariate cov on cov.covariate_id = ds.covariate_id and cov.exposures_outcome_set_id = ds.exposures_outcome_set_id and cov.analysis_id = ds.analysis_id and cov.database_id = ds.database_id inner join - @database_schema.@cg_table_prefixcohort_definition as c2 + @schema.@cg_table_prefixcohort_definition as c2 on cov.era_id = c2.cohort_definition_id ; " result <- connectionHandler$queryDb( sql, - database_schema = resultDatabaseSettings$schema, - table_prefix = resultDatabaseSettings$tablePrefix, - cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, + schema = resultDatabaseSettings$schema, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, snakeCaseToCamelCase = TRUE ) @@ -400,31 +400,31 @@ getSccsAllDiagnosticsSummary <- function( a.description as analysis, cov.covariate_name, ds.* - FROM @database_schema.@table_prefixdiagnostics_summary ds + FROM @schema.@sccs_table_prefixdiagnostics_summary ds inner join - @database_schema.@table_prefixexposures_outcome_set eos + @schema.@sccs_table_prefixexposures_outcome_set eos on ds.exposures_outcome_set_id = eos.exposures_outcome_set_id inner join - @database_schema.@cg_table_prefixcohort_definition as c + @schema.@cg_table_prefixcohort_definition as c on c.cohort_definition_id = eos.outcome_id INNER JOIN - @database_schema.@database_table_prefix@database_table d + @schema.@database_table_prefix@database_table d on d.database_id = ds.database_id INNER JOIN - @database_schema.@table_prefixanalysis a + @schema.@sccs_table_prefixanalysis a on a.analysis_id = ds.analysis_id INNER JOIN - @database_schema.@table_prefixcovariate cov + @schema.@sccs_table_prefixcovariate cov on cov.covariate_id = ds.covariate_id and cov.exposures_outcome_set_id = ds.exposures_outcome_set_id and cov.analysis_id = ds.analysis_id and cov.database_id = ds.database_id inner join - @database_schema.@cg_table_prefixcohort_definition as c2 + @schema.@cg_table_prefixcohort_definition as c2 on cov.era_id = c2.cohort_definition_id @@ -437,9 +437,9 @@ getSccsAllDiagnosticsSummary <- function( " result <- connectionHandler$queryDb( sql, - database_schema = resultDatabaseSettings$schema, - cg_table_prefix = resultDatabaseSettings$cohortTablePrefix, - table_prefix = resultDatabaseSettings$tablePrefix, + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, database_table_prefix = resultDatabaseSettings$databaseTablePrefix, database_table = resultDatabaseSettings$databaseTable, diff --git a/_pkgdown.yml b/_pkgdown.yml index 3456fd18..00fb3c41 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -47,46 +47,46 @@ reference: desc: > Modules for prediction results. contents: - - predictionHelperFile - - predictionViewer - - predictionServer - - predictionDiagnosticsServer - - predictionDiagnosticsViewer - - predictionDesignSummaryViewer - - predictionDesignSummaryServer - - predictionModelSummaryViewer - - predictionModelSummaryServer - - predictionSettingsViewer - - predictionSettingsServer - - predictionCovariateSummaryViewer - - predictionCovariateSummaryServer - - predictionCutoffViewer - - predictionCutoffServer - - predictionDiscriminationViewer - - predictionDiscriminationServer - - predictionCalibrationViewer - - predictionCalibrationServer - - predictionNbViewer - - predictionNbServer - - predictionValidationViewer - - predictionValidationServer + - patientLevelPredictionHelperFile + - patientLevelPredictionViewer + - patientLevelPredictionServer + - patientLevelPredictionDiagnosticsServer + - patientLevelPredictionDiagnosticsViewer + - patientLevelPredictionDesignSummaryViewer + - patientLevelPredictionDesignSummaryServer + - patientLevelPredictionModelSummaryViewer + - patientLevelPredictionModelSummaryServer + - patientLevelPredictionSettingsViewer + - patientLevelPredictionSettingsServer + - patientLevelPredictionCovariateSummaryViewer + - patientLevelPredictionCovariateSummaryServer + - patientLevelPredictionCutoffViewer + - patientLevelPredictionCutoffServer + - patientLevelPredictionDiscriminationViewer + - patientLevelPredictionDiscriminationServer + - patientLevelPredictionCalibrationViewer + - patientLevelPredictionCalibrationServer + - patientLevelPredictionNbViewer + - patientLevelPredictionNbServer + - patientLevelPredictionValidationViewer + - patientLevelPredictionValidationServer - title: "Decription module" desc: > Modules for the description analyses. contents: - - descriptionHelperFile - - descriptionViewer - - descriptionServer - - descriptionAggregateFeaturesViewer - - descriptionAggregateFeaturesServer - - descriptionDechallengeRechallengeViewer - - descriptionDechallengeRechallengeServer - - descriptionIncidenceViewer - - descriptionIncidenceServer - - descriptionTableViewer - - descriptionTableServer - - descriptionTimeToEventViewer - - descriptionTimeToEventServer + - characterizationHelperFile + - characterizationViewer + - characterizationServer + - characterizationAggregateFeaturesViewer + - characterizationAggregateFeaturesServer + - characterizationDechallengeRechallengeViewer + - characterizationDechallengeRechallengeServer + - characterizationIncidenceViewer + - characterizationIncidenceServer + - characterizationTableViewer + - characterizationTableServer + - characterizationTimeToEventViewer + - characterizationTimeToEventServer - title: "About module" desc: > Modules for the information page. @@ -105,35 +105,35 @@ reference: desc: > Modules for the CohortMethod package. contents: - - estimationHelperFile - - estimationViewer - - estimationServer - - estimationAttritionViewer - - estimationAttritionServer - - estimationCovariateBalanceViewer - - estimationCovariateBalanceServer - - estimationDiagnosticsSummaryViewer - - estimationDiagnosticsSummaryServer - - estimationForestPlotViewer - - estimationForestPlotServer - - estimationKaplanMeierViewer - - estimationKaplanMeierServer - - estimationPopulationCharacteristicsViewer - - estimationPopulationCharacteristicsServer - - estimationPowerViewer - - estimationPowerServer - - estimationPropensityModelViewer - - estimationPropensityModelServer - - estimationPropensityScoreDistViewer - - estimationPropensityScoreDistServer - - estimationResultsTableViewer - - estimationResultsTableServer - - estimationSubgroupsViewer - - estimationSubgroupsServer - - estimationSystematicErrorViewer - - estimationSystematicErrorServer - - estimationTitlePanelViewer - - estimationTitlePanelServer + - cohortMethodHelperFile + - cohortMethodViewer + - cohortMethodServer + - cohortMethodAttritionViewer + - cohortMethodAttritionServer + - cohortMethodCovariateBalanceViewer + - cohortMethodCovariateBalanceServer + - cohortMethodDiagnosticsSummaryViewer + - cohortMethodDiagnosticsSummaryServer + - cohortMethodForestPlotViewer + - cohortMethodForestPlotServer + - cohortMethodKaplanMeierViewer + - cohortMethodKaplanMeierServer + - cohortMethodPopulationCharacteristicsViewer + - cohortMethodPopulationCharacteristicsServer + - cohortMethodPowerViewer + - cohortMethodPowerServer + - cohortMethodPropensityModelViewer + - cohortMethodPropensityModelServer + - cohortMethodPropensityScoreDistViewer + - cohortMethodPropensityScoreDistServer + - cohortMethodResultsTableViewer + - cohortMethodResultsTableServer + - cohortMethodSubgroupsViewer + - cohortMethodSubgroupsServer + - cohortMethodSystematicErrorViewer + - cohortMethodSystematicErrorServer + - cohortMethodTitlePanelViewer + - cohortMethodTitlePanelServer - title: "Data diagnostics module" desc: > Modules for the DataDiagnostics package. @@ -152,7 +152,7 @@ reference: - cohortDiagnosticsSever - cohortDiagnosticsView - cohortDiagnosticsHelperFile - - characterizationView + - cohortDiagCharacterizationView - cohortCountsModule - cohortCountsView - cohortDefinitionsModule diff --git a/inst/prediction-document/export-main.Rmd b/inst/patient-level-prediction-document/export-main.Rmd similarity index 100% rename from inst/prediction-document/export-main.Rmd rename to inst/patient-level-prediction-document/export-main.Rmd diff --git a/inst/prediction-document/main.Rmd b/inst/patient-level-prediction-document/main.Rmd similarity index 100% rename from inst/prediction-document/main.Rmd rename to inst/patient-level-prediction-document/main.Rmd diff --git a/inst/prediction-document/model-design.Rmd b/inst/patient-level-prediction-document/model-design.Rmd similarity index 100% rename from inst/prediction-document/model-design.Rmd rename to inst/patient-level-prediction-document/model-design.Rmd diff --git a/inst/prediction-document/plp-analysis.Rmd b/inst/patient-level-prediction-document/plp-analysis.Rmd similarity index 100% rename from inst/prediction-document/plp-analysis.Rmd rename to inst/patient-level-prediction-document/plp-analysis.Rmd diff --git a/inst/prediction-document/plp-diagnostic.Rmd b/inst/patient-level-prediction-document/plp-diagnostic.Rmd similarity index 100% rename from inst/prediction-document/plp-diagnostic.Rmd rename to inst/patient-level-prediction-document/plp-diagnostic.Rmd diff --git a/inst/prediction-document/plp-introduction.Rmd b/inst/patient-level-prediction-document/plp-introduction.Rmd similarity index 100% rename from inst/prediction-document/plp-introduction.Rmd rename to inst/patient-level-prediction-document/plp-introduction.Rmd diff --git a/inst/prediction-document/plp-metrics.Rmd b/inst/patient-level-prediction-document/plp-metrics.Rmd similarity index 100% rename from inst/prediction-document/plp-metrics.Rmd rename to inst/patient-level-prediction-document/plp-metrics.Rmd diff --git a/inst/prediction-document/plp-outcome.Rmd b/inst/patient-level-prediction-document/plp-outcome.Rmd similarity index 100% rename from inst/prediction-document/plp-outcome.Rmd rename to inst/patient-level-prediction-document/plp-outcome.Rmd diff --git a/inst/prediction-document/plp-output.Rmd b/inst/patient-level-prediction-document/plp-output.Rmd similarity index 100% rename from inst/prediction-document/plp-output.Rmd rename to inst/patient-level-prediction-document/plp-output.Rmd diff --git a/inst/prediction-document/plp-participants.Rmd b/inst/patient-level-prediction-document/plp-participants.Rmd similarity index 100% rename from inst/prediction-document/plp-participants.Rmd rename to inst/patient-level-prediction-document/plp-participants.Rmd diff --git a/inst/prediction-document/plp-plots.Rmd b/inst/patient-level-prediction-document/plp-plots.Rmd similarity index 100% rename from inst/prediction-document/plp-plots.Rmd rename to inst/patient-level-prediction-document/plp-plots.Rmd diff --git a/inst/prediction-document/plp-predictors.Rmd b/inst/patient-level-prediction-document/plp-predictors.Rmd similarity index 100% rename from inst/prediction-document/plp-predictors.Rmd rename to inst/patient-level-prediction-document/plp-predictors.Rmd diff --git a/inst/prediction-document/plp-results-external.rmd b/inst/patient-level-prediction-document/plp-results-external.rmd similarity index 100% rename from inst/prediction-document/plp-results-external.rmd rename to inst/patient-level-prediction-document/plp-results-external.rmd diff --git a/inst/prediction-document/plp-results-internal.Rmd b/inst/patient-level-prediction-document/plp-results-internal.Rmd similarity index 100% rename from inst/prediction-document/plp-results-internal.Rmd rename to inst/patient-level-prediction-document/plp-results-internal.Rmd diff --git a/inst/prediction-document/plp-results-template.Rmd b/inst/patient-level-prediction-document/plp-results-template.Rmd similarity index 100% rename from inst/prediction-document/plp-results-template.Rmd rename to inst/patient-level-prediction-document/plp-results-template.Rmd diff --git a/inst/prediction-document/plp-results.Rmd b/inst/patient-level-prediction-document/plp-results.Rmd similarity index 100% rename from inst/prediction-document/plp-results.Rmd rename to inst/patient-level-prediction-document/plp-results.Rmd diff --git a/inst/prediction-www/DataInfo.html b/inst/patient-level-prediction-www/DataInfo.html similarity index 100% rename from inst/prediction-www/DataInfo.html rename to inst/patient-level-prediction-www/DataInfo.html diff --git a/inst/prediction-www/Description.html b/inst/patient-level-prediction-www/Description.html similarity index 100% rename from inst/prediction-www/Description.html rename to inst/patient-level-prediction-www/Description.html diff --git a/inst/prediction-www/Help.html b/inst/patient-level-prediction-www/Help.html similarity index 100% rename from inst/prediction-www/Help.html rename to inst/patient-level-prediction-www/Help.html diff --git a/inst/prediction-www/Log.html b/inst/patient-level-prediction-www/Log.html similarity index 100% rename from inst/prediction-www/Log.html rename to inst/patient-level-prediction-www/Log.html diff --git a/inst/prediction-www/Model.html b/inst/patient-level-prediction-www/Model.html similarity index 100% rename from inst/prediction-www/Model.html rename to inst/patient-level-prediction-www/Model.html diff --git a/inst/prediction-www/Performance.html b/inst/patient-level-prediction-www/Performance.html similarity index 100% rename from inst/prediction-www/Performance.html rename to inst/patient-level-prediction-www/Performance.html diff --git a/inst/prediction-www/Settings.html b/inst/patient-level-prediction-www/Settings.html similarity index 100% rename from inst/prediction-www/Settings.html rename to inst/patient-level-prediction-www/Settings.html diff --git a/inst/prediction-www/Summary.html b/inst/patient-level-prediction-www/Summary.html similarity index 100% rename from inst/prediction-www/Summary.html rename to inst/patient-level-prediction-www/Summary.html diff --git a/inst/prediction-www/boxHelp.html b/inst/patient-level-prediction-www/boxHelp.html similarity index 100% rename from inst/prediction-www/boxHelp.html rename to inst/patient-level-prediction-www/boxHelp.html diff --git a/inst/prediction-www/calHelp.html b/inst/patient-level-prediction-www/calHelp.html similarity index 100% rename from inst/prediction-www/calHelp.html rename to inst/patient-level-prediction-www/calHelp.html diff --git a/inst/prediction-www/demoHelp.html b/inst/patient-level-prediction-www/demoHelp.html similarity index 100% rename from inst/prediction-www/demoHelp.html rename to inst/patient-level-prediction-www/demoHelp.html diff --git a/inst/prediction-www/f1Help.html b/inst/patient-level-prediction-www/f1Help.html similarity index 100% rename from inst/prediction-www/f1Help.html rename to inst/patient-level-prediction-www/f1Help.html diff --git a/inst/prediction-www/help-designSummary.html b/inst/patient-level-prediction-www/help-designSummary.html similarity index 100% rename from inst/prediction-www/help-designSummary.html rename to inst/patient-level-prediction-www/help-designSummary.html diff --git a/inst/prediction-www/help-fullResults.html b/inst/patient-level-prediction-www/help-fullResults.html similarity index 100% rename from inst/prediction-www/help-fullResults.html rename to inst/patient-level-prediction-www/help-fullResults.html diff --git a/inst/prediction-www/main-modelSummaryHelp.html b/inst/patient-level-prediction-www/main-modelSummaryHelp.html similarity index 100% rename from inst/prediction-www/main-modelSummaryHelp.html rename to inst/patient-level-prediction-www/main-modelSummaryHelp.html diff --git a/inst/prediction-www/prediction.html b/inst/patient-level-prediction-www/patient-level-prediction.html similarity index 100% rename from inst/prediction-www/prediction.html rename to inst/patient-level-prediction-www/patient-level-prediction.html diff --git a/inst/prediction-www/prcHelp.html b/inst/patient-level-prediction-www/prcHelp.html similarity index 100% rename from inst/prediction-www/prcHelp.html rename to inst/patient-level-prediction-www/prcHelp.html diff --git a/inst/prediction-www/predDistHelp.html b/inst/patient-level-prediction-www/predDistHelp.html similarity index 100% rename from inst/prediction-www/predDistHelp.html rename to inst/patient-level-prediction-www/predDistHelp.html diff --git a/inst/prediction-www/prefDistHelp.html b/inst/patient-level-prediction-www/prefDistHelp.html similarity index 100% rename from inst/prediction-www/prefDistHelp.html rename to inst/patient-level-prediction-www/prefDistHelp.html diff --git a/inst/prediction-www/rocHelp.html b/inst/patient-level-prediction-www/rocHelp.html similarity index 100% rename from inst/prediction-www/rocHelp.html rename to inst/patient-level-prediction-www/rocHelp.html diff --git a/man/characterizationAggregateFeaturesServer.Rd b/man/characterizationAggregateFeaturesServer.Rd index edb4d4f0..97d46583 100644 --- a/man/characterizationAggregateFeaturesServer.Rd +++ b/man/characterizationAggregateFeaturesServer.Rd @@ -8,10 +8,7 @@ characterizationAggregateFeaturesServer( id, connectionHandler, mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix = "cg_", - databaseTable = "DATABASE_META_DATA" + resultDatabaseSettings ) } \arguments{ @@ -21,13 +18,7 @@ characterizationAggregateFeaturesServer( \item{mainPanelTab}{the current tab} -\item{schema}{the database schema for the model results} - -\item{tablePrefix}{a string that appends the tables in the result schema} - -\item{cohortTablePrefix}{a string that appends the COHORT_DEFINITION table in the result schema} - -\item{databaseTable}{The database table name} +\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix} } \value{ The server to the description aggregate features module diff --git a/man/characterizationDechallengeRechallengeServer.Rd b/man/characterizationDechallengeRechallengeServer.Rd index 21fba74d..d292b070 100644 --- a/man/characterizationDechallengeRechallengeServer.Rd +++ b/man/characterizationDechallengeRechallengeServer.Rd @@ -8,10 +8,7 @@ characterizationDechallengeRechallengeServer( id, connectionHandler, mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix = "cg_", - databaseTable = "DATABASE_META_DATA" + resultDatabaseSettings ) } \arguments{ @@ -21,13 +18,7 @@ characterizationDechallengeRechallengeServer( \item{mainPanelTab}{the current tab} -\item{schema}{the database schema for the model results} - -\item{tablePrefix}{a string that appends the tables in the result schema} - -\item{cohortTablePrefix}{a string that appends the cohort table in the result schema} - -\item{databaseTable}{name of the database table} +\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix} } \value{ The server to the Dechallenge Rechallenge module diff --git a/man/characterizationIncidenceServer.Rd b/man/characterizationIncidenceServer.Rd index 327b9a87..fdd4cf3a 100644 --- a/man/characterizationIncidenceServer.Rd +++ b/man/characterizationIncidenceServer.Rd @@ -8,9 +8,7 @@ characterizationIncidenceServer( id, connectionHandler, mainPanelTab, - schema, - incidenceTablePrefix, - databaseTable = "DATABASE_META_DATA" + resultDatabaseSettings ) } \arguments{ @@ -20,11 +18,7 @@ characterizationIncidenceServer( \item{mainPanelTab}{the current tab} -\item{schema}{the database schema for the model results} - -\item{incidenceTablePrefix}{a string that appends the incidence table in the result schema} - -\item{databaseTable}{name of the database table} +\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix} } \value{ The server to the prediction incidence module diff --git a/man/characterizationServer.Rd b/man/characterizationServer.Rd index 77d64feb..b85af663 100644 --- a/man/characterizationServer.Rd +++ b/man/characterizationServer.Rd @@ -15,7 +15,7 @@ characterizationServer( \item{connectionHandler}{a connection to the database with the results} -\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cohortTablePrefix} +\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix} } \value{ The server for the characterization module diff --git a/man/characterizationTableServer.Rd b/man/characterizationTableServer.Rd index 6dd3fdde..20d5e3f0 100644 --- a/man/characterizationTableServer.Rd +++ b/man/characterizationTableServer.Rd @@ -8,10 +8,7 @@ characterizationTableServer( id, connectionHandler, mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix, - databaseTable = "DATABASE_META_DATA" + resultDatabaseSettings ) } \arguments{ @@ -21,13 +18,7 @@ characterizationTableServer( \item{mainPanelTab}{the current tab} -\item{schema}{the database schema for the model results} - -\item{tablePrefix}{a string that appends the tables in the result schema} - -\item{cohortTablePrefix}{a string that appends the cohort table in the result schema} - -\item{databaseTable}{name of the database table} +\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix} } \value{ The server to the cohorts features server diff --git a/man/characterizationTimeToEventServer.Rd b/man/characterizationTimeToEventServer.Rd index a24b217f..42f783ef 100644 --- a/man/characterizationTimeToEventServer.Rd +++ b/man/characterizationTimeToEventServer.Rd @@ -8,10 +8,7 @@ characterizationTimeToEventServer( id, connectionHandler, mainPanelTab, - schema, - tablePrefix, - cohortTablePrefix = "cg_", - databaseTable = "DATABASE_META_DATA" + resultDatabaseSettings ) } \arguments{ @@ -21,13 +18,7 @@ characterizationTimeToEventServer( \item{mainPanelTab}{the current tab} -\item{schema}{the database schema for the model results} - -\item{tablePrefix}{a string that appends the tables in the result schema} - -\item{cohortTablePrefix}{a string that appends the cohort table in the result schema} - -\item{databaseTable}{name of the database table} +\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix} } \value{ The server to the prediction time to event module diff --git a/man/cohortCountsModule.Rd b/man/cohortCountsModule.Rd index 5b5229e3..8c4cce1f 100644 --- a/man/cohortCountsModule.Rd +++ b/man/cohortCountsModule.Rd @@ -8,7 +8,7 @@ cohortCountsModule( id, dataSource, cohortTable = dataSource$cohortTable, - databaseTable = dataSource$databaseTable, + databaseTable = dataSource$dbTable, selectedCohorts, selectedDatabaseIds, cohortIds diff --git a/man/cohortDefinitionsModule.Rd b/man/cohortDefinitionsModule.Rd index dcda2dca..87a60709 100644 --- a/man/cohortDefinitionsModule.Rd +++ b/man/cohortDefinitionsModule.Rd @@ -10,7 +10,7 @@ cohortDefinitionsModule( cohortDefinitions, cohortTable = dataSource$cohortTable, cohortCountTable = dataSource$cohortCountTable, - databaseTable = dataSource$databaseTable + databaseTable = dataSource$dbTable ) } \arguments{ diff --git a/man/characterizationView.Rd b/man/cohortDiagCharacterizationView.Rd similarity index 73% rename from man/characterizationView.Rd rename to man/cohortDiagCharacterizationView.Rd index 6695baaa..171c47b1 100644 --- a/man/characterizationView.Rd +++ b/man/cohortDiagCharacterizationView.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/cohort-diagnostics-characterization.R -\name{characterizationView} -\alias{characterizationView} +\name{cohortDiagCharacterizationView} +\alias{cohortDiagCharacterizationView} \title{characterization} \usage{ -characterizationView(id) +cohortDiagCharacterizationView(id) } \arguments{ \item{id}{Namespace Id - use namespaced id ns("characterization") inside diagnosticsExplorer module} diff --git a/man/cohortDiagnosticsSever.Rd b/man/cohortDiagnosticsServer.Rd similarity index 85% rename from man/cohortDiagnosticsSever.Rd rename to man/cohortDiagnosticsServer.Rd index c50aaed3..824adc41 100644 --- a/man/cohortDiagnosticsSever.Rd +++ b/man/cohortDiagnosticsServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/cohort-diagnostics-main.R -\name{cohortDiagnosticsSever} -\alias{cohortDiagnosticsSever} +\name{cohortDiagnosticsServer} +\alias{cohortDiagnosticsServer} \title{Cohort Diagnostics Explorer main module} \usage{ -cohortDiagnosticsSever( +cohortDiagnosticsServer( id, connectionHandler, resultDatabaseSettings, diff --git a/man/cohortMethodAttritionServer.Rd b/man/cohortMethodAttritionServer.Rd index 4837ea66..e1bc5703 100644 --- a/man/cohortMethodAttritionServer.Rd +++ b/man/cohortMethodAttritionServer.Rd @@ -9,9 +9,7 @@ cohortMethodAttritionServer( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix, - databaseTable + resultDatabaseSettings ) } \arguments{ @@ -23,11 +21,7 @@ cohortMethodAttritionServer( \item{connectionHandler}{the connection to the PLE results database} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} - -\item{databaseTable}{databaseTable} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ the PLE attrition results content server diff --git a/man/cohortMethodCovariateBalanceServer.Rd b/man/cohortMethodCovariateBalanceServer.Rd index 79435083..785ec960 100644 --- a/man/cohortMethodCovariateBalanceServer.Rd +++ b/man/cohortMethodCovariateBalanceServer.Rd @@ -9,8 +9,7 @@ cohortMethodCovariateBalanceServer( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix, + resultDatabaseSettings, metaAnalysisDbIds = NULL ) } @@ -23,9 +22,7 @@ cohortMethodCovariateBalanceServer( \item{connectionHandler}{the connection to the PLE results database} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} \item{metaAnalysisDbIds}{metaAnalysisDbIds} } diff --git a/man/cohortMethodDiagnosticsSummaryServer.Rd b/man/cohortMethodDiagnosticsSummaryServer.Rd index badcc665..1d69da14 100644 --- a/man/cohortMethodDiagnosticsSummaryServer.Rd +++ b/man/cohortMethodDiagnosticsSummaryServer.Rd @@ -7,10 +7,7 @@ cohortMethodDiagnosticsSummaryServer( id, connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, - databaseTable + resultDatabaseSettings ) } \arguments{ @@ -18,13 +15,7 @@ cohortMethodDiagnosticsSummaryServer( \item{connectionHandler}{the connection to the PLE results database} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} - -\item{cohortTablePrefix}{cohortTablePrefix} - -\item{databaseTable}{databaseTable} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ the PLE diagnostics summary results diff --git a/man/cohortMethodForestPlotServer.Rd b/man/cohortMethodForestPlotServer.Rd index d489c92a..95455092 100644 --- a/man/cohortMethodForestPlotServer.Rd +++ b/man/cohortMethodForestPlotServer.Rd @@ -10,9 +10,7 @@ cohortMethodForestPlotServer( selectedRow, inputParams, metaAnalysisDbIds = NULL, - resultsSchema, - tablePrefix, - databaseTable + resultDatabaseSettings ) } \arguments{ @@ -26,11 +24,7 @@ cohortMethodForestPlotServer( \item{metaAnalysisDbIds}{metaAnalysisDbIds} -\item{resultsSchema}{resultsSchema} - -\item{tablePrefix}{tablePrefix} - -\item{databaseTable}{databaseTable} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ the PLE forest plot content server diff --git a/man/cohortMethodKaplanMeierServer.Rd b/man/cohortMethodKaplanMeierServer.Rd index 94758418..2e52424c 100644 --- a/man/cohortMethodKaplanMeierServer.Rd +++ b/man/cohortMethodKaplanMeierServer.Rd @@ -9,10 +9,7 @@ cohortMethodKaplanMeierServer( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, - databaseTable, + resultDatabaseSettings, metaAnalysisDbIds = NULL ) } @@ -25,13 +22,7 @@ cohortMethodKaplanMeierServer( \item{connectionHandler}{the connection to the PLE results database} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} - -\item{cohortTablePrefix}{cohortTablePrefix} - -\item{databaseTable}{databaseTable} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} \item{metaAnalysisDbIds}{metaAnalysisDbIds} } diff --git a/man/cohortMethodPopulationCharacteristicsServer.Rd b/man/cohortMethodPopulationCharacteristicsServer.Rd index 41d7b101..1e0deca2 100644 --- a/man/cohortMethodPopulationCharacteristicsServer.Rd +++ b/man/cohortMethodPopulationCharacteristicsServer.Rd @@ -9,8 +9,7 @@ cohortMethodPopulationCharacteristicsServer( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix + resultDatabaseSettings ) } \arguments{ @@ -22,9 +21,7 @@ cohortMethodPopulationCharacteristicsServer( \item{connectionHandler}{the connection to the PLE results database} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ the PLE population characteristics content server diff --git a/man/cohortMethodPowerServer.Rd b/man/cohortMethodPowerServer.Rd index ddaeb90f..023f8251 100644 --- a/man/cohortMethodPowerServer.Rd +++ b/man/cohortMethodPowerServer.Rd @@ -9,8 +9,7 @@ cohortMethodPowerServer( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix, + resultDatabaseSettings, metaAnalysisDbIds = NULL ) } @@ -23,9 +22,7 @@ cohortMethodPowerServer( \item{connectionHandler}{the connection to the PLE results database} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} \item{metaAnalysisDbIds}{metaAnalysisDbIds} } diff --git a/man/cohortMethodPropensityModelServer.Rd b/man/cohortMethodPropensityModelServer.Rd index 1e5e2612..f0ce6248 100644 --- a/man/cohortMethodPropensityModelServer.Rd +++ b/man/cohortMethodPropensityModelServer.Rd @@ -9,8 +9,7 @@ cohortMethodPropensityModelServer( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix + resultDatabaseSettings ) } \arguments{ @@ -22,9 +21,7 @@ cohortMethodPropensityModelServer( \item{connectionHandler}{the connection to the PLE results database} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ the PLE propensity score model diff --git a/man/cohortMethodPropensityScoreDistServer.Rd b/man/cohortMethodPropensityScoreDistServer.Rd index 01f0d394..910cbb5d 100644 --- a/man/cohortMethodPropensityScoreDistServer.Rd +++ b/man/cohortMethodPropensityScoreDistServer.Rd @@ -9,9 +9,7 @@ cohortMethodPropensityScoreDistServer( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix, - cohortTablePrefix, + resultDatabaseSettings, metaAnalysisDbIds = F ) } @@ -24,11 +22,7 @@ cohortMethodPropensityScoreDistServer( \item{connectionHandler}{the connection to the PLE results database} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} - -\item{cohortTablePrefix}{cohortTablePrefix} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} \item{metaAnalysisDbIds}{metaAnalysisDbIds} } diff --git a/man/cohortMethodResultsTableServer.Rd b/man/cohortMethodResultsTableServer.Rd index a8e0f421..4b86209e 100644 --- a/man/cohortMethodResultsTableServer.Rd +++ b/man/cohortMethodResultsTableServer.Rd @@ -8,9 +8,7 @@ cohortMethodResultsTableServer( id, connectionHandler, inputParams, - resultsSchema, - tablePrefix, - databaseTable + resultDatabaseSettings ) } \arguments{ @@ -20,11 +18,7 @@ cohortMethodResultsTableServer( \item{inputParams}{the selected study parameters of interest} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} - -\item{databaseTable}{databaseTable} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ the PLE main results table server server diff --git a/man/cohortMethodSystematicErrorServer.Rd b/man/cohortMethodSystematicErrorServer.Rd index 29b136e8..d470cf17 100644 --- a/man/cohortMethodSystematicErrorServer.Rd +++ b/man/cohortMethodSystematicErrorServer.Rd @@ -9,8 +9,7 @@ cohortMethodSystematicErrorServer( selectedRow, inputParams, connectionHandler, - resultsSchema, - tablePrefix, + resultDatabaseSettings, metaAnalysisDbIds = NULL ) } @@ -21,11 +20,9 @@ cohortMethodSystematicErrorServer( \item{inputParams}{the selected study parameters of interest} -\item{connectionHandler}{the connection to the PLE results database} +\item{connectionHandler}{the connection handler to the result databases} -\item{resultsSchema}{the schema with the PLE results} - -\item{tablePrefix}{tablePrefix} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} \item{metaAnalysisDbIds}{metaAnalysisDbIds} } diff --git a/man/createCdDatabaseDataSource.Rd b/man/createCdDatabaseDataSource.Rd index 89b5359e..60bab3d9 100644 --- a/man/createCdDatabaseDataSource.Rd +++ b/man/createCdDatabaseDataSource.Rd @@ -6,11 +6,7 @@ \usage{ createCdDatabaseDataSource( connectionHandler, - schema, - vocabularyDatabaseSchema = schema, - tablePrefix = "", - cohortTableName = paste0(tablePrefix, "cohort"), - databaseTableName = paste0(tablePrefix, "database"), + resultDatabaseSettings, dataModelSpecificationsPath = system.file("cohort-diagnostics-ref", "resultsDataModelSpecification.csv", package = utils::packageName()), dataMigrationsRef = system.file("cohort-diagnostics-ref", "migrations.csv", package = @@ -21,15 +17,7 @@ createCdDatabaseDataSource( \arguments{ \item{connectionHandler}{An instance of a ResultModelManager::connectionHander - manages a connection to a database.} -\item{schema}{The schema containing the results tables in the database.} - -\item{vocabularyDatabaseSchema}{The schema containing the vocabulary tables in the database. If not provided, defaults to `resultsDatabaseSchema`.} - -\item{tablePrefix}{An optional prefix to add to the table names.} - -\item{cohortTableName}{The name of the cohort table in the database.} - -\item{databaseTableName}{The name of the database table in the database.} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} \item{dataModelSpecificationsPath}{The path to a file containing specifications for the data model used by the database.} diff --git a/man/dataDiagnosticDrillServer.Rd b/man/dataDiagnosticDrillServer.Rd index fa1b461d..cff580fb 100644 --- a/man/dataDiagnosticDrillServer.Rd +++ b/man/dataDiagnosticDrillServer.Rd @@ -4,16 +4,14 @@ \alias{dataDiagnosticDrillServer} \title{The module server for exploring prediction summary results} \usage{ -dataDiagnosticDrillServer(id, connectionHandler, mySchema, myTableAppend) +dataDiagnosticDrillServer(id, connectionHandler, resultDatabaseSettings) } \arguments{ \item{id}{the unique reference id for the module} \item{connectionHandler}{the connection to the prediction result database} -\item{mySchema}{the database schema for the model results} - -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the summary module diff --git a/man/dataDiagnosticSummaryServer.Rd b/man/dataDiagnosticSummaryServer.Rd index 44305008..a5eea92d 100644 --- a/man/dataDiagnosticSummaryServer.Rd +++ b/man/dataDiagnosticSummaryServer.Rd @@ -4,16 +4,14 @@ \alias{dataDiagnosticSummaryServer} \title{The module server for exploring prediction summary results} \usage{ -dataDiagnosticSummaryServer(id, connectionHandler, mySchema, myTableAppend) +dataDiagnosticSummaryServer(id, connectionHandler, resultDatabaseSettings) } \arguments{ \item{id}{the unique reference id for the module} \item{connectionHandler}{the connection to the prediction result database} -\item{mySchema}{the database schema for the model results} - -\item{myTableAppend}{a string that appends the tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the summary module diff --git a/man/evidenceSynthesisServer.Rd b/man/evidenceSynthesisServer.Rd index e0ce73d0..cf33e7d6 100644 --- a/man/evidenceSynthesisServer.Rd +++ b/man/evidenceSynthesisServer.Rd @@ -15,7 +15,7 @@ evidenceSynthesisServer( \item{connectionHandler}{a connection to the database with the results} -\item{resultDatabaseSettings}{a list containing the prediction result schema and connection details} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server for the PatientLevelPrediction module diff --git a/man/predictionCalibrationServer.Rd b/man/patientLevelPredictionCalibrationServer.Rd similarity index 65% rename from man/predictionCalibrationServer.Rd rename to man/patientLevelPredictionCalibrationServer.Rd index c112edc5..b06dbf22 100644 --- a/man/predictionCalibrationServer.Rd +++ b/man/patientLevelPredictionCalibrationServer.Rd @@ -1,16 +1,15 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-calibration.R -\name{predictionCalibrationServer} -\alias{predictionCalibrationServer} +% Please edit documentation in R/patient-level-prediction-calibration.R +\name{patientLevelPredictionCalibrationServer} +\alias{patientLevelPredictionCalibrationServer} \title{The module server for exploring prediction validation results} \usage{ -predictionCalibrationServer( +patientLevelPredictionCalibrationServer( id, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix + resultDatabaseSettings ) } \arguments{ @@ -22,9 +21,7 @@ predictionCalibrationServer( \item{inputSingleView}{the current tab} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the prediction calibration module diff --git a/man/predictionCalibrationViewer.Rd b/man/patientLevelPredictionCalibrationViewer.Rd similarity index 65% rename from man/predictionCalibrationViewer.Rd rename to man/patientLevelPredictionCalibrationViewer.Rd index 6514fb60..be958e62 100644 --- a/man/predictionCalibrationViewer.Rd +++ b/man/patientLevelPredictionCalibrationViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-calibration.R -\name{predictionCalibrationViewer} -\alias{predictionCalibrationViewer} +% Please edit documentation in R/patient-level-prediction-calibration.R +\name{patientLevelPredictionCalibrationViewer} +\alias{patientLevelPredictionCalibrationViewer} \title{The module viewer for exploring prediction model calibration results} \usage{ -predictionCalibrationViewer(id) +patientLevelPredictionCalibrationViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionCovariateSummaryServer.Rd b/man/patientLevelPredictionCovariateSummaryServer.Rd similarity index 69% rename from man/predictionCovariateSummaryServer.Rd rename to man/patientLevelPredictionCovariateSummaryServer.Rd index 6b7190be..32ed5c59 100644 --- a/man/predictionCovariateSummaryServer.Rd +++ b/man/patientLevelPredictionCovariateSummaryServer.Rd @@ -1,18 +1,17 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-covariateSummary.R -\name{predictionCovariateSummaryServer} -\alias{predictionCovariateSummaryServer} +% Please edit documentation in R/patient-level-prediction-covariateSummary.R +\name{patientLevelPredictionCovariateSummaryServer} +\alias{patientLevelPredictionCovariateSummaryServer} \title{The module server for exploring prediction covariate summary results} \usage{ -predictionCovariateSummaryServer( +patientLevelPredictionCovariateSummaryServer( id, modelDesignId, developmentDatabaseId, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix = "" + resultDatabaseSettings ) } \arguments{ @@ -28,9 +27,7 @@ predictionCovariateSummaryServer( \item{inputSingleView}{the current tab} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the covariate summary module diff --git a/man/predictionCovariateSummaryViewer.Rd b/man/patientLevelPredictionCovariateSummaryViewer.Rd similarity index 63% rename from man/predictionCovariateSummaryViewer.Rd rename to man/patientLevelPredictionCovariateSummaryViewer.Rd index 6be09a9b..fd4fb28d 100644 --- a/man/predictionCovariateSummaryViewer.Rd +++ b/man/patientLevelPredictionCovariateSummaryViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-covariateSummary.R -\name{predictionCovariateSummaryViewer} -\alias{predictionCovariateSummaryViewer} +% Please edit documentation in R/patient-level-prediction-covariateSummary.R +\name{patientLevelPredictionCovariateSummaryViewer} +\alias{patientLevelPredictionCovariateSummaryViewer} \title{The module viewer for exploring prediction covariate summary results} \usage{ -predictionCovariateSummaryViewer(id) +patientLevelPredictionCovariateSummaryViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionCutoffServer.Rd b/man/patientLevelPredictionCutoffServer.Rd similarity index 66% rename from man/predictionCutoffServer.Rd rename to man/patientLevelPredictionCutoffServer.Rd index 65583212..5f2916d8 100644 --- a/man/predictionCutoffServer.Rd +++ b/man/patientLevelPredictionCutoffServer.Rd @@ -1,16 +1,15 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-cutoff.R -\name{predictionCutoffServer} -\alias{predictionCutoffServer} +% Please edit documentation in R/patient-level-prediction-cutoff.R +\name{patientLevelPredictionCutoffServer} +\alias{patientLevelPredictionCutoffServer} \title{The module server for exploring prediction cut-off results} \usage{ -predictionCutoffServer( +patientLevelPredictionCutoffServer( id, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix + resultDatabaseSettings ) } \arguments{ @@ -22,9 +21,7 @@ predictionCutoffServer( \item{inputSingleView}{the current tab} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the prediction cut-off module diff --git a/man/predictionCutoffViewer.Rd b/man/patientLevelPredictionCutoffViewer.Rd similarity index 66% rename from man/predictionCutoffViewer.Rd rename to man/patientLevelPredictionCutoffViewer.Rd index 15377296..c4dcd60f 100644 --- a/man/predictionCutoffViewer.Rd +++ b/man/patientLevelPredictionCutoffViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-cutoff.R -\name{predictionCutoffViewer} -\alias{predictionCutoffViewer} +% Please edit documentation in R/patient-level-prediction-cutoff.R +\name{patientLevelPredictionCutoffViewer} +\alias{patientLevelPredictionCutoffViewer} \title{The module viewer for exploring prediction cut-off results} \usage{ -predictionCutoffViewer(id) +patientLevelPredictionCutoffViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionDesignSummaryServer.Rd b/man/patientLevelPredictionDesignSummaryServer.Rd similarity index 52% rename from man/predictionDesignSummaryServer.Rd rename to man/patientLevelPredictionDesignSummaryServer.Rd index 8610acd5..238528ae 100644 --- a/man/predictionDesignSummaryServer.Rd +++ b/man/patientLevelPredictionDesignSummaryServer.Rd @@ -1,15 +1,13 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-designSummary.R -\name{predictionDesignSummaryServer} -\alias{predictionDesignSummaryServer} +% Please edit documentation in R/patient-level-prediction-designSummary.R +\name{patientLevelPredictionDesignSummaryServer} +\alias{patientLevelPredictionDesignSummaryServer} \title{The module server for exploring prediction designs in the results} \usage{ -predictionDesignSummaryServer( +patientLevelPredictionDesignSummaryServer( id, connectionHandler, - schema, - plpTablePrefix, - cohortTablePrefix + resultDatabaseSettings ) } \arguments{ @@ -17,11 +15,7 @@ predictionDesignSummaryServer( \item{connectionHandler}{the connection to the prediction result database} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the plp tables in the result schema} - -\item{cohortTablePrefix}{a string that appends the cohort tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the prediction design module diff --git a/man/predictionDesignSummaryViewer.Rd b/man/patientLevelPredictionDesignSummaryViewer.Rd similarity index 64% rename from man/predictionDesignSummaryViewer.Rd rename to man/patientLevelPredictionDesignSummaryViewer.Rd index a26663d5..311bcd6e 100644 --- a/man/predictionDesignSummaryViewer.Rd +++ b/man/patientLevelPredictionDesignSummaryViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-designSummary.R -\name{predictionDesignSummaryViewer} -\alias{predictionDesignSummaryViewer} +% Please edit documentation in R/patient-level-prediction-designSummary.R +\name{patientLevelPredictionDesignSummaryViewer} +\alias{patientLevelPredictionDesignSummaryViewer} \title{The module viewer for exploring prediction designs that have been run} \usage{ -predictionDesignSummaryViewer(id) +patientLevelPredictionDesignSummaryViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionDiagnosticsServer.Rd b/man/patientLevelPredictionDiagnosticsServer.Rd similarity index 52% rename from man/predictionDiagnosticsServer.Rd rename to man/patientLevelPredictionDiagnosticsServer.Rd index 0fce5add..8b2db8f0 100644 --- a/man/predictionDiagnosticsServer.Rd +++ b/man/patientLevelPredictionDiagnosticsServer.Rd @@ -1,16 +1,14 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-diagnostics.R -\name{predictionDiagnosticsServer} -\alias{predictionDiagnosticsServer} +% Please edit documentation in R/patient-level-prediction-diagnostics.R +\name{patientLevelPredictionDiagnosticsServer} +\alias{patientLevelPredictionDiagnosticsServer} \title{The module server for exploring prediction diagnostic results} \usage{ -predictionDiagnosticsServer( +patientLevelPredictionDiagnosticsServer( id, modelDesignId, - schema, connectionHandler, - plpTablePrefix, - databaseTablePrefix + resultDatabaseSettings ) } \arguments{ @@ -18,16 +16,12 @@ predictionDiagnosticsServer( \item{modelDesignId}{the unique id for the model design} -\item{schema}{the database schema for the model results} - \item{connectionHandler}{the connection to the prediction result database} -\item{plpTablePrefix}{a string that appends the tables in the result schema} - -\item{databaseTablePrefix}{a string that appends the database_meta_data table} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ -The server to the predcition diagnostic module +The server to the prediction diagnostic module } \description{ The module server for exploring prediction diagnostic results diff --git a/man/predictionDiagnosticsViewer.Rd b/man/patientLevelPredictionDiagnosticsViewer.Rd similarity index 64% rename from man/predictionDiagnosticsViewer.Rd rename to man/patientLevelPredictionDiagnosticsViewer.Rd index 2ebce3e0..c2eccf8b 100644 --- a/man/predictionDiagnosticsViewer.Rd +++ b/man/patientLevelPredictionDiagnosticsViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-diagnostics.R -\name{predictionDiagnosticsViewer} -\alias{predictionDiagnosticsViewer} +% Please edit documentation in R/patient-level-prediction-diagnostics.R +\name{patientLevelPredictionDiagnosticsViewer} +\alias{patientLevelPredictionDiagnosticsViewer} \title{The module viewer for exploring prediction diagnostic results} \usage{ -predictionDiagnosticsViewer(id) +patientLevelPredictionDiagnosticsViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionDiscriminationServer.Rd b/man/patientLevelPredictionDiscriminationServer.Rd similarity index 65% rename from man/predictionDiscriminationServer.Rd rename to man/patientLevelPredictionDiscriminationServer.Rd index 76ba9f4e..1cafe353 100644 --- a/man/predictionDiscriminationServer.Rd +++ b/man/patientLevelPredictionDiscriminationServer.Rd @@ -1,16 +1,15 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-discrimination.R -\name{predictionDiscriminationServer} -\alias{predictionDiscriminationServer} +% Please edit documentation in R/patient-level-prediction-discrimination.R +\name{patientLevelPredictionDiscriminationServer} +\alias{patientLevelPredictionDiscriminationServer} \title{The module server for exploring prediction model discrimination results} \usage{ -predictionDiscriminationServer( +patientLevelPredictionDiscriminationServer( id, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix + resultDatabaseSettings ) } \arguments{ @@ -22,9 +21,7 @@ predictionDiscriminationServer( \item{inputSingleView}{the current tab} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the model discrimination module diff --git a/man/predictionDiscriminationViewer.Rd b/man/patientLevelPredictionDiscriminationViewer.Rd similarity index 64% rename from man/predictionDiscriminationViewer.Rd rename to man/patientLevelPredictionDiscriminationViewer.Rd index 5be24a48..0c2c43f1 100644 --- a/man/predictionDiscriminationViewer.Rd +++ b/man/patientLevelPredictionDiscriminationViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-discrimination.R -\name{predictionDiscriminationViewer} -\alias{predictionDiscriminationViewer} +% Please edit documentation in R/patient-level-prediction-discrimination.R +\name{patientLevelPredictionDiscriminationViewer} +\alias{patientLevelPredictionDiscriminationViewer} \title{The module viewer for exploring prediction model discrimination results} \usage{ -predictionDiscriminationViewer(id) +patientLevelPredictionDiscriminationViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionHelperFile.Rd b/man/patientLevelPredictionHelperFile.Rd similarity index 62% rename from man/predictionHelperFile.Rd rename to man/patientLevelPredictionHelperFile.Rd index 7a8c9ab2..c200cbc6 100644 --- a/man/predictionHelperFile.Rd +++ b/man/patientLevelPredictionHelperFile.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-main.R -\name{predictionHelperFile} -\alias{predictionHelperFile} +% Please edit documentation in R/patient-level-prediction-main.R +\name{patientLevelPredictionHelperFile} +\alias{patientLevelPredictionHelperFile} \title{The location of the prediction module helper file} \usage{ -predictionHelperFile() +patientLevelPredictionHelperFile() } \value{ string location of the prediction helper file diff --git a/man/predictionModelSummaryServer.Rd b/man/patientLevelPredictionModelSummaryServer.Rd similarity index 54% rename from man/predictionModelSummaryServer.Rd rename to man/patientLevelPredictionModelSummaryServer.Rd index c9de790a..0de376f4 100644 --- a/man/predictionModelSummaryServer.Rd +++ b/man/patientLevelPredictionModelSummaryServer.Rd @@ -1,16 +1,14 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-modelSummary.R -\name{predictionModelSummaryServer} -\alias{predictionModelSummaryServer} +% Please edit documentation in R/patient-level-prediction-modelSummary.R +\name{patientLevelPredictionModelSummaryServer} +\alias{patientLevelPredictionModelSummaryServer} \title{The module server for exploring prediction summary results} \usage{ -predictionModelSummaryServer( +patientLevelPredictionModelSummaryServer( id, connectionHandler, - schema, - plpTablePrefix, - modelDesignId, - databaseTablePrefix = plpTablePrefix + resultDatabaseSettings, + modelDesignId ) } \arguments{ @@ -18,13 +16,9 @@ predictionModelSummaryServer( \item{connectionHandler}{the connection to the prediction result database} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} \item{modelDesignId}{a reactable id specifying the prediction model design identifier} - -\item{databaseTablePrefix}{a string that appends the database_meta_data table} } \value{ The server to the summary module diff --git a/man/predictionModelSummaryViewer.Rd b/man/patientLevelPredictionModelSummaryViewer.Rd similarity index 62% rename from man/predictionModelSummaryViewer.Rd rename to man/patientLevelPredictionModelSummaryViewer.Rd index 99f52cf5..92aa79f3 100644 --- a/man/predictionModelSummaryViewer.Rd +++ b/man/patientLevelPredictionModelSummaryViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-modelSummary.R -\name{predictionModelSummaryViewer} -\alias{predictionModelSummaryViewer} +% Please edit documentation in R/patient-level-prediction-modelSummary.R +\name{patientLevelPredictionModelSummaryViewer} +\alias{patientLevelPredictionModelSummaryViewer} \title{The module viewer for exploring prediction summary results} \usage{ -predictionModelSummaryViewer(id) +patientLevelPredictionModelSummaryViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionNbServer.Rd b/man/patientLevelPredictionNbServer.Rd similarity index 67% rename from man/predictionNbServer.Rd rename to man/patientLevelPredictionNbServer.Rd index c4063d31..e0c3a360 100644 --- a/man/predictionNbServer.Rd +++ b/man/patientLevelPredictionNbServer.Rd @@ -1,16 +1,15 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-netbenefit.R -\name{predictionNbServer} -\alias{predictionNbServer} +% Please edit documentation in R/patient-level-prediction-netbenefit.R +\name{patientLevelPredictionNbServer} +\alias{patientLevelPredictionNbServer} \title{The module server for exploring prediction net-benefit results} \usage{ -predictionNbServer( +patientLevelPredictionNbServer( id, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix + resultDatabaseSettings ) } \arguments{ @@ -22,9 +21,7 @@ predictionNbServer( \item{inputSingleView}{the current tab} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the tables in the result schema} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the net-benefit module diff --git a/man/predictionNbViewer.Rd b/man/patientLevelPredictionNbViewer.Rd similarity index 67% rename from man/predictionNbViewer.Rd rename to man/patientLevelPredictionNbViewer.Rd index 3a4f764a..93badc2b 100644 --- a/man/predictionNbViewer.Rd +++ b/man/patientLevelPredictionNbViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-netbenefit.R -\name{predictionNbViewer} -\alias{predictionNbViewer} +% Please edit documentation in R/patient-level-prediction-netbenefit.R +\name{patientLevelPredictionNbViewer} +\alias{patientLevelPredictionNbViewer} \title{The module viewer for exploring prediction net-benefit results} \usage{ -predictionNbViewer(id) +patientLevelPredictionNbViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionServer.Rd b/man/patientLevelPredictionServer.Rd similarity index 78% rename from man/predictionServer.Rd rename to man/patientLevelPredictionServer.Rd index 61bce1a7..7c725ebd 100644 --- a/man/predictionServer.Rd +++ b/man/patientLevelPredictionServer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-main.R -\name{predictionServer} -\alias{predictionServer} +% Please edit documentation in R/patient-level-prediction-main.R +\name{patientLevelPredictionServer} +\alias{patientLevelPredictionServer} \title{The module server for exploring PatientLevelPrediction} \usage{ -predictionServer( +patientLevelPredictionServer( id, connectionHandler, resultDatabaseSettings = list(port = 1) diff --git a/man/predictionSettingsServer.Rd b/man/patientLevelPredictionSettingsServer.Rd similarity index 58% rename from man/predictionSettingsServer.Rd rename to man/patientLevelPredictionSettingsServer.Rd index 0bf22908..b987945c 100644 --- a/man/predictionSettingsServer.Rd +++ b/man/patientLevelPredictionSettingsServer.Rd @@ -1,20 +1,17 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-settings.R -\name{predictionSettingsServer} -\alias{predictionSettingsServer} +% Please edit documentation in R/patient-level-prediction-settings.R +\name{patientLevelPredictionSettingsServer} +\alias{patientLevelPredictionSettingsServer} \title{The module server for exploring prediction settings} \usage{ -predictionSettingsServer( +patientLevelPredictionSettingsServer( id, modelDesignId, developmentDatabaseId, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix, - cohortTablePrefix = plpTablePrefix, - databaseTablePrefix = plpTablePrefix + resultDatabaseSettings ) } \arguments{ @@ -30,13 +27,7 @@ predictionSettingsServer( \item{inputSingleView}{the current tab} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the tables in the result schema} - -\item{cohortTablePrefix}{a string that appends the cohort_definition table} - -\item{databaseTablePrefix}{a string that appends the database_meta_data table} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the settings module diff --git a/man/predictionSettingsViewer.Rd b/man/patientLevelPredictionSettingsViewer.Rd similarity index 63% rename from man/predictionSettingsViewer.Rd rename to man/patientLevelPredictionSettingsViewer.Rd index a3f674df..63ee8176 100644 --- a/man/predictionSettingsViewer.Rd +++ b/man/patientLevelPredictionSettingsViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-settings.R -\name{predictionSettingsViewer} -\alias{predictionSettingsViewer} +% Please edit documentation in R/patient-level-prediction-settings.R +\name{patientLevelPredictionSettingsViewer} +\alias{patientLevelPredictionSettingsViewer} \title{The module viewer for exploring prediction settings} \usage{ -predictionSettingsViewer(id) +patientLevelPredictionSettingsViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionValidationServer.Rd b/man/patientLevelPredictionValidationServer.Rd similarity index 63% rename from man/predictionValidationServer.Rd rename to man/patientLevelPredictionValidationServer.Rd index 8fe117b6..2e36700a 100644 --- a/man/predictionValidationServer.Rd +++ b/man/patientLevelPredictionValidationServer.Rd @@ -1,19 +1,17 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-validation.R -\name{predictionValidationServer} -\alias{predictionValidationServer} +% Please edit documentation in R/patient-level-prediction-validation.R +\name{patientLevelPredictionValidationServer} +\alias{patientLevelPredictionValidationServer} \title{The module server for exploring prediction validation results} \usage{ -predictionValidationServer( +patientLevelPredictionValidationServer( id, modelDesignId, developmentDatabaseId, performanceId, connectionHandler, inputSingleView, - schema, - plpTablePrefix = NULL, - databaseTablePrefix = plpTablePrefix + resultDatabaseSettings ) } \arguments{ @@ -29,11 +27,7 @@ predictionValidationServer( \item{inputSingleView}{the current tab} -\item{schema}{the database schema for the model results} - -\item{plpTablePrefix}{a string that appends the tables in the result schema} - -\item{databaseTablePrefix}{a string that appends the database_meta_data table} +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} } \value{ The server to the validation module diff --git a/man/predictionValidationViewer.Rd b/man/patientLevelPredictionValidationViewer.Rd similarity index 64% rename from man/predictionValidationViewer.Rd rename to man/patientLevelPredictionValidationViewer.Rd index d7dc0dad..abab5da5 100644 --- a/man/predictionValidationViewer.Rd +++ b/man/patientLevelPredictionValidationViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-validation.R -\name{predictionValidationViewer} -\alias{predictionValidationViewer} +% Please edit documentation in R/patient-level-prediction-validation.R +\name{patientLevelPredictionValidationViewer} +\alias{patientLevelPredictionValidationViewer} \title{The module viewer for exploring prediction validation results} \usage{ -predictionValidationViewer(id) +patientLevelPredictionValidationViewer(id) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/man/predictionViewer.Rd b/man/patientLevelPredictionViewer.Rd similarity index 68% rename from man/predictionViewer.Rd rename to man/patientLevelPredictionViewer.Rd index 93bb91bf..010e47c3 100644 --- a/man/predictionViewer.Rd +++ b/man/patientLevelPredictionViewer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prediction-main.R -\name{predictionViewer} -\alias{predictionViewer} +% Please edit documentation in R/patient-level-prediction-main.R +\name{patientLevelPredictionViewer} +\alias{patientLevelPredictionViewer} \title{The module viewer for exploring PatientLevelPrediction} \usage{ -predictionViewer(id = 1) +patientLevelPredictionViewer(id = 1) } \arguments{ \item{id}{the unique reference id for the module} diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 79616c99..d68beacc 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -16,7 +16,7 @@ dbmsTest <- 'sqlite' schemaTest <- 'main' # =========== CG START -cohortTablePrefix <- 'cg_' +cgTablePrefix <- 'cg_' connectionDetailsCG <- DatabaseConnector::createConnectionDetails( server = "../resources/cgDatabase/databaseFile.sqlite", @@ -24,6 +24,16 @@ connectionDetailsCG <- DatabaseConnector::createConnectionDetails( ) connectionHandlerCG <- ResultModelManager::ConnectionHandler$new(connectionDetailsCG, loadConnection = FALSE) +resultDatabaseSettingsCG <- list( + dbms = 'sqlite', + cgTablePrefix = 'cg_', + cgTablePrefix = 'cg_', + databaseTable = 'DATABASE_META_DATA', + databaseTablePrefix = '', + schema = 'main', + tempEmulationSchema = NULL +) + # =========== CG START # =========== PLP START @@ -38,7 +48,7 @@ connectionHandlerPlp <- ResultModelManager::ConnectionHandler$new(connectionDeta resultDatabaseSettingsPlp <- list( dbms = 'sqlite', # should this be removed - can use connection plpTablePrefix = '', - cohortTablePrefix = '', + cgTablePrefix = '', databaseTablePrefix = '', schema = 'main' ) @@ -60,8 +70,8 @@ connectionHandlerCharacterization <- ResultModelManager::ConnectionHandler$new( resultDatabaseSettingsCharacterization <- list( dbms = 'sqlite', # should this be removed - can use connection - tablePrefix = 'c_', - cohortTablePrefix = 'cg_', + cTablePrefix = 'c_', + cgTablePrefix = 'cg_', databaseTablePrefix = '', schema = 'main', databaseTable = 'DATABASE_META_DATA', @@ -86,8 +96,8 @@ connectionHandlerCm <- ResultModelManager::ConnectionHandler$new( resultDatabaseSettingsCm <- list( dbms = 'sqlite', - tablePrefix = 'cm_', - cohortTablePrefix = 'cg_', + cmTablePrefix = 'cm_', + cgTablePrefix = 'cg_', databaseTable = 'DATABASE_META_DATA', schema = "main", tempEmulationSchema = NULL @@ -106,7 +116,7 @@ connectionHandlerDataDiag <- ResultModelManager::ConnectionHandler$new(connectio resultDatabaseSettingsDataDiag <- list( dbms = 'sqlite', - tablePrefix = '', + ddTablePrefix = '', schema = "main" ) @@ -121,10 +131,13 @@ connectionDetailsCohortDiag <- DatabaseConnector::createConnectionDetails( resultDatabaseSettingsCohortDiag <- list( dbms = 'sqlite', - tablePrefix = '', + cdTablePrefix = '', schema = "main", - cohortTableName = "cohort", - databaseTableName = "database" + cgTablePrefix = '', + cgTable = "cohort", + databaseTablePrefix = '', + databaseTable = "database", + vocabularyDatabaseSchema = "main" ) connectionHandlerCohortDiag <- ResultModelManager::ConnectionHandler$new(connectionDetailsCohortDiag, loadConnection = FALSE) @@ -132,11 +145,7 @@ connectionHandlerCohortDiag <- ResultModelManager::ConnectionHandler$new(connect dataSourceCd <- createCdDatabaseDataSource( connectionHandler = connectionHandlerCohortDiag, - schema = "main", - vocabularyDatabaseSchema = "main", - tablePrefix = "", - cohortTableName = "cohort", - databaseTableName = "database", + resultDatabaseSettings = resultDatabaseSettingsCohortDiag, displayProgress = FALSE ) @@ -153,8 +162,8 @@ connectionHandlerSccs <- ResultModelManager::ConnectionHandler$new(connectionDe resultDatabaseSettingsSccs <- list( dbms = 'sqlite', - tablePrefix = 'sccs_', - cohortTablePrefix = 'cg_', + sccsTablePrefix = 'sccs_', + cgTablePrefix = 'cg_', databaseTable = 'DATABASE_META_DATA', schema = "main", tempEmulationSchema = NULL @@ -176,7 +185,7 @@ connectionHandlerES <- ResultModelManager::ConnectionHandler$new( resultDatabaseSettingsES <- list( dbms = 'sqlite', - tablePrefix = 'es_', + esTablePrefix = 'es_', cgTablePrefix = 'cg_', cmTablePrefix = 'cm_', sccsTablePrefix = 'sccs_', diff --git a/tests/testthat/test-characterization-aggregate-features.R b/tests/testthat/test-characterization-aggregate-features.R index d80c1b7d..959a3e27 100644 --- a/tests/testthat/test-characterization-aggregate-features.R +++ b/tests/testthat/test-characterization-aggregate-features.R @@ -4,12 +4,9 @@ shiny::testServer( app = characterizationAggregateFeaturesServer, args = list( connectionHandler = connectionHandlerCharacterization , - schema = resultDatabaseSettingsCharacterization$schema, - mainPanelTab = shiny::reactiveVal("Feature Comparison"), - tablePrefix = resultDatabaseSettingsCharacterization$tablePrefix, - cohortTablePrefix = resultDatabaseSettingsCharacterization$cohortTablePrefix, - databaseTable = resultDatabaseSettingsCharacterization$databaseTable - ), + resultDatabaseSettings = resultDatabaseSettingsCharacterization, + mainPanelTab = shiny::reactiveVal("Feature Comparison") + ), expr = { # expect the binaryData() to be the default diff --git a/tests/testthat/test-characterization-cohorts.R b/tests/testthat/test-characterization-cohorts.R index 05ee64cb..465b6c86 100644 --- a/tests/testthat/test-characterization-cohorts.R +++ b/tests/testthat/test-characterization-cohorts.R @@ -4,12 +4,9 @@ shiny::testServer( app = characterizationTableServer, args = list( connectionHandler = connectionHandlerCharacterization, - schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - tablePrefix = resultDatabaseSettingsCharacterization$tablePrefix, - cohortTablePrefix = resultDatabaseSettingsCharacterization$cohortTablePrefix, - databaseTable = resultDatabaseSettingsCharacterization$databaseTable - ), + resultDatabaseSettings = resultDatabaseSettingsCharacterization + ), expr = { diff --git a/tests/testthat/test-characterization-dechallengeRechallenge.R b/tests/testthat/test-characterization-dechallengeRechallenge.R index 3808662d..02906b26 100644 --- a/tests/testthat/test-characterization-dechallengeRechallenge.R +++ b/tests/testthat/test-characterization-dechallengeRechallenge.R @@ -4,11 +4,8 @@ shiny::testServer( app = characterizationDechallengeRechallengeServer, args = list( connectionHandler = connectionHandlerCharacterization, - schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - tablePrefix = resultDatabaseSettingsCharacterization$tablePrefix, - cohortTablePrefix = resultDatabaseSettingsCharacterization$cohortTablePrefix, - databaseTable = resultDatabaseSettingsCharacterization$databaseTable + resultDatabaseSettings = resultDatabaseSettingsCharacterization ), expr = { diff --git a/tests/testthat/test-characterization-incidence.R b/tests/testthat/test-characterization-incidence.R index 907caa34..351af9a5 100644 --- a/tests/testthat/test-characterization-incidence.R +++ b/tests/testthat/test-characterization-incidence.R @@ -4,10 +4,8 @@ shiny::testServer( app = characterizationIncidenceServer, args = list( connectionHandler = connectionHandlerCharacterization, - schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - incidenceTablePrefix = resultDatabaseSettingsCharacterization$incidenceTablePrefix, - databaseTable = resultDatabaseSettingsCharacterization$databaseTable + resultDatabaseSettings = resultDatabaseSettingsCharacterization ), expr = { diff --git a/tests/testthat/test-characterization-timeToEvent.R b/tests/testthat/test-characterization-timeToEvent.R index e4f98a88..fcb497bb 100644 --- a/tests/testthat/test-characterization-timeToEvent.R +++ b/tests/testthat/test-characterization-timeToEvent.R @@ -4,11 +4,8 @@ shiny::testServer( app = characterizationTimeToEventServer, args = list( connectionHandler = connectionHandlerCharacterization, - schema = resultDatabaseSettingsCharacterization$schema, mainPanelTab = shiny::reactiveVal("Feature Comparison"), - tablePrefix = resultDatabaseSettingsCharacterization$tablePrefix, - cohortTablePrefix = resultDatabaseSettingsCharacterization$cohortTablePrefix, - databaseTable = resultDatabaseSettingsCharacterization$databaseTable + resultDatabaseSettings = resultDatabaseSettingsCharacterization ), expr = { diff --git a/tests/testthat/test-cohort-diagnostics-characterization.R b/tests/testthat/test-cohort-diagnostics-characterization.R index 6754cdcc..233bcf5e 100644 --- a/tests/testthat/test-cohort-diagnostics-characterization.R +++ b/tests/testthat/test-cohort-diagnostics-characterization.R @@ -1,6 +1,7 @@ context("cohort-diagnostics-characterization") -shiny::testServer(characterizationModule, args = list( +shiny::testServer(cohortDiagCharacterizationModule, + args = list( id = "characterization", dataSource = dataSourceCd ), { diff --git a/tests/testthat/test-cohort-diagnostics-counts.R b/tests/testthat/test-cohort-diagnostics-counts.R index d69b0b5d..507052ad 100644 --- a/tests/testthat/test-cohort-diagnostics-counts.R +++ b/tests/testthat/test-cohort-diagnostics-counts.R @@ -4,7 +4,7 @@ shiny::testServer(cohortCountsModule, args = list( id = "testcohortcounts", #Any string is ok? dataSource = dataSourceCd, cohortTable = dataSourceCd$cohortTable, - databaseTable = dataSourceCd$databaseTable, + databaseTable = dataSourceCd$dbTable, selectedCohorts = shiny::reactive("Any String"), selectedDatabaseIds = shiny::reactive("Eunomia"), cohortIds = shiny::reactive({ c(14906, 14907) }) diff --git a/tests/testthat/test-cohort-diagnostics-definition.R b/tests/testthat/test-cohort-diagnostics-definition.R index 26febe64..7f22c68d 100644 --- a/tests/testthat/test-cohort-diagnostics-definition.R +++ b/tests/testthat/test-cohort-diagnostics-definition.R @@ -22,7 +22,7 @@ shiny::testServer(cohortDefinitionsModule, args = list( checkmate::expect_data_frame(getCountForConceptIdInCohort( dataSource = dataSourceCd, - databaseIds = dataSourceCd$databaseTable$databaseId, + databaseIds = dataSourceCd$dbTable$databaseId, cohortId = 14906 )) }) \ No newline at end of file diff --git a/tests/testthat/test-cohort-diagnostics-main.R b/tests/testthat/test-cohort-diagnostics-main.R index 9f5da96d..9e589ab8 100644 --- a/tests/testthat/test-cohort-diagnostics-main.R +++ b/tests/testthat/test-cohort-diagnostics-main.R @@ -1,6 +1,6 @@ context("cohort-diagnostics-main") -shiny::testServer(cohortDiagnosticsSever, args = list( +shiny::testServer(cohortDiagnosticsServer, args = list( id = "testCdServer", connectionHandler = connectionHandlerCohortDiag, resultDatabaseSettings = resultDatabaseSettingsCohortDiag, diff --git a/tests/testthat/test-cohort-method-CovariateBalance.R b/tests/testthat/test-cohort-method-CovariateBalance.R index 83949381..dbe1a6db 100644 --- a/tests/testthat/test-cohort-method-CovariateBalance.R +++ b/tests/testthat/test-cohort-method-CovariateBalance.R @@ -10,8 +10,7 @@ shiny::testServer( outcome = 3 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', + resultDatabaseSettings = resultDatabaseSettingsCm, metaAnalysisDbIds = '1' ), expr = { diff --git a/tests/testthat/test-cohort-method-DiagnosticsSummary.R b/tests/testthat/test-cohort-method-DiagnosticsSummary.R index 00f150c5..b0ea4297 100644 --- a/tests/testthat/test-cohort-method-DiagnosticsSummary.R +++ b/tests/testthat/test-cohort-method-DiagnosticsSummary.R @@ -4,9 +4,7 @@ shiny::testServer( app = cohortMethodDiagnosticsSummaryServer, args = list( connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { @@ -54,9 +52,7 @@ test_that("getCmDiagCohorts", { cohortIds <- getCmDiagCohorts( connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettingsCm, type = 'target' ) @@ -67,8 +63,7 @@ test_that("getCmDiagAnalyses", { analysisIds <- getCmDiagAnalyses( connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_' + resultDatabaseSettings = resultDatabaseSettingsCm ) testthat::expect_true(length(analysisIds) > 0) @@ -78,40 +73,30 @@ test_that("getCmDiagAnalyses", { analysisIds <- getCmDiagAnalyses( connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_' + resultDatabaseSettings = resultDatabaseSettingsCm ) cohortIds <- getCmDiagCohorts( connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettingsCm, type = 'target' ) outcomeIds <- getCmDiagCohorts( connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettingsCm, type = 'outcome' ) comparatorIds <- getCmDiagCohorts( connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, + resultDatabaseSettings = resultDatabaseSettingsCm, type = 'comparator' ) diag <- getCmDiagnosticsData( connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, - databaseTable = 'database_meta_data', + resultDatabaseSettings = resultDatabaseSettingsCm, targetIds = cohortIds, outcomeIds = outcomeIds, comparatorIds = comparatorIds, diff --git a/tests/testthat/test-cohort-method-ForestPlot.R b/tests/testthat/test-cohort-method-ForestPlot.R index 7dbd4f85..4d06b076 100644 --- a/tests/testthat/test-cohort-method-ForestPlot.R +++ b/tests/testthat/test-cohort-method-ForestPlot.R @@ -10,10 +10,8 @@ shiny::testServer( outcome = 3 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - metaAnalysisDbIds = NULL, - databaseTable = resultDatabaseSettingsCm$databaseTable + resultDatabaseSettings = resultDatabaseSettingsCm, + metaAnalysisDbIds = NULL ), expr = { diff --git a/tests/testthat/test-cohort-method-KaplanMeier.R b/tests/testthat/test-cohort-method-KaplanMeier.R index 49e89249..a05a3d0d 100644 --- a/tests/testthat/test-cohort-method-KaplanMeier.R +++ b/tests/testthat/test-cohort-method-KaplanMeier.R @@ -10,10 +10,7 @@ shiny::testServer( outcome = 3 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, - databaseTable = resultDatabaseSettingsCm$databaseTable, + resultDatabaseSettings = resultDatabaseSettingsCm, metaAnalysisDbIds = '1' ), expr = { diff --git a/tests/testthat/test-cohort-method-PopulationCharacteristics.R b/tests/testthat/test-cohort-method-PopulationCharacteristics.R index 693b2b68..cd204937 100644 --- a/tests/testthat/test-cohort-method-PopulationCharacteristics.R +++ b/tests/testthat/test-cohort-method-PopulationCharacteristics.R @@ -10,8 +10,7 @@ shiny::testServer( outcome = 3 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_' + resultDatabaseSettings = resultDatabaseSettingsCm #cohortTablePrefix = cohortTablePrefix, #databaseTable = databaseTable, #metaAnalysisDbIds = '1' diff --git a/tests/testthat/test-cohort-method-Power.R b/tests/testthat/test-cohort-method-Power.R index 40cf879f..6d85bb5f 100644 --- a/tests/testthat/test-cohort-method-Power.R +++ b/tests/testthat/test-cohort-method-Power.R @@ -15,8 +15,7 @@ shiny::testServer( outcome = 3 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', + resultDatabaseSettings = resultDatabaseSettingsCm, #cohortTablePrefix = cohortTablePrefix, #databaseTable = databaseTable, metaAnalysisDbIds = NULL diff --git a/tests/testthat/test-cohort-method-PropensityScoreDist.R b/tests/testthat/test-cohort-method-PropensityScoreDist.R index 3db9e3a4..93ba27da 100644 --- a/tests/testthat/test-cohort-method-PropensityScoreDist.R +++ b/tests/testthat/test-cohort-method-PropensityScoreDist.R @@ -15,10 +15,7 @@ shiny::testServer( outcome = 3 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - cohortTablePrefix = resultDatabaseSettingsCm$cohortTablePrefix, - #databaseTable = databaseTable, + resultDatabaseSettings = resultDatabaseSettingsCm, metaAnalysisDbIds = NULL ), expr = { diff --git a/tests/testthat/test-cohort-method-ResultsTable.R b/tests/testthat/test-cohort-method-ResultsTable.R index c7c5d292..bdf3a781 100644 --- a/tests/testthat/test-cohort-method-ResultsTable.R +++ b/tests/testthat/test-cohort-method-ResultsTable.R @@ -17,11 +17,7 @@ shiny::testServer( analysis = 1 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - #cohortTablePrefix = cohortTablePrefix, - databaseTable = resultDatabaseSettingsCm$databaseTable - #metaAnalysisDbIds = NULL + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { diff --git a/tests/testthat/test-cohort-method-attrition.R b/tests/testthat/test-cohort-method-attrition.R index 760bb09d..f47dfb04 100644 --- a/tests/testthat/test-cohort-method-attrition.R +++ b/tests/testthat/test-cohort-method-attrition.R @@ -10,9 +10,7 @@ shiny::testServer( outcome = 3 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - databaseTable = resultDatabaseSettingsCm$databaseTable + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { diff --git a/tests/testthat/test-cohort-method-propensityModel.R b/tests/testthat/test-cohort-method-propensityModel.R index c557e254..568eff83 100644 --- a/tests/testthat/test-cohort-method-propensityModel.R +++ b/tests/testthat/test-cohort-method-propensityModel.R @@ -15,8 +15,7 @@ shiny::testServer( outcome = 3 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_' + resultDatabaseSettings = resultDatabaseSettingsCm #cohortTablePrefix = cohortTablePrefix, #databaseTable = databaseTable, #metaAnalysisDbIds = NULL diff --git a/tests/testthat/test-cohort-method-systematicError.R b/tests/testthat/test-cohort-method-systematicError.R index edf0e515..95bd76ba 100644 --- a/tests/testthat/test-cohort-method-systematicError.R +++ b/tests/testthat/test-cohort-method-systematicError.R @@ -19,10 +19,7 @@ shiny::testServer( analysis = 1 )), connectionHandler = connectionHandlerCm, - resultsSchema = 'main', - tablePrefix = 'cm_', - #cohortTablePrefix = cohortTablePrefix, - #databaseTable = databaseTable + resultDatabaseSettings = resultDatabaseSettingsCm, metaAnalysisDbIds = 1 ), expr = { diff --git a/tests/testthat/test-cohortGenerator-main.R b/tests/testthat/test-cohortGenerator-main.R index de1c3bae..cc83403f 100644 --- a/tests/testthat/test-cohortGenerator-main.R +++ b/tests/testthat/test-cohortGenerator-main.R @@ -4,14 +4,7 @@ shiny::testServer( app = cohortGeneratorServer, args = list( connectionHandler = connectionHandlerCG, - resultDatabaseSettings = list( - dbms = 'sqlite', - tablePrefix = 'cg_', - cohortTablePrefix = 'cg_', - databaseTable = 'DATABASE_META_DATA', - schema = 'main', - tempEmulationSchema = NULL - ) + resultDatabaseSettings = resultDatabaseSettingsCG ), expr = { @@ -40,10 +33,7 @@ test_that("Test getCohortGeneratorCohortCounts ", { result <- getCohortGeneratorCohortCounts( connectionHandler = connectionHandlerCG, - resultsSchema = 'main', - tablePrefix = 'cg_', - databaseTable = 'DATABASE_META_DATA', - databaseTablePrefix = '' + resultDatabaseSettings = resultDatabaseSettingsCG ) testthat::expect_true( nrow(result) > 0 ) @@ -55,10 +45,7 @@ test_that("Test getCohortGeneratorCohortMeta ", { result <- getCohortGeneratorCohortMeta( connectionHandler = connectionHandlerCG, - resultsSchema = 'main', - tablePrefix = 'cg_', - databaseTable = 'DATABASE_META_DATA', - databaseTablePrefix = '' + resultDatabaseSettings = resultDatabaseSettingsCG ) testthat::expect_true( nrow(result) > 0 ) @@ -70,10 +57,7 @@ test_that("Test getCohortGeneratorCohortInclusionSummary ", { result <- getCohortGeneratorCohortInclusionSummary( connectionHandler = connectionHandlerCG, - resultsSchema = 'main', - tablePrefix = 'cg_', - databaseTable = 'DATABASE_META_DATA', - databaseTablePrefix = '' + resultDatabaseSettings = resultDatabaseSettingsCG ) testthat::expect_true( nrow(result) > 0 ) @@ -86,8 +70,7 @@ test_that("Test getCohortGeneratorInclusionRules ", { result <- getCohortGeneratorInclusionRules( connectionHandler = connectionHandlerCG, - resultsSchema = 'main', - tablePrefix = 'cg_' + resultDatabaseSettings = resultDatabaseSettingsCG ) testthat::expect_true( nrow(result) > 0 ) @@ -99,10 +82,7 @@ test_that("Test getCohortGeneratorInclusionStats ", { result <- getCohortGeneratorInclusionStats( connectionHandler = connectionHandlerCG, - resultsSchema = 'main', - tablePrefix = 'cg_', - databaseTable = 'DATABASE_META_DATA', - databaseTablePrefix = '' + resultDatabaseSettings = resultDatabaseSettingsCG ) testthat::expect_true( nrow(result) > 0 ) diff --git a/tests/testthat/test-data-diagnostic-drill.R b/tests/testthat/test-data-diagnostic-drill.R index 470057f7..f4b3e44a 100644 --- a/tests/testthat/test-data-diagnostic-drill.R +++ b/tests/testthat/test-data-diagnostic-drill.R @@ -4,8 +4,7 @@ shiny::testServer( app = dataDiagnosticDrillServer, args = list( connectionHandler = connectionHandlerDataDiag, - mySchema = resultDatabaseSettingsDataDiag$schema, - myTableAppend = resultDatabaseSettingsDataDiag$tablePrefix + resultDatabaseSettings = resultDatabaseSettingsDataDiag ), expr = { diff --git a/tests/testthat/test-data-diagnostic-summary.R b/tests/testthat/test-data-diagnostic-summary.R index 4971cfe2..e92ac26b 100644 --- a/tests/testthat/test-data-diagnostic-summary.R +++ b/tests/testthat/test-data-diagnostic-summary.R @@ -4,8 +4,7 @@ shiny::testServer( app = dataDiagnosticSummaryServer, args = list( connectionHandler = connectionHandlerDataDiag, - mySchema = resultDatabaseSettingsDataDiag$schema, - myTableAppend = resultDatabaseSettingsDataDiag$tablePrefix + resultDatabaseSettings = resultDatabaseSettingsDataDiag ), expr = { diff --git a/tests/testthat/test-evidence-synth-main.R b/tests/testthat/test-evidence-synth-main.R index 617c469d..12ececae 100644 --- a/tests/testthat/test-evidence-synth-main.R +++ b/tests/testthat/test-evidence-synth-main.R @@ -32,10 +32,7 @@ test_that("getCMEstimation", { res <- getCMEstimation( connectionHandler = connectionHandlerES, - mySchema = 'main', - cmTablePrefix = 'cm_', - cgTablePrefix = 'cg_', - databaseMetaData = 'database_meta_data', + resultDatabaseSettings = resultDatabaseSettingsES, targetId = 1, outcomeId = 3 ) @@ -48,10 +45,7 @@ test_that("getMetaEstimation", { res <- getMetaEstimation( connectionHandler = connectionHandlerES, - mySchema = 'main', - cmTablePrefix = 'cm_', - cgTablePrefix = 'cg_', - esTablePrefix = 'es_', + resultDatabaseSettings = resultDatabaseSettingsES, targetId = 1, outcomeId = 3 ) @@ -102,11 +96,7 @@ test_that("getSccsEstimation", { res <- getSccsEstimation( connectionHandlerES, - mySchema = 'main', - sccsTablePrefix = 'sccs_', - cgTablePrefix = 'cg_', - esTablePrefix = 'es_', - databaseMetaData = 'database_meta_data', + resultDatabaseSettings = resultDatabaseSettingsES, targetId = 1, outcomeId = 3 ) diff --git a/tests/testthat/test-prediction-calibration.R b/tests/testthat/test-patient-level-prediction-calibration.R similarity index 75% rename from tests/testthat/test-prediction-calibration.R rename to tests/testthat/test-patient-level-prediction-calibration.R index 5467f3d6..871c4405 100644 --- a/tests/testthat/test-prediction-calibration.R +++ b/tests/testthat/test-patient-level-prediction-calibration.R @@ -1,13 +1,12 @@ -context("prediction-calibration") +context("patient-level-prediction-calibration") shiny::testServer( - app = predictionCalibrationServer, + app = patientLevelPredictionCalibrationServer, args = list( performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Calibration"), - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { diff --git a/tests/testthat/test-prediction-covariateSummary.R b/tests/testthat/test-patient-level-prediction-covariateSummary.R similarity index 82% rename from tests/testthat/test-prediction-covariateSummary.R rename to tests/testthat/test-patient-level-prediction-covariateSummary.R index c3d90249..83bd72db 100644 --- a/tests/testthat/test-prediction-covariateSummary.R +++ b/tests/testthat/test-patient-level-prediction-covariateSummary.R @@ -1,15 +1,14 @@ -context("prediction-covariateSummary") +context("patient-level-prediction-covariateSummary") shiny::testServer( - app = predictionCovariateSummaryServer, + app = patientLevelPredictionCovariateSummaryServer, args = list( modelDesignId = shiny::reactiveVal(1), developmentDatabaseId = shiny::reactiveVal(1), performanceId = shiny::reactiveVal(NULL), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Discrimination"), - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { expect_true(is.null(covariateSummary())) diff --git a/tests/testthat/test-prediction-cutoff.R b/tests/testthat/test-patient-level-prediction-cutoff.R similarity index 77% rename from tests/testthat/test-prediction-cutoff.R rename to tests/testthat/test-patient-level-prediction-cutoff.R index e2a739d6..364ba35a 100644 --- a/tests/testthat/test-prediction-cutoff.R +++ b/tests/testthat/test-patient-level-prediction-cutoff.R @@ -1,13 +1,12 @@ -context("prediction-cutoff") +context("patient-level-prediction-cutoff") shiny::testServer( - app = predictionCutoffServer, + app = patientLevelPredictionCutoffServer, args = list( performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Threshold Dependant"), - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { diff --git a/tests/testthat/test-prediction-designSummary.R b/tests/testthat/test-patient-level-prediction-designSummary.R similarity index 53% rename from tests/testthat/test-prediction-designSummary.R rename to tests/testthat/test-patient-level-prediction-designSummary.R index 4602f373..a8931f0a 100644 --- a/tests/testthat/test-prediction-designSummary.R +++ b/tests/testthat/test-patient-level-prediction-designSummary.R @@ -1,12 +1,10 @@ -context("prediction-designSummary") +context("patient-level-prediction-designSummary") shiny::testServer( - app = predictionDesignSummaryServer, + app = patientLevelPredictionDesignSummaryServer, args = list( connectionHandler = connectionHandlerPlp, - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, - cohortTablePrefix = resultDatabaseSettingsPlp$cohortTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { @@ -22,11 +20,9 @@ shiny::testServer( #session$setInputs(show_diagnostic = list(index = 1)) #expect_true(!is.null(diagnosticId())) - designSummary <- getDesignSummary( - connectionHandler = connectionHandlerPlp, - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, - cohortTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + designSummary <- getPredictionDesignSummary( + connectionHandler = connectionHandler, #plp? + resultDatabaseSettings = resultDatabaseSettings,#plp? targetIds = targetIds[1], outcomeIds = outcomeIds[1] ) diff --git a/tests/testthat/test-prediction-diagnostics.R b/tests/testthat/test-patient-level-prediction-diagnostics.R similarity index 55% rename from tests/testthat/test-prediction-diagnostics.R rename to tests/testthat/test-patient-level-prediction-diagnostics.R index 037b8e63..941443f5 100644 --- a/tests/testthat/test-prediction-diagnostics.R +++ b/tests/testthat/test-patient-level-prediction-diagnostics.R @@ -1,22 +1,18 @@ -context("prediction-diagnostics") +context("patient-level-prediction-diagnostics") shiny::testServer( - app = predictionDiagnosticsServer, + app = patientLevelPredictionDiagnosticsServer, args = list( modelDesignId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, - databaseTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { - diag <- getDiagnostics( + diag <- getPredictionDiagnostics( modelDesignId = modelDesignId(), - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix, - databaseTablePrefix = databaseTablePrefix + resultDatabaseSettings = resultDatabaseSettings ) expect_true(nrow(diag) >0 ) diff --git a/tests/testthat/test-prediction-discrimination.R b/tests/testthat/test-patient-level-prediction-discrimination.R similarity index 80% rename from tests/testthat/test-prediction-discrimination.R rename to tests/testthat/test-patient-level-prediction-discrimination.R index 66a275e8..2783b348 100644 --- a/tests/testthat/test-prediction-discrimination.R +++ b/tests/testthat/test-patient-level-prediction-discrimination.R @@ -1,13 +1,12 @@ -context("prediction-discrimination") +context("patient-level-prediction-discrimination") shiny::testServer( - app = predictionDiscriminationServer, + app = patientLevelPredictionDiscriminationServer, args = list( performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Discrimination"), - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { diff --git a/tests/testthat/test-prediction-main.R b/tests/testthat/test-patient-level-prediction-main.R similarity index 72% rename from tests/testthat/test-prediction-main.R rename to tests/testthat/test-patient-level-prediction-main.R index 8882158b..eaba8af2 100644 --- a/tests/testthat/test-prediction-main.R +++ b/tests/testthat/test-patient-level-prediction-main.R @@ -1,7 +1,7 @@ -context("prediction-main") +context("patient-level-prediction-main") shiny::testServer( - app = predictionServer, + app = patientLevelPredictionServer, args = list( connectionHandler = connectionHandlerPlp, resultDatabaseSettings = resultDatabaseSettingsPlp @@ -27,14 +27,11 @@ shiny::testServer( session$setInputs(backToModelSummary = T) session$setInputs(backToDesignSummary = T) - result <- getResultSelection( + result <- getPlpResultSelection( # prediction?? connectionHandler = connectionHandlerPlp, - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, + resultDatabaseSettings = resultDatabaseSettingsPlp, modelDesignId = 1, - performanceId = 1, - cohortTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, - databaseTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + performanceId = 1 ) testthat::expect_is(result, 'shiny.tag.list') @@ -45,7 +42,7 @@ shiny::testServer( test_that("Test prediction ui", { # Test ui - ui <- predictionViewer() + ui <- patientLevelPredictionViewer() checkmate::expect_list(ui) }) diff --git a/tests/testthat/test-prediction-modelSummary.R b/tests/testthat/test-patient-level-prediction-modelSummary.R similarity index 66% rename from tests/testthat/test-prediction-modelSummary.R rename to tests/testthat/test-patient-level-prediction-modelSummary.R index c82d7c86..d89fbd3d 100644 --- a/tests/testthat/test-prediction-modelSummary.R +++ b/tests/testthat/test-patient-level-prediction-modelSummary.R @@ -1,13 +1,11 @@ -context("prediction-modelSummary") +context("patient-level-prediction-modelSummary") shiny::testServer( - app = predictionModelSummaryServer, + app = patientLevelPredictionModelSummaryServer, args = list( connectionHandler = connectionHandlerPlp, modelDesignId = shiny::reactiveVal(1), - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, - databaseTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { diff --git a/tests/testthat/test-prediction-netbenefit.R b/tests/testthat/test-patient-level-prediction-netbenefit.R similarity index 75% rename from tests/testthat/test-prediction-netbenefit.R rename to tests/testthat/test-patient-level-prediction-netbenefit.R index c84548fd..688c4e20 100644 --- a/tests/testthat/test-prediction-netbenefit.R +++ b/tests/testthat/test-patient-level-prediction-netbenefit.R @@ -1,13 +1,12 @@ -context("prediction-netbenefit") +context("patient-level-prediction-netbenefit") shiny::testServer( - app = predictionNbServer, + app = patientLevelPredictionNbServer, args = list( performanceId = shiny::reactiveVal(NULL), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal("Discrimination"), - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { diff --git a/tests/testthat/test-prediction-settings.R b/tests/testthat/test-patient-level-prediction-settings.R similarity index 78% rename from tests/testthat/test-prediction-settings.R rename to tests/testthat/test-patient-level-prediction-settings.R index 71553377..d429fc60 100644 --- a/tests/testthat/test-prediction-settings.R +++ b/tests/testthat/test-patient-level-prediction-settings.R @@ -1,15 +1,14 @@ -context("prediction-settings") +context("patient-level-prediction-settings") shiny::testServer( - app = predictionSettingsServer, + app = patientLevelPredictionSettingsServer, args = list( modelDesignId = shiny::reactiveVal(NULL), developmentDatabaseId = shiny::reactiveVal(1), performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal('Design Settings'), # only works with this - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { @@ -27,13 +26,11 @@ shiny::testServer( session$setInputs(showSample = T) session$setInputs(showHyperparameters = T) - design <- getModelDesign( + design <- getPredictionModelDesign( inputSingleView = inputSingleView, modelDesignId = modelDesignId, - schema = schema, connectionHandler = connectionHandler, - plpTablePrefix = plpTablePrefix, - cohortTablePrefix = '' # add as input? + resultDatabaseSettings = resultDatabaseSettings ) expect_true(class(design) == 'list') expect_true(!is.null(design$RestrictPlpData)) diff --git a/tests/testthat/test-prediction-validation.R b/tests/testthat/test-patient-level-prediction-validation.R similarity index 76% rename from tests/testthat/test-prediction-validation.R rename to tests/testthat/test-patient-level-prediction-validation.R index c1052b6b..407f60b8 100644 --- a/tests/testthat/test-prediction-validation.R +++ b/tests/testthat/test-patient-level-prediction-validation.R @@ -1,16 +1,14 @@ -context("prediction-valdiation") +context("patient-level-prediction-valdiation") shiny::testServer( - app = predictionValidationServer, + app = patientLevelPredictionValidationServer, args = list( modelDesignId = shiny::reactiveVal(1), developmentDatabaseId = shiny::reactiveVal(1), performanceId = shiny::reactiveVal(1), connectionHandler = connectionHandlerPlp, inputSingleView = shiny::reactiveVal('No Validation'), - schema = resultDatabaseSettingsPlp$schema, - plpTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix, - databaseTablePrefix = resultDatabaseSettingsPlp$plpTablePrefix + resultDatabaseSettings = resultDatabaseSettingsPlp ), expr = { From 60415c2ec4acc13a0cd08144c4858f443ff76686 Mon Sep 17 00:00:00 2001 From: jreps Date: Mon, 17 Jul 2023 14:39:58 -0400 Subject: [PATCH 15/20] updated interface updated interface --- NAMESPACE | 3 + R/about-main.R | 12 +- R/characterization-cohorts.R | 4 +- R/cohort-diagnostics-main.R | 5 - ...nerator-main.R => cohort-generator-main.R} | 349 +++++++-- R/datasources-main.R | 209 ++++++ R/phevaluator-main.R | 696 ++++++++++++++++++ man/aboutServer.Rd | 10 +- man/resultTableServer.Rd | 10 + renv/settings.dcf | 10 - tests/testthat/setup.R | 44 ++ ...or-main.R => test-cohort-generator-main.R} | 0 tests/testthat/test-datasources-main.R | 19 + 13 files changed, 1277 insertions(+), 94 deletions(-) rename R/{cohortgenerator-main.R => cohort-generator-main.R} (73%) create mode 100644 R/datasources-main.R create mode 100644 R/phevaluator-main.R delete mode 100644 renv/settings.dcf rename tests/testthat/{test-cohortGenerator-main.R => test-cohort-generator-main.R} (100%) create mode 100644 tests/testthat/test-datasources-main.R diff --git a/NAMESPACE b/NAMESPACE index d082518a..09d4724e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -96,6 +96,9 @@ export(patientLevelPredictionSettingsViewer) export(patientLevelPredictionValidationServer) export(patientLevelPredictionValidationViewer) export(patientLevelPredictionViewer) +export(phevaluatorHelperFile) +export(phevaluatorServer) +export(phevaluatorViewer) export(resultTableServer) export(resultTableViewer) export(sccsHelperFile) diff --git a/R/about-main.R b/R/about-main.R index a08eebbf..00351e85 100644 --- a/R/about-main.R +++ b/R/about-main.R @@ -42,7 +42,9 @@ aboutHelperFile <- function(){ #' The user interface to the home page module #' #' @export -aboutViewer <- function(id = 'homepage') { +aboutViewer <- function( + id = 'homepage' + ) { ns <- shiny::NS(id) shinydashboard::box( @@ -71,12 +73,18 @@ aboutViewer <- function(id = 'homepage') { #' The user specifies the id for the module #' #' @param id the unique reference id for the module +#' @param connectionHandler a connection to the database with the results +#' @param resultDatabaseSettings a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix #' #' @return #' The server for the shiny app home #' #' @export -aboutServer <- function(id = 'homepage') { +aboutServer <- function( + id = 'homepage', + connectionHandler = NULL, + resultDatabaseSettings = NULL + ) { shiny::moduleServer( id, function(input, output, session) { diff --git a/R/characterization-cohorts.R b/R/characterization-cohorts.R index 276504ab..3ba834b0 100644 --- a/R/characterization-cohorts.R +++ b/R/characterization-cohorts.R @@ -345,13 +345,13 @@ getDecCohortsInputs <- function( sql <- 'select d.database_id, d.cdm_source_abbreviation as database_name - from @result_schema.@database_table d;' + from @schema.@database_table d;' #shiny::incProgress(3/4, detail = paste("Extracting databaseIds")) database <- connectionHandler$queryDb( sql = sql, - result_schema = resultDatabaseSettings$schema, + schema = resultDatabaseSettings$schema, database_table = resultDatabaseSettings$databaseTable ) databaseIds <- database$databaseId diff --git a/R/cohort-diagnostics-main.R b/R/cohort-diagnostics-main.R index 6306ef91..4f23187f 100644 --- a/R/cohort-diagnostics-main.R +++ b/R/cohort-diagnostics-main.R @@ -107,11 +107,6 @@ getEnabledCdReports <- function(dataSource) { createCdDatabaseDataSource <- function( connectionHandler, resultDatabaseSettings, - #schema, - #vocabularyDatabaseSchema = schema, - #cdTablePrefix = "", - #cohortTableName = paste0(tablePrefix, "cohort"), - #databaseTableName = paste0(tablePrefix, "database"), dataModelSpecificationsPath = system.file("cohort-diagnostics-ref", "resultsDataModelSpecification.csv", package = utils::packageName()), diff --git a/R/cohortgenerator-main.R b/R/cohort-generator-main.R similarity index 73% rename from R/cohortgenerator-main.R rename to R/cohort-generator-main.R index 35d820f8..3ec32e53 100644 --- a/R/cohortgenerator-main.R +++ b/R/cohort-generator-main.R @@ -269,76 +269,6 @@ cohortGeneratorServer <- function( }) - # output$cohortCounts <- reactable::renderReactable({ - # data <- getCohortGeneratorCohortCounts( - # connectionHandler = connectionHandler, - # resultsSchema = resultsSchema, - # tablePrefix = resultDatabaseSettings$tablePrefix, - # databaseTable = resultDatabaseSettings$databaseTable, - # databaseTablePrefix = resultDatabaseSettings$databaseTablePrefix - # ) %>% - # dplyr::select("cdmSourceName", - # "cohortId", - # "cohortName", - # "cohortSubjects", - # "cohortEntries") - # - # data2 <- data %>% - # dplyr::select(input$cohortCountsCols) - # - # tryCatch({ - # reactable::reactable(data2, - # columns = list( - # # Render a "show details" button in the last column of the table. - # # This button won't do anything by itself, but will trigger the custom - # # click action on the column. - # cdmSourceName = reactable::colDef( - # header = withTooltip( - # "Database Name", - # "The name of the database" - # )), - # cohortId = reactable::colDef( - # header = withTooltip( - # "Cohort ID", - # "The unique numeric identifier of the cohort" - # )), - # cohortName = reactable::colDef( - # header = withTooltip( - # "Cohort Name", - # "The name of the cohort" - # )), - # cohortSubjects = reactable::colDef( - # header = withTooltip( - # "Number of Subjects", - # "The number of distinct subjects in the cohort" - # ), - # format = reactable::colFormat(separators = TRUE - # )), - # cohortEntries = reactable::colDef( - # header = withTooltip( - # "Number of Records", - # "The number of records in the cohort" - # ), - # format = reactable::colFormat(separators = TRUE - # )) - # ), - # filterable = TRUE, - # sortable = TRUE, - # resizable = T, - # searchable = T, - # striped = T, - # defaultColDef = reactable::colDef( - # align = "left" - # ) - # )}, - # error = function(e){ - # shiny::showNotification(paste0('Error: ', - # "Please select at least one column to display")); return(NULL) - # } - # ) - # }) - - # data <- getCohortGeneratorCohortCounts( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings @@ -408,10 +338,15 @@ cohortGeneratorServer <- function( rtable() }, - error = function(e){ - shiny::showNotification(paste0('Error: ', - "Please select at least one column to display")); return(NULL) - } + error = function(e){ + shiny::showNotification( + paste0( + 'Error: ', + "Please select at least one column to display" + ) + ); + return(NULL) + } ) }) @@ -770,3 +705,269 @@ cohortGeneratorServer <- function( } ) } + + + + +getCohortGeneratorCohortCounts <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT cc.cohort_id, cc.cohort_entries, cc.cohort_subjects, + dt.cdm_source_name, cd.cohort_name + FROM @schema.@cg_table_prefixCOHORT_COUNT cc + join @schema.@database_table_prefix@database_table dt + on cc.database_id = dt.database_id + join @schema.@cg_table_prefixCOHORT_DEFINITION cd + on cd.cohort_definition_id = cc.cohort_id + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix + ) + ) +} + +getCohortGeneratorCohortMeta <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT cg.cohort_id, cg.cohort_name, + cg.generation_status, cg.start_time, cg.end_time, dt.cdm_source_name + from @schema.@cg_table_prefixCOHORT_GENERATION cg + join @schema.@database_table_prefix@database_table dt + on cg.database_id = dt.database_id + ;" + + df <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix + ) + + df2 <- df %>% + dplyr::mutate( + generationDuration = dplyr::case_when( + generationStatus == "COMPLETE" + ~ tryCatch( + { + difftime( + as.POSIXct(as.numeric(.data$endTime), origin = "1970-01-01"), + as.POSIXct(as.numeric(.data$startTime), origin = "1970-01-01"), + units="mins" + ) + }, + error = function(e){return(NA)} + ), + T ~ NA + ) + ) + + return(df2) +} + +getCohortGeneratorCohortInclusionSummary <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT css.cohort_definition_id, css.base_count, css.final_count, css.mode_id, + dt.cdm_source_name, cd.cohort_name + FROM @schema.@cg_table_prefixCOHORT_SUMMARY_STATS css + join @schema.@database_table_prefix@database_table dt + on css.database_id = dt.database_id + join @schema.@cg_table_prefixCOHORT_DEFINITION cd + on cd.cohort_definition_id = css.cohort_definition_id + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema =resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix + ) + ) +} + + + +getCohortGeneratorInclusionRules <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT ci.cohort_definition_id, ci.rule_sequence, ci.name as rule_name, + cd.cohort_name FROM @schema.@cg_table_prefixCOHORT_INCLUSION ci + join @schema.@cg_table_prefixCOHORT_DEFINITION cd + on cd.cohort_definition_id = ci.cohort_definition_id + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix + ) + ) +} + +getCohortGeneratorInclusionStats <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT cir.database_id, cir.cohort_definition_id, cir.inclusion_rule_mask, cir.person_count, cir.mode_id, + dt.cdm_source_name FROM @schema.@cg_table_prefixCOHORT_INC_RESULT cir + join @schema.@database_table_prefix@database_table dt + on cir.database_id = dt.database_id + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix + ) + ) +} + +getCohortGenerationAttritionTable <- function( + rules, + stats +){ + + uniqueCohortIDs <- unique(rules$cohortDefinitionId) + + attritionTable <- data.frame() + + for(cohortId in uniqueCohortIDs){ + + cohortRules <- rules %>% + dplyr::filter(.data$cohortDefinitionId==cohortId) %>% + dplyr::select("ruleSequence", "ruleName", "cohortName") %>% + dplyr::arrange("ruleSequence") + + testMask = 0 + + for(i in 1:nrow(cohortRules)){ + + rule = cohortRules[i,] + + testMask = testMask + 2^(rule$ruleSequence) + + attritionRows <- stats %>% + dplyr::filter((.data$cohortDefinitionId == !!cohortId) & + (bitwAnd(.data$inclusionRuleMask, !!testMask) == !!testMask) + ) %>% + dplyr::select(-c("databaseId")) %>% + dplyr::group_by(.data$cdmSourceName, .data$cohortDefinitionId, .data$modeId) %>% + dplyr::summarise(personCount = sum(.data$personCount), + ) + + startingCounts <- stats %>% + dplyr::select(-c("databaseId")) %>% + dplyr::group_by(.data$cdmSourceName, .data$cohortDefinitionId, .data$modeId) %>% + dplyr::summarise(personCount = sum(.data$personCount), + ) %>% + dplyr::mutate(ruleSequence = -1, + ruleName = "Before any inclusion criteria", + ) + + attritionRowsFull <- cbind(attritionRows, rule) + + startingCountsFull <- cbind(startingCounts, rule %>% dplyr::select("cohortName")) %>% + dplyr::filter(.data$cohortDefinitionId %in% !!attritionRows$cohortDefinitionId) + + attritionTable <- rbind(attritionTable, attritionRowsFull, startingCountsFull) + + } + + } + + attritionTableDistinct <- dplyr::distinct(attritionTable) + + #adding drop counts + attritionTableFinal <- attritionTableDistinct %>% + dplyr::group_by( + .data$cdmSourceName, + .data$cohortDefinitionId, + .data$modeId) %>% + dplyr::mutate( + dropCount = dplyr::case_when( + is.na(dplyr::lag(.data$personCount, order_by = .data$ruleSequence)) ~ 0, + TRUE ~ dplyr::lag(.data$personCount, order_by = .data$ruleSequence) - .data$personCount + ), + dropPerc = dplyr::case_when( + is.na(dplyr::lag(.data$personCount, order_by = .data$ruleSequence)) ~ "0.00%", + TRUE ~ paste( + round( + (.data$dropCount/(dplyr::lag(.data$personCount, order_by = .data$ruleSequence)) * 100), + digits = 2 + ), + "%", + sep="") + ), + retainPerc = dplyr::case_when( + is.na(dplyr::lag(.data$personCount, order_by = .data$ruleSequence)) ~ "100.00%", + TRUE ~ paste( + round( + (.data$personCount/(dplyr::lag(.data$personCount, order_by = .data$ruleSequence)) * 100), + digits = 2 + ), + "%", + sep="") + + ) + ) + #newdata <- mtcars[order(mpg, -cyl),] + return(attritionTableFinal[order(attritionTableFinal$ruleSequence),]) + +} + +# test <- inputValsClean %>% +# dplyr::filter(cohortDefinitionId == 11057 & cdmSourceName == "Optum EHR" & +# modeId == "Subject") + +getCohortAttritionPlot <- function(data) { + + #colorPal <- colorRampPalette(c("darkgreen", "green", "yellow", "orange", "red")) + + fig <- plotly::plot_ly() + fig %>% + plotly::add_trace( + type = "funnel", + y = data$ruleName, + x = data$personCount, + texttemplate = "N: %{value:,d}
Number Lost: %{text:,d}", + marker = list(color = RColorBrewer::brewer.pal(length(unique(data$ruleName)), + "Greens" + ) + ), + connector = list(fillcolor = "#e9e9bf"), + text = data$dropCount, + hoverinfo = "percent initial+percent previous" , + hovertemplate='% of Previous: %{percentPrevious:.2%}
% of Initial: %{percentInitial:.2%}
' + ) %>% + plotly::layout(title = "Cohort Attrition by Inclusion Rules", + yaxis = list(categoryarray = c(order(data$personCount, decreasing = T))) + ) + +} + + + + + + + + + diff --git a/R/datasources-main.R b/R/datasources-main.R new file mode 100644 index 00000000..716909e7 --- /dev/null +++ b/R/datasources-main.R @@ -0,0 +1,209 @@ +# @file datasources-main.R +# +# Copyright 2022 Observational Health Data Sciences and Informatics +# +# This file is part of OhdsiShinyModules +# +# 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. + + + + +#' Define the helper file for the module +#' +#' @return The helper html file for the datasources module +#' @export +#' +datasourcesHelperFile <- function() { + fileLoc <- + system.file('datasources-www', "datasources.html", package = "OhdsiShinyModules") + return(fileLoc) +} + + + +#' The viewer function for hte datasources module +#' +#' @param id The unique id for the datasources viewer namespace +#' +#' @return The UI for the datasources module +#' @export +#' +datasourcesViewer <- function(id) { + ns <- shiny::NS(id) + + shinydashboard::box( + status = 'info', + width = "100%", + title = shiny::span(shiny::icon("database"), "Data Sources"), + solidHeader = TRUE, + + shinydashboard::box( + collapsible = TRUE, + collapsed = FALSE, + title = shiny::span( shiny::icon("circle-question"), "Help & Information"), + width = "100%", + shiny::htmlTemplate(system.file("datasources-www", "datasources.html", package = utils::packageName())) + ), + + shiny::tabsetPanel( + type = 'pills', + id = ns('mainPanel'), + + shiny::tabPanel( + title = "Data Source Information", + resultTableViewer(ns("datasourcesTable"), + downloadedFileName = "datasourcesTable-") + ) + ) + ) +} + + + + +#' The server function for the datasources module +#' +#' @param id The unique id for the datasources server namespace +#' @param connectionHandler A connection to the database with the results +#' @param resultDatabaseSettings A named list containing the cohort generator results database details (schema, table prefix) +#' +#' @return The server for the datasources module +#' @export +#' +datasourcesServer <- function( + id, + connectionHandler, + resultDatabaseSettings +) { + + shiny::moduleServer( + id, + function(input, output, session) { + + withTooltip <- function(value, tooltip, ...) { + shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", + tippy::tippy(value, tooltip, ...)) + } + + datasourcesData <- shiny::reactive({ + getDatasourcesData( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + }) + + # # defining column definitions + # datasourcesColDefs <- createCustomColDefList( + # rawColNames = colnames(datasourcesData), + # niceColNames = c("DB Name", + # "DB Abbreviation", + # "DB Holder", + # "DB Description", + # "DB Description Link", + # "DB ETL Link", + # "Source Data Release Date", + # "CDM DB Release Date", + # "CDM Version", + # "Vocabulary Version", + # "DB ID", + # "Max Obs. Period End Date"), + # tooltipText = c("Name of the database (DB)", + # "Abbreviation for the database (DB)", + # "Holder of the database (DB)", + # "Description of the database (DB)", + # "HTML link to the database (DB) description", + # "HTML link to the ETL for the database (DB)", + # "Date the source data was released", + # "Date the CDM database (DB) was accessible", + # "Version of the common data model (CDM)", + # "Version of the vocabulary used in the database (DB)", + # "Unique identifier (ID) of the database (DB)", + # "Maximum/Latest observation period date in the database (DB)"), + # customColDefOptions = list( + # list(NULL), + # list(NULL), + # list(NULL), + # list(show = F), + # list(html = TRUE, cell = htmlwidgets::JS(' + # function(cellInfo) { + # // Render as a link + # const url = cellInfo.value; + # return `
RHEALTH Description`; + # } + # ')), + # list(html = TRUE, cell = htmlwidgets::JS(' + # function(cellInfo) { + # // Render as a link + # const url = cellInfo.value; + # return `ETL`; + # } + # ')), + # list(format = reactable::colFormat(date = T)), + # list(format = reactable::colFormat(date = T)), + # list(NULL), + # list(NULL), + # list(NULL), + # list(NULL), + # list(format = reactable::colFormat(date = T)) + # ) + # ) + # + # #save the colDefs as json + # ParallelLogger::saveSettingsToJson(datasourcesColDefs, "./inst/components-columnInformation/datasources-colDefs.json") + + datasourcesColList <- ParallelLogger::loadSettingsFromJson(system.file("components-columnInformation", + "datasources-colDefs.json", + package = "OhdsiShinyModules") + ) + + #need to do for any colDefs that have JS and that are getting loaded in from a JSON + class(datasourcesColList[["sourceDocumentationReference"]]$cell) <- "JS_EVAL" + class(datasourcesColList[["cdmEtlReference"]]$cell) <- "JS_EVAL" + + + resultTableServer(id = "datasourcesTable", + df = datasourcesData, + colDefsInput = datasourcesColList, + downloadedFileName = "datasourcesTable-") + + return(invisible(NULL)) + + + + + }) +} + + +#pull database meta data table +getDatasourcesData <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * from @schema.@database_table + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + database_table = resultDatabaseSettings$databaseTable + ) + ) +} + + + + + diff --git a/R/phevaluator-main.R b/R/phevaluator-main.R new file mode 100644 index 00000000..22d3d809 --- /dev/null +++ b/R/phevaluator-main.R @@ -0,0 +1,696 @@ +# @file phevaluator-main.R +# +# Copyright 2022 Observational Health Data Sciences and Informatics +# +# This file is part of OhdsiShinyModules +# +# 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. + + + +#' The location of the phevaluator module helper file +#' +#' @details Returns the location of the cohort-generator helper file +#' +#' @return String location of the phevaluator helper file +#' +#' @export +#' +phevaluatorHelperFile <- function() { + fileLoc <- + system.file('phevaluator-www', "phevaluator.html", package = "OhdsiShinyModules") + return(fileLoc) +} + + +#' The viewer of the phevaluator module +#' +#' @param id The unique reference id for the module +#' +#' @return The user interface to the phevaluator results viewer +#' +#' @export +#' +phevaluatorViewer <- function(id) { + ns <- shiny::NS(id) + + shinydashboard::box( + status = 'info', + width = "100%", + title = shiny::span(shiny::icon("gauge"), "PheValuator"), + solidHeader = TRUE, + + shinydashboard::box( + collapsible = TRUE, + collapsed = FALSE, + title = shiny::span( shiny::icon("circle-question"), "Help & Information"), + width = "100%", + shiny::htmlTemplate(system.file("phevaluator-www", "phevaluator.html", package = utils::packageName())) + ), + + shinydashboard::box( + collapsible = TRUE, + collapsed = FALSE, + title = shiny::span( shiny::icon("gear"), "Options"), + width = "100%", + shiny::uiOutput(ns('phevalOptionsSelector')) + ), + + shiny::conditionalPanel( + condition = "input.generate != 0", + ns = ns, + + shiny::uiOutput(ns("inputsText")), + + shiny::tabsetPanel( + type = 'pills', + id = ns('mainPanel'), + + shiny::tabPanel( + title = "Phenotypes", + resultTableViewer(ns("cohortDefinitionSetTable"), + downloadedFileName = "cohortDefinitionSetTable-") + ), + shiny::tabPanel( + title = "Phenotype Performance Characteristics", + resultTableViewer(ns("algorithmPerformanceResultsTable"), + downloadedFileName = "algorithmPerformanceResultsTable-") + ), + shiny::tabPanel( + title = "Model Covariates", + resultTableViewer(ns("modelCovariatesTable"), + downloadedFileName = "modelCovariatesTable-") + ), + shiny::tabPanel( + title = "Model Performance", + resultTableViewer(ns("modelPerformanceTable"), + downloadedFileName = "modelPerformanceTable-") + ), + shiny::tabPanel( + title = "Model Input Parameters", + resultTableViewer(ns("modelInputParametersTable"), + downloadedFileName = "modelInputParametersTable-") + ), + shiny::tabPanel( + title = "Evaluation Cohort Diagnostics", + resultTableViewer(ns("diagnosticsTable"), + downloadedFileName = "diagnosticsTable-") + ), + shiny::tabPanel( + title = "Evaluation Cohort Parameters", + resultTableViewer(ns("evaluationInputParametersTable"), + downloadedFileName = "evaluationInputParametersTable-") + ), + shiny::tabPanel( + title = "Test Subjects", + resultTableViewer(ns("testSubjectsTable"), + downloadedFileName = "testSubjectsTable-") + ), + shiny::tabPanel( + title = "Test Subjects Covariates", + resultTableViewer(ns("testSubjectsCovariatesTable"), + downloadedFileName = "testSubjectsCovariatesTable-") + ) + ) + ) + ) +} + + +#' The module server for the main phevaluator module +#' +#' @param id The unique reference id for the module +#' @param connectionHandler A connection to the database with the results +#' @param resultDatabaseSettings A named list containing the cohort generator results database details (schema, table prefix) +#' +#' @return The phevaluator main module server +#' +#' @export +#' + +phevaluatorServer <- function( + id, + connectionHandler, + resultDatabaseSettings +) { + + shiny::moduleServer( + id, + function(input, output, session) { + + ns <- session$ns + + withTooltip <- function(value, tooltip, ...) { + shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", + tippy::tippy(value, tooltip, ...)) + } + + #use algorithm performance table to get "option columns", + #which will be used to make choices before generating result(s) + optionCols <- getPhevalAlgorithmPerformance( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::select("databaseId", "phenotype") + + databaseIds = unique(optionCols$databaseId) + phenotypeNames = unique(optionCols$phenotype) + + #build the selector + output$phevalOptionsSelector <- shiny::renderUI({ + + shiny::fluidPage( + shiny::fluidRow( + shiny::column( + width = 6, + shinyWidgets::pickerInput( + inputId = session$ns('selectedDatabaseIds'), + label = 'Database(s):', + choices = databaseIds, + selected = databaseIds, + choicesOpt = list(style = rep_len("color: black;", 999)), + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ), + width = "100%" + ) + ), + shiny::column( + width = 6, + shinyWidgets::pickerInput( + inputId = session$ns('selectedPhenotypes'), + label = 'Phenotype(s):', + choices = phenotypeNames, + selected = phenotypeNames, + choicesOpt = list(style = rep_len("color: black;", 999)), + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ), + width = "100%" + ) + ) + ), + shiny::actionButton( + inputId = session$ns('generate'), + label = 'Generate Results' + ) + ) + }) + + #if generate is pushed, extract the data + dataAlgorithmPerformance <- shiny::eventReactive( #we care about returning this value, so we use eventReactive + eventExpr = input$generate, #could add complexity to event if desired + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalAlgorithmPerformance( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::filter(.data$databaseId %in% input$selectedDatabaseIds & + .data$phenotype %in% input$selectedPhenotypes) %>% + dplyr::select("databaseId":"cohortId", "description", "sensitivity95Ci":"analysisId") + } + ) + + dataCohortDefinitionSet <- shiny::eventReactive( + eventExpr = input$generate, + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalCohortDefinitionSet( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::mutate(buttonSQL = makeButtonLabel("SQL"), + buttonJSON = makeButtonLabel("JSON")) %>% + dplyr::filter(.data$phenotype %in% input$selectedPhenotypes) + } + ) + + dataDiagnostics <- shiny::eventReactive( + eventExpr = input$generate, + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalDiagnostics( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::filter( + .data$databaseId %in% input$selectedDatabaseIds & + .data$phenotype %in% input$selectedPhenotypes + ) + } + ) + + dataEvalInputParams <- shiny::eventReactive( + eventExpr = input$generate, + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalEvalInputParams( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::filter( + .data$databaseId %in% input$selectedDatabaseIds & + .data$phenotype %in% input$selectedPhenotypes + ) + } + ) + + dataModelCovars <- shiny::eventReactive( + eventExpr = input$generate, + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalModelCovars( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::filter( + .data$databaseId %in% input$selectedDatabaseIds & + .data$phenotype %in% input$selectedPhenotypes + ) + } + ) + + dataModelInputParams <- shiny::eventReactive( + eventExpr = input$generate, + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalModelInputParams( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::filter( + .data$databaseId %in% input$selectedDatabaseIds & + .data$phenotype %in% input$selectedPhenotypes + ) + } + ) + + dataModelPerformance <- shiny::eventReactive( + eventExpr = input$generate, + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalModelPerformance( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::filter( + .data$databaseId %in% input$selectedDatabaseIds & + .data$phenotype %in% input$selectedPhenotypes + ) + } + ) + + dataTestSubjects <- shiny::eventReactive( + eventExpr = input$generate, + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalTestSubjects( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::filter( + .data$databaseId %in% input$selectedDatabaseIds & + .data$phenotype %in% input$selectedPhenotypes + ) + } + ) + + dataTestSubjectsCovars <- shiny::eventReactive( + eventExpr = input$generate, + { + if (is.null(input$selectedDatabaseIds) | + is.null(input$selectedPhenotypes)) { + data.frame() + } + + getPhevalTestSubjectsCovars( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) %>% + dplyr::filter( + .data$databaseId %in% input$selectedDatabaseIds & + .data$phenotype %in% input$selectedPhenotypes + ) + } + ) + + + + + selectedInputs <- shiny::reactiveVal() + output$inputsText <- shiny::renderUI(selectedInputs()) + + #when generate is pushed, return as text what was selected + shiny::observeEvent( + eventExpr = input$generate, + { + selectedInputs( + shinydashboard::box( + status = 'warning', + width = "100%", + title = 'Selected:', + shiny::div(shiny::fluidRow( + shiny::column( + width = 8, + shiny::tags$b("Phenotype(s):"), + + paste(unique(optionCols$databaseId[optionCols$databaseId %in% input$selectedDatabaseIds]), + collapse = ', ') + + ), + shiny::column( + width = 4, + shiny::tags$b("Database(s):"), + paste(unique(optionCols$phenotype[optionCols$phenotype %in% input$selectedPhenotypes]), + collapse = ', ') + ) + )) + ) + ) + } + ) + + #read in custom column name colDef list from rds file, generated by + #heplers-componentsCreateCustomColDefList.R + + phevalColList <- ParallelLogger::loadSettingsFromJson(system.file("components-columnInformation", + "phevaluator-colDefs.json", + package = "OhdsiShinyModules") + ) + + #define custom colDefs for SQL and JSON buttons + buttonColDefs <- list( + buttonSQL = reactable::colDef(header = withTooltip("SQL", "Downloads SQL code for the cohort"), + html = T + ), + buttonJSON = reactable::colDef(header = withTooltip("JSON", "Downloads JSON code for the cohort"), + html = T + ), + sql = reactable::colDef(show = F), + json = reactable::colDef(show = F) + ) + + #define custom column definitions and render the result table + customColDefs <- utils::modifyList(phevalColList, buttonColDefs) + + + resultTableServer(id = ns("algorithmPerformanceResultsTable"), + df = dataAlgorithmPerformance, + colDefsInput = customColDefs, + downloadedFileName = "algorithmPerformanceResultsTable-") + + resultTableServer(id = ns("cohortDefinitionSetTable"), + df = dataCohortDefinitionSet, + colDefsInput = customColDefs, + downloadedFileName = "cohortDefinitionSetTable-") + + resultTableServer(id = ns("diagnosticsTable"), + df = dataDiagnostics, + colDefsInput = customColDefs, + downloadedFileName = "diagnosticsTable-") + + resultTableServer(id = ns("evaluationInputParametersTable"), + df = dataEvalInputParams, + colDefsInput = customColDefs, + downloadedFileName = "evaluationInputParametersTable-") + + resultTableServer(id = ns("modelCovariatesTable"), + df = dataModelCovars, + colDefsInput = customColDefs, + downloadedFileName = "modelCovariatesTable-") + + resultTableServer(id = ns("modelInputParametersTable"), + df = dataModelInputParams, + colDefsInput = customColDefs, + downloadedFileName = "modelInputParametersTable-") + + resultTableServer(id = ns("modelPerformanceTable"), + df = dataModelPerformance, + colDefsInput = customColDefs, + downloadedFileName = "modelPerformanceTable-") + + resultTableServer(id = ns("testSubjectsTable"), + df = dataTestSubjects, + colDefsInput = customColDefs, + downloadedFileName = "testSubjectsTable-") + + resultTableServer(id = ns("testSubjectsCovariatesTable"), + df = dataTestSubjectsCovars, + colDefsInput = customColDefs, + downloadedFileName = "testSubjectsCovariatesTable-") + + return(invisible(NULL)) + + }) +} + +#add databaseId and phenotype as args into the function +#pass these into the sql code with 'where' + +getPhevalAlgorithmPerformance <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixALGORITHM_PERFORMANCE_RESULTS + ;" + + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + ) +} + +#test it + +# databaseIds = c("CCAE_RS", "Germany_RS") +# phenotypes = c("hyperprolactinemia") +# +# getPhevalAlgorithmPerformance(connectionHandler = connectionHandler, +# resultsSchema = resultDatabaseDetails$schema, +# tablePrefix = resultDatabaseDetails$tablePrefix +# ) + + +getPhevalCohortDefinitionSet <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixCOHORT_DEFINITION_SET + ;" + + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + ) +} + +getPhevalDiagnostics <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixDIAGNOSTICS + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + ) +} + +getPhevalEvalInputParams <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixEVALUATION_INPUT_PARAMETERS + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + ) +} + +getPhevalModelCovars <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixMODEL_COVARIATES + ;" + + df <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + + df$databaseId = stringi::stri_trans_general(df$databaseId, "latin-ascii") + df$phenotype = stringi::stri_trans_general(df$phenotype, "latin-ascii") + df$analysisName = stringi::stri_trans_general(df$analysisName, "latin-ascii") + df$covariateName = stringi::stri_trans_general(df$covariateName, "latin-ascii") + + return( + df + ) +} + +# d <- getPhevalModelCovars(connectionHandler = connectionHandler, +# resultsSchema = resultDatabaseDetails$schema, +# tablePrefix = resultDatabaseDetails$tablePrefix, +# databaseIds = databaseIds, +# phenotypes = phenotypes +# ) + + + +getPhevalModelInputParams <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixMODEL_INPUT_PARAMETERS + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + ) +} + +getPhevalModelPerformance <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixMODEL_PERFORMANCE + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + ) +} + +getPhevalTestSubjects <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixTEST_SUBJECTS + ;" + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + ) +} + +getPhevalTestSubjectsCovars <- function( + connectionHandler, + resultDatabaseSettings +) { + + sql <- "SELECT * FROM @schema.@pv_table_prefixTEST_SUBJECTS_COVARIATES + ;" + + df <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + pv_table_prefix = resultDatabaseSettings$pvTablePrefix + ) + + df$databaseId = stringi::stri_trans_general(df$databaseId, "latin-ascii") + df$phenotype = stringi::stri_trans_general(df$phenotype, "latin-ascii") + df$analysisName = stringi::stri_trans_general(df$analysisName, "latin-ascii") + df$type = stringi::stri_trans_general(df$type, "latin-ascii") + df$covariateName = stringi::stri_trans_general(df$covariateName, "latin-ascii") + + return( + df + ) + +} + + + + + + + + + diff --git a/man/aboutServer.Rd b/man/aboutServer.Rd index e2922a0e..6f767497 100644 --- a/man/aboutServer.Rd +++ b/man/aboutServer.Rd @@ -4,10 +4,18 @@ \alias{aboutServer} \title{The module server for the shiny app home} \usage{ -aboutServer(id = "homepage") +aboutServer( + id = "homepage", + connectionHandler = NULL, + resultDatabaseSettings = NULL +) } \arguments{ \item{id}{the unique reference id for the module} + +\item{connectionHandler}{a connection to the database with the results} + +\item{resultDatabaseSettings}{a list containing the characterization result schema, dbms, tablePrefix, databaseTable and cgTablePrefix} } \value{ The server for the shiny app home diff --git a/man/resultTableServer.Rd b/man/resultTableServer.Rd index f13bc361..4dc5f3ae 100644 --- a/man/resultTableServer.Rd +++ b/man/resultTableServer.Rd @@ -4,7 +4,17 @@ \alias{resultTableServer} \title{Result Table Server} \usage{ +<<<<<<< Updated upstream resultTableServer(id, df, colDefsInput, addActions = NULL) +======= +resultTableServer( + id, + df, + colDefsInput, + downloadedFileName = NULL, + addActions = NULL +) +>>>>>>> Stashed changes } \arguments{ \item{id}{string, table id must match resultsTableViewer function} diff --git a/renv/settings.dcf b/renv/settings.dcf deleted file mode 100644 index 169d82f1..00000000 --- a/renv/settings.dcf +++ /dev/null @@ -1,10 +0,0 @@ -bioconductor.version: -external.libraries: -ignored.packages: -package.dependency.fields: Imports, Depends, LinkingTo -r.version: -snapshot.type: implicit -use.cache: TRUE -vcs.ignore.cellar: TRUE -vcs.ignore.library: TRUE -vcs.ignore.local: TRUE diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index d68beacc..ad194313 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -197,6 +197,50 @@ resultDatabaseSettingsES <- list( # ==== +<<<<<<< Updated upstream +======= +# ====== PheValuator + +connectionDetailsPV <- DatabaseConnector::createConnectionDetails( + dbms = 'sqlite', + server = "../resources/pvDatabase/phevaluator.sqlite" +) + +connectionHandlerPV <- ResultModelManager::ConnectionHandler$new( + connectionDetailsPV, + loadConnection = FALSE +) + +resultDatabaseSettingsPV = list( + dbms = 'sqlite', + pvTablePrefix = 'pv_', + schema = 'main' +) + +# ==== + +# ====== DataSources + +connectionDetailsDS <- DatabaseConnector::createConnectionDetails( + dbms = 'sqlite', + server = "../resources/DSDatabase/databaseFile.sqlite" +) + +connectionHandlerDS <- ResultModelManager::ConnectionHandler$new( + connectionDetailsDS, + loadConnection = FALSE +) + +resultDatabaseSettingsDS = list( + dbms = 'sqlite', + databaseTablePrefix = '', + schema = 'main', + databaseTable = 'DATABASE_META_DATA' +) + +# ==== + +>>>>>>> Stashed changes ## cleanup after tests complete withr::defer({ options("shiny-test-env-enabled" = FALSE) diff --git a/tests/testthat/test-cohortGenerator-main.R b/tests/testthat/test-cohort-generator-main.R similarity index 100% rename from tests/testthat/test-cohortGenerator-main.R rename to tests/testthat/test-cohort-generator-main.R diff --git a/tests/testthat/test-datasources-main.R b/tests/testthat/test-datasources-main.R new file mode 100644 index 00000000..6369a297 --- /dev/null +++ b/tests/testthat/test-datasources-main.R @@ -0,0 +1,19 @@ +context("datasources-main") + +shiny::testServer(datasourcesServer, args = list( + id = "datasourcesServer", + connectionHandler = connectionHandlerPV, + resultDatabaseSettings = resultDatabaseSettingsPlp +), { + + testthat::expect_is(datasourcesData, 'reactive') + testthat::expect_true(!is.null(datasourcesColList)) + +}) + + +test_that("Test datasources ui", { + # Test ui + ui <- datasourcesViewer("datasources") + checkmate::expect_list(ui) +}) From 502e3aeb64be66386e548bd5c98e129ee0d659dc Mon Sep 17 00:00:00 2001 From: jreps Date: Mon, 17 Jul 2023 15:09:11 -0400 Subject: [PATCH 16/20] updated interface updated interface --- DESCRIPTION | 1 + NAMESPACE | 5 + R/components-data-viewer.R | 13 +- R/helpers-componentsCreateCustomColDefList.R | 92 + .../datasources-colDefs.json | 687 ++ .../phevaluator-colDefs.json | 7793 +++++++++++++++++ inst/datasources-www/datasources.html | 15 + inst/phevaluator-www/phevaluator.html | 10 + man/cohortGeneratorHelperFile.Rd | 2 +- man/cohortGeneratorServer.Rd | 2 +- man/cohortGeneratorViewer.Rd | 2 +- man/createCustomColDefList.Rd | 36 + man/datasourcesHelperFile.Rd | 14 + man/datasourcesServer.Rd | 21 + man/datasourcesViewer.Rd | 17 + man/makeButtonLabel.Rd | 17 + man/phevaluatorHelperFile.Rd | 17 + man/phevaluatorServer.Rd | 21 + man/phevaluatorViewer.Rd | 17 + man/resultTableServer.Rd | 10 +- man/resultTableViewer.Rd | 4 +- tests/testthat/setup.R | 3 - 22 files changed, 8783 insertions(+), 16 deletions(-) create mode 100644 R/helpers-componentsCreateCustomColDefList.R create mode 100644 inst/components-columnInformation/datasources-colDefs.json create mode 100644 inst/components-columnInformation/phevaluator-colDefs.json create mode 100644 inst/datasources-www/datasources.html create mode 100644 inst/phevaluator-www/phevaluator.html create mode 100644 man/createCustomColDefList.Rd create mode 100644 man/datasourcesHelperFile.Rd create mode 100644 man/datasourcesServer.Rd create mode 100644 man/datasourcesViewer.Rd create mode 100644 man/makeButtonLabel.Rd create mode 100644 man/phevaluatorHelperFile.Rd create mode 100644 man/phevaluatorServer.Rd create mode 100644 man/phevaluatorViewer.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 18851c3e..537a9ad1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -36,6 +36,7 @@ Imports: shinydashboard, shinyWidgets, SqlRender, + stringi, stringr, tibble, tidyr, diff --git a/NAMESPACE b/NAMESPACE index 09d4724e..2506823a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -56,6 +56,7 @@ export(cohortOverlapView) export(compareCohortCharacterizationView) export(conceptsInDataSourceView) export(createCdDatabaseDataSource) +export(createCustomColDefList) export(dataDiagnosticDrillServer) export(dataDiagnosticDrillViewer) export(dataDiagnosticHelperFile) @@ -64,6 +65,9 @@ export(dataDiagnosticSummaryServer) export(dataDiagnosticSummaryViewer) export(dataDiagnosticViewer) export(databaseInformationView) +export(datasourcesHelperFile) +export(datasourcesServer) +export(datasourcesViewer) export(evidenceSynthesisHelperFile) export(evidenceSynthesisServer) export(evidenceSynthesisViewer) @@ -72,6 +76,7 @@ export(getLogoImage) export(incidenceRatesView) export(inclusionRulesView) export(indexEventBreakdownView) +export(makeButtonLabel) export(orpahanConceptsView) export(patientLevelPredictionCalibrationServer) export(patientLevelPredictionCalibrationViewer) diff --git a/R/components-data-viewer.R b/R/components-data-viewer.R index 8809982e..2a63d2c0 100644 --- a/R/components-data-viewer.R +++ b/R/components-data-viewer.R @@ -7,11 +7,15 @@ #' Result Table Viewer #' #' @param id string +#' @param downloadedFileName string, desired name of downloaded data file. can use the name from the module that is being used #' #' @return shiny module UI #' @export #' -resultTableViewer <- function(id = "result-table") { +resultTableViewer <- function( + id = "result-table", + downloadedFileName = NULL + ) { ns <- shiny::NS(id) shiny::div(# UI shinydashboard::box( @@ -41,6 +45,7 @@ resultTableViewer <- function(id = "result-table") { "Reactable.downloadDataCSV('", ns('resultData'), "', 'result-data-filtered-", + downloadedFileName, Sys.Date(), ".csv')" ) @@ -142,6 +147,7 @@ ohdsiReactableTheme <- reactable::reactableTheme( #' @param colDefsInput named list of reactable::colDefs #' @param addActions add a button row selector column to the table to a column called 'actions'. #' actions must be a column in df +#' @param downloadedFileName string, desired name of downloaded data file. can use the name from the module that is being used #' #' @return shiny module server #' @export @@ -150,7 +156,8 @@ resultTableServer <- function( id, #string df, #data.frame colDefsInput, - addActions = NULL + addActions = NULL, + downloadedFileName = NULL ) #list of colDefs, can use checkmate::assertList, need a check that makes sure names = columns) { shiny::moduleServer( id, @@ -249,7 +256,7 @@ resultTableServer <- function( # download full data button output$downloadDataFull <- shiny::downloadHandler( filename = function() { - paste('data-full-', Sys.Date(), '.csv', sep = '') + paste('result-data-full-', downloadedFileName, Sys.Date(), '.csv', sep = '') }, content = function(con) { utils::write.csv( diff --git a/R/helpers-componentsCreateCustomColDefList.R b/R/helpers-componentsCreateCustomColDefList.R new file mode 100644 index 00000000..e17f7c30 --- /dev/null +++ b/R/helpers-componentsCreateCustomColDefList.R @@ -0,0 +1,92 @@ +#' Creating a list of custom column definitions for use in reactables +#' +#' @param rawColNames The raw column names taken directly from the source +#' data table that are to be overwritten in the reactable +#' @param niceColNames The formatted column names that will appear as-specified in +#' the reactable +#' @param tooltipText The text to be displayed in a toolTip when hovering over the +#' column in the reactable +#' @param case Optional argument to convert raw column names to snake or camel case. Defaults to NULL and preserves +#' whatever raw column names are passed in +#' @param customColDefOptions A list of lists, where the inner lists are any custom options from +#' reactable::colDef for each column +#' +#' @return A named list of reactable::colDef objects +#' @export +#' +createCustomColDefList <- function(rawColNames, niceColNames = NULL, + tooltipText = NULL, case = NULL, + customColDefOptions = NULL) { + withTooltip <- function(value, tooltip, ...) { + shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", + tippy::tippy(value, tooltip, ...)) + } + + if (is.null(niceColNames)) { + niceColNames <- rawColNames + } + + if (is.null(tooltipText)) { + tooltipText <- rep("", length(rawColNames)) + } + + if (!is.null(case)) { + if (case == "snakeCaseToCamelCase") { + rawColNames <- SqlRender::snakeCaseToCamelCase(rawColNames) + } else if (case == "camelCaseToSnakeCase") { + rawColNames <- SqlRender::camelCaseToSnakeCase(rawColNames) + } + } + + result <- vector("list", length(rawColNames)) + + if (is.null(customColDefOptions)) { + customColDefOptions <- vector("list", length(rawColNames)) + for (i in seq_along(rawColNames)) { + customColDefOptions[[i]] <- list() + } + } + + for (i in seq_along(rawColNames)) { + colDefOptions <- c( + list(name = rawColNames[[i]], header = withTooltip(niceColNames[[i]], tooltipText[[i]])), + customColDefOptions[[i]] + ) + + result[[i]] <- do.call(reactable::colDef, colDefOptions) + } + + names(result) <- rawColNames + + return(result) +} + + +# examples +# Define custom column definitions +# customColDefs <- createCustomColDefList( +# rawColNames = mydf$raw, +# niceColNames = c("Name", "Age", "Country"), +# tooltipText = c("Person's Name", "Person's Age", "Country"), +# customColDefOptions = list( +# list(NULL), # No aggregation for "Name" column +# list(aggregate = "mean"), # Aggregate "Age" column using mean +# list(NULL) # No aggregation for "Country" column +# ) +# ) + +# use the below as a guide to save named colDef list as JSON then read it back! +# test <- ParallelLogger::saveSettingsToJson(colDefs, "./inst/components-columnInformation/test.json") +#loadTest <- ParallelLogger::loadSettingsFromJson("./inst/components-columnInformation/test.json") + + +#' Make a label for an html button +#' +#' @param label The desired label for hte button +#' +#' @return html code to make a button label +#' @export +#' +makeButtonLabel <- function(label) { + as.character(htmltools::tags$div(htmltools::tags$button(paste(label)))) +} \ No newline at end of file diff --git a/inst/components-columnInformation/datasources-colDefs.json b/inst/components-columnInformation/datasources-colDefs.json new file mode 100644 index 00000000..8e31f223 --- /dev/null +++ b/inst/components-columnInformation/datasources-colDefs.json @@ -0,0 +1,687 @@ +{ + "cdmSourceName": { + "name": "cdmSourceName", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Name of the database (DB)" + }, + "text": "DB Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cdmSourceAbbreviation": { + "name": "cdmSourceAbbreviation", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Abbreviation for the database (DB)" + }, + "text": "DB Abbreviation" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cdmHolder": { + "name": "cdmHolder", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Holder of the database (DB)" + }, + "text": "DB Holder" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "sourceDescription": { + "name": "sourceDescription", + "show": false, + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Description of the database (DB)" + }, + "text": "DB Description" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "sourceDocumentationReference": { + "name": "sourceDocumentationReference", + "cell": "\n function(cellInfo) {\n // Render as a link\n const url = cellInfo.value;\n return `RHEALTH Description<\/a>`;\n }\n ", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "HTML link to the database (DB) description" + }, + "text": "DB Description Link" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "html": true, + "attr_class": "colDef" + }, + "cdmEtlReference": { + "name": "cdmEtlReference", + "cell": "\n function(cellInfo) {\n // Render as a link\n const url = cellInfo.value;\n return `ETL<\/a>`;\n }\n ", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "HTML link to the ETL for the database (DB)" + }, + "text": "DB ETL Link" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "html": true, + "attr_class": "colDef" + }, + "sourceReleaseDate": { + "name": "sourceReleaseDate", + "format": { + "cell": { + "date": true, + "attr_class": "colFormat" + }, + "aggregated": { + "date": true, + "attr_class": "colFormat" + } + }, + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Date the source data was released" + }, + "text": "Source Data Release Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cdmReleaseDate": { + "name": "cdmReleaseDate", + "format": { + "cell": { + "date": true, + "attr_class": "colFormat" + }, + "aggregated": { + "date": true, + "attr_class": "colFormat" + } + }, + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Date the CDM database (DB) was accessible" + }, + "text": "CDM DB Release Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cdmVersion": { + "name": "cdmVersion", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Version of the common data model (CDM)" + }, + "text": "CDM Version" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "vocabularyVersion": { + "name": "vocabularyVersion", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Version of the vocabulary used in the database (DB)" + }, + "text": "Vocabulary Version" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "databaseId": { + "name": "databaseId", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Unique identifier (ID) of the database (DB)" + }, + "text": "DB ID" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "maxObsPeriodEndDate": { + "name": "maxObsPeriodEndDate", + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Maximum/Latest observation period date in the database (DB)" + }, + "text": "Max Obs. Period End Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + } +} \ No newline at end of file diff --git a/inst/components-columnInformation/phevaluator-colDefs.json b/inst/components-columnInformation/phevaluator-colDefs.json new file mode 100644 index 00000000..33bef9c5 --- /dev/null +++ b/inst/components-columnInformation/phevaluator-colDefs.json @@ -0,0 +1,7793 @@ +{ + "databaseId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Database Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Phenotype name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cohortId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "ATLAS cohort id" + }, + "text": "Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "sensitivity95Ci": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Sensitivity with 95% CIs" + }, + "text": "Sensitivity (95 Ci)" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "ppv95Ci": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Positive predictive value with 95% CIs" + }, + "text": "PPV (95 Ci)" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "specificity95Ci": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Specificity with 95% CIs" + }, + "text": "Specificity (95 Ci)" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "npv95Ci": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Negative predictive value with 95% Cis" + }, + "text": "NPV (95 Ci)" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "estimatedPrevalence": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated prevalence based on true positives, true negatives, false positives, false negatives" + }, + "text": "Estimated Prevalence" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "f1Score": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated F1 score" + }, + "text": "F1 Score" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "truePositives": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated true positive count" + }, + "text": "True Positives" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "trueNegatives": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated true negative count" + }, + "text": "True Negatives" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "falsePositives": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated False positive count" + }, + "text": "False Positives" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "falseNegatives": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimate false negative count" + }, + "text": "False Negatives" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "washoutPeriod": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The mininum required continuous observation time prior to index date for subjects within the cohort to test" + }, + "text": "Washout Period" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "splayPrior": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of days to allow for testing phenotype visit date prior to evaluation date" + }, + "text": "Splay Prior" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "splayPost": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of days to allow for testing phenotype visit date after evaluation date" + }, + "text": "Splay Post" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cutPoint": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "A list of threshold predictions for the evaluations. Includes EV\" for the expected value\"" + }, + "text": "Cut Point" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "sensitivity": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated sensitivity" + }, + "text": "Sensitivity" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "sensitivityCi95Lb": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated sensitvity 95% CI lower bound" + }, + "text": "Sensitivity CI 95 LB" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "sensitivityCi95Ub": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated sensitvity 95% CI upper bound" + }, + "text": "Sensitivity CI 95 UB" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "ppv": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated positive predicted value" + }, + "text": "PPV" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "ppvCi95Lb": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated positive predicted value 95% CI lower bound" + }, + "text": "PPV CI 95 LB" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "ppvCi95Ub": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated positive predicted value 95% CI upper bound" + }, + "text": "PPV CI 95 UB" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "specificity": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated specificity" + }, + "text": "Specificity" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "specificityCi95Lb": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated specificity 95% CI lower bound" + }, + "text": "Specificity CI 95 LB" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "specificityCi95Ub": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated specificity 95% CI upper bound" + }, + "text": "Specificity CI 95 UB" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "npv": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated negative predicted value" + }, + "text": "NPV" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "npvCi95Lb": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated negative predicted value 95% CI lower bound" + }, + "text": "NPV CI 95 LB" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "npvCi95Ub": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Estimated negative predicted value 95% CI upper bound" + }, + "text": "NPV CI 95 UB" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTimeGMT": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The date and time (GMT) of the PheValuator run" + }, + "text": "Run Date Time GMT" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of the analysis within the run" + }, + "text": "Analysis Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "description": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Description of the cohort" + }, + "text": "Description" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "databaseId.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Database Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Phenotype name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "conceptId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Concept id" + }, + "text": "Concept Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "covariateValue": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Beta coefficient of the covariate in the model" + }, + "text": "Covariate Value" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "covariateName": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Name of the covariate" + }, + "text": "Covariate Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "covariateId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Covariate Id (concept id plus analysis id)" + }, + "text": "Covariate Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisId.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Type of analysis, e.g., 201 - condition group era during first time window" + }, + "text": "Analysis Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "databaseId.2": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Database Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype.2": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Phenotype name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName.2": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime.2": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "xSpecCohortId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Id of the xSpec cohort" + }, + "text": "xSpec Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "daysFromxSpec": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Number of allowed days from xSpec condition until analyzed visit" + }, + "text": "Days From xSpec" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "xSensCohortId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Id if the xSens cohort" + }, + "text": "xSens Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "prevalenceCohortId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Id of the prevalence cohort" + }, + "text": "Prevalence Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "modelPopulationCohortId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Id of the cohort used for cohort used as the base population for the model" + }, + "text": "Model Population Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "modelPopulationCohortIdStartDay": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of days relative to the mainPopulationCohortId cohort start date to begin including visits." + }, + "text": "Model Population Cohort Id Start Day" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "modelPopulationCohortIdEndDay": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of days relative to the mainPopulationCohortId cohort end date to begin including visits." + }, + "text": "Model Population Cohort Id End Day" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "lowerAgeLimit": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Lower age limit for subjects included in the model" + }, + "text": "Lower Age Limit" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "upperAgeLimit": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Upper age limit for subjects included in the model" + }, + "text": "Upper Age Limit" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "startDayWindow1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Start day for prediction window 1" + }, + "text": "Start Day Window 1" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "endDayWindow1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "End day for prediction window 1" + }, + "text": "End Day Window 1" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "startDayWindow2": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Start day for prediction window 2" + }, + "text": "Start Day Window 2" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "endDayWindow2": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "End day for prediction window 2" + }, + "text": "End Day Window 2" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "startDayWindow3": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Start day for prediction window 3" + }, + "text": "Start Day Window 3" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "endDayWindow3": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "End day for prediction window 3" + }, + "text": "End Day Window 3" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "visitType": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "List of allowed visit types in analysis" + }, + "text": "Visit Type" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "visitLength": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Visit length in days (0 = no specified visit length)" + }, + "text": "Visit Length" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "gender": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "List of sexes included in the model" + }, + "text": "Gender" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "race": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "List of races included in the model" + }, + "text": "Race" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "ethnicity": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "List of ethnicities included in the model" + }, + "text": "Ethnicity" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "minimumOffsetFromStart": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Minimum number of days to offset for the analysis visit from the start of the observation period" + }, + "text": "Minimum Offset From Start" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "minimumOffsetFromEnd ": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Minimum number of days to offset for the analysis visit from the end of the observation period" + }, + "text": "Minimum Offset From End " + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "startDate": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Start date for model subjects" + }, + "text": "Start Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "endDate": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "End date for model subjects" + }, + "text": "End Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "databaseId.3": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Database Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype.3": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Phenotype name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName.3": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime.3": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "inclusionEvaluationCohortId ": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of the cohort of the population to be used to designate which visits are eligible to be in the evaluation cohort" + }, + "text": "Inclusion Evaluation Cohort Id " + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "inclusionEvaluationDaysFromStart": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of days from the cohort start date of the inclusionEvaluationCohortId to start eligible included visits" + }, + "text": "Inclusion Evaluation Days From Start" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "inclusionEvaluationDaysFromEnd ": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of days from the cohort start date of the inclusionEvaluationCohortId to end eligible included visits" + }, + "text": "Inclusion Evaluation Days From End " + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "exclusionEvaluationCohortId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of the cohort of the population to be used to designate which visits are NOT eligible to be in the evaluation cohort" + }, + "text": "Exclusion Evaluation Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "exclusionEvaluationDaysFromStart": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of days from the cohort start date of the exclusionEvaluationCohortId to start ineligible included visits" + }, + "text": "Exclusion Evaluation Days From Start" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "exclusionEvaluationDaysFromEnd": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "The number of days from the cohort end date of the exclusionEvaluationCohortId to end ineligible included visits" + }, + "text": "Exclusion Evaluation Days From End" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "visitType.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "List of allowed visit types in analysis" + }, + "text": "Visit Type" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "visitLength.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Visit length in days (0 = no specified visit length)" + }, + "text": "Visit Length" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "xSpecCohortId.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Id of the xSpec cohort" + }, + "text": "xSpec Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "xSensCohortId.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Id if the xSens cohort" + }, + "text": "xSens Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "prevalenceCohortId.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Id of the prevalence cohort" + }, + "text": "Prevalence Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "lowerAgeLimit.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Lower age limit for subjects included in the model" + }, + "text": "Lower Age Limit" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "upperAgeLimit.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Upper age limit for subjects included in the model" + }, + "text": "Upper Age Limit" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "gender.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "List of sexes included in the model" + }, + "text": "Gender" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "race.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "List of races included in the model" + }, + "text": "Race" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "ethnicity.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "List of ethnicites included in the model" + }, + "text": "Ethnicity" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "minimumOffsetFromStart.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Minimum number of days to offset for the analysis visit from the start of the observation period" + }, + "text": "Minimum Offset From Start" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "minimumOffsetFromEnd .1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Minimum number of days to offset for the analysis visit from the end of the observation period" + }, + "text": "Minimum Offset From End " + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "startDate.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Start date for evaluation subjects" + }, + "text": "Start Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "endDate.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "End date for evaluation subjects" + }, + "text": "End Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "excludeModelFromEvaluation": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "True/false of whether subjects in the xSpec cohort should be excluded from the evaluation subjects" + }, + "text": "Exclude Model From Evaluation" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "databaseId.4": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Database Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype.4": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Phenotype name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName.4": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime.4": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "evaluation": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Type of evaluation - Train, test, or CV" + }, + "text": "Evaluation" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "metric": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Performance parameter name" + }, + "text": "Metric" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "value": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Value of the performance parameter" + }, + "text": "Value" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "databaseId.5": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Database Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype.5": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Phenotype name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName.5": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime.5": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "subjectId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Subject Id" + }, + "text": "Subject Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cohortStartDate": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Visit start date for analysis" + }, + "text": "Cohort Start Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "daysFromObsStart": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Days visit from observation start date" + }, + "text": "Days From Obs Start" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "daysToObsEnd": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Days visit from observation end date" + }, + "text": "Days To Obs End" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "ageYear": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Age at time of visit" + }, + "text": "Age Year" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "gender.2": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Sex of subject" + }, + "text": "Gender" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "type": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Type of subject - TP: true positive; FP: false positive; TN: true negative; FN: false negative" + }, + "text": "Type" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "value.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Predicted probability of subject having condition" + }, + "text": "Value" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "comparisonCohortStartDate": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Cohort start date for comparison to Prevalence cohort" + }, + "text": "Comparison Cohort Start Date" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "databaseId.6": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Database Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype.6": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Phenotype name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName.6": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime.6": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "subjectId.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Subject Id" + }, + "text": "Subject Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "type.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Type of subject - TP: true positive; FP: false positive; TN: true negative; FN: false negative" + }, + "text": "Type" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "conceptId.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Concept id of the covariate" + }, + "text": "Concept Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "coeffValue": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Predicted probability of subject having condition" + }, + "text": "Coefficient Value" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "covariateName.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Name of the covariate" + }, + "text": "Covariate Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "databaseId.7": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Database Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype.7": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Phenotype name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName.7": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime.7": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "Noncases": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Number of non-cases in the evaluation cohort" + }, + "text": "Noncases" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cases": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Number of cases in the evaluation cohort" + }, + "text": "Cases" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "count30And70pct": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Sum of the prediction value for predictions between 30-70%" + }, + "text": "Count 30 And 70%" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "prop30And70pct": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Proportion of sum of 30-70% subjects to total sum of predicted values" + }, + "text": "Prop 30 And 70%" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "count0And1pct": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Sum of the prediction value for predictions between 0-1%" + }, + "text": "Count 0 And 1%" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "prop0And1pct": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Proportion of sum of 0-1% subjects to total sum of predicted values" + }, + "text": "Prop 0 And 1%" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "countGT80pct": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Sum of the prediction value for predictions greater than or equal to 80%" + }, + "text": "Count GT 80%" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "propGT80pct": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Proportion of sum of greater than 80% subjects to total sum of predicted values" + }, + "text": "Prop GT 80%" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "phenotype.8": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Database name" + }, + "text": "Phenotype" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "analysisName.8": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis name" + }, + "text": "Analysis Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "runDateTime.8": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Analysis run date/time" + }, + "text": "Run Datetime" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "atlasId": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Atlas Id" + }, + "text": "Atlas Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cohortId.1": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Cohort Id" + }, + "text": "Cohort Id" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "cohortName": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "Cohort Name" + }, + "text": "Cohort Name" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "sql": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "sql code for cvohort" + }, + "text": "Sql" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "json": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "json code for cohort" + }, + "text": "Json" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "logicDescription": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "logic description" + }, + "text": "Logic Description" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + }, + "generateStats": { + "header": { + "name": "div", + "attribs": { + "style": "text-decoration: underline; text-decoration-style: dotted; cursor: help" + }, + "children": [ + { + "x": { + "opts": { + "content": "generate stats?" + }, + "text": "Generate Stats" + }, + "width": null, + "height": null, + "sizingPolicy": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "viewer": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": true, + "suppress": false, + "paneHeight": null + }, + "browser": { + "defaultWidth": null, + "defaultHeight": null, + "padding": null, + "fill": false, + "external": false + }, + "knitr": { + "defaultWidth": null, + "defaultHeight": null, + "figure": true + } + }, + "dependencies": null, + "elementId": null, + "preRenderHook": null, + "jsHooks": [], + "attr_class": ["tippy", "htmlwidget"], + "attr_package": "tippy" + } + ], + "attr_class": "shiny.tag" + }, + "attr_class": "colDef" + } +} \ No newline at end of file diff --git a/inst/datasources-www/datasources.html b/inst/datasources-www/datasources.html new file mode 100644 index 00000000..9dcb6d6d --- /dev/null +++ b/inst/datasources-www/datasources.html @@ -0,0 +1,15 @@ +

Below are the descriptions of each column in the Data Sources Module:

+
    +
  • cdmSourceName
  • +
  • cdmSourceAbbreviation
  • +
  • cdmHolder
  • +
  • sourceDescription
  • +
  • sourceDocumentationReference
  • +
  • cdmEtlReference
  • +
  • sourceReleaseDate
  • +
  • cdmReleaseDate
  • +
  • cdmVersion
  • +
  • vocabularyVersion
  • +
  • databaseId
  • +
  • maxObsPeriodEndDate
  • +
\ No newline at end of file diff --git a/inst/phevaluator-www/phevaluator.html b/inst/phevaluator-www/phevaluator.html new file mode 100644 index 00000000..92d23643 --- /dev/null +++ b/inst/phevaluator-www/phevaluator.html @@ -0,0 +1,10 @@ +

Below are the descriptions of each tab in the PheValuator Module, which will appear after clicking the "Generate Results" button below:

+
    +
  • Phenotypes – Phenotypes, with SQL and JSON code, examined using PheValuator.
  • +
  • Model Input Parameters – R function parameters used in PheValuator to develop the diagnostic predictive models.
  • +
  • Model Performance – performance characteristics of the diagnostic predictive models used in PheValuator, e.g., area under the receiver operator curve (AUROC).
  • +
  • Model Covariates – included covariates and beta coefficients of the diagnostic predictive model.
  • +
  • Evaluation Cohort Parameters - R function parameters used in PheValuator to develop the evaluation cohort used to determine phenotype algorithm performance.
  • +
  • Test Subjects and Covariates – examples of test subjects evaluated to be false positive or negative and true positive or negative and their associated covariates included in the diagnostic predictive model.
  • +
  • Phenotype Performance Characteristics – estimates for sensitivity, specificity, and positive and negative predictive value for phenotype algorithms tested by PheValuator.
  • +
\ No newline at end of file diff --git a/man/cohortGeneratorHelperFile.Rd b/man/cohortGeneratorHelperFile.Rd index 9e0d4575..cfb9d71f 100644 --- a/man/cohortGeneratorHelperFile.Rd +++ b/man/cohortGeneratorHelperFile.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohortgenerator-main.R +% Please edit documentation in R/cohort-generator-main.R \name{cohortGeneratorHelperFile} \alias{cohortGeneratorHelperFile} \title{The location of the cohort-generator module helper file} diff --git a/man/cohortGeneratorServer.Rd b/man/cohortGeneratorServer.Rd index 487a68ce..792a984f 100644 --- a/man/cohortGeneratorServer.Rd +++ b/man/cohortGeneratorServer.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohortgenerator-main.R +% Please edit documentation in R/cohort-generator-main.R \name{cohortGeneratorServer} \alias{cohortGeneratorServer} \title{The module server for the main cohort generator module} diff --git a/man/cohortGeneratorViewer.Rd b/man/cohortGeneratorViewer.Rd index 9fb3f81d..ed2776df 100644 --- a/man/cohortGeneratorViewer.Rd +++ b/man/cohortGeneratorViewer.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohortgenerator-main.R +% Please edit documentation in R/cohort-generator-main.R \name{cohortGeneratorViewer} \alias{cohortGeneratorViewer} \title{The viewer of the main cohort generator module} diff --git a/man/createCustomColDefList.Rd b/man/createCustomColDefList.Rd new file mode 100644 index 00000000..94c175e3 --- /dev/null +++ b/man/createCustomColDefList.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/helpers-componentsCreateCustomColDefList.R +\name{createCustomColDefList} +\alias{createCustomColDefList} +\title{Creating a list of custom column definitions for use in reactables} +\usage{ +createCustomColDefList( + rawColNames, + niceColNames = NULL, + tooltipText = NULL, + case = NULL, + customColDefOptions = NULL +) +} +\arguments{ +\item{rawColNames}{The raw column names taken directly from the source +data table that are to be overwritten in the reactable} + +\item{niceColNames}{The formatted column names that will appear as-specified in +the reactable} + +\item{tooltipText}{The text to be displayed in a toolTip when hovering over the +column in the reactable} + +\item{case}{Optional argument to convert raw column names to snake or camel case. Defaults to NULL and preserves +whatever raw column names are passed in} + +\item{customColDefOptions}{A list of lists, where the inner lists are any custom options from +reactable::colDef for each column} +} +\value{ +A named list of reactable::colDef objects +} +\description{ +Creating a list of custom column definitions for use in reactables +} diff --git a/man/datasourcesHelperFile.Rd b/man/datasourcesHelperFile.Rd new file mode 100644 index 00000000..02a08575 --- /dev/null +++ b/man/datasourcesHelperFile.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/datasources-main.R +\name{datasourcesHelperFile} +\alias{datasourcesHelperFile} +\title{Define the helper file for the module} +\usage{ +datasourcesHelperFile() +} +\value{ +The helper html file for the datasources module +} +\description{ +Define the helper file for the module +} diff --git a/man/datasourcesServer.Rd b/man/datasourcesServer.Rd new file mode 100644 index 00000000..5feb4cd5 --- /dev/null +++ b/man/datasourcesServer.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/datasources-main.R +\name{datasourcesServer} +\alias{datasourcesServer} +\title{The server function for the datasources module} +\usage{ +datasourcesServer(id, connectionHandler, resultDatabaseSettings) +} +\arguments{ +\item{id}{The unique id for the datasources server namespace} + +\item{connectionHandler}{A connection to the database with the results} + +\item{resultDatabaseSettings}{A named list containing the cohort generator results database details (schema, table prefix)} +} +\value{ +The server for the datasources module +} +\description{ +The server function for the datasources module +} diff --git a/man/datasourcesViewer.Rd b/man/datasourcesViewer.Rd new file mode 100644 index 00000000..5fb47f5e --- /dev/null +++ b/man/datasourcesViewer.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/datasources-main.R +\name{datasourcesViewer} +\alias{datasourcesViewer} +\title{The viewer function for hte datasources module} +\usage{ +datasourcesViewer(id) +} +\arguments{ +\item{id}{The unique id for the datasources viewer namespace} +} +\value{ +The UI for the datasources module +} +\description{ +The viewer function for hte datasources module +} diff --git a/man/makeButtonLabel.Rd b/man/makeButtonLabel.Rd new file mode 100644 index 00000000..c30a84f5 --- /dev/null +++ b/man/makeButtonLabel.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/helpers-componentsCreateCustomColDefList.R +\name{makeButtonLabel} +\alias{makeButtonLabel} +\title{Make a label for an html button} +\usage{ +makeButtonLabel(label) +} +\arguments{ +\item{label}{The desired label for hte button} +} +\value{ +html code to make a button label +} +\description{ +Make a label for an html button +} diff --git a/man/phevaluatorHelperFile.Rd b/man/phevaluatorHelperFile.Rd new file mode 100644 index 00000000..dfad1544 --- /dev/null +++ b/man/phevaluatorHelperFile.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/phevaluator-main.R +\name{phevaluatorHelperFile} +\alias{phevaluatorHelperFile} +\title{The location of the phevaluator module helper file} +\usage{ +phevaluatorHelperFile() +} +\value{ +String location of the phevaluator helper file +} +\description{ +The location of the phevaluator module helper file +} +\details{ +Returns the location of the cohort-generator helper file +} diff --git a/man/phevaluatorServer.Rd b/man/phevaluatorServer.Rd new file mode 100644 index 00000000..0f2a8275 --- /dev/null +++ b/man/phevaluatorServer.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/phevaluator-main.R +\name{phevaluatorServer} +\alias{phevaluatorServer} +\title{The module server for the main phevaluator module} +\usage{ +phevaluatorServer(id, connectionHandler, resultDatabaseSettings) +} +\arguments{ +\item{id}{The unique reference id for the module} + +\item{connectionHandler}{A connection to the database with the results} + +\item{resultDatabaseSettings}{A named list containing the cohort generator results database details (schema, table prefix)} +} +\value{ +The phevaluator main module server +} +\description{ +The module server for the main phevaluator module +} diff --git a/man/phevaluatorViewer.Rd b/man/phevaluatorViewer.Rd new file mode 100644 index 00000000..2b1530de --- /dev/null +++ b/man/phevaluatorViewer.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/phevaluator-main.R +\name{phevaluatorViewer} +\alias{phevaluatorViewer} +\title{The viewer of the phevaluator module} +\usage{ +phevaluatorViewer(id) +} +\arguments{ +\item{id}{The unique reference id for the module} +} +\value{ +The user interface to the phevaluator results viewer +} +\description{ +The viewer of the phevaluator module +} diff --git a/man/resultTableServer.Rd b/man/resultTableServer.Rd index 4dc5f3ae..fb375c88 100644 --- a/man/resultTableServer.Rd +++ b/man/resultTableServer.Rd @@ -4,17 +4,13 @@ \alias{resultTableServer} \title{Result Table Server} \usage{ -<<<<<<< Updated upstream -resultTableServer(id, df, colDefsInput, addActions = NULL) -======= resultTableServer( id, df, colDefsInput, - downloadedFileName = NULL, - addActions = NULL + addActions = NULL, + downloadedFileName = NULL ) ->>>>>>> Stashed changes } \arguments{ \item{id}{string, table id must match resultsTableViewer function} @@ -25,6 +21,8 @@ resultTableServer( \item{addActions}{add a button row selector column to the table to a column called 'actions'. actions must be a column in df} + +\item{downloadedFileName}{string, desired name of downloaded data file. can use the name from the module that is being used} } \value{ shiny module server diff --git a/man/resultTableViewer.Rd b/man/resultTableViewer.Rd index 2b6745a4..05816917 100644 --- a/man/resultTableViewer.Rd +++ b/man/resultTableViewer.Rd @@ -4,10 +4,12 @@ \alias{resultTableViewer} \title{Result Table Viewer} \usage{ -resultTableViewer(id = "result-table") +resultTableViewer(id = "result-table", downloadedFileName = NULL) } \arguments{ \item{id}{string} + +\item{downloadedFileName}{string, desired name of downloaded data file. can use the name from the module that is being used} } \value{ shiny module UI diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index ad194313..25a3f849 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -197,8 +197,6 @@ resultDatabaseSettingsES <- list( # ==== -<<<<<<< Updated upstream -======= # ====== PheValuator connectionDetailsPV <- DatabaseConnector::createConnectionDetails( @@ -240,7 +238,6 @@ resultDatabaseSettingsDS = list( # ==== ->>>>>>> Stashed changes ## cleanup after tests complete withr::defer({ options("shiny-test-env-enabled" = FALSE) From 670371707e38f0fb3fc19eed854d0aceae279c26 Mon Sep 17 00:00:00 2001 From: jreps Date: Mon, 17 Jul 2023 17:20:07 -0400 Subject: [PATCH 17/20] enabling shared db table for cohort diag - minor updates to cohort diagnostic to enable shared database table --- R/cohort-diagnostics-cohort-overlap.R | 28 +++++++++++++-- R/cohort-diagnostics-incidenceRates.R | 30 +++++++++++++--- R/cohort-diagnostics-main.R | 32 ++++++++++++++---- R/cohort-diagnostics-shared.R | 20 ++++++++--- tests/testthat/setup.R | 2 ++ tests/testthat/test-phevaluator-main.R | 47 ++++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 tests/testthat/test-phevaluator-main.R diff --git a/R/cohort-diagnostics-cohort-overlap.R b/R/cohort-diagnostics-cohort-overlap.R index 5e0e002d..50b105e3 100644 --- a/R/cohort-diagnostics-cohort-overlap.R +++ b/R/cohort-diagnostics-cohort-overlap.R @@ -259,10 +259,30 @@ getResultsCohortRelationships <- function(dataSource, databaseIds = NULL, startDays = NULL, endDays = NULL) { + #data <- dataSource$connectionHandler$queryDb( + # sql = "SELECT cr.*, db.database_name + # FROM @schema.@table_name cr + # INNER JOIN @schema.@database_table db ON db.database_id = cr.database_id + # WHERE cr.cohort_id IN (@cohort_id) + # AND cr.database_id IN (@database_id) + # {@comparator_cohort_id != \"\"} ? { AND cr.comparator_cohort_id IN (@comparator_cohort_id)} + # {@start_day != \"\"} ? { AND cr.start_day IN (@start_day)} + # {@end_day != \"\"} ? { AND cr.end_day IN (@end_day)};", + # snakeCaseToCamelCase = TRUE, + # schema = dataSource$schema, + # database_id = quoteLiterals(databaseIds), + # table_name = dataSource$prefixTable("cohort_relationships"), + # database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), + # cohort_id = cohortIds, + # comparator_cohort_id = comparatorCohortIds, + # start_day = startDays, + # end_day = endDays + #) %>% + # dplyr::tibble() + data <- dataSource$connectionHandler$queryDb( - sql = "SELECT cr.*, db.database_name + sql = "SELECT cr.* FROM @schema.@table_name cr - INNER JOIN @schema.@database_table db ON db.database_id = cr.database_id WHERE cr.cohort_id IN (@cohort_id) AND cr.database_id IN (@database_id) {@comparator_cohort_id != \"\"} ? { AND cr.comparator_cohort_id IN (@comparator_cohort_id)} @@ -272,13 +292,15 @@ getResultsCohortRelationships <- function(dataSource, schema = dataSource$schema, database_id = quoteLiterals(databaseIds), table_name = dataSource$prefixTable("cohort_relationships"), - database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), cohort_id = cohortIds, comparator_cohort_id = comparatorCohortIds, start_day = startDays, end_day = endDays ) %>% dplyr::tibble() + + # join with dbTable (moved this outside sql) + data <- merge(data, dataSource$dbTable, by = 'databaseId') return(data) } diff --git a/R/cohort-diagnostics-incidenceRates.R b/R/cohort-diagnostics-incidenceRates.R index d73b65f3..0f3d626e 100644 --- a/R/cohort-diagnostics-incidenceRates.R +++ b/R/cohort-diagnostics-incidenceRates.R @@ -115,9 +115,21 @@ getIncidenceRateResult <- function(dataSource, ) checkmate::reportAssertions(collection = errorMessage) - sql <- "SELECT ir.*, dt.database_name, cc.cohort_subjects + #sql <- "SELECT ir.*, dt.database_name, cc.cohort_subjects + # FROM @schema.@ir_table ir + # INNER JOIN @schema.@database_table dt ON ir.database_id = dt.database_id + # INNER JOIN @schema.@cc_table cc ON ( + # ir.database_id = cc.database_id AND ir.cohort_id = cc.cohort_id + # ) + # WHERE ir.cohort_id in (@cohort_ids) + # AND ir.database_id in (@database_ids) + # {@gender == TRUE} ? {AND ir.gender != ''} : { AND ir.gender = ''} + # {@age_group == TRUE} ? {AND ir.age_group != ''} : { AND ir.age_group = ''} + # {@calendar_year == TRUE} ? {AND ir.calendar_year != ''} : { AND ir.calendar_year = ''} + # AND ir.person_years > @personYears;" + + sql <- "SELECT ir.*, cc.cohort_subjects FROM @schema.@ir_table ir - INNER JOIN @schema.@database_table dt ON ir.database_id = dt.database_id INNER JOIN @schema.@cc_table cc ON ( ir.database_id = cc.database_id AND ir.cohort_id = cc.cohort_id ) @@ -127,6 +139,7 @@ getIncidenceRateResult <- function(dataSource, {@age_group == TRUE} ? {AND ir.age_group != ''} : { AND ir.age_group = ''} {@calendar_year == TRUE} ? {AND ir.calendar_year != ''} : { AND ir.calendar_year = ''} AND ir.person_years > @personYears;" + data <- dataSource$connectionHandler$queryDb( sql = sql, @@ -139,11 +152,20 @@ getIncidenceRateResult <- function(dataSource, personYears = minPersonYears, ir_table = dataSource$prefixTable("incidence_rate"), cc_table = dataSource$prefixTable("cohort_count"), - database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), + #database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), snakeCaseToCamelCase = TRUE ) %>% tidyr::tibble() - + + # join with dbTable (moved this outside sql) + data <- merge( + data, + dataSource$dbTable, + by = 'databaseId' + ) + + data <- tidyr::as_tibble(data) + data <- data %>% dplyr::mutate( gender = dplyr::na_if(.data$gender, ""), diff --git a/R/cohort-diagnostics-main.R b/R/cohort-diagnostics-main.R index 4f23187f..466aefd6 100644 --- a/R/cohort-diagnostics-main.R +++ b/R/cohort-diagnostics-main.R @@ -271,11 +271,21 @@ createCdDatabaseDataSource <- function( # SO much of the app requires this table in memory - it would be much better to re-write queries to not need it! getDatabaseTable <- function(dataSource) { + + # hot fix + if(tolower(paste0(dataSource$databaseTablePrefix, dataSource$databaseTable)) == 'database_meta_data'){ + databaseTable <- dataSource$connectionHandler$queryDb( + "SELECT *, cdm_source_abbreviation as database_name FROM @schema.@table_name", + schema = dataSource$schema, + table_name = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable) + ) # end hot fix + } else{ databaseTable <- loadResultsTable( dataSource = dataSource, tableName = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable), required = TRUE ) + } if (nrow(databaseTable) > 0 & "vocabularyVersion" %in% colnames(databaseTable)) { @@ -297,13 +307,23 @@ getCohortTable <- function(dataSource) { ) { return(data.frame()) } - cohortTable <- dataSource$connectionHandler$queryDb( - "SELECT cohort_id, cohort_name FROM @schema.@table_name", - schema = dataSource$schema, - table_name = paste0(dataSource$cgTablePrefix, dataSource$cgTable) - ) + # hot fix + if(paste0(dataSource$cgTablePrefix, dataSource$cgTable) == 'cg_cohort_definition'){ + cohortTable <- dataSource$connectionHandler$queryDb( + "SELECT cohort_definition_id as cohort_id, cohort_name FROM @schema.@table_name", + schema = dataSource$schema, + table_name = paste0(dataSource$cgTablePrefix, dataSource$cgTable) + ) + # end hot fix + } else{ + cohortTable <- dataSource$connectionHandler$queryDb( + "SELECT cohort_id, cohort_name FROM @schema.@table_name", + schema = dataSource$schema, + table_name = paste0(dataSource$cgTablePrefix, dataSource$cgTable) + ) + } - # Old label + # Old label - is this needed?? if ("cohortDefinitionId" %in% names(cohortTable)) { cohortTable <- cohortTable %>% dplyr::mutate(cohortId = .data$cohortDefinitionId) } diff --git a/R/cohort-diagnostics-shared.R b/R/cohort-diagnostics-shared.R index 30c5dea4..2be8a991 100644 --- a/R/cohort-diagnostics-shared.R +++ b/R/cohort-diagnostics-shared.R @@ -65,13 +65,21 @@ formatDataCellValueInDisplayTable <- getResultsCohortCounts <- function(dataSource, cohortIds = NULL, databaseIds = NULL) { - sql <- "SELECT cc.*, db.database_name + #sql <- "SELECT cc.*, db.database_name + # FROM @schema.@table_name cc + # INNER JOIN @schema.@database_table db ON db.database_id = cc.database_id + # WHERE cc.cohort_id IS NOT NULL + # {@use_database_ids} ? { AND cc.database_id in (@database_ids)} + # {@cohort_ids != ''} ? { AND cc.cohort_id in (@cohort_ids)} + # ;" + + sql <- "SELECT cc.* FROM @schema.@table_name cc - INNER JOIN @schema.@database_table db ON db.database_id = cc.database_id WHERE cc.cohort_id IS NOT NULL {@use_database_ids} ? { AND cc.database_id in (@database_ids)} {@cohort_ids != ''} ? { AND cc.cohort_id in (@cohort_ids)} ;" + data <- dataSource$connectionHandler$queryDb( sql = sql, @@ -79,10 +87,14 @@ getResultsCohortCounts <- function(dataSource, cohort_ids = cohortIds, use_database_ids = !is.null(databaseIds), database_ids = quoteLiterals(databaseIds), - table_name = dataSource$prefixTable("cohort_count"), - database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable) + table_name = dataSource$prefixTable("cohort_count")#, + #database_table = paste0(dataSource$databaseTablePrefix, dataSource$databaseTable) ) %>% tidyr::tibble() + + # join with dbTable (moved this outside sql) + data <- merge(data, dataSource$dbTable, by = 'databaseId') + return(data) } diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 25a3f849..7ede8354 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -249,4 +249,6 @@ withr::defer({ connectionHandlerCohortDiag$finalize() connectionHandlerSccs$finalize() connectionHandlerES$finalize() + connectionHandlerDS$finalize() + connectionHandlerPV$finalize() }, testthat::teardown_env()) \ No newline at end of file diff --git a/tests/testthat/test-phevaluator-main.R b/tests/testthat/test-phevaluator-main.R new file mode 100644 index 00000000..a41bce6c --- /dev/null +++ b/tests/testthat/test-phevaluator-main.R @@ -0,0 +1,47 @@ +context("phevaluator-main") + +shiny::testServer(phevaluatorServer, args = list( + id = "phevaluatorServer", + connectionHandler = connectionHandlerPV, + resultDatabaseSettings = resultDatabaseSettingsPV +), { + #set inputs + session$setInputs( + phenotypes = c("hyperprolactinemia", "interstitialLungDisease"), + databaseIds = c("CCAE_RS", "Amb EMR"), + generate = T + ) + + #make sure the selection options are stored in an accesible df + checkmate::expect_data_frame(optionCols) + + #make sure there is at least one selection for each input option + checkmate::expect_character(unique(optionCols$databaseId), min.len = 1) + checkmate::expect_character(unique(optionCols$phenotype), min.len = 1) + + #make sure all extracted data are accessible dfs + checkmate::expect_data_frame(dataAlgorithmPerformance()) + checkmate::expect_data_frame(dataCohortDefinitionSet()) + checkmate::expect_data_frame(dataDiagnostics()) + checkmate::expect_data_frame(dataEvalInputParams()) + checkmate::expect_data_frame(dataModelCovars()) + checkmate::expect_data_frame(dataModelInputParams()) + checkmate::expect_data_frame(dataModelPerformance()) + checkmate::expect_data_frame(dataTestSubjects()) + checkmate::expect_data_frame(dataTestSubjectsCovars()) + + #check that customColDefs are a list or that they are ser to null (no custom col defs specified) + testthat::expect_true(class(customColDefs) == 'list' | is.null(customColDefs)) + + #make sure all output tables work + # output$algorithmPerformanceResultsTable + # output$cohortDefinitionSetTable + # output$diagnosticsTable + # output$evaluationInputParametersTable + # output$modelCovariatesTable + # output$modelInputParametersTable + # output$modelPerformanceTable + # output$testSubjectsTable + # output$testSubjectsCovariatesTable + +}) \ No newline at end of file From 3bb7416d1abd2bba246530ca8f63f1770db0c268 Mon Sep 17 00:00:00 2001 From: jreps Date: Fri, 21 Jul 2023 10:46:55 -0400 Subject: [PATCH 18/20] updating version updating version --- DESCRIPTION | 2 +- NEWS.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 537a9ad1..0fc3c6b6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: OhdsiShinyModules Type: Package Title: Repository of Shiny Modules for OHDSI Result Viewers -Version: 1.1.0.9000 +Version: 1.2.0 Author: Jenna Reps Maintainer: Jenna Reps Description: Install this package to access useful shiny modules for building shiny apps to explore results using the OHDSI tools . diff --git a/NEWS.md b/NEWS.md index 929c0bc0..63041380 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,9 @@ +OhdsiShinyModules v1.2.0 +======================== +- updated all models to use the same resultDatabaseSettings +- made module function naming consistent (modules named after analysis packages) +- made table prefix inputs consistent across modules + OhdsiShinyModules v1.1.0 ======================== - Udated the style for Characterization From c82addd8dbacb1efed7c7f21e589e7679bee68de Mon Sep 17 00:00:00 2001 From: jreps Date: Fri, 4 Aug 2023 13:44:13 -0400 Subject: [PATCH 19/20] standardizing cohort method - standardizing cohort method --- NAMESPACE | 8 +- R/cohort-method-attrition.R | 237 +++++- R/cohort-method-covariateBalance.R | 142 ++-- R/cohort-method-diagnosticsSummary.R | 320 +++----- R/cohort-method-forestPlot.R | 114 --- R/cohort-method-full-result.R | 180 +++++ R/cohort-method-kaplainMeier.R | 88 ++- R/cohort-method-main.R | 459 +++++------ R/cohort-method-populationCharacteristics.R | 10 +- R/cohort-method-power.R | 174 ++++- R/cohort-method-propensityModel.R | 82 +- R/cohort-method-propensityScoreDistribution.R | 72 +- R/cohort-method-resultSummary.R | 358 +++++++++ R/cohort-method-resultsTable.R | 209 ----- R/cohort-method-subgroups.R | 138 ---- R/cohort-method-systematicError.R | 259 +++++-- R/components-data-viewer.R | 57 +- R/components.R | 3 +- R/evidence-synth-main.R | 160 ++-- R/helpers-cohort-methodDataPulls.R | 713 ------------------ R/helpers-cohort-methodPlotsAndTables.R | 621 --------------- R/helpers-getCohortMethodUtility.R | 26 - R/patient-level-prediction-covariateSummary.R | 3 +- R/patient-level-prediction-designSummary.R | 8 +- R/patient-level-prediction-diagnostics.R | 414 +++++----- R/patient-level-prediction-discrimination.R | 9 +- R/patient-level-prediction-main.R | 35 +- R/patient-level-prediction-modelSummary.R | 13 +- R/patient-level-prediction-settings.R | 30 +- R/sccs-diagnosticsSummary.R | 100 ++- .../main-diagnosticsSummaryHelp.html | 6 + man/cohortMethodAttritionServer.Rd | 3 - man/cohortMethodCovariateBalanceServer.Rd | 3 - man/cohortMethodDiagnosticsSummaryServer.Rd | 5 +- man/cohortMethodForestPlotServer.Rd | 34 - man/cohortMethodForestPlotViewer.Rd | 17 - man/cohortMethodKaplanMeierServer.Rd | 8 +- ...rtMethodPopulationCharacteristicsServer.Rd | 3 - man/cohortMethodPowerServer.Rd | 8 +- man/cohortMethodPropensityModelServer.Rd | 3 - man/cohortMethodPropensityScoreDistServer.Rd | 3 - man/cohortMethodResultSummaryServer.Rd | 28 + man/cohortMethodResultSummaryViewer.Rd | 17 + man/cohortMethodResultsTableServer.Rd | 28 - man/cohortMethodResultsTableViewer.Rd | 17 - man/cohortMethodSubgroupsServer.Rd | 34 - man/cohortMethodSubgroupsViewer.Rd | 17 - man/cohortMethodSystematicErrorServer.Rd | 8 +- .../test-cohort-method-CovariateBalance.R | 127 +++- .../test-cohort-method-DiagnosticsSummary.R | 83 +- .../testthat/test-cohort-method-ForestPlot.R | 21 - .../testthat/test-cohort-method-KaplanMeier.R | 24 +- ...-cohort-method-PopulationCharacteristics.R | 24 +- tests/testthat/test-cohort-method-Power.R | 56 +- .../test-cohort-method-PropensityScoreDist.R | 55 +- .../test-cohort-method-ResultsTable.R | 39 - tests/testthat/test-cohort-method-Subgroups.R | 53 -- tests/testthat/test-cohort-method-attrition.R | 24 +- .../testthat/test-cohort-method-full-result.R | 34 + tests/testthat/test-cohort-method-main.R | 12 +- .../test-cohort-method-propensityModel.R | 40 +- .../test-cohort-method-systematicError.R | 68 +- tests/testthat/test-evidence-synth-main.R | 20 +- ...test-helpers-cohort-methodPlotsAndTables.R | 190 ----- 64 files changed, 2579 insertions(+), 3575 deletions(-) delete mode 100644 R/cohort-method-forestPlot.R create mode 100644 R/cohort-method-full-result.R create mode 100644 R/cohort-method-resultSummary.R delete mode 100644 R/cohort-method-resultsTable.R delete mode 100644 R/cohort-method-subgroups.R delete mode 100644 R/helpers-cohort-methodDataPulls.R delete mode 100644 R/helpers-cohort-methodPlotsAndTables.R delete mode 100644 R/helpers-getCohortMethodUtility.R create mode 100644 inst/patient-level-prediction-www/main-diagnosticsSummaryHelp.html delete mode 100644 man/cohortMethodForestPlotServer.Rd delete mode 100644 man/cohortMethodForestPlotViewer.Rd create mode 100644 man/cohortMethodResultSummaryServer.Rd create mode 100644 man/cohortMethodResultSummaryViewer.Rd delete mode 100644 man/cohortMethodResultsTableServer.Rd delete mode 100644 man/cohortMethodResultsTableViewer.Rd delete mode 100644 man/cohortMethodSubgroupsServer.Rd delete mode 100644 man/cohortMethodSubgroupsViewer.Rd delete mode 100644 tests/testthat/test-cohort-method-ForestPlot.R delete mode 100644 tests/testthat/test-cohort-method-ResultsTable.R delete mode 100644 tests/testthat/test-cohort-method-Subgroups.R create mode 100644 tests/testthat/test-cohort-method-full-result.R delete mode 100644 tests/testthat/test-helpers-cohort-methodPlotsAndTables.R diff --git a/NAMESPACE b/NAMESPACE index 2506823a..c5afccfa 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -31,8 +31,6 @@ export(cohortMethodCovariateBalanceServer) export(cohortMethodCovariateBalanceViewer) export(cohortMethodDiagnosticsSummaryServer) export(cohortMethodDiagnosticsSummaryViewer) -export(cohortMethodForestPlotServer) -export(cohortMethodForestPlotViewer) export(cohortMethodHelperFile) export(cohortMethodKaplanMeierServer) export(cohortMethodKaplanMeierViewer) @@ -44,11 +42,9 @@ export(cohortMethodPropensityModelServer) export(cohortMethodPropensityModelViewer) export(cohortMethodPropensityScoreDistServer) export(cohortMethodPropensityScoreDistViewer) -export(cohortMethodResultsTableServer) -export(cohortMethodResultsTableViewer) +export(cohortMethodResultSummaryServer) +export(cohortMethodResultSummaryViewer) export(cohortMethodServer) -export(cohortMethodSubgroupsServer) -export(cohortMethodSubgroupsViewer) export(cohortMethodSystematicErrorServer) export(cohortMethodSystematicErrorViewer) export(cohortMethodViewer) diff --git a/R/cohort-method-attrition.R b/R/cohort-method-attrition.R index f2f71518..3112c019 100644 --- a/R/cohort-method-attrition.R +++ b/R/cohort-method-attrition.R @@ -26,7 +26,7 @@ #' @export cohortMethodAttritionViewer <- function(id) { ns <- shiny::NS(id) - + shiny::div( shiny::plotOutput(outputId = ns("attritionPlot"), width = 600, height = 600), shiny::uiOutput(outputId = ns("attritionPlotCaption")), @@ -42,7 +42,6 @@ cohortMethodAttritionViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database #' @param resultDatabaseSettings a list containing the result schema and prefixes #' @@ -53,45 +52,40 @@ cohortMethodAttritionViewer <- function(id) { cohortMethodAttritionServer <- function( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings ) { - + shiny::moduleServer( id, function(input, output, session) { - - attritionPlot <- shiny::reactive({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - attrition <- getCohortMethodAttrition( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - outcomeId = inputParams()$outcome, - databaseId = row$databaseId, - analysisId = row$analysisId - ) + attritionPlot <- shiny::reactive({ + attrition <- getCohortMethodAttrition( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + selectedRow = selectedRow + ) + if(!is.null(attrition)){ plot <- drawCohortMethodAttritionDiagram(attrition) return(plot) - } - }) - - output$attritionPlot <- shiny::renderPlot({ - return(attritionPlot()) + } else{ + return(NULL) + } }) - + + output$attritionPlot <- shiny::renderPlot({ + return(attritionPlot()) + }) + + output$downloadAttritionPlotPng <- shiny::downloadHandler(filename = "Attrition.png", contentType = "image/png", content = function(file) { ggplot2::ggsave(file, plot = attritionPlot(), width = 6, height = 7, dpi = 400) }) + output$downloadAttritionPlotPdf <- shiny::downloadHandler(filename = "Attrition.pdf", contentType = "application/pdf", content = function(file) { @@ -99,16 +93,203 @@ cohortMethodAttritionServer <- function( }) output$attritionPlotCaption <- shiny::renderUI({ - row <- selectedRow() - if (is.null(row)) { + if (is.null(selectedRow()$target)) { return(NULL) } else { text <- "Figure 1. Attrition diagram, showing the Number of subjects in the target (%s) and comparator (%s) group after various stages in the analysis." - return(shiny::HTML(sprintf(text, inputParams()$target, inputParams()$comparator))) + return(shiny::HTML(sprintf(text, selectedRow()$target, selectedRow()$comparator))) } }) } ) } + + +getCohortMethodAttrition <- function( + connectionHandler, + resultDatabaseSettings, + selectedRow +) { + + if(is.null(selectedRow()$targetId)){ + return(NULL) + } + + sql <- " + SELECT cmat.* + FROM + @schema.@cm_table_prefixattrition cmat + WHERE + cmat.target_id = @target_id + AND cmat.comparator_id = @comparator_id + AND cmat.outcome_id = @outcome_id + AND cmat.analysis_id = @analysis_id + AND cmat.database_id = '@database_id'; + " + result <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + #database_table = resultDatabaseSettings$databaseTable, + target_id = selectedRow()$targetId, + comparator_id = selectedRow()$comparatorId, + outcome_id = selectedRow()$outcomeId, + analysis_id = selectedRow()$analysisId, + database_id = selectedRow()$databaseId + ) + targetAttrition <- result[result$exposureId == selectedRow()$targetId, ] + comparatorAttrition <- result[result$exposureId == selectedRow()$comparatorId, ] + colnames(targetAttrition)[colnames(targetAttrition) == "subjects"] <- "targetPersons" + targetAttrition$exposureId <- NULL + colnames(comparatorAttrition)[colnames(comparatorAttrition) == "subjects"] <- "comparatorPersons" + comparatorAttrition$exposureId <- NULL + result <- merge(targetAttrition, comparatorAttrition) + result <- result[order(result$sequenceNumber), ] + + return(result) +} + + + + +drawCohortMethodAttritionDiagram <- function( + attrition, + targetLabel = "Target", + comparatorLabel = "Comparator" +) { + addStep <- function(data, attrition, row) { + label <- paste(strwrap(as.character(attrition$description[row]), width = 30), collapse = "\n") + data$leftBoxText[length(data$leftBoxText) + 1] <- label + data$rightBoxText[length(data$rightBoxText) + 1] <- paste(targetLabel, + ": n = ", + data$currentTarget - attrition$targetPersons[row], + "\n", + comparatorLabel, + ": n = ", + data$currentComparator - attrition$comparatorPersons[row], + sep = "") + data$currentTarget <- attrition$targetPersons[row] + data$currentComparator <- attrition$comparatorPersons[row] + return(data) + } + data <- list(leftBoxText = c(paste("Exposed:\n", + targetLabel, + ": n = ", + attrition$targetPersons[1], + "\n", + comparatorLabel, + ": n = ", + attrition$comparatorPersons[1], + sep = "")), rightBoxText = c(""), currentTarget = attrition$targetPersons[1], currentComparator = attrition$comparatorPersons[1]) + for (i in 2:nrow(attrition)) { + data <- addStep(data, attrition, i) + } + + + data$leftBoxText[length(data$leftBoxText) + 1] <- paste("Study population:\n", + targetLabel, + ": n = ", + data$currentTarget, + "\n", + comparatorLabel, + ": n = ", + data$currentComparator, + sep = "") + leftBoxText <- data$leftBoxText + rightBoxText <- data$rightBoxText + nSteps <- length(leftBoxText) + + boxHeight <- (1/nSteps) - 0.03 + boxWidth <- 0.45 + shadowOffset <- 0.01 + arrowLength <- 0.01 + x <- function(x) { + return(0.25 + ((x - 1)/2)) + } + y <- function(y) { + return(1 - (y - 0.5) * (1/nSteps)) + } + + downArrow <- function(p, x1, y1, x2, y2) { + p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x1, y = y1, xend = x2, yend = y2)) + p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x2, + y = y2, + xend = x2 + arrowLength, + yend = y2 + arrowLength)) + p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x2, + y = y2, + xend = x2 - arrowLength, + yend = y2 + arrowLength)) + return(p) + } + rightArrow <- function(p, x1, y1, x2, y2) { + p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x1, y = y1, xend = x2, yend = y2)) + p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x2, + y = y2, + xend = x2 - arrowLength, + yend = y2 + arrowLength)) + p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x2, + y = y2, + xend = x2 - arrowLength, + yend = y2 - arrowLength)) + return(p) + } + box <- function(p, x, y) { + p <- p + ggplot2::geom_rect(ggplot2::aes_string(xmin = x - (boxWidth/2) + shadowOffset, + ymin = y - (boxHeight/2) - shadowOffset, + xmax = x + (boxWidth/2) + shadowOffset, + ymax = y + (boxHeight/2) - shadowOffset), fill = grDevices::rgb(0, + 0, + 0, + alpha = 0.2)) + p <- p + ggplot2::geom_rect(ggplot2::aes_string(xmin = x - (boxWidth/2), + ymin = y - (boxHeight/2), + xmax = x + (boxWidth/2), + ymax = y + (boxHeight/2)), fill = grDevices::rgb(0.94, + 0.94, + 0.94), color = "black") + return(p) + } + label <- function(p, x, y, text, hjust = 0) { + p <- p + ggplot2::geom_text(ggplot2::aes_string(x = x, y = y, label = paste("\"", substring(text,1,40), "\"", + sep = "")), + hjust = hjust, + size = 3.7) + return(p) + } + + p <- ggplot2::ggplot() + for (i in 2:nSteps - 1) { + p <- downArrow(p, x(1), y(i) - (boxHeight/2), x(1), y(i + 1) + (boxHeight/2)) + p <- label(p, x(1) + 0.02, y(i + 0.5), "Y") + } + for (i in 2:(nSteps - 1)) { + p <- rightArrow(p, x(1) + boxWidth/2, y(i), x(2) - boxWidth/2, y(i)) + p <- label(p, x(1.5), y(i) - 0.02, "N", 0.5) + } + for (i in 1:nSteps) { + p <- box(p, x(1), y(i)) + } + for (i in 2:(nSteps - 1)) { + p <- box(p, x(2), y(i)) + } + for (i in 1:nSteps) { + p <- label(p, x(1) - boxWidth/2 + 0.02, y(i), text = leftBoxText[i]) + } + for (i in 2:(nSteps - 1)) { + p <- label(p, x(2) - boxWidth/2 + 0.02, y(i), text = rightBoxText[i]) + } + p <- p + ggplot2::theme(legend.position = "none", + plot.background = ggplot2::element_blank(), + panel.grid.major = ggplot2::element_blank(), + panel.grid.minor = ggplot2::element_blank(), + panel.border = ggplot2::element_blank(), + panel.background = ggplot2::element_blank(), + axis.text = ggplot2::element_blank(), + axis.title = ggplot2::element_blank(), + axis.ticks = ggplot2::element_blank()) + + return(p) +} diff --git a/R/cohort-method-covariateBalance.R b/R/cohort-method-covariateBalance.R index 808c25d0..1ecdfccf 100644 --- a/R/cohort-method-covariateBalance.R +++ b/R/cohort-method-covariateBalance.R @@ -30,33 +30,21 @@ cohortMethodCovariateBalanceViewer <- function(id) { ns <- shiny::NS(id) shiny::div( - shiny::conditionalPanel(condition = "output.isMetaAnalysis == false", - ns = ns, - shiny::uiOutput(outputId = ns("hoverInfoBalanceScatter")), - - plotly::plotlyOutput(ns("balancePlot")), - shiny::uiOutput(outputId = ns("balancePlotCaption")), - - shiny::downloadButton( - ns('downloadCovariateBalance'), - label = "Download" - ), - - shiny::textInput(ns("covariateHighlight"), "Highlight covariates containing:", ), - shiny::actionButton(ns("covariateHighlightButton"), "Highlight") - + shiny::uiOutput(outputId = ns("hoverInfoBalanceScatter")), + + plotly::plotlyOutput(ns("balancePlot")), + shiny::uiOutput(outputId = ns("balancePlotCaption")), + + shiny::downloadButton( + ns('downloadCovariateBalance'), + label = "Download" ), - shiny::conditionalPanel(condition = "output.isMetaAnalysis == true", - ns = ns, - shiny::plotOutput(outputId = ns("balanceSummaryPlot")), - shiny::uiOutput(outputId = ns("balanceSummaryPlotCaption")), - shiny::div(style = "display: inline-block;vertical-align: top;margin-bottom: 10px;", - shiny::downloadButton(outputId = ns("downloadBalanceSummaryPlotPng"), - label = "Download plot as PNG"), - shiny::downloadButton(outputId = ns("downloadBalanceSummaryPlotPdf"), - label = "Download plot as PDF") - )) + + shiny::textInput(ns("covariateHighlight"), "Highlight covariates containing:", ), + shiny::actionButton(ns("covariateHighlightButton"), "Highlight") + ) + } @@ -64,7 +52,6 @@ cohortMethodCovariateBalanceViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database #' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param metaAnalysisDbIds metaAnalysisDbIds @@ -76,7 +63,6 @@ cohortMethodCovariateBalanceViewer <- function(id) { cohortMethodCovariateBalanceServer <- function( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings, metaAnalysisDbIds = NULL) { @@ -88,12 +74,15 @@ cohortMethodCovariateBalanceServer <- function( balance <- shiny::reactive({ row <- selectedRow() + if(is.null(row$targetId)){ + return(NULL) + } balance <- tryCatch({ getCohortMethodCovariateBalanceShared( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, + targetId = row$targetId, + comparatorId = row$comparatorId, databaseId = row$databaseId, analysisId = row$analysisId)}, error = function(e){return(NULL)} @@ -101,16 +90,7 @@ cohortMethodCovariateBalanceServer <- function( return(balance) }) - output$isMetaAnalysis <- shiny::reactive({ - return(FALSE) - ##TODO: update once MA implemented - row <- selectedRow() - isMetaAnalysis <- !is.null(row) && (row$databaseId %in% metaAnalysisDbIds) - return(isMetaAnalysis) - }) - - shiny::outputOptions(output, "isMetaAnalysis", suspendWhenHidden = FALSE) - + textSearchCohortMethod <- shiny::reactiveVal(NULL) shiny::observeEvent( @@ -206,8 +186,8 @@ cohortMethodCovariateBalanceServer <- function( balanceSummary <- getCohortMethodCovariateBalanceSummary( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, + targetId = row$targetId, + comparatorId = row$comparatorId, analysisId = row$analysisId, databaseId = row$analysisId, beforeLabel = paste("Before", row$psStrategy), @@ -357,7 +337,7 @@ plotCohortMethodCovariateBalanceScatterPlotNew <- function( balance, beforeLabel = "Before propensity score adjustment", afterLabel = "After propensity score adjustment", - textsearch = NULL + textsearch = shiny::reactiveVal(NULL) ){ if(is.null(textsearch())){ @@ -406,3 +386,81 @@ plotCohortMethodCovariateBalanceScatterPlotNew <- function( return(plot) } + + + +plotCohortMethodCovariateBalanceSummary <- function(balanceSummary, + threshold = 0, + beforeLabel = "Before matching", + afterLabel = "After matching") { + balanceSummary <- balanceSummary[rev(order(balanceSummary$databaseId)), ] + dbs <- data.frame(databaseId = unique(balanceSummary$databaseId), + x = 1:length(unique(balanceSummary$databaseId))) + vizData <- merge(balanceSummary, dbs) + + vizData$type <- factor(vizData$type, levels = c(beforeLabel, afterLabel)) + + plot <- ggplot2::ggplot(vizData, ggplot2::aes(x = .data$x, + ymin = .data$ymin, + lower = .data$lower, + middle = .data$median, + upper = .data$upper, + ymax = .data$ymax, + group = .data$databaseId)) + + ggplot2::geom_errorbar(ggplot2::aes(ymin = .data$ymin, ymax = .data$ymin), size = 1) + + ggplot2::geom_errorbar(ggplot2::aes(ymin = .data$ymax, ymax = .data$ymax), size = 1) + + ggplot2::geom_boxplot(stat = "identity", fill = grDevices::rgb(0, 0, 0.8, alpha = 0.25), size = 1) + + ggplot2::geom_hline(yintercept = 0) + + ggplot2::scale_x_continuous(limits = c(0.5, max(vizData$x) + 1.75)) + + ggplot2::scale_y_continuous("Standardized difference of mean") + + ggplot2::coord_flip() + + ggplot2::facet_grid(~type) + + ggplot2::theme(panel.grid.major.y = ggplot2::element_blank(), + panel.grid.minor.y = ggplot2::element_blank(), + panel.grid.major.x = ggplot2::element_line(color = "#AAAAAA"), + panel.background = ggplot2::element_blank(), + axis.text.y = ggplot2::element_blank(), + axis.title.y = ggplot2::element_blank(), + axis.ticks.y = ggplot2::element_blank(), + axis.text.x = ggplot2::element_text(size = 11), + axis.title.x = ggplot2::element_text(size = 11), + axis.ticks.x = ggplot2::element_line(color = "#AAAAAA"), + strip.background = ggplot2::element_blank(), + strip.text = ggplot2::element_text(size = 11), + plot.margin = grid::unit(c(0,0,0.1,0), "lines")) + + if (threshold != 0) { + plot <- plot + ggplot2::geom_hline(yintercept = c(threshold, -threshold), linetype = "dotted") + } + after <- vizData[vizData$type == afterLabel, ] + after$max <- pmax(abs(after$ymin), abs(after$ymax)) + text <- data.frame(y = rep(c(after$x, nrow(after) + 1.25) , 3), + x = rep(c(1,2,3), each = nrow(after) + 1), + label = c(c(as.character(after$databaseId), + "Source", + formatC(after$covariateCount, big.mark = ",", format = "d"), + "Covariate\ncount", + formatC(after$max, digits = 2, format = "f"), + paste(afterLabel, "max(absolute)", sep = "\n"))), + dummy = "") + + data_table <- ggplot2::ggplot(text, ggplot2::aes(x = .data$x, y = .data$y, label = .data$label)) + + ggplot2::geom_text(size = 4, hjust=0, vjust=0.5) + + ggplot2::geom_hline(ggplot2::aes(yintercept=nrow(after) + 0.5)) + + ggplot2::theme(panel.grid.major = ggplot2::element_blank(), + panel.grid.minor = ggplot2::element_blank(), + legend.position = "none", + panel.border = ggplot2::element_blank(), + panel.background = ggplot2::element_blank(), + axis.text.x = ggplot2::element_text(colour="white"), + axis.text.y = ggplot2::element_blank(), + axis.ticks = ggplot2::element_line(colour="white"), + strip.background = ggplot2::element_blank(), + plot.margin = grid::unit(c(0,0,0.1,0), "lines")) + + ggplot2::labs(x="",y="") + + ggplot2::facet_grid(~dummy) + + ggplot2::coord_cartesian(xlim=c(1,4), ylim = c(0.5, max(vizData$x) + 1.75)) + + plot <- gridExtra::grid.arrange(data_table, plot, ncol = 2) + return(plot) +} diff --git a/R/cohort-method-diagnosticsSummary.R b/R/cohort-method-diagnosticsSummary.R index fd2cdd05..a7fa38bc 100644 --- a/R/cohort-method-diagnosticsSummary.R +++ b/R/cohort-method-diagnosticsSummary.R @@ -30,11 +30,15 @@ cohortMethodDiagnosticsSummaryViewer <- function(id) { shiny::div( - inputSelectionViewer(ns("input-selection")), + #shiny::conditionalPanel( + # condition = 'input.generate != 0', + # ns = shiny::NS(ns("input-selection")), - shiny::conditionalPanel( - condition = 'input.generate != 0', - ns = shiny::NS(ns("input-selection")), + shinydashboard::box( + status = 'info', + width = '100%', + title = shiny::span('Cohort Method Diagnostics'), + solidHeader = TRUE, shiny::tabsetPanel( type = 'pills', @@ -58,6 +62,7 @@ cohortMethodDiagnosticsSummaryViewer <- function(id) { #' @param id the unique reference id for the module #' @param connectionHandler the connection to the PLE results database #' @param resultDatabaseSettings a list containing the result schema and prefixes +#' @param inputSelected The target id, comparator id, outcome id and analysis id selected by the user #' #' @return #' the PLE diagnostics summary results @@ -66,133 +71,24 @@ cohortMethodDiagnosticsSummaryViewer <- function(id) { cohortMethodDiagnosticsSummaryServer <- function( id, connectionHandler, - resultDatabaseSettings + resultDatabaseSettings, + inputSelected ) { shiny::moduleServer( id, function(input, output, session) { - targetIds <- getCmDiagCohorts( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - type = 'target' - ) - outcomeIds <- getCmDiagCohorts( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - type = 'outcome' - ) - comparatorIds <- getCmDiagCohorts( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - type = 'comparator' - ) - analysisIds <- getCmDiagAnalyses( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - - inputSelected <- inputSelectionServer( - id = "input-selection", - inputSettingList = list( - createInputSetting( - rowNumber = 1, - columnWidth = 6, - varName = 'targetIds', - uiFunction = 'shinyWidgets::pickerInput', - uiInputs = list( - label = 'Target: ', - choices = targetIds, - selected = targetIds[1], - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ), - createInputSetting( - rowNumber = 1, - columnWidth = 6, - varName = 'outcomeIds', - uiFunction = 'shinyWidgets::pickerInput', - uiInputs = list( - label = 'Outcome: ', - choices = outcomeIds, - selected = outcomeIds[1], - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ), - createInputSetting( - rowNumber = 2, - columnWidth = 6, - varName = 'comparatorIds', - uiFunction = 'shinyWidgets::pickerInput', - uiInputs = list( - label = 'Comparator: ', - choices = comparatorIds, - selected = comparatorIds[1], - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ), - - createInputSetting( - rowNumber = 2, - columnWidth = 6, - varName = 'analysisIds', - uiFunction = 'shinyWidgets::pickerInput', - uiInputs = list( - label = 'Analysis: ', - choices = analysisIds, - selected = analysisIds[1], - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ) - ) - ) - data <- shiny::reactive({ getCmDiagnosticsData( connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetIds = inputSelected()$targetIds, - outcomeIds = inputSelected()$outcomeIds, - comparatorIds = inputSelected()$comparatorIds, - analysisIds = inputSelected()$analysisIds + inputSelected = inputSelected ) }) data2 <- shiny::reactive({ - diagnosticSummaryFormat(data) + diagnosticSummaryFormat(data) }) customColDefs <- list( @@ -206,13 +102,15 @@ cohortMethodDiagnosticsSummaryServer <- function( header = withTooltip( "Target", "The target cohort of interest " - ) + ), + minWidth = 300 ), comparator = reactable::colDef( header = withTooltip( "Comparator", "The comparator cohort of interest " - ) + ), + minWidth = 300 ), outcome = reactable::colDef( header = withTooltip( @@ -231,23 +129,47 @@ cohortMethodDiagnosticsSummaryServer <- function( header = withTooltip( "mdrr", "The minimum detectible relative risk" - ) + ), + format = reactable::colFormat(digits = 4) ), ease = reactable::colDef( header = withTooltip( "ease", "The ..." - ) + ), + format = reactable::colFormat(digits = 4) ), - timeTrendP = reactable::colDef( + maxSdm = reactable::colDef( header = withTooltip( - "timeTrendP", + "max SDM", "The ..." - ) + ), + format = reactable::colFormat(digits = 4) + ), + sharedMaxSdm = reactable::colDef( + header = withTooltip( + "shared max SDM", + "The ..." + ), + format = reactable::colFormat(digits = 4) + ), + equipoise = reactable::colDef( + header = withTooltip( + "equipoise", + "The ..." + ), + format = reactable::colFormat(digits = 4) + ), + attritionFraction = reactable::colDef( + header = withTooltip( + "Attrition fraction", + "The ..." + ), + format = reactable::colFormat(digits = 4) ), - preExposureP = reactable::colDef( + balanceDiagnostic = reactable::colDef( header = withTooltip( - "preExposureP", + "balanceDiagnostic", "The ..." ) ), @@ -263,15 +185,15 @@ cohortMethodDiagnosticsSummaryServer <- function( "The ..." ) ), - timeTrendDiagnostic = reactable::colDef( + attritionDiagnostic = reactable::colDef( header = withTooltip( - "timeTrendDiagnostic", + "attritionDiagnostic", "The ..." ) ), - preExposureDiagnostic = reactable::colDef( + equipoiseDiagnostic = reactable::colDef( header = withTooltip( - "preExposureDiagnostic", + "equipoiseDiagnostic", "The ..." ) ), @@ -281,7 +203,9 @@ cohortMethodDiagnosticsSummaryServer <- function( "unblind", "If the value is 1 then the diagnostics passed and results can be unblinded" ) - ) + ), + + summaryValue = reactable::colDef(show = F) ) @@ -291,7 +215,25 @@ cohortMethodDiagnosticsSummaryServer <- function( colDefsInput = customColDefs ) - customColDefs2 <- list( + resultTableServer( + id = "diagnosticsSummaryTable", + df = data2, + colDefsInput = getColDefsCmDiag( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + ) + + } + ) +} + +getColDefsCmDiag <- function( + connectionHandler, + resultDatabaseSettings +){ + + fixedColumns = list( databaseName = reactable::colDef( header = withTooltip( "Database", @@ -314,25 +256,18 @@ cohortMethodDiagnosticsSummaryServer <- function( sticky = "left" ) ) - - resultTableServer( - id = "diagnosticsSummaryTable", - df = data2, - colDefsInput = styleColumns(customColDefs2, outcomeIds, analysisIds) - ) - - - } - ) -} - -styleColumns <- function( - customColDefs, - outcomeIds, - analysisIds -){ + + outcomes <- getCmCohorts( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + type = 'outcome' + ) + analyses <- getCmAnalyses( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) - colnameFormat <- merge(names(outcomeIds), names(analysisIds)) + colnameFormat <- merge(unique(names(outcomes)), unique(names(analyses))) colnameFormat <- apply(colnameFormat, 1, function(x){paste(x, collapse = '_', sep = '_')}) styleList <- lapply( @@ -358,7 +293,7 @@ styleColumns <- function( } ) names(styleList) <- colnameFormat - result <- append(customColDefs, styleList) + result <- append(fixedColumns, styleList) return(result) } @@ -369,6 +304,10 @@ diagnosticSummaryFormat <- function( namesFrom = c('outcome','analysis') ){ + if(is.null(data())){ + return(NULL) + } + data2 <- tidyr::pivot_wider( data = data(), id_cols = idCols, @@ -379,80 +318,23 @@ diagnosticSummaryFormat <- function( return(data2) } -getCmDiagCohorts <- function( - connectionHandler, - resultDatabaseSettings, - type = 'target' -){ - - sql <- " - SELECT DISTINCT - cgcd1.cohort_name as names, - cgcd1.cohort_definition_id - FROM - @schema.@cm_table_prefixdiagnostics_summary cmds - INNER JOIN - @schema.@cg_table_prefixcohort_definition cgcd1 - ON cmds.@type_id = cgcd1.cohort_definition_id; - " - - result <- connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - cg_table_prefix = resultDatabaseSettings$cgTablePrefix, - type = type - ) - - res <- result$cohortDefinitionId - names(res) <- result$names - - return( - res - ) -} - -getCmDiagAnalyses <- function( - connectionHandler, - resultDatabaseSettings -){ - - sql <- " - SELECT DISTINCT - cma.analysis_id, - cma.description as names - FROM - @schema.@cm_table_prefixdiagnostics_summary cmds - INNER JOIN - @schema.@cm_table_prefixanalysis cma - ON cmds.analysis_id = cma.analysis_id - ; - " - - result <- connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix - ) - - res <- result$analysisId - names(res) <- result$names - - return( - res - ) - -} getCmDiagnosticsData <- function( connectionHandler, resultDatabaseSettings, - targetIds, - outcomeIds, - comparatorIds = NULL, - analysisIds = NULL + inputSelected ) { + + targetIds = inputSelected()$targetIds + outcomeIds = inputSelected()$outcomeIds + comparatorIds = inputSelected()$comparatorIds + analysisIds = inputSelected()$analysisIds + + if(is.null(targetIds) || is.null(outcomeIds)){ + return(NULL) + } + sql <- " SELECT DISTINCT dmd.cdm_source_abbreviation database_name, diff --git a/R/cohort-method-forestPlot.R b/R/cohort-method-forestPlot.R deleted file mode 100644 index 1caa5172..00000000 --- a/R/cohort-method-forestPlot.R +++ /dev/null @@ -1,114 +0,0 @@ -# @file cohort-method-forestPlot -# -# Copyright 2022 Observational Health Data Sciences and Informatics -# -# This file is part of OhdsiShinyModules -# -# 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. - -#' The module viewer for rendering the PLE results forest plot -#' -#' @param id the unique reference id for the module -#' -#' @return -#' The user interface to the cohort method forest plot -#' -#' @export -cohortMethodForestPlotViewer <- function(id) { - ns <- shiny::NS(id) - - shiny::div( - shiny::plotOutput(outputId = ns("forestPlot")), - shiny::uiOutput(outputId = ns("forestPlotCaption")), - shiny::div(style = "display: inline-block;vertical-align: top;margin-bottom: 10px;", - shiny::downloadButton(outputId = ns("downloadForestPlotPng"), - label = "Download plot as PNG"), - shiny::downloadButton(outputId = ns("downloadForestPlotPdf"), - label = "Download plot as PDF")) - ) -} - - - - - -#' The module server for rendering the PLE multiple results forest plot -#' -#' @param id the unique reference id for the module -#' @param connectionHandler connection -#' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest -#' @param metaAnalysisDbIds metaAnalysisDbIds -#' @param resultDatabaseSettings a list containing the result schema and prefixes -#' -#' @return -#' the PLE forest plot content server -#' -#' @export -cohortMethodForestPlotServer <- function( - id, connectionHandler, selectedRow, inputParams, metaAnalysisDbIds = NULL, - resultDatabaseSettings - ) { - - shiny::moduleServer( - id, - function(input, output, session) { - forestPlot <- shiny::reactive({ - row <- selectedRow() - if (is.null(row) || !(row$databaseId %in% metaAnalysisDbIds)) { - return(NULL) - } else { - results <- getCohortMethodMainResults( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - targetIds = row$targetId, - comparatorIds = row$comparatorId, - outcomeIds = row$outcomeId, - analysisIds = row$analysisId - ) - plot <- plotCohortMethodForest(results) - return(plot) - } - }) - - output$forestPlot <- shiny::renderPlot({ - forestPlot() - }) - - output$forestPlotCaption <- shiny::renderUI({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - text <- "Figure 6. Forest plot showing the per-database and summary hazard ratios (and 95 percent confidence - intervals) comparing %s to %s for the outcome of %s, using %s. Estimates are shown both before and after empirical - calibration. The I2 is computed on the uncalibrated estimates." - return(shiny::HTML(sprintf(text, inputParams()$target, inputParams()$comparator, inputParams()$outcome, row$psStrategy))) - } - }) - - output$downloadForestPlotPng <- shiny::downloadHandler(filename = "ForestPlot.png", - contentType = "image/png", - content = function(file) { - ggplot2::ggsave(file, plot = forestPlot(), width = 12, height = 9, dpi = 400) - }) - - output$downloadForestPlotPdf <- shiny::downloadHandler(filename = "ForestPlot.pdf", - contentType = "application/pdf", - content = function(file) { - ggplot2::ggsave(file = file, plot = forestPlot(), width = 12, height = 9) - }) - - } - ) -} diff --git a/R/cohort-method-full-result.R b/R/cohort-method-full-result.R new file mode 100644 index 00000000..7f4e5010 --- /dev/null +++ b/R/cohort-method-full-result.R @@ -0,0 +1,180 @@ +cohortMethodFullResultViewer <- function(id) { + ns <- shiny::NS(id) + + shinydashboard::box( + status = 'info', + width = '100%', + title = shiny::span('Result Explorer'), + solidHeader = TRUE, + + # add selected settings + shinydashboard::box( + status = 'warning', + width = "100%", + title = 'Selected: ', + collapsible = T, + shiny::uiOutput(ns('selection')) + ), + + shiny::tabsetPanel( + id = ns("fullTabsetPanel"), + type = 'pills', + shiny::tabPanel( + title = "Power", + cohortMethodPowerViewer(ns("power")) + ), + shiny::tabPanel( + title = "Attrition", + cohortMethodAttritionViewer(ns("attrition")) + ), + shiny::tabPanel( + title = "Population characteristics", + cohortMethodPopulationCharacteristicsViewer(ns("popCharacteristics")) + ), + shiny::tabPanel( + title = "Propensity model", + cohortMethodPropensityModelViewer(ns("propensityModel")) + ), + shiny::tabPanel( + title = "Propensity scores", + cohortMethodPropensityScoreDistViewer(ns("propensityScoreDist")) + ), + shiny::tabPanel( + title = "Covariate balance", + cohortMethodCovariateBalanceViewer(ns("covariateBalance")) + ), + shiny::tabPanel( + title = "Systematic error", + cohortMethodSystematicErrorViewer(ns("systematicError")) + ), + shiny::tabPanel( + title = "Kaplan-Meier", + cohortMethodKaplanMeierViewer(ns("kaplanMeier")) + ) + ) + ) + +} + +cohortMethodFullResultServer <- function( + id, + connectionHandler, + resultDatabaseSettings, + selectedRow +) { + + shiny::moduleServer( + id, + function(input, output, session) { + + output$selection <- shiny::renderUI({ + otext <- list() + otext[[1]] <- shiny::fluidRow( + shiny::column( + width = 6, + shiny::tags$b('Target: '), + selectedRow()$target + ), + shiny::column( + width = 6, + shiny::tags$b('Comparator: '), + selectedRow()$comparator + ) + ) + otext[[2]] <- shiny::fluidRow( + shiny::column( + width = 6, + shiny::tags$b('Outcome: '), + selectedRow()$outcome + ), + shiny::column( + width = 6, + shiny::tags$b('Analysis: '), + selectedRow()$description + ) + ) + otext[[3]] <- shiny::fluidRow( + shiny::column( + width = 3, + shiny::tags$b('Database: '), + selectedRow()$cdmSourceAbbreviation + ), + shiny::column( + width = 6, + shiny::tags$b('') + ) + ) + shiny::div(otext) + }) + + shiny::observeEvent(selectedRow(),{ + if(!is.null(selectedRow()$unblind)){ + if (selectedRow()$unblind == 1) { + shiny::showTab("fullTabsetPanel", "Kaplan-Meier", session = session) + } else{ + shiny::hideTab("fullTabsetPanel", "Kaplan-Meier", session = session) + } + } + }) + + # selected row: : - reactive list with: psStrategy + + cohortMethodPowerServer( + id = "power", + selectedRow = selectedRow, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + cohortMethodAttritionServer( + id = "attrition", + selectedRow = selectedRow, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + cohortMethodPopulationCharacteristicsServer( + id = "popCharacteristics", + selectedRow = selectedRow, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + cohortMethodPropensityModelServer( + id = "propensityModel", + selectedRow = selectedRow, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + cohortMethodPropensityScoreDistServer( + id = "propensityScoreDist", + selectedRow = selectedRow, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + cohortMethodCovariateBalanceServer( + id = "covariateBalance", + selectedRow = selectedRow, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + cohortMethodSystematicErrorServer( + id = "systematicError", + selectedRow = selectedRow, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + cohortMethodKaplanMeierServer( + id = "kaplanMeier", + selectedRow = selectedRow, + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + } + ) +} \ No newline at end of file diff --git a/R/cohort-method-kaplainMeier.R b/R/cohort-method-kaplainMeier.R index 8474f9c8..6eb75396 100644 --- a/R/cohort-method-kaplainMeier.R +++ b/R/cohort-method-kaplainMeier.R @@ -43,10 +43,8 @@ cohortMethodKaplanMeierViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database #' @param resultDatabaseSettings a list containing the result schema and prefixes -#' @param metaAnalysisDbIds metaAnalysisDbIds #' #' @return #' the PLE Kaplain Meier content server @@ -55,26 +53,15 @@ cohortMethodKaplanMeierViewer <- function(id) { cohortMethodKaplanMeierServer <- function( id, selectedRow, - inputParams, connectionHandler, - resultDatabaseSettings, - metaAnalysisDbIds = NULL + resultDatabaseSettings ) { shiny::moduleServer( id, function(input, output, session) { - output$isMetaAnalysis <- shiny::reactive({ - #TODO: update once MA implemented - return(FALSE) - row <- selectedRow() - isMetaAnalysis <- !is.null(row) && (row$databaseId %in% metaAnalysisDbIds) - return(isMetaAnalysis) - }) - - shiny::outputOptions(output, "isMetaAnalysis", suspendWhenHidden = FALSE) - + kaplanMeierPlot <- shiny::reactive({ row <- selectedRow() if (is.null(row)) { @@ -83,9 +70,9 @@ cohortMethodKaplanMeierServer <- function( km <- getCohortMethodKaplanMeier( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - outcomeId = inputParams()$outcome, + targetId = row$targetId, + comparatorId = row$comparatorId, + outcomeId = row$outcomeId, databaseId = row$databaseId, analysisId = row$analysisId ) @@ -95,21 +82,13 @@ cohortMethodKaplanMeierServer <- function( km$targetAtRisk[removeInd] <- NA km$comparatorAtRisk[removeInd] <- NA - targetName <- getCohortNameFromId( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - cohortId = inputParams()$target - ) - comparatorName <- getCohortNameFromId( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - cohortId = inputParams()$comparator - ) + targetName <- row$target + comparatorName <- row$comparator plot <- plotCohortMethodKaplanMeier( kaplanMeier = km, - targetName = targetName$cohortName, - comparatorName = comparatorName$cohortName + targetName = targetName, + comparatorName = comparatorName ) return(plot) } @@ -141,7 +120,7 @@ cohortMethodKaplanMeierServer <- function( comparator curve (%s) applies reweighting to approximate the counterfactual of what the target survival would look like had the target cohort been exposed to the comparator instead. The shaded area denotes the 95 percent confidence interval." - return(shiny::HTML(sprintf(text, inputParams()$target, inputParams()$comparator))) + return(shiny::HTML(sprintf(text, row$target, row$comparator))) } }) @@ -152,6 +131,43 @@ cohortMethodKaplanMeierServer <- function( +getCohortMethodKaplanMeier <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + outcomeId, + databaseId, + analysisId +) { + + sql <- " + SELECT + * + FROM + @schema.@cm_table_prefixkaplan_meier_dist cmkmd + WHERE + cmkmd.target_id = @target_id + AND cmkmd.comparator_id = @comparator_id + AND cmkmd.outcome_id = @outcome_id + AND cmkmd.analysis_id = @analysis_id + AND cmkmd.database_id = '@database_id'; + " + + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + #database_table = resultDatabaseSettings$databaseTable, + target_id = targetId, + comparator_id = comparatorId, + outcome_id = outcomeId, + analysis_id = analysisId, + database_id = databaseId + ) + ) +} # CohortMethod-kaplainMeier @@ -167,14 +183,14 @@ plotCohortMethodKaplanMeier <- function( s = kaplanMeier$targetSurvival, lower = kaplanMeier$targetSurvivalLb, upper = kaplanMeier$targetSurvivalUb, - strata = paste0(" ", targetName, " ") + strata = ' Target' #paste0(" ", targetName, " ") ), data.frame( time = kaplanMeier$time, s = kaplanMeier$comparatorSurvival, lower = kaplanMeier$comparatorSurvivalLb, upper = kaplanMeier$comparatorSurvivalUb, - strata = paste0(" ", comparatorName) + strata = ' Comparator'#paste0(" ", comparatorName) ) ) @@ -225,8 +241,8 @@ plotCohortMethodKaplanMeier <- function( x = c(0, xBreaks, xBreaks), y = as.factor( c("Number at risk", - rep(targetName, length(xBreaks)), - rep(comparatorName, length(xBreaks)) + rep('Target', length(xBreaks)), + rep('Comparator', length(xBreaks)) ) ), label = c( @@ -235,7 +251,7 @@ plotCohortMethodKaplanMeier <- function( formatC(comparatorAtRisk, big.mark = ",", mode = "integer") ) ) - labels$y <- factor(labels$y, levels = c(comparatorName, targetName, "Number at risk")) + labels$y <- factor(labels$y, levels = c('Comparator','Target', "Number at risk")) dataTable <- ggplot2::ggplot( data = labels, diff --git a/R/cohort-method-main.R b/R/cohort-method-main.R index 5ba555db..c555d062 100644 --- a/R/cohort-method-main.R +++ b/R/cohort-method-main.R @@ -48,96 +48,32 @@ cohortMethodViewer <- function(id) { title = shiny::span( shiny::icon("chart-column"), 'Cohort Method'), solidHeader = TRUE, - #shiny::fluidPage(style = "width:1500px;", - shinydashboard::box( - collapsible = TRUE, - collapsed = TRUE, - title = "Cohort Method Evidence Explorer", - width = "100%"#, - #shiny::htmlTemplate(system.file("cohort-diagnostics-www", "cohortCounts.html", package = utils::packageName())) - ), - - htmltools::tags$head(htmltools::tags$style(type = "text/css", " - #loadmessage { - position: fixed; - top: 0px; - left: 0px; - width: 100%; - padding: 5px 0px 5px 0px; - text-align: center; - font-weight: bold; - font-size: 100%; - color: #000000; - background-color: #ADD8E6; - z-index: 105; - } - ")), - shiny::conditionalPanel(id = ns("loadmessage"), - condition = "$('html').hasClass('shiny-busy')", - htmltools::tags$div("Processing...")), - shiny::tabsetPanel( - type = 'pills', - id = ns("mainTabsetPanel"), - shiny::tabPanel( - title = "Diagnostics", - cohortMethodDiagnosticsSummaryViewer(ns("estimationDiganostics")) - ), - shiny::tabPanel( - title = "Results", - shiny::fluidRow( - shiny::column(width = 3, - shiny::uiOutput(outputId = ns("targetWidget")), - shiny::uiOutput(outputId = ns("comparatorWidget")), - shiny::uiOutput(outputId = ns("outcomeWidget")), - shiny::uiOutput(outputId = ns("databaseWidget")), - shiny::uiOutput(outputId = ns("analysisWidget")) - ), - shiny::column(width = 9, - cohortMethodResultsTableViewer(ns("resultsTable")), - shiny::conditionalPanel("output.rowIsSelected == true", ns = ns, - shiny::tabsetPanel(id = ns("detailsTabsetPanel"), - shiny::tabPanel(title = "Power", - cohortMethodPowerViewer(ns("power")) - ), - shiny::tabPanel(title = "Attrition", - cohortMethodAttritionViewer(ns("attrition")) - ), - shiny::tabPanel(title = "Population characteristics", - cohortMethodPopulationCharacteristicsViewer(ns("popCharacteristics")) - ), - shiny::tabPanel(title = "Propensity model", - cohortMethodPropensityModelViewer(ns("propensityModel")) - ), - shiny::tabPanel(title = "Propensity scores", - cohortMethodPropensityScoreDistViewer(ns("propensityScoreDist")) - ), - shiny::tabPanel(title = "Covariate balance", - cohortMethodCovariateBalanceViewer(ns("covariateBalance")) - ), - shiny::tabPanel(title = "Systematic error", - cohortMethodSystematicErrorViewer(ns("systematicError")) - ), - shiny::tabPanel(title = "Forest plot", - cohortMethodForestPlotViewer(ns("forestPlot")) - ), - shiny::tabPanel(title = "Kaplan-Meier", - cohortMethodKaplanMeierViewer(ns("kaplanMeier")) - ), - shiny::tabPanel(title = "Subgroups", - cohortMethodSubgroupsViewer(ns("subgroups")) - ) - - ) # end tabsetPanel - ) # end conditionalPanel - ) - - ) - ) - ) + # Input selection of T, C and Os + inputSelectionViewer(ns("input-selection")), + + shiny::conditionalPanel( + condition = 'input.generate != 0', + ns = shiny::NS(ns("input-selection")), + + shiny::tabsetPanel( + type = 'pills', + id = ns('mainPanel'), + + shiny::tabPanel( + title = "Diagnostics", + cohortMethodDiagnosticsSummaryViewer(ns("cmDiganostics")) + ), + + shiny::tabPanel( + title = "Results", + cohortMethodResultSummaryViewer(ns("cmResults")) + ) + ) + ) + ) - } - + #' The module server for the main cohort method module #' @@ -161,203 +97,192 @@ cohortMethodServer <- function( dataFolder <- NULL - output$targetWidget <- shiny::renderUI({ - targets <- getCohortMethodTargetChoices( - connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - shiny::selectInput(inputId = session$ns("target"), - label = "Target", - choices = getCohortMethodSelectNamedChoices(targets$targetId, - targets$cohortName)) - }) - - output$comparatorWidget <- shiny::renderUI({ - comparators <- getCohortMethodComparatorChoices( - connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - shiny::selectInput(inputId = session$ns("comparator"), - label = "Comparator", - choices = getCohortMethodSelectNamedChoices(comparators$comparatorId, - comparators$cohortName)) - }) - - output$outcomeWidget <- shiny::renderUI({ - outcomes <- getCohortMethodOutcomeChoices( - connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - shiny::selectInput(inputId = session$ns("outcome"), - label = "Outcome", - choices = getCohortMethodSelectNamedChoices(outcomes$outcomeId, - outcomes$cohortName)) - }) - output$databaseWidget<- shiny::renderUI({ - databases <- getCohortMethodDatabaseChoices( - connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - shiny::checkboxGroupInput(inputId = session$ns("database"), - label = "Data source", - choices = getCohortMethodSelectNamedChoices(databases$databaseId, - databases$cdmSourceAbbreviation), - selected = unique(databases$databaseId)) - }) - output$analysisWidget <- shiny::renderUI({ - analyses <- getCmAnalysisOptions( - connectionHandler, - resultDatabaseSettings - ) - shiny::checkboxGroupInput(inputId = session$ns("analysis"), - label = "Analysis", - choices = getCohortMethodSelectNamedChoices(analyses$analysisId, - analyses$description), - selected = unique(analyses$analysisId)) - }) - - - inputParams <- shiny::reactive({ - t <- list() - t$target <- input$target - t$comparator <- input$comparator - t$outcome <- input$outcome - t$analysis <- input$analysis - t$database <- input$database - return(t) - }) - - - cohortMethodDiagnosticsSummaryServer( - id = "estimationDiganostics", - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - - - selectedRow <- cohortMethodResultsTableServer( - id = "resultsTable", - connectionHandler = connectionHandler, - inputParams = inputParams, - resultDatabaseSettings = resultDatabaseSettings - ) - - output$rowIsSelected <- shiny::reactive({ - return(!is.null(selectedRow())) - }) - - - if (!exists("cmInteractionResult")) { # ISSUE: this should be an input resultDatabaseSettings$cmInteractionResult and not null check - #TODO: update for testing once subgroup analysis completed - shiny::hideTab(inputId = "detailsTabsetPanel", target = "Subgroups", - session = session) - } - - shiny::outputOptions(output, "rowIsSelected", suspendWhenHidden = FALSE) - - output$isMetaAnalysis <- shiny::reactive({ - #TODO: update once MA implemented - row <- selectedRow() - isMetaAnalysis <- FALSE # !is.null(row) && (row$databaseId %in% metaAnalysisDbIds) - if (!is.null(row)) { - if (isMetaAnalysis) { - shiny::hideTab("detailsTabsetPanel", "Attrition", session = session) - shiny::hideTab("detailsTabsetPanel", "Population characteristics", session = session) - shiny::hideTab("detailsTabsetPanel", "Kaplan-Meier", session = session) - shiny::hideTab("detailsTabsetPanel", "Propensity model", session = session) - shiny::showTab("detailsTabsetPanel", "Forest plot", session = session) - } else { - shiny::showTab("detailsTabsetPanel", "Attrition", session = session) - shiny::showTab("detailsTabsetPanel", "Population characteristics", session = session) - if (row$unblind) { - shiny::showTab("detailsTabsetPanel", "Kaplan-Meier", session = session) - } else{ - shiny::hideTab("detailsTabsetPanel", "Kaplan-Meier", session = session) - } - shiny::showTab("detailsTabsetPanel", "Propensity model", session = session) - shiny::hideTab("detailsTabsetPanel", "Forest plot", session = session) - } - } - return(isMetaAnalysis) - }) - shiny::outputOptions(output, "isMetaAnalysis", suspendWhenHidden = FALSE) - - - cohortMethodPowerServer( - id = "power", - selectedRow = selectedRow, - inputParams = inputParams, + targetIds <- getCmCohorts( connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings + resultDatabaseSettings = resultDatabaseSettings, + type = 'target' ) - - cohortMethodAttritionServer( - id = "attrition", - selectedRow = selectedRow, - inputParams = inputParams, + outcomeIds <- getCmCohorts( connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings + resultDatabaseSettings = resultDatabaseSettings, + type = 'outcome' ) - - cohortMethodPopulationCharacteristicsServer( - id = "popCharacteristics", - selectedRow = selectedRow, - inputParams = inputParams, + comparatorIds <- getCmCohorts( connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings + resultDatabaseSettings = resultDatabaseSettings, + type = 'comparator' ) - - cohortMethodPropensityModelServer( - id = "propensityModel", - selectedRow = selectedRow, - inputParams = inputParams, + analysisIds <- getCmAnalyses( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings ) - cohortMethodPropensityScoreDistServer( - id = "propensityScoreDist", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings + inputSelected <- inputSelectionServer( + id = "input-selection", + inputSettingList = list( + createInputSetting( + rowNumber = 1, + columnWidth = 6, + varName = 'targetIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Target: ', + choices = targetIds, + selected = targetIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + createInputSetting( + rowNumber = 1, + columnWidth = 6, + varName = 'outcomeIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Outcome: ', + choices = outcomeIds, + selected = outcomeIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + createInputSetting( + rowNumber = 2, + columnWidth = 6, + varName = 'comparatorIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Comparator: ', + choices = comparatorIds, + selected = comparatorIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ), + + createInputSetting( + rowNumber = 2, + columnWidth = 6, + varName = 'analysisIds', + uiFunction = 'shinyWidgets::pickerInput', + uiInputs = list( + label = 'Analysis: ', + choices = analysisIds, + selected = analysisIds[1], + multiple = T, + options = shinyWidgets::pickerOptions( + actionsBox = TRUE, + liveSearch = TRUE, + size = 10, + liveSearchStyle = "contains", + liveSearchPlaceholder = "Type here to search", + virtualScroll = 50 + ) + ) + ) + ) ) - cohortMethodCovariateBalanceServer( - id = "covariateBalance", - selectedRow = selectedRow, - inputParams = inputParams, + cohortMethodDiagnosticsSummaryServer( + id = "cmDiganostics", connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) + resultDatabaseSettings = resultDatabaseSettings, + inputSelected = inputSelected + ) - cohortMethodSystematicErrorServer( - id = "systematicError", - selectedRow = selectedRow, - inputParams = inputParams, + cohortMethodResultSummaryServer( + id = "cmResults", connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - - cohortMethodKaplanMeierServer( - id = "kaplanMeier", - selectedRow = selectedRow, - inputParams = inputParams, - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - - #TODO: complete once MA implemented - # estimationForestPlotServer("forestPlot", connection, selectedRow, inputParams) - - #TODO: revisit once subgroup example conducted - cohortMethodSubgroupsServer( - id = "subgroups", - selectedRow = selectedRow, - inputParams = inputParams + resultDatabaseSettings = resultDatabaseSettings, + inputSelected = inputSelected ) } ) } +getCmCohorts <- function( + connectionHandler, + resultDatabaseSettings, + type = 'target' +){ + + sql <- " + SELECT DISTINCT + cgcd1.cohort_name as names, + cgcd1.cohort_definition_id + FROM + @schema.@cm_table_prefixresult cmds + INNER JOIN + @schema.@cg_table_prefixcohort_definition cgcd1 + ON cmds.@type_id = cgcd1.cohort_definition_id; + " + + result <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + type = type + ) + + res <- result$cohortDefinitionId + names(res) <- result$names + + return( + res + ) +} + +getCmAnalyses <- function( + connectionHandler, + resultDatabaseSettings +){ + + sql <- " + SELECT DISTINCT + cma.analysis_id, + cma.description as names + FROM + @schema.@cm_table_prefixresult cmds + INNER JOIN + @schema.@cm_table_prefixanalysis cma + ON cmds.analysis_id = cma.analysis_id + ; + " + + result <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix + ) + + res <- result$analysisId + names(res) <- result$names + + return( + res + ) + +} diff --git a/R/cohort-method-populationCharacteristics.R b/R/cohort-method-populationCharacteristics.R index 56cfd621..0951d3bd 100644 --- a/R/cohort-method-populationCharacteristics.R +++ b/R/cohort-method-populationCharacteristics.R @@ -39,7 +39,6 @@ cohortMethodPopulationCharacteristicsViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database #' @param resultDatabaseSettings a list containing the result schema and prefixes #' @@ -50,7 +49,6 @@ cohortMethodPopulationCharacteristicsViewer <- function(id) { cohortMethodPopulationCharacteristicsServer <- function( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings ) { @@ -68,7 +66,7 @@ cohortMethodPopulationCharacteristicsServer <- function( text <- "Table 2. Select characteristics before and after propensity score adjustment, showing the (weighted) percentage of subjects with the characteristics in the target (%s) and comparator (%s) group, as well as the standardized difference of the means." - return(shiny::HTML(sprintf(text, inputParams()$target, inputParams()$comparator))) + return(shiny::HTML(sprintf(text, row$target, row$comparator))) } }) @@ -80,9 +78,9 @@ cohortMethodPopulationCharacteristicsServer <- function( balance <- getCohortMethodPopChar( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - outcomeId = inputParams()$outcome, + targetId = row$targetId, + comparatorId = row$comparatorId, + outcomeId = row$outcomeId, databaseId = row$databaseId, analysisId = row$analysisId ) diff --git a/R/cohort-method-power.R b/R/cohort-method-power.R index f68dabf3..3f9626ae 100644 --- a/R/cohort-method-power.R +++ b/R/cohort-method-power.R @@ -42,10 +42,8 @@ cohortMethodPowerViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database #' @param resultDatabaseSettings a list containing the result schema and prefixes -#' @param metaAnalysisDbIds metaAnalysisDbIds #' #' @return #' the PLE systematic error power server @@ -54,10 +52,8 @@ cohortMethodPowerViewer <- function(id) { cohortMethodPowerServer <- function( id, selectedRow, - inputParams, connectionHandler, - resultDatabaseSettings, - metaAnalysisDbIds = NULL + resultDatabaseSettings ) { shiny::moduleServer( @@ -67,12 +63,12 @@ cohortMethodPowerServer <- function( output$powerTableCaption <- shiny::renderUI({ row <- selectedRow() - if (!is.null(row)) { + if (!is.null(row$target)) { text <- "Table 1a. Number of subjects, follow-up time (in years), number of outcome events, and event incidence rate (IR) per 1,000 patient years (PY) in the target (%s) and comparator (%s) group after propensity score adjustment, as well as the minimum detectable relative risk (MDRR). Note that the IR does not account for any stratification." - return(shiny::HTML(sprintf(text, inputParams()$target, inputParams()$comparator))) + return(shiny::HTML(sprintf(text, row$target, row$comparator))) } else { return(NULL) } @@ -80,7 +76,7 @@ cohortMethodPowerServer <- function( output$powerTable <- shiny::renderTable({ row <- selectedRow() - if (is.null(row)) { + if (is.null(row$target)) { return(NULL) } else { table <- prepareCohortMethodPowerTable( @@ -88,8 +84,6 @@ cohortMethodPowerServer <- function( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings ) - table$description <- NULL - table$databaseId <- NULL if (!row$unblind) { table$targetOutcomes <- NA table$comparatorOutcomes <- NA @@ -112,11 +106,11 @@ cohortMethodPowerServer <- function( output$timeAtRiskTableCaption <- shiny::renderUI({ row <- selectedRow() - if (!is.null(row)) { + if (!is.null(row$target)) { text <- "Table 1b. Time (days) at risk distribution expressed as minimum (min), 25th percentile (P25), median, 75th percentile (P75), and maximum (max) in the target (%s) and comparator (%s) cohort after propensity score adjustment." - return(shiny::HTML(sprintf(text, inputParams()$target, inputParams()$comparator))) + return(shiny::HTML(sprintf(text, row$target, row$comparator))) } else { return(NULL) } @@ -124,15 +118,15 @@ cohortMethodPowerServer <- function( output$timeAtRiskTable <- shiny::renderTable({ row <- selectedRow() - if (is.null(row)) { + if (is.null(row$target)) { return(NULL) } else { followUpDist <- getCmFollowUpDist( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, - outcomeId = inputParams()$outcome, + targetId = row$targetId, + comparatorId = row$comparatorId, + outcomeId = row$outcomeId, databaseId = row$databaseId, analysisId = row$analysisId ) @@ -143,3 +137,151 @@ cohortMethodPowerServer <- function( }) }) } + + +prepareCohortMethodFollowUpDistTable <- function(followUpDist) { + targetRow <- data.frame(Database = followUpDist$databaseId, + Cohort = "Target", + Min = followUpDist$targetMinDays, + P10 = followUpDist$targetP10Days, + P25 = followUpDist$targetP25Days, + Median = followUpDist$targetMedianDays, + P75 = followUpDist$targetP75Days, + P90 = followUpDist$targetP90Days, + Max = followUpDist$targetMaxDays) + comparatorRow <- data.frame(Database = followUpDist$databaseId, + Cohort = "Comparator", + Min = followUpDist$comparatorMinDays, + P10 = followUpDist$comparatorP10Days, + P25 = followUpDist$comparatorP25Days, + Median = followUpDist$comparatorMedianDays, + P75 = followUpDist$comparatorP75Days, + P90 = followUpDist$comparatorP90Days, + Max = followUpDist$comparatorMaxDays) + table <- rbind(targetRow, comparatorRow) + table$Min <- formatC(table$Min, big.mark = ",", format = "d") + table$P10 <- formatC(table$P10, big.mark = ",", format = "d") + table$P25 <- formatC(table$P25, big.mark = ",", format = "d") + table$Median <- formatC(table$Median, big.mark = ",", format = "d") + table$P75 <- formatC(table$P75, big.mark = ",", format = "d") + table$P90 <- formatC(table$P90, big.mark = ",", format = "d") + table$Max <- formatC(table$Max, big.mark = ",", format = "d") + if (length(unique(followUpDist$databaseId)) == 1) + table$Database <- NULL + return(table) +} + + +prepareCohortMethodPowerTable <- function( + mainResults, + connectionHandler , + resultDatabaseSettings +) { + #analyses <- getCohortMethodAnalyses( + # connectionHandler = connectionHandler, + # resultDatabaseSettings = resultDatabaseSettings + #) + #table <- merge(mainResults, analyses) + table <- mainResults + alpha <- 0.05 + power <- 0.8 + z1MinAlpha <- stats::qnorm(1 - alpha/2) + zBeta <- -stats::qnorm(1 - power) + pA <- table$targetSubjects/(table$targetSubjects + table$comparatorSubjects) + pB <- 1 - pA + totalEvents <- abs(table$targetOutcomes) + abs(table$comparatorOutcomes) + table$mdrr <- exp(sqrt((zBeta + z1MinAlpha)^2/(totalEvents * pA * pB))) + table$targetYears <- table$targetDays/365.25 + table$comparatorYears <- table$comparatorDays/365.25 + table$targetIr <- 1000 * table$targetOutcomes/table$targetYears + table$comparatorIr <- 1000 * table$comparatorOutcomes/table$comparatorYears + table <- table[, c("targetSubjects", + "comparatorSubjects", + "targetYears", + "comparatorYears", + "targetOutcomes", + "comparatorOutcomes", + "targetIr", + "comparatorIr", + "mdrr"), drop = F] + table$targetSubjects <- formatC(table$targetSubjects, big.mark = ",", format = "d") + table$comparatorSubjects <- formatC(table$comparatorSubjects, big.mark = ",", format = "d") + table$targetYears <- formatC(table$targetYears, big.mark = ",", format = "d") + table$comparatorYears <- formatC(table$comparatorYears, big.mark = ",", format = "d") + table$targetOutcomes <- formatC(table$targetOutcomes, big.mark = ",", format = "d") + table$comparatorOutcomes <- formatC(table$comparatorOutcomes, big.mark = ",", format = "d") + table$targetIr <- sprintf("%.2f", table$targetIr) + table$comparatorIr <- sprintf("%.2f", table$comparatorIr) + table$mdrr <- sprintf("%.2f", table$mdrr) + table$targetSubjects <- gsub("^-", "<", table$targetSubjects) + table$comparatorSubjects <- gsub("^-", "<", table$comparatorSubjects) + table$targetOutcomes <- gsub("^-", "<", table$targetOutcomes) + table$comparatorOutcomes <- gsub("^-", "<", table$comparatorOutcomes) + table$targetIr <- gsub("^-", "<", table$targetIr) + table$comparatorIr <- gsub("^-", "<", table$comparatorIr) + idx <- (table$targetOutcomes < 0 | table$comparatorOutcomes < 0) + table$mdrr[idx] <- paste0(">", table$mdrr[idx]) + return(table) +} + + +getCohortMethodAnalyses <- function( + connectionHandler, + resultDatabaseSettings +) { + sql <- " + SELECT + cma.* + FROM + @schema.@cm_table_prefixanalysis cma + " + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix + ) + ) +} + +getCmFollowUpDist <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + outcomeId, + databaseId = NULL, + analysisId +) { + + if(is.null(targetId)){ + return(NULL) + } + + sql <- " + SELECT + * + FROM + @schema.@cm_table_prefixfollow_up_dist cmfud + WHERE + cmfud.target_id = @target_id + AND cmfud.comparator_id = @comparator_id + AND cmfud.outcome_id = @outcome_id + AND cmfud.analysis_id = @analysis_id + " + if(!is.null(databaseId)) { + sql <- paste(sql, paste("AND cmfud.database_id = '@database_id'"), collapse = "\n") + } + return( + connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + target_id = targetId, + comparator_id = comparatorId, + outcome_id = outcomeId, + analysis_id = analysisId, + database_id = databaseId + ) + ) +} diff --git a/R/cohort-method-propensityModel.R b/R/cohort-method-propensityModel.R index 343a9829..5ce5f48a 100644 --- a/R/cohort-method-propensityModel.R +++ b/R/cohort-method-propensityModel.R @@ -39,7 +39,6 @@ cohortMethodPropensityModelViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database #' @param resultDatabaseSettings a list containing the result schema and prefixes #' @@ -50,7 +49,6 @@ cohortMethodPropensityModelViewer <- function(id) { cohortMethodPropensityModelServer <- function( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings ) { @@ -67,8 +65,8 @@ cohortMethodPropensityModelServer <- function( model <- getCohortMethodPropensityModel( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, + targetId = row$targetId, + comparatorId = row$comparatorId, databaseId = row$databaseId, analysisId = row$analysisId ) @@ -94,3 +92,79 @@ cohortMethodPropensityModelServer <- function( } ) } + + +getCohortMethodPropensityModel <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + analysisId, + databaseId +) { + sqlTmp <- " + SELECT + cmpm.coefficient, + cmc.covariate_id, + cmc.covariate_name + FROM + @schema.@cm_table_prefixcovariate cmc + JOIN @schema.@cm_table_prefixpropensity_model cmpm + ON cmc.covariate_id = cmpm.covariate_id + AND cmc.database_id = cmpm.database_id + WHERE + cmpm.target_id = @target_id + AND cmpm.comparator_id = @comparator_id + AND cmpm.analysis_id = @analysis_id + AND cmpm.database_id = '@database_id' + " + + sql <- " + SELECT + cmc.covariate_id, + cmc.covariate_name, + cmpm.coefficient + FROM + ( + SELECT + covariate_id, + covariate_name + FROM + @schema.@cm_table_prefixcovariate + WHERE + analysis_id = @analysis_id + AND database_id = '@database_id' + UNION + SELECT + 0 as covariate_id, + 'intercept' as covariate_name) cmc + JOIN @schema.@cm_table_prefixpropensity_model cmpm + ON cmc.covariate_id = cmpm.covariate_id + WHERE + cmpm.target_id = @target_id + AND cmpm.comparator_id = @comparator_id + AND cmpm.analysis_id = @analysis_id + AND cmpm.database_id = '@database_id' + " + + model <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + target_id = targetId, + comparator_id = comparatorId, + analysis_id = analysisId, + database_id = databaseId + ) + return(model) +} + +prepareCohortMethodPropensityModelTable <- function(model) { + rnd <- function(x) { + ifelse(x > 10, sprintf("%.1f", x), sprintf("%.2f", x)) + } + table <- model[order(-abs(model$coefficient)), c("coefficient", "covariateName")] + table$coefficient <- sprintf("%.2f", table$coefficient) + colnames(table) <- c("Beta", "Covariate") + return(table) +} \ No newline at end of file diff --git a/R/cohort-method-propensityScoreDistribution.R b/R/cohort-method-propensityScoreDistribution.R index ce0e596b..0ece5fd5 100644 --- a/R/cohort-method-propensityScoreDistribution.R +++ b/R/cohort-method-propensityScoreDistribution.R @@ -46,7 +46,6 @@ cohortMethodPropensityScoreDistViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection to the PLE results database #' @param resultDatabaseSettings a list containing the result schema and prefixes #' @param metaAnalysisDbIds metaAnalysisDbIds @@ -58,7 +57,6 @@ cohortMethodPropensityScoreDistViewer <- function(id) { cohortMethodPropensityScoreDistServer <- function( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings, metaAnalysisDbIds = F @@ -70,14 +68,14 @@ cohortMethodPropensityScoreDistServer <- function( psDistPlot <- shiny::reactive({ row <- selectedRow() - if (is.null(row)) { + if (is.null(row$targetId)) { return(NULL) } else { ps <- getCohortMethodPs( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, + targetId = row$targetId, + comparatorId = row$comparatorId, analysisId = row$analysisId, databaseId = row$databaseId ) @@ -86,17 +84,11 @@ cohortMethodPropensityScoreDistServer <- function( return(NULL) #TODO: handle more gracefully } - targetName <- getCohortNameFromId( - connectionHandler = connectionHandler , - resultDatabaseSettings = resultDatabaseSettings, - cohortId = inputParams()$target - ) - comparatorName <- getCohortNameFromId( - connectionHandler = connectionHandler , - resultDatabaseSettings = resultDatabaseSettings, - cohortId = inputParams()$comparator - ) - plot <- plotCohortMethodPs(ps, targetName$cohortName, comparatorName$cohortName) + targetName <- row$target + + comparatorName <- row$comparator + + plot <- plotCohortMethodPs(ps, targetName, comparatorName) return(plot) } }) @@ -121,11 +113,55 @@ cohortMethodPropensityScoreDistServer <- function( ) } - +getCohortMethodPs <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + analysisId, + databaseId = NULL +) { + if(is.null(targetId)){ + return(NULL) + } + sql <- " + SELECT + * + FROM + @schema.@cm_table_prefixpreference_score_dist cmpsd + WHERE + cmpsd.target_id = @target_id + AND cmpsd.comparator_id = @comparator_id + AND cmpsd.analysis_id = @analysis_id + " + if(!is.null(databaseId)) { + sql <- paste(sql, paste("AND cmpsd.database_id = '@database_id'"), collapse = "\n") + } + + + ps <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + target_id = targetId, + comparator_id = comparatorId, + analysis_id = analysisId, + database_id = databaseId + ) + + + if (!is.null(databaseId)) { + ps$databaseId <- NULL + } + return(ps) +} # CohortMethod-propensityScoreDist plotCohortMethodPs <- function(ps, targetName, comparatorName) { - if (is.null(ps$databaseId)) { + if(is.null(ps$preferenceScore)){ + return(NULL) + } + if(is.null(ps$databaseId)) { ps <- rbind(data.frame(x = ps$preferenceScore, y = ps$targetDensity, group = targetName), data.frame(x = ps$preferenceScore, y = ps$comparatorDensity, group = comparatorName)) diff --git a/R/cohort-method-resultSummary.R b/R/cohort-method-resultSummary.R new file mode 100644 index 00000000..e90981b1 --- /dev/null +++ b/R/cohort-method-resultSummary.R @@ -0,0 +1,358 @@ +# @file cohort-method-resultSummary +# +# Copyright 2022 Observational Health Data Sciences and Informatics +# +# This file is part of OhdsiShinyModules +# +# 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. + + +#' The module viewer for rendering the cohort method results +#' +#' @param id the unique reference id for the module +#' +#' @return +#' The user interface to the cohort method diagnostics viewer +#' +#' @export +cohortMethodResultSummaryViewer <- function(id) { + ns <- shiny::NS(id) + + shiny::tabsetPanel( + type = 'hidden', + id = ns('resultPanel'), + + shiny::tabPanel( + title = "Table", + shinydashboard::box( + status = 'info', + width = '100%', + title = shiny::span('Result Summary'), + solidHeader = TRUE, + resultTableViewer(ns("resultSummaryTable")) + ) + ), + + shiny::tabPanel( + title = "Results", + shiny::actionButton( + inputId = ns('goBackCmResults'), + label = "Back To Result Summary", + shiny::icon("arrow-left"), + style="color: #fff; background-color: #337ab7; border-color: #2e6da4" + ), + cohortMethodFullResultViewer(ns("cmFullResults")) + ) + + ) + + +} + + +#' The module server for rendering the PLE diagnostics summary +#' +#' @param id the unique reference id for the module +#' @param connectionHandler the connection to the PLE results database +#' @param resultDatabaseSettings a list containing the result schema and prefixes +#' @param inputSelected The target id, comparator id, outcome id and analysis id selected by the user +#' +#' @return +#' the PLE diagnostics summary results +#' +#' @export +cohortMethodResultSummaryServer <- function( + id, + connectionHandler, + resultDatabaseSettings, + inputSelected +) { + + shiny::moduleServer( + id, + function(input, output, session) { + + shiny::observeEvent( + eventExpr = input$goBackCmResults, + { + shiny::updateTabsetPanel(session, "resultPanel", selected = "Table") + }) + + data <- shiny::reactive({ + getCmResultData( + connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + inputSelected = inputSelected + ) + }) + + resultTableOutputs <- resultTableServer( + id = "resultSummaryTable", + df = data, + colDefsInput = getCmResultSummaryTableColDef(), + addActions = c('results') + ) + + selectedRow <- shiny::reactiveVal(value = NULL) + shiny::observeEvent(resultTableOutputs$actionCount(), { + if(resultTableOutputs$actionType() == 'results'){ + selectedRow(data()[resultTableOutputs$actionIndex()$index,]) + shiny::updateTabsetPanel(session, "resultPanel", selected = "Results") + } + }) + + cohortMethodFullResultServer( + id = "cmFullResults", + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + selectedRow = selectedRow + ) + + } + ) +} + + +getCmResultSummaryTableColDef <- function(){ + result <- list( + + analysisId = reactable::colDef(show = F), + description = reactable::colDef( + header = withTooltip( + "Analysis", + "The analysis description" + ), + minWidth = 140 + ), + databaseId = reactable::colDef(show = F), + + cdmSourceAbbreviation = reactable::colDef( + header = withTooltip( + "Database", + "The database name" + ) + ), + + targetId = reactable::colDef(show = F), + target = reactable::colDef( + header = withTooltip( + "Target", + "The target cohort of interest" + ), + minWidth = 300 + ), + + comparatorId = reactable::colDef(show = F), + comparator = reactable::colDef( + header = withTooltip( + "Comparator", + "The comparator cohort of interest" + ), + minWidth = 300 + ), + + outcomeId = reactable::colDef(show = F), + outcome = reactable::colDef( + header = withTooltip( + "Outcome", + "The outcome of interest" + ), + minWidth = 300 + ), + + rr = reactable::colDef( + header = withTooltip( + "rr", + "The uncalibrated relative risk" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + + ci95Lb = reactable::colDef( + header = withTooltip( + "ci95Lb", + "The lower bound of the 95% confidence internval of the uncalibrated relative risk" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + + ci95Ub = reactable::colDef( + header = withTooltip( + "ci95Ub", + "The upper bound of the 95% confidence internval of the uncalibrated relative risk" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + + p = reactable::colDef( + header = withTooltip( + "p", + "The p value of the uncalibrated relative risk" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + + calibratedRr = reactable::colDef( + header = withTooltip( + "calibrated rr", + "The calibrated relative risk" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + + calibratedCi95Lb = reactable::colDef( + header = withTooltip( + "calibrated ci95Lb", + "The lower bound of the 95% confidence internval of the calibrated relative risk" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + + calibratedCi95Ub = reactable::colDef( + header = withTooltip( + "calibrated ci95Ub", + "The upper bound of the 95% confidence internval of the calibrated relative risk" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + + calibratedP = reactable::colDef( + header = withTooltip( + "calibrated p", + "The p value of the calibrated relative risk" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + + logRr = reactable::colDef(show = F), + seLogRr = reactable::colDef(show = F), + targetSubjects = reactable::colDef(show = F), + comparatorSubjects = reactable::colDef(show = F), + targetDays = reactable::colDef(show = F), + comparatorDays = reactable::colDef(show = F), + targetOutcomes = reactable::colDef(show = F), + comparatorOutcomes = reactable::colDef(show = F), + calibratedLogRr = reactable::colDef(show = F), + calibratedSeLogRr = reactable::colDef(show = F), + calibratedSeLogRr = reactable::colDef(show = F), + unblind = reactable::colDef(show = F) + ) + + return(result) +} + +getCmResultData <- function( + connectionHandler, + resultDatabaseSettings, + inputSelected +) { + + targetIds = inputSelected()$targetIds + outcomeIds = inputSelected()$outcomeIds + comparatorIds = inputSelected()$comparatorIds + analysisIds = inputSelected()$analysisIds + + if(is.null(comparatorIds) || is.null(targetIds) || is.null(outcomeIds) || is.null(analysisIds)){ + return(NULL) + } + + sql <- " + SELECT + cma.analysis_id, + cma.description description, + dmd.database_id database_id, + dmd.cdm_source_abbreviation cdm_source_abbreviation, + cmr.target_id, + cg1.cohort_name as target, + cmr.outcome_id, + cg2.cohort_name as outcome, + cmr.comparator_id, + cg3.cohort_name as comparator, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.rr end rr, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.ci_95_lb end ci_95_lb, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.ci_95_ub end ci_95_ub, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.p end p, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.log_rr end log_rr, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.se_log_rr end se_log_rr, + cmr.target_subjects, + cmr.comparator_subjects, + cmr.target_days, + cmr.comparator_days, + cmr.target_outcomes, + cmr.comparator_outcomes, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.calibrated_rr end calibrated_rr, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.calibrated_ci_95_lb end calibrated_ci_95_lb, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.calibrated_ci_95_ub end calibrated_ci_95_ub, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.calibrated_p end calibrated_p, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.calibrated_log_rr end calibrated_log_rr, + case when COALESCE(cmds.unblind, 0) = 0 then NULL else cmr.calibrated_se_log_rr end calibrated_se_log_rr, + COALESCE(cmds.unblind, 0) unblind +FROM + @schema.@cm_table_prefixanalysis cma + JOIN @schema.@cm_table_prefixresult cmr + on cmr.analysis_id = cma.analysis_id + + JOIN @schema.@database_table dmd + on dmd.database_id = cmr.database_id + + LEFT JOIN @schema.@cm_table_prefixdiagnostics_summary cmds + on cmds.analysis_id = cmr.analysis_id + AND cmds.target_id = cmr.target_id + AND cmds.comparator_id = cmr.comparator_id + AND cmds.outcome_id = cmr.outcome_id + AND cmds.database_id = cmr.database_id + + inner join @schema.@cg_table_prefixcohort_definition cg1 + on cg1.cohort_definition_id = cmr.target_id + + inner join @schema.@cg_table_prefixcohort_definition cg2 + on cg2.cohort_definition_id = cmr.outcome_id + + inner join @schema.@cg_table_prefixcohort_definition cg3 + on cg3.cohort_definition_id = cmr.comparator_id + + where cmr.target_id in (@targets) + {@use_comparators}?{and cmr.comparator_id in (@comparators)} + and cmr.outcome_id in (@outcomes) + {@use_analyses}?{and cmr.analysis_id in (@analyses)} + ; + " + + result <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + + targets = paste0(targetIds, collapse = ','), + comparators = paste0(comparatorIds, collapse = ','), + outcomes = paste0(outcomeIds, collapse = ','), + analyses = paste0(analysisIds, collapse = ','), + + use_comparators = !is.null(comparatorIds), + use_analyses = !is.null(analysisIds) + ) + + return( + result + ) +} \ No newline at end of file diff --git a/R/cohort-method-resultsTable.R b/R/cohort-method-resultsTable.R deleted file mode 100644 index 1b27fa17..00000000 --- a/R/cohort-method-resultsTable.R +++ /dev/null @@ -1,209 +0,0 @@ -# @file cohort-method-resultsTable -# -# Copyright 2022 Observational Health Data Sciences and Informatics -# -# This file is part of OhdsiShinyModules -# -# 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. - - - - - -#' The module viewer for rendering the PLE main results -#' -#' @param id the unique reference id for the module -#' -#' @return -#' The user interface to the PLE main results -#' -#' @export -cohortMethodResultsTableViewer <- function(id) { - ns <- shiny::NS(id) - - reactable::reactableOutput(outputId = ns("mainTable")) -} - - - - -#' The module server for rendering the PLE results per current selections -#' -#' @param id the unique reference id for the module -#' @param connectionHandler the connection to the PLE results database -#' @param inputParams the selected study parameters of interest -#' @param resultDatabaseSettings a list containing the result schema and prefixes -#' -#' @return -#' the PLE main results table server server -#' -#' @export -cohortMethodResultsTableServer <- function( - id, - connectionHandler, - inputParams, - resultDatabaseSettings - ) { - - shiny::moduleServer( - id, - function(input, output, session) { - - withTooltip <- function(value, tooltip, ...) { - shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", - tippy::tippy(value, tooltip, ...)) - } - - - mainColumns <- c("description", - "cdmSourceAbbreviation", - "rr", - "ci95Lb", - "ci95Ub", - "p", - "calibratedRr", - "calibratedCi95Lb", - "calibratedCi95Ub", - "calibratedP") - - resultSubset <- shiny::reactive({ - - results <- getCohortMethodMainResults( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - targetIds = filterCohortMethodEmptyNullValues(inputParams()$target), - comparatorIds = filterCohortMethodEmptyNullValues(inputParams()$comparator), - outcomeIds = filterCohortMethodEmptyNullValues(inputParams()$outcome), - databaseIds = filterCohortMethodEmptyNullValues(inputParams()$database), - analysisIds = filterCohortMethodEmptyNullValues(inputParams()$analysis) - ) - results <- results[order(results$analysisId), ] - - - results[which(results$unblind == 0), getCohortMethodColumnsToBlind(results)] <- NA - - return(results) - }) - - selectedRow <- shiny::reactive({ - idx <- reactable::getReactableState( - outputId = 'mainTable', - name = 'selected' - ) - if (is.null(idx)) { - return(NULL) - } else { - subset <- resultSubset() - if (nrow(subset) == 0) { - return(NULL) - } - row <- subset[idx, ] - # row$psStrategy <- gsub("^PS ", "", gsub(", .*$", "", cohortMethodAnalysis$description[cohortMethodAnalysis$analysisId == row$analysisId])) - return(row) - } - }) - - output$mainTable <- reactable::renderReactable({ - table <- resultSubset() - if (is.null(table) || nrow(table) == 0) { - shiny::validate(shiny::need(nrow(table) > 0, "No CM results for selections.")) - return(NULL) - } - table <- table[, mainColumns] - table$rr <- prettyCohortMethodHr(table$rr) - table$ci95Lb <- prettyCohortMethodHr(table$ci95Lb) - table$ci95Ub <- prettyCohortMethodHr(table$ci95Ub) - table$p <- prettyCohortMethodHr(table$p) - table$calibratedRr <- prettyCohortMethodHr(table$calibratedRr) - table$calibratedCi95Lb <- prettyCohortMethodHr(table$calibratedCi95Lb) - table$calibratedCi95Ub <- prettyCohortMethodHr(table$calibratedCi95Ub) - table$calibratedP <- prettyCohortMethodHr(table$calibratedP) - #colnames(table) <- mainColumnNames - - reactable::reactable( # add extras - data = table, - rownames = FALSE, - defaultPageSize = 15, - showPageSizeOptions = T, - onClick = 'select', - selection = 'single', - striped = T, - - columns = list( - description = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Analysis", - "Analysis" - )), - cdmSourceAbbreviation = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Data source", - "Data source" - )), - rr = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "HR", - "Hazard ratio (uncalibrated)" - )), - ci95Lb = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "LB", - "Lower bound of the 95 percent confidence interval (uncalibrated)" - )), - ci95Ub = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "UB", - "Upper bound of the 95 percent confidence interval (uncalibrated)" - )), - p = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "P", - "Two-sided p-value (uncalibrated)" - )), - calibratedRr = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Cal.HR", - "Hazard ratio (calibrated)" - )), - calibratedCi95Lb = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Cal.LB", - "Lower bound of the 95 percent confidence interval (calibrated)" - )), - calibratedCi95Ub = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Cal.UB", - "Upper bound of the 95 percent confidence interval (calibrated)" - )), - calibratedP = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Cal.P", - "Two-sided p-value (calibrated)" - )) - ) - ) - }) - - return(selectedRow) - }) -} diff --git a/R/cohort-method-subgroups.R b/R/cohort-method-subgroups.R deleted file mode 100644 index 1c6d3269..00000000 --- a/R/cohort-method-subgroups.R +++ /dev/null @@ -1,138 +0,0 @@ -# @file cohort-method-subgroups -# -# Copyright 2022 Observational Health Data Sciences and Informatics -# -# This file is part of OhdsiShinyModules -# -# 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. - - -#' The module viewer for rendering the PLE subgroup results -#' -#' @param id the unique reference id for the module -#' -#' @return -#' The user interface to the cohort method subgroup results module -#' -#' @export -cohortMethodSubgroupsViewer <- function(id) { - ns <- shiny::NS(id) - - shiny::div( - shiny::uiOutput(outputId = ns("subgroupTableCaption")), - DT::dataTableOutput(outputId = ns("subgroupTable")) - ) -} - - -#' The module server for rendering the subgroup results -#' -#' @param id the unique reference id for the module -#' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest -#' @param exposureOfInterest exposureOfInterest -#' @param outcomeOfInterest outcomeOfInterest -#' @param connectionHandler connection -#' -#' @return -#' the PLE subgroup results server -#' -#' @export -cohortMethodSubgroupsServer <- function( - id, - selectedRow, - inputParams, - exposureOfInterest, - outcomeOfInterest, - connectionHandler - ) { - - shiny::moduleServer( - id, - function(input, output, session) { - - interactionEffects <- shiny::reactive({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - targetId <- exposureOfInterest$exposureId[exposureOfInterest$exposureName == inputParams()$target] - comparatorId <- exposureOfInterest$exposureId[exposureOfInterest$exposureName == inputParams()$comparator] - outcomeId <- outcomeOfInterest$outcomeId[outcomeOfInterest$outcomeName == inputParams()$outcome] - subgroupResults <- getCohortMethodSubgroupResults(connectionHandler = connectionHandler, - targetIds = targetId, - comparatorIds = comparatorId, - outcomeIds = outcomeId, - databaseIds = row$databaseId, - analysisIds = row$analysisId) - if (nrow(subgroupResults) == 0) { - return(NULL) - } else { - if (!row$unblind) { - subgroupResults$rrr <- rep(NA, nrow(subgroupResults)) - subgroupResults$ci95Lb <- rep(NA, nrow(subgroupResults)) - subgroupResults$ci95Ub <- rep(NA, nrow(subgroupResults)) - subgroupResults$logRrr <- rep(NA, nrow(subgroupResults)) - subgroupResults$seLogRrr <- rep(NA, nrow(subgroupResults)) - subgroupResults$p <- rep(NA, nrow(subgroupResults)) - subgroupResults$calibratedP <- rep(NA, nrow(subgroupResults)) - } - return(subgroupResults) - } - } - }) - - output$subgroupTableCaption <- shiny::renderUI({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - text <- "Table 4. Subgroup interactions. For each subgroup, the number of subject within the subroup - in the target (%s) and comparator (%s) cohorts are provided, as well as the hazard ratio ratio (HRR) - with 95 percent confidence interval and p-value (uncalibrated and calibrated) for interaction of the main effect with - the subgroup." - return(shiny::HTML(sprintf(text, inputParams()$target, inputParams()$comparator))) - } - }) - - output$subgroupTable <- DT::renderDataTable({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - subgroupResults <- interactionEffects() - if (is.null(subgroupResults)) { - return(NULL) - } - subgroupTable <- prepareCohortMethodSubgroupTable(subgroupResults, output = "html") - colnames(subgroupTable) <- c("Subgroup", - "Target subjects", - "Comparator subjects", - "HRR", - "P", - "Cal.P") - options <- list(searching = FALSE, - ordering = FALSE, - paging = FALSE, - bInfo = FALSE) - subgroupTable <- DT::datatable(subgroupTable, - options = options, - rownames = FALSE, - escape = FALSE, - class = "stripe nowrap compact") - return(subgroupTable) - } - }) - } - ) -} diff --git a/R/cohort-method-systematicError.R b/R/cohort-method-systematicError.R index c2a2af16..1c826246 100644 --- a/R/cohort-method-systematicError.R +++ b/R/cohort-method-systematicError.R @@ -36,20 +36,12 @@ cohortMethodSystematicErrorViewer <- function(id) { estimator should have the true effect size within the 95 percent confidence interval 95 percent of times."), shiny::div(style = "display: inline-block;vertical-align: top;margin-bottom: 10px;", shiny::downloadButton(outputId = ns("downloadSystematicErrorPlotPng"), - label = "Download plot as PNG"), + label = "Download plot as PNG"), shiny::downloadButton(outputId = ns("downloadSystematicErrorPlotPdf"), - label = "Download plot as PDF") - ), - shiny::conditionalPanel(condition = "output.isMetaAnalysis == true", - ns = ns, - shiny::plotOutput(outputId = ns("systematicErrorSummaryPlot")), - shiny::div(shiny::strong("Figure 8."),"Fitted null distributions per data source."), - shiny::div(style = "display: inline-block;vertical-align: top;margin-bottom: 10px;", - shiny::downloadButton(outputId = ns("downloadSystematicErrorSummaryPlotPng"), - label = "Download plot as PNG"), - shiny::downloadButton(outputId = ns("downloadSystematicErrorSummaryPlotPdf"), - label = "Download plot as PDF"))) - ) + label = "Download plot as PDF") + ) + ) + } @@ -58,10 +50,8 @@ cohortMethodSystematicErrorViewer <- function(id) { #' #' @param id the unique reference id for the module #' @param selectedRow the selected row from the main results table -#' @param inputParams the selected study parameters of interest #' @param connectionHandler the connection handler to the result databases #' @param resultDatabaseSettings a list containing the result schema and prefixes -#' @param metaAnalysisDbIds metaAnalysisDbIds #' #' @return #' the PLE systematic error content server @@ -70,28 +60,14 @@ cohortMethodSystematicErrorViewer <- function(id) { cohortMethodSystematicErrorServer <- function( id, selectedRow, - inputParams, connectionHandler, - resultDatabaseSettings, - metaAnalysisDbIds = NULL + resultDatabaseSettings ) { shiny::moduleServer( id, function(input, output, session) { - output$isMetaAnalysis <- shiny::reactive({ - return(FALSE) - # TODO: update once MA implemented - row <- selectedRow() - isMetaAnalysis <- !is.null(row) && (row$databaseId %in% metaAnalysisDbIds) - return(isMetaAnalysis) - }) - - shiny::outputOptions(output, "isMetaAnalysis", suspendWhenHidden = FALSE) - - - systematicErrorPlot <- shiny::reactive({ row <- selectedRow() if (is.null(row)) { @@ -100,8 +76,8 @@ cohortMethodSystematicErrorServer <- function( controlResults <- getCohortMethodControlResults( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputParams()$target, - comparatorId = inputParams()$comparator, + targetId = row$targetId, + comparatorId = row$comparatorId, analysisId = row$analysisId, databaseId = row$databaseId ) @@ -123,54 +99,183 @@ cohortMethodSystematicErrorServer <- function( return(systematicErrorPlot()) }) - output$downloadSystematicErrorPlotPng <- shiny::downloadHandler(filename = "SystematicError.png", - contentType = "image/png", - content = function(file) { - ggplot2::ggsave(file, plot = systematicErrorPlot(), width = 12, height = 5.5, dpi = 400) - }) - - output$downloadSystematicErrorPlotPdf <- shiny::downloadHandler(filename = "SystematicError.pdf", - contentType = "application/pdf", - content = function(file) { - ggplot2::ggsave(file = file, plot = systematicErrorPlot(), width = 12, height = 5.5) - }) - - systematicErrorSummaryPlot <- shiny::reactive({ - row <- selectedRow() - if (is.null(row) || !(row$databaseId %in% metaAnalysisDbIds)) { - return(NULL) - } else { - ##negativeControls <- getCohortMethodNegativeControlEstimates(connection = connection, - ## #resultsSchema = resultsSchema, unused argument - ## targetId = inputParams()$target, - ## comparatorId = inputParams()$comparator, - ## analysisId = row$analysisId) - ##if (is.null(negativeControls)) - return(NULL) - - ## plotCohortMethodEmpiricalNulls() not found - #plot <- plotCohortMethodEmpiricalNulls(negativeControls) - ##return(plot) + output$downloadSystematicErrorPlotPng <- shiny::downloadHandler( + filename = "SystematicError.png", + contentType = "image/png", + content = function(file) { + ggplot2::ggsave(file, plot = systematicErrorPlot(), width = 12, height = 5.5, dpi = 400) } - }) - - output$systematicErrorSummaryPlot <- shiny::renderPlot({ - return(systematicErrorSummaryPlot()) - }, res = 100) - - output$downloadSystematicErrorSummaryPlotPng <- shiny::downloadHandler(filename = "SystematicErrorSummary.png", - contentType = "image/png", - content = function(file) { - ggplot2::ggsave(file, plot = systematicErrorSummaryPlot(), width = 12, height = 5.5, dpi = 400) - }) - - output$downloadSystematicErrorSummaryPlotPdf <- shiny::downloadHandler(filename = "SystematicErrorSummary.pdf", - contentType = "application/pdf", - content = function(file) { - ggplot2::ggsave(file = file, plot = systematicErrorSummaryPlot(), width = 12, height = 5.5) - }) + ) + output$downloadSystematicErrorPlotPdf <- shiny::downloadHandler( + filename = "SystematicError.pdf", + contentType = "application/pdf", + content = function(file) { + ggplot2::ggsave(file = file, plot = systematicErrorPlot(), width = 12, height = 5.5) + } + ) } ) } + + +getCohortMethodControlResults <- function( + connectionHandler, + resultDatabaseSettings, + targetId, + comparatorId, + analysisId, + databaseId = NULL, + includePositiveControls = TRUE, + emptyAsNa = TRUE +) { + + sql <- " + SELECT + cmr.*, + cmtco.true_effect_size effect_size + FROM + @schema.@cm_table_prefixresult cmr + JOIN @schema.@cm_table_prefixtarget_comparator_outcome cmtco + ON cmr.target_id = cmtco.target_id AND cmr.comparator_id = cmtco.comparator_id AND cmr.outcome_id = cmtco.outcome_id + WHERE + cmtco.outcome_of_interest != 1 + AND cmr.target_id = @target_id + AND cmr.comparator_id = @comparator_id + AND cmr.analysis_id = @analysis_id + " + + + if (!is.null(databaseId)) { + # update sql + sql <- paste(sql, paste("AND cmr.database_id = '@database_id'"), collapse = "\n") + } + + if (!includePositiveControls) { + # update sql + sql <- paste(sql, paste("AND cmtco.true_effect_size = 1"), collapse = "\n") + } + + results <- connectionHandler$queryDb( + sql = sql, + schema = resultDatabaseSettings$schema, + cm_table_prefix = resultDatabaseSettings$cmTablePrefix, + target_id = targetId, + comparator_id = comparatorId, + analysis_id = analysisId, + database_id = databaseId + ) + + if (emptyAsNa) { + results[results == ''] <- NA + } + + return(results) +} + + +plotCohortMethodScatter <- function(controlResults) { + + if(nrow(controlResults)==0){ + return(NULL) + } + + size <- 2 + labelY <- 0.7 + d <- rbind(data.frame(yGroup = "Uncalibrated", + logRr = controlResults$logRr, + seLogRr = controlResults$seLogRr, + ci95Lb = controlResults$ci95Lb, + ci95Ub = controlResults$ci95Ub, + trueRr = controlResults$effectSize), + data.frame(yGroup = "Calibrated", + logRr = controlResults$calibratedLogRr, + seLogRr = controlResults$calibratedSeLogRr, + ci95Lb = controlResults$calibratedCi95Lb, + ci95Ub = controlResults$calibratedCi95Ub, + trueRr = controlResults$effectSize)) + d <- d[!is.na(d$logRr), ] + d <- d[!is.na(d$ci95Lb), ] + d <- d[!is.na(d$ci95Ub), ] + if (nrow(d) == 0) { + return(NULL) + } + d$Group <- as.factor(d$trueRr) + d$Significant <- d$ci95Lb > d$trueRr | d$ci95Ub < d$trueRr + temp1 <- stats::aggregate(Significant ~ Group + yGroup, data = d, length) + temp2 <- stats::aggregate(Significant ~ Group + yGroup, data = d, mean) + temp1$nLabel <- paste0(formatC(temp1$Significant, big.mark = ","), " estimates") + temp1$Significant <- NULL + + temp2$meanLabel <- paste0(formatC(100 * (1 - temp2$Significant), digits = 1, format = "f"), + "% of CIs include ", + temp2$Group) + temp2$Significant <- NULL + dd <- merge(temp1, temp2) + dd$tes <- as.numeric(as.character(dd$Group)) + + breaks <- c(0.1, 0.25, 0.5, 1, 2, 4, 6, 8, 10) + theme <- ggplot2::element_text(colour = "#000000", size = 12) + themeRA <- ggplot2::element_text(colour = "#000000", size = 12, hjust = 1) + themeLA <- ggplot2::element_text(colour = "#000000", size = 12, hjust = 0) + + d$Group <- paste("True hazard ratio =", d$Group) + dd$Group <- paste("True hazard ratio =", dd$Group) + alpha <- 1 - min(0.95 * (nrow(d)/nrow(dd)/50000)^0.1, 0.95) + plot <- ggplot2::ggplot(d, ggplot2::aes(x = .data$logRr, y = .data$seLogRr), environment = environment()) + + ggplot2::geom_vline(xintercept = log(breaks), colour = "#AAAAAA", lty = 1, size = 0.5) + + ggplot2::geom_abline(ggplot2::aes(intercept = (-log(.data$tes))/stats::qnorm(0.025), slope = 1/stats::qnorm(0.025)), + colour = grDevices::rgb(0.8, 0, 0), + linetype = "dashed", + size = 1, + alpha = 0.5, + data = dd) + + ggplot2::geom_abline(ggplot2::aes(intercept = (-log(.data$tes))/stats::qnorm(0.975), slope = 1/stats::qnorm(0.975)), + colour = grDevices::rgb(0.8, 0, 0), + linetype = "dashed", + size = 1, + alpha = 0.5, + data = dd) + + ggplot2::geom_point(size = size, + color = grDevices::rgb(0, 0, 0, alpha = 0.05), + alpha = alpha, + shape = 16) + + ggplot2::geom_hline(yintercept = 0) + + ggplot2::geom_label(x = log(0.15), + y = 0.9, + alpha = 1, + hjust = "left", + ggplot2::aes(label = .data$nLabel), + size = 5, + data = dd) + + ggplot2::geom_label(x = log(0.15), + y = labelY, + alpha = 1, + hjust = "left", + ggplot2::aes(label = .data$meanLabel), + size = 5, + data = dd) + + ggplot2::scale_x_continuous("Hazard ratio", + limits = log(c(0.1, 10)), + breaks = log(breaks), + labels = breaks) + + ggplot2::scale_y_continuous("Standard Error", limits = c(0, 1)) + + ggplot2::facet_grid(yGroup ~ Group) + + ggplot2::theme(panel.grid.minor = ggplot2::element_blank(), + panel.background = ggplot2::element_blank(), + panel.grid.major = ggplot2::element_blank(), + axis.ticks = ggplot2::element_blank(), + axis.text.y = themeRA, + axis.text.x = theme, + axis.title = theme, + legend.key = ggplot2::element_blank(), + strip.text.x = theme, + strip.text.y = theme, + strip.background = ggplot2::element_blank(), + legend.position = "none") + + return(plot) +} + + diff --git a/R/components-data-viewer.R b/R/components-data-viewer.R index 2a63d2c0..4dd67e54 100644 --- a/R/components-data-viewer.R +++ b/R/components-data-viewer.R @@ -163,10 +163,27 @@ resultTableServer <- function( id, function(input, output, session) { - if(inherits(df, 'data.frame')){ + # convert a data.frame to a reactive + if(!inherits(df, 'reactive')){ df <- shiny::reactiveVal(df) } + # initialize the reactables + actionCount <- shiny::reactiveVal(0) + actionIndex <- shiny::reactiveVal(0) + actionType <- shiny::reactiveVal('none') + + # add action column to data + newdf <- shiny::reactive({ + if(!is.null(nrow(df())) & !is.null(addActions)){ + cbind( + actions = rep("", nrow(df())), + df() + )} else{ + df() + } + }) + # add a new entry to colDefs with an action dropdown menu # add a onClick action if(!is.null(addActions)){ @@ -195,8 +212,8 @@ resultTableServer <- function( shinyWidgets::pickerInput( inputId = session$ns('dataCols'), label = 'Select Columns to Display: ', - choices = colnames(df()), - selected = colnames(df()), + choices = colnames(newdf()), + selected = colnames(newdf()), choicesOpt = list(style = rep_len("color: black;", 999)), multiple = T, options = shinyWidgets::pickerOptions( @@ -215,21 +232,35 @@ resultTableServer <- function( #need to try adding browser() to all reactives to see why selected cols isnt working colDefs <- shiny::reactive( + if(!is.null(newdf())){ create_colDefs_list( - df = df()[, input$dataCols], + df = newdf()[, input$dataCols], customColDefs = colDefsInput - ) ) + } else{ + NULL + } + ) output$resultData <- reactable::renderReactable({ if (is.null(input$dataCols)) { - data = df() + data = newdf() } else{ - data = df()[, input$dataCols, drop = FALSE] + data = newdf()[, input$dataCols, drop = FALSE] } - if (nrow(data) == 0) - return(NULL) + if(is.null(data)){ + return(NULL) + } + if(nrow(data) == 0){ + return(NULL) + } + # set row height based on nchar of table + if(max(apply(data, 1, function(x) max(nchar(x))), na.rm = T) < 100){ + height <- 40*3 + } else{ + height <- NULL + } reactable::reactable( data, @@ -247,12 +278,15 @@ resultTableServer <- function( highlight = TRUE, defaultColDef = reactable::colDef(align = "left"), - rowStyle = list(height = 40*3) + rowStyle = list( + height = height + ) #, experimental #theme = ohdsiReactableTheme ) }) + # download full data button output$downloadDataFull <- shiny::downloadHandler( filename = function() { @@ -269,9 +303,6 @@ resultTableServer <- function( # capture the actions - actionCount <- shiny::reactiveVal(0) - actionIndex <- shiny::reactiveVal(0) - actionType <- shiny::reactiveVal('none') shiny::observeEvent(input$action_index, { actionIndex(input$action_index) }) diff --git a/R/components.R b/R/components.R index be1bcdad..db45822e 100644 --- a/R/components.R +++ b/R/components.R @@ -20,7 +20,8 @@ inputSelectionViewer <- function(id = "input-selection") { shinydashboard::box( status = 'warning', width = "100%", - title = 'Selected: ', + title = 'Selected: ', + collapsible = T, shiny::uiOutput(ns("inputsText")) ) ) diff --git a/R/evidence-synth-main.R b/R/evidence-synth-main.R index 6e462e6f..c508fe99 100644 --- a/R/evidence-synth-main.R +++ b/R/evidence-synth-main.R @@ -118,26 +118,10 @@ evidenceSynthesisServer <- function( targetIds <- getESTargetIds( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings - #mySchema = resultDatabaseSettings$schema, - #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - #cgTablePrefix = resultDatabaseSettings$cgTablePrefix ) outcomeIds <- getESOutcomeIds( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings - #mySchema = resultDatabaseSettings$schema, - #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - #cgTablePrefix = resultDatabaseSettings$cgTablePrefix - ) - - diagnosticColumnNames <- getOACcombinations( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - #resultsSchema = resultDatabaseSettings$schema, - #sccsTablePrefix = resultDatabaseSettings$sccsTablePrefix, - #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - #cgTablePrefix = resultDatabaseSettings$cgTablePrefix, - #databaseTable = resultDatabaseSettings$databaseMetaData ) inputSelected <- inputSelectionServer( @@ -146,7 +130,7 @@ evidenceSynthesisServer <- function( createInputSetting( rowNumber = 1, columnWidth = 6, - varName = 'targetId', + varName = 'targetIds', uiFunction = 'shinyWidgets::pickerInput', uiInputs = list( label = 'Target: ', @@ -165,7 +149,7 @@ evidenceSynthesisServer <- function( createInputSetting( rowNumber = 1, columnWidth = 6, - varName = 'outcomeId', + varName = 'outcomeIds', uiFunction = 'shinyWidgets::pickerInput', uiInputs = list( label = 'Outcome: ', @@ -189,12 +173,8 @@ evidenceSynthesisServer <- function( getCMEstimation( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - #mySchema = resultDatabaseSettings$schema, - #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - #cgTablePrefix = resultDatabaseSettings$cgTablePrefix, - #databaseMetaData = resultDatabaseSettings$databaseMetaData, - targetId = inputSelected()$targetId, - outcomeId = inputSelected()$outcomeId + targetId = inputSelected()$targetIds, + outcomeId = inputSelected()$outcomeIds ) }) @@ -202,12 +182,8 @@ evidenceSynthesisServer <- function( getMetaEstimation( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - #mySchema = resultDatabaseSettings$schema, - #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - #cgTablePrefix = resultDatabaseSettings$cgTablePrefix, - #esTablePrefix = resultDatabaseSettings$tablePrefix, - targetId = inputSelected()$targetId, - outcomeId = inputSelected()$outcomeId + targetId = inputSelected()$targetIds, + outcomeId = inputSelected()$outcomeIds ) }) @@ -215,36 +191,20 @@ evidenceSynthesisServer <- function( getEvidenceSynthDiagnostics( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - #resultsSchema = resultDatabaseSettings$schema, - #cmTablePrefix = resultDatabaseSettings$cmTablePrefix, - #cgTablePrefix = resultDatabaseSettings$cgTablePrefix, - #databaseTable = resultDatabaseSettings$databaseTable, - targetIds = inputSelected()$targetId, - outcomeIds = inputSelected()$outcomeId + inputSelected = inputSelected, + targetIds = inputSelected()$targetIds, + outcomeIds = inputSelected()$outcomeIds ) }) - customColDefs2 <- list( - databaseName = reactable::colDef( - header = withTooltip( - "Database", - "The database name" - ), - sticky = "left" - ), - target = reactable::colDef( - header = withTooltip( - "Target", - "The target cohort of interest " - ), - sticky = "left" - ) - ) resultTableServer( id = "diagnosticsSummaryTable", df = diagSumData, - colDefsInput = styleColumns(customColDefs2, diagnosticColumnNames, outcomeIds) + colDefsInput = getColDefsESDiag( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) ) output$esCohortMethodPlot <- shiny::renderPlot( @@ -338,8 +298,8 @@ evidenceSynthesisServer <- function( getSccsEstimation( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetId = inputSelected()$targetId, - outcomeId = inputSelected()$outcomeId + targetId = inputSelected()$targetIds, + outcomeId = inputSelected()$outcomeIds ) }) @@ -675,6 +635,10 @@ return(unique(result)) createPlotForAnalysis <- function(data) { + if(is.null(data$comparator)){ + return(NULL) + } + compText <- data.frame( comparatorText = paste0('Comp', 1:length(unique(data$comparator))), comparator = unique(data$comparator) @@ -762,6 +726,10 @@ getSccsEstimation <- function( outcomeId ){ + if(is.null(targetId)){ + return(NULL) + } + sql <- "select c1.cohort_name as target, c3.cohort_name as outcome, @@ -928,6 +896,10 @@ createPlotForSccsAnalysis <- function( data ){ + if(is.null(data)){ + return(NULL) + } + breaks <- c(0.1, 0.25, 0.5, 1, 2, 4, 6, 8) plot <- ggplot2::ggplot( data = data, @@ -1011,10 +983,15 @@ getOACcombinations <- function( getEvidenceSynthDiagnostics <- function( connectionHandler, resultDatabaseSettings, + inputSelected, targetIds, outcomeIds ){ + if(is.null(targetIds)){ + return(NULL) + } + sccsDiagTemp <- getSccsAllDiagnosticsSummary( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, @@ -1024,11 +1001,14 @@ getEvidenceSynthDiagnostics <- function( cmDiagTemp <- getCmDiagnosticsData( connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - targetIds = targetIds, - outcomeIds = outcomeIds + resultDatabaseSettings = resultDatabaseSettings, + inputSelected = inputSelected ) + if(is.null(cmDiagTemp) | is.null(sccsDiagTemp)){ + return(NULL) + } + # select columns of interest and rename for consistency sccsDiagTemp <- diagnosticSummaryFormat( data = shiny::reactive({sccsDiagTemp}), @@ -1052,3 +1032,67 @@ getEvidenceSynthDiagnostics <- function( # return return(allResult) } + + + +getColDefsESDiag <- function( + connectionHandler, + resultDatabaseSettings +){ + + fixedColumns = list( + databaseName = reactable::colDef( + header = withTooltip( + "Database", + "The database name" + ), + sticky = "left" + ), + target = reactable::colDef( + header = withTooltip( + "Target", + "The target cohort of interest " + ), + sticky = "left" + ) + ) + + outcomes <- getESOutcomeIds( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + analyses <- getOACcombinations( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + colnameFormat <- merge(unique(names(analyses)), unique(names(outcomes))) + colnameFormat <- apply(colnameFormat, 1, function(x){paste(x, collapse = '_', sep = '_')}) + + styleList <- lapply( + colnameFormat, + FUN = function(x){ + reactable::colDef( + header = withTooltip( + substring(x,1,40), + x + ), + style = function(value) { + color <- 'orange' + if(is.na(value)){ + color <- 'black' + }else if(value == 'Pass'){ + color <- '#AFE1AF' + }else if(value == 'Fail'){ + color <- '#E97451' + } + list(background = color) + } + ) + } + ) + names(styleList) <- colnameFormat + result <- append(fixedColumns, styleList) + + return(result) +} \ No newline at end of file diff --git a/R/helpers-cohort-methodDataPulls.R b/R/helpers-cohort-methodDataPulls.R deleted file mode 100644 index 71c6d777..00000000 --- a/R/helpers-cohort-methodDataPulls.R +++ /dev/null @@ -1,713 +0,0 @@ - -getCohortNameFromId <- function( - connectionHandler, - resultDatabaseSettings, - cohortId) { - sql <- " - SELECT - cohort_name - FROM - @schema.@cg_table_prefixcohort_definition cd - WHERE - cd.cohort_definition_id = @cohort_id; - " - return( - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cg_table_prefix = resultDatabaseSettings$cgTablePrefix, - cohort_id = cohortId - ) - ) -} - - -getCohortMethodTcoChoice <- function( - connectionHandler, - resultDatabaseSettings, - tcoVar, - sorted = TRUE) { - sql <- " - SELECT - DISTINCT - cmtco.@tco_var, - cd.cohort_name -FROM - @schema.@cm_table_prefixtarget_comparator_outcome cmtco - join @schema.@cg_table_prefixcohort_definition cd - on cmtco.@tco_var = cd.cohort_definition_id - " - - if (sorted) { - sql <- paste(sql, "ORDER BY\n cd.cohort_name desc;", collapse = "\n") - } else{ - sql <- paste(sql, ';') - } - - return( - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - cg_table_prefix = resultDatabaseSettings$cgTablePrefix, - tco_var = tcoVar - ) - ) -} - - -getCohortMethodTargetChoices <- function( - connectionHandler, - resultDatabaseSettings - ) { - return( - getCohortMethodTcoChoice( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - "target_id" - ) - ) -} - - -getCohortMethodComparatorChoices <- function( - connectionHandler, - resultDatabaseSettings - ) { - return( - getCohortMethodTcoChoice( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - "comparator_id" - ) - ) -} - - -getCohortMethodOutcomeChoices <- function( - connectionHandler, - resultDatabaseSettings - ) { - return( - getCohortMethodTcoChoice( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - "outcome_id" - ) - ) -} - - -getCohortMethodDatabaseChoices <- function( - connectionHandler, - resultDatabaseSettings, - sorted = TRUE - ) { - sql <- " -SELECT -DISTINCT -dmd.database_id, -dmd.cdm_source_abbreviation -FROM - @schema.@cm_table_prefixresult cmr - join @schema.@database_table dmd - on dmd.database_id = cmr.database_id - - " - - if (sorted) { - sql <- paste(sql, "ORDER BY\n dmd.cdm_source_abbreviation desc;", collapse = "\n") - } else{ - sql <- paste(sql, ';') - } - return( - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - database_table = resultDatabaseSettings$databaseTable - ) - ) -} - - -getCmAnalysisOptions <- function( - connectionHandler, - resultDatabaseSettings, - sorted = TRUE) { - sql <- " -SELECT -DISTINCT -cma.analysis_id, -cma.description -FROM - @schema.@cm_table_prefixanalysis cma - " - - if (sorted) { - sql <- paste(sql, "ORDER BY\n cma.description desc;", collapse = "\n") - } else{ - sql <- paste(sql, ';') - } - - return( - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix - ) - ) -} - -getAllCohortMethodResults <- function( - connectionHandler, - resultDatabaseSettings - ) { - sql <- " -SELECT - cma.analysis_id, - cma.description description, - dmd.database_id database_id, -- break? - dmd.cdm_source_abbreviation cdm_source_abbreviation, - cmr.rr rr, - cmr.ci_95_lb ci_95_lb, - cmr.ci_95_ub ci_95_ub, - cmr.p p, - cmr.log_rr, - cmr.se_log_rr, - cmr.target_subjects, - cmr.comparator_subjects, - cmr.target_days, - cmr.comparator_days, - cmr.target_outcomes, - cmr.comparator_outcomes, - cmr.calibrated_rr calibrated_rr, - cmr.calibrated_ci_95_lb calibrated_ci_95_lb, - cmr.calibrated_ci_95_ub calibrated_ci_95_ub, - cmr.calibrated_p calibrated_p, - cmr.calibrated_log_rr, - cmr.calibrated_se_log_rr, - COALESCE(cmds.unblind, 0) unblind -- TODO: assume unblinded? (or always populated and moot) -FROM - @schema.@cm_table_prefixanalysis cma - JOIN @schema.@cm_table_prefixresult cmr on cmr.analysis_id = cma.analysis_id - JOIN @schema.@database_table dmd on dmd.database_id = cmr.database_id - LEFT JOIN @schema.@cm_table_prefixdiagnostics_summary cmds on cmds.analysis_id = cmr.analysis_id; - " - - - return( - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - database_table = resultDatabaseSettings$databaseTable - ) - ) -} - - -getCohortMethodMainResults <- function( - connectionHandler, - resultDatabaseSettings, - targetIds = c(), - comparatorIds = c(), - outcomeIds = c(), - databaseIds = c(), - analysisIds = c() -) { - - sql <- " -SELECT - cma.analysis_id, - cma.description description, - dmd.database_id database_id, -- break? - dmd.cdm_source_abbreviation cdm_source_abbreviation, - cmr.rr rr, - cmr.ci_95_lb ci_95_lb, - cmr.ci_95_ub ci_95_ub, - cmr.p p, - cmr.log_rr, - cmr.se_log_rr, - cmr.target_subjects, - cmr.comparator_subjects, - cmr.target_days, - cmr.comparator_days, - cmr.target_outcomes, - cmr.comparator_outcomes, - cmr.calibrated_rr calibrated_rr, - cmr.calibrated_ci_95_lb calibrated_ci_95_lb, - cmr.calibrated_ci_95_ub calibrated_ci_95_ub, - cmr.calibrated_p calibrated_p, - cmr.calibrated_log_rr, - cmr.calibrated_se_log_rr, - COALESCE(cmds.unblind, 0) unblind -- TODO: assume unblinded? (or always populated and moot) -FROM - @schema.@cm_table_prefixanalysis cma - JOIN @schema.@cm_table_prefixresult cmr - on cmr.analysis_id = cma.analysis_id - - JOIN @schema.@database_table dmd - on dmd.database_id = cmr.database_id - - LEFT JOIN @schema.@cm_table_prefixdiagnostics_summary cmds - on cmds.analysis_id = cmr.analysis_id - AND cmds.target_id = cmr.target_id - AND cmds.comparator_id = cmr.comparator_id - AND cmds.outcome_id = cmr.outcome_id - AND cmds.database_id = cmr.database_id - " - if (length(targetIds) > 0 || - length(comparatorIds) > 0 || - length(outcomeIds) > 0 || - length(databaseIds) > 0 || - length(analysisIds) > 0) { - sql <- paste0(sql, "\nWHERE\n\t") - } - - clauses <- c() - if (length(targetIds) > 0 ) { - clauses <- c(clauses, "cmr.target_id IN (@target_ids)\n\t") - } - if (length(comparatorIds) > 0) { - clauses <- c(clauses, "cmr.comparator_id IN (@comparator_ids)\n\t") - } - if (length(outcomeIds) > 0) { - clauses <- c(clauses, "cmr.outcome_id IN (@outcome_ids)\n\t") - } - if (length(databaseIds) > 0) { - clauses <- c(clauses, "cmr.database_id IN (@database_ids)\n\t") - } - if (length(analysisIds) > 0) { - clauses <- c(clauses, "cmr.analysis_id IN (@analysis_ids)\n\t") - } - sql <- paste0(sql, paste(clauses, collapse = " AND "), ";") - return( - suppressWarnings( # ignoring warnings due to parameter not found - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - database_table = resultDatabaseSettings$databaseTable, - target_ids = paste0("'", paste(targetIds, collapse = "', '"), "'"), - comparator_ids = paste0("'", paste(comparatorIds, collapse = "', '"), "'"), - outcome_ids = paste0("'", paste(outcomeIds, collapse = "', '"), "'"), - database_ids = paste0("'", paste(databaseIds, collapse = "', '"), "'"), - analysis_ids = paste0("'", paste(analysisIds, collapse = "', '"), "'") - ) - ) - ) - -} - - -getCohortMethodAnalyses <- function( - connectionHandler, - resultDatabaseSettings - ) { - sql <- " - SELECT - cma.* - FROM - @schema.@cm_table_prefixanalysis cma - " - return( - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix - ) - ) -} - - -getCohortMethodSubgroupResults <- function(connectionHandler, # not used? - targetIds = c(), - comparatorIds = c(), - outcomeIds = c(), - databaseIds = c(), - analysisIds = c(), - subgroupIds = c(), - estimatesOnly = FALSE, - cmInteractionResult = c(), # added to clean check - covariate = c() # added to clean check - ) { - idx <- rep(TRUE, nrow(cmInteractionResult)) - if (length(targetIds) != 0) { - idx <- idx & cmInteractionResult$targetId %in% targetIds - } - if (length(comparatorIds) != 0) { - idx <- idx & cmInteractionResult$comparatorId %in% comparatorIds - } - if (length(outcomeIds) != 0) { - idx <- idx & cmInteractionResult$outcomeId %in% outcomeIds - } - if (length(databaseIds) != 0) { - idx <- idx & cmInteractionResult$databaseId %in% databaseIds - } - if (length(analysisIds) != 0) { - idx <- idx & cmInteractionResult$analysisId %in% analysisIds - } - if (length(subgroupIds) != 0) { - idx <- idx & cmInteractionResult$interactionCovariateId %in% subgroupIds - } - result <- cmInteractionResult[idx, ] - result <- merge(result, data.frame(interactionCovariateId = covariate$covariateId, - databaseId = covariate$databaseId, - covariateName = covariate$covariateName)) - result <- result[, c("covariateName", - "targetSubjects", - "comparatorSubjects", - "rrr", - "ci95Lb", - "ci95Ub", - "p", - "calibratedP")] - colnames(result) <- c("interactionCovariateName", - "targetSubjects", - "comparatorSubjects", - "rrr", - "ci95Lb", - "ci95Ub", - "p", - "calibratedP") - return(result) -} - - -getCohortMethodControlResults <- function( - connectionHandler, - resultDatabaseSettings, - targetId, - comparatorId, - analysisId, - databaseId = NULL, - includePositiveControls = TRUE, - emptyAsNa = TRUE - ) { - - sql <- " - SELECT - cmr.*, - cmtco.true_effect_size effect_size - FROM - @schema.@cm_table_prefixresult cmr - JOIN @schema.@cm_table_prefixtarget_comparator_outcome cmtco - ON cmr.target_id = cmtco.target_id AND cmr.comparator_id = cmtco.comparator_id AND cmr.outcome_id = cmtco.outcome_id - WHERE - cmtco.outcome_of_interest != 1 - AND cmr.target_id = @target_id - AND cmr.comparator_id = @comparator_id - AND cmr.analysis_id = @analysis_id - " - - - if (!is.null(databaseId)) { - # update sql - sql <- paste(sql, paste("AND cmr.database_id = '@database_id'"), collapse = "\n") - } - - if (!includePositiveControls) { - # update sql - sql <- paste(sql, paste("AND cmtco.true_effect_size = 1"), collapse = "\n") - } - - results <- connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - target_id = targetId, - comparator_id = comparatorId, - analysis_id = analysisId, - database_id = databaseId - ) - - if (emptyAsNa) { - results[results == ''] <- NA - } - - return(results) -} - - -getCmFollowUpDist <- function( - connectionHandler, - resultDatabaseSettings, - targetId, - comparatorId, - outcomeId, - databaseId = NULL, - analysisId -) { - - sql <- " - SELECT - * - FROM - @schema.@cm_table_prefixfollow_up_dist cmfud - WHERE - cmfud.target_id = @target_id - AND cmfud.comparator_id = @comparator_id - AND cmfud.outcome_id = @outcome_id - AND cmfud.analysis_id = @analysis_id - " - if(!is.null(databaseId)) { - sql <- paste(sql, paste("AND cmfud.database_id = '@database_id'"), collapse = "\n") - } - return( - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - target_id = targetId, - comparator_id = comparatorId, - outcome_id = outcomeId, - analysis_id = analysisId, - database_id = databaseId - ) - ) -} - - -getCohortMethodPs <- function( - connectionHandler, - resultDatabaseSettings, - targetId, - comparatorId, - analysisId, - databaseId = NULL - ) { - sql <- " - SELECT - * - FROM - @schema.@cm_table_prefixpreference_score_dist cmpsd - WHERE - cmpsd.target_id = @target_id - AND cmpsd.comparator_id = @comparator_id - AND cmpsd.analysis_id = @analysis_id - " - if(!is.null(databaseId)) { - sql <- paste(sql, paste("AND cmpsd.database_id = '@database_id'"), collapse = "\n") - } - - - ps <- connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - target_id = targetId, - comparator_id = comparatorId, - analysis_id = analysisId, - database_id = databaseId - ) - - - if (!is.null(databaseId)) { - ps$databaseId <- NULL - } - return(ps) -} - - -getCohortMethodKaplanMeier <- function( - connectionHandler, - resultDatabaseSettings, - targetId, - comparatorId, - outcomeId, - databaseId, - analysisId - ) { - - sql <- " - SELECT - * - FROM - @schema.@cm_table_prefixkaplan_meier_dist cmkmd - WHERE - cmkmd.target_id = @target_id - AND cmkmd.comparator_id = @comparator_id - AND cmkmd.outcome_id = @outcome_id - AND cmkmd.analysis_id = @analysis_id - AND cmkmd.database_id = '@database_id'; - " - - return( - connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - #database_table = resultDatabaseSettings$databaseTable, - target_id = targetId, - comparator_id = comparatorId, - outcome_id = outcomeId, - analysis_id = analysisId, - database_id = databaseId - ) - ) -} - - -getCohortMethodAttrition <- function( - connectionHandler, - resultDatabaseSettings, - targetId, - comparatorId, - outcomeId, - analysisId, - databaseId - ) { - - sql <- " - SELECT cmat.* - FROM - @schema.@cm_table_prefixattrition cmat - WHERE - cmat.target_id = @target_id - AND cmat.comparator_id = @comparator_id - AND cmat.outcome_id = @outcome_id - AND cmat.analysis_id = @analysis_id - AND cmat.database_id = '@database_id'; - " - result <- connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - #database_table = resultDatabaseSettings$databaseTable, - target_id = targetId, - comparator_id = comparatorId, - outcome_id = outcomeId, - analysis_id = analysisId, - database_id = databaseId - ) - targetAttrition <- result[result$exposureId == targetId, ] - comparatorAttrition <- result[result$exposureId == comparatorId, ] - colnames(targetAttrition)[colnames(targetAttrition) == "subjects"] <- "targetPersons" - targetAttrition$exposureId <- NULL - colnames(comparatorAttrition)[colnames(comparatorAttrition) == "subjects"] <- "comparatorPersons" - comparatorAttrition$exposureId <- NULL - result <- merge(targetAttrition, comparatorAttrition) - result <- result[order(result$sequenceNumber), ] - return(result) -} - - -getCohortMethodStudyPeriod <- function( - connectionHandler, - resultDatabaseSettings, - targetId, - comparatorId, - databaseId - ) { - sql <- "SELECT min_date, max_date - FROM @schema.@cm_table_prefixcomparison_summary - WHERE target_id = @target_id - AND comparator_id = @comparator_id - AND database_id = '@database_id';" - - studyPeriod <- connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - target_id = targetId, - comparator_id = comparatorId, - database_id = databaseId - ) - return(studyPeriod) -} - - -getCohortMethodPropensityModel <- function( - connectionHandler, - resultDatabaseSettings, - targetId, - comparatorId, - analysisId, - databaseId - ) { - sqlTmp <- " - SELECT - cmpm.coefficient, - cmc.covariate_id, - cmc.covariate_name - FROM - @schema.@cm_table_prefixcovariate cmc - JOIN @schema.@cm_table_prefixpropensity_model cmpm - ON cmc.covariate_id = cmpm.covariate_id - AND cmc.database_id = cmpm.database_id - WHERE - cmpm.target_id = @target_id - AND cmpm.comparator_id = @comparator_id - AND cmpm.analysis_id = @analysis_id - AND cmpm.database_id = '@database_id' - " - - sql <- " - SELECT - cmc.covariate_id, - cmc.covariate_name, - cmpm.coefficient - FROM - ( - SELECT - covariate_id, - covariate_name - FROM - @schema.@cm_table_prefixcovariate - WHERE - analysis_id = @analysis_id - AND database_id = '@database_id' - UNION - SELECT - 0 as covariate_id, - 'intercept' as covariate_name) cmc - JOIN @schema.@cm_table_prefixpropensity_model cmpm - ON cmc.covariate_id = cmpm.covariate_id - WHERE - cmpm.target_id = @target_id - AND cmpm.comparator_id = @comparator_id - AND cmpm.analysis_id = @analysis_id - AND cmpm.database_id = '@database_id' - " - - model <- connectionHandler$queryDb( - sql = sql, - schema = resultDatabaseSettings$schema, - cm_table_prefix = resultDatabaseSettings$cmTablePrefix, - target_id = targetId, - comparator_id = comparatorId, - analysis_id = analysisId, - database_id = databaseId - ) - return(model) -} - - - - -getCohortMethodNegativeControlEstimates <- function( - connectionHandler, - resultDatabaseSettings, - targetId, - comparatorId, - analysisId - ) { - - subset <- getCohortMethodControlResults( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - targetId = targetId, - comparatorId =comparatorId, - analysisId = analysisId, - includePositiveControls = FALSE - ) - subset <- subset[, c("databaseId", "logRr", "seLogRr")] - if(nrow(subset) == 0) - return(NULL) - return(subset) -} - - - - diff --git a/R/helpers-cohort-methodPlotsAndTables.R b/R/helpers-cohort-methodPlotsAndTables.R deleted file mode 100644 index e7a5122c..00000000 --- a/R/helpers-cohort-methodPlotsAndTables.R +++ /dev/null @@ -1,621 +0,0 @@ -# used in estimation-power -prepareCohortMethodFollowUpDistTable <- function(followUpDist) { - targetRow <- data.frame(Database = followUpDist$databaseId, - Cohort = "Target", - Min = followUpDist$targetMinDays, - P10 = followUpDist$targetP10Days, - P25 = followUpDist$targetP25Days, - Median = followUpDist$targetMedianDays, - P75 = followUpDist$targetP75Days, - P90 = followUpDist$targetP90Days, - Max = followUpDist$targetMaxDays) - comparatorRow <- data.frame(Database = followUpDist$databaseId, - Cohort = "Comparator", - Min = followUpDist$comparatorMinDays, - P10 = followUpDist$comparatorP10Days, - P25 = followUpDist$comparatorP25Days, - Median = followUpDist$comparatorMedianDays, - P75 = followUpDist$comparatorP75Days, - P90 = followUpDist$comparatorP90Days, - Max = followUpDist$comparatorMaxDays) - table <- rbind(targetRow, comparatorRow) - table$Min <- formatC(table$Min, big.mark = ",", format = "d") - table$P10 <- formatC(table$P10, big.mark = ",", format = "d") - table$P25 <- formatC(table$P25, big.mark = ",", format = "d") - table$Median <- formatC(table$Median, big.mark = ",", format = "d") - table$P75 <- formatC(table$P75, big.mark = ",", format = "d") - table$P90 <- formatC(table$P90, big.mark = ",", format = "d") - table$Max <- formatC(table$Max, big.mark = ",", format = "d") - if (length(unique(followUpDist$databaseId)) == 1) - table$Database <- NULL - return(table) -} - - -# used in estimation-power -prepareCohortMethodPowerTable <- function( - mainResults, - connectionHandler , - resultDatabaseSettings - ) { - analyses <- getCohortMethodAnalyses( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - table <- merge(mainResults, analyses) - alpha <- 0.05 - power <- 0.8 - z1MinAlpha <- stats::qnorm(1 - alpha/2) - zBeta <- -stats::qnorm(1 - power) - pA <- table$targetSubjects/(table$targetSubjects + table$comparatorSubjects) - pB <- 1 - pA - totalEvents <- abs(table$targetOutcomes) + abs(table$comparatorOutcomes) - table$mdrr <- exp(sqrt((zBeta + z1MinAlpha)^2/(totalEvents * pA * pB))) - table$targetYears <- table$targetDays/365.25 - table$comparatorYears <- table$comparatorDays/365.25 - table$targetIr <- 1000 * table$targetOutcomes/table$targetYears - table$comparatorIr <- 1000 * table$comparatorOutcomes/table$comparatorYears - table <- table[, c("description", - "databaseId", - "targetSubjects", - "comparatorSubjects", - "targetYears", - "comparatorYears", - "targetOutcomes", - "comparatorOutcomes", - "targetIr", - "comparatorIr", - "mdrr")] - table$targetSubjects <- formatC(table$targetSubjects, big.mark = ",", format = "d") - table$comparatorSubjects <- formatC(table$comparatorSubjects, big.mark = ",", format = "d") - table$targetYears <- formatC(table$targetYears, big.mark = ",", format = "d") - table$comparatorYears <- formatC(table$comparatorYears, big.mark = ",", format = "d") - table$targetOutcomes <- formatC(table$targetOutcomes, big.mark = ",", format = "d") - table$comparatorOutcomes <- formatC(table$comparatorOutcomes, big.mark = ",", format = "d") - table$targetIr <- sprintf("%.2f", table$targetIr) - table$comparatorIr <- sprintf("%.2f", table$comparatorIr) - table$mdrr <- sprintf("%.2f", table$mdrr) - table$targetSubjects <- gsub("^-", "<", table$targetSubjects) - table$comparatorSubjects <- gsub("^-", "<", table$comparatorSubjects) - table$targetOutcomes <- gsub("^-", "<", table$targetOutcomes) - table$comparatorOutcomes <- gsub("^-", "<", table$comparatorOutcomes) - table$targetIr <- gsub("^-", "<", table$targetIr) - table$comparatorIr <- gsub("^-", "<", table$comparatorIr) - idx <- (table$targetOutcomes < 0 | table$comparatorOutcomes < 0) - table$mdrr[idx] <- paste0(">", table$mdrr[idx]) - return(table) -} - -# estimation-subgroups -prepareCohortMethodSubgroupTable <- function(subgroupResults, output = "latex") { - rnd <- function(x) { - ifelse(x > 10, sprintf("%.1f", x), sprintf("%.2f", x)) - } - - subgroupResults$hrr <- paste0(rnd(subgroupResults$rrr), - " (", - rnd(subgroupResults$ci95Lb), - " - ", - rnd(subgroupResults$ci95Ub), - ")") - - subgroupResults$hrr[is.na(subgroupResults$rrr)] <- "" - subgroupResults$p <- sprintf("%.2f", subgroupResults$p) - subgroupResults$p[subgroupResults$p == "NA"] <- "" - subgroupResults$calibratedP <- sprintf("%.2f", subgroupResults$calibratedP) - subgroupResults$calibratedP[subgroupResults$calibratedP == "NA"] <- "" - - if (any(grepl("on-treatment", subgroupResults$analysisDescription)) && - any(grepl("intent-to-treat", subgroupResults$analysisDescription))) { - idx <- grepl("on-treatment", subgroupResults$analysisDescription) - onTreatment <- subgroupResults[idx, c("interactionCovariateName", - "targetSubjects", - "comparatorSubjects", - "hrr", - "p", - "calibratedP")] - itt <- subgroupResults[!idx, c("interactionCovariateName", "hrr", "p", "calibratedP")] - colnames(onTreatment)[4:6] <- paste("onTreatment", colnames(onTreatment)[4:6], sep = "_") - colnames(itt)[2:4] <- paste("itt", colnames(itt)[2:4], sep = "_") - table <- merge(onTreatment, itt) - } else { - table <- subgroupResults[, c("interactionCovariateName", - "targetSubjects", - "comparatorSubjects", - "hrr", - "p", - "calibratedP")] - } - table$interactionCovariateName <- gsub("Subgroup: ", "", table$interactionCovariateName) - if (output == "latex") { - table$interactionCovariateName <- gsub(">=", "$\\\\ge$ ", table$interactionCovariateName) - } - table$targetSubjects <- formatC(table$targetSubjects, big.mark = ",", format = "d") - table$targetSubjects <- gsub("^-", "<", table$targetSubjects) - table$comparatorSubjects <- formatC(table$comparatorSubjects, big.mark = ",", format = "d") - table$comparatorSubjects <- gsub("^-", "<", table$comparatorSubjects) - table$comparatorSubjects <- gsub("^<", "$<$", table$comparatorSubjects) - return(table) -} - - - - - - -# estiamtion-covariateBal -plotCohortMethodCovariateBalanceSummary <- function(balanceSummary, - threshold = 0, - beforeLabel = "Before matching", - afterLabel = "After matching") { - balanceSummary <- balanceSummary[rev(order(balanceSummary$databaseId)), ] - dbs <- data.frame(databaseId = unique(balanceSummary$databaseId), - x = 1:length(unique(balanceSummary$databaseId))) - vizData <- merge(balanceSummary, dbs) - - vizData$type <- factor(vizData$type, levels = c(beforeLabel, afterLabel)) - - plot <- ggplot2::ggplot(vizData, ggplot2::aes(x = .data$x, - ymin = .data$ymin, - lower = .data$lower, - middle = .data$median, - upper = .data$upper, - ymax = .data$ymax, - group = .data$databaseId)) + - ggplot2::geom_errorbar(ggplot2::aes(ymin = .data$ymin, ymax = .data$ymin), size = 1) + - ggplot2::geom_errorbar(ggplot2::aes(ymin = .data$ymax, ymax = .data$ymax), size = 1) + - ggplot2::geom_boxplot(stat = "identity", fill = grDevices::rgb(0, 0, 0.8, alpha = 0.25), size = 1) + - ggplot2::geom_hline(yintercept = 0) + - ggplot2::scale_x_continuous(limits = c(0.5, max(vizData$x) + 1.75)) + - ggplot2::scale_y_continuous("Standardized difference of mean") + - ggplot2::coord_flip() + - ggplot2::facet_grid(~type) + - ggplot2::theme(panel.grid.major.y = ggplot2::element_blank(), - panel.grid.minor.y = ggplot2::element_blank(), - panel.grid.major.x = ggplot2::element_line(color = "#AAAAAA"), - panel.background = ggplot2::element_blank(), - axis.text.y = ggplot2::element_blank(), - axis.title.y = ggplot2::element_blank(), - axis.ticks.y = ggplot2::element_blank(), - axis.text.x = ggplot2::element_text(size = 11), - axis.title.x = ggplot2::element_text(size = 11), - axis.ticks.x = ggplot2::element_line(color = "#AAAAAA"), - strip.background = ggplot2::element_blank(), - strip.text = ggplot2::element_text(size = 11), - plot.margin = grid::unit(c(0,0,0.1,0), "lines")) - - if (threshold != 0) { - plot <- plot + ggplot2::geom_hline(yintercept = c(threshold, -threshold), linetype = "dotted") - } - after <- vizData[vizData$type == afterLabel, ] - after$max <- pmax(abs(after$ymin), abs(after$ymax)) - text <- data.frame(y = rep(c(after$x, nrow(after) + 1.25) , 3), - x = rep(c(1,2,3), each = nrow(after) + 1), - label = c(c(as.character(after$databaseId), - "Source", - formatC(after$covariateCount, big.mark = ",", format = "d"), - "Covariate\ncount", - formatC(after$max, digits = 2, format = "f"), - paste(afterLabel, "max(absolute)", sep = "\n"))), - dummy = "") - - data_table <- ggplot2::ggplot(text, ggplot2::aes(x = .data$x, y = .data$y, label = .data$label)) + - ggplot2::geom_text(size = 4, hjust=0, vjust=0.5) + - ggplot2::geom_hline(ggplot2::aes(yintercept=nrow(after) + 0.5)) + - ggplot2::theme(panel.grid.major = ggplot2::element_blank(), - panel.grid.minor = ggplot2::element_blank(), - legend.position = "none", - panel.border = ggplot2::element_blank(), - panel.background = ggplot2::element_blank(), - axis.text.x = ggplot2::element_text(colour="white"), - axis.text.y = ggplot2::element_blank(), - axis.ticks = ggplot2::element_line(colour="white"), - strip.background = ggplot2::element_blank(), - plot.margin = grid::unit(c(0,0,0.1,0), "lines")) + - ggplot2::labs(x="",y="") + - ggplot2::facet_grid(~dummy) + - ggplot2::coord_cartesian(xlim=c(1,4), ylim = c(0.5, max(vizData$x) + 1.75)) - - plot <- gridExtra::grid.arrange(data_table, plot, ncol = 2) - return(plot) -} - -# estimation-systematicError -plotCohortMethodScatter <- function(controlResults) { - - if(nrow(controlResults)==0){ - return(NULL) - } - - size <- 2 - labelY <- 0.7 - d <- rbind(data.frame(yGroup = "Uncalibrated", - logRr = controlResults$logRr, - seLogRr = controlResults$seLogRr, - ci95Lb = controlResults$ci95Lb, - ci95Ub = controlResults$ci95Ub, - trueRr = controlResults$effectSize), - data.frame(yGroup = "Calibrated", - logRr = controlResults$calibratedLogRr, - seLogRr = controlResults$calibratedSeLogRr, - ci95Lb = controlResults$calibratedCi95Lb, - ci95Ub = controlResults$calibratedCi95Ub, - trueRr = controlResults$effectSize)) - d <- d[!is.na(d$logRr), ] - d <- d[!is.na(d$ci95Lb), ] - d <- d[!is.na(d$ci95Ub), ] - if (nrow(d) == 0) { - return(NULL) - } - d$Group <- as.factor(d$trueRr) - d$Significant <- d$ci95Lb > d$trueRr | d$ci95Ub < d$trueRr - temp1 <- stats::aggregate(Significant ~ Group + yGroup, data = d, length) - temp2 <- stats::aggregate(Significant ~ Group + yGroup, data = d, mean) - temp1$nLabel <- paste0(formatC(temp1$Significant, big.mark = ","), " estimates") - temp1$Significant <- NULL - - temp2$meanLabel <- paste0(formatC(100 * (1 - temp2$Significant), digits = 1, format = "f"), - "% of CIs include ", - temp2$Group) - temp2$Significant <- NULL - dd <- merge(temp1, temp2) - dd$tes <- as.numeric(as.character(dd$Group)) - - breaks <- c(0.1, 0.25, 0.5, 1, 2, 4, 6, 8, 10) - theme <- ggplot2::element_text(colour = "#000000", size = 12) - themeRA <- ggplot2::element_text(colour = "#000000", size = 12, hjust = 1) - themeLA <- ggplot2::element_text(colour = "#000000", size = 12, hjust = 0) - - d$Group <- paste("True hazard ratio =", d$Group) - dd$Group <- paste("True hazard ratio =", dd$Group) - alpha <- 1 - min(0.95 * (nrow(d)/nrow(dd)/50000)^0.1, 0.95) - plot <- ggplot2::ggplot(d, ggplot2::aes(x = .data$logRr, y = .data$seLogRr), environment = environment()) + - ggplot2::geom_vline(xintercept = log(breaks), colour = "#AAAAAA", lty = 1, size = 0.5) + - ggplot2::geom_abline(ggplot2::aes(intercept = (-log(.data$tes))/stats::qnorm(0.025), slope = 1/stats::qnorm(0.025)), - colour = grDevices::rgb(0.8, 0, 0), - linetype = "dashed", - size = 1, - alpha = 0.5, - data = dd) + - ggplot2::geom_abline(ggplot2::aes(intercept = (-log(.data$tes))/stats::qnorm(0.975), slope = 1/stats::qnorm(0.975)), - colour = grDevices::rgb(0.8, 0, 0), - linetype = "dashed", - size = 1, - alpha = 0.5, - data = dd) + - ggplot2::geom_point(size = size, - color = grDevices::rgb(0, 0, 0, alpha = 0.05), - alpha = alpha, - shape = 16) + - ggplot2::geom_hline(yintercept = 0) + - ggplot2::geom_label(x = log(0.15), - y = 0.9, - alpha = 1, - hjust = "left", - ggplot2::aes(label = .data$nLabel), - size = 5, - data = dd) + - ggplot2::geom_label(x = log(0.15), - y = labelY, - alpha = 1, - hjust = "left", - ggplot2::aes(label = .data$meanLabel), - size = 5, - data = dd) + - ggplot2::scale_x_continuous("Hazard ratio", - limits = log(c(0.1, 10)), - breaks = log(breaks), - labels = breaks) + - ggplot2::scale_y_continuous("Standard Error", limits = c(0, 1)) + - ggplot2::facet_grid(yGroup ~ Group) + - ggplot2::theme(panel.grid.minor = ggplot2::element_blank(), - panel.background = ggplot2::element_blank(), - panel.grid.major = ggplot2::element_blank(), - axis.ticks = ggplot2::element_blank(), - axis.text.y = themeRA, - axis.text.x = theme, - axis.title = theme, - legend.key = ggplot2::element_blank(), - strip.text.x = theme, - strip.text.y = theme, - strip.background = ggplot2::element_blank(), - legend.position = "none") - - return(plot) -} - -# estimation-attrition -drawCohortMethodAttritionDiagram <- function(attrition, - targetLabel = "Target", - comparatorLabel = "Comparator") { - addStep <- function(data, attrition, row) { - label <- paste(strwrap(as.character(attrition$description[row]), width = 30), collapse = "\n") - data$leftBoxText[length(data$leftBoxText) + 1] <- label - data$rightBoxText[length(data$rightBoxText) + 1] <- paste(targetLabel, - ": n = ", - data$currentTarget - attrition$targetPersons[row], - "\n", - comparatorLabel, - ": n = ", - data$currentComparator - attrition$comparatorPersons[row], - sep = "") - data$currentTarget <- attrition$targetPersons[row] - data$currentComparator <- attrition$comparatorPersons[row] - return(data) - } - data <- list(leftBoxText = c(paste("Exposed:\n", - targetLabel, - ": n = ", - attrition$targetPersons[1], - "\n", - comparatorLabel, - ": n = ", - attrition$comparatorPersons[1], - sep = "")), rightBoxText = c(""), currentTarget = attrition$targetPersons[1], currentComparator = attrition$comparatorPersons[1]) - for (i in 2:nrow(attrition)) { - data <- addStep(data, attrition, i) - } - - - data$leftBoxText[length(data$leftBoxText) + 1] <- paste("Study population:\n", - targetLabel, - ": n = ", - data$currentTarget, - "\n", - comparatorLabel, - ": n = ", - data$currentComparator, - sep = "") - leftBoxText <- data$leftBoxText - rightBoxText <- data$rightBoxText - nSteps <- length(leftBoxText) - - boxHeight <- (1/nSteps) - 0.03 - boxWidth <- 0.45 - shadowOffset <- 0.01 - arrowLength <- 0.01 - x <- function(x) { - return(0.25 + ((x - 1)/2)) - } - y <- function(y) { - return(1 - (y - 0.5) * (1/nSteps)) - } - - downArrow <- function(p, x1, y1, x2, y2) { - p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x1, y = y1, xend = x2, yend = y2)) - p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x2, - y = y2, - xend = x2 + arrowLength, - yend = y2 + arrowLength)) - p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x2, - y = y2, - xend = x2 - arrowLength, - yend = y2 + arrowLength)) - return(p) - } - rightArrow <- function(p, x1, y1, x2, y2) { - p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x1, y = y1, xend = x2, yend = y2)) - p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x2, - y = y2, - xend = x2 - arrowLength, - yend = y2 + arrowLength)) - p <- p + ggplot2::geom_segment(ggplot2::aes_string(x = x2, - y = y2, - xend = x2 - arrowLength, - yend = y2 - arrowLength)) - return(p) - } - box <- function(p, x, y) { - p <- p + ggplot2::geom_rect(ggplot2::aes_string(xmin = x - (boxWidth/2) + shadowOffset, - ymin = y - (boxHeight/2) - shadowOffset, - xmax = x + (boxWidth/2) + shadowOffset, - ymax = y + (boxHeight/2) - shadowOffset), fill = grDevices::rgb(0, - 0, - 0, - alpha = 0.2)) - p <- p + ggplot2::geom_rect(ggplot2::aes_string(xmin = x - (boxWidth/2), - ymin = y - (boxHeight/2), - xmax = x + (boxWidth/2), - ymax = y + (boxHeight/2)), fill = grDevices::rgb(0.94, - 0.94, - 0.94), color = "black") - return(p) - } - label <- function(p, x, y, text, hjust = 0) { - p <- p + ggplot2::geom_text(ggplot2::aes_string(x = x, y = y, label = paste("\"", text, "\"", - sep = "")), - hjust = hjust, - size = 3.7) - return(p) - } - - p <- ggplot2::ggplot() - for (i in 2:nSteps - 1) { - p <- downArrow(p, x(1), y(i) - (boxHeight/2), x(1), y(i + 1) + (boxHeight/2)) - p <- label(p, x(1) + 0.02, y(i + 0.5), "Y") - } - for (i in 2:(nSteps - 1)) { - p <- rightArrow(p, x(1) + boxWidth/2, y(i), x(2) - boxWidth/2, y(i)) - p <- label(p, x(1.5), y(i) - 0.02, "N", 0.5) - } - for (i in 1:nSteps) { - p <- box(p, x(1), y(i)) - } - for (i in 2:(nSteps - 1)) { - p <- box(p, x(2), y(i)) - } - for (i in 1:nSteps) { - p <- label(p, x(1) - boxWidth/2 + 0.02, y(i), text = leftBoxText[i]) - } - for (i in 2:(nSteps - 1)) { - p <- label(p, x(2) - boxWidth/2 + 0.02, y(i), text = rightBoxText[i]) - } - p <- p + ggplot2::theme(legend.position = "none", - plot.background = ggplot2::element_blank(), - panel.grid.major = ggplot2::element_blank(), - panel.grid.minor = ggplot2::element_blank(), - panel.border = ggplot2::element_blank(), - panel.background = ggplot2::element_blank(), - axis.text = ggplot2::element_blank(), - axis.title = ggplot2::element_blank(), - axis.ticks = ggplot2::element_blank()) - - return(p) -} - -# used in helpers-estPandT -nonZeroCohortMethodHazardRatio <- function(hrLower, hrUpper, terms) { - if (hrUpper < 1) { - return(terms[1]) - } else if (hrLower > 1) { - return(terms[2]) - } else { - return(terms[3]) - } -} - -# estimation-resultsTable -prettyCohortMethodHr <- function(x) { - if (!is.numeric(x)) { - x <- as.numeric(x) - } - result <- sprintf("%.2f", x) - result[is.na(x) | x > 100] <- "NA" - return(result) -} - -# used in here -goodCohortMethodPropensityScore <- function(value) { - return(value > 1) -} - -# used in here -goodCohortMethodSystematicBias <- function(value) { - return(value > 1) -} - - -# estmation-propensity -prepareCohortMethodPropensityModelTable <- function(model) { - rnd <- function(x) { - ifelse(x > 10, sprintf("%.1f", x), sprintf("%.2f", x)) - } - table <- model[order(-abs(model$coefficient)), c("coefficient", "covariateName")] - table$coefficient <- sprintf("%.2f", table$coefficient) - colnames(table) <- c("Beta", "Covariate") - return(table) -} - -# estimation-forestPlot -plotCohortMethodForest <- function(results, limits = c(0.1, 10), metaAnalysisDbIds = NULL) { - - dbResults <- results[!(results$databaseId %in% metaAnalysisDbIds), ] - dbResults <- dbResults[!is.na(dbResults$seLogRr), ] - dbResults <- dbResults[order(dbResults$databaseId), ] - maResult <- results[results$databaseId %in% metaAnalysisDbIds, ] - summaryLabel <- sprintf("Summary (I\u00B2 = %.2f)", as.numeric(maResult$i2)) - d1 <- data.frame(x = "Uncalibrated", - logRr = -100, - logLb95Ci = -100, - logUb95Ci = -100, - name = "Source", - type = "header", - stringsAsFactors = FALSE) - d2 <- data.frame(x = "Uncalibrated", - logRr = dbResults$logRr, - logLb95Ci = log(dbResults$ci95Lb), - logUb95Ci = log(dbResults$ci95Ub), - name = dbResults$databaseId, - type = "db", - stringsAsFactors = FALSE) - d3 <- data.frame(x = "Uncalibrated", - logRr = maResult$logRr, - logLb95Ci = log(maResult$ci95Lb), - logUb95Ci = log(maResult$ci95Ub), - name = summaryLabel, - type = "ma", - stringsAsFactors = FALSE) - d4 <- data.frame(x = "Calibrated", - logRr = -100, - logLb95Ci = -100, - logUb95Ci = -100, - name = "Source", - type = "header", - stringsAsFactors = FALSE) - d5 <- data.frame(x = "Calibrated", - logRr = dbResults$calibratedLogRr, - logLb95Ci = log(dbResults$calibratedCi95Lb), - logUb95Ci = log(dbResults$calibratedCi95Ub), - name = dbResults$databaseId, - type = "db", - stringsAsFactors = FALSE) - d6 <- data.frame(x = "Calibrated", - logRr = maResult$calibratedLogRr, - logLb95Ci = log(maResult$calibratedCi95Lb), - logUb95Ci = log(maResult$calibratedCi95Ub), - name = summaryLabel, - type = "ma", - stringsAsFactors = FALSE) - - d <- rbind(d1, d2, d3, d4, d5, d6) - d$name <- factor(d$name, levels = c(summaryLabel, rev(as.character(dbResults$databaseId)), "Source")) - d$x <- factor(d$x, levels = c("Uncalibrated", "Calibrated")) - - breaks <- c(0.1, 0.25, 0.5, 1, 2, 4, 6, 8, 10) - plot <- ggplot2::ggplot(d,ggplot2::aes(x = exp(.data$logRr), y = .data$name, xmin = exp(.data$logLb95Ci), xmax = exp(.data$logUb95Ci))) + - ggplot2::geom_vline(xintercept = breaks, colour = "#AAAAAA", lty = 1, size = 0.2) + - ggplot2::geom_vline(xintercept = 1, size = 0.5) + - ggplot2::geom_errorbarh(height = 0.15) + - ggplot2::geom_point(size=3, shape = 23, ggplot2::aes(fill=.data$type)) + - ggplot2::scale_fill_manual(values = c("#000000", "#000000", "#FFFFFF")) + - ggplot2::scale_x_continuous("Hazard ratio", trans = "log10", breaks = breaks, labels = breaks) + - ggplot2::coord_cartesian(xlim = limits) + - ggplot2::facet_grid(~ x) + - ggplot2::theme(text = ggplot2::element_text(size = 18), - panel.grid.major = ggplot2::element_blank(), - panel.grid.minor = ggplot2::element_blank(), - panel.background = ggplot2::element_blank(), - legend.position = "none", - panel.border = ggplot2::element_blank(), - axis.text.y = ggplot2::element_blank(), - axis.title.y = ggplot2::element_blank(), - axis.ticks = ggplot2::element_blank(), - strip.background = ggplot2::element_blank(), - plot.margin = grid::unit(c(0,0,0.1,0), "lines")) - - d$hr <- paste0(formatC(exp(d$logRr), digits = 2, format = "f"), - " (", - formatC(exp(d$logLb95Ci), digits = 2, format = "f"), - "-", - formatC(exp(d$logUb95Ci), digits = 2, format = "f"), - ")") - d <- d[order(d$x), ] - - labels <- data.frame(y = factor(c(as.character(d$name[d$x == "Uncalibrated"]), as.character(d$name)), levels = levels(d$name)), - x = rep(1:3, each = nrow(d)/2), - label = c(as.character(d$name[d$x == "Uncalibrated"]), d$hr), - dummy = "dummy", - stringsAsFactors = FALSE) - labels$label[nrow(d)/2 + 1] <- paste("HR (95% CI)") - labels$label[nrow(d) + 1] <- paste("Calibrated HR (95% CI)") - dataTable <- ggplot2::ggplot(labels, ggplot2::aes(x = .data$x, y = .data$y, label = .data$label)) + - ggplot2::geom_text(size = 5, hjust = 0, vjust = 0.5) + - ggplot2::geom_hline(ggplot2::aes(yintercept = nrow(d) - 0.5)) + - ggplot2::facet_grid(~dummy) + - ggplot2::theme(text = ggplot2::element_text(size = 18), - panel.grid.major = ggplot2::element_blank(), - panel.grid.minor = ggplot2::element_blank(), - legend.position = "none", - panel.border = ggplot2::element_blank(), - panel.background = ggplot2::element_blank(), - axis.text.x = ggplot2::element_text(colour = "white"), - axis.text.y = ggplot2::element_blank(), - axis.ticks = ggplot2::element_line(colour = "white"), - strip.background = ggplot2::element_blank(), - strip.text = ggplot2::element_text(colour = "white"), - plot.margin = grid::unit(c(0,0,0.1,0), "lines")) + - ggplot2::labs(x = "", y = "") + - ggplot2::coord_cartesian(xlim = c(1,4)) - plot <- gridExtra::grid.arrange(dataTable, plot, ncol = 2) - return(plot) -} diff --git a/R/helpers-getCohortMethodUtility.R b/R/helpers-getCohortMethodUtility.R deleted file mode 100644 index a2276592..00000000 --- a/R/helpers-getCohortMethodUtility.R +++ /dev/null @@ -1,26 +0,0 @@ - -getCohortMethodColumnsToBlind <- function(results) { - columnsToBlind <- c("rr", "ci95Ub", "ci95Lb", - "logRr", "seLogRr", "p", - "calibratedRr", "calibratedCi95Ub", - "calibratedCi95Lb", "calibratedLogRr", - "calibratedSeLogRr", - "calibratedP") - - return(intersect(columnsToBlind, colnames(results))) - -} - -getCohortMethodSelectNamedChoices <- function(v1, v2) { - l <- as.list(v1) - names(l) <- as.vector(v2) - return(l) -} - - -filterCohortMethodEmptyNullValues <- function(v, includeNull=TRUE) { - valsToFilter <- c('') - if (includeNull) - valsToFilter <- c(valsToFilter, NULL) - return(v[! v %in% valsToFilter]) -} diff --git a/R/patient-level-prediction-covariateSummary.R b/R/patient-level-prediction-covariateSummary.R index a321a801..06acb5c0 100644 --- a/R/patient-level-prediction-covariateSummary.R +++ b/R/patient-level-prediction-covariateSummary.R @@ -175,8 +175,7 @@ patientLevelPredictionCovariateSummaryServer <- function( ) ) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) diff --git a/R/patient-level-prediction-designSummary.R b/R/patient-level-prediction-designSummary.R index f88b90d5..72861356 100644 --- a/R/patient-level-prediction-designSummary.R +++ b/R/patient-level-prediction-designSummary.R @@ -380,10 +380,10 @@ getPredictionDesignSummary <- function( dplyr::relocate("devDatabases", .before = "valDatabases") %>% dplyr::relocate("diagDatabases", .before = "devDatabases") - summaryTable <- cbind( - actions = rep("",nrow(summaryTable)), - summaryTable - ) + ##summaryTable <- cbind( + ## actions = rep("",nrow(summaryTable)), + ## summaryTable + ##) shiny::incProgress(3/3, detail = paste("Finished")) diff --git a/R/patient-level-prediction-diagnostics.R b/R/patient-level-prediction-diagnostics.R index ff388595..3755264e 100644 --- a/R/patient-level-prediction-diagnostics.R +++ b/R/patient-level-prediction-diagnostics.R @@ -31,11 +31,29 @@ patientLevelPredictionDiagnosticsViewer <- function(id) { ns <- shiny::NS(id) - shiny::div( - reactable::reactableOutput(ns('diagnosticSummaryTable')), - shiny::uiOutput(ns('main')) + shiny::tagList( + shinydashboard::box( + collapsible = TRUE, + collapsed = TRUE, + title = "All Database Diagnostics For Selected Model Design", + width = "100%", + shiny::htmlTemplate(system.file("patient-level-prediction-www", "main-diagnosticsSummaryHelp.html", package = utils::packageName())) + ), + shinydashboard::box( + status = "warning", + width = "100%", + shiny::uiOutput(outputId = ns("diagnosticSummaryText")) + ), + shinydashboard::box( + width = "100%", + shiny::div( + resultTableViewer(ns('diagnosticSummaryTable')), + shiny::uiOutput(ns('main')) + ) + ) ) + } #' The module server for exploring prediction diagnostic results @@ -62,222 +80,190 @@ patientLevelPredictionDiagnosticsServer <- function( id, function(input, output, session) { - withTooltip <- function(value, tooltip, ...) { - shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", - tippy::tippy(value, tooltip, ...)) - } - shiny::observe({ - if(!is.null(modelDesignId()) ){ - - diagnosticTable <- getPredictionDiagnostics( - modelDesignId = modelDesignId(), - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - # input tables - output$diagnosticSummaryTable <- reactable::renderReactable({ - reactable::reactable( - data = cbind( - diagnosticTable, - participants = rep("",nrow(diagnosticTable)), - predictors = rep("",nrow(diagnosticTable)), - outcomes = rep("",nrow(diagnosticTable)) - ), - columns = list( - '1.1' = reactable::colDef( - header = withTooltip( - "1.1", - "Participants: Were appropriate data sources used, e.g. cohort, RCT or nested case-control study data?" - ), - cell = reactable::JS(" + selectedModelDesign <- shiny::reactive( + getModelDesignInfo( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + modelDesignId = modelDesignId + ) + ) + output$diagnosticSummaryText <- shiny::renderUI(selectedModelDesign()) + + diagnosticTable <- shiny::reactive({ + getPredictionDiagnostics( + modelDesignId = modelDesignId(), + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + }) + + colDefsInput <- list( + '1.1' = reactable::colDef( + header = withTooltip( + "1.1", + "Participants: Were appropriate data sources used, e.g. cohort, RCT or nested case-control study data?" + ), + cell = reactable::JS(" function(cellInfo) { // Render as an X mark or check mark if(cellInfo.value === 'Fail'){return '\u274c Fail'} else if(cellInfo.value === 'Pass'){return '\u2714\ufe0f Pass'} else{return '? Unkown'} } ")), - '1.2' = reactable::colDef( - header = withTooltip( - "1.2", - "Participants: Were all inclusions and exclusions of participants appropriate?" - ), - cell = reactable::JS(" + '1.2' = reactable::colDef( + header = withTooltip( + "1.2", + "Participants: Were all inclusions and exclusions of participants appropriate?" + ), + cell = reactable::JS(" function(cellInfo) { // Render as an X mark or check mark if(cellInfo.value === 'Fail'){return '\u274c Fail'} else if(cellInfo.value === 'Pass'){return '\u2714\ufe0f Pass'} else{return '? Unkown'} } ")), - '2.1' = reactable::colDef( - header = withTooltip( - "2.1", - "Predictors: Were predictors defined and assessed in a similar way for all participants?" - ), - cell = reactable::JS(" + '2.1' = reactable::colDef( + header = withTooltip( + "2.1", + "Predictors: Were predictors defined and assessed in a similar way for all participants?" + ), + cell = reactable::JS(" function(cellInfo) { // Render as an X mark or check mark if(cellInfo.value === 'Fail'){return '\u274c Fail'} else if(cellInfo.value === 'Pass'){return '\u2714\ufe0f Pass'} else{return '? Unkown'} } ")), - '2.2' = reactable::colDef( - header = withTooltip( - "2.2", - "Predictors: Were predictor assessments made without knowledge of outcome data?" - ), - cell = reactable::JS(" + '2.2' = reactable::colDef( + header = withTooltip( + "2.2", + "Predictors: Were predictor assessments made without knowledge of outcome data?" + ), + cell = reactable::JS(" function(cellInfo) { // Render as an X mark or check mark if(cellInfo.value === 'Fail'){return '\u274c Fail'} else if(cellInfo.value === 'Pass'){return '\u2714\ufe0f Pass'} else{return '? Unkown'} } ")), - '2.3' = reactable::colDef( - header = withTooltip( - "2.3", - "Predictors: Are all predictors available at the time the model is intended to be used?" - ), - cell = reactable::JS(" + '2.3' = reactable::colDef( + header = withTooltip( + "2.3", + "Predictors: Are all predictors available at the time the model is intended to be used?" + ), + cell = reactable::JS(" function(cellInfo) { // Render as an X mark or check mark if(cellInfo.value === 'Fail'){return '\u274c Fail'} else if(cellInfo.value === 'Pass'){return '\u2714\ufe0f Pass'} else{return '? Unkown'} } ")), - '3.4' = reactable::colDef( - header = withTooltip( - "3.4", - "Outcome: Was the outcome defined and determined in a similar way for all participants?" - ), - cell = reactable::JS(" + '3.4' = reactable::colDef( + header = withTooltip( + "3.4", + "Outcome: Was the outcome defined and determined in a similar way for all participants?" + ), + cell = reactable::JS(" function(cellInfo) { // Render as an X mark or check mark if(cellInfo.value === 'Fail'){return '\u274c Fail'} else if(cellInfo.value === 'Pass'){return '\u2714\ufe0f Pass'} else{return '? Unkown'} } ")), - '3.6' = reactable::colDef( - header = withTooltip( - "3.6", - "Outcome: Was the time interval between predictor assessment and outcome determination appropriate?" - ), - cell = reactable::JS(" + '3.6' = reactable::colDef( + header = withTooltip( + "3.6", + "Outcome: Was the time interval between predictor assessment and outcome determination appropriate?" + ), + cell = reactable::JS(" function(cellInfo) { // Render as an X mark or check mark if(cellInfo.value === 'Fail'){return '\u274c Fail'} else if(cellInfo.value === 'Pass'){return '\u2714\ufe0f Pass'} else{return '? Unkown'} } ")), - '4.1' = reactable::colDef( - header = withTooltip( - "4.1", - "Design: Were there a reasonable number of participants with the outcome?" - ), - cell = reactable::JS(" + '4.1' = reactable::colDef( + header = withTooltip( + "4.1", + "Design: Were there a reasonable number of participants with the outcome?" + ), + cell = reactable::JS(" function(cellInfo) { // Render as an X mark or check mark if(cellInfo.value === 'Fail'){return '\u274c Fail'} else if(cellInfo.value === 'Pass'){return '\u2714\ufe0f Pass'} else{return '? Unkown'} } - ")), - participants = reactable::colDef( - name = "", - sortable = FALSE, - cell = function() htmltools::tags$button("View Participants") - ), - predictors = reactable::colDef( - name = "", - sortable = FALSE, - cell = function() htmltools::tags$button("View Predictors") - ), - outcomes = reactable::colDef( - name = "", - sortable = FALSE, - cell = function() htmltools::tags$button("View Outcomes") - ) - ), - - onClick = reactable::JS( - paste0( - "function(rowInfo, column) { - // Only handle click events on the 'details' column - if (column.id !== 'participants' & column.id !== 'predictors' & column.id !== 'outcomes') { - return - } - - // Display an alert dialog with details for the row - //window.alert('Details for row ' + rowInfo.index + ':\\n' + JSON.stringify(rowInfo.values, null, 2)) + ")) + ) + + modelTableOutputs <- resultTableServer( + id = "diagnosticSummaryTable", + df = diagnosticTable, + colDefsInput = colDefsInput, + addActions = c('participants','predictors', 'outcomes') + ) - // Send the click event to Shiny, which will be available in input$show_details - // Note that the row index starts at 0 in JavaScript, so we add 1 - if(column.id == 'participants'){ - Shiny.setInputValue('",session$ns('show_participants'),"', { index: rowInfo.index + 1 }, { priority: 'event' }) - } - if(column.id == 'predictors'){ - Shiny.setInputValue('",session$ns('show_predictors'),"', { index: rowInfo.index + 1 }, { priority: 'event' }) - } - if(column.id == 'outcomes'){ - Shiny.setInputValue('",session$ns('show_outcomes'),"', { index: rowInfo.index + 1 }, { priority: 'event' }) - } - }" - ) - - ) - ) - - }) # end reactable - - + # listen # PARTICIPANTS #============ - shiny::observeEvent( - input$show_participants, - { - participants <- getPredictionDiagnosticParticipants( - diagnosticId = diagnosticTable$diagnosticId[input$show_participants$index], - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - - output$participants <- reactable::renderReactable({ - reactable::reactable( - data = participants %>% - dplyr::filter(.data$parameter == ifelse(is.null(input$participantParameters), unique(participants$parameter)[1], input$participantParameters)) %>% - dplyr::select( - c( + shiny::observeEvent(modelTableOutputs$actionCount(), { + + if(modelTableOutputs$actionType() == 'participants'){ + { + participants <- getPredictionDiagnosticParticipants( + diagnosticId = diagnosticTable()$diagnosticId[modelTableOutputs$actionIndex()$index], + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + output$participants <- reactable::renderReactable({ + reactable::reactable( + data = participants %>% + dplyr::filter(.data$parameter == ifelse(is.null(input$participantParameters), unique(participants$parameter)[1], input$participantParameters)) %>% + dplyr::select( + c( "probastId", "paramvalue", "metric", "value" - ) - ) %>% - dplyr::mutate( - value = format(.data$value, nsmall = 2, ) - ) %>% - tidyr::pivot_wider( - names_from = "paramvalue", #.data$paramvalue, - values_from = "value" #.data$value ) - ) - }) - output$main <- shiny::renderUI({ - shiny::div( - shiny::selectInput( - inputId = session$ns('participantParameters'), - label = 'Select Parameter', - multiple = F, - choices = unique(participants$parameter) - ), - reactable::reactableOutput(session$ns('participants')) - ) - }) # renderUI - } - ) # end observed event - - - + ) %>% + dplyr::mutate( + value = format(.data$value, nsmall = 2, ) + ) %>% + tidyr::pivot_wider( + names_from = "paramvalue", #.data$paramvalue, + values_from = "value" #.data$value + ) + ) + }) + + + shiny::showModal( + shiny::modalDialog( + title = "Participant Diagnostics", + shiny::basicPage( + shiny::tags$head(shiny::tags$style(".modal-dialog{ width:95%}")), + shiny::div( + shiny::selectInput( + inputId = session$ns('participantParameters'), + label = 'Select Parameter', + multiple = F, + choices = unique(participants$parameter) + ), + reactable::reactableOutput(session$ns('participants')) + ) + ), + size = "l", + easyClose = T + )) + + } + + } + }) + + # PREDICTOR #================== - shiny::observeEvent( - input$show_predictors, - { - + shiny::observeEvent(modelTableOutputs$actionCount(), { + if(modelTableOutputs$actionType() == 'predictors'){ predTable <- getPredictionDiagnosticPredictors( - diagnosticId = diagnosticTable$diagnosticId[input$show_predictors$index], + diagnosticId = diagnosticTable()$diagnosticId[modelTableOutputs$actionIndex()$index], connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings ) @@ -327,33 +313,40 @@ patientLevelPredictionDiagnosticsServer <- function( ) }) - output$main <- shiny::renderUI({ - shiny::div( - shiny::p('Were predictor assessments made without knowledge of outcome data? (if outcome occur shortly after index this may be problematic)'), - shiny::p(''), - - shiny::selectInput( - inputId = session$ns('predictorParameters'), - label = 'Select Parameter', - multiple = F, - choices = unique(predTable$inputType) + + shiny::showModal( + shiny::modalDialog( + title = "Predictor Diagnostics", + shiny::basicPage( + shiny::tags$head(shiny::tags$style(".modal-dialog{ width:95%}")), + shiny::div( + shiny::p('Were predictor assessments made without knowledge of outcome data? (if outcome occur shortly after index this may be problematic)'), + shiny::p(''), + + shiny::selectInput( + inputId = session$ns('predictorParameters'), + label = 'Select Parameter', + multiple = F, + choices = unique(predTable$inputType) + ), + + plotly::plotlyOutput(session$ns('predictorPlot')) + ) ), - - plotly::plotlyOutput(session$ns('predictorPlot')) - ) - - }) # renderUI + size = "l", + easyClose = T + )) + } - ) + }) # OUTCOME # ================= - shiny::observeEvent( - input$show_outcomes, - { - + shiny::observeEvent(modelTableOutputs$actionCount(), { + if(modelTableOutputs$actionType() == 'outcomes'){ + outcomeTable <- getPredictionDiagnosticOutcomes( - diagnosticId = diagnosticTable$diagnosticId[input$show_outcomes$index], + diagnosticId = diagnosticTable()$diagnosticId[modelTableOutputs$actionIndex()$index], connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings ) @@ -384,29 +377,34 @@ patientLevelPredictionDiagnosticsServer <- function( ) }) - output$main <- shiny::renderUI({ - shiny::div( - shiny::p('Was the outcome determined appropriately? (Are age/sex/year/month trends expected?)'), - shiny::p(''), - - shiny::selectInput( - inputId = session$ns('outcomeParameters'), - label = 'Select Parameter', - multiple = F, - choices = unique(outcomeTable$aggregation) + + shiny::showModal( + shiny::modalDialog( + title = "Outcome Diagnostics", + shiny::basicPage( + shiny::tags$head(shiny::tags$style(".modal-dialog{ width:95%}")), + shiny::div( + shiny::p('Was the outcome determined appropriately? (Are age/sex/year/month trends expected?)'), + shiny::p(''), + + shiny::selectInput( + inputId = session$ns('outcomeParameters'), + label = 'Select Parameter', + multiple = F, + choices = unique(outcomeTable$aggregation) + ), + + plotly::plotlyOutput(session$ns('outcomePlot')) + ) ), - - plotly::plotlyOutput(session$ns('outcomePlot')) - ) - - }) # renderUI + size = "l", + easyClose = T + )) + } - ) - - + }) - } # not null - }) # observe + } ) # server } diff --git a/R/patient-level-prediction-discrimination.R b/R/patient-level-prediction-discrimination.R index cddaa057..c457c451 100644 --- a/R/patient-level-prediction-discrimination.R +++ b/R/patient-level-prediction-discrimination.R @@ -202,10 +202,11 @@ patientLevelPredictionDiscriminationServer <- function( values_from = 'value' ) - cbind( - actions = rep('', nrow(data)), - data - ) + ##cbind( + ## actions = rep('', nrow(data)), + ## data + ## ) + data }) diff --git a/R/patient-level-prediction-main.R b/R/patient-level-prediction-main.R index dd13b674..5fd1191a 100644 --- a/R/patient-level-prediction-main.R +++ b/R/patient-level-prediction-main.R @@ -79,6 +79,17 @@ patientLevelPredictionViewer <- function(id=1) { patientLevelPredictionModelSummaryViewer(ns('modelSummaryTab')) ), + shiny::tabPanel( + "Diagnostic Summary", + shiny::actionButton( + inputId = ns("backToDesignSummaryD"), + label = "Back To Design Summary", + shiny::icon("arrow-left"), + style="color: #fff; background-color: #337ab7; border-color: #2e6da4" + ), + patientLevelPredictionDiagnosticsViewer(ns('diagnostics')) + ), + shiny::tabPanel( "Explore Selected Model", @@ -217,6 +228,14 @@ patientLevelPredictionServer <- function( ) }) + shiny::observeEvent(input$backToDesignSummaryD, { + shiny::updateTabsetPanel( + session = session, + inputId = 'allView', + selected = 'Model Designs Summary' + ) + }) + # keep a reactive variable tracking the active tab singleViewValue <- shiny::reactive({ input$singleView @@ -246,10 +265,8 @@ patientLevelPredictionServer <- function( shiny::observeEvent(designSummary$modelDesignId(), { modelDesignId(designSummary$modelDesignId()) if(!is.null(designSummary$modelDesignId())){ - #shiny::showTab(inputId = "allView", session = session, target = "Models Summary") shiny::updateTabsetPanel(session, "allView", selected = "Models Summary") - #shiny::hideTab(inputId = "allView", session = session, target = "Explore Selected Model") - } + } }) @@ -279,10 +296,8 @@ patientLevelPredictionServer <- function( performanceId(performance$performanceId()) developmentDatabaseId(performance$developmentDatabaseId()) if(!is.null(performance$performanceId())){ - #shiny::showTab(inputId = "allView", session = session, target = "Explore Selected Model") shiny::updateTabsetPanel(session, "allView", selected = "Explore Selected Model") - #shiny::hideTab(inputId = "allView", session = session, target = "Models Summary") - } + } # hide validation tab if non internal val if(performance$modelDevelopment() == 1){ @@ -299,11 +314,11 @@ patientLevelPredictionServer <- function( # ============================= # diagnostic viewer - show model diagnostic results shiny::observeEvent(designSummary$diagnosticId(), { - shiny::showModal(shiny::modalDialog( - title = "Diagnostic", - patientLevelPredictionDiagnosticsViewer(session$ns('diagnostics')) - )) + if(!is.null(designSummary$diagnosticId())){ + shiny::updateTabsetPanel(session, "allView", selected = "Diagnostic Summary") + } }) + patientLevelPredictionDiagnosticsServer( id = 'diagnostics', modelDesignId = designSummary$diagnosticId, diff --git a/R/patient-level-prediction-modelSummary.R b/R/patient-level-prediction-modelSummary.R index 78823a39..47b7f41e 100644 --- a/R/patient-level-prediction-modelSummary.R +++ b/R/patient-level-prediction-modelSummary.R @@ -185,8 +185,7 @@ patientLevelPredictionModelSummaryServer <- function( attrition() %>% dplyr::select(-c("performanceId", "outcomeId")) ) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE ) ) @@ -329,12 +328,12 @@ getModelDesignPerformanceSummary <- function( }) # adding actions column to left - summaryTable <- cbind( - actions = rep("", nrow(summaryTable)), - summaryTable - ) + ##summaryTable <- cbind( + ## actions = rep("", nrow(summaryTable)), + ## summaryTable + ##) - return(summaryTable[,c('actions','Dev', 'Val', 'T','O', 'modelDesignId', + return(summaryTable[,c('Dev', 'Val', 'T','O', 'modelDesignId', 'TAR', 'AUROC', 'AUPRC', 'T Size', 'O Count','Val (%)', 'O Incidence (%)', 'timeStamp', 'performanceId', 'developmentDatabaseId', 'modelDevelopment', 'type')]) diff --git a/R/patient-level-prediction-settings.R b/R/patient-level-prediction-settings.R index 8eba86fe..79f8de14 100644 --- a/R/patient-level-prediction-settings.R +++ b/R/patient-level-prediction-settings.R @@ -120,8 +120,7 @@ patientLevelPredictionSettingsServer <- function( shiny::showModal(shiny::modalDialog( title = "Cohort description", shiny::p(modelDesign()$cohort$cohortJson), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) @@ -142,8 +141,7 @@ patientLevelPredictionSettingsServer <- function( shiny::showModal(shiny::modalDialog( title = "Cohort description", shiny::p(modelDesign()$outcome$cohortJson), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) @@ -163,8 +161,7 @@ patientLevelPredictionSettingsServer <- function( shiny::showModal(shiny::modalDialog( title = "Exclusions done during data extraction", shiny::p(modelDesign()$RestrictPlpData), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) @@ -190,8 +187,7 @@ patientLevelPredictionSettingsServer <- function( formatPopSettings(modelDesign()$populationSettings) ) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) @@ -215,8 +211,7 @@ patientLevelPredictionSettingsServer <- function( formatCovSettings(modelDesign()$covariateSettings) ) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) @@ -242,8 +237,7 @@ patientLevelPredictionSettingsServer <- function( formatModSettings(modelDesign()$modelSettings ) ) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) @@ -264,8 +258,7 @@ patientLevelPredictionSettingsServer <- function( shiny::div( shiny::p(modelDesign()$featureEngineeringSettings) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) @@ -286,8 +279,7 @@ patientLevelPredictionSettingsServer <- function( shiny::div( shiny::p(modelDesign()$preprocessSettings) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) @@ -308,8 +300,7 @@ patientLevelPredictionSettingsServer <- function( shiny::div( shiny::p(modelDesign()$splitSettings) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) @@ -330,8 +321,7 @@ patientLevelPredictionSettingsServer <- function( shiny::div( shiny::p(modelDesign()$sampleSettings) ), - easyClose = TRUE, - footer = NULL + easyClose = TRUE )) } ) diff --git a/R/sccs-diagnosticsSummary.R b/R/sccs-diagnosticsSummary.R index e605cdc6..e538e51a 100644 --- a/R/sccs-diagnosticsSummary.R +++ b/R/sccs-diagnosticsSummary.R @@ -248,35 +248,13 @@ sccsDiagnosticsSummaryServer <- function( ) - # Summary table - customColDefs2 <- list( - databaseName = reactable::colDef( - header = withTooltip( - "Database", - "The database name" - ), - sticky = "left" - ), - target = reactable::colDef( - header = withTooltip( - "Target", - "The target cohort of interest " - ), - sticky = "left" - ), - covariateName = reactable::colDef( - header = withTooltip( - "Time Period", - "The time period of interest" - ), - sticky = "left" - ) - ) - resultTableServer( id = "diagnosticsSummaryTable", df = data2, - colDefsInput = styleColumns(customColDefs2, outcomeIds, analysisIds) + colDefsInput = getColDefsSccsDiag( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) ) } @@ -471,3 +449,73 @@ getSccsAllDiagnosticsSummary <- function( return(result) } + + +getColDefsSccsDiag <- function( + connectionHandler, + resultDatabaseSettings +){ + + fixedColumns = list( + databaseName = reactable::colDef( + header = withTooltip( + "Database", + "The database name" + ), + sticky = "left" + ), + target = reactable::colDef( + header = withTooltip( + "Target", + "The target cohort of interest " + ), + sticky = "left" + ), + covariateName = reactable::colDef( + header = withTooltip( + "Time Period", + "The time period of interest" + ), + sticky = "left" + ) + ) + + outcomes <- getSccsDiagOutcomes( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + analyses <- getSccsDiagAnalyses( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings + ) + + colnameFormat <- merge(unique(names(outcomes)), unique(names(analyses))) + colnameFormat <- apply(colnameFormat, 1, function(x){paste(x, collapse = '_', sep = '_')}) + + styleList <- lapply( + colnameFormat, + FUN = function(x){ + reactable::colDef( + header = withTooltip( + substring(x,1,40), + x + ), + style = function(value) { + color <- 'orange' + if(is.na(value)){ + color <- 'black' + }else if(value == 'Pass'){ + color <- '#AFE1AF' + }else if(value == 'Fail'){ + color <- '#E97451' + } + list(background = color) + } + ) + } + ) + names(styleList) <- colnameFormat + result <- append(fixedColumns, styleList) + + return(result) +} diff --git a/inst/patient-level-prediction-www/main-diagnosticsSummaryHelp.html b/inst/patient-level-prediction-www/main-diagnosticsSummaryHelp.html new file mode 100644 index 00000000..0d6b4b88 --- /dev/null +++ b/inst/patient-level-prediction-www/main-diagnosticsSummaryHelp.html @@ -0,0 +1,6 @@ +

This shows the model design diagnostic results for the selected model design across databases. These diagnostics are based on PROBAST.

+ +

+add details +

+ diff --git a/man/cohortMethodAttritionServer.Rd b/man/cohortMethodAttritionServer.Rd index e1bc5703..6e3444cc 100644 --- a/man/cohortMethodAttritionServer.Rd +++ b/man/cohortMethodAttritionServer.Rd @@ -7,7 +7,6 @@ cohortMethodAttritionServer( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings ) @@ -17,8 +16,6 @@ cohortMethodAttritionServer( \item{selectedRow}{the selected row from the main results table} -\item{inputParams}{the selected study parameters of interest} - \item{connectionHandler}{the connection to the PLE results database} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} diff --git a/man/cohortMethodCovariateBalanceServer.Rd b/man/cohortMethodCovariateBalanceServer.Rd index 785ec960..41a874bc 100644 --- a/man/cohortMethodCovariateBalanceServer.Rd +++ b/man/cohortMethodCovariateBalanceServer.Rd @@ -7,7 +7,6 @@ cohortMethodCovariateBalanceServer( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings, metaAnalysisDbIds = NULL @@ -18,8 +17,6 @@ cohortMethodCovariateBalanceServer( \item{selectedRow}{the selected row from the main results table} -\item{inputParams}{the selected study parameters of interest} - \item{connectionHandler}{the connection to the PLE results database} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} diff --git a/man/cohortMethodDiagnosticsSummaryServer.Rd b/man/cohortMethodDiagnosticsSummaryServer.Rd index 1d69da14..b9660fed 100644 --- a/man/cohortMethodDiagnosticsSummaryServer.Rd +++ b/man/cohortMethodDiagnosticsSummaryServer.Rd @@ -7,7 +7,8 @@ cohortMethodDiagnosticsSummaryServer( id, connectionHandler, - resultDatabaseSettings + resultDatabaseSettings, + inputSelected ) } \arguments{ @@ -16,6 +17,8 @@ cohortMethodDiagnosticsSummaryServer( \item{connectionHandler}{the connection to the PLE results database} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} + +\item{inputSelected}{The target id, comparator id, outcome id and analysis id selected by the user} } \value{ the PLE diagnostics summary results diff --git a/man/cohortMethodForestPlotServer.Rd b/man/cohortMethodForestPlotServer.Rd deleted file mode 100644 index 95455092..00000000 --- a/man/cohortMethodForestPlotServer.Rd +++ /dev/null @@ -1,34 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohort-method-forestPlot.R -\name{cohortMethodForestPlotServer} -\alias{cohortMethodForestPlotServer} -\title{The module server for rendering the PLE multiple results forest plot} -\usage{ -cohortMethodForestPlotServer( - id, - connectionHandler, - selectedRow, - inputParams, - metaAnalysisDbIds = NULL, - resultDatabaseSettings -) -} -\arguments{ -\item{id}{the unique reference id for the module} - -\item{connectionHandler}{connection} - -\item{selectedRow}{the selected row from the main results table} - -\item{inputParams}{the selected study parameters of interest} - -\item{metaAnalysisDbIds}{metaAnalysisDbIds} - -\item{resultDatabaseSettings}{a list containing the result schema and prefixes} -} -\value{ -the PLE forest plot content server -} -\description{ -The module server for rendering the PLE multiple results forest plot -} diff --git a/man/cohortMethodForestPlotViewer.Rd b/man/cohortMethodForestPlotViewer.Rd deleted file mode 100644 index a8678a9e..00000000 --- a/man/cohortMethodForestPlotViewer.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohort-method-forestPlot.R -\name{cohortMethodForestPlotViewer} -\alias{cohortMethodForestPlotViewer} -\title{The module viewer for rendering the PLE results forest plot} -\usage{ -cohortMethodForestPlotViewer(id) -} -\arguments{ -\item{id}{the unique reference id for the module} -} -\value{ -The user interface to the cohort method forest plot -} -\description{ -The module viewer for rendering the PLE results forest plot -} diff --git a/man/cohortMethodKaplanMeierServer.Rd b/man/cohortMethodKaplanMeierServer.Rd index 2e52424c..0f3f3c4b 100644 --- a/man/cohortMethodKaplanMeierServer.Rd +++ b/man/cohortMethodKaplanMeierServer.Rd @@ -7,10 +7,8 @@ cohortMethodKaplanMeierServer( id, selectedRow, - inputParams, connectionHandler, - resultDatabaseSettings, - metaAnalysisDbIds = NULL + resultDatabaseSettings ) } \arguments{ @@ -18,13 +16,9 @@ cohortMethodKaplanMeierServer( \item{selectedRow}{the selected row from the main results table} -\item{inputParams}{the selected study parameters of interest} - \item{connectionHandler}{the connection to the PLE results database} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} - -\item{metaAnalysisDbIds}{metaAnalysisDbIds} } \value{ the PLE Kaplain Meier content server diff --git a/man/cohortMethodPopulationCharacteristicsServer.Rd b/man/cohortMethodPopulationCharacteristicsServer.Rd index 1e0deca2..34119408 100644 --- a/man/cohortMethodPopulationCharacteristicsServer.Rd +++ b/man/cohortMethodPopulationCharacteristicsServer.Rd @@ -7,7 +7,6 @@ cohortMethodPopulationCharacteristicsServer( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings ) @@ -17,8 +16,6 @@ cohortMethodPopulationCharacteristicsServer( \item{selectedRow}{the selected row from the main results table} -\item{inputParams}{the selected study parameters of interest} - \item{connectionHandler}{the connection to the PLE results database} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} diff --git a/man/cohortMethodPowerServer.Rd b/man/cohortMethodPowerServer.Rd index 023f8251..d9b9b201 100644 --- a/man/cohortMethodPowerServer.Rd +++ b/man/cohortMethodPowerServer.Rd @@ -7,10 +7,8 @@ cohortMethodPowerServer( id, selectedRow, - inputParams, connectionHandler, - resultDatabaseSettings, - metaAnalysisDbIds = NULL + resultDatabaseSettings ) } \arguments{ @@ -18,13 +16,9 @@ cohortMethodPowerServer( \item{selectedRow}{the selected row from the main results table} -\item{inputParams}{the selected study parameters of interest} - \item{connectionHandler}{the connection to the PLE results database} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} - -\item{metaAnalysisDbIds}{metaAnalysisDbIds} } \value{ the PLE systematic error power server diff --git a/man/cohortMethodPropensityModelServer.Rd b/man/cohortMethodPropensityModelServer.Rd index f0ce6248..1c0ab242 100644 --- a/man/cohortMethodPropensityModelServer.Rd +++ b/man/cohortMethodPropensityModelServer.Rd @@ -7,7 +7,6 @@ cohortMethodPropensityModelServer( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings ) @@ -17,8 +16,6 @@ cohortMethodPropensityModelServer( \item{selectedRow}{the selected row from the main results table} -\item{inputParams}{the selected study parameters of interest} - \item{connectionHandler}{the connection to the PLE results database} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} diff --git a/man/cohortMethodPropensityScoreDistServer.Rd b/man/cohortMethodPropensityScoreDistServer.Rd index 910cbb5d..a5b57e16 100644 --- a/man/cohortMethodPropensityScoreDistServer.Rd +++ b/man/cohortMethodPropensityScoreDistServer.Rd @@ -7,7 +7,6 @@ cohortMethodPropensityScoreDistServer( id, selectedRow, - inputParams, connectionHandler, resultDatabaseSettings, metaAnalysisDbIds = F @@ -18,8 +17,6 @@ cohortMethodPropensityScoreDistServer( \item{selectedRow}{the selected row from the main results table} -\item{inputParams}{the selected study parameters of interest} - \item{connectionHandler}{the connection to the PLE results database} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} diff --git a/man/cohortMethodResultSummaryServer.Rd b/man/cohortMethodResultSummaryServer.Rd new file mode 100644 index 00000000..b32b7b21 --- /dev/null +++ b/man/cohortMethodResultSummaryServer.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cohort-method-resultSummary.R +\name{cohortMethodResultSummaryServer} +\alias{cohortMethodResultSummaryServer} +\title{The module server for rendering the PLE diagnostics summary} +\usage{ +cohortMethodResultSummaryServer( + id, + connectionHandler, + resultDatabaseSettings, + inputSelected +) +} +\arguments{ +\item{id}{the unique reference id for the module} + +\item{connectionHandler}{the connection to the PLE results database} + +\item{resultDatabaseSettings}{a list containing the result schema and prefixes} + +\item{inputSelected}{The target id, comparator id, outcome id and analysis id selected by the user} +} +\value{ +the PLE diagnostics summary results +} +\description{ +The module server for rendering the PLE diagnostics summary +} diff --git a/man/cohortMethodResultSummaryViewer.Rd b/man/cohortMethodResultSummaryViewer.Rd new file mode 100644 index 00000000..d1545136 --- /dev/null +++ b/man/cohortMethodResultSummaryViewer.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cohort-method-resultSummary.R +\name{cohortMethodResultSummaryViewer} +\alias{cohortMethodResultSummaryViewer} +\title{The module viewer for rendering the cohort method results} +\usage{ +cohortMethodResultSummaryViewer(id) +} +\arguments{ +\item{id}{the unique reference id for the module} +} +\value{ +The user interface to the cohort method diagnostics viewer +} +\description{ +The module viewer for rendering the cohort method results +} diff --git a/man/cohortMethodResultsTableServer.Rd b/man/cohortMethodResultsTableServer.Rd deleted file mode 100644 index 4b86209e..00000000 --- a/man/cohortMethodResultsTableServer.Rd +++ /dev/null @@ -1,28 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohort-method-resultsTable.R -\name{cohortMethodResultsTableServer} -\alias{cohortMethodResultsTableServer} -\title{The module server for rendering the PLE results per current selections} -\usage{ -cohortMethodResultsTableServer( - id, - connectionHandler, - inputParams, - resultDatabaseSettings -) -} -\arguments{ -\item{id}{the unique reference id for the module} - -\item{connectionHandler}{the connection to the PLE results database} - -\item{inputParams}{the selected study parameters of interest} - -\item{resultDatabaseSettings}{a list containing the result schema and prefixes} -} -\value{ -the PLE main results table server server -} -\description{ -The module server for rendering the PLE results per current selections -} diff --git a/man/cohortMethodResultsTableViewer.Rd b/man/cohortMethodResultsTableViewer.Rd deleted file mode 100644 index b39e34c9..00000000 --- a/man/cohortMethodResultsTableViewer.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohort-method-resultsTable.R -\name{cohortMethodResultsTableViewer} -\alias{cohortMethodResultsTableViewer} -\title{The module viewer for rendering the PLE main results} -\usage{ -cohortMethodResultsTableViewer(id) -} -\arguments{ -\item{id}{the unique reference id for the module} -} -\value{ -The user interface to the PLE main results -} -\description{ -The module viewer for rendering the PLE main results -} diff --git a/man/cohortMethodSubgroupsServer.Rd b/man/cohortMethodSubgroupsServer.Rd deleted file mode 100644 index 7dfd6e81..00000000 --- a/man/cohortMethodSubgroupsServer.Rd +++ /dev/null @@ -1,34 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohort-method-subgroups.R -\name{cohortMethodSubgroupsServer} -\alias{cohortMethodSubgroupsServer} -\title{The module server for rendering the subgroup results} -\usage{ -cohortMethodSubgroupsServer( - id, - selectedRow, - inputParams, - exposureOfInterest, - outcomeOfInterest, - connectionHandler -) -} -\arguments{ -\item{id}{the unique reference id for the module} - -\item{selectedRow}{the selected row from the main results table} - -\item{inputParams}{the selected study parameters of interest} - -\item{exposureOfInterest}{exposureOfInterest} - -\item{outcomeOfInterest}{outcomeOfInterest} - -\item{connectionHandler}{connection} -} -\value{ -the PLE subgroup results server -} -\description{ -The module server for rendering the subgroup results -} diff --git a/man/cohortMethodSubgroupsViewer.Rd b/man/cohortMethodSubgroupsViewer.Rd deleted file mode 100644 index 1eeb52f5..00000000 --- a/man/cohortMethodSubgroupsViewer.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/cohort-method-subgroups.R -\name{cohortMethodSubgroupsViewer} -\alias{cohortMethodSubgroupsViewer} -\title{The module viewer for rendering the PLE subgroup results} -\usage{ -cohortMethodSubgroupsViewer(id) -} -\arguments{ -\item{id}{the unique reference id for the module} -} -\value{ -The user interface to the cohort method subgroup results module -} -\description{ -The module viewer for rendering the PLE subgroup results -} diff --git a/man/cohortMethodSystematicErrorServer.Rd b/man/cohortMethodSystematicErrorServer.Rd index d470cf17..3f44490e 100644 --- a/man/cohortMethodSystematicErrorServer.Rd +++ b/man/cohortMethodSystematicErrorServer.Rd @@ -7,10 +7,8 @@ cohortMethodSystematicErrorServer( id, selectedRow, - inputParams, connectionHandler, - resultDatabaseSettings, - metaAnalysisDbIds = NULL + resultDatabaseSettings ) } \arguments{ @@ -18,13 +16,9 @@ cohortMethodSystematicErrorServer( \item{selectedRow}{the selected row from the main results table} -\item{inputParams}{the selected study parameters of interest} - \item{connectionHandler}{the connection handler to the result databases} \item{resultDatabaseSettings}{a list containing the result schema and prefixes} - -\item{metaAnalysisDbIds}{metaAnalysisDbIds} } \value{ the PLE systematic error content server diff --git a/tests/testthat/test-cohort-method-CovariateBalance.R b/tests/testthat/test-cohort-method-CovariateBalance.R index dbe1a6db..53d12c52 100644 --- a/tests/testthat/test-cohort-method-CovariateBalance.R +++ b/tests/testthat/test-cohort-method-CovariateBalance.R @@ -4,14 +4,8 @@ shiny::testServer( app = cohortMethodCovariateBalanceServer, args = list( selectedRow = shiny::reactiveVal(NULL), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3 - )), connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - metaAnalysisDbIds = '1' + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { @@ -19,25 +13,120 @@ shiny::testServer( testthat::expect_true(is.null(balance())) # make sure this runs if we pick the first row - selectedRow(list(databaseId = '1', analysisId = 2, psStrategy = '')) + selectedRow( + list( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + analysisId = 2, + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome', + psStrategy = '' + ) + ) + + testthat::expect_true(!is.null(output$balanceSummaryPlot)) testthat::expect_true(!is.null(balance())) testthat::expect_true(nrow(balance())>0) - testthat::expect_true(!is.null(balancePlot())) - testthat::expect_true(!is.null(output$balancePlotCaption)) - #session$setInputs(plotHoverBalanceScatter = list( - # x = balance()$absBeforeMatchingStdDiff[1], - # y = balance()$absAfterMatchingStdDiff[1], - # domain = list(left = 0.9, right = 1, top = 3, bottom = 0), - # range = list(left = 3, right = 5, top = 3, bottom = 0) - # ) - #) - #testthat::expect_true(!is.null(output$hoverInfoBalanceScatter)) - ##testthat::expect_true(!is.null(balanceSummaryPlot())) - doesnt work testthat::expect_true(!is.null(output$balanceSummaryPlotCaption)) # check textsearch textSearchCohortMethod('heart') + + balance <- getCohortMethodCovariateBalanceShared( + connectionHandler = connectionHandlerCm, + resultDatabaseSettings = resultDatabaseSettingsCm, + targetId = 1, + comparatorId = 2, + databaseId = '1', + analysisId = 2) + + testthat::expect_true(!is.null(balance)) + testthat::expect_true(nrow(balance)>0) + + plot <- plotCohortMethodCovariateBalanceScatterPlotNew( + balance = balance, + beforeLabel = "Before propensity score adjustment", + afterLabel = "After propensity score adjustment" + ) + + testthat::expect_is(object = plot, class = 'plotly') + + }) + + + +test_that("plotCohortMethodCovariateBalanceSummary", { + + # not the output of getEstimationCovariateBalance - where does it come from?? + balance <- data.frame( + databaseId = rep(1,2), + #covariateId = 1, + #covariateName = '1', + #analysisId = 1, + #beforeMatchingMeanTreated = 1, + #beforeMatchingMeanComparator = 1, + #beforeMatchingStdDiff = 0, + #afterMatchingMeanTreated = 1, + #afterMatchingMeanComparator = 1, + #afterMatchingStdDiff = 0, + absBeforeMatchingStdDiff = c(0.1,0.4), + absAfterMatchingStdDiff = c(0.1,0.4), + x = rep(1,2), + ymin = rep(1,2), + lower = rep(1,2), + median = rep(1,2), + upper = rep(1,2), + ymax = rep(1,2), + covariateCount = rep(1,2), + type = c("Before matching","After matching") + ) + + # added test for this in covariatebal + #resP <- plotEstimationCovariateBalanceScatterPlotNew( + # balance = balance, + # beforeLabel = "Before matching", + # afterLabel = "After matching", + # textsearch = shiny::reactiveVal(NULL) + #) + #testthat::expect_true(inherits(resP, 'plotly')) + + balanceSummary <- data.frame( + databaseId = rep(1,2), + #covariateId = 1, + #covariateName = '1', + #analysisId = 1, + #beforeMatchingMeanTreated = 1, + #beforeMatchingMeanComparator = 1, + #beforeMatchingStdDiff = 0, + #afterMatchingMeanTreated = 1, + #afterMatchingMeanComparator = 1, + #afterMatchingStdDiff = 0, + x = rep(1,2), + ymin = rep(1,2), + lower = rep(1,2), + median = rep(1,2), + upper = rep(1,2), + ymax = rep(1,2), + covariateCount = rep(1,2), + type = c("Before matching","After matching") + ) + + resP <- plotCohortMethodCovariateBalanceSummary( + balanceSummary = balanceSummary, + threshold = 0, + beforeLabel = "Before matching", + afterLabel = "After matching" + ) + + testthat::expect_true(inherits(resP, 'gtable')) + +}) \ No newline at end of file diff --git a/tests/testthat/test-cohort-method-DiagnosticsSummary.R b/tests/testthat/test-cohort-method-DiagnosticsSummary.R index b0ea4297..c9993dcb 100644 --- a/tests/testthat/test-cohort-method-DiagnosticsSummary.R +++ b/tests/testthat/test-cohort-method-DiagnosticsSummary.R @@ -13,25 +13,6 @@ shiny::testServer( }) -test_that("styleColumns", { - - oid <- c(1,34) - names(oid) <- c('a','b') - aid <- c(3) - names(aid) <- c('none') - -colss <- styleColumns( - customColDefs = list(a=1), - outcomeIds = oid, - analysisIds = aid -) - -testthat::expect_is(colss, 'list') -names(colss) <- c('a', 'a_none', 'b_none') -testthat::expect_is(colss$b_none$style, 'function') -testthat::expect_equal(colss$b_none$style('Pass')$background,"#AFE1AF") -}) - test_that("diagnosticSummaryFormat", { datar <- function(){ @@ -48,59 +29,35 @@ testthat::expect_true(ncol(val) == 4) }) -test_that("getCmDiagCohorts", { - -cohortIds <- getCmDiagCohorts( - connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - type = 'target' - ) +test_that("getCmDiagnosticData", { +colDefs <- getColDefsCmDiag( + connectionHandler = connectionHandlerCm, + resultDatabaseSettings = resultDatabaseSettingsCm +) -testthat::expect_true(length(cohortIds) > 0) -}) +testthat::expect_is(colDefs, 'list') +testthat::expect_is(colDefs[[1]], 'colDef') -test_that("getCmDiagAnalyses", { - - analysisIds <- getCmDiagAnalyses( - connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm - ) - - testthat::expect_true(length(analysisIds) > 0) }) -test_that("getCmDiagAnalyses", { - - analysisIds <- getCmDiagAnalyses( - connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm - ) - - cohortIds <- getCmDiagCohorts( - connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - type = 'target' - ) +test_that("getCmDiagnosticData", { - outcomeIds <- getCmDiagCohorts( - connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - type = 'outcome' - ) - - comparatorIds <- getCmDiagCohorts( - connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - type = 'comparator' - ) + inputSelected <- function(){ + return( + list( + targetIds = 1, + comparatorIds = 2, + outcomeIds = 3, + analysesIds = c(1,2) + ) + ) + } + diag <- getCmDiagnosticsData( connectionHandler = connectionHandlerCm, resultDatabaseSettings = resultDatabaseSettingsCm, - targetIds = cohortIds, - outcomeIds = outcomeIds, - comparatorIds = comparatorIds, - analysisIds = analysisIds + inputSelected ) testthat::expect_true(nrow(diag) > 0) diff --git a/tests/testthat/test-cohort-method-ForestPlot.R b/tests/testthat/test-cohort-method-ForestPlot.R deleted file mode 100644 index 4d06b076..00000000 --- a/tests/testthat/test-cohort-method-ForestPlot.R +++ /dev/null @@ -1,21 +0,0 @@ -context("cohort-method-ForestPlot") - -shiny::testServer( - app = cohortMethodForestPlotServer, - args = list( - selectedRow = shiny::reactiveVal(NULL), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3 - )), - connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - metaAnalysisDbIds = NULL - ), - expr = { - - # doesnt seem to be currently used? - testthat::expect_true(is.null(forestPlot())) - - }) diff --git a/tests/testthat/test-cohort-method-KaplanMeier.R b/tests/testthat/test-cohort-method-KaplanMeier.R index a05a3d0d..a3bff823 100644 --- a/tests/testthat/test-cohort-method-KaplanMeier.R +++ b/tests/testthat/test-cohort-method-KaplanMeier.R @@ -4,14 +4,8 @@ shiny::testServer( app = cohortMethodKaplanMeierServer, args = list( selectedRow = shiny::reactiveVal(NULL), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3 - )), connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - metaAnalysisDbIds = '1' + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { @@ -19,7 +13,21 @@ shiny::testServer( testthat::expect_true(is.null(kaplanMeierPlot())) # make sure this runs if we pick the first row - selectedRow(list(databaseId = '1', analysisId = 2, psStrategy = '')) + selectedRow( + list( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + analysisId = 1, + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome', + psStrategy = '' + ) + ) testthat::expect_true(!is.null(kaplanMeierPlot())) testthat::expect_true(!is.null(output$kaplanMeierPlotPlotCaption)) diff --git a/tests/testthat/test-cohort-method-PopulationCharacteristics.R b/tests/testthat/test-cohort-method-PopulationCharacteristics.R index cd204937..80dd2f50 100644 --- a/tests/testthat/test-cohort-method-PopulationCharacteristics.R +++ b/tests/testthat/test-cohort-method-PopulationCharacteristics.R @@ -4,21 +4,27 @@ shiny::testServer( app = cohortMethodPopulationCharacteristicsServer, args = list( selectedRow = shiny::reactiveVal(NULL), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3 - )), connectionHandler = connectionHandlerCm, resultDatabaseSettings = resultDatabaseSettingsCm - #cohortTablePrefix = cohortTablePrefix, - #databaseTable = databaseTable, - #metaAnalysisDbIds = '1' ), expr = { # make sure this runs if we pick the first row - selectedRow(list(databaseId = '1', analysisId = 2, psStrategy = '')) + selectedRow( + list( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + analysisId = 1, + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome', + psStrategy = '' + ) + ) testthat::expect_true(!is.null(output$table1Table)) testthat::expect_true(!is.null(output$table1Caption)) diff --git a/tests/testthat/test-cohort-method-Power.R b/tests/testthat/test-cohort-method-Power.R index 6d85bb5f..770149a9 100644 --- a/tests/testthat/test-cohort-method-Power.R +++ b/tests/testthat/test-cohort-method-Power.R @@ -3,30 +3,52 @@ context("cohort-method-Power") shiny::testServer( app = cohortMethodPowerServer, args = list( - selectedRow = shiny::reactiveVal( - data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - targetSubjects = 100, comparatorSubjects = 100, - targetOutcomes = 10, comparatorOutcomes = 5, - targetDays = 1000, comparatorDays = 1000) - ), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3 - )), + selectedRow = shiny::reactiveVal(NULL), connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - #cohortTablePrefix = cohortTablePrefix, - #databaseTable = databaseTable, - metaAnalysisDbIds = NULL + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { #testthat::expect_true(is.null(output$powerTable)) - # make sure this runs if we pick the first row - #selectedRow(list(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F)) + followUp <- getCmFollowUpDist( + connectionHandler = connectionHandlerCm, + resultDatabaseSettings = resultDatabaseSettingsCm, + targetId = 1, + comparatorId = 2, + outcomeId = 3, + databaseId = '1', + analysisId = 2 + ) + testthat::expect_true(nrow(followUp)>0) + + tablet <- prepareCohortMethodFollowUpDistTable(followUp) + testthat::expect_true(nrow(tablet)>0) + # make sure this runs if we pick the first row + selectedRow( + data.frame( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome', + psStrategy = '', + analysisId = 2, + psStrategy = '', + unblind = F, + targetSubjects = 100, + comparatorSubjects = 100, + targetOutcomes = 10, + comparatorOutcomes = 5, + targetDays = 1000, + comparatorDays = 1000 + ) + ) testthat::expect_true(!is.null(output$powerTable)) testthat::expect_true(!is.null(output$powerTableCaption)) testthat::expect_true(!is.null(output$timeAtRiskTableCaption)) diff --git a/tests/testthat/test-cohort-method-PropensityScoreDist.R b/tests/testthat/test-cohort-method-PropensityScoreDist.R index 93ba27da..975f1c78 100644 --- a/tests/testthat/test-cohort-method-PropensityScoreDist.R +++ b/tests/testthat/test-cohort-method-PropensityScoreDist.R @@ -3,33 +3,50 @@ context("cohort-method-PropensityScoreDist") shiny::testServer( app = cohortMethodPropensityScoreDistServer, args = list( - selectedRow = shiny::reactiveVal(NULL - #data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - # targetSubjects = 100, comparatorSubjects = 100, - # targetOutcomes = 10, comparatorOutcomes = 5, - # targetDays = 1000, comparatorDays = 1000) - ), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3 - )), + selectedRow = shiny::reactiveVal(NULL), connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - metaAnalysisDbIds = NULL + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { testthat::expect_true(is.null(psDistPlot())) + ps <- getCohortMethodPs( + connectionHandler = connectionHandlerCm, + resultDatabaseSettings = resultDatabaseSettingsCm, + targetId = 1, + comparatorId = 2, + analysisId = 2, + databaseId = '1' + ) + + testthat::expect_true('preferenceScore' %in% colnames(ps)) + testthat::expect_true('targetDensity' %in% colnames(ps)) + testthat::expect_true('comparatorDensity' %in% colnames(ps)) + # make sure this runs if we pick the first row selectedRow( - - data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - targetSubjects = 100, comparatorSubjects = 100, - targetOutcomes = 10, comparatorOutcomes = 5, - targetDays = 1000, comparatorDays = 1000) - + list( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome', + psStrategy = '', + analysisId = 2, + psStrategy = '', + unblind = 0, + targetSubjects = 100, + comparatorSubjects = 100, + targetOutcomes = 10, + comparatorOutcomes = 5, + targetDays = 1000, + comparatorDays = 1000 + ) ) testthat::expect_true(!is.null(psDistPlot())) diff --git a/tests/testthat/test-cohort-method-ResultsTable.R b/tests/testthat/test-cohort-method-ResultsTable.R deleted file mode 100644 index bdf3a781..00000000 --- a/tests/testthat/test-cohort-method-ResultsTable.R +++ /dev/null @@ -1,39 +0,0 @@ -context("cohort-method-ResultsTable") - -shiny::testServer( - app = cohortMethodResultsTableServer, - args = list( - #selectedRow = shiny::reactiveVal(NULL - #data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - # targetSubjects = 100, comparatorSubjects = 100, - # targetOutcomes = 10, comparatorOutcomes = 5, - # targetDays = 1000, comparatorDays = 1000) - #), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3, - database = 1, - analysis = 1 - )), - connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm - ), - expr = { - - # check result table loads - testthat::expect_true(!is.null(resultSubset())) - - # select first row - testthat::expect_true(is.null(selectedRow())) - #reactable::updateReactable( - # outputId = "mainTable", - # selected = 1, - # session = session - # ) - session$setInputs(mainTable__reactable__selected = 1) - #session$setInputs(mainTable_rows_selected = 1) - testthat::expect_true(!is.null(selectedRow())) # could check columns - - - }) diff --git a/tests/testthat/test-cohort-method-Subgroups.R b/tests/testthat/test-cohort-method-Subgroups.R deleted file mode 100644 index 74a44997..00000000 --- a/tests/testthat/test-cohort-method-Subgroups.R +++ /dev/null @@ -1,53 +0,0 @@ -context("cohort-method-Subgroups") - - -# tests cannot be done due to getEstimationSubgroupResults() missing? -if(F){ -shiny::testServer( - app = cohortMethodSubgroupsServer, - args = list( - selectedRow = shiny::reactiveVal(NULL - #data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - # targetSubjects = 100, comparatorSubjects = 100, - # targetOutcomes = 10, comparatorOutcomes = 5, - # targetDays = 1000, comparatorDays = 1000) - ), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3, - database = 1, - analysis = 1 - )), - connectionHandler = connectionHandlerCm, - exposureOfInterest = list(exposureId = c(1,2), exposureName = c(1,2)), - outcomeOfInterest = list(outcomeId = 3, outcomeName = 3) - #resultsSchema = 'main', - #tablePrefix = 'cm_', - #cohortTablePrefix = cohortTablePrefix, - #databaseTable = databaseTable - #metaAnalysisDbIds = NULL - ), - expr = { - - # check result table loads - testthat::expect_true(is.null(interactionEffects())) - - # select first row - selectedRow( - data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - targetSubjects = 100, comparatorSubjects = 100, - targetOutcomes = 10, comparatorOutcomes = 5, - targetDays = 1000, comparatorDays = 1000) - ) - # setting selectedRow() activates the following - testthat::expect_true(!is.null(interactionEffects())) - - testthat::expect_true(!is.null(output$subgroupTableCaption)) - testthat::expect_true(!is.null(output$subgroupTable)) - - # add code to test blind works - - }) - -} diff --git a/tests/testthat/test-cohort-method-attrition.R b/tests/testthat/test-cohort-method-attrition.R index f47dfb04..f1439766 100644 --- a/tests/testthat/test-cohort-method-attrition.R +++ b/tests/testthat/test-cohort-method-attrition.R @@ -3,12 +3,9 @@ context("cohort-method-attrition") shiny::testServer( app = cohortMethodAttritionServer, args = list( - selectedRow = shiny::reactiveVal(NULL), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3 - )), + selectedRow = shiny::reactiveVal( + NULL + ), connectionHandler = connectionHandlerCm, resultDatabaseSettings = resultDatabaseSettingsCm ), @@ -18,7 +15,20 @@ shiny::testServer( testthat::expect_true(is.null(attritionPlot())) # make sure this runs if we pick the first row - selectedRow(list(databaseId = 'Eunomia', analysisId = 1)) + selectedRow( + list( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + analysisId = 2, + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome' + ) + ) testthat::expect_true(!is.null(attritionPlot())) }) diff --git a/tests/testthat/test-cohort-method-full-result.R b/tests/testthat/test-cohort-method-full-result.R new file mode 100644 index 00000000..5ad28c8d --- /dev/null +++ b/tests/testthat/test-cohort-method-full-result.R @@ -0,0 +1,34 @@ +context("cohort-method-full-result") + +shiny::testServer( + app = cohortMethodFullResultServer, + args = list( + connectionHandler = connectionHandlerCm, + resultDatabaseSettings = resultDatabaseSettingsCm, + selectedRow = shiny::reactiveVal(NULL) + ), + expr = { + + selectedRow( + list( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + analysisId = 2, + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome' + ) + ) + + }) + + +test_that("Test full ui", { + # Test ui + ui <- cohortMethodFullResultViewer('test') + checkmate::expect_list(ui) +}) \ No newline at end of file diff --git a/tests/testthat/test-cohort-method-main.R b/tests/testthat/test-cohort-method-main.R index a9833a18..6c5b4bc9 100644 --- a/tests/testthat/test-cohort-method-main.R +++ b/tests/testthat/test-cohort-method-main.R @@ -10,17 +10,7 @@ shiny::testServer( testthat::expect_true(inherits(connectionHandler,"ConnectionHandler")) - - testthat::expect_true(!is.null(output$targetWidget)) - testthat::expect_true(!is.null(output$comparatorWidget)) - testthat::expect_true(!is.null(output$outcomeWidget)) - testthat::expect_true(!is.null(output$databaseWidget)) - testthat::expect_true(!is.null(output$analysisWidget)) - - # check setting target updates inputParams - session$setInputs(target = '1') - testthat::expect_true(inputParams()$target == '1') - + testthat::expect_true(!is.null(targetIds)) }) diff --git a/tests/testthat/test-cohort-method-propensityModel.R b/tests/testthat/test-cohort-method-propensityModel.R index 568eff83..ca5e6b62 100644 --- a/tests/testthat/test-cohort-method-propensityModel.R +++ b/tests/testthat/test-cohort-method-propensityModel.R @@ -3,30 +3,38 @@ context("cohort-method-propensityModel") shiny::testServer( app = cohortMethodPropensityModelServer, args = list( - selectedRow = shiny::reactiveVal( - data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - targetSubjects = 100, comparatorSubjects = 100, - targetOutcomes = 10, comparatorOutcomes = 5, - targetDays = 1000, comparatorDays = 1000) - ), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3 - )), + selectedRow = shiny::reactiveVal(NULL), connectionHandler = connectionHandlerCm, resultDatabaseSettings = resultDatabaseSettingsCm - #cohortTablePrefix = cohortTablePrefix, - #databaseTable = databaseTable, - #metaAnalysisDbIds = NULL ), expr = { #testthat::expect_true(is.null(output$powerTable)) # make sure this runs if we pick the first row - #selectedRow(list(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F)) - + selectedRow( + list( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome', + psStrategy = '', + analysisId = 2, + psStrategy = '', + unblind = F, + targetSubjects = 100, + comparatorSubjects = 100, + targetOutcomes = 10, + comparatorOutcomes = 5, + targetDays = 1000, + comparatorDays = 1000 + ) + ) testthat::expect_true(!is.null(output$propensityModelTable)) diff --git a/tests/testthat/test-cohort-method-systematicError.R b/tests/testthat/test-cohort-method-systematicError.R index 95bd76ba..cfad0323 100644 --- a/tests/testthat/test-cohort-method-systematicError.R +++ b/tests/testthat/test-cohort-method-systematicError.R @@ -5,22 +5,9 @@ context("cohort-method-systematicError") shiny::testServer( app = cohortMethodSystematicErrorServer, args = list( - selectedRow = shiny::reactiveVal(NULL - #data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - # targetSubjects = 100, comparatorSubjects = 100, - # targetOutcomes = 10, comparatorOutcomes = 5, - # targetDays = 1000, comparatorDays = 1000) - ), - inputParams = shiny::reactiveVal(list( - target = 1, - comparator = 2, - outcome = 3, - database = 1, - analysis = 1 - )), + selectedRow = shiny::reactiveVal(NULL), connectionHandler = connectionHandlerCm, - resultDatabaseSettings = resultDatabaseSettingsCm, - metaAnalysisDbIds = 1 + resultDatabaseSettings = resultDatabaseSettingsCm ), expr = { @@ -29,15 +16,56 @@ shiny::testServer( # select first row selectedRow( - data.frame(databaseId = '1', analysisId = 2, psStrategy = '', unblind = F, - targetSubjects = 100, comparatorSubjects = 100, - targetOutcomes = 10, comparatorOutcomes = 5, - targetDays = 1000, comparatorDays = 1000) + list( + databaseId = '1', + cdmSourceAbbreviation = 'Eunomia', + description = 'madeup', + target = 'test target', + targetId = 1, + comparatorId = 2, + comparator = 'test comparator', + outcomeId = 3, + outcome = 'test outcome', + psStrategy = '', + analysisId = 2, + psStrategy = '', + unblind = 0, + targetSubjects = 100, + comparatorSubjects = 100, + targetOutcomes = 10, + comparatorOutcomes = 5, + targetDays = 1000, + comparatorDays = 1000 + ) ) # setting selectedRow() activates the following ##testthat::expect_true(!is.null(systematicErrorPlot())) - ##testthat::expect_true(!is.null(output$systematicErrorPlot)) + testthat::expect_true(!is.null(output$systematicErrorPlot)) ##testthat::expect_true(!is.null(systematicErrorSummaryPlot())) ##testthat::expect_true(!is.null(output$systematicErrorSummaryPlot)) }) + + + +test_that("plotCohortMethodScatter", { + + + controlResults <- data.frame( + databaseId = 1:10, + seLogRr = runif(10), + logRr = runif(10), + ci95Lb = runif(10), + ci95Ub = runif(10), + effectSize = runif(10), + calibratedLogRr = runif(10), + calibratedSeLogRr = runif(10), + calibratedCi95Lb = runif(10), + calibratedCi95Ub = runif(10), + trueRr = rep(1,10) + + ) + resP <- plotCohortMethodScatter(controlResults) + testthat::expect_true(inherits(resP, 'ggplot')) + +}) diff --git a/tests/testthat/test-evidence-synth-main.R b/tests/testthat/test-evidence-synth-main.R index 12ececae..3fd7d382 100644 --- a/tests/testthat/test-evidence-synth-main.R +++ b/tests/testthat/test-evidence-synth-main.R @@ -9,13 +9,21 @@ shiny::testServer(evidenceSynthesisServer, args = list( expect_true(length(targetIds) > 0) expect_true(length(outcomeIds) > 0) - #session$setInputs( - # `input-selection-targetId` = 1, - # `input-selection-outcomeId` = 3, - # `input-selection-generate` = 1 - #) + inputSelected( + list( + targetId = targetIds[1], + targetIds = targetIds[1], + target = 'test target', + comparatorId = 2, + comparator = 'test comparator', + outcome = 'test outcome', + outcomeId = 3, + outcomeIds = 3 + ) + ) + + testthat::expect_is(output$esCohortMethodPlot, 'list') - inputSelected(list(targetId = targetIds[1], outcomeId = 3)) testthat::expect_true( nrow(unique(rbind(data(),data2()))) >0 ) testthat::expect_equal(as.double(inputSelected()$outcomeId), 3) diff --git a/tests/testthat/test-helpers-cohort-methodPlotsAndTables.R b/tests/testthat/test-helpers-cohort-methodPlotsAndTables.R deleted file mode 100644 index c81e96cd..00000000 --- a/tests/testthat/test-helpers-cohort-methodPlotsAndTables.R +++ /dev/null @@ -1,190 +0,0 @@ -context('tests-helpers-cohort-methodPlotsAndTables') - -test_that("Subgroup stuff", { - -subgroupRes <- getCohortMethodSubgroupResults( - connectionHandler = connectionHandlerCm, - targetIds = 1, - comparatorIds = 2, - outcomeIds = 3, - databaseIds = 1, - analysisIds = 1, - subgroupIds = 372328212, - cmInteractionResult = data.frame( - targetId = 1, - comparatorId = 2, - outcomeId = 3, - databaseId = 1, - analysisId = 1, - interactionCovariateId = 372328212, - targetSubjects = 10, - comparatorSubjects =10, - rrr = 1, - ci95Lb = 1, - ci95Ub = 1, - p = 1, - calibratedP = 1 - ), - covariate = list( - covariateId = 372328212, - covariateName = 'test', - databaseId = 1 - ) - ) - -testthat::expect_true(nrow(subgroupRes) > 0) - -res <- prepareCohortMethodSubgroupTable(subgroupResults = subgroupRes, output = "latex") -testthat::expect_true(nrow(res) > 0) - -}) - - -test_that("CovariateBalance stuff", { - - # not the output of getEstimationCovariateBalance - where does it come from?? - balance <- data.frame( - databaseId = rep(1,2), - #covariateId = 1, - #covariateName = '1', - #analysisId = 1, - #beforeMatchingMeanTreated = 1, - #beforeMatchingMeanComparator = 1, - #beforeMatchingStdDiff = 0, - #afterMatchingMeanTreated = 1, - #afterMatchingMeanComparator = 1, - #afterMatchingStdDiff = 0, - absBeforeMatchingStdDiff = c(0.1,0.4), - absAfterMatchingStdDiff = c(0.1,0.4), - x = rep(1,2), - ymin = rep(1,2), - lower = rep(1,2), - median = rep(1,2), - upper = rep(1,2), - ymax = rep(1,2), - covariateCount = rep(1,2), - type = c("Before matching","After matching") - ) - - # added test for this in covariatebal - #resP <- plotEstimationCovariateBalanceScatterPlotNew( - # balance = balance, - # beforeLabel = "Before matching", - # afterLabel = "After matching", - # textsearch = shiny::reactiveVal(NULL) - #) - #testthat::expect_true(inherits(resP, 'plotly')) - - balanceSummary <- data.frame( - databaseId = rep(1,2), - #covariateId = 1, - #covariateName = '1', - #analysisId = 1, - #beforeMatchingMeanTreated = 1, - #beforeMatchingMeanComparator = 1, - #beforeMatchingStdDiff = 0, - #afterMatchingMeanTreated = 1, - #afterMatchingMeanComparator = 1, - #afterMatchingStdDiff = 0, - x = rep(1,2), - ymin = rep(1,2), - lower = rep(1,2), - median = rep(1,2), - upper = rep(1,2), - ymax = rep(1,2), - covariateCount = rep(1,2), - type = c("Before matching","After matching") - ) - - resP <- plotCohortMethodCovariateBalanceSummary( - balanceSummary = balanceSummary, - threshold = 0, - beforeLabel = "Before matching", - afterLabel = "After matching" - ) - - testthat::expect_true(inherits(resP, 'gtable')) - -}) - -test_that("nonZeroCohortMethodHazardRatio", { - - testthat::expect_equal( - nonZeroCohortMethodHazardRatio(hrLower = 0.5, hrUpper = 0.5, terms = 'test'), - 'test' - ) - testthat::expect_equal( - nonZeroCohortMethodHazardRatio(hrLower = 1.1, hrUpper = 1.1, terms = c('test','sec')), - 'sec' - ) - testthat::expect_equal( - nonZeroCohortMethodHazardRatio(hrLower = 0.9, hrUpper = 1.1, terms = c('test','sec','3')), - '3' - ) - -}) - -test_that("goodCohortMethodPropensityScore", { - testthat::expect_equal(goodCohortMethodPropensityScore(0.5), F) - testthat::expect_equal(goodCohortMethodPropensityScore(1), F) - testthat::expect_equal(goodCohortMethodPropensityScore(1.01), T) -}) - -test_that("goodCohortMethodSystematicBias", { - testthat::expect_equal(goodCohortMethodSystematicBias(0.5), F) - testthat::expect_equal(goodCohortMethodSystematicBias(1), F) - testthat::expect_equal(goodCohortMethodSystematicBias(1.01), T) -}) - - -test_that("plotCohortMethodForest", { - - results <- data.frame( - databaseId = 1:10, - seLogRr = runif(10), - logRr = runif(10), - ci95Lb = runif(10), - ci95Ub = runif(10), - calibratedLogRr = runif(10), - calibratedCi95Lb = runif(10), - calibratedCi95Ub = runif(10), - i2 = runif(10) - ) -resP <- plotCohortMethodForest(results, limits = c(0.1, 10), metaAnalysisDbIds = 2) -testthat::expect_true(inherits(resP, 'gtable')) - -}) - -test_that("plotCohortMethodScatter", { - - - controlResults <- data.frame( - databaseId = 1:10, - seLogRr = runif(10), - logRr = runif(10), - ci95Lb = runif(10), - ci95Ub = runif(10), - effectSize = runif(10), - calibratedLogRr = runif(10), - calibratedSeLogRr = runif(10), - calibratedCi95Lb = runif(10), - calibratedCi95Ub = runif(10), - trueRr = rep(1,10) - - ) - resP <- plotCohortMethodScatter(controlResults) - testthat::expect_true(inherits(resP, 'ggplot')) - -}) - -## Functions remaining to add tests: -# getCohortMethodTcoChoice -# getCohortMethodTargetChoices -# getCohortMethodComparatorChoices -# getCohortMethodOutcomeChoices -# getCohortMethodDatabaseChoices -# getCmAnalysisOptions -# getAllCohortMethodResults -# getCohortMethodControlResults -# getCohortMethodStudyPeriod -# getCohortMethodNegativeControlEstimates From 00842e118176e962696d242b4a2f486a4a0b3838 Mon Sep 17 00:00:00 2001 From: jreps Date: Fri, 4 Aug 2023 16:41:12 -0400 Subject: [PATCH 20/20] standardizing sccs - standardizing sccs --- R/helpers-sccsDataPulls.R | 69 --- R/helpers-sccsPlots.R | 6 - R/sccs-diagnosticsSummary.R | 106 +---- R/sccs-main.R | 586 ++---------------------- R/sccs-results-full.R | 388 ++++++++++++++++ R/sccs-results.R | 318 +++++++++++++ tests/testthat/test-helpers-sccsPlots.R | 5 - tests/testthat/test-sccs-main.R | 37 +- tests/testthat/test-sccs-results-full.R | 42 ++ tests/testthat/test-sccs-results.R | 29 ++ 10 files changed, 825 insertions(+), 761 deletions(-) create mode 100644 R/sccs-results-full.R create mode 100644 R/sccs-results.R create mode 100644 tests/testthat/test-sccs-results-full.R create mode 100644 tests/testthat/test-sccs-results.R diff --git a/R/helpers-sccsDataPulls.R b/R/helpers-sccsDataPulls.R index cc0ee126..d2b7e928 100644 --- a/R/helpers-sccsDataPulls.R +++ b/R/helpers-sccsDataPulls.R @@ -130,76 +130,7 @@ sccsGetAnalyses <- function( return(result2) } -getSccsResults <- function(connectionHandler, - resultDatabaseSettings, - exposureIds, - outcomeIds, - databaseIds, - analysisIds) { - sql <- " - SELECT - sr.*, - ds.cdm_source_abbreviation as database_name, - sds.mdrr, - sds.ease, - sds.time_trend_p, - sds.pre_exposure_p, - sds.mdrr_diagnostic, - sds.ease_diagnostic, - sds.time_trend_diagnostic, - sds.pre_exposure_diagnostic, - sds.unblind, - sc.covariate_name, - sc.era_id, - sc.covariate_analysis_id, - a.description, - eos.outcome_id - FROM @schema.@sccs_table_prefixresult sr - INNER JOIN - @schema.@database_table_prefix@database_table ds - ON sr.database_id = ds.database_id - INNER JOIN - @schema.@sccs_table_prefixdiagnostics_summary sds ON ( - sds.exposures_outcome_set_id = sr.exposures_outcome_set_id AND - sds.database_id = sr.database_id AND - sds.analysis_id = sr.analysis_id AND - sds.covariate_id = sr.covariate_id - ) - INNER JOIN - @schema.@sccs_table_prefixcovariate sc ON ( - sc.exposures_outcome_set_id = sr.exposures_outcome_set_id AND - sc.database_id = sr.database_id AND - sc.analysis_id = sr.analysis_id AND - sc.covariate_id = sr.covariate_id - ) - INNER JOIN @schema.@sccs_table_prefixexposures_outcome_set eos - ON - eos.exposures_outcome_set_id = sr.exposures_outcome_set_id - INNER JOIN - @schema.@sccs_table_prefixanalysis a - on a.analysis_id = sr.analysis_id - - WHERE sr.analysis_id IN (@analysis_ids) - AND sr.database_id IN (@database_ids) - AND eos.outcome_id IN (@outcome_ids) - AND sc.era_id IN (@exposure_ids) - " - results <- connectionHandler$queryDb( - sql, - schema = resultDatabaseSettings$schema, - database_table_prefix = resultDatabaseSettings$databaseTablePrefix, - database_table = resultDatabaseSettings$databaseTable, - sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, - database_ids = paste(quoteLiterals(databaseIds), collapse = ','), - analysis_ids = analysisIds, - outcome_ids = paste(outcomeIds, collapse = ','), - exposure_ids = paste(exposureIds, collapse = ','), - snakeCaseToCamelCase = TRUE - ) - - return(results) -} getSccsModel <- function(connectionHandler, resultDatabaseSettings, diff --git a/R/helpers-sccsPlots.R b/R/helpers-sccsPlots.R index 8c545eb3..e8308c36 100644 --- a/R/helpers-sccsPlots.R +++ b/R/helpers-sccsPlots.R @@ -1,9 +1,3 @@ -prettyHr <- function(x) { - result <- sprintf("%.2f", x) - result[is.na(x) | x > 100] <- "NA" - return(result) -} - convertToStartDate <- function(year, month) { return(as.Date(sprintf( "%s-%s-%s", diff --git a/R/sccs-diagnosticsSummary.R b/R/sccs-diagnosticsSummary.R index e538e51a..c6d238eb 100644 --- a/R/sccs-diagnosticsSummary.R +++ b/R/sccs-diagnosticsSummary.R @@ -20,13 +20,12 @@ sccsDiagnosticsSummaryViewer <- function(id) { ns <- shiny::NS(id) - shiny::div( - inputSelectionViewer(ns("input-selection")), + shinydashboard::box( + status = 'info', + width = '100%', + title = shiny::span('Diagnostic Results'), + solidHeader = TRUE, - shiny::conditionalPanel( - condition = 'input.generate != 0', - ns = shiny::NS(ns("input-selection")), - shiny::tabsetPanel( type = 'pills', id = ns('diagnosticsTablePanel'), @@ -40,107 +39,29 @@ sccsDiagnosticsSummaryViewer <- function(id) { ) ) ) - ) } sccsDiagnosticsSummaryServer <- function( id, connectionHandler, - resultDatabaseSettings + resultDatabaseSettings, + inputSelected ) { shiny::moduleServer( id, function(input, output, session) { - targetIds <- getSccsDiagTargets( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - outcomeIds <- getSccsDiagOutcomes( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - analysisIds <- getSccsDiagAnalyses( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) - inputSelected <- inputSelectionServer( - id = "input-selection", - inputSettingList = list( - createInputSetting( - rowNumber = 1, - columnWidth = 6, - varName = 'targetIds', - uiFunction = 'shinyWidgets::pickerInput', - uiInputs = list( - label = 'Target: ', - choices = targetIds, - selected = targetIds[1], - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ), - createInputSetting( - rowNumber = 1, - columnWidth = 6, - varName = 'outcomeIds', - uiFunction = 'shinyWidgets::pickerInput', - uiInputs = list( - label = 'Outcome: ', - choices = outcomeIds, - selected = outcomeIds[1], - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ), - - createInputSetting( - rowNumber = 2, - columnWidth = 12, - varName = 'analysisIds', - uiFunction = 'shinyWidgets::pickerInput', - uiInputs = list( - label = 'Analysis: ', - choices = analysisIds, - selected = analysisIds[1], - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ) - ) - ) - data <- shiny::reactive({ + getSccsAllDiagnosticsSummary( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings, - targetIds = inputSelected()$targetIds, - outcomeIds = inputSelected()$outcomeIds, - analysisIds = inputSelected()$analysisIds + targetIds = inputSelected()$exposure, + outcomeIds = inputSelected()$outcome, + analysisIds = inputSelected()$analysis ) }) @@ -370,6 +291,11 @@ getSccsAllDiagnosticsSummary <- function( outcomeIds, analysisIds = NULL ) { + + if(is.null(targetIds)){ + return(NULL) + } + sql <- " SELECT d.cdm_source_abbreviation as database_name, diff --git a/R/sccs-main.R b/R/sccs-main.R index 617d338b..6e518992 100644 --- a/R/sccs-main.R +++ b/R/sccs-main.R @@ -21,123 +21,27 @@ sccsView <- function(id = "sccs-module") { #shiny::htmlTemplate(system.file("cohort-diagnostics-www", "cohortCounts.html", package = utils::packageName())) ), + inputSelectionViewer(ns("input-selection-sccs")), - shiny::tabsetPanel( - type = 'pills', - id = ns("mainTabsetPanel"), - shiny::tabPanel( - title = "Diagnostics", - sccsDiagnosticsSummaryViewer(ns("sccsDiganostics")) - ), - shiny::tabPanel( - title = "Results", - - inputSelectionViewer(ns("input-selection-sccs")), + shiny::conditionalPanel( + condition = 'input.generate != 0', + ns = shiny::NS(ns("input-selection-sccs")), + + shiny::tabsetPanel( + type = 'pills', + id = ns("mainTabsetPanel"), - shiny::conditionalPanel( - condition = 'input.generate != 0', - ns = shiny::NS(ns("input-selection-sccs")), - - shinydashboard::box( - width = '100%', - - # add database/analysis options here - - reactable::reactableOutput(ns("mainTable")), - - # move these to new tab? - shiny::conditionalPanel( - "output.rowIsSelected == true", - ns = ns, - shiny::tabsetPanel( - id = ns("detailsTabsetPanel"), - shiny::tabPanel( - "Power", - shiny::div(shiny::strong("Table 1."), "For each variable of interest: the number of cases (people with at least one outcome), the number of years those people were observed, the number of outcomes, the number of subjects with at least one exposure, the number of patient-years exposed, the number of outcomes while exposed, and the minimum detectable relative risk (MDRR)."), - shiny::tableOutput(ns("powerTable")) - ), - shiny::tabPanel( - "Attrition", - shiny::plotOutput(ns("attritionPlot"), width = 600, height = 500), - shiny::div( - shiny::strong("Figure 1."), - "Attrition, showing the number of cases (number of subjects with at least one outcome), and number of outcomes (number of ocurrences of the outcome) after each step in the study.") - ), - shiny::tabPanel( - "Model", - shiny::tabsetPanel( - id = ns("modelTabsetPanel"), - shiny::tabPanel( - "Model coefficients", - shiny::div( - shiny::strong("Table 2."), - "The fitted non-zero coefficent (incidence rate ratio) and 95 percent confidence interval for all variables in the model." - ), - shiny::tableOutput(ns("modelTable")) - ), - shiny::tabPanel( - "Age spline", - shiny::plotOutput(ns("ageSplinePlot")), - shiny::div(shiny::strong("Figure 2a."), "Spline fitted for age.") - ), - shiny::tabPanel( - "Season spline", - shiny::plotOutput(ns("seasonSplinePlot")), - shiny::div(shiny::strong("Figure 2b."), "Spline fitted for season") - ), - shiny::tabPanel( - "Calendar time spline", - shiny::plotOutput(ns("calendarTimeSplinePlot")), - shiny::div(shiny::strong("Figure 2c."), "Spline fitted for calendar time") - ) - ) - ), - shiny::tabPanel( - "Spanning", - shiny::radioButtons(ns("spanningType"), label = "Type:", choices = c("Age", "Calendar time")), - shiny::plotOutput(ns("spanningPlot")), - shiny::div(shiny::strong("Figure 3."), "Number of subjects observed for 3 consecutive months, centered on the indicated month.") - ), - shiny::tabPanel( - "Time trend", - shiny::plotOutput(ns("timeTrendPlot"), height = 600), - shiny::div( - shiny::strong("Figure 4."), - "Per calendar month the number of people observed, the unadjusted rate of the outcome, and the rate of the outcome after adjusting for age, season, and calendar time, if specified in the model. Red indicates months where the adjusted rate was significantly different from the mean adjusted rate." - ) - ), - shiny::tabPanel( - "Time to event", - shiny::plotOutput(ns("timeToEventPlot")), - shiny::div( - shiny::strong("Figure 5."), - "The number of events and subjects observed per week relative to the start of the first exposure (indicated by the thick vertical line)." - ) - ), - shiny::tabPanel( - "Event dep. observation", - shiny::plotOutput(ns("eventDepObservationPlot")), - shiny::div(shiny::strong("Figure 6."), "Histograms for the number of months between the first occurrence of the outcome and the end of observation, stratified by whether the end of observation was censored (inferred as not being equal to the end of database time), or uncensored (inferred as having the subject still be observed at the end of database time)." - ) - ), - shiny::tabPanel( - "Systematic error", - shiny::plotOutput(ns("controlEstimatesPlot")), - shiny::div(shiny::strong("Figure 7."), "Systematic error. Effect size estimates for the negative controls (true incidence rate ratio = 1) - and positive controls (true incidence rate ratio > 1), before and after calibration. Estimates below the diagonal dashed - lines are statistically significant (alpha = 0.05) different from the true effect size. A well-calibrated - estimator should have the true effect size within the 95 percent confidence interval 95 percent of times.") - ), - shiny::tabPanel( - "Diagnostics summary", - shiny::tableOutput(ns("diagnosticsSummary")) - - ) - ) + shiny::tabPanel( + title = "Diagnostics", + sccsDiagnosticsSummaryViewer(ns("sccsDiganostics")) + ), + shiny::tabPanel( + title = 'Results', + sccsResultsViewer(ns("sccsResults")), ) - )) - ) - ) + ) + + ) # end condition ) } @@ -162,11 +66,6 @@ sccsServer <- function( ) { ns <- shiny::NS(id) - withTooltip <- function(value, tooltip, ...) { - shiny::div(style = "text-decoration: underline; text-decoration-style: dotted; cursor: help", - tippy::tippy(value, tooltip, ...)) - } - # create functions to result list outcomes <- sccsGetOutcomes( connectionHandler = connectionHandler, @@ -176,10 +75,6 @@ sccsServer <- function( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings ) - databases <- sccsGetDatabases( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings - ) analyses <- sccsGetAnalyses( connectionHandler = connectionHandler, resultDatabaseSettings = resultDatabaseSettings @@ -188,13 +83,6 @@ sccsServer <- function( shiny::moduleServer(id, function(input, output, session) { - - sccsDiagnosticsSummaryServer( - id = "sccsDiganostics", - connectionHandler = connectionHandler, - resultDatabaseSettings - ) - inputSelected <- inputSelectionServer( id = "input-selection-sccs", inputSettingList = list( @@ -204,7 +92,7 @@ sccsServer <- function( varName = 'exposure', uiFunction = 'shinyWidgets::pickerInput', uiInputs = list( - label = 'Exposure: ', + label = 'Target: ', choices = exposures, selected = exposures[1], multiple = F, @@ -238,26 +126,6 @@ sccsServer <- function( ) ) ), - createInputSetting( - rowNumber = 2, - columnWidth = 6, - varName = 'database', - uiFunction = 'shinyWidgets::pickerInput', - uiInputs = list( - label = 'Database: ', - choices = databases, - selected = databases, - multiple = T, - options = shinyWidgets::pickerOptions( - actionsBox = TRUE, - liveSearch = TRUE, - size = 10, - liveSearchStyle = "contains", - liveSearchPlaceholder = "Type here to search", - virtualScroll = 50 - ) - ) - ), createInputSetting( rowNumber = 2, columnWidth = 6, @@ -280,413 +148,21 @@ sccsServer <- function( ) ) ) - - - # inputSelected()$analysis - # inputSelected()$outcome - # inputSelected()$exposure - # inputSelected()$database - # currently reacts to database, analysis and exposure/outcomes to return data - resultSubset <- shiny::reactive({ - results <- getSccsResults(connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - exposureIds = inputSelected()$exposure, - outcomeIds = inputSelected()$outcome, - databaseIds = inputSelected()$database, - analysisIds = inputSelected()$analysis) - - results <- results[order(results$analysisId),] - - idx <- (results$unblind == 0) - if (any(idx)) { - results$rr[idx] <- NA - results$ci95Ub[idx] <- NA - results$ci95Lb[idx] <- NA - results$logRr[idx] <- NA - results$seLogRr[idx] <- NA - results$p[idx] <- NA - results$calibratedRr[idx] <- NA - results$calibratedCi95Ub[idx] <- NA - results$calibratedCi95Lb[idx] <- NA - results$calibratedLogRr[idx] <- NA - results$calibratedSeLogRr[idx] <- NA - results$calibratedP[idx] <- NA - } - - results$rr <- prettyHr(results$rr) - results$ci95Lb <- prettyHr(results$ci95Lb) - results$ci95Ub <- prettyHr(results$ci95Ub) - results$p <- prettyHr(results$p) - results$calibratedRr <- prettyHr(results$calibratedRr) - results$calibratedCi95Lb <- prettyHr(results$calibratedCi95Lb) - results$calibratedCi95Ub <- prettyHr(results$calibratedCi95Ub) - results$calibratedP <- prettyHr(results$calibratedP) - - return(results) - }) - - # add database/analysis select above result table? - output$mainTable <- reactable::renderReactable({ - reactable::reactable( - data = resultSubset() %>% - dplyr::select("description", - "databaseName", - "rr", - "ci95Lb", - "ci95Ub", - "p", - "calibratedRr", - "calibratedCi95Lb", - "calibratedCi95Ub", - "calibratedP"), - rownames = FALSE, - defaultPageSize = 15, - showPageSizeOptions = T, - onClick = 'select', - selection = 'single', - striped = T, - - columns = list( - description = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Analysis", - "Analysis" - )), - databaseName = reactable::colDef( - filterable = TRUE, - header = withTooltip( - "Data source", - "Data source" - )), - rr = reactable::colDef( - header = withTooltip( - "IRR", - "Incidence rate ratio (uncalibrated)" - )), - ci95Lb = reactable::colDef( - header = withTooltip( - "LB", - "Lower bound of the 95 percent confidence interval (uncalibrated)" - )), - ci95Ub = reactable::colDef( - header = withTooltip( - "UB", - "Upper bound of the 95 percent confidence interval (uncalibrated)" - )), - p = reactable::colDef( - header = withTooltip( - "P", - "Two-sided p-value (uncalibrated)" - )), - calibratedRr = reactable::colDef( - header = withTooltip( - "Cal.IRR", - "Incidence rate ratio (calibrated)" - )), - calibratedCi95Lb = reactable::colDef( - header = withTooltip( - "Cal.LB", - "Lower bound of the 95 percent confidence interval (calibrated)" - )), - calibratedCi95Ub = reactable::colDef( - header = withTooltip( - "Cal.UB", - "Upper bound of the 95 percent confidence interval (calibrated)" - )), - calibratedP = reactable::colDef( - header = withTooltip( - "Cal.P", - "Two-sided p-value (calibrated)" - )) - ) - ) - - }) - - selectedRow <- shiny::reactive({ - if (getOption("shiny-test-env-enabled", default = FALSE)) { - idx <- input$mainTableRowInput - } else { - idx <- reactable::getReactableState( - outputId = 'mainTable', - name = 'selected' - ) - } - - if (is.null(idx)) { - return(NULL) - } else { - subset <- resultSubset() - if (nrow(subset) == 0) { - return(NULL) - } - row <- subset[idx,] - return(row) - } - }) - - output$rowIsSelected <- shiny::reactive({ - return(!is.null(selectedRow())) - }) - - shiny::outputOptions(output, "rowIsSelected", suspendWhenHidden = FALSE) - - - # move these to a different submodule? - output$powerTable <- shiny::renderTable({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - resTargetTable <- row %>% - dplyr::mutate(outcomeEvents = ifelse(.data$unblind == 1, .data$outcomeEvents, NA)) %>% - dplyr::select( - "covariateName", - "outcomeSubjects", - "observedDays", - "outcomeEvents", - "covariateSubjects", - "covariateDays", - "covariateOutcomes", - "mdrr" - ) %>% - dplyr::mutate(observedDays = .data$observedDays / 365.25, - covariateDays = .data$covariateDays / 365.25) - colnames(resTargetTable) <- c("Variable", - "Cases", - "Years observed", - "Outcomes", - "Persons exposed", - "Years exposed", - "Outcomes while exposed", - "MDRR") - return(resTargetTable) - } - }) - - output$attritionPlot <- shiny::renderPlot({ - - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - attrition <- getSccsAttrition( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId, - covariateId = row$covariateId - ) - drawAttritionDiagram(attrition) - } - }) - - output$modelTable <- shiny::renderTable({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - resTargetTable <- getSccsModel( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - exposureId = row$eraId, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId - ) - - resTargetTable <- resTargetTable %>% - dplyr::arrange(.data$covariateId) %>% - dplyr::select(-"covariateId") - - colnames(resTargetTable) <- c("Variable", - "IRR", - "LB", - "UB") - return(resTargetTable) - } - }) - - output$timeTrendPlot <- shiny::renderPlot({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - timeTrend <- getSccsTimeTrend( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - exposureId = row$eraId, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId - ) - plotTimeTrend(timeTrend) - } - }) - - output$timeToEventPlot <- shiny::renderPlot({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - timeToEvent <- getSccsTimeToEvent( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - exposureId = row$eraId, - covariateId = row$covariateId, - databaseId = row$databaseId, - analysisId = row$analysisId - ) - plotTimeToEventSccs(timeToEvent) - } - }) - - output$eventDepObservationPlot <- shiny::renderPlot({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - eventDepObservation <- getSccsEventDepObservation( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId - ) - plotEventDepObservation(eventDepObservation) - } - }) - - output$spanningPlot <- shiny::renderPlot({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - if (input$spanningType == "Age") { - ageSpanning <- getSccsAgeSpanning( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId - ) - plotSpanning(ageSpanning, type = "age") - } else { - calendarTimeSpanning <- getSccsCalendarTimeSpanning( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId - ) - plotSpanning(calendarTimeSpanning, type = "calendar time") - } - } - }) - - output$ageSplinePlot <- shiny::renderPlot({ - - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - ageSpline <- getSccsSpline( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId, - splineType = "age" - ) - if (nrow(ageSpline) == 0) { - return(NULL) - } - plotAgeSpline(ageSpline) - } - }) - - output$seasonSplinePlot <- shiny::renderPlot({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - seasonSpline <- getSccsSpline( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId, - splineType = "season" - ) - if (nrow(seasonSpline) == 0) { - return(NULL) - } - plotSeasonSpline(seasonSpline) - } - }) - - output$calendarTimeSplinePlot <- shiny::renderPlot({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - calendarTimeSpline <- getSccsSpline( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - databaseId = row$databaseId, - analysisId = row$analysisId, - splineType = "calendar time" - ) - if (nrow(calendarTimeSpline) == 0) { - return(NULL) - } - plotCalendarTimeSpline(calendarTimeSpline) - } - }) - - output$controlEstimatesPlot <- shiny::renderPlot({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - controlEstimates <- getSccsControlEstimates( - connectionHandler = connectionHandler, - resultDatabaseSettings, - covariateId = row$covariateId, - databaseId = row$databaseId, - analysisId = row$analysisId - ) - plotControlEstimates(controlEstimates) - } - }) - + sccsDiagnosticsSummaryServer( + id = "sccsDiganostics", + connectionHandler = connectionHandler, + resultDatabaseSettings, + inputSelected = inputSelected + ) - output$diagnosticsSummary <- shiny::renderTable({ - row <- selectedRow() - if (is.null(row)) { - return(NULL) - } else { - diagnosticsSummary <- getSccsDiagnosticsSummary( - connectionHandler = connectionHandler, - resultDatabaseSettings = resultDatabaseSettings, - outcomeId = row$outcomeId, - covariateId = row$covariateId, - databaseId = row$databaseId, - analysisId = row$analysisId, - exposureId = row$eraId - ) - - resTargetTable <- renderDiagnosticsSummary(diagnosticsSummary) - return(resTargetTable) - } - }) - + sccsResultsServer( + id = "sccsResults", + connectionHandler = connectionHandler, + resultDatabaseSettings, + inputSelected = inputSelected + ) }) } diff --git a/R/sccs-results-full.R b/R/sccs-results-full.R new file mode 100644 index 00000000..feb0d9ba --- /dev/null +++ b/R/sccs-results-full.R @@ -0,0 +1,388 @@ +sccsFullResultViewer <- function(id) { + ns <- shiny::NS(id) + + shinydashboard::box( + status = 'info', + width = '100%', + title = shiny::span('Result Explorer'), + solidHeader = TRUE, + + # add selected settings + shinydashboard::box( + status = 'warning', + width = "100%", + title = 'Selected: ', + collapsible = T, + shiny::uiOutput(ns('selection')) + ), + + shiny::tabsetPanel( + id = ns("fullTabsetPanel"), + type = 'pills', + + shiny::tabPanel( + "Power", + shiny::div(shiny::strong("Table 1."), "For each variable of interest: the number of cases (people with at least one outcome), the number of years those people were observed, the number of outcomes, the number of subjects with at least one exposure, the number of patient-years exposed, the number of outcomes while exposed, and the minimum detectable relative risk (MDRR)."), + shiny::tableOutput(ns("powerTable")) + ), + shiny::tabPanel( + "Attrition", + shiny::plotOutput(ns("attritionPlot"), width = 600, height = 500), + shiny::div( + shiny::strong("Figure 1."), + "Attrition, showing the number of cases (number of subjects with at least one outcome), and number of outcomes (number of ocurrences of the outcome) after each step in the study.") + ), + shiny::tabPanel( + "Model", + shiny::tabsetPanel( + id = ns("modelTabsetPanel"), + shiny::tabPanel( + "Model coefficients", + shiny::div( + shiny::strong("Table 2."), + "The fitted non-zero coefficent (incidence rate ratio) and 95 percent confidence interval for all variables in the model." + ), + shiny::tableOutput(ns("modelTable")) + ), + shiny::tabPanel( + "Age spline", + shiny::plotOutput(ns("ageSplinePlot")), + shiny::div(shiny::strong("Figure 2a."), "Spline fitted for age.") + ), + shiny::tabPanel( + "Season spline", + shiny::plotOutput(ns("seasonSplinePlot")), + shiny::div(shiny::strong("Figure 2b."), "Spline fitted for season") + ), + shiny::tabPanel( + "Calendar time spline", + shiny::plotOutput(ns("calendarTimeSplinePlot")), + shiny::div(shiny::strong("Figure 2c."), "Spline fitted for calendar time") + ) + ) + ), + shiny::tabPanel( + "Spanning", + shiny::radioButtons(ns("spanningType"), label = "Type:", choices = c("Age", "Calendar time")), + shiny::plotOutput(ns("spanningPlot")), + shiny::div(shiny::strong("Figure 3."), "Number of subjects observed for 3 consecutive months, centered on the indicated month.") + ), + shiny::tabPanel( + "Time trend", + shiny::plotOutput(ns("timeTrendPlot"), height = 600), + shiny::div( + shiny::strong("Figure 4."), + "Per calendar month the number of people observed, the unadjusted rate of the outcome, and the rate of the outcome after adjusting for age, season, and calendar time, if specified in the model. Red indicates months where the adjusted rate was significantly different from the mean adjusted rate." + ) + ), + shiny::tabPanel( + "Time to event", + shiny::plotOutput(ns("timeToEventPlot")), + shiny::div( + shiny::strong("Figure 5."), + "The number of events and subjects observed per week relative to the start of the first exposure (indicated by the thick vertical line)." + ) + ), + shiny::tabPanel( + "Event dep. observation", + shiny::plotOutput(ns("eventDepObservationPlot")), + shiny::div(shiny::strong("Figure 6."), "Histograms for the number of months between the first occurrence of the outcome and the end of observation, stratified by whether the end of observation was censored (inferred as not being equal to the end of database time), or uncensored (inferred as having the subject still be observed at the end of database time)." + ) + ), + shiny::tabPanel( + "Systematic error", + shiny::plotOutput(ns("controlEstimatesPlot")), + shiny::div(shiny::strong("Figure 7."), "Systematic error. Effect size estimates for the negative controls (true incidence rate ratio = 1) + and positive controls (true incidence rate ratio > 1), before and after calibration. Estimates below the diagonal dashed + lines are statistically significant (alpha = 0.05) different from the true effect size. A well-calibrated + estimator should have the true effect size within the 95 percent confidence interval 95 percent of times.") + ) + ) + + ) + +} + +sccsFullResultServer <- function( + id, + connectionHandler, + resultDatabaseSettings, + selectedRow +) { + + shiny::moduleServer( + id, + function(input, output, session) { + + output$selection <- shiny::renderUI({ + otext <- list() + otext[[1]] <- shiny::fluidRow( + shiny::column( + width = 6, + shiny::tags$b('Target: '), + selectedRow()$covariateName + ), + shiny::column( + width = 6, + shiny::tags$b('Outcome: '), + selectedRow()$outcome + ) + ) + otext[[2]] <- shiny::fluidRow( + shiny::column( + width = 6, + shiny::tags$b('Analysis: '), + selectedRow()$description + ), + shiny::column( + width = 3, + shiny::tags$b('Database: '), + selectedRow()$databaseName + ) + ) + shiny::div(otext) + }) + + + # selected row: : + + # move these to a different submodule? + output$powerTable <- shiny::renderTable({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + resTargetTable <- row %>% + dplyr::mutate(outcomeEvents = ifelse(.data$unblind == 1, .data$outcomeEvents, NA)) %>% + dplyr::select( + "covariateName", + "outcomeSubjects", + "observedDays", + "outcomeEvents", + "covariateSubjects", + "covariateDays", + "covariateOutcomes", + "mdrr" + ) %>% + dplyr::mutate(observedDays = .data$observedDays / 365.25, + covariateDays = .data$covariateDays / 365.25) + colnames(resTargetTable) <- c("Variable", + "Cases", + "Years observed", + "Outcomes", + "Persons exposed", + "Years exposed", + "Outcomes while exposed", + "MDRR") + return(resTargetTable) + } + }) + + output$attritionPlot <- shiny::renderPlot({ + + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + attrition <- getSccsAttrition( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId, + covariateId = row$covariateId + ) + drawAttritionDiagram(attrition) + } + }) + + output$modelTable <- shiny::renderTable({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + resTargetTable <- getSccsModel( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + exposureId = row$eraId, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId + ) + + resTargetTable <- resTargetTable %>% + dplyr::arrange(.data$covariateId) %>% + dplyr::select(-"covariateId") + + colnames(resTargetTable) <- c("Variable", + "IRR", + "LB", + "UB") + return(resTargetTable) + } + }) + + output$timeTrendPlot <- shiny::renderPlot({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + timeTrend <- getSccsTimeTrend( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + exposureId = row$eraId, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId + ) + plotTimeTrend(timeTrend) + } + }) + + output$timeToEventPlot <- shiny::renderPlot({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + timeToEvent <- getSccsTimeToEvent( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + outcomeId = row$outcomeId, + exposureId = row$eraId, + covariateId = row$covariateId, + databaseId = row$databaseId, + analysisId = row$analysisId + ) + plotTimeToEventSccs(timeToEvent) + } + }) + + output$eventDepObservationPlot <- shiny::renderPlot({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + eventDepObservation <- getSccsEventDepObservation( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId + ) + plotEventDepObservation(eventDepObservation) + } + }) + + output$spanningPlot <- shiny::renderPlot({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + if (input$spanningType == "Age") { + ageSpanning <- getSccsAgeSpanning( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId + ) + plotSpanning(ageSpanning, type = "age") + } else { + calendarTimeSpanning <- getSccsCalendarTimeSpanning( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId + ) + plotSpanning(calendarTimeSpanning, type = "calendar time") + } + } + }) + + output$ageSplinePlot <- shiny::renderPlot({ + + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + ageSpline <- getSccsSpline( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId, + splineType = "age" + ) + if (nrow(ageSpline) == 0) { + return(NULL) + } + plotAgeSpline(ageSpline) + } + }) + + output$seasonSplinePlot <- shiny::renderPlot({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + seasonSpline <- getSccsSpline( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId, + splineType = "season" + ) + if (nrow(seasonSpline) == 0) { + return(NULL) + } + plotSeasonSpline(seasonSpline) + } + }) + + output$calendarTimeSplinePlot <- shiny::renderPlot({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + calendarTimeSpline <- getSccsSpline( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + outcomeId = row$outcomeId, + databaseId = row$databaseId, + analysisId = row$analysisId, + splineType = "calendar time" + ) + if (nrow(calendarTimeSpline) == 0) { + return(NULL) + } + plotCalendarTimeSpline(calendarTimeSpline) + } + }) + + output$controlEstimatesPlot <- shiny::renderPlot({ + row <- selectedRow() + if (is.null(row)) { + return(NULL) + } else { + controlEstimates <- getSccsControlEstimates( + connectionHandler = connectionHandler, + resultDatabaseSettings, + covariateId = row$covariateId, + databaseId = row$databaseId, + analysisId = row$analysisId + ) + plotControlEstimates(controlEstimates) + } + }) + + } + ) +} + + + + + + diff --git a/R/sccs-results.R b/R/sccs-results.R new file mode 100644 index 00000000..53b6a3ad --- /dev/null +++ b/R/sccs-results.R @@ -0,0 +1,318 @@ +sccsResultsViewer <- function(id = "sccs-results") { + ns <- shiny::NS(id) + + shiny::tabsetPanel( + type = 'hidden', + id = ns('resultPanel'), + + shiny::tabPanel( + title = "Table", + shinydashboard::box( + status = 'info', + width = '100%', + title = shiny::span('Result Summary'), + solidHeader = TRUE, + resultTableViewer(ns("resultSummaryTable")) + ) + ), + + shiny::tabPanel( + title = "Results", + shiny::actionButton( + inputId = ns('goBackCmResults'), + label = "Back To Result Summary", + shiny::icon("arrow-left"), + style="color: #fff; background-color: #337ab7; border-color: #2e6da4" + ), + sccsFullResultViewer(ns("sccsFullResults")) + ) + + ) + + + +} + + +sccsResultsServer <- function( + id, + connectionHandler, + resultDatabaseSettings = list(port = 1), + inputSelected +) { + ns <- shiny::NS(id) + + shiny::moduleServer(id, function(input, output, session) { + + shiny::observeEvent( + eventExpr = input$goBackCmResults, + { + shiny::updateTabsetPanel(session, "resultPanel", selected = "Table") + } + ) + + data <- shiny::reactive({ + results <- getSccsResults( + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + exposureIds = inputSelected()$exposure, + outcomeIds = inputSelected()$outcome, + #databaseIds = inputSelected()$database, + analysisIds = inputSelected()$analysis + ) + }) + + resultTableOutputs <- resultTableServer( + id = "resultSummaryTable", + df = data, + colDefsInput = getSccsResultSummaryTableColDef(), + addActions = c('results') + ) + + selectedRow <- shiny::reactiveVal(value = NULL) + shiny::observeEvent(resultTableOutputs$actionCount(), { + if(resultTableOutputs$actionType() == 'results'){ + selectedRow(data()[resultTableOutputs$actionIndex()$index,]) + shiny::updateTabsetPanel(session, "resultPanel", selected = "Results") + } + }) + + sccsFullResultServer( + id = "sccsFullResults", + connectionHandler = connectionHandler, + resultDatabaseSettings = resultDatabaseSettings, + selectedRow = selectedRow + ) + + + } + ) +} + + +getSccsResultSummaryTableColDef <- function(){ + + results <- list( + + databaseId = reactable::colDef(show = F), + covariateId = reactable::colDef(show = F), + eraId = reactable::colDef(show = F), + covariateAnalysisId = reactable::colDef(show = F), + analysisId = reactable::colDef(show = F), + outcomeId = reactable::colDef(show = F), + outcomeSubjects = reactable::colDef(show = F), + outcomeEvents = reactable::colDef(show = F), + outcomeObservationPeriods = reactable::colDef(show = F), + covariateSubjects = reactable::colDef(show = F), + covariateDays = reactable::colDef(show = F), + covariateEras = reactable::colDef(show = F), + covariateOutcomes = reactable::colDef(show = F), + observedDays = reactable::colDef(show = F), + mdrr = reactable::colDef(show = F), + unblind = reactable::colDef(show = F), + + logRr = reactable::colDef(show = F), + seLogRr = reactable::colDef(show = F), + calibratedLogRr = reactable::colDef(show = F), + calibratedSeLogRr = reactable::colDef(show = F), + llr = reactable::colDef(show = F), + + description = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "Analysis", + "Analysis" + ), + minWidth = 300 + ), + databaseName = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "Data source", + "Data source" + )), + outcome = reactable::colDef( + filterable = TRUE, + header = withTooltip( + "Outcome", + "Outcome of interest" + ), + minWidth = 300 + ), + rr = reactable::colDef( + header = withTooltip( + "IRR", + "Incidence rate ratio (uncalibrated)" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + ci95Lb = reactable::colDef( + header = withTooltip( + "LB", + "Lower bound of the 95 percent confidence interval (uncalibrated)" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + ci95Ub = reactable::colDef( + header = withTooltip( + "UB", + "Upper bound of the 95 percent confidence interval (uncalibrated)" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + p = reactable::colDef( + header = withTooltip( + "P", + "Two-sided p-value (uncalibrated)" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + calibratedRr = reactable::colDef( + header = withTooltip( + "Cal.IRR", + "Incidence rate ratio (calibrated)" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + calibratedCi95Lb = reactable::colDef( + header = withTooltip( + "Cal.LB", + "Lower bound of the 95 percent confidence interval (calibrated)" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + calibratedCi95Ub = reactable::colDef( + header = withTooltip( + "Cal.UB", + "Upper bound of the 95 percent confidence interval (calibrated)" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ), + calibratedP = reactable::colDef( + header = withTooltip( + "Cal.P", + "Two-sided p-value (calibrated)" + ), + format = reactable::colFormat(digits = 4), + na = "-" + ) + ) + + return(results) +} + +getSccsResults <- function(connectionHandler, + resultDatabaseSettings, + exposureIds, + outcomeIds, + #databaseIds, + analysisIds) { + sql <- " + SELECT + + ds.cdm_source_abbreviation as database_name, + sr.database_id, + sc.covariate_id, + sc.covariate_name, + sc.era_id, + sc.covariate_analysis_id, + sr.analysis_id, + a.description, + eos.outcome_id, + cg1.cohort_name as outcome, + + sr.outcome_subjects, + sr.outcome_events, + sr.outcome_observation_periods, + sr.covariate_subjects, + sr.covariate_days, + sr.covariate_eras, + sr.covariate_outcomes, + sr.observed_days, + + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.rr end rr, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.ci_95_lb end ci_95_lb, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.ci_95_ub end ci_95_ub, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.p end p, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.log_rr end log_rr, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.se_log_rr end se_log_rr, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.calibrated_rr end calibrated_rr, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.calibrated_ci_95_lb end calibrated_ci_95_lb, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.calibrated_ci_95_ub end calibrated_ci_95_ub, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.calibrated_p end calibrated_p, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.calibrated_log_rr end calibrated_log_rr, + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.calibrated_se_log_rr end calibrated_se_log_rr, + + case when COALESCE(sds.unblind, 0) = 0 then NULL else sr.llr end llr, + + + sds.mdrr, + --sds.ease, + --sds.time_trend_p, + --sds.pre_exposure_p, + --sds.mdrr_diagnostic, + --sds.ease_diagnostic, + --sds.time_trend_diagnostic, + --sds.pre_exposure_diagnostic, + sds.unblind + + FROM @schema.@sccs_table_prefixresult sr + INNER JOIN + @schema.@database_table_prefix@database_table ds + ON sr.database_id = ds.database_id + INNER JOIN + @schema.@sccs_table_prefixdiagnostics_summary sds ON ( + sds.exposures_outcome_set_id = sr.exposures_outcome_set_id AND + sds.database_id = sr.database_id AND + sds.analysis_id = sr.analysis_id AND + sds.covariate_id = sr.covariate_id + ) + INNER JOIN + @schema.@sccs_table_prefixcovariate sc ON ( + sc.exposures_outcome_set_id = sr.exposures_outcome_set_id AND + sc.database_id = sr.database_id AND + sc.analysis_id = sr.analysis_id AND + sc.covariate_id = sr.covariate_id + ) + INNER JOIN @schema.@sccs_table_prefixexposures_outcome_set eos + ON + eos.exposures_outcome_set_id = sr.exposures_outcome_set_id + INNER JOIN + @schema.@sccs_table_prefixanalysis a + on a.analysis_id = sr.analysis_id + + inner join + @schema.@cg_table_prefixcohort_definition cg1 + on cg1.cohort_definition_id = eos.outcome_id + + WHERE sr.analysis_id IN (@analysis_ids) + -- AND sr.database_id IN (@database_ids) + AND eos.outcome_id IN (@outcome_ids) + AND sc.era_id IN (@exposure_ids) + " + + results <- connectionHandler$queryDb( + sql, + schema = resultDatabaseSettings$schema, + database_table_prefix = resultDatabaseSettings$databaseTablePrefix, + database_table = resultDatabaseSettings$databaseTable, + sccs_table_prefix = resultDatabaseSettings$sccsTablePrefix, + cg_table_prefix = resultDatabaseSettings$cgTablePrefix, + #database_ids = paste(quoteLiterals(databaseIds), collapse = ','), + analysis_ids = analysisIds, + outcome_ids = paste(outcomeIds, collapse = ','), + exposure_ids = paste(exposureIds, collapse = ','), + snakeCaseToCamelCase = TRUE + ) + + return(results) +} + + diff --git a/tests/testthat/test-helpers-sccsPlots.R b/tests/testthat/test-helpers-sccsPlots.R index e9e1af70..71651df7 100644 --- a/tests/testthat/test-helpers-sccsPlots.R +++ b/tests/testthat/test-helpers-sccsPlots.R @@ -1,10 +1,5 @@ context("helpers-sccsPlots") -test_that("prettyHr", { - testthat::expect_equal(prettyHr(2), "2.00") - testthat::expect_equal(prettyHr(200), "NA") -}) - test_that("convert to dates", { testthat::expect_equal(as.character(convertToStartDate(2020,3)), "2020-03-01") testthat::expect_equal(as.character(convertToEndDate(2020,11)), "2020-11-30") diff --git a/tests/testthat/test-sccs-main.R b/tests/testthat/test-sccs-main.R index 1a542449..eb57e4f0 100644 --- a/tests/testthat/test-sccs-main.R +++ b/tests/testthat/test-sccs-main.R @@ -5,48 +5,14 @@ shiny::testServer(sccsServer, args = list( connectionHandler = connectionHandlerSccs, resultDatabaseSettings = resultDatabaseSettingsSccs ), { - #session$setInputs( - # exposuresOutcome = "[EPI_1024] canagliflozin exposures w 0d prior obsv, 30d gap, male - [EPI_1024] Prostatitis Syndrome events (remove testicular lesions, bladder neoplasm and hernia)", - # database = "1038356333", - # analysis = 1 - #) + inputSelected( list( - database = 1, analysis = 13, outcome = 11123, exposure = 1 ) ) - - checkmate::expect_data_frame(resultSubset()) - - checkmate::expect_class(output$mainTable, "json") - - expect_null(selectedRow()) - - # Dependency injection to mimic selection of a row in the table - - session$setInputs( - mainTableRowInput = 1, - mainTable__reactable__selected = 1 - ) - - ##testthat::expect_true(nrow(resultSubset())>0) - ##testthat::expect_equal(selectedRow(),1) - # End of testing that can be done without data - # The following will be filled in when test data are available - # checkmate::expect_data_frame(selectedRow()) - # output$powerTable - # output$timeTrendPlot - # output$modelTable - # output$attritionPlot - # output$timeTrendPlot - # output$timeToEventPlot - # output$ageSplinePlot - # output$seasonSplinePlot - # output$controlEstimatesPlot - # output$diagnosticsSummary }) @@ -110,7 +76,6 @@ test_that("Test getSccsResults", { resultDatabaseSettings = resultDatabaseSettingsSccs, exposureIds = 1, outcomeIds = 11123, - databaseIds = 1, analysisIds = 13 ) diff --git a/tests/testthat/test-sccs-results-full.R b/tests/testthat/test-sccs-results-full.R new file mode 100644 index 00000000..e159778f --- /dev/null +++ b/tests/testthat/test-sccs-results-full.R @@ -0,0 +1,42 @@ +context("sccs-results-full") + +shiny::testServer(sccsFullResultServer, args = list( + id = "testSccsResultFullServer", + connectionHandler = connectionHandlerSccs, + resultDatabaseSettings = resultDatabaseSettingsSccs, + selectedRow = shiny::reactiveVal(NULL) +), { + + testthat::expect_is(selectedRow(), "NULL") + + selectedRow( + data.frame( + outcomeId = 11123, + databaseId = 1, + analysisId = 13, + covariateId = 1, + covariateName = 'test', + description = 'madeup', + databaseName = 'db', + analysis = 13, + outcome = 11123, + exposure = 1, + unblind = 1, + outcomeEvents = 10, + outcomeSubjects = 100, + observedDays = 100, + covariateSubjects = 1000, + covariateDays = 1000, + covariateOutcomes = 20, + mdrr = 2 + ) + ) + +}) + +test_that("Test sccs full results ui", { + # Test ui + ui <- sccsFullResultViewer('fullview') + checkmate::expect_list(ui) +}) + diff --git a/tests/testthat/test-sccs-results.R b/tests/testthat/test-sccs-results.R new file mode 100644 index 00000000..8602c0b1 --- /dev/null +++ b/tests/testthat/test-sccs-results.R @@ -0,0 +1,29 @@ +context("sccs-results") + +shiny::testServer(sccsResultsServer, args = list( + id = "testSccsResultsServer", + connectionHandler = connectionHandlerSccs, + resultDatabaseSettings = resultDatabaseSettingsSccs, + inputSelected = shiny::reactiveVal(NULL) +), { + + testthat::expect_is(selectedRow(), "NULL") + + inputSelected( + list( + analysis = 13, + outcome = 11123, + exposure = 1 + ) + ) + + testthat::expect_true(!is.null(data())) + +}) + +test_that("Test sccs results ui", { + # Test ui + ui <- sccsResultsViewer('testing') + checkmate::expect_list(ui) +}) +

e-I#orB=m5tR0c%sR&$2VP<_)UMB+#ruj;^4H%Vhqnx4!z`HLW zKjR-l^U4n!CMe@1Ss>>B=}&+1C*oWOLR1{ih`bvyzsf3LK?RZW7t;YO|G&CUmED~6&&+>k+MxIUi43iA zvEfIM`P`sn% zC?(T$Zep|mRf?jp097+RyOKQovItbn&F5%X$0kMzSfvD#1+0AsoH%{LKg48qunwD} zAw4B=l7Li79GORYtXN=AOpFxpOJD!Q3v(>jo%Qiax$iuqj8`~-6r|SgrAH)40iTrA zIA>qzUST$SwL_V_AC$T?)Gt-@MvgH7;6-U`5wZIe7#mJh#cT(IEh zn>tREvPJihq8iF$)fs3hTML&>LtPcjzUbk*4jL~=Q3Hf`sHEFoL7$QsA`F0%VC;4S z_+*Fo%AYo7rWgR0uceKDPGYctU-|@~#u4Vw<_Rm$%$|_~Kju-HEW^TUAfYEEP7nql zqd*V(S}pg!kpIz-_3ITHKo|Y3bbVq_5_OaGS{Bs#$~^YM6}RP-mdn1AT5BM{PDN@V z%Z);E?b6^v7Ti}ChKA4NhkSvPA$sVqhfTQM$N8+euVb!#!@$JAK4#0p zKic~&6HQrQ`rL8e5YgQ|vQ5#%faJ_yOgF zdp6VfK6=&e&RLiJcJ?y!lV)qyEm#7A%MKkKzXuLXK7&})HS3-nr` z*8<1P0=IKC7KDzT&~;Ix&%PInNu()fPe%C>b%qL=@_Ds+EU~WuZodCSrd&@BrrEX zR#X=QI5#$}K{-!CD@I(zO(3X$irxJ&UuC~>6+_+WVEiasv;wY>hvN$RJ5N={73Kvv zA|YKFU^tr|$U1f-(TMU_1S4o^r|yTuQ=^(3As)<(yOr0s3Az_USIsF`=_u zV+B;pILnACVX0LAy!H+~u;ePMg{8h3Ff5@5a9f9{=dd93``;CDU%aS6#~c;&^FVB6sotnrvt4b!`JL2GjR&hEm#KOUs{bFT-mKETqdi0O z*PO=e53?QS=gju3Jz4!TcVrq(v5X%x&NaSn_|Pz4e<8767l#i&sCK|V)13meDj;#! zwZF{@J@4!eZvz0YAzGKFWnF*&Nn^_I9OW!j=s0L!N|pSPIHg3jUO${`$r?w0$QM9z zqNu*d550Q&C{RNRKgecOvf3QXmJmoS4bY#@mV3I_tRR=inNF_8S)POLrX>P7+%jGt_cYc-ULQxQKRSVcUp zVdOt#G#m$f&|3oaz7c=W z8)=+PHiD3MEet$*tz64H z@Cb4D$UOgz)DHv9bqgz?KD;U%(68XN6See8@o&zUO(|oG;-i}ZRoVxTnP9f`%jv%QJ{0LS5@b;%toMD)g=0Ft7JeQk(881bv6%!?9FM9`m zuZ(#-K*HGwEnPK!ul{tpKR-c>#V1JorHyF>k*BtPX*wH@I4Z1LmKdfbG=KG@F|K~% zFBD8mb$X*L=J@}%sP2Kcy9g-Ogz4oH$JOmaZSP!-0Ln2{VrlN9-s?hNAf8X6VsBTqNNlhCP$@aHr?twcv0&uvKPIz++Ja;^jKX>NskLe%vhP5 z3Y*6|&rxo7SI`p@Q+dqL*CV~qd0x@emV5eawZC`u70MjT)&M-dK%$~LyoLh$7rsB2 z9+sFQz?H($$C=prk3r0VJ%dj>W6j6Pz;)F@_c!GIfJAVz08n~mx^?+?mM72M9W6d> zurdJMz!3qxnSih$F-bsZmwYr?3r=Sq-1GUr*AKp08H8NajaRP=$D?hc7EqmBBmkCD zS;@`0<=xdG>?-l>uxA?Hz9rVy0mTRkS5W(1jnKW;OKm#6$tL6ehJc)Md=x zU6)V2qsc9T8tYz{#LRaAN9!pcia@|RtAQ9tq}CgXLPJ5J0NpRyLo5r__b-i($g2)Fd&he z9Eha83(n1YWS<(U%eLSwLq^0dM(gvfnE#rT0p)9XcL-sRM1N6G{WdgMa{WacoVW@OLREf}E&HV;SSN#o8ZqYzRHILD=Iomaj!hv+{ z8L1)Rl_@I7d#z#UsXX{WSl~1=G-S_!UM zYInPxVT&M2(NUf6HB4;Uq2$XDKO(_PAGQ`hJ9fliryrx@G!1)Igi668^?SvP`uYe zr~_sNqB5L-IYrJBFg?WajEeRp!e^MOq0x57?p&{g`oC0*|NwjVhx*C#j2na_?HZCAMfqC_# zH|}}m-qTWhCDzh#B;_^SbXlTOKzEJ|bhm%SJom%c>09dUsnKEX?fmgCoL&57G0-Sbv!97SHhMYNt2vPsN+iDt2j}T92FgvaYF72TOPXprTzVf+fvUD*7@CO zdN7y-;Gi!Sh9T#m-&M!FHgwQie;j#Z>S18+oSg>JwVy>1H#?93v=1Yd1u5riG)OQ!WOE`Z!L zTNh9;FWUA>$eTbq34*;H!-1N;bRnNP&crdxwk0yng1)}QZiO7N%~}q++MSV=TPu)! zwQFf%2dE?$)7IQeb9^g0dNvl|{;2_D8n47vRmSD6i{ZNIu{+s&r{Z6T_<(`W@X#PM?;NfcJ!o^yMvcT@) znKyKF7z^w{*|tSa?{-?<6^;^A;sbA=c8?u$vE4JDbh?)o&v%sDoT$V`a~C~=&&aNp z3lYnnX#>2Ip2AP~04#t`*)|kbtdlhoSyf(abFojzGOOF;auhghZijO|Y$>oKx@@K8 zRZH5kU}SEY!&Q=}+0Ahf!g*1Fg<35ewMcWO=5{)VpXdd05#a@~#Q@zwXY+vUaslCa zUf12Ln%mI(e~@~dDu>QdXJ2Uko7tQ7UY6bTUB>msTtk!opZbNwy+p3=bKNTKN7~ah z|EIC5ABT@$+D&&D^;E>^Cq_Jym2p91PZEz9pT{L0;gIzp@rdy`Tp|est=otsjE86= z>)2YcyCMtY?pKs)Lvn(iY8V=h*n&jFr5K}(&!%G#;y}4FNapiwLTycz#%K5&tCkii z)I$a#o?vN!puIR8S_#7;JJBa&t@h%hbLbcka@Am3x}fV#2SEV=H)Sj$2)7xZ=F6}& zV%1;>z^JdqdI7O)airjx@u^dcz#y&Dh%kU72qQg5$e9@r@a}D-ZQ>^EXg}}>5cCCo zaQI`E^vnlbV6pbZY8}SIe`+Q7sR)Ir&yW;3Xl;YS+FIk2^f3MesqGpG%HtzRvJ^#8 zc;{KVQeMPs^ocw?5h6ShcpYp~<86#WPvH@!m_??I?ytB-)CisCA^(z&Q`u=%Vm^l| zk&bi?)j$QptYkdYY_#nY#H;jZ-e7U@`P4kLs4~TTVnRtOo7MR1>kuQK-<6mbD2jW;_YZw#majw18-J)2;%S~?1?i)q8 z@_4)7D`t1RWq+KTU7q9>@qbM&-l!s6>9|J4+N}P!ZTc5_5WkIy@Nu&?g_>P?+ZcAO zF7eWO9+wEkI>Lnta#O!a@s>`92%MXEaUK8lBgJ=jRBfC3>#>}yU6n2I0&Z&)H}u=T zO8woKyS20$TH<+*j8sR9v=MPQZ@pGN&SZfs@f>EO1pnPi%fb+c(sECw`TvP$QT=}( z)x)an7IVMMM$+ z>ca4%hb1V5OPpU-_3-?ppxukxhj?CJ3z=_i*{9}s^pFRV`0`*SKK8 z&zn2KR5p|aG9k?LdBa?Egm`YBc;P$X$K{EN>{ z=y-4_d($v&#!Z@`+fUCY&3?p9nu);cmRkbxLsOiMVTx++B6MR{REaG*%wyG#_`u=y zhBosERR_;Elm~_XY(Sz<@Sj1d2A(}35R+=~2I6dC7ve#P=$U(6Q&IYm$P70tlg{PlDC5vQ};~|iKg@0ZM(hu^mQX2|KiTBbod#~WJB9;vm~4zp=g(jX@Azul&2VYes`=e;6?u5MQOUG(nra2YBmqlRmJJTu*5jgOgZK>P|NE&P2mQaS=d(sZ1;F#B6Edoe zdku#T!}KxYC1Qduu6<1NndW5m8u-|8_zM(NV#&A>EhUPLX2j!!gMJG0`^#V1ckR!a zsh2S3MM&&?r6(t%0%$3mS4q%X%l=1Se|UxYaoM27PhfgRA|k++!g-a1-SeDRuEg0% z?2?$G+OQv2Oi@LN`sAo&dH+rmb9edMuGg;U!u#s1x8yKk6Z@#$3F;|Dz9l|6>n?*I9Dt<1z!Szd0n#qN*kTg4qx*FosAtY&#G>%AdJ*LWbw47JJVKZ9{7OyY9W(CQ1hVc^3I zgx5fFcou05RL2qbWbTYf6Q@kfBj@=8Ajbk(AL!czah3r3i%C+8HRP%nj~c3`y^665G^r%apyCV-_W|RzkPPKGAQQ-D5!)9)zZ^i zwF1CplD9wo?i$N8f2i45cJowa0J6=RVW7-qRj=Fi!9%GGXc$E(dR(hUKrZEm$(yz( z+7?>X0#qq&3j);cZ(C?p30S2(F$ApL+_q4dIA1_2MR1-+y6pz$Pfy?Z#V0r4D&o3W zrE>G2k_#~;pt>=x6or*RV+Ff`ZGF7}d8Xvm1`ITo-y5!xO`4Qw5b#US5TS}P%e^n;fAnMh`tGkR z1M#J}fO>(1Gl8=@syVaVx$Cm#AKv>%>I@O?Ms@`8f5+N0lO_oH{~xu)w?sJS!W?7v zS>}J5S7&{iRh{{G=4jJKlQAPZY5UqI^xe2*D<5 zO&GgknIgNQ6YdgWs9DD-X^&9_xLk(a-L=;fr^@k9Wxhk=y zc}yoPt^=NV*oEQBh~he^xuE?4!L=8dZix^-HgW91+6^Ln#_Vi4o>SzI=-AXB^}$I1 zQP^587TGR#0*}(5KL92F(CA7=M#m;L$z_}gCc8?5lQX`0YeO1 zS{?O4v=@FT`e04i7e~!fY;Al#;*&u{s-YSbnp+3e*Zxox6<@Tgz&06wDPsJzMDThTuDEt+u}|woQ~IF9Dz1A=+uCKbJAt< zOuNqIZY1cs{ zWgwJ7b6Ui7bvmm^zkPr*-+ko6u8dnl1>&5_d^sE6G+l}E|9w?Yt8z5vzh;Fq7npL5mmA&y_P@2nb3~SIA-wkYYe@CE z@R8&C7d^3ch!A>_o+eP=ySB% zR=V6B-nYruzqLL>7ELE7O`J5DoHA?j%vsPI;jG|1f6lvB%YjQbURCzML}hGUMunh_ zHo!i8DX0sgK#GelYV9k4m6DC%!T!0w<;5?i-gVj1uayC7TMK+BK-Lee2*P`xcMkyF zM-k9_2V4HH!5eDcwnhXrUdu{HXJ#W0f*itz0j)Vn(2@#xIZO8z@G4xu+ccszJ2^3u z$d?ZNd^-kS_`aC zTyT&*o*P&qs(iBA3mdc#3T1s^IK27zVLrJUN@k-_@C#7_ASMKIDG0!VrWF*%usl(P z@a=#GqJ?239fa;KaPcCct`ll=+3+p;1RJhpf1e~9ZyMK{nY8l;={JE<&V0-MMdu&- zy#B$YRZ`d)^DZOV&|syAx1M8Fr`AHa7sB^ZbdlyS4PXYC`&u9lK~ZgB4vE9d9$6gz z2g2aoWeHgj{q?EqbfeMeDKPWteLq^rA&otu+agDz;mJf z{Ny>GPd0?1$`?6QFc^n?1|PqN(IKDk#-RoZag(uF;EDk5(sps24KeRFMl@FcPji(@ z+h224P9Xb(Y_Ith^YAP$WdCQHF3fl{V}fz3QDfL((CRl5KN7XNmvvLLJ2WqA9O?t= zinKxke4tWC`@4?7p%tzve$RNcw=8_&b&xR9(IN>5{$~^%?PtKAfa$A8Q5mrbTyu^H z>7ojRKhU3|QfVrYeBlU@0sgC6D;~$ede*Q}9L?oC+ml92e2;{09`%iwdt!a_(Mr5D zm*M6&SsW*KW%*$m;PT@OJR7K}ek?B82@jxq$WHWQa20iUU+*bJ9sO)2 z7Bc=KbU?HL_BPnR*jhq2P$|N`7bOcw{;5V52fVPs!DF7?0`;?SIfOXFy(=?lR-UA8 zObux!F5?&v>2bOHmKRc=P<@%wMl-PW)Y(P0drs;_hIhZRh9K5WMS*vUJ%1tQ&R=_? z`stVwINWkmTA@ilEv@b5R;3ky^i$IszS0Un`YE_XZ=CsW>EX1$tY9o;2I020C2&e0nMmeh|)*zqV8^cwRuS)ZrpME}PX;VXO3z zj#6^It-|4Q%}4XjE^#Z`3`*^8ca;kkJ^OKolQn*o+oqq8-r>G;VQSczJJlj@1ny+e zLrboM=mprHudIpc$J1(l4-%=n;O2j4rEDy&cr}Y82_cVXbk@-`ab|Jn2-y&v5EKfA zeGrclO30%6-1H8$uDC5Rr$v;~N&>6y!?dflkdL|O43fCmkh>txzT=9EZCvZg$x+eL`z>Kq zlNr+)Z(O3eYou&nJo>Q#sm=lcsub;|#ZNK&mruNGN?+4a@lG@fie)P~)trLXd4U!y`gyEGkFMc!his5sY${n5}hZ8-% zb$oJQwDi0$@DVR$?sV_FP4(sPQV$H%@lqaN^vKpc0j>0u4xp`M9x!j3n>VdlHrgW( zIC^Yrt^iVcCo+H>!R+_n^o#DNow7k5^$4UVwT=^zO3$MsGU^`WeH_s`RzNMi3AkGs z6 z*wKriqxLwkYMdmXm2(~0)}Ogyi_I2$_&M48LW!%YjC-*ZLkz=Xce23@fI3nksQs81 zivB@NuiVpJP%B!=B-AG9BvVjm`U(?n`1q-Thx*EfIuE^gYQmwAmuu;PMpNi!fU=+{ zr20B^tg!7rP}1b93qxrW(gC2|ts{g1^^>tAw?4x>cI&dWHP1XGdqD2S5T6xig_Zy; zB+4T|Ak+4;7ax|jb$D`EbXd-?4laK8<*o)nyy^?IF3_>op zd*+i)_tN6|j&hq5f<11Sn_}ouUbNu*xfd(+eYS=Ib0=qw5Rn%VORs?%+2w|Ok3DG zG&wF>O8FR`*lI3@*a} z{U-CT=8Mdk>U%Ur+KHMwbx!TQ#Fs=s_hrUH!{1Uo?J@p1orqq3kICY|$&6vF^D8a8+w1Y@<1#SZs``z8rK&80~w~Y)B1D20;XyMXa ziQ7ko5Ey>$*=|J5)wtb=9J+p~cQ-84VD)mg>qgXU!R&KI@P;Vc=c?J3j{gEHq*$2_ zW^kiD8>Hr%^ya6hdu=sa6)7M})_!6+ks}_1J|aJ=vtubUHJ6@Z912BdnhD}t>_`tJ znxN5~oI{q{Dy-!;O)D;tJxu~<_ew21aB+I~Lkq?`>`ul6!J)T9}bO^E`u)w(t4Qx#ynq zeed_asDoHal#%1W2Q$q+ms}Sg*>#8SGi=tE6y(t|=7^YP@`wm`^l*?SqCR4f{4%>2 zAEcSNQJTXdS4AnVyt^Erpa4SG<ghXh;vgYk)R3u5n&`6*^3yOeoC-%GbpyH)eG zW~{mzK6ti#p-XEsq+AP)!ljUF;UzlHgu>2T0KI1Ernj~_3@OW4WzR)q{bHb$F;2oy z@7DnMQ%(EpI!s?-J`i>Mr|+>r6Kcmw(CL*jaGM`@AU$jUNAq?lgC1#@5WES-NFz}2 zCdfE(iaR!J?(aXoyGhvy2>OW7pxV(AX!`n+Cga3@(;>%s-8G5YsIavPO{pCm922TmFcl5erki(v_Pyor zPbytNbj??|{vQ`xt4ds*7;jjd@M*#<@c&DXyDRP@_{(!&=OS<}0_P%dE&}Hwa4rJp zB5*DOZ5jb&5X*oTnHm^S2h8c0Mu*6&X8*1(ri7Z+YQK5-_jwnndK+O$T zv4X;+jT)>rOeLd7_!iZ)x(? z1katK{*D&OQAco`I>{Ma5~f9hkCxj}RwlTQiJmi>%_4I|i>Av5Xae^}bS6#!)O6y4 zOUb8cST0Uksyw3c1&5V6qh~uEMLh2ZbQiF~o#qZwLn?u+cFqSZI&`($asB^7)pk{q zDe)G=SB9K~oe3S{y>V~Eb&0(y=E;}~*aggHMyD^8&Y(xR`$N-%x0O6)^L;53Ou|_gUadax8wJ@}+<6HsFWfK# zFS#PHY3^2QlVH<$aEpue{sz;Ytkj7s4tI|ZY~Gs(b*asl(DkGb>ivJ2c)t7ggB|`; z5aKdGv@*>FDqV<~r z{_}pU#O=h)PQDk#4L&JQ3D1cNTJA>E8(nYg|Aymt8MK63SDxSNcjLQvvmI+pZMKA# zMyNqpxB91DTp;_plc{-S(Lvkj-dXM`PY=1>cCDQzf$v4VFo5Q5y5F~MT%77=U zLi22Ico$qIfunyH066ZkEnU;@x(GxC9%k!aK6ua_(gHsa)e8P1gpzBg=kn3P+ zXu7IzR(Dd>sx{OJfWJa#Wel1FIVN?j>IUBYxlxu;rBc`Nf6>Gq{&a58comvY9c(_Y zw7XumAC$%6NBKZvqzF##o^l2eHVN}MHa)CpzNUa-2-Y@-C#k^gEMu-E&2{s?yyAVC zAP_x(Vtc;_yCI7SN@fLVF@nd@*2M_s8q`tDPR*)Hu(d{B7XVtDi8=E)-Hr7Wy8ra(7PI-HH*|5A;O zea#S#O8BfX1*=Lx$5K&sxuXn==>!d9u#t+Z3;@EZwb9BC#w|MiVND9~4sT(8!z?5% z5%IIqh9x3qnb=UMMK<~MVfy{K=&TR80}9sRd`w-SZ_l6l1qXR%X+)S_jFMJI%X?|i zDiH1>Ma&Y>R6>icB#6_CSu$}V+FpW)a8sS&WYfxek*OxF7~!R4YV(Q_vzWAEB;Z*e zjX>pwxzaF;A_CO*B`l^Yk}cVk_`FPn2)AS*&sNmpS(93_{iKC8Ja6q%SYs+tH}Tz7 zDF4h&P_2zlpc?PV;tL%=cufm`;vVHgu_89JJht(#gQj&?v3(azUvLM9Q#uo5Fyo#XHh22sPXHS@Z`KbTz#k}0(W6ID(VSlKRF*m@gJvpy9#7&WKkxzxi2QrvJ z+eg@Ks+`rlTwLR+3Rbb`c>#9T#jsfvgEu7z8Q?=d_+vToDc=DWf(0HZQo&MW+_?UK zzRICWx;*iA!wExX!kYMZ;>X2piv4@cv*7!8vHpAAVXZ?`qRxa5(R`r=wQey(qGfAn zB(!nx?0FDAZpY2pyH1QtI_tt%)1!7_aEvSI6O-8a^|zNT2ZuObk$XakPTptFRS{Ly zUKNZ`4Nb3W<0=?rdhb_F!W~`RauFhYs+z_q_#;V-UvYKP2*fv#41s4dMR-+!nh_{C zy1d3hx2voG{voxdsMZx6IhEsWval2IEZSc-t7~|9QvTn!dFqQmE() zyx1K0j~fyl%jVo3d~!mV14q1F1%DvPqX;j!wy_3w)D{N=RWpRT5|L~*nNCz4T7JB5 zPH@n(ADr9{-1BL@=FJ)Pi`*ayw`Ndd1dSv(vAfD)JcYs; z9UB{agh8gv%r~djoL^fMgujwTB3ppp>)>M3bB~<3F|FGKWz^OTzgzI{E@;dikK^T{ z1l1>>#}6Zcdjj_JHe#1@uy8JLdpt#W7U34*af9=>6RH8OLQzQyZh&rn{`lJm+-ZCu zlgD3bTt)bH(`bB(ulT}eojzo)Eeu8lT}{1mjp?^=HyOt6?6`e%lqS^-m_fJ-@}{0L zxG=#5s?6c79%rnE%TfuPSSw4#tJaXAP);q72GNZ&G>;2vKxY~Z~se+u1XOSmQr z-Hs|hP$=Qn2k;zGf!Ak`=}6A!S3SSxeZ}w`Wv~Sly4-M20J)t757tY0$aj^LR^pP|X<0HuYUfL+XXl@N#x!{N1snJ25?JN!rx=Zam z2{b)vB7Vg?rV|y1cFp_kUy4@(Jgya-b#cqv9J`la-57lD+PM;VdO|HIL7Se> zPFsHOvCdK2f}1Kr^ZNhl)hg{>+U}aQNsAIcO`K^sXy}k|dHkdCF>%$g&&Q64xjW`U zwu*SiH@bjv++_56`iYGceTFM})wggN~ z__t&wpfn26UQhkQC0Jf^-44TxBqez~a zjrEEbgC#%Tk_E0%xmiY=HG2l{bcZ>LFr_evmcwa5tc`5{*jS-B7L&5A*2zZO zj7i{>WuE~)U%bsuUS|H388+|$ZxW!tqImw9(V+~Y0EKabB;4C9P{YH!m~gLYvX}Xo zTg1|^4$U9}V^LY}+QE|M?pcgjTVg}D` zY+{vT$;h`_^Rvvk7F&*u36RRZ3opp^e+dnTY%^-*@Rx1Q!H(Z6KHx>)+JrC&aS;A&P{6JcaBXB$dfQi_0Tb?5Hb|;e;>4LTWyDnbuvB;^ zD;*{97rsg#^B0sZ#*Zb5-?}#x!%HacR4`U zi|j%jl95)C2ShZja(Grnag9wRt{u8+-04=%)%DtmMc*XJKqlPpLW@el7VVcH({Ith z)#qlvcg?0<^Y4x(iq-w{@lJz_g}whsazVHaD5Q*{!wbJS3*yt$}emo&=FvI?eVp*b$6+w6Y@s%&7=s1 z)Lt!trC%S=Ghlk|x`jpJO$M5`xLBfOa zzs6hQe6bs2-i*<+b_VHR)L*2#O1oY2izY+;SNQ0E!511^*HK(-h_m_$SB3=&`(Pcz~4qqeO73%B7j`$X=onV|ub z*7k)4PJD1kNXbgS?JMr)^!#6+`2Nw*Ln1tYhAV%foVvIm;2KuJ9IShT z^HhC$sPl{GDF&>hK#I(DvB3c$kCFi$=5Bth3XRFCQaqqYOQ^_R7ZV&rO(!LT$m0%p z*Irh3WUk^tM4@1b^6OY>T<0komucR}g|R=}a5D6`h_(r*d z<`qt5X5A8cR8c95qQP}~2{L_J7DMLNY%e@~eeGSLArtOXGNn^AuudmorZ1fFVLq19 zdDf7xZVru^Xz|D)?pLQ35rcF&{V@~Dz}(Ry+&yIdXqi1oCsL6Y7wRp9{Y8VZY+g0VCU3n$bLM%!5nB`RYY7U-xwh{p*RAi#0eg zHfEQg->E~%B)@w5?V*1);ZiZ@nzOSqZI%oh^FU;MJlmD`2v~9VI~FD4m6+gp^}%0& z{e+LXKcYUK?aEpBxy^i_nVk`>z-=Ffo(HNGcpp-UOE8^e9c+Cq^zSEJ2U%{|ISV~r zh(5G9`KLym%p$!1TB1FcaD#L~uUn|HlY%&$*D$#lIOuM4>O; zJrFaaU4^C4IU)Wi<{s2Z3?+g5s@k&k!O#OCn@Z_IW`*9D{29FYxzTLNpJkq7oNTdW znr)djW=CXwCgETaZ8Bu2wu^FXIyvJ!VdvetEOf94*PXBhlskOTIsy6s6%Ik~!N=Sk z84-8Oam7$tc9h_^!X$GByhJVLY#Vb|WL)Jf9>su!*nrwXDRkfXDmz)8z*Jj&FR??8FLQwC z7C>nfg9;s?G-F<7t|h~goo#`$*PUpvxXT(ycG)|t6(dvV9?xt>mx!?>(3OP4Y#793 z+bNwlJktQv#W1i2F|s~BvX_6ct_{Jtu10cQvfJ$ImF zF}6OE)zA=Thaq|=v_ibd=~yTS@^+E`uak2$6L;bofM%+xLFoNpWP`=E#Lc)TIQ6is;Fl_%O!pdcFJD{jc^7Jyfz?gy{GK zY=C|ea3yR20Dh1g5x;6@U1;D$k0=@L)VfO~-1LnC5b$N(!wyU8ygsu+<5qaELxbx2 zN??@~8@3!b?Q96F{_*73iou3I`a!Xwj|5psv0;mcTWjzPdC9j~F=Uy;Ar#b;CCo|+ z>RX0$ch|r1*zLc@E5|Hzd_=?QdP|_`yGZcC(sbadx1X42*smD0`~eeUsvpgy;#( z`1oqNdG}}ETmJrJ==mUf0kt}75giGk-rOTNk{TuT<_4eXp)W2p)IZfd^hgMI_EtlQ z^q`9}>MjnB3i%YQxdxAE-{xZnI`+s6Ju0I2Y^yO59EDN8y6)02lpN_AhH-IMymO=G z{$8PnK{`37JuIW~br(rcX}rD~=~WFwxd)a6{`1Q2FiCe>s9~xU@H*=z4M553?2Z-O zD+AMFKV`!t`iPc{ylNLvdAmxWl~8#%ean5ZW7V9Or-hLkBaAw+t{3WG*F{24-#r0p zWYf8ww#Rm;zBym<@}3E%q=inm+hJVd!I@xVm7@@QBlu9yx(g+=0~G8j8`p4qKYG9N zxO=T)w3wnP_FaKE7*G^COAzTj41sK1&pngOuXg^i&P#aZusGgNF0qSY0rJZzI*@9cnT|Xt-e+fN|mX9Ae0yglDfO7r{ z@PnDX??t2T+{|vAd4brD4>2@j#$fHjOIVeG98=IPg*hBa>KwQor<9?}4%${2(~VhTy)(m&^%NT=keNp!DR}&u z=q4z)DK9+Aylyx4 zS}ZSeE@qyKgw--)+vjm1o*=U8Q>5Oo%->LVF_-R?2^4Th!oX`5lOjj84>a9dgkr2Q zVsr;1+J5%AW;dToTEo~}r{4=ji6+LhL6a`#Kt$V*JS3vPg#)x7sl^$TdxOFZYHquX zC4V=En8qn?zsKvj+V3j&xWR+e$2^Pj1PC=b@{34wo8>=>A;@LqThbfgVZ^Kl&Q04Xy9Mm=b*JKtR(JW5|Noi~}14V40ZAco~3|CVnpt-Iq zL43?V@#kRwtU`GB!C!-U8g&*|!5o6PyAFO9ja9%)A7ey(RNx5%rVk9R$#%p% z6%oNT<77sNcM9P$Fk?Rsz)=l{vdJz-3HYKx9hP}A62_1wRO;6VR$~umsyF9JEZyju6&nzB66&%47CL^oq+kX;22& zERY9%@RWv(;Nz%=xa?g7%Rb{|j50`8u)KsV1~xwa6bW5y*t^mf8sB50>#`0_C&72( z-^%diJ5IO!0bWKa5HXFj)AQl>_Gl#B8Mu{vp$t>Ai;}31CagLvL7#)Qb%8B`mm#xP z1DE)?m`?aAUzSvn_*UXD!@`7z5(dZL8uxze0^uK|d;YmUor}P^2%L++xd@z#z_|$g zFGpZuO(s6(>9wn5+T>KKcZwEmNX|=_yH*n2a)o2D(+^h&NqLLJ0gGESa@+Z^AHo8jq13syO@1Ls*?wwAbdN!;PSIuHRlsdT! zx=M9)%oBy0b64~zUC}keA^If(hgq+ykzt1m(N|l}JU?919dO%)-WI(U$ut27xJ@q} zkjXC)-seN=17g)&jSi6IFB5hKxoV#PjJdxdI>4qt%GIm>1_0y<)&el4G9vvd)d71_ zM_?c+FY&hGwnWO;QI7=VxoDCQJ2}I60r}Zz8L#dMuA#Qs8 zr+@y`ZP~>#?+2oTqkLi<^{Km5LOy~XIi$=@@4S*df%`gES)T6nsdMQ37$@PR@BF|? zb#BkR$(uJm&`%jo!sI%rh>Z;bSD+$>)9$7hW_S4cn&Ao)p$gOQz?U2o91x=COTvH* z+`;p!Pu%*zFUn?uAVH5N)Qy%vyOb>R816uN*8Y#??NA0f(iA=#S~p4>0lnTgB+~!Q zy?Vpa5B3$bLy{l*7e-2`>H8OO`k&jnV;D0cO`+2Wc^Lo}`UnZTl7)Wa6nAXc+~0qE zcayR;(+vCRxn9r%HCzHs-vb2!-)}nPIPcq%4qqw*Eh|0*ub^QPWCgDvkR{>{JMwz> z>hhM%%aia_(_EeiO{*Ik#95=I-$RBUA8lGWcExS)T(L1aI3-n}2n38!@46wu;h-V( z!vSd{ZrjHjX5BRVu*`6X&MeYku*4DSQ8!pZnM#ckYx$V&J9y3NglWHrzR!@|yP9kI zpl)@8B!s<`And}u`N=yEz47p6p%D^=u!-ys2(1m2&?PHCxBYf*^NWuh==_3mxlBnJ z2s#1MC3s3Y0k&6|8bx8hb;FgKn9vZZo)=?Xe+gl_f)%;-HPgL!UG|4% z+nu2i5>9ywjUt}^ul`7tv^eqKiC)9chHDI(gzMt3i(3_26BEzYv07${{!{%_-GjOd zwJSB>YuxIO)DG2Ks;MoL0P2mr%%7aWB2a3v_GajD5Iy%Ynw!i?VuMA%11gX*+c{xu zTdO}ZCqxk{Es`T(Vjz#@DLa8o@wnZdMd|(uBM`4KGui27K8z?74?Y&1P?!tKJzh{e zG!}TBKzf3*2=hTCy%b=2?fex zKeQ@9IGMLa9-&s26bJCYM=d(=geBiJY{7u}n71O?mL@ECB}(brj8d1o2xRUYz*pvr zzs%poZV)X=Z_M~x-Y9cAd|(GvhKa-A3xx~5(9&QeTuPJN9uH8Dq5Bd_(6B+L86Eyg zkcI`_Rp!m*;zK8=*a?#~zTmJjAlXpP1But*SeQ3z{KBu7D^ufr2n@2KG$DU-V=U(N z8bPH}yOQ95keoL%N?~tGw2=4;Y0%2zg%^}D1&HzKo^tq~Gu2oD4+>uGuq#yJZ7;>| z}q5!;x1ff*z$^lUc0MYCAEE@kYo=^aXjehdO@8*9i^woiI z4|XhZ;RcnmYG`_AUJ=BCwWu$_FYmKQ`7bZ?EQWR#H;Ah;FAEyU(eIlM(XvzEFLL@k z<*p(p^AgWVf|&P|A9mQ5ZKB22+@98F-^5$LLFGg}HlgB_5g60U$ zn!a+O{-2>r%1gY$@TftTV2!VjdnvAW?9!N5K>yFfyukF;uhxC3%hvAH+@vm6WwgBa zt7>}I*~B!5mR(7e=71B0JrA~a)5)9P_V&ABmE242zio@JG1lcvAsR%J2?~bj#WBae zjD0;@E<}V6#@X{4sOeERU5XJ^$}t$D-(I#H9O96@GY~zvXHSGs_qx1bgit@~RRr%) z(>>pe?ffr$2j#D`Cht!3*r>E@ z_{hPl)U@^IqyH(5|2jGuhGs8SWUQMep`_nr0VUV4GH1`!>VYySiNe?%<0W;MNf@c* z&V)#AFPnH?r|zQ$e7!ar7+WNgu;dKLNLnPMR64E#(k&BAPu}JE;I~89D9mjGr=L%@ zgcYR$J-MkOYz>R3WL$aJ$=`7YXK#A*kpcB`FIu8S(1Jq6i=lu*RF(vlO3F%r`Yrd) zm7}Y6rWm3EwWVAN*yK(TVaBj1lX_#Cs2zEv*U9CE=Ib@Of+`@K1^8V z9`hZ)|8UH;AyzHf5j0X!6Le|aWN83SnqY(GgguWRz-JpBxBR>+KEwcs)@{W2hty?A z_!TgTKpVyM&8mMmMmq|cFPJuvZ2>0HBnh+vCJ}(1*zx)Swc78ZB1INP*ah{fGfR-E z-gW#s!ICegnnN8_a~}CD^l^dkF40o@1y$5d41%nomwd*eFx)dQ|KE z{DIhekcYHsdgy(X@Qoi*NtpSbSa?prlB*a?zQ2IiG;S0#X1){oMmjV`zR~~!%RLoC z$b&BQGB5xv#F|_|9N!Wv5(%M*Q;cJ(!&&C30Hy=DxB}(E?RSogJ<|pbbd$kTEq}@k8`#+zZPx4=g0JmABHQKG|0Ogw zvH;}*8oLGjRC!Z8OYW3xa}IXh&7Wg5XJzKvZOm6_kPxn#c#?3fpREuQ=D<(3nDg_e zW`cuZn$Z#(;-F@H`9{EB5^K)!58JvoLIWn9U%U|(dCJSN+F}u?1A1Lmz~KP-bSxd% zvD+tBwg3aQFNm$FW#JM{`6MM^KS#a9XU9Xb7}gzD99ulawmh>f$2uAO zeql8joA@3XXIbo1jd`{?8B?v3`fA-F-38hOQ2l=xKKS4Lg-mrNQhH_~O@0-o#-0Z^!o#NfzFxyUJFrja z$AM^JMf$uVdtGsG2sNFQ3?Yv@;9YxJ*%9S^PEmL|z^2J5jf={rsc~xZO!G!AjQ!z; zlM1IM8lxIiR}>r-qRK5dj!JF%YhK}0X4WmCM-`Ra2srf>N|342wn50;n(c*$udlr; zH00=PWN2VrfrQzj|@KF|E?EeBkmCt0&C zwj3x_Iz+@;7LBJy49!d2GnN}K-S^h-GI?UcNVjfcg2MU&2`W{wfnN~N-o&lH;dsLO z`xH8XoTkkQ!Z}~Us34pVKE`d^^6Xv~{xI$!Gwty-yM?&tRM?P(->APo)8|=QJ(LvsV zixaY`3q(6+O9P;iN#R!ww0m)zKR2u!Kg1rL0fcM&f^B-11e$)EhK4YX>#^XATgLw; zM|vS#Fs&NHfcG*}f=bUMf|fDvk~T@AX2*vy&pKfAan_T>rmN z6HsXSMg!%+syf5yvxct~BV!n;3V1HoE%)?AC{q1_K&ZB)t^|Pu} z^NadNAsHZg)9Jz~m8w))FJJPXCZA){w7oGB+mYCn2-e+ygDVkx0kIDd9?{t;GK>a` zseKu(UfhCuHYoKnzYsfn@QB-$%5s628JE!y#<9d&fl6qCE$FSG%wyONk(5HQep?7o zgzt0`a~3u!k}1UMY|1b-Q#K20&cY^&t#UdvgkaOmIPq}^WJIU^X1Kt-2CP0Sz16HC z65aM)Yt@3y+enxh=LLd5K&O%;lxK$hJg4KDT@zpf^>;-lE>jK0+S&d zOcMz2xW*W_w{8{EXjnz3D%Luyl zQ0|;T6yGqgh#pZgRC*LEz70)8sFsBe7U8-Rq}9qDKCFM>72E_Nk>X=@k?g1-N`721 zlqLmgo6$OjXI4ytjC5wc1sDyiHnP}kxlS>lY)^SfI#!(k?G&HiT?~$mWzdxFV>OY{ zO}1RC7>WgJWR>w-qw}!#rvtb3Bb6PBB=690IXpX~GE?gnDXca@sbC!#G4nBcS<6KwJ!7(YCkA{_|C%!qfr2q0T>Yzkh z6>*OT&tivCtL6%#pFXM4=j>~L0=k$)|_En+!gQKsJXva=wT3^JalF{NR zhx(CU8UR(RrZ|8*R&cKjOpE=LReF}Uz$wtN1emBiZ58gS!hba3uY!tlr39KvMOg&B zX*##l_Sg>9H|HykddMya=+g8_Xz7U)(43IV?KP~KkUDa;Vze#$B!G_Kl|YVBuyt(e z$sKTQ{Z03?@)y);J1L;Lf3<{^zTyetjN=aaul#=gl}5$kl&YD z_I~t!<#G2~#i*ONW1zlu9tk475&`az;GRila@Tp3JFcCsR{|sx$|a!mLL@xURo@SM zH@#b!2a4!5cUqWZ>&hgU4h4_Y#tQD%pWU$?y6jXu<-|=N@M3UF=#{(}*8hdu@R@xg z_twnN=*fZ{y5~advOw0lg+b6Y@d`q|em3{c2e;0j(|5i?(3se(q;UlNl&+G7#3~rl zx;Hpa)u)F#zj$8gArT&ilyqx=t%yq+5S6Wn`0`%&hH34}f=iD(V?z&!XkjTDj=8Qh zI2`0rGCzm8n;)w}W3rSdk0NE0$0f$8&e!f$CEloeM`zLQHMkRwCS)Wq@hF~&yE^vs z*ab0f#!O)Mv0e2$bM^e5iy^~`U|*d`es z(42Laf^jeOIDx2frK7;(#ty>ZXyGkNRQ$ceMh%A#mAXA%{+~w=9X@OX`~{ZBca?+xF#gPl zk?>c1#R3#|;lIb=-5-O?kzyG6D znE(*(g&7`yNvZA$QOH~)ryq`gck*{~bTy)FE0Vk`0h@+!ykYcc*iMmDH3%RPg&`cE z|3!k&LcWHjy-rWEr~aaBPi-_^1A?-eR4Mz+fIs%tY@gGL<-voae z3|p=h>IG!KiT@$6;X=jL;A%nK#v+Pr4^o#4G9+ZH=yX7O zlA7<30U_F+Plw}Tu|B7jqX+OfwoFh)mzhGO8Oe4hJUw7c`e(H=%Y`U5Hm7R|UaA*~ z3KSY7BZPr}ck;J#PchN&ZmICdcDpnNyZvZn><2J}UUqPc!zMH1jZ-pn%-NZ2SMgzs zL7DP9W%JNQSNxIf5{Y6ER!9u7xX7NeNTm_k3sHBGgW?PyC|sHj1CMBqBSzO5Zgki1 z3~j4CI)Vcn;rpcV2=_lgUItGnY_^mpumJG6#OH*oT8R-}-4*zqRp@fSdma3lc;UIL z{cw>hb^%X7JYvCh(TTenik&`qzm+@O;{>Y|M^O>nwD4av>i6IuQt4UjaeQNNR&|1Q&KRY zyxr}-dj|{>_94`XOThlI>49sv?YcSnRV54M^we`SD3Bnbq*qP=lGZFK08Druhz7wObqmA>M}>M( zPXp$@w}DT!F%To6qqob!?D)2F1x6(SHi)rC2@hxM5!2xlJ#OFoeW%cs8p5T~HavQG z-7~?VBKp@oOvi)VuGhPd-T!Le&_gA=S`cyC(eQv?0#0wxzym+Xjfh{hvo17nqBlMn z?$m%z!cE^-4Y-$a4?8TW^ZLvRjhk?7g<|W&OGqn$rGE)w9=~a4LtyofC%;we;UBZtP0Jsv7_3}R0O}u5 zNuZUG(6&@@e|u`F>FyH)6oU@gH2~5`DB$>UeYJMMA1!LL@W6Hvqd;lh zC;S4_cf569a`UG(y471WK5(IkmDakCFT3Ev=^3)_3v`wSKxN%04d9Ge_XRph_~~z5 zxHjC5)_sAF5@`DT3{HB>su!5yv018dv3ZSR6j~%B>6h|AF=x87q4~?B)p?re?Vj)SRk$F?SVb4;7KND1 zUn~$N*?f@{twqzr6Id{w@w>aq;GsDsV-g~;G~mVP@d8JRwURP^@N2f+89!O1oEwbp zEENp=Z{*xeA*HFwtO~Lf8Zi-U>Vahf%^dZUe~!BO-%^~UbS!bO;%Ht*O0&cunnxwN2X)p$ zN+rffK&V;nMNPJjTj?{ic}Q$JS}CI?$4^V%$mrm(gK4az!s!RO3L}=Vsse7F6I9xY zTwZt)8B>6am+mQt|3TMR1)M4{H4mnv62Gic{Lt0~$AzM5EO^!Q`!Ofa1x;)o=wB!1IH1hmXxg*9cGQN*UrVVE~98ma}O5IZ%zb ziEn!&c;kc2dGn{D)d_?!*>qZY^GMDfZHq)W4f)vVr1F6L+dZ`YD`8X!$$o4msivs_ zt4%abCzH0%i5L{Rl{BNbHP%Bi+zw#*LN5(}@nADZ+vh|`p;S!?XG@kC#!e!YS(O1q zw5Q0!F_X%yig3;bnbnD;=Asz0+Oi{zw>?o51r7^VQ*u_gL^ns)L}>IXVAyU~e45M6 z%%5qUVKWL6W9$S{A7BB%rZAF)gloH{z70E`)CX9E&_+JM?4_jQ*CLK~@BwDW;rjo1 zsy(X2#|_!>yW_gWR>!;;(~Dii>}Sr?U#YuS`>NKU8L4){N6wlrlpYun%qAfknk|kn zmfl*wk=u5|JyXUS!stj4F1u!GA4bsD%ePwdv&^|*^<_gjf#E^GHS{bAa2-5Llss&K zVbXvupss7ffR1p_Y@fTh=*T|BJCCip92#r*b2;pR*wGer61>nQigR}5l?2|m$Q&3N z92S+LN8`d;cjgp5kUcOYI0!0#$lxH(E`P{Dfx*FXQ8_6F$JKsLih)5A-Uo zc0t(#XZ#?I2-+jOFcn`w!p*~wE_8#Ol~D8=1X@caD?0`HMQ38>a{Ke`avXkK@f@dXf1p`R~l;yj z7Amy8g8-XR%vd{%()iYNEa@XQ#0zTpP#&G^sQ{@TuhH2G6khy`pcn+-3NmTNVvuGF z`RSUBK;K}nSNf^XR>?W{nqGeYgVGY!%qRuh20C;g@>b@-54v+PWcon^4o8q5TAANn z>4NctaGJvh3LrSZJh1?R?RI#LMNVKB2myCOP`ysDVRGR*15^&YP*L#mHyjYMD}$hKUK45+D_M zNjqQjZ)W$0(Rs@XiFu#DqQZ}P7&u@}BK9H2td$~C{}+oy8vMn#Qkq+cwBh5QCsf$m*Q-uIV&^QZUc2|@S3pMjpiI{ z<|K>RW`V!Toop0*kn-}uTkmo!7&}b1*fPzwOdDIURK%G~f^+-r%5j2wOjf4Nl3`;V zgtaCiM$xY|Er{fmfIG4k4?cV+HanlRYAHmdLAhwYVj~%LUSuo1p;UPg;cV(E^kC&G zP_%+OaD@j9v?}=v1jsM*$Ji!TF>r{5kNemwBU)`6*C@u>+%ti_0_nv^O-8v-nB0x^ ziUFL?YJi=Kx`{9xBv1Ut3dJypjRe@sNxdM&5Gk)dTfA`30o4Oa`TkzhH58eH7U2O$xEeQdL#8G9_Ye~g{o%6!9&)34RNtDOYuf6uBr!ABK*L0jHsV$z1PEf3F}J+F=1 z@=!{^BEqEXn-gG~zV!b6jvl`}-+U6Id+LY2xv3&n%Dy=vO@Ai)=CY&#(DcnU8NeCn zn}gn_DM9=S>LllSo2ndz&;r2cIisR(3esMg!I`0w_7YZB`zv|&4osGS(+D&+0&i1+ z#vy?W2_;R#Xb|No?%1%ozyJ8|CS~G*1Wa2H^qM4rrfC=rf^J7uNwWl*p6-T)FxyL2 za$*oLm8xVg#%GhNWOBe192KG$LcvU5(<9s78PsP>PU!O|Q3maFj9=6>ta!BB@AJcW^Aesvo0Ec3ufTW-;vnkMT^rkC1}>FA(%51Oix@z*xh-a3oP?B~ z0M*X6m;(Z1CDa)T7Qx0jrh7*AzHRHYn?gTMgzL01eIO{bH%TJ+Ky7o31eac=1)L}C z+_6)aCLRB{N7Udp0opR#0auyP5^j3>mfEsm5VtVLopM|HV#T;aF$Ym|g_p@Jo` zp+C1b?uA_!7d)pNYeX3npr((MhCu(W25R~yZomWm4;E$~Qal8lxCG*m*Ncq?pnI^; z>+yI?9AFku=8J>dd_v4MD(L@zGcn6>*f1(#W&BI=UE=()&&8&|Kb-qI7lCsTI2VC) z5jYota}hWffv83RrHJ*K<>8AVn6d)BVjqV-3K89xBP=fALS2aye4Bi_Pe|E$BGPvi zA|xjE%n~UKMv$?Ax(pWA2PGuq&DGnKJz;yUjPm~hf_|CzSHZ>{s!Yz^s&Mu4&|%xjkL?GS|*EsIDG2lA+iR#-GJUyBmO zNtdB`iI~G24u~+z91YkNq#ooL&!o#9iq3n-<|(c;!r8#*cN*PriSm@Q*ND587LAdA z8svcx1?My{mXjV8M&nut!Ynb3_?MA`jkRK(01A%dtT*oL8h?Kz1qV z0R;!f^x}c&EEG7PxE*EC1%gF=fg8tnKI6j*R4a)tQ zrcD5XJ6Ew(o3X%CR1IC2P&)Ck(7-POa}fZWwp0cf>7MGWI!!Yi1}U_gHfl$*i^X0D zEs|;2|iYy9qY(OFo*HQ(|ngmzlkB%0o6c;Rifvc@mKysoP}OwfRcQgij6Tw;Lr zp@~A+YX-@OJvpyf+1p|&+<wig#bO;rj%p*}R+#}h0MP!QW-Mq-8;s*U1%#u!&*S$(ke!PQ zq1(xbe>nc6+v6$1DgeGpkH-yKAibV|YSTt!%3 zq)}@W&z$gC=s>B!{Qr)sS5!%tCEjbeF~J%CU_28yJ+>j{^O(`BkJ+OCKtD{kRQr^+ zqvmS$-SEM){0khd=LHEERNO;>y05l0_)HIdaiO99sqUdKWu$Z6vcnBaXMje5(_Jmo z>{j3c4TC}-$_iW=92Hth!Hfxxs+4={q6^=DwAYsCkBX>o5BP~!NJF9FCpHdc;~H-7 zNAFi2cdu1El;*8{sBd7d1d+x(DhRQjdnTF5UFT8W*?GF=KQuOQxdhaqU|DRe;BNid z9owPHPQ|e#ZVv=X-5d!$4W%v^=Jux4&6aS}Q0f|Sw+p3imV}jtQWwN}#wc|;ftkS} zP>H8D4&iJOPc;W-1P4T=G8!Dv8B`ff57@;)P|8FKgJ?gQ$e{t71enI3SpW=q4{p!? zm$r?2yki^Rym0;l4GiQ4A#CsFt4HFaU;@^h;|JkuSV{>BeW>2yM7{C50{RO&8 z?L5s^_~5y(vnc{-LBJbabTu@sbU2v>f(m)X+>V>GcbyoQbk=1I(Wt=H!7-vMDIR=r zj5&94M@k2tIA4FA+}ndNuOpjNtT{hW5rn>y#wS654%tT2bB~<3F|FH#|ADL{x+vfY zMg{eyp3rW6_Lz?3e16sQYu;DBlb3lGy9!-y$abSk0_74sn!}(lX&{rw{r&4J^PjmO zHX0jwZfm(b$e@%-NM}(85?(o`?}okqnEfA*$RH(tFq)PrTRAGitI{purg>Eg5pLgC zB^nl3D50jwSBa>1e`tE}wvwl8zAvK_?eN04Xky?h3ATrNk$^m?>DkLSPP$|LlhK4t zBohj{K`wC&l)6De=-XH~=!!sT+(kn=jnSQ_VQG%=0?1x=(~~!z`fBZhKftyI>IBB| z#wnRO=Il(A5-1U)Kq*Tk`~uU*KU7bf_UqQ>(-?JGqGAy%r7V$fqdt?eL{4b{G_piZ z25<&siO}Fck%XVddq%*IrISss-J)7}=L&7}1=EU?L3Za3qoEAqEQJzg`fL}*Jh9{T z18TM3y(fd2=nfSQa#o-~f~=%6chA4Lqd#6UYHfu@@nMEeI~I;*4rwUK6fX*4#ej0i z?cC-UA34zZg}KUy66|tDX1>LoZNvQkZmL77r0m3p69*V}89F6g8~@Mv%D6*uBV)J5>SK!8ci9HE z8?%DZ={>sRx=ih}+D!F2&2adpv*!!timaGvGOU>VAG#-7--^=xZz||1fLjG_6DV+& zI;vd2&1mZ9(#o#)F&$ zHK+mJG6C9pG7X6Bny+08XcB2Pw+TF3|cs2n(= zEx36a%U9@iI?FNRCIy>hfM-S%R%98Np8`V$gyg_q1L#U=n7|2UBm6u`cM+!>$U4Fg z0$&rJLgNE36Z}K*gcdb5KLJf*IEMjEsJsZfs9+x_7uctGd3zN0PC;{9i~7n>7Tj4C zKhX~9uF5uue#%-jO#YY0d;n|AQwZKeUgvyIVWHpa1s*Us-ABw`vI^WTSTL?i_6}ld zCoBlKv&p=YqjSe(ZzpCm6QQD5rP5jxSG>qx~hUm=R z6nJF4>;}|T{M|C*)%bVop9Yn1*f6m<&U~Ox8CxAd30F==DYVG|ojcsslU+}2Oaai^ z%9!FdLSqVm(8|$)T}Nz80pH#{veCT@vbU02@8J7s`&;j?N&Dc>=?Zx;4j9qJ{IRc*PiOX%|gQQW{H zB-@&0$$-N?yC#x}YY&Hvdhvy*LpG9NY|(}e2I0ESgL9*h=NCOCoQuJ2q6p?mFsSpF zu>sUYSWSqa=Ln*tQ4DGlH1els!==oe1C9Mn?`LVoyv$rnh9x^2Y+%?rLU$MZ+^H>_ z!}uu>EghTRUF<0b>w+Sniq}TgbKLNG=%^6=avXLhi+Q%dgiU6 zA?FRQ@j{O$-|=6?XXtSYDWn<;p+mF~gq^{RoBv3f5$>Uo2ruOgqkUjX4Iiign?1hk zRJ^MKnhLKAQo_(F3StMikry($m0}VcxBT)+Z~nh=uPh|roIcfK5O*_D3$aA$;csq2 zEcFKyW$xTWmx8Idi`P>Ls9g?TglfK^{-f8)-PKg8hu^SnYtP%7st&^BM7{Yi>5xYq zwxZgIZ(aMV9j*YJ4iqO&rKLxCt zQG8?UNU*kP?9?vXQk#yYbe=WjtD6;$N=CR4^$T1hB8C=o8l451_>la0VKhRoJ5EMg}OHgUd!X&6Wwz~^3mjx3x&43iDVcZ4UC{}4@aZI!i(J_$;0q3f$|PHeJ_t#g z?kkzQan>G5w>Ct|g78uOlM6~>{(o21I91|m!|#T&gqISA#W%!X7*`j2Dt2zn3o&EB z0pKKK(*K|CBi&H#R!{--sXtfSRL{e=2w5+k4rPST!C>wt+@Ql~n-fk|Xm?KbNn)XO zSujK=t1wWW5ZTZ=G=nHEVMsa=#wCmmD7#N2gX++D5>y)og+AL8q6%U1$v%#H;!GK= zn}tM3`YT5gk%cGwn5Yk~Ls=vM*Kbk`Fq}ar`)Gu4HY$e$%&W;3bAJ9*Y&Dc)$;h`_ z^Rvvk;4@=m_o6|<^wq=@MQ3(sI7Ckxrhx^)*wT!aTzjSsEFb_G{wGWBlx%ZOj@fR_ zpEF4Oi_8qWWoD*P=z?P(AvMPm_NjGe)*Or7Lri7mcd~GBiQ*ffCt8KCy08?yQJ`K} z=wo-2I(dn)ZU5R9`*392O}O$KQ9SV)wB?y?Io8RR3>z$0W0M9v<1CAPsxi+tCu6EL zo45J0u@4a&a|jb2rfxvEGe;qt#6C#sfF;g+7?TS^)H2y3b{Dbjh7iXvh{z^7ydxL; z0I}@`;E{K`AdFJ$ln;A9>c{WcS|sDLh^NZ@*%u)cW{854;VG+dLycb4s^&c=gxj37dbUgl-jca+62)i|Hr@cgGN{2jFn`79DDZ%=IY_wUbp#!Z&{$F8 zWOopIa=-$Dq>l_V!bgrY*~4xp_T<3u+QF0KZt>QnMKWb{yfd^ZZGpXu*iHhVoU@Z~ z`4-|pGxQ04c)_4-Py+khK|9rSTTl||>J(8y~G*JF&5>$`~HX;rOw@NKPX zRezx}RVyB37*(tK&SDg3Rjv46VES-zx1SdOR@Hp|qT9)g#@BZeu~Mp932E#zsA^T; zQ5pb^s#TK#oC#H{A@vtX_^s3h5nk2mn^pgCjCK?>Uof=WQJD5*G^qZ32{er?kP!6t zl(m{xe_jxBjg~&n&}(g4Id;Wu?_9AlI&r4zxklaUI|PS=dQlGtw_|f}e)7&kZ#;aN z!n|gawrp4*NfJ8x_0f*9QD|6wq6C@h2qrQsw`ffVo_hOg+h#_ZR9Hpz2nS>_8%^4V)U(ns z=+7qD!k&9&U|Q^_?1Ku2A#Y_b)cvY0~s+v>xtMm<7*rK&diNdVG3b^6h)k9r+k5h71b__7H4FE(*Xg#I{3 zWhw6P0Cgzd=hx*cgG`DfH|HsLSF>-DW@Rx@dA&2Dt+siRv9YHn*dfEdzMM~3YtVr3 z32O{#$&Bb6#U@w#DoPzCE(iN6v4Rp@)xroo5rraR1*MmX6_fxSfm{*c3JY^1U|%8@ z;vs`X6u5AJj@IN0W&Kst!!ID5&C!&7@~~XT7!e{1-wC#a?*ubCilFb+1*(d|e^!D@ zo4*A5VMiOQoer-Lr!S$6%vj}eKx(tv2>%KNCH$AozE6~L#FZzXllQJI0F%wqY9FZn zIKa-^YZRngt_Gz`@I(dOAMn}2DxQVVgo{aK#ZG8G1JxV%IAgKP>#O9y1s;1KkC#<| zsw7Ba!Y}1pb%jPioMAOqdKPSYsf5X4L`F0T9lnHx` zSo|&g-aytA7xCus3aKO`znO2Dpo~2}1zNa;&PFVt2^PT6PR%Fk%ivApD2Mfp+b7xA zA|ZjCX+5(S#P_xiEtTIo)Ruex75Y#^xU@cPIRtu^_k=ZEAEIkmOFeP4-5yU3Wu;s``G6rd6D4+g<>d8=rlH?bxI4pH1-g&q8w~D z0H|FQ<(?xJEfYYspQ7B~h*jNqWNoGDb}+I4wWF%r0b*4*AwbHVo?B%DB6SOA_oHNC zqeDKD9l601`k^E`#WB$|G)%p4Y7UGzsc*nyeiU6&Q!yd%JIYWblqZLS%FZ_aJ)aAxfzqAPt48 z$&?>TyEK_zQr};~O65-1h?P6}`pe%BIJ_jpm4+znVc3g6(@B+(Qq_ofr0v`eJElHG zLQT&o0p=t3=;J#EI1lQYU$0vgn2F*{#()HVLO%&SRYR;0@-x>ETYrf(0IG&qVF2ye z5Sv=xSAq=Z3v=fDd|NY1lF8U>B*)<@49xn#zSiBPmRT@_Cyqu6Qd4QI zm7VJQ1i{kvVe(+l^#Rxc8>tS-D)35|s??pS(R5 zmDl$UPD>3HHG`iP@SHOJGcEJ$3)H*iriJk7ZY%GpnnCrwf+Jo@zXK;*k8;m`@WHrG zyISN%O!NY5UI3FSa%X`76dG4=42BF*k?Mp+lG}lMKCRchIir4&3mM@eAu{v=Vo6U4 zK9wCa55KMKn0wdvkl+oYe!qhGmFfBHwB`36>l~%%HZ{RQ!|N}WP*TZa@Ii!fal2YE zYwVaIPKFaHy3F(5oOeygQKEj z=QNJ$jIwhsuAe9kqaXD)qPA=r!acFP@2Qiw?+!gVM6a~wDM=8kG)Vxd#45!&x20I+ z1PLM)$FC7$8;ezrtsgG|rN6{O=J!mfKn|$CR6?JjV5x4LW4dQ#@7uOcyD9WiB|J)m zRy;we_2VSC^w~445!3dfmBZ`DN?_@`umG%`J9g^Qq~jm=h+1r$6ikHra4{5p3yqFK zR}rKr$4I!xD_9d725}2>+$p!EFIJ2@gv1Ln)sL32E>wcGKespTgRDF7=^NZ(&9uncc ztwi4iO{*U!4T!1>NPOR~d&9JLWx=J#onaOg(Htonj=6qla5%`LWPT2FH$PT|#$;6~ zo}WndVqkL}A`L>x=DO~%>AtVmaL*3x6M7Ita}x0o?Dd0#L!c9mZd_JpmvHpp`a!{Q zA^OxAE;n;)wih10zV@!rQ$zUt%gE+n{(mR+QB_jkM2jIS{+0ODxXp2f*hSF&pT$1L zUcyx9PwA)Xp4FMPb(-%q^VCPxeO1e%l>$&t=Y`;8c!QXEsmVAwB1D~%j2LR;J>b)` zMK=l~6fS{M=P9W++JV~UsHirk8?!K34;XHRZm@3!a|`(t&Yv>F21dC? zn>Ble;F$Y~VC@9hWK@0xZ0n;S)Y?j;QIKS`u^*F)-3eI2ndA_@|DqvwcZyW(PDC9> zT8xN!zc)+_`M|i&$(|$?-ogQDf8nhYq{3S~ zymk=Y`cTve)}mSR(+5iF(VV`J3hC?zVxOEA$@GBXFo6zwG*N-`T7^qcrh-n1Vt#>@ z``GtMMHG2R3tov*WiO-DGwk~qtR6c5J6$F*k8_T%i*>krx)x&p-HXK zncB*#lzj(XE<~WFkRSr`hJ%FfNqaju4zh2dIpWv|4tM`mW{T|Fg8$#Os-#(o8x5xn zLlRcSuaA2v_K%p=?3-*a#-ZP=`(9_$Zqj_B$x<&X$qepz%qs2nn-)apFcp-mfLiKw5g?`Fpa9VHw;!sf zP5brc=J2B1J`arkJP|9c;vkcdY!dn*p+NT8=t zyj2C!)^=1Jtnt=o2O+Pb^5+ub4B8~zzKehKjLaJlm6O2#v_=V3c&7!UPN(xIMExw$ zdCaQ6EI1srlzQD`l?v|YkC%*ETVYY0sijk^Kqd8-;81Gl`~0wuh3W8#9=GrPzLWBy zw27Vt5Xhzmhl-RC$hI!y9(Gt#=k=MTc&MQiFF>p_O9D&3wc`4lX=g)V^^YgNRtz@0 zCI*OgPLUwfoPLG30anXsU$M?i3APd{_!$uE>{~xs!mflV+s0~`aXKyd|1DSP7RJ`9 zmdAV@V~sP$-K+n5{Dg$36EfltFb^{wldenrCb8V`sUb_LDh;Wo-L|$dt54WQG}2Y!3fIXJuuo z?r&(Un50XVpE+ay*X7QJgjeDWkJ~L2E3hhDp`*g(c00V)qW{tVv-jnJO_b^XIop|@ zAabfO$XO|1x$hWCD3P`)Ny;H|q-kgaX;N|&3SLt@cU_yrMMY2$R6OxU)YVZy6x3DM zW4%{A*JE8*-F02B-}BB)GihNmZ{A4@-=FZuF7_aw_j&L4`8=ObsL|v0f}$70SE;Q4 zUs^$Zm-|)Z1y-! zJ(RxE|gbBQ>q~X zQjQ^}!P8I!CQly26RenOVHHv>sBy+!^$O_l$YZv!H|#|qz%g~t;cV0Woi}R$u4opGdgW_{}N?|4LIp{-JR!b zR`-}|_pFXUo)8HWuJA=X{&g;pR+Ua7{PL3hPh1&MG~3!wwamVQ8Xgv-L$QmLXI+yw z0WY{ONB=ziMywfkKK7+A5I53C|&Im#2&3{T;z{i5|O>w)(b5MXZI0&gx9X>8<= zZLp*yYw{SLOk)Otjap_f@%Cv34(0!QXndMNU4b$0aPFL(3+Y$s0aRV~gIV8Z%`{$R z*lnQnXX|d#{-PZYKc?yr)b_8G9z{qVbkM$x+w|EKcKZf z(JB?GTBMN4?f&q+@Y}wP@@pIJY-y*I3BEFQ5=4@hw6?%+?(~}T6%tUAn)3wEV^MQ{ zMyp3cPoiShhJMo`?&-@9OnCi*v8qRcRgOo_I$LWKXo)Jw+R$Emyj7676YV0ZAWO8X z%PPpA^0-FC43ecJBC7&sV5xJD?E2!z3;JG@nH^4!tqzn5mjswZBvAkiJa*H!Pcqj# zLt`=nxV^*`99ekhUj7jzfSi8Y$e56VS}rw!q|Q)mKo^Q6j%!^eK_-z$5+N5n%PoBO zf-UzfyEQX07Y2zeC!Q^7T`J)wVI>H-fi2H%`z-(3=_Srg;yyZSo*mY@L;_8s6(NFN z{u0OaU;g>k(|=aFI$>_@NAT)dOY6B3)MkQ&y#N&$_uSrJ2iGkfdP`>F+wnOvF%{tu z509nEfd|7HcNl~<7bhA*B&;bk;{pw3$@;T#L19b5+Xc@2=kh1z?ab?yduh&BIS%?!RhRw~zkDJB zCn9hn0>7;Y;Fe*cXXX=Sm>DX=H2gWeu>Ynq^KLJs4FkXMJg?*GW$;~i(1$~L>U;qY!>hPb z87M+sB9&oy1y?GAhtzpe8HT^4LpGS93_&4+*c$*W$zkjXiz(a>GFYY%ZY4j)a z05SLSghE0My!m^AUgQ#w%;*JA`A{GdbOV2TEzdPXBtuWJSP1~l3xxv#h`bBDo@=~r z(Wf4ge2C%)=P8%J7FjEYA`Oi^>(E;ZaxDoFN_5;3e%2FW&uLwsh)T;K;;29s1ot$( zA(uLMFcI=((qs}%5;r^$Nc zj)X+0grXw?RMTs`=*W)tQOqpa9vCt~5i6nSh>*-amZBr8r3R23+mae^Y(z&ICHy3! zBLe=e6&)GV8jwJfb7fFzi|PIsi$D2qqe{IZoDvrh;qptUNhBl0P(#I{>4UeMJ~(Uq zqcVj#csXx!0dRJ9YlGAjaxx9lS~tCE>GswIIUmV11#1~p(UQ@vKB*-nA`3#Nbgjt3 zDhV=)$U*{gSBfm;7}jZeYrfFvKP~h@l|PfeA+I2}F6Y&pQu}!Z?R;BY403UKm3H;ARJwb(9p(r(vzNi`T$>k4Ym?s2emjktJ z=pNhT2~nTo7XJwVu6;)y0M_WoaK$iJn#;>94r{4{dLLInTntZ&vo>aUum!}ak64<2 zo;E0zS&7(E2#8}vPTp#a`jE{Pp_Hb6E)$?!_od2Cbiidjj7qba;p68x#}>gYJ?yH3 zFNDG&>H~JT2Q-BEfRc3SMuv zHo{Lq&^>VRb(g+CC`7#o>&I49Ma?d2A3R(^e{ryNj-WsFiCEfcV8^zH*9dYBVV5sq z>8=a|Z_L~6)-toxT3IG3_fyzv`2vXJLoJ{Bo0!cqC~4D+s8gurV}(vZE1!B#T)y;5 z{s#!p%n~KBbv#TycpdcALSl4{$Cn`PQ}5!+_9G~rF56GNBUX3oaTfNo1|O0+3}S9L z;JCvpP($%7s=|hxXyUR>z0GEc8PKW9(@h=;R^Oz;@fL3K8g)>YCa%nHZ?L1qV5dUV8r zBo1WJJi#WExC!_n<%V)P;d&_d4np1ygnCdG$LFdE2u2s9K~1KPdX+szT>3o9OW&Z% zZiz)xU8)9z3nGxq3wq#+6o9N1I@U0*NUb*@$PEpjZ#NfFudtN@xE2Lw3K|hi{@qI8 zuoh^u-xGqUg}g2?Ef{b^fFU);K!iPOA;NI7EoK_LwY5G-u%RG({ovfj3nvN5PZaAK zZ=f;MJQYOL8X8@GIL0?LQ-8r!P(#hhu!0)(vZ!F9XC2BNoqN~*%pAh)-nx4EQ}|0L z|KDBHtSJcQy`4LgzMXoXnwWiM)?c#*7=4CE^}p(^y6d!GX{W)DWcCkhiWW%kCFJQF zFiSjUK9-}R`BD=|Dqn>sedPCOdU<%S1tnj`R9+Dk&cf`_Xr71|YIR9OWd+3M#`n0r zzf?c@{kqE)Mk&^W7u94S6CP|+>R?Akb0wtYvF*C5;xW-22{6g~pP1iyejoSX-ap?w z^^ubmLW_CqPLMVc`8TDR`Ca=xDZtn51r7MBJ5i+tDluGD+RG2)WDa zwxdP~H%Tr^z}>}l+fjoAnxt-91l5G@(9pBx%d9FdDZh3<1%t|Jc0(w zTB14$C`nFCi0N@qU>?;b8bVZHE;Qssfq90@UpDlNYBJL?i@Cj3%T{?$Ua2${Fn9jn zc!|u09<3Kj9n+I|je>^`?(cUVy8pha%}O1Eb-4MhcS}y|1yZ-@$-8CA`=(c(?iIOp z$u~0Z(pbG=s|mr2jX=S5yMmyk>p=sUXDh@1197>= z!+`ao&$|vD`r$u=p2k2B-cWo#AUg+}yl%w61{Xyj^77wzB|I46P%C}P3J~IE0+Om{ zeE^(LA;ZKl$;}FdLGwApFy0mXgOc>?00o_q;k_gX-*Nf*Z+rc~)rNnf7G&hn`S~dS z)F_5=0%lbI-&;FhQ+QFqPX*rm!}&|{ZqF;sjpV$aV*@2XJGGA*lD#!M*SOnY(%+}= zp$lt2*Us1W)%;QYbAawl&Ge&*RD-_qe578jq^jEeoBiK*lBz283!Z{Df^dX_HuW>E zRxSE-rMvrh^E)q@sm<~@Q z1w5?PCtZVIsW$T_emKO*X)kyqMxaeWJnzR^c{l5BNufniy0r(+JAE=*3+`!DovxD`lwvwm5`Aa52V9vJMQ+WCz7TGabC;N;u@kX|*QDvuww3?FPEA!ZHO?C=RoFj%SO)e^$N zNCWjxwvRZg6{H)4A+lYk8q`dS(>$`=T5r6aH$kTVb>mGgX#KYpJ!o+*;|N zzDf_-)(!H=uqJycT$sFWSY?1_>8*u?`5KRFRk66$Ce@UNsDH3ygqXE-#~IaIL-NRQ zM^;B{4N#{FOB1SKZ488A^Dai4Obr-z@!5W%w}Bt(g~T(oDnxzB>coClFcK2_c}t3! zh<7f6mj$>ahTuvl?sXyR3z1l5P*VOEF%-(j4G#mHXkqaVxC3stbo2>PpW}MHi8yl9 z;+W&|Gz1#!y*{`#1I0mbIPowurOWb^1@mbtUmI$iLu7Ad7*rDQu1s=Fc&6Z_$N3EyH;|p{w zdJ>RrZB~0CKH~m|b+IxYz`YELIFEzNpz&#TkQmz#R%`?Eu~l=mPI+TB?#K1$6$4u@ z50dnjoq7a-iWIt98~0(}n*^L0FD5qb#jR2l0d!84qVXx*Dn(@&ScA%XlqyBzleneJ zB8V<5T{d#;$>OZ5#+h}SovH_PTk&$zL=#?yR)E*Ln{m0lwZ#gwFIgR4%9_FA>-qS|KCfqT2ok35CZ+bL3tPD9?E6F1@I+s0bHH^r>uWv zl^R%rRq`aZ|=lo&rx$ zRTTn{O>5n8FSnl?pL_9Le~=Fk)~KFkuP$>ivu*H86~lgO!1U<0m-n6A-_kiflC&|18KR_|0efcjG^rWnB?2(8^0}P8 z4}N(A)lFqH#8pPhYw$&R;XorKseMc&)l zie+WXTyE!2u3Z-y)F0;Tfv^ksOhL$OK?QLz2uvgi@Q7${32>=8z6Fpf%N?!5=NOQRTktN?PTf(SDs+fgk2Sj^Fs7I(60G;SS z9X&~cNa8@1X_JM}9^_NI@gqj1QFq20O&0D6iTo#P@)lIjn-7;?f;Lg3#f_b z39)5|DUbeskvA|0zx+sWtpm0p)Dktjy?$x}J5YT03@Cp(lF(57@#xCss4_b$Y_rg< z%Ua1KQ8SqZR_7e1%CV?)j;-8Mh3+(z6*q|^E)P}m5V6Lc^vYMoBVek_OMy9yE?i}1 zc=of6v{@XssyXH|3!MM#mf7Xi3)^1*nBiqM`>Y~r4%aBnRGdA{mzRoQ~ zS#V|b_>UiXPG&w{u#N|AA@7vJU&caUt>d9)v08EJEAT!M9Md~%6yRWu?sC2#P_c{O z<6cFT;%W`?5T4(s00FZ@60#jflNzCB;yTs|n7Yidjxysa>+!(4PFe3PTxC5TR~IPj znQ)c$5TLG6)|-Lrdd5RiHXFkn=||{HP}9>{h&#>ojG87UWA&_Cd54;z*_fG$UkV2g zD0@Iv&1eNg%Mi%5HF^b|J0{c&4F3jdDy|A5>Z@a?f;a`&MHaz4Vcwe|^ImE)uJ|No z*s-?F-?$b7HHr8C(>$jstSPuJ|NH#2@+xu{(ofJ7H7EPBthckq8Lu#WVi>BwT=%)| zZ1}O``A^IG=tS{lMxRaO)rUsb39mjvuRiwF=!8W0S(2xGA^dO`Hr@X9lwSXGrpR1k zZqvzkVzys&yo8U$$x6Ui;QZ0Ful~8me*NfO`5P%Fz8YlH2s>x32hfen1iDW0*_sv| zE1|0+c(N5@mvwzl?x7L6-7fy@U}g>;9d8_Mr;mmRRPu+w>a?LI2>zFlWf&vjA4!Z~ z|Jgn+=F*?S75z6e_(kPQxJ}l7Umu=`39g#q-LKF}*LiTVQ=RIugwzybnE@EzNsh;_N0~#&t>gXAX zo@pWH=IA`y$4rZNuXYx7bIBh!u3}{Pmg@Au=5+OuE}Gg!xK$r$z`8GHf1Vz_{vi(7p--x5h&?kJt`+t!cK?| zlVC3*I1mZTHNYOq9T>gg(#m zP}L8=_*`m`D=Nak$$p3gcp!18kyH!!`X_JQ_uBnutDF=`YB4!^8^Qn2sV}T8_!~I? zU6u2{ob%{!bd$5rr7NgS*`I1}GhS`%p6$*0SUc2kp8ihV8yaW9-27Yesr-BM#~%GT zfUOdeWGxs;GTn97|H-!obFVSi29bVJ9aIKEUOZWqLlWgcln3x*Z~Gy)Zxj;_dK-bM zcqAd$-Cp{GT8^vfgARPr2w?~9{3fZ|IjN<%#kqWgV1HCIt1$OPYDqd%9cqwXk&iOw;#KB@w+eB^0SzQ- z5nCqqvw{_xFtc_XQjI`q`RuS9N@9rUlR>n}vlgwU=)9P`$WjZ%)SQ9EYRJ~HsvB~| z5>ZudwpLOOT%jL+pbfb8W`(yMtQA2@$b(6ghgys&$|iJbMn&1q@f+`9&lJa0zVdRY zwceDXup4t!I~Ku0U12R{=Gg2NHt@M+lGo>A#J0;1J}1 zlwP71NIPCXRfQ|}DaIj%AQ|SAP~^f>Hq5Ga0aC`R+Nnz1s&)}Tt9+MW#^h-HaH;|r zzPovA;JS1=2|c~Z91)b!h$egn9NL8L(UoSI55k(ai4%SUq$_|x2Ud2GhQ<(IG8K%} z@O4PZhkrLaSXjN;i1O;Kdvt!K3^?T|@RZ^NJn7}9mCOmRx}}D8&zhswM1$CXng$)@Vtbm{$z@Hd8-0XBpN|fMV{!DD$~*l ztLT4U_JMpOuqIyy2;NYBU6yDSOHzKFXqA?W?sOl#^vYZ0TZOqED^S4MA<@|)X7F?* zsZbO!138?#ZKtp1o;M%K#Qo<;k~n)>)FJ^bA|Cg^ALkD2>U;LNYd%!~iW%TG9yvTM z&yoO=cuoqD>^jfMfzeV4a0zilfrHL{d+k#XFZ+iQGaqXrN>^XRMTff7nG#5n42cj` zQ&$^fnkCdE3LkB#yWSXcLi8*N`eO1?@z7J;zJ`@+di3>WW}cM?)__S-GBFs;H^5e= zt%Sl42AL)aIr;Ky>OzCelIRQxG6`=$7$;qAG&x-YO=2`Dg6dqRV@z}m_wKx#!sqLTD>0R@4lBR$3VMUB!CwpP`G3iu znD=PjklaghKFukkpQ9_Ozfirh8?x>+PS7}Y7i-_ueWLSfpEG31-TSfYXKp@Z19MHK zZp2*Ye&ZcUHEA@uGlf^vwpT*zdMoINV^?prHoft3?uqfjKuR$TMrT1!ZN!btXW$48 z7CKGfNdx}@@5$(u7C3v5sYS|L;H3oxc|KQ7K&ZzX4XROf)EZn@FSPD@`f zaArkwDb(n3dmDfpy8^ximso#j1>9_TCtrTrtpEo#83}X2FjfAYKB(i(tyv$Ybk)=<_g3u0vk0KcqG>N0s+B3jgR?oPhByy z0#-Fouozr8Jw9NdxPlC_DqK}W1=kDE*&)i%`@L18`gPXrb2R~98ce`E)M{MG7Z{-# zmwahVr{RNBu){FNm#ZPLR&=_f0&IUV>Z_Eap8bTYDh17J-Py2 z37~V;gGX(15#;ZXSr5FQnb$%=UL_Q1Y>Xg7P*A-BS3M|{gQ7k-zfh~v zm}|xtr)LI}4Sd<_;KNt4eZ}#vq(KG|F+KqnhyGf?9`G*%2SMft6K2dA*Y=X*5BNvQ z#>FdeNc2kfqo}W-oG~-Vq%uaTUQD?wy0`P&^fyXd2iAP+SoufQ;hLQz5bZ;dVYBlU z>F}geoU*aa7h!}l;Ma|;sU4A;5U@7jBx0@soo1#KSq>vFY%7dLa2O5*DG$p3_te~~ zDV$lbIscn{Ti&MJ|K^tG{GNWF?n{+s2eSTTeAzhC(5nATZ_!<)-3&i^4E{hh)iUw% z$dFA`O(i@Y{a>r5S}HY~q?#(x=>J32R7(;~CaR_qo7~0KR7~_-2{w6oIVv6D9vgV& z9ooxw$y{Hs*24sYD~Sumo*rE+VIwq84-Td+O3x2mysfHg5tCTE8JZUJFI!8iD zQYJb0LTIdPR-eV~F>OKs||qD{w{p@=ilDk4LXww37E8j1{yI;3W( zSq`?2<#Nt_>vG-Q12Zs3I+XJ9bvo?ys9l0eE;0k&Cu6u>>#q9WOAqwTJk%6bhafmI zPig=;tpbW2=5deG%fG+urI)#_RY_QfshG4|I>~rpOw=X;Bq!E%xt&3! zgqp<8pl#LYOgn=L2_m_A5APE2fRfl5Bx2dgb_R1L zm?U-v37E&g&Oo^TZ`2g#70l1SD(}U-!raQ7?evfILTYpNkJ+=bT8w**8iPy!l>T(x zEN!!9V;5Zi+2zr&7*c(9%V?6t3Ep*_m5>GEZi|)Qb9ic@%ykNLcy>ZI2z;nQi8iw5 z6SooaM5dP)?3{S%<=nB#7qRC=gNdfJ5Gm{N-36H>?rC~ME_Lvr(z76Dzhxq$#GVpe zooF39gt((1*~C4vZtSQ#HvCuha+#EL0@=kz2_Df#&IiBKWEW3~1|)nc!Z-Lq)1ivb zRz4ejPj&c`QdjJZs9%D&inwk=7HkBF5}##6Wp97`F`TB!jfGM-5dI2JOVQ=@JPKY0}fD)Bcx z^^cmClQ#S!b9KPlN)nkEyi$L5RC+IGnQ zueWZsMt_6;q{8NcuM3vsKa)Qp?^e(PjN}~7vC{|W$<)16|Lm1n4`y{Y))-zf%+jsa zeyW|P*{^kIUQd1v(C^UvjyL9;u1vDhTCZ1cggf6Gb~SkYAQy5s|GA|&pSyXH>OQf8 z+X=)O2uYxe+D<=^im@m2%pd~}R|=lcoEZa?B>N+%9lw>jP1n+n!fOTEWcOSNDdVe*)|&IpsmHo`=}y3PpmBDO#r-%8TQ2-i(p6*>`X{Y+M*?;uk` zU5IVWiSTrRG3N#1twYZ)P+pBp-DW5R2yFggEE}fW>I?^t5J+@saijA^wDb|=i62>|mT;Ovk3SHx4g=-4l zE|{10Q8_9S46MsJuffErp5rGpCI1zyp5%~Wq0@t)S(J9+#xOiUb zL!?I2+Ut{%;2C$n5={&1RC1g<#jWrLLt)SxE>i+w+ikbMuSpCv_^C3 zovZKZ4ErYi!+_&T;f@A46sH%$fq3hxpVJgA2lO`rvfm5$G^3tXFzgZ{)CCt>{iOh_ zNqE&VQmn!gn*gi*oi?*&-1-3ES=+*AhK%|R3i(Zq=8{)}%Li3u%1YA-Sw>f*CjtT| z$a~QZs!vGQr35WX4S}GCaRoss2C1OHa#S)N5+eRc*b|zHB%l(%=vnIty1gM!2v#XZ z6wU$76fjP!gHmxM80GQ9zl+Mniy8PoC=-Vjt^pEKkn2R>Ug7hs_11WO-f**SErcU1 zs#X|SZ|X&D#XYUJf=?hN!=u2wLfgP~0AwKGrNJHuG%#&;fQ+2Op}qz%PQ!iBLKwU5 zK}%l-*rp`B3C3$vQVnL$Ezs%4y)(Z304L@I`U4Fv*vQcK)$DN~{{Kmuhk5<~+=)4U z`YE~()sVd>Ta#5`+-x{(7^-j7ak}o>8u+2(=MOt9mL*a(K=hob)C%5JoRvuQ{Dm(L z%{^r??ChCH}qpkkSUqSj5%}6SZ3Umv6H8O>_b(Bzz&5Z zg6Y0)*IGZgCY~9nM^7fOXT%H=+)`q=kY+GFvOd1M+gx{M;3h{lk#1l|#`F?eBFT1s z9CV#z`UJHK86O}=X*F?X*2^3SiH{&V6yye!~$M}lyi z1-T;vvBY|>4^F^q5uK2?vjSI@brMt}-FYFt0QJ59Y+m(z#W*FXShMel6b@SyZI*zV zi3c!H*-WoIvhkHuz1qwS;I=A>WJ(*z%&(Qe63NW-Q9T}H=GP<|LMAiMH{^uOe5PgQ zdq$fw(=m&=y;aLrc~4%cG;eVibWmarfbJUqgc$VHd;B3JY6$AAylyl66$u- zojpx1UxZ(X_`7!lq}J;MAy~M#xe)od*4yN*jXfuE)5Nt+vy@^$>7{EmQArDfz9nCYzy&w<^KgHBVGTkvfq7kWMi1h!( z8MB!C`<;jGzprYuQpaE|sK0U9c*Otjqy0*wzfV6{cX{FZg6|5#`S0g1%zG=ZBKL{h zsX6!L^q|kBwo;nxHCcbj>Thf^{9q{4ex+TcIiOvld7pn0xRIBg*Waz#$kXs0so#Up zbrvL-^r~MstB{N_Xaw^W0K@r@R&sk6K6;$$kG26_+|JCjIL#x=&80vv@1XNU-HvT; zp805m1pdEgZ5XWrf^u>=7-^txWBc$tDg(O&tBZ81<0K!UVy4PcX)U#u^LEcWu{|>R zaBWUeqKvs5&n&muW-*THnGUDbS?v`4r>iXX+0_oKt&(xr%B!957Y7v=sh$QFgIx(F z9ly!?5?p_AzWa^?fAL!d`CtQ%Ot{v%Ek&6Qo-(T=;#(2$!^T?+MKHIpL&bU6j580n zrm$nj`}8B6!W#pyHiXgk-%umoc*IpB*(?0!^f>Fsg$er(Y!h~VC>|RV_U$4K(ZEi{ z8USNeFT|y`qe-q&H(|>Y3(*>rc_1kS)u?UkRAHjDU>Ff-tv8%24-apm*!YS*I7R!z zA<^8y;|hb*1E24&_dvQWSPwaV!3Z0bwE+P(gj^cL9}hJ0{8x}$rna)pqkN*I2F1Pb1*RG2U>Qxz1Ez3ZL z=qmvxXBY5*_uTSw)2p#B<)exTGa63sQcWA7)1du#U1po(d`@B1T zHvFKzJpZU$5j!THGT zl-vE`d*Qcz8&#ujzn`%~Vkb)w$z{B{K+iYULjp>q=i3&`&eijcog~3@saR7t9|JvK zSYW$L=+!K+n-`gGpK$thJLdjQ^?;Sv%VEdGx=C=!r4x~o4)^j$(+c1IxPQjrCfz(c zD(bMqV}%lKawY@P+Tm7J_=aCMa;<9Iq8w63%^lVhE0C~~3v41a9&UHebN8H9b3i@T zv{ZTEoRcp#LCra5(-v;Tt~2(#EzhWK0$PUKF2NmDXaI0th5&EoZocgH*;Ddq^?+08 z)UXw?+yvm3Y!$2CwXbo3rca;g^?Y~LfYm73u;y4!qCspxO@j_{u{}-fHsCGWEm=Efz>&Osd6kTontGvR5{@%SZ9n*G@^w(X|AvSaSL!^p=*3?sOl# z^vYZ0TZMVVRZ!GH`Tt(JxfYtVXyMyIJ z)`PlWs4)Oe_d;H#(c||tgs3}2{SgCu{Ln&L?83XSQjINok3CUgi&J4sJ;BZsV%mby z&By^%e&Auv4+j*@HHP`8b}*oBUYCand4d7ndb(t?8QGVIz!w}icyLUFd?F(W z=rX1Dijv6_Q7vEC!)LhsDETn zu2|6APy?~&iHwd&u#Qj3mO_=JY_%ZuSUPJ30L)hj5LQ-5HHW+|c}S1q7FhCYMVA*? zQjeq;vcnt73VCIA(nh@2`{^GWRyg}DqY&b%o-yjLBhs(M$j!R1Z?=;3q{*1E~n z!xc7g%tN1LZ2*>>04z_y0&V~vG5DrayIGBxlN_m>aO#XQc5)${OHk8IgS245@-eiH=;+^G7S;nLL+zih8ie;_D~O|({sMNh_MgHdZ<(p0v`Fq$h zamHW~#daK$|9s5$E9uR<13?&)0i}Am9&Ccc-&SAa7vRP&#Ujqi3L*KpVH% zt!RwjmDcpyzDXVr-WaQJfnc_eXQJLI`2TIz6wZX3|L^h+=JkjCKTAJM_ohm-FUxu< z>l9<1;h>?H-mAMn`;K<9W^>xNchX;A`^8G6=ahje9*OSk!@d5=Tlc+o|Jm}POLt|0 z%;1?4IyITWolevB7e3tYmu0==(P2*B_KO$WKQ>x|SfnC=H@|P1v|#ntB|1tTBHq|T zLtoli*t*!L1VS>iaxo=%{F{{*#YQHY%9>Pk#*Xv2`(4(erGpp9cLwHoky|6#(XnC) zEP3g9S30Zri;a*Vt643!Z{qG~oVJwi9g~kQ-e8v&TK3G?84@}*Ps7^-+{XMs$)7{p ziHQc*Ty54VOTZ>uaC_HPgPw-+XLo z{$ACfr9w5i(kymTY?uU_TwnuW>rC7Je%n=(uf16{Y|P>fxRVP#W=aC+4R)% zA3h#DOg#rlIKEp;nK?Flg$)tjtL&C4v)#2mbz@^{;=DA1`?L z$cl_06*wr-k`^_oz^x(EeV_NvZ+-k!`Ica=j7K!3Apj34@Js;0Sy-i0Y-#NDM61}4 ziaD`0V0v`h%ll65Z;@{m*4g!lHlYNMLIoJeYz>r}F;z`7#&S95zID0o?t${n!0nu) zmNK!^V~hl~P7TyC+^%(3{qLm*Qaq!OLQPTZ#4e5vNHm~jkctM}a4uK(^??t47yMm5 z)_7A|I3H%D9Ez=u^_QC0TTSz3b1!{+O1%G!MQWRuk%B39O6;^m!&o(Z!7bl#pWk-z zq8I00DBm#5ftS2jq%5h*F)q{e*4(XuYX3vI6LPMje=x2n+*dfG;L(C9`H$xh&`!|Y zt(&5K!Z200->@fpdv@QfOO0PsFHkc`uk|W^57)c_CBw6JD!KjQO@Ub;&*2L+q7p!O zNUHVtT+PN?#OfZsQu!tx5Ul0 z!;1kXf}=&@=9Y*M1|@Z6gye*_a)z{(jkgIE^jN%{evvm+FKMoI-f(>|(9jGLImVs1 z)`+OjK`kmXi6mKDr${5H(}HYIsL?B?D0#~uHagj$Vc-fp>_i5Kd2X zJT4!IjXNSWp)e8}XUsJql)#h*n&5H`%4JqiHFG112eqF)RZKa1QjdDXeMtci>u8lW zx_#+XRU;2`nc!g#`h*8N6fyfE9{;+w5`2L!#-}x@z3Ipi=&n0ETlI!K+ZPE01FIum zf4~Qp7$NE@wn|J^EBFZTge5RZR}DcaS=})W&5PxYV!}ahBQRW(>U+d@*$&BV>Pc}J z>e;%~!w^l;tm}JbX7TNK%xGJIJ1|s?+tWCKonS_%mUqWnC~ouV$}bY`}t!dz}K-XQ4zf1)W|T5xav;r!`& zt-0^y4$JY-_fr4J{$IAy*sA}xUaLD-d#mOX6*u=|{HyG=SgG_z;~{>0LOTuj_sy=W zf4DeTW=+Q%NNMheP&_?T!cSg2jrJnb{Vx`O^54b_WbkKjPXY-AvxJ&lLILeorj}>A zX%;<{;zbh^c*k9fZH%3j0NFyGnbpC5#y$CB{IkfVVy1H#N?LrkGC?*cq|O z5@;{+@e8?#ar;J=?fr1+ZR$X$>m~+apCq+FP1p~A#JxOr@h{)MvqfDCFv?+o#Wzs` ztzw2lp^WJnSNCt~x_zMzv|_;wXoO9WAe+fY3Y5~A_P&1MO5LkDnYol7t)zx!V&f&W zBgxTDIIDnrY~Yo5XfN9(6JV@OBk@p_^f1KEjg3oSY|)crgmN6yhA9_b_tv?aGZRsw zvK$y7V-w9$B_em{bnZ|059_u5%YVx6T6nLU@`XFVz#b!^Rl~r(v#;sS{TE!3H}@CS zXxlNcQT=~DWT^^W16+&S0*q&jH{j`V%DmTw1P00akr@EuV*dFeGlf;nYk1m-IUt+ zkf7$TXB7kxP;|^+_09|yd%f3>lrxME3L*e{CB=IF)0g}^GoSA8`Xi9vg^zzgTxRvG zRkNG&jpzk5x*N{u#EQz_5YV^3QYx9!~Opcd7tM^%e^}1)0`PJ zOFd0>%RVRTG2=mFjy|NjL;G*-OwHw~&I9D%V<*HGiZ3PlY?j2PJCDAo*;#4odYkUi zu>}%xk_57dybDdQOJegS$Rt@t0dm)xM_Xdm5@?c=M*;M)_YUq6b0!*c35lqy(2$`^ zPdK5vJ)?seDrjJ*#vF-mVM#bbLbnX6?8ZG?JaFlvi9af3-Z58m;CUgV-ymoOt_TF( zf)B6@JbwcXo-oLv4WDl}7qR}BJpsRkB-tatXY%jnHVoOd?d|JsQ-Y7ROPZzhAb>}j zhi(t}vx8q6?3&1;GzdJ3Vg`&6Yki(#zE;ia2h|{uehl(-O~Ko^#v5o1HBV*2%?*t% zKWt(R&Fm?$d5IPi)o2SXhRP_@p^DE|J{x^c^`SbcLJBxrRT4ZBB?19n?p^zzc<|Fl zdw%e^Av1>qySJ`B41^PZZ3GSh;5!W`D3DIAV?abT1c^?YgqnP7$lX4Pi)M$H|NHc! z%%Cp!tdIviDOM?gUPh4P5<*Q+^>9m8mgj9eJws>s(xV3%ofdtlJrHg&(xdfiXentSF_2?qg?lmVrz40eoX23gxt_adS zbi)#|G7@YCzlOOIP!jbW5$L_IaqlnmK7HDSrpy4X0wuKo6psN`n2Asp5%xe(&z7)~ z2i16>+;nU9wyC3j*F7^>p?rg1C!i>ZzfV9e)+zxd@fs0=YTVCociM_Cow5IG1)x|v zPU+Dn9SC;KkjktR^UcLv`ct@~|0vf1Accsy6LlnVr3#kT{cT%4bM+(x68m7FcJ)UP}L1=zi7Fybu}|s0qsaRKQ}fz2Po&04c&FwBZwE< zflkw(F8}Dujmv-SKuU+XY7wJAWwRFI4={cF@8-F4f7;Q$moi&W391vbM6BRxN3J@- z6U+C6-oE=Fb&(7M1dof5ZY`7G{}<8}mKJQv|405b;QzmyYs|6G&D0_4wCrVBd$J0R zKEnY+cYU4iDeV{9(V7tXEua5Q&^q!;*Dz=1pwE$naKHT2P#_^^r9+$rc z;7!6RPcR$-1LIJepnHfN8e1u$B;V5kWg+*>>$g<*=>MWxl(1C5Hqg;B7A^o@2`LHF zv2B!qPv5=ef3v?4PoesT7f)rIzSCp%5@UK6gsa0025 zP?GPQoognzLV`$s0_h4fL5~EK#7wYlm~QT5Gr?L3Ci(3S;(0901dC#B2|f9(3D7Sx zaebn<&t8_o=o)LcOJOBAB32`TCBKazodWJa&NUZQ4++Vy0a##DsTHtuVlD~p`6~9U zYd_}xba+=?*ptFx9t&=oc5AMLUOkjoE|=Ou{&Wfqf_JzlzS(&BoR<4kh91Ld%?zrL zWfq5Ev$iaOpU7sdZ815{Y}Q7_mL}T8TGfm(t?A|Ay%v;wnL-m6Ym6zd4%njD5)m<^ z0ZI7(LQeo6fqS~fbXoI{dF`)%M+lwuJf16oB@Zv8?72sF zeevT3eXmgfi}|7;m%4|W^NEs~Iu@A0O6Z*UJka@z>cex-+g zEW|Sr3jtJm_{T*^xG>gYSE>DhJ0uZ=;&bB3Av9r|v%3$_1*AOF+Q-vDq`xw?O||6BE{T%bNQ z7L*V4tSdFO0(}|;S7B~tFsO7dwwFjpS&RYgp{Ep}Va}#TuWcUo^afF6%s5HdjnjP8yrl*Yqb{*uy*H0D@8 zLMV;y*0qa|*I(;dI|U@GRl=p*yPnRZjS#qyqt+z>Ymltm4y_jI? zf_80~pVWS)2IMCi8Y2Dx*q=cXXblK!!aNVccbNd8v!BX(vWd%m`;_Go9Ds z6IyjiHb$$S{8^?|m}9vVgf)VmdXGPZGNr(Lf%A+H61+eMfit9-#C zP2t)%qlRbgepg}d#%%q35TOOBYGLE1vqalRJv&-?+$dwfT7%jnFbe)=dKP2gX5FoH z=)?@dY@k2~X#=i+AKw98EZ|4$G2O)xxK3~vr*&y`jQ+z65xGhOk-Bo23{W1F z${&q2D1{H!i04(El5RN)`k-%>R#B`^wlshXBHX!~8G9hmAn5i{zhc^|`*|RpBOXZo zBJlsu))ZO`uF3yzzB6wJ-2JEKTtXLQN3x#IG8$(Yt}{&5$913T=4$WO=4j47`psXX zVW-9Q(lZDN6#_NXSqUUbxK6@P!VePgcP2lmUtB9e zB(Vq)A%0lI?b+V9_?A<68YnMjwhTCE;>j-kdj>zX*gkQMgl+)AYqNxI>nn7{$UiHg z!ko}+?X-mfNMIL8P)Sm-BGk?%ft|041SWt? zMFKldi~=DEO!xrPkHNp**#F1f?U#~_lfc%CSP4mB!c+AzBY{EwZk^Ns^8DQ~lSeI% zHB0!(8H2iL4wtWV~AZ|E6Yo4|WXZ-SLl7%;s4<^$gw zZ?QCOR9cVlm^R0`%wwm^Z-!!P5*>3fdCdlN%>CSxA6$O#fgAL{iH=dJzd0t>ln6UZ zUUUNbfKPBwH=R8x{x@6F16JBkXm*bqSrUlH^^r`O2MUtlN?q!M2QyUS3$Hl=KRY-U zkx-8(pT(sgl$zdpseA_a6)l4r^UiBVHDSI$T{AP1ndNoW`JuSL>xR`mEa4AS=!o*BV!>6f0>FnAKuH|a;WNuUwke#_?1NrnOIN)OmdSDmUpf7 z&S~7+FFyZuk4Ng|pTzKD&fDhNoVIdXnY9#5=fPZkwS;jXaZmwcXPB#xjx|c?$cq_) zBL24NuB}&H@%;y1$wv@#c9q&HW?Cx+16js4n?V$#1j*9@#=`*#GWpg4kh{`&80G)_ zY6F_Wx`I~<%=vfc56b&P?k~CL<-C$pO&_ACQx8&uv*TIsWKA~y&hRC003Om0)m^Xs z0ebn z7pRMPLiA{M0RI4IU{_%wIYT`XtJ7Rztz^tFab_{IiWzGqvS98wt6-sGoC|F9C^km~ zXvP7!DFdUa(rIxxVTPCUQ`=fO+fwSZ&bK(|k+?cn=+4>cK38ES5`{{g~e!BK!`GJH5hM1&EpMmQsCG_I8@2EtY76p|q!Zj}w(Y+>*4s$AA0|9fbj&=l4bJWA-r(HX^ls{j>>*hf8y_=v zGuZUkL-v2Xc0K&qarTFu9PcGQxN*b&fK^OqfVPX~Y`<({x};C9~s!DBlDXBLYz# zzKyu#L64`&<%{sCOa3vq0bYaagvt_NcDww*nXmOWd25l@q(GlXDpBxt_$mGufWnA0 zmtBo8q8s7F3>O?=-J|*`AbYPx&&>QkUJPI&{*VV4>~*7g2W~Go7NaVwx`?+H`MzgP zvojpeOLR=j0^%tEB^Rc>JMX%0{myckk%P4XB`q@$Gq?((*27;iUV5=1FP@ud_a)in z?SArS6ZeOS&%CqMAk%KlarhfA&S1}p=Op^0g`AiPWgFbn^oCsO;K5Ao8W||xU{8tD ziPjMbtqRK@RCaKWtQ$M(jt&1+{dk>J;Q{cdjKJ$G9oTrb2oIQyi-_l{>Cc09yn1@G zr3tydC+`|?Kyrhn2*2vKD-vwCW{E$wOn9RTLi&^69>987tYl2k8u$-jPmgCwK_$`G z5&9eoN=!R`c;SEbxnC(9s^5^C~oHsgm zN3H>Cw*Eqoq?)r|&aTdSBWtMfO2hYtTK!-2rMic8gSDHqlx8*2bAa{%o+N-#38}U2 zxL1Cqz+*H@<}9|E4yV1EPw6sdt9^la5i`r`u$Uc?;hifMq8nI@!g$0gk%xsD{f>F> z_G}XRZ1FkYfK#RdfG$xDs661W8wqcr=+GC6_*OtsBZzVXRlq9N#2av)_yEd9s-aks z6i9=o79BB<3TaMysv$Y*1Htf7v6Q7p4%oh09t+-xc2t+owp9X`VwT0hb19s5 zYlX!*$5sXZPtQ&-f4Ds$4+nE3+n~la2&oPzUj?FbBb2`SL;QWA5onVQA=;AW_!*}j z5zls;X>poImYYj~cG7{XMbBb;h@=Gt3mdeFAx4&JNL8Sn;D*v!6dS(Y8cE;7bxJ6q z4F@9)bSc|M1hqu@DIGxRRD+U_P%%?wskD|_%ge2m4tgegrhu#^9|ze@&8m?(7CD@j z3Tr7d$7Zj9!CuZ(*)3IOyTHCHW-JR4cgSH`DE@#r698;AkJx;k7|J0yfVZV!e__ta zeQ{UJS7q;VM7saQbAs$Nt|ZFZ-%)CMby z-DaKTfHkh%Y^|iHhyptXwhjx(n9Kv2WwBS76Xfi6YaKl~y|d1id8*;f2bq7wTyQb# zpnw4OcEL^_j)IfeEO7=*#tGGy6l$NCgR9E#i8OT7D5fWhIbH)h4+}?*>X;}>5YrQc zVt#{?NuaQxZH}sjWQOHbOnWy$-;8ICV&6=}iOJ?x)c~w^yR{5<#B~$%yOG;7UQUfJEqlH4Mbx8WL{;l&#rbr zT#^DbCO!6UC)4Bw{r}0DLQ6qw{@eLO@|ts>%I%ipqi>=$lq37*tRJ$H#XA#(_`N~thyIcF_qa7@evYW5`Ew# zz+Isa96v*XOQH|l2KRW-2aXp>Z6T>ZOSI+K=mS^8hbQp2kf&Qf-}p5y(Ddmuy`Jx` zdaSFVZ?oq3utbB{0P(viH0U4~+tb8OE>r)+g29)sax5yHV=K2*!8y*sI^#nVjUZBO zPK>H5ZeMWY*$syltL_!)xv`2z$yAq@TI~F0P-cerN83o7#bK+OV=lA6rPgklU0%Jg z?HQ68US_k;Dq_!!4@tC(CFiO@=V-a;PWQn}ue?QdyA(*-?5Ox)2{VZla-uJ~N@;pn ze2|D3sAJ?r4)Ui+w`;8* zToca>)T5hqvqkZN5?nL!;sS8HK*RgAI3s~2=biwMojb6r@7d?B`7|@JZ8INd%i;qh zfaK=@V7hbfK5*SbXWsU!OsXI2xUGiq4%xc?Qd7vYb-;z^HeL31-ljW0Qb)Ev3MU?p zwWmoK$rrM&({vvfKUD%uPJsbFJ$H}&kLSMG(myi;SbJlybG4Xy9 zR+8FoVF3nCK6lymCqKUXKN+$PY$F^Um75Reh`thP5+^4S^^Y-b+wh;KOd9aV%#4f_ z?0t4jypM!gMWmnK$33|B&o@tf=NZg%YuSnM-V*91#5emwp$YW=vo-p^>3bC}E%;l(viuM8 zr{r;YXXQST+Z}WO|3iDIXQ}bo+p=k6OgBq=t2W!1Z8)T95pw_V>(hH0bR$x~^_*mR z>b!wYkv}+{nQf^sms{u>_~qY~!2etVga93@@N9+tjX|9wgGjs6c>+c%S2Ft!Udkk0 zk4AcVI+e>Q;9-rLw9zeNtAxk477P)8-F4RgslSZWf`bJ}YC<9j*h|ToCLy=VlTPbF zc0u|fw{H{^4tk+lFZsvC#FQIy`(BV^hPUp3w>CsCWy{1CVWlQ^9P0EIbwCJ{mk|se z(I~ zElom?UD7u-60|oX{kIJ9M1SMLZ}gIMW(p`_O_kXJFwETypx(_Q(6~IgbkA%^V2-1XvxYuGr_H7o|fkgm~?h0f#xZ5=36vXe~^KvX%l6Rr>4dy;8IV>SPR3kev&^)b9abULEc8v49W?&Vu|aAfL2aIw78`Bn4eD_w zd^PE+v^WtvQ_%1*V^nFe&$m@O7=b@X+tM1IH@`36cB0`~$ui>fXuuhtH~Zv2A8UM; z+N#Uu012TA$dV2R%oSm1SQb`+l9|npzEIpwaE-1I^8ZzuLObODKg=JIcOKmPd*}G+ z=jfi)s_e(Je#)9>ywt$yKhqD;U8H?qo2RKt#rkI_#b=67AwUZte+ohTR@1h>-*(mH zYj2j13g)Q54j@Eipv+1ejKYToR*cR2;RWf1OQ{!hP+N7d(3-MIB zS9UW092ngz-zL0Sl1x~I;w_VekGwjD$M?iTw&INyYvl1^&7yYjX2)kp@W^Ydwbrf8 zrq~02|IYf?Kgq+x3*Qm6(A0cOk#Wn~%C`h_Vjj_y zbUC?fX?$9uRcuI2s{*D+x4pdY#(l zyL>Z9J16xx6M4NLpfE*h3W;y8kad6=GnUIa_pQrycMnwGl%q0#+3E4g64W|1Q6IzY zT6fj|UV0$K0T(yw9igV+2D4|xCrJ$;&$Mf;Tjp_hIriM9`T8W)v6f{IvqR$(CA5=O zgnP>XZl8C@&xRk=Z?8w?7ef|mf`pV@dIjQ|!|jhO`EJ<~Ms-{j8OUH@G+shILPdNx zN4eb}z88Mmw^23f_9SI?NPL_Gk=)w`D7&rPlY_Dm=!D*+`hf98Q+ z{N1Q`M)tcxK9*Sf%28oXiI0(BlIvd~3N&}k4?enEpS$E?VyUs&OU|0&qa>`WRK)oDGq~M3&)suc%>ngT(^4*nO?jl$1T~xT zrbXP-mmiq$`UUDspR97)=ED6yiv0innE!tM=sY&}`P>1(|9=3o{)X(DEG~;NRvR8R z^wh7`ZP$LME!9L*-T&2o4}{tlO3@>imP4=Bre|E;zp3l?MJGR}17&Q11ev@N0zvK? zf08re^CiIKBNRQwoA$nb;Y!`BIhlC|KiZQ#J2zgPK-;1xUJ`WhSZ~@e<-+UUI(Kts z&=Nh`0}0ZZXbwwa4%2o&>O^yx{&9zdk(?9@DTltMJNI93Mc&+Bo`g=#b^i8$aW8y7bmGQFt9<-0k7Z=(19+dSlA5CCeQxIs+_vW* z+Sltj^+guuM1}$GJT?iQnmf-GKatb^(cYEF$Caa4bAD^3ODof7zM(Rr3J+GcRW$#~x$Tt;lKuKj!DO8r= zYSu_y({S zapOMDzGSX_ifXV*T263XDw9ByA4UOm6ZiDv>rJ;F9-$hvtlATt^=C_H$whqFsmBJs=#>O;FoEE$7DwV(BMOJ;Sp`uSOdNOz zyn3&%Hd!^Aj<7?7k;FEqCd(igpjn=op^~VPLG)7KatDG);u?x&fYJ$nNghMtAgCkK zVTA#oFqT9xLGU|6!{;|H^i7A-3;W^|VP{sra0L=+&{G=$RZl1Z^CDSIUKc*#hnf)} znKB^V2)7Ox@<{a)M!he=wH^)Xn{{-sB_uBIJ(VZr(4~rD$66bH>tlU2>lA|CqENMi zmKcz(S5wd3RzV81;?9+2b^5ya~+V%)Aq zP-u1qLATP^9CEqc^`O{S>w&@mKdj?zYIzWku!k>?^skK6A-4`u$B$mg_7@|oVE&9E zoBdOr%2okNZ=lH=gf&J)09z4Knr=c>4aH2jxzWRnWrjOkkyV~>xQOqU+O(UXQLoR|4o<^#Q-9xrnYy=RBYF=%UOVq@6)m1QpS^p>J14 zg3a^_TzgB@i6FqQ00V3Ov=iQ^J?RXitm}I!Ai#Xw=y;>>xI%EoT5&}2POT`Ecx(&b zQU3pA%?p}BbHSDQujQYb+mdq?{bxF#a#6bM#aWw;-y16pSL&bF=jcw-`rrqT=Rc6F z@`-OSpy@P(Ad!K_qO-DJCpm$f9$zKFBO#Ru@L;bqJzHLU{v9v(>V!Ry4aZj|P!f46 zi70z5;O-vqk9)Jf+pls3?3!JXoe}pY+Rc(skA!x^erc-xqO#j3+q$W2ce?u~J1t%> zwSa`r+|~k*>0kbWyR^W2_b=c2?+tItjm;9_V3uau-FNujjg=2a@();J2=j zT0lZ^7J2}7Wz+68CqKRMCtGGVrH;EZuuwb_P!cIF5ol)`=S+*&N*Kw9Gwkf#LnCs# zUHsX>%mfp2hX)R^Tg0!+W=Y7-LW~D}!@ad+Qqx_-^OaV6tT}0mgu#x_i|pz6JI=ZB znjBEhm~7~-%O)XE2p|VKO@F%lqc1lu|Fwh453o;NVidssibT9n_yE%v!N1zcIcvfxm@3TZvSLxF=tX-&Q!gTz(6{JkXmJSR5Tyci;k)j@rvC0iw;hiJnnGv_ZNo+=EMJZQHzSn9OX# z><~pNF_6z|1DtBPggW5YZ>tsM<#X3!KnjB-TrjO{7cfeXMzy7}~1`WtkgYHu;j)6#|O4ex5M$R3q-eO52yRdgfuj|@Hr zU@AqR!vIt(W!ydEN?gYX5kQKY5$4#!E|a3KU`O-MwT6~lOmWMTj5Y>8? z)oi!V0gZA{j5xA*q}*JA{A%orMz^D7gEk40D4{RI6+9Axd979+Fy1*yL7kkwm>nlZ z5F?EVy6IK&fG~SkB197Fz|)uFdSr=VOo1A!-!p;v5?n1O0rRFssv~{m=mfD7>wGZv zyd2jNN1W50TS*ahy%4jK_#_+1FpT20Jziam7-$i&A^u6EI3D&S^d{W381isQKAR)C^*&4GF>&-7HE19#1?!pdR+%sfW|s1~ID@b^&&D z+gEBmO&(vM5fZ0im#-al3u^Z(<+~4a7qCZacptTdfm-~05dlUr9}twtBfwhG=UeT` zV<(2|4J*v4RQob!0F*Z74QzoJ5GziXq(X!h5G6b<>5xOg`lJ186zS|b((;35S%Wyu zF=VmLk4(r6kWw)41)aeai0K#!dgzM;{WQIjryUwE<|Y5m3Qw4k zjL`v(3z_+RVXgD}S9lO@4oC~|$cU1-^abLUq-PaegHbq4&+p3&oQ|)}35F_Co~O@e zdyC^l!8BOFRHlFlYZ4_8rO=h9&l6|1Udd6N2X@oeOaQZcLXBQgDwb#K!c&~ki+*~& zcqh@b3M%D1o}Skz;K5!0cqTfaXlEY4kEV6FMu`Xz(h*U)7aUDLg#I1M|DU9((-bZ* zcq;$Hyf1Q{^kdZN*^9F7%`zGn8*bHqrJt(1Li=ZJANV2iKTtPwf%r-SnRyah6%++$ zW!KrNjESEw0Va7u69B_9(6vSs=SiqZ)EC=OcexP-IEby6ppz5_iqLP`%Dw*c3)M5a zzo{^VvDUg2OA3@Tu9FaxBr8P3J+I-seJ?tz_Ul3g#Htq^kg#c%Ad{4iijWJQ^ou$kQCbTw{(G03Wb*KxhjT~B$%^eW>?keSzhBNB2`6wZatg{9m@m0qkn-tasQdf7YANU(PvUt_0-2K)1{i ziHja}!b>#qIzjDIGw) ztRPILkf0be+(K9u2-yv3SgMASShZ?5Vbv=B>oc$`h&&i7LUmZ%EiJ(1GW;;BmTO)R z>E`T+VY}Z!d^Lep;V|qP;#N&|ru;YtWu_a_<00yb zPM13X$^?93SCfs0j51+{o3f(@?DnUJL)bGiB4CCaNvllq25M4| zNJiqzu#>bJCl5)l8j=k%`rRHcR5IpE6*JpHjIS4B^r)D3C=ebne4m}p`!GQS7<@Qi zH7LSzT80}4E6;J(x;aDjH=@BMt=nX{K0B-I+L7KWL|>k!>JL|>St8CKG+f7?%=Z_8 z&K0T~l^z+{z#j!Ia~&VPUX^S7oGJM6JfQJO2}VaniRZebaWQTH6ZGLIT5_of#<;Q7X>} zM_F29Rz@sRDneAAS&vwCV_ZfoLWw|?E52Z$V_8NlVh~}vR1ErqQbz2QNQsDE&4M}} z1(#TBq*y>ZT&lv>H=GfBOZ+U zJ!Z0T-eKv4{9KcLJ?*qZW*HoYw$hzur)h<=S1>&#K6&3&H{wAnAocyA6Rwl z23ni2$LRDJ_QZ%zgsOZ40EsiP=PSObSnx!XHc*oiXrT7bMIuh+dnw4RG4A`u-eAk= zGqk}e6M*#WjaL!#{zD}n<6AAyzN#;VD-g5jAIW5v1|JiyFv`C*J7LmmG z##xIrMyy4O(T%$n>C?k@?m$jM@&?IHg*zXyVsy#pSf2U2c9%-yh; zSQi=kTwYj}aJfsAR(*<(gy`8dG6$^)!vlZNX4c{|^MX*CAq-hwsL%Z&lqd+o$Dmek zc#?~&>RCB~afIm}xr{SHyR<jmH#xpwU{ml5qt>Lh|HeoWP5o zt(A8e{f8RmPOb>}eeQmoOZsRQ&Y}NfwKfjhf@UhiBg9I-;Fz;NC*ZHiPl3rQe};!o zEdiCkf_xbbiX#ESEN7C2pWz|41W)57tXaPI5>2PALZ9J5_IN(R(ohtfK zw)%C32l#oQfMry{BiherjqZN-MBaE|d35=YukA`>Ji@6_piYTIMi6^vLN3Vf#+;nN zB-sy+-u2^bf_NEFl0AAV1bJUpG`Q>uW?qeM zAw2uD=S5BtPPc}YNHuanY((=nZg}`SXZ2)P`WcdFH5_}AJ?oEDi;Y%(a*F8p#%uLA z?5q9Bq_JTR;r<*lXp_uHJpzrD=9UWb=ry z&)~G6@K%SVBPWX>YZV6=FvurnefO@sZ{C{*B+(E`iSfk9Ng~E()dV&7E{i>R|D0(TeDc5P=Nd8>PS2=kPl!~C zXqD8sk8_&#&M@wM;lgi~t@^iWv{UeqY`zw$p2?+Bgc#2+SPy?uR`LH<3zw}VzA>`NIY_^h*|kA6`XcF z3yDXL5kV_go&wP0SxB5+6qzByRxUk-G8BwAe{jc@i?6#i{b(cHA`oUOu*XKGi`bQC z$mdyZcE+B5$XYYfxivj@qM1UP%mAi6T7;*@w7Yf~pZM&h^H146O(nchRQ;`tYjX{R zV<24_MYMXLz#}?;39@aGX(D#zGB_v^!g%!qAADi?^^@tbldWu{z!N1z02^qk(2`*_ zY@l61<9#0;UH-kNj!WMXLI)q!lx$NbsDgam4Bf`ONuk0G74KHmlz&U#ng0W*^>?Z6 z!{YxGUsUvQk-PAH-Nu3^3mVH_FPmNZE$R!(W%!w)rR2wHIRNoDh!Y(k=m1pQu}pOM zBeA|e#7_ky?sN-KVYZRFujA%c5A zHC)G#kIQKWv;YC1czjHt4~nqjV%{X6e_L{yt{!P&uzn)Y`+Iqb~ds%~YA z)$X*kSREjRZnd;RK?+-k$}H$E&LtZg-XoTh z26$sBCH?wuNkzx8owODmf0wwZ0r&MYRdPZnqfz2^!uZE-1j^7>|Mie;}=T zgCS{O_2zBj3K<-L@2T#alq$r(hYiD1hyy4W$%G0SNAOFfoQ4nvy!&GHfPKKa!u+BT zQ;Uk0`}$}ozP<@!ivu{3|9`SuFn4cJ4aw2(4}?-;`Gh!&pEFN}{8z#as9YBR$60pR zIArmEM4ZLX8EIUy_&+4hSLZ;DUl#udY#qO=DJNO)f0!X(ozSNo>tU(yw6xGGt@ci9 z7gVs2F03`Q`HYs1d9=fP27iIsftl=+_Nk`Y=NW&Jp8CP$~W)zadCB44ybD}`QXajv9oj`b}otsQ0?Y`+|a-w+qk!e*Ty zMYMjM-ISQA!U(BhGpAV?e#I{sdY0gE&-C#L+C-tGNfd%cwc(eX{{MDe#kb3+lnxpG zU|3obEWW$wlcKqW7ZuV4L;5%Mi}J6@`+s>&y6y4Ddd>c28zZOkM=FqUvPQ`vIJla# z3kb%>A79`1lq2}DHpa?KG=^BxQW3VI<3bMhJ-dt#wpX8a(f;GKfsMJy?5U9@BDy~1 zMm<<%vnwNuMf{3#X0dgF#~gcW@{!MOtl6%Obrbl^kR`oHM5!ork5K~EId-*6e{8J! zAKIW4DbL`h3q^>^-4xi+u}62$sJZP}H~{f+ygF4Y4`xNPvm*<5bV{qEV*`$Lb@WKH zy6Wf{WQOYKktW^>N~@#eFEH-=+t8|2UtOKpLE3V!*=3PN9;?FY=v8pQ zQXO5=fP+yTy(LmF;#V$!gE-T%y=VMj_(xa1o`x<&7Dy5JLRk^K+15y%*o?4}1qut` z;AT9v!r%)2AYFB8!X2B!cFc;*7u%tNVg~GRQhk>in*InQCvXWlU_QY&`M40RndvicFrXN#|9(S5aPeO<7UteCh$iV(w+lp7wJ$%2^<1ft&?$7RXs3 zXMvmr(plgFKL1s9BI} zFjk~h{d55HIBVdiFH>el4Fa>G{$-g3j%XG!d9ipH-Q(ZNcsMaRCMYd(RILw zQvS(*8!P!O%iJuaF{G043UEV+v^+dj{;QCztm?^11L0M}uPc`4Jv~ka62Kp^7Vxwg-3W6HSFhdIP`3_ zd><|A*`KB`HR%9{xdk*eUBPgGK@Yozmq|C|sBdx`a*a0RhL^H4`v#4sh{ODV!SLd6 zkXuzRCD>h_e73CFi6+Fu-RS$+C7jWR3DkR`6um~iu0;uY!wX!YLwT)pm>rLKqeu0_ zh@`%};rVR7`@{@ozX{g{*MNT;Uy{=BTz2Z?ed!xcG(-j=z16?P69Dy4PDG1=EhkFJ zf)rh*7oJ=gel|&ChZyx> z!q!JRMaYUsFBmdBvczuwu>87Z^-e7zr*xVi6bk7OK`Sa0!ht^L=dsuc=X?}d_O({C zdB4XC`}l2;n#t*puqQ=UiBJ_4-V>ny{{MDvexrT?U^6{JL6OLnB4|Y#Yn+99_Q!Tlf6(mu<{6SlB)T^Y@xwk;l7c2&Njs9B zvVf}FE&#k#5hD}>gy%2g-o6bx*F8wf`6?mO!+dWF9n+>w$83l_XY1bVIqKY8$7m6; z+A$?!7CS~!;W>W9Y>0iZ_rU%4*>=ix4AJa^y@idSlQ5R5%`e&f8mcKjp#a}i;?BoBlDZ-plCPJGaKp!36X zU%u-1s^LkV8QIE+Nrb07yAy&u#+N&4w%_&IG>v>i8th@0M@|z_ZdTmKP_N9``*GLg zKixbzy%C9~lx&}UAq&_jwm^9naPe1+@e`|yo|Se`RYIWOFz|Slble=3&sDZ z=DBr~!WHjVY%G7JTwk`M^jD=e>ZjB)!!3rglBLD{Meh|}RWM!OmVa}89$fl@ZVC6$ zpZ5|3vt>&HO>MrqDiOH0X{l5}HacMAUJ{7X6PE;ny+{UlM1w5bl0Z~`Hpa_;{9IF- zgi*qUO31WCk50fwr~%#cfkCbsP$1lc)c5$aJuDweL4-eA!1AL80V$nFF+ZV1~o!E>#~ytdH4iR)!@c9$)3Lo{nlaI34!a5H?(lZOh} zdifwgzUKjrR4&@W!62$3wF$Y5`~3rSEzO^_(k)&n3*rIh8D0VnUuL5W zm*4WvN9tg8xO)6NtE-k?0ns(ci-3Z2eM49fx6|Ve_3z6S}4jH04BX#f%Vqp+2c6!bCtA6vj9XeRI>D2bD}GgcbnODu(B z_!~QyADVg+6mKrl28E(RH-;~=(m>tgOh1hf?lHQ|_O;eE4w{o0H2jrq=Z!_AXMBr# z)n>WPThe9>SBl2M`l#u!WoEXay;0GlE3jlE3s~U%?)6!*a?^q9_ z)s|LsvR(d{XF3+JM)~0hcrI0qr^D~-ODP!psZjr~t>UWk-<8iTJE!#i(qhVCxS-_W z;vA##q0RC(+gkW#T1!W+Nn-bkxPy3)lA_y>W!0AwM=Uk@|B^IZ(R zLUe$ck!}(7Ton$m^QzdxfBb#uP49?m)F!Aoz!Tb`Kmt%L&K4mqR0HvQv1eu%bzSEV zs)m?KZk$~b*(d^asR23|yY@@3Ve<65RRdkoVYRl>jx{SBPK$Gmlb1BMneA5q*mq^y`gxE~vczmQ~+Yjoy;Bo(Rmd8$`Hj=Ghz8$M#+R;(}jY zs6IE((gg-~T9C3d+Ok->+RQCZ%UYPmIwNNaXorhc827r@WBx6Fereh(N2oTgnwli6 zBeGs>&=fTdIuLti&xUQi2Od}5AoW#CSX1N-p;@e7O|$mLu6}e2ySQDwEPu9w+N?9O zPH4oia`pnsZ;U-37&(35zy{TQqd}e{TNha?v`YmI6WWy*yK0Yn|7G93O?A6WnMJvQ z(zu4l4AfEOltxHvjlKHufmO$D(C8F#boM69s7?{8@&y2t#f^_$aLvP$SGl!${Fg!% z^4!?=h(koXUGYdYeehB2tp~1usP2ycq+#6=?OI#>0}ucSF?5gD@9%{^3j0D_1Y~Q! z2VO?IZ=h>$+QdM7CFF5=YiL(E4YenTM{cNFLoFGxNu)#Wr0J#S6`g(%)1^gJ(T`H{rVxkW8j=(o#KwkANTt8 ziz3@Z;EHN~;&Ffi0 zZ?u_Z62lMdsz^w{IjmQK^UX!Z9ZN2{{*AMCYJ*czfC4H#1cm0XYK~s^IE~-G=#gXo z-JPNOF5zHIjA5{Sk$?bkc&TEG=UHw!VBG)C2{%3b#Z>h>ZW)S-vM2}4E$0bMWtGn@ zDE?u*d)JlU`un?|s%~oLI0&4L4vLV~oQ>|jKXyjt7pIodzfcW1Wu%0i7x9ZomG5v- z3?+8yw3p5+_kO4vX~tLzq)qum#6}gKzx&dsryhtrHPe2oYQ*WHEs!=fAc9rC9|!YZ z9BX>N_|jGOC91(%Wa=Bjxt>yr4_mC?pD>X(_0xz9hEtU>c*O0YE`ULF5roxC{zg?P7@iSRjiu**sjJHb=Dh~=if6+eXCMd(MJ6Lqw|i?P1;iNmkMk7Ps*)j zzbHGJ+HIIya!K(A#jc{?6qySjgi3%{7aXbgcdz*_~>9Aa#PbTG#4-TeMH>Ju+#rRN9<}?sB(mj>j zmKYyJ5eniV=o*XhQHE^3TU#+cs)SS{8#Nq~%C(tB6O+k{S(=~9{qj?J;geIe!NBS5 z`TV}xAWYt2cv43iy6_|p8%mI|1&@Z5p&(?ohJ#cQtH%iQ*@f~5XMZlIEkU##Tf?D2 zzyu?ZlM<&2*%Az50_4u4`2P`k-_cDfub5sQDE*4+G5AVuFMhxHq@v!!M+=WA=+y7g zm*jiklH8xMwE%l&l;Tf~^u@zUWOLBSc6Lov*_^0BXq_V29Ika^CYuv2(FERD$>v0h zMR-cYL^$h>t(XXVTC_++sf3oxp@fye`0SZGSN!0HC&zt-U}r}Q`8MPivr5t!xHioG zJod)MMO*H!Dj$j*3?sF;@rNK{lP$KY~$aG-X)~HTwKv;1HAcdINQ!5Ov;1ApyH6Sac z76_hArpJ%XV=^@sjWkGU`Q?|RtCzHm$(W;t^;{Vfi|Itkvs`#>kDSxYcb=m!8)uk6xuTTw!n+#OR zw&Fh*FDZJosJigG1)mn2ng4G7sd+cS2hPd+R))u6K5SON>U4R_aHzje{!>DDZ9cSF&H#_df3w}SQH`Y9dz=9KI-+FMX{ z!W1A=aSz+Q<=v;#cn8tYvNLY~76u9tW|Y#R=4Uh9A?2q;4JIpu5#iJq0xTGm17a{B z?h#C@UPaAgkHx3L1uQ{~7V>(2sRmsq9E-bM-uaF&5|M5H06sVL3WV~2{OPw*geML%By6gRC$SXB)n?)mHK|7=L$Rjjuv7R6L8js? z4@s{Yl01Q$I+;|CkO!5o8WbTtnmUPX;LJ6Ag!~i*!c*$iWmFS|sS|lxU;%pp5$1l? z)*OX7Gou*}5fG!9noU|jo$K~7E1*tQX0s_q@?z>OmQ0+^AS*`A;yI@UtW0fp&Q3S3 zRt+z$Xc$G4>hJMbW~#=LQgMr#nax%Z>1wIQA*-iFoj|BM$(d&7RjMH+ie*v9^SrFDKq@IL>NphtpQ8JjuEJCH^RnVn3w5*MZ-&!Kt|@ySJ(6h2m1T5y*B zzWm?jADb6|%O?6Cc4qW=;RHufvM%ayq)Rx4I%(14M2N}*jL0d*SSKxdtO!(zPMToL zgQ1h=j2XjMc7lKM~iTk2rF@x9WP;}=rj?n5@97C?F0~3Iw3k$Y>Ea8|7P6xjlIE^ z(`RVoCqh~ZTN^z}M5rV&o<}%t(oYT1DI#Dc>A4(WNNE{Q8KwErBSq9o!s9&Z@hspQ zJwk-6B!*;m*+|2xdMu|K-rbJ7{j!~j^8t<4~tyT@Yg(|oe^#9(|6&y3kTk+?LzVbhouPnQ( zY-;J%rRCI!;fE#vDA`cFqVU!HTk^ij+pK%9u(jY3ojLIspb+k{3xfQ5)+2L>c~<|- zBIq+KKZn0nowAyKE!Qy9AiIyi1LyYsyTVqXcG8^i^%y<5V-uHa916h=^q- z&{*0wkce*-KQ5~o$4B=J(WW3!FNfTqBpjqpC(boTVC3f);*ss~_ChTnRP6>=3ZpE< zsj_&a%s@*laEw|YYTyd)MdKVY~HdR;?Atl=QghDUuV zWcYgqTzx=wsa}dH$w* z7zBlBw9A54a6;iNsZe~+2aOVS*kvM}IoB|5aYvi%g!t>PfY%q*b7Wb(< z??lh8k?(+_2mT8&* zz5KA+;sHIZZok_d4nQRen03J>c-rMkR`jCTw~y|HWjkT>%3syZT@_%^g1^ec5HDPf+hu)rLUHQzeGtwME}7{9|E5K~Vod{?~bK zxN!XbK+<15e_{k_qe_zgxD%s;o%FY3TC`4Rhl(S~Tmsza#42?OaBM?#zR)wQ^56|n z#h;Gt+j9D%Yu>i1A!U=%*dZHvo`_mWHZnghfZ!f`tKu778~03CgF0KHJG(SmE8z8d^lQsE&MTqDA7R~#xqB>C8F2W(&d+v`#TUzpL;mBnW1=v)zS zt#ZI)S=(Y&^b`T3qM8xBPXp0@BGinSAFUReqo_&&*BnH}AFnC}b4L4tUpd_#JyF0ue1zf|5G4&v zAeHg<=xm`?>;-CC)gSxKu}A;@ky*RbpQVsKk|t}X6QvihbE1`EQUpPw9^DWX-*{~3?M;?WaCq{<&A#<#FIH7Tf@ zp?gJF@ym+k<@c1&DEn@izBEX^OW6!Jmi)P-zWCyzFN&DLUlleN>@Aq8zdZkw{Jy+D z06zg;eCDJK|IL!nOdB!Qt z#7&xJf+1<2X3|5PX2Jm&qcjs2fBsg$Hj;Qprfipl<1RS=3PPx7)G1fWT$gir4Q%`92W*3m0|3eyzQ!_AdoYTWcv$^=uCchWL zV=lL+m)gh^5(?O5BqsX)eATGddO|3;PPgLVmCNVy`uoBVpE`@3$=hc$5t38XBe6gQ zCQBD>f+|d{bZZT5>1yeiM<>^vgM?z*xz0*$AWqiDo_>S+GC(N14Rm$`0jP%Sr3XCj zfM1AdQfFofXQyg=p;GLv7L(n+(roIWJ1s4CtJU6a>asXG9n^Z_6iv=L8jQ6rr`h4O zbO9EG%+l3nZgE=HnjO>`?0n9ZhD%9Y>4r1Xn}%p&OfZZC`B5^m4pV2R$!WE(pPvAg zHnp3(oDOOoaeW+Yya`etXARrPo0%Yr4t3{k>CH?QQ*O1dX?M_VRy!2%vNuIMQD%D{dcS7B|71jR}-n^4DcB5mC3w;aQyxeQhjjN7=F=^^JAyC@cRkp=af^H^6Pisqjx%HVk6pI28Wr z3tf zM&ATkC$0RUjK26%MOi1%0nVob93->*aX$Tj+*Uvy*5pwp(CXL11V`4 zj}j_7tXFJ6<4 zrXxQC<7FoMU;w2Uf9IIzK&h((jObE z{)aXwMHV(x)>*(qR9aaFLmd0cI?X&xrImFsOyj=44Xs-B)zyh+)HXK~$Ouh5R)v*y zxLE$dtgO>0HbBvC7VEVg#L7AiB7Q|PGsb_=D(g@sL%L&hXBL#rofIn77c8dQ410?I z$B?hTwAfsv_w)v<9WQj<^_6)vw0eKTaX}go_8*X5Pw2lUVtt zw)9pedx+!Wk2s{}w3|&%6!f93ZS+cWrxoJ7R!b{qRTHv*o5kT+V+T8<2%OVuv$VjA zYaC{(Kg$u6!C%Q>TjfN)L)o#Gc4rXzCPPG^@f{!*9_L1N226x=Hvy>gh=s>7s9ihK zdmK^RY?M&;R>rlthF>xyM3o@rXHVu=2)P3G9I^~E4p|qDf|i+N+v9;+$gq<6h&8-9 zoEZ*Y2)nfKJaX$F()TKv?GjiG4r=@F*x5XQ zAPIn787dK|aVx1qanvWY5sCMlmss?i>$!0jJwKOS$}a}9No;u2S!z3xE9T&WeMyK@ zqzDBVR|rczKyr3JGvEg%5){bes+l0>7QM+641xv&f4P4U7d;6F8OoC#E%3(A(}O1* z6%NUsj?+c(hg`v+Cp-Y96_J2OoC8PoWk*%oy+u8?e%K|a+s@EE@E#6iKN z0d>=};??d^&rs|#+t*sxIB0IKM_riZzV~9)9b@r9B_3SNKE$N#pRL&uUBM<=5v5Cw7zd{Gfe?KVuxNJq~b<_vc9D~2)$&#Ytbwzg<{0hB&k#>j6s4xX`iIo}Z&)AOclnD8es!VxJx`}uo28ruf|?b3J}`3nz<~|vn?<+_(4t-Ts8ej0qP#|ayg+xIyWX0k2-`_DNCM?w<^yVwYgUot&5~@6`2#tkSN)@s9l7tD0y0d9J^|dd;ewM zy)8XtLI;z>JTJOh#H>iuUchX8t!mo3`cJM-kD17MhLxj9exuGjwj-Bz=g*X1F`*tlMrzO=wpmCLm+oKpas6WT8@uGT|2wmmc~{E@B#X*ILcB$UyG*&g$E8BU zN)f7}3~fBsfiiAvY%4U(kG6|Il}{I-&24<_+p%Zwpf}670O3L%XQ9A^k>t%#mZ43A ztf(vlw(UeH%U~87qD%t~HzZeys|5RU_Kb-djTzcsJVeQcY-unuQmFd4=@0(!yFD+58&?+{lc#VKo z`Hhd?{Mz$JonThEfgcr^;$mi!BW+NiM&i?w;07#po5f}*(te3|PGgKZ>y69v@0pdp z8KhHk%D@%ug3cA8Dq{P@p&lgdmkm*m(179DDqQBKvtoUpz4%A(g&(B1HQA%JhOr!W zO|)NZ-qC8B*B1NvUyr@!gyk8MzsZ`X32%X2673Tj#wsV!g6hfVv5)S!Wc|-pU6j7x z2*(I<7z>VdH;L%g9P5sY;?S%pBch$J!ijF|ialI@)$-~E-%@SsWU(9eJ=+r$TYDFa} zdDLTBN7yAoR8&Ve4snd@2+xk5BLY=UpvMJ|FZp8L?`n_9FrdiJ4eWMHJJ9pG+G&Ti zV-4o%!5rHy!c;TI?mQUfj^{?t7SSszUK{s@^~O6FR9=6}s&A_vX6ZGC;2dnD2v>O` z4pJYAz4qRzliqy)1kK<|wyVi1L~K>`ED^WzbD2EL_vgnpcY3R?uid5^cN(1{))?I& zV%@A_A^ZL*v4^Rj{OI_e7u93UO34Vcmd_NM(5|8hH{2MTd+#YPxXmxAZbBxlBLKW! zBfvXjw|?`^wk72T^?;>O;bb)=c$%L9Vf{&$PAaNcU;c~o_OeJ>eyN{&ooY7hHk6iJ zUi?MznxaRFrWamWSX8iG|5yFi{44XW91|u0%a@7RAyX#egn_M>&$ca)OTHxApgWAn4$$ifz-b&^<@YhQejog0s!_=v52_~w=YAo$maYN<87c?T zJA^3u($C|P3j=@nOaQ794WP=J)sS)Ea`)5q%`^~J!ca-9@Ew*T zL+W?T&CH`CGyu_BIDy*#Hrt0OX8Pgn^6j7X!WQk*rIU*oxq7)WEvW1Ga!ueGN~#+1 zy$zMNLQLWBFW}nAo+RJSV@`Zf^$g$|@Ne_DJzmIaDSV69(bKas`7FrEiuP!fY1ztb zVZ8o9M9~CW_JU;NP~n@T%0pyG$cB3=Aiv|8YI;^CSBVS=(XdN};|_OYS z!ouHXLpVmscWEjZko8F?=M|99>V>mGN4O^#LNg?7>Ou2s3(TPb;QG=QsF~zO;Z*7> zz8F*iNo~0d@Q8XUYjoes#&{_6FOBgC&tdG$d0|+qP&F!7(2JI?0jM#@QJ(_|BDuH{ z8+Bzi87KLB&)lgIJQNQPGrsddvey%U;#piZ7Y@@E*^rC#uRpz5^_1A=4f_Lru$<5D zg|$6MeV3ie^P368tf1n{QgvoA4?un)RCXGo?SB6NovwHX^_^^(#sI5a9V`==0*d_w zJ+AbSzRjM%TZ*9GGe9a)2Pxb7GSs)S$rOSzEFY=E*Wv2%2RN2TExm#(oCrSEH-x$U zok$B6@;J~7)aBWA<~#3ch^!qaaeC<%FUmED7ha^knGJ0m)vQqmNw!)Iby+sqkN~Jx z9UwgVPNrA*p@=HDyn`9=)D8wbTVR_;JGc7j6;MmHiZ2m_QigdmR>QS61|)Ao0gs!y zG#g5H{^B)NiHo;Vo{@PQsvn995%xlE0L-MQ*4-s+0Y5sMGjA?YvAz7J-!*H4#pLr3 zxV%s*bQ8@CG1LxrE{9@RPlDpjMcSa~fSC)jlmSF=Gvw0n^>5M7wcz!BH}n_uXL9w% z;jD7d_RdJZ_>k>)l5)`4^Z5>&C2y)c%iihfzn*A6V|7|PtnDZz)E|6p9S?z~+a^Gni0ROEqu(QgO{ht=>=ZaHAw|IP_FJ^aPg^au%!I5yfQ?EL5j zLQ_>`T8v+rx|5^li;&d>zIWdrJEQW8Q_JXIsP3JVd57%0=#Yq1O@77hdt;YQd+EG# z?}w_9X3R`vtE1aQ#6}f!@9s;Vo_Zki)J*%Ssu8CX!(r>ATSc&nYX9*8&)pZtn%*zI zbd`OHYOr!iy1<0lB7#=KgxS3%w(qG6jJLltS2buyx|DCoY6^>Jl@q2x_oq4b%Jk2F z{*RsNgIO|)M1VP@5#}c2mH*oI^WZ(6^cE&tU*t4kz?Fj{W>u~{j%6$xq5%=K8eR{S zvApZ?yzfl@VTW2n%d5z+jnVT&)XEc`#=H2YEjlQ`9xhigGyibN`0P1fzJKBz^>i7m#OpL{eOrR=A-0O2kqC#j$X*Pt;g+7KfR;P5yo)rOVjOHxl03bJsJv@dk{6ltMD z9>Dih+azU;!6!+@gm@U^S!3`C-b8wKaprSIgblLsbspc zk)CxEz|f6obF3H7vE%b_(FrkxOb0!D;D)OqL)}JN-HhwLOm)Htrz{Qh5~xQ>tDDKA zCfm?z0s1l0FpoTFB1hB$^CP6{kv!%MrMn2-Lq-vSdWbZ7Cy$7%>*WAFOe(e{4~VE+ zi+FG)}&`;P%l^J7JvQ6CusBaQZvMJtbNpv`KqLPR;CUg#!`ExHdq8{h@?z! z>PN)1Jm{IzPCTG4>TQFy7!g^4fZ`7$WjR_{?WueDSx(PdDhV$M8DfA(^W)cM~o!<824uCZqj;y$Xmv#9^hWmdVmkv11Z`Zsxi90+>pt`G$Mfs6xeY;dQbC&W;9@}wI*yIS6Rmd9S#1{F8Z zrF%@lpM%sL*{n(gTX8~5wT#+~@8RPAcj_kDE51>_zx=4OGfJc3}9$#;Kc5=IoP$l`JJkZBJjlKB0hbkujP$PzpldFFKB=8_x zIdZg!QAtW3kMZHRjJJIG>OcESKh?&V+$sZi_&(ubdGKyl+#LgDlPtSUq3s08%D~-1}Kk7qNE4o{XeVu(?13;Ow$2` zJ1!wNBSZ27J8Ps&M6G1Qz?&V?B8(M-IJn?Y;j}2b>_qf?Ymh5J4-KTSUnhvFB^s zpZnw4cc=rMZ4w5%Xrx4Jf%51sO3#Shaz|rPbIoPyT0owr0WMQ4Vprob@4Od#t#QM@ z|NgrhHN>7ek%OH-QY3<2rC>Y9&mxfGVSLGT#OHmJKUN1?Hr0c*j}!`!RmifTBoO0! z?>LVrDEx&w$XX$w5!u%!!=%uDzgMKWvqbAVr%)d+v3czF3uv?y{;O_3dZYo z_4<827fyGFL}ZjZ?b!l)6p6*`I4QNk;1H^=%nYK6ltnZtbBTwmzDTrV$Omj*to4*w z=$YoL3Ce?>$%uwe}JnvNE;9oIQ-H#wYkYpvDnu-aCF94?gNwVT^I z)|^4NS{!DR!%SDTTkWmYMaO074H;0Ak(smreLN{Aj5qa|bHa*_A?1Y0BO)`S3+B|M|G2wt8sfG;| zO=pkhEwE93$}pYsKiU9j<2exlP}@SX2O&T0>hX9zp&|6+0LUs89gX7ulXZ{lCM~bH zu>8sLF|2F)jq^W3UVSd44{RR2|FMm#604|ttf7qsxYW|!B2mwk?r1{X+MMBGY(6dZ0~s>E*ou>87Z^-gVYXJV|db4KQfpq1#J@}S3~ z9?i(fB2?u>k+H8wvt;BX5vW_ii7jta-AL^58}GU7#tZJ##u13P@~nB}L=nA`6ete; z1Sm~2GFxbfqS7>6Lvp2QG%iiEW9mqyHXXAe_MEMIv*)OD1(IvG-|Y?u0ziLd=-zMu zF$a5HLo{J<)CLbZe$XOBIe~)#rl0Wz;eiJzw7?63rwaHH6>4^Sd<+m;nJq4F7^lAA z2PDvbg(nT9gNbrbTt0Y8>IKc&-Z0P=JwCTL%+VM@y9g8yLBEIwg(rBZqFsXk9)zio zE>*t=bV>t5ASo*5)zko(urJ7f#An}pR9?mHfxQPRJ=Yi3x?`q{%o00BNjcT{h}jVP zVDEwZ@3ZZc>lh-w!(rYn!}CXIp<8w+N!RDPWw!Oz*t2iHz4XsBEShvnqS9&54MW0* z23!@>2P(@6y08NtNK*)b93veJ2evTa;lV+M0g+@h5WFs5Ul@%FtR72qHv$!9Tu>q1tT*BlU<^OvBQ~c){Apv zkNxxPg)8f?*2H9%fq}5kb3Q+7^g`|&jCe>@L)vCl+$;g-E6^}^7#hZa0vw1E```+! z1Lp;^g?l7#fovUqcOV>ehrKTJ+qpRZz!$7u%BfIGgpN5~PKK@uLN9qi-#Q`pnV0Qk z=7;>-{GQ%wx~c=P(LrX=1ppaPfJ3@#ylgUh&T#vCK>{0Y0+ZK(3+5xiLe=bXBPR&v zXO&YBj|uwKtB74Q!X!Su}@J94~;?~vM9_ak=+?TKV zy?T72lrykIjuYW25%}l4xA?0*WADdZlmB#c2J;ckz=M9T$1hcPWxyM-s5*K8t)W9h zgACnBS2#Ly$juG*z z>47Q6XYMU2*{+X`2mX4%@9KwXcBq=tx9N`2U8XBrU-~BX0%bB>kylpGQS#3cPw|tw z%ZiRJ+*Z1}xUuLVs1HC@>?r@d{M@p4@@~(s%YRw_a$#7jLjh9BO4Qq9CRw?kR5li& z00EHv^j$@`k6dAPnrb^tElyJ>oRm5(E8s`7-2%dlHMGs#WofZ=bXd9^)C;7!Vd(rS zt$IPWyWGTe8!x&=dF(X)AZvsCK~~Y;UTq#oZT&-#ya)=qIH^anWuP~?Jl=4CDbf?B zHDUwZeVR6~R(KiM=KK1J3JTuQ4P$i)f&a9b;!G0H{r4;^)kM-uu z)z3g*Kl`u3Za2-51Cfc6xXx=mkl0xGPg03x)JaHyRvSx*iLu#8EWb<|l;S6cH&ss# zk`SqbwzefLu+&SWQtUjSCe?uCWZ(7O?aN0{sm9>T;vwz9981X$coJJ0uQB zZL_HgzvO1g0(K6ORq9n*Wv#^yf&I0>ceZqy9n>=uMk}kB-xw{+YzDkDl&y9|w%(@N zIp>qs7$O5g;vw9g6L}qa?p7Y`1rSR)4z8bPwWxkrw062@a z@GCC=|8m`=&Wh{c>3(_HHKp&AHd5CZJ~S*Z`EK!Vicc!C7H%!LS^v4dDgQfpALKRY zF4yX*UwR+*a9QP6l~xrkJG{2o1QjAudPk!2!kCDQO{0e5ym!PTo` z5C8G^p*OuF=~oOwA4HWjc~uPH3xdMKsUk!r1&sxWV_eX9$;eU>s7vv&Fxc{7R0=+2 zWQmAgNq$n?%r~r$?YsQN1;4tmQMD(eRqkXRBZ~#J!&6k)`Z}BO3dfWuZTntJkCt$G zx36h;&}~*b9MU1ar2?)a>l|4mHcNR3HP3RLE%tn1I+F|K!=2szJ+4Dj!@TK zXD`@n_}e$%P2XKatE?8Snm@8YXqBQm3GpFwU7j&&bFVB~7fIhLGAEQFU9xo}%_3yw z!C-K(yx3KH-1{&4?rrHIlg{@YmQHh*(_~*yJMD1Rjp@Oia0tG_+F@~YvhzloM9fMm zCP>WuTGh05^`Bgw9y5{i$XR*LNF$FJIEcz=aiI4Xd-dZ3tB&0O;ZiL$N~Dr^obn&6 zt_>noo*(`8F&U^t22KI=_>p=MsFDgXoELz4VeG|wXPkc4)<4StCG_67 ziXkNU)`pOCLK*8^cXw;Q?au!6{%bXfT{2qE^1>8mC?x^US#T zwBFAqX@goAYYEt)TrfkyL--{9Qe7caaHoE0$)e)z`5IN{hSNUG{LP+n+>*dF9$-RHbl3|F1Aa znFT=&FM6Y)X<;?p;_|vZ;Q`6ZjrDMSi!0Cr48&fKf13yHUeyD94D7%EV1T&@lu!Ip^8$FO-5YlMp|m?(vKW1zt>2Hrt09<>YG{{t zvkO?v=-sBeg>bdaA1X3H%=}nHu^1*epQTUvrCivRaL?BQ#^achXpVQg20f7N3NwF? z*YEE|eRd zsCc-Qv)?5P<l%r!KOYUmypkb>RdGj2aD#C(N` z8XC&|;wt9oYlA?}hYJS%!4R|tyvx@+ACplLnI75*iOAs6h@T2Eb#POV5azDHlwth4 z8z}uCU(N)J%7*!9(hh=X(rjCe(B?E&B=kbXW`yxC*q_-3bYV{rc*8)-CSSX9cM$qz@FA|5DcGrRMBtqg&t5#fxBk;m&=q-M5c;pP2KFv9d(Bm%1tiZcAj0H-dqnrBTiyXb z*puEA_Q3%0`-+Yt6}3h_F@~bnMN?P<=bWrb-cHNjF==BbUX~4K7%;F-Fd%hMbR?ixZB=B%ZL=uxG_>L<&A}dG_%#g7T*+P)$L}&!VZX-VWiY5<-xemK@ zhJ1%Xo}EUs$zh%^R5WRr&&%IIKwF({TcDBIrK=gv4Zv$Fqyl*Yu+^fgfR|qD_rYJ- zR6+_Hu2TU{7=ml*sxa&tVeao8@Wp?T~cmW*}84 zX`fM4&ho+lI|zgU2rNK07$%SbDp35tthh%vsk`FG6{Y3PWkaRUm6lUy8opEV$CBCb zM($6}0yzuhEReH6&H_0LD6jyi>aXI10>Erjp5u5h%Nm?6Hb8ljG;YAbC~I)yh(*NT ztT^C>5)WhhUAD?u(|;rLP(=7>ZSwgCT#zvh8(L`m; ztex866k~H)qmEH;s4iy7F6pfA`b$Yz{(;XHE(MX{I(2vhr{)B!@P| zZvWovkKXiEscOh_X(Mdoh*<=!JXAW~CG8iDw2H8m6bRrVyeJXZc=HE$T)FtVTUFbd zFd-LMZY=_KMZ)!Q?7M=-`#w6l{CiIwry9HJTpl0>tPono&R0DD0I|6%_Hg-C%c~cB zOLeP~vwhgPBPJ1~a`Fv;Torrtw%4}&^6F30gCsh;&6E$s9zSxL2v@nn1;DM3J+b|y z_ujqf0hMqQ6LoaDJhMO04eJC7g>-w6ItpFYxM0D;1&s?9)-*LNSlrmui2hx$pt-)L zsi_X$t7&Rn)QsM(Tex`f!e;olVd28X^$Tm@BlQdG>YCvCy1K>&bYWvtJ;dFq|5ABem_;L;cNdf@{2TTNT=|AA85WwCbArj8D}oN|~u%s>`wpj|;Y z+wWz93<3)0{PQHR+w9g(kSt+PxDK5xLMR7*uq0msx38k%e>4P!@THPlY=V#zecBek z+tq_qQii&FAtKoWG0E&Kf7GV6+TOq8%Af}$wW=(Z<3&0G}^o4bc8Wy3Z zFRE)?4F4`@Xaoy3Ha9eZVd|UVFaC8wQv+D8X;FP+a}zQU*JJ^g1Ube_jLwZOKi3Z( zNzj;X6ST?U1TwSRMMHli7(V@m(k|LfwXR= z*=|mFQ8EPqSpl4ADf|b2`Dq7hy5ac-&XQrq!aqWAI1obf1B4r4Z16w%2fL+jz^~$a zTTsrnBn%9dqun=;NRNq3lH=#fvRd6PS68%705Y-;9wqRgdUJ=V#M!)Xe833<6afNlQub8 zs?iM11>%7L;dTW>Rq)9aEl1b&c=|w;3V$TU70`5==H~R;T6zTo*U~m#K+23%_K=bI zv2L5ke=^r zK`#+Lz>nk>(NHyX3z`=AYM`PRB#g>LCz4(!advPt@i(}c)zo2jw3w@;my0cLH95_u z*48SJn5?O|uw)Ma!%d=|XP4Eq^5UyAS3U~7s$!%ajJZf|5W+y(b-Kqy0_# zE?Qu4ze<)H!I`itC0~ub$lTT1jV_Z6@U*U!e1l{+!rM+TPr^b(;6>II4zqnNSD^s; zPHNBbCm3VGxK6TB9G+j-b;a@{~_m+MgH}TR|?Ca90eHpwaPGEQ}NtX|L z`7#<^G5TH!lQ?*I!l%Ib{e!7K6rYK33%GG5cpgk+bv5u^l0NQ+A!@O_-`sc#$iXg~veQ20Om-EL7>pWUyc*da{_>l; zTKJR&v136{%tzb9pl@jbf?xpeC*U%E#MYrsW`Zj7aD71zfLRFGkCqC2A?E!yya1{DpW?Kf(+~x$H|JxGB zw#4V8y9UXsF&ZMWF%5M|x?uBx+#LVP%@{N-S*Ag8nJYyQN1hh5+b#M!Y#rQEOW`T0 zieJ$NrPmD#mm#>g3r2%Q?t{l-ZWj^X0Er7^^7NnR#?oCx4V|zt<(?W6Hm0yGNU<|b z*er-&44o|9MI>y=q|X)DwJ@rbI|VqRIXIgaz{Smyu)cHKYuKG+v#&rin+a`jOcJXl zY;b5igPNpMDopw?4cjEs?&xb^8phYe=g`qtz+I&Ca&1F>jZN|`fUBz&U`eKOxTo|J zbxGG(!-z#Au-$H5V?&Q4nTGR9(^^YcJM5v?bUCXeBfT0eX$clgHGQPskHKMh`l+U# zo805mVK1Sq*I~wj#0nBOX zg2R0ngrYCtk>4OSW~$MQ{Q#aESEET&vTK2Pw`H|t4n?Qb;^_zXu4MB@)50tzxrub^ zgTE+YmV#^nytk@`yCIyA(Ii33KmQ54GMGPG@WkFCLA5OLPz}$pE#meCzb!#oP1~7Z z*o(KRcq<#h?o=n@E;Y^O6O4)%j?sHhv`H6Y7Bt#`=C-lI zF`Q%qI7|=2>czt_a1Ho-XsjkATTtnCu`%U-5oU48c!HT#x-rA&9g@d8{H|nv6y|Wr zE#L`Tm{7r%2{U-YK64q3p4bIjr(A6K@ND9-UpCv!^uP|@Tww)%e_p$A>YqEh#9@BP zu@*f>N0;dadirC;gZXfHIE-)Zp!RUxO!B)B!V^#az*D>$PsBlfD;X!@h;L^)KBc+Y zPkMMNooe~*sKR^I1EFYXIpwsk>1u&^2n|YO9cFsAVC+pLG61~H% z-aPpDGKEfj6*CCnju0;Q)3dQDdPi{uxWi1oA2s>Llm~oCv;e+2A}2uH!zB{}paC@Q zZ2Uv~aF{pIYGI+Sp(W35=oj&h=(l)S0{xs6xyLVv?>p9XLSWJb>Su1I%4S_>wpZbB zKBkfJTFN$9&`nK zE?s{%R6|uJ4yyEt8D8PzKiqrJ2q>$FmG0r#x!)fMp`i(c2Nd){gABAhTwdU_p|{{8 z!b5S!;R_dXks$7K-EcmR%*LG|B9m_N1cD*N2|_O$_?xzgu24g_CAfQ=;O^*p{G5)) zjX@wUijIOjSu(p=67JI?VV0yiwNDt-N%x6QCE^g5+%4%~9)NMPIqp_*j>4=?coTCH ztB@sl3-P89UVws@9$;@m)Eij{US0trTg)aG9DJbo0PZM!i7SM?5QCcoBm`9K8w&ry z2Zhdx15cQBCF~w_;257RQo{xah=Bt~OlEG(#%~D@h>U<}NSH7JUoEj3_pZ{K0sWzi+vakykq(1@bS#ltyoz+c|VcRRAV zfC2qNi|ZzQj(ephA^uQXPoF~9L344JAUCK6!l2l{{KpUw{>S+GGwQ2SeHl$lZ!KUD zUq*H95?)4&4+jRlRZXX$ZHW#dkhVnl@$bV0^X%4j4j@FYfB;FWxs|rr%`N6u2$^6* zL$@N*mKR0&Lt!**I0H%AO5=kj?&TEiP2FPDIMI`EmkP!ez69N~h6d}S9>;GdSz=Vz z!$&2b7Fgcg%aV7J8~}ZhU@*yL=S4!+97->aIA z_c%9dlllQB0d%H-J_i*?fx9FbN$?&F7oj;gx+WPvXo5%{Wx|>-xy`7t$U)*tG+=tB zuv*290*LsTq!l>zupw@P=l_*3D(KUIVblk_dKfzil=t}S{OE-@fiV&1zoYrM8|G&a z_rjz>ATuo!?!(~G_ls@R$BPGATC-8FtoqOXE2+~{w?^-WO)Q%rB*uMnLzzElY8 zTeKtKr_T773+T`^k@`D?4w4Q8aY}{61kSsqdj*JMiZ+Iyq<@p2Lile*y98t+>0?OJ zTkvl3hoxVd{ORQPQhZ$8ci~|>3P7s&Q`{z*xSWh6)%z)KleAq(zDtU?Q`{s@N=*K= z^z{p1*{KB1<63wQ1743Esa#dkOX$kw%bMra&8w@dK^O3hqButdU4TAlsEogYD&C@3 zH=tJy_&xM){2*H-p$Z>`%iw)Xd$jbTR~yhbp+DlsoUl@uw_)Kvbj2o@7lg~=SD84V z{CB~})F^D1p1^%j2(C}!wZT+5R?w!<^v!Trctq!Kj?8_?wS zVgn+x;g6zDLb*N;CWOCg1G*{yDG%;Xz85M%mWI0r?nz8CbMx^)>|D@u46n}Q_lYer zR03yoK)g3N2H#Ea0iG5qxf=TomyFbHX+>`csTT+t{!__kD|L67I?cFQEkkb5Rjlo0 z24Ovby(`kmgY2eSuV*tuH=~U<6pd$m?jas2x=kgJKDt^>_SSCxcKA+?OjmTbn4l_x zwTruwFT5@NfTe3K=&HAN+s!6m_2SFcrhh--cHs43Z^7eX^zc?_v9@xJT)42Yp|K9K z&!ktiT02daF5Xn)h?8Ej*4koPv8DsG>Eo9_h^uIYtB2clg7Q9n^58zp9J3VU*Hyy zlLu1<{Fn2KLCKPs$}hMKU^%ydb2K;x+y;qlajj7-+X-vQd`LC@FYuW921w{#0I8fg zkCF2jE?GQ-$C!HIfG=Ll8wvS^`U6P3GC1Vxg`8|gYT`p}KT_YYxOvf{y1II4BPPy@ z1JC~!v;&(cdlFtzAcM2ORvgamJxD+lHYHfx5gk5A&3mZ%NW(%9SAZpGg8ImW@(*5V zB7=ii;6xOww&TUe6LfK!)rYc!)YsQFHZQKLTP$tF#F<1Ukb?*cBpDo}&E@fe7QAGk zfz67K!F3Fz20qlLk^07_hUR+M#Maj@uCHsBHYMjPskXAbNKLk`0=2b33`J_*L(Nl= zEYCvNyyhNv;7||EUczCUc%sBV26F;NDNdY(V>KHCW#B^rP@~?IJh2?wKGLudZCnqh zHHVuCHsXKqnK^f~mT=O`pWKTly&KFn3rx558{wH3O$^cpUi|nAXK3`=_=(fl9RJ3p zmNHCbU3h^5K>b65P`JV8aUIIu*-+OErGx6~7D*eCTjf%&a-1Gn2G`(EuIYHnBs_uh zn`X2}f;cF8%0fJ_U=WJ$NQ-_*t$(O3f{hEB7B#{a;ILi<;rah`q5xA|&Mt!+IR+U9 z>Nuf%yHRCF4rNDaXavzdI2_LHsO5J{SnDu@kHjNN15h9bGy{Ey^x0m+!lnfc&4{crVh66!R4L`yu|G*$2{&LmCkkQb!K7Wh!tIu^Aks6&P9J zK#vPpIymx(cOp`=9cr7?hQ;*@a*sD0>nigB4X>aaUzdQg6eltWCp0NHrkL#%LiA50 zNkuc8HX-F`NL6uzxN{HG5pxCINCp~ejJctl?IC{P*Hph4o%bz}j)}ziMxZZBFHt<( z9I~va9FgwOA=0Jf$mMu+XpaTcAQ8#H(?wE7f;Bvf8mLi{{_S- z*tJY_D%}KeiyV^)_>jSCkw!lC>G&Q#WVnc+^rr2I;FWh3hL%GL22c!^p0Vp4t^ z63;?0OD-edEM(EA`v9NC&s}bb*L*=GU&K6np4lm6?F*ODWLf*7WI#tp+!xcxL`c)K z<3bY2k*&-M66jDjwTJ|IhqF)eQz-rjU*;o~Yy;OL@OeykMf+lo@``?{bfB_R$)XG5 zJBjGwX^R(PfbtQ-Z4qJ!t)WY6>xEav?=Vz?>{PlN@rn-U8|Ck_CdgQjEIWU{#IUrX z65ci=g(B(OaXccNN>EuxRPNAPRIU->O1w{{rO)RNFp}834eQ3GxH7d02s9+_GlB#N zZd@1wjPP|5(^Xo6D69?g03r|pG5>>rT%_ZSg>MP5!JG?Cd>4}XRiXA zC*Ex3QsRTC5(K%&f8v#lQe?()nqO7Sq@cJSpE{&dY3dpIkef**jpLje&7{&76Ndqw z(&OfcuY2S(sbum;-y_Vt*sv0brvxXFd}yra(43njc8jp!N<9M4eM;O!!-)`Xh3Cr3 z7RCpUGoT$5pGy(24g7n4uh%sgWI%cfwgP>4I>jyrp3c1^Ooe8kFqg|}68 z@Fm!_`7e>|=D_o}*FpTh6Ld1McB7{^V{KVU4boh_TX%fYmlnMzsdrl^miS0t5Hwfs zRxGbB$a^04^=`+`b^K!WT3pILSMSzrHzx_prY=k??wRH5-PS3tcFRf3igoT2>LO}e z0M{L05FM5idL6A^Uxn4`q(yN-^1TnlZ_)lzP)wD)7GGNUm!MV}66l1#ydZ1*{REkA zsg^5f6=I*P0hMn_DP#O={5`lGK*$wG5KNUTj&Kl)BjhR$AUSB5JzyJ|t2h8_`MRkpf;}bs$ zxbnu#oa+ByK^DwuP!Y~EjX4dfiN!=uqR{c1>+#Rihk^}FO_?T-RybQZ%rvOr`QIuK znBqh#G#?*+${$TR9V+N}==#LjL-G`+iGT3-%jr;cwU}*Av}LYzNDZ5u#*iS&0gP;4 zuPcyH0*y%LL#f6`PKS#0@g}E3g=$jDROf5T=}-~+Q%;8}r$Z%BiwO@{7A$UTYRtUB z&DC?nTbyy+-6kYm9L!>$ISs0bR0MW{xJ6Eb3h%%VTMa6d|1Zr9@~s+2M@h$1$OZ_ThI#lGC8V`;nyh-+$Zuf%us~^~C>R@dxxjF4uniHK_O; z|AU(Uzq%K=&0L1*db7!1)iO`JbYN;tm&MvegGh^z@M$y8lknW(H!%YgevxK=qW6T1 zM)+k5(q!uPyWQbHfPv>voL_}L{`Rd-?3s z7AcmNd2S)Ap9#3&_NY3H2rv145i7b*{H+G=&*$qTed}E<)EhdMvs%uS02YQ)H zr>#amrfP8ddy4=toVZco132dj{;k3v=9}(j=AA<07qHb%bN3FQL=abxE68*Q-Tlmf zYaY<+S}jiK!EVr&fWkO%`PP0Yj{`qSYz0`!DbFnN{3++l8U(vny4_tUfEym*?w zhaZcjppQX5vnY!IU&48#)OeC!cO%&%_(&aG2pw&Pa+7Jh8Pbhuo4K6AQVR)=5%7dS zxywakks`PgZzF)VLqOwa8%bPl7oUyjpZ>iG|~aSuORs#57g03u|b2 zM{tnUseK<((xpQczk{oH&mZYTV)E|j|6aRtuuY6PKs@!;@g-HgCa`=v?YI-%a zKfQ2?b%n!hUu(is28ftgtgW=wM*Dr>&B9QEp$lFp{t7pwanab+zA26g`qX7`AIa~i z(roGv#XT5hwMjn$-&IYU@i$t!y3BSOHRb=ecWu3G99P(JiUKa+=BZD88ORSM3)<#Q zx3nojQcsJgPUE?9xOT2sBxycCgbVoU7DYEC5z;nak%ItPS)k*(z$BRXz*h zM8f9jVf}eSJo%Jlm6gOv$@Ry|i@k%VqFR4`aGYU6ElclK;NzDjDerUJkFB4yvRbMZ z;>!1|zImxC%@<699=oX=XSOAa^Z$q4CY71Wa?pmiW|zX6z0s1D#Tjscnoi!_?46XO z{4U|ABxR`OkRDPn=5}!`ead8@fR}`eOG09xSyBCjR0!rLf(lV(hMu&6ndeB}Wrm~> zR%)|Ka;Py@C?>UyCkrB5`pdH7shebD)>%TTZ=f8I zmQP5{3vErhH<)_&HUS{rqiveC*n2dA?U~Jqd{%BnByL>50bb$(oaZgxsbEYnj@0c>~T)xoaMT0%Mjfia*Ek1=!?^9lm4<UuwUml6)2T$PZ z*{SV_#$l~m+ky87D&Z0KL*Z2aj7x&EbnKa0Qi@~( z^OA}^EV$bV>`S?E@*q@*dV}6yseiUt0xxAwAjr$?j`G#A3)6cU*947Zm6FkXiPe)6 zi}hDISRhg=FVR#**99a;{g={56Y~HKnfAa*-^L~tj#-3G^5I_8VqeKohzq8(q&$aA zG@_b&R+zGC;1Z0NQeBdAP@G)CG#Csu(569yma z>w5YW?6^4}Kq>#4t?0DfZI9Zr8N62FiFS)x+dII%u8d1W%9wVxugS!uU$~ZI{Y#t#a_GJApkl0VE5w z!^!s4DseQ?b;oT-#hRt_izviWOt5vob-(E4wZmBr2ObA0*07pH7>iiskOZ12#z& zPbG#nr{{%4|K-6}nn;Nws%F#>G`>wIJLdD9oZngG#vb$p7w4-q{I`eT4C%>J&64uw zN)(A9nn)GN$T!aF>{4LM$*vPi=LAJFmSWB^q_ujV=z1;si%D;(*NXZjf2y9ZF^Xmh z)8y+BsBdqOU{7m}-f(2EQ8~5v=0*V4xr*O9{;w8NYmv>K%$E8Zj>KNL+{lmj0V7eurYKA+29awlDhAti&>kQD~|i<)n^jJ4J@ zY~tXRXbhsWE;QLoz&YV;lR(@>Zv?a{(TX~7#tv}Cwb$)jY6kL_wy;a3;)Xji;CzLa zzwIabw9@}-7lUSj?dD=7HVrQ?2(svb>JDz3{P6$$ z2>20LjS;x|=Irh-3%}aFd-rcYRIa}L-JK6wIBIXRm+k<%>~cS7cPssYC~S&izE~3F z4@=u0mI|V=|M9?WYlKvD zTzT@W#xNaK_)q9C9TBV|4(i8(a4i)&mv(Cq;G+sUwfAvmpJFKJct1Won8SfaB2vTP zMN$BUft<|V|Aod}lF*H@LIf3LkSP_snvT+F!EJ`1fDwXE6GhN`MqxJUqU^qmbp7~M zFAPq`onUa82A4@W9WB14ZJb~+u^YRAj0d@LQRP7gBDG9rT~^1P$|^gpNkWRR0Ky`f6+sy<|y8*AW!Adas5z)0fp;^tX)Logux@sN{FBp zPzr)n%qz<_C}bZ6$U8X%k+Iw4tekg5CPzZS1%`-M22HkG%|R3(3Q4qwqC4(%&@?_- zv4@{F`WSyJ3N^!Ld#VDM=NO6fX|>zO<)YdZ#BaF)R8}7Uva*EAIvGmYWWBKI4Geg_ zK`6&_`OS?RIpJ?{tmY$$8b!&)*8E4nl!#HSinQYw8C?Oy1B9g6lx8~y`yv(xoEFfP zOJcFIVBuUE)-Rfp0c{h(^|&F~S(2y$)WLh>!vCmmERIipHF0M0OCl5=rDL5#3|X@~ zzBq{nnXKr+78991!a!tHo(cnWZHeBXg#t1-khO{M&8m!-#BB;_0}m$%0<@HZa?EfE z=+BfAcy$$j%D`zSpfU7X2S$i$lj?E<#NgXnY-` zT#vN!dRW7n9#T=h7a<4PXT|Cn=-e9xZN!=BXJ7way!U}rzb>!-*ZlflA1tVUegF9T zh4inl;8zVr<3(v{)h`L&x4i1to0dH{KtO|zcgX&K?{4M}KK`E{fvF>K^`GCrulQZ_ z>c7F&Kbs5kyZYn$XSK$jDCE}**BP5j%i@;Z!nBsDA|(us2{0I&>Mpb!dZd$rT|icx-XeDB+{L zB;Ks`W^KP|jZ0;jd$ZO+2Pd2(>6Wk=J+f6}z7y#qSOv3|`$nqd6l*EeCFg>mcXQuz z03)hAs~uHqNEzmen}Nv1fanW3UdC0T&?W6Nl6P0r94csyHR39eePX$;;*K%Rl94rF zfm~PhYW4ZyVGVw7>@&z7NhDjh74c?s5ln~4MFyQCI4orXohtLN#ZnKTgUV;Alef8< zaD$Y^VT#71%P1H$d#BBl_F!~Q$#h~E{qX8S>AacG4-Q z7Xu)6{%6`y88UygJVqz4n5{3Akg{8e?AIe5SpE(C|C3tgJ)T#OR+aOV&RBUqGszpy zO!9hDI+4oJbAwoBsFa-q5-h!wApu-UkU=B$t$!S=SnmAdcWeuB4w)}7{{(0Yq$ zsZV&&H6us#C3IhRL~qZT`>(Zuir4d)@E#K$TcQ@m)?>mGcKvC4FdUI>PpkW$UUIw6 zn8=CU*e3I?-DIZn>eLkdCCtGihsaMU1MGsQFV5hAP-VN-BDO`Ha(*CDkKqXJC;_XD zA0kj4QyB;nAg}WH4|*-nIb~=hH0Z4!3VT6BU$sYqq&|%hEf6Xx6uXK)`xmuQ^^rbl~DX?5Og9? zX&A6kQ+MrT$lGkp2=19r4~<(^?9wk~JMP*e63QyX&03sG+q!e&qTS87sh0EMzUF9D z=wzHr)4}nA(WIO%nvrl6)ihlyJDvJ-4=2oQTk-;Cfl-#ew7++dVR+UOrkpjb2=@WQ zITm#6O{Jt!)F2z*(bG<2Zu>xA(xAZO9t|9d{H@62b>)=M3uobWhS}s^pQ_PkJm^oQ zJ>GB2{%HeFEN|U#AebwXk*Z z0;v*Ua2*ESGYL$)_J@zwALSpZ*sCN^ti@TiaLvzfWSy>lMl#X>vjQ4dyKU34rQ^`K zHm=P*mWJ1CAkuCdCOi$Si~|EwxAObqWE^>Cv}j6B+?kznhl^lh%1O%V+ z%YJXv>$U?PeYkQV;raFAHcsqs@t0V^5Xw}Pq$v@UhCwU{Frg3O%peVo=_Ce-J{lq1 z7eI``NxRb?UDBV>#ZG&J3rfN^98zp>Zy-*`h~fcP<)go9B(%in&`XC5tD%I7^sNz_lIAF-%d8in4&hkJ-8#MQEiQwXz#-Uxl@`c@M{+~YA$a1hTu zcE~SdBK3`N#Key^eN&ZFq_+&Q0fn6dGI|8^r6KX8=$(oXkVhwj-m73365N0Y!m!-} zgc@Ikt7hwz%Y|(M9P+UA*Qv2K*kOjTQ@soUhy1V=^|1p1$`D^7vU(Uj?6kj(#5(O_ zg0z6yVKB3#QnYF$v$15bBH9cUE|T4U zB!)WaNZDH7*qo*4R$4ecg?xgEd`eykrD1swA z^r%m6VKRpJm;uVehF~XlB3YiKK~ZEm5!jaFk&vdr$nvwTM3#g`5;H3Y z$}G@Q%3nrHfffoB+5)9au?wM4Ae6HADl2SCY0Lke`=(^cQl3o`!*h!ry`}rkJ@=e* z&-hNq@@8L@=lX)-K~I#k8g?-ljfTY>XD}G1;=c#*pY$^g|ClU&z+a>4-{ z$ft{ozocspDgJWGu9IIj9%`)3bjt)eUP$9Sg_}~ar_E;kkvr*5)P{bGM4tNID$9p{(lPAJ=`+8MZhhu?moZ-8pzND)+c1J?Ix7!;W2!^BV z7FTPhbE&hPeMhfnDAL^*4h{;J9mT0n;us%@WV$sJ=GS}te89_Rx?(Wc%lo@S!BEWa ziTZ+pZNIKN67^uz7-jOvGC#SS5A>$F-WTu<#s<5CeSHx=%3fG_bjoAXT!!Z*U*r#N z;KSXXe!ko98}vn!PsT!_%qQiS>E7TA^aeL{Tas@Uho0f=h`Xysy*uUXh`X($-koxG zy)WX6cBk;Ca1b2OA00@3SU=u`538Be;{^w+d5ez@1bkj!G-YC?VMbFgXS1q0YAPSD z%rDr}YBio@OkSc6_X>Nv3u{kVoXVl^WLTEU6<_;;^ainHLL--=H@Myt_R$K)vWnsT zAwZhhB2~_$NCM>qi30h0S#Z!%k?5xKQw);}_H?<7XC5oj%elhd+-2`{xm(l8wNcLQ zH-d_zT>P~YG&3KLF8jtR-n0vX?XW%=`sXpyHVu`6ve(g)@i0%NS6l{!$JbFPAr8yLg1)|+nbGHW~ zy3QQ(rf9@aI2cMfEek3yyH~i?lKxKh$&~y0yQAIp8NN_;j_v~KoAL_wT(rn|(*6?Z z9qwiB_Rj7`XOpYd#e`sovlEk$_V}a`(~R+ne0&E%sK2v%B5d(be2ZTs`Xfyb**Q_3}{&oZc+XKrk|FeNo?f7$DsZ zHQipHiZY?l`Vq&-hW7O_gJs0IK$`3M2+VMhueVz5-7%GlaqWg0SP% zhMRaI7)e>@UV2c_7rg82OL3kpkzh~6bG4*N8j_P8Xt2ED04kz8Knnl$! zR7cZ2Yvk#qXJYnM&mvw(Galt|(!H{wma=d&$^>ags50!>Y$Yw@PV8T6Cr`)k{K8KS zGk!Y#psDwoj>13nADsd^1#}AN6woQ4Q$VM{H;)3t1NkMEee&w?Q&5mMDKD?kXe=*K z{p{XgjP&yBd4Di8$Oobxe9=h8O}_29s}6eV9~YZ!FU&ouzVKv|?e+Vg zn)mBV>TRzdvGTgN7kzGf!+OddMJw*IZN2f2Pdt%#xNYknUOwx~-@jqodUMa2x6gFi zwk})y_;Y_~vTa>*#@uz&&$Df9o%6&G_K&rAd;xnX%vD*qDsz>Uvm8=WeTcQ1Yir40 z?QC;#7ITed4cDlq4hILmXNdC!`aFRsXRfs1RjW8J{Q8k$&*ZG6<};JJ&pf}bx`Aj_ zXKi2vtgC4N0c)!2=wOXSpnr|Ej#0HrpelVoQP)Zb>)Gw}haGG%gVd$}Ff9sQZhQ5S zr}sEzUfynJ<_%ptb70DgBS`(`zY{m#v8CAd=5HK-Ex2l{?X8wm!tN_?w7v9_^OjGo zpV(eH`&6s#fkE5L<`*w~&i93F>s~kQbNVHDwyiVwefVeGfNksk3!Xjug$1^)SMLAx zE&J?k+j`kYEocAzA=}n#$`gCceO)58rA{C<=-hCK)dEsa&YjdNs)mo8l$FNV!e?OT z1#S?xit%HerCOkIH8E+m*-GTJHdL{L)fPt1T6VCZRyf#DEATj54Qd`=YzkclA|Gvg z>D=gTYft$1w4WN^oICUC$v2Lqv1`F6w=BQJ_QtapeDAcI+ih>&&|LS%=d*23-{YCJ zxb@ezr)R!a*nY#)wr66~_Bp?1q3!+0ZguUkd8zIF2dn37Y{xf@c)^)xL_P)As z#T)0_w*IcP_2yq~ux+hWWoj)&s*Y@i5|NG_du6xY(=BMtu*q>+F zw*Kei=ki-W!QZ{_3LkWnZR>xpa&KO8o$dXH{Kp)3@k_S%@A=>NfBaah?fu7ZEnT_a zewo>Mn~1%!11g1UTVbWx{G{R!NyqO~{AKZH!au)xDqnx2Zv+M6|E@F^TXt{A%NvS6 zxN`WnRzu-PL6CWkEVyIA9op7OlK;8XL8c8Kjj*f?X6GPXz}E{4D9W|EJGs`bW)_oG zg1B6duU~!ERBl>mGPzniob8?DICXQXhbm93fE6Xhfy+%D&Ss~hle2K1h+ti@Z^bq9 zm8-a>c6SR`7UBJ{8M$5`Jj?<7!zi`NIeo*v|5goh|wM93qF zcnA1F&wPg)e*3mgZo>c{=2gSP%X4K`7{9gF+PaGBs@eu?wUz$X*49`ms$osk)>c$o z>ucz&xvrt1t_FXr>gpOSbrrb9QfD?-<9f5%T16LHt1Wdk<)*Gy1fG?Fs$K)b&v~$3 zT)Y-;=Um>bn*tLZo$hwd>*0cdWa=)$!TIDzMRX6wE^<|*>A^Q3wne8sI23`(Q=R;p zh$DXGd|v>8TVHQ?nD-!j!}%gyAm;a*Oy!&l|2a5;i1y}YPU4}eotKBI&E{IL1z4fZ zTwhgBtWa;ZHsEhx9}+^$v=C6?{%Vt~YlxvusbHM!$0DrFjFpMEC?k(#6t7I99sWL>$;+3rj_B+C?t z6VhCwD#E4`LeSS+!67;b(Zl|!WM@@y5R)w5u!2K;Uwr}}m2*OFPSER(g~KdW(ANvy zNJ{bO7zA6N4-r!KRS^?YZ)&Cu@$=QAn6DZLPN~0=u9Sy^4=E!Ojwn}zk$M-14fgQi zvT`oCo)2?nA*2~19t;uNGlVx*zKaWnk;*0>Rlbw6cR0#P(&DraQ8}n;Pb6A~JJY-| zT_;!x?3OfFV5JJ$y|R*9!o!~6+QNt*hle;P%|pZ_uH&}(;`h-b72>ohRQEI~bviR?2y$*{Hx(5=Ahp;6X_J=LjRS8>xcQT6HMoso)&6+=Y9(lh$1_%cw6? zu{GFA`GkkL?aj^(hqFw5x$?%1$g;FIHkKhVtHM&DzD9XSIpcZx9=x0*9f^_$uG~(e zmgiX|BSbOf4bGoS<}O=3i@m*r>8#pDA-%`#aCEh|J7ELDxWzrBBvr<_7mFE&E}p0m zP%#2I*oP67Bo$0TI>I&#v`1LPRh5%bW?~OcI~XL$bU;G4(Ko4P4Y#V6ut^ZotDN@s zB)Tg^DTkkZLmJmX%1-P;P58OsHQ=~OmxBA*142(S|{N-7c3GRY#&LX+*pe#V_{szK^<&S@&j z%);ug5rin`X8f!c(u;6R5lU>WsMP>>=pv4;82PLufs8>W-Syo?ur<=%BuN*HURXOa zcR?_jEAUOK8Ko>Wm%FjMqZ2BIF<3c=Cp+gW2RM6cBd30bwYH{0eF3{8{bMDrg(PS* zNwiMU=H)OkFjic$+-V}Cs}ghKl9F|~_L!f?M<^?3eo6J4iK&&8+c2(4HP0mVCt6tY z1G~*C!6`0gwT(kIo~%1DE+$Mz7--^{8E2F!5<^l(+|qC`7D^dyFTXM4AhQ?^(#y!w zedK_g7^|zb73Kg93Li*yCMi!CohUs)fK3`@hVgm53b#U$dlhOh;ZY`Pn~?gdkbDs* zkay$&+XUI6YxF?Uy3VfAE4U<+P_WUHOhTyXG&W(9@>cXErc|xflguKu6)*9ATHOuz z*!DIgL`wkLR4`{@C&=PEi*7IH>||uvRa8ncGB_sV>}sJ0p$tLUrV@-JuYsUs*GN)s z4FD+z;gJgEw1-BNjk;#OU>jv)OUOe{N8V_B5!aANY&A&&-6 zR9k1|Tst3$`C%BsY)qC%QgzifnT8hKG?ct4A~i)rTaxY>Y(5i{?ITUTkV$sH!yMg# zb|d!xQ(zBSM>mFOJymTtQ%BHgCwq5mbPhZwz1iEnTE5y zSASY(jVQ2GxqBnxs<7MeQSyILIz@QwPL7MdE{;VKpoagtAra+@X7Z8#xY zz>*#rQ?%NIG_D|5%3i-WB`!@aJaNv(<-5{4xKe`9V%S}YK&vdXl<+e~M;Zx#1Q)2N z8I(Ou1*h8m&gWKUa)#UBLomXn&%hJ>dA*$o9Ofy(jUI|?YO(*R( zX}ifRSqT^bE>%L5!se8U$0;;Hi>Hs0Aw--fNuJ1l+KQyuvcywjv_dS@hu=3M?5)a( zY<<3PBue=t5mS-K)J@7nS8z>9jNBwMvZ;s#G0IFuat@Tj0>q!HLAjEi;ZS8Lr1L^2 zEz*?dNTNYmvZ|*gIhrvn!GuhaHc0EkDVEU4-e05&BLK)D(wyJuLk1`U8xa@^iCEVHW`QjrfZV>R<%7LzJ__Zj)w48gG)=RHcQmO`@1slYYEx0m0wm6qca$ zKyVhlxtK#Fu0WaO!dc}bIb&3WB6qENPE1ART$8CtVCAMHUpP)U6+ejl#0n0uEoF*` zW29F;D#=Tt$WP*0HZMecRAxCD;|YS_R8+*N_A~eha;sQ}9cXd2mhrtZ8^TiTkTa!3 zo-9>yI_DKbt-@rpFKlG*Uiv3T>r6<)VkGCdiX^8wDzoNtX1o|9Ik5)z{{`j9 z7(xhw_fRrzNH3)D z2b(mRkn-!%MW8dyC_9<-?o8aMrY7NkGPF^Rn z!c{G&l(j@dVVY9NypuB!<+sMF2Oc9Z7{$~dxT*S)G!M>C%4uIL9v9afgRGGg+!-Pw zL?@7-7(5}%;Yqo}*Sjz&nO{1c(-*-~8u=aIv{=tt*)sQFYQUi=c$&B@ky(uonbsUM$wU-Fp|H!8La zTWKj@I`Zs@2c#yKCV%_Lvm+jmqL!2IA8~TT?NS*@@_owDV@*XRNLE_`nU0*(c2A(6 zFX0a1N)BCAGaprLB^5X?=8Q<03v?d06_rQ_no$8l2Up`@5uKrPk{4IW7L@K2PP1e+ zS1%o`!j~l9)CsBUB#^dVHX4Q6C{^GZG9p0x^{inb{kav;hc7I)p4C zJb}Gx>g+WWRH*VKdK3CoV)y%rSfCNzM)OA=<_=UK(P^4mMzazhn=*mcC4?5l?q-uk z9K?CWAO>*`UV`5U*r^Dek-xL31WBWV@?&w8JeTnO)3+*@(j&xbpl(+q9hY<3>GAX< zpKk>cE*p$v$m{n7Dtq}5gf#YL+&b(9B7CLaw~ps($V3Hv7IMIcgfX&*AmysH(caz& z&^1KtU-q23I%|~`Ri#$dMUCzjy9?-cxtwrd+aKzELh`V)ITYV$usr zm$Z0*SgSlim|kJEZX1V4bzzwqgh4I9dgw#384}z=F(lweCn`78TC0c~s#No+GecXa z$i7l>V0LDh*j#|(B?tukr?UacBtc!T&B6t69l_7F5%oY&5;B$rZ6g+-RC8i8 zh@}cejkPFo+wKg4{eR)aP#i+#aAqd4_rkTs^?7{$80wPYWsDAl0fWg!h625Cdh;sM zP9%H6Qq@pXUvD;BRO8oGh@wKoO7hHn0b?1?XdghF;6wKdjV{RIl}wzXl8M9)Rdpz* zhd$BSfoYNvszd@5$jlEexCH$Kj=(gDFXHL(`GM~{LHWUAF<9pG2CG7K#G&CnHOH|d7efe!rAZ)3xq2J_f zX6`Gs-&AmQD}GbL2NcjQMPy6WzLV`j3G{M>=-U~8XR3t$l375tGA)In3=D;W(O|&m znV?KqWv;1e!28y##;>a?m8ueukTP?E=>FxTLRM^lMZ1=E!ie+HzK4>EA`xy7tP zVt@tn1+o7xny7mrYi)JC6?kE~Hp`^V7;aW(t^o)n??oCJ5^MYjF2>NUju7KeM#_g( zD{&%OI8|2Qg2C0Q8@(BLf?=p-W(g_!GU!9nAXLgk-2zqBRo7P4d^^k<(O%t_K&=R( zHyR(vE_O0?Cbm!zj0-&omtG`@K#G}p0gxT3WHK5Z0#{6h2so@8czXhcd;EZpNkItUBOF)Knvs|jTFOm&g^_O@ugvSRSj#MuTp#jyl8DLj5+o)+P0CXeU@EDj8ml5eR9B za>cfMQBD=x@}SV8MRW(GFaKAS>PqkcGyqlfXH;MS5(pwVK{7S~oe&6ur>Q50-5?^y zrYp;rG!La?(vMV8jG7z*lufCurz?aLNu!385{vBtA}>9WMvAZ#U07+64$6-&Dscu_ zHM9Kc%0q;Tefq9!&*+NqE`oO#pOqAqEh@oTCjr2y&yILa;AT-aadd?*A{727J*33d z9|(qdRYbat!D!K2C102p$N?`Pv4(`=z}_i_cpILe5Ua!$p_nep!PyD+XlbazUx{)O zy=4AMnhB+}J;QiL;U76rrt?XJmxwR1bIFe66BX|y^GVW5$J~Rj96^6v{G@u*c20=`*fM71bPCru}iBvjr>;8@}+)v;s_*DeWFGCN5`rbs|FE2wVy z8fBo)3@Wv#ns_F@wwW2mN0f;+r-Kh5_ZlcJk{}c$9u^^3)$jL&B0L~}pr-qoEF|G4 zpOA%_v)#^Yz!GQJ)8=7Dj#+{YV$W9@RmYo-$M%03u*CGPq%r_#N-qyJwt=?cO~sq_qWvhP^7{I!WX&Snu(crWLMIp&Re2I#3`Eyr7>T+ zJ`F0xsa&XcJxJ5Fc1&Flz77TnD&0kyFx0z|3T=|dqZ_GNQ!i2h)m7;ru3DL6X;116 z^OOlJAghkVW-nt`NfFLWl7qqm(L#1Y})6n+j0!rg8<{Svr*jR;^azfE{G4 zHd;q|9pfK;oOFzT0ee@~B!Gyb=B3g19eEx>+Kx_~=k7ebY(8N`T&nIBc z-fkw%1lV#NKA-UFvFEMeb{_cr2@jo*{r_|wKEKmxZ;Xoo7ru_YSkg2D3NRD$!t!}+Y*hO?OJ1N)ghqtPArFT0t6KH|1(7E zjm%a z0X3SgsjAf>px8!3E)7wsp6vx4s4nf1dVp?MCxs3HwS8IwV*hWDydm4zOLm9zhz}cVj!$9dUP?E}&yf~tZ>)Q^E$z@DpeCe)f=NFGS!4%5Kv7m?$&RT1pN{>%G4DD9{;&V& z6xi7*5dU|jvDmVELtfrc{K1vOzqJ|)lbW)!_H`{DU%(y;bCx=;%3NjTst>U?9AdR_ zZ7mD#Sa8ScwgG;H#~<^c3+5UwCBcQd3jkQY&l8Ap=1L2<5$$cfT-yq(qupumbaI{c zCCyH*RLU{u%23zm4fgU8o@xbgXOwGocXF*=&CM0;6nZ3u*eRh>RX%Gfr{W4%YlpMF zlXJCpy44R=o(O1;p!A4xQ-`zJ>FDGv)L19!n)%9ATvNNdg)0-w-vL9&V7)k3>LsQ! z4g(Y~Q{CPd4i0jQ*9X0x9_r9DM76S>9#4etj(7+7LC<{AZss-&pqGzoc!bAaCWrc} zt7;pp)z%99W$t{yC*oX1wcxX>7M(QJ()L{5m`Po^sZ+J3uBKe9_m>B%dJPOe=fQe$ z@!B9pn8TZO+glyZj!t(wU{SbWfb*)plUsy?^D*CElnH@Jx)sXd8>o>cPE%tn^iB+` zPJT_q5x;W2FRJ?m%Eh%&J`!|P}R=ML$I=H ztLo~&IzqZLSb-(6Rtt%XV(Y?cF<%KEYpbg&s>KwAZxqAiNQqPmrX#$~4M8{Z0 ztta@)?x5Ej3x|0~Pv}zUMpBBYRb(cn-qcJR;^(VJF<*v@AOwh{YJIv=27kbZV5nPN z%r6{Ku1ErQN>i4O>*ep_SkGjxXDInQIeUkL)sf<~FW~X(NL%WeQz2tjfdABUp(AZY z(k=>4Fz+C;No-eEa!dGrSTzm_MKUdBlDJkK3cKn^TWPG??S`~fwUZ!iVY4@x?f;A7 z2K-qOmd2|n- zj*5<$cUoR?FF}NTg}syFpFtA^yi|d9Z-nBzW#3R#bUEm262p6KNe*{>QsO4~pe)b| zj?T%E5`0Lzy6GOdiG`4LU`peWMjw)FsQk);;KLruqkx1^#-!g+a< zm|wxGL@?+irIS#yg)A`TqwITC3@+tp$O$TtD1!4@4DqQ+r3$}!sh1aW9n3VLPGmJG z;wNc3kzCN`6joK4YVO0nDEicSIM%mI;yz~M`>0=@s13uCVw9$u=>j#?lK53&S6Mqq z6i7knmYz2{T6em6=G*|jSG6QSe9sr`P2sqduwnXJ)I5h9q>qBSE7O+=!*c$%ER%9> zxv)sGsi^KYXS>VY2sR|eq+)BVMevytZiYajWTW)Fr;p7&pDkNXTiK|DH zdn)qKO4nDtt{lZ&%bi0xPViOVCh7I0l|EW;COyw!r`)@mMjCNSXD#giXDLz}!ro4r z$F2xqXdboV;q0xAIIT2yC4JF0-`&=&IyB-Ms3CTZv$e6CJPc`{qINx)E80}wfl;M? zE1o#o_2p15oM~yhyQ_^`vWhf~tUHtS&NBN7SL;&F;qGefEK@0@a(XX|Lb;I|F5RBn z5gd7)xcOp9+lu*{c|^=8(lsIPjb`o*sBaTnyC={#@f)ZKRq&9C_!O7ALoI9^NgK+M zz76$ulQu;uJ`F|6RggP2^=js^kfbVkVnqi?+Rg3A+u4_8_F821#BML|nykXol3mwY z(%vTrt;KQ3;9gEjUgceZl6R=KQ*4{4^0%lG?-Y;8H5* zmdbiH*;52P?_lPKY6W3vNr~39?X^4PuM$qnUyQ-gezk}>deiQ<)hkGF?{LCyTlFq@ zXwq(*zEkZWCEI|m82PLuS5fL+uvvnkbT>(RZ;W2ZyiYc5@J*_PCi)0e_EtGX3Y;o? ziJ42u&rqLc*QbA|BuX$zZStbi`#!RgI?Rr^!;#o+Og$zmxaO94Qc4Rj zIthM0SkpeINC{k3z({DJdw_B*%nIMPj(8Naiz_ERicv zS)}4KSJE>a>cCPq{!e+1wCa@Apn96R%Ep9Yv0aqVXI5!2g=$EgVs)TpR6HTJ^@8|~ zkVX+So9{r_%gs#wl7nTiD6(==Z!R)Q9^Ko2xIQSK#LwyJp@^aaWk zX~f8{e5@d1u%MjSNw<%xpHL4=^b>u(m;i5-cM|Q31i1~5~zy;AS{sN}ks zDtO zzH=#vtU6m%LTxGOK#9F|l?;eoB3;@HoD({`nn5`MwoH6&0x*-VQ9q6ymr@&*Xh_N> zxHlcua5Fttx;oa)aT_I zH@bqV{%Le6k1BEX2ZCW<c{u4V%{X^5!&E6`r%6L%6FtYGKI)1zia0s)S11FQ z{-wM@4!orrJ3UglgE3B;l_<=o>NcIL1d@Eyxk_X&0)P`#W%%nvwx6zZJWq0DXt=RwXj@)QA*-%Q`u?vc|wz}R5)LpZz&37iO(Il0$ zLposSZ92Q9#uSpJrQ($Bg)_Pn@Ig&gb+x1&Ao%naoos8PP~_Xg3VMr9y+tRmDHT9! zYh6SAXn@pB9-lu3l+I`n!HTeNJ+G3WsoggP6JV4Jc3^wXn(BH~ADe4cqtiQj%WNPc z2@0kM% zD>%fe8voA2Av?P5i3G-*nLikAGGY&XK=}3ZTqxGVhocjf8T9^&YL}BN45Zzt-d|B@ zH?1byD$dI*1Q5tZX$HF}xc;XnBgm~UNpju25$$+TR8^DjIH1r1Wy(-3* zB&FT}9(#>kJm4aNe`ZmFHXD!~93@)&J%Rq1r=RE0yMm!=s|Y`#B#IU88th4GRO8=y zwj?{=uW6}nsIQX3Q|b1sDxL@p1Oq{Tuphly216cSm`sp}imnriFY4+Wsw}!QV}vje z_a}N+XPGiPkGL4u{R(@(HT0W8$rR2_iroq_L5bbU`?PMto&kVme*h-u5HM+wM*-NZ z!5|W`!`Pkp;ouCzi4hF=Mc2jG5X`k&t7_#ic^Xe!OUh^_s+59|s;QmG(b=>buEi4y z@hn4too1XUrf4Y)`K{~!H{}om{;&V&6woQ4Q$VMHPJtXK5WjxzB%u{&cl=IQ{PM1C zL@swPaIbc@xj1yBw`A+XsbiJr)O}Owm{?n7Mej{3OC3@=C9-URl!l48N^GwvY{y{1b?2aCoy zSwS%V?&LihVdU+K|Mn7Skb);75&_sdX|SYnr-A}?oPr`FVUo*e10bwWo=zpUD#$_l zpk})^x%Rmtb(TlYVVlO$(+rM+M6Di=z6|tMrRlg#Z+{_cUJ-15mDAo{rbe7smQlIh z{sK~O;sW$###;lTc?S?J7@)aBg=3KfE1S$9YFIVGEy>0ls;XH9ixKKqM}iZ8c6B7U z3QiF<3O3Pb_JfF9l!8oEbR;;y33YU}lzD?l7>{`QvNrchXL}j5oU2s!A;Ezu zm?GhrpGW(T@(Ru@bdgFqHRy?~19c_*u2d`$^iiObveShV5Wc8bw_5-+;amhDsX`6| zeoiW~rGiz)PD)lMd$f$6Gw#57czik6vXpSg20c;lfG^O`F%X^zZQ6l}0EjXClcB`m zntnb2lw;t*3Mepy?1WY%5hPlom?sHaCJi`OI&L?)T07lbsR~p^0pjBNeBnrxx|9Hn zOSH_J6k%P#=}2&u+&3HvPDg%0g6$%2gXQ-mm!p#K+iwab&9F9orTqWcz zdIZ}%(mwobNbcL(w#8^?_u>ap%M}kYXkfh1f zk>GezM>-N57PzQ3btJeX=(<$X#8ImD#o0%vy7=oHABgx( z#17(CR0)ZmUqi+zQpKz4>J+>SsrsbV-r{6M`>lL1;EAF@Z(t}Cj0OWf&qPf+HP=)% zpwPcwHU6C^2Rdy+X(R1@ftp112q>URmnWS*=Tw|ed7&<41YTkDq|7N-f$aW2qnCluE>S{)@C17bGxz0xcGl1Rp1;a?k zqi!F{EfWh)si~SA2udS=MLOOg4T52%A^tTxQ zujp3>{9pgkDWFq8r+`iYodP-qbPDJc*cm7gKXd8i8J67}^78n1X2q|uZyU;{xzF5} zGs-54SahJx+<-R4nj8gRQpC^{a;rxGZ?a4}DU0eG1=olzF{@6>LJ4aM1+t?z?i|pX z71d%msagso$S&k+(PKznH1ZQ6WDW1YH)?X_#H~@WI5p5H`}B_yyiTS>I z_+>1<$bkRrKRN|;3g{HjDWFq8r+`iYodP-qbPDJc&?%r(K&QYCpg@mtx@Gr$;Q7rf zp#Sx%Yw=nALyN~3a5(JF?)DCQD9l;vxGHm%m8(9)+Hi=~!nL(bd2zQhr(9rrj%>tBeye__!9g@uzR7qTB?UZDmzz=As#+`i!U`Wkax z1y^5Vtuj{KkZ)WZ{PjlTU_R))Tk%;$ZENwD#h(>_Sp07Bo5imbKUe%@@uS6?i|;MI zqxjFoe=NSH_&3Fu6kkyMQ{m#Tb9&+>yG^CHpHvXHPR`TtzQ+0kZJ++6@Qfqhyd1E) z0#bK3x~T~LR=D0+n3}KjdWJYN7aa%(WBmi1)l3H>T$uNx9DF^`q0Ndf(93T;glmaK zyndeBzzpdalr`_VNsIuMNbA{A(8Ay%nXaA99RHCNo=iwpIC&i*^zuZB zo|g~vJz>x&h{zjkmO|*7o1Ox$d=i9u0+qULL%~cL%-R zSUAiFK-X>Mwax+XX^4-}kUd^7qA%*>F^j#vFiO<9vS5I(3m3Ss29xe*%shwzg0K5x0X!Fo;UEPjpWqKLP;_MCJ>CJ%Qo{}U0q{tHf^K6VsLR@-HcZ5*=(cEQS>AayBCUOXz%~! zU1-=6cTW?CI$WfQQS5WyU7TM%ZQ*;<-^)VaoD^IW6dj3mru%@hh9x1QV9-BBDM89J z62S_KVx37s#9;W}*;oD1@Ob>-sS@!QX(oRDdFPySNzZXv-bf3}NVLBQY*J-nBb{6j zZ_z~iJl_q4iwn2xKJS#@-I!&lIT63bw-F>4MG&b|2xf`14MqMp7taKFk13KErd!jX zOIu!FY`S6AzgPTdg)B11q^2NZneHcj6AAIWcc4OEm=)amAmj^72|@J0ih$ItAhEds zyyJ<%A`6CxI5v_j`p|(Lo8tuCKzIF57KX3d8Ev<(OXys0k2C7ZH+rKks@vard zEBC$Wq45}K8|<8**B6XMxKONz4@Y6E{cp{EOEA*%d}*YMG%W%)($ClK_q$&#$uiO$ z1$>6FIw0Zqnkcch7cit_&BNp%W!+B>;`uO?e!Lq)a8ON4sAK1#r!oduJ+H~c-AN&M7I^QS} zce#dn$F90+(-i!+3?Gf(=HVmpd%>^^zvmBE<9Fq734T`$Q`ml9d;@;ZjrZbrdAuFJ z%i=ctJ~%!PzvsmF!0&_NdH8)`5Wh;-;8)4v_;o-7esOc~YyaKoLAx1#YAANkxN!PM z(^pQrdYWQQ%i_IW>bfDcsCT_D;)|+1wBzDdIfPpV+j*n0vhKf4&-7vlOHk%GdX zfXMIV;j9l~=pkeVM0suiEraxz!mmD3zB5%=4^zZ|o^*+3+`UOV> zqbrCYd}TCPNq-lLi791OatvFH?MKSFvPK>=%rY^MW^vlqfBw?=`EXyUNP?5^QJsNPSrtV*u?xE;tygFfo;J7gIJuzvlU#A2ZojqQv!>~ z%(o8A9s%D_xr?LqqVpa=1z0U!-P)SnM7N4;-YQl*U28SF>G%K)TGDCUn~^SQwTL57#3nSs*7M8zl=#10FYOCe;!P$myT4k5(U zxw2*mCQN_G18Kqghlm)SULoy^0@z+S?UB2L=cRlpn8r8g>4&%}U&w^2pO5mG`nSLO zoALAbLQa}`Z86t8$14jG?@s>DEk}GA8;kyOBb$iC0ZZ8t*4YqO$t~prP+We_>n9fI zBi)5d424h6JN*_!d6w@l&iwFnO*3D9%F(uW&%fs#*I%9(%ix>9t(XzamoFe_m|jp? z(im(~yaPUeQUDyR00vR#rCi#u+v#|x((j1x6t`%4C;MH;Cf>i~L1W*4ew^W*a%AG9 zDS`L_2M-yHhG!qTa8hY}pZ(Y<^9tezYw|-K)0Nm-Sh8vBwa<=6(5i7L5*h%HdyLON z_Y4A5p?xL3&=yoHbw6d>TH$1>JFhcu14v~6$O2e&`=$Q8TC#%ZF`aXmD4rOMPy zF;+f!6~;=uc5jLPU7Fs+=(>2=_Oan_Th5vC$(VKM5uA92w+cOKh=ak^o%txS@$e<7K-gO4U7ct9K5Ja{;q*3}bafR*hV{ISb z$z9MHsmd}+O~jQ_uxQ+=Id7jI`t;$T^OD`AHwbEagVMIWY@e7O{bT#jS7%$HIT1ST z`W$6(zW68OXSDyHWmsY;UNhr|(@&Z9`m_V59&S3ZXmip21z#34O}RM#^~qmN?wxdV z-r9s>g*44rWiN_NWHK$5+i?kB#H^>)A*r z7o2o}rU}ct`l&;0FHSl4{wMc&Z(N4zR5s~gGa-)-nfWm??U8CCZk5aUe(Yw#*yCW2 zBy74v1HnMhAMA(Khcs?VJde;OIu!P7^m&jiJ`}e_w9G0ZG7Zs8#dlSSngm^um^R);b)iJ`78wGUNzEC zwI#z%J@Faa%b~wqcjSLR9JBrhXN7`&L4U(ALH4}Ud2JU*fc-0XcyF9U~HS)_q#OHnQg zJz#q!a{CXj87LZ`fjZzhiAi?X0PjJ)mf%t$8|=WB&YFeVrvuF5pf^=&;tHyLdgaGY zhYvpGq-?yu?M_6+7q`QTpWcH1uWW%du?scPXz_uY6R$k+w@>Chc+6N9P8(J^`R=iH zD6JOm5t-r!!@tk|Gr|?|s`+fF$m?CDX}4l~<;kYr^Uk>Y>k2)!4S0}>jJ40cpybNc z?@YQd?y6*ihGaTc6MZ3>YHY7OyZ)8MHMe9SaBjTU!HO6vo2B9+P<((atRXHJm)oTk zlMalps1SK&cu!65QrfmU@#;-){r1KmeI(CrhImnq_76;9ir&y#@}k5u;Das^)H5pG z!~%{oM@uKH#7GFfW0ZedE30AL^uzvR6@ae?VQL&YPg(@)G;tO;cZ=<{>2E*s#6hoQ zd$F9#5wZL zc1!U&5ggW8#$JDN!LYVs>O9BGy|&k0?%Z{9{+0}4Ajj8chRGamNf5CK7$_b5psy0~ z&-D}l?c5OLmLNn?) zEW7R8mE&U;bohBbgneRw_sqO5Z>nJni~k!VhT_^83#SLCZJt_X`dQJ3Mdrdl!Gi_m zQ@Zl6$)7TL(WKwz?ShN+pYfnTeD>iISCneviu}7K*`B`h)k|LQ`&kyQ$Vr-Z@V;Xp zD)$)rJMT@u&qs)=i+ z{I=ir%)wvWcIMsF$0i}0JbwS|{M(O(d}~@JjcJ1>t!2Arv+bFMdDh#$G-cuB9Hlog z3ldWxn$`YWlzVLus`@FYtg7HL82)w3r>~OBd?|Yo#O&%c5x4ZC#4~H^*I!dMJsZ>K zv?SfJsF(mEg7|)o z5{t~!#3BgmK3jOwq$^H8YJ6B-9oQu&TZuzr4sE-6qwDJLCZ7GH=mjh%65$}EV-youG2^%DkeFilVk#-W#zIO9fKl$`O*%n%E+GU%z5@v|u zL+uiowPiJ84HhPzpK)C4nk)9oum*GDYq#KOMGTW!tQ{hz$7nvaxgYhwO;EB}ngVU$ zN|WEJPCUQM%)8c996vscjQBr5Ng%I*+QQG8H}zvM^WHXTkOyeulU+}{*Y>>ipy7YN zv|x<9+~+4O9!~^_JB`B!A7Z><)9#0gW7C`#`u)W7%YPL;KCdhr59h=Qx`Fi4&sRV@ zmMoH1rd9!XQLF9wUw6Oy<}bXNnI<=}(hleXzu&{H_joBEHv$28`cImT7sXxQl^FYY zO`6gE*4v5aum8Ur%~yUki1h-yo#pgyYbvorj8TbR6QK&9g`hSJ_^{LUV)R&&eSJX9 zLJA%^hCDIAf?*fjYcLvPPdt)$M||T#@m+>jYkHT`wgtB5|C2ZE7km4&ErcA+KfzTe zpl8XC%%wDx;1QH0wK6%{hbWd0X^4IwrB?P+{_TeiXTx8y^blzpwI$~O>B;uO?2F&o zxb?jZN+8EKris*y^)zo%sgVDFlVQd=)9;&JIIVhW#PqPKyl73~4TZZDteJAhl>Eu- zCY_b{VqTTurt#SPj?*)SH?9#E`$?-b@m}8Gn_adi{_}%nMTti|dq zvTPz2f)jGw;fxH#038VDE5gn28#T7t$!FBwYKOG!sbn8iL1^i!jd0bXd;w()EUxVh`&g3m$e1)mn3bD62v8$q6rLykfO9*rl2VJLTS=+x}kJyZKq$_s3_j2=J1_ zdXV_VhlF(?ru0DK&JKQdEQb5N?}@`5-d_`2m$tlT`}^NYI?w8;lI3595JFD2MY0ok z19tww4U{*B1QQ7YObRR_rNS@cf-zq1Loz=uhR}Sg{|@74oAz8KzTC-sYkRr2hQyP{ zo!5O}_`qy0m*d*);Qf7-$U^5A3{YK^sz^poSQe6RC@+v9F;Y5MuZL2a{Wt^O+&6m^QQ?|P+$!B0*A<lC;0Zgs>uscE9?&HX*`i5c8p}Fu|G4eUUwp@sEqOG@ zHR?bl5-O3UxlBI!RN5WR>6FH(EiI&IQCH$0@2_dUb5^!&i=4;`kq&CcU>s9HNnmX( zJapimKc!@QSBJ!&+M?S=*DnVWPwlhk3pX8bX=e7!nUIu12uB?s4*Gf_bWlA6fx!~i zq{1Q8*$_A|74o5;2A9*$+>d7&V@{0GnZgybm%e)pxZ=<@i7Pa_ zkSc9YUFMlyc3eRQahoFv+XjXrERd<>IH*!m-LO>oO635#cZc60_pV!f0nP5+^Pf*V z^=aYZ4}UTWTj#dZ?idQ70Ebo{t3E}}2KL)wB*u!l{iI2unI#67_}k;}M_M{#@1gqF zurWWRRbm~lCf3QDz18+~)r&Ky8E($PIyugbfI1FTO<%u4-Q!ZEN42h5c4$%dmi}FN^W-< zD?$ulZ#Bq@oGFKzp@U<4=jjpu*~cMGPXMMD!B8m~rT9O1qAQA~6fQ5g zaLOxFEcwSxerWP;d6yVJHC7qC8u#w;=AbyG!v&g_7B_QW+hgzl_)GpKv zN3f7+%T2XEa?Y(-4@!g%d$5gUWP^aA3cur<669xG9FQbMP}A#7-K9D4xar}e&R#i` z?R9b#73~-)?F)zzlS=akP#i6%2Cw(=8|Kwr^$)nO$_AxZ(3aJ>=-j_29^d1-H&1H) zdj{u8PU2z-OW9(+M&^N3I~ffhoOaCpd3OvS?U&?1i>9r~!Earfc)ae@is{$9DQud? z&}P9&nU}Q71*R$z=>(6B++P6})xc11IOrcbhVNZK2nm4EhTBIh)h2Tag#+FJRKn8U zFUn)Xr}F9ad8iN#o6(*iJqDgue8i@c9wa;h|DdnO;XzbAi#|$tY=Dvr%Jt$jWEC8? zgrp!ewE)fo1cfkWVKdG};hBa<)NICAq1XUYgU^-@j4 zw*7jo?eViO_`iz|JR^$=&&@Ph;zJD%+J^Z02cnSk0unEP__5de#hlj&irgpBSsQOd zZJTY6|MZ0u-u?OA8R(qbNRukZ6kCwt#1O|~TTaH`S(hWrb^d@fXl)AG=(_aU#FkmV zI_-B;{+fADlau_gx42r}OPg0Y+#Rk)1nd#dUt)K3I@?`B%=xmR!>|dj?U&d>n{%VI zwZOLJ$Ybw*ePbe)}=?8f%|4aBa(Q z(e-=VwygIAPW$Qh!BrR3S5qspWUjw7b#CH~OJ)KqP~usF(l@4zk*pFH-_2$QR;RX*uF>`Dm9{7D`sWKpi$BgRgL9k|vL%)_+q)d@7CYD0 z?rgKS+dEzE)(%)1ja-Y%-RW{rTHEiQkH9xtf8^MPp69#wE!z|S+WN2A$L*IzJm)0% z8mZS1dAXk51!D0&iuw~IC5$_`P+{y6P9b#yr5IT+(sY6$AZ!R9 zGkRLNG6|VZiTG0@*_%BDP5(G^DBzpEckuqInRkBUBDIe923O* zk#T|77d{XkC)v>J8K<6mz#okl3|Ad1&Azr#m%1NLJla~e$ImYMG@B8Xn`B7dnnt|9 zkz{alY%*194|X%~=&;d7b_6o306+@hK@oR_-dX}R0Y8ui6&?im4>scwKz>ll2VIzi z5h4KAF&IfgG^9O)kjz1QWK8weTL?wp(D;(JqcEb~}3CuBRlrBx9G`h|=+8!(0W2NQYA7`e2 zP7>JO>>`TW+gEX&?QmoYdktr&eTlo-)zJdN#-1OBp|2}{2 zq6~!1QRz0z5Y3|&p72x*?TWBb!+zK3g}C=b+hZ%QsQclm36-2u%fB24?Ldw#LkOlg zY;2Gv&_RR~-~+&E$fqSb4G?KjR7o)4hv$CNn?Lyna$xrlOYfo$P+(Vk(Dqo|`-$V_ zi!<{~j<3@LCsPO*RLJb22$7@zLMse3|Iy+IPjYBt>jUmuYJ2RQ{m*e-q}B==sNF>s znSN|TK}{q?!)zke<6XyM*`6LCWx=~9WKL5wmZW4jhIEYzuHPR+x)P#Q188gB?+>zr z>v+=Lc$S}!tqXdGSeOhhu`q(}n8aoHPl&BCy-sc-FVM9C$sL8J2h=n@KS1+LNtcwL zJ&5ob8G`UG!Lx0oxtc06+(S zrxoM!!p_8FuYI@p+1I}_W{z;dHA}ny5qU4by#hHXC<$5e!@F)ZGW`GDjI#{Ii)QRO z-81c{Q|~f;W$G!qzVM5}?-pD<<$?TX^DUE4ob>dhz4H9Vn{eSb|0mAHWoSxmunkjg zem`;lb2rY|0QGAvtK;zo7^h#TINjy+-z>%x!zRb4A$x+ZF9E-Ndke0~W4Til!NneCvY^*jbjuXG$-itwsK{aj7rK~G!B0ed}_WyCo$h8@vls8|{ku{hFUtq!HjHrpP2 z`8yZ?>66_vFSlF=*^5#_mYK<_8%F3RY_3VhN$JUC9te>y`ex{RexuhvA37TsDM_B> zJiFqNtKp>xDc5ZFfj8JTxBPSM1LwRj<_Yu+0)x`)gAxk# z?SB+(hj>GLnl!vMnvBfSmR~0}56pjgX8y_JBw`xX_?NUG!h9dYKa(Iy$@A7qlBYI4 zRT}(iO|+eIb+2vnb-gDoy?mUsJj=#T7py_rug{Mi%k1eVM!*&G;wEY28j}I8zB94; z&v|dheg7CAR&PY5FrgEJwR;FyZUB65FnsXF8(T2kz2imFaJ3cl7dL!i+x#!%yYUFO#DdZle0$N^F0T6e{G!@MsSLV+Tp$@L5Bh{_2v;;65mcPZrn3CT-3; z0*C!;!(;ChJ<&W)qP>%4VGG6mI{`ZEo6sS)_#|m=wMmb>Th`hWfBo^i?xSYZk8SPA zK2GwDA;N(qVYs%0#9{bTRA>}}`_pvgf?cNy_5ZsYUNg*iYld_B8PlGdcIeczO^=vn z7OgG383+I^=m79u{-Vi0pY-yiec%DO8<$SFpZNFU5`gKjHpD${?h}doiZ)FxeDJ|> zV&-JrA+IL{yUGJ}Zvn-WB{(5?BtY=@L7P&F_W;iWhXZ>N!GTtO{1vTxf(hru1$0xm z0yJ(&8rnN-_i?8e{^9-O$Hs&R%>*gBN(OF+y}hH-5nNGOWhlJvpXaZJT%Q-0P)_68 z43{OA2NL)7*iISxa#|L{B`0&h?ibSKdx9HyrFI=j96wo))R_2^xP(d?AJn8-jjqf6 ziTi>Vyz6_XC$r?vX{Zi&*V1KNM^_tikvck{l!QQx^N2Q}mATvLKm{W8ElYrG#`2UP z$*)>^4D?TRTmT^jduR*b!9KVnao_sG>VNX0I|F;<^a|~0nEH@Bm-n~Tp8 zHBfw>ChFVQd}_PzxT_p&k;*1lSqPdn_2xX>aLwAIGYNZDAG zxbM{aYo9#9nwdj#9N$Q2I|0d#GI>tScVNR-5f@r#j$yo)u20-|`?0p)ygYBr1QW&9TdsT%Q=x5=zFLJMaBtNRAGR)kXlx2YfvZsA8@dA6un3zM=_u_S`+owZu_!JyU&Pq8 z;iw}l@WFD~_G)TmJoe(Scx{oh^vM)cgjP)?hdPh0Pp{~4d*5;bML;Xx52u$6;|euJN32*C&S*~-WyjC}vN+$phKlW4QQzh~n9Yxh3v z`y1XIpOLr21<6b`qw#dp)o6CJTU_djxM`NAAs%q=M%(>2zI_f*X{iFw`4_3d& zk1`gdA_&nAbbmu=kD?tY7)HVZGCGhq!3w3}FGJ!YT32x%=$jtuU&ith0T&?3Ly|43 zq!te8#fX&j#|6Mx=%jU;I1l>t@WefHj_BX~`jZ*-X^!H@KG;JkpFwQGWX0s5 z!njdF_w)X>54P@$<1)IcwsxI(iNeG^l}qn_^~fv7_7a+qkG*(@insv2iiyys4gkkg zkhtg2KQ4Rj!xu9fU^(%R+O14U_R0B^=T(1%EbH>P1hBeETg6LhTZQeO6(|1nozTtW zq*p!5T@0JR|$r zl2^7H?@HV=xOT&=z5Q84ZI0w@hJOxtETf{=J^ybqp;p9&9;66{?XGF;$mVTJ+!H?U zu))o%$4qCXhNjKghFB5T)$9N;K2;DdwRd#3yDQz!4tLu!q+KI*y4~5-+;xOhe#Mn7 zb+1Ii-1FQkS6q7D1LH&D6+rtEs*^-SsM(Q=W`V@}#)W>K zSfK}NN?ph+{gdt9yc?{653?lz4|%1 z4!!)dYrhOXZXt^0yobsNy-2{6&YolrpJYbnv)7n&ex<%4!HA6+r8%= zKlA1%e=)Wlqv$mtfH*%aB%sG2T*VM8gnyzek)T|?ewZB$BLaEe0#NfITyo7UKxa^~ zK}q1kon`fAa}E5JA`%z%7F8Npb7hVt4ivx18Su<^rF>HN2jEsNaUkCu_j6_SHRh@s z%I=_l)UliJFLQM{2SYc2D zjEpX^n~|%1rG1rTT~J8q3g2ww1+4zxIMYzPV#XQMU!1;R+9^|iWO~=Mpzx;!?-sbH zoRz<7^6w_^I;l7BEaPTe_AU5hu$wID$9+*hZ>)M0|&zWr%mcDp(ws(QmZ@6XxGG%XZiIE?n}9pWik<;}%RrDxx9B zF>6&$_0%eqemEWg(E||8J^HSmig##_OW3%>(>2Llqw7~gw!4n{;`IX$9{WD2V^v3| zvjukhGIx86n`$1_NFWuQ^9V=te6GWJgm?jJA<4D}$U;{$eqoLM;%*ANmd6FaT-a;c zHuCc)FR|Tq{CRz!Za!;lngkhoUi6Vi>v+MN$ zow)0o>pr;Uhu<5cBum;x&qN5VEG_`uf{@xQG+Wn(#9jCN<(;Sh_2aP-68MA6556E2 zgl!IbN+st8!{_|702&&$#3dx#P1^GQs7U^<7hhcX=H9MxG0_loLgbe<2_XNyG+-ge zNOZ$c!8%UvkQ#Zkh(P>K=#2_Jsxf|tggV<@@AVAN-1NT;JBOT@mLVI^x_d4HhYqKq#97(A+ zS$pX`Y6agA7doYag|s;b5eF!@-Tk9omY-<9C&L2GbxKBp*Gn5Eho8EpNO3R94u~9% z4gP-dCTVeMgL$-H^vA^Azr6qQMEKeaW6zEEn?x{PFeeJ22hqQvd3F?71nc8MnDv<_!8RC+Rl=r;p06`e8QoqP#}#ZRHyrj0J+AG>}q6NF1T_ z3^k5`7EzICArc*cp%`UOXXXC>CD#$*;*(SQeK%u4=QG?4cJDcmJ>J&d-lIHOr*29gPvGNxcvg-+wr@P&Y(YXTsbnviYmz^HF?F;D}XRURY>3~&}?eUzr3GU=@@2G`gm*x?g`hIZ%Ko?^!)g;*%RQ4S= zx+~6W*)Ya2k3h=41p{0V7jR^;Oj|VhFaDps?|^TrYX48tJ(7TIWP~Lxv~*FRAY4KR z1Uf<&AWBKw&<4_`G!0M`;oh=Erics$8G@jofC!>q1Qc=a*M$T3eBA?4{@-(NR!c*# z=LYomf7{RdUdg)WInO=gInVfhgV*29KmPQ6Uw^*6DWWP#iXGv~@^a@elbu@J`Z_$_ z`4MbcC@hI*=nK~tjDS1YHURF*TDQ+}mdsmg{#u@TQ!N;Y~ z9GRXc0^rv6KL@4vWnFx%EJHaeHcnvp_ABqrDC#P+2QKs*F`v#0XMm()FMOR;{SIZ_ z9Hw*%Ck{Ej)7{4Z*216S29y-6U}Q{+Ob^b~6)%)iiEHO2oOB^U3$K4zbqXFuH?Jw3 zM-ib)p6@dG%QZv)^wSBs@mM90Na{4Ki`8@iU4?SDV}COItpiWJyE)KX+kaoPmkx$W zjHv0Y#SQ>%AoQ6$>cw zfKx!cE((d9!aLZzqRDTQ1|m|HT29DW$j5^c`zIyi4N_N+h!ap6@mBHxyfsq9$0Dy& zNP9*CFJEq|K}Y~WJL@zPp+du_{o@DX+eRCpDC^#OKLlH2Z&svY#Uf5;o}v_C1%IqZ zaP~UeC2A)mj(0xvAv<8a{_qZ{gSkwRzd9`UI1gZ%W;T} zl@yTyq}E)D$p+$V5E?~gnaTkaR~y|me$Ojd@_VsvVpF;*S*G>p{IPxAgSg>-g8N8C z{Z%okZK?vN6RH4yvFsry_G(sSH$@5?UEDx*hv6K5?8Vi)v>BPcbuDoW>6<|9X^6)o zq#^Vp=)r`<+1t7sEg(f9Gz-2ilaR{~@0Q5U;iBadf9FitCwUTf zGgjv^f`}+!^n=wAOdl@Dqv^o;5(q9_DIWqL2)Z@JEYJ*+k^zyao%{sAltj~28194w z9UxGql5+Rtz&C1avk2m^OBO;A1~&Js=uyhwMErlFLSs|!i2P4vT*Tb)r^Ba(oeB#J z%?!CW_{ZRDg60K29Pq1ZuJUDtv1tbYH-{DRYy(2()0tM?p6bgVIX7wGsDxEbJx59F z#ibr&lrw3@PT-i3&xpht_|XU{8(068p}TtHu={@=j8(076MC+(EFKmvjt!;SBHru zzVt2?a@=cJ7yRygy5uln-apd$SK9z%U$-{oWU^gqy!`($?K3b+u4P3GVVsib0ELc0`fpfEsd1TAW zCt7z&_q|I>T47S_BiCV(H=CyUqSY?8Apb?A)qXY+c1d?ugc3$aVyQYJpkyEIwrsa@ zk*}bXq+Ylq-r|}i8ny%^`xS(>4Xg`Jm=e6X_ooZ^ql0bQ%HrAb%TjY@)skQoraNf5 z$a3faa<349!jTFoj&RM3ji7wQ45y$7m5HL&cWXaF(P|yQi8>sS|0E=!47{M0@C zqp5FQ<@m6Re^j3jBeWKFD@c?(&ip-9@#=N#wD5#BSEr~#`UZy8%Qq6}s;y6O(lP2PdTr#xyc!b<1ZSUHtLaYbRg8(rT{#51@}pMv;d0es`>{d}5PR+9XCEWcxhLzU zf2B(yQdth@_(z}aT6fo;4F3$Y3}g`zOL+>;qKYu=+#+>b2uab7w5yS#6wN+MCW&(ix%SlzI1w7a_W*nX1kP+(78{~C~hD7+z4|}{C z&;vykB0a2(qKZG6$EL4Kntx`44_~mfv7TCwZLwip#Q%mXI8=3vod4S@9#d$uG! zR~sUCMEn#{7``U#<*=(lOGEgOtAZ^-#{>TiOb%G4dQa6wIR#(-xBRiaI5$3W3f-cA z>QCn2;Uc#4t?ipwj3mwU3^?=v>VY|3Im2NE^I~PkImsOn;p@-2k&@}$Tg-DO_=Cq^ z*goV)>m~%1=-Bg_+5$TX$>6^ae47|W*K%%<8 zhl48wD^P{G0zTDBbBEJsNrb%*7w)nO=;D$?*SyX@H0ZZp&#yQ5SZ*Zh+gbO7tpKA# zD6?`GQUNJ>$?Q`I%k<(zq+~4fNmR?c(y%L&d1(9FaXq(Y$=tMZ$l8tty~ZNq)Htnx zBU1^KRT}3;O{P12>-lH-hrakTZs7b^|{!44y9p3AO2AIv->(1KjmBD5{F2kA)heX z>-0huNm>ko@1(xO8`Y7JaRGc-mp?8C#sQa1VlzEBH(D|sR4)#O+592(%3;w1E`gRT zu@kxVux8DIr*HKbh}nx1agcH9rd+EZnZO*H+4{+~=X3nC1m;(p93)?|1ZLP&M|Ew- zkze14H6xmHK_63A>kNIa7k}u@SD$qpyW2m40`a6$HxsKyDOu^Jz@LFcL-X{B5VjlV zMj)m`$wVY~C4cCn_iEm;u9ZE4^;j-oh!cOfNEf;2K7w7)hjUZ+(yhY7dp^TFoN{XX zo@*A$vtEsO+$2Z>;6t(qV}*13j1`J=D-I{&NH&BMxqC5CO;oEebK*_@;Ul~HnokBc z@%$wj6(ek=V2m(VV1WZqJivNT!3%#zLjXs`?`;BClVl>!&DKkYK{<5)H2!cTzj5G+ zHBE>w&C+hTV+-ljWFVI!r5t2i=nJAPCCEB_zkI*xVI&eA3H>SM3tkL>;GkDEK(?jNDW z&_ZJ(|Fu2}1uf+K-$qfQ(57p4YJwxDMH~+QBYa3$UFh-9Rw1Rq`-9#NN(^Md|JPkL zP5G>{1-|-s|6owLQIY9tP~hVX;`dqFjQu(78{bny+RPQ%?g6@#KT4mOlRS$cgyJSR3g|u#^TMIqZ9_)8ON;^pY#AbSQ^T5WN#8A|hjzq=W^!u!ngdfJ_Gqgl#9Be<1Vy+&5Q8$W^U092kP_k;I93$WSX~ zWZ}RE%ma6nJ(g$xstL79T-}?D7ZMM6PfmnFhS+o!b*k`Z_y_LkGil(IF-?dqX`|QI zpz@)_t41y4T(7gRS7SLh;&D2aF#=Ku=D`;03}ANF;1YrC7jS+L;HxY^PrF zSSR1pO`=jS3Taj#mk9?NlI2uo116gYJOqwkAz5%7r%JRzn$APApoYX2%5DSMwsd<(nyrN*d4Q{5soCuE$iRsS2 z*i&xe_nQpSlX{Mm{Uk)sUZu%SQcsZ|VG?r_iKphCJO6-!`*JR*WJ;V=Vk+*hjcxnh zmck|mxU_x5x1|_}ZDlAY()B{Sbg4`zNsr_AZ+q*8>GNLoaa@!*$a6|c94KXj-ygco z7a&h-y|8s*l{Y9$UwRY{hyI+K%a<|-ZqIcanf))ueEM6J%-FR%x(AEV3KHzuuCtkU zQc!rsgzq*&?rS-b&KEljo#F|1-hs+mW?{Q)eCS=$&O*bSHx)@uDV&?)moDRjd2S?s zVDpOP-CSN1<6Kfd46bTpQv8VJL|}nJ3W2z;2&DAD?fyOamp<4w<2X0HFWph2+Y`r` z17D3^dBdiceJp~Kgm%`Yn04^ClTb`l>KH)AMWq_YARSgHd_iXFyB{o9Jzm$2b8-DH zXh*dnA=mRTf8f`k9qBXHy8U)Oay_N3EdTOkPa%=pl>m$-#g$Hsls9KiwXx`CWXK1_XR#1=G!KR79E%f`LEeJq6% z#cBY=c`4A-BUr@A`Tr`#L4yB3PyMF44Hy8P41YeneOP(uj*t&RdIZl6njG+?>bxo& zU;3B))wShZNWGK`NspX9%$~>#Z&o}q)<5fr5vj@`z7u#oFgXEvM+(Y0F0!E*;5v52lhl3Gas)&-dk@H{Z|P_>6oGcWGHA(&`DA zr|V<81C<{pJGKr-jiZ8?q0SpRGm8Y~2o$dAkU~eSZyRU@E6pGUfaLh6kUNWLYAM>n zk8zwpgal0y*4gR`OARs8VXX^6Rx>dkNf*t!VxX{pGb|MANFUCPOq@!!js%_5@q2P& z77jc4T@%w(+V0eN{%6~TonOHon$Ed#is{1GLR!_H@pp0imcQ;}+DhAp+;-RB&#HPY zpeJ@#7jiK*VAl1_p0bG>#~t(p#C&Y564xOkGQ(^Liy(Yafq_y^Li339;GZ)vh{!)H z%=nm8s8*Xj9Kb>fnI}tj18Sn*ttTZmVdlvi4&JIFGkE2wmuvQF4_f_K=Gd!$`Cnu)C0hq$MfS@Te!wL7yp65#Tl${#i z#G)qgQA%W=*$C4_&&3KEP%usC*k@3Ze45$w?MnWMem#9FSkh`N0b9bDHz9RYPNqPY zK%DZkzC4cjAH#|8%CP<{RHJ0rt!?@H#%?@T8vbw-Pe4*fa8eU${yZTdvIJliveW=q z6{`)F6j#C0Pvg2A$6gb|i5$du4z!8hD9gJq@9-|gcUSp-4iX2@r3>={-~>wG`nbl; z7%G*6#^D|2zRk;*{Cf6V+445bfg&TxO<_pJ$@b`05W0qQ5e##5(9NN3$MN@f{pNv? zpHBLgS<(>k5nm|QMdjMyAcU!hdWUEon4iIX3NSR3wb$BBB_-tuEThO1D{Ix z;xnAcJ&egpmw^J;c_P1e_VRNt9q8a&WQmhCH76J3F*(4Z&do8T<;CdI(sQ%)`L5Vy zJy2?NVePwLVJh_G+|hWS%PBY|zn1;0D!=l;PI=pQu0()JWV@YQlIM#5&sJzhX|`%q z>f*=;BZDJ!;T2&ALyv^2LkffM4>}i=8@MCjw}5ojX61*9+5U0@a3!3GMhv6?x)Iv# zi411zZ{5GQq|f(Z8A}?V1!Tj<;%k9r4>$&#z_2=7N~V#6FW54y<{F#?aMEy|*$MAh zwF7Y~fS8H^JTa&(tgw|?LCJ_Wr&z~!g__m3iP2y@? zXd##_&MNhegN;|i$d2PgU}5N(c8goTk>9@P-i*QOFxiV3F^7QYsx576DYG<-G2tGSSn0@^7Lm+HV&J7Vv zx2~zWuG`LU|763srQ@3_nvt}?Wul~0j3^1Ixb_=e~DBliA$E4^EM(qUmu&PPeqXl3QQWDpYGmt z6pKO>=VsZZV-*VH;gB zju9AG>HN;tw>>>zSgH^6LE4eR%NZbA3BY*c2yFZHh|f zTW0618EuXktNk+#1RDxY4JSOkQwQ)lvWRU>ZmL(4TVvwCxG_FOuC+7Y84kOJ$FtCctJ1 z`%@>*&8JJ3mfO+sCUft1gWGpCWJJ6j|>{-4?j!W?&h_Io|}76CL7Jcn@3-c$^3nR=}V~ zVW6;!A!3C$0nTf+$GSfN$lk^`9|DX8!d4>QHFjXif~39W%qF9ayxueeq?%6SrU>Kbul4Q;D8sLAOOAuHWwyR3~{x1 zr8Br86@i;5xJVF9ao66FuU0$CkX_~t%;?Ngs5ZH98L_o}*ZyW8-F4#JP}y|LZ_tZF znY*Tb);;QjXPVgBByQwRo3`zPi-_+ah7-}TvAod+hIUpln+Nk)|(*RS|4IoW7 zRpCM6H`iqy997$|iRUkA7Buu90Um@ib0TguT%mN}#666$%;uM#>)2!QIDb!7PeMox zV3jfZDr_We0B#TXHH8Glgk%r~Rs!%A4#`S0T$rvoNHRN+LLuZ{kP}=53T8>}6Ip*z zoJbbp5*v~z-02y}A$1KntkuBC#X5luC*&9jNf*TGMfZ^5L5dwnnzdlL0^t{I&$)4|>BucfUGC;r!?I#uAolmA>T)Fw23MTy91Pn; z&W%t_=QCjbSjBIN`g8E0uD3NIo5aOH9I8Iz2fu&2k9fN}a3WkamIJ!bW8mlOnJoim zefRJ;H#Z@*#O0u>(rjuKbNfx0`<*!#sx&oO$<90ZEzdo4qV2O2Wt)!mcry+?{t*__ z-kh6jnC{#)yw&f_me;@29bfxI6WWvb%stLRb1fjM0R?m}JLpC{=)s&wFN}SPE{*^L zSH*1keAn`nb=zg@(zi1!MrTaVHl!LdofN!V-Xi7CNt{S5jA2S=o)6<+=eK;JUN~Li})IyTv)Gu?mYe-D7`l)G6zHHv_uV?zhLejwC{fHM?t=oE@`bIKEqgjYIb_V zfb$(YNsdl5C-MeER5~BM(y+cYfA`w-^qQm3`xaH=F!ls;Q^6*YZ!m&iA}1TCwK=~M zM`tvbC1kf?I1Ht-jhRWi_`5fpdtyNA-LjmAquh~0_&34Nt0RaQ1e6A)@QPoToKV2s z*OzlK2~%2#Oyr;Z-7jrA+wP^&O^jWMORSJH=yG|JogHpj5EzVptP>X9uAIo>3*FMC z^bIXO&2MdgFx|9hybs+<90ZwwGD9Yfo1aIa`0weDJ_9#%2shf9+{DeKdi<)T_AUI@ z{?!}aGfZmA;F2h;0dd`G)FW2b#Nu>{Oi7!FFW-QuxvJb&g9tAyE0Q1tv&a-e;$OsF=h9BYtMg|8v0?@GN#Zb2*?CV!||Em0zoc~)XhAXrN&35%)>amfl zB0dZMA-r4Yf{+(OItEt;?F@V;;N5@}<-Lku@U@HnV6}+I(3tjgI1R`zRPrl#mYiO= zY=`V=ug^p_PjT${>O151T>_kbwuCsHNPr0 z_x#bH9+oY29^vt`;d$z!l1JqyMdK;en9A^my@oiL=1ja%xv?&i6Y-ycoEbnhW(kYZ zsvAc3oKXFM*PQSMxRtaGGy`Y8^?oq&D!OuRuxmOx$bc_S@T*J#SM$-oHL)s7TtS@N zs-S<>8oJ*C>)}SO!nw`wzqkp2|&g?pUmi>QRz3ZhqC_ z8EneP8NT}>aikGbunCgDixas|VBL-4rn>AdIwO(EFXh*~X$*=wJG6;OBki$>RyZgb zFl&U06cfxY)6N>$F$tU-nwvHQdYzKF%~&!jc-$J_DwimygpF-J#z`XQ#^}rfkaoDE6CoqwJr&JTYJuh)=gQzBvb~k9s97M z#l)DVRw{|IH&)7^;%nbAUm~|nlI`&~k<=S<+0}N~ zii7HOPJ|!_E(+beR+*ItncD-_KNV)S*1ogI3iDY5u4sf#=Fm0kh8}M5=5~ zCOdbDfIF|mjl8Z)&CNz3~#thm>3Zu06OR4F4s17dDxAC1v< z^%JORV|YLx!DzXb6FIE0YttDmi0@bNxBqSpYtjBb-;YP)Dlj55CnrspS1C1idf#;nBMCX(Igx=HXLY*l7X4u(zkbm>k?Z$-EH6l#VY6CoGh-c99&A>me?I)j zHbh&yac<7(zLdt#&U|LW+=n~)V7=;VRR^VA@#IA=fPelC zwojcK0-J8m0mnOzpO$5fx+8X$Z{15AAcdA1QgW)c@3bf*s#8wc}qkG%Kwq{5B9yClt`7j0u3AyF||D@fG?4!l-k6Q67u2}?eq z&JCJPXUQ{nujl9f{LIYwZ_@lRO0sQmAtG1CLEyqrzF+^$^xKMIvoUD8)w!{=6R6CL z(D%&@*MIS%-|9v+WyecYvzYi;Xo35)`>yT8FVLe-M99WUO*j8kcZ?2UxKk|@6CV29 z_b`yST4Y!qsI#B$K+%tt6b>oEyMGw-BSvc_C&FMOfocquE_Ler`MO?vx4d)5uBLP; zX`RrE!FfBb~Y{ofhH)D50{$J^I$Y+}qyd^($df%1@*o)Mc6-8JEB!aEhqxpBAYpcaSCs^I7E_~p7-b5n@2k~-l^wXY+X2=gIQ!SlgLrlz~z;6p$9a!a` zb;PTV#fy0I$D>#z`g1PEY&MU|&|?02ieI#$^<#&A@lzndGtF>b^sqFz5G{WHV?Zk8 z)^j4!G#0n%R7%#xuHl!e2jACv=a#1SC#mDO)LeWgt1G0!#J~T`gdL%CHf^ z)Dl615)$X^4~>I&tAKOyPjjuPln?Kgk-0Um)uYBIU-mtwrP(T;qy?h@fTAnNOij-f zVwhqLdTdU59-zx{ghOFxZhA&$!FU~t(bM&L>AI+qs7a58`8e|`0tTMKxe1=>xLAW8 zxA4m|7o3eYR`_QMxbX7CY_xW7bRd`z>;xilpkf zM~TJGiB!&*5M!w(M8wuX{K}A;$v=0G_K(UlNy!|!RU;g@vD5d!L?P$@mH~C-{6AH_ zEApktga}9Y&0#0Q+K1MJJQn;{@R*>x0zVH-#8;aCnl;d@0sm`&?OP|}ZNsjjg$Lid z;PE!kta)tN8}hSMGi{Y+u_TWO;dMY0O#uWPPIR!b5v+Tw2h704IuUOh%N!kjdc=Jv zA3ylnBW*rPTkoG`E?1!P_O;G!c+XC@lsY%aHtm+OS4ewoZiD#?YYkSZz+?!>@mPvFbs!Z>c4kR~G>h88h_;l2p0& z15yGPS100WLs;6Rs`Z=rzI*$eEJ;5lTUb5Ri<%;VQOXLZ1nLR}2yKvXld(K6{11pc zq)tT9hR8)!<{yB2Uu5Fb@8oB_)t18qs0C} zkmHl;T!`A#F^8@5`2L@SEEtpffozQ%1e92ydcr5-YXy07Bebro6Vaxjb-D~urD6LA z%%E}8H{YPz>sxGTMz=FS;sJfUFLD!%xu`l3a2hhwW#$Wq-SORjx;9exs&5%3&OG18 zU4r#BpiYFJ#)?dbKE-IB=EtA8dDFh4d;GH^i)l3tkB;tiE#wN1eY_P`WqY04m556h zG(#-@C^PZU^!(@+Cf{0?v?@!8#i!^!fPIwF_>)&*SszsArh_h`Iw^z=G4nS}&5T^z z{b&C?u++v62_#_b#@%QUFcFXJ>N+D1V*jJ7UNTs}s4QF~_G-4VjShWlZT;*)2ZZ zatRaxNfSkqD>(^(!6pa5&}!gJ{_zL&;D$PpG#cLh9+Xe!&g=QI@1FX|dh;d1F49(g z=Yont*BZMMGQ%q~mEgI-a3BT#?ij9aibX&>cYeK|v9|ny^FqWHn;PZ z4)cbWey8=%%n+Ahz_D0O6(}kpNY_r_GOWPX+!NyYU#YEB;D613%^GOdK(hv#HPEbq zW(_oJpjiXW8fex)vj&JsAP_qZnR zvAD-(DJ!~ky>_UwA|)|CAx4L5qHs-$(IpPRKa&R~^dC5IP;&gh7~P=cq(o)n!64;u z+m0agOR4>X^lp&$56vT*VohuHF|}FUKJvN9xsk0S_C#cae-zG!w+g!_EHm_6=?mexbZmIa2YxVuxSUHaaGh zXsuMhcUR$v3q#^$?q5sGFN_J?wv*h-c^TMWVvZNUPIq;bF( z+A(6xrOfB!iu*?=p3mzj-n)lC?#--Hzj6KO$xmc;5N}GgX^D%Puy1{{#Qe#VS05YK zUc4REz9rnQ;nwxvTc?h^YfkdGcK*1r_wM!ItOfllet$o!t$YQL4eY-aGmi!jS(@EO zydBjBCMcl(9%gJU-j8Yz6Yke=8ynwBxMLf-Z4AI)7w=+UDYJgMaAwG~tHe9g?P9pI zq)qI*U;DNH@Q?ScEyc#@HnAoe^DN7yUye(N4t#EM3$Zo2Jq)e+-om~a^FqhYZ%;RA z#TMwcu*O>Obb_zSU)yu&10@=<3A!B&O#N^VekxOojnHjijW^O@{~FLK z;p#^ZSR%!S==QJ18)~?H?LT_{@e|WWj)@SPqT9aEl(2i9`lT+(cKf{1;o{Bcb}w=> zY+h?uCxxAkNE;I-S1_`7%^$h>m4rjjPY4xnMz?oCutv77k9T*x>aO?S%MB54O}BN` zzcq8mhP-V<&wY{^EZ&oD=fXXm2QJ0v+jHmaC=3$sMz?X{ZVmUXZ$E6Av2Opbg@Jy! zulKg~ZO9W3?HZet86dm&!m+Eya zLaMUa>-|y_gaDRek)_n@-D*c=5k4^&0cg|v+a3~fuP;}aLEKm3^~Ma7)q!6@+|2Ok zMY`aSb_}Du73EzZ3UrHY$Tk+_7a8@rdSDNNC(`E!{eASi;SDZ(=VuiWr2V22TRCVC z1bq<g0HDHl10X1g*w_Re^H2k{oRK4B}kLF^7 zLfR!IDd#KePtnwzLVd0QYmR4fdfw{dV_JH?aoHy%62OS};-aZoVw(YmXtI2Htwh2k z^?cG`8T5QW)qTkl8HQ{F!H_S?OV7tcX!QMniylX#U!!<1zKm8=jn!mtWRqyrjB{Fd4YmjSbgS1M zJk@Ti^m-}45*4BRucX={Fs{^p(jrk1HY4oFHJ(v`ZKRhnq+M2)^46g)Md~wApl-+? zmF0aFD9TOGE6B_zqxzDct1eRPCBJ?N1vh>s@L~57!xp%Cgqyo4CAp}?;-#6Ysu>r( zC$d!u2FpdCRW|Z{9LNaRzqz!?VvstD#WNEyu}7P&f9QvegN{G`tO4| z0Mf&O=A}t5Eqis;>s=_GsMA_}$&Vf0ALHqGeVII~M(*KxyWVfTo}$-1yxwi>UKf8{ z@1A)2UiTtnp%HmJZ`b>+uj8%HHNlDTf6=l~ZyJ-+b9gj4|HmLquidAeti7r^{(tFY zi`2r4oKem3|G%5Dl3M6F>o>>$J()a0OzBdA8c*1vIsWg?e-gBIsqhKk9RK%~J0|F^ zIsV@q|3`9NJG%G`NxmX`qIV#rIsQ+PVwaDzKy&>6ZzjJIr8UR@rKAZHO*OJhHpl;6 zdDKKf&GG-{`2YV-{2!^;9ZSjiUmcOF(C*b1Xu~zfG!9LqdWU*M zSp&@)Xx2cp2AVa{tbt|?G;5$)1I-%nse!^2Aw{<(RTT(|RWGV!{HpN#O>@HkV;Lmg zm#PLNweV7b1y31c^{1acmUU+8K(Q&R3Xn@T<%wa`28|ed$g1H`td>OWEoeS3et zPx`2p?yDP$lEwQ|Rev;ce|HC%XC|)idhUyErv73BRJ9+eg^f66JWXuuc-83#nObv_ z*aTJON25(N@cW2$i)D#oGgNgSjW*M;>f?a6Q}&GBR~Zt-hN!AOQVYe1#ofKCfB0j? zsCe;KR5c&C6)HX!3|#iX)~XSPIC(+|^&WKtTkiYvvvK{zTT#_}q!u<(?eWR*XJ-8Q z{OiV8@y1lu9`$a_tlMAxa@w)K$oXHX-KxODO!%qr;;=u%?g)zwJsvtPcHCYz*np zZAgChxZds?hf?0S&GqG7apV5635l@@@rw6D1`0R6#(iU2iQ6Qvio%WKW8(+LCdMm% zZdoSWxR>l3UsZQurXX>AQfxwh#ghRSy5&O^J>}mx{fV1!7Q> z{!EUj3Ji>dTIFNHv(w2IIAr}NLvddWiUG0liOM47DdEOlW#2eh``!}V7?T6TxWA$< z@Fj9%Qx~rrhjpWwc(Koq{78_PjEh9o>J}He4e`9rH<~)jZXzc4=T5j~GBh2VkfMAv zYiN|nEQZ5hVor)4( z=Nl(?bT$D-8Wr8M=oNeP44S}09vGVxKdXgS=!EknI?+LPC%R|t_)Ta5jU>e@t=ij! zPT;wa*Jz@>{3d#3-iIcHryZZ5JRNpMXu|m#O|+BUgznayiD&`?c0g=mg5s>IpU{N! zHJWHEyNNCpGZ&)?Ok`|H1C;|>KInSd9A7g;}vS@MO0kjIlWDmGAF8?|DDNXldncDvYwJp)d42Dc%yh z)&RxFsuc~JBJZQAtK3b|_G)nD3q~}B`;jF}u_5S{Zuwrla=xEoENLk;6(pJ^R0>oC zFM4|4E;NRL&>u5IaZuIEH4w;aG}1zTBZ@tbFD(&zM0Vz6#l|3q&)=2auAyF9;0E5Nz8jWcD(8$6))A!+~ShI-a%5E*T3XM2lXBbUtp%JI4NcBvn z>|1dUnh>{>fy$zw4}~V2uhB%Ly9pRHR80(PH#rndpbIIn34;{7!_BVeL0+SY2zL`O zZK#^iohv3&Q+OJQ0~C*jjdAW+cshxgC#G<(rx8MBQ!5k;Uag*tMg|J~NL2o)ybCks zzyG~+f2^i3Bij@vzX@f;OBKt|1op%fj4oxd@-@=LuP?mSc2Qq65h}k4#q%jg$?U+w zi{(C9c`kesmKIMFA%19LU%NLCp$TyZ8K~H=it_HogkY~G!YH4`fA8EKj3%(Q!-5;6 zyg6t=w;>tX6N3D3>$gj1ZU9-GU?{-;9<6+?TYg6N_(12aiJ43*^@)6KYe+2|pW^$^ zQ7#sQ4)BA}ykQ?Pb}%+EbCZ>dpngQ?2`WF_djIP=+g(B@CaS_!ACp^;Qwp~hmmWHO zWQ@_MaAUz-f}$6z&J%SI{=ZuyPm8NR~(P%<7D)o8wFY52qU#dS= zzo&jv{fhcI^;6(2d_;Xvy;r?Uy;Z$QeY<+4dZ~Ioycx69)oQDHih8p8I(5D}TRloW zLVc}zfI42?N8MB1McrO~l{!)#tX4#xi~K3_+sMx&&qlr*`9|bRkIi^uOU$K?YIBjTq{LBOO_1_+cx|aH(;-x% z8<3!@DX+FU%F1;~1Cq&Gc3m}?A%RM2*45a+b5?4eHCUHl4g$O?v!ljR0+ul6En{>E zI$O1_f-qj0s?8>QjLvK-Dc2<=>ws(Qs4?4z;O3qmnrDGB%>p=Ia${%bboLs^1nhPl zkb89q*f^?95HUuF|A1!AHU+mQgk-|UQ>^A$mST$)c&S{k)*elz$kNn>MH80avLXyluq@gfRTOig#A=!W zp2yfSpwemFie=sM?pFMiv<1@0QD|P_O)>dgLHM8wC9^tym z5srW2zJ1?4vhX_J!bw_TyhQ7#c8V4n!j7dDjrpoaMoRxJVIqNn*Dm2qN6-oQ=-%_A zo}8|v1@T*rY#pP(>#8dxEFElOq(|Th^a8)vW7})pYm+XQz{YY@b%m*(At$Nw-n#hgm56PcG)xod~elh-elM?M*wrj zMumDLW$2$ZBvlR6Wb58}tqDmb?v;&+niS#@wSZpKt;3i{ci7(l^VH2^&9<;3drx+W zB4(ID9ZPIV%akdW5{I?M0yB@8bQKnR1@_1?@Ecdwu>FHQQjepSI`E8{M5eB z`bWVdO)x`kSn{jNAt~lnwRwuwF-x3Yu+C4dWpI6JZ8WY=uI+&9$+ZEvp17?7*W!WK*aXq>w8`nqHT#M_GHGObByrwm-53jx% z*F&qVxIVO+-0k4%5x5>$9fRxs)opQoa5ZW1fmM~b?psB^vv*Z0uJ^AZE#9}P6Rvw! z1>?GVWeu+Pt}McJ*UHhj?p!$#*Lzm>#C69?4X)c)%)xcrimAA6T`>;VyH}9AZCTL| z*Uc-)-R@dJ?sn($YFsxhpM>ki<>YP~mM7!7emS|@9m`wdy6)DSaJ~K3GF;c*nuqIc zw+_Q~&8*8C+;=1UTA-FEQr8lk%ZXtJ@e+w>(x~0=_<(3j{uuF-?<}HoIb?(ykxZb>!96#qQ zslxT9B@=PIaS4%s_LBa%)-LIa>#QZ=xXxVcz;(uAqA|x}Bd#@zNsIQyQMgtwZiVag zMdTS(Eh6&U7LgV!7mdWVViCEUbrHGSv_<4@Qy0#})v~Yz*Ybr~xRxy>`k%6}7p~@o zEpRPeFdNsB1?9LFFDSs(v|u={lNa>GwP*nuW0MvH;CjP+qK)h47vnl{ekQKh%^!;E zg!x3a@$-pn__X6x0#j=GTqFHLs4en_EZP9b0!5u11c0GlwJXW^<$$SseLh zCYOZk7>@K}G^fSYz>*O%iY4D1$&zQ9!7{j}v!uUiY&Tp}*+^VRknZZI;4+-3jv0*0 zFw*4EHYEPvMj5Bj7HIZqg4E@aPep1YCWP+|4-6|qyk8x1eee^(J%bhoeimp5U{w?F z)&J!`uKE1meE!$@(@)ZT{!bYUd(fCAdd8a1|7`R5ziD@;*CD(4{NH^3ce&)*=JWsG z3mY__|NY|YX+HnEVD!aZ{K}sH;a=++~dG3F5{%@hUUZL5bPKvxL z{J&w(hb9N#8T5Tndf=RZV*zbd70M$@6}~|6$M)#u8S}~1YG7J_&L7*?J%}5gTwfEk zaluq`wYiu$=c~YpS5-D&<;k=d#yoZJtc~g;=lvt45zI=uDG2WqudS&DzFJd_ zt-^wOJRL$cW;UURM{N4w(lGvYe#;l?bwd;K{Uf#>44-DWXYEypf7IBjYmuQa1%i)BOuK-T0t?hCwPq+m*;gamK$$o{0CD zu-Dpa%oS{USC8=Yxer4RtmBVg=vg-DnjQWT-cUKkYN`M+FB!h*fvvjKTur**r~(OY zy^hpWn@Vcf=q{dq=&2vV&^^caXAY~rWG(;kPe0OJv7TC8G17l(%CVHN(VhJvYwyn= z<)6BC;-@uhPNBb+5_6Gx7Fdaq$3zjE%C?p0rXW!X8z>^KSl^&$E8bWz)(|i)Bb!B64jTYt z6X{%@H7iCi1ng!cY?Q^BAVrA;uS zEYUGSlSm3f;ur=!*?Sa)x&DH*REz6j4h`UEtQB_iaNGK$vTH!NS8C&?Zkp3*?Z=a z@=6;R*0IP4bI2ZNDm8&+9iN$}J1kWsA{&hb2su_YI$NbV)>euCnTh3BgDzZ@&Bt}^ z=zcDB^v9qc#sSRPRc}@IUNpa{=OS%?^-fOos6NKkvnWs3qG6MXhP1Pfx+tgGN^3z| zj~VCry^#Hg>1TtwrZ`{`!^9wu#$1iH1dqPN9DT8dbR9ezilbgph=!g^dHJib*D{;Z zki-?`U!?Lv7Yak6aE+i#mZ9}Fo+3InG!6_C8o61u14Pdo6 z4?6CZnJa1QQ8WGNu?N(=`K9MN_Em?i>E!)v^%(SG=UqW^3+Lf2-~t zSs8I8A~?J-?4hv8P)i6Oq6wZDbZ_8m0e=ScR+TEZ;H&@YKQ?QybJPTea5R94q61TD zSob<_oAKGHw$HWnoewH;Z69H_T1}<4%1V=9R}iym2IkicSRXJ8U}h9MCzEa_Qi63= z_S%wio0UwK5<9C;arcJ>zR3*p-=xTO_vbXf>hKIUW#kOm{n2OV8}f|$JYd`yQuB0% zY+Y(j)(AtkKHrd&ts7^^AEh(qO-LP;lbLSJ$2ZvFgFJnxkkf~`{65XmGb_6m$nHa0 zZo$YrT}Do>E~6kjl?blS)Q!~V<>%%Ql&QRYbSG1nX2{FQO-s+sWAg^OyTG-Q(*+}Q zYhJ5IjZePpy9?5mdruFHf|_*&nW^cyI(>GUvmdcJ>3KQEQTmbT=t*vRMrOfy(xG&H zUb-%7B>EN2<`3}ns*S8(@h7U+j;?rSqVHZwoGkw6R&KJVTQs-=?m1&*CJmSt_Vc2% zzPlx9)9^>9;`+Ng#g38ltdz`c#*$INy{zRRCq@PFn*z5Qs zd%ybQ<_@>}M?f(+;R;AN>~4j|$9Uw;p`Q25AFKE+QGX5|)b%$1$Sc;6cstHrvRAT+ zeLW)Q(Tn`t3I5>m7q$;M(%L^F`}Cccn@y$cfIc3fC(;Z3&=dTLim5YOb+GzJ=uF$p z#{Bv)&z+$wHZ9sC_)L1i?|X)Mdx$FO(LcidBRFwHBV+})mv@)~fzg>_!7e#ldr8a2 zM0vU~((410HI3BQWWhQI`Wr0YMpetHM9V=D8?iVb&c>Um2O z-H>KTjIdNg+?i%eSvlgI#g)W;M{0=Ke!V^7(%U@D-JAJ0{(Qb*Sj)GX5LeQIoa+_a z6v97V!zAt+kHGXM57Xv$-qK;-@Y3(JO$aP;VRo;3^}<@DFpZr5+bS#yZ9mOy^<(Nb zk@koeB07do4|^^wI@A&JP)J1ZfS{7VT>(D?3{@>vzNzenFVp<7$wsGY0z+8(FuT&Q z?E!w@Yn=ukf2Egi(@NT~^H}9MBXIC5z%j5eU8gtXj?+(Yos{y}{y84G>Fc-zxli)R z5%cf6)wkS|#-B8?}+DD zM`ZdO42T@S?0!UfON+ZReTyt@OdHLJx(ij?cF~(;o>c?T26L&-UWF)9wWESf8sia~ z-f$3vKJnOFM<=O|`xaW_$dDlQpwS+wZ=l|}Ahn9w^7*diDeJb&mU;w^y0MuU!1}BP zV>w1=OwTr?8Zt8t*?Fwd;1-=z%MktUTIPw#zkkxR_a@n*=S|4VPtP)>>PF?{W(mG8 zV{W=ppDVcV@?v!9<5M&H>GIOYJHL>gCmin4n*yOrxuZN?lH+07Y-b+)vYmRxW1VDo zDb-dDhbbZt=4zXz6fsx=AdL{M8L{0e8zuol7+$_s=ky?f@?r}ROyFwmJ<{DNmj3vs zG;B`d!>@mPvFbs!{7&WM!h@QFhXuc68Xk->Q*X#-hh}&rEt4^#HYf6XXD>hZ(t!^C zmlST(EPZ}XX3j`MDjbmBkOzFrFKJITRKQI!8%}pI z{Q+4P!V51~J*!Le$Q&y}=bN&bGdqet`s}U}-!e;E>vJ8&B(k8JVJac1CU&-asz<~G zIU=^_kEaABGfN(mDIk`ljxw1mPyuhT!gGP6snubwoNY4eMtEfIKHMX- zoDs0`0{@hDsdka^Ub!;64Ln_;J~h>lou0>bWjsRCd#{v+jVt*>AH7%ej&-eXAtf3h zdTayE!#=`tY_DM+3F#yJkZ=WmtVeM6I-6a#gofN)!$@q;*&}s%Ihh3{VwJ}Z9_o?P zBx5dZtm1F~-5S=S{e80K9Fdumlcoa=5JG7A1&E{xhJ!IZH=`hrgaJ>A*u>BsA>Ra_3my`5W8e#cl>zsvzEbIxOBMf75X1tu^Atf5LBPSIDp&<*27nGT zvO8WT$f|g=Q$9-QC6|26*>>}<>CtB5<-#)Z0$=K1>Jga+e90~H;{yCr`sEV20sK<$ z636`=rU?q*OPkr7DDG<9Brd__mKO{`l*)lP@@PqlfA;(zEoL>1>}O z&m5vDymGa`y#90Tm@((K)z=V4k{?U2*rAgIVFO#jBT9dYOWyuPIDgpw&i$uD=lKYU zN;^)#3?_jDOdBh_@p+D7l9UH9QhhN=!A(UTU^PieHy}|D>4COt_Szdf@|!6}q?l$Q z$p869)0*GsNBEFm((e7Y#jm>FEj~x@Z&cl3IL9A*arG{3#wDD0Qq9m(V6CF+5#ESK zQ^SO`e&mMXwCDO#iM1b++bvZ%x?b$vD5>ugVUs7iyDY;8uSz{Tgg-bbZOg{Jy?s0} ziT!O*;u)(ONxF|gm}(_y@&GCma|e>H^Tc@*EZ!lUC#(5;CKUkKQL>H;{Qqp+{yp zW97rw_@@RPRPI*H_93&R5uQb=mrT~UJO^23Fdy@r6Nj=x(6z+?Orh~wdXLRW@79eg49hM=8+zX#?7Y*T%sN>R?lSN?5( zY|2dGAqhSRnvkH|*ne&$e_->9D_-BV)VK0Aw*K%{QeRcXg2I6OU|F`wPq zzjDbgY*LL!=CLxgeq~V;qU|23X>xpBn!XZn1ih<0vdZxW5n12+W#hExviiHLtmKq! zT@0Mice+PXnwou=R)@Z3V_rS7_SH@nrGI9i%NtbB`;%1Vkyg&g9eSVx^Xd#!<&yO? zn~+wT)1dxxK?((7ExJ8!U2Ptzqh-vup~a{9t?dt{n-+~n`87cqAT3cK4P+t-l~i!M zZoy_$dL*RD>vWBR-ajTYFU)Jdc6^T-AEv7`eWum}6u^nT!cmD#NC6T7<_Ynm5wr#D z=cse`B64BVD?A->$WS#pGF-9vPu-c??|gJb(k4jTkGghNcR#2gA6yTl8T0`e4gLjv znC5vhG{s_GPli7;_U@WFswlrK?IZ`rc%NVY|joM34gzrHsskfSgufd9XdjbQ3O>Bl4ATT&!07*n} z&7eZR%t5^L0$qkqyb)k9gu-Gwj)=~hdtIrm#3Ag~q&7?3OoSSIxF0OD;cF1VOUO6a zo66lf<7iw{?(voN3G?ym<0Dg|j7{lG;#no1fEo#wfOyP;7Gi)^BdQ=qH^YXVQb_tE zo2ckYc4HElfU_f@;|Q|A@#z&Q9D?qiYqh|trg z{nY(bl(31&YUFrUVyZ&kuL*cR#a5e5%=fiGIU?c>faJ=|)iJtqv)M{eQ3&LMrDR$( zIsdm&yrR$=G@HPtH!zZmcp?1f@VKz+L-&E>c5LvfpihJJfp-Ob7Z9tOr`(0F{>%T^ zTvlW;3PN0sD&N-i#FU1AQO0dPE7BYZy`u40dV2M*O1h0^!=3XBQBWFJn^(Usd)_U> zicCr5Ay9>Ey@dUTQg8dPB88IB1v%aXrD4-p{*kcBzb-m2x5&8=i5ewBD(h?^f7_GK z*p(Golmuye%UFFOZ32IEPOp#8-g&^cD^}9crqGa!d}enxV>ec$OcF$tlhtrJ6B>K7 zB0#brBuz%6XW(A$oW^Te5h+;^RZf}?^!ZPuHO8_cn6e;$s;nWqaRPJifbMr}8?)5+ zkd^$EZMJydKXLgNaGJ$s@CzGdUiH@`?e)#NX%yem-mcXUt;XR*W9!@zme$O=vefYE`?xIGdqmjUa8#2ZN z`pWj7g$^7o=2syjx9Fx;sYiU|*WF8jEhVRhK5Z9vE;==mso=W5tCZgYITob(t~ z#i#rEM>_v%8({3)RFp=Nat%TP$84dDL1!uj!3=7MMB)Hl4LDN3zNJeTq^m_lM&KeK zvAY=-0u)@U!>7ppMQEq~@0`&*0$M1tJ?M&oa3N&JTRn4|7s1LO0)C-tJE#SaDq4yBToOQsK4IXZQL_m#MQ1TQ`9_Ux7H~D(vVcRW zl=?(q_QV(Og$@7Fw~P|kGa}^3Onm{6 zoAmV%JcOd|EJKa}@2q26dSsWU-|ZhTgT_tYe1qx|J~=A=TG$qD8L2Y5U8^sEy#DPu z-E0D@b$3>#8|ccxe{A>^xI6q*%>K>&9Un759A}c~sljCb0odM=Nb0Lf7rR=D- zw{QBnsm)f}q))4ty#B9Bqv29m5e{3>8r?8)YAt&m|JCzn{>&Vm(v;RDF7s(N@NFTT z6!|kY7zw2oTUoUU76tNx99BCFh?ypE>^P}9U~-}e+JS0r@oh1+NC1};Q-7-fB&v(b zgq?$IRe>dmKyV@UWGWRs6oj~^1lGM>B>k%2DDwC$6{a$zt3(eG3=*puWQW(WA}}{m z9w*0R(j)5|=Jgl5=P%DoY)W~O437FKNHa|YR9Z+SsQvb$fCk^Xhxc=B+A<_Ual%xdBSSgakdT zl{nauiUllY0#O+`vsF6KacIC)f^$_=TDG1XvvRZb$k(j2;S5Hq_hwpvz2K}MGnWq0 znJU1HC>-iAz>A$6a+c{1u(N%j0$qT)3E*r;Jn# z7l9QDPt@+H5P0se!+@632HijpF4cPHvH=%j{uthUFTO&Lx3{w0miQP?7@ zZ8e|_w3yhQtjKaM2q|Z>2Y%k4Khxr>CvJ$D;~ymwbTRDb&dEN26`9QiImgLZlmgEf z`C~u6%}?$7tbgPjX@b>d!$t)R^D;tRTWtn#=q#7yJ1Zg#3rdo6YWV+}qFl#{EWd)5=xT1nIZm|n z|0hd1JBt;WgNcr~i88jR9>zH4%z0JhT^+1Vt%DM`C=tQW>>%zx_bsJxXqTBRH9)%r zOBwO8M_DRMti%aJPKOro^jIyVtfefPo5G5W!=&$26z#5DIKm#@OwxV{l6EsI5+e(m zPL?&wnt9q?ks_fIpNw!5@sh2rcU<~8te%S0+eB;A(+7#ol#F*^JQc~VGHLS%xVtYLua zrdZ9hK;=Vt1G(O;$N()Uiw=g$wLio#7ZzzuH%+*+DP>8r0jXEM%cUxE{%@@qr_h)q zzl-b@Q5pVl*q>peLT?NCPe|Y3IYG|^MI-wET0jrg4CQfUB)%x^4=E@j7Nv}ANAU@e zs-7!FSe?j<$drQ6bosCzp|4yi5-WmL3R2S*_IRYeLbTP_pct1T~MgN3tSmqab#zjJqxpXU(P)3 z{rOh@m4QFUsPFp>3ITpmk$F~?jR4DfCyGhi%F`=h$C2z%FyA9}E=I@>t#aY|BOj`~ zwzS%2tpze)oC!5VHX&!sMMaHFs#37Ol*27XjDZ?6!24p5aO-rAO)=HrCGvWUZR9q{ zqrgje708XmTi_q0dAwbL)LrKfQ5#fD5aV3MM}nd0Dp^9E5z^U2nVn`x4cJKG!;6nc zQ&uDm;v_#*m!Ko?wFsAqyp%-gl&G%ag5Hek>St6}vHstp&_-#p)sDyqBEE=dAD$jo z6?!Z*EF>-X_MlIKdIjDT@Nz(mYQFM0WgCS>ngigXZy|Tbg(C^enRJ z1^NhC>SKJzgbw zXdC6(y_R*6FG5f{Ogfi~?H|IVo9ro3lj9JAqOS-hT_4s(ZBvsLK?O|H&_>dxCJj(rzpReqs5pPzkim6a zm_fxIbv_lB0d>@Ix$a(YUuMRuIO@3lpL0&WgJLFu1Lb4r#J-&*ul(d5GO3Y7f!^UL$BxmV`w$QhUI%i5gzbmkacB;)Q~UiO3V z4Y@&%3!VB!cIV7VuT9ejW}I{RtEa3@O+>PuCXGo5(z}e1-n-hygVss@TnT?&w;hyN zWNbU(Hwji~r=znK#Vmv^q)|Z~DKxz{3|C%y@73F1+;qI)^fEV_>&01eKSmvMP^?HXy z=itm}sctsSH%bPR7T5Wa&Hv+lP;ui1^&eRA`pi7>w5S3^<| zl=|;j*@FT*6RXCaH7xOPBy|C*W{%BB4SS$Xhy2wFhizE}Q%)e_ zF>#ZFW?3~R#h8d)-}Wf`9HXq-m||4CLW^X*Tfh10=eF#BxN%@cMLQmB%@Z@~Rwqh$ z@jNY;RfkhFMC|BZp9UWw)z}nZ`P6IG6~2Cue1Im(s`DwDAaE@KHWDl>?F_N#0Ex3$f}G2Y}3Ukg(8h$N}%o?dyBta^I_<#?f8`k?A>;^}l&AG1lY;9u(9bABucbeN=~+y#M#jn4B@BsdR10=OvSi&o0_j_+{aQ zg5`PN<=V0z&w4hiG;^MAUB(yqkoqMb>sJLPQMYO5nlD?LhwNY5n0fHO^sh{KYv1CG z`%?4zZlG#X5b1KZ+o&@nrH0tNcy`CigS_%+zp6C}{CtT=hF>>rwEl}r@_)Pd{px)pTFp7bNPXAmwN8A*J(p>yZET^|jURgF5=D zCG1lkhl@$GfUg6)Ovg%D?~fZV3Z06BsOj#HyDZ9ln(pjir%f0PO?Qt!?x*M%d0J;) zZu*b%&@-3ZG^BPnue~JqNJATlTnsqFx($EoD{14qeHXignLXwPp;^1Xp)?k|K(%j_xa=Ynn95e z3)RhkXvcZCJ-#lhzp?pMEE_@L{AqrTY3M?(3luwNGM$fppnu`3e|>fEnTPc~+}mLv z(5)>+?sJy6*?PUGj{eaRK0~6e4rGg_h61oxz;29PL4!~>_%&XlA{p4s>K*fxEk`YA z-}dfH2}J*sT*rpaR+LTCDsQWg93X&PP23JDr8*kU#ecexeSvZ&vjLZ<`ZX@2BEfSs zVy1HE;+m`1ZX6@l2xBMCJ~)f@N*1@fGcokJevQGXNHIR3Pu9O;9Qx|nh3_PQp46PN zQeoW6dewU#yd)gu?)F}Z(VcrWZL#+qhKYWS_ozq|0&OW#9^JQh`=uMdNzj&L=Mrw- zU|W0fU`RN7X81KOq$07<%pfz2+trY^(bO}^Sb08c*WT5CEkQY#c z(+-v(`vzV=aa&TMrzz0CtKWF;^|!7b_4fpwp40^DgT1=L(Mdc~dch&rn2pZUY(X|x zvgR|^ z7Yh5?*^>lsTuFp>N!l3^wN5*O!I_vLmPBe zdlAi9P6dAqHTC5*HcZ|m&?b)&yQm-#=u1T45 z6WI@uc0`VmDsj^XsShgK_-*8h!1#uc>FyxzQaHr0@t~`{<85s=s#z@&kD+dYyc{7V z53-eTPjXbcyYPRYVZj~&O^C{IS51~cu7Zsd1YG=w#kkG^zLI8jX#NWXT>Pq;A++N7 zBE9j>`c(_K1cv!lCq-_N&}dhg$Lco}|L}6><`ZJeC4n$da!LMZiloM0`Ia6*^6 zi&9LZ-*I-Kwu04JK;_5QL$?ADAh7`u+cOAytpFJjTdcZJ*-ogD4C?lIK)6KlYjgu8 zKiuZDwRa&dM3lwUzxMjzWM+d(Shugo*GYnvY~`z=C&`BNvDfXAqNN{hupYY{cu@d3 z{@6(biP3~M{4V@3P-7#;ucGb*oz5QAE%ZnLg5wnA*=Q3TQmcH1Up0utkz?b!zeGm{ zwjQgO)-4|0a#CtWrmt(s!1!9oXZlrlNi<(1Nigac>T2RweJas@K39|2@c-gmP2}l* z)$S507QQEv+WH%mZ5>B%-!~`J-{ep7CQ?HdG>&Dq`QX@xB({fMJ5-(dQ~av!C5CR4 zz{-`zzMwo-5Gcyux-~`MtKBYF)ItQv?Y3?dQQ5$5Z*{rdtaysv8mZ&TQe2wI4%Xpt zxX6VBWvJK~mzCoMsQP6J=D+gaa_IkBUvbtg&kaja`^@ZOJKu@@R3773{Uw1@ zd7=L975hK7=38@W-crpTn~yx9;8xXjnh%fFX@1pY5>WZ%Hrag7-O#4rc57R(?)u58 zf!ai@L4PWeCoN^B1xPch=99qAC&LrPeq-lxnNv#}rPp3a4fdKohHh<$SN6(B_*J_} zpcQ$^TvgT%9sry(r10PJ;7m5f}Lp)SSh+L0A zO6^uI{n}M9boieVS4bpzILxUxn`43jl;E+fcsdk)SwV4B8k!4N+E5k3oxpv8j#2ubg=+ESVd?(Tk)a*)|7be9I zsw*7eiaeOp!vXfIE^a`}mla`qD9{cX2Y9?+bx=bi>V&5B<)0|4D=pvNzw@5NGbG6+ zX(fIzFxu*p$|lcko=74nhE8Cy$D$`;R><2$#VEIjd2z;cwU{HhrnW*%QEDb}*|$r1ZiZ#IDD zE9HuVPM;jHU-f7MYCaj#IO=rC5&Jb>Z6a5VbjT5x`8CFEd`t<}!TOdJ%EMcp`1qBt zRtq|Nl9VlKF2;Aeop8jAyLZavevN;dNXO1T^;tzZ{M3(rKkK^`1sbY5oUS&lsjWQG zuQ74sBg(OkO|LlP2>-}fVo5F+I8|r0ZdI-wey7nESs*qa>K!&PoZ*6rgc}L#BP}V# zAad3KvYy?m|2H7|e_Jz#w3Oaf@^VRWadXiPMY_UM3hpmBFuyjhEBC(K%$#}IE3+QV z%E@fhU68RgWdVTH{zIrfR~3(O?(lK4*c=T^~17){`-KG!s1Fsjcd`c)NVw1BUk zi?twgvvYy|)8IdB(`>E%1m}``MN%pEKD%R#5F$_Vt2)SlEwb}xZa8VJe$z7*?|s#M zwpewDg;W_Jfr{V={5l<7t3cOsdQiKw+-A2&a~G&$R8^3Ho3Dy7+TCfdVl?|zC1k;O zJ1YOC>z`aO^y{-HeI(Z9$<#3#m)l&vXyOQF?T$)E>>Id0s4B^#lRvG+j1&E;BC;^- z?e8ehO+5MgFJHe(Y$W;=1SY89t@5jy!hkLEq(c>B+NuMa{i>rYW<4JbU0tx-+w|{j zZ{8<6=XtTdrp3lbNpsvbPaDb_t+>ipc2i+7ht#~nEzLpcLsx@*|t>Ln>W(%1j5e%gtruV0k8F~uW2 z&aZko0;5>w=DIVLO*T{c;?XB2#+YPBnh_61S(nY*x9HM~>y&E02)H8GDaiYzQ*mal zUo|rXZa!uO+r=Pp(yL#6#|Jx2|MNiNZcc8|@1k+i4Tb+M6qxSHiqz_9H2YP*K*5s+ zZSm@F|LDMyiyk~Qaa)p{GP^XU6KMcqNq{2XjdHMgz z`!@I6obPf*WbMp+0w37>#nA(PhhG(4h1&?9J?g7%E3T|PsV~5Vi({bKxF}hJL0c`s(MDLBlYv0?&UP#S0S~cwwGFU+TCtXTlBqJ zAwdO^va3iM(Tf6pIEJ8dwg+)ST9;3h645+dKT)oaVtd4rNl`CK!JtzRBTD?FJ*r=o zg+;+QU!Ym-=>LZ5dR2awyShH!S-bu(@3B6=aRH^?H~3py?5{#U(2Wyui3QBwbKTnP(3w)KyViw2rR?OKsgR zvqD`psYUbcLWBFR{+$;W6i+W}>Tht9yeOrmLVuNC^}9v;mx#6h7gwR~FYv3rxoG`a z0<9nU*i`-VZ(eZj>Lulxq?eX@pX8v!%#4uD1vO`MExkiD5u66v=lPra#vnm$F@ySfD)<=5B` z^Syqe$Tz*Jf8!JFGP}80w9vTUKp?xkcO9 zuh75vMen?M-(J^ulu|3&#`yo;8Ksw&c#7+b#uQwT_kVefxxdYMBj?!caMs4m7c=+M z&CclNc>Mn4kI2XSH8y}Er!E{K-c=shzOlW>(U>@XlhkAQXZorT<0tsi64lxh{Bff| zPB)+Eq&HpmhVt?k`SYIMd2Qm>Bo{HX@>J?-8a3Omu@U4eLx?r%b>%OgtvPS5|MtX< zN@|Em>Z2cv6;0xy#UD2msC0{ z>+hNV#DWb+3Ca}h&a+RX5=@THAe69%#tQ#C8(mWW{x_ts*jxE2#-vZ@86 z;Lwa}+N<7^TJ@fhe${3YA@fRsy=hd_6#b6XuXx5pg8hXGliTyPnVh(GQUxBlRGO$P zu1!>qfJu*&#Fels5|JXQEUJ6d*4qU)coJHIh6!{`P7ddO!Sh-6O{DQ zch8s}t+GAaUoRzf$KHebXsKUy-$dS2B!oorrXL-$vF)CB{@q_6{ru$?`xk$?iBt0a zAC}RQF{GjN`jSsdjw`;R=)`G3yao;NCYSM3U_4{RDU z?2J$Tl{lu5+*&ro0y$I^5Qzn%{EFHHFGNP-PJ*1iEo?FX@3G2j&ph?%e)n}G#+%f9 zU`}sZS4PRNu>cf#ZVX_R>Y;v(*WXC7kskF3Q21=kFrpWp&wsasJf=o zS{6FPANTX)lrmFs4}$aET))P*kFTgThJLV%T8;j=iyx=kU92sG>UKBLud(gp#-*%n-LE?QVxl^2#q0@p1Z&WQ=l@mp26sYNLkEesU z#6%ByQViuJ!lZcRlIp@9cQ@s>;4Lhl=#Lxw`41G@XxG&$&whT$C5O*mDB3_6u9Mxh z)@+fQEk;9g1O8Jo*GhAZ4Q7jFuGv(>wqB9|k!np=YYTQ{_HlC)v2T`ItVZ0xG=_g- zH?Xug=Z*4r#%riWf(bg)fO^vTH3olt5{2rLxN4w1=_dI#Hh*FV@KuG%qrOEumn@PJ z&+a5&40CN*qTNtqK-p7_?COx_#J%*6^lNu$+K3B37f<>9y9n)&`grdZ%5 zfj)IoBUqzNFvT@XBr2WSXX}TCy9-Z4BJYl410wy`QT%v}AZ>6$M)BpXz;8CLv zIC(kIQ`PnsLcrpde>0@HM9ZIvwN4Ib6fax)bYQX^P=*-=Yscx%e&vz-hV`c+n%o7U z??A_gORWN%641C=@>SZ!fTy?8Zc;$wV`&xa@}Zk`Cl8;tu%C=6$I{xh$Bzh<#Ov<( z&UV)0IggIi-`91(_h+2apIB~kQ)MnhlF4#)x#7NfL_qbkL^4+hxQQ(L?z>jQCbMNZ8dcHbC8&6_~nf{;`5 zM$?2r(Yy(0y!=Go@@dt)2@DU!ZTvVjxmd^V_2p!eJa7;q!Gx6vH4tvLfM=>aDWGvD z)Fk3ta&A(F-~D34FOQ+G?oDv@auRq7dUReeyczHV^95bGqqXLt$`FEgU^ zLkuEa4)XrrFT{44RyQJ`DyIVG7=h;!U{)#jpLOIrJJ#KiIR2H?&N|;@L4bC? zp}N}CXtZMIR|Qi6k#FZ`_fJ4fYltqOYNrA>Ux-e{J+L9V;{&Q%DuD9wPgJ0L0RLom zKowR+YxqvBSZfA{sxdAQ_vPgDOJyxEe0;cmQ*Y(4H?N-G_sz9S(RXs80IPdxkA%0Y z*GF967%{g?9v_GsY;vkwN8whUq};dh<8RJB=+b`RCi&VnP}@8eDPrk(YeIt8A&(8j zjVC$9qZs70kO0a8apy-)N@fhLvg3tkc8=RBbe`^xurNIkw_fBd3}9IGPt_@RtiNdT zEq-f1lOd@y1w3+>t&`YOfxArPA&8eO{9>h6cN;7f3Fq4@R|Vqkgq-T~QSjGXp`7!@ zwiUKR)=u5>`+i_2ceun>Xta|l9i1%IHYb>+bl$@i*$(COK-{U1 zGfKl+u$QfqYPQY+fw(zgh|eu0HZ;l`CtTBWR%TgBg4#`Z*}0+DN~2J52@R-eEJtpU zscGP^iQokEyJGsq^ns|c<;HBZ5pS+GP(s>#sG4CK8!Vei1WM=+MA`4QZa{}_7lbh+ zNOOP`ha{vx*)n{GqV0V86VzIX`6Y%$}3=yUdp|B`5%{{prX5fPEL) z4UHTppQ1k-$HB~Q#8o(j-e4QZI_#_4UyKjSr?#AgY$RVPThk z(~|wazW-v>$;8}aIE1tsG(U}Fj6QohZC+Ae*5+90&kMwjF#Rh;5TEb8TDf+T=gK!t zi~Bpele~nRx`^RDDFUi&#p}IILeUUW+Vt9SRzp&%oz*9Gcfb-c_B}{Q`Fh-V@HyHl zRWoaM7iy4Noi2P6`hPQ7ITCqWIdMc2wGX%gdyxfhLyz3^`)pjSYp-8PVi`dg~6J?5a9S)nT zrvnz~Ae*68OC4U=Tw`9(M0vLdl5pflx}Yl^&&A@GQp)nKBc)<-cpPoAHxPG0^$Q&y z4&D9wlkYygxqPTiFil|X@jwvQZFk%42<>%4g|`z1s5TTN5_8nvZiiGg-9YT%y>z!A zH*DO@@to}L0HqE0a;e(VP&Iuj>8TzE6!CB>oaSpM*Y#*`$fpM?V+GfKp~J_ayHkH@ z))kLcJ~?FIn=RJpS%J9Ms?3+ai=MdEJH{)Uu6t$C>E{X>a+op1q$r8ZW#}AY_wMf1 zWPR-}9AqS#M?WG@4`@79`F!VN4Jg+?^1GoYoMYIMAOV@=5k?K?iCf67;H&$BKOzu! zQWdIQ8&HsmD@Eo za8zs`<@7CAhXX{L%>}1^8Tod#;66GZWvzCH%Vm>RL2ey|=h02yOWyzc=v=D)|M|sl z6;CZXt8i;!S;5%+v3cWiPs^#y&dhufAKCjQ*aLn?K(#33b_2Nez?bjd>ju!T8$duc zQiQ|`pBsSM(ZBi#pnpuDHdc%u;A61GJL^~N2H>9_P<JHFGcFX z*=cJ>u%dh>eSW#1V=m7MsAiQQD)7~+sGT=d`D?|<#S5o?lcLT;?LSFycOE3m@*h0H zwbtaOOtRt8|8Vn|IS)D-(?FS=5>Rb3(W!j(@(eR5lj&t1jt;2)m_W`~?-@mY_Z#}_ zzJB^Y9Yz1_CsC2)oS2U~5PF8u;Ln zMlds=x@7{Y$nlc%7pHPzeP~B{Q)g`$?^dV*{$SAfSqD7l7KWT>7=EVCe8a2}7MzZ#LIJ6>N^R+0@+93>_Ln zV~x~gwA8j(P3A_8R>u5*>evT^Qoiyjbv|6yq-^o5Ilgn7xO;JprKOH(=QJ6cnoZSG zOMSJ`LK{(Mur^!Fm1d*W+%y-OJfLk@jJ5SG3#1y8)o8F9rLsD+rKVgi55(>DI0JyJ zWpj?dU3vQBZSxK}nY`H8!)poh0n#od?Ma@E+M0GKTHqZgR|Mh)cXGABj3@Q?_nmRc z-9zWuQx7&)(+R3k+_Fxt62bfM8OlGe-l|*r?P{@fN1uERNwy(h1D-(KT8@(^z~+D< zZ9IWVfw&u-%qJ$nAnTvnqTKn&->#o|-~OqY2??uZkpga$CkEm^Zk&`UhC98r$>o8# z*4+N&weub&lfKIEH!W;kBeG$YH7ENEDDOf<91}6#W)Q1 z-eT`m*A$No#NF38_5az>{Hpc;191yDPW}IQC;fW${{zRz^DQC|pYq%@$~fj?X`_rZ*rRHR5fNzFR_wsciJGcrHYYq%WN@#1240sDv@%>$QV%l z=To+g+;P^ZzWs<;AM%?pxWTu@RSrQ!IW1n%!$(A6yU}10Q8db=BFWc5BckX78skur zLCD;Enn8JL^|&RcmQE9U`CA;P`{1(Vg*&y)i+D?vwOyiyn**f87fLTvz1jDZSKj}H$PZ?t5LwPaFY zX?#V9Jg_o1oU~TI>6wc6zUn?ZMJ++B$mMf%t+Juy+=EtVMgVk&xG|{E3u`akAzDG0 zI?=OPfw;FRXDN1cbqrD|_QZh3#FURW5Nkk*^60+3+b`Ytjo6|vFiRUR>i;XHI!70w z0!cqA!agpbx|kx7z-PM|gPpHzsjPeC?Zr2V!T#CWXH@~!=oD?><1VOe*#3zA3EMuO zwGaJRtPTCUH#G!Qzf)vDvP6!9Nv}=Q2WFge`Kza_?Ryd8O%&AAo5GxmplF`oRZRd&=A`M zIMbNs7^<6%7L%dgDo+lmey9K~67~k@Ed7=TU%7vA$s@v`o51yBg)J!2bAsmNSlL5- z=G&;43tlcKM8}4(ty-OkK*Kn!BA|L;VpK#TyRH8pn(;)& zkQ(^^eN|FZd{xmmMGFh>Ehx*oG&d*vtE~TI9jcS@fj#?6o)SOK?Nyn5+bDS;&1aNjE!r|`0vcO+Vm^8IqsE!n{b+}2ywg*Q8lp9>jz-XtRo^;Gqe7ecq6DSR+rkNO!Ji!4` zemC`r*Vkt!7?31OhyL3~q&T43gknVa9&6G40!w7|qPo!wJ+~zok))1*|MKxbE=ILP z#f6WLkHE&`;8$Cf`&Lan`j)^~!q;qY^Or+^f$R|S;Q`g{6aYn%MF;y{*5CZ;jA8%R zJX9E!B&a3Q0weJT0KQh5{d- z1_vAe+zoB|ZMU`s>#pz5&ML`iN~Jj5E_XY1IMs}@w3PKS$^Rdaab3oc6H3D+&y*Zo zytwF(gPT=9bL;b#{C--Cy#tK~>KYAr-z#chk3zgja0HdnI7crpA7^*2vB;_~a}oiBV8n84{Fj9w$DT_JH@O!~Du@L0gQ_th z8pcPFP$$kFMv-U^s>X%f(cgH>^oyq~EBGR^Bk_dPXGU! z>~nKZ{C(nfB{vlf^(JB&ZLlnae>5WVl+DtBIQ<-Ry~)}jPY9|$hgj}>5}$F<%EdR@ zw}vmcAu;GAkFNeu9}`r44}sbww(52I=gNkiSoMjh%uaupO~JzG^uR?yBXZnud_}ixjvE$zp?~bm z4c_YG4@wPKD}*?p$qAFp@j=xJ5g0{gbow|$Q-Zni2`!R#Ic`Fea;xS4o$`wR@YHnc z9H{3L-9w`4@e|$v^4Oqie~8W&x#Oh86=hOTH3S4&z9co9zG>!WO$(}qfWR&CGSc7m zu(GA|v=#duh*hhfs-Wr!2<#%qqCTBQ9YNIu5TN-A^Vmd5 zFBz^bsJj0FcCWx$GV6}Zlt+FT-M;v!8&We*5^@h=QS(z12vj-M5fDw`D*#|4Fl)m@ z%C>tmKa!pQlbWW~;2?KGO}eL7Y9&dE4x|j(``@22q^0z>k{6427FQJo3;$H`&w}Ck zC*@s}`%&(soU^mHWgnj9%-jqHz?ckws-OSz$e>2PQsijXnQm;>UwO_w2Yz>!&a>MaIn6AG@7$i+B*;&mB%2i}%FfKjm z7YXVCFnz^wWkHP{C7*l_+ssg7wnK9g9DzwsYZUgOCQiI=o&Ne%OHlQpM1-EmX*R$<2|ZoxIC!3T4Gehu5IflDut)KdS2%3CBk#9 z+H8RUk{Q=ui`i6z$<7e&xLdzotKJYn#J2BxO(vQ2lX_D6m%ij}&s_!Klc%abcwXRlq_VYt7 zIehj)(FQ^?tFf7BYc*SphGvqJAbgCu#s;&+GMCITnwc<=ZLQX1wYFHWebt&RQnR^< z4q}Vdh}WWOh>7VZ_F5c38M&Zp4GEBZGIHt~8DJSX_WpNd3|UgTsWhjgsrZVb*NR3J zE-APv{~!5B=U$R?Rragd2WC05@-vs{?$MQGI5^(_^5~$bTBxq*+0F}U8wXY!Qa&oE zQ48f7we6}r#BP#0A`LxcaibvELrk4Xu3&UfBNob48WlyVn>Jei z#U=T_UHty_sX*G`^P)~!B1br=hhF)}psEVWt4I*VIlzhpi-Ur)pi!8+kp&Hk%7R9J zh5mn69{knH@0Rx+u2f4N1ckjf*$y^{k_SOy?@dh;2E%*P6O4Or`bFN^nU|aXqdfG? zB{vPJtxtT6C!3h>C!F`=Kpid5KN{B=CkzU-(Z48r?nmLj!0=$)yOWcn&&F*pM?d8p z{h2|HLFp2Ki)Ci@j%W187}qH)UpPDg&o0U7*sNZY$D3PWhPRx>ek^WxCn)c+zm&)~ zSbB?7b9WVwV2IVBE7*D0&6x=@b_T#{ES( z#RZ~^xq8Q7i3>~$#(g-2a;llt+yAZnJ#xldn_K@aHft<4u#!;LON;?)oVNBZP?m8) ztOOm&0YcP*N{g*mo)L`uSPBK;fcqomx|=5EPOb=x;jRad3(;BP`(7zUOEg$J$fLHw zX$Mb@H4Nd}%6KWANVpd0-%*~Mc=GpOzJ65-(I-&Ts0wNfB}LLafSooait?bwL{cmO zwINqI=e{>TSv-Gj;)}-535d9ffvL^asTQ$&QFk^rJ~0Hr<|wy14>;kAntup`rD1#s zgE!AL)HkSMC#!r|P~#mb7HYb2opSY4cWoK=q`1mC39-}}t)^-#dH)Z|cr0VcjM6Jh zJ}a4Bysl_np{Kx@_j;}eb^m2qS-KAV^`8ADj|j%qS>2a_#Utjkd^w z1a*~3-b(7BOig9M5T7G4yug9MxHc-^tu_jAFU>#2G=Kldphl3jQegJ{s zZQtnpC7tx^)%Fd>9XRDefNy%(JI@s{V^7kiXb*qUjm1Z+!9e4>s*c~A`vWX zgMJ}&GRxduj!L-u;D5@&tjFIW?SKGN4?ZiEA*Dca5N*9(sJYPoJaGYZf~oI@wV z*ednwpMC1!QRkhQ^4S>s2P>p1Qu>8FmqW7QQBNwrZ1xT*K6PeF@Vy6n88K$+a_@nX ziI4f9llj?+X-Ll63aQlw61E-U3U)WVwW4B86%ypj@H*;nkVW8ibP@H1ZakcDM1$AB zB+h`Q+v9W-(O6PKLK15z&hOW6HmLCh<)T8wmi>Q$-)u1M1Ip(&tLdy?wco6KW>Die zY8P3+zf8-`!-8?kQ9hA}=u{an)5)Of3Chd-j{;1i>E!63#>rD8b(lM_){Tb-RYOk{ z(sMnXqkTI-tp7Pdjd>?z0+%~n?rw7aBu(p%;>&qpKmEfet)26SD<0^Z)8kd|G}v*` z+O1n7qHltU^ri=#C`!Pe9$8E8#f zO@(A?B&D@K786F;S86`dEYA4BqYUGN` z0~c{6ikf(E-gKj@_10JJ>*sJzDh~4#4xHsd)yNf4MOJG%c=Y3gs%tC2a*=GJ{hB_G z`q-exj+Kii7KNM^rgvFTwHQ$<3vlldbY(7G1u%k%$G@qvzyarWD z757W7;A9l)!15Xv2Tw1oI^dA$Is0VhaYcxtD0fE}P&H4v9TM4?=^y)|ciy~jSN9#T zl+y)N1yo*AV07~vOuB$-K8hxAIV5VEFbH%3e_pUNRv71BA#lgaJl}h@a_uC~m2a9B z$8P&e+;+Rq10@g09=G`*2>`nZTd$Plp>OK)64N3?$17VQd*3FxSqj%{M-mre`R!7f zqaEqk4tI|`_C1Z-T&2gc)I$mv++8RsXmz?Ev5hPKOr{`$$^~+Dph2Zxcoq6r22~@} z?7ZMYk*?@`zeWGTx983+P@Wa52*Wj*$KGKkQKZuP8CKAwbSGqdD4(LIw;KddlBV{x zR?`2dpo9z9OTw8e`l*$83PGqCWgHN>>O%iIX?nXtTG8R?Qp+_QZ8Qt6`JvtTM=T{` z3(^E>*I)1N398<%=zEci5cnILl+A~I>7LRwuD@xR+)Jgi)7Rx(#TWv%HUuN#6B`wa zAhPK2_?4=rNxcr62VJ_9*l@r@(uQf(E8%Z3!N@oJ{2aQFxO0)WvDX1@ns(yV(cOVb z2#+*8hdfap`{_QIJWwl+4u{P}egY*91Mk{{`E_2QrG>_#~bqyN%E zh+WwnDi+8yw&97Taj6xV^E%TFC+c5#_`^2~j{96Vj%tA0T3x-N1%XpTAF%?aLTWOD zV{U~wj>evLp1^vA$VK{`QF7lsO!F{q|qAXN~}ajBt40hkVayZE3pzsJJ5-hDM5{fC|B?{idCQf;H}1olk4&yMV{8+ zE!a82CY)@2?q%nsn4J?iAQ(5{4Dp#@#D+$Bc4K@RUV96f(E$CgkpX4-@q}6yLIw$ zOf;SK>*ko85sVvY`njre)vf}^WcAXz#iLtJ>hF9_P5>Y--omO%;9guIf!NjV!>L#y zEqB9#ksNq5s>^6#F&>xA*XE?>9)*GsD@j*4ycV?PvA>%QJ>FlwMx)M#SiU>&(Y&(U^K;(MF=gM7 z^+VRI%(Ha&>ki0RE*=e#4+$~F)9iha=H+p3mHyM3RogUBw4NhEOyiWoc-P%dPsI2#vFO0+h=7 zK*ODLdynoXc$U;;neQY;rZ`o=Rd&b+hnUMJ#mGZfi((uYi`o$(X6Z@M@jTWTx~vbk zD64H*hbZM=_0xk%o^f;FLTZQC93)K-4lx@~!4AxNyIi^V+Jh=?I0RP%ob#k)U@DTIjr07O)PeFoi{qqG4m4E9GT~bq!VH*=-ZkpMsM&j}^V}m>D zbLG(mSG{!CxQqI`z$At3N6L2dohZ?eM}?R{CWXo){t$)wneytQY0LjqR+18^W?zpg zdO=~H5-O(UT?8dhc^3njxVWn<#JnrB_u(w=V!uHDeO360^UgS_?>tX+X;(<~tmG~2 zie8Whv9v3sI#u$Pc4?b17^Pi3A=SQ;x27iAg1t30KdYu@W=OT<#LN+?8y+OR(~xS< z3HTy;r1a~ZR)ti5PC)0X2vTQ!8g&%Mhcq^wThTQrX9KX%O?A7b8+6fys~kxr^?RfriwQph|tORSUATCpS_7Gk!L6m&Uv zJZj(sj}Hhj`$qzhOYoTW>fVB?6xZzp4hS)4OW}bzS$U*s$uj5ur}YoHj zD1YnL{)Q#V^-}G2Q3eK+-LTuboi3LRrG8**xZ9Wv2MCvpSP8v_c88||K9UX>(bWZC z5UlHE<>dW;K*s-M44GBBvgGFCmy0Wl{Drp`>I$0kZ^(N;@3`E{a(3p-&i;Q{pJ&x% zF4J9rkNy(AeDNt<^d_mu_B+p{<{^0*MQx1^JeM<9^x5RkW= zr`&tPbIV^?{fXdYv$(<9VwD(eP8*D49r#b1ofIQ`adl)amlW<8fpNI1O1b~6Bj4Gv z?hZk?^Gz0bzRovPSDPA*R{5w9GqDL8$y4R2E~&MHp};yW#EdX0dXWt_J^HKV zA?9F7;qpfwsa&iw{NkHymy8gd6DYI6W)Kx4K*?-tlrZ82i;r14Qm_Vr)8LAQ%46p} zKINseCng4)VC9qCkZi!^7evijcT^7OjpiJOz; ztS8IU+GMacm}?+Pf|ab5dzMZ#HP4lrtP88>n(K{CRJKGPVrGxD5zB-cq2KV&i+fAQA@$r28+@=}?prA(@CzLqVj8*> zu*mVdVV3g1rZL0L_~c)Sr%-ad8w1pc+8t0Z^3rX}7BfvEHM&eYm!ciX-Mu=~hT+Of z@4b5ai<^#5j5etWZ*Gp8ZMKJ)LM}n)7dc6<+os>T{qS{n?>rzex@7m1e%#W3<;0(Z7fcLwnoty^aLrhPX4g`Nj^&CF z6Un8BjUqj}QF(s9Yku?R+@}&FPO4{#$wOki6GO~cmg41+_)?$E*JkOrJow7}i%T9! zj5o<1Zh~H-9ie__mzUiW9V>f?Isgm#6O~1IT8Np=QrJ9NIWgGjm-lK4F*jC%ol7c5 z#ePk%{^3)p#?AGws$t4|zK$@iaIO%}EW49a27G{;&Mz94vK@_@8I*I4Jc{Ym62WQ>r)D znW}%Z9m&Upm}?|$0DlP<8c?DX9(3m?>xv#q+<>H}O!S%+pVm~vw#VH~RsUfxj3^{? zdZd^|o zjI)lv?=aDzzZR{bu|}$%YiuyXoXTQoG9pufXa;G+k7f;;Lf-%TWh^E9{|ie#D49^~ zDZ0NXr*Pkb!}4y-Ey+13dtuh)nV)1DbvNn?@iERXSqd?!R8m7BkLT#H*$1AQU)z+k zu|t1VlWyeUPmk!Qjxo8HuDORWPpM9mO+F~ZR8c8Lp0_Q-2!^Nr&)&<{R=toKjEv%W zaEOVXQgEZVJ8*!=v7n`zIMZm0U}wl=0V5HAoR(Yzk_N<3Uh=SSt-wW94(Sv^$z;Kkc*i@pkw2vYwY zq8@9@9lHDVC*OT~bNNtP z;yoa#o3ypZgM1*ZLqd5^H!Vu)a@ri!&Y9}#R82R)T%g4&S3Fkv`<{99S|_ILFg&%OTE)uaBN0CiIPSiP;)jczB(v=nD2aboF} zXNH))EJbM-LHUC6__RB9w`2&JpEeKyUz=;Z)km^!IM=Pd?rxV8vxC%c)Hu|cVnuY) z>2e?|$E;>4e1WYqeUb&_{l9O<%NaweOV^aVSTeQv+@e>DstSKw@M%F+-j%t!?2fGU znV)4&(#iPHFX#&)_f^pvS413mvW@JmH~Z9Ql{f#rcG*)6llr^KCN=M?QJ34MRcywb zVcNI!WzX}@hyoo`r@!xf<>8y8WqseJyHGrn+*PV!h=fEFDIr#Ut5m7886hS`OCj^D z(kSGNHOl6z{v5vQjN4NSIf|1=Yilily}igHbjsx+rVUHc^Mo21`dKfy_0Qk8=J`XN zy40da+&>!1@RnicyrzLC1UOH&%>7dLSRd`_b(m`$TMjY>PdY4H{p?Zq9KWy22 z)GGzMD6R<97r@7Xeb z7F z4&1G!viTN6xqM*g^!UB#Fzy585a((7M{j@VF~>c-srFAU31fFGg%!BdgC`2Jyrn(M zL#1IoJHV>TM~0ZyEk((Hu^f0SQCHpDDgDLS4K2DXxNuRK7x zYh2OL;D=jN)2TncH4@=ua`bijU|U1-u$`^&rzO<@%*vGFujZZhQi|5bkM=K4<-+>Vj`F6?)Sy*gaMY|Z!9*tS|6v)cGKRF2 zUR&}>$-Lq}6n$P~DEwW)tIz?Qo!6QBm)xN_4cXUZeV8>P^Gx0Ix?}L+lzhn(!m8dg zXV8^R(sXlY!>SH6P>;dJ@hE+LeK;)@5R6BN{xwA9Esu(nY@-H0p<)DyK!^LqH zHUB0>S)MmfIpXr`=AEB7Pm<&v(3F_xIVr4aPNTE=4_d%&R-Sw3*dedIdw9y=YBJNT z0&*#+4tB|Acecr8VWvM#W2!t7iRx^=a=fyv!Buv7LCC8h6P+OOk5{L6sCM!;gCo2)2~OE}3g3 z)QU;XCz|E4VWu=qo4_9(0~<9Rbl`Derq4{#imbuQKT#epy!7mrBX=VckW_#d`6IZb zc42aqO`h94v7Dq}ZSDAmx5q0tgqa>Qf$QfXrK)pzPa&lm!c3)^_CHD$mOEVTZu0Q7 zxoY5s+uLAsc2!p!j7uz5L$^n&nkr2=X2L{i(u|4IXW(_%)DUaXAhI~EVW!$lM`b^e zMl|VvXWj3CrpKO3ylRts6UX`2O_XXXwbWM|Ep!Ff8LZ6~bEVm6H8;%#g&ftt7GrIF z%L1v!WHlPBMyagMY^f=i$A_6=G94S9WK?Wy(kD6`W;V$bGJh@v`p>%bi4KRE3o^yb zL&DN@^K)gxPOSQ5b>i8R)D~o@H#Ha=n+=wQQnLlzD>`i&jLn8Q=6aL0K^_xkPRR7V z!hfR9IsSI#>5sR~JLF_kDW$?_#p+(#BO&$S^*JOL_^0kJ28qKNttMXW@ zKGOS5aX(O#>tGjxx!_y1x>q<{aX;+CLd^S^g5{CqWHZ6=&jXdmHy(8SDJ$OZ2W*m; zLQ4n2Ys6t^LWsE>Q-C}LBMhMasV&N#kNoZWnfL9V8h}3z3&fI1kgaM%f|MBmQ`G#M zLOK;?$Ap+QE``j$!btL2IsDX*en0EG6vc>lNk23G|GJDJ^Gok4%`I6_d=B{kZxv1| zaOVF$Z)@%sxwCW5%YHt4M3$WSH{IJhea5_0vu~ zef^@$-RO-ZcQO;Ag+`A?3ai51Kv*eocew6({Vg{f|AXoJYZCANNv`)g7s6@L(q2&J zfmJ^bky{;| zJ&MR(7gjBgF+Mz;(O9cGl$Q@V@XdQhUYmG)lH8Sk<^X}FLOJPU!m2$on!-cojx}ZA z$=qSpBNtwW=kztK~@_6nUmwEZ|d3yg7Lo*iM^?~qmu66m`9yi+2 z1@;dScJB_yWsI#60hx!)6xD`ayq#iN@*=X;x! z&4+#Ip3*d~ziv+|Y4WS?b@}vgT*TYYA9Dg}`j^BM569)e{ru6^UlLPXACAk4%ls8S zATQ5Twj8yfecQV)iCtt2ovmQ{QyNx`rCM$CI1=-hCx_!g;#{WS(G8+{$4uqU#Wh#2 z-8e?93CR-2tHK(|aDg>ez5N~Kxrrx#|K;mfi2?6JVosp%JTk122p70GPC2erd9LK) z6RtfplmK*chhOaCtG(Sy;kfU%Ko%>LfV_X?1-IxT8xw#`>ZtfhjHAOE4{aV%-srTQ z^!j9FtF7sXk;7k3P4?Q*-zR=4BsbPkH*wqo%IuHYG-w;HVZagt3XE1%tTdR7b4&)Si3zE)>`9Xa zV&5m3QScP4bh65agjN4+yt753V>hl-u72vSEyJEXS-dIGhpjVOp}8lI2& z#)MT7XCbJD+wN7azjo86l4%Q4v~jd{x#14rl3G0uTMvqK+U3fyD%=dHCXNZidNqwE z>tR)`8Gw1Lju_zdn5>6Y6=q=NNn6CQ4!FsBSe0Q0Ql6U*Lz-Tb^{^_x49q8UPhEZb zP1eVURf%OF=c#ds_VxjnD$aw>J+AH3At~A)CivhCi?k7Vcnq^6&1oJ*HF?LJoztaH_!IIv$>6;Xy>1rPh4 zfrVa$^2Q0*^qiGhmXavSf4dp86jpVjF^71bQ*2mr@B63n`0IC-4!ve_YUWTglc|R* zgI7Kxtg1r;<3#SAaDa)|M}$>}WFeHO;b+tuY`T$vxbeg-_}iT{8FH6VH7+ zH4u&VRgO-Zd{kI9Ck8zJTTf=;HvOgY0~OMUYf=NxK;_?w2_960Xh$9vR(**x`CdzG ztXH)tujL1`Zan|g)BvsN^>%mI+MPCeR9N*T20$KSEdw~CJZ()1E>ipO9%kpOB zUXke>5ByCKtH-!{RWV`lHjr zacOaRkwCw1|46y+rir1dw7bPV1=1*T;wd{V3mT;S%fGYu%SDlP@hWjLyKd20A)&wjPGKseJIzWyy0+fci^V(a(lbo5LNVI-`1!}^IuR1c`1EF zAWJx|Azd})mp9ac+#*vxlNfH}?LGpi_5!RSA+qkbMMd>ZQ{tGOPQZ)W(8&IoHPmqlj6pa^VLhBY?KbprDk;y$Y@E#Kb1^B&=uuv>Yb zW5OCgW|1>8eexyb{l9-kZN?B|X{hAR;%|!ci}ov6mw$8KH+eI2SLHmFGb%ff^=8(z zOpopkT|PeelV9@eFw<1eA^v_mduB#Pc*E(p);0~AEkt7dO=ccXTNkqk3F_4~?Bg0Irnxf=MpQtDYGJ$ev zm`PGokUVeY7|6HFm3yx}sN#l0aHB|tA`$c6JTlC5sdM(>4EwA220HLRHq4}{bN1m3 z{Hs9I6#7^7sZkuqb7ruM-e89QRq1LpfG6~?X~1AOT#gN^8rHziUyqTw`P9EDk8OQq z=l(yGq-NQ*bP?7I6hL81*CyG#Jsrqnc866>YcPfRFIT@LavQijr^RAmTwESD!}hv9 z{yf*mu&U>cnZ_SB`lWGsREKUfnup61ooc@z*T>0W)$kkp--#R-(P)#?PK0R)t7hOB zt2`0l)Adg-82a_ulRgql?O3}V4tqysy{)U=XKQyzO(Q(3I4T{nZ^*}nRsV3IlN3qWO8v{%*-FaJ%oA%+Q%A3t8vgwxelx`%Z;(7StQw00 zKY#5j@R#c!`Q6YH&M|CBIsTvKCz1F6e#HN8Zt0DsIVCkk8w!UObmm{1_j%sL-1Bl? z%Nd=0TGm}z1(^-Hi+@QDfbyhBAw4?jqk@Nh9J>prSM73ogh_}~+&p>mIPN;-j`bH! zzQu3t=T?>!Co87W2{i;lb@FyQ>`s);LIkx9+$T@F4GEWN5hfl^!Sk$x82I#8ftN*? zlsLuClc9)VZ&{%{yyc0HU-@b^5ts0Jm=jZ*k4158y;71PUD&sFl1*rQw;Kj~U6R-C zaX6qX-Y!=~n2tEb%5xWvVNGMr`jiOM3a6lXj@uaM(!VMT-Z=ZJTbKN?pP8LxN3*z3 zo3kBuxy0#*8AZkeZWAL+AeO*N3B$5i0JFw+Pp5V`bpRET?>T2t=S8fHT1 zbZB@eN%5iCYYCc4OVC+iCihO4HBTT|9hku~1wA~>%+hHC#t58f2m4;u-~8!}VgJ`W zR5;acTjlJ52YF|&-Pt7{7G^H#6ycE~2uI(k-`4QqX@9-=O>u;s?v+lv6Qr*$c~Y2p zqSM^VLIHsKzl^%>g(G^6%bi|l&l0!Y?(=vYFcu~$E!4O5;D3E^Ak;i3`n=zwpIX8` z<$(zgz0&r$QD@WUSUE$AmEqD~K;HicWNgbA(olLc^#7ZRR~G%D@UepL3&!QI%zG>^ zBiE91dG`C+W3tZ9d_MDN-Ew?%5B-wMB24x>2j)s#nv3cx9C+15L?@WqH^nOQq5#%H zv9 zVA*JhFnw)`cA3z`7(CO)@e!t?O&d`s)QBstQpRl@`>5UcMB;5E*{~X_X4{-Lj|VnR zE(xJbkDJDco!(BbJU+tou~Pwgt#WI{vr zO|#182ouRBkh#)s@g=#Z_%~KZn2(XI&* zX1+~Vlt^kOttOTuBFu@KKCeXrlLKd7C`FjvHiani^e`;?LjTyA8@$!WAC#K(k=5Dd z!&@E7FXJQ3W1C_$2~6s=3AnNMKm7j|mHxTpqmo(0-9SATHR zh0`y)UU;uaY}HvIH5nUC)u#G-s6NY<2vg1`-R>_DT7`R%FXQ16rnXHdlSt-DXS%vs zdHzP}Un4JmB=MLerwFd4W6N_ROhlU^6e+OS1H2=fBTR9d&LOd9 zp|!Ezt>1k0b6fU5+?aS4CG}k1#XWL`!?vsf`Nj&Kkvpm9_wP|BSYDUwcYoU)YZ)^&dhi@0ZM64eqWs8ak^U9`85UE^4+B6-u ziX$S-J)7bcsjC3qD&_vOj(lgwx;q3{?R=93q3!vG>S|M?(aP%ozsVSKYUu-|!%A$$ z>x$kh?8^Up{YopmJUYS@%W1TRFZp`Wu=gr! zoATds=>J+@an>!*4eL+jIoanGQKWWA^GOYed_;ullT)00RC9)NKvj=tN0=lzMaj3l zL{VOOfO6NkqM^YLx27gO7o#q?J-w>1FrhGX`cNc893VSeJ+>|f<^$6w7oHCTtcg4} z!lcOsXx^-+(?avsN0?%_@Ub&^MDN)VCg@F@AX48qSSqiwKElMlg$H5q99U~3ObMJe z!6$G7&fGk|QF(AqfzA5|yBNzZ=2O+??Cu}|F{iCU>T&egkPl!YR}N2)4=PvQXayBh zxSAAUg5k6!B4Lrt4JWPDZ+fQUy|22@PEku@Wna>Arw6Ujx`d62FePz{o9`TAB0mG_ zf*TkfVKU+brced@UQgIvtw!T^q*~KasjS`!&r&ao13O@b22nR#TN{#M;H{zd)Q;K$ zX{p=a6JZMKWNf$u4b(TtUP*(WC224*!X)A8(iI6Hr&p}|xCm2+r?8zOdw%tfd}VOt zx~n2gAfC2CBq0L%=(H&@9}!`G?G&^~9&kV{A*Bd2Wv3{`GO7b+ws>fSd9qWCVyUGK z!XBTSi?LKO);P^bf1_s!IZ zDNw$;?^vI9E0=!lDi}KaPl=~*lHrj#^=5O8WNn#aZ8kNxG#i;qXp_-W+hT?AsYU_y zgb33eCiwkDTqlT%|B405t8ZPpWZ{uZ65~%QZr-QJ)LJ2#8qoqMw<0VTb!x3QG&C5R z&6b7Z`&hTa9@W^`Y?YfMOre-IiZ98bHVWmAdrs}#F%c$QOeY&(`Bbc3I{kBH!%nRF zWVPsE)teg7=?2R}so7$vhBa+tjnrUlHq0^CqbgV~k1)An8Z_doc4wH=;)EJu!o?IU zAIUxr_NE(Mt+&2%Uq3Orq*jF0y|hO{0iD<9kX#Ng9u4x?2$L?RQ2EMU7}T^_iE2US16xf_V7JR{&8R`CP6(NcoLZHa@d!FBF7Ad zDNMd14A!Ok^a}Wlh%mQdikt6J%5cvbIF5gWxeODCoDmWRaWBU|~yq&rrza{;*xtbJl$#Qf?8iEK1Ft2TNj~-JC{4#eE7$lU3QnRjp&Z!`$W@^ zxMIO-1%8c^tsBU@5$*1ANF50LA+J^;Iab2mfq6KP8W3NX*8yI9`*>3PVRv@h$fu-s zUy2t@m)q-~9${|TWM~3>Rc~y~jB310`S|HKzNm2&HMJcz86W%d8`OK(Y#23_64$_Oyicm zFGWfe1~V1SG;3)S_)^hquN$;f^r#3Euca;E%ObKC40IOp|Fd@{@J&^D`|rND-~tK= zTsCQ;rIfNGgwTc(ZJU~e%?;Buw2`!_Ndpwu3n*Df(LqFUP}Fh9eZ%FVAPDZ`zKc4J zqt1-4+c>WO=iHNpZx7PniZTzZTBHC3{(d-DcGV4_TSpy#<-5Yx)}TzKo^5L=T}SDV6EP>!<;+td}PfkQ7)Nbpn>PvenB)#x}XIH0lm4>mJMT#zB&|ySyyP z`Ty{gj+8!gi`EwIFVq&ClmAfOzw6dkO#{TrVX&Y0YP9261CFlz|GZhAx zEoVkgjfC|q4LSXjOK1Mr)%GpkB@OhuCUqa7GWhLT}btKkSgrv-o%OP6f<8~jxJ`}<5Hc; z#a@fE*QxA#mvQLM{-IgZ^l5$HkFNd=;(1*Axpt?+?o@Z(*pP&^5X*UDgNeqX&UTwn zDHNM5o@KU^v!YDP?!Kat6A%)6R@0)<(bGm<{m$J z9kl|MuYk?liVkwd+YQo=FW->P82eOW2Dt|Ixh|*84X!(?I8aC6vH|SAQt5-H{J-x~ z`sj-;Dtx1`Z-F^~bKZw}V{*^Sc{*okc4yYpStXe(GoH#gD&3NHYwAlW-z6vr(BprN zxWKEz`SR>OdmmfX8>RX(MvLieBv;mp>ccC%QOvK%mUxI;jt8Xcw%ⅅ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 P0F_>3s;;&)nC<@$JB8S2 From 85d59b372b69af6cd0e0115ac6fe2492fe8af96c Mon Sep 17 00:00:00 2001 From: nhall6 Date: Tue, 2 May 2023 09:04:28 -0400 Subject: [PATCH 04/20] Adding updated function to helpers to properly sort cohort generator attrition table --- R/helpers-cohortGeneratorDataPulls.R | 10 ++---- errorReportSql.txt | 30 ++++++++++++++++++ inst/doc/AddingShinyModules.pdf | Bin 243494 -> 0 bytes tests/resources/pvDatabase/phevaluator.sqlite | Bin 0 -> 1421312 bytes 4 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 errorReportSql.txt delete mode 100644 inst/doc/AddingShinyModules.pdf create mode 100644 tests/resources/pvDatabase/phevaluator.sqlite diff --git a/R/helpers-cohortGeneratorDataPulls.R b/R/helpers-cohortGeneratorDataPulls.R index a591ef77..d68dd52b 100644 --- a/R/helpers-cohortGeneratorDataPulls.R +++ b/R/helpers-cohortGeneratorDataPulls.R @@ -204,13 +204,9 @@ getCohortGenerationAttritionTable <- function( sep="") ) - ) %>% - dplyr::arrange("cdmSourceName", - "cohortDefinitionId", - "modeId", - "ruleSequence") - - return(attritionTableFinal) + ) + #newdata <- mtcars[order(mpg, -cyl),] + return(attritionTableFinal[order(attritionTableFinal$ruleSequence),]) } diff --git a/errorReportSql.txt b/errorReportSql.txt new file mode 100644 index 00000000..0baa3be8 --- /dev/null +++ b/errorReportSql.txt @@ -0,0 +1,30 @@ +DBMS: +postgresql + +Error: +org.postgresql.util.PSQLException: This connection has been closed. + +SQL: +ABORT + +R version: +R version 4.2.2 (2022-10-31 ucrt) + +Platform: +x86_64-w64-mingw32 + +Attached base packages: +- stats +- graphics +- grDevices +- utils +- datasets +- methods +- base + +Other attached packages: +- OhdsiShinyModules (1.1.0.9000) +- shiny (1.7.4) +- ShinyAppBuilder (1.1.2) +- dplyr (1.1.0) +- testthat (3.1.6) \ No newline at end of file diff --git a/inst/doc/AddingShinyModules.pdf b/inst/doc/AddingShinyModules.pdf deleted file mode 100644 index d97f662a5723840c57add4b856de592b649fec63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 243494 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ȋM8;;aj5_fUpddZN3BGHn|ZUxB=9 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