How to generate qualtrics questions

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:

  1. Adding questions;
  2. Adding and editing blocks;
  3. Editing flows;

Here is what we will not cover:

  1. Creating survey from scratch --- its easier to do as much of the work as possible using the qualtrics web interface;
  2. 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;
  3. 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:

  1. Question ID's are consecutive, and you need to update Question Count. Question ID format is QID1, QID2, ... .
  2. Question ID is both in PrimaryAttribute and Payload.QuestionID
  3. Description (I quess) visible while editing the survey is in: SecondaryAttribute and Payload.QuestionText
  4. Answers to this question will be visible under Payload.DataExportTag property.
  5. 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:

  1. Prepare the JSON object you want to insert
  2. Calculate question ID by:
    1. Finding Question Count
    2. Incrementing it
    3. Create question id in format QID<updated question count>.
  3. Update QuestionID, DataExportTag, QuestionText and whatever you need ;)
  4. Add resulting object to SurveyElements.

Adding blocks

To add block you'll need:

  1. Generate new block ID (unique string starting with BL_);
  2. Set Description, BlockElements and whatever you need;
  3. Add questions to the block;
  4. Insert the block to the Payload of the Survey Blocks element;

Adding block(s) to flows

  1. Find the parent flow you want your appear.

    To do this you'll need to recursively search the Survey Flow element.

  2. Generate new flow ID, which is done similarly to question id:

    1. Get Properties.Count property of the Survey Flow elements;
    2. Increment it;
    3. Create new flow ID FL_<updated flow count>;
  3. 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.