Saving survey results in Shiny

1,811 views
Skip to first unread message

Conal Monaghan

unread,
Jan 1, 2017, 7:45:49 AM1/1/17
to Shiny - Web Framework for R
Hi everyone,
         I was wondering if anyone is familiar with saving data in a Shiny app. I want to give people a few questions (below simply inputs score 1 and score 2, and sys.Date) and save them into a .csv which I have called "Data". As each new person completes the survey their results are added as a new row in the data (to do this I am using "nrow(Data)+1"). "Data.csv" is placed into the directory folder (three columns - Score 1, Score 2, Time; attached to post).  

       The second role of the app is to display the ever growing dataset as a table in the UI and a small summary. This will also include a download data button so that people can download the latest Data file (with their data in it) if they want. This can be achieved using datahandler().    

Below is my attempt so far but I think it's a while off. Any help on this from would be amazing. 

Kind Regards and thank you for any help in advance, 
                 - Conal Monaghan



ui.R
____________________________________
library(shiny)


shinyUI(fluidPage(                                                                                                                      # Open UI
  "survey demo",                                                                                                                         # Title 
  
                                                      ######         Questions       ###### 
  selectInput(inputId = "Score1",                                                                                                 # What we are calling the object
              label = "The Question 1",                                                                                              # Question
              choices = c("Disagree Strongly" = 1, "Disagree" = 2, "Disagree Somewhat " = 3,       # Responses
                          "Neither Agree nor Disagree" = 4, "Agree Somewhat" = 5, "Agree" = 6,
                          "Agree Strongly" = 7)            
             ), 
  
  selectInput(inputId = "Score2",                                                                                                   # What we are calling the object
              label = "The Question 1",                                                                                                # Question
              choices = c("Disagree Strongly" = 1, "Disagree" = 2, "Disagree Somewhat " = 3,         # Responses
                          "Neither Agree nor Disagree" = 4, "Agree Somewhat" = 5, "Agree" = 6,
                          "Agree Strongly" = 7)            
             ), 
  
  # Submitbutton
  actionButton(inputId = "Action", label = "Submit"),                                                                      # Create submit action button
  
                              ######         Results display output      ###### 

      tableOutput(table),                                                                                                                   # Output the data for display 
      textOutput(Datadisplay),                                                                                                          # Display the summary of the data
            
     downloadButton(downloadData, "Download Data")                                                                 # Create a download button
 
          ))                                                                                                                                         # Close Shiny UI and fluid page




server.R
____________________________________
library(shiny)
 
Data <- read.csv("Data.csv", header = TRUE, sep = ",")                                                              # Load Datafile from dir

shinyServer(function(input, output) {                                                                                             # Open Shiny Server
 
##### Function 1, Download Data to .csv so that each respondent's data is recorded on a new row in the Data.csv file  ####
 

Results <- reactive(cbind(Score1, Score2, Sys.Date()))                                                                   #  Create data row

Data[nrow(Data)+1,] <- reactive(  if  (input$Action == 1){Results()})                                                  #  Put data into next row of the "Data" when the action button is pressed
write.csv(Data, file = "Data.csv")                                                                                                         #  Download new Data to replace Data.csv file in the shiny folder

##### Function 2, Output the data in a table to view and calculate a short summary using the table() function         ####

output$table <- renderTable({Data()})                                                                                          # Display Data() in a table for the UI

output$Datadisplay  <- summary(Data())                                                                                      # Produce a summary of the data in the UI                                                
 
#####                                   Function 3, Create a data download option                                     ####

output$downloadData <- downloadHandler(                                                                                  # Create the download file name
 filename = function() {
   paste("data-", Sys.Date(), ".csv", sep="")
  },
 content = function(file) {
   write.csv(Data, file)                                                                                                                     # put Data() into the download file
  })                                                                          

                                     }                                                                                                                # Close
            )                                                                                                                                        # Close







Data.csv

Laz C. Peterson

unread,
Jan 1, 2017, 4:17:56 PM1/1/17
to Conal Monaghan, Shiny - Web Framework for R
Hello there Conal,

(I forgot to reply-all and send this to the group as well … Sorry for the duplicate!)

We have been wrestling with this for the past few months.  In your case, if each person is only inputting a new line in the CSV file (or database, or any other data source), you can simply have your inputs and then when they click “Submit”, Shiny will load the existing CSV file into a data frame, rbind() the new data to that data frame, and then you can write.csv() back to the same file.

It would be even easier if the data is stored in a database (MySQL, for example).  We use the pool, dplyr and RMySQL packages to achieve this quite easily.  It’s extremely fast and powerful, and we can add data to any table in the database without much effort.

I don’t have any code for you, but after briefly looking at your code, it seems you are pretty close to achieving it.  The action button should be watched by an observeEvent() and that event will modify the data frame that your table is displaying (from the CSV file), save the file back, then reload the data into the table.  But you may definitely have issues if two people are inputting data at the exact same time.

Before we used Shiny, we used the DataTables library (datatabies.net), along with the Editor extension to achieve this exact thing you are doing.  Instead of there being separate inputs from the table, the table itself has a “New” button, which pops up a lightbox-type input to create a new record.  Then when the user clicks save, the data is validated and then immediately updated in the database so there is no issue with multiple users adding at the same time.  The DataTables library also has an Buttons extension that allows export as CSV.  So we would use that.  You can see how it looks if you go to the DataTables website, it is very nice.

I’ve been trying to get this exact same Editor functionality from within Shiny, but have been unable to do so.  They do support the CSV export using Buttons in the DT package though.  And the DT table is very nice.  You should check that one out.

~ Laz Peterson
Paravis, LLC

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/shiny-discuss/6ec49775-b95c-4a51-bb8e-2754e3038fa9%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
<Data.csv>

Stephen McDaniel

unread,
Jan 2, 2017, 8:32:21 AM1/2/17
to Conal Monaghan, Shiny - Web Framework for R
Hi Conal,

You have received very good advice from Laz.  However, I wanted to point out a few things to consider:

Shiny Server Pro & shinyapps.io allow multiple concurrent client sessions/workers (which could still work with a CSV approach) and multiple application instances/processes (which would be problematic.) So, while you only publish one app, it is entirely possible for multiple people to concurrently edit different copies of the CSV file (each in separate directories, never to be brought back together.) 

See this inline image for how a single app can be subdivided into separate "worlds" where your response CSVs are also duplicated (from https://shiny.rstudio.com/articles/scaling-and-tuning.html).

Inline image 1


I would recommend switching to a database approach (MySQL, PostgreSQL, etc) to ensure this is never an issue. Also, a database approach ensures that the data is much more resilient against crashes or code errors that could lead to file corruption.  With a database in place, you simply issue an insert of a record as each user submits their responses. Likewise, you can have the app query the database in regular intervals (30 seconds?) to update a datatable of current responses in the database.

Alternatively, you can configure the server to only allow one instance (the default for open source Shiny Server.) This bypasses the entire issue of multiple instances & multiple CSVs. The extensive Shiny Server config guide is at http://docs.rstudio.com/shiny-server/#default-configuration

Best regards,
Stephen McDaniel

Chief Data Scientist
PowerTrip Analytics
Automatic data, analytic & visualization solutions in the cloud.


--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discuss+unsubscribe@googlegroups.com.



--
Best,
Stephen McDaniel
Principal and Co-Founder, Freakalytics™ LLC
Rapid Analytics to Explore, Understand, Communicate & Act™
www.Freakalytics.com

Conal Monaghan

unread,
Jan 3, 2017, 6:29:43 PM1/3/17
to Shiny - Web Framework for R, conal.m...@gmail.com
Hi Laz and Stephen,
 
     Thank you for your suggestions regarding instances and database usage. I agree that a database approach would be more suitable, especially given Datatables.net is free and the DT package (https://rstudio.github.io/DT/shiny.html) allows for easy integration between the two. Buttons extension could be used instead of downloadButton, and DT has a plethora of options for very nice tables. Great suggestions.

     After looking at the options available, I think the best approach is to simply use data tables to display the results and Googlesheets package based on the rundown of external data storage options here:  https://shiny.rstudio.com/articles/persistent-data-storage.html to run the data storage. The interactions are straight forward (googlesheets package; https://github.com/jennybc/googlesheets) and it is easy to run and manage.  I have included a functioning app below to play around with and screenshots from both the app and google docs ends. As soon as the participant clicks the submit button, their data is added to google sheets instantly. First, one needs to setup the authorisation token and a worksheet in googlesheets to use. See below for the setup that works with the app below. 

    Note that before this will work you will need to run the following in your console (once installed googlesheets() ) 
                 1)   ttt <- gs_auth()                                                       # now follow the html. prompts to login
                 2) saveRDS(ttt, "ttt.rds")                                              # then copy ttt.rds file to the app's Dir() 
                 3) Now one needs to create a worksheet to use using the following code:

         Data <- gs_new("Data") %>% 
                    gs_ws_rename(from = "Sheet1", to = "Data")      
 
                 4) Insert the titles that we want
           Data <- Data %>% 
                       gs_edit_cells(ws = "Data", input = cbind("Score1", "Score2", "Time", "Mean"), trim = TRUE)                # Note! you will need to have manually add one row of data to your google sheet otherwise it will error

     The only two issues I am having which are unrelated to the basic functioning of the app, would anyone have any suggestions for these?
 
                                                             1) How to use the formatDate('Time', 'toDateString')" into the DT table (currently producing errors). This will be very useful and I do not have a good grasp on DT()
                                                             2) How to make the upload dependent upon the input$radio option. This input asks participants whether they want their data stored. I tried using observeEvent(input$radio==1, {  
                                                                observeEvent(input$Action, { ...})        })  but this does not work. I wonder how you can only make input$Action trigger when input#radio == 1?

 Kind Regards and good luck with all of your Shiny creations (see app below),
       Conal Monaghan

UI
----------------------------------------------------------------------------------------

library(shiny)
library("googlesheets")                                                                                                                                                  # Don't forget to install.packages("googlesheets")
library("DT")                                                                                                                                                                   # Don't forget to install.packages("DT")

shinyUI(fluidPage(                                                                                                                                                          # Open UI
  "survey demo",                                                                                                                                                             # Title 
  
  # Questions 
  selectInput(inputId = "Score1",                                                                                                                                     # What we are calling the object
              label = "The Question 1",                                                                                                                                 # Question
              choices = c("Disagree Strongly" = 1, "Disagree" = 2, "Disagree Somewhat " = 3,                                         # Responses
                          "Neither Agree nor Disagree" = 4, "Agree Somewhat" = 5, "Agree" = 6,
                          "Agree Strongly" = 7)            
             ), 
  
  selectInput(inputId = "Score2",                                                                                                                                  # What we are calling the object
              label = "The Question 1",                                                                                                                              # Question
              choices = c("Disagree Strongly" = 1, "Disagree" = 2, "Disagree Somewhat " = 3,                                        # Responses
                          "Neither Agree nor Disagree" = 4, "Agree Somewhat" = 5, "Agree" = 6,
                          "Agree Strongly" = 7)            
             ), 
  
  radioButtons("radio", label = h3("Do you consent to having your anonymous data stored for research"),                # insert Radio for data storage consent
               choices = list("Sure" = 1, "No, erase all evidence I was here" = 2
                              )),
  # Submitbutton
  actionButton(inputId = "Action", label = "Submit"), tags$hr(),                                                                                   # Create submit action button
  
  dataTableOutput('mytable')                                                                                                                                      # Data display with downloads using DT
          ))                                                                                                                                                                      # Close Shiny UI and fluid page



Server
---------------------------------------------------------------------------------------------------------------
library(shiny)
library("googlesheets")                                                                                                                                                  # Don't forget to install.packages("googlesheets")
library("DT")                                                                                                                                                                   # Don't forget to install.packages("DT")
suppressMessages(library(dplyr))                                                                                                                                 # Don't forget to install.packages("dplyr")

gs_auth(new_user = FALSE, gs_auth(token = "ttt.rds"))                                                                                              # login from token file "ttt.rds" in dir(). see post for how to do this

shinyServer(function(input, output) {                                                                                                                           #  Open Shiny Server
   
Results <- reactive(c(Score1, Score2, Sys.Date(), mean(Score1,Score2)))                                                              #  Create reactive data to input
  
                                    ##### Function 1, Add data when action button is pressed  ####
                                                    
observeEvent(input$Action, {                                                                                                                                        #  Observe event action from Actionbutton
  Data <- Data %>% 
    gs_add_row(ws = "Data", input = Results() )                                                                                                             #  When actionbutton is pressed this will add their data to the good .doc                    
                           })
                                                                              
                
                             #####     Function 2, Create a data display with download option     ####

output$mytable <- renderDataTable({gs_read(Data)}, filter = 'top', extensions = c('Scroller', 'Buttons'), options = list(         # use the Scroller and  Buttons extensions for scroll options and download options respectively
  dom = 'Bfrtip',
  buttons = 
    list('copy', 'print', list(
      extend = 'collection',
      buttons = c('csv', 'excel', 'pdf'),                                                                                                                                           #  Modify the downloads extension to make it nicer (places download options in menu under "Download")
      text = 'Download'
                               )  # close list
                               )  # close list
                                                                                           )                                                                                                   # close list
                                 ,  caption = 'Table X: This is a simple caption for the table.')                                                                 # Adds the caption. Note, I tried to add " %>% formatDate('Time', 'toDateString') " here but does not work
                                     }                                                                                                                                                         # Close shiny server function
            )                                                                                                                                                                                  # Close shiny server


Screen Shot 2017-01-04 at 9.58.27 am.png
Screen Shot 2017-01-04 at 9.58.46 am.png
Reply all
Reply to author
Forward
0 new messages