from otree.api import * doc = """ Your app description """ class Constants(BaseConstants): name_in_url = 'walnut' players_per_group = None num_rounds = 1 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): total_payoff = models.FloatField() ExNo = models.IntegerField(label="Enter your experimental number as shown on your desk:") code1 = models.StringField(label="code1 to proceed:") boss = models.StringField() rcp_1 = models.IntegerField() rcp_2 = models.IntegerField() dist_1 = models.FloatField() dist_2 = models.FloatField() # part 1 payment part1_payment = models.IntegerField(initial=0) # guess payment guess_payoff = models.IntegerField(initial=0) # part 2 payment allocation_1 = models.FloatField(initial=-1) allocation_2 = models.FloatField() # CQ_1 = models.StringField( # label="How much are you going to be paid in Part 1?", # choices=[ # ['A','$2 or $5'], # ['B','$8 or $5'], # ['C','$2 or $8'], # ['D','$5 for sure'], # ], # widget=widgets.RadioSelect # ) guess = models.StringField( label="How much do you think each boss pays their workers? " "If you guess correctly, you'll get an additional $1.", choices=[ ['A', 'My employer pays $2, the other employer pays $5'], ['B', 'They both pay $2'], ['C','My employer pays $5, the other employer pays $2'], ['D', 'They both pay $5'], ], widget=widgets.RadioSelect ) CQ_2 = models.StringField( label="What’s your payoff in this part?", choices=[ ['A', 'One of the distributions I get from two allocators'], ['B', 'The average of distributions I get from two allocators'], ['C', 'A flat payment'], ['D', 'The distribution I get from the unique allocator'], ], widget=widgets.RadioSelect ) CQ_3 = models.StringField( label="How is your allocation going to decide participants’ payoffs in Part 2?", choices=[ ['A', '50% chance my allocation will fully decide other two subjects’ payoffs.'], ['B', 'My allocation will decide 50% of two other subjects’ payoffs. '], ['C', 'My allocation decides my own payoff.'], ['D', 'My allocation decides my own payoff and one other subject’s payoff.'], ], widget=widgets.RadioSelect ) age = models.IntegerField( label="1. What's your age?", min=10, max=90 ) gender = models.StringField( label="2. What's your gender?", choices=[ ['A', 'Female'], ['B', 'Male'], ['C', 'Other'], ], widget=widgets.RadioSelect ) # major = models.StringField( # label="What's your major?" # ) # # education = models.StringField( # label="Are you an undergraduate or graduate student?", # choices=[ # ['A', 'Undergraduate student'], # ['B', 'Graduate student'], # ['C', 'None of them'], # ], # widget=widgets.RadioSelect # ) # # race = models.StringField( # label="Which of the following best describes your racial or ethnic background?", # choices=[ # ['A', 'Caucasian'], # ['B', 'Asian/Pacific Islander'], # ['C', 'Hispanic/Latino'], # ['D', 'Black/African American'], # ['E', 'Others'], # ], # widget=widgets.RadioSelect # ) expectation_part2 = models.FloatField( label="3. How much do you expect to receive for Part 2?", min=0,max=10 ) # FUNCTIONS # @staticmethod # def error_message(player, values): # solutions = dict( # code1='sweetpotato', # ) # # error_messages = dict() # for field_name in solutions: # if values[field_name] != solutions[field_name]: # error_messages[field_name] = "Try again." # return error_messages # @staticmethod # def boss_assignment(player): # if player.id_in_group % 2 == 0: # player.boss = "B" # else: # player.boss = "A" # PAGES class ExperimentNumber(Page): form_model = 'player' form_fields = ['ExNo'] # random assign the boss @staticmethod def before_next_page(player: Player, timeout_happened): # even number works for Employer B, odd number works for Employer A. if player.id_in_group % 2 == 0: player.boss = "B" else: player.boss = "A" class Crack(Page): form_model = 'player' form_fields = ['code1'] @staticmethod # assignm payment for cracking def before_next_page(player: Player, timeout_happened): if player.boss == "B": # Employer B pays $5 if player.code1 == "juice" or player.code1 == "pear": player.part1_payment = 5 else: if player.code1 == "juice" or player.code1 == "pear": player.part1_payment = 2 # proceed if the code1 is correct def error_message(player, value): if value['code1'] != "juice" and value['code1'] != "lemon" and value['code1'] != "pear": return "Try code1 again." class WalnutWaitPage(WaitPage): body_text = "Please wait others finishing cracking." class Results(Page): pass class AssignmentWaitPage(WaitPage): # design allocation order def after_all_players_arrive(group): bossA_group = [] bossB_group = [] players = group.subsession.get_players() # put subjects' id with the same boss in a group for player in players: if player.boss == "A": bossA_group.append(player.id_in_group) else: bossB_group.append(player.id_in_group) # get the dynamic length of a group, -1 because python starts from 0 num_in_a_group = len(bossA_group)-1 print("length of a group", num_in_a_group+1) # assign recipients for each subject # python starts from 0, otree id starts from 1 for i in bossA_group: #get player by id player = group.get_player_by_id(i) # get i's index, starts from 0 index_i = bossA_group.index(i) if index_i < num_in_a_group: player.rcp_1 = bossB_group[index_i] player.rcp_2 = bossA_group[index_i+1] else: player.rcp_1 = bossB_group[num_in_a_group] player.rcp_2 = bossA_group[0] for i in bossB_group: # get player by id player = group.get_player_by_id(i) # get i's index, starts from 0 index_i = bossB_group.index(i) if index_i < num_in_a_group: player.rcp_1 = bossA_group[index_i+1] player.rcp_2 = bossB_group[index_i+1] else: player.rcp_1 = bossA_group[0] player.rcp_2 = bossB_group[0] class PracticeAllocation(Page): pass class Allocation(Page): form_model = 'player' form_fields = ['dist_1', 'dist_2'] @staticmethod def js_vars(player): return dict( boss=player.boss, ) class AllocationWaitPage(WaitPage): def after_all_players_arrive(group): players = group.subsession.get_players() # assign allocation to recipients for player in players: # get the recipients a = player.rcp_1 b = player.rcp_2 recipient1 = group.get_player_by_id(a) if recipient1.allocation_1 == -1: recipient1.allocation_1 = player.dist_1 else: recipient1.allocation_2 = player.dist_1 recipient2 = group.get_player_by_id(b) if recipient2.allocation_1 == -1: recipient2.allocation_1 = player.dist_2 else: recipient2.allocation_2 = player.dist_2 for player in players: # player's payment is equal to the sum of show-up fee, and three parts payoffs # $3 show up fee player.total_payoff = 3+player.part1_payment+player.guess_payoff+(player.allocation_1+player.allocation_2)/2 class Part1(Page): pass class Part2(Page): pass # class CQ_1(Page): # form_model = 'player' # form_fields = ['CQ_1'] # # def error_message(player, value): # if value['CQ_1'] != "A": # return "Try again." class Guess(Page): form_model = 'player' form_fields = ['guess'] def before_next_page(player: Player, timeout_happened): # A pays $2, B pays $5 is correct if player.boss == "A" and player.guess == "A": player.guess_payoff = 1 if player.boss == "B" and player.guess == "C": player.guess_payoff = 1 class CQ_2(Page): form_model = 'player' form_fields = ['CQ_2', 'CQ_3'] def error_message(player, value): if value['CQ_2'] != "B": return "Try again." if value['CQ_3'] != 'B': return "Try again." class PostSurvey(Page): form_model = 'player' form_fields = ['age', 'gender', 'expectation_part2'] page_sequence = [ExperimentNumber, Part1, # CQ_1, # no comprehension question for Part 1 Crack, WalnutWaitPage, Guess, Results, Part2, CQ_2, AssignmentWaitPage, PracticeAllocation, Allocation, AllocationWaitPage, PostSurvey, ]