How to get a folder path (not file path) in Shiny - r

I need to get the path of a folder, which will be selected by the user. I tried using shinyFiles but cant get it to work properly.
So far, this code has worked. However, I can get the path from files but not from folders.
library(shiny)
library(shinyFiles)
ui <- fluidPage(
shinyFilesButton("Btn_GetFile", "Choose a file" ,
title = "Please select a file:", multiple = FALSE,
buttonType = "default", class = NULL),
textOutput("txt_file")
)
server <- function(input,output,session){
volumes = getVolumes()
observe({
shinyFileChoose(input, "Btn_GetFile", roots = volumes, session = session)
if(!is.null(input$Btn_GetFile)){
# browser()
file_selected<-parseFilePaths(volumes, input$Btn_GetFile)
output$txt_file <- renderText(as.character(file_selected$datapath))
}
})
}
shinyApp(ui = ui, server = server)
I got it from: Getting file path from Shiny UI (Not just directory) using browse button without uploading the file but cannot answer because of reputation.

For choosing a folder change the code to use shinyDirButton and shinyDirChoose.
Also noted that you should reference to roots where running users have permission to access otherwise it will throw errors. Example here is I assign volumes = c(home = 'C:/Users/sinhn/') for trial run on my Windows computer. You can have multiple location as named vector here.
library(shiny)
library(shinyFiles)
ui <- fluidPage(
shinyDirButton("Btn_GetFolder", "Choose a folder" ,
title = "Please select a folder:", multiple = FALSE,
buttonType = "default", class = NULL),
textOutput("txt_file")
)
server <- function(input,output,session){
volumes = c(home = "C:/Users/sinhn/")
observe({
shinyDirChoose(input, "Btn_GetFolder",
roots = volumes)
})
output$txt_file <- renderText({
file_selected <- parseDirPath(roots = volumes, input$Btn_GetFolder)
})
}
shinyApp(ui = ui, server = server)

Related

File path from user input as get_fields file input in Shiny App

I'm building a little applet that will run locally, where people can upload a csv and a fillable pdf and the tool will execute a loop that will fill out the pdfs with names from the csv and save them as png files in an /output directory.
I am having trouble with the pdf portion. Using shinyFiles they navigate to the pdf and get its path, but am getting an invalid path error trying to get the pdf fields (staplr). I think it is happening with get_fields but I can't think of another way to get the pdf location.
Warning: Error in path.expand: invalid 'path' argument
[No stack trace available]
Code snip below. Any ideas welcome!
library(tidyverse)
library(staplr)
library(DT)
library(shinyFiles)
library(pdftools)
ui <- fluidPage(
titlePanel(p("Award PDF Creation App", style = "color:#3474A7")),
sidebarLayout(
sidebarPanel(
p("Award Creation Tool"),
# Horizontal line ----
tags$hr(),
fileInput(inputId = "filedata",
label = "Choose your CSV file",
accept = c(".csv")),
shinyFilesButton("pdf", "PDF select", "Please select a PDF", multiple = TRUE, viewtype = "detail"),
tags$p(),
tags$p('Please choose the fillable PDF for award creation.'),
tags$hr()
),
mainPanel(h3("Review your CSV format before you Create PDFs"),
DTOutput(outputId = "table"),
tableOutput("contents"),
actionButton("go", "Create PDFs")
)
)
)
server <- shinyServer(function(input, output, session){
volumes <- c(Home = fs::path_home(), "R Installation" = R.home(), getVolumes()())
shinyFileChoose(input, "pdf", roots = volumes, session = session,
filetypes = c('', 'pdf'))
# by setting `allowDirCreate = FALSE` a user will not be able to create a new directory
pdf <- reactive(input$pdf)
data <- reactive({
req(input$filedata)
read.csv(input$filedata$datapath)
})
pdfpath <- reactive({
req(input$pdf)
as.character(parseFilePaths(volumes,pdf())$datapath)
})
output$table <- renderDT(
data()
)
observeEvent(input$go,{
req(input$filedata)
req(input$pdf)
data <- data()
pdffields <-get_fields(input_filepath = pdfpath, convert_field_names = F)
withProgress(message = 'Making PDFs', value = 0, {
for(i in 1:nrow(data)){
pdffields$`Date`$value <- paste(format(data$AWARD_DATE[i], "%B %d, %Y"))
pdffields$`First Name Last Name`$value <- paste0(data$FIRST_NAME[i], " ", data$LAST_NAME[i])
filename <- paste0('./output/', Sys.Date(),
'_', data$LAST_NAME[i],
'_', data$AWARD[i], '.png')
set_fields(pdf, filename, pdffields)
bitmap <- pdf_render_page(filename, page = 1, dpi = 300)
png::writePNG(bitmap, filename)
# Increment the progress bar, and update the detail text.
incProgress(1/nrow(data), detail = paste("Processing"))
# Pause
Sys.sleep(0.1)
}
})
})
})
shinyApp(ui = ui, server = server)

changing variables of a separate R script in a shiny app

I have a set of scripts which are run from below, with aspects of the final output influenced by lines 2-4
setwd()
inputyear = ""
inputmonth = ""
dataType = ""
source("1.R")
source("2.R")
source("3.R")
source("4.R")
source("5.R")
#input required file name
saveWorkbook(wb, "Workbook.xlsx", overwrite = TRUE)
I'd like to be able to change the input year, input month, dataType and the name of the workbook produced by the source() 1-5, from a shiny app, and then run the respective files and generate the excel file.
So far I have the following code, which does not produce any errors, but does not function as desired.
I have only included the 'server' section of the code to save space, and this is the part I need help with if possible;
ui<-shinyUI(fluidPage(theme = shinytheme("flatly"),
tags$head(
tags$style(HTML(
".shiny-output-error-validation {
color; green;
}
"))
),
basicPage(
headerPanel("Workbook"),
sidebarPanel(
selectInput("inputmonth","Select Publication Month",c("JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC")),
selectInput("inputyear","Select Year",c("2018/19","2019/20","2020/21")),
selectInput("dataType","Select Version",c("provisional","final"))),
textInput("WorkBookName","Enter File Name (include .xlsx)"),
actionButton("Generate", "Generate Workbook"))
))
server <- function(input, output, session){
observeEvent(input$Generate, {
validate(need(input$WorkBookName != "", "Please enter file name"))
req(input$inputmonth, input$inputyear, input$dataType, input$WorkBookName)
inputyear = input$inputmonth
inputmonth = input$inputyear
dataType = input$dataType
source("1.R",local = TRUE)
source("2.R", local = TRUE)
source("3.R", local = TRUE)
source("4.R", local = TRUE)
source("5.R", local = TRUE)
saveWorkbook(wb, paste0(input$WorkBookName, ".xlsx"), overwrite = TRUE)
})
}
shinyApp(ui, server)
How can I alter the server script to get the desired functionality?
edit: Full script added, sourced names removed
You'll somehow need to trigger the execution of your reactive code. Reactive code only is executed if it was invalidated. Please see this for further information.
In the following app the code will be executed once the Save Workbook button is clicked.
I don't know your UI and sourced R-scripts, so you might want to replace here accordingly:
library(shiny)
library(openxlsx)
library(shinythemes)
ui <- shinyUI(fluidPage(
theme = shinytheme("flatly"),
tags$head(tags$style(
HTML(".shiny-output-error-validation {
color; green;
}
")
)),
basicPage(
headerPanel("Workbook"),
sidebarPanel(
selectInput(
"inputmonth",
"Select Publication Month",
toupper(month.abb)
),
selectInput("inputyear", "Select Year", c("2018/19", "2019/20", "2020/21")),
selectInput("dataType", "Select Version", c("provisional", "final"))
),
textInput("WorkBookName", "Enter File Name (include .xlsx)"),
actionButton("Generate", "Generate Workbook"),
uiOutput("test")
)
))
server <- function(input, output, session) {
observeEvent(input$Generate, {
req(input$inputmonth,
input$inputyear,
input$dataType,
input$WorkBookName)
inputyear = input$inputmonth
inputmonth = input$inputyear
dataType = input$dataType
# source("1.R", local = TRUE)
# source("2.R", local = TRUE)
# source("3.R", local = TRUE)
# source("4.R", local = TRUE)
# source("5.R", local = TRUE)
#
# saveWorkbook(wb, paste0(input$WorkBookName, ".xlsx"), overwrite = TRUE)
output$test <- renderUI("Everything fine...")
})
}
shinyApp(ui, server)

Reload fileInput when actionButton is clicked

I wrote a Shiny app that loads several user-defined csv files as fileInput. The app is designed to plot data from a running measurement and new datapoints are written to the input files about every five minutes. I want to be able to reload all inputs by clicking on an actionButton.
I tried to define the function reading the .csv as eventReactive:
library(shiny)
ui <- fluidPage(
actionButton(inputId = "update", label = "Reload input files"),
fileInput(inputId = "file", label = "Choose file"),
textOutput("test")
)
server <- function(input, output) {
data <- eventReactive(input$update, {
mydata <- read.delim(input$file$datapath)
return(nrow(mydata))
})
output$test <- renderText(print(data()))
}
shinyApp(ui = ui, server = server)
When I choose an input file and click the action button, the output is correctly rendered. If I now open the csv file, add additional rows and click the action button again, the output is not updated.
Based on this answer I was able to create a workaround for you problem.
As I pointed out in my comment above, the reason why it is not possible to update fileInput with an action button is that, apparently, fileInput creates a temporary file in a temporary directory and the Input$file$datapth links to this temporary file. So you can reload the file with using the action button as often as you like, changes to the orignial file will not be reflected, since the link is pointing to the temporary file. I really don't know why inputFile works with temp files, but using the shinyFiles packages, you can build a workaround. You have one button which gets the real link to your file and load the data in and another button to reload the data. Pressing the load button will reload the original data and all changes to it will be reflected.
library(shiny)
library(shinyFiles)
ui <- fluidPage(
shinyFilesButton("GetFile", "Choose a file" ,
title = "Please select a file:", multiple = FALSE,
buttonType = "default", class = NULL),
actionButton(inputId = "reload", label = "Reload data"),
tableOutput("test")
)
server <- function(input,output,session){
volumes <- getVolumes()
v = reactiveValues(path = NULL)
observe({
shinyFileChoose(input, "GetFile", roots = volumes, session = session)
if (!is.null(input$GetFile)) {
file_selected <- parseFilePaths(volumes, input$GetFile)
v$path <- as.character(file_selected$datapath)
req(v$path)
v$data <- read.csv(v$path)
}
})
observeEvent(input$reload, {
req(v$path)
v$data <- read.csv(v$path)
})
output$test <- renderTable({
print(v$path)
if (is.null(v$data)) return()
v$data
})
}
shinyApp(ui = ui, server = server)
Update
It is also possible to combine this approach with reactiveFileReader, see example below:
library(shiny)
library(shinyFiles)
ui <- fluidPage(
shinyFilesButton("GetFile", "Choose a file" ,
title = "Please select a file:", multiple = FALSE,
buttonType = "default", class = NULL),
tableOutput("test")
)
server <- function(input,output,session){
volumes <- getVolumes()
v = reactiveValues(path = NULL)
observe({
shinyFileChoose(input, "GetFile", roots = volumes, session = session)
req(input$GetFile)
file_selected <- parseFilePaths(volumes, input$GetFile)
v$path <- as.character(file_selected$datapath)
req(v$path)
v$data <- reactiveFileReader(1000, session, filePath = v$path, readFun = read.csv, sep = ";")
})
output$test <- renderTable({
print(v$path)
req(v$data)
v$data()
})
}
shinyApp(ui = ui, server = server)

shinyFiles package within module

I'm having trouble using the shinyFiles package with module. When i'm using it without module it works fine. When i'm using it within module i can't dive into directories (other threads have no positive answer) :
#' #export
dirModule = function(input, output, session, fileRoot = NULL) {
root = c(C = "/")
shinyFileChoose(input, session$ns('files'), roots = root, session = session)
shinyDirChoose(input, session$ns("directory"), session=session, roots = c(home = '/home', root = '/'), filetypes=c(''))
shinyFileSave(input, session$ns("fileSave"), roots = root, session = session)
observeEvent(input$files, { print(parseFilePaths(root, input$files)$datapath) })
observeEvent(input$directory, { print(parseDirPath(root, input$directory)) })
observeEvent(input$fileSave, { print(parseSavePath(root, input$fileSave)$datapath) })
}
#' #export
dirModuleUI = function(id) {
ns = NS(id)
fluidPage(
fluidRow(
shinyFilesButton(ns('files'), label='File select', title='Please select a file', multiple=T),
shinyDirButton(ns("directory"), label="Directory select", title = "Select directory", FALSE),
shinySaveButton(ns("fileSave"), label = "File save", title = "Save file as", filetype=list(text='txt'))
)
)
}
I had the same issue and solved by using the Github version.
You can first install devtools package by install.packages('devtools') and then use devtools::install_github("thomasp85/shinyFiles") to install the latest shinyFiles package.
After that, just get rid of all the session$ns call in your module server function.
Similar post here
It is namespace issue. I stuck here multiple times in different R version and here is the solution in R 3.6.
I cannot get it work in R 3.4 because it always have problem navigate into subfolders.
#' #export
dirModule = function(input, output, session, fileRoot = NULL) {
root = c(C = "/")
shinyFileChoose(input, 'files', roots = root, session = session)
shinyDirChoose(input, "directory", session=session, roots = c(home = '/home', root = '/'), filetypes=c(''))
shinyFileSave(input, "fileSave", roots = root, session = session)
observeEvent(input$files, { print(parseFilePaths(root, input$files)$datapath) })
observeEvent(input$directory, { print(parseDirPath(root, input$directory)) })
observeEvent(input$fileSave, { print(parseSavePath(root, input$fileSave)$datapath) })
}
#' #export
dirModuleUI = function(id) {
ns = NS(id)
fluidPage(
fluidRow(
shinyFilesButton(ns('files'), label='File select', title='Please select a file', multiple=T),
shinyDirButton(ns("directory"), label="Directory select", title = "Select directory", FALSE),
shinySaveButton(ns("fileSave"), label = "File save", title = "Save file as", filetype=list(text='txt'))
)
)
}

dependencies in shiny objects

In my app, the user needs to pick a folder, and in that folder he needs to pick a file (the suffix of the file name is '.seg')
This code is working-
library(shiny)
ui <- shinyUI(fluidPage(
# select a folder
column(2, absolutePanel(fixed = TRUE, width = '180px',
selectInput("pick_a_folder", label = '', selected='choose a folder',
choices = setNames(as.list(c('choose a folder',
basename(list.dirs(recursive = FALSE)))),
c('choose a folder',
basename(list.dirs(recursive = FALSE))))))),
# select a file
column(2, absolutePanel(fixed = TRUE, width = '180px',
conditionalPanel(condition='!(input.pick_a_folder=="choose a folder")',
uiOutput('fileselection'))))
))
server <- shinyServer(function(input, output) {
# dinamic file selection. find the files list after folder is choosen
output$fileselection <- renderUI({
selectInput('pick_file', '', selected = 'choose a file',
choices=setNames(as.list(c('choose a file',basename(list.files(path=input$pick_a_folder,recursive=FALSE, pattern='\\.seg$')))),
c('choose a file',basename(list.files(path = input$pick_a_folder, recursive = FALSE, pattern='\\.seg$')))))
})
})
shinyApp(ui = ui, server = server)
The issue is that if I add a folder to the working directory after I ran the code, it will not appear.
So I tried to move the folder selection to the server, and make it dependent on a refresh button, but I get an error
Error in list.files: invalid 'path' argument
this is my code-
library(shiny)
ui <- shinyUI(fluidPage(
# refresh butten for root directory
column(1, absolutePanel(fixed=TRUE, actionButton("refresh_wd", "refresh"))),
# select a folder
column(2, absolutePanel(fixed = TRUE, width = '180px', uiOutput('folderselection'))),
# select a file
column(2, absolutePanel(fixed = TRUE, width = '180px',
conditionalPanel(condition='!(input.pick_a_folder=="choose a folder")',
uiOutput('fileselection'))))
))
server <- shinyServer(function(input, output) {
# refresh root directory
wd_folders <- eventReactive(input$refresh_wd, {
basename(list.dirs(recursive = FALSE))
})
output$folderselection <- renderUI({
selectInput('pick_a_folder', '', selected = 'choose a folder',
choices = setNames(as.list(c('choose a folder', wd_folders())),
c('choose a folder', wd_folders())))
})
# dinamic file selection. find the file list after folder is choosen
output$fileselection <- renderUI({
selectInput('pick_a_file', '', selected = 'choose a file',
choices=setNames(as.list(c('choose a file',basename(list.files(path=input$pick_a_folder,recursive=FALSE, pattern='\\.seg$')))),
c('choose a file',basename(list.files(path = input$pick_a_folder, recursive = FALSE, pattern='\\.seg$')))))
})
})
shinyApp(ui = ui, server = server)
Any help would be appreciated
Because you use eventReactive() the list of folders and even the folder selection will only be displayed after somebody clicked on the 'Refresh' button. You can avoid this by using ignoreNULL = FALSE :
wd_folders <- eventReactive(input$refresh_wd, {
basename(list.dirs(recursive = FALSE))
}, ignoreNULL = FALSE)
If you don't do this, the value of wd_folders() will be NULL to start with, so your condition for your conditionalPanel is fulfilled (it's not "select a folder") and hence your app tries to read the files in directory NULL. This gives you your error.
If you want to be extra safe, you can add validate(need()) to the UI rendering as well, eg:
output$fileselection <- renderUI({
validate(need(input$pick_a_folder, label = "Pick a folder first"))
validate(need(dir.exists(input$pick_a_folder),
label = "Something went wrong. Contact me."))
selectInput('pick_a_file', '', selected = 'choose a file',
...)
})
This isn't necessary to fix your problem, but I find it good practice in Shiny.
Here is a minimal example that auto refresh the folders every 5 seconds.
It does still produce an initial warning about path being invalid for the reasons #JoriMeys explained.
library(shiny)
ui <- shinyUI(fluidPage(
column(1,
absolutePanel(fixed=TRUE,
textOutput('wd'),
uiOutput('folderselection'),
conditionalPanel(
condition='!(input.pick_a_folder=="choose a folder")',
uiOutput('fileselection'))
)
)
)
)
server <- shinyServer(function(input, output) {
output$wd <- renderText(basename(
list.files(path = input$pick_a_folder,
recursive=FALSE)
)
)
button <- reactiveTimer(intervalMs = 5000)
# refresh root directory
wd_folders <- reactive({
button()
basename(list.dirs(recursive = FALSE))
})
output$folderselection <- renderUI({
selectInput('pick_a_folder', '',
choices = c('choose a folder', wd_folders()
)
)
})
# dinamic file selection. find the file list after folder is choosen
output$fileselection <- renderUI({
selectInput('pick_a_file', '',
selected = 'choose a file',
choices=c('choose a file',
basename(list.files(path = input$pick_a_folder,recursive=FALSE))))
})
})
shinyApp(ui = ui, server = server)

Resources