Survey Rules and Flow Control
Rules define conditional navigation through surveys using Jinja2 template expressions.
Rule Types
| Type | When Evaluated | Purpose |
|---|---|---|
| Skip Rule | BEFORE question | Skip question entirely if condition is True |
| Navigation Rule | AFTER question | Jump to specific question based on answer |
| Stop Rule | AFTER question | End survey if condition is True |
Skip Rules (Before Rules)
Skip a question entirely based on previous answers:
python
from edsl import Survey, QuestionMultipleChoice, QuestionFreeText
q1 = QuestionMultipleChoice(
question_name="has_pet",
question_text="Do you have a pet?",
question_options=["Yes", "No"]
)
q2 = QuestionFreeText(
question_name="pet_name",
question_text="What is your pet's name?"
)
q3 = QuestionFreeText(
question_name="conclusion",
question_text="Any final thoughts?"
)
survey = Survey([q1, q2, q3])
# Skip pet_name if no pet
survey = survey.add_skip_rule(
"pet_name",
"{{ has_pet.answer }} == 'No'"
)
Navigation Rules (After Rules)
Jump to a different question based on the answer:
python
q1 = QuestionMultipleChoice(
question_name="preference",
question_text="Which do you prefer?",
question_options=["A", "B", "C"]
)
q2a = QuestionFreeText(question_name="section_a", question_text="Section A questions...")
q2b = QuestionFreeText(question_name="section_b", question_text="Section B questions...")
q2c = QuestionFreeText(question_name="section_c", question_text="Section C questions...")
q3 = QuestionFreeText(question_name="conclusion", question_text="Final question...")
survey = Survey([q1, q2a, q2b, q2c, q3])
# Add navigation rules (chain them)
survey = (survey
.add_rule("preference", "{{ preference.answer }} == 'A'", "section_a")
.add_rule("preference", "{{ preference.answer }} == 'B'", "section_b")
.add_rule("preference", "{{ preference.answer }} == 'C'", "section_c"))
# Also skip sections that shouldn't be shown
survey = (survey
.add_skip_rule("section_a", "{{ preference.answer }} != 'A'")
.add_skip_rule("section_b", "{{ preference.answer }} != 'B'")
.add_skip_rule("section_c", "{{ preference.answer }} != 'C'"))
Stop Rules (Early Termination)
End the survey when a condition is met:
python
q1 = QuestionMultipleChoice(
question_name="eligibility",
question_text="Are you 18 or older?",
question_options=["Yes", "No"]
)
survey = Survey([q1, q2, q3])
# End survey if not eligible
survey = survey.add_stop_rule(
"eligibility",
"{{ eligibility.answer }} == 'No'"
)
Expression Syntax
Expressions use Jinja2 template syntax:
python
# Reference answers
"{{ question_name.answer }} == 'value'"
"{{ age.answer }} > 18"
"{{ score.answer }} >= 50 and {{ score.answer }} <= 100"
# String comparisons
"{{ color.answer }} == 'Red'"
"{{ color.answer }} != 'Blue'"
"{{ color.answer }} in ['Red', 'Green', 'Blue']"
# Numeric comparisons
"{{ age.answer }} > 25"
"{{ age.answer }} >= 18 and {{ age.answer }} <= 65"
# Logical operators
"{{ q1.answer }} == 'yes' and {{ q2.answer }} != 'no'"
"{{ q1.answer }} == 'yes' or {{ q2.answer }} == 'yes'"
# Reference agent traits
"{{ agent.persona }} == 'expert'"
# Reference scenario parameters
"{{ scenario.condition }} == 'treatment'"
# Random functions
"randint(1, 10) > 5"
Rule Priority
When multiple rules apply, higher priority wins:
python
# Default rules have priority -1
# Custom rules default to priority 0
# High priority rule takes precedence
survey = survey.add_rule("q1", "{{ q1.answer }} == 'special'", "special_section", priority=1)
survey = survey.add_rule("q1", "{{ q1.answer }} != ''", "normal_section", priority=0)
EndOfSurvey Marker
Navigate directly to end of survey:
python
from edsl.surveys.navigation_markers import EndOfSurvey
# Jump to end on certain answer
survey = survey.add_rule("screening", "{{ screening.answer }} == 'disqualified'", EndOfSurvey)
View Rules
python
# Show all rules in the survey survey.show_rules() # Access rule collection rules = survey.rule_collection print(rules.non_default_rules)
Common Patterns
Branching Survey
python
survey = (Survey([q1, section_a, section_b, conclusion])
.add_rule("q1", "{{ q1.answer }} == 'A'", "section_a")
.add_rule("q1", "{{ q1.answer }} == 'B'", "section_b")
.add_skip_rule("section_a", "{{ q1.answer }} != 'A'")
.add_skip_rule("section_b", "{{ q1.answer }} != 'B'"))
Screening with Early Exit
python
survey = (Survey([screener, main_q1, main_q2, main_q3])
.add_stop_rule("screener", "{{ screener.answer }} == 'No'"))
Conditional Follow-up
python
survey = (Survey([main_q, followup, next_q])
.add_skip_rule("followup", "{{ main_q.answer }} != 'Yes'"))
Quick Reference
| Task | Method |
|---|---|
| Add skip rule | survey.add_skip_rule(question, expression) |
| Add navigation rule | survey.add_rule(question, expression, next_question) |
| Add stop rule | survey.add_stop_rule(question, expression) |
| Set priority | survey.add_rule(q, expr, next_q, priority=1) |
| View rules | survey.show_rules() |
| Jump to end | survey.add_rule(q, expr, EndOfSurvey) |