import random from otree.api import * doc = """ Comprehension test. If the user fails too many times, they exit. """ class C(BaseConstants): NAME_IN_URL = 'Instruction_Quiz' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): treatment = models.IntegerField() num_failed_attempts = models.IntegerField(initial=0) failed_too_many = models.BooleanField(initial=False) seat = models.IntegerField(min=1, max=40, label="Enter your Player ID to start (Seat number in front of you)") quiz1 = models.IntegerField(min=0, max=10, label='''1. Based on this Payoff Table, if Player 3 chooses A3, and Player 2 chooses C2, what is the payoff of Player 2.''', ) quiz21 = models.StringField(label='''Which option did this player chose?''', choices=["A1", "B1", "C1", "D1", "A2", "B2", "C2", "D2", "A3", "B3", "C3", "D3", "A4", "B4", "C4", "D4"], ) quiz22 = models.StringField( label='''Which option did his direct opponent choose? (Which option besides the player's own choice leads to this payoff?)''', choices=["A1", "B1", "C1", "D1", "A2", "B2", "C2", "D2", "A3", "B3", "C3", "D3", "A4", "B4", "C4", "D4"], ) quiz3 = models.StringField( label="3. What a computer-controlled Player 3 will choose if it has believed that Player 4 is going to choose D4? ", choices=["A3", "B3", "C3", "D3"], widget=widgets.RadioSelectHorizontal, ) quiz4 = models.StringField( label='''4. Which of the following belief should computer Player 2 hold for Player 3 if it also knows that computer Player 4 is going to choose D4? ''', choices=["The computer Player 3 will choose at random", "The computer Player 3 will choose A3 for sure", "The computer Player 3 will choose B3 for sure", "The computer Player 3 will choose C3 and D3 with equal probability", "The computer Player 3 will choose D3 for sure", "You don't know what the computer Player 3 going to chose"], widget=widgets.RadioSelectHorizontal) clicked_cell_quiz1 = models.LongStringField(blank=True) clicked_cell_quiz3 = models.LongStringField(blank=True) clicked_cell_ins1 = models.LongStringField(blank=True) clicked_cell_ins2 = models.LongStringField(blank=True) class Welcome(Page): form_model = 'player' form_fields = ['seat'] class Instruction0(Page): pass class Instruction1(Page): pass class InstructionPT(Page): form_model = 'player' form_fields = ['clicked_cell_ins1'] class Comprehension1(Page): form_model = 'player' form_fields = ['quiz1', 'quiz21', 'quiz22', 'clicked_cell_quiz1'] @staticmethod def error_message(player: Player, values): solutions = dict(quiz1=9, quiz21='B2', quiz22='D3') # error_message can return a dict whose keys are field names and whose # values are error messages errors = {name: 'Wrong' for name in solutions if values[name] != solutions[name]} # print('errors is', errors) if errors: player.num_failed_attempts += 1 if player.num_failed_attempts >= 2: player.failed_too_many = True # we don't return any error here; just let the user proceed to the # next page, but the next page is the 'failed' page that boots them # from the experiment. else: return errors class Failed1(Page): @staticmethod def is_displayed(player: Player): return player.failed_too_many class InstructionPC(Page): form_model = 'player' form_fields = ['clicked_cell_ins2'] class Comprehension2(Page): form_model = 'player' form_fields = ['quiz3', 'quiz4', 'clicked_cell_quiz3'] @staticmethod def error_message(player: Player, values): # alternatively, you could make quiz1_error_message, quiz2_error_message, etc. # but if you have many similar fields, this is more efficient. solutions = dict(quiz3='D3', quiz4='The computer Player 3 will choose D3 for sure') # error_message can return a dict whose keys are field names and whose # values are error messages errors = {name: 'Wrong' for name in solutions if values[name] != solutions[name]} # print('errors is', errors) if errors: player.num_failed_attempts += 1 if player.num_failed_attempts >= 3: player.failed_too_many = True # we don't return any error here; just let the user proceed to the # next page, but the next page is the 'failed' page that boots them # from the experiment. else: return errors class Failed2(Page): @staticmethod def is_displayed(player: Player): return player.failed_too_many class InstructionP2(Page): @staticmethod def app_after_this_page(player, upcoming_apps): # Example condition based on a participant variable if player.seat % 2 == 1: return 'Main' else: return 'Reverse' page_sequence = [Welcome, Instruction0, Instruction1, InstructionPT, Comprehension1, Failed1, InstructionPC, Comprehension2, Failed2, InstructionP2]