from otree.api import * import random import json from common import * doc = ''' This is the first app - the Introduction app. It contains 1. Demgraphics page 2. Instructions that participants can always access 3. Comprehension checks 4. and the first attention checks Following are saved to the participant level - Comprehension_passed: whether they passed the comprehension checks - Attention_1: whether they passed the first attention check - Treatment: which treatment they are assigned to ''' class C(CommonConstants): NAME_IN_URL = 'Introduction' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 # Treatment quotas. This will be copied to the session variable. #TODO: if you have multiple treatments and want to gender balance it use this. if not you can delete this. Make sure these exist in session fields # If instead you want a non-gender balanced treatment assignment with quotas remove one of these and use it for both genders. Female_quotas = { 'Treatment1': 0, 'Treatment2': 0, 'Control': 0, } Male_quotas = { 'Treatment1': 0, 'Treatment2': 0, 'Control': 0, } class Subsession(BaseSubsession): pass def creating_session(subsession): ''' 1. create the quotas for each treatment to be saved to the session variable - make sure that in the settings.py file the SESSION_FIELDS has initialized the session variables 2. These quotas are initially zero but as participants are assigned they are incremented. - It is important to note that although prolific ensures gender balanced sample, we need this balancing to be within treatment level also ''' # people in v_1_first see the first version of the vignettes first. subsession.session.Male_quotas = C.Male_quotas.copy() subsession.session.Female_quotas = C.Female_quotas.copy() for player in subsession.get_players(): player.participant.Comprehension_passed = False player.participant.Attention_passed= True player.participant.Treatment = 'Default' class Group(BaseGroup): pass class Player(BasePlayer): treatment = models.StringField() # Demographics prolific_id = models.StringField(default=str("None")) #prolific id, will be fetched automatically. age = models.IntegerField(label="Age", min=18, max=100) gender = models.StringField(label='Gender at birth', choices=['Male', 'Female', 'Other/Prefer not to say'], widget=widgets.RadioSelect) education = models.StringField(label = 'Education level', choices=['Haven’t graduated high school','GED','High school graduate','Bachelors','Masters','Professional degree (JD, MD, MBA)','Doctorate', 'Other'], widget=widgets.RadioSelect) # education = models.StringField(label = 'Education level', # choices=['High school or lower','Bachelors degree','Masters degree','PhD','Other'], widget=widgets.RadioSelect) employment = models.StringField(label='Employment status', choices=['Employed full-time', 'Employed part-time', 'Self-employed', 'Out of work, or seeking work', 'Student', 'Out of labor force (e.g. retired or parent raising one or more children)'], widget=widgets.RadioSelect) income = models.StringField(label='Approximately, what was your total household income in the last year, before taxes?', choices=['$0-$10.000', '$10.000-$20.000','$20.000-$30.000','$30.000-$40.000','$40.000-$50.000','$50.000-$60.000', '$50.000-$75.000', '$75.000-$100.000', '$100.000-$150.000', '$150.000-$200.000', '$200.000+', 'Prefer not to answer', ],) # data quality browser = models.StringField(blank=True) blur_log = models.LongStringField(blank=True) blur_count = models.IntegerField(initial=0) blur_warned = models.IntegerField(initial=0) 'Comprehension and attention checks' #whether the player got the comprehension questions rigt at the first try Comprehension_1 = models.BooleanField(initial=True) #In the first comprehension check, the questions the player has answered wrong are stored as a string below. Comprehension_wrong_answers = models.StringField(initial='') Comprehension_2 = models.BooleanField(initial=True) Comprehension_question_1 = models.BooleanField(choices=[ [True,'Correct answer'], # Correct answer here [False, 'False answer'], [False, 'False answer'],], label = 'Comprehension question 1', widget=widgets.RadioSelect) Comprehension_question_2 = models.BooleanField(choices=[ [True,'Correct answer'], [False, 'False answer'], [False, 'False answer'],], label = 'Comprehension question 1', widget=widgets.RadioSelect) Comprehension_question_3 = models.BooleanField(choices=[ [True,'Correct answer'], [False, 'False answer'], [False, 'False answer'],], label = 'Comprehension question 1', widget=widgets.RadioSelect) Attention_1 = models.BooleanField(choices=[ [False, 'Horse'], [False, 'Duck'], [False, 'Goose'], [True, 'Unicorn'], [False, 'Beetle'] ], label='Which is not a real animal?', widget=widgets.RadioSelect) #%% Functions def treatment_assignment(player): session=player.subsession.session #TODO: make sure gender is Male, Female and Other/Prefer not to say if player.gender == 'Male': Quotas = session.Male_quotas elif player.gender == 'Female': Quotas = session.Female_quotas elif player.gender == 'Other/Prefer not to say': Quotas = session.Male_quotas #the line below does: splits the Quotas into two halves, picks one of them randomly from the bottom half. ''' Quota/Treatment assignment works as follows: 1. get the current quotas 2. assign a random treatment from the bottom half of the quotas (i.e. the treatment with the lowest quota) 3. update quotas accordingly. ''' treatment = random.choice([key for key, value in Quotas.items() if value in sorted(Quotas.values())[:1]]) # print('Treatment:', treatment) player.participant.Treatment = treatment player.treatment = treatment if player.gender == 'Male': Quotas.update({treatment: Quotas[treatment]+1}) session.Male_quotas = Quotas # print('incrementing male quotas: ', Quotas) elif player.gender == 'Female': Quotas.update({treatment: Quotas[treatment]+1}) # print('incrementing female quotas: ', Quotas) session.Female_quotas = Quotas # PAGES #%% Base Pages from common import MyBasePage #%% Pages #Consent, Demographics, Introduction, Comprehension checks and attention check 1 class Consent(Page): pass class Demographics(MyBasePage): extra_fields = ['age', 'gender', 'education', 'employment', 'income','browser'] form_fields = MyBasePage.form_fields + extra_fields @staticmethod def before_next_page(player: Player, timeout_happened=False): treatment_assignment(player) #assign treatment and update quotas @staticmethod def vars_for_template(player: Player): variables = MyBasePage.vars_for_template(player) variables['hidden_fields'].append('browser') return variables class Introduction(MyBasePage): pass class Comprehension_check_1(MyBasePage): extra_fields = ['Comprehension_question_1', 'Comprehension_question_2', 'Comprehension_question_3'] form_fields = MyBasePage.form_fields + extra_fields @staticmethod def before_next_page(player: Player, timeout_happened=False): player_passed_comprehension = player.Comprehension_question_1 and player.Comprehension_question_2 and player.Comprehension_question_3 # if player has answered a question wrong then I save it in a string wrong_answers = '' if not player.Comprehension_question_1: player.Comprehension_question_1 = None #reset player answer so it doesnt show up in the next page wrong_answers+= 'first question' if not player.Comprehension_question_2: if not wrong_answers =='': wrong_answers += ', ' player.Comprehension_question_2 = None wrong_answers+= 'second question' if not player.Comprehension_question_3: if not wrong_answers =='': wrong_answers += ', ' player.Comprehension_question_3 = None wrong_answers+= 'third question' player.Comprehension_wrong_answers = wrong_answers player.Comprehension_1 = player_passed_comprehension # save at the participant level if player_passed_comprehension: player.participant.vars['Comprehension_passed'] = True class Comprehension_check_2(MyBasePage): extra_fields = ['Comprehension_question_1', 'Comprehension_question_2', 'Comprehension_question_3'] form_fields = MyBasePage.form_fields + extra_fields @staticmethod def is_displayed(player: Player): condition = MyBasePage.is_displayed(player) and not player.Comprehension_1 return condition @staticmethod def vars_for_template(player: Player): variables = MyBasePage.vars_for_template(player) # Add or modify variables specific to ExtendedPage variables['Comprehension_wrong_answers'] = player.Comprehension_wrong_answers return variables @staticmethod def before_next_page(player: Player, timeout_happened=False): player_passed_comprehension = (player.Comprehension_question_1 and player.Comprehension_question_2 and player.Comprehension_question_3) #failing two compr. checks player is not allowed to continue player.Comprehension_2 = player_passed_comprehension # save at the participant level if they passed if player_passed_comprehension: player.participant.vars['Comprehension_passed'] = True else: player.participant.vars['Comprehension_passed'] = False class Comprehension_check_3(MyBasePage): extra_fields = [] form_fields = MyBasePage.form_fields + extra_fields @staticmethod def is_displayed(player: Player): return not player.Comprehension_1 @staticmethod def vars_for_template(player: Player): variables = MyBasePage.vars_for_template(player) # Add or modify variables specific to ExtendedPage variables['Comprehension_wrong_answers'] = player.Comprehension_wrong_answers return variables @staticmethod def before_next_page(player: Player, timeout_happened=False): player_passed_comprehension = (player.Comprehension_question_1 and player.Comprehension_question_2 and player.Comprehension_question_3) #failing two compr. checks player is not allowed to continue player.Comprehension_2 = player_passed_comprehension # save at the participant level if they passed if player_passed_comprehension: player.participant.vars['Comprehension_passed'] = True else: player.participant.vars['Comprehension_passed'] = False class Attention_check_1(MyBasePage): extra_fields = ['Attention_1'] form_fields = MyBasePage.form_fields + extra_fields #save at the participant level @staticmethod def before_next_page(player: Player, timeout_happened=False): player.participant.vars['Attention_1'] = player.Attention_1 page_sequence = [Consent, ]