from otree.api import * c = cu doc = """ Comprehension test. If the user fails too many times, they exit. """ class C(BaseConstants): NAME_IN_URL = 'comprehension_test_complex' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): num_failed_attempts = models.IntegerField(initial=0) quiz1 = models.BooleanField( label='Will this study involve you navigating PAC-MAN around a maze to collect fruits?') quiz2 = models.BooleanField( label='For each round, do both fruits carry the same value?') quiz3 = models.BooleanField( label='Is the value of a fruit displayed by a bar under PAC-MAN?') prolific_id = models.StringField(default=str(" ")) # PAGES class InstructionPage1(Page): timeout_seconds=600 @staticmethod def before_next_page(player: Player, timeout_happened): if timeout_happened: player.participant.failed_too_many = True else: player.participant.failed_too_many = False @staticmethod def app_after_this_page(player: Player, upcoming_apps): if player.participant.failed_too_many: return upcoming_apps[-1] # Redirects to the final app. class MyPage1(Page): timeout_seconds=300 form_model = 'player' form_fields = ['quiz1', 'quiz2', '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(quiz1=True, quiz2=False, quiz3=True) #3 is false or true depending on the condition # 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) #add error or label a pass straight away if no mistakes if errors: player.num_failed_attempts += 1 else: player.participant.failed_too_many = False @staticmethod def before_next_page(player: Player, timeout_happened): if timeout_happened: player.participant.failed_too_many = True @staticmethod def app_after_this_page(player: Player, upcoming_apps): if player.participant.failed_too_many: return upcoming_apps[-1] # Redirects to the final app. # PAGES class InstructionPage2(Page): timeout_seconds=600 @staticmethod def is_displayed(player: Player): return player.num_failed_attempts > 0 @staticmethod def before_next_page(player: Player, timeout_happened): if timeout_happened: player.participant.failed_too_many = True @staticmethod def app_after_this_page(player: Player, upcoming_apps): if player.participant.failed_too_many: return upcoming_apps[-1] # Redirects to the final app. class MyPage2(Page): timeout_seconds=300 form_model = 'player' form_fields = ['quiz1', 'quiz2', 'quiz3'] @staticmethod def is_displayed(player: Player): return player.num_failed_attempts > 0 @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(quiz1=True, quiz2=False, quiz3=True) #3 is false or true depending on the condition # 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 #work out pass or fail if player.num_failed_attempts >= 2: player.participant.failed_too_many = True else: player.participant.failed_too_many = False @staticmethod def before_next_page(player: Player, timeout_happened): if timeout_happened: player.participant.failed_too_many = True @staticmethod def app_after_this_page(player: Player, upcoming_apps): if player.participant.failed_too_many: return upcoming_apps[-1] # Redirects to the final app. #class Failed(Page): # @staticmethod # def is_displayed(player: Player): # return player.failed_too_many class Results(Page): @staticmethod def before_next_page(player: Player, timeout_happened): player.prolific_id = player.participant.label import time player.participant.grouping_timeout = time.time() + 600 #give a participant 10 minutes to be grouped - after that they are removed #progress to the end if timed out on instructions if timeout_happened: player.participant.failed_too_many = True @staticmethod def app_after_this_page(player: Player, upcoming_apps): #progress to the end if failed too many if player.participant.failed_too_many==True: return upcoming_apps[-1] page_sequence = [InstructionPage1, MyPage1,InstructionPage2, MyPage2, Results] #page_sequence = [Results]