Create a questionnaire
create_quetzio.Rmd
Package shiny.quetzio provides a relatively simple but also highly customizable framework for creation and deploying questionnaire as a part of your shinyApp.
Every questionnaire created in this framework takes a form of object of class Quetzio, and needs to be created inside the server of your application.
This vignette is going to take you on all basic things you should know about using this package to include a questionnaire within your application - how to build your configuration file, what are the core possibilities of Quetzio both as an semi-independent object and as a part of more complex logic in your application.
What is the Quetzio?
It is main element of the whole functionality of shiny.quetzio. Its the R6 class definition used to create objects creating Shiny modules upon initialization. It takes a source of the questions (and optionally: additional instruction and descriptions for individual inputs) and generates the whole questionnaire UI. Within the created module the entire backend for the reactivity is contained (consisting of answers validation and their output).
Generally, the basic dataflow within this process is shown in figure below.
What is the structure of question source?
As seen on the figure above, there are three possible sources for the questions:
- googlesheets (needs googlesheets4 package installed)
- yaml (needs yaml package installed)
- R object (list or data.frame)
Only one source method can be used. It needs to be defined during initialization by specifying source_method
argument in the call of Quetzio_create()
In the source, for every shinyInput you can specify following parameters:
- inputId
- type
- label
- mandatory: (true/false) if the input must be filled
- width: the same as in regular input specification. If not provided, defaults to 500px
Bold ones are mandatory!
Additionally, various supported shinyInput types support additional parameters. Table of the supported shinyInputs with all possible parameters is provided below:
parameter | textInput | numericInput | selectizeInput | radioButtons | likertRadioButtons |
---|---|---|---|---|---|
placeholder | x | x | x | x | |
regex | x | ||||
value | x | ||||
min | x | ||||
max | x | ||||
step | x | ||||
choices | x | x | |||
choiceValues | x | x | x | ||
choiceNames | x | x | x | ||
maxItems | x | ||||
create | x | ||||
maxOptions | x | ||||
selected | x | x | x | ||
inline | x |
Parameters with bolded x are mandatory. You can specify either choices or both choiceValues and choiceNames for
selectizeInput
andradioButtons
.
YAML source method (source_method == 'yaml'
)
The main intended way of sourcing data for the module is by creating a YAML source file in a very simple form - template shown below. It also requires installation of the yaml R package
inputId_1:
type: shinyInput_type
label: shinyInput_label
parameter_1: value
parameter_2: value
parameter_n: value
inputId_2:
type: shinyInput_type
label: shinyInput_label
parameter: value
multi-value_parameter:
- value_1
- value_2
- value_n
inputId_n: ...
inputId don’t need to be specified as a parameter, but need to be provided as the name of the sequence
googlesheets source method (source_method == 'gsheet'
)
Additional way of sourcing data, especially useful if you intend to frequently update the questions or there will be multiple people collaborating during the run of the Shiny application.
To use this method the googlesheets4 R package needs to be installed.
You can create example googlesheet using helper function provided with the package. It makes sure that all needed columns are present and they are containing correct classes.
# assignement to hold the ID of created googlesheet
id <- create_Quetzio_source(
method = "gsheet")
# see the data structure:
str(
googlesheets4::read_sheet(ss = id, sheet = "Questions")
)
#> tibble [1 × 18] (S3: tbl_df/tbl/data.frame)
#> $ inputId : chr "placeholder"
#> $ type : chr "textInput"
#> $ mandatory : logi TRUE
#> $ label : chr "Remove before production"
#> $ width : chr "500px"
#> $ placeholder : chr "some text"
#> $ chr_regex : chr "^pattern$"
#> $ num_value : num 1
#> $ num_min : num 0
#> $ num_max : num 2
#> $ num_step : num 0.5
#> $ mult_choices : chr "Something\nElse\nMore"
#> $ mult_choiceValues: chr "1\n2\n3"
#> $ mult_choiceNames : chr "One\nTwo\nThree"
#> $ mult_selected : logi NA
#> $ select_maxItems : num 1
#> $ select_create : logi TRUE
#> $ radio_inline : logi TRUE
# I remove the googlesheet created for this presentation
googledrive::drive_trash(id)
As you can see, it contains one placeholder row with exemplary values and correct types. You can populate the googlesheet using Google Drive GUI - remember to remove the placeholder row!
Values of multi-values parameters need to be separated within one cell with either a semicolon or newline
providing R object as a source (source_method == 'raw'
)
If, for any reason you can’t or won’t install yaml or googlesheets4 packages or just prefer assigning the created R object as a source, you can provide either a list or data.frame during initialization of new Quetzio object.
They are analogous to the structure of yaml (list) or googlesheet (data.frame) source file. You can check the correct structure for the same two-item example questionnaire configured in both list and data.frame below.
# every nested list need to be named with the inputId
list_source <- list(
textItem = list(
type = "textInput",
label = "First input",
placeholder = "Write inside of me, please!"),
selectizeItem = list(
type = "selectizeInput",
label = "Choose up to two",
choices = c("I am first", "I am second", "I am third", "Who am I?"),
maxItems = 2,
mandatory = TRUE
)
)
# data.frame can be configured explicitly by 'data.frame'
df_source <- data.frame(
inputId = c("textItem", "selectizeItem"),
type = c("textInput", "selectizeInput"),
mandatory = c(NA, TRUE),
label = c("First input", "Choose up to two"),
placeholder = c("Write inside of me, please!", NA),
mult_choices = c(NA, "I am first;I am second;I am third;Who am I?"),
select_maxItems = c(NA, 2)
)
# You can also use the same function as for googlesheet generation to generate
# example data.frame with placeholder row, though you need to modify it
# afterwards
df_source2 <- create_Quetzio_source(method = "df")
str(df_source2)
#> 'data.frame': 1 obs. of 18 variables:
#> $ inputId : chr "placeholder"
#> $ type : chr "textInput"
#> $ mandatory : logi TRUE
#> $ label : chr "Remove before production"
#> $ width : chr "500px"
#> $ placeholder : chr "some text"
#> $ chr_regex : chr "^pattern$"
#> $ num_value : num 1
#> $ num_min : num 0
#> $ num_max : num 2
#> $ num_step : num 0.5
#> $ mult_choices : chr "Something\nElse\nMore"
#> $ mult_choiceValues: chr "1\n2\n3"
#> $ mult_choiceNames : chr "One\nTwo\nThree"
#> $ mult_selected : chr NA
#> $ select_maxItems : num 1
#> $ select_create : logi TRUE
#> $ radio_inline : logi TRUE
The list is recommended for source, though with long questionnaires and similiar shinyInput types the data.frame source can be easier to generate - it all depends on your workflow and the structure of the questionnaire
Embedding the questionnaire in your shinyApp
If you have your source created correctly, it is the time to embed the questionnaire in your Shiny application. You need to make some calls both in the server and in the ui parts of your application.
Generation of the Quetzio object
You need to create the Quetzio object inside of your server code. It is done using Quetzio_create function and it is recommended, though you can also create new object explicitly: Quetzio_create() There are following mandatory arguments for the Quetzio_create
- source_method: to choose the source method (‘yaml’, ‘gsheet’, ‘raw’)
- for
source_method == 'yaml'
: source_yaml - for
source_method == 'gsheet'
: source_gsheet_id and source_gsheet_sheetname - for
source_method == 'raw'
: source_object
- for
- module_id: character string to reference created module
So to create the questionnaire based on the source created above:
quetzio_from_list <- Quetzio_create(
source_method = "raw",
source_object = list_source,
module_id = "my_questionnaire"
)
Adding the questionnaire to you UI
The questionnaire isn’t seen anywhere in the UI yet! You can show it by adding in your UI the following call:
Quetzio_UI(module_id = "my_questionnaire")
Yes - this is all!
Remember that all shiny modules IDs needs to be unique withing your application environement!
Using the built-in googlesheets output method
If you wish to send the results of the survey automatically to some googlesheet file, you can specify additional arguments in the Quetzio_create().
# the same questionnaire as above
Quetzio_create(
source_method = "raw",
source_object = list_source,
module_id = "my_questionnaire",
# but with specific arguments for the googlesheet output
output_gsheet = TRUE,
output_gsheet_id = "your_googlesheet_id",
output_gsheet_sheetname = "sheet_name"
)
As you can see above, the assignement of the object isn’t necessary. Lack of assignement don’t allow for reading the state of the questionnaire or using any other methods on it. In the example above, the answers will be saved in the external googlesheet, so the answer reading won’t be necessary.
Reading the state of the created Quetzio object within the application.
You can get some information from your questionnaire by calling the following calls on your object:
# if you assigned your Quetzio object as `quetzio_from_list`
quetzio_from_list <- Quetzio_create(
source_method = "raw",
source_object = list_source,
module_id = "my_questionnaire"
)
# All of the above are 'reactiveVal' object, so they need to be
# accessed in the observe() or reactive() context
observe({
# is the questionnaire done: TRUE or FALSE
quetzio_from_list$is_done()
# check if there are any warning messages: if none, then NULL
quetzio_from_list$message()
# get the list of answers. If questionnaire isn't done, then NULL
quetzio_from_list$answers()
})
Adding instructions and/or item descriptions (optional)
One of the optional features of the Quetzio is the ability to generate multi-paragraph instructions and item descriptions for you questionnaire.
For elements of instruction and/or item description you need to provide correct config, describing following element types:
- for instruction
- instruction_title: generates h1 tag of class quetzio_title
- instruction_para: generates p tag of class quetzio_paragraph
- instruction_list: generates ul or ol tag of class quetzio_list
- all elements are pasted together in the given order within div of css class quetzio_instruction. It will be located just before the first question.
- for descriptions
- item_desc: generates item description, which will appear between item label and input
- it will be contained within div of css class quetzio_description
Following parameters can be provided to these element (with some mandatory!):
-
content: the whole content of the given element
- mandatory
- for every element type
-
align: how the content should be aligned withing its container
- optional: if not specified, the default is ‘left’
- for every element type
-
html: are there any HTML tags specified in the text
- optional: if not specified, the default is FALSE
- for every element type but instruction_list
-
order: should the list be ordered
- optional: if not specified, the default is FALSE
- for instruction_list only
-
inputId: for which inputId it should be generated
- mandatory, but only for item_desc
YAML source
Needs the yaml R package to be installed, and should be passed to desc_yaml argument during initialization of Quetzio object.
Its form is similiar to the one of the YAML question source, but the sequences don’t need to be named:
googlesheet source
Needs the googlesheet4 R package to be installed. The googlesheet id need to be passed to desc_gsheet_id, and sheetname to desc_gsheet_sheetname arguments during initialization of Quetzio object.
Its form is very similar to the one of the googlesheet question source. It can also be created with helper function.
# assignement to hold the ID of created googlesheet
id <- create_desc_source(
method = "gsheet")
# see the data structure:
str(
googlesheets4::read_sheet(ss = id, sheet = "Descriptions")
)
#> tibble [1 × 6] (S3: tbl_df/tbl/data.frame)
#> $ type : chr "placeholder"
#> $ align : chr "left"
#> $ html : logi TRUE
#> $ order : logi TRUE
#> $ inputId: chr "placeholder"
#> $ content: chr "<b>Delete</b> this <i>example</i>"
# I remove the googlesheet created for this presentation
googledrive::drive_trash(id)
R object as a source
Analogous to R object as the source of questions, you can provide them in the form of list or dataframe. You should then pass that object to desc_object argument during Quetzio initialization:
# nested list don't need to be named!
list_source <- list(
list(
type = "instruction_title",
content = "I am the <b>title</b> of this <i>questionnaire</i>",
html = TRUE,
align = "center"),
list(
type = "instruction_list",
content = c("I am first", "I am second", "I am third", "Who am I?"),
order = TRUE
)
)
# data.frame can be configured explicitly by 'data.frame'
df_source <- data.frame(
type = c("instruction_title", "instruction_list"),
content = c("I am the <b>title</b> of this <i>questionnaire</i>",
"I am first;I am second;I am third;Who am I?"),
html = c(TRUE, NA),
align = c("center", NA),
order = c(NA, TRUE)
)
# You can also use the same function as for googlesheet generation to generate
# example data.frame with placeholder row, though you need to modify it
# afterwards
df_source2 <- create_desc_source(method = "df")
str(df_source2)
#> 'data.frame': 1 obs. of 6 variables:
#> $ type : chr "placeholder"
#> $ align : chr "left"
#> $ html : logi TRUE
#> $ order : logi TRUE
#> $ inputId: chr "placeholder"
#> $ content: chr "<b>Delete</b> this <i>example</i>"
Additional features
There are even more features that are out of the scope of this vignette. For more information check other vignettes and help files.
- Functional features
- use Quetzio_get_df() function to get answers in form of the data.frame
- update item labels based on some reactive with Quetzio_label_update() function
- update item selected values based on named list with Quetzio_value_update() function
- link multiple Quetzio objects using QuetzioLink object
- Customizable features
- add your own labels and texts by utilizing custom_txts argument (check
?quetzio_txt
for more info) - add custom css to alternate the look of your Quetzio by adding the styles for specific classes using custom_css argument
- use specific div id to customize css styles more easily
- add your own labels and texts by utilizing custom_txts argument (check
Alternatives to shiny.quetzio
I feel like it needs to be stated that shiny.quetzio isn’t your only option for creating questionnaires with Shiny. The main alternative is shinysurveys package.
Main difference between these are the embedding - while shiny.quetzio can be used to create questionnaire (or multiple of them) within more complex Shiny application, the shinysurverys are tailored to create whole ShinyApp by itself. It also currently (as of 0.2.0.) doesn’t offer built-in support of googlesheets4, either for source nor data collection.