Create a static shiny app

Introduction

This vignette shows how to create static Shiny apps using the OmopViewer package. These static shiny apps are separated projects with their own ui, server, global and files to pre-process the results that later can easily later be customised and deployed. This functionality is adequate if what you are willing is to deploy a shiny, customise it later and/or be able to access this shiny app in the future. If what you want is to visualise the data briefly and you do not need to access to the shiny in the future, edit it or deploy it maybe the Dynamic app functionality is what are you searching for.

Loading Necessary Libraries and Data

In this vignette we will use simply to packages:

library(OmopViewer)
library(omopgenerics, warn.conflicts = FALSE)
library(shiny)

We’ll use the omopViewerResults mock data from this package for illustration:

# Inspect the structure of the sample data
summary(omopViewerResults)
#> A summarised_result object with 39307 rows, 96 different result_id, 1 different
#> cdm names, and 44 settings.
#> CDM names: synthea-covid19-200k.
#> Settings: result_type, package_name, package_version, group, strata,
#> additional, min_cell_count, analysis, analysis_censor_cohort_name,
#> analysis_complete_database_intervals, analysis_full_contribution,
#> analysis_outcome_washout, analysis_repeated_events, analysis_type, censor_date,
#> cohort_definition_id, cohort_table_name, denominator_age_group, …, type, and
#> unknown_indication_table.

Subsetting the Data

For this example, we’ll use a subset of the data containing specific result types:

result <- omopViewerResults |>
  filterSettings(
    result_type %in% c("summarise_omop_snapshot", "summarise_characteristics", "incidence")
  )

This filters the omopViewerResults data to include only entries where result_type is one of “summarise_omop_snapshot”, “summarise_characteristics”, or “incidence”.

summary(result)
#> A summarised_result object with 1245 rows, 20 different result_id, 1 different
#> cdm names, and 20 settings.
#> CDM names: synthea-covid19-200k.
#> Settings: result_type, package_name, package_version, group, strata,
#> additional, min_cell_count, analysis_censor_cohort_name,
#> analysis_complete_database_intervals, analysis_outcome_washout,
#> analysis_repeated_events, denominator_age_group,
#> denominator_days_prior_observation, denominator_end_date,
#> denominator_requirements_at_entry, denominator_sex, denominator_start_date,
#> denominator_target_cohort_name, denominator_time_at_risk, and table_name.

Generating the Shiny App

The exportStaticApp function generates a Shiny app from the prepared data. Using the default parameters, it only requires a directory to save the app and the processed data (a object):

dir <- tempdir()
exportStaticApp(result = result, directory = dir)
#> ℹ Processing data
#> ✔ Data processed: 3 panels idenfied: `summarise_omop_snapshot`,
#>   `summarise_characteristics`, and `incidence`.
#> ℹ Creating shiny from provided data
#> ✔ Shiny created in:
#>   /var/folders/pl/k11lm9710hlgl02nvzx4z9wr0000gp/T//RtmpnCIQ1o/shiny

Note that by default if executed in an interactive environment like R Studio the project will be opened in a separated window. Use open = FALSE if you do not wish to open the shiny app after generating it.

Generated shiny app

See that this created a new project shiny in the specified directory along with some files:

cat(list.files(path = here::here(dir, "shiny"), recursive = TRUE), sep = "\n")
#> background.md
#> data/preprocess.R
#> data/results.csv
#> functions.R
#> global.R
#> server.R
#> shiny.Rproj
#> ui.R
#> www/hds_logo.svg
#> www/ohdsi_logo.svg

Panels generated (panelDetails)

The shiny generated contained a total of 3 panels, this was determined by the argument panelDetails. Each element in panelDetails will be used to create a different panel in the shiny app. The package contains in total 28 predefined panels:

omopViewerPanels
#> $summarise_omop_snapshot
#> Snapshot (OmopViewer panel)
#> •  icon: clipboard-list
#> •  data: result_type: <summarise_omop_snapshot>
#> •  filters: 1 filters + 1 automatic filters
#> •  content: Tidy (DT); Table Snapshot (gt)
#> 
#> $summarise_observation_period
#> Observation period (OmopViewer panel)
#> •  icon: eye
#> •  data: result_type: <summarise_observation_period>
#> •  filters: 1 filters + 4 automatic filters
#> •  content: Tidy (DT); Table Observation period (gt); Plot Observation period (ui)
#> 
#> $summarise_clinical_records
#> Clinical records (OmopViewer panel)
#> •  icon: bars-staggered
#> •  data: result_type: <summarise_clinical_records>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Clinical records (gt)
#> 
#> $summarise_record_count
#> Record count (OmopViewer panel)
#> •  icon: signal
#> •  data: result_type: <summarise_record_count>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Plot record count (ui)
#> 
#> $summarise_missing_data
#> Missing data (OmopViewer panel)
#> •  icon: circle-exclamation
#> •  data: result_type: <summarise_missing_data>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Missing data (gt)
#> 
#> $summarise_in_observation
#> In Observation (OmopViewer panel)
#> •  icon: explosion
#> •  data: result_type: <summarise_in_observation>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Plot in observation (ui)
#> 
#> $orphan_code_use
#> Orphan codes (OmopViewer panel)
#> •  icon: magnifying-glass-arrow-right
#> •  data: result_type: <orphan_code_use>
#> •  filters: 1 filters + 4 automatic filters
#> •  content: Tidy (DT); Table Orphan codes (gt)
#> 
#> $cohort_code_use
#> Cohort code use (OmopViewer panel)
#> •  icon: chart-column
#> •  data: result_type: <cohort_code_use>
#> •  filters: 1 filters + 4 automatic filters
#> •  content: Tidy (DT); Table Cohort code use (gt)
#> 
#> $code_use
#> Code use (OmopViewer panel)
#> •  icon: chart-column
#> •  data: result_type: <code_use>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Code use (gt)
#> 
#> $achilles_code_use
#> Achilles code use (OmopViewer panel)
#> •  icon: chart-column
#> •  data: result_type: <achilles_code_use>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Achilles code use (gt)
#> 
#> $unmapped_codes
#> Unmapped codes (OmopViewer panel)
#> •  icon: chart-column
#> •  data: result_type: <unmapped_codes>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Unmapped codes (gt)
#> 
#> $summarise_cohort_overlap
#> Cohort Overlap (OmopViewer panel)
#> •  icon: circle-half-stroke
#> •  data: result_type: <summarise_cohort_overlap>
#> •  filters: 1 filters + 5 automatic filters
#> •  content: Tidy (DT); Table Overlap (gt); Plot Overlap (ui)
#> 
#> $summarise_cohort_count
#> Cohort Count (OmopViewer panel)
#> •  icon: users
#> •  data: result_type: <summarise_cohort_count>
#> •  filters: 1 filters + 4 automatic filters
#> •  content: Tidy (DT); Table Counts (gt); Plot Counts (ui)
#> 
#> $summarise_cohort_attrition
#> Cohort Attrition (OmopViewer panel)
#> •  icon: layer-group
#> •  data: result_type: <summarise_cohort_attrition>
#> •  filters: 1 filters + 2 automatic filters
#> •  content: Tidy (DT); Table Attrition (gt); Diagram (grViz)
#> 
#> $summarise_cohort_timing
#> Cohort Timing (OmopViewer panel)
#> •  icon: chart-simple
#> •  data: result_type: <summarise_cohort_timing>
#> •  filters: 1 filters + 2 automatic filters
#> •  content: Tidy (DT); Table Timing (gt); Plot Timing (ui)
#> 
#> $summarise_characteristics
#> Cohort Characteristics (OmopViewer panel)
#> •  icon: users-gear
#> •  data: result_type: <summarise_characteristics>
#> •  filters: 1 filters + 4 automatic filters
#> •  content: Tidy (DT); Table Characteristics (gt); Plot Characteristics (ui)
#> 
#> $summarise_large_scale_characteristics
#> Large Scale Characteristics (OmopViewer panel)
#> •  icon: arrow-up-right-dots
#> •  data: result_type: <summarise_large_scale_characteristics>
#> •  filters: 1 filters + 4 automatic filters
#> •  content: Table (reactable); Most common codes (gt); Plot Compared (plotly)
#> 
#> $incidence
#> Incidence (OmopViewer panel)
#> •  icon: chart-line
#> •  data: result_type: <incidence>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Incidence (gt); Plot Incidence (ui); Plot population (ui)
#> 
#> $incidence_attrition
#> Incidence Attrition (OmopViewer panel)
#> •  icon: layer-group
#> •  data: result_type: <incidence_attrition>
#> •  filters: 1 filters + 2 automatic filters
#> •  content: Tidy (DT); Table Incidence Attrition (gt)
#> 
#> $prevalence
#> Prevalence (OmopViewer panel)
#> •  icon: chart-column
#> •  data: result_type: <prevalence>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Prevalence (gt); Plot Prevalence (ui); Plot population (ui)
#> 
#> $prevalence_attrition
#> Prevalence Attrition (OmopViewer panel)
#> •  icon: layer-group
#> •  data: result_type: <prevalence_attrition>
#> •  filters: 1 filters + 2 automatic filters
#> •  content: Tidy (DT); Table Prevalence Attrition (gt)
#> 
#> $summarise_dose_coverage
#> Dose coverage (OmopViewer panel)
#> •  icon: pills
#> •  data: result_type: <summarise_dose_coverage>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Dose coverage (gt)
#> 
#> $summarise_proportion_of_patients_covered
#> Proportion of patients covered (OmopViewer panel)
#> •  icon: chart-gantt
#> •  data: result_type: <summarise_proportion_of_patients_covered>
#> •  filters: 1 filters + 3 automatic filters
#> •  content: Tidy (DT); Table PPC (gt); Plot PPC (ui)
#> 
#> $summarise_drug_restart
#> Drug Restart (OmopViewer panel)
#> •  icon: chart-gantt
#> •  data: result_type: <summarise_drug_restart>
#> •  filters: 1 filters + 3 automatic filters
#> •  content: Tidy (DT); Table Drug Restart (gt); Plot Drug Restart (ui)
#> 
#> $summarise_drug_utilisation
#> Drug Utilisation (OmopViewer panel)
#> •  icon: capsules
#> •  data: result_type: <summarise_drug_utilisation>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Drug Utilisation (gt); Plot Drug Utilisation (ui)
#> 
#> $summarise_indication
#> Indication (OmopViewer panel)
#> •  icon: disease
#> •  data: result_type: <summarise_indication>
#> •  filters: 1 filters + 7 automatic filters
#> •  content: Tidy (DT); Table Indication (gt); Plot Indication (ui)
#> 
#> $summarise_treatment
#> Treatments (OmopViewer panel)
#> •  icon: disease
#> •  data: result_type: <summarise_treatment>
#> •  filters: 1 filters + 7 automatic filters
#> •  content: Tidy (DT); Table Treatments (gt); Plot Treatment (ui)
#> 
#> $default
#> <result_type> (OmopViewer panel)
#> •  icon: folder
#> •  data: -no data-
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table (gt)

Each panel is associated with a determined result_type as showed in the print. By default, the function panelDetailsFromResult() groups the results by result_type and displays each result_type in a separate panel, if a pre-build panel does not exist for that result_type then the default panel is used. In our case we have pre-build panels that are:

panelDetailsFromResult(result = result)
#> $summarise_omop_snapshot
#> Snapshot (OmopViewer panel)
#> •  icon: clipboard-list
#> •  data: result_type: <summarise_omop_snapshot>
#> •  filters: 1 filters + 1 automatic filters
#> •  content: Tidy (DT); Table Snapshot (gt)
#> 
#> $summarise_characteristics
#> Cohort Characteristics (OmopViewer panel)
#> •  icon: users-gear
#> •  data: result_type: <summarise_characteristics>
#> •  filters: 1 filters + 4 automatic filters
#> •  content: Tidy (DT); Table Characteristics (gt); Plot Characteristics (ui)
#> 
#> $incidence
#> Incidence (OmopViewer panel)
#> •  icon: chart-line
#> •  data: result_type: <incidence>
#> •  filters: 1 filters + 6 automatic filters
#> •  content: Tidy (DT); Table Incidence (gt); Plot Incidence (ui); Plot population (ui)

Understanding Panel Details (panelDetails)

Each panel’s entry contains key information:

  1. Icon: Defines the icon displayed next to the panel, helping to visually distinguish between the different types of panels (e.g., clipboard-list, chart-line …).

  2. Data: the fields used in data will be passed to filterSettings() function to determine the data that will be included in that panel. Usually result_type is the most common way (e.g. panelDetails$data <- list(result_type = "incidence")), but other fields can be used for example: panelDetails$data <- list(result_type = "incidence", denominator_age_group = c("0 to 19", "20 to 39")) would only include the results obtained by the following code:

result |>
  filterSettings(
    result_type == "incidence" & 
      denominator_age_group %in% c("0 to 19", "20 to 39")
  ) 
  1. Filters: Filters (filters + automatic_filters) that allow users to refine the results within each panel. Each panel has its own defaults. For example, the default incidence panel includes automatic_filters: “settings”, “variable_name” and filters: “cdm_name”. This means that there will be a field for any column in settings, and variable_name and cdm_name columns.

  2. Content: Defines the types of content displayed in the panel, such as tables (DT, gt, reactable, …) and plots (ui, ggplot2, plotly, …). For example, the summarise_omop_snapshot panel includes a table displaying the snapshot data and a gt table generated from that snapshot data.

All of these elements - icon, data, filters, and content - can be customised by the user if needed. See more details in the customised_panels vignette.

Panel Structure (omopViewerPanels)

The arrangement of panels within the app is controlled by the panelStructure variable. This variable determines how panels are grouped into logical sections or tabs within the Shiny app. By default, the different panels are grouped by the package the produced the result object. In this case OmopSketch, CohortCharacteristics and IncidencePrevalence respectively.

Customise panelStructure

ps1 <- list(
  grp_1 = c("summarise_omop_snapshot", "incidence"),
  grp_2 = c("summarise_characteristics")
)

exportStaticApp(result = result, directory = tempdir(), panelStructure = ps1)

This custom panelStructure groups the “summarise_omop_snapshot” and “incidence” result types together in grp_1, while placing the “summarise_characteristics” result type in grp_2. You can pass this custom structure to exportStaticApp to organize the panels according to your preference.