I would like to find a similar function to reactiveFileReaderfunction from R Shiny pacakge to dynamically read data from an Oracle database using RJDBC driver.
Please find an example below to explain my issue:
Suppose my_data.csv is a extraction of my MY_ORACLE_TABLE (which is hosted on my oracle database).
In the first case below, when I manualy change the values of my_data.csv, my Shiny app automaticaly updates:
server <- function(input, output) {
output$table1 <- DT::renderDataTable({
reactiveFileReader(1000,session = NULL,filePath = "my_data.csv",readFunc = read.csv2)()
})
}
But in the second case below, supposing MY_ORACLE_TABLE updates, I have to reload my Shiny app URL to make the display update:
server <- function(input, output) {
output$table1 <- DT::renderDataTable({
reactive({dbGetQuery(con, "SELECT * FROM MY_ORACLE_TABLE})()
})
}
Any tips ?
The problem is that
dbGetQuery(con, "SELECT * FROM MY_ORACLE_TABLE")
does not depend on any reactive context and will not automatically be re-run when something in your database changes.
One solution is to wrap your query in shiny::reactivePoll(), which is actually used to implement shiny::reactiveFileReader()
For more information, see:
https://shiny.rstudio.com/reference/shiny/1.0.3/reactivePoll.html
Related
I need to save the edits performed in Shiny by the user on a table that is rendered using the renderDataTable back to the original table.
I'm using the DT package to render a table used in the server instance. The table is meant to be viewed, filtered, and edited by the user. Then, more importantly, to be saved back to the server for further processing.
DataTableOuput ui instance provides the ability to edit records in the rendered table, and it allows to save a csv copy with the edited version (using the extensions and buttons property), but I cannot find a way to save the edited table back to the server instead of saving it to external file.
Below is a sample reproducible code using mtcars table of what I've done. I appreciate any assistance to modify the code or even to suggest other packages that can accomplish the task.
library(shiny)
library(DT)
if (interactive()) {
tbl1 <- mtcars
ui <- fluidPage(
DT::dataTableOutput("MasterData")
)
server <- function(input, output, session) {
output$MasterData <- DT::renderDataTable({
DT::datatable(tbl1,
extensions = "Buttons",
rownames=FALSE,
editable = TRUE,
filter="top",
style = "bootstrap",
options = list(
autoWidth=TRUE,
dom='Blfrtip',
buttons=c('copy','csv','excel'),
lengthMenu=c(50,100,500,1000)
)
)
})
}
shinyApp(ui, server)
}
I want to have a Shiny dashboard on Rstudio Connect where data is read from my companies data store via JDBC, and then plotted. Ultimately the user would be able to give some parameters and the appropriate output would be displayed.
What I've discovered is that if I show a table of the data and the plot, everything works out fine. However, if I do not display the table I get an error:
Error: An error has occurred. Check your logs or contact the app author for clarification.
The line in the log files related to the error is not meaningful to me, but appears to be java related (and so maybe related to the JDBC connection):
Warning: Error in .jcheck: Java Exception <no description because toString() failed>
.jcall(conn#jc, "Ljava/sql/Statement;", "createStatement")
new("jobjRef", jobj = <pointer: 0x55cbc9a35608>, jclass = "java/lang/Throwable")
What works?
Here's as minimum a reproducible example as I could get. I appreciate that the architecture is internal to my company, so it won't run for others, but I think it can be helpful. The below code works:
# app.R
library(RJDBC)
library(shiny)
getconnection <- function(){
ORACLE_JAR <- '/usr/lib/oracle/21/client64/lib/ojdbc8.jar'
db_host = "db_host.company.information:1521"
drv <- RJDBC::JDBC("oracle.jdbc.OracleDriver",
ORACLE_JAR,
identifier.quote="`")
sid <- "sid.prd.tns"
url <- paste("jdbc:oracle:thin:#/",
db_host,
sid, sep = '/')
connection<-dbConnect(drv,
url,
user='****', # anonymised for stack overflow
password='****',
believeNRows=FALSE )
return(connection)
}
ui <- fluidPage(
titlePanel("MinRepEx"),
tableOutput('result'),
plotOutput('plot')
)
server <- function(input, output) {
conn <- getconnection()
mydata <- reactive({query_result(conn)})
output$result <- renderTable(head(mydata()))
output$result <- NULL
myplot <- reactive({plot(OBS_VALUE ~ OBS_DATE, data = mydata())})
output$plot <-renderPlot({myplot()})
}
shinyApp(ui = ui, server = server)
When I deploy this, as I say it works:
What doesn't work?
If I remove the lines related to result, i.e. the table output things stop working:
## Everyhing above this line is kept unchanged
ui <- fluidPage(
titlePanel("MinRepEx"),
# tableOutput('result'),
plotOutput('plot')
)
server <- function(input, output) {
conn <- getconnection()
mydata <- reactive({query_result(conn)})
# output$result <- renderTable(head(mydata()))
myplot <- reactive({plot(OBS_VALUE ~ OBS_DATE, data = mydata())})
output$plot <-renderPlot({myplot()})
}
shinyApp(ui = ui, server = server)
which leads to:
Possible solutions that need tweeking:
The code works as intended on my machine it does not work when I ship it to RStudio Connect.
The code is a minimised version of the code where there is a need for query_result to be a reactive function, so I can't take it out of the reactive world. However, if I do, I can display the chart on its own. `
Likewise, if I open the connection conn inside mydata it will generate the image. However opening the connection each time is very slow. If there was someway to check if a connection was open and if not open one, or to make the connection visible inside mydata?
What else have I tried?
I've tried a few other things that might help inform about the solution:
If I just do renderTable, there are no problems (except that I do't have the chart I want).
If I change the order of plotOutput and tableOutput in the UI, the table and plot appear
If I change the order of renderTable and renderPlot in the server, neither table nor plot are produced and I have the same error message in the logs twice.
If I introduce a Sys.sleep(60) command to myplot, it doesn't fix the issue
I have put a the line data <- mydata() inside the renderPlot({...}) before myplot()
The above 'possible solutions' might yield something, but rather frustratingly, I would like to know why the connection remains visible if I show the table or the table and plot, but not the plot on its own. Likewise, the code works without this issue locally, but not remotely. It would be interesting to understand why.
I know that using something like this code:
library(shiny)
ui <- fluidPage(
mainPanel(
textOutput("Query_String")
)
)
server <- function(input, output, session) {
observeEvent(session$clientData$url_search,{
Query <- session$clientData$url_search
output$Query_String <- renderText(Query)
# Long list of operations dependant on the parameters passed in the URL
})
}
shinyApp(ui = ui, server = server)
Can enable me to read parameters into my shiny app via URL queries. However it seems like I basically have to wrap my whole server content into one big observe() or observeEvent() to create the needed reactive context.
Is there any way to avoid this?
To avoid recalculating everything if just a single item changed, one wants to create multiple separate observes instead of just a big one. Long code should be refactored in functions e.g. process_query keeping the server function small, allowing to read the overall structure. Important intermediate results can be refactored in their own reactive values (here query). output$Query_String doesn't need to be nested in another reactive context.
process_query <- function(url) {
# many steps to process the url
return(url)
}
server <- function(input, output, session) {
query <- reactive(process_query(session$clientData$url_search))
output$Query_String <- renderText(query())
}
I'm trying to deploy a Shiny app that allows the user to upload a pdf document and extract a table from a selected page. For this I'm using the package tabulizer. A basic reproducible example:
library(shiny)
library(tabulizer)
ui <- fluidPage(
fileInput("report", NULL,buttonLabel = "Upload report"),
numericInput("page","Specify page number",value = 1),
actionButton("extract","Extract"),
verbatimTextOutput("data")
)
server <- function(input, output, session) {
generate_data <- reactive({
req(input$report)
# This locate_area function calls runApp() from the tabulizer package
area <- locate_areas(file = input$report$datapath,
pages = input$page,
widget = "reduced")
table <- extract_tables(file = input$report$datapath,
pages = input$page,
area = area)
return(table)
})%>% bindCache(input$page) %>% bindEvent(input$extract)
output$data <- renderPrint({
# Just for the sake of this example to show it works
generate_data()
})
}
shinyApp(ui = ui, server = server)
If I run this locally, the locate_area() will make the pdf page pop-up on my viewer in RStudio and all is well. However, if I publish the app it doesn't run after clicking the action button. I know the problem comes from the locate_area() as it essentially calls another runApp within the shiny app. I have tried using different widgets for locate_area() to no avail. Does anybody know a way to circumvent this issue?
Judging by the relevant issues - issue 15 and issue 53 - it appears that your best way to go is really to copy the functionality from the original tabulizer function into your own app, as currently the package does not provide an easy integration with other Shiny apps.
I'm trying to write tests in order to check if a shiny function fileInput() is reading files correctly.
My problem is that I don't know what to write in session$setInputs() in order to grab the file from my system.
Here is an example app:
library(shiny)
ui <- fluidPage(
tagList(
fileInput("file", "Please upload a file"),
tableOutput("text")
)
)
server <- function(input, output, session){
file <- reactive({input$file})
output$text <- renderTable({
req(file())
read.csv(file()$datapath)
})
}
shinyApp(ui, server)
Now, I want to be able to use testServer() in order to set a file address and see if my app loads it correctly, but I can't figure out how to do it:
address <- "path/to/text.csv"
testServer(server, {
session$setInputs(file = address)
print(file())
})
I think it has to do with the fact that fileInput() uploads the file to a temp folder and returns to shiny a dataframe where you can get the datapath, but I'm unable to simulate this pass in order to make the test work
I have the same question as you do, I did some investigating and could not find any way of testing fileInput with testServer or testthat. The best solution that I found was testing fileInput by taking a snapshot when recording a test with recordTest() of the shinytest package.
Sorry for answering this late.
I asked the same question at rstudio's forums and got an answer here
The basics of it are setting the file's datapath as a list:
address <- "path/to/text.csv"
testServer(server, { session$setInputs(file= list(datapath = address)) })