diff --git a/.Rprofile b/.Rprofile new file mode 100644 index 0000000..81b960f --- /dev/null +++ b/.Rprofile @@ -0,0 +1 @@ +source("renv/activate.R") diff --git a/.github/workflows/deploy-dev.yaml b/.github/workflows/deploy-dev.yaml index cad3b60..a80bb82 100644 --- a/.github/workflows/deploy-dev.yaml +++ b/.github/workflows/deploy-dev.yaml @@ -17,20 +17,9 @@ jobs: with: use-public-rspm: true - # install dependency from DESCRIPTION file and some extra dependencies required to lunch to rsconnect + # install dependency from renv.lock - name: Install dependencies - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: | - any::rsconnect - any::BH - any::cpp11 - any::prettyunits - any::progress - - - name: Update MASS - shell: Rscript {0} - run: pak::pkg_install("MASS@7.3-60") + uses: r-lib/actions/setup-renv@v2 # file required by Connect for deploying content programmatically. - name: Generate manifest.json diff --git a/.gitignore b/.gitignore index 1fd9c88..7bc341f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,6 @@ inst/doc doc Meta scratch.R -renv/ -renv* -.Rprofile trial_scripts/ inst/app/www/*.docx secret-data/ \ No newline at end of file diff --git a/R/app_server.R b/R/app_server.R index 5b8bad3..95d7bb4 100644 --- a/R/app_server.R +++ b/R/app_server.R @@ -279,7 +279,7 @@ app_server <- function(input, output, session) { # modules---- ## add information to dashboard header ---- - mod_header_message_server("messageMenu", db_data, data_exists) + mod_header_message_server("messageMenu", pool, db_data, data_exists) ## combine ALL sub-modules---- mod_patient_experience_server("patient_experience_ui_1") diff --git a/R/fct_nhs_shiny_theme.R b/R/fct_nhs_shiny_theme.R index f7b2bc6..a26b48d 100644 --- a/R/fct_nhs_shiny_theme.R +++ b/R/fct_nhs_shiny_theme.R @@ -13,7 +13,7 @@ nhs_shiny_theme <- function() { fresh::adminlte_sidebar( width = "250px", dark_hover_bg = "#41B6E6", - dark_bg = "#D8DEE9", + dark_bg = "#E8EDEE", ), fresh::adminlte_global( content_bg = "#FFF", diff --git a/R/mod_data_management.R b/R/mod_data_management.R index fbb82d5..9f22984 100644 --- a/R/mod_data_management.R +++ b/R/mod_data_management.R @@ -66,7 +66,7 @@ mod_data_management_server <- function(id, db_conn, filter_data, data_exists) { "extra_variable_1" = get_golem_config("extra_variable_1"), "extra_variable_2" = get_golem_config("extra_variable_2"), "extra_variable_3" = get_golem_config("extra_variable_3"), - "pt_id" = "Patient ID" + "pt_id" = "Responder ID" ) ) @@ -233,11 +233,19 @@ mod_data_management_server <- function(id, db_conn, filter_data, data_exists) { rowselected <- dt_out$data[input$pat_table_rows_selected, "comment_id"] %>% unlist(use.name = FALSE) - # update database - query <- glue::glue_sql("UPDATE {`get_golem_config('trust_name')`} SET hidden = 1 WHERE comment_id IN ({ids*})", + # Instead of actually deleting the rows from the database, we Set the hidden flag to 1 (for all the deleted rows). + # Only rows with hidden == 0 are loaded into the dashboard. By doing this the data can be recovered if needed + query <- glue::glue_sql( + "UPDATE {`get_golem_config('trust_name')`} SET hidden = 1 WHERE comment_id IN ({ids*})", ids = rowselected, .con = db_conn ) DBI::dbExecute(db_conn, query) + + # Update the edit date for the deleted rows + query <- glue::glue_sql("UPDATE {`get_golem_config('trust_name')`} SET last_edit_date = {as.POSIXlt(Sys.time(), tz = 'UTC')} WHERE comment_id IN ({ids*})", + ids = rowselected, .con = db_conn + ) + DBI::dbExecute(db_conn, query) # update UI dt_out$data <- dt_out$data %>% dplyr::filter(!comment_id %in% rowselected) @@ -288,6 +296,9 @@ mod_data_management_server <- function(id, db_conn, filter_data, data_exists) { }) # Save (write edited data to source) #### + ### The save functionalities doesn't work for now. The handling of the list columns + ### (category and super_category) after users press ENTER is causing the issue + ### This will need revisiting if we need this data editing functionality observeEvent(input$save_to_db, { if (length(dt_out$index) < 1) { @@ -319,10 +330,17 @@ mod_data_management_server <- function(id, db_conn, filter_data, data_exists) { dplyr::rows_update(trust_db, dt_out$data %>% dplyr::filter(comment_id %in% unlist(dt_out$index)), by = "comment_id", copy = TRUE, unmatched = "ignore", in_place = TRUE ) + + # Update the edit date for the edited rows + query2 <- glue::glue_sql( + "UPDATE {`get_golem_config('trust_name')`} SET last_edit_date = {as.POSIXlt(Sys.time(), tz = 'UTC')} WHERE comment_id IN ({ids*})", + ids = unlist(dt_out$index, use.names = FALSE), .con = db_conn + ) + DBI::dbExecute(db_conn, query2) showModal(modalDialog( title = "Success!", - p(paste("Record of", length(dt_out$index), "Patient(s) have been successfully updated.")), + p(paste("Record of", length(dt_out$index), "Responder(s) have been successfully updated.")), em("Please refresh your browser to visualise the update"), easyClose = TRUE )) diff --git a/R/mod_demographics.R b/R/mod_demographics.R index c19a0d0..7127ea9 100644 --- a/R/mod_demographics.R +++ b/R/mod_demographics.R @@ -58,8 +58,9 @@ mod_demographics_server <- function(id, filter_data, data_exists) { } ), hr(), - h3("Categories with fewer than 10 individuals are excluded"), - p("The below chart shows the average percentage of maximum FFT score for each category."), + pre("The below chart shows the average percentage of FFT score for each group in the demographic feature.", + "Note: Categories with fewer than 10 individuals are excluded", + style = "background-color:#005EB8; color:#fff"), fluidRow( if (has_demography_1) { column(width, plotly::plotlyOutput(ns("compare_demography_1"))) diff --git a/R/mod_documentation_page.R b/R/mod_documentation_page.R index 8d186a7..a9ae577 100644 --- a/R/mod_documentation_page.R +++ b/R/mod_documentation_page.R @@ -42,10 +42,6 @@ mod_documentation_page_server <- function(id) { # table output$framework_table <- DT::renderDT({ - # framework <- readxl::read_excel(here::here(app_sys(), "app/www", "FFT-QDC Framework v5 - 20230428.xlsx"), - # sheet=2) %>% - # dplyr::arrange(Category, `Sub-category`) %>% - # dplyr::select(-Examples) # JaveScript code to collapse the table callback_js <- DT::JS( diff --git a/R/mod_header_message.R b/R/mod_header_message.R index 396b248..ee0b215 100644 --- a/R/mod_header_message.R +++ b/R/mod_header_message.R @@ -14,7 +14,7 @@ mod_header_message_ui <- function(id) { #' header_message Server Functions #' #' @noRd -mod_header_message_server <- function(id, db_data, data_exists) { +mod_header_message_server <- function(id, pool, db_data, data_exists) { moduleServer(id, function(input, output, session) { ns <- session$ns @@ -22,12 +22,14 @@ mod_header_message_server <- function(id, db_data, data_exists) { req(data_exists) isolate({ - last_upload_date <- unique(dplyr::pull(db_data, last_upload_date)) %>% na.omit() - last_upload_date <- if (length(last_upload_date) < 1) "No edit yet" else strptime(max(last_upload_date), format = "%Y-%m-%d %H:%M") - - last_date_edit <- unique(dplyr::pull(db_data, last_edit_date)) %>% na.omit() - last_date_edit <- if (length(last_date_edit) < 1) "No edit yet" else strptime(max(last_date_edit), format = "%Y-%m-%d %H:%M") - + last_upload_date <- DBI::dbGetQuery(pool, paste0("SELECT MAX(last_upload_date) FROM ", + get_golem_config('trust_name')))$`MAX(last_upload_date)` + last_upload_date <- if (is.na(last_upload_date)) "No edit yet" else paste(strptime(last_upload_date, format = "%Y-%m-%d %H:%M"), "GMT") + + last_date_edit <- DBI::dbGetQuery(pool, paste0("SELECT MAX(last_edit_date) FROM ", + get_golem_config('trust_name')))$`MAX(last_edit_date)` + last_date_edit <- if (is.na(last_date_edit)) "No edit yet" else paste(strptime(last_date_edit, format = "%Y-%m-%d %H:%M"), "GMT") + total_users <- db_data %>% dplyr::pull(pt_id) %>% unique() %>% @@ -41,17 +43,15 @@ mod_header_message_server <- function(id, db_data, data_exists) { messageItem( from = strong(total_users, style = "color: #005EB8;"), message = p("Total number of responders"), - icon = icon("users", style = "color: #005EB8;"), - time = Sys.Date() + icon = icon("users", style = "color: #005EB8;") ), messageItem( - from = strong(paste(last_upload_date), style = "color: #005EB8;"), + from = strong(last_upload_date, style = "color: #005EB8;"), message = p("Date data was last uploaded"), - icon = icon("file-pen", style = "color: #005EB8;"), - time = Sys.Date() + icon = icon("file-pen", style = "color: #005EB8;") ), messageItem( - from = strong(paste(last_date_edit), style = "color: #005EB8;"), + from = strong(last_date_edit, style = "color: #005EB8;"), message = p("Date data was last editted"), icon("calendar", style = "color: #005EB8;") ) diff --git a/R/mod_overlap_1.R b/R/mod_overlap_1.R index 8c543f9..148c61a 100644 --- a/R/mod_overlap_1.R +++ b/R/mod_overlap_1.R @@ -113,8 +113,7 @@ mod_overlap_1_server <- function(id, filter_data, input_select_super_category, i # server codes - the overlapping plot / upset plot ---- upset_data <- reactive({ filter_data()$single_labeled_filter_data %>% - dplyr::rename(value = category) %>% - one_hot_labels(column = "value") # apply one hot encoding to the single label column + one_hot_labels(column = "category") # apply one hot encoding to the single label column }) diff --git a/R/mod_report_builder.R b/R/mod_report_builder.R index 9bd9f2a..854dbba 100644 --- a/R/mod_report_builder.R +++ b/R/mod_report_builder.R @@ -118,7 +118,7 @@ mod_report_builder_server <- function(id, filter_data, params <- list( dates = dates, inputs = all_inputs(), - data = filter_data()$filter_data, + data = filter_data()$unique_data, single_label_data = filter_data()$single_labeled_filter_data, options = input$report_components, comment_1 = get_golem_config("comment_1"), diff --git a/R/tidy_upload.R b/R/tidy_upload.R index 112707d..0b46fba 100644 --- a/R/tidy_upload.R +++ b/R/tidy_upload.R @@ -188,6 +188,9 @@ upload_data <- function(data, conn, trust_id, write_db = TRUE) { ) ) |> dplyr::select(comment_id, comment_text, question_type) + } else{ + tidy_data <- tidy_data %>% + dplyr::filter(question_type == api_question_code(get_golem_config("comment_1"))) } cat("Making predictions for ", nrow(db_tidy), "comments from pxtextming API \n") diff --git a/README.md b/README.md index d98dda0..c6317bf 100644 --- a/README.md +++ b/README.md @@ -9,29 +9,130 @@ experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) -## Installation +## About -The development version is available from GitHub with: +This read me is based on version [0.7.2](https://github.com/CDU-data-science-team/experiencesdashboard/tree/0.7.2) + +The Experience dashboard is the front end tool (Shiny App) for the [Patient Experience Qualitative Data Categorisation project](https://cdu-data-science-team.github.io/PatientExperience-QDC/), funded by NHS England and hosted by Nottinghamshire Healthcare NHS Foundation Trust. It ties a machine learning back-end, the [Pxtextmining](https://cdu-data-science-team.github.io/pxtextmining/reference/API/API/), to the data source via an API and present metrics and graphs to help clinical staffs and managers quickly gain insight from patient experience data collected via the [NHS England Friends and Family Test](https://www.england.nhs.uk/fft/). + +A hosted version can be found [here](https://feedbackmatters.uk/rsconnect/experience_a/). Please note that some of the data has been modified for the purposes of +demonstration so it should NOT be used for reporting and is not accurate +in several important ways. + +### Folder Stucture + +This shiny app is built using [golem](https://engineering-shiny.org/golem.html#golem) and follows the advised [folder structure](https://engineering-shiny.org/golem.html#understanding-golem-app-structure). Because a golem application is an R package, this package incorporates all the benefits that comes with R package development and management process. + +Below is the folder structure and general description of the content of each important folder. ``` r -# install.packages("devtools") -devtools::install_github("CDU-data-science-team/experiencesdashboard") +fs::dir_tree(recurse = 0) ``` -## Running +``` +experiencesdashboard +├── .github/workflows +├── LICENSE +├── LICENSE.md +├── NEWS.md +├── README.md +├── CODE_OF_CONDUCT.md +├── app.R +├── DESCRIPTION +├── NAMESPACE +├── vignettes/ +├── R/ +├── dev/ +├── inst/ +├── tests/ +├── data/ +├── data-raw/ +├── man/ +└── rsconnect/ +``` + +------ + +| Name | Link | Description | +| ---- | ---- | ----------- | +| .github/workflows | [[Link](/.github/workflows)] | Github Action workflow files that automate the `R CMD check` and `deployment` process | +| app.R | [[Link](.)] | A `golem` file that contains the function to deploy the app on Posit platforms | +| DESCRIPTION | [[Link](.)] | A standard `R` package file containing series of metadata about the package including the package dependencies required to run the app. It forms a key part of the dependency management | +| NAMESPACE | [[Link](.)] | A standard `R` package file that contains functions to import and from which package and what functions to export | +| R/ | [[Link](R/)] | Standard `R` package folder holding all the package functions. It contains the functions required for the app core functionality such as the Server function `app_server.R`, UI function `app_ui.R`, all the modules `mod_*` files and utilitarian/business logic functions `fct_*.R` or `*utils*.R`/ or other `.R` files. It also contains an important file, `run_app.R`, which in turn contains the [`run_app()`](R/run_app.R) function that is called to launch the app | +| dev/ | [[Link](dev/)] | This folder contains utilitarian files used during development phase only and not core functionalities of the app. | +| inst/ | [[Link](inst)] | It contains the [`golem-config.yml`](inst/golem-config.yml) file and [`inst/app/www/`](inst/app/www/) files. [`inst/app/www/`](inst/app/www/) contains all files that are made available at application run time, while [`golem-config.yml`](inst/golem-config.yml) is an important yaml file to configure the app. | +| test/ | [[Link](test/)] | This folder contains the unit test infrastructure codes | +| data/ | [[Link](data/)] | Contains `.rda` data used by the app during runtime | +| data-raw/ | [[Link](data-raw/)] | It contains scripts to prepare dataset in the `data` folder. We also store some data in there that are not required at runtime | +| man/ | [[Link](man/)] | This is a standard `R` package folder containing automatically filled files for function documentations | +| rsconnect/ | [[Link](rsconnect/)] | Contains posit connect deployment files | + +### Built With + +- [golem](https://github.com/ThinkR-open/golem) +- [R](https://www.r-project.org/) +- [GitHub Actions](https://github.com/features/actions) +- [pxtextmining API](https://cdu-data-science-team.github.io/pxtextmining/reference/API/API/) + +## Using this Solution + +The implementation you will follow will depend on your use case and if you have access to the project database (for internal users). + +### A. Installation: + +Follow this approach if you have the right access to this project database -Run with: +#### Install the package + +{experiencesdashboard} is not currently on CRAN, so you will have to install it directly from Github. ``` r -library(experiencesdashboard) -run_app() +# install.packages("devtools") +devtools::install_github("CDU-data-science-team/experiencesdashboard") ``` -A hosted version can be found -[here](https://feedbackmatters.uk/rsconnect/experience_a/). -Please note the some of the data has been modified for the purposes of -demonstration so it should NOT be used for reporting and is not accurate -in several important ways. +#### Run the app +``` r +library(experiencesdashboard) # load the package +# usethis::edit_r_environ() # add the environment variables to connect to the database. see `get_pool()` +Sys.setenv("R_CONFIG_ACTIVE" = "my_config") # set the configuration to use inline with the `golem-config.yml` file +run_app() # run the app +``` + +### B. Local Implementation with your own data + +This package uses data from a database to populate the dashboard. The [`get_pool()`](R/fct_app_server-helpers.R) set up the DB connection. To use this package locally and on your own dataset, you will need to do the following: + +1. Clone the repo. [cloning-a-repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) + +2. Connect your data either + a. _**Via Database Connection**_: Set the environmental variables needed to establish a Database connection (see [`get_pool()`](R/fct_app_server-helpers.R)) + b. _**Via Local file**_: Read in your data into the `db_data` object in the [`app_server.R`](R/app_server.R) by replacing `db_data <- get_db_data(pool, get_golem_config("trust_name")))` with e.g. `db_data <- read.csv(''my_data_path.csv)`. With this you can safely ignore the preceding codes that creates the db connection. + +3. Set up your data/app configuration: If you need to use this app locally, then you will need to set up a configuration for your use case in the [`golem-config.yml`](inst/golem-config.yml) ([get help here](https://engineering-shiny.org/golem.html#golem-config)) + +5. choose your configuration: run `Sys.setenv("R_CONFIG_ACTIVE" = "my_config")` and run the app with `run_app()`. please see sample code in the [`run_dev.R`](dev/run_dev.R) + + +#### Format your data for the app + +Your data type must follow the schema in [Database table schema](data-raw\phase_2_schema.csv) before you can load the data into the app in step 2 above. Though not all the columns are required but to ignore any will depend on your configuration in step 3 above. + + i. You can safely ignore these columns without any modification: `'extra_variable_1', 'extra_variable_2', 'extra_variable_3'` + + ii. To ignore the following columns ` + 'location_2', 'location_3', 'sex', 'gender', 'age', 'ethnicity', 'sexuality', 'disability', 'religion',`, You need to set your configuration file accordingly. A sample configuation is this: + + ``` + my_config: + trust_name: my_config + comment_1: Why did you answer this way? + comment_2: What could be improved? + question_1: fft + location_1: Division + ``` +Please [get in touch](mailto:PHUdatascience@nottshc.nhs.uk) if you need additional help implementing this solution locally. ## Code of Conduct @@ -39,3 +140,7 @@ Please note that the experiencesdashboard project is released with a [Contributor Code of Conduct](https://contributor-covenant.org/version/2/0/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. + +## License + +Distributed under a MIT License. _See [LICENSE.md](/LICENSE.md) for more information._ diff --git a/data-raw/phase_2_schema.csv b/data-raw/phase_2_schema.csv new file mode 100644 index 0000000..d3b8589 --- /dev/null +++ b/data-raw/phase_2_schema.csv @@ -0,0 +1,25 @@ +"Field","Type","Null","Key","Default","Extra" +"comment_id","int unsigned","NO","PRI",NA,"auto_increment" +"date","date","YES","",NA,"" +"pt_id","int","YES","",NA,"" +"location_1","text","NO","",NA,"" +"location_2","text","YES","",NA,"" +"location_3","text","YES","",NA,"" +"comment_type","text","YES","",NA,"" +"comment_txt","text","YES","",NA,"" +"fft","double","YES","",NA,"" +"sex","text","YES","",NA,"" +"gender","text","YES","",NA,"" +"age","text","YES","",NA,"" +"ethnicity","text","YES","",NA,"" +"sexuality","text","YES","",NA,"" +"disability","text","YES","",NA,"" +"religion","text","YES","",NA,"" +"category","text","YES","",NA,"" +"super_category","text","YES","",NA,"" +"last_upload_date","datetime","NO","",NA,"" +"last_edit_date","datetime","YES","",NA,"" +"extra_variable_1","text","YES","",NA,"" +"extra_variable_2","text","YES","",NA,"" +"extra_variable_3","text","YES","",NA,"" +"hidden","int","NO","","0","" diff --git a/inst/app/www/report.Rmd b/inst/app/www/report.Rmd index fc4423c..2883419 100644 --- a/inst/app/www/report.Rmd +++ b/inst/app/www/report.Rmd @@ -20,8 +20,7 @@ knitr::opts_chunk$set(echo = FALSE) # produce dataset for unique individuals -unique_data <- params$data %>% - dplyr::distinct(pt_id, .keep_all = TRUE) +unique_data <- params$data # where is this report for? @@ -148,7 +147,7 @@ if("fft_graph" %in% params$options){ # confirm we have at least 10 groups to plot no_group <- graph_data %>% - dplyr::pull() %>% + dplyr::pull(1) %>% unique() %>% length() diff --git a/inst/golem-config.yml b/inst/golem-config.yml index ded6727..2cdfa06 100644 --- a/inst/golem-config.yml +++ b/inst/golem-config.yml @@ -113,14 +113,9 @@ trust_NUH: trust_LPT: trust_name: trust_LPT comment_1: Why did you answer this way? - comment_2: What could be improved? question_1: fft - location_1: Division - location_2: Directorate - location_3: Team - demography_1: sex - demography_2: age - demography_3: ethnicity + location_1: Directorate + location_2: Location trust_GOSH: trust_name: trust_GOSH comment_1: Why did you answer this way? diff --git a/renv.lock b/renv.lock new file mode 100644 index 0000000..e93a963 --- /dev/null +++ b/renv.lock @@ -0,0 +1,2639 @@ +{ + "R": { + "Version": "4.3.0", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://packagemanager.posit.co/cran/latest" + } + ] + }, + "Packages": { + "BH": { + "Package": "BH", + "Version": "1.81.0-1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "68122010f01c4dcfbe58ce7112f2433d" + }, + "ComplexUpset": { + "Package": "ComplexUpset", + "Version": "1.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "ggplot2", + "patchwork", + "scales" + ], + "Hash": "33a906b96379ddbd41dcb85f80f4fed9" + }, + "DBI": { + "Package": "DBI", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "b2866e62bab9378c3cc9476a1954226b" + }, + "DT": { + "Package": "DT", + "Version": "0.28", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "crosstalk", + "htmltools", + "htmlwidgets", + "jquerylib", + "jsonlite", + "magrittr", + "promises" + ], + "Hash": "ab745834dfae7eaf71dd0b90f3b66759" + }, + "MASS": { + "Package": "MASS", + "Version": "7.3-60", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats", + "utils" + ], + "Hash": "a56a6365b3fa73293ea8d084be0d9bb0" + }, + "Matrix": { + "Package": "Matrix", + "Version": "1.6-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "31262fd18481fab05c5e7258dac163ca" + }, + "NHSRdatasets": { + "Package": "NHSRdatasets", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "tibble" + ], + "Hash": "02c9417179975a83e37329f40c451591" + }, + "NHSRplotthedots": { + "Package": "NHSRplotthedots", + "Version": "0.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "NHSRdatasets", + "assertthat", + "crayon", + "dplyr", + "ggplot2", + "grid", + "rlang", + "scales", + "utils" + ], + "Hash": "aa3cf9998167e64289ba1c3c88a93194" + }, + "NLP": { + "Package": "NLP", + "Version": "0.2-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "94b0a21c13933f61cf6272a192964624" + }, + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "RColorBrewer": { + "Package": "RColorBrewer", + "Version": "1.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "45f0398006e83a5b10b72a90663d8d8c" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "ae6cbbe1492f4de79c45fce06f967ce8" + }, + "SnowballC": { + "Package": "SnowballC", + "Version": "0.7.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "46da3912f69e3e6258a033802c4af32e" + }, + "anytime": { + "Package": "anytime", + "Version": "0.3.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "BH", + "R", + "Rcpp" + ], + "Hash": "74a64813f17b492da9c6afda6b128e3d" + }, + "askpass": { + "Package": "askpass", + "Version": "1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "sys" + ], + "Hash": "e8a22846fff485f0be3770c2da758713" + }, + "assertr": { + "Package": "assertr", + "Version": "3.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "dplyr", + "methods", + "rlang", + "stats", + "utils" + ], + "Hash": "e500845b6d773cecb7864ec21eb84081" + }, + "assertthat": { + "Package": "assertthat", + "Version": "0.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "50c838a310445e954bc13f26f26a6ecf" + }, + "attachment": { + "Package": "attachment", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "desc", + "glue", + "knitr", + "magrittr", + "rmarkdown", + "roxygen2", + "stats", + "stringr", + "utils", + "withr", + "yaml" + ], + "Hash": "4dbcfe629c3b357eb16158af0f20ea29" + }, + "attempt": { + "Package": "attempt", + "Version": "0.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "rlang" + ], + "Hash": "d7421bb5dfeb2676b9e4a5a60c2fcfd2" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bit": { + "Package": "bit", + "Version": "4.0.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "d242abec29412ce988848d0294b208fd" + }, + "bit64": { + "Package": "bit64", + "Version": "4.0.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit", + "methods", + "stats", + "utils" + ], + "Hash": "9fe98599ca456d6552421db0d6772d8f" + }, + "blob": { + "Package": "blob", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "rlang", + "vctrs" + ], + "Hash": "40415719b5a479b87949f3aa0aee737c" + }, + "brew": { + "Package": "brew", + "Version": "1.0-8", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "d69a786e85775b126bddbee185ae6084" + }, + "brio": { + "Package": "brio", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "976cf154dfb043c012d87cddd8bca363" + }, + "bslib": { + "Package": "bslib", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "1b117970533deb6d4e992c1b34e9d905" + }, + "cachem": { + "Package": "cachem", + "Version": "1.0.8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "c35768291560ce302c0a6589f92e837d" + }, + "callr": { + "Package": "callr", + "Version": "3.7.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "9b2191ede20fa29828139b9900922e51" + }, + "cellranger": { + "Package": "cellranger", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "rematch", + "tibble" + ], + "Hash": "f61dbaec772ccd2e17705c1e872e9e7c" + }, + "cli": { + "Package": "cli", + "Version": "3.6.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "89e6d8219950eac806ae0c489052048a" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" + }, + "colorspace": { + "Package": "colorspace", + "Version": "2.1-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "methods", + "stats" + ], + "Hash": "f20c47fd52fae58b4e377c37bb8c335b" + }, + "commonmark": { + "Package": "commonmark", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "d691c61bff84bd63c383874d2d0c3307" + }, + "config": { + "Package": "config", + "Version": "0.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "yaml" + ], + "Hash": "31d77b09f63550cee9ecb5a08bf76e8f" + }, + "covr": { + "Package": "covr", + "Version": "3.6.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "crayon", + "digest", + "httr", + "jsonlite", + "methods", + "rex", + "stats", + "utils", + "withr", + "yaml" + ], + "Hash": "a0d8f9a55add5311d48227b6f7f38e34" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.4.4", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "3f7d8664d7324406cd10cd650ad85e5f" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "e8a1e41acf02548751f45c718d55aa6a" + }, + "credentials": { + "Package": "credentials", + "Version": "1.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass", + "curl", + "jsonlite", + "openssl", + "sys" + ], + "Hash": "93762d0a34d78e6a025efdbfb5c6bb41" + }, + "crosstalk": { + "Package": "crosstalk", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "htmltools", + "jsonlite", + "lazyeval" + ], + "Hash": "6aa54f69598c32177e920eb3402e8293" + }, + "curl": { + "Package": "curl", + "Version": "5.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "2118af9cb164c8d2dddc7b89eaf732d9" + }, + "data.table": { + "Package": "data.table", + "Version": "1.14.8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "b4c06e554f33344e044ccd7fdca750a9" + }, + "data.validator": { + "Package": "data.validator", + "Version": "0.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "assertr", + "dplyr", + "htmltools", + "htmlwidgets", + "knitr", + "purrr", + "rlang", + "rmarkdown", + "shiny.semantic", + "tibble", + "tidyr", + "utils" + ], + "Hash": "f8f5773f5b999573999a6ce831f953f2" + }, + "datamods": { + "Package": "datamods", + "Version": "1.4.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "data.table", + "htmltools", + "htmlwidgets", + "phosphoricons", + "reactable", + "readxl", + "rio", + "rlang", + "shiny", + "shinyWidgets", + "shinybusy", + "tibble", + "tools", + "writexl" + ], + "Hash": "bfe71f2780b426763072e059457f7650" + }, + "dbplyr": { + "Package": "dbplyr", + "Version": "2.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "DBI", + "R", + "R6", + "blob", + "cli", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "purrr", + "rlang", + "tibble", + "tidyr", + "tidyselect", + "utils", + "vctrs", + "withr" + ], + "Hash": "d6fd1b1440c1cacc6623aaa4e9fe352b" + }, + "desc": { + "Package": "desc", + "Version": "1.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "rprojroot", + "utils" + ], + "Hash": "6b9602c7ebbe87101a9c8edb6e8b6d21" + }, + "devtools": { + "Package": "devtools", + "Version": "2.4.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "desc", + "ellipsis", + "fs", + "lifecycle", + "memoise", + "miniUI", + "pkgbuild", + "pkgdown", + "pkgload", + "profvis", + "rcmdcheck", + "remotes", + "rlang", + "roxygen2", + "rversions", + "sessioninfo", + "stats", + "testthat", + "tools", + "urlchecker", + "usethis", + "utils", + "withr" + ], + "Hash": "ea5bc8b4a6a01e4f12d98b58329930bb" + }, + "diffobj": { + "Package": "diffobj", + "Version": "0.3.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "crayon", + "methods", + "stats", + "tools", + "utils" + ], + "Hash": "bcaa8b95f8d7d01a5dedfd959ce88ab8" + }, + "digest": { + "Package": "digest", + "Version": "0.6.33", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "b18a9cf3c003977b0cc49d5e76ebe48d" + }, + "downlit": { + "Package": "downlit", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "brio", + "desc", + "digest", + "evaluate", + "fansi", + "memoise", + "rlang", + "vctrs", + "withr", + "yaml" + ], + "Hash": "14fa1f248b60ed67e1f5418391a17b14" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "dea6970ff715ca541c387de363ff405e" + }, + "ellipsis": { + "Package": "ellipsis", + "Version": "0.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "rlang" + ], + "Hash": "bb0eec2fe32e88d9e2836c2f73ea2077" + }, + "evaluate": { + "Package": "evaluate", + "Version": "0.21", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "d59f3b464e8da1aef82dc04b588b8dfb" + }, + "experiencesdashboard": { + "Package": "experiencesdashboard", + "Version": "0.7.0", + "Source": "GitHub", + "Remotes": "nhs-r-community/NHSRtheme", + "RemoteType": "github", + "RemoteHost": "api.github.com", + "RemoteRepo": "experiencesdashboard", + "RemoteUsername": "CDU-data-science-team", + "RemoteRef": "HEAD", + "RemoteSha": "b714102f6422a93e228738130adc854850fb86a3", + "Requirements": [ + "ComplexUpset", + "DBI", + "DT", + "NHSRplotthedots", + "R", + "SnowballC", + "config", + "datamods", + "dbplyr", + "dplyr", + "fresh", + "ggplot2", + "glue", + "golem", + "here", + "htmltools", + "httr", + "jsonlite", + "lubridate", + "magrittr", + "memoise", + "odbc", + "pander", + "plotly", + "purrr", + "rlang", + "rmarkdown", + "shiny", + "shinycssloaders", + "shinydashboard", + "stringr", + "tidyr", + "tidyselect", + "tm", + "writexl", + "xml2" + ], + "Hash": "12675ab1dadd631342662b7c4cad91f3" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "1d9e7ad3c8312a192dea7d3db0274fde" + }, + "farver": { + "Package": "farver", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "8106d78941f34855c440ddb946b8f7a5" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "f7736a18de97dea803bde0a2daaafb27" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "1e22b8cabbad1eae951a75e9f8b52378" + }, + "forcats": { + "Package": "forcats", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "tibble" + ], + "Hash": "1a0a9a3d5083d0d573c4214576f1e690" + }, + "foreign": { + "Package": "foreign", + "Version": "0.8-84", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods", + "stats", + "utils" + ], + "Hash": "467ec0ca895d4e61a22cfbac9bccddf8" + }, + "fresh": { + "Package": "fresh", + "Version": "0.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools", + "rstudioapi", + "sass", + "shiny" + ], + "Hash": "fa54367040deb4537da49b7ac0ee5770" + }, + "fs": { + "Package": "fs", + "Version": "1.6.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "94af08e0aa9675a16fadbb3aaaa90d2a" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "gert": { + "Package": "gert", + "Version": "1.9.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass", + "credentials", + "openssl", + "rstudioapi", + "sys", + "zip" + ], + "Hash": "9122b3958e749badb5c939f498038b57" + }, + "ggplot2": { + "Package": "ggplot2", + "Version": "3.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "cli", + "glue", + "grDevices", + "grid", + "gtable", + "isoband", + "lifecycle", + "mgcv", + "rlang", + "scales", + "stats", + "tibble", + "vctrs", + "withr" + ], + "Hash": "3a147ee02e85a8941aad9909f1b43b7b" + }, + "gh": { + "Package": "gh", + "Version": "1.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "gitcreds", + "httr2", + "ini", + "jsonlite", + "rlang" + ], + "Hash": "03533b1c875028233598f848fda44c4c" + }, + "gitcreds": { + "Package": "gitcreds", + "Version": "0.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ab08ac61f3e1be454ae21911eb8bc2fe" + }, + "glue": { + "Package": "glue", + "Version": "1.6.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "4f2596dfb05dac67b9dc558e5c6fba2e" + }, + "golem": { + "Package": "golem", + "Version": "0.4.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "attempt", + "config", + "here", + "htmltools", + "rlang", + "shiny", + "utils", + "yaml" + ], + "Hash": "dc12172dc35c6c80e18b430dc546fc24" + }, + "gtable": { + "Package": "gtable", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "grid", + "lifecycle", + "rlang" + ], + "Hash": "b44addadb528a0d227794121c00572a0" + }, + "haven": { + "Package": "haven", + "Version": "2.5.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "cpp11", + "forcats", + "hms", + "lifecycle", + "methods", + "readr", + "rlang", + "tibble", + "tidyselect", + "vctrs" + ], + "Hash": "9b302fe352f9cfc5dcf0a4139af3a565" + }, + "here": { + "Package": "here", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "rprojroot" + ], + "Hash": "24b224366f9c2e7534d2344d10d59211" + }, + "highr": { + "Package": "highr", + "Version": "0.10", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "06230136b2d2b9ba5805e1963fa6e890" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "ellipsis", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "ba0240784ad50a62165058a27459304a" + }, + "htmlwidgets": { + "Package": "htmlwidgets", + "Version": "1.6.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "htmltools", + "jsonlite", + "knitr", + "rmarkdown", + "yaml" + ], + "Hash": "a865aa85bcb2697f47505bfd70422471" + }, + "httpuv": { + "Package": "httpuv", + "Version": "1.6.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "Rcpp", + "later", + "promises", + "utils" + ], + "Hash": "838602f54e32c1a0f8cc80708cefcefa" + }, + "httr": { + "Package": "httr", + "Version": "1.4.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "curl", + "jsonlite", + "mime", + "openssl" + ], + "Hash": "7e5e3cbd2a7bc07880c94e22348fb661" + }, + "httr2": { + "Package": "httr2", + "Version": "0.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "curl", + "glue", + "magrittr", + "openssl", + "rappdirs", + "rlang", + "withr" + ], + "Hash": "193bb297368afbbb42dc85784a46b36e" + }, + "hunspell": { + "Package": "hunspell", + "Version": "3.0.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "digest" + ], + "Hash": "656219b6f3f605499d7cdbe208656639" + }, + "ini": { + "Package": "ini", + "Version": "0.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "6154ec2223172bce8162d4153cda21f7" + }, + "isoband": { + "Package": "isoband", + "Version": "0.2.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grid", + "utils" + ], + "Hash": "0080607b4a1a7b28979aecef976d8bc2" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "266a20443ca13c65688b2116d5220f76" + }, + "knitr": { + "Package": "knitr", + "Version": "1.43", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "9775eb076713f627c07ce41d8199d8f6" + }, + "labeling": { + "Package": "labeling", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "graphics", + "stats" + ], + "Hash": "3d5108641f47470611a32d0bdf357a72" + }, + "later": { + "Package": "later", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Rcpp", + "rlang" + ], + "Hash": "40401c9cf2bc2259dfe83311c9384710" + }, + "lattice": { + "Package": "lattice", + "Version": "0.21-8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "stats", + "utils" + ], + "Hash": "0b8a6d63c8770f02a8b5635f3c431e6b" + }, + "lazyeval": { + "Package": "lazyeval", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "d908914ae53b04d4c0c0fd72ecc35370" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "001cecbeac1cff9301bdc3775ee46a86" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "e25f18436e3efd42c7c590a1c4c15390" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mgcv": { + "Package": "mgcv", + "Version": "1.9-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "Matrix", + "R", + "graphics", + "methods", + "nlme", + "splines", + "stats", + "utils" + ], + "Hash": "086028ca0460d0c368028d3bda58f31b" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "miniUI": { + "Package": "miniUI", + "Version": "0.1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools", + "shiny", + "utils" + ], + "Hash": "fec5f52652d60615fdb3957b3d74324a" + }, + "mockery": { + "Package": "mockery", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "testthat" + ], + "Hash": "c28ce5f7c2293d76b91657f3fe30c50b" + }, + "munsell": { + "Package": "munsell", + "Version": "0.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "colorspace", + "methods" + ], + "Hash": "6dfe8bf774944bd5595785e3229d8771" + }, + "nlme": { + "Package": "nlme", + "Version": "3.1-162", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "graphics", + "lattice", + "stats", + "utils" + ], + "Hash": "0984ce8da8da9ead8643c5cbbb60f83e" + }, + "odbc": { + "Package": "odbc", + "Version": "1.3.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "DBI", + "R", + "Rcpp", + "bit64", + "blob", + "hms", + "methods", + "rlang" + ], + "Hash": "d42c4f464e14ce542718a17525265e05" + }, + "openssl": { + "Package": "openssl", + "Version": "2.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass" + ], + "Hash": "273a6bb4a9844c296a459d2176673270" + }, + "openxlsx": { + "Package": "openxlsx", + "Version": "4.2.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "grDevices", + "methods", + "stats", + "stringi", + "utils", + "zip" + ], + "Hash": "c03b4c18d42da881fb8e15a085c2b9d6" + }, + "packrat": { + "Package": "packrat", + "Version": "0.9.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "tools", + "utils" + ], + "Hash": "481428983c19a7c443f7ea1beff0a2de" + }, + "pander": { + "Package": "pander", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "Rcpp", + "digest", + "grDevices", + "graphics", + "methods", + "stats", + "tools", + "utils" + ], + "Hash": "737924139a1e4fc96356ff377c754c35" + }, + "parsedate": { + "Package": "parsedate", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "7f5024cc7af45eeecef657fa62beb568" + }, + "patchwork": { + "Package": "patchwork", + "Version": "1.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "ggplot2", + "grDevices", + "graphics", + "grid", + "gtable", + "stats", + "utils" + ], + "Hash": "63b611e9d909a9ed057639d9c3b77152" + }, + "phosphoricons": { + "Package": "phosphoricons", + "Version": "0.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools" + ], + "Hash": "9c7e25aa49773b758f0a96245577a6d9" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgbuild": { + "Package": "pkgbuild", + "Version": "1.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "callr", + "cli", + "crayon", + "desc", + "prettyunits", + "processx", + "rprojroot" + ], + "Hash": "beb25b32a957a22a5c301a9e441190b3" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "pkgdown": { + "Package": "pkgdown", + "Version": "2.0.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "callr", + "cli", + "desc", + "digest", + "downlit", + "fs", + "httr", + "jsonlite", + "magrittr", + "memoise", + "purrr", + "ragg", + "rlang", + "rmarkdown", + "tibble", + "whisker", + "withr", + "xml2", + "yaml" + ], + "Hash": "16fa15449c930bf3a7761d3c68f8abf9" + }, + "pkgload": { + "Package": "pkgload", + "Version": "1.3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "crayon", + "desc", + "fs", + "glue", + "methods", + "rlang", + "rprojroot", + "utils", + "withr" + ], + "Hash": "a7f498a1b2a4a6816148e498509f6e1d" + }, + "plotly": { + "Package": "plotly", + "Version": "4.10.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "RColorBrewer", + "base64enc", + "crosstalk", + "data.table", + "digest", + "dplyr", + "ggplot2", + "htmltools", + "htmlwidgets", + "httr", + "jsonlite", + "lazyeval", + "magrittr", + "promises", + "purrr", + "rlang", + "scales", + "tibble", + "tidyr", + "tools", + "vctrs", + "viridisLite" + ], + "Hash": "6c00a09ba7d34917d9a3e28b15dd74e3" + }, + "pool": { + "Package": "pool", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "DBI", + "R", + "R6", + "later", + "methods", + "rlang", + "withr" + ], + "Hash": "52d086ff1a2ccccbae6d462cb0773835" + }, + "praise": { + "Package": "praise", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "a555924add98c99d2f411e37e7d25e9f" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "95ef9167b75dde9d2ccc3c7528393e7e" + }, + "processx": { + "Package": "processx", + "Version": "3.8.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "3efbd8ac1be0296a46c55387aeace0f3" + }, + "profvis": { + "Package": "profvis", + "Version": "0.3.8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmlwidgets", + "purrr", + "rlang", + "stringr", + "vctrs" + ], + "Hash": "aa5a3864397ce6ae03458f98618395a1" + }, + "progress": { + "Package": "progress", + "Version": "1.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "14dc9f7a3c91ebb14ec5bb9208a07061" + }, + "promises": { + "Package": "promises", + "Version": "1.2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "Rcpp", + "later", + "magrittr", + "rlang", + "stats" + ], + "Hash": "4ab2c43adb4d4699cf3690acd378d75d" + }, + "ps": { + "Package": "ps", + "Version": "1.7.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "709d852d33178db54b17c722e5b1e594" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "d71c815267c640f17ddbf7f16144b4bb" + }, + "ragg": { + "Package": "ragg", + "Version": "1.2.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "systemfonts", + "textshaping" + ], + "Hash": "690bc058ea2b1b8a407d3cfe3dce3ef9" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "rcmdcheck": { + "Package": "rcmdcheck", + "Version": "1.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "callr", + "cli", + "curl", + "desc", + "digest", + "pkgbuild", + "prettyunits", + "rprojroot", + "sessioninfo", + "utils", + "withr", + "xopen" + ], + "Hash": "8f25ebe2ec38b1f2aef3b0d2ef76f6c4" + }, + "reactR": { + "Package": "reactR", + "Version": "0.4.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "75389c8091eb14ee21c6bc87a88b3809" + }, + "reactable": { + "Package": "reactable", + "Version": "0.4.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "digest", + "htmltools", + "htmlwidgets", + "jsonlite", + "reactR" + ], + "Hash": "6069eb2a6597963eae0605c1875ff14c" + }, + "readr": { + "Package": "readr", + "Version": "2.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "clipr", + "cpp11", + "crayon", + "hms", + "lifecycle", + "methods", + "rlang", + "tibble", + "tzdb", + "utils", + "vroom" + ], + "Hash": "b5047343b3825f37ad9d3b5d89aa1078" + }, + "readxl": { + "Package": "readxl", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cellranger", + "cpp11", + "progress", + "tibble", + "utils" + ], + "Hash": "8cf9c239b96df1bbb133b74aef77ad0a" + }, + "rematch": { + "Package": "rematch", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "c66b930d20bb6d858cd18e1cebcfae5c" + }, + "rematch2": { + "Package": "rematch2", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tibble" + ], + "Hash": "76c9e04c712a05848ae7a23d2f170a40" + }, + "remotes": { + "Package": "remotes", + "Version": "2.4.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods", + "stats", + "tools", + "utils" + ], + "Hash": "63d15047eb239f95160112bcadc4fcb9" + }, + "renv": { + "Package": "renv", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "c321cd99d56443dbffd1c9e673c0c1a2" + }, + "rex": { + "Package": "rex", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "lazyeval" + ], + "Hash": "ae34cd56890607370665bee5bd17812f" + }, + "rhub": { + "Package": "rhub", + "Version": "1.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "assertthat", + "callr", + "cli", + "crayon", + "desc", + "digest", + "httr", + "jsonlite", + "parsedate", + "pillar", + "prettyunits", + "processx", + "rappdirs", + "rcmdcheck", + "rematch", + "tibble", + "utils", + "uuid", + "whoami", + "withr" + ], + "Hash": "e0880f6783f720d136755fb92d63c78b" + }, + "rio": { + "Package": "rio", + "Version": "0.5.29", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "curl", + "data.table", + "foreign", + "haven", + "openxlsx", + "readxl", + "stats", + "tibble", + "tools", + "utils" + ], + "Hash": "9ee902fb3142b159f60bdb4eaf763a3a" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "a85c767b55f0bf9b7ad16c6d7baee5bb" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.23", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "stringr", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "79f14e53725f28900d936f692bfdd69f" + }, + "roxygen2": { + "Package": "roxygen2", + "Version": "7.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "brew", + "cli", + "commonmark", + "cpp11", + "desc", + "knitr", + "methods", + "pkgload", + "purrr", + "rlang", + "stringi", + "stringr", + "utils", + "withr", + "xml2" + ], + "Hash": "7b153c746193b143c14baa072bae4e27" + }, + "rprojroot": { + "Package": "rprojroot", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "1de7ab598047a87bba48434ba35d497d" + }, + "rsconnect": { + "Package": "rsconnect", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "curl", + "digest", + "jsonlite", + "lifecycle", + "openssl", + "packrat", + "renv", + "rlang", + "rstudioapi", + "tools", + "yaml" + ], + "Hash": "f334565fbf2db469bfb800aa178254d1" + }, + "rstudioapi": { + "Package": "rstudioapi", + "Version": "0.15.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "5564500e25cffad9e22244ced1379887" + }, + "rversions": { + "Package": "rversions", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "curl", + "utils", + "xml2" + ], + "Hash": "a9881dfed103e83f9de151dc17002cd1" + }, + "sass": { + "Package": "sass", + "Version": "0.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "6bd4d33b50ff927191ec9acbf52fd056" + }, + "scales": { + "Package": "scales", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "RColorBrewer", + "farver", + "labeling", + "lifecycle", + "munsell", + "rlang", + "viridisLite" + ], + "Hash": "906cb23d2f1c5680b8ce439b44c6fa63" + }, + "sessioninfo": { + "Package": "sessioninfo", + "Version": "1.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "tools", + "utils" + ], + "Hash": "3f9796a8d0a0e8c6eb49a4b029359d1f" + }, + "shiny": { + "Package": "shiny", + "Version": "1.7.4.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "bslib", + "cachem", + "commonmark", + "crayon", + "ellipsis", + "fastmap", + "fontawesome", + "glue", + "grDevices", + "htmltools", + "httpuv", + "jsonlite", + "later", + "lifecycle", + "methods", + "mime", + "promises", + "rlang", + "sourcetools", + "tools", + "utils", + "withr", + "xtable" + ], + "Hash": "f7f201522eedbc95f10ef323b100ff29" + }, + "shiny.semantic": { + "Package": "shiny.semantic", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "glue", + "grDevices", + "htmltools", + "htmlwidgets", + "jsonlite", + "magrittr", + "purrr", + "shiny", + "stats" + ], + "Hash": "c211db8327fd25b8f73ba8ef5afd3f2d" + }, + "shinyWidgets": { + "Package": "shinyWidgets", + "Version": "0.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "anytime", + "bslib", + "grDevices", + "htmltools", + "jsonlite", + "rlang", + "sass", + "shiny" + ], + "Hash": "fd889b32caa37b8ed9b1e9e7ef1564bc" + }, + "shinybusy": { + "Package": "shinybusy", + "Version": "0.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools", + "htmlwidgets", + "jsonlite", + "shiny" + ], + "Hash": "cf0b9330f677599316a4b36682081e8b" + }, + "shinycssloaders": { + "Package": "shinycssloaders", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "digest", + "glue", + "grDevices", + "shiny" + ], + "Hash": "f39bb3c44a9b496723ec7e86f9a771d8" + }, + "shinydashboard": { + "Package": "shinydashboard", + "Version": "0.7.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "promises", + "shiny", + "utils" + ], + "Hash": "e418b532e9bb4eb22a714b9a9f1acee7" + }, + "slam": { + "Package": "slam", + "Version": "0.1-50", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "stats" + ], + "Hash": "e25793551cbdb843154152e5ee88cbd6" + }, + "sourcetools": { + "Package": "sourcetools", + "Version": "0.1.7-1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5f5a7629f956619d519205ec475fe647" + }, + "spelling": { + "Package": "spelling", + "Version": "2.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "commonmark", + "hunspell", + "knitr", + "xml2" + ], + "Hash": "8ed9f010f7caeb8586523088b7f23dcd" + }, + "stringi": { + "Package": "stringi", + "Version": "1.7.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "ca8bd84263c77310739d2cf64d84d7c9" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "671a4d384ae9d32fc47a14e98bfa3dc8" + }, + "sys": { + "Package": "sys", + "Version": "3.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "3a1be13d68d47a8cd0bfd74739ca1555" + }, + "systemfonts": { + "Package": "systemfonts", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "90b28393209827327de889f49935140a" + }, + "testthat": { + "Package": "testthat", + "Version": "3.1.10", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "brio", + "callr", + "cli", + "desc", + "digest", + "ellipsis", + "evaluate", + "jsonlite", + "lifecycle", + "magrittr", + "methods", + "pkgload", + "praise", + "processx", + "ps", + "rlang", + "utils", + "waldo", + "withr" + ], + "Hash": "6f403dc49295610a3a67ea1a9ca64346" + }, + "textshaping": { + "Package": "textshaping", + "Version": "0.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "systemfonts" + ], + "Hash": "1ab6223d3670fac7143202cb6a2d43d5" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "e47debdc7ce599b070c8e78e8ac0cfcf" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "79540e5fcd9e0435af547d885f184fd5" + }, + "timechange": { + "Package": "timechange", + "Version": "0.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "8548b44f79a35ba1791308b61e6012d7" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.45", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "e4e357f28c2edff493936b6cb30c3d65" + }, + "tm": { + "Package": "tm", + "Version": "0.7-11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "BH", + "NLP", + "R", + "Rcpp", + "graphics", + "parallel", + "slam", + "stats", + "tools", + "utils", + "xml2" + ], + "Hash": "625329dd66ec8e8fbeb5ea6003e9be0a" + }, + "tzdb": { + "Package": "tzdb", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "f561504ec2897f4d46f0c7657e488ae1" + }, + "urlchecker": { + "Package": "urlchecker", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "curl", + "tools", + "xml2" + ], + "Hash": "409328b8e1253c8d729a7836fe7f7a16" + }, + "usethis": { + "Package": "usethis", + "Version": "2.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "clipr", + "crayon", + "curl", + "desc", + "fs", + "gert", + "gh", + "glue", + "jsonlite", + "lifecycle", + "purrr", + "rappdirs", + "rlang", + "rprojroot", + "rstudioapi", + "stats", + "utils", + "whisker", + "withr", + "yaml" + ], + "Hash": "60e51f0b94d0324dc19e44110098fa9f" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "1fe17157424bb09c48a8b3b550c753bc" + }, + "uuid": { + "Package": "uuid", + "Version": "1.1-0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "f1cb46c157d080b729159d407be83496" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "d0ef2856b83dc33ea6e255caf6229ee2" + }, + "viridisLite": { + "Package": "viridisLite", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + }, + "vroom": { + "Package": "vroom", + "Version": "1.6.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bit64", + "cli", + "cpp11", + "crayon", + "glue", + "hms", + "lifecycle", + "methods", + "progress", + "rlang", + "stats", + "tibble", + "tidyselect", + "tzdb", + "vctrs", + "withr" + ], + "Hash": "8318e64ffb3a70e652494017ec455561" + }, + "waldo": { + "Package": "waldo", + "Version": "0.5.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "cli", + "diffobj", + "fansi", + "glue", + "methods", + "rematch2", + "rlang", + "tibble" + ], + "Hash": "2c993415154cdb94649d99ae138ff5e5" + }, + "whisker": { + "Package": "whisker", + "Version": "0.4.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "c6abfa47a46d281a7d5159d0a8891e88" + }, + "whoami": { + "Package": "whoami", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "httr", + "jsonlite", + "utils" + ], + "Hash": "ef0f4d9b8f2cc2ebeccae1d725b8a023" + }, + "withr": { + "Package": "withr", + "Version": "2.5.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "grDevices", + "graphics", + "stats" + ], + "Hash": "c0e49a9760983e81e55cdd9be92e7182" + }, + "writexl": { + "Package": "writexl", + "Version": "1.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "43da939eaf6681c88eba977b9012dad9" + }, + "xfun": { + "Package": "xfun", + "Version": "0.39", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "stats", + "tools" + ], + "Hash": "8f56e9acb54fb525e66464d57ab58bcb" + }, + "xml2": { + "Package": "xml2", + "Version": "1.3.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "6c40e5cfcc6aefd88110666e18c31f40" + }, + "xopen": { + "Package": "xopen", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "processx" + ], + "Hash": "6c85f015dee9cc7710ddd20f86881f58" + }, + "xtable": { + "Package": "xtable", + "Version": "1.8-4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "stats", + "utils" + ], + "Hash": "b8acdf8af494d9ec19ccb2481a9b11c2" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.7", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "0d0056cc5383fbc240ccd0cb584bf436" + }, + "zip": { + "Package": "zip", + "Version": "2.3.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "d98c94dacb7e0efcf83b0a133a705504" + } + } +} diff --git a/renv/.gitignore b/renv/.gitignore new file mode 100644 index 0000000..0ec0cbb --- /dev/null +++ b/renv/.gitignore @@ -0,0 +1,7 @@ +library/ +local/ +cellar/ +lock/ +python/ +sandbox/ +staging/ diff --git a/renv/activate.R b/renv/activate.R new file mode 100644 index 0000000..cc742fc --- /dev/null +++ b/renv/activate.R @@ -0,0 +1,1181 @@ + +local({ + + # the requested version of renv + version <- "1.0.0" + attr(version, "sha") <- NULL + + # the project directory + project <- getwd() + + # figure out whether the autoloader is enabled + enabled <- local({ + + # first, check config option + override <- getOption("renv.config.autoloader.enabled") + if (!is.null(override)) + return(override) + + # next, check environment variables + # TODO: prefer using the configuration one in the future + envvars <- c( + "RENV_CONFIG_AUTOLOADER_ENABLED", + "RENV_AUTOLOADER_ENABLED", + "RENV_ACTIVATE_PROJECT" + ) + + for (envvar in envvars) { + envval <- Sys.getenv(envvar, unset = NA) + if (!is.na(envval)) + return(tolower(envval) %in% c("true", "t", "1")) + } + + # enable by default + TRUE + + }) + + if (!enabled) + return(FALSE) + + # avoid recursion + if (identical(getOption("renv.autoloader.running"), TRUE)) { + warning("ignoring recursive attempt to run renv autoloader") + return(invisible(TRUE)) + } + + # signal that we're loading renv during R startup + options(renv.autoloader.running = TRUE) + on.exit(options(renv.autoloader.running = NULL), add = TRUE) + + # signal that we've consented to use renv + options(renv.consent = TRUE) + + # load the 'utils' package eagerly -- this ensures that renv shims, which + # mask 'utils' packages, will come first on the search path + library(utils, lib.loc = .Library) + + # unload renv if it's already been loaded + if ("renv" %in% loadedNamespaces()) + unloadNamespace("renv") + + # load bootstrap tools + `%||%` <- function(x, y) { + if (is.null(x)) y else x + } + + catf <- function(fmt, ..., appendLF = TRUE) { + + quiet <- getOption("renv.bootstrap.quiet", default = FALSE) + if (quiet) + return(invisible()) + + msg <- sprintf(fmt, ...) + cat(msg, file = stdout(), sep = if (appendLF) "\n" else "") + + invisible(msg) + + } + + header <- function(label, + ..., + prefix = "#", + suffix = "-", + n = min(getOption("width"), 78)) + { + label <- sprintf(label, ...) + n <- max(n - nchar(label) - nchar(prefix) - 2L, 8L) + if (n <= 0) + return(paste(prefix, label)) + + tail <- paste(rep.int(suffix, n), collapse = "") + paste0(prefix, " ", label, " ", tail) + + } + + startswith <- function(string, prefix) { + substring(string, 1, nchar(prefix)) == prefix + } + + bootstrap <- function(version, library) { + + friendly <- renv_bootstrap_version_friendly(version) + section <- header(sprintf("Bootstrapping renv %s", friendly)) + catf(section) + + # attempt to download renv + catf("- Downloading renv ... ", appendLF = FALSE) + withCallingHandlers( + tarball <- renv_bootstrap_download(version), + error = function(err) { + catf("FAILED") + stop("failed to download:\n", conditionMessage(err)) + } + ) + catf("OK") + on.exit(unlink(tarball), add = TRUE) + + # now attempt to install + catf("- Installing renv ... ", appendLF = FALSE) + withCallingHandlers( + status <- renv_bootstrap_install(version, tarball, library), + error = function(err) { + catf("FAILED") + stop("failed to install:\n", conditionMessage(err)) + } + ) + catf("OK") + + # add empty line to break up bootstrapping from normal output + catf("") + + return(invisible()) + } + + renv_bootstrap_tests_running <- function() { + getOption("renv.tests.running", default = FALSE) + } + + renv_bootstrap_repos <- function() { + + # get CRAN repository + cran <- getOption("renv.repos.cran", "https://cloud.r-project.org") + + # check for repos override + repos <- Sys.getenv("RENV_CONFIG_REPOS_OVERRIDE", unset = NA) + if (!is.na(repos)) { + + # check for RSPM; if set, use a fallback repository for renv + rspm <- Sys.getenv("RSPM", unset = NA) + if (identical(rspm, repos)) + repos <- c(RSPM = rspm, CRAN = cran) + + return(repos) + + } + + # check for lockfile repositories + repos <- tryCatch(renv_bootstrap_repos_lockfile(), error = identity) + if (!inherits(repos, "error") && length(repos)) + return(repos) + + # retrieve current repos + repos <- getOption("repos") + + # ensure @CRAN@ entries are resolved + repos[repos == "@CRAN@"] <- cran + + # add in renv.bootstrap.repos if set + default <- c(FALLBACK = "https://cloud.r-project.org") + extra <- getOption("renv.bootstrap.repos", default = default) + repos <- c(repos, extra) + + # remove duplicates that might've snuck in + dupes <- duplicated(repos) | duplicated(names(repos)) + repos[!dupes] + + } + + renv_bootstrap_repos_lockfile <- function() { + + lockpath <- Sys.getenv("RENV_PATHS_LOCKFILE", unset = "renv.lock") + if (!file.exists(lockpath)) + return(NULL) + + lockfile <- tryCatch(renv_json_read(lockpath), error = identity) + if (inherits(lockfile, "error")) { + warning(lockfile) + return(NULL) + } + + repos <- lockfile$R$Repositories + if (length(repos) == 0) + return(NULL) + + keys <- vapply(repos, `[[`, "Name", FUN.VALUE = character(1)) + vals <- vapply(repos, `[[`, "URL", FUN.VALUE = character(1)) + names(vals) <- keys + + return(vals) + + } + + renv_bootstrap_download <- function(version) { + + sha <- attr(version, "sha", exact = TRUE) + + methods <- if (!is.null(sha)) { + + # attempting to bootstrap a development version of renv + c( + function() renv_bootstrap_download_tarball(sha), + function() renv_bootstrap_download_github(sha) + ) + + } else { + + # attempting to bootstrap a release version of renv + c( + function() renv_bootstrap_download_tarball(version), + function() renv_bootstrap_download_cran_latest(version), + function() renv_bootstrap_download_cran_archive(version) + ) + + } + + for (method in methods) { + path <- tryCatch(method(), error = identity) + if (is.character(path) && file.exists(path)) + return(path) + } + + stop("All download methods failed") + + } + + renv_bootstrap_download_impl <- function(url, destfile) { + + mode <- "wb" + + # https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17715 + fixup <- + Sys.info()[["sysname"]] == "Windows" && + substring(url, 1L, 5L) == "file:" + + if (fixup) + mode <- "w+b" + + args <- list( + url = url, + destfile = destfile, + mode = mode, + quiet = TRUE + ) + + if ("headers" %in% names(formals(utils::download.file))) + args$headers <- renv_bootstrap_download_custom_headers(url) + + do.call(utils::download.file, args) + + } + + renv_bootstrap_download_custom_headers <- function(url) { + + headers <- getOption("renv.download.headers") + if (is.null(headers)) + return(character()) + + if (!is.function(headers)) + stopf("'renv.download.headers' is not a function") + + headers <- headers(url) + if (length(headers) == 0L) + return(character()) + + if (is.list(headers)) + headers <- unlist(headers, recursive = FALSE, use.names = TRUE) + + ok <- + is.character(headers) && + is.character(names(headers)) && + all(nzchar(names(headers))) + + if (!ok) + stop("invocation of 'renv.download.headers' did not return a named character vector") + + headers + + } + + renv_bootstrap_download_cran_latest <- function(version) { + + spec <- renv_bootstrap_download_cran_latest_find(version) + type <- spec$type + repos <- spec$repos + + baseurl <- utils::contrib.url(repos = repos, type = type) + ext <- if (identical(type, "source")) + ".tar.gz" + else if (Sys.info()[["sysname"]] == "Windows") + ".zip" + else + ".tgz" + name <- sprintf("renv_%s%s", version, ext) + url <- paste(baseurl, name, sep = "/") + + destfile <- file.path(tempdir(), name) + status <- tryCatch( + renv_bootstrap_download_impl(url, destfile), + condition = identity + ) + + if (inherits(status, "condition")) + return(FALSE) + + # report success and return + destfile + + } + + renv_bootstrap_download_cran_latest_find <- function(version) { + + # check whether binaries are supported on this system + binary <- + getOption("renv.bootstrap.binary", default = TRUE) && + !identical(.Platform$pkgType, "source") && + !identical(getOption("pkgType"), "source") && + Sys.info()[["sysname"]] %in% c("Darwin", "Windows") + + types <- c(if (binary) "binary", "source") + + # iterate over types + repositories + for (type in types) { + for (repos in renv_bootstrap_repos()) { + + # retrieve package database + db <- tryCatch( + as.data.frame( + utils::available.packages(type = type, repos = repos), + stringsAsFactors = FALSE + ), + error = identity + ) + + if (inherits(db, "error")) + next + + # check for compatible entry + entry <- db[db$Package %in% "renv" & db$Version %in% version, ] + if (nrow(entry) == 0) + next + + # found it; return spec to caller + spec <- list(entry = entry, type = type, repos = repos) + return(spec) + + } + } + + # if we got here, we failed to find renv + fmt <- "renv %s is not available from your declared package repositories" + stop(sprintf(fmt, version)) + + } + + renv_bootstrap_download_cran_archive <- function(version) { + + name <- sprintf("renv_%s.tar.gz", version) + repos <- renv_bootstrap_repos() + urls <- file.path(repos, "src/contrib/Archive/renv", name) + destfile <- file.path(tempdir(), name) + + for (url in urls) { + + status <- tryCatch( + renv_bootstrap_download_impl(url, destfile), + condition = identity + ) + + if (identical(status, 0L)) + return(destfile) + + } + + return(FALSE) + + } + + renv_bootstrap_download_tarball <- function(version) { + + # if the user has provided the path to a tarball via + # an environment variable, then use it + tarball <- Sys.getenv("RENV_BOOTSTRAP_TARBALL", unset = NA) + if (is.na(tarball)) + return() + + # allow directories + if (dir.exists(tarball)) { + name <- sprintf("renv_%s.tar.gz", version) + tarball <- file.path(tarball, name) + } + + # bail if it doesn't exist + if (!file.exists(tarball)) { + + # let the user know we weren't able to honour their request + fmt <- "- RENV_BOOTSTRAP_TARBALL is set (%s) but does not exist." + msg <- sprintf(fmt, tarball) + warning(msg) + + # bail + return() + + } + + catf("- Using local tarball '%s'.", tarball) + tarball + + } + + renv_bootstrap_download_github <- function(version) { + + enabled <- Sys.getenv("RENV_BOOTSTRAP_FROM_GITHUB", unset = "TRUE") + if (!identical(enabled, "TRUE")) + return(FALSE) + + # prepare download options + pat <- Sys.getenv("GITHUB_PAT") + if (nzchar(Sys.which("curl")) && nzchar(pat)) { + fmt <- "--location --fail --header \"Authorization: token %s\"" + extra <- sprintf(fmt, pat) + saved <- options("download.file.method", "download.file.extra") + options(download.file.method = "curl", download.file.extra = extra) + on.exit(do.call(base::options, saved), add = TRUE) + } else if (nzchar(Sys.which("wget")) && nzchar(pat)) { + fmt <- "--header=\"Authorization: token %s\"" + extra <- sprintf(fmt, pat) + saved <- options("download.file.method", "download.file.extra") + options(download.file.method = "wget", download.file.extra = extra) + on.exit(do.call(base::options, saved), add = TRUE) + } + + url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version) + name <- sprintf("renv_%s.tar.gz", version) + destfile <- file.path(tempdir(), name) + + status <- tryCatch( + renv_bootstrap_download_impl(url, destfile), + condition = identity + ) + + if (!identical(status, 0L)) + return(FALSE) + + renv_bootstrap_download_augment(destfile) + + return(destfile) + + } + + # Add Sha to DESCRIPTION. This is stop gap until #890, after which we + # can use renv::install() to fully capture metadata. + renv_bootstrap_download_augment <- function(destfile) { + sha <- renv_bootstrap_git_extract_sha1_tar(destfile) + if (is.null(sha)) { + return() + } + + # Untar + tempdir <- tempfile("renv-github-") + on.exit(unlink(tempdir, recursive = TRUE), add = TRUE) + untar(destfile, exdir = tempdir) + pkgdir <- dir(tempdir, full.names = TRUE)[[1]] + + # Modify description + desc_path <- file.path(pkgdir, "DESCRIPTION") + desc_lines <- readLines(desc_path) + remotes_fields <- c( + "RemoteType: github", + "RemoteHost: api.github.com", + "RemoteRepo: renv", + "RemoteUsername: rstudio", + "RemotePkgRef: rstudio/renv", + paste("RemoteRef: ", sha), + paste("RemoteSha: ", sha) + ) + writeLines(c(desc_lines[desc_lines != ""], remotes_fields), con = desc_path) + + # Re-tar + local({ + old <- setwd(tempdir) + on.exit(setwd(old), add = TRUE) + + tar(destfile, compression = "gzip") + }) + invisible() + } + + # Extract the commit hash from a git archive. Git archives include the SHA1 + # hash as the comment field of the tarball pax extended header + # (see https://www.kernel.org/pub/software/scm/git/docs/git-archive.html) + # For GitHub archives this should be the first header after the default one + # (512 byte) header. + renv_bootstrap_git_extract_sha1_tar <- function(bundle) { + + # open the bundle for reading + # We use gzcon for everything because (from ?gzcon) + # > Reading from a connection which does not supply a ‘gzip’ magic + # > header is equivalent to reading from the original connection + conn <- gzcon(file(bundle, open = "rb", raw = TRUE)) + on.exit(close(conn)) + + # The default pax header is 512 bytes long and the first pax extended header + # with the comment should be 51 bytes long + # `52 comment=` (11 chars) + 40 byte SHA1 hash + len <- 0x200 + 0x33 + res <- rawToChar(readBin(conn, "raw", n = len)[0x201:len]) + + if (grepl("^52 comment=", res)) { + sub("52 comment=", "", res) + } else { + NULL + } + } + + renv_bootstrap_install <- function(version, tarball, library) { + + # attempt to install it into project library + dir.create(library, showWarnings = FALSE, recursive = TRUE) + output <- renv_bootstrap_install_impl(library, tarball) + + # check for successful install + status <- attr(output, "status") + if (is.null(status) || identical(status, 0L)) + return(status) + + # an error occurred; report it + header <- "installation of renv failed" + lines <- paste(rep.int("=", nchar(header)), collapse = "") + text <- paste(c(header, lines, output), collapse = "\n") + stop(text) + + } + + renv_bootstrap_install_impl <- function(library, tarball) { + + # invoke using system2 so we can capture and report output + bin <- R.home("bin") + exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R" + R <- file.path(bin, exe) + + args <- c( + "--vanilla", "CMD", "INSTALL", "--no-multiarch", + "-l", shQuote(path.expand(library)), + shQuote(path.expand(tarball)) + ) + + system2(R, args, stdout = TRUE, stderr = TRUE) + + } + + renv_bootstrap_platform_prefix <- function() { + + # construct version prefix + version <- paste(R.version$major, R.version$minor, sep = ".") + prefix <- paste("R", numeric_version(version)[1, 1:2], sep = "-") + + # include SVN revision for development versions of R + # (to avoid sharing platform-specific artefacts with released versions of R) + devel <- + identical(R.version[["status"]], "Under development (unstable)") || + identical(R.version[["nickname"]], "Unsuffered Consequences") + + if (devel) + prefix <- paste(prefix, R.version[["svn rev"]], sep = "-r") + + # build list of path components + components <- c(prefix, R.version$platform) + + # include prefix if provided by user + prefix <- renv_bootstrap_platform_prefix_impl() + if (!is.na(prefix) && nzchar(prefix)) + components <- c(prefix, components) + + # build prefix + paste(components, collapse = "/") + + } + + renv_bootstrap_platform_prefix_impl <- function() { + + # if an explicit prefix has been supplied, use it + prefix <- Sys.getenv("RENV_PATHS_PREFIX", unset = NA) + if (!is.na(prefix)) + return(prefix) + + # if the user has requested an automatic prefix, generate it + auto <- Sys.getenv("RENV_PATHS_PREFIX_AUTO", unset = NA) + if (auto %in% c("TRUE", "True", "true", "1")) + return(renv_bootstrap_platform_prefix_auto()) + + # empty string on failure + "" + + } + + renv_bootstrap_platform_prefix_auto <- function() { + + prefix <- tryCatch(renv_bootstrap_platform_os(), error = identity) + if (inherits(prefix, "error") || prefix %in% "unknown") { + + msg <- paste( + "failed to infer current operating system", + "please file a bug report at https://github.com/rstudio/renv/issues", + sep = "; " + ) + + warning(msg) + + } + + prefix + + } + + renv_bootstrap_platform_os <- function() { + + sysinfo <- Sys.info() + sysname <- sysinfo[["sysname"]] + + # handle Windows + macOS up front + if (sysname == "Windows") + return("windows") + else if (sysname == "Darwin") + return("macos") + + # check for os-release files + for (file in c("/etc/os-release", "/usr/lib/os-release")) + if (file.exists(file)) + return(renv_bootstrap_platform_os_via_os_release(file, sysinfo)) + + # check for redhat-release files + if (file.exists("/etc/redhat-release")) + return(renv_bootstrap_platform_os_via_redhat_release()) + + "unknown" + + } + + renv_bootstrap_platform_os_via_os_release <- function(file, sysinfo) { + + # read /etc/os-release + release <- utils::read.table( + file = file, + sep = "=", + quote = c("\"", "'"), + col.names = c("Key", "Value"), + comment.char = "#", + stringsAsFactors = FALSE + ) + + vars <- as.list(release$Value) + names(vars) <- release$Key + + # get os name + os <- tolower(sysinfo[["sysname"]]) + + # read id + id <- "unknown" + for (field in c("ID", "ID_LIKE")) { + if (field %in% names(vars) && nzchar(vars[[field]])) { + id <- vars[[field]] + break + } + } + + # read version + version <- "unknown" + for (field in c("UBUNTU_CODENAME", "VERSION_CODENAME", "VERSION_ID", "BUILD_ID")) { + if (field %in% names(vars) && nzchar(vars[[field]])) { + version <- vars[[field]] + break + } + } + + # join together + paste(c(os, id, version), collapse = "-") + + } + + renv_bootstrap_platform_os_via_redhat_release <- function() { + + # read /etc/redhat-release + contents <- readLines("/etc/redhat-release", warn = FALSE) + + # infer id + id <- if (grepl("centos", contents, ignore.case = TRUE)) + "centos" + else if (grepl("redhat", contents, ignore.case = TRUE)) + "redhat" + else + "unknown" + + # try to find a version component (very hacky) + version <- "unknown" + + parts <- strsplit(contents, "[[:space:]]")[[1L]] + for (part in parts) { + + nv <- tryCatch(numeric_version(part), error = identity) + if (inherits(nv, "error")) + next + + version <- nv[1, 1] + break + + } + + paste(c("linux", id, version), collapse = "-") + + } + + renv_bootstrap_library_root_name <- function(project) { + + # use project name as-is if requested + asis <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT_ASIS", unset = "FALSE") + if (asis) + return(basename(project)) + + # otherwise, disambiguate based on project's path + id <- substring(renv_bootstrap_hash_text(project), 1L, 8L) + paste(basename(project), id, sep = "-") + + } + + renv_bootstrap_library_root <- function(project) { + + prefix <- renv_bootstrap_profile_prefix() + + path <- Sys.getenv("RENV_PATHS_LIBRARY", unset = NA) + if (!is.na(path)) + return(paste(c(path, prefix), collapse = "/")) + + path <- renv_bootstrap_library_root_impl(project) + if (!is.null(path)) { + name <- renv_bootstrap_library_root_name(project) + return(paste(c(path, prefix, name), collapse = "/")) + } + + renv_bootstrap_paths_renv("library", project = project) + + } + + renv_bootstrap_library_root_impl <- function(project) { + + root <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT", unset = NA) + if (!is.na(root)) + return(root) + + type <- renv_bootstrap_project_type(project) + if (identical(type, "package")) { + userdir <- renv_bootstrap_user_dir() + return(file.path(userdir, "library")) + } + + } + + renv_bootstrap_validate_version <- function(version, description = NULL) { + + # resolve description file + description <- description %||% { + path <- getNamespaceInfo("renv", "path") + packageDescription("renv", lib.loc = dirname(path)) + } + + # check whether requested version 'version' matches loaded version of renv + sha <- attr(version, "sha", exact = TRUE) + valid <- if (!is.null(sha)) + renv_bootstrap_validate_version_dev(sha, description) + else + renv_bootstrap_validate_version_release(version, description) + + if (valid) + return(TRUE) + + # the loaded version of renv doesn't match the requested version; + # give the user instructions on how to proceed + remote <- if (!is.null(description[["RemoteSha"]])) { + paste("rstudio/renv", description[["RemoteSha"]], sep = "@") + } else { + paste("renv", description[["Version"]], sep = "@") + } + + # display both loaded version + sha if available + friendly <- renv_bootstrap_version_friendly( + version = description[["Version"]], + sha = description[["RemoteSha"]] + ) + + fmt <- paste( + "renv %1$s was loaded from project library, but this project is configured to use renv %2$s.", + "- Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.", + "- Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.", + sep = "\n" + ) + catf(fmt, friendly, renv_bootstrap_version_friendly(version), remote) + + FALSE + + } + + renv_bootstrap_validate_version_dev <- function(version, description) { + expected <- description[["RemoteSha"]] + is.character(expected) && startswith(expected, version) + } + + renv_bootstrap_validate_version_release <- function(version, description) { + expected <- description[["Version"]] + is.character(expected) && identical(expected, version) + } + + renv_bootstrap_hash_text <- function(text) { + + hashfile <- tempfile("renv-hash-") + on.exit(unlink(hashfile), add = TRUE) + + writeLines(text, con = hashfile) + tools::md5sum(hashfile) + + } + + renv_bootstrap_load <- function(project, libpath, version) { + + # try to load renv from the project library + if (!requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) + return(FALSE) + + # warn if the version of renv loaded does not match + renv_bootstrap_validate_version(version) + + # execute renv load hooks, if any + hooks <- getHook("renv::autoload") + for (hook in hooks) + if (is.function(hook)) + tryCatch(hook(), error = warning) + + # load the project + renv::load(project) + + TRUE + + } + + renv_bootstrap_profile_load <- function(project) { + + # if RENV_PROFILE is already set, just use that + profile <- Sys.getenv("RENV_PROFILE", unset = NA) + if (!is.na(profile) && nzchar(profile)) + return(profile) + + # check for a profile file (nothing to do if it doesn't exist) + path <- renv_bootstrap_paths_renv("profile", profile = FALSE, project = project) + if (!file.exists(path)) + return(NULL) + + # read the profile, and set it if it exists + contents <- readLines(path, warn = FALSE) + if (length(contents) == 0L) + return(NULL) + + # set RENV_PROFILE + profile <- contents[[1L]] + if (!profile %in% c("", "default")) + Sys.setenv(RENV_PROFILE = profile) + + profile + + } + + renv_bootstrap_profile_prefix <- function() { + profile <- renv_bootstrap_profile_get() + if (!is.null(profile)) + return(file.path("profiles", profile, "renv")) + } + + renv_bootstrap_profile_get <- function() { + profile <- Sys.getenv("RENV_PROFILE", unset = "") + renv_bootstrap_profile_normalize(profile) + } + + renv_bootstrap_profile_set <- function(profile) { + profile <- renv_bootstrap_profile_normalize(profile) + if (is.null(profile)) + Sys.unsetenv("RENV_PROFILE") + else + Sys.setenv(RENV_PROFILE = profile) + } + + renv_bootstrap_profile_normalize <- function(profile) { + + if (is.null(profile) || profile %in% c("", "default")) + return(NULL) + + profile + + } + + renv_bootstrap_path_absolute <- function(path) { + + substr(path, 1L, 1L) %in% c("~", "/", "\\") || ( + substr(path, 1L, 1L) %in% c(letters, LETTERS) && + substr(path, 2L, 3L) %in% c(":/", ":\\") + ) + + } + + renv_bootstrap_paths_renv <- function(..., profile = TRUE, project = NULL) { + renv <- Sys.getenv("RENV_PATHS_RENV", unset = "renv") + root <- if (renv_bootstrap_path_absolute(renv)) NULL else project + prefix <- if (profile) renv_bootstrap_profile_prefix() + components <- c(root, renv, prefix, ...) + paste(components, collapse = "/") + } + + renv_bootstrap_project_type <- function(path) { + + descpath <- file.path(path, "DESCRIPTION") + if (!file.exists(descpath)) + return("unknown") + + desc <- tryCatch( + read.dcf(descpath, all = TRUE), + error = identity + ) + + if (inherits(desc, "error")) + return("unknown") + + type <- desc$Type + if (!is.null(type)) + return(tolower(type)) + + package <- desc$Package + if (!is.null(package)) + return("package") + + "unknown" + + } + + renv_bootstrap_user_dir <- function() { + dir <- renv_bootstrap_user_dir_impl() + path.expand(chartr("\\", "/", dir)) + } + + renv_bootstrap_user_dir_impl <- function() { + + # use local override if set + override <- getOption("renv.userdir.override") + if (!is.null(override)) + return(override) + + # use R_user_dir if available + tools <- asNamespace("tools") + if (is.function(tools$R_user_dir)) + return(tools$R_user_dir("renv", "cache")) + + # try using our own backfill for older versions of R + envvars <- c("R_USER_CACHE_DIR", "XDG_CACHE_HOME") + for (envvar in envvars) { + root <- Sys.getenv(envvar, unset = NA) + if (!is.na(root)) + return(file.path(root, "R/renv")) + } + + # use platform-specific default fallbacks + if (Sys.info()[["sysname"]] == "Windows") + file.path(Sys.getenv("LOCALAPPDATA"), "R/cache/R/renv") + else if (Sys.info()[["sysname"]] == "Darwin") + "~/Library/Caches/org.R-project.R/R/renv" + else + "~/.cache/R/renv" + + } + + renv_bootstrap_version_friendly <- function(version, sha = NULL) { + sha <- sha %||% attr(version, "sha", exact = TRUE) + parts <- c(version, sprintf("[sha: %s]", substring(sha, 1L, 7L))) + paste(parts, collapse = " ") + } + + renv_bootstrap_run <- function(version, libpath) { + + # perform bootstrap + bootstrap(version, libpath) + + # exit early if we're just testing bootstrap + if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA))) + return(TRUE) + + # try again to load + if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) { + return(renv::load(project = getwd())) + } + + # failed to download or load renv; warn the user + msg <- c( + "Failed to find an renv installation: the project will not be loaded.", + "Use `renv::activate()` to re-initialize the project." + ) + + warning(paste(msg, collapse = "\n"), call. = FALSE) + + } + + + renv_bootstrap_in_rstudio <- function() { + commandArgs()[[1]] == "RStudio" + } + + renv_json_read <- function(file = NULL, text = NULL) { + + jlerr <- NULL + + # if jsonlite is loaded, use that instead + if ("jsonlite" %in% loadedNamespaces()) { + + json <- catch(renv_json_read_jsonlite(file, text)) + if (!inherits(json, "error")) + return(json) + + jlerr <- json + + } + + # otherwise, fall back to the default JSON reader + json <- catch(renv_json_read_default(file, text)) + if (!inherits(json, "error")) + return(json) + + # report an error + if (!is.null(jlerr)) + stop(jlerr) + else + stop(json) + + } + + renv_json_read_jsonlite <- function(file = NULL, text = NULL) { + text <- paste(text %||% read(file), collapse = "\n") + jsonlite::fromJSON(txt = text, simplifyVector = FALSE) + } + + renv_json_read_default <- function(file = NULL, text = NULL) { + + # find strings in the JSON + text <- paste(text %||% read(file), collapse = "\n") + pattern <- '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]' + locs <- gregexpr(pattern, text, perl = TRUE)[[1]] + + # if any are found, replace them with placeholders + replaced <- text + strings <- character() + replacements <- character() + + if (!identical(c(locs), -1L)) { + + # get the string values + starts <- locs + ends <- locs + attr(locs, "match.length") - 1L + strings <- substring(text, starts, ends) + + # only keep those requiring escaping + strings <- grep("[[\\]{}:]", strings, perl = TRUE, value = TRUE) + + # compute replacements + replacements <- sprintf('"\032%i\032"', seq_along(strings)) + + # replace the strings + mapply(function(string, replacement) { + replaced <<- sub(string, replacement, replaced, fixed = TRUE) + }, strings, replacements) + + } + + # transform the JSON into something the R parser understands + transformed <- replaced + transformed <- gsub("{}", "`names<-`(list(), character())", transformed, fixed = TRUE) + transformed <- gsub("[[{]", "list(", transformed, perl = TRUE) + transformed <- gsub("[]}]", ")", transformed, perl = TRUE) + transformed <- gsub(":", "=", transformed, fixed = TRUE) + text <- paste(transformed, collapse = "\n") + + # parse it + json <- parse(text = text, keep.source = FALSE, srcfile = NULL)[[1L]] + + # construct map between source strings, replaced strings + map <- as.character(parse(text = strings)) + names(map) <- as.character(parse(text = replacements)) + + # convert to list + map <- as.list(map) + + # remap strings in object + remapped <- renv_json_remap(json, map) + + # evaluate + eval(remapped, envir = baseenv()) + + } + + renv_json_remap <- function(json, map) { + + # fix names + if (!is.null(names(json))) { + lhs <- match(names(json), names(map), nomatch = 0L) + rhs <- match(names(map), names(json), nomatch = 0L) + names(json)[rhs] <- map[lhs] + } + + # fix values + if (is.character(json)) + return(map[[json]] %||% json) + + # handle true, false, null + if (is.name(json)) { + text <- as.character(json) + if (text == "true") + return(TRUE) + else if (text == "false") + return(FALSE) + else if (text == "null") + return(NULL) + } + + # recurse + if (is.recursive(json)) { + for (i in seq_along(json)) { + json[i] <- list(renv_json_remap(json[[i]], map)) + } + } + + json + + } + + # load the renv profile, if any + renv_bootstrap_profile_load(project) + + # construct path to library root + root <- renv_bootstrap_library_root(project) + + # construct library prefix for platform + prefix <- renv_bootstrap_platform_prefix() + + # construct full libpath + libpath <- file.path(root, prefix) + + # attempt to load + if (renv_bootstrap_load(project, libpath, version)) + return(TRUE) + + if (renv_bootstrap_in_rstudio()) { + setHook("rstudio.sessionInit", function(...) { + renv_bootstrap_run(version, libpath) + + # Work around buglet in RStudio if hook uses readline + tryCatch( + { + tools <- as.environment("tools:rstudio") + tools$.rs.api.sendToConsole("", echo = FALSE, focus = FALSE) + }, + error = function(cnd) {} + ) + }) + } else { + renv_bootstrap_run(version, libpath) + } + + invisible() + +}) diff --git a/renv/settings.json b/renv/settings.json new file mode 100644 index 0000000..ffdbb32 --- /dev/null +++ b/renv/settings.json @@ -0,0 +1,19 @@ +{ + "bioconductor.version": null, + "external.libraries": [], + "ignored.packages": [], + "package.dependency.fields": [ + "Imports", + "Depends", + "LinkingTo" + ], + "ppm.enabled": null, + "ppm.ignored.urls": [], + "r.version": null, + "snapshot.type": "implicit", + "use.cache": true, + "vcs.ignore.cellar": true, + "vcs.ignore.library": true, + "vcs.ignore.local": true, + "vcs.manage.ignores": true +} diff --git a/tests/testthat/_snaps/server-modules.md b/tests/testthat/_snaps/server-modules.md index 8725297..4be919c 100644 --- a/tests/testthat/_snaps/server-modules.md +++ b/tests/testthat/_snaps/server-modules.md @@ -144,13 +144,6 @@ Output {"x":{"filter":"none","vertical":false,"container":"\n \n \n \n
<\/th>\n comment_id<\/th>\n comment_type<\/th>\n date<\/th>\n pt_id<\/th>\n location_1<\/th>\n location_2<\/th>\n location_3<\/th>\n comment_txt<\/th>\n fft<\/th>\n sex<\/th>\n gender<\/th>\n age<\/th>\n ethnicity<\/th>\n sexuality<\/th>\n disability<\/th>\n religion<\/th>\n extra_variable_1<\/th>\n extra_variable_2<\/th>\n extra_variable_3<\/th>\n hidden<\/th>\n last_upload_date<\/th>\n category<\/th>\n super_category<\/th>\n <\/tr>\n <\/thead>\n<\/table>","options":{"columnDefs":[{"className":"dt-right","targets":[1,4,9,20]},{"orderable":false,"targets":0}],"order":[],"autoWidth":false,"orderClasses":false,"serverSide":true,"processing":true},"selection":{"mode":"multiple","selected":null,"target":"row","selectable":null}},"evals":[],"jsHooks":[],"deps":[{"name":"jquery","version":"3.6.0","src":{"href":"jquery-3.6.0"},"meta":null,"script":"jquery-3.6.0.min.js","stylesheet":null,"head":null,"attachment":null,"all_files":true},{"name":"dt-core","version":"1.13.4","src":{"href":"dt-core-1.13.4"},"meta":null,"script":"js/jquery.dataTables.min.js","stylesheet":["css/jquery.dataTables.min.css","css/jquery.dataTables.extra.css"],"head":null,"attachment":null,"package":null,"all_files":false},{"name":"crosstalk","version":"1.2.0","src":{"href":"crosstalk-1.2.0"},"meta":null,"script":"js/crosstalk.min.js","stylesheet":"css/crosstalk.min.css","head":null,"attachment":null,"all_files":true}]} -# mod_data_management_server work correctly - - Code - output$pat_table - Output - {"x":{"filter":"top","vertical":false,"filterHTML":"
\n
\n \n <\/span>\n <\/div>\n
\n
<\/div>\n <\/span>\n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n
\n
<\/div>\n <\/span>\n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n
\n
<\/div>\n <\/span>\n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n <\/td>\n
\n
\n \n <\/span>\n <\/div>\n
\n
<\/div>\n <\/span>\n <\/span>\n <\/div>\n <\/td>\n<\/tr>","container":"\n \n \n
Comment ID<\/th>\n Date<\/th>\n Division<\/th>\n Specialty<\/th>\n Team<\/th>\n Question Type<\/th>\n Comment<\/th>\n Sub-Category<\/th>\n Category<\/th>\n FFT Score<\/th>\n Sex<\/th>\n Gender<\/th>\n Age Group<\/th>\n Ethnicity<\/th>\n Sexuality<\/th>\n Disability<\/th>\n Religion<\/th>\n Patient ID<\/th>\n <\/tr>\n <\/thead>\n<\/table>","options":{"pageLength":10,"lengthMenu":[10,30,50],"dom":"lrtip","search":{"caseInsensitive":false},"scrollX":true,"columnDefs":[{"className":"dt-right","targets":[0,9,17]}],"order":[],"autoWidth":false,"orderClasses":false,"orderCellsTop":true,"serverSide":true,"processing":true},"selection":{"mode":"multiple","selected":null,"target":"row","selectable":null}},"evals":[],"jsHooks":[],"deps":[{"name":"jquery","version":"3.6.0","src":{"href":"jquery-3.6.0"},"meta":null,"script":"jquery-3.6.0.min.js","stylesheet":null,"head":null,"attachment":null,"all_files":true},{"name":"dt-core","version":"1.13.4","src":{"href":"dt-core-1.13.4"},"meta":null,"script":"js/jquery.dataTables.min.js","stylesheet":["css/jquery.dataTables.min.css","css/jquery.dataTables.extra.css"],"head":null,"attachment":null,"package":null,"all_files":false},{"name":"nouislider","version":"7.0.10","src":{"href":"nouislider-7.0.10"},"meta":null,"script":"jquery.nouislider.min.js","stylesheet":"jquery.nouislider.min.css","head":null,"attachment":null,"package":null,"all_files":true},{"name":"selectize","version":"0.12.0","src":{"href":"selectize-0.12.0"},"meta":null,"script":"selectize.min.js","stylesheet":"selectize.bootstrap3.css","head":null,"attachment":null,"package":null,"all_files":true},{"name":"crosstalk","version":"1.2.0","src":{"href":"crosstalk-1.2.0"},"meta":null,"script":"js/crosstalk.min.js","stylesheet":"css/crosstalk.min.css","head":null,"attachment":null,"all_files":true}]} - # mod_demographics_selection_server work correctly: CONFIG with demographic feature Code diff --git a/tests/testthat/test-app_config.R b/tests/testthat/test-app_config.R index a406541..95c7a44 100644 --- a/tests/testthat/test-app_config.R +++ b/tests/testthat/test-app_config.R @@ -11,12 +11,12 @@ test_that("trust configuration is still the same", { withr::local_envvar("R_CONFIG_ACTIVE" = "trust_LPT") expect_true(isTruthy(get_golem_config("location_1"))) expect_true(isTruthy(get_golem_config("location_2"))) - expect_true(isTruthy(get_golem_config("location_3"))) + expect_false(isTruthy(get_golem_config("location_3"))) expect_true(isTruthy(get_golem_config("comment_1"))) - expect_true(isTruthy(get_golem_config("comment_2"))) - expect_equal(get_golem_config("demography_1"), "sex") - expect_equal(get_golem_config("demography_2"), "age") - expect_equal(get_golem_config("demography_3"), "ethnicity") + expect_false(isTruthy(get_golem_config("comment_2"))) + expect_null(get_golem_config("demography_1")) + expect_null(get_golem_config("demography_2")) + expect_null(get_golem_config("demography_3")) # trust_NUH ---- withr::local_envvar("R_CONFIG_ACTIVE" = "trust_NUH") diff --git a/tests/testthat/test-app_server.R b/tests/testthat/test-app_server.R index 9240adc..b0ebffd 100644 --- a/tests/testthat/test-app_server.R +++ b/tests/testthat/test-app_server.R @@ -160,7 +160,7 @@ test_that("loads mod_header_message_server correctly", { testServer(app_server, { expect_called(m, 1) - expect_args(m, 1, "messageMenu", phase_2_db_data, data_exists) + expect_args(m, 1, "messageMenu", "get_pool", phase_2_db_data, data_exists) }) }) diff --git a/tests/testthat/test-fct_app_server-helpers.R b/tests/testthat/test-fct_app_server-helpers.R index db4da10..f080247 100644 --- a/tests/testthat/test-fct_app_server-helpers.R +++ b/tests/testthat/test-fct_app_server-helpers.R @@ -72,18 +72,18 @@ test_that("get_location_data works", { }) test_that("get_demography_data works", { - withr::local_envvar("R_CONFIG_ACTIVE" = "trust_LPT") - # > get_golem_config("demography_2") - # [1] "age" + withr::local_envvar("R_CONFIG_ACTIVE" = "phase_2_demo") # > get_golem_config("demography_1") + # [1] "age" + # > get_golem_config("demography_2") # [1] "sex" # > get_golem_config("demography_3") # [1] "ethnicity" test1 <- get_demography_data( return_data = phase_2_db_data, - select_demography_1 = "Male", - select_demography_2 = "0 - 11", + select_demography_1 = "0 - 11", + select_demography_2 = "Male", select_demography_3 = "Mixed" ) expect_equal(nrow(test1), 2) diff --git a/tests/testthat/test-server-modules.R b/tests/testthat/test-server-modules.R index 879a16e..b14b469 100644 --- a/tests/testthat/test-server-modules.R +++ b/tests/testthat/test-server-modules.R @@ -87,7 +87,7 @@ test_that("mod_data_management_server work correctly", { expect_equal(nrow(dt_out$data), 100) expect_equal(ncol(dt_out$data), 18) expect_equal(class(dt_out$data$category), "list") - expect_snapshot(output$pat_table) + expect_no_error(output$pat_table) expect_equal(class(proxy), "dataTableProxy") expect_true(inherits(dt_out$complex_comments, "data.frame")) }) @@ -316,19 +316,21 @@ test_that("it validates the plot data when group is at least 10", { # mod_header_message_server ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ test_that("mod_header_message_server work correctly", { # arrange + + stub(mod_header_message_server, "DBI::dbGetQuery", + tibble::tibble("MAX(last_upload_date)" = Sys.Date(), + "MAX(last_edit_date)" = Sys.Date())) data <- phase_2_db_data %>% mutate(last_edit_date = NA) # no data in the database - # there is data in the database - testServer(mod_header_message_server, args = list(data, FALSE), { + testServer(mod_header_message_server, args = list('pool', data, FALSE), { # act/assert expect_error(output$dynamic_messageMenu) }) - # There is data in the database # there is data in the database - testServer(mod_header_message_server, args = list(data, TRUE), { + testServer(mod_header_message_server, args = list('pool', data, TRUE), { # act/assert expect_no_error(output$dynamic_messageMenu) expect_identical(db_data, data) diff --git a/tests/testthat/test-tidy_upload.R b/tests/testthat/test-tidy_upload.R index adb8d6b..c9e8bf9 100644 --- a/tests/testthat/test-tidy_upload.R +++ b/tests/testthat/test-tidy_upload.R @@ -45,9 +45,9 @@ test_that("Cleaning of uploaded data works", { expect_equal(nrow(test_template), 1) withr::local_envvar("R_CONFIG_ACTIVE" = "trust_LPT") - test_upload <- upload_data(head(phase_2_upload_data, 50), conn = NULL, trust_id = "trust_LPT", write_db = F) + test_upload <- upload_data(data = head(phase_2_upload_data, 50), conn = NULL, trust_id = get_golem_config('trust_name'), write_db = F) expect_true(inherits(test_upload, "data.frame")) expect_true(inherits(test_upload$super_category, "list")) expect_true(inherits(test_upload$category, "list")) -}) \ No newline at end of file +})