from otree.api import * from constants import * import random doc = """Understanding Questions for Generation 1 before Public Goods Game""" class C(BaseConstants): NAME_IN_URL = 'B_PGGame_UQ_Gen1_D' PLAYERS_PER_GROUP = None NUM_ROUNDS = Round_FirstTask class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): Ana_generation = models.IntegerField() Ana_own_id = models.IntegerField() Ana_family_assignment = models.StringField() first_practice = models.IntegerField() # Understanding questions UQ1 = models.IntegerField( label="""1) True or False? "In a round of the Decision Task, holding Member 1 and 2's contributions constant, if Member 3 decreases his/her contribution, his/her earnings will increase." """, choices=[ (1, 'True'), (2, 'False'), ], widget=widgets.RadioSelect, blank=True, ) UQ2 = models.IntegerField( label="2) If all three group members contribute their entire endowments to the joint project in a round of Decision Task, what will the outcome be?", choices=[ (1, 'Each member earns 30 ECUs'), (2, 'The high endowment member earns 30 ECUs. The low endowment members earn 15 ECUs.'), (3, 'The high endowment member earns 37.5 ECUs. The low endowment members earn 22.5 ECUs.'), ], widget=widgets.RadioSelect, blank=True, ) UQ3 = models.IntegerField( label="3) If all three group members contribute nothing to the joint project in a round of Decision Task, what will the outcome be?", choices=[ (1, 'Each member earns 30 ECUs'), (2, 'The high endowment member earns 30 ECUs. The low endowment members earn 15 ECUs.'), (3, 'The high endowment member earns 37.5 ECUs. The low endowment members earn 22.5 ECUs.'), ], widget=widgets.RadioSelect, blank=True, ) UQ4 = models.IntegerField( label="4) If each group member contributes 15 ECUs to the joint project in a round of the Decision Task, what will the outcome be?", choices=[ (1, 'Each member earns 30 ECUs'), (2, 'The high endowment member earns 30 ECUs. The low endowment members earn 15 ECUs.'), (3, 'The high endowment member earns 37.5 ECUs. The low endowment members earn 22.5 ECUs.'), ], widget=widgets.RadioSelect, blank=True, ) UQ5 = models.IntegerField( label="5) When assigning points to others based on their contributions, what does more points mean?", choices=[ (1, 'More approval'), (2, 'More disapproval'), ], widget=widgets.RadioSelect, blank=True, ) UQ6 = models.IntegerField( label="6) Will (i) assigning points or (ii) being assigned points affect your earnings directly in a round?", choices=[ (1, 'Both affect my earnings'), (2, 'Only ii) being assigned points affects my earnings'), (3, 'Neither affects my earnings'), ], widget=widgets.RadioSelect, blank=True, ) UQ7 = models.IntegerField( label="""7) True or False? "During the 16 rounds of the decision task, the group of families you will be interacting with will remain the same throughout." """, choices=[ (1, 'True'), (2, 'False'), ], widget=widgets.RadioSelect, blank=True, ) UQ8 = models.IntegerField( label="""8) True or False? "During the 16 rounds of the decision task, your endowment type (either low or high) remains fixed throughout." """, choices=[ (1, 'True'), (2, 'False'), ], widget=widgets.RadioSelect, blank=True, ) Ana_practice_contrib_high = models.IntegerField(label="", min=0, max=30, blank=False) Ana_practice_contrib_low = models.IntegerField(label="", min=0, max=15, blank=False) Ana_demo_group_id = models.IntegerField() Ana_demo_endowment = models.IntegerField() Ana_demo_contribution = models.IntegerField() def UQ1_error_message(player, value): if value != 1: return 'UQ1: Incorrect' def UQ2_error_message(player, value): if value != 1: return 'UQ2: Incorrect' def UQ3_error_message(player, value): if value != 2: return 'UQ3: Incorrect' def UQ4_error_message(player, value): if value != 1: return 'UQ4: Incorrect' class WaitForUQ_G1(Page): wait_for_all_groups = True @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") class UnderstandingQ0_G1(Page): @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") class UnderstandingQ1_G1(Page): form_model = 'player' form_fields = ['UQ1', 'UQ2', 'UQ3', 'UQ4'] @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") @staticmethod def error_message(player, values): player.participant.vars['Ana_UQ_attempts_page1'] = player.participant.vars.get('Ana_UQ_attempts_page1', 0) + 1 class UnderstandingQ2_G1(Page): form_model = 'player' form_fields = ['UQ5', 'UQ6'] @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") @staticmethod def error_message(player, values): player.participant.vars['Ana_UQ_attempts_page2'] = player.participant.vars.get('Ana_UQ_attempts_page2', 0) + 1 class UnderstandingQ3_G1(Page): form_model = 'player' form_fields = ['UQ7', 'UQ8'] @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") @staticmethod def error_message(player, values): player.participant.vars['Ana_UQ_attempts_page3'] = player.participant.vars.get('Ana_UQ_attempts_page3', 0) + 1 class DemoRound_Intro_G1(Page): @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") @staticmethod def vars_for_template(player): return {'first_practice_order': player.participant.first_practice_order} class DemoRound_High_Endowment(Page): form_model = 'player' form_fields = ['Ana_practice_contrib_high'] @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active" and player.participant.vars.get('first_practice_order') == 0) @staticmethod def before_next_page(player, timeout_happened): player.participant.vars['Ana_practice_contrib_high'] = player.Ana_practice_contrib_high class DemoRound_Low_Endowment(Page): form_model = 'player' form_fields = ['Ana_practice_contrib_low'] @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") @staticmethod def before_next_page(player, timeout_happened): player.participant.vars['Ana_practice_contrib_low'] = player.Ana_practice_contrib_low class DemoRound_High_Endowment_2(Page): form_model = 'player' form_fields = ['Ana_practice_contrib_high'] @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active" and player.participant.vars.get('first_practice_order') == 1) @staticmethod def before_next_page(player, timeout_happened): player.participant.vars['Ana_practice_contrib_high'] = player.Ana_practice_contrib_high class DemoRound_Calculation(WaitPage): wait_for_all_groups = True @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") @staticmethod def after_all_players_arrive(subsession): players = [p for p in subsession.get_players() if p.participant.vars.get('Ana_generation') == 1 and p.participant.vars.get('state') == "active"] random.shuffle(players) groups = [players[i:i + 3] for i in range(0, len(players), 3) if len(players[i:i + 3]) == 3] for group_idx, group in enumerate(groups): high_endowment_player = random.choice(group) for player in group: player.Ana_demo_group_id = group_idx if player == high_endowment_player: player.Ana_demo_endowment = 30 player.Ana_demo_contribution = player.participant.vars.get('Ana_practice_contrib_high', 0) else: player.Ana_demo_endowment = 15 player.Ana_demo_contribution = player.participant.vars.get('Ana_practice_contrib_low', 0) total_contribution = sum(p.Ana_demo_contribution for p in group) for player in group: individual_share = (1.5 * total_contribution) / 3 demo_payoff = (player.Ana_demo_endowment - player.Ana_demo_contribution) + individual_share player.participant.vars['DemoRound_Payoff'] = demo_payoff player.participant.vars['Ana_demo_endowment'] = player.Ana_demo_endowment player.participant.vars['Ana_demo_contribution'] = player.Ana_demo_contribution player.participant.vars['Ana_demo_group_id'] = player.Ana_demo_group_id class DemoRound_Outro(Page): @staticmethod def is_displayed(player): return (player.round_number == C.NUM_ROUNDS and player.participant.vars.get('Ana_generation') == 1 and player.participant.vars.get('state') == "active") @staticmethod def vars_for_template(player): return { 'DemoRound_Payoff': player.participant.DemoRound_Payoff, 'Ana_demo_endowment': player.participant.Ana_demo_endowment, 'Ana_demo_contribution': player.participant.Ana_demo_contribution, 'Ana_demo_group_id': player.participant.Ana_demo_group_id, } page_sequence = [ WaitForUQ_G1, UnderstandingQ0_G1, UnderstandingQ1_G1, UnderstandingQ2_G1, UnderstandingQ3_G1, DemoRound_Intro_G1, DemoRound_High_Endowment, DemoRound_Low_Endowment, DemoRound_High_Endowment_2, DemoRound_Calculation, DemoRound_Outro ]