Qualtrics is a enterprisy survey tool that is very popular in psychology research field.
Qualtrics comes with import/export tool. This tool exposes a json data format for the survey, this format can then be edited to generate the survey code.
Here is what we will cover:
- Adding questions;
- Adding and editing blocks;
- Editing flows;
Here is what we will not cover:
- Creating survey from scratch --- its easier to do as much of the work as possible using the qualtrics web interface;
- Creating questions --- to create a new question just create one in the UI, download survey and find out which properties you'll need to edit;
- Creating flows --- I didn't need this;
Warning
Qualtrics discourages you from editing this format:
Do not edit this file! Editing the file can corrupt the contents and make it unable to upload to your account. You may only rename the file, if desired, but do not change the contents or file type.
However I found it, well editable ;)
Warning
This worked in early 2019, but I don't guarantee it will work in the future, Qualtrics may change it's format without any warning.
Keep backup copies of edited surveys.
Any changes you are doing you are doing on your own risk.
Overall survey structure
*.qsf files exported from Qualtrics are just json files, so to parse them you'll need only a json parser.
Each file contains one object with two properties: SurveyEntry and SurveyElements.
Survey entry contains metadata which I treated mostly as opaque object, only interesting property I found is SurveyEntry.SurveyName (which contains the name of the survey).
Everything else is in SurveyElements, which suprisingly is an array.
This array contains questions, and special objects that define blocks, flows, and other metadata.
Interesting SurveyElements
Question Count
Is in SurveyElements array and has Element property equal to QC.
It's SecondaryAttribute contains question count in the survey.
You need to update it when you add questions to survey, if you won't update it survey will explode once someone adds new question using the interface.
Survey Blocks
Survey block is a logical grouping of questions, usually displayed on a single page.
Survey blocks are inside SurveyElements array, and have Element property equal to BL.
Blocks are inside Payload element. Payload element is (weirdly) either an array or object. In both cases indices of the are positive numbers.
So it looks like that:
{
"SurveyID": "SV_...",
"Element": "BL",
"PrimaryAttribute": "Survey Blocks",
"SecondaryAttribute": null,
"TertiaryAttribute": null,
"Payload": {
"0": {
"BlockElements": [
{
"QuestionID": "QID1",
"Type": "Question"
},
{
"Type": "Question",
"QuestionID": "QID2"
}
],
"Description": "Block Name",
"ID": "BL_...",
"Options": {
"BlockLocking": "false",
"BlockVisibility": "Expanded",
"RandomizeQuestions": "false"
},
"Type": "Default"
},
"1": ...
}
}
or that:
{
"SurveyID": "SV_1IgvwG9mQykPEvX",
"Element": "BL",
"PrimaryAttribute": "Survey Blocks",
"SecondaryAttribute": null,
"TertiaryAttribute": null,
"Payload": [
{
"BlockElements": [
{
"QuestionID": "QID1",
"Type": "Question"
},
{
"Type": "Question",
"QuestionID": "QID2"
}
],
"Description": "Block Name",
"ID": "BL_...",
"Options": {
"BlockLocking": "false",
"BlockVisibility": "Expanded",
"RandomizeQuestions": "false"
},
"Type": "Default"
},
...
]
}
Question block
Survey block is a logical grouping of questions, usually displayed on a single page.
All blocks are inside Survey Blocks element described above.
Block contains three properties that need to be edited:
- Description
- Textual description of the block (visible in the editing UI, but invisible to person filling the survey.
- ID
Block id's seem to be generated randomly, but need to start with BL_.
I generated them using:
"BL_{}".format(base64.b32encode(os.urandom(5)).decode("ascii"))
- BlockElements
- Array of objects that contain Type equal to Question and QuestionID equal to referenced question ID.
Flow
Flows allow for more advanced display logic than blocks.
Flows are are inside SurveyElements array, and have Element property equal to FL. Flows are stored in a tree-like structure inside Payload property.
{
"SurveyID": "SV_...",
"Element": "FL",
"PrimaryAttribute": "Survey Flow",
"SecondaryAttribute": null,
"TertiaryAttribute": null,
"Payload": {
"Type": "Root",
"FlowID": "FL_1",
"Flow": [
{
"Type": "Block",
"ID": "BL_...",
"FlowID": "FL_2"
},
{
"Type": "BlockRandomizer",
"FlowID": "FL_3",
"SubSet": 2,
"Flow": [
{
"Type": "Block",
"ID": "BL_...",
"FlowID": "FL_4"
},
{
"Type": "Standard",
"ID": "BL_...",
"FlowID": "FL_5"
}
]
}
],
"Properties": {
"Count": 5,
"RemovedFieldsets": []
}
}
}
Things of note:
- Flows use consecutive IDS
- There is flow count, which you should update.
Questions
Each question is an element in inside SurveyElements array, and have Element property equal to SQ.
Things to note:
- Question ID's are consecutive, and you need to update Question Count. Question ID format is QID1, QID2, ... .
- Question ID is both in PrimaryAttribute and Payload.QuestionID
- Description (I quess) visible while editing the survey is in: SecondaryAttribute and Payload.QuestionText
- Answers to this question will be visible under Payload.DataExportTag property.
- Question text (visible to people filling the survey) is the QuestionText property.
Common operations
Adding Questions
To add question to survey you'll need to:
- Prepare the JSON object you want to insert
- Calculate question ID by:
- Finding Question Count
- Incrementing it
- Create question id in format QID<updated question count>.
- Update QuestionID, DataExportTag, QuestionText and whatever you need ;)
- Add resulting object to SurveyElements.
Adding blocks
To add block you'll need:
- Generate new block ID (unique string starting with BL_);
- Set Description, BlockElements and whatever you need;
- Add questions to the block;
- Insert the block to the Payload of the Survey Blocks element;
Adding block(s) to flows
Find the parent flow you want your appear.
To do this you'll need to recursively search the Survey Flow element.
Generate new flow ID, which is done similarly to question id:
- Get Properties.Count property of the Survey Flow elements;
- Increment it;
- Create new flow ID FL_<updated flow count>;
Add new flow to the flow element you found in the step 1, this added flow should look like:
{ "ID": "BL_...", "Type": "Block", "FlowID": "FL_..." }
Where ID references block ID you want to add, and FlowID is flow ID is
Final notes
If this sound like black magic (or just something you'd rather avoid doing), do get in touch I might help you with whatever you need :). My address is jacek@askesis.pl.