from otree.api import * import random doc = """ Part II: Allocation Games: Self-Other, Self-Other-Other, Self-Other (No reciprocity) """ class C(BaseConstants): NAME_IN_URL = 'PartII' PLAYERS_PER_GROUP = None NUM_ROUNDS = 8 Endowment1 = 10 Endowment2 = 20 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): name = models.LongStringField() assigned_group = models.LongStringField() match1 = models.LongStringField() match2 = models.LongStringField() match1_ingroup = models.IntegerField() match2_ingroup = models.IntegerField() keep = models.FloatField() give1 = models.FloatField() give2 = models.FloatField() payout_round = models.IntegerField(initial=0) divider = models.IntegerField(initial=0) #Questionnaire study = models.LongStringField(label= 'What do you think this experiment is studying?') identifyA = models.StringField( label=''' How would you describe your connection with Group A during the experiment? ''', widget=widgets.RadioSelectHorizontal, choices=['Strongly Negative', 'Weakly Negative', 'No connection', 'Weakly Positive ', 'Strongly Positive'], ) identifyB = models.StringField( label=''' How would you describe your connection with Group B during the experiment? ''', widget=widgets.RadioSelectHorizontal, choices=['Strongly Negative', 'Weakly Negative', 'No connection', 'Weakly Positive ', 'Strongly Positive'], ) IR = models.StringField( label=''' In Part I Task II, all members of your group were asked to nominate two members of your group for a bonus. What do you think was the criterion others used to make their nominations? ''', widget=widgets.RadioSelect, choices=['Based on contributions during Group Quiz in Task I', 'Random Choice', 'Based on reasons unrelated to Task I'], ) OE = models.StringField(label='In Part I, after Task I (the group quiz) Group A was selected to participate in another task, while your group was not.' ' Which of the following comes closest to why you think Group A was selected for an additional task?', widget = widgets.RadioSelect, choices = ['Group A was randomly selected', 'Group A performed better in Task I', 'Group A performed worse in Task I', 'I didnt think about it'] ) dictator = models.LongStringField(label= 'In Part II, as a "divider", what about your match(es) influenced how many ECUs you gave to them?') pass def creating_session(subsession): # Group Specific Lists rowAT = [] rowBT = [] rowAC = [] rowBC = [] for player in subsession.get_players(): player.name = player.participant.participant_name player.assigned_group = player.participant.group_name if player.participant.group_name == 'A_treat': rowAT.append(player.id_in_subsession) elif player.participant.group_name == 'B_treat': rowBT.append(player.id_in_subsession) elif player.participant.group_name == 'A_cont': rowAC.append(player.id_in_subsession) elif player.participant.group_name == 'B_cont': rowBC.append(player.id_in_subsession) # Draws for Payment random.seed(subsession.session.seed_value) payout_rounds = random.sample(range(1, 9), 2) random.seed(subsession.session.seed_value) payout_role = random.randint(0,1) # Matching for Dictator Game # Create types of sequences for matching: T_num = min(len(rowAT), len(rowBT)) C_num = min(len(rowAC), len(rowBC)) # ABAB sequence1 (not sensitive to total count) Treat_AB = [None]*(T_num*2) Treat_AB[::2] = rowAT[:T_num] Treat_AB[1::2] = rowBT[:T_num] Treat_AB.extend(rowAT[T_num:]) Treat_AB.extend(rowBT[T_num:]) Cont_AB = [None] * (C_num * 2) Cont_AB[::2] = rowAC[:C_num] Cont_AB[1::2] = rowBC[:C_num] Cont_AB.extend(rowAC[C_num:]) Cont_AB.extend(rowBC[C_num:]) # AABB sequence2 (sensitive to total count - OR 1 person repeated) Treat_AABB = [None]*(8) Treat_AABB[::4] = rowAT[:2] Treat_AABB[1::4] = rowAT[-2:] Treat_AABB[2::4] = rowBT[:2] Treat_AABB[3::4] = rowBT[-2:] Cont_AABB = [None]*(8) Cont_AABB[::4] = rowAC[:2] Cont_AABB[1::4] = rowAC[-2:] Cont_AABB[2::4] = rowBC[:2] Cont_AABB[3::4] = rowBC[-2:] #Matching Code for player in subsession.get_players(): player.participant.pay_round1 = payout_rounds[0] player.participant.pay_round2 = payout_rounds[1] player.participant.pay1 = 0 player.participant.pay2 = 0 if player.round_number in payout_rounds: player.payout_round = 1 if player.participant.in_treatment: sequence1 = Treat_AB sequence2 = Treat_AABB else: sequence1 = Cont_AB sequence2 = Cont_AABB player_index1 = sequence1.index(player.id_in_subsession) player_index2 = sequence2.index(player.id_in_subsession) # Matching for ROUNDS 1 TO 4 # Self-Other (R) +1 (Out-group) if player_index1 + 1 < len(sequence1): match_SOR1 = sequence1[player_index1 + 1] else: match_SOR1 = sequence1[0] # Self-Other (R) -1 (Out-group) match_SOR2 = sequence1[player_index1 - 1] # Self-Other (R) +2 (In-group) if player_index1 + 2 < len(sequence1): match_SOR3 = sequence1[player_index1 + 2] else: match_SOR3 = sequence1[player_index1 + 2 - len(sequence1)] # Self-Other (R) -2 (In-group) match_SOR4 = sequence1[player_index1 - 2] SOR_Matches = [match_SOR1, match_SOR2, match_SOR3, match_SOR4] SOR_dividers = [sequence1[::2], sequence1[1::2], sequence1[::4] + sequence1[1::4], sequence1[2::4] + sequence1[3::4]] random.seed(subsession.session.seed_value) #We want the matching 'type' (+1, -1, +2 or -2) to be the same for all players random.shuffle(SOR_Matches) random.seed(subsession.session.seed_value) random.shuffle(SOR_dividers) if player.round_number in [1,2,3,4]: player.match1 = SOR_Matches[player.round_number-1] if player.id_in_subsession in SOR_dividers[player.round_number-1]: player.divider = 1*payout_role else: player.divider = 1 - 1*payout_role # Matching for ROUNDS 5 TO 6 Dividers_56 = sequence2[::2] if player.round_number == 5: # Self-Other-Other (R) +1,+2 if player_index2 + 2 < len(sequence2): player.match1 = sequence2[player_index2 + 1] player.match2 = sequence2[player_index2 + 2] else: if player_index2 + 1 < len(sequence2): player.match1 = sequence2[player_index2 + 1] else: player.match1 = sequence2[player_index2 + 1 - len(sequence2)] player.match2 = sequence2[player_index2 + 2 - len(sequence2)] if player.id_in_subsession in Dividers_56: player.divider = 1*payout_role else: player.divider = 1 - 1*payout_role if player.round_number == 6: # Self-Other-Other (R) -3,-4 player.match1 = sequence2[player_index2 - 3] player.match2 = sequence2[player_index2 - 4] if player.id_in_subsession in Dividers_56: player.divider = 1*payout_role else: player.divider = 1- 1*payout_role # Matching for ROUNDS 7 TO 8 Dividers_78 = sequence2[1::2] if player.round_number == 7: # Self-Other (NR) +1 if player_index2 + 1 < len(sequence2): player.match1 = sequence2[player_index2 + 1] else: player.match1 = sequence2[0] if player.id_in_subsession in Dividers_78: player.divider = 1*payout_role else: player.divider = 1 - 1*payout_role if player.round_number == 8: # Self-Other (NR) -1 player.match1 = sequence2[player_index2 - 1] if player.id_in_subsession in Dividers_78: player.divider = 1*payout_role else: player.divider = 1 - 1*payout_role #Enter if Match in-group or out-group if player.participant.participant_name[0] == player.group.get_player_by_id(player.match1).participant.participant_name[0]: player.match1_ingroup = 1 else: player.match1_ingroup = 0 if player.match2 !=None: if player.participant.participant_name[0] == player.group.get_player_by_id(player.match2).participant.participant_name[0]: player.match2_ingroup = 1 else: player.match2_ingroup = 0 # PAGES class General_Instructions(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class T1(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class T2(Page): @staticmethod def is_displayed(player): return player.round_number == 5 class T3(Page): @staticmethod def is_displayed(player): return player.round_number == 7 class AllocationPage(Page): form_model = 'player' @staticmethod def get_form_fields(player): if player.round_number in [5,6]: return ['keep', 'give1', 'give2'] else: return ['keep', 'give1'] @staticmethod def vars_for_template(player): if player.round_number in [1,2,3,4]: endowment = C.Endowment1 task_round = player.round_number match1_name = player.group.get_player_by_id(player.match1).name return dict( endowment=endowment, task = 'I', task_round = task_round, match1 = match1_name ) elif player.round_number in [5,6]: endowment = C.Endowment2 task_round = player.round_number - 4 match1_name = player.group.get_player_by_id(player.match1).name match2_name = player.group.get_player_by_id(player.match2).name return dict( endowment = endowment, task = 'II', task_round = task_round, match1=match1_name, match2=match2_name ) else: endowment = C.Endowment1 task_round = player.round_number - 8 match1_name = player.group.get_player_by_id(player.match1).name return dict( endowment=endowment, task= 'III', task_round = task_round, match1=match1_name ) @staticmethod def error_message(player, values): if player.round_number in [5,6]: if values['keep'] + values['give1'] + values['give2'] != 20: return 'The sum of divisions must exactly equal 20 ECUs.' if values['keep']<0 or values['give1']<0 or values['give2']<0: return 'You can only give positive number of ECUs to anyone.' else: if values['keep'] + values['give1'] != 10: return 'The sum of divisions must exactly equal 10 ECUs.' if values['keep']<0 or values['give1']<0: return 'You can only give positive number of ECUs to anyone.' @staticmethod def before_next_page(player, timeout_happened): if player.round_number == player.participant.pay_round1: if player.divider == 1: player.participant.role1 = "Divider" player.participant.pay1 = player.keep recepient1 = player.group.get_player_by_id(player.match1) if recepient1.divider != 1: recepient1.participant.pay1 = player.give1 if player.field_maybe_none('match2') != None: recepient2 = player.group.get_player_by_id(player.match2) if recepient2.divider != 1: recepient2.participant.pay1 = player.give2 else: player.participant.role1 = "Receiver" if player.round_number == player.participant.pay_round2: if player.divider == 1: player.participant.role2 = "Divider" player.participant.pay2 = player.keep recepient1 = player.group.get_player_by_id(player.match1) if recepient1.divider != 1: recepient1.participant.pay2 = player.give1 if player.field_maybe_none('match2') != None: recepient2 = player.group.get_player_by_id(player.match2) if recepient2.divider != 1: recepient2.participant.pay2 = player.give2 else: player.participant.role2 = "Receiver" class Conclusion(Page): form_model = 'player' @staticmethod def get_form_fields(player): if player.participant.in_GroupA: return ['study', 'identifyA', 'identifyB', 'IR', 'dictator'] elif (player.participant.in_GroupA == False) and player.participant.in_treatment: return ['study', 'identifyA', 'identifyB', 'OE', 'dictator'] else: return ['study', 'identifyA', 'identifyB', 'dictator'] @staticmethod def is_displayed(player): return player.round_number == 8 class Calculation(WaitPage): @staticmethod def is_displayed(player): return player.round_number == 8 @staticmethod def after_all_players_arrive(group: Group): for player in group.get_players(): player.payoff = player.participant.quiz_score * 2 + player.participant.pay1 + player.participant.pay2 + 4 * player.participant.high_rep class Results(Page): @staticmethod def is_displayed(player): return player.round_number == 8 @staticmethod def vars_for_template(player): Task1_earning = player.participant.quiz_score*2 Pay_1 = player.participant.pay1 Pay_2 = player.participant.pay2 Total = (Task1_earning + Pay_1 + Pay_2 + 4*player.participant.high_rep)*0.4 + 10 return dict( Task1_earning=Task1_earning, Pay_1=Pay_1, Pay_2=Pay_2, Total=Total ) page_sequence = [General_Instructions, T1, T2, T3, AllocationPage, Conclusion, Calculation, Results]