In the MX8 platform, surveys are written in Python using the Survey class.
Surveys should always start with the code block:
from survey import Survey
s = Survey(**globals())
You can import other Python libraries that are part of the standard library, e.g. datetime and math, but not modules that are not in the standard library, like pandas.
Survey
Survey class for creating surveys.
style
@property
style: Literal['traditional', 'chat']
Return the style of the survey.
Can be either 'traditional' or 'chat' depending on the configuration of the survey theme.
disable_bot_detection
disable_bot_detection() -> None
Disable bot detection mechanisms for this survey.
Appropriate use
Use this method when respondents are interacting with an interviewer who completes the survey, rather than responding on their own devices at home or work.
Bot detection tradeoff
This disables bot detection for the whole survey. Only use it when the survey collection mode makes automated bot checks inappropriate.
tag
tag(**kwargs: Any = {}) -> dict[str, Any]
Create tags for questions, piping, or temporary tag contexts.
Context manager use
When used in a with s.tag(...): block, the tags are attached to every question created
inside that block.
Direct use
When passed as tags=s.tag(...), the tags are attached only to that question.
Piping
Tags can also provide values for placeholders such as {brand} in question text.
Example
from survey import Survey
s = Survey(**globals())
# Using as a context manager
with s.tag(brand="Ford", category="SUV"):
s.text_question("What do you like about {brand} {category}s?")
s.numeric_question("How many {brand} {category}s have you owned?")
# Passing directly to a question
s.text_question(
"Why is {brand} your favorite?",
tags=s.tag(brand=brand_name),
)
Parameters
| Name | Type | Description |
|---|---|---|
**kwargs | Any | Key-value pairs to use as tags. |
standard_screener
standard_screener(country: str | None = None, name: str = 'GenPop') -> None
Add a standard screener to the survey.
For many countries, MX8 has a standard screener that can be used to screen respondents. This is a standard set of questions that are used to screen respondents for the survey. The questions are standard demo questions and include weights to national averages.
Availability
Standard screeners are available only for supported country and screener-name combinations.
Weighting
Standard screener questions include weights to national averages where the selected screener supports them.
Parameters
| Name | Type | Description |
|---|---|---|
country | str | None | The country to use for the screener. This is the country code in the MX8 system. For example, "US" for the United States, "GB" for Great Britain, or "AU" for Australia. |
name | str | The name of the screener to use. This is the name of the screener in the MX8 system. The default is "GenPop". |
show_message
show_message(
message: str,
image: MediaItem | None = None,
require_response: bool = True,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> StringResponse | BoolResponse | IntResponse | ListResponse | None
Show a message to the user.
Presents a message to the user. This could be an introductory message, a warning, or any text based piece of information.
Response requirement
By default respondents must acknowledge the message before continuing. Set
require_response=False to show the message without requiring acknowledgement.
Example
from survey import Survey
s = Survey(**globals())
s.show_message("Welcome to our market research survey! Your feedback is"
"important to us as it helps us understand market trends"
"and consumer behavior. This survey is designed to gather"
"information about your experiences and preferences related"
"to our industry and company. Your responses will be kept"
"confidential and used only for research purposes. Thank"
"you for taking the time to participate!")
Parameters
| Name | Type | Description |
|---|---|---|
message | str | The message to present to the user |
require_response | bool | Whether the user must acknowledge the message |
image | MediaItem | None | An image to show with the message, from the s.media collection |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
get_consent
get_consent(
consent_text: str | None = None,
topic: str | None = None,
client: str | None = None,
image: MediaItem | None = None,
) -> BoolResponse
Get consent for the survey.
Asks the user the standard MX8 consent question. Terminates the respondent if they do not agree to the consent.
Consent termination
Respondents who do not agree to the consent question are terminated automatically.
Custom consent text
Pass consent_text to replace the standard MX8 consent text. topic, client,
and image customize the standard consent question.
Example
from survey import Survey
s = Survey(**globals())
Parameters
| Name | Type | Description |
|---|---|---|
consent_text | str | None | Custom consent text to show instead of the standard MX8 consent text. |
topic | str | None | Topic to display in the standard consent question. |
client | str | None | Client description to use in the standard consent question. |
image | MediaItem | None | Image to show with the consent question, from the s.media collection. |
text_question
text_question(
question: str,
image: MediaItem | None = None,
default: str | list[str] | None = None,
short: bool = False,
recodes: dict[str, str] | None = None,
validation_instructions: str | None = None,
quality_threshold: int = 3,
termination_threshold: int = 1,
max_attempts: int = 3,
custom_validator: Callable[[Any], str | None] | None = None,
number_of_responses: int = 1,
min_responses: int | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> StringResponse | ListResponse
Add a text question to the survey.
Asks an open ended question to the respondent.
Clarification and termination
If the response does not meet quality_threshold, the respondent is asked to clarify.
If the response still does not meet termination_threshold after max_attempts, the
survey terminates. To prevent termination, set quality_threshold to 1.
Multiple responses
number_of_responses and min_responses are only supported in landscape orientation.
Example
from survey import Survey
s = Survey(**globals())
s.text_question("When you think about cars, which brands immediately come to mind?")
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.text_question(
"What do you like about {brand} cars?",
tags=s.tag(brand=brand),
)
Custom Errors
Text questions by default are validated by AI to ensure that the response is a valid response. You can provide custom instructions for the validation by using the validation_instructions parameter.
By default, users are given 3 attempts to provide a valid response. If they fail to do so, the survey will terminate. You can change the maximum number of attempts by using the max_attempts parameter, and you can disable the termination by setting the terminate_after_max_attempts parameter to False.
If disabled, the validation error will be included in any calls to s.count_validation_errors().
If you do not want to use the AI validation, you can provide a custom_validator function that takes the response as a parameter and returns a string if the response is invalid. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if x == "Sleepy Cows" else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
image | MediaItem | None | an image to show with the question, from the s.media collection |
default | str | list[str] | None | the default answer to use in generating test data. If omitted, test data uses a random string of 10 characters. |
short | bool | whether to use a short text input field |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
validation_instructions | str | None | a string describing AI validation rules for the question. Responses are scored on a scale of 1 to 5. |
quality_threshold | int | the minimum score (on a scale of 1-5) required for a valid response, defaults to 3 |
termination_threshold | int | the minimum score required to terminate, defaults to 1, e.g. no termination |
max_attempts | int | the maximum number of attempts to get a valid response |
custom_validator | Callable[[Any], str | None] | None | function that returns a custom error message |
number_of_responses | int | the number of responses to collect. Only supported in landscape orientation. |
min_responses | int | None | the minimum number of responses to collect. Only supported in landscape orientation. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
text_highlighter_question
text_highlighter_question(
question: str,
text_to_highlight: str,
color: str = '#FFFF00',
default: str | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> ListResponse
Add a text highlighter question to the survey.
Asks the respondent to highlight one or more parts of a given text.
Example
from survey import Survey
s = Survey(**globals())
s.text_highlighter_question(
"Please highlight the parts of the following text that you find most interesting:",
text_to_highlight="The quick brown fox jumps over the lazy dog."
)
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
text_to_highlight | str | the text that the respondent will highlight parts of |
color | str | the color to use for highlighting. Must be a hex color string. |
default | str | None | the default highlights to use in generating test data. If omitted, test data uses random highlights. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
numeric_question
numeric_question(
question: str,
image: MediaItem | None = None,
min_max: tuple[int, int] = DEFAULT_MIN_MAX,
default: int | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[str], str] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> IntResponse
Add a numeric question to the survey.
This presents the user with a numeric input for choosing a value between a range of numbers.
Range validation
The respondent must enter a number between the inclusive minimum and maximum values in
min_max.
Example
from survey import Survey
s = Survey(**globals())
s.numeric_question("What year were you born in?",
min_max=(1900, 2024))
Recodes
Recodes can be used to change the values of the responses.
Recodes for numeric question take the format of a dictionary where the key is the range of values to be recoded, and the value is the value to recode to. You can also use percentages to recode a percentage of the values.
Example
from survey import Survey
s = Survey(**globals())
s.numeric_question("How old are you?",
min_max=(18, 100),
recodes={
"18-25": "18-25",
"26-54": "26-54",
"55-74": "55-74",
"75-100": "75+"
})
s.numeric_question("How many times have you visited our website?",
min_max=(0, 100),
recodes={
"0-30%": "Low",
"31-70%": "Medium",
"71-100%": "High"
})
Default values
If no default value is specified, then the test data will consist of random values between the min and max value.
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.numeric_question(
"How many {brand} cars have you owned?",
min_max=(0, 10),
recodes={
"0-30%": "Low",
"31-70%": "Medium",
"71-100%": "High",
},
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention, e.g.
custom_validator=lambda x: "Are you sure that you're 150 years old" if x > 150 else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
min_max | tuple[int, int] | the inclusive minimum and maximum numeric values allowed |
default | int | None | the default value to use when generating test data |
image | MediaItem | None | an image to show with the question, from the s.media collection |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
custom_validator | Callable[[str], str] | None | function that returns a custom error message |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
select_question
select_question(
question: str,
options: list[str | MediaItem],
image: MediaItem | None = None,
randomize: bool = False,
other_options: list[str] | None = None,
disabled_options: list[str] | None = None,
fixed_options: list[str] | None = None,
specify_option: str | None = None,
specify_text: str = DEFAULT_SPECIFY_TEXT,
default: str | list[str] | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[str], str] | None = None,
skip_empty: bool = False,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
style: ChoiceQuestionStyle = 'default',
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> StringResponse
Add a multiple-choice question to the survey.
This presents the user with a number of choices from which the user can choose exactly one.
Example
from survey import Survey
s = Survey(**globals())
s.select_question("What is your gender?",
options=["Male", "Female", "Non-Binary"])
Randomization
Set randomize to True to randomize the order of the options. If you have any additional options you do not want to have randomized, then you can specify these in the fixed_options parameter.
Default values
If no default value is specified, then the test data will consist of random values chosen from the options. If a list is provided, one value is sampled once when the question is created.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question on a five point scale, but want to store the response on a three point scale, you can use recodes to map the response to the correct value.
Disabled Options
Options can be disabled using the disabled_options parameter. This is useful if you want to disable an option based on a previous response. For example, if you want to ask a question about what they enjoyed about the experience, and then ask about what they didn't enjoy, you can disable the options they selected in the first question.
Other, please specify
If you want to allow the user to specify an option that is not in the list, you can set the specify_option parameter. This will present the user with an additional option to specify their own value.
from survey import Survey
s = Survey(**globals())
s.select_question("On a scale of 1 to 5, how much do you like this product?",
options=["1", "2", "3", "4", "5"],
recodes={
"1": "dislike",
"2": "dislike",
"3": "neutral",
"4": "like",
"5": "like"})
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.select_question(
"How many {brand} cars have you owned?",
options=["0", "1", "2", "3", "4+"],
recodes={
"0": "never owned",
"1": "single owner",
"2": "loyalist",
"3": "loyalist",
"4+": "enthusiast",
},
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if x == "Sleepy Cows" else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
options | list[str | MediaItem] | a list of strings or items from s.media containing the options |
image | MediaItem | None | an image to show with the question, from the s.media collection |
randomize | bool | whether to randomize the order of the options |
other_options | list[str] | None | a list of additional options to present to the user |
disabled_options | list[str] | None | a list of strings containing options to be disabled |
fixed_options | list[str] | None | a list of strings containing the fixed options |
skip_empty | bool | if set to True, the question will be skipped if there are no options |
specify_option | str | None | an "other" option that the respondent can specify |
specify_text | str | text to show when the respondent selects specify_option; defaults to "Please specify" |
default | str | list[str] | None | the default value or values to use when generating test data |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
custom_validator | Callable[[str], str] | None | function that returns a custom error message |
image_label_field | str | None | the field to use as the label for the image if using media items in the options |
show_image_label | bool | whether to show the image label or just to show the image |
image_size | tuple[int, int] | None | the bounding box size of the image to display |
style | ChoiceQuestionStyle | presentation style for the options. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
multi_select_question
multi_select_question(
question: str,
options: list[str | MediaItem],
max_options: int | None = None,
image: MediaItem | None = None,
randomize: bool = False,
other_options: list[str] | None = None,
disabled_options: list[str] | None = None,
fixed_options: list[str] | None = None,
exclusive_options: list[str] | None = None,
specify_option: str | None = None,
specify_text: str = DEFAULT_SPECIFY_TEXT,
default: list[str] | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[str], str] | None = None,
skip_empty: bool = False,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
style: ChoiceQuestionStyle = 'default',
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> ListResponse
Add a multi-select question to the survey.
This presents the user with a number of choices from which the user can choose one or more.
Example
from survey import Survey
s = Survey(**globals())
s.select_question("What is your gender?",
options=["Male", "Female", "Non-Binary"])
Randomization
Set randomize to True to randomize the order of the options. If you have any additional options you do not want to have randomized, then you can specify these in the fixed_options parameter.
Default values
If no default value is specified, then the test data will consist of random values chosen from the options.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and classify it into different categories, you can use recodes to map the response to the correct value.
Disabled Options
Options can be disabled using the disabled_options parameter. This is useful if you want to disable an option based on a previous response. For example, if you want to ask a question about what they enjoyed about the experience, and then ask about what they didn't enjoy, you can disable the options they selected in the first question.
Exclusive options
Options listed in exclusive_options are mutually exclusive with other selected options.
Other, please specify
If you want to allow the user to specify an option that is not in the list, you can set the specify_option parameter. This will present the user with an additional option to specify their own value.
from survey import Survey
s = Survey(**globals())
enjoyment = s.multi_select_question("What did you enjoy about the experience?",
options=["The food", "The service", "The atmosphere", "The price"],
fixed_options=["Other"],
exclusive_options=["Other"],
recodes={
"The food": "food",
"The service": "service",
"The atmosphere": "atmosphere",
"The price": "price",
"Other": "other"})
if "Other" in enjoyment:
s.text_question("What else did you enjoy about the experience?")
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.select_question(
"Which of the following do you associate with {brand} cars?",
options=["Reliability", "Safety", "Performance", "Luxury"],
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if "Sleepy Cows" in x else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
options | list[str | MediaItem] | a list of strings or items from s.media containing the options |
max_options | int | None | the maximum number of options to be selected, defaults to all options |
image | MediaItem | None | an image to show with the question, from the s.media collection |
randomize | bool | whether to randomize the order of the options |
other_options | list[str] | None | a list of additional options to present to the user |
disabled_options | list[str] | None | a list of strings containing options to be disabled |
fixed_options | list[str] | None | a list of strings containing the fixed options |
exclusive_options | list[str] | None | a list of strings containing the exclusive options |
specify_option | str | None | an "other" option that the respondent can specify |
specify_text | str | text to show when the respondent selects specify_option; defaults to "Please specify" |
skip_empty | bool | if set to True, the question will be skipped if there are no options |
default | list[str] | None | the default values to use when generating test data |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
custom_validator | Callable[[str], str] | None | function that returns a custom error message |
image_label_field | str | None | the field to use as the label for the image if using media items in the options |
show_image_label | bool | whether to show the image label or just to show the image |
image_size | tuple[int, int] | None | the bounding box size of the image to display |
style | ChoiceQuestionStyle | presentation style for the options. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
grid_select_question
grid_select_question(
question: str,
rows: list[str | MediaItem],
row_name: str,
options: list[str | MediaItem],
style: GridSelectStyle = 'radio',
randomize: bool = False,
randomize_options: bool = False,
other_options: list[str] | None = None,
fixed_options: list[str] | None = None,
specify_option: str | None = None,
specify_text: str = DEFAULT_SPECIFY_TEXT,
image: MediaItem | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[str], str] | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
skip_empty: bool = False,
default: dict[str, str | list[str]] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> DictResponse
Add a grid select question to the survey.
This presents the user with a grid of options where they can select one option from each row.
Example
from survey import Survey
s = Survey(**globals())
s.grid_select_question(
"Please rate the following aspects of the product",
row_name="Product Aspect",
rows=["Quality", "Price", "Service", "Delivery"],
options=["Poor", "Fair", "Good", "Very Good", "Excellent"],
)
Randomization
Set randomize=True to randomize rows and randomize_options=True to randomize options.
fixed_options remain fixed when option randomization is used.
Default values
Defaults are provided as a dictionary keyed by row. List values are sampled once when the question is created.
Other, please specify
Use specify_option to add an option where the respondent can provide their own value.
Skip empty
If skip_empty=True, the question is skipped when there are no rows or no options.
Style
style controls the grid control style, either "button" or "radio".
Recodes: Recodes map responses to different values. This is useful when you want to classify responses into categories.
s.grid_select_question(
"Please rate the following aspects of the product",
row_name="Product Aspect",
rows=["Quality", "Price", "Service", "Delivery"],
options=["Poor", "Fair", "Good", "Very Good", "Excellent"],
recodes={
"Poor": "1",
"Fair": "2",
"Good": "3",
"Very Good": "4",
"Excellent": "5",
},
)
Custom Errors:
By default, custom_validator is set to a straight-line check that raises an error if
the respondent enters the same value for each cell. You can override this by passing a
custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | The question to ask the respondent. |
rows | list[str | MediaItem] | Strings or media items containing the rows. |
row_name | str | Label to use for each row in topics. |
options | list[str | MediaItem] | Strings or media items containing the options. |
style | GridSelectStyle | The style of the question ("button" or "radio"). Defaults to "radio". |
randomize | bool | Whether to randomize the order of the rows. |
randomize_options | bool | Whether to randomize the order of the options. |
other_options | list[str] | None | Additional options to present to the user. |
fixed_options | list[str] | None | Fixed options that should not be randomized. |
specify_option | str | None | An "other" option that the respondent can specify. |
specify_text | str | Text to show when the respondent selects specify_option. |
image | MediaItem | None | An image to show with the grid, from the s.media collection. |
recodes | dict[str, str] | None | A dictionary of recodes to apply to the question. |
custom_validator | Callable[[str], str] | None | A function that returns a custom error message. |
image_label_field | str | None | The field to use as the label for media items in the rows. |
show_image_label | bool | Whether to show the image label or just the image in the rows. |
image_size | tuple[int, int] | None | The bounding box size of the image to display. |
skip_empty | bool | If True, skip the question when there are no rows or options. |
default | dict[str, str | list[str]] | None | Per-row defaults; list values are sampled once at creation. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). |
**_kwargs | Any | Additional question configuration. |
grid_multi_select_question
grid_multi_select_question(
question: str,
rows: list[str | MediaItem],
row_name: str,
options: list[str | MediaItem],
style: GridMultiSelectStyle = 'checkbox',
randomize: bool = False,
randomize_options: bool = False,
other_options: list[str] | None = None,
fixed_options: list[str] | None = None,
exclusive_options: list[str] | None = None,
specify_option: str | None = None,
specify_text: str = DEFAULT_SPECIFY_TEXT,
image: MediaItem | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[str], str] | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
skip_empty: bool = False,
default: dict[str, list[str]] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> DictResponse
Add a grid multi-select question to the survey.
This presents the user with a grid of options where they can select multiple options from each row.
Example
from survey import Survey
s = Survey(**globals())
s.grid_multi_select_question("What do you associate with these brands? Select all that apply",
rows=["Coke", "Pepsi", "Dasani", "Fanta"],
options=["Tasty", "Refreshing", "Cool", "Value", "Convenient"])
Randomization
Set randomize=True to randomize rows and randomize_options=True to randomize options.
fixed_options remain fixed when option randomization is used.
Default values
Defaults are provided as a dictionary keyed by row.
Exclusive options
Options listed in exclusive_options are mutually exclusive with other selected options
in the same row.
Other, please specify
Use specify_option to add an option where the respondent can provide their own value.
Skip empty
If skip_empty=True, the question is skipped when there are no rows or no options.
Style
style controls the grid control style, either "button" or "checkbox".
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and classify it into different categories, you can use recodes to map the response to the correct value.
Example
from survey import Survey
s = Survey(**globals())
s.grid_multi_select_question("What do you associate with these brands? Select all that apply",
row_name="Brand",
rows=["Coke", "Pepsi", "Dasani", "Fanta"],
options=["Tasty", "Refreshing", "Cool", "Value", "Convenient"],
recodes={
"Tasty": "Product",
"Refreshing": "Product",
"Cool": "Brand",
"Value": "Pricing",
"Convenient": "Distribution"
})
Custom Errors
By default, the custom_validator parameter is set to a straight-line check that raises an error if the respondent enters the same value for each cell but you can override this by passing a custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
rows | list[str | MediaItem] | the rows to show in the grid |
row_name | str | the name of the row |
options | list[str | MediaItem] | the options available for each row |
style | GridMultiSelectStyle | the question style, either "button" or "checkbox"; defaults to "checkbox" |
randomize | bool | whether to randomize the order of the rows |
randomize_options | bool | whether to randomize the order of the options |
other_options | list[str] | None | additional options to present to the user |
fixed_options | list[str] | None | options that should remain fixed in place |
exclusive_options | list[str] | None | options that should be mutually exclusive |
specify_option | str | None | an "other" option that the respondent can specify |
specify_text | str | text to show when the respondent selects specify_option |
image | MediaItem | None | an image to show with the grid, from the s.media collection |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
default | dict[str, list[str]] | None | default values to use when generating test data |
skip_empty | bool | if True, skip the question when there are no rows |
image_label_field | str | None | the field to use as the label for the image if using media items in the rows |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the image to display |
custom_validator | Callable[[str], str] | None | a function to return a custom error message |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
grid_rating_question
grid_rating_question(
question: str,
rows: list[str | MediaItem],
row_name: str,
image: MediaItem | None = None,
number_of_points: int | None = None,
style: RatingQuestionStyle = 'slider',
first_point: int | None = None,
labels: dict[int, str] | None = None,
randomize: bool = False,
recodes: dict[str, str] | None = None,
dont_know_option: str = '',
skip_empty: bool = False,
custom_validator: Callable[[str], str] | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
default: dict[str, int] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> DictResponse
Add a grid rating question to the survey.
This presents the user with a grid of options where they can rate each row on user defined scale.
Example
from survey import Survey
s = Survey(**globals())
s.grid_rating_question("Please rate the following aspects of the product",
rows=["Quality", "Price", "Service", "Delivery"],
row_name="Product Aspect",
number_of_points=5)
Randomization
Set randomize=True to randomize the row order.
Default values
Defaults are provided as a dictionary keyed by row.
Style and labels
style, number_of_points, first_point, and labels control the rating scale shown
for each row.
Using the don't know option
Use dont_know_option to add a "Don't know" option. It is reported using the provided
text and stored as a sentinel value.
Skip empty
If skip_empty=True, the question is skipped when there are no rows.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and classify it into different categories, you can use recodes to map the response to the correct value.
Example
from survey import Survey
s = Survey(**globals())
s.grid_rating_question("Please rate the following aspects of the product",
row_name="Product Aspect",
rows=["Quality", "Price", "Service", "Delivery"],
number_of_points=5,
recodes={
"1": "Poor",
"2": "Poor",
"3": "Fair",
"4": "Fair",
"5": "Good"
})
Custom Errors
By default, the custom_validator parameter is set to a straight-line check that raises an error if the respondent enters the same value for each cell but you can override this by passing a custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
rows | list[str | MediaItem] | a list of strings or media items containing the rows |
row_name | str | the name of the row |
image | MediaItem | None | an image to show with the grid, from the s.media collection |
number_of_points | int | None | the number of points to rate the row on, defaults to 5 or the number of labels |
style | RatingQuestionStyle | the style of the question, either "slider", "button" or "star" |
first_point | int | None | the value of the first point, defaults to 1 or the first label |
labels | dict[int, str] | None | a dictionary of labels to use on the question |
randomize | bool | whether to randomize the order of the rows |
default | dict[str, int] | None | a dictionary of default values to use when generating test data |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
dont_know_option | str | label to use for the don't-know option, if any. |
skip_empty | bool | if set to True, the question will be skipped if there are no rows |
custom_validator | Callable[[str], str] | None | function that returns a custom error message |
image_label_field | str | None | the field to use as the label for the image if using media items in the rows |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the images to display |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
grid_numeric_question
grid_numeric_question(
question: str,
rows: list[str | MediaItem],
row_name: str,
columns: list[str] | None = None,
column_name: str | None = None,
image: MediaItem | None = None,
min_max: tuple[int, int] = DEFAULT_MIN_MAX,
randomize: bool = False,
randomize_columns: bool = False,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[int], str] | None = None,
autosum_columns: bool = False,
autosum_rows: bool = False,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
default: dict[str, int | dict[str, int]] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> DictResponse
Add a grid numeric question to the survey.
This presents the user with a grid of options where they can select one option from each row.
Example
from survey import Survey
s = Survey(**globals())
s.grid_numeric_question("How many hours do you spend per week on the following activities?",
rows=["Work", "Sleep", "Exercise", "Socializing"],
row_name="Activity",
columns=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
randomize=True,
)
Randomization
Set randomize=True to randomize rows and randomize_columns=True to randomize columns.
Default values
Defaults are provided as a dictionary keyed by row, or by row and column when columns are used.
Range validation
Each numeric response must be between the inclusive minimum and maximum values in min_max.
Autosum
Set autosum_columns or autosum_rows to require responses to sum correctly by column or row.
Recodes
Recodes can be used to change the values of the responses.
Recodes for numeric question take the format of a dictionary where the key is the range of values to be recoded, and the value is the value to recode to. You can also use percentages to recode a percentage of the values.
Example
from survey import Survey
s = Survey(**globals())
s.grid_numeric_question("How many hours do you spend per week on the following activities?",
rows=["Work", "Sleep", "Exercise", "Socializing"],
row_name="Activity",
columns=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
randomize=True,
recodes={
"0-30%": "Low",
"31-70%": "Medium",
"71-100%": "High"
})
Custom Errors
By default, the custom_validator parameter is set to a straight-line check that raises an error if the respondent enters the same value for each cell but you can override this by passing a custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
rows | list[str | MediaItem] | a list of strings containing the rows |
row_name | str | the name of the row |
columns | list[str] | None | a list of strings containing the columns |
column_name | str | None | the name of the column |
image | MediaItem | None | an image to show with the grid, from the s.media collection |
min_max | tuple[int, int] | the minimum and maximum values for the question |
randomize | bool | whether to randomize the order of the rows |
randomize_columns | bool | whether to randomize the order of the columns |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
default | dict[str, int | dict[str, int]] | None | a dictionary of default values to use when generating test data |
autosum_columns | bool | whether to autosum the columns |
autosum_rows | bool | whether to autosum the rows |
image_label_field | str | None | the field to use as the label for the image if using media items in the rows |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the images to display |
custom_validator | Callable[[int], str] | None | function that returns a custom error message |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
this_or_that_question
this_or_that_question(
question: str,
row_options: list[list[str | MediaItem]],
randomize: bool = False,
randomize_columns: bool = False,
default: dict[str, str | list[str]] | None = None,
recodes: dict[str, str] | None = None,
image: MediaItem | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
custom_validator: Callable[[int], str] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> DictResponse
Add a this or that question to the survey.
This presents the user with a series of questions where they can select one option from each row.
Example
from survey import Survey
s = Survey(**globals())
s.this_or_that_question("Which of the following do you prefer?",
row_options=[
["Apples", "Oranges"],
["Coke", "Pepsi"],
["Dogs", "Cats"],
["Winter", "Summer"]
])
Randomization
Set randomize=True to randomize row order and randomize_columns=True to randomize the
order of options within each row.
Default values
Defaults are provided as a dictionary keyed by row id. List values are sampled once when the question is created.
Media options
row_options may contain media items. Use image_label_field, show_image_label, and
image_size to control media option labels and display size.
Recodes
Recodes can be used to change the values of the responses.
Recodes for this or that question take the format of a dictionary where the key is the range of values to be recoded, and the value is the value to recode to. You can also use percentages to recode a percentage of the values.
Example
from survey import Survey
s = Survey(**globals())
s.this_or_that_question("Which of the following do you prefer?",
row_options=[
["Apples", "Oranges"],
["Coke", "Pepsi"],
["Dogs", "Cats"],
["Winter", "Summer"]
])
Custom Errors
By default, the custom_validator parameter is set to a straight-line check that raises an error if the respondent enters the same value for each cell but you can override this by passing a custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
row_options | list[list[str | MediaItem]] | a list of lists containing the options |
randomize | bool | whether to randomize the order of the rows |
randomize_columns | bool | whether to randomize the order of the columns |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
default | dict[str, str | list[str]] | None | per-row defaults; list values are sampled once at creation |
image | MediaItem | None | an image to show with the question, from the s.media collection |
image_label_field | str | None | the media field to use as the image label |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the images to display |
custom_validator | Callable[[int], str] | None | function that returns a custom error message |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
this_or_that_rating_question
this_or_that_rating_question(
question: str,
row_options: list[list[str]],
row_name: str = 'scale',
number_of_points: int | None = None,
style: ThisOrThatRatingStyle = 'slider',
first_point: int | None = None,
dont_know_option: str = '',
randomize: bool = False,
default: dict[str, int] | None = None,
recodes: dict[str, str] | None = None,
image: MediaItem | None = None,
custom_validator: Callable[[int], str] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> DictResponse
Add a this or that rating question to the survey.
This presents the user with a series of questions where they can rate one option from each row.
Example
from survey import Survey
s = Survey(**globals())
s.this_or_that_rating_question("Which of the following do you prefer?",
number_of_points=5,
row_options=[
["Apples", "Oranges"],
["Coke", "Pepsi"],
["Dogs", "Cats"],
["Winter", "Summer"]
])
Randomization
Set randomize=True to randomize row order.
Default values
Defaults are provided as a dictionary keyed by row name.
Style and scale
style, number_of_points, first_point, and row_options define the rating scale shown
for each row.
Using the don't know option
Use dont_know_option to add a "Don't know" option. It is reported using the provided
text and stored as a sentinel value.
Recodes
Recodes can be used to change the values of the responses.
Recodes for this or that question take the format of a dictionary where the key is the range of values to be recoded, and the value is the value to recode to. You can also use percentages to recode a percentage of the values.
Example
from survey import Survey
s = Survey(**globals())
s.this_or_that_rating_question("Which of the following do you prefer?",
row_options=[
["Apples", "Oranges"],
["Coke", "Pepsi"],
["Dogs", "Cats"],
["Winter", "Summer"]
])
Custom Errors
By default, the custom_validator parameter is set to a straight-line check that raises an error if the respondent enters the same value for each cell but you can override this by passing a custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
row_options | list[list[str]] | a list of lists containing the options |
row_name | str | the name of the row (default "scale") |
number_of_points | int | None | the number of points to rate the row on, defaults to 5 |
style | ThisOrThatRatingStyle | the style of the question, either "slider" or "button" |
first_point | int | None | the value of the first point, defaults to 1 |
dont_know_option | str | Optionally add a "Don't know" option to the question using this text. |
randomize | bool | whether to randomize the order of the rows |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
default | dict[str, int] | None | a dictionary of default values to use when generating test data |
image | MediaItem | None | an image to show with the question, from the s.media collection |
custom_validator | Callable[[int], str] | None | function that returns a custom error message |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
show_image
show_image(image: MediaItem, number_seconds: int = 5) -> StringResponse | BoolResponse | IntResponse | ListResponse | None
Show an image to the respondent.
This will show an image to the respondent for a set number of seconds.
Timed display
The image is displayed for number_seconds seconds before the respondent can continue.
Response requirement
The respondent does not provide an answer; this method is for controlled image exposure.
Example
from survey import Survey
s = Survey(**globals())
for media in s.media:
s.show_image("Take a look at this image for 5 seconds",
image=media,
number_seconds=5)
Parameters
| Name | Type | Description |
|---|---|---|
image | MediaItem | the image to show |
number_seconds | int | the number of seconds to show the image |
play_video
play_video(
video: MediaItem,
end_message: str = 'Thank you for watching the video!',
start_message: str = "Hit play when you're ready to watch the video.",
dial: str = 'none',
dial_labels: dict[int, str] | None = None,
dial_caption: str = 'Please adjust the slider to reflect your feelings as you watch the video.',
start_position: int | None = None,
) -> StringResponse | BoolResponse | IntResponse | ListResponse | None
Play a video to the respondent.
This will play a video to the respondent and then present them with a message at the end, which defaults to "Thank you for watching the video!".
Video security
Videos are DRM protected to prevent copying or screen recording. Secure videos are watermarked with the respondent's IP address, and video links expire after 60 seconds.
Example
from survey import Survey
s = Survey(**globals())
for media in s.media:
s.play_video(video=media)
... now ask questions about the video
Dial testing
You can also add a dial to the video to allow the respondent to indicate their feelings as they watch the video. The dial can be set to one of the following values:
- "none" - no dial is shown
- "right" - a dial is shown on the right of the video
- "bottom" - a dial is shown at the bottom of the video
The dial defaults to a range of 0-100, with captions "Dislike", "Neutral", and "Like" but you can set the labels and caption for the dial using the dial_labels and dial_caption parameters.
Parameters
| Name | Type | Description |
|---|---|---|
video | MediaItem | the video to play, from the s.media collection |
end_message | str | the message to show the respondent at the end of the video |
start_message | str | the message to show the respondent at the start of the video |
dial | str | the position of the dial, either "none", "right", or "bottom", defaults to "none" |
dial_labels | dict[int, str] | None | the labels to show on the dial |
dial_caption | str | the caption to show above the dial |
start_position | int | None | starting value for the dial; defaults to the midpoint of the dial labels |
show_embedded_media
show_embedded_media(question: str, url: str, duration: int) -> StringResponse
Embed external content from a URL in the survey.
Supported providers
Embedded media currently supports YouTube URLs only.
Example
from survey import Survey
s = Survey(**globals())
s.show_embedded_media("Watch this video", "https://www.youtube.com/watch?v=2gcsgfzqN8k", duration=231)
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to show above the embedded media |
url | str | the URL to embed in the survey |
duration | int | the number of seconds the respondent must wait before continuing |
get_location
get_location(
validation_type: str | None = None,
reporting_type: str | None = None,
default: str | None = None,
number_options: int = 5,
tags: dict[str, Any] | None = None,
strict: bool = False,
**_kwargs: Any = {},
) -> StringResponse
Ask the respondent for their location and validate it with a follow-up gotcha question.
Adds two questions to the survey. The first asks for the user's ZIP code,
and the second validates that ZIP code based on validation_type. The
location is reported based on the reporting_type.
Example
s.get_location(validation_type="state", default="90210")
Recodes
The location question does not support recodes.
Parameters
| Name | Type | Description |
|---|---|---|
validation_type | str | None | the type of location to validate against; defaults to city |
reporting_type | str | None | the type of location to report, defaults to region |
default | str | None | the default ZIP code to use in test data. If omitted, test data uses random values chosen from the options. |
number_options | int | the number of matching locations to show the respondent |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
strict | bool | whether to terminate the respondent if they enter invalid location data |
quota
quota(
name: str,
criteria: bool,
quota: float = 0.0,
min_respondents: int = 0,
max_respondents: int | None = None,
) -> Quota
Create a quota line.
Use this to define an individual quota line that can be passed to s.set_quota().
Quota line only
This method only defines a quota line. Pass the returned quota to s.set_quota() to apply
quota logic to the survey.
Minimum and maximum respondents
min_respondents enforces a minimum fill for the line. max_respondents caps the number
of respondents assigned to the line.
Example
from survey import Survey
s = Survey(**globals())
age = s.numeric_question(question="How old are you?",
min_max=(18, 100),
recodes={
"0-17": "Under 18",
"18-34": "18-34",
"35-54": "35-54",
"55-120": "55+"
})
gender = s.select_question(question="What is your gender?",
options=["Male", "Female"])
s.set_quota(
name="Quads",
quotas=[
s.quota("Younger Men", criteria=(age < 40) & (gender == "Male"), quota=0.25),
s.quota("Older Men", criteria=(age >= 40) & (gender == "Male"), quota=0.25),
s.quota("Younger Women", criteria=(age < 40) & (gender == "Female"), quota=0.25),
s.quota("Older Women", criteria=(age >= 40) & (gender == "Female"), quota=0.25)
]
)
You can also specify a minimum number of respondents that must be included in this quota line by passing the min_respondents parameter.
Example
from survey import Survey
s = Survey(**globals())
s.set_quota(
name="Age Quota",
quotas=[
s.quota("Under 18", criteria=(age < 18), quota=0.1, min_respondents=50),
s.quota("18-34", criteria=(age >= 18) & (age < 35), quota=0.3, min_respondents=100),
s.quota("35-54", criteria=(age >= 35) & (age < 55), quota=0.3, min_respondents=100),
s.quota("55+", criteria=(age >= 55), quota=0.3, min_respondents=100)
]
)
If you only pass in values with min_respondents, then the quota will not be used for weighting, but it will still be used to terminate respondents that do not meet the criteria.
You can optionally set max_respondents to cap the number of respondents assigned to a quota line. If omitted, that line has no explicit cap.
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name to give this quota line when it is reported |
criteria | bool | the boolean condition based on responses to other questions that the respondent must meet to be included in this quota line |
quota | float | the proportion of respondents that should fall into this quota |
min_respondents | int | the minimum number of respondents that must be included in this quota line |
max_respondents | int | None | optional maximum number of respondents for this quota line |
set_quota
set_quota(name: str, quotas: list[Quota], exclusive: bool = True) -> QuotaResponse
Set a quota group based on a number of quota lines.
Takes a group of individual quotas from s.quota() and creates a quota group.
Termination behavior
Respondents are terminated when they do not match any quota line or when their matching quota line is over quota.
Exclusive quotas
By default quotas are exclusive and each respondent fills one matching quota line. If
exclusive=False, respondents may be included in multiple matching quota lines.
Example
from survey import Survey
s = Survey(**globals())
age = s.numeric_question(question="How old are you?",
min_max=(18, 100),
recodes={
"0-17": "Under 18",
"18-34": "18-34",
"35-54": "35-54",
"55-120": "55+"
})
gender = s.select_question(question="What is your gender?",
options=["Male", "Female"])
# Check age and gender quotas
s.set_quota(
name="Quads",
quotas=[
s.quota("Younger Men", criteria=(age < 40) & (gender == "Male"), quota=0.25),
s.quota("Older Men", criteria=(age >= 40) & (gender == "Male"), quota=0.25),
s.quota("Younger Women", criteria=(age < 40) & (gender == "Female"), quota=0.25),
s.quota("Older Women", criteria=(age >= 40) & (gender == "Female"), quota=0.25)
]
)
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name of this group of quotas, for example "demos" |
quotas | list[Quota] | a list of quotas to be added, each created using s.quota() |
exclusive | bool | whether the quotas are exclusive, defaults to True. If set to False, then respondents can be included in multiple quota lines, but they will still be terminated if they do not meet any of the quota criteria. |
get_least_filled
get_least_filled(number: int, from_list: list[Any], quota: str) -> list[Any]
Get the least filled quotas from a list of elements in a quota.
Selects the least-filled items from a list for balanced follow-up questions.
Selection only
This function returns the least-filled items from the list, but does not terminate the respondent based on quota state.
Terminating by quota
To terminate respondents based on quota state, use s.set_quota() to establish quotas.
Example
from survey import Survey
s = Survey(**globals())
brand_list = ['Acura', 'Alfa Romeo', 'Aston Martin', 'Audi', 'Bentley',
'BMW', 'Bugatti', 'Buick', 'Cadillac', 'Chevrolet', 'Chrysler',
'Citroen', 'Dodge', 'Ferrari', 'Fiat', 'Ford', 'Geely', 'Genesis',
'GMC', 'Honda', 'Hyundai', 'Infiniti', 'Jaguar', 'Jeep', 'Kia',
'Koenigsegg', 'Lamborghini', 'Land Rover', 'Lexus', 'Lincoln',
'Lotus', 'Maserati', 'Mazda', 'McLaren', 'Mercedes-Benz', 'Mini',
'Mitsubishi', 'Nissan', 'Pagani', 'Peugeot', 'Polestar', 'Porsche',
'Ram', 'Renault', 'Rolls-Royce', 'Subaru', 'Suzuki', 'Tesla', 'Toyota',
'Volkswagen', 'Volvo']
brands = s.get_least_filled(number=10,
from_list=brand_list,
quota="Brand familiarity")
familiar_brands = s.multi_select_question(
question="Which of the following car brands have you heard of?",
options=brands)
Parameters
| Name | Type | Description |
|---|---|---|
number | int | the number of items to return |
from_list | list[Any] | the list of items to choose from |
quota | str | the name of the quota to check against |
terminate
terminate(reason: str) -> NoReturn
Terminate the survey.
Use this when the respondent does not qualify for a specific reason.
Immediate termination
This immediately adds a terminal survey element and stops execution.
Quota termination
Use s.set_quota() for quota-based termination; it also terminates respondents who do not
qualify for the quotas.
Example
if not has_xbox:
s.terminate(
"Sorry you don't qualify for this survey "
"because you don't have an Xbox"
)
Parameters
| Name | Type | Description |
|---|---|---|
reason | str | The reason to give to the user to explain why they don't qualify. |
terminate_if
terminate_if(condition: bool, reason: str) -> None
Terminate the survey if a condition is met.
Conditionally stops the survey for a respondent.
Condition evaluation
If condition is true, this calls s.terminate(reason) immediately. If it is false,
survey execution continues.
Example
s.terminate_if(
not has_xbox,
"Sorry you don't qualify for this survey because you don't have an Xbox",
)
Parameters
| Name | Type | Description |
|---|---|---|
condition | bool | The condition to check and terminate the user if it's True |
reason | str | The reason to give to the user to explain why they don't qualify. |
complete
complete(
message: str = 'Thank you for completing our survey! See you again.',
) -> None
Complete the survey.
Call this function at the end of the survey to complete the survey and thank the user.
Completion behavior
This adds a non-terminated completion element. Use s.terminate() instead when the
respondent should be screened out.
Example
from survey import Survey
s = Survey(**globals())
# Add questions here
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
message | str | The message to display to the user upon completing the survey. |
rating_question
rating_question(
question: str,
image: MediaItem | None = None,
number_of_points: int | None = None,
style: RatingQuestionStyle = 'slider',
first_point: int | None = None,
labels: dict[int, str] | None = None,
default: int | None = None,
recodes: dict[str, str] | None = None,
dont_know_option: str = '',
custom_validator: Callable[[str], str] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> IntResponse
Add a rating-scale question to the survey.
This presents the respondent with a numeric scale and records the selected point as an integer response.
There are three available styles:
- style="slider" - For the slider style, the user can drag the slider.
- style="button" - For the button style, the user can click on the button they want.
In both the slider and button styles, the rating scale is presented as a number with optional text labels for appropriate points on the scale.
Style and scale
style, number_of_points, first_point, and labels control how the rating scale is
displayed and what values it records.
Example
from survey import Survey
s = Survey(**globals())
s.rating_question("How much do you like this product?",
number_of_points=5,
style="slider",
labels={
1: "Dislike",
3: "Neutral",
5: "Like"
})
Default values
If no default value is specified, then the test data will consist of random values chosen from the options.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and classify it into different categories, you can use recodes to map the top two box response to a different value.
from survey import Survey
s = Survey(**globals())
s.rating_question("On a scale of 1 to 5, how much do you like this product?",
number_of_points=5,
style="slider",
labels={
1: "Dislike",
3: "Neutral",
5: "Like"
},
recodes={
"1": "dislike",
"2": "dislike",
"3": "neutral",
"4": "like",
"5": "like"})
Using the don't know option
If you want to add a "Don't know" option to the question, then you can specify the text to use for the "Don't know" option using the dont_know_option parameter. If the user selects this option then the response will be recorded as a value of -999 in the survey data and reported as the text specified in the dont_know_option parameter.
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.rating_question(
"How do you rate {brand} cars?",
number_of_points=5,
style="slider",
labels={1: "Dislike", 3: "Neutral", 5: "Like"},
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
number_of_points | int | None | the number of points on the scale (default to 5 or the number of labels) |
first_point | int | None | the first point on the scale (default to 1 or the first label) |
style | RatingQuestionStyle | the style of the scale, either "slider", "button", or "star" |
labels | dict[int, str] | None | a dictionary of text labels for each point on the scale |
image | MediaItem | None | an image to show with the question, from the s.media collection |
default | int | None | the default value to use when generating test data |
dont_know_option | str | Optionally add a "Don't know" option to the question using this text. |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
custom_validator | Callable[[str], str] | None | function that returns a custom error message |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
net_promoter_score_question
net_promoter_score_question(
question: str,
image: MediaItem | None = None,
number_of_points: int | None = 11,
style: RatingQuestionStyle = 'slider',
first_point: int | None = None,
labels: dict[int, str] | None = None,
default: int | None = None,
recodes: dict[str, str] | None = None,
dont_know_option: str = '',
custom_validator: Callable[[str], str] | None = None,
min_promoter_score: int | None = None,
max_detractor_score: int | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> IntResponse
Add a Net Promoter Score question to the survey.
This asks the respondent how likely they are to recommend a product, brand, service, or experience. By default, it uses an 11-point scale from 0 to 10 with endpoint labels for "Not at all likely" and "Extremely likely".
There are three available styles:
- style="slider" - For the slider style, the user can drag the slider.
- style="button" - For the button style, the user can click on the button they want.
In both the slider and button styles, the NPS scale is presented as a number with optional text labels for appropriate points on the scale.
Style and scale
style, number_of_points, first_point, and labels control how the rating scale is
displayed and what values it records.
Promoter and detractor scoring
min_promoter_score sets the minimum score classified as promoter, and
max_detractor_score sets the maximum score classified as detractor.
Example
from survey import Survey
s = Survey(**globals())
s.net_promoter_score_question(
"How likely are you to recommend this product to a friend or colleague?",
number_of_points=11,
style="slider",
labels={
0: "Not at all likely",
10: "Extremely likely",
},
)
Default values
If no default value is specified, then the test data will consist of random values chosen from the options.
Recodes
Recodes are used to map the response to a different value. For NPS questions, recodes are most often used to map raw scores into detractor, passive, and promoter groups.
from survey import Survey
s = Survey(**globals())
s.net_promoter_score_question(
"How likely are you to recommend this product to a friend or colleague?",
number_of_points=11,
style="slider",
recodes={
"0": "detractor",
"1": "detractor",
"2": "detractor",
"3": "detractor",
"4": "detractor",
"5": "detractor",
"6": "detractor",
"7": "passive",
"8": "passive",
"9": "promoter",
"10": "promoter",
},
)
Using the don't know option
If you want to add a "Don't know" option to the question, then you can specify the text to use for the "Don't know" option using the dont_know_option parameter. If the user selects this option then the response will be recorded as a value of -999 in the survey data and reported as the text specified in the dont_know_option parameter.
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.net_promoter_score_question(
"How likely are you to recommend {brand} cars to a friend or colleague?",
number_of_points=11,
style="slider",
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
number_of_points | int | None | the number of points on the NPS scale; defaults to 11 for a 0-10 scale |
first_point | int | None | the first point on the scale (default to 1 or the first label) |
style | RatingQuestionStyle | the style of the scale, either "slider", "button", or "star" |
labels | dict[int, str] | None | a dictionary of labels (default {0:"Not at all likely", 10:"Extremely likely"}) |
image | MediaItem | None | an image to show with the question, from the s.media collection |
default | int | None | the default value to use when generating test data |
dont_know_option | str | Optionally add a "Don't know" option to the question using this text. |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
custom_validator | Callable[[str], str] | None | function that returns a custom error message |
min_promoter_score | int | None | minimum score required to classify a respondent as a promoter |
max_detractor_score | int | None | maximum score that classifies a respondent as a detractor |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
ranking_question
ranking_question(
question: str,
options: list[str | MediaItem],
min_options: int | None = None,
max_options: int | None = None,
style: ChoiceQuestionStyle = 'button',
labels: tuple[str, str] = ('Least', 'Most'),
randomize: bool = False,
fixed_options: list[str] | None = None,
other_options: list[str] | None = None,
skip_empty: bool = False,
default: list[str] | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[str], str] | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
dont_know_option: str = '',
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> ListResponse
Add a ranking question to the survey.
This presents the user with a number of choices to rank in order of preference.
Ranking limits
min_options and max_options control how many options the respondent must rank.
Randomization
Set randomize=True to randomize option order. fixed_options remain fixed when
randomization is used.
Other options
Use other_options to add additional options to the ranking list.
Using the don't know option
Use dont_know_option to add a "Don't know" option to the question.
Example
from survey import Survey
s = Survey(**globals())
s.ranking_question("Rank the following car brands in order of preference",
options=["Ford", "Toyota", "Honda", "Tesla"],
labels=("Least", "Most"),
Default values
If no default value is specified, then the test data will consist of the options returned in a random order.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and ranks the different activities, you can use recodes to map the top two box response to a different value.
from survey import Survey
s = Survey(**globals())
s.ranking_question("Rank the following activities in order of preference",
options=["Eating", "Drinking", "Sleeping", "Working"],
labels=("Least", "Most"),
recodes={
"Eating": "eating",
"Drinking": "drinking",
"Sleeping": "sleeping",
"Working": "working"
})
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
brand_cars = {
"Ford": ["Fiesta", "Focus", "Mustang", "F150"],
"Toyota": ["Corolla", "Camry", "Prius", "RAV4"],
"Honda": ["Civic", "Accord", "CR-V", "Pilot"],
"Tesla": ["Model S", "Model 3", "Model X", "Model Y"],
}
for brand in car_brands:
s.ranking_question(
"Rank the following {brand} cars in order of preference?",
options=brand_cars[brand],
labels=("Least", "Most"),
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if "Sleepy Cows" in x else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
options | list[str | MediaItem] | a list of strings or media items containing the options |
min_options | int | None | the minimum number of options to be ranked, defaults to all options |
max_options | int | None | the maximum number of options to be ranked, defaults to all options |
style | ChoiceQuestionStyle | presentation style for the options. |
labels | tuple[str, str] | a tuple of text labels for the least and most options |
randomize | bool | whether to randomize the order of the options |
fixed_options | list[str] | None | a list of options that will not be randomized |
other_options | list[str] | None | a list of additional options to present to the user |
default | list[str] | None | the default ranking to use when generating test data |
skip_empty | bool | if set to True, the question will be skipped if there are no options |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
dont_know_option | str | Optionally add a "Don't know" option to the question using this text. |
custom_validator | Callable[[str], str] | None | function that returns a custom error message |
image_label_field | str | None | the field to use as the label for the image if using media items in the rows |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the images to display |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
media
@property
media: MediaCollection
Access the media collection.
The media collection contains images and videos that can be used in questions. The media are added to the survey automatically and can be uploaded from the MX8 dashboard. Additional columns added in the dashboard will be added to the media collection.
Each item in the media collection is a MediaItem object. You can access the media collection by iterating over the media object, and pass it to the image parameter of questions.
Columns can be accessed using dot notation. For example, if you have a column called "brand" in your media collection, then you can access it using media.brand. You can also get all the values of a column using the get_media_values function.
If you have not uploaded any media to the survey, then a dummy media collection will be used that contains dummy images with the following columns:
- name
- description
- product
- brand
- campaign
- agency
- id
You can create your own metadata columns in the MX8 dashboard and these will be added to the media collection after you have created the survey and uploaded the media.
The columns are automatically used when reporting the results. For example, if you have a column called "brand" and you ask a question about the brand, then the results will be automatically grouped by brand.
Example
from survey import Survey
s = Survey(**globals())
for media in s.media:
s.select_question("What do you think of this image?",
options=["Like", "Dislike"],
image=media)
get_media_values
get_media_values(column: str) -> list[str]
Get all values of the passed metadata column from the media collection.
This is useful if you want to get all the values of a column in the media collection and use them to create questions.
Example
from survey import Survey
s = Survey(**globals())
car_brands = s.get_media_values("brand")
for car in s.media:
s.select_question("What brand is this car?",
options=car_brands,
image=car)
Parameters
| Name | Type | Description |
|---|---|---|
column | str | the name of the media metadata column to read from each media item. The column must exist on each media item. |
get_image
get_image(**kwargs: Any = {}) -> MediaItem
Get the image with the specified values from the media collection.
Media lookup
Keyword arguments filter the media collection by metadata fields.
Single match
This returns the first matching image. If no image matches, the lookup fails.
Example
from survey import Survey
s = Survey(**globals())
ford_image = s.get_image(brand="Ford")
s.image_question("What do you think of this Ford car?", image=ford_image)
s.complete()
get_video
get_video(**kwargs: Any = {}) -> MediaItem
Get the video with the specified values from the media collection.
Media lookup
Keyword arguments filter the media collection by metadata fields.
Single match
This returns the first matching video. If no video matches, the lookup fails.
Example
from survey import Survey
s = Survey(**globals())
ford_video = s.get_video(brand="Ford")
s.play_video(video=ford_video)
s.complete()
get_images
get_images(**kwargs: Any = {}) -> list[MediaItem]
Get the images with the specified values from the media collection.
Media lookup
Keyword arguments filter the media collection by metadata fields.
Multiple matches
This returns all matching images.
Example
from survey import Survey
s = Survey(**globals())
for image in s.get_images(brand="Ford"):
s.image_question("What do you think of this Ford car?", image=image)
s.complete()
get_videos
get_videos(**kwargs: Any = {}) -> list[MediaItem]
Get the videos with the specified values from the media collection.
Media lookup
Keyword arguments filter the media collection by metadata fields.
Multiple matches
This returns all matching videos.
Example
from survey import Survey
s = Survey(**globals())
for video in s.get_videos(brand="Ford"):
s.play_video(video=video)
s.complete()
store_value
store_value(
name: str,
value: str | int | bool,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> StringResponse | BoolResponse | IntResponse | ListResponse
Store a calculated value for reporting.
Use this to add a value calculated from previous answers to the survey data. For survey logic, keep using normal Python variables instead of retrieving stored reporting values.
Reporting
Stored values are added to the survey as calculated variables and can be tagged.
Example
from datetime import datetime
from survey import Survey
s = Survey(**globals())
birth_year = s.numeric_question("What year were you born in?",
min_max=(1920, 2020),
)
age = datetime.now().year - birth_year
s.store_value("age", age)
s.terminate_if(age < 18, "Sorry this survey is for 18+ only.")
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name of the value to store |
value | str | int | bool | the value to store for reporting |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
random
random() -> float
Return a random float between 0 and 1.
Respondent-stable randomness
The value is generated from the respondent's random seed or respondent id, so it is stable for the same respondent context.
randomize
randomize(items: Sequence[Any], flatten: bool = False) -> list[Any]
Randomizes a list of items.
Typically used when iterating over a list of items that you want to ask questions about.
Grouped items
Nested lists stay together during top-level randomization, so you can randomize a set of options while keeping grouped options adjacent.
Example
from survey import Survey
s = Survey(**globals())
car_brands = s.randomize(["Ford", "Toyota", "Honda", "Tesla"])
for brand in s.randomize(car_brands):
s.select_question(
"What do you think of {brand} cars?",
options=["Like", "Dislike"],
tags=s.tag(brand=brand),
)
Example with flattening
from survey import Survey
s = Survey(**globals())
genres = [
"Jazz",
["Punk Rock", "Classic Rock", "Indie Rock"], # keep this group together
"Hip-Hop",
["House", "Techno"], # keep this group together
]
all_genres = s.randomize(genres, flatten=True)
for genre in all_genres:
s.select_question(
"What do you think of {genre} music?",
options=["Like", "Dislike", "Neutral"],
tags=s.tag(genre=genre),
)
Parameters
| Name | Type | Description |
|---|---|---|
items | Sequence[Any] | The sequence of items to randomize. |
flatten | bool | If True, flatten any nested lists after randomizing the top-level items. |
conjoint_question
conjoint_question(
question: str,
concepts: list[dict[str, Any]] | list[MediaItem],
task_sets: dict[str, list[list[str]]],
concept_id_tag: str = 'id',
required_tags: list[str] | None = None,
dont_know_option: str = '',
randomize: bool = False,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> DictResponse
Add a choice-based conjoint question to the survey.
Presents one task set as a series of select-style questions.
Task assignment
Each respondent is assigned one task set using least-fill quota behavior.
Dict-backed concepts
Dict-backed concepts use concept_id_tag as the option value and attach required_tags
to the selected response.
Media-backed concepts
Media-backed concepts use existing media option rendering, labels, and media tags.
Fixed don't know option
dont_know_option is added as a fixed non-concept option when provided.
Randomization
Set randomize=True to randomize option order within each conjoint task.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | The question to ask for each conjoint task. |
concepts | list[dict[str, Any]] | list[MediaItem] | Concept dictionaries or media items to use in tasks. |
task_sets | dict[str, list[list[str]]] | Mapping of task set id to tasks, where each task contains concept ids. |
concept_id_tag | str | Tag containing the concept id used by task sets. |
required_tags | list[str] | None | Concept tags to require and report with selected dict-backed concepts. |
dont_know_option | str | Optional fixed non-concept option label. |
randomize | bool | Whether to randomize option order within each task. |
image_label_field | str | None | Media field to use as the display label for media concepts. |
show_image_label | bool | Whether to show media labels. |
image_size | tuple[int, int] | None | Bounding box size for media options. |
tags | dict[str, Any] | None | Tags to attach to each conjoint task question. |
max_diff_question
max_diff_question(
question: str,
items: list[str | MediaItem] | list[list[str | MediaItem]],
labels: list[str],
image: MediaItem | None = None,
randomize: bool = False,
custom_validator: Callable[[str], str] | None = None,
dont_know_option: str = '',
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
tags: dict[str, Any] | None = None,
**_kwargs: Any = {},
) -> DictResponse
Add a MaxDiff question to the survey.
This presents the user with a series of sets of items, and asks the user to select the most and least preferred items in each set.
The items are presented in a random order, and the user is asked to select the most and least preferred items in each set. The items are presented in a random order, and the user is asked to select the most and least preferred items in each set.
Randomization
Set randomize=True to randomize item order within each MaxDiff set.
Using the don't know option
Use dont_know_option to add a fixed "Don't know" option to each MaxDiff task.
Example
from survey import Survey
s = Survey(**globals())
car_brands = ["Ford", "Toyota", "Honda", "Tesla", "Chevrolet", "BMW", "Audi", "Mercedes"]
s.max_diff_question("Which of the following car brands do you prefer?",
items=car_brands,
labels=["Least", "Most"])
Portrait orientation
If you want to use portrait orientation, then the questions are asked multiple times, once for each label, and you must include a placholder for the labels in the question text. For example, if you have a question "Which of the following cars do you prefer?", and you want to use portrait orientation, then include a placeholder for the label in the question text, i.e., "Which of the following cars do you prefer the {label}?"
from survey import Survey
s = Survey(**globals())
car_brands = ["Ford", "Toyota", "Honda", "Tesla", "Chevrolet", "BMW", "Audi", "Mercedes"]
s.max_diff_question("Which of the following cars do you prefer the **{label}**?",
items=car_brands,
labels=["Least", "Most"])
Default values
If no default value is specified, then the test data will consist of random values chosen from the options.
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
brand_cars = {
"Ford": ["Fiesta", "Focus", "Mustang", "F150"],
"Toyota": ["Corolla", "Camry", "Prius", "RAV4"],
"Honda": ["Civic", "Accord", "CR-V", "Pilot"],
"Tesla": ["Model S", "Model 3", "Model X", "Model Y"],
}
for brand in car_brands:
s.max_diff_question(
"Which of the following {brand} cars do you prefer?",
items=brand_cars[brand],
labels=["Least", "Most"],
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if "Sleepy Cows" in x else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
items | list[str | MediaItem] | list[list[str | MediaItem]] | items to compare, or predefined sets of items to compare |
labels | list[str] | the two labels to use for the most and least preferred items |
image | MediaItem | None | an image to show with the question, from the s.media collection |
randomize | bool | if set to True, the order of the items will be randomized |
custom_validator | Callable[[str], str] | None | function that returns a custom error message |
dont_know_option | str | label to use for the don't-know option, if any. |
image_label_field | str | None | label field to use for media items in the options. |
show_image_label | bool | whether to show image labels for media options. |
image_size | tuple[int, int] | None | bounding box size for media options. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tags(...). You can also attach tags using the with s.tag(...): context manager. |
count_validation_errors
count_validation_errors(question: str | None = None) -> int
Return the number of validation errors in the survey, or for a specific question.
Scope
If question is omitted, this counts all current respondent validation errors. If
question is provided, only errors for questions with that exact text are counted.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | None | If provided, only count validation errors for questions with this exact text. |
authenticate_by_phone_number
authenticate_by_phone_number(
question_1: str,
question_2: str = 'Please enter the code we sent you.',
termination_message: str = 'Sorry, the code you entered was incorrect',
store_phone_number: bool = False,
image: MediaItem | None = None,
default_country: str | None = None,
) -> StringResponse
Authenticate the respondent by phone number.
Asks the respondent for their phone number and sends a verification code.
OTP authentication
The respondent must enter the one-time password sent to their phone number before the survey continues.
Phone number storage
If store_phone_number=True, the response contains the phone number. Otherwise, the
stored response text is "obfuscated".
Response fields
The returned response always exposes the phone number as response.phone_number and the
one-time password as response.otp.
Example
from survey import Survey
s = Survey(**globals())
s.authenticate_by_phone_number("This is a high security survey, please enter your phone number" +
"to continue, and we will send you a verification code.")
Parameters
| Name | Type | Description |
|---|---|---|
question_1 | str | the question that asks the respondent to enter their phone number |
question_2 | str | the question that asks the respondent to enter the verification code |
termination_message | str | the message to show the respondent if the code is incorrect |
store_phone_number | bool | if set to True, the phone number will be stored in the survey data |
image | MediaItem | None | an image to show with the phone number question, from the s.media collection |
default_country | str | None | the default country code to use for the phone number |
call_api
call_api(
url: str,
method: HttpMethod,
data: dict | None = None,
params: dict | None = None,
headers: dict | None = None,
timeout: int = 10,
) -> StringResponse
Call an external HTTPS API.
Allowed domains
External HTTPS API calls are only available for pre-approved domains.
Request payloads
Use params for GET query parameters and data for POST request bodies.
Timeout and errors
A disallowed URL domain raises a validation error.
:param url: The URL of the request. :param method: HTTP method. Must be "GET" or "POST". :param data: Data to send for POST requests. :param params: Query parameters for GET requests. :param headers: HTTP headers for the request. :param timeout: Request timeout in seconds. :return: Response object from the HTTP request. :raises ValueError: If the URL domain is not allowed.
get_all_children_age_gender
get_all_children_age_gender(
min_age: int,
max_age: int,
age_recodes: dict[int, str] | None = None,
gender_map: dict[str, str] | None = None,
question_1_text: str | None = None,
question_2_text: str | None = None,
question_3_text: str | None = None,
) -> list[StringResponse]
Ask the user a series of questions to get the ages and genders of their children.
Returns a list of StringResponse objects, one for each child, with the following tags attached:
- age: the recoded age of the child
- gender: the gender of the child
- name: the name of the child to present to the respondent, e.g. 16 year old son.
Return shape and tags
The returned list contains one response per child, tagged with age, gender, and display name.
No children
If the user has no children, an empty list is returned.
Default question sequence
Unless custom question text is provided, this asks for child count, each child's age, and each child's gender.
Age recodes
age_recodes maps raw ages to reported age groups.
Gender mapping
gender_map maps gender responses to child terminology such as son or daughter.
The default questions are:
- "How many children do you have between the ages of {min_age} and {max_age}?"
- "For each child, please let us know their age"
- "Now, please let us know their gender
By default, genders are Male and Female and are mapped to son and daughter using the gender_map parameter.
Age recodes may be specified as a dictionary mapping the age to the recode value, e.g.
{
5: "Pre-school",
6: "Primary",
7: "Primary",
8: "Primary",
9: "Primary",
10: "Primary",
11: "Secondary",
12: "Secondary",
13: "Secondary",
14: "Secondary",
15: "Secondary",
16: "Secondary",
17: "Secondary",
}
Example
from survey import Survey
s = Survey(**globals())
children = s.get_all_children_age_gender(
min_age=5,
max_age=18,
)
for child in children:
s.text_question(
"Thinking about {child}, what is their favorite subject in school?",
tags=s.tag(child=child.name),
)
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
min_age | int | the minimum age of the children |
max_age | int | the maximum age of the children |
age_recodes | dict[int, str] | None | a dictionary to recode the ages |
gender_map | dict[str, str] | None | a dictionary to map genders to child terminology |
question_1_text | str | None | the text to use for the first question |
question_2_text | str | None | the text to use for the second question |
question_3_text | str | None | the text to use for the third question |
kid_picker_question
kid_picker_question(
min_age: int = 13,
max_age: int = 17,
age_recodes: dict[int, str] | None = None,
gender_map: dict[str, str] | None = None,
question_1_text: str | None = None,
question_2_text: str | None = None,
question_3_text: str | None = None,
question_4_text: str | None = None,
) -> StringResponse | None
Ask for child age and gender, then select the child available to take the survey.
This asks a series of questions to collect eligible children and then asks which child is available.
Returns the selected child as a StringResponse with age, gender, and name attributes.
Return shape and tags
The returned child can be used directly in survey logic and tags. The response value is the raw age
and gender, child.age is the recoded age if age_recodes is provided, child.gender is the raw
gender response, and child.name is the respondent-facing child label.
No children
If the user has no children, None is returned.
Default question sequence
Unless custom question text is provided, this asks for child count, age, gender, and which child is available to take the survey now.
Age recodes
age_recodes maps raw ages to reported age groups.
Gender mapping
gender_map maps gender responses to child terminology such as son or daughter.
The default questions are:
- "How many children do you have between the ages of {min_age} and {max_age}?"
- "For each child, please let us know their age"
- "Now, please let us know their gender"
- "Which, if any, of your children are available to take the survey now?"
By default, genders are Male and Female and the children are referred to as son and daughter using the default gender map:
{
"Male": "son",
"Female": "daughter"
}
Age recodes can be specified as a dictionary mapping the age to the recode value as in the get_all_children question.
Example
from survey import Survey
s = Survey(**globals())
child = s.kid_picker_question(
min_age=5,
max_age=18,
)
if child:
s.show_message(
"Please ask your {child} to take the survey now.",
child=child.name,
tags=s.tag(selected_child_age=child.age, selected_child_gender=child.gender),
)
else:
s.terminate("Sorry, this survey is for children only.")
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
min_age | int | the minimum age of the children |
max_age | int | the maximum age of the children |
age_recodes | dict[int, str] | None | a dictionary to recode the ages |
gender_map | dict[str, str] | None | a dictionary to map the genders to child terminology |
question_1_text | str | None | the text to use for the first question |
question_2_text | str | None | the text to use for the second question |
question_3_text | str | None | the text to use for the third question |
question_4_text | str | None | the text to use for the fourth question |
get_exposed_values
get_exposed_values(
source: str,
exposed_dimension: str,
allowed_values: list[str] | None = None,
number_allowed_unknown_values: int = 0,
) -> ListResponse
Return a list of exposure values for a configured dimension.
Looks up exposure values from a configured exposure source.
Pre-loaded exposures
Exposure data must be pre-loaded into MX8 for the configured source and
exposed_dimension.
Example
from survey import Survey
s = Survey(**globals())
products = ["SUV", "Sedan", "Coupe"]
exposed_products = s.get_exposed_values(
source="campaign-123",
exposed_dimension="product",
allowed_values=products,
number_allowed_unknown_values=1,
)
for product in products:
with s.tag(product=product,
exposed=product in exposed_products):
s.select_question(
"What do you think of {product} cars?",
options=["Like", "Dislike"],
)
# Remaining question about the product...
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
source | str | the source of the exposures, set with the server side integration, e.g. 'campaign-123' |
exposed_dimension | str | the exposure dimension to return, e.g. 'brand' or 'product' |
allowed_values | list[str] | None | an optional list of known values to filter the returned exposures by |
number_allowed_unknown_values | int | the number of values that are not in the allow-list that are allowed before the question is failed. |
tag_exposed_media
tag_exposed_media(
source: str,
field_name: str,
exposed_dimension: str = 'brand',
number_allowed_unknown_values: int | None = None,
number_allowed_unknown_brands: int | None = None,
) -> None
Tags media items in the survey as exposed based on IP address matching.
Tags media items by matching a media metadata field against the respondent's exposure values.
Pre-loaded exposures
Exposure data must be pre-loaded into MX8 for the configured source and
exposed_dimension.
IP matching
Exposure lookup is based on the current respondent's IP address.
Deprecated alias
number_allowed_unknown_brands is a deprecated alias for number_allowed_unknown_values.
Provide only one of these parameters.
Example
from survey import Survey
s = Survey(**globals())
s.tag_exposed_media(
source="campaign-123",
field_name="brand",
exposed_dimension="brand",
number_allowed_unknown_values=1,
)
for media in s.media:
s.select_question(
"What do you think of this car?",
options=["Like", "Dislike"],
image=media,
exposed=media.exposed,
)
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
source | str | the source of the exposures, set with the server side integration, e.g. 'campaign-123' |
field_name | str | the name of the field in the media collection to match against the exposures |
exposed_dimension | str | the exposure dimension to match against, defaults to 'brand' |
number_allowed_unknown_values | int | None | the number of values that are not in the allow-list that are allowed before the lookup is failed |
number_allowed_unknown_brands | int | None | deprecated alias for number_allowed_unknown_values |
insert_block
insert_block(name: str) -> None
Insert a block into the survey at the current position.
Blocks are defined in the MX8 dashboard and can be used to add custom questions to surveys without modifying the survey code.
Dashboard-defined blocks
The block name must refer to a block configured in the MX8 dashboard.
Missing blocks
If the named block is unavailable, block insertion fails during survey processing.
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name of the block |