from otree.api import * import random doc = """ Your app description """ def creating_session(subsession): same_groups = True count = 1 while same_groups: subsession.group_randomly() same_groups = False for i in subsession.get_group_matrix(): if (i[0] % 2) == 0: odd_or_even = 'even' else: odd_or_even = 'odd' if odd_or_even == 'odd': if (i[0] + 1) == i[1]: same_groups = True print(f"Reshuffling because {i[0]} and {i[1]} were in the same group during stage1") break else: if (i[0] - 1) == i[1]: same_groups = True print(f"Reshuffling because {i[0]} and {i[1]} were in the same group during stage1") break for player in subsession.get_players(): if i[0] == player.id_in_subsession: sender = player.participant.condition elif i[1] == player.id_in_subsession: responder = player.participant.condition if sender == 'Strong' and responder == 'Weak': same_groups = True count += 1 print(f"Strong can't send to Weak, attempt number: {count}") break elif sender == 'Strong': print(f"Sender (Player {i[0]}) is sending to {responder} (Player {i[1]})") # Causes warnings but this post says it should be ok: https://groups.google.com/g/otree/c/GOsVPnCv1ek/m/rh-znHV1AgAJ # new_structure = [[1, 3], [2, 4], [5, 7], [6, 21], [8, 22], [9, 23], # [11, 13], [12, 14], [15, 17], [16, 24], [18, 25], # [19, 26], [10, 20]] # subsession.set_group_matrix(new_structure) # def creating_session(subsession): # mover = itertools.cycle(['Responder','Sender']) # if subsession.round_number == 1: # for group in subsession.get_groups(): # group.mover = next(mover) # elif subsession.round_number > 1: # for group in subsession.get_groups(): # group.mover = group.in_round(1).mover class C(BaseConstants): NAME_IN_URL = 'stage2' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 1 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): condition = models.StringField() company = models.StringField() other_player_company = models.StringField() mover = models.StringField(initial='') sender_amount = models.IntegerField(initial=0, min=0, max=3000) responder_amount = models.IntegerField(initial=0, min=0) pool_amount = models.IntegerField(initial=0) max_pool = models.IntegerField(initial=0) compensation = models.IntegerField(initial=0) identifier = models.IntegerField() #Comprehension Check questions def make_field(label): return models.IntegerField( choices=[[1, "True",], [2, "False"]], label=label, widget=widgets.RadioSelect, ) quiz11 = make_field("In Stage 2, you will be paired with the same participant as in Stage 1.") quiz12 = make_field("You will learn the type of company (in terms of individual bonus) the other participant worked for in Stage 1.") quiz13 = make_field("The minimum amount the Responder can give back to the Sender is zero.") quiz14 = make_field("The Sender keeps any amount not sent to the Responder as part of the Sender’s Stage 2 payoff.") #Comprehension Check questions def make_field2(label): return models.IntegerField( choices=[[1, "Strongly disagree",], [2, "Disagree",], [3, "Somewhat disagree",], [4, "Neither agree nor disagree",], [5, "Somewhat agree",], [6, "Agree",], [7, "Strongly agree"] ], label=label, widget=widgets.RadioSelectHorizontal, ) process1 = make_field2("I felt the other employee I was paired with in Stage 1 was trustworthy.") process3a = make_field2("In Stage 2, I considered the amount of individual bonus the other employee received in Stage 1 when deciding how much to give.") process3b = make_field2("In Stage 2, I considered the amount of individual bonus the other employee received in Stage 1 when deciding how much to return.") # We need to insert the type of company that the other employee is paired with in Stage 2, and insert variable for my company type. process2 = models.StringField( label = "Do you think employees in the other Type X company chose HIGH more (or less) frequently in Stage 1 than employees in your company?", choices = ["Much less frequently", "Less frequently", "Slightly less frequently", "About the same", "Slightly more frequently", "More frequently", "Much more frequently"], widget = widgets.RadioSelect) incorrect_quiz11 = models.IntegerField(initial=0) incorrect_quiz12 = models.IntegerField(initial=0) incorrect_quiz13 = models.IntegerField(initial=0) incorrect_quiz14 = models.IntegerField(initial=0) #Demographic Questions age = models.IntegerField(min = 18, max = 99, label = 'What is your age?') gender = models.StringField( label = 'What is your gender?', choices = ['Male', 'Female', 'Non-Binary', 'Prefer not to answer'], widget=widgets.RadioSelect) experience = models.IntegerField(max = 70, label = 'How many years of working experience have you had?',) # work = models.StringField( # label = 'Have you ever worked on any inter-organizational project with someone from a different organization or company?', # choices = [ # [1, "Yes"], # [2, "No"] # ], # widget=widgets.RadioSelect) def responder_amount_max(player): return player.pool_amount def update_pool_sender(group: Group): players = group.get_players() for i in players: if i.mover == 'Sender': sender_value = i.sender_amount for i in players: if i.mover == 'Responder': i.sender_amount = sender_value for i in players: max_pool = sender_value * 3 i.max_pool = max_pool i.pool_amount = max_pool def update_pool_responder(group: Group): players = group.get_players() for i in players: if i.mover == 'Responder': responder_value = i.responder_amount for i in players: if i.mover == 'Sender': i.responder_amount = responder_value for i in players: i.pool_amount = i.pool_amount - responder_value if i.mover == 'Sender': i.compensation = 3000 - i.sender_amount + i.responder_amount if i.mover == 'Responder': i.compensation = i.max_pool - i.responder_amount # Sender Amount = 1,000 - sender_amount + amount sent by responder # Responder Amount = max_pool - responder_amount_sent_back # PAGES class Process1(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 form_model = 'player' form_model = 'player' form_fields = ["process1"] class Stage2Set(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 #Adding the Condition (None, Weak, Strong from Stage 1 into a collectible variable in Stage 2) @staticmethod def before_next_page(player: Player, timeout_happened): if player.participant.condition == "None": player.condition = "None" elif player.participant.condition == "Weak": player.condition = "Weak" elif player.participant.condition == "Strong": player.condition = "Strong" class Quiz1(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 form_model = 'player' form_fields = ["quiz11"] @staticmethod def error_message(player, values): solutions = dict( quiz11=2, ) error_messages = dict() for field_name in solutions: if values[field_name] != solutions[field_name] and field_name == 'quiz11': error_messages[field_name] = 'INCORRECT! Please try again.' player.incorrect_quiz11 += 1 return error_messages class Stage2Task(Page): @staticmethod def is_displayed(player: Player): return player.mover == "" #Adding the Condition (None, Weak, Strong from Stage 1 into a collectible variable in Stage 2) @staticmethod def before_next_page(player: Player, timeout_happened): if player.id_in_group == 1: player.mover = "Responder" elif player.id_in_group == 2: player.mover = "Sender" class Quiz2(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 form_model = 'player' form_fields = ["quiz12","quiz13","quiz14"] @staticmethod def error_message(player, values): solutions = dict( quiz12=1, quiz13=1, quiz14=1) error_messages = dict() for field_name in solutions: if values[field_name] != solutions[field_name] and field_name == 'quiz12': error_messages[field_name] = 'INCORRECT! Please try again.' player.incorrect_quiz12 += 1 if values[field_name] != solutions[field_name] and field_name == 'quiz13': error_messages[field_name] = 'INCORRECT! Please try again.' player.incorrect_quiz13 += 1 if values[field_name] != solutions[field_name] and field_name == 'quiz14': error_messages[field_name] = 'INCORRECT! Please try again.' player.incorrect_quiz14 += 1 return error_messages class Decision(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 form_model = 'player' form_fields = ["process2"] @staticmethod #Need to show P1 whether P2 was in None, Weak, Strong #Need to show P2 whether P1 wasin None, Weak, Strong def vars_for_template(player: Player): other_player = player.get_others_in_group() if other_player[0].participant.condition == "None": player.other_player_company = "C" return_value = { "otherplayerbonus": "0", "otherplayercompany": "C" } elif other_player[0].participant.condition == "Weak": player.other_player_company = "B" return_value = { "otherplayerbonus": "90", "otherplayercompany": "B" } elif other_player[0].participant.condition == "Strong": player.other_player_company = "A" return_value = { "otherplayerbonus": "220", "otherplayercompany": "A" } if player.condition == "None": player.company = 'C' return_value['mybonus'] = 0 return_value['mycompany'] = 'C' elif player.condition == "Weak": player.company = 'B' return_value['mybonus'] = 90 return_value['mycompany'] = 'B' elif player.condition == "Strong": player.company = 'A' return_value['mybonus'] = 220 return_value['mycompany'] = 'A' return_value['process2_label'] = f"Do you think employees in the other Type {player.other_player_company} company chose HIGH more (or less) frequently in Stage 1 than employees in your company, Type {player.company}?" if player.condition != other_player[0].participant.condition: return_value['process2_label'] = f"Do you think employees in the Type {player.other_player_company} company chose HIGH more (or less) frequently in Stage 1 than employees in your company, Type {player.company}?" return return_value class SenderPage(Page): @staticmethod def is_displayed(player: Player): return player.mover == "Sender" form_model = 'player' form_fields = ["sender_amount"] @staticmethod #Need to show P1 whether P2 was in None, Weak, Strong #Need to show P2 whether P1 wasin None, Weak, Strong def vars_for_template(player: Player): other_player = player.get_others_in_group() if other_player[0].participant.condition == "None": return_value = { "otherplayerbonus": "0", "otherplayercompany": "C" } elif other_player[0].participant.condition == "Weak": return_value = { "otherplayerbonus": "90", "otherplayercompany": "B" } elif other_player[0].participant.condition == "Strong": return_value = { "otherplayerbonus": "220", "otherplayercompany": "A" } if player.condition == "None": return_value['mybonus'] = 0 return_value['mycompany'] = 'C' return return_value elif player.condition == "Weak": return_value['mybonus'] = 90 return_value['mycompany'] = 'B' return return_value elif player.condition == "Strong": return_value['mybonus'] = 220 return_value['mycompany'] = 'A' return return_value class WaitForSender(WaitPage): body_text = "Please wait until the other participant makes decision." after_all_players_arrive = update_pool_sender class ResponderPage(Page): @staticmethod def is_displayed(player: Player): return player.mover == "Responder" form_model = 'player' form_fields = ["responder_amount"] # Need to see amount sender sent before multiplying by 3 def vars_for_template(player: Player): other_player = player.get_others_in_group() return {"sent_amount": other_player[0].sender_amount} class WaitForResponder(WaitPage): body_text = "Please wait until the other participant makes decision." after_all_players_arrive = update_pool_responder class Process(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 form_model = 'player' @staticmethod def get_form_fields(player: Player): if player.mover == 'Sender': return ["process3a"] elif player.mover == 'Responder': return ["process3b"] class Demo(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 form_model = 'player' form_fields = ["age", "gender", "experience"] # We have to show them their full payoff including the Lira they received in Stage 1 + Stage and $5 show up fee # Payoff = (Stage 1 Lira + Stage 2 Lira)/100) + $5) # def payoff(player: Player): # player.payoff class Feedback(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 def vars_for_template(player: Player): player.identifier = random.randint(1, 6000) player.payoff = (player.participant.compensation + player.compensation)/500 return_dict = { 'stage1_lira': player.participant.compensation, 'stage2_lira': player.compensation, # 'show_up_fee': 5.00, 'payoff': player.payoff, 'identifier': f'player_{player.id_in_subsession}_{player.identifier}'} return return_dict # class Final(Page): # @staticmethod # def is_displayed(player: Player): # return player.round_number == 1 # def vars_for_template(player: Player): # player.identifier = random.randint(1, 6000) # return_dict = { # 'identifier': f'player_{player.id_in_subsession}_{player.identifier}' # } # return return_dict # page_sequence = [Stage2Task, Quiz2, Decision, SenderPage, WaitForSender, ResponderPage, WaitForResponder] page_sequence = [Process1, Stage2Set, Quiz1, Stage2Task, Quiz2, Decision, SenderPage, WaitForSender, ResponderPage, WaitForResponder, Process, Demo, Feedback]