from otree.api import * doc = """ Group decision-making game """ ## CONSTANTS class C(BaseConstants): NAME_IN_URL = 'member' PLAYERS_PER_GROUP = None TREATMENT = ['old', 'yng'] NUM_ROUNDS = 1 GROUP_REWARD = cu(8) # reward if accepts and correct GROUP_OUTSIDE = cu(7) # reward if rejects PATCPN_FEE = cu(8) PAYOFF_MAX = GROUP_REWARD*3 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): prolific_id = models.StringField(default = str(" ")) consent = models.BooleanField(blank = True) factual = models.BooleanField(blank = True) atten1 = models.IntegerField(blank = True) atten2 = models.IntegerField(blank = True) atten3 = models.IntegerField(blank = True) treat_old = models.BooleanField() # treatment assignment method 1 assign_treat = models.StringField() # treatment assignment method 2 treatment_group = models.IntegerField() # treatment assignment method 3 vote1 = models.BooleanField( doc = """Vote for Leader's answer""", label = 'Your answer:' ) vote2 = models.BooleanField( doc = """Vote for Leader's answer""", label = 'Your answer:' ) vote3 = models.BooleanField( doc = """Vote for Leader's answer""", label = 'Your answer:' ) vote4 = models.BooleanField( doc = """Vote for Leader's answer""", label = 'Your answer:' ) surv1 = models.BooleanField(blank = True) surv2 = models.BooleanField(blank = True) surv3 = models.BooleanField(blank = True) surv4 = models.BooleanField(blank = True) surv5 = models.BooleanField(blank = True) gues3 = models.BooleanField(blank = True) gues4 = models.BooleanField(blank = True) gues5 = models.BooleanField(blank = True) conf3 = models.BooleanField(blank = True) conf4 = models.BooleanField(blank = True) conf5 = models.BooleanField(blank = True) atten4 = models.BooleanField( label = 'Were you paying sufficient attention throughout the study, and do you believe your data should remain in the dataset?' ) """ prolificid = models.StringField( label = 'Enter your Prolific ID' ## (include in template {{ formfield "prolificid" }} ) ) age = models.IntegerField( label = 'What is your age?', min=18, max=50 # need to incorporate this in completion path Prolific ) sex = models.StringField( choices = [['Male', 'Male'], ['Female', 'Female']], label = 'What is your sex, as recorded on official documents?', widget = widgets.RadioSelect, ) """ ## FUNCTIONS # Method 1: Balanced treatment groups <- require all participants to be present in the session. Here they join one by one, sequentially. # Produce a finite sequence of old_true, old_false, old_true, ... and assign each element to players in session in that order. # This creates balanced treatment groups if all responses are approved. # If one treatment group has fewer valid responses than the other and open more spaces, # we'll get half of new responses in the filled treatment group (which we don't need) and half for the other unfilled treatment group. # -> same problem as independent draw for each player def creating_session(subsession): import itertools old = itertools.cycle([True, False]) for player in subsession.get_players(): player.treat_old = next(old) # Method 2: Random draw independently for each player (also participant cos only 1 round) # Treatment groups may be unbalanced by the time session ends (max_participants reached per room). # Might end up with more people in 'old' treatment group than 'young'. # Opening up more spaces will risk adding more responses to the filled treatment group (which has received enough responses) # since don't know how the new participants will get which treatment. """ def creating_session(subsession): import random for player in subsession.get_players(): player.assign_treat = random.choice(C.TREATMENT) """ # Method 3 (similar to method 1): Permutation of treatment status among fixed number of participants # 1-old, 2-young, each treatment multiplied for 30 times (N=30 in each treatment group) # if the 30 slots are used up, continue on random basis # same problem as method 1 and 2, cannot arbitrarily assign treatment to new participants once one treatment group is filled. # Assuming that (1) fraction of invalid responses per session is 50% # and (2) this fraction is same for either treatment group # -> can pre-set N = 30*(1+0.5*2) = 42 # and stop the study earlier once enough responses have been collected. """ def creating_session(subsession): import random treatment = [1, 2] * 60 random.shuffle(treatment) for player in subsession.get_players(): if treatment: player.treatment_group = treatment.pop() else: player.treatment_group = random.randint(1, 2) """ # Spawning participants into groups of 3 or letting individuals vote and grouping them afterwards? # In the latter, subjects don't have to wait for others to be online & take the study at the same time. # Obs is a group of 3. Group ID is assigned at time of entry, first come first serve. Grouping is done at data analysis stage. Reward is paid out as 'bonus payment' on Prolific. """ def creating_session(subsession): subsession.group_randomly() import itertools roles = itertools.cycle(['Leader', 'Member', 'Member', 'Member']) for player in subsession.get_players(): player.set_role = next(roles) player.is_member = (player.set_role == 'Member') player.is_leader = (player.set_role == 'Leader') """ ## PAGES ### Welcome page for Members class Welcome(Page): form_model = 'player' form_fields = ['consent', 'factual'] preserve_unsubmitted_inputs = True @staticmethod def before_next_page(self, timeout_happened): self.prolific_id = self.participant.label pass """ # Prolific ID validation def prolificid_error_message(player, value): if len(value) != 24: return "Your Prolific ID must have 24 alphanumeric characters." """ """ ### Demographics page for Members class Demographics(Page): form_model = 'player' form_fields = ['age', 'sex'] """ ### Instructions page for Leader class Instruction(Page): pass ### Attention check 1 class Attention1(Page): form_model = 'player' form_fields = ['atten1'] ### Attention check 2 class Attention2(Page): form_model = 'player' form_fields = ['atten2'] ### Attention check 3 class Attention3(Page): form_model = 'player' form_fields = ['atten3'] ### Introduction page for Members class Intro(Page): pass ### Members vote 1 class Member1(Page): form_model = 'player' form_fields = ['vote1'] ### Members vote 2 class Member2(Page): form_model = 'player' form_fields = ['vote2'] ### Members vote 3 class Member3(Page): form_model = 'player' form_fields = ['vote3'] ### Members vote 3 class Member4(Page): form_model = 'player' form_fields = ['vote4'] ### Survey fillers class Survey(Page): form_model = 'player' form_fields = ['surv1', 'surv2'] ### Survey incentivized instructions class Survey_i0(Page): pass ### Survey incentivized 1 class Survey_i1(Page): form_model = 'player' form_fields = ['surv3', 'gues3', 'conf3'] ### Survey incentivized 2 class Survey_i2(Page): form_model = 'player' form_fields = ['surv4', 'gues4', 'conf4'] ### Survey incentivized 3 class Survey_i3(Page): form_model = 'player' form_fields = ['surv5', 'gues5', 'conf5'] ### Attention check 3 class Attention4(Page): form_model = 'player' form_fields = ['atten4'] ### End page class End(Page): pass ### Redirect page class Redirect(Page): @staticmethod def js_vars(player): return dict( completionlink= player.subsession.session.config['completionlink'] ) pass page_sequence = [Welcome, Instruction, Attention1, Attention2, Attention3, Intro, Member1, Member2, Member3, Member4, Survey, Survey_i0, Survey_i1, Survey_i2, Survey_i3, Attention4, End, Redirect]