Read_html in R shiny app based on user url input - r

I am trying to build a shiny app where the user can copy and paste a web address as text input, and the app will then read in the web data from the address they provided. I tried using the textinput to let user enter their web address, and then using the read_html function in rvset to read in the web data.
On the ui side I have something like this:
conditionalPanel(
condition = "input.Articlesource == 'Web page'",
textInput("fileRequest", "Copy and paste link to aritcle here")
)
)
On the server side:
article <- reactive({
if (input$Articlesource == "Web page") {
read_html(input$fileRequest)
}
})
I don't think it reads anything in like this. I just need to read the web data in and then do some further analysis on it. And no, I do not have a specific website I want to use. Basically I want the user to be able to enter whatever they want. Anyone with experience on something like this?

Related

how to connect to R Shiny website using programming

We have a R shiny based website where we input some parameters and get some results. We can do it manually, but considering many actions are repeat, we want write some R code to automatically pull out the results. Look looks we cannot use some simple REST request, because the HTTP request used some information like "nonce" and "session". Also I am not familiar with the JavaScript. Could someone help me to understand how to do it?
There is a functionality session$registerDataObj which allows you to add an API endpoint for a specific shiny session. This will make the provided data available at http://{host}/session/{session_id}/dataobj/{name}.
The minimal app below invokes the produced endpoint via browseURL(). See ?shiny::session for more details.
shinyApp(
ui = fluidPage(),
server = function(input, output, session) {
name <- "iris"
session$registerDataObj(
name = name,
data = iris,
filterFunc = function(data, req) {
httpResponse(content = paste('nrow: ', nrow(data)))
}
)
uri <- paste0("http://", session$request$HTTP_HOST, "/session/",
session$token, "/dataobj/", name)
browseURL(print(uri))
}
)
#> Listening on http://127.0.0.1:5130
#> [1] "http://127.0.0.1:5130/session/fd47ff29025bbe4d9dbf922b935186b3/dataobj/iris"
This should open up a new browser tab that shows: "nrow: 150". The return value of session$registerDataObj() can also be used to create the uri with additional query parameters.
But I personally think it is better to use a database in 99% of all usecases. This would mean that there is an export button in your ui which causes the app server to write all relevant results into the database and the database is then accessed by another process that needs the app data.

Shiny: conditionally build UI

I'm building a Shiny dashboard to show a large amount of data. People access the dashboard through a separate login page (non-Shiny) that sits in front, at which point a JWT is generated and put in a cookie. I've managed to read in the cookie in Shiny and parse the data, saving the token in a variable called userPermissions. Now, however, I'd like to show/hide tabs in the dashboard depending on the user permissions.
For example: I have data on both managers and assistants. I want to show the manager data to any user with userPermissions == 'manager', and the assistant data to anyone with userPermissions == assistant.
I thought the best way would be to use conditionalPanel() – here's a (simplified) reproducible example:
library(shiny)
# UI file
ui <- fluidPage(
# JS to read cookie -- simplified to just return value!
tags$head(tags$script(
HTML('
Shiny.addCustomMessageHandler("goReadTheCookie", function (message) {
Shiny.onInputChange("cookie", "manager");
})
')
)
# Title
,titlePanel('Test')
# Navbar
,navbarPage(
id="navbar"
,conditionalPanel(condition = "userPermissions() == 'manager'",
mainPanel(textOutput('Manager data')))
,conditionalPanel(condition = "userPermissions() == 'assistant'",
mainPanel(textOutput('Assistant data')))
)
))
# Server file
server <- shinyServer(function(input, output,session){
## Grab permissions from Cookie
# Prepare output
userPermissions <- reactiveVal("")
# Tell JS to return cookie
session$sendCustomMessage(type="goReadTheCookie", message=list(name="cookie_name"))
# Listen for cookie
observeEvent(input$cookie,{
## -- Bunch of code omitted for sake of brevity -- ##
userPermissions("manager")
})
})
# Run app
shinyApp(ui=ui, server=server)
The problem with this code is that, in a browser console, I get the error Can't find variable: userPermissions.
I guess what's going on here is that the entire ui is executed, before JS can grab and return the cookie. What would be the best way to solve this?
Or, maybe I'm going about this the wrong way. I obviously need to verify the cookie server-side (i.e., in R) not to divulge the secret; and preferably this check, and the hiding/showing is completed at the very start of the Shiny application (userPermissions won't change during the session). Maybe there's a different (& better) solution to get to that point?
Any help would be very much appreciated!
In the end I found that the function appendTab does exactly what I was looking for. This needs to be run in server.R though, within the function to look for the cookie (otherwise userPermissions indeed doesn't exist). I could then do:
appendTab("navbar" # the id of the navigation bar created in ui.R
,tabPanel("tab name"
,mainPanel(...))
)
where tabPanel(...) could be anything you'd normally put in ui.R.
The added benefit here is that hidden tabs are also not available in the HTML source, as they're never even passed from the server to the client!

How to create a log-in page for shiny R

Is there a simple way for me to create a log-in page for my app? I want to render a ui with multiple tabs (ie. fluidPage(navbarPage(tabPanel("tab1", ...), tabPanel("tab2", ...),...)) conditional upon successful user authentication. I initiate a database connection with an R6 object, if it fails, I print out an "error" and the user can retry entering their credentials. If the connection is successful, I'd like to render the ui (ui2) I specify above.
ui <- fluidPage(fluidRow(column(width=12, textInput('uid', label =
"Enter your user id:"), passwordInput("pwd",
label = "Enter your password:"), actionButton("login",
"Login"), uiOutput("resulting_ui"), offset = 5)))
If you already set up a database that can hold usernames and passwords, you can have users enter their credentials, compare them to the ones in the database, and change a value of a reactiveVal() based on that comparison. The value of that reactiveVal() will control whether you show your UI or not.
For example you can use something like this:
logged <- reactiveVal(value = F)
# change the value of logged() when a user enters correct credentials
output$your_ui <- renderUI({
req(logged())
# your ui here...
})
For a more elaborated example take a look here:
https://github.com/yanirmor/shiny-user-management
Starting Shiny app after password input
This did mostly resolve my question. I just had to make some minor changes to get it to work.

Limiting the number of users in a locally hosted R shiny app

I'd like to limit the number of users of my locally hosted r shiny app to one user at any one time.
So ideally when a second user attempted to run the app at the same time (users access the app by typing the local IP into the address field) the app would display a default message and stop any further progress. Nullifying any other user commands may not matter if the only thing shown upon entry is this denial message.
The content of the app doesn't matter so we can use this app as an example: http://shiny.rstudio.com/gallery/tabsets.html
Thanks for any help or info you can give.
I wouldn't recommend doing this, I think it's very dangerous, but there are ways you could hack this together. Here's one solution (as I said, it's hacky and I wouldn't do it myself). The basic idea is to have a global variable that keeps track of whether or not someone is using the app. If nobody is using the app, show the app and turn on the flag and make sure to turn off the flag when the user exits.
shinyBusy <- FALSE
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::hidden(
h1(id = "busyMsg", "App is busy")
),
shinyjs::hidden(
div(
id = "app",
p("Hello!")
)
)
),
server = function(input, output, session) {
if (shinyBusy) {
shinyjs::show("busyMsg")
} else {
shinyBusy <<- TRUE
session$onSessionEnded(function() shinyBusy <<- FALSE)
shinyjs::show("app")
}
}
),
launch.browser = TRUE)
Note: in order to show/hide app elements, I'm using a package that I wrote shinyjs

Execute a function in background when clicking a button with no output being changed (Shiny)

I want to be able to click a button within my Shiny App. So far so easy.
But when doing so, a function that loads, writes and then saves an excel sheet should be executed "behind the scenes" with no output changed in the App itself.
How can I do this?
Kind regards,
Martin
Yes. Try to use an Observer.
in server.R:
shinyServer(function(input, output, session) {
# other code ...
observe({
# do_something_button is your actionButton id.
if (input$do_something_button > 0) {
# perform behind the scenes analysis here.
# if you want to send a message to the client side, you can try
# something like:
message <- list(type="completed", excel_file=saved_file)
session$sendCustomMesage("background_task", message)
}
})
# other code ...
})
then write some custom javascript snippet and load it via ui.R:
Shiny.addCustomMessageHandler("background_task", function(message) {
console.log("Finished processing: " + message.excel_file);
});
In this way when the server completes the background processing, or should an error occur, you can get some feedback on the client side. Maybe you want to notify the user about an error or something.
The reference for using observe is here

Resources