chain simple forms into longer runs, use the power of R to generate pretty feedback and complex designs
Most documentation is inside formr – you can just get going and it will be waiting for you where you need it.
If something just doesn't make sense or if you run into errors, please let us know.
To begin creating studies using formr, you need to sign-up with your email and obtain an administrator account. An administrator account is obtained by sending a request via email to accounts@formr.org. Studies in formr are created using spreadsheets. As a good starting point, you can clone the following Google spreadsheet and study it to get versed with the definitions of the item types formr supports.
1. Upload the items:
With your spreadsheet ready, login to formr admin and go to Surveys > Create new Surveys.
You can either upload your spreadsheet if it was stored locally on your computer using the form Upload an item table or
you could import a Google spreadsheet by enabling link sharing and using the form Import a Googlesheet. When importing a Googlesheet,
you will need to manually specify the name of your survey whereas if uploading a spreadsheet, the name of your survey is obtained from the filename
of the spreadsheet.
2. Manage your survey:
If your spreadsheet was well formed (as described here) and the items were successfully uploaded, your survey will be added
to the Surveys menu. To manage your created survey, go to Surveys > YourSurveyName.
In the survey admin area you can test your survey, change survey settings, view and download results, upload and delete survey items etc.
The survey menu to the left in the survey admin area contains hints that you can trigger by hovering over the link.
3.Create a Run/Study: A formr "run" contains your study's complete design. All the things that a single participant is going to do in your study should take place in one run. Designs can range from the simple (a single survey or a randomized experiment) to the complex (like a diary study with daily reminders by email and text message or a longitudinal study tracking social network changes). If you want to go beyond simply surveys, we recommended you read more about runs before you begin. To create a run go to Runs > Create a new Run. Enter a meaningful run name which should contain only of alphanumeric characters and the dash. The name will be shown in the URL of the study, so unlike survey names your participants will see it. If the name was valid and not taken, the run will be added to the Runs menu and you will be redirected to the run admin area. Here you can add Run Units, design the complexity of your study and test your study. To modify your study definition later, you can go to Runs > YourRunName. Your run is the entry point of your study. By default, it is accessible only to you and test users, that you can create. For participants to access your study, you need to set your run as public in the admin area and it will be accessible under the URL https://yourrunname.formr.org
If you wish to set up your own instance of formr please follow the guidelines in our installation guide.
There is always help if you need assistance.
A formr "run" contains your study's complete design. All the things that a single participant is going to do in your study should take place in one run. Designs can range from the simple (a single survey or a randomised experiment) to the complex (like a diary study with daily reminders by email and text message or a longitudinal study tracking social network changes).
Inside a run, participants' data is connected, so you can track how many times a participant filled out her diary or whether her social network grew in size since the first measurement time point.
So, why "run"? In formr, runs consist of simple modules that are chained together linearly. Because most modules are boombox-themed, it may help to think of a tape running. Using controls such as the skip backward button, the pause button and the stop button, you control the participant's progression along the run. Surveys can be thought of as the record button: whenever you place a survey in your run, the participant can input data.
Data for the control units are supplied on-the-fly to the statistics programming language R. Therefore, you can dynamically generate feedback graphics for your participants with minimal programming knowledge. With more programming knowledge, nothing keeps you from making full use of R. You could for example conduct complex sentiment analyses on participants' tweets and invite them to follow-up surveys only if they express anger.
Since runs contain your study's complete design, it makes sense that runs' administration side is where every user management-related action takes place. There is a user overview, where you can see at which position in the run each participant is and when they were last active. Here, you can send people custom reminders (if they are running late), shove them to a different position in the run (if they get lost somewhere due to an unforeseen complication) or see what the study looks like for them (if they report problems).
Runs are also where you customise your study's look, upload files (such as images, videos, Javascript files), control access and enrollment. In addition, there are logs of every email sent, every position a participant has visited and of automatic progressions.
Surveys are series of questions (or other items) that are created using simple spreadsheets/item tables (e.g. Excel).
Survey item tables are just spreadsheets and they can just as easily be shared, reused, recycled and collaboratively edited using e.g. Google Sheets.
Surveys can remain fairly simple: a bunch of items that belong together and that a participant can respond to in one sitting. For some people, simple surveys are all they need, but in formr a survey always has to be part of a (simple) run/study.
Surveys can feature various items, allowing e.g. numeric, textual input, agreement on a Likert scale, geolocation and so on.
Items can be optionally shown depending on the participant's responses in the same survey, in previous surveys and entirely different data sources (e.g. data gleaned from Facebook activity). Item labels and choice labels can also be customised using knitr, so you can e.g. refer to a participant's pet or last holiday location by name or use people's preferred term of address.
If you use R, formr includes a few nice timesavers in its R package on Github. Data import can be automated without any funny format business. Items will be correctly typed according to the item table, not according to flawed heuristics. You will also have item and value labels available in R.
If you name your items according to the schema BFI_extra_2R
, items with an R at the end can be automatically reversed and items ending on consecutive numbers with the same prefix will be aggregated to a mean score (with the name of the prefix). Using our R package codebook you can then document the data, as well as automatically generate internal consistency analyses and item frequency plots. Hence, some tedious manual data wrangling can be avoided, especially if you start giving your items meaningful and memorable names early on. The formr:: functions are also always available, whenever you use R inside formr runs and surveys.
These are external links - use them to send participants to other, specialised data collection modules, such as a social network generator, a reaction time task, another survey software (we won't be too sad), anything really. However, you can also simply call upon external functionality without sending the participant anywhere – one popular application of this is sending text messages via an R API call.
If you insert the placeholder {{login_code}}
, it will be replaced by the participant's run session code, allowing you to link data later (but only if your external module picks this variable up!).
Sometimes, you may find yourself wanting to do more complicated stuff like (a) sending along more data, the participant's age or sex for example, (b) calling an API to do some operations before the participant is sent off (e.g. making sure the other end is ready to receive, this is useful if you plan to integrate formr tightly with some other software) (c) redirecting the participant to a large number of custom links (e.g. you want to redirect participants to the profile of the person who last commented on their Facebook wall to assess closeness) (d) you want to optionally redirect participants back to the run (e.g. as a fallback or to do complicated stuff in formr).
You can either choose to "finish/wrap up" this component before the participant is redirected (the simple way) or enable your external module to call our API to close it only once the external component is finished (the proper way). If you do the latter, the participant will always be redirected to the external page until that page makes the call that the required input has been made.
Using an SMTP account (most email addresses come with one) that you can set up in the mail section, you can send emails to your participants, their friends or yourself. Using the tag {{login_link}}
, you can send participants a personalised link to the run. You can also use {{login_code}}
to use the session code to create custom links, e.g. for inviting peers to rate this person (informants). Many ISPs limit using their SMTP server to send automated email. Gmail users cannot send more than 500 emails a day and have to disable some advanced security features. Vendors like Sendgrid offer free student accounts as part of the Github education pack and are more amenable to automated emails via SMTP.
A simple one-shot survey with feedback. Let's say your run contains
email_address
).
big5$email_address
.
A participant fills out your survey. After completing it, they see the feedback page, which contains a bar chart of their individual big 5 scores. Before they see the page marked by the stop point, an email containing the same feedback is sent off to their email address - this way they get a take-home copy as well.
A simple one-shot survey after which you receive a notification.
'youremailaddress@example.org'
. Note the single quotes, they mean that this is a constant.
A participant fills out your survey. After completing it, they see the thank you note at pos. 30. Before they see the page marked by the stop point, an email is sent off to youremailaddress@example.org - this way you (or whoever's email address you use here) would get an email notification for every participant. This might be helpful in longitudinal surveys where experimenter intervention is required to e.g. set up a phone interview, in an assessment context when you want the scores to be automatically generated, or in a clinical study where you want to do a structured interview after a screening task.
See the Knitr & Markdown section to find out how to generate personalised emails, which contain feedback, including plots. In the next section, you'll learn how to use the email module for invitations in a diary study.
Skip backward allows you to jump back in the run, if a specific condition is fulfilled.
This way, you can create a loop. Loops, especially in combination with reminder emails are useful for diary, training, and experience sampling studies.
The condition is specified in R and all necessary survey data is automatically available. The simplest condition would be TRUE
– always skip back, no matter what. A slightly more complex one is nrow(diary) < 14
, this means that the diary must have been filled out at least fourteen times. Even more complex: nrow(diary) < 14 | !time_passed(days = 20, time = first(diary$created))
, this means that at least 20 days must have passed since the first diary was done and that at least 14 diaries must have been filled out. But any complexity is possible, as shown in Example 2.
A simple diary. Let's say your run contains
nrow(diary) < 14
and the instructions to jump back to position 20, the pause, if that is true.
Starting at 20, participants would receive their first invitation to the diary at 6PM the next day after enrolling. After completion, the Skip Backward would send them back to the pause, where you could thank them for completing today's diary and instruct them to close their web browser. Automatically, once it is 6PM the next day, they would receive another invitation, complete another diary etc. Once this cycle repeated 14 times, the condition would no longer be true and they would progress to position 60, where they might receive feedback on their mood fluctuation in the diary.
But you can also make a loop that doesn't involve user action, to periodically check for external events:
In this scenario, the participant takes part in the short survey first. We obtain the geolocation, which can be used to retrieve the local weather using API calls to weather information services in the Skip Backward at position 30. The weather gets checked once each day (pause at 20) and if there ever is a thunderstorm in the area, the participant is invited via email (40) to take a survey (50) detailing their experience of the thunderstorm. This way, the participants only get invited when necessary, we don't have to ask them to report weather events on a daily basis and risk driving them away.
This simple component allows you to delay the continuation of the run, be it
until a certain date (01.01.2014 for research on new year's hangovers),
time of day (asking participants to sum up their day in their diary after 7PM)
or to wait relative to a date that a participant specified (such as her graduation date or the last time he cut his nails).
See the Knitr & Markdown section to find out how to personalise the text shown while waiting.
Skip forward allows you to jump forward in the run, if a specific condition is fulfilled.
This way, you can create filters, and parallel paths or branches in a study. Filters are useful to screen participants. You may need parallel paths in a study to randomise people to one experimental branch out of many, or to make sure a certain part of your study is only completed by those for whom it is relevant.
Let's say your run contains
depression$suicidal != 1
. If the person is not suicidal, it skips forward to pos 40.
Starting at 10, participants would complete a survey on depression. If they indicated suicidal tendencies, they would receive the numbers for suicide hotlines at which point the run would end for them. If they did not indicate suicidal tendencies, they would be eligible to participate in the main survey.
Let's say your run contains
optimism$pessimist == 1
. If the person is a pessimist, it skips forward to pos 5.
TRUE
, so it always skips forward to pos 6.
Starting at 10, participants would complete a survey on optimism. If they indicated that they are pessimists, they fill out a different survey than if they are optimists. Both groups receive the same feedback at the end. It is important to note that we have to let the optimists jump over the survey tailored to pessimists at position 40, so that they do not have to take both surveys.
Waiting Time are like Pauses, but instead of making the participant wait, we wait for the participant.
By waiting for the participant for a certain amount of time, we can make sure that people are reminded to participate in our diary study after one hour—but only if they need a reminder. We can also make sure that a part of a study is only accessible at certain times of day.
Let's say your run contains
The pause would simply lead to all exchange students being invited once they've been in their host country for a week (we left out the part where we obtained or entered the necessary information). After the invitation, however, we don't just give up, if they don't react. After another week has passed (one week in the host country), we remind them.
How is this done? We set a waiting time for the participant of 7 days.
Now if he doesn't answer for one week, the run will automatically go on to 40, to our email reminder (tentatively titled "Oh lover boy..."). We hope the participant clicks on the link in our invitation email before then though.
If he does, he will jump to the survey at position 60.
If he still doesn't answer, we will patiently wait for another seven weeks. This time, we set an expiry time in the survey settings to achieve this. Until seven weeks have passed he can do the survey. Once the seven weeks are over without him finishing the survey, the run moves on to the next position, which stands for waiting for return home, i.e. we gave up on getting a reaction in the first wave (but we still have "Baby, oh baby, My sweet baby, you're the one" up our sleeve).
You will always need at least one. These are stop points in your run, where you can give short or complex feedback, ranging from "You're not eligible to participate." to "This is the scatter plot of your mood and your alcohol consumption across the last two weeks".
If you combine these end points with Skip Forward, you can have several in your run: You would use the Skip Forward to check whether participants are eligible, and if so, skip over the stop point between the Skip Forward and the survey that they are eligible for. This way, ineligible participants end up in a dead end before the survey. In the edit run interface, you can see green counts of the number of people on this position on the left, so you can see easily how many people are ineligible by checking the count.
See the Knitr & Markdown section to find out how to generate personalised feedback, including plots.
This is a very simple component. You simply choose how many groups you want to randomly assign your participants to. We start counting at one (1), so if you have two groups you will check shuffle$group == 1
and shuffle$group == 2
. You can read a person's group using shuffle$group
. If you generate random groups at more than one point in a run, you might have to use the last one tail(shuffle$group,1)
or check the unit id shuffle$unit_id
, but usually you needn't do this.
If you combine a Shuffle with Skip Forward, you could send one group to an entirely different arm/path of the study. But maybe you just want to randomly switch on a specific item in a survey - then you would use a "showif" in the survey item table containing e.g. shuffle$group == 2
. The randomisation always has to occur before you try to use the number, but the participants won't notice it unless you tell them somehow (for example by switching on a note telling them which group they've been assigned to).
Survey spreadsheets contain the questions on a first sheet called "survey". They can optionally have a second sheet called "choices" where you define choices for multiple choice items in a long format. They can also optionally have a third sheet called "settings" where you define settings such as pagination and validation (most set these settings after uploading the survey though).
You can clone a Google spreadsheet to get started or start with an empty spread sheet.
Some helpful tips:
__bold__
, make it italic using *italic*
.
type | name | label | optional | showif |
---|---|---|---|---|
text | name | Please enter your name | * | |
number 1,130,1 | age | How old are you? | ||
mc agreement | emotional_stability1R | I worry a lot. | age >= 18 | |
mc agreement | emotional_stability2R | I easily get nervous and unsure of myself. | age >= 18 | |
mc agreement | emotional_stability3 | I am relaxed and not easily stressed. | age >= 18 |
You can use more columns than the ones shown above. Unknown column types are simply ignored, so you can use them for other information.
The following column types exist:
sex == 1
, it will only be shown if that condition is true. Conditions are written in R and can be arbitrarily complex. You should always test them well. It is also possible to refer to data in other surveys using e.g. other_survey$item_name != 2
. If you refer to data on the same page, items will also be shown dynamically using Javascript.
*
in this column, you can turn items optional instead. Using !
requires a response to items that are optional by default (check, check_button).
a_different_survey$item1 + a_different_survey$item2
) to pre-set a value, but you can also simply use 1
to choose the option with the value 1 (remember that choices for mc-family items are saved as numbers, not as the choice labels per default). There is one special word, sticky
, which always pre-sets the items value to the most recently chosen value. You also have to keep in mind when pre-setting strings, that they have to be marked up in R, like this "I am text"
(preferably do not use single quotes because Excel will mess them up).
You might want to tinker with the look of certain form items. To do so you can use a variety of pre-set CSS classes. This is a fancy way of saying that if you make a new column in your survey sheet, call it "class" and add space-separated magic words, stuff will look different.
These are the available styling classes:
Choices sheets are fairly simple. The list_name
column defines the choice set. By using that name in the type column of a multiple-choice item, you assign that choice set. As you'd expect, the value
column defines the value recorded in the database, the label
defines what participants see by default. It is not currently possible to randomly order choice sets. You can use Javascript to achieve this.
You can clone a Google spreadsheet to get started.
list_name | name | label |
---|---|---|
agreement | 1 | disagree completely |
agreement | 2 | rather disagree |
agreement | 3 | neither agree nor disagree |
agreement | 4 | rather agree |
agreement | 5 | agree completely |
showif
or for dynamically generating item text have been given. text 100
defines the maximum number of characters that may be entered.
step
defaults to 1
, using any
will allow any decimals.
A-Za-züäöß.;,!:
), no numbers.
1,100,1
.
2013-01-01,2014-01-01
or -2years,now
.
12:00,17:00
The, by far, biggest family of items. Please note, that there is some variability in how the answers are stored. You need to know about this, if you (a) intend to analyse the data in a certain way, for example you will want to store numbers for Likert scale choices, but text for timezones and cities (b) if you plan to use conditions in the run or in showif or somewhere else where R is executed. (b) is especially important, because you might not notice if demographics$sex == 'male'
never turns true because sex is stored as 0/1 and you're testing as female.
mc
but instead of the text appearing next to a small button, a big button contains each choice label
min,max,step
in between. Defaults to 1,5,1.
mc_button
with the ♂, ♀ symbols as choices
var
from the query string, so in the example above get param1
would lead to 10 being stored.
(item1 + item2) > 100
to add further requirements.
You can format text/feedback everywhere (i.e. item labels, choice labels, the feedback shown in pauses, stops, in emails) in a natural fashion using Github-flavoured Markdown.
The philosophy is that you write like you would in a plain-text email and Markdown turns it nice.
In most cases, characters with special meaning won't entail unintended side effects if you use them normally, but if you ever need to specify that they shouldn't have side effects, escape it with a backslash: \*10\*
doesn't turn italic.
* list item 1 * list item 2
will turn into a nice bulleted list.
#
at the beginning of a line turns it into a large headline, ##
up to ######
turn it into smaller ones.
*italics* and __bold__
are also easy to do.
[Named links](http://yihui.name/knitr/)
and embedded images ![image description](http://imgur.com/imagelink)
are easy. If you simply paste a link, it will be clickable automatically too, even easier. Email addresses are a bit special, you need the "mailto:" prefix: [Contact us](mailto:contact_email@example.com).
You can quote something by placing a > at the beginning of the line.
If you're already familiar with HTML you can also use that instead, though it is a little less readable for humans. Or mix it with Markdown! You may for example use it to go beyond Markdown's features and e.g. add icons to your text using <i class="fa fa-smile-o"></i>
to get for instance. Check the full set of available icons at Font Awesome.
If you want to customise the text or generate custom feedback, including plots, you can use Knitr. Thanks to Knitr you can freely mix Markdown and chunks of R. You can load data using R commands, but the data you just collected for this participant will automatically be made available as R data frames. See R helpers for more information. Some examples:
Today is `r date()`
shows today's date.Hello `r demographics$name`
greets someone using the variable "name" from the survey "demographics".Dear `r ifelse(demographics$sex == 1, 'Sir', 'Madam')`
greets someone differently based on the variable "sex" from the survey "demographics".```{r}
library(formr)
# build scales automatically
big5 = formr_aggregate(results = big5)
# standardise
big5$extraversion = scale(big5$extraversion, center = 3.2, scale = 2.1)
# plot
qplot_on_normal(big$extraversion, xlab = "Extraversion")
```
yieldsIn formr, you can use R to write simple and complex code. Various places allow you to specify either R code (e.g., showif column, value column, SkipForward/SkipBackward, External, Pause button conditions) or R code interspersed with Markdown (as in knitr, e.g., labels, Stop button, Pause button texts). The R code you wrote will be automatically enriched with the data objects you name and processed using OpenCPU. By default, your participants cannot view the R code you write.
When you write R code in formr, we try to automatically determine what data you need and supply it. To do so, formr has a look up table of all the available data (the surveys defined in the run, the items defined in these surveys, as well as some metadata about the participant and run progress).
For example, to obtain somebody's age, you need only write demographics$age
. formr will then automatically create a data frame named "demographics" containing the variable "age". To give another example, to see whether a participant ever reported a headache in your diary, you might just write any(diary$headache > 1)
. In this case, formr would create a data frame containing all responses to the headache question. It's important to note that formr simply checks whether the name of the survey exists anywhere in the text and whether the name of the item exists anywhere else. So, demographics$age
works, but so does demographics[, 'age']
. If an item name exists in multiple surveys that you have named, it will be supplied for all surveys.
Wherever you use R in formr you can also use the functions in its R package. If you want to use the package in a different environment, you'll need to install it using the following code.
install.packages('formr', repos = c('https://rforms.r-universe.dev', 'https://cloud.r-project.org'))
The package currently has the following feature sets
first(cars) # first non-missing value
last(cars) # last non-missing value
current(cars) # last value, even if missing
"formr." %contains% "mr." # will yield TRUE
"formr." %contains_word% "mr" # will yield FALSE
"12, 15" %contains% "1" # will yield TRUE
"12, 15" %contains_word% "1" # will yield FALSE
time_passed(hours = 7)
next_day()
in_time_window(time1, time2)
qplot_on_normal(0.8, "Extraversion")
The package also has a function to simulate possible data, so you can try to make feedback plots ahead of collecting data.Sometimes, you need more than the data that formr auto-enriches your study with. For example, you might have designed a couples' diary study and need the partner's data to synchronize participation. In these cases, you will have to explicitly load the data using formr's API.
Other times, you might want to import data from elsewhere on the web. You can R packages and functions to, for example, read a participant's social media posts or to look up information in an external, online database.
The following designs and many more are possible:
The formr API is the primary way to get data/results out of the platform. It's a low-level HTTP-based API that you can use principally to get results of a study for specified participants (sessions)
Resource requests to the formr API require that you have a valid access token which you can obtain by providing API credentials (a client id and a client secret)
API base URL:
https://api.formr.org
Access to the API is restricted, so only the administrators of formr are able to provide API credentials to formr users. To obtain API credentials, send an email to Cyril and your credentials will be sent to you.
An access token is an opaque string that identifies a formr user and can be used to make API calls without further authentication. formr API access tokens are short-lived and have a life span of about an hour.
To generate an access token you need to make an HTTP POST request to the token endpoint of the API
POST /oauth/access_token?
client_id={client-id}
&client_secret={client-secret}
&grant_type=client_credentials
This call will return a JSON object containing an access token which can be used to access the API without further authentication required.
Sample successful response
{
"access_token":"XXXXXX3635f0dc13d563504b4d",
"expires_in":3600,
"token_type":"Bearer",
"scope":null
}
The attribute expires_in
indicates the number of seconds for which is token is valid from its time of creation. If there is an error for example if the
client details are not correct, then an error object is returned containing an error_description and sometimes an error_uri where you can read more about the
generated error. An example of an error object is
{
"error":"invalid_client",
"error_description":"The client credentials are invalid"
}
GET /get/results?
access_token={access-token}
&run[name]={name of the run as it appears on formr}
&run[sessions]={comma separated list of session codes OR leave empty to get all sessions}
&surveys[survey_name1]={comma separated list items to get from survey_name1 OR leave empty to get all items}
&surveys[survey_name2]={comma separated list items to get from survey_name2 OR leave empty to get all items}
Notes:
The response to a results request is a JSON object. The keys of this JSON structure are the names of the survey that were indicated in the requested and the value associated to each survey entry is an array of objects representing the results collected for that survey for all the requested sessions. An example of a response object could be the following:
{
"survey_1": [{
"session": "sdaswew434df",
"survey_1_item_1": "answer1",
"survey_1_item_2": "answer2",
"survey_1_item_2": "answer3",
},
{
"session": "fdgdfg4323",
"survey_1_item_1": "answer4",
"survey_1_item_2": "answer5",
"survey_1_item_2": "answer6",
}],
"survey_2": [........]
}
# load the httr package
library(httr)
# Login using your client ID and client Secret to get an access token
login <- list( # define login credentials
client_id = "bb472xxxxxxxxe1918",
client_secret = "zEfgeyJ0eXXXXXEwYwNGZj",
grant_type = "client_credentials"
)
request <- POST( # send POST request
"https://api.formr.org/oauth/access_token",
body = login,
encode = "form"
)
# parse response to get access token
# If there an error then the response object would contain the details of the error in response$error
response <- content(request)
access_token <- response$access_token
#With a valid access token, call API to get the results of a particular study (run)
query <- list(
access_token = access_token,
"run[name]" = "/enter run name here/",
"run[session]" = "/here you can specify a full session code or just a substring(but include the beginning /",
"surveys[survey_1]" = "item_1, item_2, item_etc.", # comma separated list of items to get from the survey or leave empty string to get all items
"surveys[survey_2]" = "item_3, item_4, item_etc."
)
request <- GET("https://api.formr.org/get/results", query=query)
results <- content(request)
# With a valid response you can, for example, extract the results of a particular survey say 'survey_1':
survey_1 = results$survey_1
survey_1[, c("item_1","item_2")]
If you're a participant in one of the studies implemented in formr, please reach out to the person running the study.
If you're running a study yourself, there's several places to look for help.