Skip to content

[Developers] Beiwe skip logic spec

zagorsky edited this page Apr 4, 2017 · 1 revision

Beiwe skip logic spec

Documentation of the current front-end code

New features to support skip logic

This needs a few new features in the survey editor interface:

  • Each question can have attached to it a conditional statement about whether to display the question

  • Every question in the survey should have a visible number (starting from Q1, as in Q1, Q2, etc.) in the UI so that the user can reference that question in conditional logic statements. When a user re-orders, deletes, adds, etc. a question, the randomly-generated question_ids don't change, but the question_ids are invisible to the user; the visible question numbers in the UI should change automatically.

  • The logic inside a conditional can reference the value of the answer to any previous question in the survey.

    • Conditional logic should reference other questions by the unique IDs of those questions, but the user won't know that; the user will only see the visible question number.
      • So when the user edits the conditional logic attached to Question 5 in the survey, the user should see an HTML select/dropdown menu offering Q1, Q2, Q3, or Q4. But if the user selects Q4, what gets written into the JSON is the unique question_id of Q4.
      • And if after choosing Q4 in for the conditional logic statement, the user deletes Q2 from the survey, then the conditional logic statement automatically updates to say that it's based on Q3 (because the question_id of that question hasn't changed.
  • Conditional logic does not need to take into account any information outside the current survey. The only variables it can use are the answers to previous questions in the current survey.

  • Conditional logic can only operate on answers with numeric values, i.e., the answers to slider, free_response: NUMERIC, and radio_button questions. You don't need to enforce this restriction in the UI, but if it's relatively easy to build something enforcing that restriction, go ahead and do it.

    • radio_button questions don't have numeric answers, but we're going to give the answer options numeric values. You don't need to make any JSON spec changes to reflect this; we'll just assume that the first answer choice has value 0, the second choice has value 1, and so on. Please make a UI change reflecting this, though: in the radio_button question modal, the answer options should have numbers 0, 1, 2, etc. displayed next to them. It's fine if both radio_button and checkbox questions have numbers next to the answer options (even though logical statements shouldn't be able to operate on checkbox answers); that's probably marginally easier to implement.
  • The logic inside a conditional can include multiple logical expressions connected by AND and/or OR operators.

  • Besides AND and OR, we only need to support 5 logical operators: ==, <, <=, >, >=. As long as we're only doing numeric operations, it's not necessary to support the NOT operator.

  • Each logical expression with one of the ==, <, <=, >, >= operators can take only two operands. A logical expression with AND or OR can take one or more operands.

JSON spec for a survey with skip logic

The only change to the previous survey JSON spec is that each question object now has an optional attribute display_if.

  • If a question does not have a display_if attribute, always display that question.

  • If the display_if logical expression evaluates as True, display the question. If it evaluates as False, skip to the next question.

  • The operators ==, <, <=, >, >= take a list of two objects: the first object goes in front of the operator, and the second object goes after it. So {'>': [2, 1]} encodes the expression 2 > 1.

  • The operators and and or take a list of any number of objects. {'and': [ {'>': [2, 1]}, {'==': [5, 6]}, {'<=': [0, 1]} ]} encodes the expression (2 > 1) AND (5 == 6) AND (0 <= 1).

  • A string references a question by its unique ID. {'>': ['6695d6c4-916b-4225-8688-89b6089a24d1', 0]} encodes the expression ([answer to Question with ID '6695d6c4-916b-4225-8688-89b6089a24d1'] > 0)

  • If a logical expression references a question that wasn't answered, treat that expression as False. For example, if you're evaluating (Q3 < 1) AND (4 > 3) and Q3 wasn't answered (it's Null/None/nil etc.), it should evaluate as (False) AND (4 > 3).

  • Question randomization overrides skip logic; if survey.settings.randomize == 'True', ignore the display_if attributes.

Example JSON for a survey with skip logic

{
 'content': [
  {'answers': [
    {'text': 'Never'},
    {'text': 'Rarely'},
    {'text': 'Occasionally'},
    {'text': 'Frequently'},
    {'text': 'Almost Constantly'}],
   'question_id': '6695d6c4-916b-4225-8688-89b6089a24d1',
   'question_text': 'In the last 7 days, how OFTEN did you EAT BROCCOLI?',
   'question_type': 'radio_button'},
  {'answers': [
    {'text': 'None'},
    {'text': 'Mild'},
    {'text': 'Moderate'},
    {'text': 'Severe'},
    {'text': 'Very Severe'}],
   'display_if': {'>': ['6695d6c4-916b-4225-8688-89b6089a24d1', 0]},
   'question_id': '41d54793-dc4d-48d9-f370-4329a7bc6960',
   'question_text': 'In the last 7 days, what was the SEVERITY of your CRAVING FOR BROCCOLI?',
   'question_type': 'radio_button'},
  {'answers': [
    {'text': 'Not at all'},
    {'text': 'A little bit'},
    {'text': 'Somewhat'},
    {'text': 'Quite a bit'},
    {'text': 'Very much'}],
   'display_if': {'and': [
                          {'>': ['6695d6c4-916b-4225-8688-89b6089a24d1', 0]},
                          {'>': ['41d54793-dc4d-48d9-f370-4329a7bc6960', 0]}
                          ]},
   'question_id': '5cfa06ad-d907-4ba7-a66a-d68ea3c89fba',
   'question_text': 'In the last 7 days, how much did your CRAVING FOR BROCCOLI INTERFERE with your usual or daily activities, (e.g. eating cauliflower)?',
   'question_type': 'radio_button'},
  {'display_if': {'or': [
                         {'and': [
                                  {'<=': ['6695d6c4-916b-4225-8688-89b6089a24d1', 3]},
                                  {'==': ['41d54793-dc4d-48d9-f370-4329a7bc6960', 2]},
                                  {'<': ['5cfa06ad-d907-4ba7-a66a-d68ea3c89fba', 3]}
                                 ]},
                         {'and': [
                                  {'<=': ['6695d6c4-916b-4225-8688-89b6089a24d1', 3]},
                                  {'<': ['41d54793-dc4d-48d9-f370-4329a7bc6960', 3]},
                                  {'==': ['5cfa06ad-d907-4ba7-a66a-d68ea3c89fba', 2]}
                                 ]},
                         {'and': [
                                  {'==': ['6695d6c4-916b-4225-8688-89b6089a24d1', 4]},
                                  {'<=': ['41d54793-dc4d-48d9-f370-4329a7bc6960', 1]},
                                  {'<=': ['5cfa06ad-d907-4ba7-a66a-d68ea3c89fba', 1]}
                                 ]},
                         ]},
   'question_id': '9d7f737d-ef55-4231-e901-b3b68ca74190',
   'question_text': "While broccoli is a nutritious and healthful food, it's important to recognize that craving too much broccoli can have adverse consequences on your health.  If in a single day you find yourself eating broccoli steamed, stir-fried, and raw with a 'vegetable dip', you may be a broccoli addict.  This is an additional paragraph (following a double newline) warning you about the dangers of broccoli consumption.",
   'question_type': 'info_text_box'},
  {'display_if': {'or': [
                         {'and': [
                                  {'==': ['6695d6c4-916b-4225-8688-89b6089a24d1', 4]},
                                  {'or': [
                                           {'>=': ['41d54793-dc4d-48d9-f370-4329a7bc6960', 2]},
                                           {'>=': ['5cfa06ad-d907-4ba7-a66a-d68ea3c89fba', 2]}
                                          ]}
                                 ]},
                         {'or': [
                                 {'>=': ['41d54793-dc4d-48d9-f370-4329a7bc6960', 3]},
                                 {'>=': ['5cfa06ad-d907-4ba7-a66a-d68ea3c89fba', 3]}
                                ]}
                        ]},
   'question_id': '59f05c45-df67-40ed-a299-8796118ad173',
   'question_text': 'OK, it sounds like your broccoli habit is getting out of hand.  Please call your clinician immediately.',
   'question_type': 'info_text_box'},
  {'question_id': '9745551b-a0f8-4eec-9205-9e0154637513',
   'question_text': 'How many pounds of broccoli per day could a woodchuck chuck if a woodchuck could chuck broccoli?',
   'question_type': 'free_response',
   'text_field_type': 'NUMERIC'},
  {'display_if': {'<': ['9745551b-a0f8-4eec-9205-9e0154637513', 10]},
   'question_id': 'cedef218-e1ec-46d3-d8be-e30cb0b2d3aa',
   'question_text': 'That seems a little low.',
   'question_type': 'info_text_box'},
  {'display_if': {'==': ['9745551b-a0f8-4eec-9205-9e0154637513', 10]},
   'question_id': '64a2a19b-c3d0-4d6e-9c0d-06089fd00424',
   'question_text': 'That sounds about right.',
   'question_type': 'info_text_box'},
  {'display_if': {'>': ['9745551b-a0f8-4eec-9205-9e0154637513', 10]},
   'question_id': '166d74ea-af32-487c-96d6-da8d63cfd368',
   'question_text': "What?! No way- that's way too high!",
   'question_type': 'info_text_box'},
  {'max': '5',
   'min': '1',
   'question_id': '059e2f4a-562a-498e-d5f3-f59a2b2a5a5b',
   'question_text': 'On a scale of 1 (awful) to 5 (delicious) stars, how would you rate your dinner at Chez Broccoli Restaurant?',
   'question_type': 'slider'},
  {'display_if': {'>=': ['059e2f4a-562a-498e-d5f3-f59a2b2a5a5b', 4]},
   'question_id': '6dd9b20b-9dfc-4ec9-cd29-1b82b330b463',
   'question_text': 'Wow, you are a true broccoli fan.',
   'question_type': 'info_text_box'},
  {'question_id': 'ec0173c9-ac8d-449d-d11d-1d8e596b4ec9',
   'question_text': 'THE END. This survey is over.',
   'question_type': 'info_text_box'}],
 'settings': {'number_of_random_questions': None,
  'randomize': False,
  'randomize_with_memory': False,
  'trigger_on_first_download': False},
 'survey_type': 'tracking_survey',
 'timings': [[], [67500], [], [], [], [], []]}
Clone this wiki locally