from otree.api import * import json from datetime import datetime, timezone import random import itertools import time from random import choice import re doc = """ """ def child_age_choices(): # 0 = under 1, 1..17, 18 = 18 or over return ( [(0, 'Under 1')] + [(a, str(a)) for a in range(1, 18)] + [(18, '18 or over')] ) class C(BaseConstants): NAME_IN_URL = 'health_survey' PLAYERS_PER_GROUP = None NUM_ROUNDS = 29 AB_CHOICES = [ ['A', 'A'], ['B', 'B'], ] class Subsession(BaseSubsession): pass def creating_session(subsession): # treatments for p in subsession.get_players(): p.Prolific_ID = p.participant.label if p.id_in_subsession: pressures = itertools.cycle([1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4]) for player in subsession.get_players(): player.code_treatment = next(pressures) if p.code_treatment == 1: p.treatment = "yc" elif p.code_treatment == 2: p.treatment = "yu" elif p.code_treatment == 3: p.treatment = "oc" elif p.code_treatment == 4: p.treatment = "ou" for p in subsession.get_players(): if p.id_in_subsession: pressures2 = itertools.cycle([1, 2, 3, 4 ]) for player in subsession.get_players(): player.code_treatment2 = next(pressures2) if player.code_treatment2 == 1: player.treatment2 = "base" elif player.code_treatment2 == 2: player.treatment2 = "base" elif player.code_treatment2 == 3: player.treatment2 = "base" elif player.code_treatment2 == 4: player.treatment2 = "base" for p in subsession.get_players(): if p.id_in_subsession: pressures3 = itertools.cycle([1, 2, 3, 4, ]) for player in subsession.get_players(): player.codeorder = next(pressures3) if player.codeorder == 1: player.order2 = 1 elif player.codeorder == 2: player.order2 = 2 elif player.codeorder == 3: player.order2 = 3 elif player.codeorder == 4: player.order2 = 4 class Group(BaseGroup): pass class Player(BasePlayer): code_treatment = models.IntegerField() treatment = models.StringField() code_treatment2 = models.IntegerField() treatment2 = models.StringField() codeorder = models.IntegerField() order2 = models.IntegerField() # Checkbox (tick) items consent_read = models.BooleanField( label="", widget=widgets.CheckboxInput ) consent_voluntary = models.BooleanField( label="", widget=widgets.CheckboxInput ) consent_no_financial_benefit = models.BooleanField( label="", widget=widgets.CheckboxInput ) consent_take_part = models.BooleanField( label="", widget=widgets.CheckboxInput ) # Yes/No items (radio buttons) consent_future_research = models.StringField( label="", choices=[["Yes", "Yes"], ["No", "No"]], widget=widgets.RadioSelect ) consent_future_prolific = models.StringField( label="", choices=[["Yes", "Yes"], ["No", "No"]], widget=widgets.RadioSelect ) Prolific_ID = models.StringField( label="Prolific ID", ) commitment = models.StringField( choices=[ ['1', 'Yes'], ['2', 'No'], ], label="We care about the quality of our data. In order to obtain the most accurate measures of your knowledge and opinions, it is important that you thoughtfully provide your best answers to each question in this survey. Do you commit to thoughtfully providing your best answers to each question in this survey?", widget=widgets.RadioSelect ) # Postcode (first part) postcode_first_part = models.StringField( label='', blank=False ) # Gender gender = models.StringField( label='', choices=[ ['1', 'Male'], ['2', 'Female'], ['3', 'Prefer not to say'], ], widget=widgets.RadioSelect, blank=False ) # Age categories (keys exactly as you provided) birth_year = models.IntegerField( label='', min=1900, max=2026, ) # Ethnicity ethnicity = models.StringField( label='', choices=[ ['African', 'African'], ['African-American/Black', 'African-American/Black'], ['Caucasian/White', 'Caucasian/White (Eg: Australian, German, Greek, English, Italian, Spanish, Polish, Romanian, etc.)'], ['East Asian', 'East Asian (Eg: Chinese, Vietnamese, Japanese, Korean, Mongolian, Tibetan and Taiwanese)'], ['Hispanic, Latino or Spanish origin', 'Hispanic, Latino or Spanish origin (Eg: Mexican or Mexican American, Cuban, Colombian, Argentinian, etc.)'], ['Middle Eastern or North African', 'Middle Eastern or North African (Eg: Lebanese, Iranian, Egyptian, Syrian, Moroccan, Algerian, Saudi Arabia, etc.)'], ['South Asian', 'South Asian (e.g. Bangladeshi, Bhutanese, Indian, Nepali, Pakistani and Sri Lankan)'], ['Southeast Asian', 'Southeast Asian (e.g. Burmese, Cambodian, Filipino, Hmong, Indonesian, Laotian, Malaysian, Mien, Singaporean, Thai and Vietnamese)'], ['Other', 'Other'], ], widget=widgets.RadioSelect, blank=False ) # Highest school leaving certificate / degree education = models.StringField( label='', choices=[ ['No qualification', 'No qualification'], ['Nursery to 8th Grade', 'Nursery to 8th Grade'], ['Year 12 (or equivalent) or lower', 'Year 12 (or equivalent) or lower'], ['High school graduate, diploma (or equivalent)', 'High school graduate, diploma (or equivalent)'], ["Bachelor's degree", "Bachelor's degree"], ["Master's degree", "Master's degree (including professional degrees, or equivalent)"], ['Doctorate degree', 'Doctorate degree'], ['Other', 'Other'] ], widget=widgets.RadioSelect, blank=False ) # Marital status + other specify marital_status = models.StringField( label='', choices=[ ['Single and never married or never in a Civil Partnership', 'Single and never married or never in a Civil Partnership'], ['Married/in a Civil Partnership', 'Married/in a Civil Partnership'], ['Separated but legally married/Separated from Civil Partner', 'Separated but legally married/Separated from Civil Partner'], ['Divorced/Civil Partnership legally dissolved', 'Divorced/Civil Partnership legally dissolved'], ['Widowed/Surviving Civil Partner', 'Widowed/Surviving Civil Partner'], ['Cohabitate with a partner but do not fall on the options listed above', 'Cohabitate with a partner but do not fall on the options listed above'], ['Other', 'Other (please specify):'], ], widget=widgets.RadioSelect, blank=False ) marital_other = models.StringField( label='', blank=True ) vuln_65plus = models.BooleanField( label='You are 65 years old or over', blank=True ) vuln_live_with_65plus = models.BooleanField( label='You live with a person(s) who is 65 years old or over', blank=True ) vuln_health_condition = models.BooleanField( label='You have a chronic health condition (chronic long-term respiratory disease, chronic kidney disease, chronic liver disease, chronic neurological condition, spleen issues, condition which causes a weakened immune system, cancer, being severely overweight, diabetes, high or low blood pressure, etc.)', blank=True ) vuln_children = models.BooleanField( label='You have children', blank=True ) vuln_pregnant_now = models.BooleanField( label='You are/your partner is currently pregnant', blank=True ) vuln_pregnant_last_autumn_winter = models.BooleanField( label='You were/your partner was pregnant last autumn/winter', blank=True ) vuln_informal_carer = models.BooleanField( label='You informally (i.e. not for pay) care for someone with an illness or disability', blank=True ) vuln_care_facility = models.BooleanField( label='You are living in a long stay care facility', blank=True ) vuln_none = models.BooleanField( label='I do not belong to any of these groups', blank=True ) # Number of children: 1..10 plus "More than 10" as (15) children = models.IntegerField( label='', choices=[ [0, '0'], [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7'], [8, '8'], [9, '9'], [10, '10'], [15, 'More than 10'], ], blank=False ) child_age_1 = models.IntegerField(label='Child #1', choices=child_age_choices(), blank=True) child_age_2 = models.IntegerField(label='Child #2', choices=child_age_choices(), blank=True) child_age_3 = models.IntegerField(label='Child #3', choices=child_age_choices(), blank=True) child_age_4 = models.IntegerField(label='Child #4', choices=child_age_choices(), blank=True) child_age_5 = models.IntegerField(label='Child #5', choices=child_age_choices(), blank=True) child_age_6 = models.IntegerField(label='Child #6', choices=child_age_choices(), blank=True) child_age_7 = models.IntegerField(label='Child #7', choices=child_age_choices(), blank=True) child_age_8 = models.IntegerField(label='Child #8', choices=child_age_choices(), blank=True) child_age_9 = models.IntegerField(label='Child #9', choices=child_age_choices(), blank=True) child_age_10 = models.IntegerField(label='Child #10', choices=child_age_choices(), blank=True) child_underlying = models.BooleanField(label='Has an underlying health condition', blank=True) child_vax = models.BooleanField(label='Got the flu vaccine last flu season (October to May)', blank=True) child_flu = models.BooleanField(label='Caught the flu last flu season', blank=True) child_none = models.BooleanField(label='None of these apply to my child', blank=True) # Current situation + other specify age18 = models.IntegerField(label='People aged under 18 years old',min=0, max=100) age1864 = models.IntegerField(label='People aged 18 to 64 years old',min=0, max=100) age65 = models.IntegerField(label='People aged 65 years old and older',min=0, max=100) employment_status = models.StringField( label='', choices=[ ['Employed full-time', 'Employed full-time'], ['Employed part-time', 'Employed part-time'], ['Self-employed', 'Self-employed'], ['Unemployed and looking for a job', 'Unemployed and looking for a job'], ['Unemployed but not looking for a job', 'Unemployed but not looking for a job'], ['Retired', 'Retired'], ['Student', 'Student'], ['Other', 'Other (please specify):'], ], widget=widgets.RadioSelect, blank=False ) employment_other = models.StringField( label='', blank=True ) industry_sic_section = models.StringField( label='', choices=[ ['1', 'Agriculture, forestry and fishing'], ['2', 'Mining and quarrying'], ['3', 'Manufacturing'], ['4', 'Electricity, gas, air cond supply'], ['5', 'Water supply, sewerage, waste'], ['6', 'Construction'], ['7', 'Wholesale, retail, repair of vehicles'], ['8', 'Transport and storage'], ['9', 'Accommodation and food services'], ['10', 'Information and communication'], ['11', 'Financial and insurance activities'], ['12', 'Real estate activities'], ['13', 'Prof, scientific, technical activities'], ['14', 'Admin and support services'], ['15', 'Public admin and defence'], ['16', 'Education'], ['17', 'Health and social work'], ['18', 'Arts, entertainment and recreation'], ['19', 'Other service activities'], ['20', 'Households as employers'], ['21', 'Extraterritorial organisations'], ['22', "Don't know"], ['23', 'Prefer not to say'], ], ) # Income band (7 options) - replace options 2-6 with your exact wording income_band = models.IntegerField( label='', choices=[ [1, 'Less than £10,000'], [2, '£10,000–£19,999'], [3, '£20,000–£29,999'], [4, '£30,000–£39,999'], [5, '£40,000–£49,999'], [6, '£50,000–£59,999'], [7, '£60,000–£69,999'], [8, '£70,000–£79,999'], [9, '£80,000–£89,999'], [10, '£90,000–£99,999'], [11, '£100,000 or more'], [12, 'Prefer not to answer']], blank=False ) income_welfare = models.StringField( label='', choices=[ ['1', 'Finding it very difficult on present income '], ['2', 'Finding it difficult on present income '], ['3', 'Coping on present income '], ['4', 'Living comfortably on present income '], ['Other', 'Other (please specify):'], ], widget=widgets.RadioSelect, blank=False ) income_other = models.StringField( label='', blank=True ) political = models.IntegerField( label='', choices=[ [0, 'Left'], [1, ''], [2, ''], [3, ''], [4, ''], [5, ''], [6, ''], [7, ''], [8, ''], [9, ''], [10, 'Right'], ], blank=False, widget=widgets.RadioSelectHorizontal, ) party_closeness = models.StringField( label='', choices=[ ['Conservative', 'Conservative'], ['Labour', 'Labour'], ['Liberal Democrat', 'Liberal Democrat'], ['United Kingdom Independence Party (UKIP)', 'United Kingdom Independence Party (UKIP)'], ['Green Party', 'Green Party'], ['The Independent Group', 'The Independent Group'], ['Other', 'Other (please specify):'], ['None', 'No – None'], ['Don’t know', "Don’t know"], ], widget=widgets.RadioSelect, blank=False ) party_closeness_other = models.StringField( label='', blank=True ) party_like_conservative = models.IntegerField( label='', choices=[ [0, 'Strongly dislike'], [1, ''], [2, ''], [3, ''], [4, ''], [5, ''], [6, ''], [7, ''], [8, ''], [9, ''], [10, 'Strongly like'], [11, "Don't know"] ], blank=False, widget=widgets.RadioSelectHorizontal, ) party_like_labour = models.IntegerField( label='', choices=[ [0, 'Strongly dislike'], [1, ''], [2, ''], [3, ''], [4, ''], [5, ''], [6, ''], [7, ''], [8, ''], [9, ''], [10, 'Strongly like'], [11, "Don't know"] ], blank=False, widget=widgets.RadioSelectHorizontal, ) party_like_libdem = models.IntegerField( label='', choices=[ [0, 'Strongly dislike'], [1, ''], [2, ''], [3, ''], [4, ''], [5, ''], [6, ''], [7, ''], [8, ''], [9, ''], [10, 'Strongly like'], [11, "Don't know"] ], blank=False, widget=widgets.RadioSelectHorizontal, ) # Health slider 0..100 step 10 health_general = models.IntegerField( label='', choices=[ [1, "Very good"], [2, "Good"], [3, "Fair"], [4, "Bad"], [5, "Very bad"], ], widget=widgets.RadioSelectHorizontal, blank=False ) # Prescription medication prescription_medication = models.StringField( label='', choices=[ ['Yes', 'Yes'], ['No', 'No'], ], widget=widgets.RadioSelect, blank=False ) confident_catch = models.StringField( label='', choices=[ ['Very confident', 'Very confident'], ['Fairly confident', 'Fairly confident'], ['Unsure', 'Unsure'], ['Not very confident', 'Not very confident'], ['Not at all confident', 'Not at all confident'], ], widget=widgets.RadioSelectHorizontal, blank=False ) confident_die = models.StringField( label='', choices=[ ['Very confident', 'Very confident'], ['Fairly confident', 'Fairly confident'], ['Unsure', 'Unsure'], ['Not very confident', 'Not very confident'], ['Not at all confident', 'Not at all confident'], ], widget=widgets.RadioSelectHorizontal, blank=False ) confident_avoid = models.StringField( label='', choices=[ ['Very confident', 'Very confident'], ['Fairly confident', 'Fairly confident'], ['Unsure', 'Unsure'], ['Not very confident', 'Not very confident'], ['Not at all confident', 'Not at all confident'], ], widget=widgets.RadioSelectHorizontal, blank=False ) # Q1: Catch virus # ------------------------- catch_likely = models.IntegerField(min=0, max=100, initial=0) catch_moved = models.BooleanField(initial=False) catch_t0 = models.IntegerField(min=0, max=100, initial=0) catch_t1 = models.IntegerField(min=0, max=100, initial=0) catch_t2 = models.IntegerField(min=0, max=100, initial=0) catch_t3 = models.IntegerField(min=0, max=100, initial=0) catch_t4 = models.IntegerField(min=0, max=100, initial=0) catch_t5 = models.IntegerField(min=0, max=100, initial=0) catch_t6 = models.IntegerField(min=0, max=100, initial=0) catch_t7 = models.IntegerField(min=0, max=100, initial=0) catch_t8 = models.IntegerField(min=0, max=100, initial=0) catch_t9 = models.IntegerField(min=0, max=100, initial=0) # ------------------------- # Q2: Die if infected # ------------------------- die_likely = models.IntegerField(min=0, max=100, initial=0) die_moved = models.BooleanField(initial=False) die_t0 = models.IntegerField(min=0, max=100, initial=0) die_t1 = models.IntegerField(min=0, max=100, initial=0) die_t2 = models.IntegerField(min=0, max=100, initial=0) die_t3 = models.IntegerField(min=0, max=100, initial=0) die_t4 = models.IntegerField(min=0, max=100, initial=0) die_t5 = models.IntegerField(min=0, max=100, initial=0) die_t6 = models.IntegerField(min=0, max=100, initial=0) die_t7 = models.IntegerField(min=0, max=100, initial=0) die_t8 = models.IntegerField(min=0, max=100, initial=0) die_t9 = models.IntegerField(min=0, max=100, initial=0) # ------------------------- # Q3: Avoid activities # ------------------------- avoid_likely = models.IntegerField(min=0, max=100, initial=0) avoid_moved = models.BooleanField(initial=False) avoid_t0 = models.IntegerField(min=0, max=100, initial=0) avoid_t1 = models.IntegerField(min=0, max=100, initial=0) avoid_t2 = models.IntegerField(min=0, max=100, initial=0) avoid_t3 = models.IntegerField(min=0, max=100, initial=0) avoid_t4 = models.IntegerField(min=0, max=100, initial=0) avoid_t5 = models.IntegerField(min=0, max=100, initial=0) avoid_t6 = models.IntegerField(min=0, max=100, initial=0) avoid_t7 = models.IntegerField(min=0, max=100, initial=0) avoid_t8 = models.IntegerField(min=0, max=100, initial=0) avoid_t9 = models.IntegerField(min=0, max=100, initial=0) # -------- Block C: Risk preferences (7 decisions) -------- risk_1 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) risk_2 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) risk_3 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) risk_4 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) risk_5 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) risk_6 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) risk_7 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) # -------- Block D: Time preferences (7 decisions) -------- time_1 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) time_2 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) time_3 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) time_4 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) time_5 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) time_6 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) time_7 = models.StringField(choices=C.AB_CHOICES, widget=widgets.RadioSelectHorizontal) # --- sliders: 0-100 --- trust_health_professional = models.IntegerField(min=0, max=100, label='') trust_family = models.IntegerField(min=0, max=100, label='') trust_friends = models.IntegerField(min=0, max=100, label='') trust_internet = models.IntegerField(min=0, max=100, label='') trust_media = models.IntegerField(min=0, max=100, label='') trust_AI = models.IntegerField(min=0, max=100, label='') trust_authority = models.IntegerField(min=0, max=100, label='') trust_local_government = models.IntegerField(min=0, max=100, label='') trust_national_government = models.IntegerField(min=0, max=100, label='') trust_prime_minister = models.IntegerField(min=0, max=100, label='') # --- moved flags (must interact with each slider) --- trust_health_professional_moved = models.BooleanField(initial=False) trust_family_moved = models.BooleanField(initial=False) trust_friends_moved = models.BooleanField(initial=False) trust_internet_moved = models.BooleanField(initial=False) trust_media_moved = models.BooleanField(initial=False) trust_AI_moved = models.BooleanField(initial=False) trust_authority_moved = models.BooleanField(initial=False) trust_local_government_moved = models.BooleanField(initial=False) trust_national_government_moved = models.BooleanField(initial=False) trust_prime_minister_moved = models.BooleanField(initial=False) # --- follow-ups (1-4) --- flu_eligible = models.IntegerField( label='', choices=[ [1, 'Yes'], [2, 'No'], [3, "I don't know"] ], widget=widgets.RadioSelect ) flu_vac = models.IntegerField( label='', choices=[ [1, 'Yes'], [2, 'No'], ], widget=widgets.RadioSelect ) covid_status = models.IntegerField( label='', choices=[ [1, 'Yes'], [2, 'No'], ], widget=widgets.RadioSelect ) covid_vaxx_status = models.IntegerField( label='', choices=[ [1, 'I have never got any COVID-19 vaccine'], [2, 'I have got a single COVID-19 vaccine, but was not fully vaccinated'], [3, 'I was fully vaccinated with either single-shot COVID-19 vaccine or with two shots'], [4, 'I was fully vaccinated and got booster COVID-19 vaccine(s)'], ], widget=widgets.RadioSelect ) covid_shots = models.IntegerField( label='', choices=[ [0, '0'], [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5 or more'], ], widget=widgets.RadioSelect ) contracted_covid = models.IntegerField( label='', choices=[ [1, 'Yes, and I (they) tested positive'], [2, 'Yes, and I (they) tested negative'], [3, 'Yes, and I (they) have not received my (their) results yet'], [4, 'No'], ], widget=widgets.RadioSelect ) flu_reason_trust = models.BooleanField(blank=True) flu_reason_self = models.BooleanField(blank=True) flu_reason_children = models.BooleanField(blank=True) flu_reason_family_friends = models.BooleanField(blank=True) flu_reason_beyond = models.BooleanField(blank=True) flu_reason_fear_symptoms = models.BooleanField(blank=True) flu_reason_at_risk = models.BooleanField(blank=True) flu_reason_employer_req = models.BooleanField(blank=True) flu_reason_workplace_offered = models.BooleanField(blank=True) flu_reason_hcp = models.BooleanField(blank=True) flu_reason_family_friend = models.BooleanField(blank=True) flu_reason_social_norm = models.BooleanField(blank=True) flu_reason_other = models.BooleanField(blank=True) flu_reason_other_text = models.StringField(label ='', blank=True) novax_reason_distrust = models.BooleanField(blank=True) novax_reason_not_at_risk = models.BooleanField(blank=True) novax_reason_mild_symptoms = models.BooleanField(blank=True) novax_reason_didnt_know = models.BooleanField(blank=True) novax_reason_too_expensive = models.BooleanField(blank=True) novax_reason_no_time = models.BooleanField(blank=True) novax_reason_forgot = models.BooleanField(blank=True) novax_reason_not_available = models.BooleanField(blank=True) novax_reason_scared_needles = models.BooleanField(blank=True) novax_reason_underlying_shouldnt = models.BooleanField(blank=True) novax_reason_scared_side_effects = models.BooleanField(blank=True) novax_reason_negative_experience = models.BooleanField(blank=True) novax_reason_not_recommended = models.BooleanField(blank=True) novax_reason_social_norm_no = models.BooleanField(blank=True) novax_reason_other = models.BooleanField(blank=True) novax_reason_other_text = models.StringField(label = '',blank=True) covid_reason_trust = models.BooleanField(blank=True) covid_reason_self = models.BooleanField(blank=True) covid_reason_children = models.BooleanField(blank=True) covid_reason_family_friends = models.BooleanField(blank=True) covid_reason_beyond = models.BooleanField(blank=True) covid_reason_fear_symptoms = models.BooleanField(blank=True) covid_reason_at_risk = models.BooleanField(blank=True) covid_reason_employer_req = models.BooleanField(blank=True) covid_reason_workplace_offered = models.BooleanField(blank=True) covid_reason_hcp = models.BooleanField(blank=True) covid_reason_family_friend = models.BooleanField(blank=True) covid_reason_social_norm = models.BooleanField(blank=True) covid_reason_other = models.BooleanField(blank=True) covid_reason_other_text = models.StringField(blank=True) covid_novax_reason_distrust = models.BooleanField(blank=True) covid_novax_reason_not_at_risk = models.BooleanField(blank=True) covid_novax_reason_mild_symptoms = models.BooleanField(blank=True) covid_novax_reason_didnt_know = models.BooleanField(blank=True) covid_novax_reason_too_expensive = models.BooleanField(blank=True) covid_novax_reason_no_time = models.BooleanField(blank=True) covid_novax_reason_forgot = models.BooleanField(blank=True) covid_novax_reason_not_available = models.BooleanField(blank=True) covid_novax_reason_scared_needles = models.BooleanField(blank=True) covid_novax_reason_underlying_shouldnt = models.BooleanField(blank=True) covid_novax_reason_scared_side_effects = models.BooleanField(blank=True) covid_novax_reason_negative_experience = models.BooleanField(blank=True) covid_novax_reason_not_recommended = models.BooleanField(blank=True) covid_novax_reason_social_norm_no = models.BooleanField(blank=True) covid_novax_reason_other = models.BooleanField(blank=True) covid_novax_reason_other_text = models.StringField(blank=True) gov_handling = models.IntegerField( label='How well or badly do you think the Government handled the issue of the Coronavirus?', choices=[ [1, 'Very badly'], [2, 'Badly'], [3, 'Well'], [4, 'Very well'], ], widget=widgets.RadioSelectHorizontal ) nhs_confidence = models.IntegerField( label='How much confidence did you have in the NHS to respond to the Coronavirus outbreak in the UK?', choices=[ [1, 'No confidence at all'], [2, 'Not much confidence'], [3, 'Some confidence'], [4, 'A lot of confidence'], ], widget=widgets.RadioSelectHorizontal ) vaccine_trust = models.IntegerField( label='How much do you trust COVID-19 vaccines?', choices=[ [1, 'Not at all'], [2, 'Not much'], [3, 'Somewhat'], [4, 'Very much'], ], widget=widgets.RadioSelectHorizontal ) # Q1 stored as JSON string like ["green","red"] cc_possible_colors = models.StringField( choices=[ ['1', 'red and blue'], ['2', 'green and blue'], ['3', 'red and green'], ['4', 'yellow and green'], ], label = '', widget=widgets.RadioSelect ) cc_start_color = models.StringField( choices=[ ['1', 'purple'], ['2', 'green'], ['3', 'red'], ['4', 'orange'],], widget=widgets.RadioSelect, label='', blank=True ) cc_total_players = models.IntegerField(label = '', blank=True) cc_bonus_who = models.StringField( choices=[ ['1', 'All players'], ['2', 'All green players'], ['3', 'All red players'], ['4', 'Only some of the blue players'], ['5', 'Bonus money will be distributed at random'], ], widget=widgets.RadioSelect, label='', blank=True ) cc_start_green = models.IntegerField(label = '', blank=True) cc_action = models.StringField( choices=[ ['1', '0'], ['2', '1'], ['3', '2'], ['4', '3'], ['5', '4'], ['6', '99'], ['7', '100'], ['8', 'It depends on the round'], ], widget=widgets.RadioSelect, label='', blank=True ) cc_know_color = models.StringField( choices=[ ['1', 'Yes'], ['2', 'No'], ['3', 'Sometimes'], ], widget=widgets.RadioSelect, label='', blank=True ) cc_redtogreen = models.StringField( choices=[ ['1', 'This is not possible (0%). I cannot change back into green.'], ['2', '5%'], ['3', '15%'], ['4', '25%'], ['5', 'Always (100%)'], ], widget=widgets.RadioSelect, label='', blank=True ) cc_greentoredhh = models.StringField( choices=[ ['1', 'This is not possible (0%)'], ['2', '5%'], ['3', '15%'], ['4', '25%'], ['5', 'Always (100%)'], ], widget=widgets.RadioSelect, label='', blank=True ) cc_greentoredhg = models.StringField( choices=[ ['1', 'This is not possible (0%)'], ['2', '5%'], ['3', '15%'], ['4', '25%'], ['5', 'Always (100%)'], ], widget=widgets.RadioSelect, label='', blank=True ) cc_greentoredbothgreen = models.StringField( choices=[ ['1', 'This is not possible (0%)'], ['2', '5%'], ['3', '15%'], ['4', '25%'], ['5', 'Always (100%)'], ], widget=widgets.RadioSelect, label='', blank=True ) cc_attempt_count = models.IntegerField(initial=0) cc_attempt_count2 = models.IntegerField(initial=0) cc_attempt_count3 = models.IntegerField(initial=0) choice = models.StringField( choices=[ ['G', 'Action G: I receive 8 points'], ['H', 'Action H: I receive 40 points'], ], widget=widgets.RadioSelect, label='', ) belief = models.IntegerField( label='', min = 0, max = 100 ) points_gained_so_far = models.IntegerField(initial=0) possible_bonus = models.FloatField(initial=0) round_points = models.IntegerField(initial=0) total_points = models.IntegerField(initial=0) total_bonus = models.FloatField(initial=0) def commitment_error_message(player,value): correct_answer = '1' if value != correct_answer: error_message = "If you do not agree, please close your browser tab." return error_message def postcode_first_part_error_message(player, value): s = (value or "").strip().upper() # Validate outward code: A9, A9A, A99, AA9, AA9A, AA99 if not re.match(r"^[A-Z]{1,2}\d{1,2}[A-Z]?$", s): return "Please enter the first part of your postcode (Eg: HD6, WF12)." # Standardise stored value (optional but useful) player.postcode_first_part = s class Consent(Page): form_model = 'player' form_fields = [ 'consent_read', 'consent_voluntary', 'consent_future_research', 'consent_no_financial_benefit', 'consent_take_part', 'consent_future_prolific', ] def vars_for_template(player): return dict( info_sheet_version="01", info_sheet_date="02 February", ) def is_displayed(player): return player.round_number == 1 class ProlificID(Page): form_model = 'player' form_fields = ['Prolific_ID'] def is_displayed(player): return player.round_number == 1 class Commitment(Page): form_model = 'player' form_fields = ['commitment'] def is_displayed(player): return player.round_number == 1 class Demo(Page): form_model = 'player' form_fields = [ 'postcode_first_part', 'gender', 'birth_year', 'ethnicity', 'education', 'marital_status', 'marital_other', 'vuln_65plus', 'vuln_live_with_65plus', 'vuln_health_condition', 'vuln_children', 'vuln_pregnant_now', 'vuln_pregnant_last_autumn_winter', 'vuln_informal_carer', 'vuln_care_facility', 'vuln_none', 'children', 'child_age_1', 'child_age_2', 'child_age_3', 'child_age_4', 'child_age_5', 'child_age_6', 'child_age_7', 'child_age_8', 'child_age_9', 'child_age_10', 'child_underlying', 'child_vax', 'child_flu', 'child_none', 'age18', 'age1864', 'age65', 'employment_status', 'employment_other', 'industry_sic_section', 'income_band', 'income_welfare', 'income_other', 'political', 'party_closeness', 'party_closeness_other', 'party_like_conservative', 'party_like_labour', 'party_like_libdem', 'health_general', 'prescription_medication', ] def error_message(player, values): if values.get('vuln_none') and ( values.get('vuln_65plus') or values.get('vuln_live_with_65plus') or values.get('vuln_health_condition') or values.get('vuln_children') or values.get('vuln_pregnant_now') or values.get('vuln_pregnant_last_autumn_winter') or values.get('vuln_informal_carer') or values.get('vuln_care_facility') ): return 'Please select either "I do not belong to any of these groups" OR the other options, not both.' def error_message(player, values): if values.get('child_none') and (values.get('child_underlying') or values.get('child_vax') or values.get('child_flu')): return 'Please select either "None of these apply to my child" OR the other options, not both.' def error_message(player, values): if values.get('marital_status') == 'Other' and not (values.get('marital_other') or '').strip(): return "Please specify your marital status in the 'Other' text box." if values.get('employment_status') == 'Other' and not (values.get('employment_other') or '').strip(): return "Please specify your current situation in the 'Other' text box." if values.get('income_welfare') == 'Other' and not (values.get('income_other') or '').strip(): return "Please specify your feeling in the 'Other' text box." if values.get('party_closeness') == 'Other' and not (values.get('party_closeness_other') or '').strip(): return "Please specify the party in the 'Other' text box." def is_displayed(player): return player.round_number == 1 class TrustSources(Page): form_model = 'player' form_fields = ['commitment'] slider_items = [ dict(field='trust_prime_minister', moved='trust_prime_minister_moved', label='The Prime Minister'), dict(field='trust_health_professional', moved='trust_health_professional_moved', label='Health professional (i.e. General Practitioner, doctors, nurses, etc)'), dict(field='trust_family', moved='trust_family_moved', label='Family'), dict(field='trust_friends', moved='trust_friends_moved', label='Friends'), dict(field='trust_internet', moved='trust_internet_moved', label='Internet (i.e. social media, websites, blogs, chat rooms, apps, etc.)'), dict(field='trust_media', moved='trust_media_moved', label='The Media'), dict(field='trust_AI', moved='trust_AI_moved', label='AI tools (i.e. ChatGPT, AI chatbots, AI search tools, etc.)'), dict(field='trust_authority', moved='trust_authority_moved', label='Public health authorities (i.e. NHS, UKHSA, etc.)'), dict(field='trust_local_government', moved='trust_local_government_moved', label='Local governments'), dict(field='trust_national_government', moved='trust_national_government_moved', label='National governments') ] form_fields = ( [x['field'] for x in slider_items] + [x['moved'] for x in slider_items] ) @staticmethod def vars_for_template(player): return dict(slider_items=TrustSources.slider_items) @staticmethod def error_message(player, values): # Require interaction with each slider (not just leaving default) missing = [] for x in TrustSources.slider_items: if not values.get(x['moved']): missing.append(x['field']) if missing: # Put a single page-level error. (You can customize wording.) return "Please move each slider at least once before continuing." def is_displayed(player): return player.round_number == 1 class Covid(Page): form_model = 'player' form_fields = [ 'flu_eligible', 'flu_vac', 'covid_vaxx_status', 'covid_shots', 'contracted_covid', 'gov_handling', 'nhs_confidence', 'vaccine_trust' ] def is_displayed(player): return player.round_number == 1 class Covid2(Page): form_model = 'player' def get_form_fields(player): fields = [] if player.flu_vac == 1: fields += [ 'flu_reason_trust', 'flu_reason_self', 'flu_reason_children', 'flu_reason_family_friends', 'flu_reason_beyond', 'flu_reason_fear_symptoms', 'flu_reason_at_risk', 'flu_reason_employer_req', 'flu_reason_workplace_offered', 'flu_reason_hcp', 'flu_reason_family_friend', 'flu_reason_social_norm', 'flu_reason_other', 'flu_reason_other_text', ] elif player.flu_vac == 2: fields += [ 'novax_reason_distrust', 'novax_reason_not_at_risk', 'novax_reason_mild_symptoms', 'novax_reason_didnt_know', 'novax_reason_too_expensive', 'novax_reason_no_time', 'novax_reason_forgot', 'novax_reason_not_available', 'novax_reason_scared_needles', 'novax_reason_underlying_shouldnt', 'novax_reason_scared_side_effects', 'novax_reason_negative_experience', 'novax_reason_not_recommended', 'novax_reason_social_norm_no', 'novax_reason_other', 'novax_reason_other_text', ] if player.covid_shots and player.covid_shots >= 1: fields += [ 'covid_reason_trust', 'covid_reason_self', 'covid_reason_children', 'covid_reason_family_friends', 'covid_reason_beyond', 'covid_reason_fear_symptoms', 'covid_reason_at_risk', 'covid_reason_employer_req', 'covid_reason_workplace_offered', 'covid_reason_hcp', 'covid_reason_family_friend', 'covid_reason_social_norm', 'covid_reason_other', 'covid_reason_other_text', ] elif player.covid_shots == 0: fields += [ 'covid_novax_reason_distrust', 'covid_novax_reason_not_at_risk', 'covid_novax_reason_mild_symptoms', 'covid_novax_reason_didnt_know', 'covid_novax_reason_too_expensive', 'covid_novax_reason_no_time', 'covid_novax_reason_forgot', 'covid_novax_reason_not_available', 'covid_novax_reason_scared_needles', 'covid_novax_reason_underlying_shouldnt', 'covid_novax_reason_scared_side_effects', 'covid_novax_reason_negative_experience', 'covid_novax_reason_not_recommended', 'covid_novax_reason_social_norm_no', 'covid_novax_reason_other', 'covid_novax_reason_other_text', ] return fields def error_message(player, values): # Helper: checkbox is ticked if truthy (True/1) def any_ticked(field_names): return any(values.get(f) for f in field_names) if player.flu_vac == 1: flu_checks = [ 'flu_reason_trust', 'flu_reason_self', 'flu_reason_children', 'flu_reason_family_friends', 'flu_reason_beyond', 'flu_reason_fear_symptoms', 'flu_reason_at_risk', 'flu_reason_employer_req', 'flu_reason_workplace_offered', 'flu_reason_hcp', 'flu_reason_family_friend', 'flu_reason_social_norm', 'flu_reason_other', ] if not any_ticked(flu_checks): return 'Please select at least one option for "Why did you get the flu vaccine?"' if values.get('flu_reason_other'): other_text = (values.get('flu_reason_other_text') or '').strip() if not other_text: return 'You selected "Other". Please specify.' elif player.flu_vac == 2: flu_novax_checks = [ 'novax_reason_distrust', 'novax_reason_not_at_risk', 'novax_reason_mild_symptoms', 'novax_reason_didnt_know', 'novax_reason_too_expensive', 'novax_reason_no_time', 'novax_reason_forgot', 'novax_reason_not_available', 'novax_reason_scared_needles', 'novax_reason_underlying_shouldnt', 'novax_reason_scared_side_effects', 'novax_reason_negative_experience', 'novax_reason_not_recommended', 'novax_reason_social_norm_no', 'novax_reason_other', ] if not any_ticked(flu_novax_checks): return 'Please select at least one option for "Why did you not get the flu vaccine?"' if values.get('novax_reason_other'): other_text = (values.get('novax_reason_other_text') or '').strip() if not other_text: return 'You selected "Other". Please specify.' if player.covid_shots >= 1: covid_checks = [ 'covid_reason_trust', 'covid_reason_self', 'covid_reason_children', 'covid_reason_family_friends', 'covid_reason_beyond', 'covid_reason_fear_symptoms', 'covid_reason_at_risk', 'covid_reason_employer_req', 'covid_reason_workplace_offered', 'covid_reason_hcp', 'covid_reason_family_friend', 'covid_reason_social_norm', 'covid_reason_other', ] if not any_ticked(covid_checks): return 'Please select at least one option for "Why did you get the COVID-19 vaccine?"' if values.get('covid_reason_other'): other_text = (values.get('covid_reason_other_text') or '').strip() if not other_text: return 'You selected "Other". Please specify.' elif player.covid_shots == 0: covid_novax_checks = [ 'covid_novax_reason_distrust', 'covid_novax_reason_not_at_risk', 'covid_novax_reason_mild_symptoms', 'covid_novax_reason_didnt_know', 'covid_novax_reason_too_expensive', 'covid_novax_reason_no_time', 'covid_novax_reason_forgot', 'covid_novax_reason_not_available', 'covid_novax_reason_scared_needles', 'covid_novax_reason_underlying_shouldnt', 'covid_novax_reason_scared_side_effects', 'covid_novax_reason_negative_experience', 'covid_novax_reason_not_recommended', 'covid_novax_reason_social_norm_no', 'covid_novax_reason_other', ] if not any_ticked(covid_novax_checks): return 'Please select at least one option for "Why did you not get the COVID-19 vaccine?"' if values.get('covid_novax_reason_other'): other_text = (values.get('covid_novax_reason_other_text') or '').strip() if not other_text: return 'You selected "Other". Please specify.' def is_displayed(player): return player.round_number == 1 class Info1(Page): def is_displayed(player): return player.round_number == 1 class Info2(Page): def is_displayed(player): return player.round_number == 1 class BlockA(Page): form_model = 'player' def is_displayed(player): return 1 <= player.round_number <= 4 def vars_for_template(player: Player): tr = player.treatment # 'yc','yu','oc','ou' order = int(player.order2) # 1,2,3,4 rnd = int(player.round_number) # 1..4 impacted = 'children' if tr[0] == 'y' else 'elderly' uncertain = (tr[1] == 'u') seq_by_order = { 1: [('L', 'L'), ('L', 'H'), ('H', 'L'), ('H', 'H')], 2: [('L', 'H'), ('H', 'H'), ('L', 'L'), ('H', 'L')], 3: [('H', 'L'), ('L', 'L'), ('H', 'H'), ('L', 'H')], 4: [('H', 'H'), ('H', 'L'), ('L', 'H'), ('L', 'L')], } if tr not in ['yc', 'yu', 'oc', 'ou']: raise Exception(f"Unexpected player.treatment={tr}. Expected yc/yu/oc/ou.") if order not in seq_by_order: raise Exception(f"Unexpected player.order2={order}. Expected 1-4.") if rnd not in [1, 2, 3, 4]: raise Exception(f"Unexpected round_number={rnd}. Expected 1-4.") T_level, S_level = seq_by_order[order][rnd - 1] # ---------- FIGURE FILENAMES (UNCHANGED LOGIC) ---------- suffix = 'U' if uncertain else '' t_base = 'TL' if T_level == 'L' else 'TH' s_base = 'SL' if S_level == 'L' else 'SH' t_fig = f'{t_base}{suffix}.png' s_fig = f'{s_base}{suffix}.png' # ---------- TEXT: Transmissibility ---------- if not uncertain: # certain t_num = 2 if T_level == 'L' else 5 t_bracket = f"Effective reproduction number is about {t_num}" t_sentence = ( f"On average, one infected person infects {t_num} others over the course of their infection if people behave normally. " "In practice, some infected people may infect no one, while others may infect many." ) else: # uncertain if T_level == 'L': t_bracket = "Effective reproduction number is between 1 and 3" t_sentence = ( "On average, one infected person infects 2 others over the course of their infection if people behave normally. " "In practice, some infected people may infect no one, while others may infect many. " "Because information is limited, the true value could plausibly be between 1 and 3." ) else: t_bracket = "Effective reproduction number is between 2 and 8" t_sentence = ( "On average, one infected person infects 5 others over the course of their infection if people behave normally. " "In practice, some infected people may infect no one, while others may infect many. " "Because information is limited, the true value could plausibly be between 2 and 8." ) # ---------- TEXT: Severity ---------- if not uncertain: # certain s_pct = "0.2%" if S_level == 'L' else "10%" s_deaths = 2 if S_level == 'L' else 100 s_bracket = f"Case fatality ratio is about {s_pct}" s_sentence = f"Among 1,000 confirmed cases, about {s_deaths} would die." else: # uncertain if S_level == 'L': s_bracket = "Case fatality ratio is between 0.1% and 0.3%" s_sentence = "Among 1,000 confirmed cases, about 1–3 would die." else: s_bracket = "Case fatality ratio is between 5% and 15%" s_sentence = "Among 1,000 confirmed cases, about 50–150 would die." return dict( impacted=impacted, t_fig=t_fig, s_fig=s_fig, t_bracket=t_bracket, t_sentence=t_sentence, s_bracket=s_bracket, s_sentence=s_sentence, ) class BlockB(Page): form_model = 'player' form_fields = [ # Catch 'catch_likely', 'catch_moved', 'catch_t0', 'catch_t1', 'catch_t2', 'catch_t3', 'catch_t4', 'catch_t5', 'catch_t6', 'catch_t7', 'catch_t8', 'catch_t9', # Die 'die_likely', 'die_moved', 'die_t0', 'die_t1', 'die_t2', 'die_t3', 'die_t4', 'die_t5', 'die_t6', 'die_t7', 'die_t8', 'die_t9', # Avoid 'avoid_likely', 'avoid_moved', 'avoid_t0', 'avoid_t1', 'avoid_t2', 'avoid_t3', 'avoid_t4', 'avoid_t5', 'avoid_t6', 'avoid_t7', 'avoid_t8', 'avoid_t9', 'confident_catch', 'confident_die', 'confident_avoid' ] def is_displayed(player): return 1 <= player.round_number <= 4 def vars_for_template(player: Player): tr = player.treatment # 'yc','yu','oc','ou' order = int(player.order2) # 1,2,3,4 rnd = int(player.round_number) # 1..4 impacted = 'children' if tr[0] == 'y' else 'elderly' uncertain = (tr[1] == 'u') seq_by_order = { 1: [('L', 'L'), ('L', 'H'), ('H', 'L'), ('H', 'H')], 2: [('L', 'H'), ('H', 'H'), ('L', 'L'), ('H', 'L')], 3: [('H', 'L'), ('L', 'L'), ('H', 'H'), ('L', 'H')], 4: [('H', 'H'), ('H', 'L'), ('L', 'H'), ('L', 'L')], } if tr not in ['yc', 'yu', 'oc', 'ou']: raise Exception(f"Unexpected player.treatment={tr}. Expected yc/yu/oc/ou.") if order not in seq_by_order: raise Exception(f"Unexpected player.order2={order}. Expected 1-4.") if rnd not in [1, 2, 3, 4]: raise Exception(f"Unexpected round_number={rnd}. Expected 1-4.") T_level, S_level = seq_by_order[order][rnd - 1] # ---------- FIGURE FILENAMES (UNCHANGED LOGIC) ---------- suffix = 'U' if uncertain else '' t_base = 'TL' if T_level == 'L' else 'TH' s_base = 'SL' if S_level == 'L' else 'SH' t_fig = f'{t_base}{suffix}.png' s_fig = f'{s_base}{suffix}.png' # ---------- TEXT: Transmissibility ---------- if not uncertain: # certain t_num = 2 if T_level == 'L' else 5 t_bracket = f"Effective reproduction number is about {t_num}" t_sentence = ( f"On average, one infected person infects {t_num} others over the course of their infection if people behave normally. " "In practice, some infected people may infect no one, while others may infect many." ) else: # uncertain if T_level == 'L': t_bracket = "Effective reproduction number is between 1 and 3" t_sentence = ( "On average, one infected person infects 2 others over the course of their infection if people behave normally. " "In practice, some infected people may infect no one, while others may infect many. " "Because information is limited, the true value could plausibly be between 1 and 3." ) else: t_bracket = "Effective reproduction number is between 2 and 8" t_sentence = ( "On average, one infected person infects 5 others over the course of their infection if people behave normally. " "In practice, some infected people may infect no one, while others may infect many. " "Because information is limited, the true value could plausibly be between 2 and 8." ) # ---------- TEXT: Severity ---------- if not uncertain: # certain s_pct = "0.2%" if S_level == 'L' else "10%" s_deaths = 2 if S_level == 'L' else 100 s_bracket = f"Case fatality ratio is about {s_pct}" s_sentence = f"Among 1,000 confirmed cases, about {s_deaths} would die." else: # uncertain if S_level == 'L': s_bracket = "Case fatality ratio is between 0.1% and 0.3%" s_sentence = "Among 1,000 confirmed cases, about 1–3 would die." else: s_bracket = "Case fatality ratio is between 5% and 15%" s_sentence = "Among 1,000 confirmed cases, about 50–150 would die." return dict( impacted=impacted, t_fig=t_fig, s_fig=s_fig, t_bracket=t_bracket, t_sentence=t_sentence, s_bracket=s_bracket, s_sentence=s_sentence, ) def error_message(player: Player, values): # Must move each main slider at least once if not values.get('catch_moved'): return "Please move the first slider (catching the virus)." if not values.get('die_moved'): return "Please move the second slider (dying if infected)." if not values.get('avoid_moved'): return "Please move the third slider (avoiding activities)." def sum_tokens(prefix): return sum(int(values.get(f'{prefix}_t{i}', 0) or 0) for i in range(10)) if sum_tokens('catch') != 100: return "For the first token allocation, your tokens must add up to 100." if sum_tokens('die') != 100: return "For the second token allocation, your tokens must add up to 100." if sum_tokens('avoid') != 100: return "For the third token allocation, your tokens must add up to 100." class RiskPrefs(Page): form_model = 'player' form_fields = ['risk_1','risk_2','risk_3','risk_4','risk_5','risk_6','risk_7'] @staticmethod def error_message(player, values): missing = [k for k in RiskPrefs.form_fields if not values.get(k)] if missing: return "Please make a decision in every row before continuing." @staticmethod def vars_for_template(player): # Option B amounts for the 7 rows (from your screenshot) # 50% £0 and 50% £X return dict( risk_rows=[ dict(i=1, b=30), dict(i=2, b=40), dict(i=3, b=50), dict(i=4, b=60), dict(i=5, b=80), dict(i=6, b=100), dict(i=7, b=120), ] ) def is_displayed(player): return player.round_number == 5 class TimePrefs(Page): form_model = 'player' form_fields = ['time_1','time_2','time_3','time_4','time_5','time_6','time_7'] @staticmethod def error_message(player, values): missing = [k for k in TimePrefs.form_fields if not values.get(k)] if missing: return "Please make a decision in every row before continuing." @staticmethod def vars_for_template(player): # Option B amounts for the 7 rows (from your screenshot) # £X in 15 days return dict( time_rows=[ dict(i=1, b=21), dict(i=2, b=22), dict(i=3, b=23), dict(i=4, b=24), dict(i=5, b=26), dict(i=6, b=28), dict(i=7, b=30), ] ) def is_displayed(player): return player.round_number == 5 class Part2(Page): def is_displayed(player): return player.round_number == 5 class Part2_2(Page): def is_displayed(player): return player.round_number == 5 class ComprehensionChecks(Page): form_model = 'player' form_fields = ['cc_possible_colors', 'cc_start_color', 'cc_total_players', 'cc_bonus_who'] @staticmethod def error_message(player, values): errors = {} if values.get('cc_possible_colors') != '3': errors['cc_possible_colors'] = "Incorrect. Please select again." if values.get('cc_start_color') != '2': errors['cc_start_color'] = "Incorrect. Please select again." if values.get('cc_total_players') != 100: errors['cc_total_players'] = "Incorrect. Please enter the number again." if values.get('cc_bonus_who') != '2': errors['cc_bonus_who'] = "Incorrect. Please select again." # record EVERY click player.cc_attempt_count += 1 return errors or None def is_displayed(player): return player.round_number == 5 class Part2_3(Page): def is_displayed(player): return player.round_number == 5 class ComprehensionChecks2(Page): form_model = 'player' form_fields = ['cc_start_green', 'cc_action', 'cc_know_color'] @staticmethod def error_message(player, values): errors = {} if values.get('cc_start_green') != 92: errors['cc_start_green'] = "Incorrect. Please enter the number again." if values.get('cc_action') != '3': errors['cc_action'] = "Incorrect. Please select again." if values.get('cc_know_color') != '2': errors['cc_know_color'] = "Incorrect. Please select again." # record EVERY click player.cc_attempt_count2 += 1 return errors or None def is_displayed(player): return player.round_number == 5 class Part2_4(Page): def is_displayed(player): return player.round_number == 5 class ComprehensionChecks3(Page): form_model = 'player' form_fields = ['cc_redtogreen', 'cc_greentoredhh', 'cc_greentoredhg', 'cc_greentoredbothgreen'] @staticmethod def error_message(player, values): errors = {} if values.get('cc_redtogreen') != '1': errors['cc_redtogreen'] = "Incorrect. Please select again." if values.get('cc_greentoredhh') != '4': errors['cc_greentoredhh'] = "Incorrect. Please select again." if values.get('cc_greentoredhg') != '3': errors['cc_greentoredhg'] = "Incorrect. Please select again." if values.get('cc_greentoredbothgreen') != '1': errors['cc_greentoredbothgreen'] = "Incorrect. Please select again." # record EVERY click player.cc_attempt_count3 += 1 return errors or None def is_displayed(player): return player.round_number == 5 class Instruction(Page): def is_displayed(player): return player.round_number == 5 class Game(Page): form_model = 'player' @staticmethod def get_form_fields(player): fields = ['choice'] if player.round_number < 29: fields.append('belief') return fields def is_displayed(player): return 5<= player.round_number <= 29 def before_next_page(player, timeout_happened): player.round_points = 8 if player.choice == 'G' else 40 # update cumulative points if player.round_number == 5: player.total_points = player.round_points else: player.total_points = player.in_round(player.round_number - 1).total_points + player.round_points def vars_for_template(player): # display round number should equal player.round_number - 4 round_number = player.round_number - 4 # show points earned up to the END of the previous round if player.round_number == 5: points_gained_so_far = 0 else: points_gained_so_far = player.in_round(player.round_number - 1).total_points possible_bonus = points_gained_so_far / 200 return dict( round_number=round_number, points_gained_so_far=points_gained_so_far, possible_bonus=f"{possible_bonus:.2f}", ) class Feedback(Page): @staticmethod def vars_for_template(player): total_points = player.total_points total_bonus = total_points / 200 # £ = points/200 # optional: format to 2dp in python to match screenshot return dict( total_points=total_points, total_bonus=f"{total_bonus:.2f}", ) def is_displayed(player): return player.round_number == 29 class Thanks(Page): def is_displayed(player): return player.round_number == 29 page_sequence = [Consent,ProlificID,Commitment,Demo,TrustSources,Covid, Covid2,Info1,Info2, BlockA,BlockB,RiskPrefs,TimePrefs, Part2,Part2_2,ComprehensionChecks, Part2_3,ComprehensionChecks2, Part2_4,ComprehensionChecks3, Instruction,Game,Feedback,Thanks]